Skip to content

Commit

Permalink
feat(react-charting): add functionality to export chart as image (#33445
Browse files Browse the repository at this point in the history
)
  • Loading branch information
krkshitij authored Dec 23, 2024
1 parent 1cbd8b7 commit 7b4a378
Show file tree
Hide file tree
Showing 24 changed files with 606 additions and 53 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat: add functionality to export chart as image",
"packageName": "@fluentui/react-charting",
"email": "110246001+krkshitij@users.noreply.github.com",
"dependentChangeType": "patch"
}
31 changes: 31 additions & 0 deletions packages/charts/react-charting/etc/react-charting.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { IFocusZoneProps } from '@fluentui/react-focus';
import { IHoverCardStyleProps } from '@fluentui/react/lib/HoverCard';
import { IHoverCardStyles } from '@fluentui/react/lib/HoverCard';
import { IOverflowSetProps } from '@fluentui/react/lib/OverflowSet';
import { IRefObject } from '@fluentui/react/lib/Utilities';
import { IRenderFunction } from '@fluentui/react/lib/Utilities';
import { IStyle } from '@fluentui/react/lib/Styling';
import { IStyle as IStyle_2 } from '@fluentui/react';
Expand Down Expand Up @@ -125,6 +126,7 @@ export const DeclarativeChart: React_2.FunctionComponent<DeclarativeChartProps>;
// @public
export interface DeclarativeChartProps extends React_2.RefAttributes<HTMLDivElement> {
chartSchema: Schema;
componentRef?: IRefObject<IDeclarativeChart>;
onSchemaChange?: (eventData: Schema) => void;
}

