Skip to content

Commit

Permalink
Fix: [IGL-109] AreaChart dot colors (#622)
Browse files Browse the repository at this point in the history
  • Loading branch information
vicky-comeau authored Jan 3, 2024
1 parent dd2579d commit fece90c
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 97 deletions.
5 changes: 5 additions & 0 deletions .changeset/rotten-olives-kiss.md
Original file line number Diff line number Diff line change
@@ -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.
67 changes: 53 additions & 14 deletions packages/AreaChart/src/AreaChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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<AreaChartProps> = ({
className,
dataSet,
Expand All @@ -126,7 +144,7 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
const SkeletonAxisTick = ({
x,
y,
className,
className: skeletonClassName,
payload,
skeletonWidth = DEFAULT_SKELETON_WIDTH,
skeletonHeight = DEFAULT_SKELETON_HEIGHT,
Expand Down Expand Up @@ -157,7 +175,7 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
<rect
x={positionX}
y={positionY}
className={cx("ids-area-chart-skeleton-animation", className)}
className={cx("ids-area-chart-skeleton-animation", skeletonClassName)}
width={skeletonWidth}
height={skeletonHeight}
rx="4"
Expand Down Expand Up @@ -347,7 +365,7 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
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 = {
Expand Down Expand Up @@ -384,6 +402,26 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
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 (
<defs>
Expand All @@ -395,15 +433,15 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
y2="96%"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#188A71" />
<stop offset="0.119792" stopColor="#47A584" />
<stop offset="0.239583" stopColor="#7DC291" />
<stop offset="0.369792" stopColor="#AAD89D" />
<stop offset="0.498366" stopColor="#F7E694" />
<stop offset="0.625" stopColor="#FFBCB7" />
<stop offset="0.75" stopColor="#FF8E8E" />
<stop offset="0.875" stopColor="#F56263" />
<stop offset="1" stopColor="#DF3236" />
<stop stopColor="var(--hop-dataviz-diverging-sequence-1-positive5)" />
<stop offset="0.119792" stopColor="var(--hop-dataviz-diverging-sequence-1-positive4)" />
<stop offset="0.239583" stopColor="var(--hop-dataviz-diverging-sequence-1-positive3)" />
<stop offset="0.369792" stopColor="var(--hop-dataviz-diverging-sequence-1-positive2)" />
<stop offset="0.498366" stopColor="var(--hop-dataviz-diverging-sequence-1-neutral)" />
<stop offset="0.625" stopColor="var(--hop-dataviz-diverging-sequence-1-negative2)" />
<stop offset="0.75" stopColor="var(--hop-dataviz-diverging-sequence-1-negative3)" />
<stop offset="0.875" stopColor="var(--hop-dataviz-diverging-sequence-1-negative4)" />
<stop offset="1" stopColor="var(--hop-dataviz-diverging-sequence-1-negative5)" />
</linearGradient>
</defs>
);
Expand Down Expand Up @@ -527,6 +565,7 @@ const AreaChart: React.FunctionComponent<AreaChartProps> = ({
dateFormatter={tooltipDateFormatter}
scoreFormatter={tooltipScoreFormatter}
unavailableDataMessage={unavailableDataMessage}
getDataScoreHue={score => getDataHue(score, range)}
/>
}
/>
Expand Down
164 changes: 86 additions & 78 deletions packages/AreaChart/src/ChartTooltip.tsx
Original file line number Diff line number Diff line change
@@ -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<ValueType, NameType> {
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<ChartTooltipProps> = (
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 (
<div className="ids-tooltip-score">
<div
className={cx('ids-tooltip-score__circle', {
'ids-tooltip-score__circle--secondary': isSecondary,
})}
/>
<div className="ids-tooltip-score__value">{formattedScore}</div>
<div className="ids-tooltip-score__text">{text}</div>
</div>
);
};
return (
<div className="ids-tooltip-score"
style={{
"--ids-tooltip-score-dot-color": dotColor ? dotColor : undefined
} as React.CSSProperties}
>
<div
className={cx("ids-tooltip-score__circle", {
"ids-tooltip-score__circle--secondary": isSecondary
})}
/>
<div className="ids-tooltip-score__value">{formattedScore}</div>
<div className="ids-tooltip-score__text">{text}</div>
</div>
);
};

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 (
<div className="ids-chart-tooltip">
{isPrimaryScoreValid || isSecondaryScoreValid ? (
<div>
<div className="ids-chart-tooltip__date">
{dateFormatter(label)}
return (
<div className="ids-chart-tooltip">
{isPrimaryScoreValid || isSecondaryScoreValid ? (
<div>
<div className="ids-chart-tooltip__date">
{dateFormatter(label)}
</div>
{isPrimaryScoreValid && (
<TooltipScore
score={Number(payload[0].payload.score)}
text={payload[0].payload.name || ""}
formatter={scoreFormatter}
dotColor={getDataScoreHue?.(payload[0].payload.score)}
/>
)}
{isSecondaryScoreValid && (
<TooltipScore
score={Number(payload[0].payload.secondaryScore)}
text={payload[0].payload.secondaryName || ""}
formatter={scoreFormatter}
isSecondary
/>
)}
</div>
) : (
<div className="ids-chart-tooltip__unavailable-score">
{unavailableDataMessage}
</div>
)}
</div>
{isPrimaryScoreValid && (
<TooltipScore
score={Number(payload[0].payload.score)}
text={payload[0].payload.name || ''}
scoreFormatter={scoreFormatter}
/>
)}
{isSecondaryScoreValid && (
<TooltipScore
score={Number(payload[0].payload.secondaryScore)}
text={payload[0].payload.secondaryName || ''}
scoreFormatter={scoreFormatter}
isSecondary
/>
)}
</div>
) : (
<div className="ids-chart-tooltip__unavailable-score">
{unavailableDataMessage}
</div>
)}
</div>
);
}
);
}

return null;
return null;
};

export default ChartTooltip;
4 changes: 1 addition & 3 deletions packages/AreaChart/src/area-chart.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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);

Expand Down
7 changes: 5 additions & 2 deletions packages/AreaChart/src/chart-tooltip.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit fece90c

Please sign in to comment.