@centreon/ui 23.10.65-develop.0 → 23.10.66

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 (38) hide show
  1. package/package.json +1 -1
  2. package/src/Dashboard/Dashboard.styles.ts +6 -1
  3. package/src/Dashboard/Item.tsx +4 -1
  4. package/src/Graph/Gauge/Gauge.stories.tsx +10 -0
  5. package/src/Graph/Gauge/Gauge.tsx +10 -1
  6. package/src/Graph/Gauge/PieData.tsx +8 -8
  7. package/src/Graph/Gauge/ResponsiveGauge.tsx +32 -23
  8. package/src/Graph/Gauge/Thresholds.tsx +2 -25
  9. package/src/Graph/Gauge/models.ts +1 -0
  10. package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +15 -9
  11. package/src/Graph/SingleBar/SingleBar.stories.tsx +10 -0
  12. package/src/Graph/SingleBar/ThresholdLine.tsx +3 -5
  13. package/src/Graph/SingleBar/models.ts +2 -0
  14. package/src/Graph/Text/Text.stories.tsx +13 -0
  15. package/src/Graph/Text/Text.tsx +19 -12
  16. package/src/Graph/common/timeSeries/index.test.ts +106 -3
  17. package/src/Graph/common/timeSeries/index.ts +67 -4
  18. package/src/Graph/common/timeSeries/models.ts +4 -1
  19. package/src/Graph/common/utils.ts +4 -2
  20. package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
  21. package/src/InputField/Select/Autocomplete/index.tsx +3 -1
  22. package/src/InputField/Text/index.tsx +10 -7
  23. package/src/RichTextEditor/ContentEditable.tsx +0 -1
  24. package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +5 -13
  25. package/src/RichTextEditor/RichTextEditor.tsx +5 -2
  26. package/src/RichTextEditor/plugins/AutoLinkPlugin/index.tsx +40 -1
  27. package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +33 -8
  28. package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +10 -4
  29. package/src/ThemeProvider/index.tsx +6 -4
  30. package/src/api/useFetchQuery/index.ts +10 -8
  31. package/src/components/Inputs/Switch/Switch.stories.tsx +22 -0
  32. package/src/components/Inputs/Switch/Switch.styles.ts +13 -0
  33. package/src/components/Inputs/Switch/Switch.tsx +18 -0
  34. package/src/components/Inputs/index.ts +1 -0
  35. package/src/components/Modal/Modal.styles.ts +3 -1
  36. package/src/components/index.ts +1 -0
  37. package/src/utils/centreonBaseURL.ts +4 -0
  38. package/src/utils/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "23.10.65-develop.0",
3
+ "version": "23.10.66",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -79,7 +79,12 @@ export const useDashboardItemStyles = makeStyles<{ hasHeader: boolean }>()(
79
79
  '&:hover': {
80
80
  backgroundColor: theme.palette.action.hover
81
81
  },
82
- cursor: 'move',
82
+ '&[data-canMove="false"]': {
83
+ cursor: 'default'
84
+ },
85
+ '&[data-canMove="true"]': {
86
+ cursor: 'move'
87
+ },
83
88
  padding: theme.spacing(0.5, 2)
84
89
  },