Expand Down Expand Up @@ -267,6 +269,7 @@ export interface ICartesianChartProps {
// @deprecated
chartLabel?: string;
className?: string;
componentRef?: IRefObject<IChart>;
customDateTimeFormatter?: (dateTime: Date) => string;
dateLocalizeOptions?: Intl.DateTimeFormatOptions;
enabledLegendsWrapLines?: boolean;
Expand Down Expand Up @@ -352,6 +355,12 @@ export interface ICartesianChartStyles {
yAxis?: IStyle;
}

// @public (undocumented)
export interface IChart {
// (undocumented)
chartContainer: HTMLElement | null;
}

// @public (undocumented)
export interface IChartDataPoint {
callOutAccessibilityData?: IAccessibilityProps;
Expand Down Expand Up @@ -481,13 +490,20 @@ export interface IDataPoint {
y: number;
}

// @public (undocumented)
export interface IDeclarativeChart {
// (undocumented)
exportAsImage: (opts?: IImageExportOptions) => Promise<string>;
}

// @public (undocumented)
export interface IDonutChart {
}

// @public
export interface IDonutChartProps extends ICartesianChartProps {
calloutProps?: Partial<ICalloutProps>;
componentRef?: IRefObject<IChart>;
culture?: string;
data?: IChartProps;
enableGradient?: boolean;
Expand Down Expand Up @@ -536,6 +552,7 @@ export interface IGaugeChartProps {
chartValue: number;
chartValueFormat?: GaugeValueFormat | ((sweepFraction: [number, number]) => string);
className?: string;
componentRef?: IRefObject<IChart>;
culture?: string;
enableGradient?: boolean;
height?: number;
Expand Down Expand Up @@ -832,6 +849,18 @@ export interface IHorizontalDataPoint {
y: number;
}

// @public (undocumented)
export interface IImageExportOptions {
// (undocumented)
background?: string;
// (undocumented)
height?: number;
// (undocumented)
scale?: number;
// (undocumented)
width?: number;
}

// @public
export interface ILegend {
action?: VoidFunction;
Expand Down Expand Up @@ -1068,6 +1097,7 @@ export interface IModifiedCartesianChartProps extends ICartesianChartProps {
maxOfYVal?: number;
onChartMouseLeave?: () => void;
points: any;
ref?: IRefObject<IChart>;
showYAxisLables?: boolean;
showYAxisLablesTooltip?: boolean;
stringDatasetForYAxisDomain?: string[];
Expand Down Expand Up @@ -1200,6 +1230,7 @@ export interface ISankeyChartProps {
borderColorsForNodes?: string[];
className?: string;
colorsForNodes?: string[];
componentRef?: IRefObject<IChart>;
data: IChartProps;
enableReflow?: boolean;
formatNumberOptions?: Intl.NumberFormatOptions;
Expand Down
2 changes: 1 addition & 1 deletion packages/charts/react-charting/src/DeclarativeChart.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './components/DeclarativeChart/DeclarativeChart';
export * from './components/DeclarativeChart/index';
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ import { max as d3Max, bisector } from 'd3-array';
import { pointer } from 'd3-selection';
import { select as d3Select } from 'd3-selection';
import { area as d3Area, stack as d3Stack, curveMonotoneX as d3CurveBasis, line as d3Line } from 'd3-shape';
import { classNamesFunction, find, getId, memoizeFunction } from '@fluentui/react/lib/Utilities';
import {
classNamesFunction,
find,
getId,
initializeComponentRef,
memoizeFunction,
} from '@fluentui/react/lib/Utilities';
import {
IAccessibilityProps,
CartesianChart,
Expand Down Expand Up @@ -38,6 +44,7 @@ import {
} from '../../utilities/index';
import { ILegend, Legends } from '../Legends/index';
import { DirectionalHint } from '@fluentui/react/lib/Callout';
import { IChart } from '../../types/index';

const getClassNames = classNamesFunction<IAreaChartStyleProps, IAreaChartStyles>();

Expand Down Expand Up @@ -82,7 +89,7 @@ export interface IAreaChartState extends IBasestate {
activePoint: string;
}

export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartState> {
export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartState> implements IChart {
public static defaultProps: Partial<IAreaChartProps> = {
useUTC: true,
};
Expand Down Expand Up @@ -119,9 +126,13 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
private _enableComputationOptimization: boolean;
private _firstRenderOptimization: boolean;
private _emptyChartId: string;
private _cartesianChartRef: React.RefObject<IChart>;

public constructor(props: IAreaChartProps) {
super(props);

initializeComponentRef(this);

this._createSet = memoizeFunction(this._createDataSet);
this.state = {
selectedLegend: props.legendProps?.selectedLegend ?? '',
Expand All @@ -148,6 +159,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
this._enableComputationOptimization = true;
this._firstRenderOptimization = true;
this._emptyChartId = getId('_AreaChart_empty');
this._cartesianChartRef = React.createRef();
}

public componentDidUpdate() {
Expand Down Expand Up @@ -212,6 +224,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
customizedCallout={this._getCustomizedCallout()}
onChartMouseLeave={this._handleChartMouseLeave}
enableFirstRenderOptimization={this.props.enablePerfOptimization && this._firstRenderOptimization}
ref={this._cartesianChartRef}
/* eslint-disable react/jsx-no-bind */
// eslint-disable-next-line react/no-children-prop
children={(props: IChildProps) => {
Expand Down Expand Up @@ -249,6 +262,10 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
);
}

public get chartContainer(): HTMLElement | null {
return this._cartesianChartRef.current?.chartContainer || null;
}

private _getDomainNRangeValues = (
points: ILineChartPoints[],
margins: IMargins,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '../../utilities/index';
import { LegendShape, Shape } from '../Legends/index';
import { SVGTooltipText } from '../../utilities/SVGTooltipText';
import { IChart } from '../../types/index';

const getClassNames = classNamesFunction<ICartesianChartStyleProps, ICartesianChartStyles>();
const ChartHoverCard = React.lazy(() =>
Expand Down Expand Up @@ -63,9 +64,12 @@ export interface ICartesianChartState {
* 2.Callout
* 3.Fit parent Continer
*/
export class CartesianChartBase extends React.Component<IModifiedCartesianChartProps, ICartesianChartState> {
export class CartesianChartBase
extends React.Component<IModifiedCartesianChartProps, ICartesianChartState>
implements IChart
{
public chartContainer: HTMLDivElement;
private _classNames: IProcessedStyleSet<ICartesianChartStyles>;
private chartContainer: HTMLDivElement;
private legendContainer: HTMLDivElement;
private minLegendContainerHeight: number = 32;
private xAxisElement: SVGSVGElement | null;
Expand Down Expand Up @@ -619,6 +623,7 @@ export class CartesianChartBase extends React.Component<IModifiedCartesianChartP
</div>
);
}

/**
* Dedicated function to return the Callout JSX Element , which can further be used to only call this when
* only the calloutprops and charthover props changes.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import * as React from 'react';
import { IStyleFunctionOrObject } from '@fluentui/react/lib/Utilities';
import { IRefObject, IStyleFunctionOrObject } from '@fluentui/react/lib/Utilities';
import { ITheme, IStyle } from '@fluentui/react/lib/Styling';
import { IOverflowSetProps } from '@fluentui/react/lib/OverflowSet';
import { IFocusZoneProps, FocusZoneDirection } from '@fluentui/react-focus';
import { ICalloutProps } from '@fluentui/react/lib/Callout';
import { ILegendsProps } from '../Legends/index';
import {
IAccessibilityProps,
IChart,
IDataPoint,
IGroupedVerticalBarChartData,
IHeatMapChartDataPoint,
Expand Down Expand Up @@ -445,6 +446,12 @@ export interface ICartesianChartProps {
* Used for enabling negative values in Y axis.
*/
supportNegativeData?: boolean;

/**
* Optional callback to access the IChart interface. Use this instead of ref for accessing
* the public methods and properties of the component.
*/
componentRef?: IRefObject<IChart>;
}

export interface IYValueHover {
Expand Down Expand Up @@ -690,4 +697,9 @@ export interface IModifiedCartesianChartProps extends ICartesianChartProps {
isRtl: boolean,
barWidth: number | undefined,
) => ScaleBand<string>;

/**
* Callback to access the public methods and properties of the component.
*/
ref?: IRefObject<IChart>;
}
Loading

0 comments on commit 7b4a378

Please sign in to comment.