@centreon/ui 24.4.59 → 24.4.61

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 (163) hide show
  1. package/package.json +50 -42
  2. package/public/mockServiceWorker.js +1 -1
  3. package/src/ActionsList/ActionsList.styles.ts +40 -71
  4. package/src/Button/Icon/index.tsx +1 -1
  5. package/src/Button/Save/StartIcon.tsx +3 -3
  6. package/src/Button/Save/index.tsx +9 -5
  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/Confirm/index.tsx +10 -2
  12. package/src/Dialog/UnsavedChanges/index.tsx +21 -20
  13. package/src/Dialog/UnsavedChanges/translatedLabels.ts +4 -6
  14. package/src/Dialog/index.tsx +9 -2
  15. package/src/FallbackPage/FallbackPage.tsx +3 -3
  16. package/src/FileDropZone/index.tsx +3 -1
  17. package/src/Form/Form.cypress.spec.tsx +133 -0
  18. package/src/Form/Inputs/List/Content.tsx +62 -0
  19. package/src/Form/Inputs/List/List.styles.ts +29 -0
  20. package/src/Form/Inputs/List/List.tsx +58 -0
  21. package/src/Form/Inputs/List/useList.ts +81 -0
  22. package/src/Form/Inputs/Text.tsx +3 -1
  23. package/src/Form/Inputs/index.tsx +3 -1
  24. package/src/Form/Inputs/models.ts +10 -1
  25. package/src/Graph/BarStack/BarStack.cypress.spec.tsx +154 -0
  26. package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
  27. package/src/Graph/BarStack/BarStack.styles.ts +37 -0
  28. package/src/Graph/BarStack/BarStack.tsx +14 -0
  29. package/src/Graph/BarStack/ResponsiveBarStack.tsx +222 -0
  30. package/src/Graph/BarStack/index.ts +1 -0
  31. package/src/Graph/BarStack/models.ts +20 -0
  32. package/src/Graph/BarStack/useResponsiveBarStack.ts +137 -0
  33. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
  34. package/src/Graph/Gauge/Gauge.tsx +1 -1
  35. package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
  36. package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
  37. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
  38. package/src/Graph/Legend/Legend.tsx +21 -0
  39. package/src/Graph/Legend/index.ts +1 -0
  40. package/src/Graph/Legend/models.ts +11 -0
  41. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  42. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  43. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  44. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  45. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  46. package/src/Graph/LineChart/Legend/Legend.styles.ts +1 -1
  47. package/src/Graph/LineChart/Legend/LegendHeader.tsx +1 -1
  48. package/src/Graph/LineChart/Legend/useInteractiveValues.ts +2 -2
  49. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  50. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  51. package/src/Graph/LineChart/helpers/index.ts +1 -1
  52. package/src/Graph/LineChart/index.stories.tsx +4 -2
  53. package/src/Graph/LineChart/index.tsx +1 -1
  54. package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
  55. package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
  56. package/src/Graph/PieChart/PieChart.styles.ts +39 -0
  57. package/src/Graph/PieChart/PieChart.tsx +14 -0
  58. package/src/Graph/PieChart/ResponsivePie.tsx +254 -0
  59. package/src/Graph/PieChart/index.ts +1 -0
  60. package/src/Graph/PieChart/models.ts +20 -0
  61. package/src/Graph/PieChart/useResponsivePie.ts +85 -0
  62. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
  63. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  64. package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
  65. package/src/Graph/Text/Text.stories.tsx +60 -4
  66. package/src/Graph/Text/Text.tsx +1 -1
  67. package/src/Graph/common/testUtils.ts +71 -0
  68. package/src/Graph/common/timeSeries/index.ts +22 -14
  69. package/src/Graph/common/utils.ts +19 -0
  70. package/src/Graph/index.ts +3 -0
  71. package/src/Graph/translatedLabels.ts +1 -0
  72. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  73. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  74. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  75. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  76. package/src/InputField/Select/Autocomplete/Multi/index.tsx +4 -2
  77. package/src/InputField/Select/Autocomplete/index.tsx +129 -116
  78. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  79. package/src/InputField/Select/index.tsx +15 -2
  80. package/src/InputField/Text/index.tsx +2 -2
  81. package/src/Listing/ActionBar/index.tsx +9 -8
  82. package/src/Listing/Cell/DataCell.styles.ts +3 -0
  83. package/src/Listing/Cell/DataCell.tsx +23 -5
  84. package/src/Listing/Header/ListingHeader.tsx +1 -1
  85. package/src/Listing/Listing.cypress.spec.tsx +218 -33
  86. package/src/Listing/Listing.styles.ts +4 -7
  87. package/src/Listing/Row/Row.tsx +7 -3
  88. package/src/Listing/index.stories.tsx +37 -3
  89. package/src/Listing/index.test.tsx +1 -1
  90. package/src/Listing/index.tsx +80 -36
  91. package/src/Listing/models.ts +1 -0
  92. package/src/Listing/useStyleTable.ts +1 -0
  93. package/src/Module/Module.cypress.spec.tsx +129 -0
  94. package/src/Module/index.tsx +2 -4
  95. package/src/PopoverMenu/index.tsx +6 -5
  96. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  97. package/src/SortableItems/index.tsx +2 -7
  98. package/src/ThemeProvider/index.tsx +24 -0
  99. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  100. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  101. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  102. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  103. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  104. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  105. package/src/TimePeriods/helpers/index.ts +1 -1
  106. package/src/TimePeriods/index.stories.tsx +12 -4
  107. package/src/TimePeriods/index.tsx +2 -2
  108. package/src/Typography/FluidTypography/FluidTypography.cypress.spec.tsx +27 -0
  109. package/src/Typography/FluidTypography/index.stories.tsx +2 -2
  110. package/src/Typography/FluidTypography/index.tsx +21 -28
  111. package/src/api/QueryProvider.tsx +1 -1
  112. package/src/api/TestQueryProvider.tsx +1 -1
  113. package/src/api/index.ts +3 -3
  114. package/src/api/useFetchQuery/index.ts +27 -23
  115. package/src/api/useMutationQuery/index.test.ts +4 -4
  116. package/src/api/useMutationQuery/index.ts +60 -25
  117. package/src/components/Button/Icon/IconButton.tsx +6 -2
  118. package/src/components/DataTable/DataListing.tsx +6 -0
  119. package/src/components/DataTable/DataTable.cypress.spec.tsx +174 -0
  120. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  121. package/src/components/DataTable/DataTable.styles.ts +3 -0
  122. package/src/components/DataTable/DataTable.tsx +3 -3
  123. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  124. package/src/components/DataTable/Item/DataTableItem.tsx +4 -60
  125. package/src/components/DataTable/index.ts +3 -1
  126. package/src/components/Form/AccessRights/AccessRights.cypress.spec.tsx +13 -13
  127. package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
  128. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
  129. package/src/components/Form/AccessRights/storiesData.ts +22 -22
  130. package/src/components/Form/Dashboard/DashboardDuplicationForm.tsx +85 -0
  131. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  132. package/src/components/Form/Dashboard/index.ts +1 -0
  133. package/src/components/Form/FormActions.tsx +7 -2
  134. package/src/components/ItemComposition/ItemComposition.styles.ts +2 -2
  135. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  136. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  137. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  138. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  139. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  140. package/src/components/Layout/PageLayout/index.ts +3 -1
  141. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  142. package/src/components/List/Item/ListItem.tsx +3 -3
  143. package/src/components/Modal/ConfirmationModal/ConfirmationModal.cypress.spec.tsx +168 -0
  144. package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +62 -0
  145. package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +87 -0
  146. package/src/components/Modal/Modal.styles.ts +8 -3
  147. package/src/components/Modal/index.ts +2 -0
  148. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  149. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  150. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  151. package/src/index.ts +2 -2
  152. package/src/queryParameters/url/index.ts +5 -1
  153. package/src/utils/index.ts +1 -1
  154. package/src/utils/useFullscreen/Fullscreen.cypress.spec.tsx +1 -3
  155. package/src/utils/useFullscreen/useFullscreenListener.ts +10 -7
  156. package/src/utils/useInfiniteScrollListing.ts +4 -1
  157. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  158. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  159. package/src/utils/usePluralizedTranslation.ts +21 -0
  160. package/src/Typography/FluidTypography/useFluidResizeObserver.ts +0 -56
  161. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  162. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  163. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -140,83 +140,83 @@ export const roles = [
140
140
 
141
141
  export const removedAccessRights = [
142
142
  {
143
- email: 'Virgie14@yahoo.com',
143
+ email: 'Adrienne.Kassulke-Rutherford@gmail.com',
144
144
  id: 1,
145
145
  isContactGroup: false,
146
- name: 'Jody Blanda',
146
+ name: 'Linda Schultz',
147
147
  role: 'editor'
148
148
  },
149
149
  {
150
- email: 'Wyman29@yahoo.com',
150
+ email: 'Merle7@hotmail.com',
151
151
  id: 2,
152
152
  isContactGroup: false,
153
- name: 'Susan Dooley',
153
+ name: 'Lewis Buckridge PhD',
154
154
  role: 'viewer'
155
155
  },
156
156
  {
157
- email: 'Merle.Schamberger@hotmail.com',
157
+ email: 'Linda.Harris37@hotmail.com',
158
158
  id: 3,
159
159
  isContactGroup: false,
160
- name: 'Lewis Buckridge PhD',
160
+ name: "Jodi O'Reilly",
161
161
  role: 'editor'
162
162
  },
163
163
  {
164
- email: 'Rossie6@gmail.com',
164
+ email: 'Louvenia.Torphy@yahoo.com',
165
165
  id: 4,
166
166
  isContactGroup: false,
167
- name: 'Bertha Hane',
167
+ name: 'Mildred Ratke-Stanton',
168
168
  role: 'viewer'
169
169
  },
170
170
  {
171
- email: 'Monty_Nienow47@gmail.com',
171
+ email: 'Kelli.Russel4@hotmail.com',
172
172
  id: 5,
173
173
  isContactGroup: true,
174
- name: 'Mildred Ratke-Stanton',
174
+ name: 'Rudolph Brown',
175
175
  role: 'editor'
176
176
  }
177
177
  ];
178
178
 
179
179
  export const updatedAccessRights = [
180
180
  {
181
- email: 'Jovani.Willms74@gmail.com',
181
+ email: 'Kylie_Wintheiser54@hotmail.com',
182
182
  id: 0,
183
183
  isContactGroup: true,
184
- name: 'Leah McGlynn',
184
+ name: 'Kathy Schmitt',
185
185
  role: 'editor'
186
186
  },
187
187
  {
188
- email: 'Virgie14@yahoo.com',
188
+ email: 'Adrienne.Kassulke-Rutherford@gmail.com',
189
189
  id: 1,
190
190
  isContactGroup: false,
191
- name: 'Jody Blanda',
191
+ name: 'Linda Schultz',
192
192
  role: 'editor'
193
193
  },
194
194
  {
195
- email: 'Wyman29@yahoo.com',
195
+ email: 'Merle7@hotmail.com',
196
196
  id: 2,
197
197
  isContactGroup: false,
198
- name: 'Susan Dooley',
198
+ name: 'Lewis Buckridge PhD',
199
199
  role: 'viewer'
200
200
  },
201
201
  {
202
- email: 'Merle.Schamberger@hotmail.com',
202
+ email: 'Linda.Harris37@hotmail.com',
203
203
  id: 3,
204
204
  isContactGroup: false,
205
- name: 'Lewis Buckridge PhD',
205
+ name: "Jodi O'Reilly",
206
206
  role: 'editor'
207
207
  },
208
208
  {
209
- email: 'Rossie6@gmail.com',
209
+ email: 'Louvenia.Torphy@yahoo.com',
210
210
  id: 4,
211
211
  isContactGroup: false,
212
- name: 'Bertha Hane',
212
+ name: 'Mildred Ratke-Stanton',
213
213
  role: 'viewer'
214
214
  },
215
215
  {
216
- email: 'Monty_Nienow47@gmail.com',
216
+ email: 'Kelli.Russel4@hotmail.com',
217
217
  id: 5,
218
218
  isContactGroup: true,
219
- name: 'Mildred Ratke-Stanton',
219
+ name: 'Rudolph Brown',
220
220
  role: 'editor'
221
221
  }
222
222
  ];
@@ -0,0 +1,85 @@
1
+ import { ReactElement, useCallback, useMemo } from 'react';
2
+
3
+ import { string, object } from 'yup';
4
+ import { useTranslation } from 'react-i18next';
5
+
6
+ import { InputType } from '../../../Form/Inputs/models';
7
+ import { Form, FormProps } from '../../../Form';
8
+ import { FormVariant } from '../Form.models';
9
+ import { FormActions, FormActionsProps } from '../FormActions';
10
+
11
+ import { useStyles } from './DashboardForm.styles';
12
+ import {
13
+ labelMustBeAtLeast,
14
+ labelMustBeMost,
15
+ labelRequired
16
+ } from './translatedLabels';
17
+ import { DashboardResource } from './Dashboard.resource';
18
+
19
+ type DashboardFormProps = {
20
+ labels: DashboardFormLabels;
21
+ name: string;
22
+ onSubmit?: FormProps<DashboardResource>['submit'];
23
+ variant?: FormVariant;
24
+ } & Pick<FormActionsProps, 'onCancel'>;
25
+
26
+ type DashboardFormLabels = {
27
+ actions: FormActionsProps['labels'];
28
+ entity: Required<DashboardResource>;
29
+ };
30
+
31
+ const DashboardDuplicationForm = ({
32
+ variant = 'create',
33
+ labels,
34
+ onSubmit,
35
+ onCancel,
36
+ name
37
+ }: DashboardFormProps): ReactElement => {
38
+ const { classes } = useStyles();
39
+ const { t } = useTranslation();
40
+
41
+ const formProps = useMemo<FormProps<DashboardResource>>(
42
+ () => ({
43
+ initialValues: { name },
44
+ inputs: [
45
+ {
46
+ autoFocus: true,
47
+ fieldName: 'name',
48
+ group: 'main',
49
+ label: labels?.entity?.name,
50
+ required: true,
51
+ type: InputType.Text
52
+ }
53
+ ],
54
+ submit: (values, bag) => onSubmit?.(values, bag),
55
+ validationSchema: object({
56
+ name: string()
57
+ .label(labels?.entity?.name)
58
+ .min(3, ({ min, label }) => t(labelMustBeAtLeast, { label, min }))
59
+ .max(50, ({ max, label }) => t(labelMustBeMost, { label, max }))
60
+ .required(t(labelRequired) as string)
61
+ })
62
+ }),
63
+ [labels, onSubmit]
64
+ );
65
+
66
+ const Actions = useCallback(
67
+ () => (
68
+ <FormActions<DashboardResource>
69
+ enableSubmitWhenNotDirty
70
+ labels={labels?.actions}
71
+ variant={variant}
72
+ onCancel={onCancel}
73
+ />
74
+ ),
75
+ [labels, onCancel, variant]
76
+ );
77
+
78
+ return (
79
+ <div className={classes.dashboardForm}>
80
+ <Form<DashboardResource> {...formProps} Buttons={Actions} />
81
+ </div>
82
+ );
83
+ };
84
+
85
+ export { DashboardDuplicationForm };
@@ -1,7 +1,8 @@
1
1
  import { ReactElement, useCallback, useMemo } from 'react';
2
2
 
3
- import * as Yup from 'yup';
3
+ import { string, number, object } from 'yup';
4
4
  import { useTranslation } from 'react-i18next';
5
+ import { equals } from 'ramda';
5
6
 
6
7
  import { InputType } from '../../../Form/Inputs/models';
7
8
  import { Form, FormProps } from '../../../Form';
@@ -84,8 +85,8 @@ const DashboardForm = ({
84
85
  }
85
86
  ],
86
87
  submit: (values, bag) => onSubmit?.(values, bag),
87
- validationSchema: Yup.object().shape({
88
- description: Yup.string()
88
+ validationSchema: object({
89
+ description: string()
89
90
  .label(labels?.entity?.description || '')
90
91
  .max(
91
92
  180,
@@ -93,17 +94,19 @@ const DashboardForm = ({
93
94
  `${p.label} ${t(labelMustBeMost)} ${p.max} ${t(labelCharacters)}`
94
95
  )
95
96
  .nullable(),
96
- globalRefreshInterval: Yup.object().shape({
97
- interval: Yup.number().when('type', {
98
- is: 'global',
99
- otherwise: Yup.number().nullable(),
100
- then: Yup.number()
101
- .min(1, ({ min }) => t(labelMustBeAtLeast, { min }))
102
- .required(t(labelRequired) as string)
97
+ globalRefreshInterval: object({
98
+ interval: number().when('type', ([type], schema) => {
99
+ if (equals(type, 'manual')) {
100
+ schema
101
+ .min(1, ({ min }) => t(labelMustBeAtLeast, { min }))
102
+ .required(t(labelRequired) as string);
103
+ }
104
+
105
+ return schema.nullable();
103
106
  }),
104
- type: Yup.string()
107
+ type: string()
105
108
  }),
106
- name: Yup.string()
109
+ name: string()
107
110
  .label(labels?.entity?.name)
108
111
  .min(3, ({ min, label }) => t(labelMustBeAtLeast, { label, min }))
109
112
  .max(50, ({ max, label }) => t(labelMustBeMost, { label, max }))
@@ -1,2 +1,3 @@
1
1
  export * from './DashboardForm';
2
2
  export * from './Dashboard.resource';
3
+ export * from './DashboardDuplicationForm';
@@ -8,6 +8,7 @@ import { FormVariant } from './Form.models';
8
8
  import { useStyles } from './Form.styles';
9
9
 
10
10
  export type FormActionsProps = {
11
+ enableSubmitWhenNotDirty?: boolean;
11
12
  labels: FormActionsLabels;
12
13
  onCancel: () => void;
13
14
  variant: FormVariant;
@@ -21,12 +22,16 @@ export type FormActionsLabels = {
21
22
  const FormActions = <TResource extends object>({
22
23
  labels,
23
24
  onCancel,
24
- variant
25
+ variant,
26
+ enableSubmitWhenNotDirty
25
27
  }: FormActionsProps): ReactElement => {
26
28
  const { classes } = useStyles();
27
29
  const { isSubmitting, dirty, isValid, submitForm } =
28
30
  useFormikContext<TResource>();
29
31
 
32
+ const isSubmitDisabled =
33
+ isSubmitting || (!dirty && !enableSubmitWhenNotDirty) || !isValid;
34
+
30
35
  return (
31
36
  <div className={classes.actions}>
32
37
  <Button
@@ -42,7 +47,7 @@ const FormActions = <TResource extends object>({
42
47
  <Button
43
48
  aria-label={labels.submit[variant]}
44
49
  data-testid="submit"
45
- disabled={isSubmitting || !dirty || !isValid}
50
+ disabled={isSubmitDisabled}
46
51
  size="medium"
47
52
  type="submit"
48
53
  variant="primary"
@@ -52,8 +52,8 @@ export const useItemStyles = makeStyles()((theme) => ({
52
52
  width: '100%'
53
53
  },
54
54
  itemContent: {
55
- display: 'flex',
56
- flexDirection: 'row',
55
+ display: 'grid',
56
+ gridAutoFlow: 'column',
57
57
  width: '100%'
58
58
  },
59
59
  visibilityHiden: {
@@ -14,7 +14,7 @@ export const PageLayout = ({
14
14
  const { classes } = useStyles();
15
15
 
16
16
  return (
17
- <section className={classes.pageLayout} data-variant={variant}>
17
+ <section className={classes.pageLayout} data-variant={variant} id="page">
18
18
  {children}
19
19
  </section>
20
20
  );
@@ -17,6 +17,7 @@ export const PageLayoutActions = ({
17
17
  <section
18
18
  className={classes.pageLayoutActions}
19
19
  data-row-reverse={rowReverse}
20
+ id="actions"
20
21
  >
21
22
  {children}
22
23
  </section>
@@ -17,6 +17,7 @@ export const PageLayoutBody = ({
17
17
  <section
18
18
  className={classes.pageLayoutBody}
19
19
  data-has-background={hasBackground}
20
+ id="page-body"
20
21
  >
21
22
  {children}
22
23
  </section>
@@ -11,5 +11,9 @@ export const PageLayoutHeader = ({
11
11
  }: PageLayoutHeaderProps): JSX.Element => {
12
12
  const { classes } = useStyles();
13
13
 
14
- return <header className={classes.pageLayoutHeader}>{children}</header>;
14
+ return (
15
+ <header className={classes.pageLayoutHeader} id="header">
16
+ {children}
17
+ </header>
18
+ );
15
19
  };
@@ -0,0 +1,76 @@
1
+ import { useTranslation } from 'react-i18next';
2
+
3
+ import AddIcon from '@mui/icons-material/Add';
4
+ import ArrowBackIcon from '@mui/icons-material/ArrowBack';
5
+
6
+ import { Button, Menu } from '../..';
7
+
8
+ interface NamedEntity {
9
+ id: number | string;
10
+ name: string;
11
+ }
12
+
13
+ type Props = {
14
+ create: () => void;
15
+ elements: Array<NamedEntity>;
16
+ goBack: () => void;
17
+ isActive: (id: number | string) => boolean;
18
+ labels: {
19
+ create: string;
20
+ goBack: string;
21
+ };
22
+ navigateToElement: (id: number | string) => () => void;
23
+ };
24
+
25
+ export const PageQuickAccess = ({
26
+ elements,
27
+ isActive,
28
+ navigateToElement,
29
+ goBack,
30
+ create,
31
+ labels
32
+ }: Props): JSX.Element => {
33
+ const { t } = useTranslation();
34
+
35
+ return (
36
+ <Menu>
37
+ <Menu.Button data-testid="quickaccess" />
38
+ <Menu.Items>
39
+ {elements &&
40
+ elements.map((element) => (
41
+ <Menu.Item
42
+ key={`${element.id}`}
43
+ onClick={navigateToElement(element.id)}
44
+ {...(isActive(element.id) && {
45
+ isActive: true,
46
+ isDisabled: true
47
+ })}
48
+ >
49
+ {element.name}
50
+ </Menu.Item>
51
+ ))}
52
+ <Menu.Divider key="divider" />
53
+ <Menu.Item key="create">
54
+ <>
55
+ <Button
56
+ icon={<ArrowBackIcon />}
57
+ iconVariant="start"
58
+ variant="ghost"
59
+ onClick={goBack}
60
+ >
61
+ {t(labels.goBack)}
62
+ </Button>
63
+ <Button
64
+ icon={<AddIcon />}
65
+ iconVariant="start"
66
+ variant="secondary"
67
+ onClick={create}
68
+ >
69
+ {t(labels.create)}
70
+ </Button>
71
+ </>
72
+ </Menu.Item>
73
+ </Menu.Items>
74
+ </Menu>
75
+ );
76
+ };
@@ -2,9 +2,11 @@ import { PageLayout as PageLayoutRoot } from './PageLayout';
2
2
  import { PageLayoutHeader } from './PageLayoutHeader';
3
3
  import { PageLayoutBody } from './PageLayoutBody';
4
4
  import { PageLayoutActions } from './PageLayoutActions';
5
+ import { PageQuickAccess } from './PageQuickAccess';
5
6
 
6
7
  export const PageLayout = Object.assign(PageLayoutRoot, {
7
8
  Actions: PageLayoutActions,
8
9
  Body: PageLayoutBody,
9
- Header: PageLayoutHeader
10
+ Header: PageLayoutHeader,
11
+ QuickAccess: PageQuickAccess
10
12
  });
@@ -0,0 +1,66 @@
1
+ import { T } from 'ramda';
2
+
3
+ import { PageHeader } from '..';
4
+
5
+ import { AreaIndicator } from './AreaIndicator';
6
+
7
+ import { PageLayout } from '.';
8
+
9
+ const initialize = (): void => {
10
+ cy.mount({
11
+ Component: (
12
+ <PageLayout>
13
+ <PageLayout.Header>
14
+ <PageHeader>
15
+ <PageHeader.Menu>
16
+ <PageLayout.QuickAccess
17
+ create={cy.stub()}
18
+ elements={[
19
+ {
20
+ id: 1,
21
+ name: 'Entity'
22
+ }
23
+ ]}
24
+ goBack={cy.stub()}
25
+ isActive={T}
26
+ labels={{
27
+ create: 'Create',
28
+ goBack: 'Go back'
29
+ }}
30
+ navigateToElement={cy.stub()}
31
+ />
32
+ </PageHeader.Menu>
33
+ <PageHeader.Main>
34
+ <PageHeader.Title description="Description" title="Title" />
35
+ </PageHeader.Main>
36
+ <PageHeader.Actions>Actions</PageHeader.Actions>
37
+ </PageHeader>
38
+ </PageLayout.Header>
39
+ <PageLayout.Body>
40
+ <PageLayout.Actions>
41
+ <AreaIndicator name="Body actions" />
42
+ </PageLayout.Actions>
43
+ <h1>Content</h1>
44
+ </PageLayout.Body>
45
+ </PageLayout>
46
+ )
47
+ });
48
+ };
49
+
50
+ describe('Page layout', () => {
51
+ beforeEach(initialize);
52
+
53
+ it('displays the page layout', () => {
54
+ cy.makeSnapshot();
55
+ });
56
+
57
+ it('opens the quick access poppin when the corresponding logo is displayed', () => {
58
+ cy.findByTestId('quickaccess').click();
59
+
60
+ cy.contains('Entity').should('be.visible');
61
+ cy.contains('Create').should('be.visible');
62
+ cy.contains('Go back').should('be.visible');
63
+
64
+ cy.makeSnapshot();
65
+ });
66
+ });
@@ -1,10 +1,10 @@
1
1
  import { ForwardedRef, forwardRef, ReactElement, ReactNode } from 'react';
2
2
 
3
- import { ListItem as MuiListItem } from '@mui/material';
3
+ import { ListItemProps, ListItem as MuiListItem } from '@mui/material';
4
4
 
5
5
  import { useStyles } from './ListItem.styles';
6
6
 
7
- type ListItemProps = {
7
+ type Props = {
8
8
  action?: ReactElement;
9
9
  children: ReactNode | Array<ReactNode>;
10
10
  className?: string;
@@ -12,7 +12,7 @@ type ListItemProps = {
12
12
 
13
13
  export const ListItem = forwardRef(
14
14
  (
15
- { action, children, className, ...attr }: ListItemProps,
15
+ { action, children, className, ...attr }: Props & ListItemProps,
16
16
  ref?: ForwardedRef<HTMLLIElement>
17
17
  ) => {
18
18
  const { classes, cx } = useStyles();
@@ -0,0 +1,168 @@
1
+ import { atom, createStore, useSetAtom, Provider } from 'jotai';
2
+
3
+ import { Button } from '@mui/material';
4
+
5
+ import { ConfirmationModal, ConfirmationModalProps } from './ConfirmationModal';
6
+
7
+ const testAtom = atom<string | null>(null);
8
+
9
+ const buttonLabel = 'Click to open modal';
10
+
11
+ const TestComponent = (args: ConfirmationModalProps<string>): JSX.Element => {
12
+ const setAtom = useSetAtom(testAtom);
13
+
14
+ return (
15
+ <>
16
+ <Button onClick={() => setAtom('John')}>{buttonLabel}</Button>
17
+ <ConfirmationModal<string> {...args} />
18
+ </>
19
+ );
20
+ };
21
+
22
+ const staticLabels = {
23
+ cancel: 'Cancel',
24
+ confirm: 'Confirm',
25
+ description: 'Description',
26
+ title: 'Title'
27
+ };
28
+
29
+ const dynamicLabels = {
30
+ cancel: 'Cancel',
31
+ confirm: 'Confirm',
32
+ description: (data) => `Description ${data}`,
33
+ title: (data) => `Hello ${data}`
34
+ };
35
+
36
+ const initialize = (
37
+ props: Pick<
38
+ ConfirmationModalProps<string>,
39
+ 'labels' | 'disabled' | 'hasCloseButton' | 'isDanger'
40
+ >
41
+ ): { cancel; confirm } => {
42
+ const store = createStore();
43
+
44
+ const cancel = cy.stub();
45
+ const confirm = cy.stub();
46
+
47
+ cy.mount({
48
+ Component: (
49
+ <Provider store={store}>
50
+ <TestComponent
51
+ {...props}
52
+ atom={testAtom}
53
+ onCancel={cancel}
54
+ onConfirm={confirm}
55
+ />
56
+ </Provider>
57
+ )
58
+ });
59
+
60
+ return {
61
+ cancel,
62
+ confirm
63
+ };
64
+ };
65
+
66
+ describe('Confirmation modal', () => {
67
+ it('displays the modal with static labels', () => {
68
+ initialize({ labels: staticLabels });
69
+
70
+ cy.contains(buttonLabel).click();
71
+
72
+ cy.contains('Title').should('be.visible');
73
+ cy.contains('Description').should('be.visible');
74
+ cy.findByLabelText('close').should('be.visible');
75
+
76
+ cy.makeSnapshot();
77
+ });
78
+
79
+ it('displays the modal with dynamic labels', () => {
80
+ initialize({ labels: dynamicLabels });
81
+
82
+ cy.contains(buttonLabel).click();
83
+
84
+ cy.contains('Hello John').should('be.visible');
85
+ cy.contains('Description John').should('be.visible');
86
+
87
+ cy.makeSnapshot();
88
+ });
89
+
90
+ it('displays the confirm button as disabled when a prop is set', () => {
91
+ initialize({ disabled: true, labels: staticLabels });
92
+
93
+ cy.contains(buttonLabel).click();
94
+
95
+ cy.contains('Confirm').should('be.disabled');
96
+
97
+ cy.makeSnapshot();
98
+ });
99
+
100
+ it('displays the confirm button as danger when a prop is set', () => {
101
+ initialize({ isDanger: true, labels: staticLabels });
102
+
103
+ cy.contains(buttonLabel).click();
104
+
105
+ cy.contains('Confirm').should('have.attr', 'data-is-danger', 'true');
106
+
107
+ cy.makeSnapshot();
108
+ });
109
+
110
+ it('displays the modal without the close button when a prop is set', () => {
111
+ initialize({ hasCloseButton: false, labels: staticLabels });
112
+
113
+ cy.contains(buttonLabel).click();
114
+
115
+ cy.findByLabelText('close').should('not.exist');
116
+
117
+ cy.makeSnapshot();
118
+ });
119
+
120
+ it('closes the modal when the close button is clicked', () => {
121
+ initialize({ labels: staticLabels });
122
+
123
+ cy.contains(buttonLabel).click();
124
+
125
+ cy.contains('Title').should('be.visible');
126
+
127
+ cy.findByLabelText('close').click();
128
+
129
+ cy.contains('Title').should('not.exist');
130
+
131
+ cy.makeSnapshot();
132
+ });
133
+
134
+ it('closes the modal when the cancel button is clicked', () => {
135
+ const { cancel } = initialize({ labels: staticLabels });
136
+
137
+ cy.contains(buttonLabel).click();
138
+
139
+ cy.contains('Title').should('be.visible');
140
+
141
+ cy.contains('Cancel')
142
+ .click()
143
+ .then(() => {
144
+ expect(cancel).to.be.calledWith('John');
145
+ });
146
+
147
+ cy.contains('Title').should('not.exist');
148
+
149
+ cy.makeSnapshot();
150
+ });
151
+ it('closes the modal when the confirm button is clicked', () => {
152
+ const { confirm } = initialize({ labels: staticLabels });
153
+
154
+ cy.contains(buttonLabel).click();
155
+
156
+ cy.contains('Title').should('be.visible');
157
+
158
+ cy.contains('Confirm')
159
+ .click()
160
+ .then(() => {
161
+ expect(confirm).to.be.calledWith('John');
162
+ });
163
+
164
+ cy.contains('Title').should('not.exist');
165
+
166
+ cy.makeSnapshot();
167
+ });
168
+ });