85
90
  widgetPadding: {
@@ -15,6 +15,7 @@ import { useMemoComponent } from '../utils';
15
15
  import { useDashboardItemStyles } from './Dashboard.styles';
16
16
 
17
17
  interface DashboardItemProps {
18
+ canMove?: boolean;
18
19
  children: ReactElement;
19
20
  className?: string;
20
21
  disablePadding?: boolean;
@@ -37,7 +38,8 @@ const Item = forwardRef(
37
38
  onMouseUp,
38
39
  onTouchEnd,
39
40
  id,
40
- disablePadding = false
41
+ disablePadding = false,
42
+ canMove = false
41
43
  }: DashboardItemProps,
42
44
  ref: ForwardedRef<HTMLDivElement>
43
45
  ): ReactElement => {
@@ -66,6 +68,7 @@ const Item = forwardRef(
66
68
  <div
67
69
  {...listeners}
68
70
  className={classes.widgetHeader}
71
+ data-canMove={canMove}
69
72
  data-testid={`${id}_move_panel`}
70
73
  >
71
74
  {header}
@@ -134,3 +134,13 @@ export const withRangeThresholds: Story = {
134
134
  },
135
135
  render: Template
136
136
  };
137
+
138
+ export const RawValue: Story = {
139
+ args: {
140
+ data: dataLastWeek,
141
+ displayAsRaw: true,
142
+ thresholdTooltipLabels: ['Warning', 'Critical'],
143
+ thresholds: [0.5, 0.6]
144
+ },
145
+ render: Template
146
+ };
@@ -7,11 +7,18 @@ import { Metric } from '../common/timeSeries/models';
7
7
  import ResponsiveGauge from './ResponsiveGauge';
8
8
 
9
9
  interface Props {
10
+ baseColor?: string;
10
11
  data?: LineChartData;
12
+ displayAsRaw?: boolean;
11
13
  thresholds: Thresholds;
12
14
  }
13
15
 
14
- export const Gauge = ({ thresholds, data }: Props): JSX.Element | null => {
16
+ export const Gauge = ({
17
+ thresholds,
18
+ data,
19
+ displayAsRaw,
20
+ baseColor
21
+ }: Props): JSX.Element | null => {
15
22
  if (!data) {
16
23
  return null;
17
24
  }
@@ -22,6 +29,8 @@ export const Gauge = ({ thresholds, data }: Props): JSX.Element | null => {
22
29
  <Responsive.ParentSize>
23
30
  {({ width, height }) => (
24
31
  <ResponsiveGauge
32
+ baseColor={baseColor}
33
+ displayAsRaw={displayAsRaw}
25
34
  height={height}
26
35
  metric={metric}
27
36
  thresholds={thresholds}
@@ -16,7 +16,8 @@ const PieData = ({
16
16
  metric,
17
17
  adaptedMaxValue,
18
18
  thresholds,
19
- radius
19
+ radius,
20
+ baseColor
20
21
  }: Omit<
21
22
  GaugeProps,
22
23
  'width' | 'height' | 'showTooltip' | 'hideTooltip' | 'thresholdTooltipLabels'
@@ -24,13 +25,12 @@ const PieData = ({
24
25
  const theme = useTheme();
25
26
 
26
27
  const pieData = [metric.data[0], adaptedMaxValue - metric.data[0]];
27
- const pieColor = !thresholds.enabled
28
- ? theme.palette.success.main
29
- : getColorFromDataAndTresholds({
30
- data: metric.data[0],
31
- theme,
32
- thresholds
33
- });
28
+ const pieColor = getColorFromDataAndTresholds({
29
+ baseColor,
30
+ data: metric.data[0],
31
+ theme,
32
+ thresholds
33
+ });
34
34
 
35
35
  const getDataColor = scaleOrdinal({
36
36
  domain: pieData,
@@ -7,7 +7,7 @@ import { Tooltip } from '@visx/visx';
7
7
  import { Box, Fade, useTheme } from '@mui/material';
8
8
 
9
9
  import { Metric } from '../common/timeSeries/models';
10
- import { formatMetricValue } from '../common/timeSeries';
10
+ import { formatMetricValueWithUnit } from '../common/timeSeries';
11
11
  import { getColorFromDataAndTresholds } from '../common/utils';
12
12
  import { margins } from '../common/margins';
13
13
 
@@ -15,7 +15,8 @@ import Thresholds from './Thresholds';
15
15
  import PieData from './PieData';
16
16
  import { GaugeProps } from './models';
17
17
 
18
- interface Props extends Pick<GaugeProps, 'thresholds'> {
18
+ interface Props extends Pick<GaugeProps, 'thresholds' | 'baseColor'> {
19
+ displayAsRaw?: boolean;
19
20
  height: number;
20
21
  metric: Metric;
21
22
  width: number;
@@ -30,7 +31,9 @@ const ResponsiveGauge = ({
30
31
  width,
31
32
  height,
32
33
  thresholds,
33
- metric
34
+ metric,
35
+ displayAsRaw,
36
+ baseColor
34
37
  }: Props): JSX.Element => {
35
38
  const svgRef = useRef<SVGSVGElement>(null);
36
39
 
@@ -50,28 +53,36 @@ const ResponsiveGauge = ({
50
53
  const centerY = innerHeight / 2;
51
54
  const centerX = innerWidth / 2;
52
55
  const radius = Math.min(innerWidth, innerHeight) / 2;
53
- const thresholdValues = flatten([
54
- pluck('value', thresholds.warning),
55
- pluck('value', thresholds.critical)
56
- ]);
56
+ const thresholdValues = thresholds.enabled
57
+ ? flatten([
58
+ pluck('value', thresholds.warning),
59
+ pluck('value', thresholds.critical)
60
+ ])
61
+ : [0];
57
62
  const adaptedMaxValue = Math.max(
58
63
  metric.maximum_value || 0,
59
64
  Math.max(...thresholdValues) * 1.1,
60
65
  head(metric.data) as number
61
66
  );
62
67
 
63
- const pieColor = !thresholds.enabled
64
- ? theme.palette.success.main
65
- : getColorFromDataAndTresholds({
66
- data: metric.data[0],
67
- theme,
68
- thresholds
69
- });
68
+ const pieColor = getColorFromDataAndTresholds({
69
+ baseColor,
70
+ data: metric.data[0],
71
+ theme,
72
+ thresholds
73
+ });
70
74
 
71
75
  const svgTop = svgRef.current?.getBoundingClientRect().top || 0;
72
76
  const svgLeft = svgRef.current?.getBoundingClientRect().left || 0;
73
77
 
74
- const isSmallHeight = height < 250;
78
+ const isSmallWidget = height < 240;
79
+
80
+ const gaugeValue = formatMetricValueWithUnit({
81
+ base: 1000,
82
+ isRaw: displayAsRaw,
83
+ unit: metric.unit,
84
+ value: metric.data[0]
85
+ });
75
86
 
76
87
  return (
77
88
  <Box sx={{ position: 'relative' }}>
@@ -87,6 +98,7 @@ const ResponsiveGauge = ({
87
98
  />
88
99
  <PieData
89
100
  adaptedMaxValue={adaptedMaxValue}
101
+ baseColor={baseColor}
90
102
  metric={metric}
91
103
  radius={radius}
92
104
  thresholds={thresholds}
@@ -97,18 +109,15 @@ const ResponsiveGauge = ({
97
109
  style={{
98
110
  fill: pieColor,
99
111
  ...theme.typography.h3,
100
- fontSize: Math.min(width, height) / 7
112
+ fontSize:
113
+ Math.min(width, height) / 7 -
114
+ (isSmallWidget ? 0 : (gaugeValue?.length || 0) * 2)
101
115
  }}
102
116
  textAnchor="middle"
103
117
  x="50%"
104
- y={isSmallHeight ? 140 : 100 + Math.min(width, height) / 3}
118
+ y={isSmallWidget ? 130 : height - height / 2.3}
105
119
  >
106
- {formatMetricValue({
107
- base: 1000,
108
- unit: metric.unit,
109
- value: metric.data[0]
110
- })}{' '}
111
- {metric.unit}
120
+ {gaugeValue}
112
121
  </text>
113
122
  </svg>
114
123
  <Fade in={tooltipOpen && thresholds.enabled}>
@@ -16,7 +16,7 @@ const Thresholds = ({
16
16
  showTooltip,
17
17
  hideTooltip,
18
18
  metric
19
- }: Omit<GaugeProps, 'width' | 'height'>): JSX.Element => {
19
+ }: Omit<GaugeProps, 'width' | 'height'>): JSX.Element | null => {
20
20
  const theme = useTheme();
21
21
 
22
22
  const criticalThresholdValues = pluck('value', thresholds.critical);
@@ -65,30 +65,7 @@ const Thresholds = ({
65
65
  });
66
66
 
67
67
  if (!thresholds.enabled) {
68
- return (
69
- <Pie
70
- data={successThresholdArc}
71
- endAngle={-Math.PI / 2}
72
- innerRadius={radius - thresholdThickness}
73
- outerRadius={radius}
74
- pieSortValues={() => -1}
75
- pieValue={(d) => d.value}
76
- startAngle={Math.PI / 2}
77
- >
78
- {(pie) => (
79
- <AnimatedPie<{ name: string; value: number }>
80
- {...pie}
81
- animate
82
- getColor={(arc) => successThresholdScaleOrdinal(arc.data.name)}
83
- getKey={(arc) => `${arc.data.name}_${arc.data.value}`}
84
- hideTooltip={hideTooltip}
85
- metric={metric}
86
- showTooltip={showTooltip}
87
- thresholds={thresholds}
88
- />
89
- )}
90
- </Pie>
91
- );
68
+ return null;
92
69
  }
93
70
 
94
71
  const arcs = [
@@ -3,6 +3,7 @@ import { Metric } from '../common/timeSeries/models';
3
3
 
4
4
  export interface GaugeProps {
5
5
  adaptedMaxValue: number;
6
+ baseColor?: string;
6
7
  height: number;
7
8
  hideTooltip: () => void;
8
9
  metric: Metric;
@@ -9,7 +9,7 @@ import { useSpring, animated } from '@react-spring/web';
9
9
  import { alpha, Box, Fade, useTheme } from '@mui/material';
10
10
 
11
11
  import {
12
- formatMetricValue,
12
+ formatMetricValueWithUnit,
13
13
  getMetricWithLatestData
14
14
  } from '../common/timeSeries';
15
15
  import { Metric } from '../common/timeSeries/models';
@@ -34,16 +34,21 @@ const ResponsiveSingleBar = ({
34
34
  data,
35
35
  thresholds,
36
36
  width,
37
- height
37
+ height,
38
+ displayAsRaw,
39
+ baseColor
38
40
  }: Props): JSX.Element => {
39
41
  const theme = useTheme();
40
42
 
41
43
  const metric = getMetricWithLatestData(data) as Metric;
42
44
  const latestMetricData = head(metric.data) as number;
43
- const thresholdValues = flatten([
44
- pluck('value', thresholds.warning),
45
- pluck('value', thresholds.critical)
46
- ]);
45
+ const thresholdValues = thresholds.enabled
46
+ ? flatten([
47
+ pluck('value', thresholds.warning),
48
+ pluck('value', thresholds.critical)
49
+ ])
50
+ : [0];
51
+
47
52
  const adaptedMaxValue = Math.max(
48
53
  metric.maximum_value || 0,
49
54
  Math.max(...thresholdValues) * 1.1,
@@ -66,6 +71,7 @@ const ResponsiveSingleBar = ({
66
71
  const barColor = useMemo(
67
72
  () =>
68
73
  getColorFromDataAndTresholds({
74
+ baseColor,
69
75
  data: latestMetricData,
70
76
  theme,
71
77
  thresholds
@@ -81,12 +87,12 @@ const ResponsiveSingleBar = ({
81
87
  x="50%"
82
88
  y={25}
83
89
  >
84
- {formatMetricValue({
90
+ {formatMetricValueWithUnit({
85
91
  base: 1000,
92
+ isRaw: displayAsRaw,
86
93
  unit: metric.unit,
87
94
  value: metric.data[0]
88
- })}{' '}
89
- {metric.unit}
95
+ })}
90
96
  </text>
91
97
  );
92
98
 
@@ -156,3 +156,13 @@ export const rangeThresholds: Story = {
156
156
  },
157
157
  render: Template
158
158
  };
159
+
160
+ export const RawValue: Story = {
161
+ args: {
162
+ data: dataLastWeek,
163
+ displayAsRaw: true,
164
+ thresholdTooltipLabels: ['Warning', 'Critical'],
165
+ thresholds: [0.5, 1.5]
166
+ },
167
+ render: Template
168
+ };
@@ -4,13 +4,11 @@ import { useTheme } from '@mui/material';
4
4
 
5
5
  import { groupMargin } from './Thresholds';
6
6
 
7
- const lineMargin = 10;
8
-
9
- const thresholdLineHeight = 60 + 2 * lineMargin;
10
-
7
+ export const barHeight = 72;
11
8
  export const margin = 40;
12
9
 
13
- export const barHeight = 50;
10
+ const lineMargin = 10;
11
+ const thresholdLineHeight = barHeight + 2 * lineMargin;
14
12
 
15
13
  interface Props {
16
14
  hideTooltip: () => void;
@@ -1,6 +1,8 @@
1
1
  import { LineChartData, Thresholds } from '../common/models';
2
2
 
3
3
  export interface SingleBarProps {
4
+ baseColor?: string;
4
5
  data?: LineChartData;
6
+ displayAsRaw?: boolean;
5
7
  thresholds: Thresholds;
6
8
  }
@@ -52,3 +52,16 @@ export const critical: Story = {
52
52
  },
53
53
  render: Template
54
54
  };
55
+
56
+ export const rawValue: Story = {
57
+ args: {
58
+ data: dataLastWeek,
59
+ displayAsRaw: true,
60
+ labels: {
61
+ critical: 'Critical',
62
+ warning: 'Warning'
63
+ },
64
+ thresholds: [0.5, 1.5]
65
+ },
66
+ render: Template
67
+ };
@@ -4,7 +4,7 @@ import { Typography, useTheme } from '@mui/material';
4
4
 
5
5
  import { LineChartData, Thresholds } from '../common/models';
6
6
  import {
7
- formatMetricValue,
7
+ formatMetricValueWithUnit,
8
8
  getMetricWithLatestData
9
9
  } from '../common/timeSeries';
10
10
  import { getColorFromDataAndTresholds } from '../common/utils';
@@ -12,7 +12,9 @@ import { getColorFromDataAndTresholds } from '../common/utils';
12
12
  import { useTextStyles } from './Text.styles';
13
13
 
14
14
  interface Props {
15
+ baseColor?: string;
15
16
  data?: LineChartData;
17
+ displayAsRaw?: boolean;
16
18
  labels: {
17
19
  critical: string;
18
20
  warning: string;
@@ -23,7 +25,9 @@ interface Props {
23
25
  export const Text = ({
24
26
  thresholds,
25
27
  data,
26
- labels
28
+ displayAsRaw,
29
+ labels,
30
+ baseColor
27
31
  }: Props): JSX.Element | null => {
28
32
  const theme = useTheme();
29
33
  const { classes } = useTextStyles();
@@ -37,20 +41,21 @@ export const Text = ({
37
41
  const metricUnit = metric?.unit ?? '';
38
42
 
39
43
  const color = getColorFromDataAndTresholds({
44
+ baseColor,
40
45
  data: metricValue,
41
46
  theme,
42
47
  thresholds
43
48
  });
44
49
 
45
50
  const warningThresholdLabels = thresholds.warning.map(({ value }) =>
46
- formatMetricValue({
51
+ formatMetricValueWithUnit({
47
52
  unit: metricUnit,
48
53
  value
49
54
  })
50
55
  );
51
56
 
52
57
  const criticalThresholdLabels = thresholds.critical.map(({ value }) =>
53
- formatMetricValue({
58
+ formatMetricValueWithUnit({
54
59
  unit: metricUnit,
55
60
  value
56
61
  })
@@ -58,24 +63,26 @@ export const Text = ({
58
63
 
59
64
  return (
60
65
  <div className={classes.graphText}>
61
- <Typography sx={{ color }} variant="h3">
62
- {formatMetricValue({
63
- unit: metricUnit,
64
- value: metricValue
65
- })}{' '}
66
- {metricUnit}
66
+ <Typography sx={{ color }} variant="h2">
67
+ <strong>
68
+ {formatMetricValueWithUnit({
69
+ isRaw: displayAsRaw,
70
+ unit: metricUnit,
71
+ value: metricValue
72
+ })}
73
+ </strong>
67
74
  </Typography>
68
75
  {thresholds.enabled && (
69
76
  <div className={classes.thresholds}>
70
77
  <Typography sx={{ color: 'warning.main' }} variant="h5">
71
78
  {labels.warning}
72
79
  {': '}
73
- {warningThresholdLabels.join(' - ')} {metricUnit}
80
+ {warningThresholdLabels.join(' - ')}
74
81
  </Typography>
75
82
  <Typography sx={{ color: 'error.main' }} variant="h5">
76
83
  {labels.critical}
77
84
  {': '}
78
- {criticalThresholdLabels.join(' - ')} {metricUnit}
85
+ {criticalThresholdLabels.join(' - ')}
79
86
  </Typography>
80
87
  </div>
81
88
  )}
@@ -335,10 +335,10 @@ describe('timeSeries', () => {
335
335
  describe(timeSeries.formatMetricValue, () => {
336
336
  const cases: Array<TestCase> = [
337
337
  [218857269, '', 1000, '218.86m'],
338
- [218857269, '', 1024, '208.72M'],
338
+ [218857269, '', 1024, '208.72 M'],
339
339
  [0.12232323445, '', 1000, '0.12'],
340
- [1024, 'B', 1000, '1K'],
341
- [1024, 'B', 1024, '1K'],
340
+ [1024, 'B', 1000, '1 KB'],
341
+ [1024, 'B', 1024, '1 KB'],
342
342
  [null, 'B', 1024, null]
343
343
  ];
344
344
 
@@ -504,3 +504,106 @@ describe('timeSeries', () => {
504
504
  });
505
505
  });
506
506
  });
507
+
508
+ describe('Format value with unit', () => {
509
+ const units = [
510
+ 'B',
511
+ 'bytes',
512
+ 'bytespersecond',
513
+ 'B/s',
514
+ 'B/sec',
515
+ 'o',
516
+ 'octets',
517
+ 'b/s',
518
+ 'b',
519
+ 'ms',
520
+ '%',
521
+ ''
522
+ ];
523
+
524
+ const getExpectedResult = (unit): string => {
525
+ if (unit === '') {
526
+ return '324.23m';
527
+ }
528
+
529
+ return `309.21 M${unit}`;
530
+ };
531
+
532
+ const humanReadableTestCases = units.map((unit) => {
533
+ if (unit === '%') {
534
+ return {
535
+ expectedResult: '45.56%',
536
+ unit,
537
+ value: 45.55678
538
+ };
539
+ }
540
+
541
+ if (unit === 'ms') {
542
+ return {
543
+ expectedResult: '34.23 seconds',
544
+ unit,
545
+ value: 34232
546
+ };
547
+ }
548
+
549
+ return {
550
+ expectedResult: getExpectedResult(unit),
551
+ unit,
552
+ value: 324234232.34233
553
+ };
554
+ });
555
+
556
+ const rawTestCases = units.map((unit) => {
557
+ if (unit === '%') {
558
+ return {
559
+ expectedResult: '45.55678%',
560
+ unit,
561
+ value: 45.55678
562
+ };
563
+ }
564
+
565
+ if (unit === 'ms') {
566
+ return {
567
+ expectedResult: '34232 ms',
568
+ unit,
569
+ value: 34232
570
+ };
571
+ }
572
+
573
+ return {
574
+ expectedResult:
575
+ unit === '' ? '324234232.34233 ' : `324234232.34233 ${unit}`,
576
+ unit,
577
+ value: 324234232.34233
578
+ };
579
+ });
580
+
581
+ describe('Format the value as human readable', () => {
582
+ it.each(humanReadableTestCases)(
583
+ 'formats the value with $unit',
584
+ ({ value, unit, expectedResult }) => {
585
+ expect(
586
+ timeSeries.formatMetricValueWithUnit({
587
+ unit,
588
+ value
589
+ })
590
+ ).toEqual(expectedResult);
591
+ }
592
+ );
593
+ });
594
+
595
+ describe('Format the value as raw', () => {
596
+ it.each(rawTestCases)(
597
+ 'formats the value with $unit',
598
+ ({ value, unit, expectedResult }) => {
599
+ expect(
600
+ timeSeries.formatMetricValueWithUnit({
601
+ isRaw: true,
602
+ unit,
603
+ value
604
+ })
605
+ ).toEqual(expectedResult);
606
+ }
607
+ );
608
+ });
609
+ });