@centreon/ui 25.3.3 → 25.4.0-MON-191119-npm-develop.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.
Files changed (187) hide show
  1. package/package.json +25 -11
  2. package/public/mockServiceWorker.js +8 -31
  3. package/src/ActionsList/index.tsx +1 -0
  4. package/src/Button/Icon/index.tsx +3 -1
  5. package/src/Button/Save/index.stories.tsx +1 -0
  6. package/src/Checkbox/Checkbox.tsx +3 -1
  7. package/src/Checkbox/CheckboxGroup/index.tsx +6 -1
  8. package/src/Colors/index.tsx +1 -1
  9. package/src/Dashboard/Dashboard.styles.ts +1 -1
  10. package/src/Dashboard/Layout.tsx +1 -1
  11. package/src/Dialog/UnsavedChanges/index.stories.tsx +1 -0
  12. package/src/Form/CollapsibleGroup.tsx +13 -13
  13. package/src/Form/Form.cypress.spec.tsx +137 -2
  14. package/src/Form/Form.stories.tsx +11 -31
  15. package/src/Form/Form.tsx +2 -0
  16. package/src/Form/Inputs/Checkbox.tsx +3 -2
  17. package/src/Form/Inputs/ConnectedAutocomplete.tsx +6 -1
  18. package/src/Form/Inputs/Grid.tsx +18 -29
  19. package/src/Form/Inputs/SubGroupDivider.tsx +7 -0
  20. package/src/Form/Inputs/Text.tsx +1 -0
  21. package/src/Form/Inputs/index.tsx +31 -24
  22. package/src/Form/Inputs/models.ts +8 -1
  23. package/src/Form/Section/FormSection.tsx +34 -0
  24. package/src/Form/Section/PanelTabs.tsx +13 -0
  25. package/src/Form/Section/navigateToSection.ts +9 -0
  26. package/src/Form/storiesData.tsx +14 -4
  27. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +46 -6
  28. package/src/Graph/BarChart/BarChart.stories.tsx +60 -0
  29. package/src/Graph/BarChart/BarChart.tsx +56 -32
  30. package/src/Graph/BarChart/BarGroup.tsx +22 -32
  31. package/src/Graph/BarChart/MemoizedGroup.tsx +8 -11
  32. package/src/Graph/BarChart/ResponsiveBarChart.tsx +145 -32
  33. package/src/Graph/BarChart/Tooltip/BarChartTooltip.tsx +2 -2
  34. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +7 -1
  35. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/useStackedLines.ts +18 -45
  36. package/src/Graph/Chart/BasicComponents/Lines/index.tsx +42 -28
  37. package/src/Graph/Chart/Chart.cypress.spec.tsx +85 -15
  38. package/src/Graph/Chart/Chart.stories.tsx +84 -1
  39. package/src/Graph/Chart/Chart.tsx +17 -4
  40. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/RegularAnchorPoint.tsx +8 -2
  41. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/StackedAnchorPoint.tsx +10 -3
  42. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/useTickGraph.ts +19 -2
  43. package/src/Graph/Chart/InteractiveComponents/Annotations/Annotation/index.tsx +1 -1
  44. package/src/Graph/Chart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltip.ts +2 -4
  45. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/index.tsx +14 -3
  46. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/models.ts +3 -0
  47. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/useZoomPreview.ts +12 -10
  48. package/src/Graph/Chart/InteractiveComponents/index.tsx +63 -5
  49. package/src/Graph/Chart/Legend/index.tsx +26 -2
  50. package/src/Graph/Chart/helpers/index.ts +4 -3
  51. package/src/Graph/Chart/index.tsx +45 -45
  52. package/src/Graph/Chart/models.ts +8 -0
  53. package/src/Graph/Chart/useChartData.ts +14 -2
  54. package/src/Graph/Gauge/Gauge.tsx +18 -14
  55. package/src/Graph/Gauge/ResponsiveGauge.tsx +10 -6
  56. package/src/Graph/Gauge/useResizeObserver.ts +68 -0
  57. package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +18 -16
  58. package/src/Graph/SingleBar/ThresholdLine.tsx +4 -4
  59. package/src/Graph/SingleBar/models.ts +1 -0
  60. package/src/Graph/Text/Text.styles.ts +2 -2
  61. package/src/Graph/Text/Text.tsx +23 -10
  62. package/src/Graph/Timeline/ResponsiveTimeline.tsx +4 -0
  63. package/src/Graph/Timeline/Timeline.tsx +21 -4
  64. package/src/Graph/Tree/Links.tsx +2 -2
  65. package/src/Graph/Tree/Tree.tsx +2 -2
  66. package/src/Graph/Tree/constants.ts +1 -1
  67. package/src/Graph/common/BaseChart/BaseChart.tsx +6 -1
  68. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +5 -4
  69. package/src/Graph/common/BaseChart/Header/index.tsx +3 -1
  70. package/src/Graph/common/BaseChart/useComputeBaseChartDimensions.ts +13 -9
  71. package/src/Graph/common/timeSeries/index.test.ts +20 -0
  72. package/src/Graph/common/timeSeries/index.ts +225 -44
  73. package/src/Graph/common/timeSeries/models.ts +6 -2
  74. package/src/Graph/common/utils.ts +45 -12
  75. package/src/Graph/index.ts +3 -1
  76. package/src/Graph/mockedData/dataWithMissingPoint.json +74 -0
  77. package/src/Graph/mockedData/pingServiceWithStackedKeys.json +205 -0
  78. package/src/Icon/RegexIcon.tsx +20 -0
  79. package/src/Icon/index.ts +1 -0
  80. package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +68 -14
  81. package/src/InputField/Select/Autocomplete/Connected/index.tsx +49 -14
  82. package/src/InputField/Select/Autocomplete/Multi/Listbox.tsx +78 -0
  83. package/src/InputField/Select/Autocomplete/Multi/Multi.styles.ts +26 -0
  84. package/src/InputField/Select/Autocomplete/Multi/Multi.tsx +124 -0
  85. package/src/InputField/Select/Autocomplete/Multi/index.tsx +1 -117
  86. package/src/InputField/Select/Autocomplete/index.tsx +28 -17
  87. package/src/InputField/Select/Option.tsx +3 -3
  88. package/src/InputField/Select/index.tsx +4 -0
  89. package/src/InputField/Text/index.tsx +4 -2
  90. package/src/InputField/translatedLabels.ts +4 -0
  91. package/src/Listing/ActionBar/Pagination.tsx +10 -23
  92. package/src/Listing/ActionBar/PaginationActions.tsx +1 -10
  93. package/src/Listing/ActionBar/index.tsx +1 -1
  94. package/src/Listing/Cell/DataCell.tsx +6 -6
  95. package/src/Listing/Cell/EllipsisTypography.tsx +10 -32
  96. package/src/Listing/Cell/index.tsx +57 -89
  97. package/src/Listing/Checkbox.tsx +8 -20
  98. package/src/Listing/Header/Cell/ListingHeaderCell.tsx +17 -14
  99. package/src/Listing/Header/Cell/SelectActionListingHeaderCell.tsx +5 -9
  100. package/src/Listing/Header/ListingHeader.tsx +2 -5
  101. package/src/Listing/Header/_internals/Label.tsx +1 -17
  102. package/src/Listing/Row/EmptyRow.tsx +2 -6
  103. package/src/Listing/Row/Row.tsx +7 -36
  104. package/src/Listing/index.stories.tsx +1 -0
  105. package/src/Listing/index.tsx +26 -26
  106. package/src/Listing/useStyleTable.ts +58 -32
  107. package/src/ListingPage/index.stories.tsx +1 -0
  108. package/src/Module/index.tsx +8 -2
  109. package/src/MultiSelectEntries/index.stories.tsx +1 -0
  110. package/src/MultiSelectEntries/index.tsx +1 -1
  111. package/src/Pagination/Pagination.cypress.spec.tsx +137 -0
  112. package/src/Pagination/Pagination.stories.tsx +46 -0
  113. package/src/Pagination/Pagination.styles.ts +56 -0
  114. package/src/Pagination/Pagination.tsx +146 -0
  115. package/src/Pagination/index.ts +3 -0
  116. package/src/Pagination/utils.ts +7 -0
  117. package/src/SortableItems/index.stories.tsx +2 -2
  118. package/src/StoryBookThemeProvider/index.tsx +3 -1
  119. package/src/ThemeProvider/base.css +49 -0
  120. package/src/ThemeProvider/index.tsx +21 -47
  121. package/src/ThemeProvider/palettes.ts +5 -3
  122. package/src/ThemeProvider/tailwindcss.css +230 -0
  123. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +9 -11
  124. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +1 -0
  125. package/src/TimePeriods/DateTimePickerInput.tsx +3 -1
  126. package/src/api/customFetch.ts +0 -9
  127. package/src/api/models.ts +9 -0
  128. package/src/api/useBulkResponse.ts +58 -0
  129. package/src/api/useGraphQuery/index.ts +108 -12
  130. package/src/components/Avatar/Avatar.stories.tsx +1 -0
  131. package/src/components/Button/Button.module.css +38 -0
  132. package/src/components/Button/Button.stories.tsx +25 -0
  133. package/src/components/Button/Button.tsx +2 -5
  134. package/src/components/CrudPage/Actions/Actions.styles.ts +15 -1
  135. package/src/components/CrudPage/Actions/Actions.tsx +7 -4
  136. package/src/components/CrudPage/Actions/Search.tsx +15 -14
  137. package/src/components/CrudPage/CrudPage.stories.tsx +1 -0
  138. package/src/components/CrudPage/CrudPageRoot.tsx +1 -1
  139. package/src/components/DataTable/DataTable.stories.tsx +1 -0
  140. package/src/components/DataTable/EmptyState/DataTableEmptyState.stories.tsx +1 -0
  141. package/src/components/DataTable/EmptyState/DataTableEmptyState.styles.ts +3 -1
  142. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +4 -1
  143. package/src/components/DataTable/Item/DataTableItem.stories.tsx +1 -0
  144. package/src/components/Form/AccessRights/AccessRights.stories.tsx +1 -0
  145. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +4 -3
  146. package/src/components/Form/AccessRights/ShareInput/useShareInput.tsx +15 -10
  147. package/src/components/Form/FormActions.tsx +21 -12
  148. package/src/components/Header/PageHeader/PageHeader.styles.ts +5 -5
  149. package/src/components/Layout/AreaIndicator.tsx +4 -6
  150. package/src/components/Layout/PageLayout/PageLayout.stories.tsx +1 -0
  151. package/src/components/Layout/PageLayout/PageLayout.styles.ts +1 -1
  152. package/src/components/Layout/PageLayout/PageLayout.tsx +9 -3
  153. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +5 -3
  154. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +5 -3
  155. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -3
  156. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +17 -17
  157. package/src/components/Menu/Button/MenuButton.tsx +6 -6
  158. package/src/components/Menu/MenuDivider.tsx +1 -5
  159. package/src/components/Menu/MenuItem.tsx +1 -5
  160. package/src/components/Menu/MenuItems.tsx +5 -4
  161. package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +1 -0
  162. package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +4 -1
  163. package/src/components/Modal/Modal.stories.tsx +21 -0
  164. package/src/components/Modal/Modal.styles.ts +1 -19
  165. package/src/components/Modal/Modal.tsx +1 -1
  166. package/src/components/Modal/ModalBody.tsx +6 -4
  167. package/src/components/Modal/ModalHeader.tsx +9 -5
  168. package/src/components/Modal/modal.module.css +16 -0
  169. package/src/components/Tabs/Tab.styles.ts +0 -6
  170. package/src/components/Tabs/Tabs.tsx +37 -15
  171. package/src/index.ts +4 -0
  172. package/src/queryParameters/url/index.ts +7 -2
  173. package/src/utils/index.ts +1 -0
  174. package/src/utils/useLocale/index.ts +9 -0
  175. package/src/utils/useLocale/useLocale.cypress.spec.tsx +38 -0
  176. package/src/utils/useLocaleDateTimeFormat/index.ts +4 -2
  177. package/src/utils/usePluralizedTranslation.ts +2 -3
  178. package/src/Listing/Cell/DataCell.styles.ts +0 -27
  179. package/src/Listing/Header/Cell/ListingHeaderCell.styles.ts +0 -71
  180. package/src/Listing/Header/Cell/SelectActionListingHeaderCell.styles.ts +0 -26
  181. package/src/Listing/Header/ListingHeader.styles.ts +0 -16
  182. package/src/Listing/Listing.styles.ts +0 -78
  183. package/src/Listing/Row/EmptyRow.styles.ts +0 -14
  184. package/src/components/Button/Button.styles.ts +0 -44
  185. package/src/components/Layout/AreaIndicator.styles.ts +0 -33
  186. package/src/components/Menu/Button/MenuButton.styles.ts +0 -27
  187. package/src/components/Menu/Menu.styles.ts +0 -68
