Skip to content

Commit 05db4d4

Browse files
refactor(Charts - New): introduce new api based on dimensions and measures (#423)
1 parent 79c9f3f commit 05db4d4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+2013
-1430
lines changed

packages/base/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"build": "npm-run-all -s build:rollup build:polyfills"
2828
},
2929
"dependencies": {
30-
"core-js": "3.6.5",
30+
"core-js": "3.6.4",
3131
"intersection-observer": "0.8.0",
3232
"proxy-polyfill": "0.3.1",
3333
"resize-observer-polyfill": "1.5.1",

packages/charts/src/components/BarChart/BarChart.tsx

Lines changed: 148 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils';
21
import { ThemingParameters } from '@ui5/webcomponents-react-base/lib/ThemingParameters';
32
import { useConsolidatedRef } from '@ui5/webcomponents-react-base/lib/useConsolidatedRef';
3+
import { enrichEventWithDetails } from '@ui5/webcomponents-react-base/lib/Utils';
44
import { BarChartPlaceholder } from '@ui5/webcomponents-react-charts/lib/BarChartPlaceholder';
5-
import { useInitialize } from '@ui5/webcomponents-react-charts/lib/initialize';
65
import { ChartContainer } from '@ui5/webcomponents-react-charts/lib/next/ChartContainer';
76
import { useLegendItemClick } from '@ui5/webcomponents-react-charts/lib/useLegendItemClick';
8-
import { useResolveDataKeys } from '@ui5/webcomponents-react-charts/lib/useResolveDataKeys';
97
import React, { FC, forwardRef, Ref, useCallback } from 'react';
108
import {
119
Bar,
@@ -18,51 +16,99 @@ import {
1816
XAxis,
1917
YAxis
2018
} from 'recharts';
21-
import { RechartBaseProps } from '../../interfaces/RechartBaseProps';
22-
import { useDataLabel, useAxisLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements';
2319
import { useChartMargin } from '../../hooks/useChartMargin';
20+
import { useAxisLabel, useDataLabel, useSecondaryDimensionLabel } from '../../hooks/useLabelElements';
21+
import { usePrepareDimensionsAndMeasures } from '../../hooks/usePrepareDimensionsAndMeasures';
22+
import { useTooltipFormatter } from '../../hooks/useTooltipFormatter';
23+
import { IChartDimension } from '../../interfaces/IChartDimension';
24+
import { IChartMeasure } from '../../interfaces/IChartMeasure';
25+
import { RechartBaseProps } from '../../interfaces/RechartBaseProps';
26+
27+
const formatYAxisTicks = (tick) => {
28+
const splitTick = tick.split(' ');
29+
return splitTick.length > 3
30+
? `${splitTick.slice(0, 3).join(' ')}...`
31+
: tick.length > 11
32+
? `${tick.slice(0, 12)}...`
33+
: tick;
34+
};
2435

25-
type BarChartProps = RechartBaseProps;
36+
const dimensionDefaults = {
37+
formatter: formatYAxisTicks
38+
};
39+
40+
const measureDefaults = {
41+
formatter: (d) => d,
42+
opacity: 1
43+
};
44+
45+
interface MeasureConfig extends IChartMeasure {
46+
/**
47+
* Bar Width
48+
* @default auto
49+
*/
50+
width?: number;
51+
/**
52+
* Bar Opacity
53+
* @default 1
54+
*/
55+
opacity?: number;
56+
/**
57+
* Bar Stack ID
58+
* @default undefined
59+
*/
60+
stackId?: string;
61+
}
62+
63+
interface DimensionConfig extends IChartDimension {
64+
interval?: number;
65+
}
66+
67+
interface BarChartProps extends RechartBaseProps {
68+
dimensions: DimensionConfig[];
69+
/**
70+
* An array of config objects. Each object is defining one bar in the chart.
71+
*
72+
* <h4>Required properties</h4>
73+
* - `accessor`: string containing the path to the dataset key this bar should display. Supports object structures by using <code>'parent.child'</code>.
74+
* Can also be a getter.
75+
*
76+
* <h4>Optional properties</h4>
77+
*
78+
* - `label`: Label to display in legends or tooltips. Falls back to the <code>accessor</code> if not present.
79+
* - `color`: any valid CSS Color or CSS Variable. Defaults to the `sapChart_OrderedColor_` colors
80+
* - `formatter`: function will be called for each data label and allows you to format it according to your needs
81+
* - `hideDataLabel`: flag whether the data labels should be hidden in the chart for this bar.
82+
* - `DataLabel`: a custom component to be used for the data label
83+
* - `width`: bar width, defaults to `auto`
84+
* - `opacity`: bar opacity, defaults to `1`
85+
* - `stackId`: bars with the same stackId will be stacked
86+
*
87+
*/
88+
measures: MeasureConfig[];
89+
}
2690

2791
/**
2892
* <code>import { BarChart } from '@ui5/webcomponents-react-charts/lib/next/BarChart';</code>
2993
* **This component is under active development. The API is not stable yet and might change without further notice.**
3094
*/
3195
const BarChart: FC<BarChartProps> = forwardRef((props: BarChartProps, ref: Ref<any>) => {
3296
const {
33-
color,
3497
loading,
35-
labelKey = 'name',
36-
secondaryDimensionKey,
37-
dataKeys,
38-
width = '100%',
39-
height = '500px',
4098
dataset,
4199
noLegend = false,
42100
onDataPointClick,
43101
onLegendClick,
44-
labels,
45-
axisInterval,
46-
labelFormatter = (el) => formatYAxisTicks(el),
47-
valueFormatter = (el) => el,
48-
dataLabelCustomElement = undefined,
49102
chartConfig = {
50103
margin: {},
51-
yAxisVisible: false,
104+
yAxisVisible: true,
52105
xAxisVisible: true,
53-
xAxisUnit: '',
54-
yAxisUnit: '',
55106
gridStroke: ThemingParameters.sapList_BorderColor,
56107
gridHorizontal: true,
57108
gridVertical: false,
58109
legendPosition: 'top',
59-
barSize: undefined,
60110
barGap: 3,
61111
zoomingTool: false,
62-
strokeOpacity: 1,
63-
fillOpacity: 1,
64-
stacked: false,
65-
dataLabel: true,
66112
referenceLine: {
67113
label: undefined,
68114
value: undefined,
@@ -74,22 +120,22 @@ const BarChart: FC<BarChartProps> = forwardRef((props: BarChartProps, ref: Ref<a
74120
tooltip,
75121
slot
76122
} = props;
77-
useInitialize();
78123

79-
const chartRef = useConsolidatedRef<any>(ref);
124+
const { dimensions, measures } = usePrepareDimensionsAndMeasures(
125+
props.dimensions,
126+
props.measures,
127+
dimensionDefaults,
128+
measureDefaults
129+
);
80130

81-
const currentDataKeys = useResolveDataKeys(dataKeys, labelKey, dataset, secondaryDimensionKey);
131+
const tooltipValueFormatter = useTooltipFormatter(measures);
82132

83-
const onItemLegendClick = useLegendItemClick(onLegendClick);
133+
const primaryDimension = dimensions[0];
134+
const primaryMeasure = measures[0];
84135

85-
const formatYAxisTicks = (tick) => {
86-
const splitTick = tick.split(' ');
87-
return splitTick.length > 3
88-
? `${splitTick.slice(0, 3).join(' ')}...`
89-
: tick.length > 11
90-
? `${tick.slice(0, 12)}...`
91-
: tick;
92-
};
136+
const chartRef = useConsolidatedRef<any>(ref);
137+
138+
const onItemLegendClick = useLegendItemClick(onLegendClick);
93139

94140
const onDataPointClickInternal = useCallback(
95141
(payload, i, event) => {
@@ -110,36 +156,24 @@ const BarChart: FC<BarChartProps> = forwardRef((props: BarChartProps, ref: Ref<a
110156
[onDataPointClick]
111157
);
112158

113-
const BarDataLabel = useDataLabel(
114-
chartConfig.dataLabel,
115-
dataLabelCustomElement,
116-
valueFormatter,
117-
chartConfig.stacked,
118-
true
119-
);
159+
const isBigDataSet = dataset?.length > 30 ?? false;
160+
const primaryDimensionAccessor = primaryDimension?.accessor;
120161

121162
const marginChart = useChartMargin(
122163
dataset,
123-
labelFormatter,
124-
labelKey,
164+
(d) => d,
165+
primaryDimensionAccessor,
125166
chartConfig.margin,
126167
true,
127-
secondaryDimensionKey,
168+
dimensions.length > 1,
128169
chartConfig.zoomingTool
129170
);
130171

131-
const XAxisLabel = useAxisLabel(valueFormatter, chartConfig.xAxisUnit);
132-
const YAxisLabel = useAxisLabel(labelFormatter, chartConfig.yAxisUnit, true);
133-
const SecondaryDimensionLabel = useSecondaryDimensionLabel(true, labelFormatter);
134-
const bigDataSet = dataset?.length > 30 ?? false;
135-
136172
return (
137173
<ChartContainer
138174
dataset={dataset}
139175
loading={loading}
140-
placeholder={BarChartPlaceholder}
141-
width={width}
142-
height={height}
176+
Placeholder={BarChartPlaceholder}
143177
ref={chartRef}
144178
style={style}
145179
className={className}
@@ -152,43 +186,60 @@ const BarChart: FC<BarChartProps> = forwardRef((props: BarChartProps, ref: Ref<a
152186
horizontal={chartConfig.gridHorizontal}
153187
stroke={chartConfig.gridStroke ?? ThemingParameters.sapList_BorderColor}
154188
/>
155-
{(chartConfig.xAxisVisible ?? true) && <XAxis interval={0} type="number" tick={XAxisLabel} />}
156-
<YAxis
157-
unit={chartConfig.yAxisUnit}
158-
axisLine={chartConfig.yAxisVisible ?? false}
159-
tickLine={false}
160-
tick={YAxisLabel}
161-
type="category"
162-
dataKey={labelKey}
163-
interval={axisInterval ?? bigDataSet ? 2 : 0}
164-
yAxisId={0}
165-
/>
166-
{secondaryDimensionKey && (
167-
<YAxis
189+
{(chartConfig.xAxisVisible ?? true) && (
190+
<XAxis
168191
interval={0}
169-
type={'category'}
170-
dataKey={'dimension'}
171-
tickLine={false}
172-
tick={SecondaryDimensionLabel}
173-
axisLine={false}
174-
yAxisId={1}
192+
type="number"
193+
axisLine={chartConfig.xAxisVisible ?? true}
194+
tickFormatter={primaryMeasure?.formatter}
175195
/>
176196
)}
177-
{currentDataKeys.map((key, index) => (
178-
<Bar
179-
stackId={chartConfig.stacked ? 'A' : undefined}
180-
strokeOpacity={chartConfig.strokeOpacity}
181-
fillOpacity={chartConfig.fillOpacity}
182-
label={BarDataLabel}
183-
key={key}
184-
name={labels?.[key] || key}
185-
dataKey={key}
186-
fill={color ?? `var(--sapUiChartAccent${(index % 12) + 1})`}
187-
stroke={color ?? `var(--sapUiChartAccent${(index % 12) + 1})`}
188-
barSize={chartConfig.barSize}
189-
onClick={onDataPointClickInternal}
190-
/>
191-
))}
197+
{(chartConfig.yAxisVisible ?? true) &&
198+
dimensions.map((dimension, index) => {
199+
const YAxisLabel =
200+
index > 0
201+
? useSecondaryDimensionLabel(true, dimension.formatter)
202+
: useAxisLabel(dimension.formatter, true);
203+
return (
204+
<YAxis
205+
type="category"
206+
key={dimension.accessor}
207+
dataKey={dimension.accessor}
208+
xAxisId={index}
209+
interval={dimension.interval ?? isBigDataSet ? 2 : 0}
210+
tick={YAxisLabel}
211+
tickLine={index < 1}
212+
axisLine={index < 1}
213+
yAxisId={index}
214+
/>
215+
);
216+
})}
217+
{measures.map((element, index) => {
218+
const ColumnDataLabel = useDataLabel(
219+
!element.hideDataLabel,
220+
element.DataLabel,
221+
element.formatter,
222+
false,
223+
true,
224+
false
225+
);
226+
return (
227+
<Bar
228+
stackId={element.stackId}
229+
fillOpacity={element.opacity}
230+
key={element.accessor}
231+
name={element.label ?? element.accessor}
232+
strokeOpacity={element.opacity}
233+
label={isBigDataSet ? false : ColumnDataLabel}
234+
type="monotone"
235+
dataKey={element.accessor}
236+
fill={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`}
237+
stroke={element.color ?? `var(--sapChart_OrderedColor_${(index % 11) + 1})`}
238+
barSize={element.width}
239+
onClick={onDataPointClickInternal}
240+
/>
241+
);
242+
})}
192243
{!noLegend && <Legend verticalAlign={chartConfig.legendPosition ?? 'top'} onClick={onItemLegendClick} />}
193244
{chartConfig.referenceLine && (
194245
<ReferenceLine
@@ -197,9 +248,15 @@ const BarChart: FC<BarChartProps> = forwardRef((props: BarChartProps, ref: Ref<a
197248
label={chartConfig.referenceLine.label}
198249
/>
199250
)}
200-
<Tooltip cursor={{ fillOpacity: 0.3 }} labelFormatter={valueFormatter} />
251+
<Tooltip cursor={{ fillOpacity: 0.3 }} formatter={tooltipValueFormatter} />
201252
{chartConfig.zoomingTool && (
202-
<Brush y={0} dataKey={labelKey} stroke={`var(--sapUiChartAccent6)`} travellerWidth={10} height={20} />
253+
<Brush
254+
y={0}
255+
dataKey={primaryDimensionAccessor}
256+
stroke={ThemingParameters.sapObjectHeader_BorderColor}
257+
travellerWidth={10}
258+
height={20}
259+
/>
203260
)}
204261
</BarChartLib>
205262
</ChartContainer>

0 commit comments

Comments
 (0)