@centreon/ui 24.4.48 → 24.4.49

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 (137) hide show
  1. package/package.json +36 -29
  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/Confirm/index.tsx +10 -2
  11. package/src/Dialog/index.tsx +9 -2
  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/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/BarStack/BarStack.cypress.spec.tsx +154 -0
  22. package/src/Graph/BarStack/BarStack.stories.tsx +123 -0
  23. package/src/Graph/BarStack/BarStack.styles.ts +36 -0
  24. package/src/Graph/BarStack/BarStack.tsx +14 -0
  25. package/src/Graph/BarStack/ResponsiveBarStack.tsx +208 -0
  26. package/src/Graph/BarStack/index.ts +1 -0
  27. package/src/Graph/BarStack/models.ts +19 -0
  28. package/src/Graph/BarStack/useResponsiveBarStack.ts +139 -0
  29. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +102 -0
  30. package/src/Graph/Gauge/Gauge.tsx +1 -1
  31. package/src/Graph/HeatMap/HeatMap.cypress.spec.tsx +145 -0
  32. package/src/Graph/HeatMap/HeatMap.stories.tsx +0 -25
  33. package/src/Graph/HeatMap/ResponsiveHeatMap.tsx +8 -2
  34. package/src/Graph/Legend/Legend.tsx +21 -0
  35. package/src/Graph/Legend/index.ts +1 -0
  36. package/src/Graph/Legend/models.ts +11 -0
  37. package/src/Graph/LineChart/BasicComponents/Lines/Threshold/Circle.tsx +2 -2
  38. package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +2 -2
  39. package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +1 -1
  40. package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/GuidingLines.tsx +2 -2
  41. package/src/Graph/LineChart/InteractiveComponents/Annotations/EventAnnotations.tsx +1 -1
  42. package/src/Graph/LineChart/Legend/Legend.styles.ts +1 -1
  43. package/src/Graph/LineChart/Legend/LegendHeader.tsx +1 -1
  44. package/src/Graph/LineChart/Legend/useInteractiveValues.ts +2 -2
  45. package/src/Graph/LineChart/Legend/useLegend.ts +3 -3
  46. package/src/Graph/LineChart/helpers/doc.ts +16 -13
  47. package/src/Graph/LineChart/helpers/index.ts +1 -1
  48. package/src/Graph/LineChart/index.stories.tsx +4 -2
  49. package/src/Graph/LineChart/index.tsx +1 -1
  50. package/src/Graph/PieChart/PieChart.cypress.spec.tsx +169 -0
  51. package/src/Graph/PieChart/PieChart.stories.tsx +194 -0
  52. package/src/Graph/PieChart/PieChart.styles.ts +39 -0
  53. package/src/Graph/PieChart/PieChart.tsx +14 -0
  54. package/src/Graph/PieChart/ResponsivePie.tsx +251 -0
  55. package/src/Graph/PieChart/index.ts +1 -0
  56. package/src/Graph/PieChart/models.ts +19 -0
  57. package/src/Graph/PieChart/useResponsivePie.ts +86 -0
  58. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +121 -0
  59. package/src/Graph/SingleBar/Thresholds.tsx +2 -2
  60. package/src/Graph/Text/Text.cypress.spec.tsx +101 -0
  61. package/src/Graph/Text/Text.stories.tsx +60 -4
  62. package/src/Graph/Text/Text.tsx +1 -1
  63. package/src/Graph/common/testUtils.ts +71 -0
  64. package/src/Graph/common/timeSeries/index.ts +22 -14
  65. package/src/Graph/common/utils.ts +19 -0
  66. package/src/Graph/index.ts +3 -0
  67. package/src/Graph/translatedLabels.ts +1 -0
  68. package/src/InputField/Select/Autocomplete/Connected/index.tsx +10 -7
  69. package/src/InputField/Select/Autocomplete/Draggable/SortableList.tsx +1 -1
  70. package/src/InputField/Select/Autocomplete/Draggable/SortableListContent.tsx +1 -1
  71. package/src/InputField/Select/Autocomplete/Draggable/index.tsx +1 -1
  72. package/src/InputField/Select/Autocomplete/index.tsx +121 -115
  73. package/src/InputField/Select/IconPopover/index.tsx +2 -2
  74. package/src/InputField/Select/index.tsx +1 -1
  75. package/src/InputField/Text/index.tsx +2 -2
  76. package/src/Listing/ActionBar/index.tsx +9 -8
  77. package/src/Listing/Cell/DataCell.styles.ts +3 -0
  78. package/src/Listing/Cell/DataCell.tsx +23 -5
  79. package/src/Listing/Header/ListingHeader.tsx +1 -1
  80. package/src/Listing/Listing.cypress.spec.tsx +80 -4
  81. package/src/Listing/Listing.styles.ts +4 -7
  82. package/src/Listing/index.stories.tsx +37 -3
  83. package/src/Listing/index.test.tsx +1 -1
  84. package/src/Listing/index.tsx +4 -3
  85. package/src/Listing/models.ts +1 -0
  86. package/src/Module/Module.cypress.spec.tsx +129 -0
  87. package/src/Module/index.tsx +2 -4
  88. package/src/RichTextEditor/RichTextEditor.tsx +12 -1
  89. package/src/SortableItems/index.tsx +2 -7
  90. package/src/ThemeProvider/index.tsx +24 -0
  91. package/src/TimePeriods/CustomTimePeriod/CompactCustomTimePeriod.styles.ts +6 -7
  92. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -3
  93. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -2
  94. package/src/TimePeriods/DateTimePickerInput.tsx +56 -19
  95. package/src/TimePeriods/ResolutionTimePeriod.cypress.spec.tsx +12 -9
  96. package/src/TimePeriods/TimePeriods.cypress.spec.tsx +9 -33
  97. package/src/TimePeriods/helpers/index.ts +1 -1
  98. package/src/TimePeriods/index.stories.tsx +12 -4
  99. package/src/TimePeriods/index.tsx +2 -2
  100. package/src/api/QueryProvider.tsx +1 -1
  101. package/src/api/TestQueryProvider.tsx +1 -1
  102. package/src/api/useFetchQuery/index.ts +27 -23
  103. package/src/api/useMutationQuery/index.test.ts +4 -4
  104. package/src/api/useMutationQuery/index.ts +60 -25
  105. package/src/components/Button/Icon/IconButton.tsx +6 -2
  106. package/src/components/DataTable/DataListing.tsx +6 -0
  107. package/src/components/DataTable/DataTable.cypress.spec.tsx +193 -0
  108. package/src/components/DataTable/DataTable.stories.tsx +40 -0
  109. package/src/components/DataTable/DataTable.styles.ts +3 -0
  110. package/src/components/DataTable/DataTable.tsx +3 -3
  111. package/src/components/DataTable/Item/DataTableItem.styles.ts +7 -2
  112. package/src/components/DataTable/Item/DataTableItem.tsx +4 -4
  113. package/src/components/DataTable/index.ts +3 -1
  114. package/src/components/Form/AccessRights/ShareInput/ContactSwitch.tsx +3 -3
  115. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +1 -0
  116. package/src/components/Form/Dashboard/DashboardForm.tsx +15 -12
  117. package/src/components/Layout/PageLayout/PageLayout.tsx +1 -1
  118. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +1 -0
  119. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +1 -0
  120. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -1
  121. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +76 -0
  122. package/src/components/Layout/PageLayout/index.ts +3 -1
  123. package/src/components/Layout/PageLayout.cypress.spec.tsx +66 -0
  124. package/src/components/Modal/Modal.styles.ts +1 -1
  125. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +3 -3
  126. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +1 -1
  127. package/src/components/Tooltip/ConfirmationTooltip/models.ts +1 -1
  128. package/src/index.ts +2 -2
  129. package/src/queryParameters/url/index.ts +5 -1
  130. package/src/utils/index.ts +1 -1
  131. package/src/utils/useFullscreen/useFullscreenListener.ts +10 -7
  132. package/src/utils/{useLicenseExpirationWarning.cypress.spec.tsx → useLicenseExpirationWarning.test.tsx} +48 -37
  133. package/src/utils/useLicenseExpirationWarning.ts +18 -18
  134. package/src/utils/usePluralizedTranslation.ts +21 -0
  135. package/src/screens/dashboard/DashboardsDetail.stories.tsx +0 -108
  136. package/src/screens/dashboard/DashboardsOverview.stories.tsx +0 -281
  137. package/src/utils/useDateTimePickerAdapter.ts +0 -309
