@centreon/ui 24.10.12 → 24.10.13

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.
Files changed (153) hide show
  1. package/package.json +3 -2
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Icon/index.tsx +3 -1
  4. package/src/Dashboard/Dashboard.styles.ts +3 -4
  5. package/src/Dashboard/DashboardLayout.stories.tsx +1 -1
  6. package/src/Dashboard/Grid.tsx +11 -17
  7. package/src/Dashboard/Layout.tsx +27 -56
  8. package/src/Dialog/UnsavedChanges/index.tsx +15 -13
  9. package/src/Dialog/UnsavedChanges/translatedLabels.ts +15 -13
  10. package/src/Form/Form.tsx +0 -1
  11. package/src/Form/Inputs/Autocomplete.tsx +1 -1
  12. package/src/Form/Inputs/ConnectedAutocomplete.tsx +5 -2
  13. package/src/Form/Inputs/Grid.tsx +7 -1
  14. package/src/Form/Inputs/Radio.tsx +1 -1
  15. package/src/Form/Inputs/Switch.tsx +1 -1
  16. package/src/Form/Inputs/Text.tsx +1 -1
  17. package/src/Form/Inputs/index.tsx +25 -24
  18. package/src/Form/Inputs/models.ts +2 -0
  19. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +3 -3
  20. package/src/Graph/BarChart/BarChart.tsx +24 -31
  21. package/src/Graph/BarChart/BarGroup.tsx +32 -59
  22. package/src/Graph/BarChart/BarStack.tsx +64 -13
  23. package/src/Graph/BarChart/MemoizedGroup.tsx +123 -0
  24. package/src/Graph/BarChart/ResponsiveBarChart.tsx +21 -7
  25. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +87 -9
  26. package/src/Graph/BarStack/BarStack.stories.tsx +13 -4
  27. package/src/Graph/BarStack/BarStack.styles.ts +57 -33
  28. package/src/Graph/BarStack/Graph.tsx +173 -0
  29. package/src/Graph/BarStack/GraphAndLegend.tsx +117 -0
  30. package/src/Graph/BarStack/ResponsiveBarStack.tsx +61 -168
  31. package/src/Graph/BarStack/constants.ts +5 -0
  32. package/src/Graph/BarStack/models.ts +0 -1
  33. package/src/Graph/BarStack/useGraphAndLegend.ts +84 -0
  34. package/src/Graph/BarStack/useResponsiveBarStack.ts +73 -97
  35. package/src/Graph/Chart/Chart.cypress.spec.tsx +14 -26
  36. package/src/Graph/Chart/Chart.stories.tsx +1 -1
  37. package/src/Graph/Chart/Chart.tsx +53 -37
  38. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +3 -3
  39. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/useTickGraph.ts +19 -6
  40. package/src/Graph/Chart/Legend/Legend.styles.ts +25 -11
  41. package/src/Graph/Chart/Legend/index.tsx +6 -24
  42. package/src/Graph/Chart/index.tsx +34 -43
  43. package/src/Graph/Chart/models.ts +0 -1
  44. package/src/Graph/Chart/useChartData.ts +19 -1
  45. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +20 -2
  46. package/src/Graph/HeatMap/model.ts +6 -2
  47. package/src/Graph/Legend/Legend.styles.ts +10 -0
  48. package/src/Graph/Legend/Legend.tsx +6 -1
  49. package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +9 -10
  50. package/src/Graph/SingleBar/ThresholdLine.tsx +6 -6
  51. package/src/Graph/Text/Text.styles.ts +2 -2
  52. package/src/Graph/Text/Text.tsx +23 -10
  53. package/src/Graph/Timeline/ResponsiveTimeline.tsx +152 -0
  54. package/src/Graph/Timeline/Timeline.cypress.spec.tsx +148 -0
  55. package/src/Graph/Timeline/Timeline.stories.tsx +91 -0
  56. package/src/Graph/Timeline/Timeline.tsx +28 -0
  57. package/src/Graph/Timeline/index.ts +1 -0
  58. package/src/Graph/Timeline/models.ts +20 -0
  59. package/src/Graph/Timeline/timeline.styles.ts +11 -0
  60. package/src/Graph/Timeline/translatedLabel.ts +6 -0
  61. package/src/Graph/Timeline/useTimeline.ts +90 -0
  62. package/src/Graph/Tree/Links.tsx +2 -2
  63. package/src/Graph/Tree/Tree.tsx +2 -2
  64. package/src/Graph/Tree/constants.ts +1 -1
  65. package/src/Graph/common/Axes/index.tsx +1 -1
  66. package/src/Graph/common/Axes/useAxisY.ts +8 -4
  67. package/src/Graph/common/BaseChart/BaseChart.tsx +3 -12
  68. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +12 -4
  69. package/src/Graph/common/BaseChart/Header/index.tsx +3 -1
  70. package/src/Graph/common/BaseChart/useComputeBaseChartDimensions.ts +23 -11
  71. package/src/Graph/common/BaseChart/useComputeYAxisMaxCharacters.ts +92 -0
  72. package/src/Graph/common/models.ts +7 -8
  73. package/src/Graph/common/timeSeries/index.test.ts +1 -1
  74. package/src/Graph/common/timeSeries/index.ts +56 -29
  75. package/src/Graph/common/timeSeries/models.ts +2 -0
  76. package/src/Graph/common/utils.ts +51 -3
  77. package/src/Graph/index.ts +4 -1
  78. package/src/Graph/mockedData/lastDayWithNullValues.json +6 -6
  79. package/src/Graph/mockedData/pingServiceLinesBars.json +47 -47
  80. package/src/Icon/DowntimeIcon.tsx +8 -1
  81. package/src/Icon/FlappingIcon.tsx +22 -0
  82. package/src/Icon/index.ts +1 -0
  83. package/src/InputField/Select/Autocomplete/Connected/Multi/index.test.tsx +21 -1
  84. package/src/InputField/Select/Autocomplete/Connected/index.test.tsx +2 -2
  85. package/src/InputField/Select/Autocomplete/Connected/index.tsx +52 -15
  86. package/src/InputField/Select/Autocomplete/Multi/index.stories.tsx +19 -0
  87. package/src/InputField/Select/Autocomplete/Multi/index.tsx +8 -5
  88. package/src/InputField/Select/Autocomplete/index.tsx +79 -54
  89. package/src/InputField/Text/index.tsx +6 -4
  90. package/src/InputField/translatedLabels.ts +2 -0
  91. package/src/Listing/ActionBar/index.tsx +1 -1
  92. package/src/Listing/Listing.styles.ts +3 -3
  93. package/src/Listing/index.tsx +40 -37
  94. package/src/Listing/models.ts +0 -8
  95. package/src/Listing/useStyleTable.ts +58 -32
  96. package/src/MultiSelectEntries/index.tsx +2 -0
  97. package/src/PopoverMenu/index.tsx +2 -9
  98. package/src/SortableItems/index.tsx +0 -1
  99. package/src/ThemeProvider/index.tsx +1 -1
  100. package/src/ThemeProvider/palettes.ts +6 -0
  101. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +2 -3
  102. package/src/TimePeriods/DateTimePickerInput.tsx +3 -1
  103. package/src/api/buildListingEndpoint/getSearchQueryParameterValue.ts +7 -1
  104. package/src/api/buildListingEndpoint/models.ts +1 -0
  105. package/src/api/customFetch.ts +4 -1
  106. package/src/api/models.ts +9 -0
  107. package/src/api/useGraphQuery/index.ts +117 -20
  108. package/src/api/useGraphQuery/models.ts +1 -0
  109. package/src/api/useMutationQuery/index.ts +1 -1
  110. package/src/components/DataTable/DataTable.styles.ts +1 -1
  111. package/src/components/DataTable/EmptyState/DataTableEmptyState.styles.ts +2 -1
  112. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +4 -1
  113. package/src/components/DataTable/Item/DataTableItem.styles.ts +28 -2
  114. package/src/components/DataTable/Item/DataTableItem.tsx +19 -4
  115. package/src/components/Form/FormActions.tsx +21 -12
  116. package/src/components/Layout/AreaIndicator.tsx +1 -1
  117. package/src/components/Layout/PageLayout/PageLayout.styles.ts +2 -7
  118. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +0 -1
  119. package/src/components/Zoom/Zoom.tsx +9 -2
  120. package/src/components/Zoom/ZoomContent.tsx +143 -136
  121. package/src/components/Zoom/models.ts +18 -15
  122. package/src/components/Zoom/useMinimap.ts +5 -8
  123. package/src/components/Zoom/useZoom.ts +3 -3
  124. package/src/index.ts +2 -0
  125. package/src/utils/index.ts +1 -0
  126. package/src/utils/useLocale/index.ts +9 -0
  127. package/src/utils/useLocale/useLocale.cypress.spec.tsx +38 -0
  128. package/src/utils/useLocaleDateTimeFormat/index.ts +4 -2
  129. package/src/utils/usePluralizedTranslation.ts +2 -3
  130. package/src/Graph/common/timeSeries/index.test.ts-E +0 -622
  131. package/src/components/CrudPage/Actions/Actions.styles.ts +0 -16
  132. package/src/components/CrudPage/Actions/Actions.tsx +0 -24
  133. package/src/components/CrudPage/Actions/AddButton.tsx +0 -23
  134. package/src/components/CrudPage/Actions/Filters.tsx +0 -25
  135. package/src/components/CrudPage/Actions/Search.tsx +0 -31
  136. package/src/components/CrudPage/Actions/useSearch.tsx +0 -24
  137. package/src/components/CrudPage/Columns/Actions.tsx +0 -88
  138. package/src/components/CrudPage/CrudPage.cypress.spec.tsx +0 -559
  139. package/src/components/CrudPage/CrudPage.stories.tsx +0 -278
  140. package/src/components/CrudPage/CrudPageRoot.tsx +0 -142
  141. package/src/components/CrudPage/DeleteModal.tsx +0 -77
  142. package/src/components/CrudPage/Form/AddModal.tsx +0 -35
  143. package/src/components/CrudPage/Form/Buttons.tsx +0 -98
  144. package/src/components/CrudPage/Form/UpdateModal.tsx +0 -60
  145. package/src/components/CrudPage/Listing.tsx +0 -63
  146. package/src/components/CrudPage/atoms.ts +0 -30
  147. package/src/components/CrudPage/hooks/useDeleteItem.ts +0 -53
  148. package/src/components/CrudPage/hooks/useGetItem.ts +0 -36
  149. package/src/components/CrudPage/hooks/useGetItems.ts +0 -67
  150. package/src/components/CrudPage/hooks/useListingQueryKey.ts +0 -31
  151. package/src/components/CrudPage/index.tsx +0 -7
  152. package/src/components/CrudPage/models.ts +0 -118
  153. package/src/components/CrudPage/utils.ts +0 -4
