From e8b795e03c7f8011bc8f5b05db5bdcaa9c355da6 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 23 Apr 2020 10:03:57 +0200 Subject: [PATCH 01/30] remove unused chart margin from radar chart --- .../src/components/RadarChart/RadarChart.tsx | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/packages/charts/src/components/RadarChart/RadarChart.tsx b/packages/charts/src/components/RadarChart/RadarChart.tsx index 768b10e2bd2..8d5cf5b4278 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.tsx @@ -1,6 +1,6 @@ -import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; import { ThemingParameters } from '@ui5/webcomponents-react-base/lib/ThemingParameters'; import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef'; +import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; import { ChartContainer } from '@ui5/webcomponents-react-charts/lib/next/ChartContainer'; import { PieChartPlaceholder } from '@ui5/webcomponents-react-charts/lib/PieChartPlaceholder'; import { useLegendItemClick } from '@ui5/webcomponents-react-charts/lib/useLegendItemClick'; @@ -14,13 +14,12 @@ import { RadarChart as RadarChartLib, Tooltip } from 'recharts'; -import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; import { useDataLabel } from '../../hooks/useLabelElements'; -import { useChartMargin } from '../../hooks/useChartMargin'; -import { IChartMeasure } from '../../interfaces/IChartMeasure'; -import { IChartDimension } from '../../interfaces/IChartDimension'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; +import { IChartDimension } from '../../interfaces/IChartDimension'; +import { IChartMeasure } from '../../interfaces/IChartMeasure'; +import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; interface MeasureConfig extends IChartMeasure { /** @@ -121,8 +120,6 @@ const RadarChart: FC = forwardRef((props: RadarChartProps, ref: [onDataPointClick] ); - const marginChart = useChartMargin(dataset, (d) => d, primaryDimensionAccessor, chartConfig.margin, true, true, true); - return ( = forwardRef((props: RadarChartProps, ref: tooltip={tooltip} slot={slot} > - + = forwardRef((props: RadarChartProps, ref: ); })} - {!noLegend && ( - - )} + {!noLegend && } ); From 20bea21796b902148ccf02e8b677263422750422 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 23 Apr 2020 10:04:14 +0200 Subject: [PATCH 02/30] fix useChartMargin for complex accessors --- packages/charts/src/hooks/useChartMargin.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/charts/src/hooks/useChartMargin.ts b/packages/charts/src/hooks/useChartMargin.ts index 5df69b6b21a..7190d903822 100644 --- a/packages/charts/src/hooks/useChartMargin.ts +++ b/packages/charts/src/hooks/useChartMargin.ts @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { getValueByDataKey } from 'recharts/lib/util/ChartUtils'; import { getTextWidth } from '../util/Utils'; export const useChartMargin = (dataset, formatter, labelKey, margin, isBar?, hasSecondaryDimension?, hasZoomingTool?) => @@ -7,7 +8,7 @@ export const useChartMargin = (dataset, formatter, labelKey, margin, isBar?, has if (dataset && isBar && typeof margin?.left !== 'number') { marginLeft = Math.max( ...dataset - .map((data) => formatter(data[labelKey]).split(' ')) + .map((data) => formatter(getValueByDataKey(data, labelKey, '')).split(' ')) .flat() .map(getTextWidth) ); From db5abdd7c5ed808a8e0c88eded3d25eb4f46fc69 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 23 Apr 2020 12:59:29 +0200 Subject: [PATCH 03/30] Add color and font family to container --- packages/charts/src/internal/ChartContainer.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/charts/src/internal/ChartContainer.tsx b/packages/charts/src/internal/ChartContainer.tsx index 5842e239c61..8be39cf9859 100644 --- a/packages/charts/src/internal/ChartContainer.tsx +++ b/packages/charts/src/internal/ChartContainer.tsx @@ -24,6 +24,8 @@ const ChartContainer: FC = forwardRef((props: ContainerProps, re const internalStyles: CSSProperties = useMemo(() => { return { fontSize: ThemingParameters.sapFontSmallSize, + color: ThemingParameters.sapTextColor, + fontFamily: ThemingParameters.sapFontFamily, width: '100%', height: '400px', position: 'relative', From 9c2138d2b6279e34b9207579b01f8a15499007a6 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 23 Apr 2020 12:59:45 +0200 Subject: [PATCH 04/30] remove useMemo from useLabelElements --- packages/charts/src/hooks/useLabelElements.ts | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/charts/src/hooks/useLabelElements.ts b/packages/charts/src/hooks/useLabelElements.ts index 9bf84d78103..0aaa7a0ff36 100644 --- a/packages/charts/src/hooks/useLabelElements.ts +++ b/packages/charts/src/hooks/useLabelElements.ts @@ -8,33 +8,34 @@ import { } from '../internal/CustomElements'; import { getTextWidth, renderAxisTicks } from '../util/Utils'; -export const useDataLabel = (showDataLabel, dataLabelCustomElement, dataLabelFormatter, stacked?, bar?, noSizeCheck?) => - useMemo(() => { - if (showDataLabel ?? true) { - if (dataLabelCustomElement) { - return (props) => DataLabel(props, dataLabelFormatter, dataLabelCustomElement); - } else { - return { - position: bar ? (stacked ? 'insideRight' : 'right') : stacked ? 'inside' : 'top', - content: (props) => { - const formattedDataValue = dataLabelFormatter(props.value); - if (noSizeCheck) { - return formattedDataValue; - } - if (props.viewBox.width < getTextWidth(formattedDataValue)) { - return null; - } - if (props.viewBox.height < 12) { - return null; - } - return formattedDataValue; - }, - fill: ThemingParameters.sapContent_LabelColor - }; - } - } +export const useDataLabel = (elementConfig, stacked?, bar?, noSizeCheck?) => { + const { hideDataLabel, DataLabel: DataLabelCustomComponent, formatter } = elementConfig; + + if (hideDataLabel) { return false; - }, [stacked, bar, showDataLabel, dataLabelFormatter, dataLabelCustomElement]); + } + + if (DataLabelCustomComponent) { + return (props) => DataLabel(props, formatter, DataLabelCustomComponent); + } + return { + position: bar ? (stacked ? 'insideRight' : 'right') : stacked ? 'inside' : 'top', + content: (props) => { + const formattedDataValue = formatter(props.value); + if (noSizeCheck) { + return formattedDataValue; + } + if (props.viewBox.width < getTextWidth(formattedDataValue)) { + return null; + } + if (props.viewBox.height < 12) { + return null; + } + return formattedDataValue; + }, + fill: ThemingParameters.sapContent_LabelColor + }; +}; export const usePieDataLabel = (dataLabel, dataLabelCustomElement, dataLabelFormatter) => useMemo(() => { From d69be7923e8a8e09357e3187121bc42417ac80f1 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Thu, 23 Apr 2020 13:00:10 +0200 Subject: [PATCH 05/30] refactor: useDataLabel --- .../charts/src/components/BarChart/BarChart.tsx | 13 +++---------- .../src/components/ColumnChart/ColumnChart.tsx | 10 ++-------- .../charts/src/components/ComposedChart/index.tsx | 6 ++---- .../charts/src/components/LineChart/LineChart.tsx | 11 ++--------- .../charts/src/components/RadarChart/RadarChart.tsx | 9 +-------- 5 files changed, 10 insertions(+), 39 deletions(-) diff --git a/packages/charts/src/components/BarChart/BarChart.tsx b/packages/charts/src/components/BarChart/BarChart.tsx index 011889f452b..7c79474e987 100644 --- a/packages/charts/src/components/BarChart/BarChart.tsx +++ b/packages/charts/src/components/BarChart/BarChart.tsx @@ -24,7 +24,7 @@ import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; -const formatYAxisTicks = (tick) => { +const formatYAxisTicks = (tick = '') => { const splitTick = tick.split(' '); return splitTick.length > 3 ? `${splitTick.slice(0, 3).join(' ')}...` @@ -164,7 +164,7 @@ const BarChart: FC = forwardRef((props: BarChartProps, ref: Ref d, + primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, true, @@ -221,14 +221,7 @@ const BarChart: FC = forwardRef((props: BarChartProps, ref: Ref { - const BarDataLabel = useDataLabel( - !element.hideDataLabel, - element.DataLabel, - element.formatter, - false, - true, - false - ); + const BarDataLabel = useDataLabel(element, !!element.stackId, true); return ( = forwardRef((props: ColumnChartProps, r const marginChart = useChartMargin( dataset, - (d) => d, + primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, false, @@ -226,13 +226,7 @@ const ColumnChart: FC = forwardRef((props: ColumnChartProps, r /> )} {measures.map((element, index) => { - const ColumnDataLabel = useDataLabel( - !element.hideDataLabel, - element.DataLabel, - element.formatter, - !!element.stackId, - false - ); + const ColumnDataLabel = useDataLabel(element, !!element.stackId, false); return ( = forwardRef((props: ComposedChartPr const marginChart = useChartMargin( dataset, - (d) => d, + primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, false, @@ -267,9 +267,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr {!noLegend && } {measures?.map((element, index) => { const ComposedDataLabel = useDataLabel( - !element.hideDataLabel, - element.DataLabel, - element.formatter, + element, !!(element.type === 'bar' && element.stackId), false, element.type === 'line' || element.type === 'area' diff --git a/packages/charts/src/components/LineChart/LineChart.tsx b/packages/charts/src/components/LineChart/LineChart.tsx index 42b72e254db..a9803f8c955 100644 --- a/packages/charts/src/components/LineChart/LineChart.tsx +++ b/packages/charts/src/components/LineChart/LineChart.tsx @@ -159,7 +159,7 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re const marginChart = useChartMargin( dataset, - (d) => d, + primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, false, @@ -217,14 +217,7 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re /> )} {measures.map((element, index) => { - const LineDataLabel = useDataLabel( - !element.hideDataLabel, - element.DataLabel, - element.formatter, - false, - false, - true - ); + const LineDataLabel = useDataLabel(element, false, false, true); return ( = forwardRef((props: RadarChartProps, ref: /> {measures.map((element, index) => { - const RadarDataLabel = useDataLabel( - !element.hideDataLabel, - element.DataLabel, - element.formatter, - false, - false, - true - ); + const RadarDataLabel = useDataLabel(element, false, false, true); return ( Date: Mon, 27 Apr 2020 13:22:08 +0200 Subject: [PATCH 06/30] calculate marginLeft by measures --- .../src/components/BarChart/BarChart.tsx | 1 + .../components/ColumnChart/ColumnChart.tsx | 1 + .../src/components/ComposedChart/index.tsx | 1 + .../src/components/LineChart/LineChart.tsx | 1 + .../src/components/RadarChart/RadarChart.tsx | 17 +++++--- packages/charts/src/hooks/useChartMargin.ts | 43 +++++++++++++++---- 6 files changed, 48 insertions(+), 16 deletions(-) diff --git a/packages/charts/src/components/BarChart/BarChart.tsx b/packages/charts/src/components/BarChart/BarChart.tsx index 7c79474e987..2e59884d8e2 100644 --- a/packages/charts/src/components/BarChart/BarChart.tsx +++ b/packages/charts/src/components/BarChart/BarChart.tsx @@ -164,6 +164,7 @@ const BarChart: FC = forwardRef((props: BarChartProps, ref: Ref d), primaryDimensionAccessor, chartConfig.margin, diff --git a/packages/charts/src/components/ColumnChart/ColumnChart.tsx b/packages/charts/src/components/ColumnChart/ColumnChart.tsx index 8c475c12872..db4785269da 100644 --- a/packages/charts/src/components/ColumnChart/ColumnChart.tsx +++ b/packages/charts/src/components/ColumnChart/ColumnChart.tsx @@ -168,6 +168,7 @@ const ColumnChart: FC = forwardRef((props: ColumnChartProps, r const marginChart = useChartMargin( dataset, + measures, primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, diff --git a/packages/charts/src/components/ComposedChart/index.tsx b/packages/charts/src/components/ComposedChart/index.tsx index e4db6e3c787..cd9992d7e95 100644 --- a/packages/charts/src/components/ComposedChart/index.tsx +++ b/packages/charts/src/components/ComposedChart/index.tsx @@ -205,6 +205,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr const marginChart = useChartMargin( dataset, + measures, primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, diff --git a/packages/charts/src/components/LineChart/LineChart.tsx b/packages/charts/src/components/LineChart/LineChart.tsx index a9803f8c955..28f2420fb2f 100644 --- a/packages/charts/src/components/LineChart/LineChart.tsx +++ b/packages/charts/src/components/LineChart/LineChart.tsx @@ -159,6 +159,7 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re const marginChart = useChartMargin( dataset, + measures, primaryDimension?.formatter ?? ((d) => d), primaryDimensionAccessor, chartConfig.margin, diff --git a/packages/charts/src/components/RadarChart/RadarChart.tsx b/packages/charts/src/components/RadarChart/RadarChart.tsx index a53f3f4330d..dcf443633bd 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.tsx @@ -107,13 +107,16 @@ const RadarChart: FC = forwardRef((props: RadarChartProps, ref: (payload, eventOrIndex) => { if (eventOrIndex.value && onDataPointClick) { onDataPointClick( - enrichEventWithDetails(event, { - value: eventOrIndex.value, - dataKey: eventOrIndex.dataKey, - name: eventOrIndex.payload.label, - xIndex: eventOrIndex.index, - payload: eventOrIndex.payload - }) + enrichEventWithDetails( + {}, + { + value: eventOrIndex.value, + dataKey: eventOrIndex.dataKey, + name: eventOrIndex.payload.label, + xIndex: eventOrIndex.index, + payload: eventOrIndex.payload + } + ) ); } }, diff --git a/packages/charts/src/hooks/useChartMargin.ts b/packages/charts/src/hooks/useChartMargin.ts index 7190d903822..a228e4150a4 100644 --- a/packages/charts/src/hooks/useChartMargin.ts +++ b/packages/charts/src/hooks/useChartMargin.ts @@ -2,22 +2,47 @@ import { useMemo } from 'react'; import { getValueByDataKey } from 'recharts/lib/util/ChartUtils'; import { getTextWidth } from '../util/Utils'; -export const useChartMargin = (dataset, formatter, labelKey, margin, isBar?, hasSecondaryDimension?, hasZoomingTool?) => +export const useChartMargin = ( + dataset, + measures, + formatter, + labelKey, + margin, + isBar?, + hasSecondaryDimension?, + hasZoomingTool? +) => useMemo(() => { let marginLeft = 0; - if (dataset && isBar && typeof margin?.left !== 'number') { - marginLeft = Math.max( - ...dataset - .map((data) => formatter(getValueByDataKey(data, labelKey, '')).split(' ')) - .flat() - .map(getTextWidth) - ); + if (dataset && typeof margin?.left !== 'number') { + if (isBar) { + marginLeft = Math.max( + ...dataset + .map((data) => formatter(getValueByDataKey(data, labelKey, '')).split(' ')) + .flat() + .map(getTextWidth) + ); + } else { + marginLeft = getTextWidth( + Math.max( + ...dataset.map((item) => { + return Math.max(...measures.map((measure) => item[measure.accessor])); + }) + ) + ); + } } return { right: margin?.right ?? 60, top: margin?.top ?? hasZoomingTool ? 40 : 10, bottom: margin?.bottom ?? (!isBar && hasSecondaryDimension) ? 100 : 30, left: - margin?.left ?? isBar ? (hasSecondaryDimension ? marginLeft : marginLeft / 2) : hasSecondaryDimension ? 20 : 0 + margin?.left ?? isBar + ? hasSecondaryDimension + ? marginLeft + : marginLeft / 2 + : hasSecondaryDimension + ? marginLeft / 2 + : marginLeft / 2 }; }, [dataset, labelKey, margin]); From 8067c41fbba8362c71b4c57385e93f0f3f7ca614 Mon Sep 17 00:00:00 2001 From: MarcusNotheis Date: Mon, 27 Apr 2020 14:15:08 +0200 Subject: [PATCH 07/30] update to recharts-beta.5 --- packages/charts/package.json | 2 +- .../src/components/BarChart/BarChart.tsx | 13 +- .../components/ColumnChart/ColumnChart.tsx | 13 +- .../ComposedChart/ComposedChart.stories.tsx | 5 +- .../src/components/ComposedChart/index.tsx | 29 +- .../src/components/LineChart/LineChart.tsx | 5 +- .../src/components/PieChart/PieChart.tsx | 14 +- .../src/components/RadarChart/RadarChart.tsx | 11 +- packages/charts/src/hooks/useLabelElements.ts | 70 --- .../charts/src/hooks/useLabelElements.tsx | 72 +++ packages/charts/src/internal/ChartLabel.tsx | 476 ++++++++++++++++++ yarn.lock | 50 +- 12 files changed, 623 insertions(+), 137 deletions(-) delete mode 100644 packages/charts/src/hooks/useLabelElements.ts create mode 100644 packages/charts/src/hooks/useLabelElements.tsx create mode 100644 packages/charts/src/internal/ChartLabel.tsx diff --git a/packages/charts/package.json b/packages/charts/package.json index 6d734983a76..ebb0e005458 100644 --- a/packages/charts/package.json +++ b/packages/charts/package.json @@ -27,7 +27,7 @@ "lodash.merge": "^4.6.2", "react-chartjs-2": "^2.8.0", "react-content-loader": "^5.0.4", - "recharts": "2.0.0-beta.1" + "recharts": "2.0.0-beta.5" }, "devDependencies": { "@types/chart.js": "^2.9.8" diff --git a/packages/charts/src/components/BarChart/BarChart.tsx b/packages/charts/src/components/BarChart/BarChart.tsx index 7c79474e987..42eff220fa4 100644 --- a/packages/charts/src/components/BarChart/BarChart.tsx +++ b/packages/charts/src/components/BarChart/BarChart.tsx @@ -17,7 +17,7 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { useAxisLabel, useDataLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; @@ -185,7 +185,7 @@ const BarChart: FC = forwardRef((props: BarChartProps, ref: Ref - + = forwardRef((props: BarChartProps, ref: Ref { - const BarDataLabel = useDataLabel(element, !!element.stackId, true); return ( = forwardRef((props: BarChartProps, ref: Ref + } type="monotone" dataKey={element.accessor} fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} diff --git a/packages/charts/src/components/ColumnChart/ColumnChart.tsx b/packages/charts/src/components/ColumnChart/ColumnChart.tsx index 8c475c12872..18f8bea81e3 100644 --- a/packages/charts/src/components/ColumnChart/ColumnChart.tsx +++ b/packages/charts/src/components/ColumnChart/ColumnChart.tsx @@ -1,6 +1,6 @@ -import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; import { ThemingParameters } from '@ui5/webcomponents-react-base/lib/ThemingParameters'; import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef'; +import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils'; import { ColumnChartPlaceholder } from '@ui5/webcomponents-react-charts/lib/ColumnChartPlaceholder'; import { ChartContainer } from '@ui5/webcomponents-react-charts/lib/next/ChartContainer'; import { useLegendItemClick } from '@ui5/webcomponents-react-charts/lib/useLegendItemClick'; @@ -16,13 +16,13 @@ import { XAxis, YAxis } from 'recharts'; +import { useChartMargin } from '../../hooks/useChartMargin'; +import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; +import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; -import { useDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; -import { useChartMargin } from '../../hooks/useChartMargin'; -import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; interface MeasureConfig extends IChartMeasure { /** @@ -226,7 +226,6 @@ const ColumnChart: FC = forwardRef((props: ColumnChartProps, r /> )} {measures.map((element, index) => { - const ColumnDataLabel = useDataLabel(element, !!element.stackId, false); return ( = forwardRef((props: ColumnChartProps, r key={element.accessor} name={element.label ?? element.accessor} strokeOpacity={element.opacity} - label={ColumnDataLabel} + label={ + + } type="monotone" dataKey={element.accessor} fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} diff --git a/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx b/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx index b52660823f4..a606eda975d 100644 --- a/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx +++ b/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx @@ -2,7 +2,7 @@ import { action } from '@storybook/addon-actions'; import { ComposedChart } from '@ui5/webcomponents-react-charts/lib/next/ComposedChart'; import React from 'react'; import { bigDataSet, complexDataSet, secondaryDimensionDataSet, simpleDataSet } from '../../resources/DemoProps'; -import { boolean } from '@storybook/addon-knobs'; +import { boolean, select } from '@storybook/addon-knobs'; export default { title: 'Charts - Unstable / ComposedChart', @@ -17,6 +17,7 @@ export const renderStory = () => { onLegendClick={action('onLegendClick')} dataset={complexDataSet} style={{ height: '60vh' }} + layout={select('layout', ['horizontal', 'vertical'], 'horizontal')} dimensions={[ { accessor: 'name', @@ -34,7 +35,7 @@ export const renderStory = () => { accessor: 'users', label: 'Users', formatter: (val) => val.toLocaleString(), - type: 'line' + type: 'area' }, { accessor: 'volume', diff --git a/packages/charts/src/components/ComposedChart/index.tsx b/packages/charts/src/components/ComposedChart/index.tsx index e4db6e3c787..618f037aec9 100644 --- a/packages/charts/src/components/ComposedChart/index.tsx +++ b/packages/charts/src/components/ComposedChart/index.tsx @@ -18,7 +18,7 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { useAxisLabel, useDataLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; @@ -31,8 +31,7 @@ const dimensionDefaults = { const measureDefaults = { formatter: (d) => d, - opacity: 1, - width: 1 + opacity: 1 }; interface MeasureConfig extends IChartMeasure { @@ -83,6 +82,11 @@ export interface ComposedChartProps extends RechartBaseProps { * */ measures: MeasureConfig[]; + /** + * layout for showing measures. `horizontal` bars would equal the column chart, `vertical` would be a bar chart. + * Default Value: `horizontal` + */ + // layout?: 'horizontal' | 'vertical'; } enum ChartTypes { @@ -106,6 +110,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr onDataPointClick, noLegend = false, onLegendClick, + layout = 'horizontal', chartConfig = { margin: {}, yAxisVisible: false, @@ -224,7 +229,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr tooltip={tooltip} slot={slot} > - + = forwardRef((props: ComposedChartPr {!noLegend && } {measures?.map((element, index) => { - const ComposedDataLabel = useDataLabel( - element, - !!(element.type === 'bar' && element.stackId), - false, - element.type === 'line' || element.type === 'area' - ); const ChartElement = (ChartTypes[element.type] as any) as FC; const chartElementProps: any = {}; + let labelPosition = 'top'; switch (element.type) { case 'line': chartElementProps.activeDot = { onClick: onDataPointClickInternal }; - chartElementProps.strokeWidth = element.width ?? 1; + chartElementProps.strokeWidth = element.width; chartElementProps.strokeOpacity = element.opacity; chartElementProps.dot = !isBigDataSet; break; case 'bar': chartElementProps.fillOpacity = element.opacity; chartElementProps.strokeOpacity = element.opacity; - chartElementProps.barSize = element.width === 1 ? 20 : element.width; + chartElementProps.barSize = element.width; chartElementProps.onClick = onDataPointClickInternal; chartElementProps.stackId = element.stackId ?? undefined; + labelPosition = element.stackId ? 'insideRight' : 'right'; break; case 'area': chartElementProps.dot = !isBigDataSet; chartElementProps.fillOpacity = 0.3; chartElementProps.strokeOpacity = element.opacity; chartElementProps.onClick = onDataPointClickInternal; - chartElementProps.strokeWidth = element.width ?? 1; + chartElementProps.strokeWidth = element.width; break; } return ( @@ -305,7 +306,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr yAxisId={chartConfig?.secondYAxis?.dataKey === element.accessor ? 'right' : 'left'} key={element.accessor} name={element.label ?? element.accessor} - label={isBigDataSet ? false : ComposedDataLabel} + label={} stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} type="monotone" diff --git a/packages/charts/src/components/LineChart/LineChart.tsx b/packages/charts/src/components/LineChart/LineChart.tsx index a9803f8c955..110b4661190 100644 --- a/packages/charts/src/components/LineChart/LineChart.tsx +++ b/packages/charts/src/components/LineChart/LineChart.tsx @@ -17,7 +17,7 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { useAxisLabel, useDataLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; @@ -217,7 +217,6 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re /> )} {measures.map((element, index) => { - const LineDataLabel = useDataLabel(element, false, false, true); return ( = forwardRef((props: LineChartProps, ref: Re key={element.accessor} name={element.label ?? element.accessor} strokeOpacity={element.opacity} - label={isBigDataSet ? false : LineDataLabel} + label={isBigDataSet ? false : } type="monotone" dataKey={element.accessor} stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} diff --git a/packages/charts/src/components/PieChart/PieChart.tsx b/packages/charts/src/components/PieChart/PieChart.tsx index 1a9b80e8e1c..e300a895c4c 100644 --- a/packages/charts/src/components/PieChart/PieChart.tsx +++ b/packages/charts/src/components/PieChart/PieChart.tsx @@ -3,9 +3,8 @@ import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils' import { ChartContainer } from '@ui5/webcomponents-react-charts/lib/next/ChartContainer'; import { PieChartPlaceholder } from '@ui5/webcomponents-react-charts/lib/PieChartPlaceholder'; import { useLegendItemClick } from '@ui5/webcomponents-react-charts/lib/useLegendItemClick'; -import React, { FC, forwardRef, Ref, useCallback, useMemo, CSSProperties } from 'react'; +import React, { CSSProperties, FC, forwardRef, Ref, useCallback, useMemo } from 'react'; import { Cell, Label, Legend, Pie, PieChart as PieChartLib, Tooltip } from 'recharts'; -import { usePieDataLabel } from '../../hooks/useLabelElements'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; @@ -110,9 +109,14 @@ const PieChart: FC = forwardRef((props: PieChartProps, ref: Ref { + return { + position: 'outside', + content: measure.hideDataLabel ?? measure.DataLabel, + formatter: measure.formatter + }; + }, [measure]); return ( = forwardRef((props: PieChartProps, ref: Ref {centerLabel && } diff --git a/packages/charts/src/components/RadarChart/RadarChart.tsx b/packages/charts/src/components/RadarChart/RadarChart.tsx index a53f3f4330d..3be24f27264 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.tsx @@ -14,7 +14,7 @@ import { RadarChart as RadarChartLib, Tooltip } from 'recharts'; -import { useDataLabel } from '../../hooks/useLabelElements'; +import { CustomDataLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; @@ -142,7 +142,6 @@ const RadarChart: FC = forwardRef((props: RadarChartProps, ref: /> {measures.map((element, index) => { - const RadarDataLabel = useDataLabel(element, false, false, true); return ( = forwardRef((props: RadarChartProps, ref: stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} fillOpacity={element.opacity} - label={RadarDataLabel} + label={ + + } /> ); })} diff --git a/packages/charts/src/hooks/useLabelElements.ts b/packages/charts/src/hooks/useLabelElements.ts deleted file mode 100644 index 0aaa7a0ff36..00000000000 --- a/packages/charts/src/hooks/useLabelElements.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { ThemingParameters } from '@ui5/webcomponents-react-base/lib/ThemingParameters'; -import { useCallback, useMemo } from 'react'; -import { - DataLabel, - SecondaryDimensionTicksXAxis, - SecondaryDimensionTicksYAxis, - YAxisTicks -} from '../internal/CustomElements'; -import { getTextWidth, renderAxisTicks } from '../util/Utils'; - -export const useDataLabel = (elementConfig, stacked?, bar?, noSizeCheck?) => { - const { hideDataLabel, DataLabel: DataLabelCustomComponent, formatter } = elementConfig; - - if (hideDataLabel) { - return false; - } - - if (DataLabelCustomComponent) { - return (props) => DataLabel(props, formatter, DataLabelCustomComponent); - } - return { - position: bar ? (stacked ? 'insideRight' : 'right') : stacked ? 'inside' : 'top', - content: (props) => { - const formattedDataValue = formatter(props.value); - if (noSizeCheck) { - return formattedDataValue; - } - if (props.viewBox.width < getTextWidth(formattedDataValue)) { - return null; - } - if (props.viewBox.height < 12) { - return null; - } - return formattedDataValue; - }, - fill: ThemingParameters.sapContent_LabelColor - }; -}; - -export const usePieDataLabel = (dataLabel, dataLabelCustomElement, dataLabelFormatter) => - useMemo(() => { - return dataLabel || typeof dataLabel === 'undefined' - ? dataLabelCustomElement - ? (props) => DataLabel(props, dataLabelFormatter, dataLabelCustomElement) - : (props): number | string => dataLabelFormatter(props.value) - : false; - }, [dataLabelFormatter, dataLabelCustomElement, dataLabel]); - -export const useAxisLabel = (AxisFormatter, yAxis?) => { - return useCallback( - (labelProps) => { - return yAxis ? YAxisTicks(labelProps, AxisFormatter) : renderAxisTicks(labelProps, AxisFormatter); - }, - [AxisFormatter] - ); -}; - -let dimension = ''; -export const useSecondaryDimensionLabel = (yAxis?: boolean, yAxisFormatter?) => { - return useCallback((labelProps) => { - if (dimension === labelProps.payload.value) { - return undefined; - } else { - dimension = labelProps.payload.value; - return yAxis - ? SecondaryDimensionTicksYAxis(labelProps, yAxisFormatter) - : SecondaryDimensionTicksXAxis(labelProps); - } - }, []); -}; diff --git a/packages/charts/src/hooks/useLabelElements.tsx b/packages/charts/src/hooks/useLabelElements.tsx new file mode 100644 index 00000000000..4323a4d47ca --- /dev/null +++ b/packages/charts/src/hooks/useLabelElements.tsx @@ -0,0 +1,72 @@ +import { ThemingParameters } from '@ui5/webcomponents-react-base/lib/ThemingParameters'; +import React, { FC, useCallback } from 'react'; +import { IChartMeasure } from '../interfaces/IChartMeasure'; +import Label from '../internal/ChartLabel'; +import { SecondaryDimensionTicksXAxis, SecondaryDimensionTicksYAxis, YAxisTicks } from '../internal/CustomElements'; +import { getTextWidth, renderAxisTicks } from '../util/Utils'; + +interface CustomDataLabelProps { + config: IChartMeasure; + viewBox?: any; + chartType: 'bar' | 'column' | 'line' | 'radar' | 'pie'; + position?: string; + value?: any; + children?: any; +} + +export const CustomDataLabel: FC = (props: CustomDataLabelProps) => { + const { config, chartType } = props; + + const viewBox = Label.parseViewBox(props); + + if (config.hideDataLabel) { + return null; + } + + if (config.DataLabel) { + return config.DataLabel(props); + } + + const formattedLabel = config.formatter(props.value ?? props.children); + if (chartType === 'bar' || chartType === 'column') { + if (viewBox.width < getTextWidth(formattedLabel)) { + return null; + } + if (viewBox.height < 12) { + return null; + } + } + + return ( + = forwardRef((props: BarChartProps, ref: Ref} axisLine={chartConfig.xAxisVisible ?? true} tickFormatter={primaryMeasure?.formatter} /> @@ -204,9 +205,7 @@ const BarChart: FC = forwardRef((props: BarChartProps, ref: Ref { const YAxisLabel = - index > 0 - ? useSecondaryDimensionLabel(true, dimension.formatter) - : useAxisLabel(dimension.formatter, true); + index > 0 ? useSecondaryDimensionLabel(true, dimension.formatter) : ; return ( = forwardRef((props: BarChartProps, ref: Ref + } type="monotone" dataKey={element.accessor} diff --git a/packages/charts/src/components/ColumnChart/ColumnChart.tsx b/packages/charts/src/components/ColumnChart/ColumnChart.tsx index d61826a7fa5..17d2940ee97 100644 --- a/packages/charts/src/components/ColumnChart/ColumnChart.tsx +++ b/packages/charts/src/components/ColumnChart/ColumnChart.tsx @@ -17,12 +17,14 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; +import { ChartDataLabel } from '../../internal/ChartDataLabel'; +import { XAxisTicks } from '../../internal/XAxisTicks'; interface MeasureConfig extends IChartMeasure { /** @@ -196,14 +198,13 @@ const ColumnChart: FC = forwardRef((props: ColumnChartProps, r /> {(chartConfig.xAxisVisible ?? true) && dimensions.map((dimension, index) => { - const XAxisLabel = useAxisLabel(dimension.formatter); return ( : SecondaryDimensionLabel} tickLine={index < 1} axisLine={index < 1} /> @@ -236,7 +237,7 @@ const ColumnChart: FC = forwardRef((props: ColumnChartProps, r name={element.label ?? element.accessor} strokeOpacity={element.opacity} label={ - + } type="monotone" dataKey={element.accessor} diff --git a/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx b/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx index a606eda975d..e0f189312a6 100644 --- a/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx +++ b/packages/charts/src/components/ComposedChart/ComposedChart.stories.tsx @@ -163,6 +163,7 @@ export const withReferenceLineStory = () => { onLegendClick={action('onLegendClick')} dataset={bigDataSet} dimensions={[{ accessor: 'name' }]} + layout={select('layout', ['horizontal', 'vertical'], 'horizontal')} measures={[ { accessor: 'users', diff --git a/packages/charts/src/components/ComposedChart/index.tsx b/packages/charts/src/components/ComposedChart/index.tsx index b4641c4d215..8968eaf217c 100644 --- a/packages/charts/src/components/ComposedChart/index.tsx +++ b/packages/charts/src/components/ComposedChart/index.tsx @@ -19,12 +19,14 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; +import { ChartDataLabel } from '../../internal/ChartDataLabel'; +import { XAxisTicks } from '../../internal/XAxisTicks'; const dimensionDefaults = { formatter: (d) => d @@ -244,13 +246,12 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr /> {(chartConfig.xAxisVisible ?? true) && dimensions.map((dimension, index) => { - const XAxisLabel = useAxisLabel(dimension.formatter); let AxisComponent; const axisProps = { key: dimension.accessor, dataKey: dimension.accessor, interval: dimension?.interval ?? (isBigDataSet ? 'preserveStart' : 0), - tick: index === 0 ? XAxisLabel : SecondaryDimensionLabel, + tick: index === 0 ? : SecondaryDimensionLabel, tickLine: index < 1, axisLine: index < 1, padding: { left: paddingCharts, right: paddingCharts } @@ -310,9 +311,11 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr {chartConfig.referenceLine && ( )} @@ -334,7 +337,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr chartElementProps.label = isBigDataSet ? ( false ) : ( - + ); break; case 'bar': @@ -357,7 +360,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr chartElementProps.label = isBigDataSet ? ( false ) : ( - + ); break; } @@ -373,7 +376,7 @@ const ComposedChart: FC = forwardRef((props: ComposedChartPr } + label={} stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} type="monotone" diff --git a/packages/charts/src/components/LineChart/LineChart.tsx b/packages/charts/src/components/LineChart/LineChart.tsx index 08572a25630..5f20d4f2a60 100644 --- a/packages/charts/src/components/LineChart/LineChart.tsx +++ b/packages/charts/src/components/LineChart/LineChart.tsx @@ -17,12 +17,14 @@ import { YAxis } from 'recharts'; import { useChartMargin } from '../../hooks/useChartMargin'; -import { CustomDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; +import { useSecondaryDimensionLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; +import { ChartDataLabel } from '../../internal/ChartDataLabel'; +import { XAxisTicks } from '../../internal/XAxisTicks'; interface MeasureConfig extends IChartMeasure { /** @@ -187,14 +189,13 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re /> {(chartConfig.xAxisVisible ?? true) && dimensions.map((dimension, index) => { - const XAxisLabel = useAxisLabel(dimension.formatter); return ( : SecondaryDimensionLabel} tickLine={index < 1} axisLine={index < 1} /> @@ -225,7 +226,7 @@ const LineChart: FC = forwardRef((props: LineChartProps, ref: Re key={element.accessor} name={element.label ?? element.accessor} strokeOpacity={element.opacity} - label={isBigDataSet ? false : } + label={isBigDataSet ? false : } type="monotone" dataKey={element.accessor} stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} diff --git a/packages/charts/src/components/RadarChart/RadarChart.tsx b/packages/charts/src/components/RadarChart/RadarChart.tsx index eccde741494..33e8933bd1e 100644 --- a/packages/charts/src/components/RadarChart/RadarChart.tsx +++ b/packages/charts/src/components/RadarChart/RadarChart.tsx @@ -14,12 +14,12 @@ import { RadarChart as RadarChartLib, Tooltip } from 'recharts'; -import { CustomDataLabel } from '../../hooks/useLabelElements'; import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures'; import { useTooltipFormatter } from '../../hooks/useTooltipFormatter'; import { IChartDimension } from '../../interfaces/IChartDimension'; import { IChartMeasure } from '../../interfaces/IChartMeasure'; import { RechartBaseProps } from '../../interfaces/RechartBaseProps'; +import { ChartDataLabel } from '../../internal/ChartDataLabel'; interface MeasureConfig extends IChartMeasure { /** @@ -155,7 +155,7 @@ const RadarChart: FC = forwardRef((props: RadarChartProps, ref: fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`} fillOpacity={element.opacity} label={ - = (props: CustomDataLabelProps) => { - const { config, chartType } = props; - - const viewBox = Label.parseViewBox(props); - - if (config.hideDataLabel) { - return null; - } - - if (config.DataLabel) { - return config.DataLabel(props); - } - - const formattedLabel = config.formatter(props.value ?? props.children); - if (chartType === 'bar' || chartType === 'column') { - if (viewBox.width < getTextWidth(formattedLabel)) { - return null; - } - if (viewBox.height < 12) { - return null; - } - } - - return ( -