@centreon/ui 25.11.9 → 26.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "25.11.9",
3
+ "version": "26.1.0",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -87,6 +87,61 @@ const checkWidth = (orientation): void => {
87
87
  };
88
88
 
89
89
  describe('Bar chart', () => {
90
+ it('displays a tooltip when a single bar is hovered', () => {
91
+ initialize({
92
+ orientation: 'horizontal'
93
+ });
94
+
95
+ checkWidth('horizontal');
96
+ cy.contains('0 ms').should('be.visible');
97
+ cy.contains('20').should('be.visible');
98
+ cy.contains(':40 AM').should('be.visible');
99
+
100
+ cy.findByTestId('stacked-bar-10-0-7650.368581547736').realHover();
101
+
102
+ cy.contains('06/19/2024').should('be.visible');
103
+ cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
104
+ 'be.visible'
105
+ );
106
+ cy.contains('7.47 KB').should('be.visible');
107
+
108
+ cy.makeSnapshot();
109
+ });
110
+
111
+ it('displays a tooltip when a stacked bar is hovered', () => {
112
+ initialize({
113
+ data: dataPingServiceStacked,
114
+ orientation: 'horizontal',
115
+ tooltip: {
116
+ mode: 'all',
117
+ sortOrder: 'ascending'
118
+ }
119
+ });
120
+
121
+ checkWidth('horizontal');
122
+ cy.contains('0 ms').should('be.visible');
123
+ cy.contains('20').should('be.visible');
124
+ cy.contains(':40 AM').should('be.visible');
125
+
126
+ cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
127
+
128
+ cy.contains('06/19/2024').should('be.visible');
129
+ cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
130
+ 'be.visible'
131
+ );
132
+ cy.contains('Centreon-Server: Round-Trip Average Time').should(
133
+ 'be.visible'
134
+ );
135
+ cy.contains('Centreon-Server: Round-Trip Minimum Time').should(
136
+ 'be.visible'
137
+ );
138
+ cy.contains('0.05 ms').should('be.visible');
139
+ cy.contains('0.02 ms').should('be.visible');
140
+ cy.contains('0.11 ms').should('be.visible');
141
+
142
+ cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
143
+ });
144
+
90
145
  ['horizontal', 'vertical'].forEach((orientation) => {
91
146
  it(`displays the bar chart ${orientation}ly`, () => {
92
147
  initialize({ orientation });
@@ -186,27 +241,6 @@ describe('Bar chart', () => {
186
241
  });
187
242
  });
188
243
 
189
- it('displays a tooltip when a single bar is hovered', () => {
190
- initialize({
191
- orientation: 'horizontal'
192
- });
193
-
194
- checkWidth('horizontal');
195
- cy.contains('0 ms').should('be.visible');
196
- cy.contains('20').should('be.visible');
197
- cy.contains(':40 AM').should('be.visible');
198
-
199
- cy.findByTestId('stacked-bar-10-0-7650.368581547736').realHover();
200
-
201
- cy.contains('06/19/2024').should('be.visible');
202
- cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
203
- 'be.visible'
204
- );
205
- cy.contains('7.47 KB').should('be.visible');
206
-
207
- cy.makeSnapshot();
208
- });
209
-
210
244
  it('does not display a tooltip when a bar is hovered and a props is set', () => {
211
245
  initialize({
212
246
  data: dataPingServiceStacked,
@@ -231,68 +265,6 @@ describe('Bar chart', () => {
231
265
  cy.makeSnapshot();
232
266
  });
233
267
 
234
- it('displays a tooltip when a stacked bar is hovered', () => {
235
- initialize({
236
- data: dataPingServiceStacked,
237
- orientation: 'horizontal',
238
- tooltip: {
239
- mode: 'all',
240
- sortOrder: 'ascending'
241
- }
242
- });
243
-
244
- checkWidth('horizontal');
245
- cy.contains('0 ms').should('be.visible');
246
- cy.contains('20').should('be.visible');
247
- cy.contains(':40 AM').should('be.visible');
248
-
249
- cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
250
-
251
- cy.contains('06/19/2024').should('be.visible');
252
- cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
253
- 'be.visible'
254
- );
255
- cy.contains('Centreon-Server: Round-Trip Average Time').should(
256
- 'be.visible'
257
- );
258
- cy.contains('Centreon-Server: Round-Trip Minimum Time').should(
259
- 'be.visible'
260
- );
261
- cy.contains('0.05 ms').should('be.visible');
262
- cy.contains('0.02 ms').should('be.visible');
263
- cy.contains('0.11 ms').should('be.visible');
264
-
265
- cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
266
- });
267
-
268
- it('displays a tooltip with a single metric when a stacked bar is hovered and a prop is set', () => {
269
- initialize({
270
- data: dataPingServiceStacked,
271
- orientation: 'horizontal',
272
- tooltip: {
273
- mode: 'single',
274
- sortOrder: 'descending'
275
- }
276
- });
277
-
278
- checkWidth('horizontal');
279
- cy.contains('0 ms').should('be.visible');
280
- cy.contains('20').should('be.visible');
281
- cy.contains(':40 AM').should('be.visible');
282
-
283
- cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
284
-
285
- cy.contains('06/19/2024').should('be.visible');
286
- cy.contains('Centreon-Server: Round-Trip Average Time').should(
287
- 'be.visible'
288
- );
289
- cy.contains('0.05 ms').should('be.visible');
290
-
291
- cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
292
-
293
- cy.makeSnapshot();
294
- });
295
-
296
268
  it('displays the bottom axis correctly when data starts from several days ago', () => {
297
269
  initialize({
298
270
  data: dataLastWeek,
@@ -101,6 +101,10 @@ const BarChart = ({
101
101
  );
102
102
  }
103
103
 
104
+ if (!adjustedData) {
105
+ return <div />;
106
+ }
107
+
104
108
  return (
105
109
  <Provider>
106
110
  <Box ref={ref} sx={{ height: '100%', overflow: 'hidden', width: '100%' }}>
@@ -1,4 +1,4 @@
1
- import { memo } from 'react';
1
+ import { memo, ReactElement } from 'react';
2
2
 
3
3
  import { ScaleType, scaleBand } from '@visx/scale';
4
4
  import { BarRounded } from '@visx/shape';
@@ -22,6 +22,7 @@ interface Props extends Omit<UseBarStackProps, 'xScale'> {
22
22
  barWidth: number;
23
23
  isTooltipHidden: boolean;
24
24
  neutralValue: number;
25
+ isStacked?: boolean;
25
26
  }
26
27
 
27
28
  const getPadding = ({ padding, size, isNegativeValue }): number => {
@@ -83,8 +84,9 @@ const BarStack = ({
83
84
  barIndex,
84
85
  isTooltipHidden,
85
86
  barStyle = { opacity: 1, radius: 0.2 },
86
- neutralValue
87
- }: Props): JSX.Element => {
87
+ neutralValue,
88
+ isStacked
89
+ }: Props): ReactElement => {
88
90
  const {
89
91
  BarStackComponent,
90
92
  commonBarStackProps,
@@ -118,6 +120,15 @@ const BarStack = ({
118
120
  metricId: Number(bar.key)
119
121
  }) as BarStyle;
120
122
 
123
+ const barY =
124
+ isNegativeValue && isStacked && !shouldApplyRadiusOnBottom
125
+ ? getPadding({
126
+ isNegativeValue,
127
+ padding: bar.y,
128
+ size: bar.height
129
+ })
130
+ : bar.y;
131
+
121
132
  return (
122
133
  <BarRounded
123
134
  {...barRoundedProps}
@@ -128,10 +139,10 @@ const BarStack = ({
128
139
  barWidth,
129
140
  y: isHorizontal
130
141
  ? getPadding({
131
- isNegativeValue,
132
- padding: bar.y,
133
- size: bar.height
134
- })
142
+ isNegativeValue,
143
+ padding: bar.y,
144
+ size: bar.height
145
+ })
135
146
  : barPadding,
136
147
  isFirstBar: shouldApplyRadiusOnBottom,
137
148
  isHorizontal,
@@ -146,19 +157,19 @@ const BarStack = ({
146
157
  isHorizontal
147
158
  ? barPadding
148
159
  : getPadding({
149
- isNegativeValue,
150
- padding: bar.x,
151
- size: bar.width
152
- })
160
+ isNegativeValue,
161
+ padding: bar.x,
162
+ size: bar.width
163
+ })
153
164
  }
154
- y={isHorizontal ? bar.y : barPadding}
165
+ y={isHorizontal ? barY : barPadding}
155
166
  onMouseEnter={
156
167
  isTooltipHidden
157
168
  ? undefined
158
169
  : hoverBar({
159
- barIndex,
160
- highlightedMetric: Number(bar.key)
161
- })
170
+ barIndex,
171
+ highlightedMetric: Number(bar.key)
172
+ })
162
173
  }
163
174
  onMouseLeave={isTooltipHidden ? undefined : exitBar}
164
175
  />
@@ -59,17 +59,18 @@ const MemoizedGroup = ({
59
59
  const linesBar = isStackedBar
60
60
  ? stackedLinesTimeSeriesPerStackKeyAndUnit[bar.key].lines
61
61
  : (notStackedLines.find(({ metric_id }) =>
62
- equals(metric_id, Number(bar.key))
63
- ) as Line);
62
+ equals(metric_id, Number(bar.key))
63
+ ) as Line);
64
64
  const timeSeriesBar = isStackedBar
65
65
  ? stackedLinesTimeSeriesPerStackKeyAndUnit[bar.key].timeSeries
66
66
  : notStackedTimeSeries.map((timeSerie) => ({
67
- timeTick: timeSerie.timeTick,
68
- [bar.key]: timeSerie[Number(bar.key)]
69
- }));
67
+ timeTick: timeSerie.timeTick,
68
+ [bar.key]: timeSerie[Number(bar.key)]
69
+ }));
70
70
 
71
71
  return isStackedBar ? (
72
72
  <BarStack
73
+ isStacked
73
74
  key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
74
75
  barIndex={barGroup.index}
75
76
  barPadding={isHorizontal ? bar.x : bar.y}
@@ -79,7 +80,7 @@ const MemoizedGroup = ({
79
80
  isTooltipHidden={isTooltipHidden}
80
81
  lines={linesBar as Array<Line>}
81
82
  timeSeries={timeSeriesBar}
82
- yScale={yScalesPerUnit[bar.key.split('-')[1] || undefined]}
83
+ yScale={yScalesPerUnit[bar.key.split('-')[1] ?? undefined]}
83
84
  neutralValue={neutralValue}
84
85
  />
85
86
  ) : (
@@ -93,7 +94,7 @@ const MemoizedGroup = ({
93
94
  isTooltipHidden={isTooltipHidden}
94
95
  lines={[linesBar as Line]}
95
96
  timeSeries={timeSeriesBar}
96
- yScale={yScalesPerUnit[(linesBar as Line).unit]}
97
+ yScale={yScalesPerUnit[(linesBar as Line).unit ?? undefined]}
97
98
  neutralValue={neutralValue}
98
99
  />
99
100
  );
@@ -27,6 +27,7 @@ import { StackValue } from '../../../InteractiveComponents/AnchorPoint/models';
27
27
  import { getCurveFactory, getFillColor } from '../../../common';
28
28
  import { LineStyle } from '../../../models';
29
29
  import Point from '../Point';
30
+ import { ReactElement } from 'react';
30
31
 
31
32
  interface Props {
32
33
  areaTransparency?: number;
@@ -56,7 +57,7 @@ const StackLines = ({
56
57
  lineStyle,
57
58
  hasSecondUnit,
58
59
  maxLeftAxisCharacters
59
- }: Props): JSX.Element => {
60
+ }: Props): ReactElement => {
60
61
  const curveType = getCurveFactory(
61
62
  (equals(type(lineStyle), 'Array')
62
63
  ? lineStyle?.[0].curve
@@ -134,9 +135,9 @@ const StackLines = ({
134
135
  equals(style?.showArea, false)
135
136
  ? 'transparent'
136
137
  : getFillColor({
137
- areaColor: areaColor || lineColor,
138
- transparency: formattedTransparency
139
- })
138
+ areaColor: areaColor || lineColor,
139
+ transparency: formattedTransparency
140
+ })
140
141
  }
141
142
  opacity={highlight === false ? 0.3 : 1}
142
143
  stroke={lineColor}
@@ -109,7 +109,7 @@ const Lines = ({
109
109
  key={`stacked-${unit}`}
110
110
  lines={lines}
111
111
  timeSeries={stackedTimeSeries}
112
- yScale={yScalesPerUnit[unit || undefined]}
112
+ yScale={yScalesPerUnit[unit ?? undefined]}
113
113
  {...commonStackedLinesProps}
114
114
  />
115
115
  );
@@ -128,7 +128,7 @@ const Lines = ({
128
128
  invert: '1',
129
129
  scale,
130
130
  scaleLogarithmicBase,
131
- unit: unit || undefined,
131
+ unit: unit ?? undefined,
132
132
  yScalesPerUnit
133
133
  })}
134
134
  {...commonStackedLinesProps}
@@ -152,107 +152,107 @@ const Lines = ({
152
152
 
153
153
  {displayAreaRegularLines
154
154
  ? regularLines.map(
155
- ({
156
- areaColor,
157
- transparency,
158
- lineColor,
159
- filled,
155
+ ({
156
+ areaColor,
157
+ transparency,
158
+ lineColor,
159
+ filled,
160
+ unit,
161
+ highlight,
162
+ invert,
163
+ metric_id,
164
+ ...rest
165
+ }) => {
166
+ const yScale = getYScale({
167
+ invert,
168
+ scale,
169
+ scaleLogarithmicBase,
160
170
  unit,
161
- highlight,
171
+ yScalesPerUnit
172
+ });
173
+ const relatedTimeSeries = getTimeSeriesForLines({
162
174
  invert,
163
- metric_id,
164
- ...rest
165
- }) => {
166
- const yScale = getYScale({
167
- invert,
168
- scale,
169
- scaleLogarithmicBase,
170
- unit,
171
- yScalesPerUnit
172
- });
173
- const relatedTimeSeries = getTimeSeriesForLines({
174
- invert,
175
- lines: [
176
- {
177
- areaColor,
178
- filled,
179
- highlight,
180
- invert,
181
- lineColor,
182
- metric_id,
183
- transparency,
184
- unit,
185
- ...rest
186
- }
187
- ],
188
- timeSeries
189
- });
175
+ lines: [
176
+ {
177
+ areaColor,
178
+ filled,
179
+ highlight,
180
+ invert,
181
+ lineColor,
182
+ metric_id,
183
+ transparency,
184
+ unit,
185
+ ...rest
186
+ }
187
+ ],
188
+ timeSeries
189
+ });
190
190
 
191
- const style = getStyle({
192
- style: lineStyle,
193
- metricId: metric_id
194
- }) as LineStyle;
191
+ const style = getStyle({
192
+ style: lineStyle,
193
+ metricId: metric_id
194
+ }) as LineStyle;
195
195
 
196
- return (
197
- <g key={metric_id}>
198
- {displayGuidingLines && (
199
- <RegularAnchorPoint
200
- areaColor={areaColor || lineColor}
201
- lineColor={lineColor}
202
- metric_id={metric_id}
203
- timeSeries={relatedTimeSeries}
204
- transparency={transparency}
205
- xScale={xScale}
206
- yScale={yScale}
207
- maxLeftAxisCharacters={maxLeftAxisCharacters}
208
- hasSecondUnit={hasSecondUnit}
209
- />
210
- )}
211
- {style?.showPoints &&
212
- getDates(relatedTimeSeries).map((timeTick) => (
213
- <Point
214
- key={timeTick.toString()}
215
- lineColor={lineColor}
216
- metric_id={metric_id}
217
- radius={getPointRadius(style?.lineWidth)}
218
- timeSeries={relatedTimeSeries}
219
- timeTick={timeTick}
220
- xScale={xScale}
221
- yPoint={getYAnchorPoint({
222
- metric_id,
223
- timeSeries: relatedTimeSeries,
224
- timeTick,
225
- yScale
226
- })}
227
- yScale={yScale}
228
- />
229
- ))}
230
- <RegularLine
196
+ return (
197
+ <g key={metric_id}>
198
+ {displayGuidingLines && (
199
+ <RegularAnchorPoint
231
200
  areaColor={areaColor || lineColor}
232
- curve={style?.curve || 'linear'}
233
- dashLength={style?.dashLength}
234
- dashOffset={style?.dashOffset}
235
- dotOffset={style?.dotOffset}
236
- filled={isNil(style?.showArea) ? filled : style.showArea}
237
- graphHeight={height}
238
- highlight={highlight}
239
201
  lineColor={lineColor}
240
- lineWidth={style?.lineWidth || 2}
241
202
  metric_id={metric_id}
242
203
  timeSeries={relatedTimeSeries}
243
- transparency={
244
- isNil(style?.areaTransparency)
245
- ? transparency || 80
246
- : style.areaTransparency
247
- }
248
- unit={unit}
204
+ transparency={transparency}
249
205
  xScale={xScale}
250
206
  yScale={yScale}
207
+ maxLeftAxisCharacters={maxLeftAxisCharacters}
208
+ hasSecondUnit={hasSecondUnit}
251
209
  />
252
- </g>
253
- );
254
- }
255
- )
210
+ )}
211
+ {style?.showPoints &&
212
+ getDates(relatedTimeSeries).map((timeTick) => (
213
+ <Point
214
+ key={timeTick.toString()}
215
+ lineColor={lineColor}
216
+ metric_id={metric_id}
217
+ radius={getPointRadius(style?.lineWidth)}
218
+ timeSeries={relatedTimeSeries}
219
+ timeTick={timeTick}
220
+ xScale={xScale}
221
+ yPoint={getYAnchorPoint({
222
+ metric_id,
223
+ timeSeries: relatedTimeSeries,
224
+ timeTick,
225
+ yScale
226
+ })}
227
+ yScale={yScale}
228
+ />
229
+ ))}
230
+ <RegularLine
231
+ areaColor={areaColor || lineColor}
232
+ curve={style?.curve || 'linear'}
233
+ dashLength={style?.dashLength}
234
+ dashOffset={style?.dashOffset}
235
+ dotOffset={style?.dotOffset}
236
+ filled={isNil(style?.showArea) ? filled : style.showArea}
237
+ graphHeight={height}
238
+ highlight={highlight}
239
+ lineColor={lineColor}
240
+ lineWidth={style?.lineWidth || 2}
241
+ metric_id={metric_id}
242
+ timeSeries={relatedTimeSeries}
243
+ transparency={
244
+ isNil(style?.areaTransparency)
245
+ ? transparency || 80
246
+ : style.areaTransparency
247
+ }
248
+ unit={unit}
249
+ xScale={xScale}
250
+ yScale={yScale}
251
+ />
252
+ </g>
253
+ );
254
+ }
255
+ )
256
256
  : null}
257
257
  </g>
258
258
  );
@@ -1,5 +1,6 @@
1
1
  import {
2
2
  type MutableRefObject,
3
+ ReactElement,
3
4
  useEffect,
4
5
  useMemo,
5
6
  useRef,
@@ -117,7 +118,7 @@ const Chart = ({
117
118
  min,
118
119
  max,
119
120
  boundariesUnit
120
- }: Props): JSX.Element => {
121
+ }: Props): ReactElement => {
121
122
  const { classes } = useChartStyles();
122
123
 
123
124
  const { title, timeSeries, baseAxis, lines } = graphData;
@@ -202,7 +203,8 @@ const Chart = ({
202
203
  valueGraphHeight: graphHeight - margin.bottom,
203
204
  min,
204
205
  max,
205
- boundariesUnit
206
+ boundariesUnit,
207
+ isFilled: lineStyle?.showArea
206
208
  }),
207
209
  [
208
210
  linesGraph,
@@ -81,17 +81,17 @@ const MainLegend = ({
81
81
 
82
82
  const contextMenuClick =
83
83
  (metricId: number) =>
84
- (event: MouseEvent): void => {
85
- if (!secondaryClick) {
86
- return;
87
- }
88
- event.preventDefault();
89
- secondaryClick({
90
- element: event.target,
91
- metricId,
92
- position: [event.pageX, event.pageY]
93
- });
94
- };
84
+ (event: MouseEvent): void => {
85
+ if (!secondaryClick) {
86
+ return;
87
+ }
88
+ event.preventDefault();
89
+ secondaryClick({
90
+ element: event.target,
91
+ metricId,
92
+ position: [event.pageX, event.pageY]
93
+ });
94
+ };
95
95
 
96
96
  const selectMetric = ({
97
97
  event,
@@ -120,16 +120,17 @@ const MainLegend = ({
120
120
 
121
121
  return (
122
122
  <div
123
- className={`overflow-x-hidden overflow-y-auto ${!equals(placement, 'bottom') ? 'h-full mt-[15px]' : 'ml-[50px] mr-[40px]'} legend`}
123
+ className={`overflow-x-hidden overflow-y-auto ${!equals(placement, 'bottom') ? 'h-full mt-[15px]' : 'ml-[50px] mr-[40px]'}`}
124
124
  data-display-side={!equals(placement, 'bottom')}
125
125
  >
126
126
  <ul
127
- className={`list-none flex gap-3 w-full ${!isListMode && equals(placement, 'bottom') && 'flex-wrap'} ${isListMode || !equals(placement, 'bottom') ? 'flex-col h-full w-fit' : ''} ${equals(placement, 'bottom') ? 'max-h-17' : 'max-h-0'}`}
127
+ className={`list-none flex gap-3 w-full overflow-y-auto ${!isListMode && equals(placement, 'bottom') && 'flex-wrap'} ${isListMode || !equals(placement, 'bottom') ? 'flex-col h-full w-fit' : ''} ${equals(placement, 'bottom') ? 'max-h-17' : 'max-h-fit'} ${!equals(placement, 'bottom') ? 'overflow-x-hidden' : ''}`}
128
128
  style={{
129
129
  height: equals(placement, 'bottom') ? 'auto' : `${graphHeight}px`
130
130
  }}
131
131
  data-as-list={isListMode || !equals(placement, 'bottom')}
132
132
  data-mode={itemMode}
133
+ data-legend
133
134
  >
134
135
  {displayedLines.map((line) => {
135
136
  const { color, display, metric_id, unit } = line;
@@ -155,7 +156,7 @@ const MainLegend = ({
155
156
 
156
157
  return (
157
158
  <li
158
- className={`${!display ? 'text-text-disabled' : 'text-text-primary'} flex gap-1 ${toggable && 'cursor-pointer'}`}
159
+ className={`${!display ? 'text-text-disabled' : 'text-text-primary'} flex gap-1 ${toggable && 'cursor-pointer'} ${!equals(placement, 'bottom') ? 'w-fit' : ''}`}
159
160
  key={metric_id}
160
161
  onClick={(event): void => selectMetric({ event, metric_id })}
161
162
  onKeyUp={(event) =>
@@ -102,6 +102,10 @@ const WrapperChart = ({
102
102
  );
103
103
  }
104
104
 
105
+ if (!adjustedData) {
106
+ return <div />;
107
+ }
108
+
105
109
  return (
106
110
  <div
107
111
  ref={ref}
@@ -33,7 +33,8 @@ import {
33
33
  reject,
34
34
  sortBy,
35
35
  split,
36
- uniq
36
+ uniq,
37
+ isNotNil
37
38
  } from 'ramda';
38
39
 
39
40
  import { margin } from '../../Chart/common';
@@ -160,8 +161,8 @@ const getMetrics = (timeValue: TimeValue): Array<string> =>
160
161
 
161
162
  const getValueForMetric =
162
163
  (timeValue: TimeValue) =>
163
- (metric_id: number): number =>
164
- prop(metric_id, timeValue) as number;
164
+ (metric_id: number): number =>
165
+ prop(metric_id, timeValue) as number;
165
166
 
166
167
  const getUnits = (lines: Array<Line>): Array<string> =>
167
168
  pipe(map(prop('unit')), uniq)(lines);
@@ -308,8 +309,8 @@ const getTimeSeriesForLines = ({
308
309
  ...acc,
309
310
  [metric_id]:
310
311
  invert &&
311
- metricsValue[metric_id] &&
312
- gt(metricsValue[metric_id], 0)
312
+ metricsValue[metric_id] &&
313
+ gt(metricsValue[metric_id], 0)
313
314
  ? negate(metricsValue[metric_id])
314
315
  : metricsValue[metric_id]
315
316
  };
@@ -342,10 +343,10 @@ const getYScale = ({
342
343
 
343
344
  return invert
344
345
  ? getScaleType(scale)({
345
- base: scaleLogarithmicBase,
346
- domain: yScale.domain().reverse(),
347
- range: yScale.range().reverse()
348
- })
346
+ base: scaleLogarithmicBase,
347
+ domain: yScale.domain().reverse(),
348
+ range: yScale.range().reverse()
349
+ })
349
350
  : yScale;
350
351
  };
351
352
 
@@ -382,6 +383,7 @@ const getScale = ({
382
383
  invert,
383
384
  hasDisplayAsBar,
384
385
  hasLineFilled,
386
+ hasStackedLines,
385
387
  min,
386
388
  max
387
389
  }): ScaleLinear<number, number> => {
@@ -389,30 +391,42 @@ const getScale = ({
389
391
  const sanitizedValuesForMinimum = min
390
392
  ? [min]
391
393
  : getSanitizedValues([
392
- invert && graphValues.every(lt(0))
393
- ? negate(getMax(graphValues))
394
- : getMin(graphValues),
395
- !isEmpty(stackedValues) &&
396
- !equals(stackedValues, [0]) &&
397
- getMin(stackedValues),
398
- Math.min(...thresholds)
399
- ]);
400
- const minValue = Math.min(...sanitizedValuesForMinimum);
401
- const minValueWithMargin =
402
- (hasDisplayAsBar || hasLineFilled) && minValue > 0 && !min
403
- ? 0
404
- : minValue - Math.abs(minValue) * 0.05;
394
+ invert && graphValues.every(lt(0))
395
+ ? negate(getMax(graphValues))
396
+ : getMin(graphValues),
397
+ !isEmpty(stackedValues) &&
398
+ !equals(stackedValues, [0]) &&
399
+ getMin(stackedValues),
400
+ Math.min(...thresholds)
401
+ ]);
402
+ const minValue = Math.min(...sanitizedValuesForMinimum.filter(isNotNil));
405
403
 
406
404
  const sanitizedValuesForMaximum = max
407
405
  ? [max]
408
406
  : getSanitizedValues([
409
- getMax(graphValues),
410
- getMax(stackedValues),
411
- hasOnlyZeroesHasValue(graphValues) ? 1 : 0,
412
- Math.max(...thresholds)
413
- ]);
414
- const maxValue = Math.max(...sanitizedValuesForMaximum);
415
- const maxValueWithMargin = maxValue + Math.abs(maxValue) * 0.05;
407
+ getMax(graphValues),
408
+ getMax(stackedValues),
409
+ hasOnlyZeroesHasValue(graphValues) ? 1 : null,
410
+ Math.max(...thresholds)
411
+ ]);
412
+ const maxValue = Math.max(...sanitizedValuesForMaximum.filter(isNotNil));
413
+
414
+ const minValueWithMargin =
415
+ (hasDisplayAsBar && minValue > 0) ||
416
+ (hasLineFilled &&
417
+ Math.max(maxValue, minValue) > minValue &&
418
+ minValue > 0) ||
419
+ (hasStackedLines && minValue > maxValue)
420
+ ? 0
421
+ : minValue - Math.abs(minValue) * 0.05;
422
+ const maxValueWithMargin =
423
+ (hasDisplayAsBar && maxValue < 0) ||
424
+ (hasLineFilled &&
425
+ Math.min(maxValue, minValue) < maxValue &&
426
+ maxValue < 0) ||
427
+ (hasStackedLines && minValue > maxValue)
428
+ ? 0
429
+ : maxValue + Math.abs(maxValue) * 0.05;
416
430
 
417
431
  const scaleType = getScaleType(scale);
418
432
 
@@ -479,7 +493,8 @@ const getYScaleUnit = ({
479
493
  min,
480
494
  max,
481
495
  isBarChart,
482
- boundariesUnit
496
+ boundariesUnit,
497
+ isFilled
483
498
  }: AxeScale & {
484
499
  invert?: boolean | string | null;
485
500
  unit: string;
@@ -487,6 +502,7 @@ const getYScaleUnit = ({
487
502
  min?: number;
488
503
  boundariesUnit?: string;
489
504
  isBarChart?: boolean;
505
+ isFilled?: boolean;
490
506
  }): ScaleLinear<number, number> => {
491
507
  const [firstUnit] = getUnits(dataLines);
492
508
  const shouldApplyThresholds =
@@ -505,12 +521,12 @@ const getYScaleUnit = ({
505
521
 
506
522
  const stackedValues = hasStackedLines
507
523
  ? getStackedMetricValues({
508
- lines: getSortedStackedLines(dataLines).filter(
509
- ({ unit: stackedUnit }) => equals(unit, stackedUnit)
510
- ),
511
- timeSeries: dataTimeSeries
512
- })
513
- : [0];
524
+ lines: getSortedStackedLines(dataLines).filter(
525
+ ({ unit: stackedUnit }) => equals(unit, stackedUnit)
526
+ ),
527
+ timeSeries: dataTimeSeries
528
+ })
529
+ : [];
514
530
 
515
531
  return getScale({
516
532
  graphValues,
@@ -520,8 +536,14 @@ const getYScaleUnit = ({
520
536
  ({ displayAs, unit: lineUnit }) =>
521
537
  equals(unit, lineUnit) && equals(displayAs, 'bar')
522
538
  ),
523
- hasLineFilled: dataLines.some(
524
- ({ unit: lineUnit, filled }) => equals(unit, lineUnit) && filled
539
+ hasLineFilled: isNil(isFilled)
540
+ ? dataLines.some(
541
+ ({ unit: lineUnit, filled }) => equals(unit, lineUnit) && filled
542
+ )
543
+ : isFilled,
544
+ hasStackedLines: dataLines.some(
545
+ ({ unit: lineUnit, stackKey, stackOrder }) =>
546
+ equals(unit, lineUnit) && (stackKey || stackOrder)
525
547
  ),
526
548
  height: valueGraphHeight,
527
549
  invert,
@@ -561,12 +583,14 @@ const getYScalePerUnit = ({
561
583
  isBarChart,
562
584
  min,
563
585
  max,
564
- boundariesUnit
586
+ boundariesUnit,
587
+ isFilled
565
588
  }: AxeScale & {
566
589
  min?: number;
567
590
  max?: number;
568
591
  isBarChart?: boolean;
569
592
  boundariesUnit?: string;
593
+ isFilled?: boolean;
570
594
  }): Record<string, ScaleLinear<number, number>> => {
571
595
  const units = getUnits(dataLines);
572
596
 
@@ -590,7 +614,8 @@ const getYScalePerUnit = ({
590
614
  min,
591
615
  max,
592
616
  isBarChart,
593
- boundariesUnit
617
+ boundariesUnit,
618
+ isFilled
594
619
  })
595
620
  };
596
621
  }, {});
@@ -6,6 +6,7 @@
6
6
  "metric_id": 1,
7
7
  "legend": "count",
8
8
  "displayAs": "bar",
9
+ "unit": "",
9
10
  "ds_data": {
10
11
  "ds_stack": true,
11
12
  "ds_transparency": 0,
@@ -30,6 +31,7 @@
30
31
  "metric_id": 2,
31
32
  "legend": "count",
32
33
  "displayAs": "bar",
34
+ "unit": "",
33
35
  "ds_data": {
34
36
  "ds_stack": true,
35
37
  "ds_transparency": 0,