@@ -1,5 +1,3 @@
1
- import { useRef } from 'react';
2
-
3
1
  import dayjs from 'dayjs';
4
2
  import 'dayjs/locale/en';
5
3
  import 'dayjs/locale/es';
@@ -12,12 +10,13 @@ import { Provider } from 'jotai';
12
10
 
13
11
  import { Box } from '@mui/material';
14
12
 
15
- import { ParentSize } from '../../ParentSize';
13
+ import Loading from '../../LoadingSkeleton';
16
14
  import LoadingSkeleton from '../Chart/LoadingSkeleton';
17
15
  import { LineChartProps } from '../Chart/models';
18
16
  import useChartData from '../Chart/useChartData';
19
17
  import { LineChartData, Thresholds } from '../common/models';
20
18
 
19
+ import useResizeObserver from 'use-resize-observer';
21
20
  import ResponsiveBarChart from './ResponsiveBarChart';
22
21
  import { BarStyle } from './models';
23
22
 
@@ -38,7 +37,6 @@ export interface BarChartProps
38
37
  start: string;
39
38
  thresholdUnit?: string;
40
39
  thresholds?: Thresholds;
41
- skipIntersectionObserver?: boolean;
42
40
  }
43
41
 
44
42
  const BarChart = ({
@@ -58,11 +56,10 @@ const BarChart = ({
58
56
  barStyle = {
59
57
  opacity: 1,
60
58
  radius: 0.2
61
- },
62
- skipIntersectionObserver
59
+ }
63
60
  }: BarChartProps): JSX.Element => {
64
61
  const { adjustedData } = useChartData({ data, end, start });
65
- const lineChartRef = useRef<HTMLDivElement | null>(null);
62
+ const { ref, width, height: responsiveHeight } = useResizeObserver();
66
63
 
67
64
  if (loading && !adjustedData) {
68
65
  return (
@@ -75,30 +72,26 @@ const BarChart = ({
75
72
 
76
73
  return (
77
74
  <Provider>
78
- <Box
79
- ref={lineChartRef}
80
- sx={{ height: '100%', overflow: 'hidden', width: '100%' }}
81
- >
82
- <ParentSize>
83
- {({ height: responsiveHeight, width }) => (
84
- <ResponsiveBarChart
85
- axis={axis}
86
- barStyle={barStyle}
87
- graphData={adjustedData}
88
- graphRef={lineChartRef}
89
- header={header}
90
- height={height || responsiveHeight}
91
- legend={legend}
92
- limitLegend={limitLegend}
93
- orientation={orientation}
94
- thresholdUnit={thresholdUnit}
95
- thresholds={thresholds}
96
- tooltip={tooltip}
97
- width={width}
98
- skipIntersectionObserver={skipIntersectionObserver}
99
- />
100
- )}
101
- </ParentSize>
75
+ <Box ref={ref} sx={{ height: '100%', overflow: 'hidden', width: '100%' }}>
76
+ {!responsiveHeight ? (
77
+ <Loading height={height || '100%'} width={width} />
78
+ ) : (
79
+ <ResponsiveBarChart
80
+ axis={axis}
81
+ barStyle={barStyle}
82
+ graphData={adjustedData}
83
+ graphRef={ref}
84
+ header={header}
85
+ height={height || responsiveHeight}
86
+ legend={legend}
87
+ limitLegend={limitLegend}
88
+ orientation={orientation}
89
+ thresholdUnit={thresholdUnit}
90
+ thresholds={thresholds}
91
+ tooltip={tooltip}
92
+ width={width || 0}
93
+ />
94
+ )}
102
95
  </Box>
103
96
  </Provider>
104
97
  );
@@ -1,10 +1,8 @@
1
- import { memo, useMemo } from 'react';
2
-
3
- import { Group } from '@visx/group';
4
1
  import { scaleBand, scaleOrdinal } from '@visx/scale';
5
2
  import { BarGroupHorizontal, BarGroup as VisxBarGroup } from '@visx/shape';
6
3
  import { ScaleLinear } from 'd3-scale';
7
4
  import { difference, equals, keys, omit, pick, pluck, uniq } from 'ramda';
5
+ import { memo, useMemo } from 'react';
8
6
 
9
7
  import { useDeepMemo } from '../../utils';
10
8
  import {
@@ -14,10 +12,15 @@ import {
14
12
  getUnits
15
13
  } from '../common/timeSeries';
16
14
  import { Line, TimeValue } from '../common/timeSeries/models';
17
-
18
- import BarStack from './BarStack';
15
+ import MemoizedGroup from './MemoizedGroup';
19
16
  import { BarStyle } from './models';
20
17
 
18
+ // Minimum value for logarithmic scale to avoid log(0)
19
+ const minLogScaleValue = 0.001;
20
+
21
+ const getNeutralValue = (scaleType?: 'linear' | 'logarithmic') =>
22
+ equals(scaleType, 'logarithmic') ? minLogScaleValue : 0;
23
+
21
24
  interface Props {
22
25
  barStyle: BarStyle;
23
26
  isTooltipHidden: boolean;
@@ -27,6 +30,7 @@ interface Props {
27
30
  timeSeries: Array<TimeValue>;
28
31
  xScale;
29
32
  yScalesPerUnit: Record<string, ScaleLinear<number, number>>;
33
+ scaleType?: 'linear' | 'logarithmic';
30
34
  }
31
35
 
32
36
  const BarGroup = ({
@@ -37,7 +41,8 @@ const BarGroup = ({
37
41
  xScale,
38
42
  yScalesPerUnit,
39
43
  isTooltipHidden,
40
- barStyle
44
+ barStyle,
45
+ scaleType
41
46
  }: Props): JSX.Element => {
42
47
  const isHorizontal = equals(orientation, 'horizontal');
43
48
 
@@ -142,6 +147,8 @@ const BarGroup = ({
142
147
  [isHorizontal, placeholderScale, xScale, metricScale]
143
148
  );
144
149
 
150
+ const neutralValue = useMemo(() => getNeutralValue(scaleType), [scaleType]);
151
+
145
152
  return (
146
153
  <BarComponent<TimeValue>
147
154
  color={colorScale}
@@ -151,58 +158,23 @@ const BarGroup = ({
151
158
  {...barComponentBaseProps}
152
159
  >
153
160
  {(barGroups) =>
154
- barGroups.map((barGroup) => (
155
- <Group
156
- key={`bar-group-${barGroup.index}-${barGroup.x0}`}
157
- left={barGroup.x0}
158
- top={barGroup.y0}
159
- >
160
- {barGroup.bars.map((bar) => {
161
- const isStackedBar = bar.key.startsWith('stacked-');
162
- const linesBar = isStackedBar
163
- ? stackedLinesTimeSeriesPerUnit[bar.key.replace('stacked-', '')]
164
- .lines
165
- : (notStackedLines.find(({ metric_id }) =>
166
- equals(metric_id, Number(bar.key))
167
- ) as Line);
168
- const timeSeriesBar = isStackedBar
169
- ? stackedLinesTimeSeriesPerUnit[bar.key.replace('stacked-', '')]
170
- .timeSeries
171
- : notStackedTimeSeries.map((timeSerie) => ({
172
- timeTick: timeSerie.timeTick,
173
- [bar.key]: timeSerie[Number(bar.key)]
174
- }));
175
-
176
- return isStackedBar ? (
177
- <BarStack
178
- key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
179
- barIndex={barGroup.index}
180
- barPadding={isHorizontal ? bar.x : bar.y}
181
- barStyle={barStyle}
182
- barWidth={isHorizontal ? bar.width : bar.height}
183
- isHorizontal={isHorizontal}
184
- isTooltipHidden={isTooltipHidden}
185
- lines={linesBar}
186
- timeSeries={timeSeriesBar}
187
- yScale={yScalesPerUnit[bar.key.replace('stacked-', '')]}
188
- />
189
- ) : (
190
- <BarStack
191
- key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
192
- barIndex={barGroup.index}
193
- barPadding={isHorizontal ? bar.x : bar.y}
194
- barStyle={barStyle}
195
- barWidth={isHorizontal ? bar.width : bar.height}
196
- isHorizontal={isHorizontal}
197
- isTooltipHidden={isTooltipHidden}
198
- lines={[linesBar]}
199
- timeSeries={timeSeriesBar}
200
- yScale={yScalesPerUnit[linesBar.unit]}
201
- />
202
- );
203
- })}
204
- </Group>
205
- ))
161
+ barGroups.map((barGroup, index) => {
162
+ return (
163
+ <MemoizedGroup
164
+ key={`bar-group-${barGroup.index}-${barGroup.x0}`}
165
+ barGroup={barGroup}
166
+ barStyle={barStyle}
167
+ stackedLinesTimeSeriesPerUnit={stackedLinesTimeSeriesPerUnit}
168
+ notStackedTimeSeries={notStackedTimeSeries}
169
+ notStackedLines={notStackedLines}
170
+ isTooltipHidden={isTooltipHidden}
171
+ isHorizontal={isHorizontal}
172
+ neutralValue={neutralValue}
173
+ yScalesPerUnit={yScalesPerUnit}
174
+ barIndex={index}
175
+ />
176
+ );
177
+ })
206
178
  }
207
179
  </BarComponent>
208
180
  );
@@ -215,7 +187,8 @@ const propsToMemoize = [
215
187
  'lines',
216
188
  'secondUnit',
217
189
  'isCenteredZero',
218
- 'barStyle'
190
+ 'barStyle',
191
+ 'scaleType'
219
192
  ];
220
193
 
221
194
  export default memo(BarGroup, (prevProps, nextProps) => {
@@ -1,9 +1,10 @@
1
1
  import { memo } from 'react';
2
2
 
3
- import { scaleBand } from '@visx/scale';
3
+ import { ScaleType, scaleBand } from '@visx/scale';
4
4
  import { BarRounded } from '@visx/shape';
5
5
  import { dec, equals, gt, pick } from 'ramda';
6
6
 
7
+ import { BarGroupBar, SeriesPoint, StackKey } from '@visx/shape/lib/types';
7
8
  import { BarStyle } from './models';
8
9
  import { UseBarStackProps, useBarStack } from './useBarStack';
9
10
 
@@ -19,6 +20,7 @@ interface Props extends Omit<UseBarStackProps, 'xScale'> {
19
20
  barStyle: BarStyle;
20
21
  barWidth: number;
21
22
  isTooltipHidden: boolean;
23
+ neutralValue: number;
22
24
  }
23
25
 
24
26
  const getPadding = ({ padding, size, isNegativeValue }): number => {
@@ -29,6 +31,47 @@ const getPadding = ({ padding, size, isNegativeValue }): number => {
29
31
  return padding + size;
30
32
  };
31
33
 
34
+ interface GetFirstBarHeightProps {
35
+ bar: Omit<BarGroupBar<StackKey>, 'key' | 'value'> & {
36
+ bar: SeriesPoint<unknown>;
37
+ key: StackKey;
38
+ };
39
+ isHorizontal: boolean;
40
+ barWidth: number;
41
+ y: number;
42
+ isFirstBar: boolean;
43
+ yScale: ScaleType;
44
+ neutralValue: number;
45
+ }
46
+
47
+ const getFirstBarHeight = ({
48
+ bar,
49
+ isHorizontal,
50
+ barWidth,
51
+ y,
52
+ isFirstBar,
53
+ yScale,
54
+ neutralValue
55
+ }: GetFirstBarHeightProps): number => {
56
+ if (!isFirstBar || !isHorizontal) {
57
+ return isHorizontal ? Math.abs(bar.height) : barWidth;
58
+ }
59
+
60
+ if (equals(bar.height, 0)) {
61
+ return 0;
62
+ }
63
+
64
+ if (isHorizontal && bar.height < 0) {
65
+ return bar.height;
66
+ }
67
+
68
+ if (isHorizontal) {
69
+ return Math.abs(bar.width) - (y - yScale(neutralValue));
70
+ }
71
+
72
+ return barWidth;
73
+ };
74
+
32
75
  const BarStack = ({
33
76
  timeSeries,
34
77
  isHorizontal,
@@ -38,7 +81,8 @@ const BarStack = ({
38
81
  barPadding,
39
82
  barIndex,
40
83
  isTooltipHidden,
41
- barStyle = { opacity: 1, radius: 0.2 }
84
+ barStyle = { opacity: 1, radius: 0.2 },
85
+ neutralValue
42
86
  }: Props): JSX.Element => {
43
87
  const {
44
88
  BarStackComponent,
@@ -73,7 +117,21 @@ const BarStack = ({
73
117
  {...barRoundedProps}
74
118
  data-testid={`stacked-bar-${bar.key}-${bar.index}-${bar.bar[1]}`}
75
119
  fill={bar.color}
76
- height={isHorizontal ? Math.abs(bar.height) : barWidth}
120
+ height={getFirstBarHeight({
121
+ bar,
122
+ barWidth,
123
+ y: isHorizontal
124
+ ? getPadding({
125
+ isNegativeValue,
126
+ padding: bar.y,
127
+ size: bar.height
128
+ })
129
+ : barPadding,
130
+ isFirstBar: shouldApplyRadiusOnBottom,
131
+ isHorizontal,
132
+ yScale,
133
+ neutralValue
134
+ })}
77
135
  key={`bar-stack-${barStack.index}-${bar.index}`}
78
136
  opacity={barStyle.opacity ?? 1}
79
137
  radius={barWidth * barStyle.radius}
@@ -87,15 +145,7 @@ const BarStack = ({
87
145
  size: bar.width
88
146
  })
89
147
  }
90
- y={
91
- isHorizontal
92
- ? getPadding({
93
- isNegativeValue,
94
- padding: bar.y,
95
- size: bar.height
96
- })
97
- : barPadding
98
- }
148
+ y={isHorizontal ? bar.y : barPadding}
99
149
  onMouseEnter={
100
150
  isTooltipHidden
101
151
  ? undefined
@@ -122,7 +172,8 @@ const propsToMemoize = [
122
172
  'barPadding',
123
173
  'barIndex',
124
174
  'isTooltipHidden',
125
- 'barStyle'
175
+ 'barStyle',
176
+ 'neutralValue'
126
177
  ];
127
178
 
128
179
  export default memo(BarStack, (prevProps, nextProps) => {
@@ -0,0 +1,123 @@
1
+ import { Group } from '@visx/group';
2
+ import { BarGroup } from '@visx/shape/lib/types';
3
+ import { ScaleLinear } from 'd3-scale';
4
+ import { equals, omit } from 'ramda';
5
+ import { memo } from 'react';
6
+ import { Line, TimeValue } from '../common/timeSeries/models';
7
+ import BarStack from './BarStack';
8
+ import { BarStyle } from './models';
9
+
10
+ interface Props {
11
+ neutralValue: number;
12
+ isTooltipHidden: boolean;
13
+ barStyle: BarStyle;
14
+ yScalesPerUnit: Record<string, ScaleLinear<number, number>>;
15
+ stackedLinesTimeSeriesPerUnit: Record<
16
+ string,
17
+ { lines: Array<Line>; timeSeries: Array<TimeValue> }
18
+ >;
19
+ notStackedLines: Array<Line>;
20
+ notStackedTimeSeries: Array<TimeValue>;
21
+ isHorizontal: boolean;
22
+ barGroup: BarGroup<'id'>;
23
+ barIndex: number;
24
+ }
25
+
26
+ const MemoizedGroup = ({
27
+ barGroup,
28
+ stackedLinesTimeSeriesPerUnit,
29
+ notStackedLines,
30
+ notStackedTimeSeries,
31
+ isHorizontal,
32
+ barStyle,
33
+ isTooltipHidden,
34
+ neutralValue,
35
+ yScalesPerUnit,
36
+ barIndex
37
+ }: Props): JSX.Element | null => {
38
+ const hasEmptyValues = barGroup.bars.every(({ key, value }) => {
39
+ if (key.startsWith('stacked-')) {
40
+ const timeValueBar =
41
+ stackedLinesTimeSeriesPerUnit[key.replace('stacked-', '')].timeSeries[
42
+ barIndex
43
+ ];
44
+
45
+ return Object.values(omit(['timeTick'], timeValueBar)).every(
46
+ (value) => !value
47
+ );
48
+ }
49
+
50
+ return !value;
51
+ });
52
+
53
+ if (hasEmptyValues) {
54
+ return null;
55
+ }
56
+
57
+ return (
58
+ <Group left={barGroup.x0} top={barGroup.y0}>
59
+ {barGroup.bars.map((bar) => {
60
+ const isStackedBar = bar.key.startsWith('stacked-');
61
+ const linesBar = isStackedBar
62
+ ? stackedLinesTimeSeriesPerUnit[bar.key.replace('stacked-', '')].lines
63
+ : (notStackedLines.find(({ metric_id }) =>
64
+ equals(metric_id, Number(bar.key))
65
+ ) as Line);
66
+ const timeSeriesBar = isStackedBar
67
+ ? stackedLinesTimeSeriesPerUnit[bar.key.replace('stacked-', '')]
68
+ .timeSeries
69
+ : notStackedTimeSeries.map((timeSerie) => ({
70
+ timeTick: timeSerie.timeTick,
71
+ [bar.key]: timeSerie[Number(bar.key)]
72
+ }));
73
+
74
+ return isStackedBar ? (
75
+ <BarStack
76
+ key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
77
+ barIndex={barGroup.index}
78
+ barPadding={isHorizontal ? bar.x : bar.y}
79
+ barStyle={barStyle}
80
+ barWidth={isHorizontal ? bar.width : bar.height}
81
+ isHorizontal={isHorizontal}
82
+ isTooltipHidden={isTooltipHidden}
83
+ lines={linesBar as Array<Line>}
84
+ timeSeries={timeSeriesBar}
85
+ yScale={yScalesPerUnit[bar.key.replace('stacked-', '')]}
86
+ neutralValue={neutralValue}
87
+ />
88
+ ) : (
89
+ <BarStack
90
+ key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
91
+ barIndex={barGroup.index}
92
+ barPadding={isHorizontal ? bar.x : bar.y}
93
+ barStyle={barStyle}
94
+ barWidth={isHorizontal ? bar.width : bar.height}
95
+ isHorizontal={isHorizontal}
96
+ isTooltipHidden={isTooltipHidden}
97
+ lines={[linesBar as Line]}
98
+ timeSeries={timeSeriesBar}
99
+ yScale={yScalesPerUnit[(linesBar as Line).unit]}
100
+ neutralValue={neutralValue}
101
+ />
102
+ );
103
+ })}
104
+ </Group>
105
+ );
106
+ };
107
+
108
+ export default memo(
109
+ MemoizedGroup,
110
+ (prevProps, nextProps) =>
111
+ equals(prevProps.barGroup, nextProps.barGroup) &&
112
+ equals(
113
+ prevProps.stackedLinesTimeSeriesPerUnit,
114
+ nextProps.stackedLinesTimeSeriesPerUnit
115
+ ) &&
116
+ equals(prevProps.notStackedLines, nextProps.notStackedLines) &&
117
+ equals(prevProps.notStackedTimeSeries, nextProps.notStackedTimeSeries) &&
118
+ equals(prevProps.isHorizontal, nextProps.isHorizontal) &&
119
+ equals(prevProps.barStyle, nextProps.barStyle) &&
120
+ equals(prevProps.isTooltipHidden, nextProps.isTooltipHidden) &&
121
+ equals(prevProps.neutralValue, nextProps.neutralValue) &&
122
+ equals(prevProps.barIndex, nextProps.barIndex)
123
+ );
@@ -13,6 +13,7 @@ import { useIntersection } from '../Chart/useChartIntersection';
13
13
  import BaseChart from '../common/BaseChart/BaseChart';
14
14
  import ChartSvgWrapper from '../common/BaseChart/ChartSvgWrapper';
15
15
  import { useComputeBaseChartDimensions } from '../common/BaseChart/useComputeBaseChartDimensions';
16
+ import { useComputeYAxisMaxCharacters } from '../common/BaseChart/useComputeYAxisMaxCharacters';
16
17
  import Thresholds from '../common/Thresholds/Thresholds';
17
18
  import { Thresholds as ThresholdsModel } from '../common/models';
18
19
  import {
@@ -22,7 +23,6 @@ import {
22
23
  } from '../common/timeSeries';
23
24
  import { Line } from '../common/timeSeries/models';
24
25
  import { useTooltipStyles } from '../common/useTooltipStyles';
25
-
26
26
  import BarGroup from './BarGroup';
27
27
  import BarChartTooltip from './Tooltip/BarChartTooltip';
28
28
  import { tooltipDataAtom } from './atoms';
@@ -39,6 +39,7 @@ interface Props
39
39
  thresholdUnit?: string;
40
40
  thresholds?: ThresholdsModel;
41
41
  width: number;
42
+ skipIntersectionObserver?: boolean;
42
43
  }
43
44
 
44
45
  const ResponsiveBarChart = ({
@@ -54,10 +55,9 @@ const ResponsiveBarChart = ({
54
55
  limitLegend,
55
56
  orientation,
56
57
  tooltip,
57
- barStyle,
58
- skipIntersectionObserver
58
+ barStyle
59
59
  }: Props): JSX.Element => {
60
- const { title, timeSeries, baseAxis, lines } = graphData;
60
+ const { title, timeSeries, baseAxis, lines } = graphData || {};
61
61
 
62
62
  const { classes, cx } = useTooltipStyles();
63
63
 
@@ -69,19 +69,30 @@ const ResponsiveBarChart = ({
69
69
  const { isInViewport } = useIntersection({ element: graphRef?.current });
70
70
 
71
71
  const displayedLines = useMemo(
72
- () => linesGraph.filter(({ display }) => display),
72
+ () => (linesGraph || []).filter(({ display }) => display),
73
73
  [linesGraph]
74
74
  );
75
75
 
76
76
  const [firstUnit, secondUnit] = getUnits(displayedLines);
77
77
  const allUnits = getUnits(lines);
78
78
 
79
+ const { maxLeftAxisCharacters, maxRightAxisCharacters } =
80
+ useComputeYAxisMaxCharacters({
81
+ graphData,
82
+ thresholds,
83
+ thresholdUnit,
84
+ axis,
85
+ firstUnit,
86
+ secondUnit
87
+ });
88
+
79
89
  const { legendRef, graphWidth, graphHeight } = useComputeBaseChartDimensions({
80
90
  hasSecondUnit: Boolean(secondUnit),
81
91
  height,
82
92
  legendDisplay: legend?.display,
83
93
  legendPlacement: legend?.placement,
84
- width
94
+ width,
95
+ maxAxisCharacters: maxRightAxisCharacters || maxLeftAxisCharacters
85
96
  });
86
97
 
87
98
  const thresholdValues = flatten([
@@ -150,7 +161,7 @@ const ResponsiveBarChart = ({
150
161
  [axis?.showGridLines]
151
162
  );
152
163
 
153
- if (!isInViewport && !skipIntersectionObserver) {
164
+ if (!isInViewport) {
154
165
  return (
155
166
  <Skeleton
156
167
  height={graphSvgRef?.current?.clientHeight ?? graphHeight}
@@ -215,6 +226,8 @@ const ResponsiveBarChart = ({
215
226
  svgRef={graphSvgRef}
216
227
  timeSeries={timeSeries}
217
228
  xScale={xScale}
229
+ maxAxisCharacters={maxLeftAxisCharacters}
230
+ hasSecondUnit={Boolean(secondUnit)}
218
231
  >
219
232
  <>
220
233
  <BarGroup
@@ -226,6 +239,7 @@ const ResponsiveBarChart = ({
226
239
  timeSeries={timeSeries}
227
240
  xScale={xScale}
228
241
  yScalesPerUnit={yScalesPerUnit}
242
+ scaleType={axis?.scale}
229
243
  />
230
244
  {thresholds?.enabled && (
231
245
  <Thresholds