Skip to content

Commit d9446b6

Browse files
author
Anush
committed
support chart title from plotly schema
1 parent 29998d3 commit d9446b6

File tree

15 files changed

+477
-113
lines changed

15 files changed

+477
-113
lines changed

packages/charts/react-charts/library/etc/react-charts.api.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ export interface CartesianChartProps {
240240
tickLayout?: 'default' | 'auto';
241241
};
242242
xAxisAnnotation?: string;
243+
xAxisAnnotationStyles?: React_2.CSSProperties;
243244
xAxisCategoryOrder?: AxisCategoryOrder;
244245
xAxisTickCount?: number;
245246
xAxisTickPadding?: number;
@@ -711,6 +712,7 @@ export const DonutChart: React_2.FunctionComponent<DonutChartProps>;
711712
export interface DonutChartProps extends CartesianChartProps {
712713
calloutProps?: ChartPopoverProps;
713714
calloutPropsPerDataPoint?: (dataPointCalloutProps: ChartDataPoint) => ChartPopoverProps;
715+
chartTitleStyles?: React_2.CSSProperties;
714716
className?: string;
715717
componentRef?: React_2.Ref<Chart>;
716718
culture?: string;
@@ -803,6 +805,7 @@ export interface FunnelChartDataPoint {
803805
export interface FunnelChartProps {
804806
calloutProps?: ChartPopoverProps;
805807
chartTitle?: string;
808+
chartTitleStyles?: React_2.CSSProperties;
806809
className?: string;
807810
componentRef?: React_2.Ref<Chart>;
808811
culture?: string;
@@ -1590,6 +1593,7 @@ export interface SankeyChartProps {
15901593
accessibility?: SankeyChartAccessibilityProps;
15911594
borderColorsForNodes?: string[];
15921595
calloutProps?: ChartPopoverProps;
1596+
chartTitleStyles?: React.CSSProperties;
15931597
className?: string;
15941598
colorsForNodes?: string[];
15951599
componentRef?: Ref<Chart>;
@@ -1599,6 +1603,7 @@ export interface SankeyChartProps {
15991603
enableReflow?: boolean;
16001604
formatNumberOptions?: Intl.NumberFormatOptions;
16011605
height?: number;
1606+
hideLegend?: boolean;
16021607
parentRef?: HTMLElement | null;
16031608
pathColor?: string;
16041609
reflowProps?: {

packages/charts/react-charts/library/src/components/CommonComponents/CartesianChart.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,16 @@ export const CartesianChart: React.FunctionComponent<ModifiedCartesianChartProps
786786
content={props.xAxisAnnotation}
787787
textProps={{
788788
x: margins.left! + AXIS_TITLE_PADDING + xAxisTitleMaxWidth / 2,
789-
y: VERTICAL_MARGIN_FOR_XAXIS_TITLE - AXIS_TITLE_PADDING,
789+
y: Math.max(
790+
(typeof props.xAxisAnnotationStyles?.fontSize === 'number'
791+
? props.xAxisAnnotationStyles.fontSize
792+
: 13) + AXIS_TITLE_PADDING,
793+
VERTICAL_MARGIN_FOR_XAXIS_TITLE - AXIS_TITLE_PADDING,
794+
),
790795
className: classes.axisAnnotation!,
791796
textAnchor: 'middle',
792797
'aria-hidden': true,
798+
style: props.xAxisAnnotationStyles,
793799
}}
794800
maxWidth={xAxisTitleMaxWidth}
795801
/>

packages/charts/react-charts/library/src/components/CommonComponents/CartesianChart.types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,12 @@ export interface CartesianChartProps {
465465
*/
466466
xAxisAnnotation?: string;
467467

468+
/**
469+
* Optional inline styles to apply to the x axis annotation.
470+
* @default undefined
471+
*/
472+
xAxisAnnotationStyles?: React.CSSProperties;
473+
468474
/**
469475
* Prop to set the y axis annotation. Used to display additional information on the y-axis.
470476
* This is shown on the right side of the chart. Not shown if secondary y-axis is enabled.

packages/charts/react-charts/library/src/components/DeclarativeChart/DeclarativeChart.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
SINGLE_REPEAT,
3939
transformPlotlyJsonToFunnelChartProps,
4040
transformPlotlyJsonToGanttChartProps,
41+
getTitleFontStyles,
4142
transformPlotlyJsonToAnnotationChartProps,
4243
} from './PlotlySchemaAdapter';
4344
import type { ColorwayType } from './PlotlyColorAdapter';
@@ -537,9 +538,21 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
537538
);
538539

539540
type ChartType = keyof ChartTypeMap;
541+
542+
const titleObj = plotlyInputWithValidData.layout?.title;
543+
const chartTitle = typeof titleObj === 'string' ? titleObj : titleObj?.text ?? '';
544+
const titleFont = typeof titleObj === 'object' ? titleObj?.font : undefined;
545+
546+
const titleStyle: React.CSSProperties = {
547+
textAlign: 'center',
548+
marginBottom: '8px',
549+
...getTitleFontStyles(titleFont),
550+
};
551+
540552
// map through the grouped traces and render the appropriate chart
541553
return (
542554
<>
555+
{isMultiPlot.current && chartTitle && <div style={titleStyle}>{chartTitle}</div>}
543556
<div
544557
style={{
545558
display: 'grid',
@@ -581,8 +594,6 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
581594
? {}
582595
: {
583596
...interactiveCommonProps,
584-
xAxisAnnotation: cellProperties?.xAnnotation,
585-
yAxisAnnotation: cellProperties?.yAnnotation,
586597
}
587598
) as Partial<ReturnType<typeof transformer>>;
588599

@@ -592,8 +603,8 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
592603
[transformedInput, isMultiPlot.current, colorMap, colorwayType, isDarkTheme],
593604
{
594605
...resolvedCommonProps,
595-
xAxisAnnotation: cellProperties?.xAnnotation,
596-
yAxisAnnotation: cellProperties?.yAnnotation,
606+
...(cellProperties?.xAnnotation && { xAxisAnnotation: cellProperties.xAnnotation }),
607+
...(cellProperties?.yAnnotation && { yAxisAnnotation: cellProperties.yAnnotation }),
597608
componentRef: (ref: Chart | null) => {
598609
chartRefs.current[chartIdx] = {
599610
compRef: ref,

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import type {
7070
AxisType,
7171
Shape,
7272
Annotations,
73+
Font,
7374
} from '@fluentui/chart-utilities';
7475
import {
7576
isArrayOrTypedArray,
@@ -90,6 +91,7 @@ import { curveCardinal as d3CurveCardinal } from 'd3-shape';
9091
import type { ColorwayType } from './PlotlyColorAdapter';
9192
import { getOpacity, extractColor, resolveColor, createColorScale } from './PlotlyColorAdapter';
9293
import { rgb } from 'd3-color';
94+
import { tokens } from '@fluentui/react-theme';
9395
import { Legend, LegendsProps } from '../Legends/index';
9496
import { ScatterChartProps } from '../ScatterChart/ScatterChart.types';
9597
import { CartesianChartProps } from '../CommonComponents/index';
@@ -172,11 +174,28 @@ const dashOptions = {
172174
},
173175
} as const;
174176

177+
export function getTitleFontStyles(titleFont: Partial<Font> | undefined): React.CSSProperties {
178+
return {
179+
fontFamily: titleFont?.family ?? 'Arial, sans-serif',
180+
fontSize: titleFont?.size ? `${titleFont.size}px` : '13px',
181+
fontWeight: titleFont?.weight ?? 400,
182+
color: (titleFont?.color as string) ?? tokens.colorNeutralForeground1,
183+
...(titleFont?.shadow && titleFont.shadow !== 'none' && { textShadow: titleFont.shadow }),
184+
};
185+
}
186+
175187
function getTitles(layout: Partial<Layout> | undefined) {
188+
const titleObj = layout?.title;
189+
const chartTitle = typeof titleObj === 'string' ? titleObj : titleObj?.text ?? '';
190+
const titleFont = typeof titleObj === 'object' ? titleObj?.font : undefined;
191+
const titleStyles: React.CSSProperties = getTitleFontStyles(titleFont);
192+
176193
const titles = {
177-
chartTitle: typeof layout?.title === 'string' ? layout.title : layout?.title?.text ?? '',
194+
chartTitle,
178195
xAxisTitle: typeof layout?.xaxis?.title === 'string' ? layout?.xaxis?.title : layout?.xaxis?.title?.text ?? '',
179196
yAxisTitle: typeof layout?.yaxis?.title === 'string' ? layout?.yaxis?.title : layout?.yaxis?.title?.text ?? '',
197+
xAxisAnnotation: chartTitle,
198+
xAxisAnnotationStyles: titleStyles,
180199
};
181200
return titles;
182201
}
@@ -1357,6 +1376,9 @@ export const transformPlotlyJsonToDonutProps = (
13571376
chartTitle,
13581377
chartData: reorderedEntries.map(([, v]) => v as ChartDataPoint),
13591378
},
1379+
chartTitleStyles: getTitleFontStyles(
1380+
typeof input.layout?.title === 'object' ? input.layout.title?.font : undefined,
1381+
),
13601382
hideLegend: isMultiPlot || input.layout?.showlegend === false,
13611383
width: input.layout?.width,
13621384
height,
@@ -2688,8 +2710,12 @@ export const transformPlotlyJsonToSankeyProps = (
26882710
chartTitle,
26892711
SankeyChartData: sankeyChartData,
26902712
},
2713+
chartTitleStyles: getTitleFontStyles(
2714+
typeof input.layout?.title === 'object' ? input.layout.title?.font : undefined,
2715+
),
26912716
width: input.layout?.width,
26922717
height: input.layout?.height ?? 468,
2718+
hideLegend: isMultiPlot || input.layout?.showlegend === false,
26932719
// TODO
26942720
// styles,
26952721
};
@@ -3137,9 +3163,14 @@ export const transformPlotlyJsonToFunnelChartProps = (
31373163
});
31383164
});
31393165
}
3166+
const { chartTitle } = getTitles(input.layout);
31403167

31413168
return {
31423169
data: funnelData,
3170+
chartTitle,
3171+
chartTitleStyles: getTitleFontStyles(
3172+
typeof input.layout?.title === 'object' ? input.layout.title?.font : undefined,
3173+
),
31433174
width: input.layout?.width,
31443175
height: input.layout?.height,
31453176
orientation: (input.data[0] as Partial<PlotData>)?.orientation === 'v' ? 'horizontal' : 'vertical',

packages/charts/react-charts/library/src/components/DeclarativeChart/__snapshots__/PlotlySchemaAdapterUT.test.tsx.snap

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ Object {
8383

8484
exports[`transform Plotly Json To chart Props transformPlotlyJsonToDonutProps - Should return donut chart props 1`] = `
8585
Object {
86+
"chartTitleStyles": Object {
87+
"color": "var(--colorNeutralForeground1)",
88+
"fontFamily": "Arial, sans-serif",
89+
"fontSize": "13px",
90+
"fontWeight": 400,
91+
},
8692
"data": Object {
8793
"chartData": Array [
8894
Object {
@@ -211,6 +217,12 @@ Object {
211217

212218
exports[`transform Plotly Json To chart Props transformPlotlyJsonToDonutProps - Should return pie chart props 1`] = `
213219
Object {
220+
"chartTitleStyles": Object {
221+
"color": "var(--colorNeutralForeground1)",
222+
"fontFamily": "Arial, sans-serif",
223+
"fontSize": "13px",
224+
"fontWeight": 400,
225+
},
214226
"data": Object {
215227
"chartData": Array [
216228
Object {
@@ -338,6 +350,13 @@ Object {
338350
"showRoundOffXTickValues": false,
339351
"showYAxisLables": true,
340352
"width": 850,
353+
"xAxisAnnotation": "PHP Framework Popularity at Work - SitePoint, 2015",
354+
"xAxisAnnotationStyles": Object {
355+
"color": "var(--colorNeutralForeground1)",
356+
"fontFamily": "Arial, sans-serif",
357+
"fontSize": "13px",
358+
"fontWeight": 400,
359+
},
341360
"xAxisCategoryOrder": Array [
342361
"Jan",
343362
"Feb",
@@ -2844,6 +2863,13 @@ Object {
28442863
"xAxis": Object {
28452864
"tickLayout": "auto",
28462865
},
2866+
"xAxisAnnotation": "",
2867+
"xAxisAnnotationStyles": Object {
2868+
"color": "var(--colorNeutralForeground1)",
2869+
"fontFamily": "Arial, sans-serif",
2870+
"fontSize": "13px",
2871+
"fontWeight": 400,
2872+
},
28472873
"xAxisCategoryOrder": Array [
28482874
"x_0",
28492875
"x_1",
@@ -3048,6 +3074,13 @@ Object {
30483074
"showYAxisLables": true,
30493075
"showYAxisLablesTooltip": true,
30503076
"width": 850,
3077+
"xAxisAnnotation": "PHP Framework Popularity at Work - SitePoint, 2015",
3078+
"xAxisAnnotationStyles": Object {
3079+
"color": "var(--colorNeutralForeground1)",
3080+
"fontFamily": "Arial, sans-serif",
3081+
"fontSize": "13px",
3082+
"fontWeight": 400,
3083+
},
30513084
"xAxisCategoryOrder": "data",
30523085
"xAxisTitle": "Votes",
30533086
"xMaxValue": 1830.6731869091736,
@@ -4634,6 +4667,13 @@ Object {
46344667
"supportNegativeData": true,
46354668
"useUTC": false,
46364669
"width": undefined,
4670+
"xAxisAnnotation": "",
4671+
"xAxisAnnotationStyles": Object {
4672+
"color": "var(--colorNeutralForeground1)",
4673+
"fontFamily": "Arial, sans-serif",
4674+
"fontSize": "13px",
4675+
"fontWeight": 400,
4676+
},
46374677
"xAxisTitle": "",
46384678
"yAxisTitle": "",
46394679
"yMaxValue": 16.562957844203055,
@@ -4643,6 +4683,12 @@ Object {
46434683

46444684
exports[`transform Plotly Json To chart Props transformPlotlyJsonToSankeyProps - Should return sankey chart props 1`] = `
46454685
Object {
4686+
"chartTitleStyles": Object {
4687+
"color": "var(--colorNeutralForeground1)",
4688+
"fontFamily": "Arial, sans-serif",
4689+
"fontSize": "13px",
4690+
"fontWeight": 400,
4691+
},
46464692
"data": Object {
46474693
"SankeyChartData": Object {
46484694
"links": Array [
@@ -4783,6 +4829,7 @@ Object {
47834829
"chartTitle": "Scottish Referendum Voters who now want Independence",
47844830
},
47854831
"height": 772,
4832+
"hideLegend": false,
47864833
"width": undefined,
47874834
}
47884835
`;
@@ -5052,6 +5099,13 @@ Object {
50525099
"supportNegativeData": true,
50535100
"useUTC": false,
50545101
"width": undefined,
5102+
"xAxisAnnotation": "",
5103+
"xAxisAnnotationStyles": Object {
5104+
"color": "var(--colorNeutralForeground1)",
5105+
"fontFamily": "Arial, sans-serif",
5106+
"fontSize": "13px",
5107+
"fontWeight": 400,
5108+
},
50555109
"xAxisTitle": "",
50565110
"yAxisTitle": "",
50575111
}
@@ -5200,6 +5254,13 @@ Object {
52005254
"supportNegativeData": true,
52015255
"useUTC": false,
52025256
"width": undefined,
5257+
"xAxisAnnotation": "",
5258+
"xAxisAnnotationStyles": Object {
5259+
"color": "var(--colorNeutralForeground1)",
5260+
"fontFamily": "Arial, sans-serif",
5261+
"fontSize": "13px",
5262+
"fontWeight": 400,
5263+
},
52035264
"xAxisCategoryOrder": "data",
52045265
"xAxisTitle": "",
52055266
"yAxisCategoryOrder": "data",
@@ -5489,6 +5550,13 @@ Object {
54895550
"roundedTicks": true,
54905551
"showYAxisLables": true,
54915552
"width": undefined,
5553+
"xAxisAnnotation": "",
5554+
"xAxisAnnotationStyles": Object {
5555+
"color": "#4D5663",
5556+
"fontFamily": "Arial, sans-serif",
5557+
"fontSize": "13px",
5558+
"fontWeight": 400,
5559+
},
54925560
"xAxisCategoryOrder": "data",
54935561
"xAxisTitle": "",
54945562
"yAxisCategoryOrder": "data",
@@ -5607,6 +5675,13 @@ Object {
56075675
"xAxis": Object {
56085676
"tickLayout": "auto",
56095677
},
5678+
"xAxisAnnotation": "",
5679+
"xAxisAnnotationStyles": Object {
5680+
"color": "var(--colorNeutralForeground1)",
5681+
"fontFamily": "Arial, sans-serif",
5682+
"fontSize": "13px",
5683+
"fontWeight": 400,
5684+
},
56105685
"xAxisCategoryOrder": Array [
56115686
"Jan",
56125687
"Feb",

packages/charts/react-charts/library/src/components/DonutChart/DonutChart.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,17 @@ export const DonutChart: React.FunctionComponent<DonutChartProps> = React.forwar
317317
}}
318318
onMouseLeave={_handleChartMouseLeave}
319319
>
320+
{!hideLegend && data?.chartTitle && (
321+
<div
322+
style={{
323+
textAlign: 'center',
324+
marginBottom: '8px',
325+
...props.chartTitleStyles,
326+
}}
327+
>
328+
{data?.chartTitle}
329+
</div>
330+
)}
320331
{props.xAxisAnnotation && (
321332
<text className={classes.axisAnnotation} x={_width! / 2} y={_height! - 10} textAnchor="middle">
322333
{props.xAxisAnnotation}

packages/charts/react-charts/library/src/components/DonutChart/DonutChart.types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ export interface DonutChartProps extends CartesianChartProps {
8686
*/
8787
className?: string;
8888

89+
/**
90+
* Inline styles for the chart title
91+
*/
92+
chartTitleStyles?: React.CSSProperties;
93+
8994
// eslint-disable-next-line @typescript-eslint/no-explicit-any
9095
legendsOverflowText?: any;
9196

0 commit comments

Comments
 (0)