@@ -1,4 +1,4 @@
1
- import type { MutableRefObject } from 'react';
1
+ import { type MutableRefObject, ReactElement, useMemo } from 'react';
2
2
 
3
3
  import { Event } from '@visx/visx';
4
4
  import type { ScaleLinear, ScaleTime } from 'd3-scale';
@@ -9,9 +9,11 @@ import {
9
9
  find,
10
10
  isEmpty,
11
11
  isNil,
12
+ isNotNil,
12
13
  keys,
13
14
  map,
14
15
  negate,
16
+ omit,
15
17
  pick,
16
18
  pipe,
17
19
  pluck,
@@ -37,6 +39,10 @@ import type {
37
39
  InteractedZone as ZoomPreviewModel
38
40
  } from '../models';
39
41
 
42
+ import {
43
+ computPixelsToShiftMouse,
44
+ computeGElementMarginLeft
45
+ } from '../../common/utils';
40
46
  import Annotations from './Annotations';
41
47
  import type { TimelineEvent } from './Annotations/models';
42
48
  import Bar from './Bar';
@@ -80,6 +86,9 @@ interface Props {
80
86
  fx?: (pointX: number) => number;
81
87
  fy?: (pointY: number) => number;
82
88
  };
89
+ hasSecondUnit?: boolean;
90
+ maxLeftAxisCharacters: number;
91
+ additionalZoomMargin?: number;
83
92
  }
