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 6 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 @@
{
srmukher marked this conversation as resolved.
Show resolved Hide resolved
"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) => {
const dataValues = d3Stack().keys(keys)(dataSet);
const maxOfYVal = d3Max(dataValues[dataValues.length - 1], dp => dp[1])!;
const renderData: Array<IAreaChartDataSetPoint[]> = [];
// 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,
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