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

Enable 'tozeroy' mode for Area Charts #33581

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Support tozeroy mode for Area Charts",
"packageName": "@fluentui/react-charting",
"email": "[email protected]",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions packages/charts/react-charting/etc/react-charting.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ export interface IAreaChartProps extends ICartesianChartProps {
data: IChartProps;
enableGradient?: boolean;
enablePerfOptimization?: boolean;
mode?: 'tozeroy' | 'tonexty';
onRenderCalloutPerDataPoint?: IRenderFunction<ICustomizedCalloutData>;
onRenderCalloutPerStack?: IRenderFunction<ICustomizedCalloutData>;
// (undocumented)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
import { ILegend, Legends } from '../Legends/index';
import { DirectionalHint } from '@fluentui/react/lib/Callout';
import { IChart } from '../../types/index';
import { AreaChartModes } from './AreaChart.types';

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

Expand All @@ -64,7 +65,7 @@ export interface IAreaChartAreaPoint {
values: IAreaChartDataSetPoint;
}
export interface IAreaChartDataSetPoint {
[key: string]: number | string;
[key: string]: number | string | number[];
}
export interface IDPointType {
values: { 0: number; 1: number; data: {} };
Expand Down Expand Up @@ -102,7 +103,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
colors: string[];
opacity: number[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stackedInfo: any;
data: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
calloutPoints: any;
};
Expand All @@ -113,7 +114,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
private _circleId: string;
private _uniqueCallOutID: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _stackedData: any;
private _data: any;
private _chart: JSX.Element[];
private margins: IMargins;
private _rectId: string;
Expand Down Expand Up @@ -184,12 +185,12 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
if (!this._isChartEmpty()) {
const { lineChartData } = this.props.data;
const points = this._addDefaultColors(lineChartData);
const { colors, opacity, stackedInfo, calloutPoints } = this._createSet(points);
const { colors, opacity, data, calloutPoints } = this._createSet(points);
this._calloutPoints = calloutPoints;
const isXAxisDateType = getXAxisType(points);
this._colors = colors;
this._opacity = opacity;
this._stackedData = stackedInfo.stackedData;
this._data = data.renderData;
const legends: JSX.Element = this._getLegendData(points);

const tickParams = {
Expand Down Expand Up @@ -223,7 +224,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
isCalloutForStack
xAxisType={isXAxisDateType ? XAxisTypes.DateAxis : XAxisTypes.NumericAxis}
tickParams={tickParams}
maxOfYVal={stackedInfo.maxOfYVal}
maxOfYVal={data.maxOfYVal}
getGraphData={this._getGraphData}
getDomainNRangeValues={this._getDomainNRangeValues}
createStringYAxis={createStringYAxis}
Expand Down Expand Up @@ -434,25 +435,25 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
private _getStackedData = (keys: string[], dataSet: any) => {
const stackedValues = d3Stack().keys(keys)(dataSet);
const maxOfYVal = d3Max(stackedValues[stackedValues.length - 1], dp => dp[1])!;
const stackedData: Array<IAreaChartDataSetPoint[]> = [];
private _getData = (keys: string[], dataSet: any) => {
srmukher marked this conversation as resolved.
Show resolved Hide resolved
const dataValues = d3Stack().keys(keys)(dataSet);
srmukher marked this conversation as resolved.
Show resolved Hide resolved
const maxOfYVal = d3Max(dataValues[dataValues.length - 1], dp => dp[1])!;
const renderData: Array<IAreaChartDataSetPoint[]> = [];
srmukher marked this conversation as resolved.
Show resolved Hide resolved
// eslint-disable-next-line @typescript-eslint/no-explicit-any
stackedValues.forEach((layer: any) => {
const currentStack: IAreaChartDataSetPoint[] = [];
dataValues.forEach((layer: any) => {
const currentLayer: IAreaChartDataSetPoint[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
layer.forEach((d: any) => {
currentStack.push({
values: d,
currentLayer.push({
values: this.props.mode === AreaChartModes.toZeroY ? [0, d[1]] : d,
Copy link
Contributor

Choose a reason for hiding this comment

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

add a snapshot test for this change

Copy link
Contributor Author

Choose a reason for hiding this comment

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

added

xVal: d.data.xVal,
});
});
stackedData.push(currentStack);
renderData.push(currentLayer);
});
this._isMultiStackChart = stackedData && stackedData.length > 1 ? true : false;
this._isMultiStackChart = renderData && renderData.length > 1 ? true : false;
return {
stackedData,
renderData,
maxOfYVal,
};
};
Expand Down Expand Up @@ -504,14 +505,14 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
keys.push(keyVal);
}

// Stacked Info used to draw graph
const stackedInfo = this._getStackedData(keys, dataSet);
// Data used to draw graph
const data = this._getData(keys, dataSet);

return {
colors,
opacity,
keys,
stackedInfo,
data,
calloutPoints,
};
} else {
Expand Down Expand Up @@ -557,14 +558,14 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
keys.push(keyVal);
}

// Stacked Info used to draw graph
const stackedInfo = this._getStackedData(keys, dataSet);
// Data used to draw graph
const data = this._getData(keys, dataSet);

return {
colors,
opacity,
keys,
stackedInfo,
data,
calloutPoints,
};
}
Expand Down Expand Up @@ -721,7 +722,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
const graph: JSX.Element[] = [];
let lineColor: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._stackedData.forEach((singleStackedData: Array<any>, index: number) => {
this._data.forEach((singleStackedData: Array<any>, index: number) => {
graph.push(
<React.Fragment key={`${index}-graph-${this._uniqueIdForGraph}`}>
{this.props.enableGradient && (
Expand Down Expand Up @@ -786,7 +787,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt

const circleRadius = pointOptions && pointOptions.r ? Number(pointOptions.r) : 8;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._stackedData.forEach((singleStackedData: Array<any>, index: number) => {
this._data.forEach((singleStackedData: Array<any>, index: number) => {
if (points.length === index) {
return;
}
Expand All @@ -804,7 +805,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
} data points.`}
>
{singleStackedData.map((singlePoint: IDPointType, pointIndex: number) => {
const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`;
const circleId = `${this._circleId}_${index * this._data[0].length + pointIndex}`;
const xDataPoint = singlePoint.xVal instanceof Date ? singlePoint.xVal.getTime() : singlePoint.xVal;
lineColor = points[index]!.color!;
const legend = points[index]!.legend;
Expand Down Expand Up @@ -837,7 +838,7 @@ export class AreaChartBase extends React.Component<IAreaChartProps, IAreaChartSt
singleStackedData.forEach((singlePoint: IDPointType, pointIndex: number) => {
const xDataPoint = singlePoint.xVal instanceof Date ? singlePoint.xVal.getTime() : singlePoint.xVal;
if (this.state.nearestCircleToHighlight === xDataPoint) {
const circleId = `${this._circleId}_${index * this._stackedData[0].length + pointIndex}`;
const circleId = `${this._circleId}_${index * this._data[0].length + pointIndex}`;
lineColor = points[index]!.color!;
const legend = points[index]!.legend;
graph.push(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,22 @@ import {

export type { IChildProps, IRefArrayData, IBasestate, ILineChartDataPoint, ILineChartPoints, IMargins };

/**
* Area Chart modes
* {@docCategory AreaChart}
*/
export enum AreaChartModes {
/**
* This mode will fill the area from the line to the x-axis.
*/
toZeroY = 'tozeroy',

/**
* This mode will fill the area from the line to the next line.
*/
toNextY = 'tonexty',
}

/**
* Area Chart properties.
* {@docCategory AreaChart}
Expand Down Expand Up @@ -70,6 +86,11 @@ export interface IAreaChartProps extends ICartesianChartProps {
* The prop used to enable gradient fill color for the chart.
*/
enableGradient?: boolean;

/**
* The prop used to define the Y axis mode (tonexty or tozeroy)
*/
mode?: 'tozeroy' | 'tonexty';
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { ISankeyChartProps } from '../SankeyChart/index';
import { IVerticalStackedBarChartProps } from '../VerticalStackedBarChart/index';
import { IHorizontalBarChartWithAxisProps } from '../HorizontalBarChartWithAxis/index';
import { ILineChartProps } from '../LineChart/index';
import { IAreaChartProps } from '../AreaChart/index';
import { AreaChartModes, IAreaChartProps } from '../AreaChart/index';
import { IHeatMapChartProps } from '../HeatMapChart/index';
import { DataVizPalette, getNextColor } from '../../utilities/colors';
import { GaugeChartVariant, IGaugeChartProps, IGaugeChartSegment } from '../GaugeChart/index';
Expand Down Expand Up @@ -339,6 +339,7 @@ export const transformPlotlyJsonToScatterChartProps = (
isDarkTheme?: boolean,
): ILineChartProps | IAreaChartProps => {
const { data, layout } = jsonObj;
let mode: AreaChartModes = AreaChartModes.toNextY;

const chartData: ILineChartPoints[] = data.map((series: any, index: number) => {
const xValues = series.x;
Expand All @@ -347,6 +348,7 @@ export const transformPlotlyJsonToScatterChartProps = (
const isXNumber = isNumberArray(xValues);
const legend: string = series.name || `Series ${index + 1}`;
const lineColor = getColor(legend, colorMap, isDarkTheme);
mode = series.fill === 'tozeroy' ? AreaChartModes.toZeroY : AreaChartModes.toNextY;

return {
legend,
Expand All @@ -371,6 +373,7 @@ export const transformPlotlyJsonToScatterChartProps = (
supportNegativeData: true,
xAxisTitle,
yAxisTitle,
mode,
} as IAreaChartProps;
} else {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface IAreaChartBasicState {
isCalloutselected: boolean;
showAxisTitles: boolean;
legendMultiSelect: boolean;
changeChartMode: boolean;
}

const options: IChoiceGroupOption[] = [
Expand All @@ -26,6 +27,7 @@ export class AreaChartBasicExample extends React.Component<{}, IAreaChartBasicSt
isCalloutselected: false,
showAxisTitles: true,
legendMultiSelect: false,
changeChartMode: false,
};
}
public componentDidMount(): void {
Expand Down Expand Up @@ -75,6 +77,11 @@ export class AreaChartBasicExample extends React.Component<{}, IAreaChartBasicSt
this.setState({ legendMultiSelect: checked });
};

private _onToggleChartMode = (ev: React.MouseEvent<HTMLElement>, checked: boolean) => {
this.forceUpdate();
this.setState({ changeChartMode: checked });
};

private _basicExample(): JSX.Element {
const chart1Points = [
{
Expand Down Expand Up @@ -249,6 +256,14 @@ export class AreaChartBasicExample extends React.Component<{}, IAreaChartBasicSt
onChange={this._onToggleLegendMultiSelect}
styles={{ root: { marginTop: '10px' } }}
/>
<Toggle
label="Change chart mode to toZeroY"
onText="ON"
offText="OFF"
checked={this.state.changeChartMode}
onChange={this._onToggleChartMode}
styles={{ root: { marginTop: '10px' } }}
/>
{this.state.showAxisTitles && (
<div style={rootStyle}>
<AreaChart
Expand All @@ -274,6 +289,7 @@ export class AreaChartBasicExample extends React.Component<{}, IAreaChartBasicSt
legendProps={{
canSelectMultipleLegends: this.state.legendMultiSelect,
}}
mode={this.state.changeChartMode ? 'tozeroy' : 'tonexty'}
/>
</div>
)}
Expand All @@ -300,6 +316,7 @@ export class AreaChartBasicExample extends React.Component<{}, IAreaChartBasicSt
legendProps={{
canSelectMultipleLegends: this.state.legendMultiSelect,
}}
mode={this.state.changeChartMode ? 'tozeroy' : 'tonexty'}
/>
</div>
)}
Expand Down
Loading