84
93
 
85
94
  const InteractionWithGraph = ({
@@ -87,8 +96,11 @@ const InteractionWithGraph = ({
87
96
  commonData,
88
97
  annotationData,
89
98
  timeShiftZonesData,
90
- transformMatrix
91
- }: Props): JSX.Element => {
99
+ transformMatrix,
100
+ hasSecondUnit,
101
+ maxLeftAxisCharacters,
102
+ additionalZoomMargin = 0
103
+ }: Props): ReactElement => {
92
104
  const { classes } = useStyles();
93
105
 
94
106
  const setEventMouseDown = useSetAtom(eventMouseDownAtom);
@@ -142,6 +154,15 @@ const InteractionWithGraph = ({
142
154
  setEventMouseDown(event);
143
155
  };
144
156
 
157
+ const graphMarginLeft = useMemo(
158
+ () =>
159
+ computeGElementMarginLeft({
160
+ maxCharacters: maxLeftAxisCharacters,
161
+ hasSecondUnit
162
+ }) + additionalZoomMargin,
163
+ [additionalZoomMargin, maxLeftAxisCharacters, hasSecondUnit]
164
+ );
165
+
145
166
  const updateMousePosition = (pointPosition: MousePosition): void => {
146
167
  if (isNil(pointPosition)) {
147
168
  changeMousePosition({
@@ -151,10 +172,12 @@ const InteractionWithGraph = ({
151
172
 
152
173
  return;
153
174
  }
175
+ const pixelToShift = computPixelsToShiftMouse(xScale);
154
176
  const timeValue = getTimeValue({
155
177
  timeSeries,
156
- x: pointPosition[0],
157
- xScale
178
+ x: pointPosition[0] - pixelToShift,
179
+ xScale,
180
+ marginLeft: graphMarginLeft
158
181
  });
159
182
 
160
183
  if (isNil(timeValue)) {
@@ -199,6 +222,39 @@ const InteractionWithGraph = ({
199
222
  yScalesPerUnit
200
223
  });
201
224
 
225
+ if (isNotNil(lineData?.stackOrder)) {
226
+ const test = Object.entries(omit(['timeTick'], timeValue)).reduce(
227
+ (acc, [key, value]) => {
228
+ const line = getLineForMetric({
229
+ lines,
230
+ metric_id: Number(key)
231
+ });
232
+
233
+ const isBelowStackOrder =
234
+ isNotNil(line?.stackOrder) &&
235
+ (line?.stackOrder as number) >= (lineData.stackOrder as number);
236
+
237
+ if (isBelowStackOrder) {
238
+ return acc + value;
239
+ }
240
+
241
+ return acc;
242
+ },
243
+ 0
244
+ );
245
+
246
+ const y0 = yScale(test);
247
+
248
+ const diffBetweenY0AndPointPosition = Math.abs(
249
+ y0 - margin.top - (graphHeight - pointPosition[1])
250
+ );
251
+
252
+ return {
253
+ ...acc,
254
+ [metricId]: diffBetweenY0AndPointPosition
255
+ };
256
+ }
257
+
202
258
  const y0 = yScale(value);
203
259
 
204
260
  const diffBetweenY0AndPointPosition = Math.abs(
@@ -248,6 +304,8 @@ const InteractionWithGraph = ({
248
304
  graphHeight={graphHeight}
249
305
  graphWidth={graphWidth}
250
306
  xScale={xScale}
307
+ graphSvgRef={graphSvgRef}
308
+ graphMarginLeft={graphMarginLeft}
251
309
  />
252
310
  )}
253
311
  {displayEventAnnotations && (
@@ -26,6 +26,11 @@ interface Props extends Pick<LegendModel, 'placement' | 'mode'> {
26
26
  setLinesGraph: Dispatch<SetStateAction<Array<Line> | null>>;
27
27
  shouldDisplayLegendInCompactMode: boolean;
28
28
  toggable?: boolean;
29
+ secondaryClick?: (props: {
30
+ element: EventTarget | null;
31
+ metricId: number | string;
32
+ position: [number, number];
33
+ }) => void;
29
34
  }
30
35
 
31
36
  const MainLegend = ({
@@ -38,7 +43,8 @@ const MainLegend = ({
38
43
  shouldDisplayLegendInCompactMode,
39
44
  placement,
40
45
  height,
41
- mode
46
+ mode,
47
+ secondaryClick
42
48
  }: Props): JSX.Element => {
43
49
  const { classes, cx } = useStyles({
44
50
  limitLegendRows: Boolean(limitLegend),
@@ -65,7 +71,24 @@ const MainLegend = ({
65
71
  value
66
72
  }) || 'N/A';
67
73
 
68
- const selectMetric = ({ event, metric_id }): void => {
74
+ const contextMenuClick =
75
+ (metricId: number) =>
76
+ (event: MouseEvent): void => {
77
+ if (!secondaryClick) {
78
+ return;
79
+ }
80
+ event.preventDefault();
81
+ secondaryClick({
82
+ element: event.target,
83
+ metricId,
84
+ position: [event.pageX, event.pageY]
85
+ });
86
+ };
87
+
88
+ const selectMetric = ({
89
+ event,
90
+ metric_id
91
+ }: { event: MouseEvent; metric_id: number }): void => {
69
92
  if (!toggable) {
70
93
  return;
71
94
  }
@@ -127,6 +150,7 @@ const MainLegend = ({
127
150
  onClick={(event): void => selectMetric({ event, metric_id })}
128
151
  onMouseEnter={(): void => highlightLine(metric_id)}
129
152
  onMouseLeave={(): void => clearHighlight()}
153
+ onContextMenu={contextMenuClick(metric_id)}
130
154
  >
131
155
  <LegendHeader
132
156
  color={markerColor}
@@ -38,9 +38,10 @@ export const getXAxisTickFormat = (graphInterval: GraphInterval): string => {
38
38
  return gte(numberDays, 2) ? dateFormat : timeFormat;
39
39
  };
40
40
 
41
- export const truncate = (content?: string): string => {
42
- const maxLength = 180;
43
-
41
+ export const truncate = ({
42
+ content,
43
+ maxLength = 180
44
+ }: { content?: string; maxLength?: number }): string => {
44
45
  if (isNil(content)) {
45
46
  return '';
46
47
  }
@@ -1,4 +1,4 @@
1
- import { type MutableRefObject, memo, useEffect, useRef } from 'react';
1
+ import { RefCallback, memo, useEffect } from 'react';
2
2
 
3
3
  import dayjs from 'dayjs';
4
4
  import 'dayjs/locale/en';
@@ -8,11 +8,10 @@ import 'dayjs/locale/pt';
8
8
  import localizedFormat from 'dayjs/plugin/localizedFormat';
9
9
  import timezonePlugin from 'dayjs/plugin/timezone';
10
10
  import utcPlugin from 'dayjs/plugin/utc';
11
-
12
- import { ParentSize } from '../..';
13
11
  import Loading from '../../LoadingSkeleton';
14
12
  import type { LineChartData, Thresholds } from '../common/models';
15
13
 
14
+ import useResizeObserver from 'use-resize-observer';
16
15
  import Chart from './Chart';
17
16
  import { useChartStyles } from './Chart.styles';
18
17
  import LoadingSkeleton from './LoadingSkeleton';
@@ -32,7 +31,7 @@ interface Props extends Partial<LineChartProps> {
32
31
  start: string;
33
32
  thresholdUnit?: string;
34
33
  thresholds?: Thresholds;
35
- getRef?: (ref: MutableRefObject<HTMLDivElement | null>) => void;
34
+ getRef?: (ref: RefCallback<Element>) => void;
36
35
  containerStyle?: string;
37
36
  transformMatrix?: {
38
37
  fx?: (pointX: number) => number;
@@ -71,16 +70,23 @@ const WrapperChart = ({
71
70
  getRef,
72
71
  transformMatrix,
73
72
  additionalLines,
73
+ min,
74
+ max,
75
+ boundariesUnit,
74
76
  ...rest
75
77
  }: Props): JSX.Element | null => {
76
78
  const { classes, cx } = useChartStyles();
77
79
 
78
80
  const { adjustedData } = useChartData({ data, end, start });
79
- const lineChartRef = useRef<HTMLDivElement | null>(null);
81
+ const {
82
+ ref,
83
+ width: responsiveWidth,
84
+ height: responsiveHeight
85
+ } = useResizeObserver();
80
86
 
81
87
  useEffect(() => {
82
- getRef?.(lineChartRef);
83
- }, [lineChartRef?.current]);
88
+ getRef?.(ref);
89
+ }, [ref?.current]);
84
90
 
85
91
  if (loading && !adjustedData) {
86
92
  return (
@@ -91,48 +97,42 @@ const WrapperChart = ({
91
97
  );
92
98
  }
93
99
 
94
- if (!adjustedData) {
95
- return <Loading height={height} width={width} />;
96
- }
97
-
98
100
  return (
99
101
  <div
100
- ref={lineChartRef as MutableRefObject<HTMLDivElement>}
102
+ ref={ref}
101
103
  className={cx(classes.wrapperContainer, rest?.containerStyle)}
102
104
  >
103
- <ParentSize>
104
- {({
105
- height: responsiveHeight,
106
- width: responsiveWidth
107
- }): JSX.Element => {
108
- return (
109
- <Chart
110
- annotationEvent={annotationEvent}
111
- axis={axis}
112
- barStyle={barStyle}
113
- displayAnchor={displayAnchor}
114
- graphData={adjustedData}
115
- graphInterval={{ end, start }}
116
- graphRef={lineChartRef}
117
- header={header}
118
- height={height || responsiveHeight}
119
- legend={legend}
120
- limitLegend={limitLegend}
121
- lineStyle={lineStyle}
122
- shapeLines={shapeLines}
123
- thresholdUnit={thresholdUnit}
124
- thresholds={thresholds}
125
- timeShiftZones={timeShiftZones}
126
- tooltip={tooltip}
127
- width={width ?? responsiveWidth}
128
- zoomPreview={zoomPreview}
129
- skipIntersectionObserver={rest.skipIntersectionObserver}
130
- additionalLines={additionalLines}
131
- transformMatrix={transformMatrix}
132
- />
133
- );
134
- }}
135
- </ParentSize>
105
+ {!responsiveHeight ? (
106
+ <Loading height={height || '100%'} width={width} />
107
+ ) : (
108
+ <Chart
109
+ annotationEvent={annotationEvent}
110
+ axis={axis}
111
+ barStyle={barStyle}
112
+ displayAnchor={displayAnchor}
113
+ graphData={adjustedData}
114
+ graphInterval={{ end, start }}
115
+ graphRef={ref}
116
+ header={header}
117
+ height={height || responsiveHeight || 0}
118
+ legend={legend}
119
+ limitLegend={limitLegend}
120
+ lineStyle={lineStyle}
121
+ shapeLines={shapeLines}
122
+ thresholdUnit={thresholdUnit}
123
+ thresholds={thresholds}
124
+ timeShiftZones={timeShiftZones}
125
+ tooltip={tooltip}
126
+ width={width || responsiveWidth || 0}
127
+ zoomPreview={zoomPreview}
128
+ skipIntersectionObserver={rest.skipIntersectionObserver}
129
+ additionalLines={additionalLines}
130
+ transformMatrix={transformMatrix}
131
+ min={min}
132
+ max={max}
133
+ boundariesUnit={boundariesUnit}
134
+ />
135
+ )}
136
136
  </div>
137
137
  );
138
138
  };
@@ -122,6 +122,9 @@ export interface LineChartProps {
122
122
  zoomPreview?: InteractedZone;
123
123
  skipIntersectionObserver?: boolean;
124
124
  additionalLines?: Array<AdditionalLineProps>;
125
+ min?: number;
126
+ max?: number;
127
+ boundariesUnit?: string;
125
128
  }
126
129
 
127
130
  export interface Area {
@@ -172,6 +175,11 @@ export interface LegendModel {
172
175
  mode: 'grid' | 'list';
173
176
  placement: 'bottom' | 'left' | 'right';
174
177
  renderExtraComponent?: ReactNode;
178
+ secondaryClick?: (props: {
179
+ element: EventTarget | null;
180
+ metricId: number | string;
181
+ position: [number, number];
182
+ }) => void;
175
183
  }
176
184
 
177
185
  export interface GetDate {
@@ -30,6 +30,8 @@ interface Props {
30
30
  start?: string;
31
31
  }
32
32
 
33
+ const getBoolean = (value) => Boolean(Number(value));
34
+
33
35
  const useGraphData = ({ data, end, start }: Props): GraphDataResult => {
34
36
  const adjustedDataRef = useRef<Data>();
35
37
 
@@ -38,7 +40,7 @@ const useGraphData = ({ data, end, start }: Props): GraphDataResult => {
38
40
  return data;
39
41
  }
40
42
 
41
- if (isEmpty(data.metrics) || isEmpty(data.times)) {
43
+ if (isEmpty(data.metrics)) {
42
44
  return undefined;
43
45
  }
44
46
 
@@ -48,7 +50,16 @@ const useGraphData = ({ data, end, start }: Props): GraphDataResult => {
48
50
 
49
51
  const newMetrics = Object.entries(metricsGroupedByColor).map(
50
52
  ([color, value]) => {
51
- return value?.map((metric, index) =>
53
+ const adjustedValue = value?.map((item) => ({
54
+ ...item,
55
+ ds_data: {
56
+ ...item?.ds_data,
57
+ ds_invert: getBoolean(item?.ds_data?.ds_invert),
58
+ ds_filled: getBoolean(item?.ds_data?.ds_filled)
59
+ }
60
+ }));
61
+
62
+ return adjustedValue?.map((metric, index) =>
52
63
  set(
53
64
  lensPath(['ds_data', 'ds_color_line']),
54
65
  emphasizeCurveColor({ color, index }),
@@ -76,6 +87,7 @@ const useGraphData = ({ data, end, start }: Props): GraphDataResult => {
76
87
  const { title } = dataWithAdjustedMetricsColor.global;
77
88
 
78
89
  const newLineData = adjustGraphData(dataWithAdjustedMetricsColor).lines;
90
+
79
91
  const sortedLines = sortBy(compose(toLower, prop('name')), newLineData);
80
92
 
81
93
  adjustedDataRef.current = {
@@ -1,23 +1,28 @@
1
- import { ParentSize } from '../..';
1
+ import { Box } from '@mui/material';
2
2
  import { LineChartData, Thresholds } from '../common/models';
3
3
  import { getMetricWithLatestData } from '../common/timeSeries';
4
4
  import { Metric } from '../common/timeSeries/models';
5
5
 
6
6
  import ResponsiveGauge from './ResponsiveGauge';
7
+ import { useResizeObserver } from './useResizeObserver';
7
8
 
8
9
  export interface Props {
9
10
  baseColor?: string;
10
11
  data?: LineChartData;
11
12
  displayAsRaw?: boolean;
12
13
  thresholds: Thresholds;
14
+ max?: number;
13
15
  }
14
16
 
15
17
  export const Gauge = ({
16
18
  thresholds,
17
19
  data,
18
20
  displayAsRaw,
19
- baseColor
21
+ baseColor,
22
+ max
20
23
  }: Props): JSX.Element | null => {
24
+ const { width, height, ref } = useResizeObserver();
25
+
21
26
  if (!data) {
22
27
  return null;
23
28
  }
@@ -25,17 +30,16 @@ export const Gauge = ({
25
30
  const metric = getMetricWithLatestData(data) as Metric;
26
31
 
27
32
  return (
28
- <ParentSize>
29
- {({ width, height }) => (
30
- <ResponsiveGauge
31
- baseColor={baseColor}
32
- displayAsRaw={displayAsRaw}
33
- height={height}
34
- metric={metric}
35
- thresholds={thresholds}
36
- width={width}
37
- />
38
- )}
39
- </ParentSize>
33
+ <Box sx={{ width: '100%', height: '100%' }} ref={ref}>
34
+ <ResponsiveGauge
35
+ baseColor={baseColor}
36
+ displayAsRaw={displayAsRaw}
37
+ height={height}
38
+ metric={metric}
39
+ thresholds={thresholds}
40
+ width={width}
41
+ max={max}
42
+ />
43
+ </Box>
40
44
  );
41
45
  };
@@ -22,6 +22,7 @@ interface Props extends Pick<GaugeProps, 'thresholds' | 'baseColor'> {
22
22
  height: number;
23
23
  metric: Metric;
24
24
  width: number;
25
+ max?: number;
25
26
  }
26
27
 
27
28
  const ResponsiveGauge = ({
@@ -30,7 +31,8 @@ const ResponsiveGauge = ({
30
31
  thresholds,
31
32
  metric,
32
33
  displayAsRaw,
33
- baseColor
34
+ baseColor,
35
+ max
34
36
  }: Props): JSX.Element => {
35
37
  const { classes } = useTooltipStyles();
36
38
  const svgRef = useRef<SVGSVGElement>(null);
@@ -53,11 +55,13 @@ const ResponsiveGauge = ({
53
55
  pluck('value', thresholds.critical)
54
56
  ])
55
57
  : [0];
56
- const adaptedMaxValue = Math.max(
57
- metric.maximum_value || 0,
58
- Math.max(...thresholdValues) * 1.1,
59
- head(metric.data) as number
60
- );
58
+ const adaptedMaxValue =
59
+ max ||
60
+ Math.max(
61
+ metric.maximum_value || 0,
62
+ Math.max(...thresholdValues) * 1.1,
63
+ head(metric.data) as number
64
+ );
61
65
 
62
66
  const pieColor = getColorFromDataAndTresholds({
63
67
  baseColor,
@@ -0,0 +1,68 @@
1
+ import {
2
+ type RefObject,
3
+ useCallback,
4
+ useEffect,
5
+ useRef,
6
+ useState
7
+ } from 'react';
8
+
9
+ interface UseResizeObserverState {
10
+ width: number;
11
+ height: number;
12
+ ref: RefObject<HTMLDivElement | null>;
13
+ }
14
+
15
+ export const useResizeObserver = (): UseResizeObserverState => {
16
+ const [elementResolved, setElementResolved] = useState(false);
17
+ const [size, setSize] = useState([0, 0]);
18
+ const targetRef = useRef<HTMLDivElement | null>(null);
19
+ const observerRef = useRef<ResizeObserver | null>(null);
20
+
21
+ const resize = useCallback((entries: Array<ResizeObserverEntry>): void => {
22
+ setSize([entries[0].contentRect.width, entries[0].contentRect.height]);
23
+ }, []);
24
+
25
+ const observeElement = useCallback(() => {
26
+ if (!targetRef.current) {
27
+ return;
28
+ }
29
+
30
+ observerRef.current = new ResizeObserver(resize);
31
+ observerRef.current?.observe(targetRef.current);
32
+ }, [resize, targetRef.current]);
33
+
34
+ const unobserveElement = useCallback((): void => {
35
+ if (!targetRef.current) {
36
+ return;
37
+ }
38
+
39
+ observerRef.current?.unobserve(targetRef.current);
40
+ observerRef.current = null;
41
+ setElementResolved(false);
42
+ }, []);
43
+
44
+ useEffect(() => {
45
+ if (elementResolved) {
46
+ return;
47
+ }
48
+ setElementResolved(!!targetRef.current?.tagName);
49
+ });
50
+
51
+ useEffect(() => {
52
+ if (!elementResolved) {
53
+ return;
54
+ }
55
+
56
+ observeElement();
57
+
58
+ return () => {
59
+ unobserveElement();
60
+ };
61
+ }, [elementResolved]);
62
+
63
+ return {
64
+ width: size[0],
65
+ height: size[1],
66
+ ref: targetRef
67
+ };
68
+ };
@@ -4,7 +4,7 @@ import { animated, useSpring } from '@react-spring/web';
4
4
  import { scaleLinear } from '@visx/scale';
5
5
  import { Bar } from '@visx/shape';
6
6
  import { Group, Tooltip } from '@visx/visx';
7
- import { equals, flatten, head, lt, pluck } from 'ramda';
7
+ import { clamp, equals, flatten, head, pluck } from 'ramda';
8
8
 
9
9
  import { Box, alpha, useTheme } from '@mui/material';
10
10
 
@@ -35,13 +35,12 @@ const ResponsiveSingleBar = ({
35
35
  displayAsRaw,
36
36
  baseColor,
37
37
  size = 'medium',
38
- showLabels = true
38
+ showLabels = true,
39
+ max
39
40
  }: Props): JSX.Element => {
40
41
  const { classes } = useTooltipStyles();
41
42
  const theme = useTheme();
42
43
 
43
- const isSmallHeight = lt(height, 150);
44
-
45
44
  const metric = getMetricWithLatestData(data) as Metric;
46
45
  const latestMetricData = head(metric.data) as number;
47
46
  const thresholdValues = thresholds.enabled
@@ -51,11 +50,13 @@ const ResponsiveSingleBar = ({
51
50
  ])
52
51
  : [0];
53
52
 
54
- const adaptedMaxValue = Math.max(
55
- metric.maximum_value || 0,
56
- Math.max(...thresholdValues) * 1.1,
57
- head(metric.data) as number
58
- );
53
+ const adaptedMaxValue =
54
+ max ||
55
+ Math.max(
56
+ metric.maximum_value || 0,
57
+ Math.max(...thresholdValues) * 1.1,
58
+ head(metric.data) as number
59
+ );
59
60
 
60
61
  const { showTooltip, hideTooltip, tooltipOpen, tooltipData } =
61
62
  Tooltip.useTooltip();
@@ -72,7 +73,7 @@ const ResponsiveSingleBar = ({
72
73
  [latestMetricData, thresholds, theme]
73
74
  );
74
75
 
75
- const isSmall = equals(size, 'small') || isSmallHeight;
76
+ const isSmall = equals(size, 'small');
76
77
 
77
78
  const textStyle = isSmall ? theme.typography.h6 : theme.typography.h4;
78
79
 
@@ -118,14 +119,15 @@ const ResponsiveSingleBar = ({
118
119
 
119
120
  const springStyle = useSpring({ width: metricBarWidth });
120
121
 
121
- const barHeight = isSmallHeight ? barHeights.small : barHeights[size];
122
-
123
122
  const barY = groupMargin + (isSmall ? 0 : 2 * margins.top);
124
123
 
125
- const realBarHeight =
126
- !isSmall && textHeight + barHeight > height
127
- ? height - textHeight - 2 * margins.top
128
- : barHeight;
124
+ const realBarHeight = !isSmall
125
+ ? clamp(
126
+ barHeights.small,
127
+ barHeights.medium,
128
+ height - textHeight - 2 * margins.top
129
+ )
130
+ : barHeights.small;
129
131
 
130
132
  return (
131
133
  <div
@@ -66,12 +66,12 @@ export const ThresholdLine = ({
66
66
  x2={scaledValue + 1}
67
67
  y1={
68
68
  isSmall
69
- ? groupMargin - lineMargin + 6
69
+ ? groupMargin - lineMargin
70
70
  : groupMargin + lineMargin + margins.top
71
71
  }
72
72
  y2={
73
73
  isSmall
74
- ? barHeight + groupMargin - lineMargin + margins.top - 2
74
+ ? barHeight + groupMargin - lineMargin + margins.top - 6
75
75
  : barHeight + groupMargin + lineMargin + 2 * margins.top
76
76
  }
77
77
  />
@@ -83,12 +83,12 @@ export const ThresholdLine = ({
83
83
  x2={scaledValue + 1}
84
84
  y1={
85
85
  isSmall
86
- ? groupMargin - lineMargin + 5
86
+ ? groupMargin - lineMargin
87
87
  : groupMargin + lineMargin + margins.top
88
88
  }
89
89
  y2={
90
90
  isSmall
91
- ? barHeight + groupMargin - lineMargin + margins.top + 5
91
+ ? barHeight + groupMargin - lineMargin + margins.top - 6
92
92
  : barHeight + groupMargin + lineMargin + 2 * margins.top
93
93
  }
94
94
  onMouseEnter={onMouseEnter}
@@ -7,4 +7,5 @@ export interface SingleBarProps {
7
7
  showLabels?: boolean;
8
8
  size?: 'medium' | 'small';
9
9
  thresholds: Thresholds;
10
+ max?: number;
10
11
  }