@centreon/ui 24.4.44 → 24.4.45-develop.0

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/StartIcon.tsx +3 -3
  5. package/src/Button/Save/index.tsx +9 -5
  6. package/src/Checkbox/Checkbox.tsx +2 -2
  7. package/src/Checkbox/CheckboxGroup/index.tsx +2 -2
  8. package/src/Dashboard/Item.tsx +1 -1
  9. package/src/Dashboard/Layout.tsx +2 -2
  10. package/src/Dialog/index.tsx +1 -1
  11. package/src/FallbackPage/FallbackPage.tsx +3 -3
  12. package/src/FileDropZone/index.tsx +3 -1
  13. package/src/Form/Form.cypress.spec.tsx +133 -0
  14. package/src/Form/Inputs/CheckboxGroup.tsx +1 -4
  15. package/src/Form/Inputs/List/Content.tsx +62 -0
  16. package/src/Form/Inputs/List/List.styles.ts +29 -0
  17. package/src/Form/Inputs/List/List.tsx +58 -0
  18. package/src/Form/Inputs/List/useList.ts +81 -0
  19. package/src/Form/Inputs/index.tsx +3 -1
  20. package/src/Form/Inputs/models.ts +9 -1
  21. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  22. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/index.tsx +5 -4
  23. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  24. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  25. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  26. package/src/Graph/LineChart/InteractiveComponents/Annotations/Annotation/index.tsx +2 -3
  27. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  28. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  29. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  30. package/src/Graph/LineChart/helpers/index.ts +1 -1
  31. package/src/Graph/LineChart/index.stories.tsx +4 -2
  32. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  33. package/src/Graph/Text/Text.stories.tsx +60 -4
  34. package/src/Graph/common/timeSeries/index.ts +3 -3
  35. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  36. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  37. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  38. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  39. package/src/InputField/Select/Autocomplete/index.tsx +121 -115
  40. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  41. package/src/InputField/Select/index.tsx +1 -1
  42. package/src/InputField/Text/index.tsx +2 -2
  43. package/src/Listing/Cell/DataCell.tsx +15 -1
  44. package/src/Listing/Header/ListingHeader.tsx +1 -1
  45. package/src/Listing/Listing.styles.ts +2 -3
  46. package/src/Listing/index.stories.tsx +12 -1
  47. package/src/Listing/index.tsx +1 -2
  48. package/src/Module/Module.cypress.spec.tsx +129 -0
  49. package/src/Module/index.tsx +2 -4
  50. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  51. package/src/SortableItems/index.tsx +2 -7
  52. package/src/ThemeProvider/index.tsx +24 -0
  53. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  54. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  55. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  56. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  57. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  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 +4 -4
  74. package/src/components/DataTable/index.ts +3 -1
  75. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  76. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  77. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  78. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  79. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  80. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  81. package/src/components/Layout/PageLayout/index.ts +3 -1
  82. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  83. package/src/components/Modal/Modal.styles.ts +1 -1
  84. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  85. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  86. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  87. package/src/index.ts +2 -2
  88. package/src/queryParameters/url/index.ts +5 -1
  89. package/src/utils/index.ts +2 -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
