@centreon/ui 24.4.38 → 24.4.39
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.
- package/package.json +19 -14
- package/public/mockServiceWorker.js +1 -1
- package/src/Button/Icon/index.tsx +1 -1
- package/src/Button/Save/StartIcon.tsx +3 -3
- package/src/Button/Save/index.tsx +9 -5
- package/src/Checkbox/Checkbox.tsx +2 -2
- package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
- package/src/Dashboard/Item.tsx +1 -1
- package/src/Dashboard/Layout.tsx +2 -2
- package/src/Dialog/index.tsx +1 -1
- package/src/FallbackPage/FallbackPage.tsx +3 -3
- package/src/FileDropZone/index.tsx +3 -1
- package/src/Form/Form.cypress.spec.tsx +133 -0
- package/src/Form/Inputs/CheckboxGroup.tsx +1 -4
- package/src/Form/Inputs/List/Content.tsx +62 -0
- package/src/Form/Inputs/List/List.styles.ts +29 -0
- package/src/Form/Inputs/List/List.tsx +58 -0
- package/src/Form/Inputs/List/useList.ts +81 -0
- package/src/Form/Inputs/index.tsx +3 -1
- package/src/Form/Inputs/models.ts +9 -1
- package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
- package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +5 -4
- package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
- package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
- package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +2 -3
- package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
- package/src/Graph/LineChart/Legend/index.tsx +7 -8
- package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
- package/src/Graph/LineChart/LineChart.tsx +4 -1
- package/src/Graph/LineChart/helpers/doc.ts +16 -13
- package/src/Graph/LineChart/helpers/index.ts +1 -1
- package/src/Graph/LineChart/index.stories.tsx +4 -2
- package/src/Graph/LineChart/index.tsx +4 -1
- package/src/Graph/SingleBar/Thresholds.tsx +2 -2
- package/src/Graph/Text/Text.stories.tsx +60 -4
- package/src/Graph/common/timeSeries/index.ts +3 -3
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
- package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
- package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
- package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
- package/src/InputField/Select/Autocomplete/index.tsx +121 -115
- package/src/InputField/Select/IconPopover/index.tsx +2 -2
- package/src/InputField/Select/index.tsx +1 -1
- package/src/InputField/Text/index.tsx +2 -2
- package/src/Listing/Cell/DataCell.tsx +15 -1
- package/src/Listing/Header/ListingHeader.tsx +1 -1
- package/src/Listing/Listing.styles.ts +2 -3
- package/src/Listing/index.stories.tsx +12 -1
- package/src/Listing/index.tsx +1 -2
- package/src/Module/Module.cypress.spec.tsx +129 -0
- package/src/Module/index.tsx +2 -4
- package/src/RichTextEditor/RichTextEditor.tsx +12 -1
- package/src/SortableItems/index.tsx +2 -7
- package/src/ThemeProvider/index.tsx +24 -0
- package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
- package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
- package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
- package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
- package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
- package/src/TimePeriods/helpers/index.ts +1 -1
- package/src/TimePeriods/index.stories.tsx +12 -4
- package/src/TimePeriods/index.tsx +2 -2
- package/src/api/QueryProvider.tsx +1 -1
- package/src/api/TestQueryProvider.tsx +1 -1
- package/src/api/useFetchQuery/index.ts +27 -23
- package/src/api/useGraphQuery/index.ts +26 -5
- package/src/api/useGraphQuery/models.ts +5 -0
- package/src/api/useMutationQuery/index.ts +45 -21
- package/src/components/Button/Icon/IconButton.tsx +6 -2
- package/src/components/CollapsibleItem/CollapsibleItem.cypress.spec.tsx +76 -0
- package/src/components/CollapsibleItem/CollapsibleItem.stories.tsx +26 -0
- package/src/components/CollapsibleItem/CollapsibleItem.tsx +43 -14
- package/src/components/CollapsibleItem/useCollapsibleItemStyles.ts +24 -1
- package/src/components/DataTable/DataListing.tsx +6 -0
- package/src/components/DataTable/DataTable.cypress.spec.tsx +193 -0
- package/src/components/DataTable/DataTable.stories.tsx +40 -0
- package/src/components/DataTable/DataTable.styles.ts +3 -0
- package/src/components/DataTable/DataTable.tsx +3 -3
- package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
- package/src/components/DataTable/Item/DataTableItem.tsx +4 -4
- package/src/components/DataTable/index.ts +3 -1
- package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
- package/src/components/ItemComposition/Item.tsx +1 -1
- package/src/components/ItemComposition/ItemComposition.cypress.spec.tsx +116 -0
- package/src/components/ItemComposition/ItemComposition.styles.ts +8 -1
- package/src/components/ItemComposition/ItemComposition.tsx +26 -16
- package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
- package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
- package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
- package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
- package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
- package/src/components/Layout/PageLayout/index.ts +3 -1
- package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
- package/src/components/Modal/Modal.styles.ts +1 -1
- package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
- package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
- package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
- package/src/index.ts +2 -2
- package/src/queryParameters/url/index.ts +5 -1
- package/src/utils/index.ts +2 -1
- package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
- package/src/utils/useLicenseExpirationWarning.ts +18 -18
- package/src/utils/usePluralizedTranslation.ts +21 -0
- package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
- package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
- 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
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
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
|
-
|
|
170
|
-
|
|
173
|
+
const areSelectEntriesEqual = (option, value): boolean => {
|
|
174
|
+
const identifyingProps = ['id', 'name'];
|
|
171
175
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
72
|
+
return options.find(propEq(id, 'id')) as SelectEntry;
|
|
73
73
|
};
|
|
74
74
|
|
|
75
75
|
const changeOption = (event): void => {
|
|
@@ -154,9 +154,9 @@ const TextField = forwardRef(
|
|
|
154
154
|
helperText={displayErrorInTooltip ? undefined : error}
|
|
155
155
|
id={getNormalizedId(dataTestId || '')}
|
|
156
156
|
inputProps={{
|
|
157
|
-
...rest.inputProps,
|
|
158
157
|
'aria-label': ariaLabel,
|
|
159
|
-
'data-testid': dataTestId
|
|
158
|
+
'data-testid': dataTestId,
|
|
159
|
+
...rest.inputProps
|
|
160
160
|
}}
|
|
161
161
|
label={label}
|
|
162
162
|
ref={ref}
|
|
@@ -107,7 +107,21 @@ const DataCell = ({
|
|
|
107
107
|
const isCellHidden = getHiddenCondition?.(isRowSelected);
|
|
108
108
|
|
|
109
109
|
if (isCellHidden) {
|
|
110
|
-
return
|
|
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 (
|
|
@@ -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
|
|
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 ||
|
|
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,
|
|
@@ -382,5 +392,6 @@ export const ListingWithSubItems = {
|
|
|
382
392
|
labelExpand: 'Expand'
|
|
383
393
|
},
|
|
384
394
|
totalRows: 10
|
|
385
|
-
}
|
|
395
|
+
},
|
|
396
|
+
render: TemplateSubItems
|
|
386
397
|
};
|
package/src/Listing/index.tsx
CHANGED
|
@@ -69,7 +69,7 @@ const getVisibleColumns = ({
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
return selectedColumnIds.map((id) =>
|
|
72
|
-
columns.find(propEq(
|
|
72
|
+
columns.find(propEq(id, 'id'))
|
|
73
73
|
) as Array<Column>;
|
|
74
74
|
};
|
|
75
75
|
|
|
@@ -233,7 +233,6 @@ const Listing = <TRow extends { id: RowId }>({
|
|
|
233
233
|
const { classes } = useListingStyles({
|
|
234
234
|
dataStyle,
|
|
235
235
|
getGridTemplateColumn,
|
|
236
|
-
limit,
|
|
237
236
|
listingVariant,
|
|
238
237
|
rows: rowsToDisplay
|
|
239
238
|
});
|
|
@@ -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
|
+
});
|
package/src/Module/index.tsx
CHANGED
|
@@ -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
|
|
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={
|
|
232
|
+
<OnChangePlugin onChange={change} />
|
|
222
233
|
<AutoCompleteLinkPlugin openLinkInNewTab={openLinkInNewTab} />
|
|
223
234
|
<FloatingLinkEditorPlugin
|
|
224
235
|
editable={editable}
|
|
@@ -15,11 +15,7 @@ import {
|
|
|
15
15
|
DragEndEvent,
|
|
16
16
|
Over
|
|
17
17
|
} from '@dnd-kit/core';
|
|
18
|
-
import {
|
|
19
|
-
SortableContext,
|
|
20
|
-
sortableKeyboardCoordinates,
|
|
21
|
-
SortingStrategy
|
|
22
|
-
} from '@dnd-kit/sortable';
|
|
18
|
+
import { SortableContext, SortingStrategy } from '@dnd-kit/sortable';
|
|
23
19
|
import {
|
|
24
20
|
equals,
|
|
25
21
|
find,
|
|
@@ -117,7 +113,6 @@ const SortableItems = <T extends { [propertyToFilterItemsOn]: string }>({
|
|
|
117
113
|
useSensor(MouseSensor),
|
|
118
114
|
useSensor(PointerSensor),
|
|
119
115
|
useSensor(KeyboardSensor, {
|
|
120
|
-
coordinateGetter: sortableKeyboardCoordinates,
|
|
121
116
|
keyboardCodes: {
|
|
122
117
|
cancel: ['Escape'],
|
|
123
118
|
end: ['Space', 'Enter'],
|
|
@@ -169,7 +164,7 @@ const SortableItems = <T extends { [propertyToFilterItemsOn]: string }>({
|
|
|
169
164
|
};
|
|
170
165
|
|
|
171
166
|
const getItemById = (id): T | undefined =>
|
|
172
|
-
find(propEq(
|
|
167
|
+
find(propEq(id, propertyToFilterItemsOn), items);
|
|
173
168
|
|
|
174
169
|
const activeItem = getItemById(activeId) as Record<string, unknown>;
|
|
175
170
|
|
|
@@ -147,6 +147,30 @@ export const getTheme = (mode: ThemeMode): ThemeOptions => ({
|
|
|
147
147
|
},
|
|
148
148
|
MuiCssBaseline: {
|
|
149
149
|
styleOverrides: (theme) => `
|
|
150
|
+
::-webkit-scrollbar {
|
|
151
|
+
height: ${theme.spacing(1)};
|
|
152
|
+
width: ${theme.spacing(1)};
|
|
153
|
+
background-color: transparent;
|
|
154
|
+
}
|
|
155
|
+
::-webkit-scrollbar-thumb {
|
|
156
|
+
background-color: ${
|
|
157
|
+
equals(mode, 'dark')
|
|
158
|
+
? theme.palette.divider
|
|
159
|
+
: theme.palette.text.disabled
|
|
160
|
+
};
|
|
161
|
+
border-radius: ${theme.spacing(0.5)};
|
|
162
|
+
}
|
|
163
|
+
::-webkit-scrollbar-thumb:hover {
|
|
164
|
+
background-color: ${theme.palette.primary.main};
|
|
165
|
+
}
|
|
166
|
+
* {
|
|
167
|
+
scrollbar-color: ${
|
|
168
|
+
equals(mode, 'dark')
|
|
169
|
+
? theme.palette.divider
|
|
170
|
+
: theme.palette.text.disabled
|
|
171
|
+
} ${theme.palette.background.default};
|
|
172
|
+
scrollbar-width: thin;
|
|
173
|
+
}
|
|
150
174
|
html {
|
|
151
175
|
margin: 0;
|
|
152
176
|
padding: 0;
|