Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(react-charting): Heatmap text color based on Contrast Ratio #33659

Merged
merged 12 commits into from
Jan 22, 2025
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why did so many screenshots change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are using different version of d3-color now (as per yarn.lock file). I can see that reason only.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no we are not. it is mapping to same 3.1.0.

Looks like something else has broken.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems unrelated to my changes. let me take a master pull.

Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
"type": "patch",
"comment": "Heatmap text color based on contrast ratio",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 2 additions & 0 deletions packages/charts/react-charting/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@types/d3-shape": "^3.0.0",
"@types/d3-time-format": "^3.0.0",
"@types/d3-time": "^3.0.0",
"@types/d3-color": "^3.0.0",
"@fluentui/set-version": "^8.2.23",
"d3-array": "^3.0.0",
"d3-axis": "^3.0.0",
Expand All @@ -63,6 +64,7 @@
"d3-shape": "^3.0.0",
"d3-time-format": "^3.0.0",
"d3-time": "^3.0.0",
"d3-color": "^3.0.0",
"tslib": "^2.1.0"
},
"peerDependencies": {
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { CartesianChart, IChildProps, IModifiedCartesianChartProps } from '../../components/CommonComponents/index';
import { IAccessibilityProps, IChart, IHeatMapChartData, IHeatMapChartDataPoint } from '../../types/IDataPoint';
import { scaleLinear as d3ScaleLinear } from 'd3-scale';
import { rgb as d3Rgb } from 'd3-color';
import { classNamesFunction, getId, initializeComponentRef, memoizeFunction } from '@fluentui/react/lib/Utilities';
import { FocusZoneDirection } from '@fluentui/react-focus';
import { DirectionalHint } from '@fluentui/react/lib/Callout';
Expand All @@ -20,10 +21,12 @@ import {
IDomainNRange,
domainRangeOfXStringAxis,
createStringYAxis,
resolveCSSVariables,
} from '../../utilities/utilities';
import { Target } from '@fluentui/react';
import { format as d3Format } from 'd3-format';
import { timeFormat as d3TimeFormat } from 'd3-time-format';
import { getColorContrast } from '../../utilities/colors';

type DataSet = {
dataSet: RectanglesGraphData;
Expand Down Expand Up @@ -351,6 +354,12 @@ export class HeatMapChartBase extends React.Component<IHeatMapChartProps, IHeatM
});
};

private _getInvertedTextColor = (color: string): string => {
return color === this.props.theme!.palette.white
? this.props.theme!.palette.black
: this.props.theme!.palette.white;
};

