@centreon/ui 23.10.50 → 23.10.52

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 (33) hide show
  1. package/package.json +1 -1
  2. package/src/Dashboard/Layout.tsx +4 -2
  3. package/src/Graph/BasicComponents/Axes/index.tsx +1 -1
  4. package/src/Graph/BasicComponents/Lines/RegularLines/index.tsx +6 -3
  5. package/src/Graph/BasicComponents/Lines/StackedLines/index.tsx +6 -3
  6. package/src/Graph/BasicComponents/Lines/Threshold/BasicThreshold.tsx +5 -3
  7. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithPatternLines.tsx +5 -1
  8. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithVariation.tsx +5 -1
  9. package/src/Graph/BasicComponents/Lines/Threshold/index.tsx +5 -1
  10. package/src/Graph/BasicComponents/Lines/index.tsx +6 -1
  11. package/src/Graph/BasicComponents/Lines/models.ts +3 -0
  12. package/src/Graph/Graph.tsx +40 -20
  13. package/src/Graph/Header/index.tsx +7 -7
  14. package/src/Graph/InteractiveComponents/AnchorPoint/TooltipAnchorPoint.tsx +48 -22
  15. package/src/Graph/InteractiveComponents/AnchorPoint/index.tsx +14 -12
  16. package/src/Graph/InteractiveComponents/TimeShiftZones/TimeShiftZone.tsx +1 -1
  17. package/src/Graph/index.stories.tsx +10 -0
  18. package/src/Graph/index.tsx +35 -23
  19. package/src/Graph/models.ts +1 -1
  20. package/src/InputField/Select/Autocomplete/Connected/index.tsx +9 -1
  21. package/src/InputField/Select/Autocomplete/Multi/index.tsx +7 -2
  22. package/src/Panel/index.tsx +3 -1
  23. package/src/api/useGraphQuery/index.ts +92 -0
  24. package/src/api/useGraphQuery/models.ts +12 -0
  25. package/src/components/ItemComposition/Item.tsx +37 -0
  26. package/src/components/ItemComposition/ItemComposition.stories.tsx +78 -0
  27. package/src/components/ItemComposition/ItemComposition.styles.ts +26 -0
  28. package/src/components/ItemComposition/ItemComposition.tsx +41 -0
  29. package/src/components/ItemComposition/index.ts +6 -0
  30. package/src/components/Modal/Modal.styles.ts +1 -1
  31. package/src/components/index.ts +1 -0
  32. package/src/index.ts +3 -0
  33. package/src/Graph/InteractiveComponents/AnchorPoint/AnchorPoint.styles.ts +0 -58
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "23.10.50",
3
+ "version": "23.10.52",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -16,6 +16,7 @@ interface DashboardLayoutProps<T> {
16
16
  changeLayout?: (newLayout: Array<Layout>) => void;
17
17
  children: Array<JSX.Element>;
18
18
  displayGrid?: boolean;
19
+ isStatic?: boolean;
19
20
  layout: Array<T>;
20
21
  }
21
22
 
