From d6c32c06ac76e2ef116ba2cbd7d26b1bf4e1881e Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Tue, 3 Dec 2024 12:30:56 +0100 Subject: [PATCH 1/2] fix(Pie- & DonutChart): improve `activeSegment` handling & fix focus behavior --- .../src/components/DonutChart/DonutChart.mdx | 2 +- .../DonutChart/DonutChart.stories.tsx | 34 +++++++++++++----- .../src/components/DonutChart/DonutChart.tsx | 2 +- .../components/PieChart/PieChart.module.css | 7 ++++ .../components/PieChart/PieChart.stories.tsx | 36 +++++++++++++++++++ .../src/components/PieChart/PieChart.tsx | 30 +++++++++++----- 6 files changed, 92 insertions(+), 19 deletions(-) diff --git a/packages/charts/src/components/DonutChart/DonutChart.mdx b/packages/charts/src/components/DonutChart/DonutChart.mdx index 263efa8218d..a2db4b3c14b 100644 --- a/packages/charts/src/components/DonutChart/DonutChart.mdx +++ b/packages/charts/src/components/DonutChart/DonutChart.mdx @@ -43,7 +43,7 @@ import LegendStory from '../../resources/LegendConfig.mdx'; ### With highlighted active segment - + ### Hide labels diff --git a/packages/charts/src/components/DonutChart/DonutChart.stories.tsx b/packages/charts/src/components/DonutChart/DonutChart.stories.tsx index 8fb755775a9..57fb982de9c 100644 --- a/packages/charts/src/components/DonutChart/DonutChart.stories.tsx +++ b/packages/charts/src/components/DonutChart/DonutChart.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { useEffect, useState } from 'react'; import { legendConfig, simpleDataSet, simpleDataSetWithSmallValues, tooltipConfig } from '../../resources/DemoProps.js'; import { DonutChart } from './DonutChart.js'; @@ -73,15 +74,6 @@ export const WithFormatter: Story = { } }; -export const WithHighlightedActiveSegment: Story = { - args: { - chartConfig: { - activeSegment: 9, - showActiveSegmentDataLabel: true - } - } -}; - export const HideLabels: Story = { args: { measure: { @@ -103,3 +95,27 @@ export const WithCustomTooltipConfig: Story = { export const WithCustomLegendConfig: Story = { args: legendConfig }; + +export const WithActiveShape: Story = { + args: { + chartConfig: { + activeSegment: 1, + showActiveSegmentDataLabel: true + } + }, + render(args) { + const [activeSegment, setActiveSegment] = useState(args.chartConfig.activeSegment); + const handleChartClick = (e) => { + const { dataIndex } = e.detail; + if (dataIndex != null) { + setActiveSegment(dataIndex); + } + }; + + useEffect(() => { + setActiveSegment(args.chartConfig.activeSegment); + }, [args.chartConfig.activeSegment]); + + return ; + } +}; diff --git a/packages/charts/src/components/DonutChart/DonutChart.tsx b/packages/charts/src/components/DonutChart/DonutChart.tsx index a68d13bd8b7..f57b0e4f7bc 100644 --- a/packages/charts/src/components/DonutChart/DonutChart.tsx +++ b/packages/charts/src/components/DonutChart/DonutChart.tsx @@ -17,7 +17,7 @@ const DonutChart = forwardRef((props, ref) => { ...props.chartConfig }; - return ; + return ; }); DonutChart.displayName = 'DonutChart'; diff --git a/packages/charts/src/components/PieChart/PieChart.module.css b/packages/charts/src/components/PieChart/PieChart.module.css index 0cdf5dffd31..5493c9e7a11 100644 --- a/packages/charts/src/components/PieChart/PieChart.module.css +++ b/packages/charts/src/components/PieChart/PieChart.module.css @@ -4,3 +4,10 @@ outline: none; } } + +[data-active-legend] { + background: color-mix(in srgb, var(--sapSelectedColor), transparent 87%); + :global(.recharts-legend-item-text) { + color: var(--sapTextColor) !important; + } +} diff --git a/packages/charts/src/components/PieChart/PieChart.stories.tsx b/packages/charts/src/components/PieChart/PieChart.stories.tsx index 8080b15a32a..f01e01b0242 100644 --- a/packages/charts/src/components/PieChart/PieChart.stories.tsx +++ b/packages/charts/src/components/PieChart/PieChart.stories.tsx @@ -1,4 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; +import { useEffect, useState } from 'react'; import { legendConfig, simpleDataSet, simpleDataSetWithSmallValues, tooltipConfig } from '../../resources/DemoProps.js'; import { PieChart } from './PieChart.js'; @@ -51,6 +52,41 @@ export const WithFormatter: Story = { activeSegment: 1, showActiveSegmentDataLabel: true } + }, + render(args) { + const [activeSegment, setActiveSegment] = useState(1); + const handleChartClick = (e) => { + const { dataIndex } = e.detail; + if (dataIndex != null) { + setActiveSegment(dataIndex); + } + }; + + return ; + } +}; + +export const WithActiveShape: Story = { + args: { + chartConfig: { + activeSegment: 1, + showActiveSegmentDataLabel: true + } + }, + render(args) { + const [activeSegment, setActiveSegment] = useState(args.chartConfig.activeSegment); + const handleChartClick = (e) => { + const { dataIndex } = e.detail; + if (dataIndex != null) { + setActiveSegment(dataIndex); + } + }; + + useEffect(() => { + setActiveSegment(args.chartConfig.activeSegment); + }, [args.chartConfig.activeSegment]); + + return ; } }; diff --git a/packages/charts/src/components/PieChart/PieChart.tsx b/packages/charts/src/components/PieChart/PieChart.tsx index 7ad052a0a0c..4ad11114100 100644 --- a/packages/charts/src/components/PieChart/PieChart.tsx +++ b/packages/charts/src/components/PieChart/PieChart.tsx @@ -1,6 +1,6 @@ 'use client'; -import { enrichEventWithDetails, useStylesheet } from '@ui5/webcomponents-react-base'; +import { enrichEventWithDetails, useStylesheet, useSyncRef } from '@ui5/webcomponents-react-base'; import { clsx } from 'clsx'; import type { CSSProperties } from 'react'; import { cloneElement, forwardRef, isValidElement, useCallback, useMemo } from 'react'; @@ -109,6 +109,8 @@ const PieChart = forwardRef((props, ref) => { } = props; useStylesheet(styleData, PieChart.displayName); + const [componentRef, chartRef] = useSyncRef(ref); + const isDonutChart = props['data-component-name'] === 'DonutChart'; const chartConfig: PieChartProps['chartConfig'] = { margin: { right: 30, left: 30, bottom: 30, top: 30, ...(props.chartConfig?.margin ?? {}) }, @@ -193,12 +195,23 @@ const PieChart = forwardRef((props, ref) => { const ex = mx + (cos >= 0 ? 1 : -1) * 22; const ey = my; const textAnchor = cos >= 0 ? 'start' : 'end'; + const activeLegendItem = chartRef.current?.querySelector( + `.legend-item-${chartConfig.activeSegment}` + ); + if (!activeLegendItem?.dataset.activeLegend) { + const allLegendItems = chartRef.current?.querySelectorAll('.recharts-legend-item'); + + allLegendItems.forEach((item) => item.removeAttribute('data-active-legend')); + activeLegendItem.setAttribute('data-active-legend', 'true'); + } return ( - - {payload.name} - + {isDonutChart && ( + + {payload.name} + + )} ((props, ref) => { ); }, - [showActiveSegmentDataLabel] + [showActiveSegmentDataLabel, chartConfig.activeSegment, isDonutChart] ); const renderLabelLine = useCallback( @@ -249,11 +262,11 @@ const PieChart = forwardRef((props, ref) => { if (chartConfig.activeSegment != null && showActiveSegmentDataLabel) { if (chartConfig.legendPosition === 'bottom') { return { - paddingTop: '30px' + paddingBlockStart: '30px' }; } else if (chartConfig.legendPosition === 'top') { return { - paddingBottom: '30px' + paddingBlockEnd: '30px' }; } } @@ -266,7 +279,7 @@ const PieChart = forwardRef((props, ref) => { return ( ((props, ref) => { label={dataLabel} activeIndex={chartConfig.activeSegment} activeShape={chartConfig.activeSegment != null && renderActiveShape} + rootTabIndex={-1} > {centerLabel && {centerLabel}} {dataset && From 6540872ec59344422aa24717c484dcd7e67c9811 Mon Sep 17 00:00:00 2001 From: Lukas Harbarth Date: Tue, 3 Dec 2024 13:24:19 +0100 Subject: [PATCH 2/2] Update PieChart.module.css --- .../charts/src/components/PieChart/PieChart.module.css | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/charts/src/components/PieChart/PieChart.module.css b/packages/charts/src/components/PieChart/PieChart.module.css index 5493c9e7a11..1c18aa43261 100644 --- a/packages/charts/src/components/PieChart/PieChart.module.css +++ b/packages/charts/src/components/PieChart/PieChart.module.css @@ -3,11 +3,11 @@ path:focus { outline: none; } -} -[data-active-legend] { - background: color-mix(in srgb, var(--sapSelectedColor), transparent 87%); - :global(.recharts-legend-item-text) { - color: var(--sapTextColor) !important; + [data-active-legend] { + background: color-mix(in srgb, var(--sapSelectedColor), transparent 87%); + :global(.recharts-legend-item-text) { + color: var(--sapTextColor) !important; + } } }