@centreon/ui 25.3.1 → 25.3.3

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 (41) hide show
  1. package/package.json +49 -46
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Save/index.tsx +17 -35
  4. package/src/Button/Save/useSave.tsx +89 -0
  5. package/src/Form/Inputs/LoadingSkeleton.tsx +1 -1
  6. package/src/Form/Inputs/Radio.tsx +7 -5
  7. package/src/Form/Inputs/Switch.tsx +4 -2
  8. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +2 -2
  9. package/src/Graph/BarChart/BarChart.stories.tsx +39 -0
  10. package/src/Graph/BarChart/BarStack.tsx +8 -2
  11. package/src/Graph/BarChart/ResponsiveBarChart.tsx +10 -8
  12. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +41 -24
  13. package/src/Graph/Chart/BasicComponents/Lines/index.tsx +22 -37
  14. package/src/Graph/Chart/Chart.cypress.spec.tsx +104 -31
  15. package/src/Graph/Chart/Chart.stories.tsx +24 -2
  16. package/src/Graph/Chart/Chart.tsx +23 -18
  17. package/src/Graph/Chart/index.tsx +2 -0
  18. package/src/Graph/Chart/models.ts +4 -3
  19. package/src/Graph/SingleBar/ThresholdLine.tsx +2 -2
  20. package/src/Graph/Tree/Tree.stories.tsx +4 -1
  21. package/src/Graph/common/Axes/index.tsx +1 -1
  22. package/src/Graph/common/BaseChart/AdditionalLine.tsx +37 -0
  23. package/src/Graph/common/BaseChart/BaseChart.tsx +5 -1
  24. package/src/Graph/common/BaseChart/Header/index.tsx +5 -7
  25. package/src/Graph/common/BaseChart/Header/useHeaderStyles.ts +5 -1
  26. package/src/Graph/common/BaseChart/useComputeBaseChartDimensions.ts +9 -2
  27. package/src/Graph/common/Thresholds/ThresholdLine.tsx +2 -2
  28. package/src/Graph/common/models.ts +7 -0
  29. package/src/Graph/common/timeSeries/index.ts +1 -1
  30. package/src/Graph/common/utils.ts +22 -1
  31. package/src/Graph/mockedData/lastDayWithNullValues.json +6 -40
  32. package/src/Graph/mockedData/pingService.json +3 -3
  33. package/src/InputField/Select/index.tsx +11 -9
  34. package/src/Listing/Listing.cypress.spec.tsx +9 -11
  35. package/src/RichTextEditor/RichTextEditor.tsx +1 -1
  36. package/src/api/useMutationQuery/useMutationQuery.cypress.spec.tsx +0 -18
  37. package/src/components/Button/Button.tsx +19 -15
  38. package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +8 -8
  39. package/src/components/CopyCommand/CopyCommand.cypress.spec.tsx +11 -10
  40. package/src/Button/Save/Content.tsx +0 -34
  41. package/src/Button/Save/StartIcon.tsx +0 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "25.3.1",
3
+ "version": "25.3.3",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -16,7 +16,10 @@
16
16
  "cypress:run:updateSnapshots": "cypress run --component --browser=chrome --env updateSnapshots=true",
17
17
  "cypress:run:coverage": "cypress run --component --browser=chrome --env codeCoverageTasksRegistered=true",
18
18
  "cypress:run": "cypress run --component --browser=chrome",
19
- "tokens:transform": "TS_NODE_PROJECT=tsconfig.node.json ts-node style-dictionary.transform.ts"
19
+ "cypress:install": "cypress install",
20
+ "cypress:cli": "./cypress/scripts/cypress-cli.sh",
21
+ "tokens:transform": "TS_NODE_PROJECT=tsconfig.node.json ts-node style-dictionary.transform.ts",
22
+ "install:arm:binaries": "pnpm i -D @swc/core-linux-arm64-gnu@1.6.6 @rspack/binding-linux-arm64-gnu@1.1.6 -w && pnpm remove @swc/core-linux-arm64-gnu @rspack/binding-linux-arm64-gnu"
20
23
  },
21
24
  "type": "module",
22
25
  "sideEffects": false,
@@ -50,71 +53,71 @@
50
53
  "test/*"
51
54
  ],
