@centreon/ui 23.10.18-MON-17793-create-graph-component-centreon-ui.7 → 23.10.18-MON-17793-create-graph-component-centreon-ui.8

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 (32) hide show
  1. package/package.json +7 -1
  2. package/src/Dashboard/Dashboard.styles.ts +62 -60
  3. package/src/Dashboard/Grid.tsx +1 -1
  4. package/src/Dashboard/Item.tsx +9 -6
  5. package/src/Dialog/Confirm/index.tsx +14 -4
  6. package/src/Dialog/index.tsx +3 -0
  7. package/src/Graph/BasicComponents/Lines/RegularLines/index.tsx +3 -9
  8. package/src/Graph/BasicComponents/Lines/Threshold/BasicThreshold.tsx +14 -10
  9. package/src/Graph/BasicComponents/Lines/Threshold/Circle.tsx +28 -34
  10. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithPatternLines.tsx +82 -0
  11. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithVariation.tsx +72 -0
  12. package/src/Graph/BasicComponents/Lines/Threshold/helpers/index.ts +122 -0
  13. package/src/Graph/BasicComponents/Lines/Threshold/index.tsx +14 -65
  14. package/src/Graph/BasicComponents/Lines/Threshold/models.ts +42 -2
  15. package/src/Graph/BasicComponents/Lines/Threshold/useCoordinateCircle.ts +59 -0
  16. package/src/Graph/BasicComponents/Lines/index.tsx +55 -21
  17. package/src/Graph/BasicComponents/Lines/models.ts +6 -1
  18. package/src/Graph/helpers/doc.ts +6 -14
  19. package/src/Graph/index.stories.tsx +143 -16
  20. package/src/Graph/mockedData/exclusionPeriodFirstPeriod.json +714 -0
  21. package/src/Graph/mockedData/exclusionPeriodFourthPeriod.json +588 -0
  22. package/src/Graph/mockedData/exclusionPeriodSecondPeriod.json +847 -0
  23. package/src/Graph/mockedData/exclusionPeriodThirdPeriod.json +434 -0
  24. package/src/Graph/mockedData/lastDayThreshold.json +2037 -2037
  25. package/src/Graph/models.ts +7 -4
  26. package/src/Graph/timeSeries/index.test.ts +22 -0
  27. package/src/Wizard/index.stories.tsx +5 -18
  28. package/src/components/Button/Button.tsx +1 -1
  29. package/src/components/DataTable/Item/DataTableItem.tsx +2 -0
  30. package/src/components/Header/Header.tsx +1 -1
  31. package/src/components/Modal/ModalActions.tsx +4 -2
  32. package/src/Graph/BasicComponents/Lines/Threshold/usePointsOnline.ts +0 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "23.10.18-MON-17793-create-graph-component-centreon-ui.7",
3
+ "version": "23.10.18-MON-17793-create-graph-component-centreon-ui.8",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -92,6 +92,12 @@
92
92
  "@lexical/react": "0.8.0",
93
93
  "@lexical/selection": "0.8.0",
94
94
  "@lexical/utils": "0.8.0",
95
+ "@visx/curve": "^2.1.0",
96
+ "@visx/pattern": "^3.0.0",
97
+ "@visx/scale": "^3.0.0",
98
+ "@visx/shape": "^2.12.2",
99
+ "@visx/threshold": "^2.12.2",
100
+ "@visx/visx": "2.16.0",
95
101
  "anylogger": "^1.0.11",
96
102
  "d3-array": "3.2.0",
97
103
  "humanize-duration": "^3.27.3",
@@ -2,67 +2,69 @@ import { makeStyles } from 'tss-react/mui';
2
2
 
3
3
  import { alpha } from '@mui/material';
4
4
 
