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): make charts responsive #33723

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
Copy link
Collaborator

@fabricteam fabricteam Jan 24, 2025

Choose a reason for hiding this comment

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

🕵🏾‍♀️ visual regressions to review in the fluentuiv8 Visual Regression Report

Keytip 1 screenshots
Image Name Diff(in Pixels) Image Type
Keytip.Offset.chromium.png 121 Changed
react-charting-AreaChart 1 screenshots
Image Name Diff(in Pixels) Image Type
react-charting-AreaChart.Custom Accessibility.chromium.png 11 Changed
react-charting-LineChart 1 screenshots
Image Name Diff(in Pixels) Image Type
react-charting-LineChart.Gaps.chromium.png 1 Changed

"type": "patch",
"comment": "feat: make charts responsive",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
24 changes: 24 additions & 0 deletions packages/charts/react-charting/etc/react-charting.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -1214,6 +1214,24 @@ export interface IRefArrayData {
refElement?: SVGGElement;
}

// @public
export interface IResponsiveChildProps {
// (undocumented)
height?: number;
// (undocumented)
shouldResize?: number;
// (undocumented)
width?: number;
}

// @public
export interface IResponsiveContainerProps {
children: React_2.ReactElement<IResponsiveChildProps>;
height?: number | string;
onResize?: (width: number, height: number) => void;
width?: number | string;
}