52
55
  "devDependencies": {
53
- "@cypress/react": "^8.0.2",
54
- "@cypress/webpack-dev-server": "^3.11.0",
55
- "@faker-js/faker": "^9.3.0",
56
+ "@cypress/react": "^9.0.0",
57
+ "@cypress/webpack-dev-server": "^4.0.1",
58
+ "@faker-js/faker": "^9.5.1",
56
59
  "@mdx-js/react": "^3.1.0",
57
60
  "@simonsmith/cypress-image-snapshot": "^9.1.0",
58
- "@storybook/addon-a11y": "^8.4.7",
59
- "@storybook/addon-docs": "^8.4.7",
60
- "@storybook/addon-essentials": "^8.4.7",
61
- "@storybook/addon-interactions": "^8.4.7",
62
- "@storybook/addon-themes": "^8.4.7",
63
- "@storybook/blocks": "^8.4.7",
64
- "@storybook/manager-api": "^8.4.7",
61
+ "@storybook/addon-a11y": "^8.6.3",
62
+ "@storybook/addon-docs": "^8.6.3",
63
+ "@storybook/addon-essentials": "^8.6.3",
64
+ "@storybook/addon-interactions": "^8.6.3",
65
+ "@storybook/addon-themes": "^8.6.3",
66
+ "@storybook/blocks": "^8.6.3",
67
+ "@storybook/manager-api": "^8.6.3",
65
68
  "@storybook/mdx2-csf": "^1.1.0",
66
- "@storybook/preview-api": "^8.4.7",
67
- "@storybook/react": "^8.4.7",
68
- "@storybook/react-vite": "^8.4.7",
69
- "@storybook/test": "^8.4.7",
70
- "@storybook/test-runner": "^0.20.1",
71
- "@storybook/theming": "^8.4.7",
72
- "@testing-library/cypress": "^10.0.2",
69
+ "@storybook/preview-api": "^8.6.3",
70
+ "@storybook/react": "^8.6.3",
71
+ "@storybook/react-vite": "^8.6.3",
72
+ "@storybook/test": "^8.6.3",
73
+ "@storybook/test-runner": "^0.22.0",
74
+ "@storybook/theming": "^8.6.3",
75
+ "@testing-library/cypress": "^10.0.3",
73
76
  "@testing-library/jest-dom": "^6.6.3",
74
- "@testing-library/react": "^16.1.0",
77
+ "@testing-library/react": "^16.2.0",
75
78
  "@testing-library/react-hooks": "^8.0.1",
76
79
  "@types/jest": "^29.5.14",
77
80
  "@types/mocha": "^10.0.10",
78
81
  "@types/ramda": "^0.30.2",
79
- "@types/react": "^18.3.3",
82
+ "@types/react": "^19.0.10",
80
83
  "@types/testing-library__jest-dom": "^6.0.0",
81
84
  "@vitejs/plugin-react": "^4.3.4",
82
- "@vitejs/plugin-react-swc": "^3.7.2",
83
- "chai": "^5.1.2",
84
- "cypress": "^13.16.1",
85
+ "@vitejs/plugin-react-swc": "^3.8.0",
86
+ "chai": "^5.2.0",
87
+ "cypress": "^14.1.0",
85
88
  "identity-obj-proxy": "^3.0.0",
86
89
  "jest-transform-stub": "^2.0.0",
87
90
  "mochawesome": "^7.1.3",
88
- "msw": "2.6.8",
91
+ "msw": "2.7.3",
89
92
  "msw-storybook-addon": "^2.0.4",
90
- "react": "^18.3.1",
91
- "react-dom": "^18.3.1",
93
+ "react": "^19.0.0",
94
+ "react-dom": "^19.0.0",
92
95
  "react-test-renderer": "^19.0.0",
93
- "remark-gfm": "^4.0.0",
96
+ "remark-gfm": "^4.0.1",
94
97
  "speed-measure-vite-plugin": "^1.1.0",
95
- "storybook": "^8.4.7",
98
+ "storybook": "^8.6.3",
96
99
  "storybook-addon-mock": "^5.0.0",
97
100
  "storybook-dark-mode": "^4.0.2",
98
- "style-dictionary": "^4.3.0",
101
+ "style-dictionary": "^4.3.3",
99
102
  "ts-node": "^10.9.2",
100
103
  "use-resize-observer": "^9.1.0",
101
- "vite": "^6.0.3",
102
- "vite-plugin-istanbul": "^6.0.2",
104
+ "vite": "^6.2.0",
105
+ "vite-plugin-istanbul": "^7.0.0",
103
106
  "vite-plugin-svgr": "^4.3.0",
104
107
  "vite-plugin-turbosnap": "^1.0.3"
105
108
  },
