Skip to content

Commit 1115b93

Browse files
authored
feat(react-charting): make charts responsive (#33723)
1 parent 21d0156 commit 1115b93

File tree

47 files changed

+27609
-27029
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+27609
-27029
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": "feat: make charts responsive",
4+
"packageName": "@fluentui/react-charting",
5+
"email": "110246001+krkshitij@users.noreply.github.com",
6+
"dependentChangeType": "patch"
7+
}

packages/charts/react-charting/UnitTests/__snapshots__/VerticalBarChartUT.test.tsx.snap

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ exports[`vertical bar chart with numeric x-axis data Should render the vertical
2828
&:focus {
2929
outline: none;
3030
}
31-
{
32-
overflow: auto;
33-
}
31+
3432
data-focuszone-id="FocusZone5"
3533
data-tabster="{\\"uncontrolled\\": {}}"
3634
>

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ export interface ICartesianChartProps {
325325
export interface ICartesianChartStyleProps {
326326
className?: string;
327327
color?: string;
328+
enableReflow?: boolean;
328329
height?: number;
329330
href?: string;
330331
isRtl?: boolean;
@@ -1216,6 +1217,32 @@ export interface IRefArrayData {
12161217
refElement?: SVGGElement;
12171218
}
12181219

1220+
// @public
1221+
export interface IResponsiveChildProps {
1222+
// (undocumented)
1223+
height?: number;
1224+
// (undocumented)
1225+
shouldResize?: number;
1226+
// (undocumented)
1227+
styles?: IStyleFunctionOrObject_2<{}, {
1228+
root: IStyle_2;
1229+
}>;
1230+
// (undocumented)
1231+
width?: number;
1232+
}
1233+
1234+
// @public
1235+
export interface IResponsiveContainerProps {
1236+
aspect?: number;
1237+
children: React_2.ReactElement<IResponsiveChildProps>;
1238+
height?: number | string;
1239+
maxHeight?: number;
1240+
minHeight?: number | string;
1241+
minWidth?: number | string;
1242+
onResize?: (width: number, height: number) => void;
1243+
width?: number | string;
1244+
}
1245+
12191246
// @public
12201247
export interface ISankeyChartAccessibilityProps {
12211248
emptyAriaLabel?: string;
@@ -1262,6 +1289,8 @@ export interface ISankeyChartStyleProps {
12621289
// (undocumented)
12631290
className?: string;
12641291
// (undocumented)
1292+
enableReflow?: boolean;
1293+
// (undocumented)
12651294
height: number;
12661295
// (undocumented)
12671296
pathColor?: string;
@@ -1641,6 +1670,9 @@ export enum NodesComposition {
16411670
// @public
16421671
export const PieChart: React_2.FunctionComponent<IPieChartProps>;
16431672

1673+
// @public
1674+
export const ResponsiveContainer: React_2.FC<IResponsiveContainerProps>;
1675+
16441676
// @public
16451677
export const SankeyChart: React_2.FunctionComponent<ISankeyChartProps>;
16461678

@@ -1687,6 +1719,9 @@ export const VerticalBarChart: React_2.FunctionComponent<IVerticalBarChartProps>
16871719
// @public
16881720
export const VerticalStackedBarChart: React_2.FunctionComponent<IVerticalStackedBarChartProps>;
16891721

1722+
// @public
1723+
export function withResponsiveContainer<TProps extends Omit<IResponsiveContainerProps, 'children'>>(WrappedComponent: React_2.ComponentType<TProps>): React_2.FC<TProps>;
1724+
16901725
// (No @packageDocumentation comment for this package)
16911726

16921727
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './components/ResponsiveContainer/index';

packages/charts/react-charting/src/components/AreaChart/__snapshots__/AreaChart.test.tsx.snap

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ exports[`AreaChart - mouse events Should render callout correctly on mouseover 1
2828
&:focus {
2929
outline: none;
3030
}
31-
{
32-
overflow: auto;
33-
}
31+
3432
data-focuszone-id="FocusZone8"
3533
data-tabster="{\\"uncontrolled\\": {}}"
3634
onFocus={[Function]}
@@ -599,9 +597,7 @@ exports[`AreaChart - mouse events Should render customized callout on mouseover
599597
&:focus {
600598
outline: none;
601599
}
602-
{
603-
overflow: auto;
604-
}
600+
605601
data-focuszone-id="FocusZone8"
606602
data-tabster="{\\"uncontrolled\\": {}}"
607603
onFocus={[Function]}
@@ -1067,9 +1063,7 @@ exports[`AreaChart - mouse events Should render customized callout per stack on
10671063
&:focus {
10681064
outline: none;
10691065
}
1070-
{
1071-
overflow: auto;
1072-
}
1066+
10731067
data-focuszone-id="FocusZone8"
10741068
data-tabster="{\\"uncontrolled\\": {}}"
10751069
onFocus={[Function]}

packages/charts/react-charting/src/components/AreaChart/__snapshots__/AreaChartRTL.test.tsx.snap

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,7 @@ exports[`Area chart rendering Should render the Area Chart with negative y value
2828
&:focus {
2929
outline: none;
3030
}
31-
{
32-
overflow: auto;
33-
}
31+
3432
data-focuszone-id="FocusZone8"
3533
data-tabster="{\\"uncontrolled\\": {}}"
3634
>
@@ -1170,9 +1168,7 @@ exports[`Area chart rendering Should render the Area Chart with tozeroy mode 1`]
11701168
&:focus {
11711169
outline: none;
11721170
}
1173-
{
1174-
overflow: auto;
1175-
}
1171+
11761172
data-focuszone-id="FocusZone8"
11771173
data-tabster="{\\"uncontrolled\\": {}}"
11781174
>
@@ -6169,9 +6165,7 @@ exports[`Area chart rendering Should render the Area chart with secondary Y axis
61696165
&:focus {
61706166
outline: none;
61716167
}
6172-
{
6173-
overflow: auto;
6174-
}
6168+
61756169
data-focuszone-id="FocusZone8"
61766170
data-tabster="{\\"uncontrolled\\": {}}"
61776171
>
@@ -9782,9 +9776,7 @@ exports[`Area chart rendering Should render the area chart with numeric x-axis d
97829776
&:focus {
97839777
outline: none;
97849778
}
9785-
{
9786-
overflow: auto;
9787-
}
9779+
97889780
data-focuszone-id="FocusZone8"
97899781
data-tabster="{\\"uncontrolled\\": {}}"
97909782
>
@@ -10937,9 +10929,7 @@ exports[`AreaChart - Theme Should reflect theme change 1`] = `
1093710929
&:focus {
1093810930
outline: none;
1093910931
}
10940-
{
10941-
overflow: auto;
10942-
}
10932+
1094310933
data-focuszone-id="FocusZone11"
1094410934
data-tabster="{\\"uncontrolled\\": {}}"
1094510935
>
@@ -12080,9 +12070,7 @@ exports[`Screen resolution Should remain unchanged on zoom in 1`] = `
1208012070
&:focus {
1208112071
outline: none;
1208212072
}
12083-
{
12084-
overflow: auto;
12085-
}
12073+
1208612074
data-focuszone-id="FocusZone8"
1208712075
data-tabster="{\\"uncontrolled\\": {}}"
1208812076
>
@@ -13222,9 +13210,7 @@ exports[`Screen resolution Should remain unchanged on zoom out 1`] = `
1322213210
&:focus {
1322313211
outline: none;
1322413212
}
13225-
{
13226-
overflow: auto;
13227-
}
13213+
1322813214
data-focuszone-id="FocusZone8"
1322913215
data-tabster="{\\"uncontrolled\\": {}}"
1323013216
>

packages/charts/react-charting/src/components/CommonComponents/CartesianChart.base.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ export class CartesianChartBase
480480
height: this.state._height,
481481
className: this.props.className,
482482
isRtl: this._isRtl,
483+
enableReflow: this.props.enableReflow,
483484
});
484485

485486
const svgDimensions = {

packages/charts/react-charting/src/components/CommonComponents/CartesianChart.styles.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,16 @@ import { NeutralColors, isIE11 } from '@fluentui/react';
55
const isIE11Var: boolean = isIE11();
66

77
export const getStyles = (props: ICartesianChartStyleProps): ICartesianChartStyles => {
8-
const { className, theme, isRtl, shouldHighlight, href, lineColor = 'transparent', toDrawShape } = props;
8+
const {
9+
className,
10+
theme,
11+
isRtl,
12+
shouldHighlight,
13+
href,
14+
lineColor = 'transparent',
15+
toDrawShape,
16+
enableReflow,
17+
} = props;
918
const { fonts } = theme!;
1019
return {
1120
root: [
@@ -20,7 +29,7 @@ export const getStyles = (props: ICartesianChartStyleProps): ICartesianChartStyl
2029
className,
2130
],
2231
chartWrapper: {
23-
overflow: 'auto',
32+
...(enableReflow ? { overflow: 'auto' } : {}),
2433
},
2534
axisTitle: [
2635
theme.fonts.xSmall,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,11 @@ export interface ICartesianChartStyleProps {
7676
* boolean flag which determines if shape is drawn in callout
7777
*/
7878
toDrawShape?: boolean;
79+
80+
/**
81+
* Prop to disable shrinking of the chart beyond a certain limit and enable scrolling when the chart overflows
82+
*/
83+
enableReflow?: boolean;
7984
}
8085

8186
/**

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

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ import { GroupedVerticalBarChart } from '../GroupedVerticalBarChart/index';
3333
import { VerticalBarChart } from '../VerticalBarChart/index';
3434
import { IImageExportOptions, toImage } from './imageExporter';
3535
import { IChart } from '../../types/index';
36+
import { withResponsiveContainer } from '../ResponsiveContainer/withResponsiveContainer';
37+
38+
const ResponsiveDonutChart = withResponsiveContainer(DonutChart);
39+
const ResponsiveVerticalStackedBarChart = withResponsiveContainer(VerticalStackedBarChart);
40+
const ResponsiveLineChart = withResponsiveContainer(LineChart);
41+
const ResponsiveHorizontalBarChartWithAxis = withResponsiveContainer(HorizontalBarChartWithAxis);
42+
const ResponsiveAreaChart = withResponsiveContainer(AreaChart);
43+
const ResponsiveHeatMapChart = withResponsiveContainer(HeatMapChart);
44+
const ResponsiveSankeyChart = withResponsiveContainer(SankeyChart);
45+
const ResponsiveGaugeChart = withResponsiveContainer(GaugeChart);
46+
const ResponsiveGroupedVerticalBarChart = withResponsiveContainer(GroupedVerticalBarChart);
47+
const ResponsiveVerticalBarChart = withResponsiveContainer(VerticalBarChart);
3648

3749
/**
3850
* DeclarativeChart schema.
@@ -167,7 +179,7 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
167179
// Unsupported schema, render as VerticalStackedBarChart
168180
fallbackVSBC = true;
169181
return (
170-
<VerticalStackedBarChart
182+
<ResponsiveVerticalStackedBarChart
171183
{...transformPlotlyJsonToVSBCProps(plotlySchema, colorMap, isDarkTheme, fallbackVSBC)}
172184
{...commonProps}
173185
/>
@@ -195,12 +207,17 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
195207

196208
switch (plotlyInput.data[0].type) {
197209
case 'pie':
198-
return <DonutChart {...transformPlotlyJsonToDonutProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />;
210+
return (
211+
<ResponsiveDonutChart
212+
{...transformPlotlyJsonToDonutProps(plotlySchema, colorMap, isDarkTheme)}
213+
{...commonProps}
214+
/>
215+
);
199216
case 'bar':
200217
const orientation = plotlyInput.data[0].orientation;
201218
if (orientation === 'h' && isNumberArray((plotlyInput.data[0] as PlotData).x)) {
202219
return (
203-
<HorizontalBarChartWithAxis
220+
<ResponsiveHorizontalBarChartWithAxis
204221
{...transformPlotlyJsonToHorizontalBarWithAxisProps(plotlySchema, colorMap, isDarkTheme)}
205222
{...commonProps}
206223
/>
@@ -211,14 +228,14 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
211228
);
212229
if (['group', 'overlay'].includes(plotlySchema?.layout?.barmode) && !containsLines) {
213230
return (
214-
<GroupedVerticalBarChart
231+
<ResponsiveGroupedVerticalBarChart
215232
{...transformPlotlyJsonToGVBCProps(plotlySchema, colorMap, isDarkTheme)}
216233
{...commonProps}
217234
/>
218235
);
219236
}
220237
return (
221-
<VerticalStackedBarChart
238+
<ResponsiveVerticalStackedBarChart
222239
{...transformPlotlyJsonToVSBCProps(plotlySchema, colorMap, isDarkTheme)}
223240
{...commonProps}
224241
/>
@@ -233,35 +250,50 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
233250
);
234251
const renderChartJsx = (chartProps: ILineChartProps | IAreaChartProps) => {
235252
if (isAreaChart) {
236-
return <AreaChart {...chartProps} />;
253+
return <ResponsiveAreaChart {...chartProps} />;
237254
}
238-
return <LineChart {...chartProps} />;
255+
return <ResponsiveLineChart {...chartProps} />;
239256
};
240257
return checkAndRenderChart(renderChartJsx, isAreaChart);
241258
case 'heatmap':
242-
return <HeatMapChart {...transformPlotlyJsonToHeatmapProps(plotlySchema)} {...commonProps} legendProps={{}} />;
259+
return (
260+
<ResponsiveHeatMapChart
261+
{...transformPlotlyJsonToHeatmapProps(plotlySchema)}
262+
{...commonProps}
263+
legendProps={{}}
264+
/>
265+
);
243266
case 'sankey':
244267
return (
245-
<SankeyChart {...transformPlotlyJsonToSankeyProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
268+
<ResponsiveSankeyChart
269+
{...transformPlotlyJsonToSankeyProps(plotlySchema, colorMap, isDarkTheme)}
270+
{...commonProps}
271+
/>
246272
);
247273
case 'indicator':
248274
case 'gauge':
249275
if (plotlyInput.data?.[0]?.mode?.includes('gauge') || plotlyInput.data?.[0]?.type === 'gauge') {
250276
return (
251-
<GaugeChart {...transformPlotlyJsonToGaugeProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
277+
<ResponsiveGaugeChart
278+
{...transformPlotlyJsonToGaugeProps(plotlySchema, colorMap, isDarkTheme)}
279+
{...commonProps}
280+
/>
252281
);
253282
}
254283
throw new Error(`Unsupported chart - type: ${plotlyInput.data[0]?.type}, mode: ${plotlyInput.data[0]?.mode}`);
255284
case 'histogram':
256285
return (
257-
<VerticalBarChart {...transformPlotlyJsonToVBCProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
286+
<ResponsiveVerticalBarChart
287+
{...transformPlotlyJsonToVBCProps(plotlySchema, colorMap, isDarkTheme)}
288+
{...commonProps}
289+
/>
258290
);
259291
default:
260292
const xValues = (plotlyInput.data[0] as PlotData).x;
261293
const yValues = (plotlyInput.data[0] as PlotData).y;
262294
if (xValues && yValues && xValues.length > 0 && yValues.length > 0) {
263295
const renderLineChartJsx = (chartProps: ILineChartProps) => {
264-
return <LineChart {...chartProps} />;
296+
return <ResponsiveLineChart {...chartProps} />;
265297
};
266298
return checkAndRenderChart(renderLineChartJsx);
267299
}

0 commit comments

Comments
 (0)