@centreon/ui 24.4.23 → 24.4.24

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 (95) hide show
  1. package/package.json +19 -14
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/Button/Icon/index.tsx +1 -1
  4. package/src/Button/Save/Content.tsx +26 -9
  5. package/src/Button/Save/StartIcon.tsx +3 -3
  6. package/src/Button/Save/index.tsx +13 -6
  7. package/src/Checkbox/Checkbox.tsx +2 -2
  8. package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
  9. package/src/Dashboard/Item.tsx +1 -1
  10. package/src/Dashboard/Layout.tsx +2 -2
  11. package/src/Dialog/index.tsx +1 -1
  12. package/src/FallbackPage/FallbackPage.tsx +3 -3
  13. package/src/FileDropZone/index.tsx +3 -1
  14. package/src/Form/Form.cypress.spec.tsx +133 -0
  15. package/src/Form/Inputs/CheckboxGroup.tsx +1 -4
  16. package/src/Form/Inputs/List/Content.tsx +62 -0
  17. package/src/Form/Inputs/List/List.styles.ts +29 -0
  18. package/src/Form/Inputs/List/List.tsx +58 -0
  19. package/src/Form/Inputs/List/useList.ts +81 -0
  20. package/src/Form/Inputs/index.tsx +3 -1
  21. package/src/Form/Inputs/models.ts +9 -1
  22. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  23. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +5 -4
  24. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  25. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  26. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  27. package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +2 -3
  28. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  29. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  30. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  31. package/src/Graph/LineChart/helpers/index.ts +1 -1
  32. package/src/Graph/LineChart/index.stories.tsx +4 -2
  33. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  34. package/src/Graph/Text/Text.stories.tsx +60 -4
  35. package/src/Graph/common/timeSeries/index.ts +3 -3
  36. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  37. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  38. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  39. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  40. package/src/InputField/Select/Autocomplete/index.tsx +121 -115
  41. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  42. package/src/InputField/Select/index.tsx +1 -1
  43. package/src/InputField/Text/index.tsx +2 -2
  44. package/src/Listing/Cell/DataCell.tsx +15 -1
  45. package/src/Listing/Header/ListingHeader.tsx +1 -1
  46. package/src/Listing/Listing.cypress.spec.tsx +2 -2
  47. package/src/Listing/Listing.styles.ts +2 -3
  48. package/src/Listing/index.stories.tsx +14 -3
  49. package/src/Listing/index.tsx +8 -9
  50. package/src/Module/Module.cypress.spec.tsx +129 -0
  51. package/src/Module/index.tsx +2 -4
  52. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  53. package/src/SortableItems/index.tsx +2 -7
  54. package/src/ThemeProvider/index.tsx +24 -0
  55. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  56. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  57. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  58. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  59. package/src/TimePeriods/helpers/index.ts +1 -1
  60. package/src/TimePeriods/index.stories.tsx +12 -4
  61. package/src/TimePeriods/index.tsx +2 -2
  62. package/src/api/QueryProvider.tsx +1 -1
  63. package/src/api/TestQueryProvider.tsx +1 -1
  64. package/src/api/useFetchQuery/index.ts +27 -23
  65. package/src/api/useMutationQuery/index.ts +45 -21
  66. package/src/components/Button/Icon/IconButton.tsx +6 -2
  67. package/src/components/DataTable/DataListing.tsx +6 -0
  68. package/src/components/DataTable/DataTable.cypress.spec.tsx +193 -0
  69. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  70. package/src/components/DataTable/DataTable.styles.ts +3 -0
  71. package/src/components/DataTable/DataTable.tsx +3 -3
  72. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  73. package/src/components/DataTable/Item/DataTableItem.tsx +2 -2
  74. package/src/components/DataTable/index.ts +3 -1
  75. package/src/components/Form/AccessRights/__fixtures__/contactAccessRight.mock.ts +2 -0
  76. package/src/components/Form/AccessRights/useAccessRightsForm.utils.ts +1 -1
  77. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  78. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  79. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  80. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  81. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  82. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  83. package/src/components/Layout/PageLayout/index.ts +3 -1
  84. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  85. package/src/components/Modal/Modal.styles.ts +4 -3
  86. package/src/components/Modal/ModalActions.tsx +4 -2
  87. package/src/index.ts +2 -2
  88. package/src/queryParameters/url/index.ts +5 -1
  89. package/src/utils/index.ts +1 -1
  90. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  91. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  92. package/src/utils/usePluralizedTranslation.ts +21 -0
  93. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  94. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  95. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -141,126 +141,132 @@ type Multiple = boolean;
