diff --git a/change/@fluentui-react-charting-5666e4e9-e586-46f9-b033-5f121bcecf5f.json b/change/@fluentui-react-charting-5666e4e9-e586-46f9-b033-5f121bcecf5f.json new file mode 100644 index 0000000000000..d298ee70c5d83 --- /dev/null +++ b/change/@fluentui-react-charting-5666e4e9-e586-46f9-b033-5f121bcecf5f.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Dark mode theme colors for declarative charts", + "packageName": "@fluentui/react-charting", + "email": "120183316+srmukher@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx index 1716212e66fb8..7c27bfc94b911 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx +++ b/packages/charts/react-charting/src/components/DeclarativeChart/DeclarativeChart.tsx @@ -25,6 +25,13 @@ import { GaugeChart } from '../GaugeChart/index'; import { GroupedVerticalBarChart } from '../GroupedVerticalBarChart/index'; import { VerticalBarChart } from '../VerticalBarChart/index'; +import { useTheme } from '@fluentui/react'; + +export const UseIsDarkTheme = (): boolean => { + const theme = useTheme(); + return theme.isInverted; +}; + export interface Schema { /** * Plotly schema represented as JSON object @@ -76,42 +83,45 @@ export const DeclarativeChart: React.FunctionComponent = const isXDate = isDateArray(xValues); const isXNumber = isNumberArray(xValues); const colorMap = useColorMapping(); + const isDarkTheme = UseIsDarkTheme(); switch (plotlySchema.data[0].type) { case 'pie': - return ; + return ; case 'bar': const orientation = plotlySchema.data[0].orientation; if (orientation === 'h') { return ( - + ); } else { if (['group', 'overlay'].includes(plotlySchema?.layout?.barmode)) { - return ; + return ; } - return ; + return ; } case 'scatter': const isAreaChart = plotlySchema.data.some((series: any) => series.fill === 'tonexty'); if (isXDate || isXNumber) { if (isAreaChart) { - return ; + return ; } - return ; + return ; } - return ; + return ; case 'heatmap': return ; case 'sankey': - return ; + return ; case 'indicator': if (plotlySchema?.data?.[0]?.mode?.includes('gauge')) { - return ; + return ; } return
Unsupported Schema
; case 'histogram': - return ; + return ; default: return
Unsupported Schema
; } diff --git a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts index cdda8e02c9cd5..5fd4e09b3330d 100644 --- a/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts +++ b/packages/charts/react-charting/src/components/DeclarativeChart/PlotlySchemaAdapter.ts @@ -35,9 +35,13 @@ const isNumber = (value: any): boolean => !isNaN(parseFloat(value)) && isFinite( export const isDateArray = (array: any[]): boolean => isArrayOrTypedArray(array) && array.every(isDate); export const isNumberArray = (array: any[]): boolean => isArrayOrTypedArray(array) && array.every(isNumber); -export const getColor = (legendLabel: string, colorMap: React.MutableRefObject>): string => { +export const getColor = ( + legendLabel: string, + colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, +): string => { if (!colorMap.current.has(legendLabel)) { - const nextColor = getNextColor(colorMap.current.size + 1); + const nextColor = getNextColor(colorMap.current.size + 1, 0, isDarkTheme); colorMap.current.set(legendLabel, nextColor); return nextColor; } @@ -48,12 +52,13 @@ export const getColor = (legendLabel: string, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IDonutChartProps => { const { data, layout } = jsonObj; const firstData = data[0]; const donutData = firstData.labels?.map((label: string, index: number): IChartDataPoint => { - const color = getColor(label, colorMap); + const color = getColor(label, colorMap, isDarkTheme); return { legend: label, data: firstData.values?.[index], @@ -96,6 +101,7 @@ export const transformPlotlyJsonToDonutProps = ( export const transformPlotlyJsonToVSBCProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IVerticalStackedBarChartProps => { const { data, layout } = jsonObj; const mapXToDataPoints: { [key: string]: IVerticalStackedChartProps } = {}; @@ -108,14 +114,14 @@ export const transformPlotlyJsonToVSBCProps = ( } const legend: string = series.name || `Series ${index1 + 1}`; if (series.type === 'bar') { - const color = getColor(legend, colorMap); + const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].chartData.push({ legend, data: series.y?.[index2], color, }); } else if (series.type === 'line') { - const color = getColor(legend, colorMap); + const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].lineData!.push({ legend, y: series.y?.[index2], @@ -140,6 +146,7 @@ export const transformPlotlyJsonToVSBCProps = ( export const transformPlotlyJsonToGVBCProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IGroupedVerticalBarChartProps => { const { data, layout } = jsonObj; const mapXToDataPoints: Record = {}; @@ -151,7 +158,7 @@ export const transformPlotlyJsonToGVBCProps = ( } if (series.type === 'bar') { const legend: string = series.name || `Series ${index1 + 1}`; - const color = getColor(legend, colorMap); + const color = getColor(legend, colorMap, isDarkTheme); mapXToDataPoints[x].series.push({ key: legend, @@ -175,6 +182,7 @@ export const transformPlotlyJsonToGVBCProps = ( export const transformPlotlyJsonToVBCProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IVerticalBarChartProps => { const { data, layout } = jsonObj; const vbcData: IVerticalBarChartDataPoint[] = []; @@ -216,7 +224,7 @@ export const transformPlotlyJsonToVBCProps = ( buckets.forEach(bucket => { const legend = series.name || `Series ${index + 1}`; - const color = getColor(legend, colorMap); + const color = getColor(legend, colorMap, isDarkTheme); let y = bucket.length; if (series.histnorm === 'percent') { @@ -262,6 +270,7 @@ export const transformPlotlyJsonToScatterChartProps = ( jsonObj: any, isAreaChart: boolean, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): ILineChartProps | IAreaChartProps => { const { data, layout } = jsonObj; @@ -271,7 +280,7 @@ export const transformPlotlyJsonToScatterChartProps = ( const isXDate = isDateArray(xValues); const isXNumber = isNumberArray(xValues); const legend = series.name || `Series ${index + 1}`; - const lineColor = getColor(legend, colorMap); + const lineColor = getColor(legend, colorMap, isDarkTheme); return { legend, @@ -304,13 +313,14 @@ export const transformPlotlyJsonToScatterChartProps = ( export const transformPlotlyJsonToHorizontalBarWithAxisProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IHorizontalBarChartWithAxisProps => { const { data, layout } = jsonObj; const chartData: IHorizontalBarChartWithAxisDataPoint[] = data .map((series: any, index: number) => { return series.y.map((yValue: string, i: number) => { - const color = getColor(yValue, colorMap); + const color = getColor(yValue, colorMap, isDarkTheme); return { x: series.x[i], y: yValue, @@ -385,6 +395,7 @@ export const transformPlotlyJsonToHeatmapProps = (jsonObj: any): IHeatMapChartPr export const transformPlotlyJsonToSankeyProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): ISankeyChartProps => { const { data, layout } = jsonObj; const { link, node } = data[0]; @@ -400,7 +411,7 @@ export const transformPlotlyJsonToSankeyProps = ( const sankeyChartData = { nodes: node.label.map((label: string, index: number) => { - const color = getColor(label, colorMap); + const color = getColor(label, colorMap, isDarkTheme); return { nodeId: index, @@ -439,13 +450,14 @@ export const transformPlotlyJsonToSankeyProps = ( export const transformPlotlyJsonToGaugeProps = ( jsonObj: any, colorMap: React.MutableRefObject>, + isDarkTheme?: boolean, ): IGaugeChartProps => { const { data, layout } = jsonObj; const firstData = data[0]; const segments = firstData.gauge?.steps?.map((step: any, index: number): IGaugeChartSegment => { const legend = step.name || `Segment ${index + 1}`; - const color = getColor(legend, colorMap); + const color = getColor(legend, colorMap, isDarkTheme); return { legend, size: step.range?.[1] - step.range?.[0], @@ -459,11 +471,11 @@ export const transformPlotlyJsonToGaugeProps = ( const diff = firstData.value - firstData.delta.reference; if (diff >= 0) { sublabel = `\u25B2 ${diff}`; - const color = getColor(firstData.delta.increasing?.color || '', colorMap); + const color = getColor(firstData.delta.increasing?.color || '', colorMap, isDarkTheme); sublabelColor = color; } else { sublabel = `\u25BC ${Math.abs(diff)}`; - const color = getColor(firstData.delta.decreasing?.color || '', colorMap); + const color = getColor(firstData.delta.decreasing?.color || '', colorMap, isDarkTheme); sublabelColor = color; } }