106
109
  "peerDependencies": {
107
- "@centreon/ui-context": "file:../ui-context"
110
+ "@centreon/ui-context": "link:../ui-context"
108
111
  },
109
112
  "dependencies": {
110
- "@lexical/html": "^0.21.0",
111
- "@lexical/link": "^0.21.0",
112
- "@lexical/list": "^0.21.0",
113
- "@lexical/react": "^0.21.0",
114
- "@lexical/rich-text": "^0.21.0",
115
- "@lexical/selection": "^0.21.0",
116
- "@lexical/utils": "^0.21.0",
117
- "@mui/material": "^6.2.0",
113
+ "@lexical/html": "^0.27.0",
114
+ "@lexical/link": "^0.27.0",
115
+ "@lexical/list": "^0.27.0",
116
+ "@lexical/react": "^0.27.0",
117
+ "@lexical/rich-text": "^0.27.0",
118
+ "@lexical/selection": "^0.27.0",
119
+ "@lexical/utils": "^0.27.0",
120
+ "@mui/material": "^6.4.6",
118
121
  "@react-spring/web": "^9.7.5",
119
122
  "@visx/clip-path": "^3.12.0",
120
123
  "@visx/curve": "^3.12.0",
@@ -133,18 +136,18 @@
133
136
  "anylogger": "^1.0.11",
134
137
  "d3-array": "3.2.4",
135
138
  "dayjs": "^1.11.13",
136
- "highlight.js": "^11.10.0",
137
- "html-react-parser": "^5.2.0",
139
+ "highlight.js": "^11.11.1",
140
+ "html-react-parser": "^5.2.2",
138
141
  "humanize-duration": "^3.32.1",
139
- "lexical": "^0.21.0",
140
- "notistack": "^3.0.1",
142
+ "lexical": "^0.27.0",
143
+ "notistack": "^3.0.2",
141
144
  "numeral": "^2.0.6",
142
145
  "ramda": "0.30.1",
143
146
  "react-grid-layout": "^1.5.0",
144
147
  "react-resizable": "^3.0.5",
145
148
  "react-router": "7",
146
149
  "react-transition-group": "^4.4.5",
147
- "sanitize-html": "^2.13.1",
150
+ "sanitize-html": "^2.14.0",
148
151
  "ulog": "^2.0.0-beta.19"
149
152
  },