@@ -0,0 +1,193 @@
1
+ import { Box } from '@mui/material';
2
+
3
+ import { ColumnType } from '../../Listing/models';
4
+
5
+ import { DataTable } from '.';
6
+
7
+ const data = Array(5)
8
+ .fill(0)
9
+ .map((_, idx) => ({
10
+ description: `Description ${idx}`,
11
+ id: idx,
12
+ title: `Entity ${idx}`
13
+ }));
14
+
15
+ const initializeDataTableGrid = ({
16
+ hasActions,
17
+ hasCardAction,
18
+ canDelete
19
+ }): void => {
20
+ cy.viewport(1200, 590);
21
+ cy.mount({
22
+ Component: (
23
+ <DataTable variant="grid">
24
+ {data.map(({ title, description }) => (
25
+ <DataTable.Item
26
+ description={description}
27
+ hasActions={hasActions}
28
+ hasCardAction={hasCardAction}
29
+ key={title}
30
+ labelsDelete={{
31
+ cancel: 'Cancel',
32
+ confirm: {
33
+ label: 'Delete'
34
+ }
35
+ }}
36
+ title={title}
37
+ onDelete={canDelete ? cy.stub() : undefined}
38
+ />
39
+ ))}
40
+ </DataTable>
41
+ )
42
+ });
43
+ };
44
+
45
+ const initializeDataTableEmpty = (canCreate = false): void => {
46
+ cy.viewport(1200, 590);
47
+ cy.mount({
48
+ Component: (
49
+ <DataTable isEmpty variant="grid">
50
+ <DataTable.EmptyState
51
+ canCreate={canCreate}
52
+ labels={{
53
+ actions: {
54
+ create: 'Create'
55
+ },
56
+ title: 'Welcome'
57
+ }}
58
+ onCreate={cy.stub()}
59
+ />
60
+ </DataTable>
61
+ )
62
+ });
63
+ };
64
+
65
+ const initializeDataTableListing = (): void => {
66
+ cy.viewport(1200, 590);
67
+ cy.mount({
68
+ Component: (
69
+ <Box sx={{ height: '100vh' }}>
70
+ <DataTable variant="listing">
71
+ <DataTable.Listing
72
+ columns={[
73
+ {
74
+ getFormattedString: (row) => row.title,
75
+ id: 'title',
76
+ label: 'Title',
77
+ type: ColumnType.string
78
+ },
79
+ {
80
+ getFormattedString: (row) => row.description,
81
+ id: 'description',
82
+ label: 'Description',
83
+ type: ColumnType.string
84
+ }
85
+ ]}
86
+ rows={data}
87
+ />
88
+ </DataTable>
89
+ </Box>
90
+ )
91
+ });
92
+ };
93
+
94
+ describe('DataTable: Grid', () => {
95
+ it('displays items with title and description only', () => {
96
+ initializeDataTableGrid({
97
+ canDelete: false,
98
+ hasActions: false,
99
+ hasCardAction: false
100
+ });
101
+
102
+ data.forEach(({ title, description }) => {
103
+ cy.contains(title).should('be.visible');
104
+ cy.contains(description).should('be.visible');
105
+ });
106
+
107
+ cy.makeSnapshot();
108
+ });
109
+
110
+ it('displays items with actions', () => {
111
+ initializeDataTableGrid({
112
+ canDelete: false,
113
+ hasActions: true,
114
+ hasCardAction: false
115
+ });
116
+
117
+ cy.findAllByLabelText('edit access rights').should('have.length', 5);
118
+ cy.findAllByLabelText('edit').should('have.length', 5);
119
+
120
+ cy.makeSnapshot();
121
+ });
122
+
123
+ it('displays items with delete action', () => {
124
+ initializeDataTableGrid({
125
+ canDelete: true,
126
+ hasActions: true,
127
+ hasCardAction: false
128
+ });
129
+
130
+ cy.findAllByLabelText('delete').should('have.length', 5);
131
+
132
+ cy.makeSnapshot();
133
+ });
134
+
135
+ it('displays items with card action only', () => {
136
+ initializeDataTableGrid({
137
+ canDelete: false,
138
+ hasActions: false,
139
+ hasCardAction: true
140
+ });
141
+
142
+ cy.findAllByLabelText('view').should('have.length', 5);
143
+
144
+ cy.makeSnapshot();
145
+ });
146
+
147
+ it('displays items with card action and bottom actions', () => {
148
+ initializeDataTableGrid({
149
+ canDelete: true,
150
+ hasActions: true,
151
+ hasCardAction: true
152
+ });
153
+
154
+ cy.findAllByLabelText('view').should('have.length', 5);
155
+ cy.findAllByLabelText('delete').should('have.length', 5);
156
+ cy.findAllByLabelText('edit access rights').should('have.length', 5);
157
+ cy.findAllByLabelText('edit').should('have.length', 5);
158
+
159
+ cy.makeSnapshot();
160
+ });
161
+ });
162
+
163
+ describe('DataTable: Empty', () => {
164
+ it('displays the title', () => {
165
+ initializeDataTableEmpty();
166
+
167
+ cy.contains('Welcome').should('be.visible');
168
+
169
+ cy.makeSnapshot();
170
+ });
171
+
172
+ it('displays the title and the action button', () => {
173
+ initializeDataTableEmpty(true);
174
+
175
+ cy.contains('Welcome').should('be.visible');
176
+ cy.contains('Create').should('be.visible');
177
+
178
+ cy.makeSnapshot();
179
+ });
180
+ });
181
+
182
+ describe('DataTable: Listing', () => {
183
+ it('displays the listing', () => {
184
+ initializeDataTableListing();
185
+
186
+ data.forEach(({ title, description }) => {
187
+ cy.contains(title).should('be.visible');
188
+ cy.contains(description).should('be.visible');
189
+ });
190
+
191
+ cy.makeSnapshot();
192
+ });
193
+ });
@@ -1,5 +1,9 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
 