141
141
  type DisableClearable = boolean;
142
142
  type FreeSolo = boolean;
143
143
 
144
- const AutocompleteField = ({
145
- options,
146
- label,
147
- placeholder,
148
- loading = false,
149
- onTextChange = (): void => undefined,
150
- endAdornment = undefined,
151
- inputValue,
152
- displayOptionThumbnail = false,
153
- required = false,
154
- error,
155
- displayPopupIcon = true,
156
- autoFocus = false,
157
- hideInput = false,
158
- dataTestId,
159
- autoSize = false,
160
- autoSizeDefaultWidth = 0,
161
- autoSizeCustomPadding,
162
- getOptionItemLabel = (option) => option.name,
163
- ...autocompleteProps
164
- }: Props): JSX.Element => {
165
- const { classes, cx } = useStyles({ hideInput });
166
- const { t } = useTranslation();
167
- const theme = useTheme();
144
+ const AutocompleteField = React.forwardRef(
145
+ (
146
+ {
147
+ options,
148
+ label,
149
+ placeholder,
150
+ loading = false,
151
+ onTextChange = (): void => undefined,
152
+ endAdornment = undefined,
153
+ inputValue,
154
+ displayOptionThumbnail = false,
155
+ required = false,
156
+ error,
157
+ displayPopupIcon = true,
158
+ autoFocus = false,
159
+ hideInput = false,
160
+ dataTestId,
161
+ autoSize = false,
162
+ autoSizeDefaultWidth = 0,
163
+ autoSizeCustomPadding,
164
+ getOptionItemLabel = (option) => option.name,
165
+ ...autocompleteProps
166
+ }: Props,
167
+ ref?: React.ForwardedRef<HTMLDivElement>
168
+ ): JSX.Element => {
169
+ const { classes, cx } = useStyles({ hideInput });
170
+ const { t } = useTranslation();
171
+ const theme = useTheme();
168
172
 
169
- const areSelectEntriesEqual = (option, value): boolean => {
170
- const identifyingProps = ['id', 'name'];
173
+ const areSelectEntriesEqual = (option, value): boolean => {
174
+ const identifyingProps = ['id', 'name'];
171
175
 
172
- return equals(
173
- pick(identifyingProps, option),
174
- pick(identifyingProps, value)
176
+ return equals(
177
+ pick(identifyingProps, option),
178
+ pick(identifyingProps, value)
179
+ );
180
+ };
181
+
182
+ const renderInput = (params): JSX.Element => (
183
+ <TextField
184
+ {...params}
185
+ InputLabelProps={{
186
+ classes: {
187
+ marginDense: classes.inputLabel,
188
+ shrink: classes.inputLabelShrink
189
+ }
190
+ }}
191
+ InputProps={{
192
+ ...params.InputProps,
193
+ endAdornment: (
194
+ <>
195
+ {endAdornment && (
196
+ <InputAdornment position="end">{endAdornment}</InputAdornment>
197
+ )}
198
+ {params.InputProps.endAdornment}
199
+ </>
200
+ ),
201
+ style: {
202
+ background: 'transparent',
203
+ paddingRight: theme.spacing(5)
204
+ }
205
+ }}
206
+ autoFocus={autoFocus}
207
+ autoSize={autoSize}
208
+ autoSizeCustomPadding={7 + (autoSizeCustomPadding || 0)}
209
+ autoSizeDefaultWidth={autoSizeDefaultWidth}
210
+ classes={{
211
+ root: classes.textfield
212
+ }}
213
+ error={error}
214
+ externalValueForAutoSize={autocompleteProps?.value?.name}
215
+ inputProps={{
216
+ ...params.inputProps,
217
+ 'aria-label': label,
218
+ 'data-testid': dataTestId || label,
219
+ id: getNormalizedId(label || '')
220
+ }}
221
+ label={label}
222
+ placeholder={isNil(placeholder) ? t(searchLabel) : placeholder}
223
+ required={required}
224
+ value={inputValue || undefined}
225
+ onChange={onTextChange}
226
+ />
175
227
  );
176
- };
177
228
 
178
- const renderInput = (params): JSX.Element => (
179
- <TextField
180
- {...params}
181
- InputLabelProps={{
182
- classes: {
183
- marginDense: classes.inputLabel,
184
- shrink: classes.inputLabelShrink
185
- }
186
- }}
187
- InputProps={{
188
- ...params.InputProps,
189
- endAdornment: (
190
- <>
191
- {endAdornment && (
192
- <InputAdornment position="end">{endAdornment}</InputAdornment>
193
- )}
194
- {params.InputProps.endAdornment}
195
- </>
196
- ),
197
- style: {
198
- background: 'transparent',
199
- paddingRight: theme.spacing(5)
229
+ return (
230
+ <Autocomplete
231
+ disableClearable
232
+ classes={{
233
+ groupLabel: classes.inputLabel,
234
+ inputRoot: cx([
235
+ classes.input,
236
+ label ? classes.inputWithLabel : classes.inputWithoutLabel
237
+ ]),
238
+ popper: classes.popper,
239
+ root: classes.textfield
240
+ }}
241
+ forcePopupIcon={displayPopupIcon}
242
+ getOptionLabel={(option): string =>
243
+ (option as SelectEntry)?.name?.toString() || ''
200
244
  }
201
- }}
202
- autoFocus={autoFocus}
203
- autoSize={autoSize}
204
- autoSizeCustomPadding={7 + (autoSizeCustomPadding || 0)}
205
- autoSizeDefaultWidth={autoSizeDefaultWidth}
206
- classes={{
207
- root: classes.textfield
208
- }}
209
- error={error}
210
- externalValueForAutoSize={autocompleteProps?.value?.name}
211
- inputProps={{
212
- ...params.inputProps,
213
- 'aria-label': label,
214
- 'data-testid': dataTestId || label,
215
- id: getNormalizedId(label || '')
216
- }}
217
- label={label}
218
- placeholder={isNil(placeholder) ? t(searchLabel) : placeholder}
219
- required={required}
220
- value={inputValue || undefined}
221
- onChange={onTextChange}
222
- />
223
- );
224
-
225
- return (
226
- <Autocomplete
227
- disableClearable
228
- classes={{
229
- groupLabel: classes.inputLabel,
230
- inputRoot: cx([
231
- classes.input,
232
- label ? classes.inputWithLabel : classes.inputWithoutLabel
233
- ]),
234
- popper: classes.popper,
235
- root: classes.textfield
236
- }}
237
- forcePopupIcon={displayPopupIcon}
238
- getOptionLabel={(option): string =>
239
- (option as SelectEntry)?.name?.toString() || ''
240
- }
241
- isOptionEqualToValue={areSelectEntriesEqual}
242
- loading={loading}
243
- loadingText={<LoadingIndicator />}
244
- options={options}
245
- renderInput={renderInput}
246
- renderOption={(props, option): JSX.Element => {
247
- return (
248
- <li
249
- className={classes.options}
250
- {...(props as React.HTMLAttributes<HTMLLIElement>)}
251
- >
252
- <Option
253
- thumbnailUrl={displayOptionThumbnail ? option.url : undefined}
245
+ isOptionEqualToValue={areSelectEntriesEqual}
246
+ loading={loading}
247
+ loadingText={<LoadingIndicator />}
248
+ options={options}
249
+ ref={ref}
250
+ renderInput={renderInput}
251
+ renderOption={(props, option): JSX.Element => {
252
+ return (
253
+ <li
254
+ className={classes.options}
255
+ {...(props as React.HTMLAttributes<HTMLLIElement>)}
254
256
  >
255
- {getOptionItemLabel(option)}
256
- </Option>
257
- </li>
258
- );
259
- }}
260
- size="small"
261
- {...autocompleteProps}
262
- />
263
- );
264
- };
257
+ <Option
258
+ thumbnailUrl={displayOptionThumbnail ? option.url : undefined}
259
+ >
260
+ {getOptionItemLabel(option)}
261
+ </Option>
262
+ </li>
263
+ );
264
+ }}
265
+ size="small"
266
+ {...autocompleteProps}
267
+ />
268
+ );
269
+ }
270
+ );
265
271
 
266
272
  export default AutocompleteField;
@@ -73,14 +73,14 @@ const IconPopoverMultiAutocomplete = ({
73
73
  };
74
74
 
75
75
  const isSelected = (id: number | string): boolean => {
76
- return pipe(find(propEq('id', id)), Boolean)(value);
76
+ return pipe(find(propEq(id, 'id')), Boolean)(value);
77
77
  };
78
78
 
79
79
  const unSelect = (option: SelectEntry): void => {
80
80
  const { id } = option;
81
81
 
82
82
  const updatedValue = isSelected(id)
83
- ? reject(propEq('id', id), value)
83
+ ? reject(propEq(id, 'id'), value)
84
84
  : [...value, option];
85
85
 
86
86
  onChange(updatedValue);
@@ -69,7 +69,7 @@ const SelectField = ({
69
69
  const { classes, cx } = useStyles();
70
70
 
71
71
  const getOption = (id): SelectEntry => {
72
- return options.find(propEq('id', id)) as SelectEntry;
72
+ return options.find(propEq(id, 'id')) as SelectEntry;
73
73
  };
74
74
 
75
75
  const changeOption = (event): void => {
@@ -155,9 +155,9 @@ const TextField = forwardRef(
155
155
  helperText={displayErrorInTooltip ? undefined : error}
156
156
  id={getNormalizedId(dataTestId || '')}
157
157
  inputProps={{
158
- ...rest.inputProps,
159
158
  'aria-label': ariaLabel,
160
- 'data-testid': dataTestId
159
+ 'data-testid': dataTestId,
160
+ ...rest.inputProps
161
161
  }}
162
162
  label={label}
163
163
  ref={ref}
@@ -107,7 +107,21 @@ const DataCell = ({
107
107
  const isCellHidden = getHiddenCondition?.(isRowSelected);
108
108
 
109
109
  if (isCellHidden) {
110
- return null;
110
+ return (
111
+ <Cell
112
+ className={classes.cell}
113
+ isRowHighlighted={isRowHighlighted}
114
+ listingVariant={listingVariant}
115
+ onClick={(e): void => {
116
+ if (!clickable) {
117
+ return;
118
+ }
119
+ e.preventDefault();
120
+ e.stopPropagation();
121
+ }}
122
+ {...commonCellProps}
123
+ />
124
+ );
111
125
  }
112
126
 
113
127
  return (
@@ -71,7 +71,7 @@ const ListingHeader = ({
71
71
  });
72
72
 
73
73
  const getColumnById = (id: string): Column => {
74
- return find(propEq('id', id), columns) as Column;
74
+ return find(propEq(id, 'id'), columns) as Column;
75
75
  };
76
76
 
77
77
  const Content = React.useCallback(
@@ -77,9 +77,9 @@ const mountListing = (): void => {
77
77
  subItems={{
78
78
  canCheckSubItems: false,
79
79
  enable: true,
80
+ getRowProperty: () => 'subItems',
80
81
  labelCollapse: 'Collapse',
81
- labelExpand: 'Expand',
82
- rowProperty: 'subItems'
82
+ labelExpand: 'Expand'
83
83
  }}
84
84
  totalRows={10}
85
85
  />
@@ -9,13 +9,12 @@ const loadingIndicatorHeight = 3;
9
9
  interface StylesProps {
10
10
  dataStyle: TableStyle;
11
11
  getGridTemplateColumn: string;
12
- limit: number;
13
12
  listingVariant: ListingVariant;
14
13
  rows: Array<unknown>;
15
14
  }
16
15
 
17
16
  const useListingStyles = makeStyles<StylesProps>()(
18
- (theme, { dataStyle, getGridTemplateColumn, rows, limit }) => ({
17
+ (theme, { dataStyle, getGridTemplateColumn, rows }) => ({
19
18
  actionBar: {
20
19
  alignItems: 'center',
21
20
  display: 'flex'
@@ -56,7 +55,7 @@ const useListingStyles = makeStyles<StylesProps>()(
56
55
  display: 'grid',
57
56
  gridTemplateColumns: getGridTemplateColumn,
58
57
  gridTemplateRows: `${theme.spacing(dataStyle.header.height / 8)} repeat(${
59
- rows?.length || limit
58
+ rows?.length || 1
60
59
  }, ${dataStyle.body.height}px)`,
61
60
  position: 'relative'
62
61
  },
@@ -366,6 +366,16 @@ const columnsWithSubItems = [
366
366
  }
367
367
  ];
368
368
 
369
+ const TemplateSubItems = (args): JSX.Element => {
370
+ const { classes } = useStyles();
371
+
372
+ return (
373
+ <div className={classes.listing}>
374
+ <Listing {...args} />
375
+ </div>
376
+ );
377
+ };
378
+
369
379
  export const ListingWithSubItems = {
370
380
  args: {
371
381
  checkable: true,
@@ -377,10 +387,11 @@ export const ListingWithSubItems = {
377
387
  subItems: {
378
388
  canCheckSubItems: false,
379
389
  enable: true,
390
+ getRowProperty: () => 'subItems',
380
391
  labelCollapse: 'Collapse',
381
- labelExpand: 'Expand',
382
- rowProperty: 'subItems'
392
+ labelExpand: 'Expand'
383
393
  },
384
394
  totalRows: 10
385
- }
395
+ },
396
+ render: TemplateSubItems
386
397
  };
@@ -69,7 +69,7 @@ const getVisibleColumns = ({
69
69
  }
70
70
 
71
71
  return selectedColumnIds.map((id) =>
72
- columns.find(propEq('id', id))
72
+ columns.find(propEq(id, 'id'))
73
73
  ) as Array<Column>;
74
74
  };
75
75
 
@@ -121,9 +121,9 @@ export interface Props<TRow> {
121
121
  subItems?: {
122
122
  canCheckSubItems: boolean;
123
123
  enable: boolean;
124
+ getRowProperty: (row?) => string;
124
125
  labelCollapse: string;
125
126
  labelExpand: string;
126
- rowProperty: string;
127
127
  };
128
128
  totalRows?: number;
129
129
  viewerModeConfiguration?: ViewerModeConfiguration;
@@ -176,9 +176,9 @@ const Listing = <TRow extends { id: RowId }>({
176
176
  subItems = {
177
177
  canCheckSubItems: false,
178
178
  enable: false,
179
+ getRowProperty: () => '',
179
180
  labelCollapse: 'Collapse',
180
- labelExpand: 'Expand',
181
- rowProperty: ''
181
+ labelExpand: 'Expand'
182
182
  }
183
183
  }: Props<TRow>): JSX.Element => {
184
184
  const currentVisibleColumns = getVisibleColumns({
@@ -211,10 +211,10 @@ const Listing = <TRow extends { id: RowId }>({
211
211
  ? reduce<TRow, Array<TRow>>(
212
212
  (acc, row): Array<TRow> => {
213
213
  if (
214
- row[subItems.rowProperty] &&
214
+ row[subItems.getRowProperty()] &&
215
215
  subItemsPivots.includes(row.id)
216
216
  ) {
217
- return [...acc, row, ...row[subItems.rowProperty]];
217
+ return [...acc, row, ...row[subItems.getRowProperty()]];
218
218
  }
219
219
 
220
220
  return [...acc, row];
@@ -229,7 +229,6 @@ const Listing = <TRow extends { id: RowId }>({
229
229
  const { classes } = useListingStyles({
230
230
  dataStyle,
231
231
  getGridTemplateColumn,
232
- limit,
233
232
  listingVariant,
234
233
  rows: rowsToDisplay
235
234
  });
@@ -450,7 +449,7 @@ const Listing = <TRow extends { id: RowId }>({
450
449
  reduce<TRow | number, Array<string | number>>(
451
450
  (acc, row) => [
452
451
  ...acc,
453
- ...pluck('id', row[subItems?.rowProperty || ''] || [])
452
+ ...pluck('id', row[subItems?.getRowProperty() || ''] || [])
454
453
  ],
455
454
  [],
456
455
  rows
@@ -621,7 +620,7 @@ const Listing = <TRow extends { id: RowId }>({
621
620
  listingVariant={listingVariant}
622
621
  row={row}
623
622
  rowColorConditions={rowColorConditions}
624
- subItemsRowProperty={subItems?.rowProperty}
623
+ subItemsRowProperty={subItems?.getRowProperty(row)}
625
624
  />
626
625
  ))}
627
626
  </ListingRow>
@@ -0,0 +1,129 @@
1
+ import { createStore } from 'jotai';
2
+
3
+ import { Method } from '..';
4
+
5
+ import LicensedModule from './LicensedModule';
6
+
7
+ import Module from '.';
8
+
9
+ const initializeModule = (): void => {
10
+ cy.mount({
11
+ Component: (
12
+ <Module seedName="seed" store={createStore()}>
13
+ <p>Module</p>
14
+ </Module>
15
+ )
16
+ });
17
+ };
18
+
19
+ const initializeModuleWithValidLicense = (
20
+ isFederatedComponent = false
21
+ ): void => {
22
+ cy.interceptAPIRequest({
23
+ alias: 'getValidLicense',
24
+ method: Method.GET,
25
+ path: './api/internal.php?object=centreon_license_manager&action=licenseValid&productName=valid',
26
+ response: {
27
+ success: true
28
+ }
29
+ });
30
+
31
+ cy.mount({
32
+ Component: (
33
+ <div style={{ height: '100vh' }}>
34
+ <LicensedModule
35
+ isFederatedComponent={isFederatedComponent}
36
+ moduleName="valid"
37
+ seedName="seed"
38
+ store={createStore()}
39
+ >
40
+ <p>Module</p>
41
+ </LicensedModule>
42
+ </div>
43
+ )
44
+ });
45
+ };
46
+
47
+ const initializeModuleWithInvalidLicense = (
48
+ isFederatedComponent = false
49
+ ): void => {
50
+ cy.interceptAPIRequest({
51
+ alias: 'getInvalidLicense',
52
+ method: Method.GET,
53
+ path: './api/internal.php?object=centreon_license_manager&action=licenseValid&productName=invalid',
54
+ response: {
55
+ success: false
56
+ }
57
+ });
58
+
59
+ cy.mount({
60
+ Component: (
61
+ <div style={{ height: '100vh' }}>
62
+ <LicensedModule
63
+ isFederatedComponent={isFederatedComponent}
64
+ moduleName="invalid"
65
+ seedName="seed"
66
+ store={createStore()}
67
+ >
68
+ <p>Module</p>
69
+ </LicensedModule>
70
+ </div>
71
+ )
72
+ });
73
+ };
74
+
75
+ describe('Module', () => {
76
+ beforeEach(() => {
77
+ initializeModule();
78
+ });
79
+
80
+ it('displays the content of the module', () => {
81
+ cy.contains('Module').should('be.visible');
82
+
83
+ cy.makeSnapshot();
84
+ });
85
+ });
86
+
87
+ describe('Valid license module', () => {
88
+ it('displays the content of the page when the license is valid license', () => {
89
+ initializeModuleWithValidLicense();
90
+ cy.waitForRequest('@getValidLicense');
91
+
92
+ cy.contains('Module').should('be.visible');
93
+
94
+ cy.makeSnapshot();
95
+ });
96
+
97
+ it('displays the content of the component when the license is valid license', () => {
98
+ initializeModuleWithValidLicense(true);
99
+
100
+ cy.contains('Module').should('be.visible');
101
+
102
+ cy.makeSnapshot();
103
+ });
104
+ });
105
+
106
+ describe('Invalid license module', () => {
107
+ it('displays the content of the page when the license is invalid license', () => {
108
+ initializeModuleWithInvalidLicense();
109
+ cy.waitForRequest('@getInvalidLicense');
110
+
111
+ cy.contains('Module').should('not.exist');
112
+
113
+ cy.contains('Oops').should('be.visible');
114
+ cy.contains('License invalid or expired').should('be.visible');
115
+ cy.contains('Please contact your administrator.').should('be.visible');
116
+ cy.get('img[alt="License invalid or expired !"]').should('be.visible');
117
+ cy.get('img[alt="Centreon Logo"]').should('be.visible');
118
+
119
+ cy.makeSnapshot();
120
+ });
121
+
122
+ it('displays the content of the module when the license is invalid license', () => {
123
+ initializeModuleWithInvalidLicense(true);
124
+
125
+ cy.contains('Module').should('not.exist');
126
+
127
+ cy.makeSnapshot();
128
+ });
129
+ });
@@ -1,5 +1,3 @@
1
- import * as React from 'react';
2
-
3
1
  import { Provider as JotaiProvider, createStore } from 'jotai';
4
2
 
5
3
  import { createGenerateClassName, StylesProvider } from '@mui/styles';
@@ -9,7 +7,7 @@ import SnackbarProvider from '../Snackbar/SnackbarProvider';
9
7
 
10
8
  export interface ModuleProps {
11
9
  children: React.ReactElement;
12
- maxSnackbars: number;
10
+ maxSnackbars?: number;
13
11
  seedName: string;
14
12
  store: ReturnType<typeof createStore>;
15
13
  }
@@ -17,7 +15,7 @@ export interface ModuleProps {
17
15
  const Module = ({
18
16
  children,
19
17
  seedName,
20
- maxSnackbars,
18
+ maxSnackbars = 3,
21
19
  store
22
20
  }: ModuleProps): JSX.Element => {
23
21
  const generateClassName = createGenerateClassName({
@@ -12,6 +12,7 @@ import { LinkPlugin } from '@lexical/react/LexicalLinkPlugin';
12
12
  import { equals } from 'ramda';
13
13
  import { ListPlugin } from '@lexical/react/LexicalListPlugin';
14
14
  import { OnChangePlugin } from '@lexical/react/LexicalOnChangePlugin';
15
+ import { $generateHtmlFromNodes } from '@lexical/html';
15
16
 
16
17
  import { Typography } from '@mui/material';
17
18
 
@@ -38,6 +39,7 @@ export interface RichTextEditorProps {
38
39
  openLinkInNewTab?: boolean;
39
40
  placeholder?: string;
40
41
  resetEditorToInitialStateCondition?: () => boolean;
42
+ setHtmlString?: (htmlString: string) => void;
41
43
  toolbarClassName?: string;
42
44
  toolbarPositions?: 'start' | 'end';
43
45
  }
@@ -143,6 +145,7 @@ const RichTextEditor = ({
143
145
  openLinkInNewTab = true,
144
146
  initialize,
145
147
  displayBlockButtons = true,
148
+ setHtmlString,
146
149
  toolbarClassName
147
150
  }: RichTextEditorProps): JSX.Element => {
148
151
  const { classes } = useStyles({ toolbarPositions });
@@ -178,6 +181,13 @@ const RichTextEditor = ({
178
181
  }
179
182
  };
180
183
 
184
+ const change = (state: EditorState, editor: LexicalEditor): void => {
185
+ editor.update(() => {
186
+ setHtmlString?.($generateHtmlFromNodes(editor, null));
187
+ });
188
+ getEditorState?.(state, editor);
189
+ };
190
+
181
191
  return (
182
192
  <LexicalComposer initialConfig={initialConfig}>
183
193
  <div className={classes.container}>
@@ -210,6 +220,7 @@ const RichTextEditor = ({
210
220
  resetEditorToInitialStateCondition={
211
221
  resetEditorToInitialStateCondition
212
222
  }
223
+ setHtmlString={setHtmlString}
213
224
  onBlur={onBlur}
214
225
  />
215
226
  }
@@ -218,7 +229,7 @@ const RichTextEditor = ({
218
229
  <HistoryPlugin />
219
230
  <LinkPlugin />
220
231
  <ListPlugin />
221
- <OnChangePlugin onChange={getEditorState} />
232
+ <OnChangePlugin onChange={change} />
222
233
  <AutoCompleteLinkPlugin openLinkInNewTab={openLinkInNewTab} />
223
234
  <FloatingLinkEditorPlugin
224
235
  editable={editable}