// @public
export interface ISankeyChartAccessibilityProps {
emptyAriaLabel?: string;
Expand Down Expand Up @@ -1639,6 +1657,9 @@ export enum NodesComposition {
// @public
export const PieChart: React_2.FunctionComponent<IPieChartProps>;

// @public
export const ResponsiveContainer: React_2.FC<IResponsiveContainerProps>;

// @public
export const SankeyChart: React_2.FunctionComponent<ISankeyChartProps>;

Expand Down Expand Up @@ -1685,6 +1706,9 @@ export const VerticalBarChart: React_2.FunctionComponent<IVerticalBarChartProps>
// @public
export const VerticalStackedBarChart: React_2.FunctionComponent<IVerticalStackedBarChartProps>;

// @public
export function withResponsiveContainer<TProps extends Omit<IResponsiveContainerProps, 'children'>>(WrappedComponent: React_2.ComponentType<TProps>): React_2.FC<TProps>;

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components/ResponsiveContainer/index';
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@ import { GroupedVerticalBarChart } from '../GroupedVerticalBarChart/index';
import { VerticalBarChart } from '../VerticalBarChart/index';
import { IImageExportOptions, toImage } from './imageExporter';
import { IChart } from '../../types/index';
import { withResponsiveContainer } from '../ResponsiveContainer/withResponsiveContainer';

const ResponsiveDonutChart = withResponsiveContainer(DonutChart);
const ResponsiveVerticalStackedBarChart = withResponsiveContainer(VerticalStackedBarChart);
const ResponsiveLineChart = withResponsiveContainer(LineChart);
const ResponsiveHorizontalBarChartWithAxis = withResponsiveContainer(HorizontalBarChartWithAxis);
const ResponsiveAreaChart = withResponsiveContainer(AreaChart);
const ResponsiveHeatMapChart = withResponsiveContainer(HeatMapChart);
const ResponsiveSankeyChart = withResponsiveContainer(SankeyChart);
const ResponsiveGaugeChart = withResponsiveContainer(GaugeChart);
const ResponsiveGroupedVerticalBarChart = withResponsiveContainer(GroupedVerticalBarChart);
const ResponsiveVerticalBarChart = withResponsiveContainer(VerticalBarChart);

/**
* DeclarativeChart schema.
Expand Down Expand Up @@ -167,7 +179,7 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
// Unsupported schema, render as VerticalStackedBarChart
fallbackVSBC = true;
return (
<VerticalStackedBarChart
<ResponsiveVerticalStackedBarChart
{...transformPlotlyJsonToVSBCProps(plotlySchema, colorMap, isDarkTheme, fallbackVSBC)}
{...commonProps}
/>
Expand Down Expand Up @@ -195,12 +207,17 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =

switch (plotlyInput.data[0].type) {
case 'pie':
return <DonutChart {...transformPlotlyJsonToDonutProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />;
return (
<ResponsiveDonutChart
{...transformPlotlyJsonToDonutProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
);
case 'bar':
const orientation = plotlyInput.data[0].orientation;
if (orientation === 'h') {
return (
<HorizontalBarChartWithAxis
<ResponsiveHorizontalBarChartWithAxis
{...transformPlotlyJsonToHorizontalBarWithAxisProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
Expand All @@ -211,14 +228,14 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
);
if (['group', 'overlay'].includes(plotlySchema?.layout?.barmode) && !containsLines) {
return (
<GroupedVerticalBarChart
<ResponsiveGroupedVerticalBarChart
{...transformPlotlyJsonToGVBCProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
);
}
return (
<VerticalStackedBarChart
<ResponsiveVerticalStackedBarChart
{...transformPlotlyJsonToVSBCProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
Expand All @@ -233,34 +250,49 @@ export const DeclarativeChart: React.FunctionComponent<DeclarativeChartProps> =
);
const renderChartJsx = (chartProps: ILineChartProps | IAreaChartProps) => {
if (isAreaChart) {
return <AreaChart {...chartProps} />;
return <ResponsiveAreaChart {...chartProps} />;
}
return <LineChart {...chartProps} />;
return <ResponsiveLineChart {...chartProps} />;
};
return checkAndRenderChart(renderChartJsx, isAreaChart);
case 'heatmap':
return <HeatMapChart {...transformPlotlyJsonToHeatmapProps(plotlySchema)} {...commonProps} legendProps={{}} />;
return (
<ResponsiveHeatMapChart
{...transformPlotlyJsonToHeatmapProps(plotlySchema)}
{...commonProps}
legendProps={{}}
/>
);
case 'sankey':
return (
<SankeyChart {...transformPlotlyJsonToSankeyProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
<ResponsiveSankeyChart
{...transformPlotlyJsonToSankeyProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
);
case 'indicator':
if (plotlyInput.data?.[0]?.mode?.includes('gauge')) {
return (
<GaugeChart {...transformPlotlyJsonToGaugeProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
<ResponsiveGaugeChart
{...transformPlotlyJsonToGaugeProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
);
}
throw new Error(`Unsupported chart - type: ${plotlyInput.data[0]?.type}, mode: ${plotlyInput.data[0]?.mode}`);
case 'histogram':
return (
<VerticalBarChart {...transformPlotlyJsonToVBCProps(plotlySchema, colorMap, isDarkTheme)} {...commonProps} />
<ResponsiveVerticalBarChart
{...transformPlotlyJsonToVBCProps(plotlySchema, colorMap, isDarkTheme)}
{...commonProps}
/>
);
default:
const xValues = (plotlyInput.data[0] as PlotData).x;
const yValues = (plotlyInput.data[0] as PlotData).y;
if (xValues && yValues && xValues.length > 0 && yValues.length > 0) {
const renderLineChartJsx = (chartProps: ILineChartProps) => {
return <LineChart {...chartProps} />;
return <ResponsiveLineChart {...chartProps} />;
};
return checkAndRenderChart(renderLineChartJsx);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ export const transformPlotlyJsonToDonutProps = (
chartData: donutData,
},
hideLegend: input.layout?.showlegend === false ? true : false,
width,
width: input.layout?.width,
height,
innerRadius,
hideLabels,
Expand Down Expand Up @@ -339,8 +339,8 @@ export const transformPlotlyJsonToVSBCProps = (

return {
data: Object.values(mapXToDataPoints),
// width: layout?.width,
// height: layout?.height,
width: input.layout?.width,
height: input.layout?.height ?? 350,
barWidth: 'auto',
yMaxValue,
chartTitle,
Expand Down Expand Up @@ -390,8 +390,8 @@ export const transformPlotlyJsonToGVBCProps = (

return {
data: Object.values(mapXToDataPoints),
// width: layout?.width,
// height: layout?.height,
width: input.layout?.width,
height: input.layout?.height ?? 350,
barwidth: 'auto',
chartTitle,
xAxisTitle,
Expand Down Expand Up @@ -483,8 +483,8 @@ export const transformPlotlyJsonToVBCProps = (

return {
data: vbcData,
// width: layout?.width,
// height: layout?.height,
width: input.layout?.width,
height: input.layout?.height ?? 350,
supportNegativeData: true,
chartTitle,
xAxisTitle,
Expand Down Expand Up @@ -541,6 +541,8 @@ export const transformPlotlyJsonToScatterChartProps = (
secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle,
secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions,
mode,
width: input.layout?.width,
height: input.layout?.height ?? 350,
} as IAreaChartProps;
} else {
return {
Expand All @@ -550,6 +552,8 @@ export const transformPlotlyJsonToScatterChartProps = (
yAxisTitle,
secondaryYAxistitle: secondaryYAxisValues.secondaryYAxistitle,
secondaryYScaleOptions: secondaryYAxisValues.secondaryYScaleOptions,
width: input.layout?.width,
height: input.layout?.height ?? 350,
} as ILineChartProps;
}
};
Expand Down Expand Up @@ -599,12 +603,8 @@ export const transformPlotlyJsonToHorizontalBarWithAxisProps = (
: input.layout?.yaxis2?.title?.text || '',
barHeight,
showYAxisLables: true,
styles: {
root: {
height: chartHeight,
width: input.layout?.width ?? 600,
},
},
height: chartHeight,
width: input.layout?.width,
};
};

Expand Down Expand Up @@ -662,6 +662,8 @@ export const transformPlotlyJsonToHeatmapProps = (input: PlotlySchema): IHeatMap
xAxisTitle,
yAxisTitle,
sortOrder: 'none',
width: input.layout?.width,
height: input.layout?.height ?? 350,
};
};

Expand Down Expand Up @@ -698,14 +700,11 @@ export const transformPlotlyJsonToSankeyProps = (
}),
} as ISankeyChartData;

const width: number = input.layout?.width ?? 440;
const height: number = input.layout?.height ?? 220;
const styles: ISankeyChartProps['styles'] = {
root: {
...(input.layout?.font?.size ? { fontSize: input.layout.font?.size } : {}),
},
};
const shouldResize: number = width + height;

const { chartTitle } = getTitles(input.layout);

Expand All @@ -714,10 +713,9 @@ export const transformPlotlyJsonToSankeyProps = (
chartTitle,
SankeyChartData: sankeyChartData,
},
width,
height,
width: input.layout?.width,
height: input.layout?.height ?? 468,
styles,
shouldResize,
enableReflow: true,
};
};
Expand Down Expand Up @@ -784,7 +782,7 @@ export const transformPlotlyJsonToGaugeProps = (
minValue: typeof firstData.gauge?.axis?.range?.[0] === 'number' ? firstData.gauge?.axis?.range?.[0] : undefined,
maxValue: typeof firstData.gauge?.axis?.range?.[1] === 'number' ? firstData.gauge?.axis?.range?.[1] : undefined,
chartValueFormat: () => firstData.value?.toString() ?? '',
width: input.layout?.width ?? 440,
width: input.layout?.width,
height: input.layout?.height ?? 220,
styles,
variant: firstData.gauge?.steps?.length ? GaugeChartVariant.MultipleSegments : GaugeChartVariant.SingleSegment,
Expand Down
Loading