/**
* This is the function which is responsible for
* drawing the rectangle in the graph and also
Expand All @@ -374,6 +383,15 @@ export class HeatMapChartBase extends React.Component<IHeatMapChartProps, IHeatM
* data point such as x, y , value, rectText property of the rectangle
*/
const dataPointObject = this._dataSet[yAxisDataPoint][index];
let styleRules = '#ffffff';
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
let foregroundColor = this.props.theme!.palette.white;
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
if (this.chartContainer) {
styleRules = resolveCSSVariables(this.chartContainer!, foregroundColor);
}
const contrastRatio = getColorContrast(d3Rgb(styleRules), d3Rgb(this._colorScale(dataPointObject.value)));
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
if (contrastRatio < 3) {
foregroundColor = this._getInvertedTextColor(foregroundColor);
}
rectElement = (
<g
key={id}
Expand Down Expand Up @@ -401,6 +419,7 @@ export class HeatMapChartBase extends React.Component<IHeatMapChartProps, IHeatM
textAnchor={'middle'}
className={this._classNames.text}
transform={`translate(${this._xAxisScale.bandwidth() / 2}, ${this._yAxisScale.bandwidth() / 2})`}
fill={foregroundColor}
>
{convertToLocaleString(dataPointObject.rectText, this.props.culture)}
</text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const getHeatMapChartStyles = (props: IHeatMapChartStyleProps): IHeatMapC
theme.fonts.medium,
{
pointerEvents: 'none',
fill: theme.palette.white,
fontWeight: FontWeights.semibold,
},
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -828,13 +828,13 @@ exports[`HeatMapChart snapshot tests should render HeatMapChart correctly with n
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
/>
Expand All @@ -858,13 +858,13 @@ exports[`HeatMapChart snapshot tests should render HeatMapChart correctly with n
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
/>
Expand All @@ -888,13 +888,13 @@ exports[`HeatMapChart snapshot tests should render HeatMapChart correctly with n
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
/>
Expand All @@ -918,13 +918,13 @@ exports[`HeatMapChart snapshot tests should render HeatMapChart correctly with n
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
/>
Expand Down Expand Up @@ -1428,13 +1428,13 @@ exports[`HeatMapChart snapshot tests should render axis labels correctly When cu
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
>
Expand All @@ -1460,13 +1460,13 @@ exports[`HeatMapChart snapshot tests should render axis labels correctly When cu
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
>
Expand All @@ -1492,13 +1492,13 @@ exports[`HeatMapChart snapshot tests should render axis labels correctly When cu
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
>
Expand All @@ -1524,13 +1524,13 @@ exports[`HeatMapChart snapshot tests should render axis labels correctly When cu
{
-moz-osx-font-smoothing: grayscale;
-webkit-font-smoothing: antialiased;
fill: #ffffff;
font-family: 'Segoe UI', 'Segoe UI Web (West European)', 'Segoe UI', -apple-system, BlinkMacSystemFont, 'Roboto', 'Helvetica Neue', sans-serif;
font-size: 14px;
font-weight: 600;
pointer-events: none;
}
dominant-baseline="middle"
fill="#000000"
text-anchor="middle"
transform="translate(143.11881188118812, 57.49009900990099)"
>
Expand Down
25 changes: 25 additions & 0 deletions packages/charts/react-charting/src/utilities/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,3 +141,28 @@ export const getColorFromToken = (token: string, isDarkTheme: boolean = false):
}
return token;
};

const rgbLrgb1 = (v: number): number => {
return v <= 0.04045 ? v / 12.92 : ((v + 0.055) / 1.055) ** 2.4;
};

const rgbLrgb = ({ r, g, b }: { r: number; g: number; b: number }): { r: number; g: number; b: number } => {
return {
r: rgbLrgb1(r / 255),
g: rgbLrgb1(g / 255),
b: rgbLrgb1(b / 255),
};
};
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved

const lrgbLuminance = ({ r, g, b }: { r: number; g: number; b: number }): number => {
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};

export const getColorContrast = (
c1: { r: number; g: number; b: number },
c2: { r: number; g: number; b: number },
): number => {
const l1 = lrgbLuminance(rgbLrgb(c1));
const l2 = lrgbLuminance(rgbLrgb(c2));
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
};
9 changes: 9 additions & 0 deletions packages/charts/react-charting/src/utilities/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1433,3 +1433,12 @@ export function areArraysEqual(arr1?: string[], arr2?: string[]): boolean {
}
return true;
}

const cssVarRegExp = /var\((--[a-zA-Z0-9\-]+)\)/g;

export function resolveCSSVariables(chartContainer: HTMLElement, styleRules: string) {
Anush2303 marked this conversation as resolved.
Show resolved Hide resolved
const containerStyles = getComputedStyle(chartContainer);
return styleRules.replace(cssVarRegExp, (match, group1) => {
return containerStyles.getPropertyValue(group1);
});
}
7 changes: 6 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5079,6 +5079,11 @@
dependencies:
"@types/d3-selection" "*"

"@types/d3-color@^3.0.0":
version "3.1.3"
resolved "https://registry.yarnpkg.com/@types/d3-color/-/d3-color-3.1.3.tgz#368c961a18de721da8200e80bf3943fb53136af2"
integrity sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==

"@types/d3-dsv@*":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@types/d3-dsv/-/d3-dsv-3.0.0.tgz#f3c61fb117bd493ec0e814856feb804a14cfc311"
Expand Down Expand Up @@ -9365,7 +9370,7 @@ d3-collection@^1.0.7:
resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.7.tgz#349bd2aa9977db071091c13144d5e4f16b5b310e"
integrity sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==

"d3-color@1 - 3", d3-color@^3.1.0:
"d3-color@1 - 3", d3-color@^3.0.0, d3-color@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-3.1.0.tgz#395b2833dfac71507f12ac2f7af23bf819de24e2"
integrity sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==
Expand Down
Loading