Skip to content

Commit a3c3f0a

Browse files
authored
fix(react-charting): Colorscale support for bar charts (#34578)
1 parent 26b88c5 commit a3c3f0a

File tree

2 files changed

+71
-18
lines changed

2 files changed

+71
-18
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "patch",
3+
"comment": "fix(react-charting): Colorscale support for bar charts",
4+
"packageName": "@fluentui/react-charting",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,16 @@ export const transformPlotlyJsonToVSBCProps = (
340340
let yMinValue = 0;
341341
const secondaryYAxisValues = getSecondaryYAxisValues(input.data, input.layout);
342342
const { legends, hideLegend } = getLegendProps(input.data, input.layout);
343+
let colorScale: ((value: number) => string) | undefined = undefined;
343344
input.data.forEach((series: Partial<PlotData>, index1: number) => {
345+
if (
346+
input.layout?.coloraxis?.colorscale?.length &&
347+
isArrayOrTypedArray(series.marker?.color) &&
348+
(series.marker?.color as Color[]).length > 0 &&
349+
typeof (series.marker?.color as Color[])?.[0] === 'number'
350+
) {
351+
colorScale = createColorScale(input.layout, series);
352+
}
344353
const isXYearCategory = isYearArray(series.x); // Consider year as categorical not numeric continuous axis
345354
// extract bar colors for each series only once
346355
const extractedBarColors = extractColor(
@@ -377,8 +386,14 @@ export const transformPlotlyJsonToVSBCProps = (
377386
};
378387
}
379388
const legend: string = legends[index1];
380-
// resolve color for each legend's bars from the extracted colors
381-
const color = resolveColor(extractedBarColors, index1, legend, colorMap, isDarkTheme);
389+
// resolve color for each legend's bars from the colorscale or extracted colors
390+
const color = colorScale
391+
? colorScale(
392+
isArrayOrTypedArray(series.marker?.color)
393+
? ((series.marker?.color as Color[])?.[index2 % (series.marker?.color as Color[]).length] as number)
394+
: 0,
395+
)
396+
: resolveColor(extractedBarColors, index2, legend, colorMap, isDarkTheme);
382397
const yVal: number = rangeYValues[index2] as number;
383398
if (series.type === 'bar') {
384399
mapXToDataPoints[x].chartData.push({
@@ -444,12 +459,17 @@ export const transformPlotlyJsonToVSBCProps = (
444459
};
445460
};
446461

447-
const createColorScale = (colorscale: Array<[number, string]>, domain: [number, number]) => {
448-
const [dMin, dMax] = domain;
462+
const createColorScale = (layout: Partial<Layout>, series: Partial<PlotData>) => {
463+
const scale = layout?.coloraxis?.colorscale as Array<[number, string]>;
464+
const colorValues = series.marker?.color as number[];
465+
const [dMin, dMax] = [
466+
layout?.coloraxis?.cmin ?? Math.min(...colorValues),
467+
layout?.coloraxis?.cmax ?? Math.max(...colorValues),
468+
];
449469

450470
// Normalize colorscale domain to actual data domain
451-
const scaleDomain = colorscale.map(([pos]) => dMin + pos * (dMax - dMin));
452-
const scaleColors = colorscale.map(item => item[1]);
471+
const scaleDomain = scale.map(([pos]) => dMin + pos * (dMax - dMin));
472+
const scaleColors = scale.map(item => item[1]);
453473

454474
return d3ScaleLinear<string>().domain(scaleDomain).range(scaleColors);
455475
};
@@ -471,13 +491,7 @@ export const transformPlotlyJsonToGVBCProps = (
471491
(series.marker?.color as Color[]).length > 0 &&
472492
typeof (series.marker?.color as Color[])?.[0] === 'number'
473493
) {
474-
const scale = input.layout.coloraxis.colorscale as Array<[number, string]>;
475-
const colorValues = series.marker?.color as number[];
476-
const [dMin, dMax] = [
477-
input.layout.coloraxis?.cmin ?? Math.min(...colorValues),
478-
input.layout.coloraxis?.cmax ?? Math.max(...colorValues),
479-
];
480-
colorScale = createColorScale(scale, [dMin, dMax]);
494+
colorScale = createColorScale(input.layout, series);
481495
}
482496
// extract colors for each series only once
483497
const extractedColors = extractColor(
@@ -501,7 +515,9 @@ export const transformPlotlyJsonToGVBCProps = (
501515
// resolve color for each legend's bars from the colorscale or extracted colors
502516
const color = colorScale
503517
? colorScale(
504-
isArrayOrTypedArray(series.marker?.color) ? ((series.marker?.color as Color[])?.[xIndex] as number) : 0,
518+
isArrayOrTypedArray(series.marker?.color)
519+
? ((series.marker?.color as Color[])?.[xIndex % (series.marker?.color as Color[]).length] as number)
520+
: 0,
505521
)
506522
: resolveColor(extractedColors, index1, legend, colorMap, isDarkTheme);
507523

@@ -543,12 +559,21 @@ export const transformPlotlyJsonToVBCProps = (
543559
): IVerticalBarChartProps => {
544560
const vbcData: IVerticalBarChartDataPoint[] = [];
545561
const { legends, hideLegend } = getLegendProps(input.data, input.layout);
562+
let colorScale: ((value: number) => string) | undefined = undefined;
546563

547564
input.data.forEach((series: Partial<PlotData>, seriesIdx: number) => {
548565
if (!series.x) {
549566
return;
550567
}
551568

569+
if (
570+
input.layout?.coloraxis?.colorscale?.length &&
571+
isArrayOrTypedArray(series.marker?.color) &&
572+
(series.marker?.color as Color[]).length > 0 &&
573+
typeof (series.marker?.color as Color[])?.[0] === 'number'
574+
) {
575+
colorScale = createColorScale(input.layout, series);
576+
}
552577
// extract colors for each series only once
553578
const extractedColors = extractColor(
554579
input.layout?.template?.layout?.colorway,
@@ -592,8 +617,14 @@ export const transformPlotlyJsonToVBCProps = (
592617

593618
xBins.forEach((bin, index) => {
594619
const legend: string = legends[seriesIdx];
595-
// resolve color for each legend's bars from the extracted colors
596-
const color = resolveColor(extractedColors, seriesIdx, legend, colorMap, isDarkTheme);
620+
// resolve color for each legend's bars from the colorscale or extracted colors
621+
const color = colorScale
622+
? colorScale(
623+
isArrayOrTypedArray(series.marker?.color)
624+
? ((series.marker?.color as Color[])?.[index % (series.marker?.color as Color[]).length] as number)
625+
: 0,
626+
)
627+
: resolveColor(extractedColors, index, legend, colorMap, isDarkTheme);
597628
const yVal = calculateHistNorm(
598629
series.histnorm,
599630
y[index],
@@ -766,8 +797,17 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = (
766797
isDarkTheme?: boolean,
767798
): IHorizontalBarChartWithAxisProps => {
768799
const { legends, hideLegend } = getLegendProps(input.data, input.layout);
800+
let colorScale: ((value: number) => string) | undefined = undefined;
769801
const chartData: IHorizontalBarChartWithAxisDataPoint[] = input.data
770802
.map((series: Partial<PlotData>, index: number) => {
803+
if (
804+
input.layout?.coloraxis?.colorscale?.length &&
805+
isArrayOrTypedArray(series.marker?.color) &&
806+
(series.marker?.color as Color[]).length > 0 &&
807+
typeof (series.marker?.color as Color[])?.[0] === 'number'
808+
) {
809+
colorScale = createColorScale(input.layout, series);
810+
}
771811
// extract colors for each series only once
772812
const extractedColors = extractColor(
773813
input.layout?.template?.layout?.colorway,
@@ -777,13 +817,19 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = (
777817
isDarkTheme,
778818
) as string[] | string | undefined;
779819
const legend = legends[index];
780-
// resolve color for each legend's bars from the extracted colors
781-
const color = resolveColor(extractedColors, index, legend, colorMap, isDarkTheme);
782820
return (series.y as Datum[])
783821
.map((yValue, i: number) => {
784822
if (isInvalidValue(series.x?.[i]) || isInvalidValue(yValue)) {
785823
return null;
786824
}
825+
// resolve color for each legend's bars from the colorscale or extracted colors
826+
const color = colorScale
827+
? colorScale(
828+
isArrayOrTypedArray(series.marker?.color)
829+
? ((series.marker?.color as Color[])?.[i % (series.marker?.color as Color[]).length] as number)
830+
: 0,
831+
)
832+
: resolveColor(extractedColors, i, legend, colorMap, isDarkTheme);
787833

788834
return {
789835
x: series.x![i],

0 commit comments

Comments
 (0)