150
153
  "jest-junit": {
@@ -8,7 +8,7 @@
8
8
  * - Please do NOT serve this file on production.
9
9
  */
10
10
 
11
- const PACKAGE_VERSION = '2.6.8'
11
+ const PACKAGE_VERSION = '2.7.3'
12
12
  const INTEGRITY_CHECKSUM = '00729d72e3b82faf54ca8b9621dbb96f'
13
13
  const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
14
14
  const activeClientIds = new Set()
@@ -1,13 +1,11 @@
1
- import { any, isEmpty, isNil, not, or, pipe } from 'ramda';
2
1
  import { makeStyles } from 'tss-react/mui';
3
2
 
4
- import { LoadingButton, LoadingButtonProps } from '@mui/lab';
5
3
  import { Theme, Tooltip } from '@mui/material';
6
4
 
7
5
  import { getNormalizedId } from '../../utils';
8
6
 
9
- import Content from './Content';
10
- import StartIcon from './StartIcon';
7
+ import { Button, ButtonProps } from '../../components';
8
+ import { useSave } from './useSave';
11
9
 
12
10
  const useStyles = makeStyles()((theme: Theme) => ({
13
11
  loadingButton: {
@@ -15,7 +13,7 @@ const useStyles = makeStyles()((theme: Theme) => ({
15
13
  }
16
14
  }));
17
15
 
18
- interface Props {
16
+ export interface Props {
19
17
  className?: string;
20
18
  labelLoading?: string;
21
19
  labelSave?: string;
@@ -28,15 +26,6 @@ interface Props {
28
26
  tooltipLabel?: string;
29
27
  }
30
28
 
31
- interface StartIconConfigProps {
32
- hasLabel: boolean;
33
- loading: boolean;
34
- succeeded: boolean;
35
- }
36
-
37
- const isNilOrEmpty = (value): boolean => or(isNil(value), isEmpty(value));
38
- const hasValue = any(pipe(isNilOrEmpty, not));
39
-
40
29
  const SaveButton = ({
41
30
  succeeded = false,
42
31
  loading = false,
@@ -48,20 +37,22 @@ const SaveButton = ({
48
37
  className,
49
38
  startIcon = true,
50
39
  ...rest
51
- }: Props & LoadingButtonProps): JSX.Element => {
40
+ }: Props & Omit<ButtonProps, 'children'>): JSX.Element => {
52
41
  const { classes, cx } = useStyles();
53
- const hasLabel = hasValue([labelLoading, labelSave, labelSucceeded]);
54
42
 
55
- const startIconConfig = {
56
- hasLabel,
43
+ const { content, startIconToDisplay, hasLabel } = useSave({
44
+ labelLoading,
45
+ labelSave,
46
+ labelSucceeded,
57
47
  loading,
58
- succeeded
59
- } as StartIconConfigProps;
48
+ succeeded,
49
+ startIcon
50
+ });
60
51
 
61
52
  return (
62
53
  <Tooltip placement="bottom" title={tooltipLabel}>
63
54
  <div>
64
- <LoadingButton
55
+ <Button
65
56
  aria-label="save button"
66
57
  className={cx(
67
58
  {
@@ -69,26 +60,17 @@ const SaveButton = ({
69
60
  },
70
61
  className
71
62
  )}
72
- color="primary"
73
63
  data-testid={labelSave}
74
64
  id={getNormalizedId(labelSave)}
75
65
  loading={loading}
76
- loadingPosition={labelLoading ? 'start' : 'center'}
66
+ loadingPosition={labelLoading ? 'start' : undefined}
77
67
  size={size}
78
- startIcon={
79
- startIcon && <StartIcon startIconConfig={startIconConfig} />
80
- }
81
- variant="contained"
68
+ startIcon={startIconToDisplay}
69
+ variant="primary"
82
70
  {...rest}
83
71
  >
84
- {Content({
85
- labelLoading,
86
- labelSave,
87
- labelSucceeded,
88
- loading,
89
- succeeded
90
- })}
91
- </LoadingButton>
72
+ {content}
73
+ </Button>
92
74
  </div>
93
75
  </Tooltip>
94
76
  );
@@ -0,0 +1,89 @@
1
+ import CheckIcon from '@mui/icons-material/Check';
2
+ import SaveIcon from '@mui/icons-material/Save';
3
+ import {
4
+ T,
5
+ always,
6
+ any,
7
+ cond,
8
+ isEmpty,
9
+ isNil,
10
+ not,
11
+ or,
12
+ pipe,
13
+ propEq
14
+ } from 'ramda';
15
+ import { useMemo } from 'react';
16
+ import { useTranslation } from 'react-i18next';
17
+ import { Props } from '.';
18
+
19
+ interface StartIconConfigProps {
20
+ hasLabel: boolean;
21
+ loading: boolean;
22
+ succeeded: boolean;
23
+ enabled: boolean;
24
+ }
25
+
26
+ const isNilOrEmpty = (value): boolean => or(isNil(value), isEmpty(value));
27
+ const hasValue = any(pipe(isNilOrEmpty, not));
28
+
29
+ interface UseSaveState {
30
+ content: string | JSX.Element;
31
+ startIconToDisplay: null | JSX.Element;
32
+ hasLabel: boolean;
33
+ }
34
+
35
+ export const useSave = ({
36
+ labelLoading,
37
+ labelSave,
38
+ labelSucceeded,
39
+ loading,
40
+ succeeded,
41
+ startIcon
42
+ }: Pick<
43
+ Props,
44
+ | 'startIcon'
45
+ | 'succeeded'
46
+ | 'loading'
47
+ | 'labelSave'
48
+ | 'labelSucceeded'
49
+ | 'labelLoading'
50
+ >): UseSaveState => {
51
+ const { t } = useTranslation();
52
+
53
+ const hasLabel = hasValue([labelLoading, labelSave, labelSucceeded]);
54
+
55
+ const startIconConfig = {
56
+ hasLabel,
57
+ loading,
58
+ succeeded,
59
+ enabled: startIcon
60
+ } as StartIconConfigProps;
61
+
62
+ const content = useMemo(() => {
63
+ if (loading) {
64
+ return t(labelLoading || 'loading');
65
+ }
66
+
67
+ if (succeeded) {
68
+ return labelSucceeded ? t(labelSucceeded) : <CheckIcon />;
69
+ }
70
+
71
+ return labelSave ? t(labelSave) : <SaveIcon />;
72
+ }, [labelLoading, labelSucceeded, labelSave, loading, succeeded]);
73
+
74
+ const startIconToDisplay = useMemo(() => {
75
+ return cond<Array<StartIconConfigProps>, JSX.Element | null>([
76
+ [propEq(true, 'enabled'), always(null)],
77
+ [pipe(propEq(true, 'hasLabel'), not), always(null)],
78
+ [propEq(true, 'succeeded'), always(<CheckIcon />)],
79
+ [propEq(true, 'loading'), always(<SaveIcon />)],
80
+ [T, always(<SaveIcon />)]
81
+ ])(startIconConfig);
82
+ }, [startIconConfig]);
83
+
84
+ return {
85
+ content,
86
+ startIconToDisplay,
87
+ hasLabel
88
+ };
89
+ };
@@ -1,6 +1,6 @@
1
1
  import { always, cond, equals } from 'ramda';
2
2
 
3
- import { LoadingSkeleton } from '../..';
3
+ import LoadingSkeleton from '../../LoadingSkeleton';
4
4
 
5
5
  import { InputProps, InputType } from './models';
6
6
 
@@ -80,11 +80,13 @@ const Radio = ({
80
80
  data-testid={`${dataTestId} ${optionLabel}`}
81
81
  disabled={disabled}
82
82
  id={getNormalizedId(`${dataTestId}${optionLabel}`)}
83
- inputProps={{
84
- 'aria-label':
85
- (equals(type(optionLabel), 'String') &&
86
- t(optionLabel as string)) ||
87
- ''
83
+ slotProps={{
84
+ input: {
85
+ 'aria-label':
86
+ (equals(type(optionLabel), 'String') &&
87
+ t(optionLabel as string)) ||
88
+ ''
89
+ }
88
90
  }}
89
91
  />
90
92
  }
@@ -58,8 +58,10 @@ const Switch = ({
58
58
  data-testid={dataTestId}
59
59
  disabled={disabled}
60
60
  id={getNormalizedId(dataTestId || '')}
61
- inputProps={{
62
- 'aria-label': t(label) || ''
61
+ slotProps={{
62
+ input: {
63
+ 'aria-label': t(label) || ''
64
+ }
63
65
  }}
64
66
  onChange={changeSwitchValue}
65
67
  />
@@ -83,7 +83,7 @@ describe('Bar chart', () => {
83
83
  cy.contains('20').should('be.visible');
84
84
  cy.contains(':40 AM').should('be.visible');
85
85
 
86
- cy.findByTestId('stacked-bar-3-0-0.08644').should('be.visible');
86
+ cy.findByTestId('stacked-bar-3-0-0.08084').should('be.visible');
87
87
 
88
88
  cy.makeSnapshot();
89
89
  });
@@ -96,7 +96,7 @@ describe('Bar chart', () => {
96
96
  cy.contains('20').should('be.visible');
97
97
  cy.contains(':40 AM').should('be.visible');
98
98
 
99
- cy.findByTestId('stacked-bar-3-0-0.08644').should('be.visible');
99
+ cy.findByTestId('stacked-bar-3-0-0.08084').should('be.visible');
100
100
 
101
101
  cy.makeSnapshot();
102
102
  });
@@ -195,6 +195,45 @@ export const customBarStyle: Story = {
195
195
  render: Template
196
196
  };
197
197
 
198
+ export const customBarStyleForABar: Story = {
199
+ args: {
200
+ ...defaultArgs,
201
+ barStyle: [
202
+ {
203
+ opacity: 0.5,
204
+ radius: 0.5,
205
+ metricId: 10
206
+ },
207
+ {
208
+ opacity: 0.2,
209
+ radius: 0.3,
210
+ metricId: 1
211
+ }
212
+ ]
213
+ },
214
+ render: Template
215
+ };
216
+
217
+ export const customBarStyleForABarStacked: Story = {
218
+ args: {
219
+ ...defaultArgs,
220
+ data: dataPingServiceStacked,
221
+ barStyle: [
222
+ {
223
+ opacity: 0.5,
224
+ radius: 0.5,
225
+ metricId: 10
226
+ },
227
+ {
228
+ opacity: 0.2,
229
+ radius: 0.3,
230
+ metricId: 1
231
+ }
232
+ ]
233
+ },
234
+ render: Template
235
+ };
236
+
198
237
  export const mixedStacked: Story = {
199
238
  args: {
200
239
  ...defaultArgs,
@@ -5,6 +5,7 @@ import { BarRounded } from '@visx/shape';
5
5
  import { dec, equals, gt, pick } from 'ramda';
6
6
 
7
7
  import { BarGroupBar, SeriesPoint, StackKey } from '@visx/shape/lib/types';
8
+ import { getStyle } from '../common/utils';
8
9
  import { BarStyle } from './models';
9
10
  import { UseBarStackProps, useBarStack } from './useBarStack';
10
11
 
@@ -112,6 +113,11 @@ const BarStack = ({
112
113
  [isHorizontal ? 'top' : 'right']: shouldApplyRadiusOnTop
113
114
  };
114
115
 
116
+ const style = getStyle({
117
+ style: barStyle,
118
+ metricId: Number(bar.key)
119
+ }) as BarStyle;
120
+
115
121
  return (
116
122
  <BarRounded
117
123
  {...barRoundedProps}
@@ -133,8 +139,8 @@ const BarStack = ({
133
139
  neutralValue
134
140
  })}
135
141
  key={`bar-stack-${barStack.index}-${bar.index}`}
136
- opacity={barStyle.opacity ?? 1}
137
- radius={barWidth * barStyle.radius}
142
+ opacity={style?.opacity || 1}
143
+ radius={style?.radius ? barWidth * style.radius : 0}
138
144
  width={isHorizontal ? barWidth : Math.abs(bar.width)}
139
145
  x={
140
146
  isHorizontal
@@ -87,14 +87,15 @@ const ResponsiveBarChart = ({
87
87
  secondUnit
88
88
  });
89
89
 
90
- const { legendRef, graphWidth, graphHeight } = useComputeBaseChartDimensions({
91
- hasSecondUnit: Boolean(secondUnit),
92
- height,
93
- legendDisplay: legend?.display,
94
- legendPlacement: legend?.placement,
95
- width,
96
- maxAxisCharacters: maxRightAxisCharacters || maxLeftAxisCharacters
97
- });
90
+ const { legendRef, graphWidth, graphHeight, titleRef } =
91
+ useComputeBaseChartDimensions({
92
+ hasSecondUnit: Boolean(secondUnit),
93
+ height,
94
+ legendDisplay: legend?.display,
95
+ legendPlacement: legend?.placement,
96
+ width,
97
+ maxAxisCharacters: maxRightAxisCharacters || maxLeftAxisCharacters
98
+ });
98
99
 
99
100
  const thresholdValues = flatten([
100
101
  pluck('value', thresholds?.warning || []),
@@ -192,6 +193,7 @@ const ResponsiveBarChart = ({
192
193
  lines={linesGraph}
193
194
  setLines={setLinesGraph}
194
195
  title={title}
196
+ titleRef={titleRef}
195
197
  >
196
198
  <Tooltip
197
199
  classes={{
@@ -1,15 +1,31 @@
1
1
  import { Shape } from '@visx/visx';
2
2
  import { ScaleLinear, ScaleTime } from 'd3-scale';
3
- import { path, all, equals, isNil, map, not, nth, pipe, prop } from 'ramda';
3
+ import {
4
+ path,
5
+ all,
6
+ equals,
7
+ isNil,
8
+ map,
9
+ not,
10
+ nth,
11
+ pipe,
12
+ prop,
13
+ type
14
+ } from 'ramda';
4
15
 
5
16
  import { getDates, getTime } from '../../../../common/timeSeries';
6
17
  import { Line, TimeValue } from '../../../../common/timeSeries/models';
7
- import { getPointRadius, getStrokeDashArray } from '../../../../common/utils';
18
+ import {
19
+ getPointRadius,
20
+ getStrokeDashArray,
21
+ getStyle
22
+ } from '../../../../common/utils';
8
23
  import StackedAnchorPoint, {
9
24
  getYAnchorPoint
10
25
  } from '../../../InteractiveComponents/AnchorPoint/StackedAnchorPoint';
11
26
  import { StackValue } from '../../../InteractiveComponents/AnchorPoint/models';
12
27
  import { getCurveFactory, getFillColor } from '../../../common';
28
+ import { LineStyle } from '../../../models';
13
29
  import Point from '../Point';
14
30
 
15
31
  interface Props {
@@ -26,6 +42,7 @@ interface Props {
26
42
  timeSeries: Array<TimeValue>;
27
43
  xScale: ScaleTime<number, number>;
28
44
  yScale: ScaleLinear<number, number>;
45
+ lineStyle: LineStyle | Array<LineStyle>;
29
46
  }
30
47
 
31
48
  const StackLines = ({
@@ -34,19 +51,13 @@ const StackLines = ({
34
51
  yScale,
35
52
  xScale,
36
53
  displayAnchor,
37
- curve,
38
- showPoints,
39
- showArea,
40
- areaTransparency,
41
- lineWidth,
42
- dashLength,
43
- dashOffset,
44
- dotOffset
54
+ lineStyle
45
55
  }: Props): JSX.Element => {
46
- const curveType = getCurveFactory(curve);
47
-
48
- const formattedLineWidth = lineWidth ?? 2;
49
-
56
+ const curveType = getCurveFactory(
57
+ (equals(type(lineStyle), 'Array')
58
+ ? lineStyle?.[0].curve
59
+ : lineStyle?.curve) || 'linear'
60
+ );
50
61
  return (
51
62
  <Shape.AreaStack
52
63
  curve={curveType}
@@ -69,15 +80,21 @@ const StackLines = ({
69
80
  const { areaColor, transparency, lineColor, highlight, metric_id } =
70
81
  nth(index, lines) as Line;
71
82
 
72
- const formattedTransparency = isNil(areaTransparency)
83
+ const style = getStyle({
84
+ style: lineStyle,
85
+ metricId: metric_id
86
+ }) as LineStyle;
87
+ const formattedLineWidth = style?.lineWidth ?? 2;
88
+
89
+ const formattedTransparency = isNil(style?.areaTransparency)
73
90
  ? transparency || 80
74
- : areaTransparency;
91
+ : style.areaTransparency;
75
92
 
76
93
  return (
77
94
  <g key={`stack-${prop('key', stack)}`}>
78
95
  {displayAnchor && (
79
96
  <StackedAnchorPoint
80
- areaColor={areaColor}
97
+ areaColor={style?.areaColor}
81
98
  lineColor={lineColor}
82
99
  stackValues={stack as unknown as Array<StackValue>}
83
100
  timeSeries={timeSeries}
@@ -86,13 +103,13 @@ const StackLines = ({
86
103
  yScale={yScale}
87
104
  />
88
105
  )}
89
- {showPoints &&
106
+ {style?.showPoints &&
90
107
  getDates(timeSeries).map((timeTick) => (
91
108
  <Point
92
109
  key={timeTick.toString()}
93
110
  lineColor={lineColor}
94
111
  metric_id={metric_id}
95
- radius={getPointRadius(lineWidth)}
112
+ radius={getPointRadius(style?.lineWidth)}
96
113
  timeSeries={timeSeries}
97
114
  timeTick={timeTick}
98
115
  xScale={xScale}
@@ -108,7 +125,7 @@ const StackLines = ({
108
125
  d={linePath(stack) || ''}
109
126
  data-metric={metric_id}
110
127
  fill={
111
- equals(showArea, false)
128
+ equals(style?.showArea, false)
112
129
  ? 'transparent'
113
130
  : getFillColor({
114
131
  areaColor: areaColor || lineColor,
@@ -118,10 +135,10 @@ const StackLines = ({
118
135
  opacity={highlight === false ? 0.3 : 1}
119
136
  stroke={lineColor}
120
137
  strokeDasharray={getStrokeDashArray({
121
- dashLength,
122
- dashOffset,
123
- dotOffset,
124
- lineWidth: formattedLineWidth
138
+ dashLength: style?.dashLength,
139
+ dashOffset: style?.dashOffset,
140
+ dotOffset: style?.dotOffset,
141
+ lineWidth: style?.lineWidth ?? 2
125
142
  })}
126
143
  strokeWidth={
127
144
  highlight