@centreon/ui 23.10.18-MON-17793-create-graph-component-centreon-ui.6 → 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 (38) 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/{useDataRegularLines.ts → RegularLines/useDataRegularLines.ts} +3 -4
  9. package/src/Graph/BasicComponents/Lines/{useDataStackLines.ts → StackedLines/useDataStackLines.ts} +4 -5
  10. package/src/Graph/BasicComponents/Lines/Threshold/BasicThreshold.tsx +52 -0
  11. package/src/Graph/BasicComponents/Lines/Threshold/Circle.tsx +45 -0
  12. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithPatternLines.tsx +82 -0
  13. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithVariation.tsx +72 -0
  14. package/src/Graph/BasicComponents/Lines/Threshold/helpers/index.ts +122 -0
  15. package/src/Graph/BasicComponents/Lines/Threshold/index.tsx +19 -30
  16. package/src/Graph/BasicComponents/Lines/Threshold/models.ts +54 -0
  17. package/src/Graph/BasicComponents/Lines/Threshold/useCoordinateCircle.ts +59 -0
  18. package/src/Graph/BasicComponents/Lines/{useDataThreshold.ts → Threshold/useDataThreshold.ts} +41 -7
  19. package/src/Graph/BasicComponents/Lines/index.tsx +73 -14
  20. package/src/Graph/BasicComponents/Lines/models.ts +10 -1
  21. package/src/Graph/BasicComponents/useFilterLines.ts +6 -15
  22. package/src/Graph/Graph.tsx +7 -6
  23. package/src/Graph/helpers/doc.ts +6 -14
  24. package/src/Graph/helpers/index.ts +28 -1
  25. package/src/Graph/index.stories.tsx +143 -16
  26. package/src/Graph/mockedData/exclusionPeriodFirstPeriod.json +714 -0
  27. package/src/Graph/mockedData/exclusionPeriodFourthPeriod.json +588 -0
  28. package/src/Graph/mockedData/exclusionPeriodSecondPeriod.json +847 -0
  29. package/src/Graph/mockedData/exclusionPeriodThirdPeriod.json +434 -0
  30. package/src/Graph/mockedData/lastDayThreshold.json +2037 -2037
  31. package/src/Graph/models.ts +10 -3
  32. package/src/Graph/timeSeries/index.test.ts +22 -0
  33. package/src/Graph/timeSeries/models.ts +5 -4
  34. package/src/Wizard/index.stories.tsx +5 -18
  35. package/src/components/Button/Button.tsx +1 -1
  36. package/src/components/DataTable/Item/DataTableItem.tsx +2 -0
  37. package/src/components/Header/Header.tsx +1 -1
  38. package/src/components/Modal/ModalActions.tsx +4 -2
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.6",
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
  });
@@ -1,9 +1,8 @@
1
1
  import { ScaleLinear } from 'd3-scale';
2
2
 
3
- import { Line, TimeValue } from '../../timeSeries/models';
4
- import { displayArea } from '../../helpers/index';
5
-
6
- import { AreaRegularLines } from './models';
3
+ import { Line, TimeValue } from '../../../timeSeries/models';
4
+ import { displayArea } from '../../../helpers/index';
5
+ import { AreaRegularLines } from '../models';
7
6
 
8
7
  interface RegularLinesData {
9
8
  display: boolean;
@@ -1,10 +1,9 @@
1
1
  import { ScaleLinear } from 'd3-scale';
2
2
 
3
- import { getStackedYScale } from '../../timeSeries';
4
- import { displayArea } from '../../helpers/index';
5
- import { Line, TimeValue } from '../../timeSeries/models';
6
-
7
- import { AreaStackedLines } from './models';
3
+ import { getStackedYScale } from '../../../timeSeries';
4
+ import { displayArea } from '../../../helpers/index';
5
+ import { Line, TimeValue } from '../../../timeSeries/models';
6
+ import { AreaStackedLines } from '../models';
8
7
 
9
8
  interface StackedLinesData {
10
9
  displayAreaInvertedStackedLines: boolean;
@@ -0,0 +1,52 @@
1
+ import { curveBasis } from '@visx/curve';
2
+ import { ScaleLinear } from 'd3-scale';
3
+ import { Threshold } from '@visx/threshold';
4
+
5
+ import { TimeValue } from '../../../timeSeries/models';
6
+
7
+ interface Props {
8
+ fillAboveArea: string;
9
+ fillBelowArea: string;
10
+ fillOpacity?: number;
11
+ getX: ScaleLinear<number, number>;
12
+ getY0: ScaleLinear<number, number>;
13
+ getY1: ScaleLinear<number, number>;
14
+ graphHeight: number;
15
+ id?: string;
16
+ timeSeries: Array<TimeValue>;
17
+ }
18
+
19
+ const BasicThreshold = ({
20
+ getX,
21
+ getY0,
22
+ getY1,
23
+ graphHeight,
24
+ timeSeries,
25
+ fillOpacity = 0.1,
26
+ id = 'threshold',
27
+ fillAboveArea,
28
+ fillBelowArea
29
+ }: Props): JSX.Element => {
30
+ return (
31
+ <Threshold
32
+ aboveAreaProps={{
33
+ fill: fillAboveArea,
34
+ fillOpacity
35
+ }}
36
+ belowAreaProps={{
37
+ fill: fillBelowArea,
38
+ fillOpacity
39
+ }}
40
+ clipAboveTo={0}
41
+ clipBelowTo={graphHeight}
42
+ curve={curveBasis}
43
+ data={timeSeries}
44
+ id={id}
45
+ x={getX}
46
+ y0={getY0}
47
+ y1={getY1}
48
+ />
49
+ );
50
+ };
51
+
52
+ export default BasicThreshold;
@@ -0,0 +1,45 @@
1
+ import { Shape } from '@visx/visx';
2
+ import { isEmpty, isNil } from 'ramda';
3
+
4
+ import { Circle } from './models';
5
+ import useCoordinateCircle from './useCoordinateCircle';
6
+
7
+ const Circle = ({
8
+ dataY0,
9
+ dataY1,
10
+ dataYOrigin,
11
+ xScale,
12
+ factors,
13
+ timeSeries,
14
+ getCountDisplayedCircles
15
+ }: Circle): JSX.Element | null => {
16
+ const coordinates = useCoordinateCircle({
17
+ dataY0,
18
+ dataY1,
19
+ dataYOrigin,
20
+ factors,
21
+ getCountDisplayedCircles,
22
+ timeSeries,
23
+ xScale
24
+ });
25
+
26
+ if (isEmpty(coordinates) || isNil(coordinates)) {
27
+ return null;
28
+ }
29
+
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
+ );
43
+ };
44
+
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;