Skip to content
Draft
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
47 changes: 47 additions & 0 deletions packages/charts/src/components/BarChart/BarChart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,53 @@ You can set a reference line to any value by using the `referenceLine` `chartCon

<Canvas of={ComponentStories.WithHighlightedMeasure} />

### With Stack Aggregate Totals

You can display a total label at the end of each stacked bar group by setting `chartConfig.showStackAggregateTotals` to `true`. The tooltip includes the total automatically when only a single bar per dimension is present.

<Canvas of={ComponentStories.WithStackAggregateTotalsAndTooltip} />

### With Custom Tooltip Total

When multiple bars per dimension are present (e.g. stacked + standalone), the built-in tooltip total is not available. You can provide a custom tooltip via the `tooltipConfig.content` prop to display a total for specific measures.

```jsx
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { DefaultTooltipContent } from 'recharts';

const stackedAccessors = new Set(['users', 'sessions']);

const CustomTooltipContent = (props) => {
const { payload, ...rest } = props;
if (!payload?.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const stackedEntries = payload.filter((entry) => stackedAccessors.has(entry.dataKey));
if (!stackedEntries.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const total = stackedEntries.reduce((sum, entry) => sum + (Number(entry.value) || 0), 0);
const augmentedPayload = [
...payload,
{
name: `Total (${stackedEntries.map((entry) => entry.name).join(' + ')})`,
value: total,
color: ThemingParameters.sapTextColor,
},
];
return <DefaultTooltipContent {...rest} payload={augmentedPayload} />;
};

<BarChart
{...props}
tooltipConfig={{
content: <CustomTooltipContent />
}}
/>
```

<Canvas of={ComponentStories.WithCustomTooltipTotal} />

<TooltipStory of={ComponentStories.WithCustomTooltipConfig} />

<LegendStory of={ComponentStories.WithCustomLegendConfig} />
Expand Down
74 changes: 74 additions & 0 deletions packages/charts/src/components/BarChart/BarChart.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { DefaultTooltipContent } from 'recharts';
import {
complexDataSet,
legendConfig,
Expand Down Expand Up @@ -165,6 +167,78 @@ export const WithNormalizedStacks: Story = {
args: stackedNormalizedConfig,
};

export const WithStackAggregateTotalsAndTooltip: Story = {
args: {
dataset: complexDataSet.slice(0, 3),
measures: [
{
accessor: 'users',
stackId: 'A',
label: 'Users',
},
{
accessor: 'sessions',
stackId: 'A',
label: 'Active Sessions',
},
],
chartConfig: {
showStackAggregateTotals: true,
},
},
};

const stackedAccessors = new Set(['users', 'sessions']);

const CustomTooltipContent = (props) => {
const { payload, ...rest } = props;
if (!payload?.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const stackedEntries = payload.filter((entry) => stackedAccessors.has(entry.dataKey));
if (!stackedEntries.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const total = stackedEntries.reduce((sum, entry) => sum + (Number(entry.value) || 0), 0);
const augmentedPayload = [
...payload,
{
name: `Total (${stackedEntries.map((entry) => entry.name).join(' + ')})`,
value: total,
color: ThemingParameters.sapTextColor,
},
];
return <DefaultTooltipContent {...rest} payload={augmentedPayload} />;
};

export const WithCustomTooltipTotal: Story = {
args: {
dataset: complexDataSet.slice(0, 5),
measures: [
{
accessor: 'users',
stackId: 'A',
label: 'Users',
},
{
accessor: 'sessions',
stackId: 'A',
label: 'Active Sessions',
},
{
accessor: 'volume',
label: 'Vol.',
},
],
chartConfig: {
showStackAggregateTotals: true,
},
tooltipConfig: {
content: <CustomTooltipContent />,
},
},
};

export const WithCustomTooltipConfig: Story = {
args: tooltipConfig,
};
Expand Down
28 changes: 27 additions & 1 deletion packages/charts/src/components/BarChart/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import type { IChartMeasure } from '../../interfaces/IChartMeasure.js';
import { ChartContainer } from '../../internal/ChartContainer.js';
import { ChartDataLabel } from '../../internal/ChartDataLabel.js';
import { defaultFormatter } from '../../internal/defaults.js';
import { StackAggregateLabel } from '../../internal/StackAggregateLabel.js';
import { StackedTooltipContent } from '../../internal/StackedTooltipContent.js';
import { brushProps, tickLineConfig, tooltipContentStyle, tooltipFillOpacity } from '../../internal/staticProps.js';
import { getCellColors, resolvePrimaryAndSecondaryMeasures } from '../../internal/Utils.js';
import { XAxisTicks } from '../../internal/XAxisTicks.js';
Expand Down Expand Up @@ -168,11 +170,12 @@ const BarChart = forwardRef<HTMLDivElement, BarChartProps>((props, ref) => {
};
const referenceLine = chartConfig.referenceLine;

const { dimensions, measures } = usePrepareDimensionsAndMeasures(
const { dimensions, measures, stackGroups, lastInStack } = usePrepareDimensionsAndMeasures(
props.dimensions,
props.measures,
dimensionDefaults,
measureDefaults,
chartConfig.showStackAggregateTotals,
);

const tooltipValueFormatter = useTooltipFormatter(measures);
Expand Down Expand Up @@ -224,6 +227,10 @@ const BarChart = forwardRef<HTMLDivElement, BarChartProps>((props, ref) => {

const { isMounted, handleBarAnimationStart, handleBarAnimationEnd } = useCancelAnimationFallback(noAnimation);

const stackGroupKeys = Object.keys(stackGroups);
const showStackTotalInTooltip =
chartConfig.showStackAggregateTotals && stackGroupKeys.length === 1 && measures.every((m) => m.stackId != null);

const { chartConfig: _0, dimensions: _1, measures: _2, ...propsWithoutOmitted } = rest;
return (
<ChartContainer
Expand Down Expand Up @@ -337,6 +344,17 @@ const BarChart = forwardRef<HTMLDivElement, BarChartProps>((props, ref) => {
valueAccessor={valueAccessor(element.accessor)}
content={<ChartDataLabel config={element} chartType="bar" position={'insideRight'} />}
/>
{chartConfig.showStackAggregateTotals &&
element.stackId &&
typeof element.accessor === 'string' &&
lastInStack.has(element.accessor) && (
<LabelList
data={dataset}
valueAccessor={valueAccessor(element.accessor)}
position="right"
content={<StackAggregateLabel stackAccessors={stackGroups[element.stackId]} dataset={dataset} />}
/>
)}
{dataset.map((data, i) => {
return (
<Cell
Expand Down Expand Up @@ -374,6 +392,14 @@ const BarChart = forwardRef<HTMLDivElement, BarChartProps>((props, ref) => {
contentStyle={tooltipContentStyle}
labelFormatter={tooltipLabelFormatter}
{...tooltipConfig}
{...(showStackTotalInTooltip && {
content: (
<StackedTooltipContent
stackAccessors={stackGroups[stackGroupKeys[0]]}
totalFormatter={chartConfig.stackAggregateTotalFormatter}
/>
),
})}
/>
)}
{!!chartConfig.zoomingTool && (
Expand Down
47 changes: 47 additions & 0 deletions packages/charts/src/components/ColumnChart/ColumnChart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,53 @@ You can set a reference line to any value by using the `referenceLine` `chartCon

<Canvas of={ComponentStories.WithHighlightedMeasure} />

### With Stack Aggregate Totals

You can display a total label at the top of each stacked column group by setting `chartConfig.showStackAggregateTotals` to `true`. The tooltip includes the total automatically when only a single column per dimension is present.

<Canvas of={ComponentStories.WithStackAggregateTotals} />

### With Custom Tooltip Total

When multiple columns per dimension are present (e.g. stacked + standalone), the built-in tooltip total is not available. You can provide a custom tooltip via the `tooltipConfig.content` prop to display a total for specific measures.

```jsx
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { DefaultTooltipContent } from 'recharts';

const stackedAccessors = new Set(['users', 'sessions']);

const CustomTooltipContent = (props) => {
const { payload, ...rest } = props;
if (!payload?.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const stackedEntries = payload.filter((entry) => stackedAccessors.has(entry.dataKey));
if (!stackedEntries.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const total = stackedEntries.reduce((sum, entry) => sum + (Number(entry.value) || 0), 0);
const augmentedPayload = [
...payload,
{
name: `Total (${stackedEntries.map((entry) => entry.name).join(' + ')})`,
value: total,
color: ThemingParameters.sapTextColor,
},
];
return <DefaultTooltipContent {...rest} payload={augmentedPayload} />;
};

<ColumnChart
{...props}
tooltipConfig={{
content: <CustomTooltipContent />
}}
/>
```

<Canvas of={ComponentStories.WithCustomTooltipTotal} />

<TooltipStory of={ComponentStories.WithCustomTooltipConfig} />;

<LegendStory of={ComponentStories.WithCustomLegendConfig} />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { ThemingParameters } from '@ui5/webcomponents-react-base';
import { DefaultTooltipContent } from 'recharts';
import {
complexDataSet,
legendConfig,
Expand Down Expand Up @@ -156,6 +158,80 @@ export const WithHighlightedMeasure: Story = {
},
};

export const WithStackAggregateTotals: Story = {
args: {
dataset: complexDataSet.slice(0, 3),
dimensions: [{ accessor: 'name' }],
measures: [
{
accessor: 'users',
stackId: 'A',
label: 'Users',
},
{
accessor: 'sessions',
stackId: 'A',
label: 'Active Sessions',
},
],
chartConfig: {
showStackAggregateTotals: true,
},
},
};

const stackedAccessors = new Set(['users', 'sessions']);

const CustomTooltipContent = (props) => {
const { payload, ...rest } = props;
if (!payload?.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const stackedEntries = payload.filter((entry) => stackedAccessors.has(entry.dataKey));
if (!stackedEntries.length) {
return <DefaultTooltipContent {...rest} payload={payload} />;
}
const total = stackedEntries.reduce((sum, entry) => sum + (Number(entry.value) || 0), 0);
const augmentedPayload = [
...payload,
{
name: `Total (${stackedEntries.map((entry) => entry.name).join(' + ')})`,
value: total,
color: ThemingParameters.sapTextColor,
},
];
return <DefaultTooltipContent {...rest} payload={augmentedPayload} />;
};

export const WithCustomTooltipTotal: Story = {
args: {
dataset: complexDataSet.slice(0, 5),
dimensions: [{ accessor: 'name' }],
measures: [
{
accessor: 'users',
stackId: 'A',
label: 'Users',
},
{
accessor: 'sessions',
stackId: 'A',
label: 'Active Sessions',
},
{
accessor: 'volume',
label: 'Vol.',
},
],
chartConfig: {
showStackAggregateTotals: true,
},
tooltipConfig: {
content: <CustomTooltipContent />,
},
},
};

export const WithCustomTooltipConfig: Story = {
args: tooltipConfig,
};
Expand Down
Loading
Loading