5
- export const useDashboardLayoutStyles = makeStyles()((theme) => ({
6
- container: {
7
- '& .react-grid-item': {
8
- borderRadius: theme.shape.borderRadius,
9
- transition: theme.transitions.create('all', {
10
- delay: 0,
11
- duration: 100,
12
- easing: theme.transitions.easing.easeOut
13
- })
14
- },
15
- '& .react-grid-item.react-draggable-dragging': {
16
- boxShadow: theme.shadows[3]
17
- },
18
- '& .react-grid-item.react-grid-placeholder': {
19
- backgroundColor: alpha(theme.palette.primary.main, 0.7)
20
- },
21
- '& .react-grid-item.resizing': {
22
- boxShadow: theme.shadows[3]
23
- },
24
- '& .react-resizable-handle': {
25
- backgroundColor: theme.palette.action.focus,
26
- backgroundImage: 'none',
27
- borderRadius: theme.shape.borderRadius,
28
- display: 'block',
29
- opacity: 0,
30
- position: 'absolute'
31
- },
32
- '& .react-resizable-handle.react-resizable-handle-e': {
33
- cursor: 'ew-resize',
34
- height: `calc(100% - ${theme.spacing(3)})`,
35
- marginTop: 0,
36
- right: 0,
37
- top: 0,
38
- transform: 'rotate(0deg)',
39
- width: theme.spacing(1)
40
- },
41
- '& .react-resizable-handle.react-resizable-handle-s': {
42
- bottom: 0,
43
- cursor: 'ns-resize',
44
- height: theme.spacing(1),
45
- left: 0,
46
- marginLeft: 0,
47
- transform: 'rotate(0deg)',
48
- width: `calc(100% - ${theme.spacing(3)})`
49
- },
50
- '& .react-resizable-handle.react-resizable-handle-se': {
51
- bottom: 0,
52
- cursor: 'nwse-resize',
53
- height: theme.spacing(2),
54
- right: 0,
55
- transform: 'rotate(0deg)',
56
- width: theme.spacing(2)
57
- },
58
- '& .react-resizable-handle::after': {
59
- content: 'none'
60
- },
61
- '& .react-resizable-handle:hover': {
62
- opacity: 1
5
+ export const useDashboardLayoutStyles = makeStyles<boolean>()(
6
+ (theme, isStatic: boolean) => ({
7
+ container: {
8
+ '& .react-grid-item': {
9
+ borderRadius: theme.shape.borderRadius,
10
+ transition: theme.transitions.create('all', {
11
+ delay: 0,
12
+ duration: 100,
13
+ easing: theme.transitions.easing.easeOut
14
+ })
15
+ },
16
+ '& .react-grid-item.react-draggable-dragging': {
17
+ boxShadow: theme.shadows[3]
18
+ },
19
+ '& .react-grid-item.react-grid-placeholder': {
20
+ backgroundColor: alpha(theme.palette.primary.main, 0.7)
21
+ },
22
+ '& .react-grid-item.resizing': {
23
+ boxShadow: theme.shadows[3]
24
+ },
25
+ '& .react-resizable-handle': {
26
+ backgroundColor: theme.palette.action.focus,
27
+ backgroundImage: 'none',
28
+ borderRadius: theme.shape.borderRadius,
29
+ display: isStatic ? 'none' : 'block',
30
+ opacity: 0,
31
+ position: 'absolute'
32
+ },
33
+ '& .react-resizable-handle.react-resizable-handle-e': {
34
+ cursor: 'ew-resize',
35
+ height: `calc(100% - ${theme.spacing(3)})`,
36
+ marginTop: 0,
37
+ right: 0,
38
+ top: 0,
39
+ transform: 'rotate(0deg)',
40
+ width: theme.spacing(1)
41
+ },
42
+ '& .react-resizable-handle.react-resizable-handle-s': {
43
+ bottom: 0,
44
+ cursor: 'ns-resize',
45
+ height: theme.spacing(1),
46
+ left: 0,
47
+ marginLeft: 0,
48
+ transform: 'rotate(0deg)',
49
+ width: `calc(100% - ${theme.spacing(3)})`
50
+ },
51
+ '& .react-resizable-handle.react-resizable-handle-se': {
52
+ bottom: 0,
53
+ cursor: 'nwse-resize',
54
+ height: theme.spacing(2),
55
+ right: 0,
56
+ transform: 'rotate(0deg)',
57
+ width: theme.spacing(2)
58
+ },
59
+ '& .react-resizable-handle::after': {
60
+ content: 'none'
61
+ },
62
+ '& .react-resizable-handle:hover': {
63
+ opacity: 1
64
+ }
63
65
  }
64
- }
65
- }));
66
+ })
67
+ );
66
68
 
67
69
  export const useDashboardItemStyles = makeStyles()((theme) => ({
68
70
  widgetContainer: {
@@ -1,4 +1,4 @@
1
- import { ReactElement, useMemo } from 'react';
1
+ import { useMemo, ReactElement } from 'react';
2
2
 
3
3
  import { Grid as VisxGrid } from '@visx/visx';
4
4
  import { scaleLinear } from '@visx/scale';
@@ -18,7 +18,7 @@ interface DashboardItemProps {
18
18
  children: ReactElement;
19
19
  className?: string;
20
20
  header?: ReactElement;
21
- key: string;
21
+ id: string;
22
22
  onMouseDown?: (e: MouseEvent<HTMLDivElement>) => void;
23
23
  onMouseUp?: (e: MouseEvent<HTMLDivElement>) => void;
24
24
  onTouchEnd?: (e) => void;
@@ -29,13 +29,13 @@ const Item = forwardRef(
29
29
  (
30
30
  {
31
31
  children,
32
- key,
33
32
  style,
34
33
  className,
35
34
  header,
36
35
  onMouseDown,
37
36
  onMouseUp,
38
- onTouchEnd
37
+ onTouchEnd,
38
+ id
39
39
  }: DashboardItemProps,
40
40
  ref: ForwardedRef<HTMLDivElement>
41
41
  ): ReactElement => {
@@ -55,14 +55,17 @@ const Item = forwardRef(
55
55
  Component: (
56
56
  <div
57
57
  className={className}
58
- key={key}
59
58
  ref={ref}
60
59
  style={style}
61
60
  {...cardContainerListeners}
62
61
  >
63
62
  <Card className={classes.widgetContainer}>
64
63
  {header && (
65
- <div {...listeners} className={classes.widgetHeader}>
64
+ <div
65
+ {...listeners}
66
+ className={classes.widgetHeader}
67
+ data-testid={`${id}_move_panel`}
68
+ >
66
69
  {header}
67
70
  </div>
68
71
  )}
@@ -70,7 +73,7 @@ const Item = forwardRef(
70
73
  </Card>
71
74
  </div>
72
75
  ),
73
- memoProps: [key, style, className, header]
76
+ memoProps: [style, className, header]
74
77
  });
75
78
  }
76
79
  );
@@ -1,12 +1,22 @@
1
- import DialogContentText from '@mui/material/DialogContentText';
1
+ import { DialogContentText, Typography } from '@mui/material';
2
2
 
3
3
  import Dialog, { Props as DialogProps } from '..';
4
4
 
5
- type Props = DialogProps & { labelMessage?: string | null };
5
+ type Props = DialogProps & {
6
+ labelMessage?: string | null;
7
+ labelSecondMessage?: string | null;
8
+ };
6
9
 
7
- const Confirm = ({ labelMessage, ...rest }: Props): JSX.Element => (
10
+ const Confirm = ({
11
+ labelMessage,
12
+ labelSecondMessage,
13
+ ...rest
14
+ }: Props): JSX.Element => (
8
15
  <Dialog {...rest}>
9
- {labelMessage && <DialogContentText>{labelMessage}</DialogContentText>}
16
+ <DialogContentText>
17
+ {labelMessage && <Typography>{labelMessage}</Typography>}
18
+ {labelSecondMessage && <Typography>{labelSecondMessage}</Typography>}
19
+ </DialogContentText>
10
20
  </Dialog>
11
21
  );
12
22
 
@@ -35,6 +35,7 @@ export type Props = {
35
35
  confirmDisabled?: boolean;
36
36
  contentWidth?: number;
37
37
  dialogActionsClassName?: string;
38
+ dialogConfirmButtonClassName?: string;
38
39
  dialogContentClassName?: string;
39
40
  dialogPaperClassName?: string;
40
41
  dialogTitleClassName?: string;
@@ -65,6 +66,7 @@ const Dialog = ({
65
66
  dialogTitleClassName,
66
67
  dialogContentClassName,
67
68
  dialogActionsClassName,
69
+ dialogConfirmButtonClassName,
68
70
  ...rest
69
71
  }: Props): JSX.Element => {
70
72
  const { classes, cx } = useStyles({ contentWidth });
@@ -97,6 +99,7 @@ const Dialog = ({
97
99
  )}
98
100
  <Button
99
101
  aria-label={labelConfirm || ''}
102
+ className={dialogConfirmButtonClassName}
100
103
  color="primary"
101
104
  disabled={confirmDisabled}
102
105
  endIcon={submitting && <CircularProgress size={15} />}
@@ -72,17 +72,13 @@ export default memo(RegularLine, (prevProps, nextProps) => {
72
72
  timeSeries: prevTimeSeries,
73
73
  graphHeight: prevGraphHeight,
74
74
  highlight: prevHighlight,
75
- xScale: prevXScale,
76
- shapeLinePath: prevShapeLinePath,
77
- shapeAreaClosed: prevShapeAreaClosed
75
+ xScale: prevXScale
78
76
  } = prevProps;
79
77
  const {
80
78
  timeSeries: nextTimeSeries,
81
79
  graphHeight: nextGraphHeight,
82
80
  highlight: nextHighlight,
83
- xScale: nextXScale,
84
- shapeLinePath: nextShapeLinePath,
85
- shapeAreaClosed: nextShapeAreaClosed
81
+ xScale: nextXScale
86
82
  } = nextProps;
87
83
 
88
84
  const prevXScaleRange = prevXScale.range();
@@ -92,8 +88,6 @@ export default memo(RegularLine, (prevProps, nextProps) => {
92
88
  equals(prevTimeSeries, nextTimeSeries) &&
93
89
  equals(prevGraphHeight, nextGraphHeight) &&
94
90
  equals(prevHighlight, nextHighlight) &&
95
- equals(prevXScaleRange, nextXScaleRange) &&
96
- equals(prevShapeAreaClosed, nextShapeAreaClosed) &&
97
- equals(prevShapeLinePath, nextShapeLinePath)
91
+ equals(prevXScaleRange, nextXScaleRange)
98
92
  );
99
93
  });
@@ -5,12 +5,14 @@ import { Threshold } from '@visx/threshold';
5
5
  import { TimeValue } from '../../../timeSeries/models';
6
6
 
7
7
  interface Props {
8
+ fillAboveArea: string;
9
+ fillBelowArea: string;
10
+ fillOpacity?: number;
8
11
  getX: ScaleLinear<number, number>;
9
12
  getY0: ScaleLinear<number, number>;
10
13
  getY1: ScaleLinear<number, number>;
11
14
  graphHeight: number;
12
- lineColorY0: string;
13
- lineColorY1: string;
15
+ id?: string;
14
16
  timeSeries: Array<TimeValue>;
15
17
  }
16
18
 
@@ -18,26 +20,28 @@ const BasicThreshold = ({
18
20
  getX,
19
21
  getY0,
20
22
  getY1,
21
- lineColorY0,
22
- lineColorY1,
23
23
  graphHeight,
24
- timeSeries
24
+ timeSeries,
25
+ fillOpacity = 0.1,
26
+ id = 'threshold',
27
+ fillAboveArea,
28
+ fillBelowArea
25
29
  }: Props): JSX.Element => {
26
30
  return (
27
31
  <Threshold
28
32
  aboveAreaProps={{
29
- fill: lineColorY1,
30
- fillOpacity: 0.1
33
+ fill: fillAboveArea,
34
+ fillOpacity
31
35
  }}
32
36
  belowAreaProps={{
33
- fill: lineColorY0,
34
- fillOpacity: 0.1
37
+ fill: fillBelowArea,
38
+ fillOpacity
35
39
  }}
36
40
  clipAboveTo={0}
37
41
  clipBelowTo={graphHeight}
38
42
  curve={curveBasis}
39
43
  data={timeSeries}
40
- id="threshold"
44
+ id={id}
41
45
  x={getX}
42
46
  y0={getY0}
43
47
  y1={getY1}
@@ -1,51 +1,45 @@
1
1
  import { Shape } from '@visx/visx';
2
- import { isEmpty, isNil, prop } from 'ramda';
3
- import { ScaleLinear } from 'd3-scale';
2
+ import { isEmpty, isNil } from 'ramda';
4
3
 
5
- import { getTime } from '../../../timeSeries/index';
6
- import { TimeValue } from '../../../timeSeries/models';
7
-
8
- import usePointsOnline from './usePointsOnline';
9
- import { Data } from './models';
10
-
11
- interface Circle {
12
- dataY0: Data;
13
- dataY1: Data;
14
- dataYOrigin: Data;
15
- timeValue: TimeValue;
16
- variation: number;
17
- xScale: ScaleLinear<number, number>;
18
- }
4
+ import { Circle } from './models';
5
+ import useCoordinateCircle from './useCoordinateCircle';
19
6
 
20
7
  const Circle = ({
21
- timeValue,
22
8
  dataY0,
23
9
  dataY1,
24
10
  dataYOrigin,
25
11
  xScale,
26
- variation
12
+ factors,
13
+ timeSeries,
14
+ getCountDisplayedCircles
27
15
  }: Circle): JSX.Element | null => {
28
- const { metric: metricY0, yScale: y0Scale } = dataY0;
29
- const { metric: metricY1, yScale: y1Scale } = dataY1;
30
- const { metric: metricYOrigin, yScale } = dataYOrigin;
31
-
32
- const x = xScale(getTime(timeValue));
33
- const y = yScale(prop(metricYOrigin, timeValue));
34
-
35
- const y0 = y0Scale(prop(metricY0, timeValue) + variation);
36
-
37
- const y1 = y1Scale(prop(metricY1, timeValue) - variation);
38
- const coordinate = usePointsOnline({
39
- pointLower: { x, y: y0 },
40
- pointOrigin: { x, y },
41
- pointUpper: { x, y: y1 }
16
+ const coordinates = useCoordinateCircle({
17
+ dataY0,
18
+ dataY1,
19
+ dataYOrigin,
20
+ factors,
21
+ getCountDisplayedCircles,
22
+ timeSeries,
23
+ xScale
42
24
  });
43
25
 
44
- if (isEmpty(coordinate) || isNil(coordinate)) {
26
+ if (isEmpty(coordinates) || isNil(coordinates)) {
45
27
  return null;
46
28
  }
47
29
 
48
- return <Shape.Circle cx={coordinate.x} cy={coordinate.y} fill="red" r={2} />;
30
+ return (
31
+ <g>
32
+ {coordinates.map(({ x, y }) => (
33
+ <Shape.Circle
34
+ cx={x}
35
+ cy={y}
36
+ fill="red"
37
+ key={`${x.toString()}-${y.toString()}`}
38
+ r={2}
39
+ />
40
+ ))}
41
+ </g>
42
+ );
49
43
  };
50
44
 
51
45
  export default Circle;
@@ -0,0 +1,82 @@
1
+ import { PatternLines } from '@visx/pattern';
2
+ import { ScaleLinear } from 'd3-scale';
3
+ import { prop } from 'ramda';
4
+
5
+ import { useTheme } from '@mui/material/styles';
6
+
7
+ import { getTime } from '../../../timeSeries/index';
8
+ import { TimeValue } from '../../../timeSeries/models';
9
+ import { adjustGraphData } from '../../../helpers/index';
10
+ import { GraphData } from '../../../models';
11
+
12
+ import BasicThreshold from './BasicThreshold';
13
+ import useDataThreshold from './useDataThreshold';
14
+
15
+ interface Props {
16
+ data: GraphData;
17
+ display: boolean;
18
+ graphHeight: number;
19
+ leftScale: ScaleLinear<number, number>;
20
+ rightScale: ScaleLinear<number, number>;
21
+ xScale: ScaleLinear<number, number>;
22
+ }
23
+
24
+ const ThresholdWithPatternLines = ({
25
+ graphHeight,
26
+ data,
27
+ display,
28
+ leftScale,
29
+ rightScale,
30
+ xScale
31
+ }: Props): JSX.Element | null => {
32
+ const theme = useTheme();
33
+
34
+ const { lines, timeSeries } = adjustGraphData(data);
35
+
36
+ const { dataY0, dataY1, displayThreshold } = useDataThreshold({
37
+ display,
38
+ leftScale,
39
+ lines,
40
+ rightScale
41
+ });
42
+
43
+ if (!dataY0 || !dataY1 || !displayThreshold) {
44
+ return null;
45
+ }
46
+ const { metric: metricY0, yScale: y0Scale } = dataY0;
47
+ const { metric: metricY1, yScale: y1Scale } = dataY1;
48
+
49
+ const getX = (timeValue: TimeValue): number => xScale(getTime(timeValue));
50
+
51
+ const getY0 = (timeValue: TimeValue): number =>
52
+ y0Scale(prop(metricY0, timeValue)) ?? null;
53
+
54
+ const getY1 = (timeValue: TimeValue): number =>
55
+ y1Scale(prop(metricY1, timeValue)) ?? null;
56
+
57
+ return (
58
+ <>
59
+ <BasicThreshold
60
+ fillAboveArea={"url('#lines')"}
61
+ fillBelowArea={"url('#lines')"}
62
+ fillOpacity={0.8}
63
+ getX={getX}
64
+ getY0={getY0}
65
+ getY1={getY1}
66
+ graphHeight={graphHeight}
67
+ timeSeries={timeSeries}
68
+ />
69
+ <PatternLines
70
+ data-testid="patternLinesExclusionPeriods"
71
+ height={5}
72
+ id="lines"
73
+ orientation={['diagonal']}
74
+ stroke={theme.palette.text.primary}
75
+ strokeWidth={1}
76
+ width={5}
77
+ />
78
+ </>
79
+ );
80
+ };
81
+
82
+ export default ThresholdWithPatternLines;
@@ -0,0 +1,72 @@
1
+ import { curveBasis } from '@visx/curve';
2
+ import { LinePath } from '@visx/shape';
3
+ import { ScaleLinear } from 'd3-scale';
4
+
5
+ import { useTheme } from '@mui/material/styles';
6
+
7
+ import { useMemoComponent } from '@centreon/ui';
8
+
9
+ import { TimeValue } from '../../../timeSeries/models';
10
+
11
+ import BasicThreshold from './BasicThreshold';
12
+ import { Data, FactorsVariation } from './models';
13
+ import { getVariationEnvelopThresholdData } from './helpers';
14
+
15
+ interface Props {
16
+ dataY0: Data;
17
+ dataY1: Data;
18
+ factors: FactorsVariation;
19
+ graphHeight: number;
20
+ timeSeries: Array<TimeValue>;
21
+ xScale: ScaleLinear<number, number>;
22
+ }
23
+
24
+ const ThresholdWithVariation = ({
25
+ dataY0,
26
+ dataY1,
27
+ timeSeries,
28
+ xScale,
29
+ graphHeight,
30
+ factors
31
+ }: Props): JSX.Element => {
32
+ const theme = useTheme();
33
+
34
+ const { getX, getY0, getY1, lineColorY0, lineColorY1 } =
35
+ getVariationEnvelopThresholdData({
36
+ dataY0,
37
+ dataY1,
38
+ factors,
39
+ xScale
40
+ });
41
+
42
+ const props = {
43
+ curve: curveBasis,
44
+ data: timeSeries,
45
+ stroke: theme.palette.secondary.main,
46
+ strokeDasharray: 5,
47
+ strokeOpacity: 0.8,
48
+ x: getX
49
+ };
50
+
51
+ return useMemoComponent({
52
+ Component: (
53
+ <>
54
+ <BasicThreshold
55
+ fillAboveArea={lineColorY1}
56
+ fillBelowArea={lineColorY0}
57
+ getX={getX}
58
+ getY0={getY0}
59
+ getY1={getY1}
60
+ graphHeight={graphHeight}
61
+ timeSeries={timeSeries}
62
+ />
63
+
64
+ <LinePath {...props} y={getY0} />
65
+ <LinePath {...props} y={getY1} />
66
+ </>
67
+ ),
68
+ memoProps: [timeSeries, factors]
69
+ });
70
+ };
71
+
72
+ export default ThresholdWithVariation;