Skip to content

Commit

Permalink
Merge pull request getredash#26 from codygo-solutions/counter-viz-text
Browse files Browse the repository at this point in the history
Counter viz text
  • Loading branch information
danikenan authored Sep 4, 2023
2 parents ae6df89 + 0a12e02 commit 743c3c9
Show file tree
Hide file tree
Showing 13 changed files with 714 additions and 33 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React from "react";
import { Section, Input, InputNumber, Switch } from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations/prop-types";

import { isValueNumber } from "../utils";

export default function FormatSettings({ options, data, onOptionsChange }: any) {
const inputsEnabled = isValueNumber(data.rows, options);
return (
<React.Fragment>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<InputNumber
layout="horizontal"
label="Formatting Decimal Place"
data-test="Counter.Formatting.DecimalPlace"
defaultValue={options.stringDecimal}
disabled={!inputsEnabled}
onChange={(stringDecimal: any) => onOptionsChange({ stringDecimal })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
layout="horizontal"
label="Formatting Decimal Character"
data-test="Counter.Formatting.DecimalCharacter"
defaultValue={options.stringDecChar}
disabled={!inputsEnabled}
onChange={(e: any) => onOptionsChange({ stringDecChar: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
layout="horizontal"
label="Formatting Thousands Separator"
data-test="Counter.Formatting.ThousandsSeparator"
defaultValue={options.stringThouSep}
disabled={!inputsEnabled}
onChange={(e: any) => onOptionsChange({ stringThouSep: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
layout="horizontal"
label="Formatting String Prefix"
data-test="Counter.Formatting.StringPrefix"
defaultValue={options.stringPrefix}
disabled={!inputsEnabled}
onChange={(e: any) => onOptionsChange({ stringPrefix: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
layout="horizontal"
label="Formatting String Suffix"
data-test="Counter.Formatting.StringSuffix"
defaultValue={options.stringSuffix}
disabled={!inputsEnabled}
onChange={(e: any) => onOptionsChange({ stringSuffix: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Switch
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
data-test="Counter.Formatting.FormatTargetValue"
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
defaultChecked={options.formatTargetValue}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(formatTargetValue: any) => any' is not assi... Remove this comment to see the full error message
onChange={(formatTargetValue: any) => onOptionsChange({ formatTargetValue })}>
Format Target Value
</Switch>
</Section>
</React.Fragment>
);
}

FormatSettings.propTypes = EditorPropTypes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { map } from "lodash";
import React from "react";
import { Section, Select, Input, InputNumber, Switch } from "@/components/visualizations/editor";
import { EditorPropTypes } from "@/visualizations/prop-types";

export default function GeneralSettings({ options, data, visualizationName, onOptionsChange }: any) {
return (
<React.Fragment>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Input
layout="horizontal"
label="Counter Label"
data-test="Counter.General.Label"
defaultValue={options.counterLabel}
placeholder={visualizationName}
onChange={(e: any) => onOptionsChange({ counterLabel: e.target.value })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Select
layout="horizontal"
label="Counter Value Column Name"
data-test="Counter.General.ValueColumn"
defaultValue={options.counterColName}
disabled={options.countRow}
onChange={(counterColName: any) => onOptionsChange({ counterColName })}>
{map(data.columns, col => (
// @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message
<Select.Option key={col.name} data-test={"Counter.General.ValueColumn." + col.name}>
{col.name}
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
</Select.Option>
))}
</Select>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<InputNumber
layout="horizontal"
label="Counter Value Row Number"
data-test="Counter.General.ValueRowNumber"
defaultValue={options.rowNumber}
disabled={options.countRow}
onChange={(rowNumber: any) => onOptionsChange({ rowNumber })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<Select
layout="horizontal"
label="Target Value Column Name"
data-test="Counter.General.TargetValueColumn"
defaultValue={options.targetColName}
onChange={(targetColName: any) => onOptionsChange({ targetColName })}>
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
<Select.Option value="">No target value</Select.Option>
{map(data.columns, col => (
// @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message
<Select.Option key={col.name} data-test={"Counter.General.TargetValueColumn." + col.name}>
{col.name}
{/* @ts-expect-error ts-migrate(2339) FIXME: Property 'Option' does not exist on type '({ class... Remove this comment to see the full error message */}
</Select.Option>
))}
</Select>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
<InputNumber
layout="horizontal"
label="Target Value Row Number"
data-test="Counter.General.TargetValueRowNumber"
defaultValue={options.targetRowNumber}
onChange={(targetRowNumber: any) => onOptionsChange({ targetRowNumber })}
/>
</Section>

{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Section>
{/* @ts-expect-error ts-migrate(2745) FIXME: This JSX tag's 'children' prop expects type 'never... Remove this comment to see the full error message */}
<Switch
// @ts-expect-error ts-migrate(2322) FIXME: Type 'string' is not assignable to type 'never'.
data-test="Counter.General.CountRows"
// @ts-expect-error ts-migrate(2322) FIXME: Type 'any' is not assignable to type 'never'.
defaultChecked={options.countRow}
// @ts-expect-error ts-migrate(2322) FIXME: Type '(countRow: any) => any' is not assignable to... Remove this comment to see the full error message
onChange={(countRow: any) => onOptionsChange({ countRow })}>
Count Rows
</Switch>
</Section>
</React.Fragment>
);
}

GeneralSettings.propTypes = EditorPropTypes;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import createTabbedEditor from "@/components/visualizations/editor/createTabbedEditor";

import GeneralSettings from "./GeneralSettings";
import FormatSettings from "./FormatSettings";

export default createTabbedEditor([
{ key: "General", title: "General", component: GeneralSettings },
{ key: "Format", title: "Format", component: FormatSettings },
]);
98 changes: 98 additions & 0 deletions viz-lib/src/visualizations/addressable-counter/Renderer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { isFinite } from "lodash";
import React, { useState, useEffect } from "react";
import cx from "classnames";
import resizeObserver from "@/services/resizeObserver";
import { RendererPropTypes } from "@/visualizations/prop-types";

import { getCounterData } from "./utils";

import "./render.less";

import { formatNumber } from '@/services/formatNumber';
import NotEnoughData from '@/components/NotEnoughData';

function getCounterStyles(scale: any) {
return {
msTransform: `scale(${scale})`,
MozTransform: `scale(${scale})`,
WebkitTransform: `scale(${scale})`,
transform: `scale(${scale})`,
};
}

function getCounterScale(container: any) {
const inner = container.firstChild;
const scale = Math.min(container.offsetWidth / inner.offsetWidth, container.offsetHeight / inner.offsetHeight);
return Number(isFinite(scale) ? scale : 1).toFixed(2); // keep only two decimal places
}

const format = (num: string) => formatNumber(Number(num.replace(/\,/g,'')));

export default function Renderer({ data, options, visualizationName }: any) {
const [scale, setScale] = useState("1.00");
const [container, setContainer] = useState(null);

useEffect(() => {
if (container) {
const unwatch = resizeObserver(container, () => {
setScale(getCounterScale(container));
});
return unwatch;
}
}, [container]);

useEffect(() => {
if (container) {
// update scaling when options or data change (new formatting, values, etc.
// may change inner container dimensions which will not be tracked by `resizeObserver`);
setScale(getCounterScale(container));
}
}, [data, options, container]);

const {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'showTrend' does not exist on type '{}'.
showTrend,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'trendPositive' does not exist on type '{... Remove this comment to see the full error message
trendPositive,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'counterValue' does not exist on type '{}... Remove this comment to see the full error message
counterValue,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'counterValueTooltip' does not exist on t... Remove this comment to see the full error message
counterValueTooltip,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'targetValue' does not exist on type '{}'... Remove this comment to see the full error message
targetValue,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'targetValueTooltip' does not exist on ty... Remove this comment to see the full error message
targetValueTooltip,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'counterLabel' does not exist on type '{}... Remove this comment to see the full error message
counterLabel,
} = getCounterData(data.rows, options, visualizationName);

if(data?.rows?.length === 0 || !data?.rows ) return <NotEnoughData />

return (
<div
className={cx("addressable-counter-visualization-container", {
"trend-positive": showTrend && trendPositive,
"trend-negative": showTrend && !trendPositive,
})}>
{/* @ts-expect-error ts-migrate(2322) FIXME: Type 'Dispatch<SetStateAction<null>>' is not assig... Remove this comment to see the full error message */}
<div className="counter-visualization-content" ref={setContainer}>
<div style={getCounterStyles(scale)}>
<div className="counter-visualization-value-wrap">
<div className="counter-visualization-target" title={targetValue ? targetValueTooltip : counterValueTooltip}>
{format(targetValue ?? counterValue)}
</div>
{targetValue && (
<div className="counter-visualization-value" title={counterValueTooltip}>
{showTrend ? trendPositive ? '+' : '-' : ''}
{format(counterValue)}
</div>
)}
</div>
<div className="counter-visualization-label">{counterLabel}</div>
</div>
</div>
</div>
);
}

Renderer.propTypes = RendererPropTypes;
27 changes: 27 additions & 0 deletions viz-lib/src/visualizations/addressable-counter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import Renderer from "./Renderer";
import Editor from "./Editor";

const DEFAULT_OPTIONS = {
counterLabel: "",
counterColName: "counter",
rowNumber: 1,
targetRowNumber: 1,
stringDecimal: 0,
stringDecChar: ".",
stringThouSep: ",",
tooltipFormat: "0,0.000", // TODO: Show in editor
};

export default {
type: "ADDRESSABLE COUNTER",
name: "Counter (Addressable)",
getOptions: (options: any) => ({
...DEFAULT_OPTIONS,
...options,
}),
Renderer,
Editor,

defaultColumns: 2,
defaultRows: 5,
};
56 changes: 56 additions & 0 deletions viz-lib/src/visualizations/addressable-counter/render.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
@import (reference, less) "~@/assets/less/inc/variables";

.addressable-counter-visualization-container {
display: block;
text-align: center;
padding: 15px 24px;
overflow: hidden;
position: relative;

.counter-visualization-content {
margin: 0;
padding: 0;
font-size: 80px;
line-height: normal;
overflow: hidden;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: @gray-400;
font-weight: 400;

.counter-visualization-value-wrap {
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
margin-bottom: 4px;
}

.counter-visualization-target {
font-size: 20px;
line-height: 20px;
font-weight: 400;
display: block;
color: #596284;
}

.counter-visualization-value,
.counter-visualization-label {
font-size: 11px;
line-height: 16px;
font-weight: 400;
display: block;
color: #596284;
}
}

&.trend-positive .counter-visualization-value {
color: #5cb85c;
}

&.trend-negative .counter-visualization-value {
color: #d9534f;
}
}
Loading

0 comments on commit 743c3c9

Please sign in to comment.