From fece90c47299e0ddbea10367dd3b15ec8963ca8c Mon Sep 17 00:00:00 2001 From: vicky-comeau <110498164+vicky-comeau@users.noreply.github.com> Date: Wed, 3 Jan 2024 09:44:37 -0500 Subject: [PATCH] Fix: [IGL-109] AreaChart dot colors (#622) --- .changeset/rotten-olives-kiss.md | 5 + packages/AreaChart/src/AreaChart.tsx | 67 +++++++-- packages/AreaChart/src/ChartTooltip.tsx | 164 ++++++++++++---------- packages/AreaChart/src/area-chart.scss | 4 +- packages/AreaChart/src/chart-tooltip.scss | 7 +- 5 files changed, 150 insertions(+), 97 deletions(-) create mode 100644 .changeset/rotten-olives-kiss.md diff --git a/.changeset/rotten-olives-kiss.md b/.changeset/rotten-olives-kiss.md new file mode 100644 index 00000000..9bb94811 --- /dev/null +++ b/.changeset/rotten-olives-kiss.md @@ -0,0 +1,5 @@ +--- +"@igloo-ui/area-chart": minor +--- + +Updated AreaChart's dot colors for Workleap on both the line and tooltip. Also fixed the DataRange min and max prop types so that it only accepts specific string values; like it always should have been. diff --git a/packages/AreaChart/src/AreaChart.tsx b/packages/AreaChart/src/AreaChart.tsx index fc81fc86..03b53eee 100644 --- a/packages/AreaChart/src/AreaChart.tsx +++ b/packages/AreaChart/src/AreaChart.tsx @@ -61,11 +61,11 @@ interface DataRange { /** The min value of the y axis score * (Possible values: number, 'auto', 'dataMin' or 'dataMax') */ - min: number | string; + min: number | "auto" | "dataMin" | "dataMax"; /** The min value of the y axis score * (Possible values: number, 'auto', 'dataMin' or 'dataMax') */ - max: number | string; + max: number | "auto" | "dataMin" | "dataMax"; } interface DateTimeRange { @@ -104,6 +104,24 @@ export interface AreaChartProps extends React.ComponentProps<"div"> { loading?: boolean; } +const getScores = (data: DataSet[]): number[] => { + const scores = data.filter(score => score !== null).map(dataSet => dataSet.score) as number[]; + + return scores; +}; + +const getScoreMin = (data: DataSet[]): number => { + const min = Math.min(...getScores(data)); + + return min; +}; + +const getScoreMax = (data: DataSet[]): number => { + const max = Math.max(...getScores(data)); + + return max; +}; + const AreaChart: React.FunctionComponent = ({ className, dataSet, @@ -126,7 +144,7 @@ const AreaChart: React.FunctionComponent = ({ const SkeletonAxisTick = ({ x, y, - className, + className: skeletonClassName, payload, skeletonWidth = DEFAULT_SKELETON_WIDTH, skeletonHeight = DEFAULT_SKELETON_HEIGHT, @@ -157,7 +175,7 @@ const AreaChart: React.FunctionComponent = ({ = ({ r: 4, strokeWidth: 1, stroke: "var(--ids-area-chart-dot-stroke-color)", - fill: "var(--ids-area-chart-dot-color)" + fill: "var(--ids-area-chart-line-color)" }; const commonAreaConfig = { @@ -384,6 +402,26 @@ const AreaChart: React.FunctionComponent = ({ stroke: "none", fill: "none" }; + + const getDataHue = (score: number, scoreRange: DataRange): string => { + const scoreMin = (typeof scoreRange.min === "number") ? scoreRange.min : getScoreMin(dataSet); + const scoreMax = (typeof scoreRange.max === "number") ? scoreRange.max : getScoreMax(dataSet); + const relativeScore = Math.round((score - scoreMin) * 100) / 100; + const scale = Math.round((scoreMax - scoreMin) * 100) / 100; + const ratio = relativeScore / scale; + + if (ratio <= 0.06) { return "var(--hop-dataviz-diverging-sequence-1-negative5)"; } + if (ratio <= 0.12) { return "var(--hop-dataviz-diverging-sequence-1-negative4)"; } + if (ratio <= 0.3) { return "var(--hop-dataviz-diverging-sequence-1-negative3)"; } + if (ratio <= 0.43) { return "var(--hop-dataviz-diverging-sequence-1-negative2)"; } + if (ratio <= 0.56) { return "var(--hop-dataviz-diverging-sequence-1-neutral)"; } + if (ratio <= 0.68) { return "var(--hop-dataviz-diverging-sequence-1-positive2)"; } + if (ratio <= 0.81) { return "var(--hop-dataviz-diverging-sequence-1-positive3)"; } + if (ratio <= 0.94) { return "var(--hop-dataviz-diverging-sequence-1-positive4)"; } + + return "var(--hop-dataviz-diverging-sequence-1-positive5)"; + }; + const buildAreaDefs = (): JSX.Element => { return ( @@ -395,15 +433,15 @@ const AreaChart: React.FunctionComponent = ({ y2="96%" gradientUnits="userSpaceOnUse" > - - - - - - - - - + + + + + + + + + ); @@ -527,6 +565,7 @@ const AreaChart: React.FunctionComponent = ({ dateFormatter={tooltipDateFormatter} scoreFormatter={tooltipScoreFormatter} unavailableDataMessage={unavailableDataMessage} + getDataScoreHue={score => getDataHue(score, range)} /> } /> diff --git a/packages/AreaChart/src/ChartTooltip.tsx b/packages/AreaChart/src/ChartTooltip.tsx index 948cb334..2c39c0c8 100644 --- a/packages/AreaChart/src/ChartTooltip.tsx +++ b/packages/AreaChart/src/ChartTooltip.tsx @@ -1,99 +1,107 @@ -import * as React from 'react'; -import cx from 'classnames'; -import { TooltipProps } from 'recharts'; +import * as React from "react"; +import cx from "classnames"; +import type { TooltipProps } from "recharts"; -import { - NameType, - ValueType, -} from 'recharts/types/component/DefaultTooltipContent'; +import type { + NameType, + ValueType +} from "recharts/types/component/DefaultTooltipContent"; -import './chart-tooltip.scss'; +import "./chart-tooltip.scss"; export interface ChartTooltipProps extends TooltipProps { - dateFormatter: (date: number) => string; - scoreFormatter?: (score: number) => string; - unavailableDataMessage?: string; + dateFormatter: (date: number) => string; + scoreFormatter?: (score: number) => string; + getDataScoreHue?: (score: number) => string; + unavailableDataMessage?: string; } export interface TooltipScoreProps { - score: number; - text: string; - isSecondary?: boolean; - scoreFormatter?: (score: number) => string; + score: number; + text: string; + isSecondary?: boolean; + formatter?: (score: number) => string; + dotColor?: string; } const ChartTooltip: React.FunctionComponent = ( - props: ChartTooltipProps, + props: ChartTooltipProps ) => { - const { - active, - payload, - label, - dateFormatter, - scoreFormatter, - unavailableDataMessage, - } = props; + const { + active, + payload, + label, + dateFormatter, + scoreFormatter, + getDataScoreHue, + unavailableDataMessage + } = props; - const isValidScore = (score: number): boolean => { - return score !== undefined && score !== null; - }; + const isValidScore = (score: number): boolean => { + return score !== undefined && score !== null; + }; - const TooltipScore = (scoreProps: TooltipScoreProps): JSX.Element => { - const { score, text, isSecondary, scoreFormatter } = scoreProps; - const formattedScore = scoreFormatter ? scoreFormatter(score) : score; + const TooltipScore = (scoreProps: TooltipScoreProps): JSX.Element => { + const { score, text, isSecondary, formatter, dotColor } = scoreProps; + const formattedScore = formatter ? formatter(score) : score; - return ( -
-
-
{formattedScore}
-
{text}
-
- ); - }; + return ( +
+
+
{formattedScore}
+
{text}
+
+ ); + }; - if (active && payload && payload[0]) { - const isPrimaryScoreValid = isValidScore(payload[0].payload.score); - const isSecondaryScoreValid = isValidScore( - payload[0].payload.secondaryScore, - ); + if (active && payload && payload[0]) { + const isPrimaryScoreValid = isValidScore(payload[0].payload.score); + const isSecondaryScoreValid = isValidScore( + payload[0].payload.secondaryScore + ); - return ( -
- {isPrimaryScoreValid || isSecondaryScoreValid ? ( -
-
- {dateFormatter(label)} + return ( +
+ {isPrimaryScoreValid || isSecondaryScoreValid ? ( +
+
+ {dateFormatter(label)} +
+ {isPrimaryScoreValid && ( + + )} + {isSecondaryScoreValid && ( + + )} +
+ ) : ( +
+ {unavailableDataMessage} +
+ )}
- {isPrimaryScoreValid && ( - - )} - {isSecondaryScoreValid && ( - - )} -
- ) : ( -
- {unavailableDataMessage} -
- )} -
- ); - } + ); + } - return null; + return null; }; export default ChartTooltip; diff --git a/packages/AreaChart/src/area-chart.scss b/packages/AreaChart/src/area-chart.scss index 5d352d34..c62f454d 100644 --- a/packages/AreaChart/src/area-chart.scss +++ b/packages/AreaChart/src/area-chart.scss @@ -14,7 +14,6 @@ --ids-area-chart-color: #{tokens.$grey-600}; --ids-area-chart-grid-color: #{tokens.$grey-200}; --ids-area-chart-axis-color: #{tokens.$grey-400}; - --ids-area-chart-dot-color: #{tokens.$electric-blue-500}; --ids-area-chart-dot-stroke-color: #{tokens.$samoyed}; --ids-area-chart-line-color: #{tokens.$electric-blue-500}; --ids-area-chart-null-line-color: #{tokens.$grey-400}; @@ -38,8 +37,7 @@ --ids-area-chart-color: var(--hop-neutral-text-weak); --ids-area-chart-grid-color: var(--hop-neutral-border-weakest); --ids-area-chart-axis-color: var(--hop-neutral-border-weak); - --ids-area-chart-dot-color: #BFDCA9; /* Missing semantic token */ - --ids-area-chart-dot-stroke-color: #ffffff; /* Missing semantic token */ + --ids-area-chart-dot-stroke-color: var(--hop-neutral-icon-strong); --ids-area-chart-line-color: url("#areaChartLineGradient"); --ids-area-chart-null-line-color: var(--hop-neutral-border-weak); diff --git a/packages/AreaChart/src/chart-tooltip.scss b/packages/AreaChart/src/chart-tooltip.scss index 1d44af60..9cf3ed74 100644 --- a/packages/AreaChart/src/chart-tooltip.scss +++ b/packages/AreaChart/src/chart-tooltip.scss @@ -59,10 +59,13 @@ --ids-chart-tooltip-score-text-font-weight: var(--hop-body-sm-font-weight); /* Circle */ - --ids-chart-tooltip-circle-background: #BFDCA9; /* Missing semantic token */ - --ids-chart-tooltip-circle-secondary-background: #6C8FFD; /* Missing semantic token */ + --ids-chart-tooltip-circle-secondary-background: var(--hop-dataviz-categorical-2colorgroup-option4-category2); --ids-chart-tooltip-circle-margin-right: var(--hop-space-inline-sm); --ids-chart-tooltip-circle-size: 0.75rem; + + .ids-tooltip-score__circle { + --ids-chart-tooltip-circle-background: var(--ids-tooltip-score-dot-color, var(--hop-dataviz-categorical-2colorgroup-option4-category1)); + } } @keyframes fade-in {