diff --git a/packages/charts/package.json b/packages/charts/package.json index e84057dd73f..f6794d99d84 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -36,7 +36,7 @@ "dependencies": { "clsx": "2.1.1", "react-content-loader": "7.1.1", - "recharts": "2.15.4" + "recharts": "3.1.0" }, "peerDependencies": { "@ui5/webcomponents-react": "~2.12.0", diff --git a/packages/charts/src/components/BarChart/BarChart.stories.tsx b/packages/charts/src/components/BarChart/BarChart.stories.tsx index 303615df163..6fb4209984b 100644 --- a/packages/charts/src/components/BarChart/BarChart.stories.tsx +++ b/packages/charts/src/components/BarChart/BarChart.stories.tsx @@ -86,7 +86,18 @@ export const WithDataLabels: Story = { export const WithFormatter: Story = { args: { - dimensions: [{ accessor: 'name', formatter: (element) => element.slice(0, 3) }], + dimensions: [ + { + accessor: 'name', + formatter: (element) => { + //todo: remove once issue has been fixed (should never be number in this case) + if (typeof element === 'string') { + return element.slice(0, 3); + } + return element; + }, + }, + ], measures: [ { accessor: 'users', diff --git a/packages/charts/src/components/BarChart/BarChart.tsx b/packages/charts/src/components/BarChart/BarChart.tsx index 9d7a1bc9bbd..6a06643add4 100644 --- a/packages/charts/src/components/BarChart/BarChart.tsx +++ b/packages/charts/src/components/BarChart/BarChart.tsx @@ -2,7 +2,7 @@ import { enrichEventWithDetails, ThemingParameters, useIsRTL, useSyncRef } from '@ui5/webcomponents-react-base'; import type { CSSProperties } from 'react'; -import { forwardRef, useCallback } from 'react'; +import { useRef, forwardRef, useCallback } from 'react'; import { Bar, BarChart as BarChartLib, @@ -17,7 +17,6 @@ import { YAxis, } from 'recharts'; import type { YAxisProps } from 'recharts'; -import { getValueByDataKey } from 'recharts/lib/util/ChartUtils.js'; import { useCancelAnimationFallback } from '../../hooks/useCancelAnimationFallback.js'; import { useChartMargin } from '../../hooks/useChartMargin.js'; import { useLabelFormatter } from '../../hooks/useLabelFormatter.js'; @@ -27,7 +26,7 @@ import { useObserveXAxisHeights } from '../../hooks/useObserveXAxisHeights.js'; import { useOnClickInternal } from '../../hooks/useOnClickInternal.js'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures.js'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter.js'; -import type { IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; +import type { ActivePayload, IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; import type { IChartDimension } from '../../interfaces/IChartDimension.js'; import type { IChartMeasure } from '../../interfaces/IChartMeasure.js'; import { ChartContainer } from '../../internal/ChartContainer.js'; @@ -48,12 +47,6 @@ const measureDefaults = { opacity: 1, }; -const valueAccessor = - (attribute) => - ({ payload }) => { - return getValueByDataKey(payload, attribute); - }; - interface MeasureConfig extends IChartMeasure { /** * Bar Width @@ -165,7 +158,6 @@ const BarChart = forwardRef((props, ref) => { ...props.chartConfig, }; const referenceLine = chartConfig.referenceLine; - const { dimensions, measures } = usePrepareDimensionsAndMeasures( props.dimensions, props.measures, @@ -187,7 +179,7 @@ const BarChart = forwardRef((props, ref) => { : 0; const [componentRef, chartRef] = useSyncRef(ref); - + const activePayloadsRef = useRef(measures); const onItemLegendClick = useLegendItemClick(onLegendClick); const labelFormatter = useLabelFormatter(primaryDimension); @@ -210,7 +202,7 @@ const BarChart = forwardRef((props, ref) => { [onDataPointClick], ); - const onClickInternal = useOnClickInternal(onClick); + const onClickInternal = useOnClickInternal(onClick, dataset, activePayloadsRef); const isBigDataSet = dataset?.length > 30; const primaryDimensionAccessor = primaryDimension?.accessor; @@ -284,8 +276,6 @@ const BarChart = forwardRef((props, ref) => { tickLine={{ stroke: chartConfig.secondYAxis.color ?? `var(--sapChart_OrderedColor_${(colorSecondY % 12) + 1})`, }} - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore label={{ value: chartConfig.secondYAxis.name, offset: 2, angle: +90, position: 'center' }} orientation="top" interval={0} @@ -316,28 +306,36 @@ const BarChart = forwardRef((props, ref) => { })} {isMounted && measures.map((element, index) => { + const color = element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`; + const dataKey = element.accessor; + const name = element.label ?? element.accessor; + const opacity = element.opacity ?? 1; + activePayloadsRef.current[index].color = color; + activePayloadsRef.current[index].stroke = color; + activePayloadsRef.current[index].dataKey = dataKey; + activePayloadsRef.current[index].hide = element.hide; + activePayloadsRef.current[index].name = name; + activePayloadsRef.current[index].fillOpacity = opacity; + activePayloadsRef.current[index].strokeOpacity = opacity; return ( } /> {dataset.map((data, i) => { @@ -353,8 +351,6 @@ const BarChart = forwardRef((props, ref) => { ); })} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore ((props, ref) => { label={referenceLine?.label} /> )} - {/*ToDo: remove conditional rendering once `active` is working again (https://github.com/recharts/recharts/issues/2703)*/} {tooltipConfig?.active !== false && ( element.slice(0, 3) }], + dimensions: [ + { + accessor: 'name', + formatter: (element) => { + //todo: remove once issue has been fixed (should never be number in this case) + if (typeof element === 'string') { + return element.slice(0, 3); + } + return element; + }, + }, + ], measures: [ { accessor: 'users', diff --git a/packages/charts/src/components/BulletChart/BulletChart.tsx b/packages/charts/src/components/BulletChart/BulletChart.tsx index 4d9b6c22d7d..04b32018041 100644 --- a/packages/charts/src/components/BulletChart/BulletChart.tsx +++ b/packages/charts/src/components/BulletChart/BulletChart.tsx @@ -2,7 +2,7 @@ import { enrichEventWithDetails, ThemingParameters, useIsRTL, useSyncRef } from '@ui5/webcomponents-react-base'; import type { CSSProperties } from 'react'; -import { forwardRef, useCallback, useMemo } from 'react'; +import { useRef, forwardRef, useCallback, useMemo } from 'react'; import { Bar, Brush, @@ -24,7 +24,7 @@ import { useObserveXAxisHeights } from '../../hooks/useObserveXAxisHeights.js'; import { useOnClickInternal } from '../../hooks/useOnClickInternal.js'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures.js'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter.js'; -import type { IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; +import type { ActivePayload, IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; import type { IChartDimension } from '../../interfaces/IChartDimension.js'; import type { IChartMeasure } from '../../interfaces/IChartMeasure.js'; import { ChartContainer } from '../../internal/ChartContainer.js'; @@ -172,6 +172,7 @@ const BulletChart = forwardRef((props, ref) => dimensionDefaults, measureDefaults, ); + const activePayloadsRef = useRef(measures); const sortedMeasures = useMemo(() => { return measures.sort((measure) => { @@ -239,7 +240,8 @@ const BulletChart = forwardRef((props, ref) => ); const onItemLegendClick = useLegendItemClick(onLegendClick); - const onClickInternal = useOnClickInternal(onClick); + //todo: implement activePayloadsRef + const onClickInternal = useOnClickInternal(onClick, dataset, activePayloadsRef); const isBigDataSet = dataset?.length > 30; const primaryDimensionAccessor = primaryDimension?.accessor; @@ -431,8 +433,6 @@ const BulletChart = forwardRef((props, ref) => /> )} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - ({ payload }) => { - return getValueByDataKey(payload, attribute); - }; - /** * A `ColumnChart` is a data visualization where each category is represented by a rectangle, with the height of the rectangle being proportional to the values being plotted. */ @@ -182,6 +175,7 @@ const ColumnChart = forwardRef((props, ref) => const labelFormatter = useLabelFormatter(primaryDimension); const [componentRef, chartRef] = useSyncRef(ref); + const activePayloadsRef = useRef(measures); const dataKeys = measures.map(({ accessor }) => accessor); const colorSecondY = chartConfig.secondYAxis @@ -210,7 +204,7 @@ const ColumnChart = forwardRef((props, ref) => [onDataPointClick], ); - const onClickInternal = useOnClickInternal(onClick); + const onClickInternal = useOnClickInternal(onClick, dataset, activePayloadsRef); const isBigDataSet = dataset?.length > 30; const primaryDimensionAccessor = primaryDimension?.accessor; @@ -308,29 +302,38 @@ const ColumnChart = forwardRef((props, ref) => )} {isMounted && measures.map((element, index) => { + const color = element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`; + const dataKey = element.accessor; + const name = element.label ?? element.accessor; + const opacity = element.opacity ?? 1; + activePayloadsRef.current[index].color = color; + activePayloadsRef.current[index].stroke = color; + activePayloadsRef.current[index].dataKey = dataKey; + activePayloadsRef.current[index].hide = element.hide; + activePayloadsRef.current[index].name = name; + activePayloadsRef.current[index].fillOpacity = opacity; + activePayloadsRef.current[index].strokeOpacity = opacity; return ( } /> {dataset.map((data, i) => { @@ -346,8 +349,6 @@ const ColumnChart = forwardRef((props, ref) => ); })} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore { measures={measures} onClick={onClick} onLegendClick={onLegendClick} + noAnimation />, ); cy.findByText('January').click(); cy.get('@onClick').should('have.been.called'); - cy.get('[name="January"]').eq(0).click(); + cy.get('[name="January"]').eq(0).realClick({ position: 'topLeft' }); cy.get('@onClick') .should('have.been.calledTwice') .and( @@ -68,6 +69,7 @@ describe('ComposedChart', () => { }), ); + //todo: onLegendClick is never fired, check why cy.contains('Users').click(); cy.get('@onLegendClick').should( 'have.been.calledWith', diff --git a/packages/charts/src/components/ComposedChart/index.tsx b/packages/charts/src/components/ComposedChart/index.tsx index eeba95b6a9d..566095b0843 100644 --- a/packages/charts/src/components/ComposedChart/index.tsx +++ b/packages/charts/src/components/ComposedChart/index.tsx @@ -2,7 +2,7 @@ import { enrichEventWithDetails, ThemingParameters, useIsRTL, useSyncRef } from '@ui5/webcomponents-react-base'; import type { CSSProperties, FC } from 'react'; -import { forwardRef, useCallback } from 'react'; +import { useRef, forwardRef, useCallback } from 'react'; import { Area, Bar, @@ -19,7 +19,6 @@ import { YAxis, } from 'recharts'; import type { YAxisProps } from 'recharts'; -import { getValueByDataKey } from 'recharts/lib/util/ChartUtils.js'; import { useChartMargin } from '../../hooks/useChartMargin.js'; import { useLabelFormatter } from '../../hooks/useLabelFormatter.js'; import { useLegendItemClick } from '../../hooks/useLegendItemClick.js'; @@ -28,7 +27,7 @@ import { useObserveXAxisHeights } from '../../hooks/useObserveXAxisHeights.js'; import { useOnClickInternal } from '../../hooks/useOnClickInternal.js'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures.js'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter.js'; -import type { IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; +import type { ActivePayload, IChartBaseProps } from '../../interfaces/IChartBaseProps.js'; import type { IChartDimension } from '../../interfaces/IChartDimension.js'; import type { IChartMeasure } from '../../interfaces/IChartMeasure.js'; import { ChartContainer } from '../../internal/ChartContainer.js'; @@ -189,6 +188,7 @@ const ComposedChart = forwardRef((props, ref measureDefaults, ); + const activePayloadsRef = useRef(measures); const tooltipValueFormatter = useTooltipFormatter(measures); const primaryDimension = dimensions[0]; @@ -204,12 +204,6 @@ const ComposedChart = forwardRef((props, ref ? dataKeys.findIndex((key) => key === chartConfig.secondYAxis?.dataKey) : 0; - const valueAccessor = - (attribute) => - ({ payload }) => { - return getValueByDataKey(payload, attribute); - }; - const onDataPointClickInternal = (payload, eventOrIndex, event) => { if (typeof onDataPointClick === 'function') { if (typeof eventOrIndex === 'number') { @@ -245,7 +239,7 @@ const ComposedChart = forwardRef((props, ref }; const onItemLegendClick = useLegendItemClick(onLegendClick); - const onClickInternal = useOnClickInternal(onClick); + const onClickInternal = useOnClickInternal(onClick, dataset, activePayloadsRef); const isBigDataSet = dataset?.length > 30; const primaryDimensionAccessor = primaryDimension?.accessor; @@ -435,8 +429,6 @@ const ComposedChart = forwardRef((props, ref /> )} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore ((props, ref )} {measures?.map((element, index) => { const ChartElement = ChartTypes[element.type] as any as FC; - const chartElementProps: any = { isAnimationActive: !noAnimation, }; let labelPosition = 'top'; + const color = element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`; + const dataKey = element.accessor; + const name = element.label ?? element.accessor; + const opacity = element.opacity ?? 1; + const hide = element.hide; + activePayloadsRef.current[index].color = color; + activePayloadsRef.current[index].stroke = color; + activePayloadsRef.current[index].dataKey = dataKey; + activePayloadsRef.current[index].hide = hide; + activePayloadsRef.current[index].name = name; + activePayloadsRef.current[index].fillOpacity = opacity; + activePayloadsRef.current[index].strokeOpacity = opacity; switch (element.type) { case 'line': @@ -461,9 +464,12 @@ const ComposedChart = forwardRef((props, ref chartElementProps.strokeWidth = element.width; chartElementProps.strokeOpacity = element.opacity; chartElementProps.dot = element.showDot ?? !isBigDataSet; + + activePayloadsRef.current[index].fillOpacity = opacity; + activePayloadsRef.current[index].strokeOpacity = opacity; break; case 'bar': - chartElementProps.hide = element.hide; + chartElementProps.hide = hide; chartElementProps.fillOpacity = element.opacity; chartElementProps.strokeOpacity = element.opacity; chartElementProps.barSize = element.width; @@ -484,6 +490,9 @@ const ComposedChart = forwardRef((props, ref chartElementProps.activeDot = { onClick: onDataPointClickInternal, }; + + activePayloadsRef.current[index].fillOpacity = 0.3; + activePayloadsRef.current[index].strokeOpacity = opacity; break; } @@ -495,23 +504,22 @@ const ComposedChart = forwardRef((props, ref return ( ) } - stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`} - fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 12) + 1})`} + stroke={color} + fill={color} type="monotone" - dataKey={element.accessor} + dataKey={dataKey} {...chartElementProps} > {element.type === 'bar' && ( <> } /> {dataset.map((data, i) => { diff --git a/packages/charts/src/components/LineChart/LineChart.tsx b/packages/charts/src/components/LineChart/LineChart.tsx index 13394e8d4ad..67a4637fb46 100644 --- a/packages/charts/src/components/LineChart/LineChart.tsx +++ b/packages/charts/src/components/LineChart/LineChart.tsx @@ -180,6 +180,8 @@ const LineChart = forwardRef((props, ref) => { const onItemLegendClick = useLegendItemClick(onLegendClick); const preventOnClickCall = useRef(0); + + //todo: check this const onDataPointClickInternal = useCallback( (payload, eventOrIndex) => { if (eventOrIndex.dataKey && typeof onDataPointClick === 'function') { @@ -265,6 +267,7 @@ const LineChart = forwardRef((props, ref) => { orientation={isRTL === true ? 'right' : 'left'} axisLine={chartConfig.yAxisVisible} tickLine={tickLineConfig} + // todo: multiple `yAxisId`s cause the Cartesian Grid to break yAxisId="left" tickFormatter={primaryMeasure?.formatter} interval={0} @@ -320,8 +323,6 @@ const LineChart = forwardRef((props, ref) => { ); })} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore ((props, ref) => { (props) => { const hideDataLabel = typeof measure.hideDataLabel === 'function' ? measure.hideDataLabel(props) : measure.hideDataLabel; - if (hideDataLabel || chartConfig.activeSegment === props.index) return null; - return Pie.renderLabelLineItem({}, props, undefined); + if (hideDataLabel || chartConfig.activeSegment === props.index) { + return null; + } + return ; }, [chartConfig.activeSegment, measure.hideDataLabel], ); @@ -311,7 +314,6 @@ const PieChart = forwardRef((props, ref) => { isAnimationActive={!noAnimation} labelLine={renderLabelLine} label={dataLabel} - activeIndex={chartConfig.activeSegment} activeShape={chartConfig.activeSegment != null && renderActiveShape} rootTabIndex={-1} > @@ -335,14 +337,17 @@ const PieChart = forwardRef((props, ref) => { {...tooltipConfig} /> )} + {chartConfig.activeSegment && ( + // tooltip that only renders the active shape + + )} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore )} diff --git a/packages/charts/src/components/RadarChart/RadarChart.mdx b/packages/charts/src/components/RadarChart/RadarChart.mdx index 1301368bb9b..6cba8dff024 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.mdx +++ b/packages/charts/src/components/RadarChart/RadarChart.mdx @@ -25,9 +25,9 @@ import LegendStory from '../../resources/LegendConfig.mdx'; -### With Data Labels +### Without Data Labels - + ### Polygon diff --git a/packages/charts/src/components/RadarChart/RadarChart.stories.tsx b/packages/charts/src/components/RadarChart/RadarChart.stories.tsx index 4ab80c9fd20..757d6bd903d 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.stories.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.stories.tsx @@ -1,5 +1,5 @@ import type { Meta, StoryObj } from '@storybook/react'; -import { complexDataSet, legendConfig, simpleDataSet, tooltipConfig } from '../../resources/DemoProps.js'; +import { legendConfig, radarChartDataset, simpleDataSet, tooltipConfig } from '../../resources/DemoProps.js'; import { RadarChart } from './RadarChart.js'; const meta = { @@ -9,27 +9,23 @@ const meta = { dimensions: [ { accessor: 'name', - formatter: (d) => `${d} 2019`, }, ], measures: [ { - accessor: 'users', - label: 'Users', - formatter: (val) => val.toLocaleString(), + accessor: 'alpha', + label: 'Alpha Series', }, { - accessor: 'sessions', - label: 'Active Sessions', - formatter: (val) => `${val} sessions`, - hideDataLabel: true, + accessor: 'beta', + label: 'Beta Series', }, { - accessor: 'volume', - label: 'Vol.', + accessor: 'gamma', + label: 'Gamma Series', }, ], - dataset: complexDataSet, + dataset: radarChartDataset, }, argTypes: { dataset: { @@ -51,18 +47,23 @@ export const WithCustomColor: Story = { }, }; -export const WithDataLabels: Story = { +export const WithoutDataLabels: Story = { args: { - dimensions: [{ accessor: 'name' }], measures: [ { - accessor: 'users', + accessor: 'alpha', + label: 'Alpha Series', + hideDataLabel: true, }, { - accessor: 'sessions', + accessor: 'beta', + label: 'Beta Series', + hideDataLabel: true, }, { - accessor: 'volume', + accessor: 'gamma', + label: 'Gamma Series', + hideDataLabel: true, }, ], }, @@ -70,20 +71,6 @@ export const WithDataLabels: Story = { export const Polygon: Story = { args: { - dimensions: [{ accessor: 'name', formatter: (element) => element.slice(0, 3) }], - measures: [ - { - accessor: 'users', - formatter: (element) => `${element / 10}`, - label: 'number of users', - }, - { - accessor: 'sessions', - }, - { - accessor: 'volume', - }, - ], chartConfig: { polarGridType: 'polygon' }, }, }; diff --git a/packages/charts/src/components/RadarChart/RadarChart.tsx b/packages/charts/src/components/RadarChart/RadarChart.tsx index 5cd6f6174b2..ba44ff5d08a 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.tsx @@ -125,8 +125,10 @@ const RadarChart = forwardRef((props, ref) => { const onItemLegendClick = useLegendItemClick(onLegendClick); const preventOnClickCall = useRef(false); + //todo: check this const onClickInternal = useCallback( (payload, event) => { + console.log(payload); if (typeof onClick === 'function' && !preventOnClickCall.current) { onClick( enrichEventWithDetails(event, { @@ -217,8 +219,6 @@ const RadarChart = forwardRef((props, ref) => { /> )} {!noLegend && ( - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore []; dataIndex: number; diff --git a/packages/charts/src/components/ScatterChart/ScatterChart.tsx b/packages/charts/src/components/ScatterChart/ScatterChart.tsx index 0db5cee1e51..31150648782 100644 --- a/packages/charts/src/components/ScatterChart/ScatterChart.tsx +++ b/packages/charts/src/components/ScatterChart/ScatterChart.tsx @@ -168,8 +168,10 @@ const ScatterChart = forwardRef((props, ref) const [componentRef, chartRef] = useSyncRef(ref); const preventOnClickCall = useRef(false); const onItemLegendClick = useLegendItemClick(onLegendClick); + //todo: error on click const onClickInternal = useCallback( (payload, event) => { + console.log(payload); if (typeof onClick === 'function' && !preventOnClickCall.current) { onClick( enrichEventWithDetails(event, { diff --git a/packages/charts/src/hooks/useOnClickInternal.ts b/packages/charts/src/hooks/useOnClickInternal.ts index 7c3f4a3cc9c..8b9a92c8b5c 100644 --- a/packages/charts/src/hooks/useOnClickInternal.ts +++ b/packages/charts/src/hooks/useOnClickInternal.ts @@ -1,17 +1,58 @@ import { enrichEventWithDetails } from '@ui5/webcomponents-react-base'; +import type { MutableRefObject } from 'react'; import { useCallback } from 'react'; - -export const useOnClickInternal = (onClick) => - useCallback( - (payload, event) => { +import type { CategoricalChartFunc } from 'recharts/types/chart/types.js'; +import type { ActivePayload, IChartBaseProps } from '../interfaces/IChartBaseProps.js'; +export const useOnClickInternal = ( + onClick: IChartBaseProps['onClick'], + dataset: IChartBaseProps['dataset'], + activePayloadsRef: MutableRefObject, +): CategoricalChartFunc => { + //todo: deprecate payload & activePayloads? + return useCallback( + (nextState, event) => { if (typeof onClick === 'function') { + const payload = nextState.activeIndex != null ? dataset?.[nextState.activeIndex] : undefined; + const activePayloads = activePayloadsRef.current.map((item) => ({ + ...item, + payload, + value: item.dataKey ? payload?.[item.dataKey] : undefined, + })); + console.log('click enriched'); onClick( + //todo: check ts-error + // @ts-expect-error: check enrichEventWithDetails(event, { - payload: payload?.activePayload?.[0]?.payload, - activePayloads: payload?.activePayload, + payload, + activePayloads, + //todo: add nextState? + ...nextState, }), ); } }, - [onClick], + [onClick, dataset, activePayloadsRef], ); +}; + +//todo: integrate LineChart, RadarChart click here as well? + +//bar, bullet, column, withTrend, composed, donut, line, pie, radial, scatter +//activeCoordinate +// : +// {x: 746, y: 243.2412109375} +// activeDataKey +// : +// undefined +// activeIndex +// : +// "8" +// activeLabel +// : +// "September" +// activeTooltipIndex +// : +// "8" +// isTooltipActive +// : +// true diff --git a/packages/charts/src/interfaces/IChartBaseProps.ts b/packages/charts/src/interfaces/IChartBaseProps.ts index 5467c97dfdf..c032abd2898 100644 --- a/packages/charts/src/interfaces/IChartBaseProps.ts +++ b/packages/charts/src/interfaces/IChartBaseProps.ts @@ -1,8 +1,26 @@ import type { CommonProps } from '@ui5/webcomponents-react'; import type { ComponentType, ReactNode } from 'react'; import type { LegendProps, TooltipProps } from 'recharts'; +import type { CategoricalChartFunc } from 'recharts/types/chart/types.js'; import type { ICartesianChartConfig } from './ICartesianChartConfig.js'; +//todo: type measures +// interface ActivePayload extends IChartMeasure{ +// opacity?: +// } + +export interface ActivePayload { + color: string; + stroke: string; + dataKey: string; + hide: boolean; + name: string; + fillOpacity: string | number; + strokeOpacity: string | number; + payload: Record; + value: number | string; +} + export interface IChartBaseProps extends Omit { /** * Flag whether the chart should display a loading indicator. @@ -37,7 +55,14 @@ export interface IChartBaseProps extends Omit[] }>) => void; + onClick?: ( + event: CustomEvent< + Parameters[0] & { + payload: unknown; + activePayloads: ActivePayload[]; + } + >, + ) => void; /** * The `onDataPointClick` event fires whenever the user clicks on e.g. a bar in `BarChart` or a point the `LineChart`. diff --git a/packages/charts/src/internal/ChartDataLabel.tsx b/packages/charts/src/internal/ChartDataLabel.tsx index 757062bf830..76fa1ca5933 100644 --- a/packages/charts/src/internal/ChartDataLabel.tsx +++ b/packages/charts/src/internal/ChartDataLabel.tsx @@ -15,16 +15,17 @@ interface CustomDataLabelProps { export const ChartDataLabel = (props: CustomDataLabelProps) => { const { config, chartType, viewBox } = props; - if (config.hideDataLabel) { + //todo: viewBox is not initially available, check if this is changed in a later version + if (config.hideDataLabel || !viewBox) { return null; } - if (config.DataLabel) { + if (config.DataLabel && !!viewBox) { return createElement(config.DataLabel, props); } const formattedLabel = config.formatter(props.value ?? props.children); - if (chartType === 'bar' || chartType === 'column') { + if ((chartType === 'bar' || chartType === 'column') && !!viewBox) { if (Math.abs(viewBox.width) < getTextWidth(formattedLabel)) { return null; } @@ -37,7 +38,6 @@ export const ChartDataLabel = (props: CustomDataLabelProps) => { if (['area', 'line', 'radar'].includes(chartType)) { fill = ThemingParameters.sapTextColor; // label is displayed outside of the colored element } - return (