3
+ import { Box } from '@mui/material';
4
+
5
+ import { ColumnType } from '../../Listing/models';
6
+
3
7
  import { DataTable } from './index';
4
8
 
5
9
  const meta: Meta<typeof DataTable> = {
@@ -49,3 +53,39 @@ export const withFixedHeightContainer: Story = {
49
53
  </div>
50
54
  )
51
55
  };
56
+
57
+ const ListingTemplate = (args): JSX.Element => (
58
+ <Box sx={{ height: '80vh' }}>
59
+ <DataTable {...args} />
60
+ </Box>
61
+ );
62
+
63
+ export const listing: Story = {
64
+ args: {
65
+ children: (
66
+ <DataTable.Listing
67
+ columns={[
68
+ {
69
+ getFormattedString: (row) => row.title,
70
+ id: 'title',
71
+ label: 'Title',
72
+ type: ColumnType.string
73
+ },
74
+ {
75
+ getFormattedString: (row) => row.description,
76
+ id: 'description',
77
+ label: 'Description',
78
+ type: ColumnType.string
79
+ }
80
+ ]}
81
+ rows={[...Array(5)].map((_, i) => ({
82
+ description: `Item description ${i}`,
83
+ id: i,
84
+ title: `Item ${i}`
85
+ }))}
86
+ />
87
+ ),
88
+ variant: 'listing'
89
+ },
90
+ render: ListingTemplate
91
+ };
@@ -10,6 +10,9 @@ const useStyles = makeStyles()((theme) => ({
10
10
  gridGap: theme.spacing(2.5),
11
11
  gridTemplateColumns: `repeat(auto-fill, ${theme.spacing(45)})`
12
12
  },
13
+ '&[data-variant="listing"]': {
14
+ height: '100%'
15
+ },
13
16
  '&[data-variant][data-is-empty="true"]': {
14
17
  display: 'flex',
15
18
  justifyContent: 'center',
@@ -1,11 +1,11 @@
1
- import React, { ReactElement, ReactNode } from 'react';
1
+ import { ReactElement, ReactNode } from 'react';
2
2
 
3
3
  import { useStyles } from './DataTable.styles';
4
4
 
5
5
  type DataTableProps = {
6
- children: ReactNode | Array<ReactNode>;
6
+ children?: ReactNode | Array<ReactNode>;
7
7
  isEmpty?: boolean;
8
- variant?: 'grid';
8
+ variant: 'grid' | 'listing';
9
9
  };
10
10
 
11
11
  /** *
@@ -1,13 +1,18 @@
1
1
  import { makeStyles } from 'tss-react/mui';
2
2
 
3
3
  const useStyles = makeStyles()((theme) => ({
4
+ actions: {
5
+ display: 'flex',
6
+ flexDirection: 'row',
7
+ justifyContent: 'space-between'
8
+ },
4
9
  dataTableItem: {
5
10
  '& .MuiCardActionArea-root': {
6
11
  alignItems: 'flex-start',
7
12
  display: 'flex',
8
13
  flexDirection: 'column',
9
14
  height: '100%',
10
- justifyContent: 'space-between'
15
+ justifyContent: 'flex-start'
11
16
  },
12
17
  '& .MuiCardActions-root': {
13
18
  '& > span': {
@@ -15,13 +20,13 @@ const useStyles = makeStyles()((theme) => ({
15
20
  gap: theme.spacing(1)
16
21
  },
17
22
  display: 'flex',
18
-
19
23
  justifyContent: 'space-between'
20
24
  },
21
25
  borderRadius: theme.shape.borderRadius,
22
26
  display: 'flex',
23
27
  flexDirection: 'column',
24
28
  height: '186px',
29
+ justifyContent: 'space-between',
25
30
  p: {
26
31
  color: theme.palette.text.secondary,
27
32
  letterSpacing: '0',
@@ -22,7 +22,7 @@ export interface DataTableItemProps {
22
22
  description?: string;
23
23
  hasActions?: boolean;
24
24
  hasCardAction?: boolean;
25
- labelsDelete: {
25
+ labelsDelete?: {
26
26
  cancel: string;
27
27
  confirm: {
28
28
  label: string;
@@ -76,20 +76,20 @@ const DataTableItem = forwardRef(
76
76
  {hasActions && (
77
77
  <MuiCardActions>
78
78
  <span>
79
- {onDelete && (
79
+ {onDelete && labelsDelete && (
80
80
  <ConfirmationTooltip
81
81
  confirmVariant="error"
82
82
  labels={labelsDelete}
83
83
  onConfirm={onDelete}
84
84
  >
85
- {(openTooltip) => (
85
+ {({ toggleTooltip }) => (
86
86
  <IconButton
87
87
  aria-label="delete"
88
88
  data-testid="delete"
89
89
  icon={<DeleteIcon />}
90
90
  size="small"
91
91
  variant="ghost"
92
- onClick={openTooltip}
92
+ onClick={toggleTooltip}
93
93
  />
94
94
  )}
95
95
  </ConfirmationTooltip>
@@ -2,9 +2,11 @@ import { DataTable as DataTableRoot } from './DataTable';
2
2
  import { DataTableItem } from './Item/DataTableItem';
3
3
  import { DataTableItemSkeleton } from './Item/DataTableItemSkeleton';
4
4
  import { DataTableEmptyState } from './EmptyState/DataTableEmptyState';
5
+ import { DataListing } from './DataListing';
5
6
 
6
7
  export const DataTable = Object.assign(DataTableRoot, {
7
8
  EmptyState: DataTableEmptyState,
8
9
  Item: DataTableItem,
9
- ItemSkeleton: DataTableItemSkeleton
10
+ ItemSkeleton: DataTableItemSkeleton,
11
+ Listing: DataListing
10
12
  });
@@ -1,4 +1,4 @@
1
- import { useSetAtom } from 'jotai';
1
+ import { useAtom } from 'jotai';
2
2
  import { useTranslation } from 'react-i18next';
3
3
 
4
4
  import { FormControlLabel, Radio, RadioGroup } from '@mui/material';
@@ -17,7 +17,7 @@ const ContactSwitch = ({ labels }: Props): JSX.Element => {
17
17
  const { classes } = useContactSwitchStyles();
18
18
  const { t } = useTranslation();
19
19
 
20
- const setContactType = useSetAtom(contactTypeAtom);
20
+ const [contactType, setContactType] = useAtom(contactTypeAtom);
21
21
 
22
22
  const change = (event: React.ChangeEvent<HTMLInputElement>): void => {
23
23
  setContactType(event.target.value as ContactType);
@@ -29,7 +29,7 @@ const ContactSwitch = ({ labels }: Props): JSX.Element => {
29
29
  <RadioGroup
30
30
  row
31
31
  className={classes.inputs}
32
- defaultValue={ContactType.Contact}
32
+ value={contactType}
33
33
  onChange={change}
34
34
  >
35
35
  <FormControlLabel
@@ -52,6 +52,7 @@ const ShareInput = ({ labels, endpoints, roles }: Props): JSX.Element => {
52
52
  ? t(labels.autocompleteContactGroup)
53
53
  : t(labels.autocompleteContact)
54
54
  )}
55
+ queryKey={isContactGroup ? labels.contactGroup : labels.contact}
55
56
  renderOption={renderOption}
56
57
  value={selectedContact}
57
58
  onChange={selectContact}
@@ -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 }))
@@ -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
+ });
@@ -57,7 +57,7 @@ const useStyles = makeStyles<{
57
57
  flexDirection: 'row',
58
58
  gap: theme.spacing(2),
59
59
  justifyContent: 'flex-end',
60
- right: theme.spacing(2)
60
+ right: theme.spacing(2.5)
61
61
  },
62
62
  modalBody: {
63
63
  '& > p': {
@@ -15,7 +15,7 @@ type Story = StoryObj<typeof ConfirmationTooltip>;
15
15
 
16
16
  export const Default: Story = {
17
17
  args: {
18
- children: (toggleTooltip) => (
18
+ children: ({ toggleTooltip }) => (
19
19
  <IconButton icon={<SaveIcon />} onClick={toggleTooltip} />
20
20
  ),
21
21
  labels: {
@@ -30,7 +30,7 @@ export const Default: Story = {
30
30
 
31
31
  export const WithConfirmVariant: Story = {
32
32
  args: {
33
- children: (toggleTooltip) => (
33
+ children: ({ toggleTooltip }) => (
34
34
  <IconButton icon={<DeleteIcon color="error" />} onClick={toggleTooltip} />
35
35
  ),
36
36
  confirmVariant: 'error',
@@ -46,7 +46,7 @@ export const WithConfirmVariant: Story = {
46
46
 
47
47
  export const WithSecondaryLabel: Story = {
48
48
  args: {
49
- children: (toggleTooltip) => (
49
+ children: ({ toggleTooltip }) => (
50
50
  <IconButton icon={<DeleteIcon color="error" />} onClick={toggleTooltip} />
51
51
  ),
52
52
  confirmVariant: 'error',
@@ -49,7 +49,7 @@ export const ConfirmationTooltip = ({
49
49
  return (
50
50
  <ClickAwayListener onClickAway={close}>
51
51
  <div>
52
- {children(isOpen ? close : open)}
52
+ {children({ isOpen, toggleTooltip: isOpen ? close : open })}
53
53
  <Popper
54
54
  anchorEl={anchorElement}
55
55
  className={classes.popper}