diff --git a/docs/data/charts/areas-demo/StackedAreaChart.js b/docs/data/charts/areas-demo/StackedAreaChart.js index 47ce2b6cf8b1..799a62195199 100644 --- a/docs/data/charts/areas-demo/StackedAreaChart.js +++ b/docs/data/charts/areas-demo/StackedAreaChart.js @@ -3,7 +3,7 @@ import { LineChart } from '@mui/x-charts/LineChart'; const uData = [4000, 3000, 2000, 2780, 1890, 2390, 3490]; const pData = [2400, 1398, 9800, 3908, 4800, 3800, 4300]; -const amtData = [2400, 2210, 2290, 2000, 2181, 2500, 2100]; +const amtData = [2400, 2210, 0, 2000, 2181, 2500, 2100]; const xLabels = [ 'Page A', 'Page B', diff --git a/docs/data/charts/areas-demo/StackedAreaChart.tsx b/docs/data/charts/areas-demo/StackedAreaChart.tsx index 47ce2b6cf8b1..799a62195199 100644 --- a/docs/data/charts/areas-demo/StackedAreaChart.tsx +++ b/docs/data/charts/areas-demo/StackedAreaChart.tsx @@ -3,7 +3,7 @@ import { LineChart } from '@mui/x-charts/LineChart'; const uData = [4000, 3000, 2000, 2780, 1890, 2390, 3490]; const pData = [2400, 1398, 9800, 3908, 4800, 3800, 4300]; -const amtData = [2400, 2210, 2290, 2000, 2181, 2500, 2100]; +const amtData = [2400, 2210, 0, 2000, 2181, 2500, 2100]; const xLabels = [ 'Page A', 'Page B', diff --git a/docs/data/charts/stacking/stacking.md b/docs/data/charts/stacking/stacking.md index 75e7c97bc9fe..4e2b86eb6e0d 100644 --- a/docs/data/charts/stacking/stacking.md +++ b/docs/data/charts/stacking/stacking.md @@ -18,7 +18,7 @@ Series with the same `stack` value will get stacked together. Based on D3 [stack orders](https://github.com/d3/d3-shape#stack-orders) and [stack offsets](https://github.com/d3/d3-shape#stack-offsets) you can modify how series are stacked. -To pass those attributes, use series properties `stackOffset` (default `'diverging'`) and `stackOrder` (default `'none'`). +To pass those attributes, use series properties `stackOffset` (default `'diverging'` for bar and `'none'` for line) and `stackOrder` (default `'none'`). You can define them for only one of the series of a stack group. ### Stack offset diff --git a/packages/x-charts/src/LineChart/formatter.ts b/packages/x-charts/src/LineChart/formatter.ts index 371ce7374098..32b88c37091a 100644 --- a/packages/x-charts/src/LineChart/formatter.ts +++ b/packages/x-charts/src/LineChart/formatter.ts @@ -14,7 +14,7 @@ let warnedOnce = false; // For now it's a copy past of bar charts formatter, but maybe will diverge later const formatter: Formatter<'line'> = (params, dataset) => { const { seriesOrder, series } = params; - const stackingGroups = getStackingGroups(params); + const stackingGroups = getStackingGroups({ ...params, defaultStrategy: { stackOffset: 'none' } }); // Create a data set with format adapted to d3 const d3Dataset: DatasetType = (dataset as DatasetType) ?? []; diff --git a/packages/x-charts/src/internals/stackSeries.ts b/packages/x-charts/src/internals/stackSeries.ts index 882c14ae979e..367a77051460 100644 --- a/packages/x-charts/src/internals/stackSeries.ts +++ b/packages/x-charts/src/internals/stackSeries.ts @@ -12,11 +12,19 @@ import { stackOffsetWiggle as d3StackOffsetWiggle, Series, } from 'd3-shape'; -import { BarSeriesType, LineSeriesType } from '../models/seriesType'; +import type { BarSeriesType, LineSeriesType } from '../models/seriesType'; +import type { StackOffsetType, StackOrderType } from '../models/stacking'; type StackableSeries = { [id: string]: BarSeriesType } | { [id: string]: LineSeriesType }; -type FormatterParams = { series: StackableSeries; seriesOrder: string[] }; +type FormatterParams = { + series: StackableSeries; + seriesOrder: string[]; + defaultStrategy?: { + stackOrder?: StackOrderType; + stackOffset?: StackOffsetType; + }; +}; export type StackingGroupsType = { ids: string[]; @@ -25,9 +33,7 @@ export type StackingGroupsType = { }[]; export const StackOrder: { - [key in 'appearance' | 'ascending' | 'descending' | 'insideOut' | 'none' | 'reverse']: ( - series: Series, - ) => number[]; + [key in StackOrderType]: (series: Series) => number[]; } = { /** * Series order such that the earliest series (according to the maximum value) is at the bottom. @@ -56,10 +62,7 @@ export const StackOrder: { }; export const StackOffset: { - [key in 'expand' | 'diverging' | 'none' | 'silhouette' | 'wiggle']: ( - series: Series, - order: Iterable, - ) => void; + [key in StackOffsetType]: (series: Series, order: Iterable) => void; } = { /** * Applies a zero baseline and normalizes the values for each point such that the topline is always one. @@ -89,7 +92,7 @@ export const StackOffset: { * @returns an array of groups, including the ids, the stacking order, and the stacking offset. */ export const getStackingGroups = (params: FormatterParams) => { - const { series, seriesOrder } = params; + const { series, seriesOrder, defaultStrategy } = params; const stackingGroups: StackingGroupsType = []; const stackIndex: { [id: string]: number } = {}; @@ -107,8 +110,8 @@ export const getStackingGroups = (params: FormatterParams) => { stackIndex[stack] = stackingGroups.length; stackingGroups.push({ ids: [id], - stackingOrder: StackOrder[stackOrder ?? 'none'], - stackingOffset: StackOffset[stackOffset ?? 'diverging'], + stackingOrder: StackOrder[stackOrder ?? defaultStrategy?.stackOrder ?? 'none'], + stackingOffset: StackOffset[stackOffset ?? defaultStrategy?.stackOffset ?? 'diverging'], }); } else { stackingGroups[stackIndex[stack]].ids.push(id); diff --git a/packages/x-charts/src/models/index.ts b/packages/x-charts/src/models/index.ts index 0a813c9dd4fe..9dd7d43710d0 100644 --- a/packages/x-charts/src/models/index.ts +++ b/packages/x-charts/src/models/index.ts @@ -1,5 +1,6 @@ export * from './seriesType'; export * from './layout'; +export * from './stacking'; export type { AxisConfig, ChartsYAxisProps, diff --git a/packages/x-charts/src/models/seriesType/bar.ts b/packages/x-charts/src/models/seriesType/bar.ts index d205852f4a96..2d5368104c49 100644 --- a/packages/x-charts/src/models/seriesType/bar.ts +++ b/packages/x-charts/src/models/seriesType/bar.ts @@ -1,4 +1,5 @@ import { DefaultizedProps } from '../helpers'; +import type { StackOffsetType } from '../stacking'; import { CartesianSeriesType, CommonSeriesType, @@ -25,6 +26,11 @@ export interface BarSeriesType * @default 'vertical' */ layout?: 'horizontal' | 'vertical'; + /** + * Defines how stacked series handle negative values. + * @default 'diverging' + */ + stackOffset?: StackOffsetType; } /** diff --git a/packages/x-charts/src/models/seriesType/common.ts b/packages/x-charts/src/models/seriesType/common.ts index 03cad304dbed..85ed67b26b9b 100644 --- a/packages/x-charts/src/models/seriesType/common.ts +++ b/packages/x-charts/src/models/seriesType/common.ts @@ -1,5 +1,5 @@ import type { HighlightScope } from '../../context/HighlightProvider'; -import type { StackOffset, StackOrder } from '../../internals/stackSeries'; +import type { StackOffsetType, StackOrderType } from '../stacking'; export type CommonSeriesType = { id?: string; @@ -38,11 +38,9 @@ export type StackableSeriesType = { stackOffset?: StackOffsetType; /** * The order in which series' of the same group are stacked together. + * @default 'none' */ stackOrder?: StackOrderType; }; -export type StackOrderType = keyof typeof StackOrder; -export type StackOffsetType = keyof typeof StackOffset; - export type DefaultizedCartesianSeriesType = Required; diff --git a/packages/x-charts/src/models/seriesType/index.ts b/packages/x-charts/src/models/seriesType/index.ts index a4e630768c81..fc960e50f729 100644 --- a/packages/x-charts/src/models/seriesType/index.ts +++ b/packages/x-charts/src/models/seriesType/index.ts @@ -31,7 +31,6 @@ export * from './line'; export * from './bar'; export * from './scatter'; export * from './pie'; -export type { StackOrderType, StackOffsetType } from './common'; export type { AllSeriesType, CartesianSeriesType, diff --git a/packages/x-charts/src/models/seriesType/line.ts b/packages/x-charts/src/models/seriesType/line.ts index b3e04133314c..f8b9f825918f 100644 --- a/packages/x-charts/src/models/seriesType/line.ts +++ b/packages/x-charts/src/models/seriesType/line.ts @@ -1,4 +1,5 @@ import { DefaultizedProps } from '../helpers'; +import type { StackOffsetType } from '../stacking'; import { CartesianSeriesType, CommonDefaultizedProps, @@ -72,6 +73,11 @@ export interface LineSeriesType * @default false */ connectNulls?: boolean; + /** + * Defines how stacked series handle negative values. + * @default 'none' + */ + stackOffset?: StackOffsetType; } /** diff --git a/packages/x-charts/src/models/stacking.ts b/packages/x-charts/src/models/stacking.ts new file mode 100644 index 000000000000..217a11fc15b2 --- /dev/null +++ b/packages/x-charts/src/models/stacking.ts @@ -0,0 +1,9 @@ +export type StackOrderType = + | 'appearance' + | 'ascending' + | 'descending' + | 'insideOut' + | 'none' + | 'reverse'; + +export type StackOffsetType = 'expand' | 'diverging' | 'none' | 'silhouette' | 'wiggle';