@@ -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 => {
@@ -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 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(
@@ -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,
@@ -382,5 +392,6 @@ export const ListingWithSubItems = {
382
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
 
@@ -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
+ });
@@ -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}
@@ -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(propertyToFilterItemsOn, id), items);
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;
@@ -12,22 +12,21 @@ const useStyles = makeStyles()((theme) => ({
12
12
  },
13
13
  containerDates: {
14
14
  display: 'flex',
15
- gap: theme.spacing(0.5),
16
15
  [theme.breakpoints.down('sm')]: {
17
- columnGap: theme.spacing(0.5),
18
- flexDirection: 'column'
16
+ flexDirection: 'column',
17
+ gap: theme.spacing(0.5)
19
18
  }
20
19
  },
21
20
  date: {
22
- minWidth: theme.spacing(12.5),
21
+ minWidth: theme.spacing(12),
23
22
  textAlign: 'start'
24
23
  },
25
24
  error: {
26
25
  textAlign: 'center'
27
26
  },
28
27
  label: {
29
- minWidth: theme.spacing(3),
30
- textAlign: 'start'
28
+ minWidth: theme.spacing(3.5),
29
+ textAlign: 'end'
31
30
  },
32
31
 
33
32
  picker: {
@@ -39,7 +38,7 @@ const useStyles = makeStyles()((theme) => ({
39
38
  },
40
39
  timeContainer: {
41
40
  alignItems: 'center',
42
- columnGap: theme.spacing(1),
41
+ columnGap: theme.spacing(0.5),
43
42
  display: 'flex',
44
43
  flexDirection: 'row',
45
44
  [theme.breakpoints.down('sm')]: {
@@ -2,12 +2,15 @@ import { useAtomValue } from 'jotai';
2
2
  import { makeStyles } from 'tss-react/mui';
3
3
  import { equals } from 'ramda';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import dayjs from 'dayjs';
6
+ import utc from 'dayjs/plugin/utc';
7
+ import timezone from 'dayjs/plugin/timezone';
5
8
 
9
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
6
10
  import { Typography } from '@mui/material';
7
11
  import { LocalizationProvider } from '@mui/x-date-pickers';
8
12
 
9
13
  import { userAtom } from '@centreon/ui-context';
10
- import { useDateTimePickerAdapter } from '@centreon/ui';
11
14
 
12
15
  import DateTimePickerInput from '../../DateTimePickerInput';
13
16
  import {
@@ -20,6 +23,9 @@ import ErrorText from './ErrorText';
20
23
  import { PickersData, PickersStartEndDateDirection } from './models';
21
24
  import { PickersStartEndDateModel } from './usePickersStartEndDate';
22
25
 
26
+ dayjs.extend(utc);
27
+ dayjs.extend(timezone);
28
+
23
29
  const useStyles = makeStyles()((theme) => ({
24
30
  error: {
25
31
  textAlign: 'center'
@@ -104,7 +110,6 @@ const PickersStartEndDate = ({
104
110
  direction = PickersStartEndDateDirection.column
105
111
  }: Props): JSX.Element => {
106
112
  const { classes, cx } = useStyles();
107
- const { Adapter } = useDateTimePickerAdapter();
108
113
 
109
114
  const { locale } = useAtomValue(userAtom);
110
115
  const error = useAtomValue(errorTimePeriodAtom);
@@ -126,7 +131,7 @@ const PickersStartEndDate = ({
126
131
  return (
127
132
  <LocalizationProvider
128
133
  adapterLocale={locale.substring(0, 2)}
129
- dateAdapter={Adapter}
134
+ dateAdapter={AdapterDayjs}
130
135
  >
131
136
  <div className={styleContainer}>
132
137
  <PickerDateWithLabel
@@ -18,8 +18,6 @@ export interface PickersData {
18
18
  getError?: (value: boolean) => void;
19
19
  isDisabledEndPicker?: boolean;
20
20
  isDisabledStartPicker?: boolean;
21
- onCloseEndPicker?: (isClosed: boolean) => void;
22
- onCloseStartPicker?: (isClosed: boolean) => void;
23
21
  rangeEndDate?: RangeDate;
24
22
  rangeStartDate?: RangeDate;
25
23
  }
@@ -1,48 +1,85 @@
1
- import dayjs from 'dayjs';
1
+ import { useCallback } from 'react';
2
2
 
3
- import { DateTimePicker } from '@mui/x-date-pickers';
3
+ import dayjs, { Dayjs } from 'dayjs';
4
+ import { useAtomValue } from 'jotai';
5
+ import { equals } from 'ramda';
4
6
 
5
- import { useDateTimePickerAdapter } from '@centreon/ui';
7
+ import {
8
+ DateTimePicker,
9
+ DateTimePickerProps,
10
+ LocalizationProvider
11
+ } from '@mui/x-date-pickers';
12
+ import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
13
+
14
+ import { userAtom } from '@centreon/ui-context';
6
15
 
7
16
  import { CustomTimePeriodProperty } from './models';
8
17
 
18
+ interface ChangeDateProps {
19
+ date: Date;
20
+ property: CustomTimePeriodProperty | string;
21
+ }
22
+
9
23
  interface Props {
10
- changeDate: (props) => void;
24
+ changeDate: (props: ChangeDateProps) => void;
11
25
  date: Date | null;
12
26
  desktopMediaQuery?: string;
13
27
  disabled?: boolean;
14
28
  maxDate?: Date;
15
29
  minDate?: Date;
16
- property: CustomTimePeriodProperty;
30
+ minDateTime?: Date;
31
+ property: CustomTimePeriodProperty | string;
17
32
  }
18
33
 
19
34
  const DateTimePickerInput = ({
20
35
  date,
21
36
  maxDate,
22
37
  minDate,
38
+ minDateTime,
23
39
  property,
24
40
  changeDate,
25
41
  disabled = false,
26
- desktopMediaQuery
27
- }: Props): JSX.Element => {
28
- const { desktopPickerMediaQuery } = useDateTimePickerAdapter();
42
+ desktopMediaQuery,
43
+ ...rest
44
+ }: Props & DateTimePickerProps<dayjs.Dayjs>): JSX.Element => {
45
+ const desktopPickerMediaQuery =
46
+ '@media (min-width: 1024px) or (pointer: fine)';
47
+
48
+ const { timezone, locale } = useAtomValue(userAtom);
49
+
50
+ const isUTC = equals(timezone, 'UTC');
29
51
 
30
52
  const changeTime = (newValue: dayjs.Dayjs | null): void => {
31
53
  changeDate({ date: dayjs(newValue).toDate(), property });
32
54
  };
33
55
 
56
+ const formatDate = useCallback(
57
+ (currentDate: Date | null): Dayjs => {
58
+ return isUTC ? dayjs.utc(currentDate) : dayjs.tz(currentDate, timezone);
59
+ },
60
+ [isUTC, timezone]
61
+ );
62
+
34
63
  return (
35
- <DateTimePicker<dayjs.Dayjs>
36
- dayOfWeekFormatter={(day: string): string =>
37
- day.substring(0, 2).toUpperCase()
38
- }
39
- desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
40
- disabled={disabled}
41
- maxDate={maxDate && dayjs(maxDate)}
42
- minDate={minDate && dayjs(minDate)}
43
- value={dayjs(date)}
44
- onChange={changeTime}
45
- />
64
+ <LocalizationProvider
65
+ adapterLocale={locale.substring(0, 2)}
66
+ dateAdapter={AdapterDayjs}
67
+ dateLibInstance={dayjs}
68
+ >
69
+ <DateTimePicker<dayjs.Dayjs>
70
+ dayOfWeekFormatter={(dayOfWeek) =>
71
+ dayOfWeek.substring(0, 2).toLocaleUpperCase()
72
+ }
73
+ desktopModeMediaQuery={desktopMediaQuery ?? desktopPickerMediaQuery}
74
+ disabled={disabled}
75
+ maxDate={maxDate && formatDate(maxDate)}
76
+ minDate={minDate && formatDate(minDate)}
77
+ minDateTime={minDateTime && formatDate(minDateTime)}
78
+ value={formatDate(date)}
79
+ onChange={changeTime}
80
+ {...rest}
81
+ />
82
+ </LocalizationProvider>
46
83
  );
47
84
  };
48
85
 
@@ -1,5 +1,5 @@
1
1
  import dayjs from 'dayjs';
2
- import 'dayjs/locale/fr';
2
+ import 'dayjs/locale/en';
3
3
  import localizedFormatPlugin from 'dayjs/plugin/localizedFormat';
4
4
  import timezonePlugin from 'dayjs/plugin/timezone';
5
5
  import utcPlugin from 'dayjs/plugin/utc';
@@ -19,18 +19,18 @@ dayjs.extend(localizedFormatPlugin);
19
19
 
20
20
  const data = [
21
21
  {
22
- locale: 'fr_FR',
23
- resolution: { height: 720, width: 500 },
22
+ locale: 'en_US',
23
+ resolution: { height: 590, width: 500 },
24
24
  timezone: 'Europe/Paris'
25
25
  },
26
26
  {
27
- locale: 'fr_FR',
28
- resolution: { height: 720, width: 200 },
27
+ locale: 'en_US',
28
+ resolution: { height: 590, width: 200 },
29
29
  timezone: 'Europe/Paris'
30
30
  },
31
31
  {
32
- locale: 'fr_FR',
33
- resolution: { height: 720, width: 1024 },
32
+ locale: 'en_US',
33
+ resolution: { height: 590, width: 1024 },
34
34
  timezone: 'Europe/Paris'
35
35
  }
36
36
  ];
@@ -45,7 +45,7 @@ data.forEach((item) =>
45
45
 
46
46
  store.set(userAtom, {
47
47
  ...retrievedUser,
48
- locale: 'fr_FR',
48
+ locale: 'en_US',
49
49
  timezone: 'Europe/Paris'
50
50
  });
51
51
 
@@ -59,7 +59,10 @@ data.forEach((item) =>
59
59
  });
60
60
 
61
61
  it(`displays correctly the dates design when screen resolution is ${width}px`, () => {
62
- cy.makeSnapshot(`${width}px`);
62
+ cy.makeSnapshotWithCustomResolution({
63
+ resolution: { height, width },
64
+ title: `${width}px`
65
+ });
63
66
  });
64
67
  })
65
68
  );