@@ -23,9 +24,10 @@ const Layout = <T extends Layout>({
23
24
  children,
24
25
  changeLayout,
25
26
  displayGrid,
26
- layout
27
+ layout,
28
+ isStatic = false
27
29
  }: DashboardLayoutProps<T>): JSX.Element => {
28
- const { classes } = useDashboardLayoutStyles();
30
+ const { classes } = useDashboardLayoutStyles(isStatic);
29
31
 
30
32
  const [columns, setColumns] = useState(getColumnsFromScreenSize());
31
33
 
@@ -55,7 +55,7 @@ const Axes = ({
55
55
  numTicks={xTickCount}
56
56
  scale={xScale}
57
57
  tickFormat={formatAxisTick}
58
- top={height}
58
+ top={height - 5}
59
59
  />
60
60
 
61
61
  {axisLeft.displayUnit && <UnitLabel unit={firstUnit} x={-4} />}
@@ -1,15 +1,17 @@
1
1
  import { memo } from 'react';
2
2
 
3
- import { Curve, Shape } from '@visx/visx';
3
+ import { Shape } from '@visx/visx';
4
4
  import { ScaleLinear, ScaleTime } from 'd3-scale';
5
5
  import { equals, isNil, prop } from 'ramda';
6
6
 
7
7
  import { getTime } from '../../../timeSeries';
8
8
  import { TimeValue } from '../../../timeSeries/models';
9
9
  import { getFillColor } from '../../../common';
10
+ import { CurveType } from '../models';
10
11
 
11
12
  interface Props {
12
13
  areaColor: string;
14
+ curve: CurveType;
13
15
  filled: boolean;
14
16
  graphHeight: number;
15
17
  highlight?: boolean;
@@ -35,10 +37,11 @@ const RegularLine = ({
35
37
  xScale,
36
38
  areaColor,
37
39
  transparency,
38
- graphHeight
40
+ graphHeight,
41
+ curve
39
42
  }: Props): JSX.Element => {
40
43
  const props = {
41
- curve: Curve.curveLinear,
44
+ curve,
42
45
  data: timeSeries,
43
46
  defined: (value): boolean => !isNil(value[metric]),
44
47
  opacity: 1,
@@ -1,4 +1,4 @@
1
- import { Curve, Shape } from '@visx/visx';
1
+ import { Shape } from '@visx/visx';
2
2
  import { ScaleLinear, ScaleTime } from 'd3-scale';
3
3
  import { all, isNil, map, not, nth, path, pipe, prop } from 'ramda';
4
4
 
@@ -7,8 +7,10 @@ import { StackValue } from '../../../InteractiveComponents/AnchorPoint/models';
7
7
  import { getFillColor } from '../../../common';
8
8
  import { getTime } from '../../../timeSeries';
9
9
  import { Line, TimeValue } from '../../../timeSeries/models';
10
+ import { CurveType } from '../models';
10
11
 
11
12
  interface Props {
13
+ curve: CurveType;
12
14
  displayAnchor: boolean;
13
15
  lines: Array<Line>;
14
16
  timeSeries: Array<TimeValue>;
@@ -21,11 +23,12 @@ const StackLines = ({
21
23
  lines,
22
24
  yScale,
23
25
  xScale,
24
- displayAnchor
26
+ displayAnchor,
27
+ curve
25
28
  }: Props): JSX.Element => {
26
29
  return (
27
30
  <Shape.AreaStack
28
- curve={Curve.curveLinear}
31
+ curve={curve}
29
32
  data={timeSeries}
30
33
  defined={(d): boolean => {
31
34
  return pipe(
@@ -1,9 +1,10 @@
1
- import { curveBasis } from '@visx/curve';
2
1
  import { Threshold } from '@visx/threshold';
3
2
 
4
3
  import { TimeValue } from '../../../timeSeries/models';
4
+ import { CurveType } from '../models';
5
5
 
6
6
  interface Props {
7
+ curve: CurveType;
7
8
  fillAboveArea: string;
8
9
  fillBelowArea: string;
9
10
  fillOpacity?: number;
@@ -24,7 +25,8 @@ const BasicThreshold = ({
24
25
  fillOpacity = 0.1,
25
26
  id,
26
27
  fillAboveArea,
27
- fillBelowArea
28
+ fillBelowArea,
29
+ curve
28
30
  }: Props): JSX.Element => {
29
31
  return (
30
32
  <Threshold
@@ -38,7 +40,7 @@ const BasicThreshold = ({
38
40
  }}
39
41
  clipAboveTo={0}
40
42
  clipBelowTo={graphHeight}
41
- curve={curveBasis}
43
+ curve={curve}
42
44
  data={timeSeries}
43
45
  id={id}
44
46
  x={getX}
@@ -5,11 +5,13 @@ import { useTheme } from '@mui/material/styles';
5
5
 
6
6
  import { adjustGraphData } from '../../../helpers/index';
7
7
  import { GraphData, PatternOrientation } from '../../../models';
8
+ import { CurveType } from '../models';
8
9
 
9
10
  import BasicThreshold from './BasicThreshold';
10
11
  import useScaleThreshold from './useScaleThreshold';
11
12
 
12
13
  interface Props {
14
+ curve: CurveType;
13
15
  data: GraphData;
14
16
  graphHeight: number;
15
17
  id: string;
@@ -26,7 +28,8 @@ const ThresholdWithPatternLines = ({
26
28
  leftScale,
27
29
  rightScale,
28
30
  xScale,
29
- id
31
+ id,
32
+ curve
30
33
  }: Props): JSX.Element | null => {
31
34
  const theme = useTheme();
32
35
 
@@ -47,6 +50,7 @@ const ThresholdWithPatternLines = ({
47
50
  return (
48
51
  <>
49
52
  <BasicThreshold
53
+ curve={curve}
50
54
  fillAboveArea={"url('#lines')"}
51
55
  fillBelowArea={"url('#lines')"}
52
56
  fillOpacity={0.8}
@@ -4,10 +4,12 @@ import { LinePath } from '@visx/shape';
4
4
  import { useTheme } from '@mui/material/styles';
5
5
 
6
6
  import { TimeValue } from '../../../timeSeries/models';
7
+ import { CurveType } from '../models';
7
8
 
8
9
  import BasicThreshold from './BasicThreshold';
9
10
 
10
11
  interface Props {
12
+ curve: CurveType;
11
13
  getX: (timeValue: TimeValue) => number;
12
14
  getY0Variation: (timeValue: TimeValue) => number;
13
15
  getY1Variation: (timeValue: TimeValue) => number;
@@ -24,7 +26,8 @@ const ThresholdWithVariation = ({
24
26
  getY1Variation,
25
27
  graphHeight,
26
28
  lineColorY1,
27
- lineColorY0
29
+ lineColorY0,
30
+ curve
28
31
  }: Props): JSX.Element => {
29
32
  const theme = useTheme();
30
33
 
@@ -40,6 +43,7 @@ const ThresholdWithVariation = ({
40
43
  return (
41
44
  <>
42
45
  <BasicThreshold
46
+ curve={curve}
43
47
  fillAboveArea={lineColorY1}
44
48
  fillBelowArea={lineColorY0}
45
49
  getX={getX}
@@ -7,6 +7,7 @@ import {
7
7
  VariationThreshold
8
8
  } from '../../../models';
9
9
  import { TimeValue } from '../../../timeSeries/models';
10
+ import { CurveType } from '../models';
10
11
 
11
12
  import BasicThreshold from './BasicThreshold';
12
13
  import Circle from './Circle';
@@ -16,6 +17,7 @@ import { WrapperThresholdLinesModel } from './models';
16
17
  import useScaleThreshold from './useScaleThreshold';
17
18
 
18
19
  interface Props extends WrapperThresholdLinesModel {
20
+ curve: CurveType;
19
21
  graphHeight: number;
20
22
  timeSeries: Array<TimeValue>;
21
23
  }
@@ -27,7 +29,8 @@ const WrapperThresholdLines = ({
27
29
  lines,
28
30
  rightScale,
29
31
  timeSeries,
30
- xScale
32
+ xScale,
33
+ curve
31
34
  }: Props): JSX.Element | null => {
32
35
  const data = useScaleThreshold({
33
36
  areaThresholdLines,
@@ -44,6 +47,7 @@ const WrapperThresholdLines = ({
44
47
  const { getX, getY0, getY1, lineColorY0, lineColorY1, ...rest } = data;
45
48
 
46
49
  const commonProps = {
50
+ curve,
47
51
  fillAboveArea: lineColorY0,
48
52
  fillBelowArea: lineColorY1,
49
53
  getX,
@@ -19,8 +19,10 @@ import {
19
19
  canDisplayThreshold,
20
20
  requiredNumberLinesThreshold
21
21
  } from './Threshold/models';
22
+ import { CurveType } from './models';
22
23
 
23
24
  interface Props extends GlobalAreaLines {
25
+ curve: CurveType;
24
26
  displayAnchor?: DisplayAnchor;
25
27
  displayedLines: Array<Line>;
26
28
  graphSvgRef: MutableRefObject<SVGSVGElement | null>;
@@ -44,7 +46,8 @@ const Lines = ({
44
46
  displayedLines,
45
47
  areaThresholdLines,
46
48
  areaStackedLines,
47
- areaRegularLines
49
+ areaRegularLines,
50
+ curve
48
51
  }: Props): JSX.Element => {
49
52
  const { stackedLinesData, invertedStackedLinesData } = useStackedLines({
50
53
  lines: displayedLines,
@@ -67,6 +70,7 @@ const Lines = ({
67
70
 
68
71
  const displayGuidingLines = displayAnchor?.displayGuidingLines ?? true;
69
72
  const commonStackedLinesProps = {
73
+ curve,
70
74
  displayAnchor: displayGuidingLines,
71
75
  graphHeight: height,
72
76
  graphSvgRef,
@@ -157,6 +161,7 @@ const Lines = ({
157
161
  )}
158
162
  <RegularLine
159
163
  areaColor={areaColor}
164
+ curve={curve}
160
165
  filled={filled}
161
166
  graphHeight={height}
162
167
  highlight={highlight}
@@ -1,4 +1,5 @@
1
1
  import { ScaleLinear } from 'd3-scale';
2
+ import { curveBasis } from '@visx/curve';
2
3
 
3
4
  import { GraphData } from '../../models';
4
5
  import { Line, TimeValue } from '../../timeSeries/models';
@@ -40,3 +41,5 @@ export interface Shape {
40
41
  areaStackedLines: AreaStackedLines;
41
42
  areaThreshold: AreaThreshold;
42
43
  }
44
+
45
+ export type CurveType = typeof curveBasis;
@@ -28,12 +28,15 @@ import {
28
28
  } from './models';
29
29
  import { getLeftScale, getRightScale, getXScale } from './timeSeries';
30
30
  import { useIntersection } from './useGraphIntersection';
31
+ import { CurveType } from './BasicComponents/Lines/models';
31
32
 
32
33
  interface Props extends GraphProps {
34
+ curve: CurveType;
33
35
  graphData: Data;
34
36
  graphInterval: GraphInterval;
35
37
  graphRef: MutableRefObject<HTMLDivElement | null>;
36
38
  legend?: LegendModel;
39
+ marginBottom: number;
37
40
  shapeLines?: GlobalAreaLines;
38
41
  }
39
42
 
@@ -52,15 +55,26 @@ const Graph = ({
52
55
  tooltip,
53
56
  legend,
54
57
  graphRef,
55
- header
58
+ header,
59
+ curve,
60
+ marginBottom
56
61
  }: Props): JSX.Element => {
57
62
  const graphSvgRef = useRef<SVGSVGElement | null>(null);
58
63
 
59
64
  const { classes } = useStyles();
60
65
  const { isInViewport } = useIntersection({ element: graphRef?.current });
61
66
 
67
+ const legendRef = useRef<HTMLDivElement | null>(null);
68
+
62
69
  const graphWidth = width > 0 ? width - margin.left - margin.right : 0;
63
- const graphHeight = height > 0 ? height - margin.top - margin.bottom : 0;
70
+ const graphHeight =
71
+ (height || 0) > 0
72
+ ? (height || 0) -
73
+ margin.top -
74
+ margin.bottom -
75
+ marginBottom -
76
+ (legendRef.current?.getBoundingClientRect().height || 0)
77
+ : 0;
64
78
 
65
79
  const { title, timeSeries, baseAxis, lines } = graphData;
66
80
 
@@ -83,7 +97,7 @@ const Graph = ({
83
97
  getLeftScale({
84
98
  dataLines: displayedLines,
85
99
  dataTimeSeries: timeSeries,
86
- valueGraphHeight: graphHeight
100
+ valueGraphHeight: graphHeight - 35
87
101
  }),
88
102
  [displayedLines, timeSeries, graphHeight]
89
103
  );
@@ -93,7 +107,7 @@ const Graph = ({
93
107
  getRightScale({
94
108
  dataLines: displayedLines,
95
109
  dataTimeSeries: timeSeries,
96
- valueGraphHeight: graphHeight
110
+ valueGraphHeight: graphHeight - 35
97
111
  }),
98
112
  [timeSeries, displayedLines, graphHeight]
99
113
  );
@@ -128,11 +142,15 @@ const Graph = ({
128
142
  />
129
143
  <ClickAwayListener onClickAway={graphTooltipData?.hideTooltip}>
130
144
  <div className={classes.container}>
131
- <LoadingProgress display={loading} height={height} width={width} />
132
- <svg height={height} ref={graphSvgRef} width="100%">
145
+ <LoadingProgress
146
+ display={loading}
147
+ height={graphHeight}
148
+ width={width}
149
+ />
150
+ <svg height={graphHeight + margin.top} ref={graphSvgRef} width="100%">
133
151
  <Group.Group left={margin.left} top={margin.top}>
134
152
  <Grids
135
- height={graphHeight}
153
+ height={graphHeight - margin.top}
136
154
  leftScale={leftScale}
137
155
  width={graphWidth}
138
156
  xScale={xScale}
@@ -145,7 +163,7 @@ const Graph = ({
145
163
  ...axis
146
164
  }}
147
165
  graphInterval={graphInterval}
148
- height={graphHeight}
166
+ height={graphHeight - margin.top}
149
167
  leftScale={leftScale}
150
168
  rightScale={rightScale}
151
169
  width={graphWidth}
@@ -153,10 +171,11 @@ const Graph = ({
153
171
  />
154
172
 
155
173
  <Lines
174
+ curve={curve}
156
175
  displayAnchor={displayAnchor}
157
176
  displayedLines={displayedLines}
158
177
  graphSvgRef={graphSvgRef}
159
- height={graphHeight}
178
+ height={graphHeight - margin.top}
160
179
  leftScale={leftScale}
161
180
  rightScale={rightScale}
162
181
  timeSeries={timeSeries}
@@ -189,7 +208,7 @@ const Graph = ({
189
208
  {(displayAnchor?.displayTooltipsGuidingLines ?? true) && (
190
209
  <TooltipAnchorPoint
191
210
  baseAxis={baseAxis}
192
- graphHeight={graphHeight}
211
+ graphHeight={graphHeight - 35}
193
212
  graphWidth={graphWidth}
194
213
  leftScale={leftScale}
195
214
  lines={displayedLines}
@@ -198,18 +217,19 @@ const Graph = ({
198
217
  xScale={xScale}
199
218
  />
200
219
  )}
201
-
202
- {displayLegend && (
203
- <Legend
204
- base={baseAxis}
205
- displayAnchor={displayAnchor?.displayGuidingLines ?? true}
206
- lines={newLines}
207
- renderExtraComponent={legend?.renderExtraComponent}
208
- timeSeries={timeSeries}
209
- />
210
- )}
211
220
  </div>
212
221
  </ClickAwayListener>
222
+ {displayLegend && (
223
+ <div ref={legendRef}>
224
+ <Legend
225
+ base={baseAxis}
226
+ displayAnchor={displayAnchor?.displayGuidingLines ?? true}
227
+ lines={newLines}
228
+ renderExtraComponent={legend?.renderExtraComponent}
229
+ timeSeries={timeSeries}
230
+ />
231
+ </div>
232
+ )}
213
233
  </>
214
234
  );
215
235
  };
@@ -43,17 +43,17 @@ const Header = ({
43
43
  Component: (
44
44
  <div className={classes.header}>
45
45
  <div />
46
- {displayTitle && (
47
- <div>
46
+ <div>
47
+ {displayTitle && (
48
48
  <Typography align="center" variant="body1">
49
49
  {title}
50
50
  </Typography>
51
+ )}
51
52
 
52
- <Typography align="center" style={{ height: 20 }} variant="body1">
53
- {time}
54
- </Typography>
55
- </div>
56
- )}
53
+ <Typography align="center" style={{ height: 20 }} variant="body1">
54
+ {time}
55
+ </Typography>
56
+ </div>
57
57
  {header?.extraComponent}
58
58
  </div>
59
59
  ),
@@ -1,10 +1,15 @@
1
- import Paper from '@mui/material/Paper';
2
- import { Typography } from '@mui/material';
1
+ import { Tooltip } from '@visx/visx';
2
+
3
+ import { Typography, useTheme } from '@mui/material';
3
4
 
4
5
  import useTooltipAnchorPoint from './useTooltipAnchorPoint';
5
- import { useStyles } from './AnchorPoint.styles';
6
6
  import { TooltipAnchorModel } from './models';
7
7
 
8
+ const baseStyles = {
9
+ ...Tooltip.defaultStyles,
10
+ textAlign: 'center'
11
+ };
12
+
8
13
  const TooltipAnchorPoint = ({
9
14
  timeSeries,
10
15
  xScale,
@@ -15,12 +20,13 @@ const TooltipAnchorPoint = ({
15
20
  lines,
16
21
  baseAxis
17
22
  }: TooltipAnchorModel): JSX.Element => {
23
+ const theme = useTheme();
24
+
18
25
  const {
19
26
  tooltipDataAxisX,
20
27
  tooltipDataAxisYLeft,
21
28
  tooltipLeftAxisX,
22
29
  tooltipLeftAxisYLeft,
23
- tooltipTopAxisX,
24
30
  tooltipTopAxisYLeft,
25
31
  tooltipDataAxisYRight,
26
32
  tooltipTopAxisYRight,
@@ -35,33 +41,53 @@ const TooltipAnchorPoint = ({
35
41
  timeSeries,
36
42
  xScale
37
43
  });
38
- const { classes, cx } = useStyles({
39
- tooltipLeftAxisX,
40
- tooltipLeftAxisYLeft,
41
- tooltipLeftAxisYRight,
42
- tooltipTopAxisX,
43
- tooltipTopAxisYLeft,
44
- tooltipTopAxisYRight
45
- });
44
+
45
+ const cardStyles = {
46
+ backgroundColor: theme.palette.background.paper,
47
+ color: theme.palette.text.primary,
48
+ padding: theme.spacing(0.25, 0.5)
49
+ };
46
50
 
47
51
  return (
48
52
  <>
49
- {tooltipDataAxisYLeft && (
50
- <Paper className={cx(classes.tooltipAxis, classes.tooltipAxisLeftY)}>
51
- <Typography variant="caption">{tooltipDataAxisYLeft}</Typography>
52
- </Paper>
53
- )}
54
53
  {tooltipDataAxisX && (
55
- <Paper className={cx(classes.tooltipAxis, classes.tooltipAxisBottom)}>
54
+ <Tooltip.Tooltip
55
+ left={tooltipLeftAxisX}
56
+ style={{
57
+ ...baseStyles,
58
+ ...cardStyles,
59
+ transform: 'translateX(-70%)'
60
+ }}
61
+ top={0}
62
+ >
56
63
  <Typography variant="caption">{tooltipDataAxisX}</Typography>
57
- </Paper>
64
+ </Tooltip.Tooltip>
65
+ )}
66
+ {tooltipDataAxisYLeft && (
67
+ <Tooltip.Tooltip
68
+ left={tooltipLeftAxisYLeft}
69
+ style={{
70
+ ...baseStyles,
71
+ ...cardStyles,
72
+ transform: 'translateX(-70%) translateY(-100%)'
73
+ }}
74
+ top={tooltipTopAxisYLeft}
75
+ >
76
+ <Typography variant="caption">{tooltipDataAxisYLeft}</Typography>
77
+ </Tooltip.Tooltip>
58
78
  )}
59
79
  {tooltipDataAxisYRight && (
60
- <Paper
61
- className={cx(classes.tooltipAxis, classes.tooltipLeftAxisRightY)}
80
+ <Tooltip.Tooltip
81
+ left={tooltipLeftAxisYRight}
82
+ style={{
83
+ ...baseStyles,
84
+ ...cardStyles,
85
+ transform: 'translateX(-70%) translateY(-80%)'
86
+ }}
87
+ top={tooltipTopAxisYRight}
62
88
  >
63
89
  <Typography variant="caption">{tooltipDataAxisYRight}</Typography>
64
- </Paper>
90
+ </Tooltip.Tooltip>
65
91
  )}
66
92
  </>
67
93
  );
@@ -1,20 +1,22 @@
1
+ import { alpha } from '@mui/system';
2
+
1
3
  interface Props {
2
4
  lineColor: string;
3
5
  x: number;
4
6
  y: number;
5
7
  }
6
8
 
7
- const Point = ({ lineColor, x, y }: Props): JSX.Element => {
8
- return (
9
- <circle
10
- cx={x}
11
- cy={y}
12
- fill="white"
13
- r={4}
14
- stroke={lineColor}
15
- strokeWidth={2}
16
- />
17
- );
18
- };
9
+ const Point = ({ lineColor, x, y }: Props): JSX.Element => (
10
+ <circle
11
+ fill={alpha(lineColor, 0.5)}
12
+ r={4}
13
+ stroke={lineColor}
14
+ strokeWidth={2}
15
+ style={{
16
+ transform: `translate(${x}px, ${y}px)`,
17
+ transition: 'transform 0.08s ease-out'
18
+ }}
19
+ />
20
+ );
19
21
 
20
22
  export default Point;
@@ -53,7 +53,7 @@ const TimeShiftZone = ({
53
53
  className={classes.translationZone}
54
54
  fill={
55
55
  equals(directionHovered, direction)
56
- ? alpha(theme.palette.common.white, 0.5)
56
+ ? alpha(theme.palette.background.paper, 0.2)
57
57
  : 'transparent'
58
58
  }
59
59
  height={graphHeight}
@@ -1,6 +1,7 @@
1
1
  import { useEffect, useState } from 'react';
2
2
 
3
3
  import { Meta, StoryObj } from '@storybook/react';
4
+ import { Curve } from '@visx/visx';
4
5
 
5
6
  import { Button } from '@mui/material';
6
7
  import ButtonGroup from '@mui/material/ButtonGroup';
@@ -335,6 +336,15 @@ export const Graph: Story = {
335
336
  args: argumentsData
336
337
  };
337
338
 
339
+ export const GraphWithStepCurve: Story = {
340
+ ...Template,
341
+ argTypes,
342
+ args: {
343
+ ...argumentsData,
344
+ curve: Curve.curveStep
345
+ }
346
+ };
347
+
338
348
  export const GraphWithTimePeriod: Story = {
339
349
  ...WithTimePeriod,
340
350
  args: {
@@ -1,6 +1,6 @@
1
1
  import { MutableRefObject, useRef } from 'react';
2
2
 
3
- import { Responsive } from '@visx/visx';
3
+ import { Curve, Responsive } from '@visx/visx';
4
4
  import dayjs from 'dayjs';
5
5
  import 'dayjs/locale/en';
6
6
  import 'dayjs/locale/es';
@@ -14,16 +14,19 @@ import Graph from './Graph';
14
14
  import LoadingSkeleton from './LoadingSkeleton';
15
15
  import { GlobalAreaLines, GraphData, GraphProps, LegendModel } from './models';
16
16
  import useGraphData from './useGraphData';
17
+ import { CurveType } from './BasicComponents/Lines/models';
17
18
 
18
19
  dayjs.extend(localizedFormat);
19
20
  dayjs.extend(utcPlugin);
20
21
  dayjs.extend(timezonePlugin);
21
22
 
22
23
  interface Props extends Partial<GraphProps> {
24
+ curve?: CurveType;
23
25
  data?: GraphData;
24
26
  end: string;
25
27
  legend: LegendModel;
26
28
  loading: boolean;
29
+ marginBottom?: number;
27
30
  shapeLines?: GlobalAreaLines;
28
31
  start: string;
29
32
  }
@@ -43,7 +46,9 @@ const WrapperGraph = ({
43
46
  tooltip,
44
47
  annotationEvent,
45
48
  legend,
46
- header
49
+ header,
50
+ curve = Curve.curveLinear,
51
+ marginBottom = 0
47
52
  }: Props): JSX.Element | null => {
48
53
  const { adjustedData } = useGraphData({ data, end, start });
49
54
  const graphRef = useRef<HTMLDivElement | null>(null);
@@ -52,36 +57,43 @@ const WrapperGraph = ({
52
57
  return (
53
58
  <LoadingSkeleton
54
59
  displayTitleSkeleton={header?.displayTitle ?? true}
55
- graphHeight={height}
60
+ graphHeight={height || 200}
56
61
  />
57
62
  );
58
63
  }
59
64
 
60
65
  return (
61
- <div ref={graphRef as MutableRefObject<HTMLDivElement>}>
66
+ <div
67
+ ref={graphRef as MutableRefObject<HTMLDivElement>}
68
+ style={{ height: '100%', width: '100%' }}
69
+ >
62
70
  <Responsive.ParentSize>
63
71
  {({
64
72
  height: responsiveHeight,
65
73
  width: responsiveWidth
66
- }): JSX.Element => (
67
- <Graph
68
- annotationEvent={annotationEvent}
69
- axis={axis}
70
- displayAnchor={displayAnchor}
71
- graphData={adjustedData}
72
- graphInterval={{ end, start }}
73
- graphRef={graphRef}
74
- header={header}
75
- height={height ?? responsiveHeight}
76
- legend={legend}
77
- loading={loading}
78
- shapeLines={shapeLines}
79
- timeShiftZones={timeShiftZones}
80
- tooltip={tooltip}
81
- width={width ?? responsiveWidth}
82
- zoomPreview={zoomPreview}
83
- />
84
- )}
74
+ }): JSX.Element => {
75
+ return (
76
+ <Graph
77
+ annotationEvent={annotationEvent}
78
+ axis={axis}
79
+ curve={curve}
80
+ displayAnchor={displayAnchor}
81
+ graphData={adjustedData}
82
+ graphInterval={{ end, start }}
83
+ graphRef={graphRef}
84
+ header={header}
85
+ height={height || responsiveHeight}
86
+ legend={legend}
87
+ loading={loading}
88
+ marginBottom={marginBottom}
89
+ shapeLines={shapeLines}
90
+ timeShiftZones={timeShiftZones}
91
+ tooltip={tooltip}
92
+ width={width ?? responsiveWidth}
93
+ zoomPreview={zoomPreview}
94
+ />
95
+ );
96
+ }}
85
97
  </Responsive.ParentSize>
86
98
  </div>
87
99
  );
@@ -91,7 +91,7 @@ export interface GraphProps {
91
91
  axis?: Axis;
92
92
  displayAnchor?: DisplayAnchor;
93
93
  header?: GraphHeader;
94
- height?: number;
94
+ height?: number | null;
95
95
  loading: boolean;
96
96
  timeShiftZones?: InteractedZone;
97
97
  tooltip?: Tooltip;
@@ -37,6 +37,7 @@ export interface ConnectedAutoCompleteFieldProps<TData> {
37
37
  getRequestHeaders?: HeadersInit;
38
38
  initialPage: number;
39
39
  labelKey?: string;
40
+ queryKey?: string;
40
41
  searchConditions?: Array<ConditionsSearchParameter>;
41
42
  }
42
43
 
@@ -55,6 +56,7 @@ const ConnectedAutocompleteField = (
55
56
  getRenderedOptionText = (option): string => option.name?.toString(),
56
57
  getRequestHeaders,
57
58
  displayOptionThumbnail,
59
+ queryKey,
58
60
  ...props
59
61
  }: ConnectedAutoCompleteFieldProps<TData> &
60
62
  Omit<AutocompleteFieldProps, 'options'>): JSX.Element => {
@@ -89,10 +91,16 @@ const ConnectedAutocompleteField = (
89
91
  search: searchParameter
90
92
  });
91
93
  },
92
- getQueryKey: () => [`autocomplete-${props.label}`, page, searchParameter],
94
+ getQueryKey: () => [
95
+ `autocomplete-${queryKey || props.label}`,
96
+ page,
97
+ searchParameter
98
+ ],
93
99
  isPaginated: true,
94
100
  queryOptions: {
101
+ cacheTime: 0,
95
102
  enabled: false,
103
+ staleTime: 0,
96
104
  suspense: false
97
105
  }
98
106
  });
@@ -34,12 +34,17 @@ export interface Props
34
34
  'multiple'
35
35
  > {
36
36
  disableSortedOptions?: boolean;
37
+ getTagLabel?: (option) => string;
38
+ optionProperty?: string;
37
39
  }
38
40
 
39
41
  const MultiAutocompleteField = ({
40
42
  value,
41
43
  options,
42
44
  disableSortedOptions = false,
45
+ optionProperty = 'name',
46
+ getOptionLabel = (option): string => option.name,
47
+ getTagLabel = (option): string => option[optionProperty],
43
48
  ...props
44
49
  }: Props): JSX.Element => {
45
50
  const { classes } = useStyles();
@@ -52,7 +57,7 @@ const MultiAutocompleteField = ({
52
57
  root: classes.tag
53
58
  }}
54
59
  key={option.id}
55
- label={option.name}
60
+ label={getTagLabel(option)}
56
61
  size="medium"
57
62
  {...getTagProps({ index })}
58
63
  />
@@ -84,7 +89,7 @@ const MultiAutocompleteField = ({
84
89
  key={option.id}
85
90
  {...(renderProps as React.HTMLAttributes<HTMLLIElement>)}
86
91
  >
87
- <Option checkboxSelected={selected}>{option.name}</Option>
92
+ <Option checkboxSelected={selected}>{getOptionLabel(option)}</Option>
88
93
  </li>
89
94
  )}
90
95
  renderTags={renderTags}
@@ -226,7 +226,9 @@ const Panel = React.forwardRef(
226
226
  className={classes.contentContainer}
227
227
  ref={ref as React.RefObject<HTMLDivElement>}
228
228
  >
229
- <div className={classes.content}>{selectedTab}</div>
229
+ <div className={classes.content} id="panel-content">
230
+ {selectedTab}
231
+ </div>
230
232
  </div>
231
233
  </div>
232
234
  </Paper>
@@ -0,0 +1,92 @@
1
+ import { T, always, cond, flatten, isEmpty, join, pipe, pluck } from 'ramda';
2
+ import dayjs from 'dayjs';
3
+
4
+ import { GraphData, useFetchQuery } from '../..';
5
+
6
+ import { ServiceMetric } from './models';
7
+
8
+ interface UseMetricsQueryProps {
9
+ baseEndpoint: string;
10
+ metrics: Array<ServiceMetric>;
11
+ timePeriod?: TimePeriod;
12
+ }
13
+
14
+ interface UseMetricsQueryState {
15
+ end: string;
16
+ graphData: GraphData | undefined;
17
+ isGraphLoading: boolean;
18
+ isMetricIdsEmpty: boolean;
19
+ start: string;
20
+ }
21
+
22
+ enum TimePeriod {
23
+ lastDay = 'last_day'
24
+ }
25
+
26
+ const getStartEndFromTimePeriod = (
27
+ timePeriod: TimePeriod
28
+ ): { end: string; start: string } => {
29
+ return cond([
30
+ [
31
+ T,
32
+ always({
33
+ end: dayjs().toISOString(),
34
+ start: dayjs().subtract(1, 'day').toISOString()
35
+ })
36
+ ]
37
+ ])(timePeriod);
38
+ };
39
+
40
+ interface PerformanceGraphData extends Omit<GraphData, 'global'> {
41
+ base: number;
42
+ }
43
+
44
+ const useGraphQuery = ({
45
+ metrics,
46
+ baseEndpoint,
47
+ timePeriod = TimePeriod.lastDay
48
+ }: UseMetricsQueryProps): UseMetricsQueryState => {
49
+ const metricIds = pipe(
50
+ pluck('metrics'),
51
+ flatten,
52
+ pluck('id'),
53
+ join(',')
54
+ )(metrics);
55
+
56
+ const { end, start } = getStartEndFromTimePeriod(timePeriod);
57
+
58
+ const {
59
+ data: graphData,
60
+ isFetching,
61
+ isLoading
62
+ } = useFetchQuery<PerformanceGraphData>({
63
+ getEndpoint: () =>
64
+ `${baseEndpoint}?metricIds=[${metricIds}]&start=${start}&end=${end}`,
65
+ getQueryKey: () => ['graph', metricIds],
66
+ queryOptions: {
67
+ enabled: !isEmpty(metricIds),
68
+ suspense: false
69
+ }
70
+ });
71
+
72
+ const formattedGraphData = graphData
73
+ ? {
74
+ global: {
75
+ base: graphData.base,
76
+ title: ''
77
+ },
78
+ metrics: graphData.metrics,
79
+ times: graphData.times
80
+ }
81
+ : undefined;
82
+
83
+ return {
84
+ end,
85
+ graphData: formattedGraphData,
86
+ isGraphLoading: isFetching || (isLoading && !isEmpty(metricIds)),
87
+ isMetricIdsEmpty: isEmpty(metricIds),
88
+ start
89
+ };
90
+ };
91
+
92
+ export default useGraphQuery;
@@ -0,0 +1,12 @@
1
+ interface NamedEntity {
2
+ id: number;
3
+ name: string;
4
+ }
5
+
6
+ interface Metric extends NamedEntity {
7
+ unit: string;
8
+ }
9
+
10
+ export interface ServiceMetric extends NamedEntity {
11
+ metrics: Array<Metric>;
12
+ }
@@ -0,0 +1,37 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import CloseIcon from '@mui/icons-material/Close';
4
+
5
+ import { IconButton } from '..';
6
+
7
+ import { useItemStyles } from './ItemComposition.styles';
8
+
9
+ type Props = {
10
+ children: Array<ReactElement>;
11
+ className?: string;
12
+ labelDelete: string;
13
+ onDeleteItem: () => void;
14
+ };
15
+
16
+ export const Item = ({
17
+ onDeleteItem,
18
+ children,
19
+ labelDelete,
20
+ className
21
+ }: Props): JSX.Element => {
22
+ const { classes, cx } = useItemStyles();
23
+
24
+ return (
25
+ <div className={classes.itemContainer}>
26
+ <div className={cx(classes.itemContent, className)}>{children}</div>
27
+ <IconButton
28
+ aria-label={labelDelete}
29
+ data-testid={labelDelete}
30
+ icon={<CloseIcon />}
31
+ size="small"
32
+ variant="ghost"
33
+ onClick={onDeleteItem}
34
+ />
35
+ </div>
36
+ );
37
+ };
@@ -0,0 +1,78 @@
1
+ /* eslint-disable react/no-array-index-key */
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+
4
+ import { Typography } from '@mui/material';
5
+
6
+ import { SelectField } from '../..';
7
+
8
+ import { ItemComposition } from '.';
9
+
10
+ const meta: Meta<typeof ItemComposition> = {
11
+ component: ItemComposition
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof ItemComposition>;
16
+
17
+ const stub = (): void => undefined;
18
+
19
+ export const Default: Story = {
20
+ args: {
21
+ children: [...Array(5)].map((_, i) => (
22
+ <ItemComposition.Item key={i} labelDelete="Delete" onDeleteItem={stub}>
23
+ <Typography>Item 1</Typography>
24
+ <Typography>Item 2</Typography>
25
+ </ItemComposition.Item>
26
+ )),
27
+ labelAdd: 'Add',
28
+ onAddItem: stub
29
+ }
30
+ };
31
+
32
+ export const Empty: Story = {
33
+ args: {
34
+ children: [],
35
+ labelAdd: 'Add',
36
+ onAddItem: stub
37
+ }
38
+ };
39
+
40
+ const options = [
41
+ {
42
+ id: 1,
43
+ name: 'Test select 1'
44
+ },
45
+ {
46
+ id: 2,
47
+ name: 'Test select 2'
48
+ },
49
+ {
50
+ id: 3,
51
+ name: 'Test select 3'
52
+ }
53
+ ];
54
+
55
+ export const WithSelectInputs: Story = {
56
+ args: {
57
+ children: [...Array(5)].map((_, i) => (
58
+ <ItemComposition.Item key={i} labelDelete="Delete" onDeleteItem={stub}>
59
+ <SelectField
60
+ dataTestId="select 1"
61
+ label="select 1"
62
+ options={options}
63
+ selectedOptionId={1}
64
+ onChange={stub}
65
+ />
66
+ <SelectField
67
+ dataTestId="select 2"
68
+ label="select 2"
69
+ options={options}
70
+ selectedOptionId={2}
71
+ onChange={stub}
72
+ />
73
+ </ItemComposition.Item>
74
+ )),
75
+ labelAdd: 'Add',
76
+ onAddItem: stub
77
+ }
78
+ };
@@ -0,0 +1,26 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useItemCompositionStyles = makeStyles()((theme) => ({
4
+ itemCompositionContainer: {
5
+ alignItems: 'flex-start',
6
+ display: 'flex',
7
+ flexDirection: 'column',
8
+ gap: theme.spacing(2),
9
+ width: '100%'
10
+ }
11
+ }));
12
+
13
+ export const useItemStyles = makeStyles()((theme) => ({
14
+ itemContainer: {
15
+ alignItems: 'center',
16
+ display: 'flex',
17
+ flexDirection: 'row',
18
+ gap: theme.spacing(1),
19
+ width: '100%'
20
+ },
21
+ itemContent: {
22
+ display: 'flex',
23
+ flexDirection: 'row',
24
+ width: '100%'
25
+ }
26
+ }));
@@ -0,0 +1,41 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import AddIcon from '@mui/icons-material/Add';
4
+
5
+ import { Button } from '..';
6
+
7
+ import { useItemCompositionStyles } from './ItemComposition.styles';
8
+
9
+ type Props = {
10
+ addbuttonDisabled?: boolean;
11
+ children: Array<ReactElement>;
12
+ labelAdd: string;
13
+ onAddItem: () => void;
14
+ };
15
+
16
+ export const ItemComposition = ({
17
+ onAddItem,
18
+ children,
19
+ labelAdd,
20
+ addbuttonDisabled
21
+ }: Props): JSX.Element => {
22
+ const { classes } = useItemCompositionStyles();
23
+
24
+ return (
25
+ <div className={classes.itemCompositionContainer}>
26
+ {children}
27
+ <Button
28
+ aria-label={labelAdd}
29
+ data-testid={labelAdd}
30
+ disabled={addbuttonDisabled}
31
+ icon={<AddIcon />}
32
+ iconVariant="start"
33
+ size="small"
34
+ variant="ghost"
35
+ onClick={onAddItem}
36
+ >
37
+ {labelAdd}
38
+ </Button>
39
+ </div>
40
+ );
41
+ };
@@ -0,0 +1,6 @@
1
+ import { ItemComposition as ItemCompositionRoot } from './ItemComposition';
2
+ import { Item } from './Item';
3
+
4
+ export const ItemComposition = Object.assign(ItemCompositionRoot, {
5
+ Item
6
+ });
@@ -17,7 +17,7 @@ const useStyles = makeStyles()((theme) => ({
17
17
  width: '400px'
18
18
  },
19
19
  '&[data-size="xlarge"] .MuiDialog-paper': {
20
- maxWidth: '1200px',
20
+ maxWidth: '1400px',
21
21
  width: 'calc(100% - 64px)'
22
22
  }
23
23
  },
@@ -7,3 +7,4 @@ export * from './Layout';
7
7
  export * from './Tooltip';
8
8
  export * from './Menu';
9
9
  export * from './List';
10
+ export * from './ItemComposition';
package/src/index.ts CHANGED
@@ -123,6 +123,7 @@ export type {
123
123
  export { default as Responsive } from './Responsive';
124
124
  export { default as useFetchQuery } from './api/useFetchQuery';
125
125
  export { default as useMutationQuery, Method } from './api/useMutationQuery';
126
+ export { default as useGraphQuery } from './api/useGraphQuery';
126
127
  export { default as QueryProvider } from './api/QueryProvider';
127
128
  export {
128
129
  default as FileDropZone,
@@ -143,3 +144,5 @@ export type { ResponseError, CatchErrorProps } from './api/customFetch';
143
144
  export * from './utils';
144
145
  export { default as FluidTypography } from './Typography/FluidTypography';
145
146
  export * from './Dashboard';
147
+ export { default as Graph } from './Graph';
148
+ export type { GraphData } from './Graph/models';
@@ -1,58 +0,0 @@
1
- import { makeStyles } from 'tss-react/mui';
2
-
3
- interface StylesProps {
4
- tooltipLeftAxisX?: number;
5
- tooltipLeftAxisYLeft?: number;
6
- tooltipLeftAxisYRight?: number;
7
- tooltipTopAxisX?: number;
8
- tooltipTopAxisYLeft?: number;
9
- tooltipTopAxisYRight?: number;
10
- }
11
-
12
- export const useStyles = makeStyles<StylesProps>()(
13
- (
14
- theme,
15
- {
16
- tooltipLeftAxisX,
17
- tooltipTopAxisX,
18
- tooltipLeftAxisYLeft,
19
- tooltipTopAxisYLeft,
20
- tooltipLeftAxisYRight,
21
- tooltipTopAxisYRight
22
- }
23
- ) => ({
24
- tooltipAxis: {
25
- '& > span': {
26
- fontSize: 'inherit',
27
- fontWeight: 'inherit',
28
- lineHeight: 'inherit'
29
- },
30
- backgroundColor: theme.palette.background.tooltip,
31
- border: 'unset',
32
- borderRadius: theme.spacing(0.25),
33
- color: theme.palette.primary.contrastText,
34
- fontSize: '10px',
35
- fontWeight: 500,
36
- letterSpacing: '0.15px',
37
- lineHeight: '100%',
38
- padding: theme.spacing(0.375, 0.5),
39
- position: 'absolute',
40
- textAlign: 'center',
41
- transform: 'translate(-50%, -50%)',
42
- transformOrigin: 'center center',
43
- transition: 'unset'
44
- },
45
- tooltipAxisBottom: {
46
- left: tooltipLeftAxisX ?? 0,
47
- top: tooltipTopAxisX ?? 0
48
- },
49
- tooltipAxisLeftY: {
50
- left: tooltipLeftAxisYLeft ?? 0,
51
- top: tooltipTopAxisYLeft ?? 0
52
- },
53
- tooltipLeftAxisRightY: {
54
- left: tooltipLeftAxisYRight ?? 0,
55
- top: tooltipTopAxisYRight ?? 0
56
- }
57
- })
58
- );