@centreon/ui 25.3.4 → 25.4.0-MON-191119-npm-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 (176) hide show
  1. package/package.json +25 -11
  2. package/public/mockServiceWorker.js +8 -31
  3. package/src/Button/Save/index.stories.tsx +1 -0
  4. package/src/Checkbox/Checkbox.tsx +3 -1
  5. package/src/Checkbox/CheckboxGroup/index.tsx +6 -1
  6. package/src/Colors/index.tsx +1 -1
  7. package/src/Dashboard/Dashboard.styles.ts +1 -1
  8. package/src/Dashboard/Layout.tsx +1 -1
  9. package/src/Dialog/UnsavedChanges/index.stories.tsx +1 -0
  10. package/src/Form/CollapsibleGroup.tsx +13 -13
  11. package/src/Form/Form.cypress.spec.tsx +137 -2
  12. package/src/Form/Form.stories.tsx +11 -31
  13. package/src/Form/Form.tsx +2 -0
  14. package/src/Form/Inputs/Checkbox.tsx +3 -2
  15. package/src/Form/Inputs/ConnectedAutocomplete.tsx +6 -1
  16. package/src/Form/Inputs/Grid.tsx +18 -29
  17. package/src/Form/Inputs/SubGroupDivider.tsx +7 -0
  18. package/src/Form/Inputs/Text.tsx +1 -0
  19. package/src/Form/Inputs/index.tsx +31 -24
  20. package/src/Form/Inputs/models.ts +8 -1
  21. package/src/Form/Section/FormSection.tsx +34 -0
  22. package/src/Form/Section/PanelTabs.tsx +13 -0
  23. package/src/Form/Section/navigateToSection.ts +9 -0
  24. package/src/Form/storiesData.tsx +14 -4
  25. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +46 -6
  26. package/src/Graph/BarChart/BarChart.stories.tsx +60 -0
  27. package/src/Graph/BarChart/BarChart.tsx +56 -32
  28. package/src/Graph/BarChart/BarGroup.tsx +22 -32
  29. package/src/Graph/BarChart/MemoizedGroup.tsx +8 -11
  30. package/src/Graph/BarChart/ResponsiveBarChart.tsx +145 -32
  31. package/src/Graph/BarChart/Tooltip/BarChartTooltip.tsx +2 -2
  32. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +7 -1
  33. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/useStackedLines.ts +18 -45
  34. package/src/Graph/Chart/BasicComponents/Lines/index.tsx +42 -28
  35. package/src/Graph/Chart/Chart.cypress.spec.tsx +85 -15
  36. package/src/Graph/Chart/Chart.stories.tsx +84 -1
  37. package/src/Graph/Chart/Chart.tsx +17 -4
  38. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/RegularAnchorPoint.tsx +8 -2
  39. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/StackedAnchorPoint.tsx +10 -3
  40. package/src/Graph/Chart/InteractiveComponents/AnchorPoint/useTickGraph.ts +19 -2
  41. package/src/Graph/Chart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltip.ts +2 -4
  42. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/index.tsx +14 -3
  43. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/models.ts +3 -0
  44. package/src/Graph/Chart/InteractiveComponents/ZoomPreview/useZoomPreview.ts +12 -10
  45. package/src/Graph/Chart/InteractiveComponents/index.tsx +63 -5
  46. package/src/Graph/Chart/Legend/index.tsx +26 -2
  47. package/src/Graph/Chart/index.tsx +45 -45
  48. package/src/Graph/Chart/models.ts +8 -0
  49. package/src/Graph/Chart/useChartData.ts +14 -2
  50. package/src/Graph/Gauge/Gauge.tsx +18 -14
  51. package/src/Graph/Gauge/ResponsiveGauge.tsx +10 -6
  52. package/src/Graph/Gauge/useResizeObserver.ts +68 -0
  53. package/src/Graph/SingleBar/ResponsiveSingleBar.tsx +18 -16
  54. package/src/Graph/SingleBar/ThresholdLine.tsx +4 -4
  55. package/src/Graph/SingleBar/models.ts +1 -0
  56. package/src/Graph/Text/Text.styles.ts +2 -2
  57. package/src/Graph/Text/Text.tsx +23 -10
  58. package/src/Graph/Timeline/ResponsiveTimeline.tsx +4 -0
  59. package/src/Graph/Timeline/Timeline.tsx +21 -4
  60. package/src/Graph/Tree/Links.tsx +2 -2
  61. package/src/Graph/Tree/Tree.tsx +2 -2
  62. package/src/Graph/Tree/constants.ts +1 -1
  63. package/src/Graph/common/BaseChart/BaseChart.tsx +6 -1
  64. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +5 -4
  65. package/src/Graph/common/BaseChart/Header/index.tsx +3 -1
  66. package/src/Graph/common/BaseChart/useComputeBaseChartDimensions.ts +13 -9
  67. package/src/Graph/common/timeSeries/index.test.ts +20 -0
  68. package/src/Graph/common/timeSeries/index.ts +225 -44
  69. package/src/Graph/common/timeSeries/models.ts +6 -2
  70. package/src/Graph/common/utils.ts +45 -12
  71. package/src/Graph/index.ts +3 -1
  72. package/src/Graph/mockedData/dataWithMissingPoint.json +74 -0
  73. package/src/Graph/mockedData/pingServiceWithStackedKeys.json +205 -0
  74. package/src/Icon/RegexIcon.tsx +20 -0
  75. package/src/Icon/index.ts +1 -0
  76. package/src/InputField/Select/Autocomplete/Connected/Multi/MultiConnectedAutocompleteField.cypress.spec.tsx +68 -14
  77. package/src/InputField/Select/Autocomplete/Connected/index.tsx +49 -14
  78. package/src/InputField/Select/Autocomplete/Multi/Listbox.tsx +78 -0
  79. package/src/InputField/Select/Autocomplete/Multi/Multi.styles.ts +26 -0
  80. package/src/InputField/Select/Autocomplete/Multi/Multi.tsx +124 -0
  81. package/src/InputField/Select/Autocomplete/Multi/index.tsx +1 -117
  82. package/src/InputField/Select/Autocomplete/index.tsx +28 -17
  83. package/src/InputField/Select/Option.tsx +3 -3
  84. package/src/InputField/Select/index.tsx +4 -0
  85. package/src/InputField/Text/index.tsx +4 -2
  86. package/src/InputField/translatedLabels.ts +4 -0
  87. package/src/Listing/ActionBar/Pagination.tsx +10 -23
  88. package/src/Listing/ActionBar/PaginationActions.tsx +1 -10
  89. package/src/Listing/ActionBar/index.tsx +1 -1
  90. package/src/Listing/Cell/DataCell.tsx +6 -6
  91. package/src/Listing/Cell/EllipsisTypography.tsx +10 -32
  92. package/src/Listing/Cell/index.tsx +37 -76
  93. package/src/Listing/Checkbox.tsx +8 -20
  94. package/src/Listing/Header/Cell/ListingHeaderCell.tsx +17 -14
  95. package/src/Listing/Header/Cell/SelectActionListingHeaderCell.tsx +5 -9
  96. package/src/Listing/Header/ListingHeader.tsx +2 -5
  97. package/src/Listing/Header/_internals/Label.tsx +1 -17
  98. package/src/Listing/Row/EmptyRow.tsx +2 -6
  99. package/src/Listing/Row/Row.tsx +7 -36
  100. package/src/Listing/index.stories.tsx +1 -0
  101. package/src/Listing/index.tsx +26 -26
  102. package/src/Listing/useStyleTable.ts +58 -32
  103. package/src/ListingPage/index.stories.tsx +1 -0
  104. package/src/Module/index.tsx +8 -2
  105. package/src/MultiSelectEntries/index.stories.tsx +1 -0
  106. package/src/MultiSelectEntries/index.tsx +1 -1
  107. package/src/Pagination/Pagination.cypress.spec.tsx +137 -0
  108. package/src/Pagination/Pagination.stories.tsx +46 -0
  109. package/src/Pagination/Pagination.styles.ts +56 -0
  110. package/src/Pagination/Pagination.tsx +146 -0
  111. package/src/Pagination/index.ts +3 -0
  112. package/src/Pagination/utils.ts +7 -0
  113. package/src/SortableItems/index.stories.tsx +2 -2
  114. package/src/StoryBookThemeProvider/index.tsx +3 -1
  115. package/src/ThemeProvider/base.css +49 -0
  116. package/src/ThemeProvider/index.tsx +21 -47
  117. package/src/ThemeProvider/palettes.ts +3 -1
  118. package/src/ThemeProvider/tailwindcss.css +230 -0
  119. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +9 -11
  120. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +1 -0
  121. package/src/TimePeriods/DateTimePickerInput.tsx +3 -1
  122. package/src/api/models.ts +9 -0
  123. package/src/api/useGraphQuery/index.ts +108 -12
  124. package/src/components/Avatar/Avatar.stories.tsx +1 -0
  125. package/src/components/Button/Button.module.css +38 -0
  126. package/src/components/Button/Button.stories.tsx +25 -0
  127. package/src/components/Button/Button.tsx +2 -5
  128. package/src/components/CrudPage/CrudPage.stories.tsx +1 -0
  129. package/src/components/CrudPage/CrudPageRoot.tsx +1 -1
  130. package/src/components/DataTable/DataTable.stories.tsx +1 -0
  131. package/src/components/DataTable/EmptyState/DataTableEmptyState.stories.tsx +1 -0
  132. package/src/components/DataTable/EmptyState/DataTableEmptyState.styles.ts +3 -1
  133. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +4 -1
  134. package/src/components/DataTable/Item/DataTableItem.stories.tsx +1 -0
  135. package/src/components/Form/AccessRights/AccessRights.stories.tsx +1 -0
  136. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +4 -3
  137. package/src/components/Form/AccessRights/ShareInput/useShareInput.tsx +15 -10
  138. package/src/components/Form/FormActions.tsx +21 -12
  139. package/src/components/Layout/AreaIndicator.tsx +4 -6
  140. package/src/components/Layout/PageLayout/PageLayout.stories.tsx +1 -0
  141. package/src/components/Layout/PageLayout/PageLayout.tsx +9 -3
  142. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +5 -3
  143. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +5 -3
  144. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +5 -3
  145. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +17 -17
  146. package/src/components/Menu/Button/MenuButton.tsx +6 -6
  147. package/src/components/Menu/MenuDivider.tsx +1 -5
  148. package/src/components/Menu/MenuItem.tsx +1 -5
  149. package/src/components/Menu/MenuItems.tsx +5 -4
  150. package/src/components/Modal/ConfirmationModal/ConfirmationModal.stories.tsx +1 -0
  151. package/src/components/Modal/ConfirmationModal/ConfirmationModal.tsx +4 -1
  152. package/src/components/Modal/Modal.stories.tsx +21 -0
  153. package/src/components/Modal/Modal.styles.ts +1 -19
  154. package/src/components/Modal/Modal.tsx +1 -1
  155. package/src/components/Modal/ModalBody.tsx +6 -4
  156. package/src/components/Modal/ModalHeader.tsx +9 -5
  157. package/src/components/Modal/modal.module.css +16 -0
  158. package/src/components/Tabs/Tab.styles.ts +0 -6
  159. package/src/components/Tabs/Tabs.tsx +37 -15
  160. package/src/index.ts +3 -0
  161. package/src/queryParameters/url/index.ts +7 -2
  162. package/src/utils/index.ts +1 -0
  163. package/src/utils/useLocale/index.ts +9 -0
  164. package/src/utils/useLocale/useLocale.cypress.spec.tsx +38 -0
  165. package/src/utils/useLocaleDateTimeFormat/index.ts +4 -2
  166. package/src/utils/usePluralizedTranslation.ts +2 -3
  167. package/src/Listing/Cell/DataCell.styles.ts +0 -27
  168. package/src/Listing/Header/Cell/ListingHeaderCell.styles.ts +0 -71
  169. package/src/Listing/Header/Cell/SelectActionListingHeaderCell.styles.ts +0 -26
  170. package/src/Listing/Header/ListingHeader.styles.ts +0 -16
  171. package/src/Listing/Listing.styles.ts +0 -78
  172. package/src/Listing/Row/EmptyRow.styles.ts +0 -14
  173. package/src/components/Button/Button.styles.ts +0 -44
  174. package/src/components/Layout/AreaIndicator.styles.ts +0 -33
  175. package/src/components/Menu/Button/MenuButton.styles.ts +0 -27
  176. package/src/components/Menu/Menu.styles.ts +0 -68
@@ -0,0 +1,205 @@
1
+ {
2
+ "global": {
3
+ "base": 1000,
4
+ "title": "Ping service"
5
+ },
6
+ "metrics": [
7
+ {
8
+ "metric_id": 2,
9
+ "metric": "Centreon-Server: pl",
10
+ "metric_legend": "Centreon-Server: pl",
11
+ "unit": "%",
12
+ "min": 0.0,
13
+ "max": 100.0,
14
+ "ds_data": {
15
+ "ds_color_line": "#F30B23",
16
+ "ds_color_area": "#F30B23",
17
+ "ds_filled": true,
18
+ "ds_invert": false,
19
+ "ds_legend": null,
20
+ "ds_stack": false,
21
+ "ds_order": 1,
22
+ "ds_transparency": 80.0,
23
+ "ds_color_line_mode": 0
24
+ },
25
+ "legend": "Centreon-Server: Packet Loss",
26
+ "stack": 0,
27
+ "warning_high_threshold": 20.0,
28
+ "critical_high_threshold": 50.0,
29
+ "warning_low_threshold": 0.0,
30
+ "critical_low_threshold": 0.0,
31
+ "ds_order": 1,
32
+ "data": [
33
+ 0.0,
34
+ 0.0,
35
+ 0.0,
36
+ 0.0,
37
+ 0.0,
38
+ 0.0,
39
+ 0.0,
40
+ 0.0,
41
+ 0.0,
42
+ 10.0,
43
+ 20.0,
44
+ null,
45
+ null
46
+ ],
47
+ "last_value": 0.0,
48
+ "minimum_value": null,
49
+ "maximum_value": 0.0,
50
+ "average_value": 0.0
51
+ },
52
+ {
53
+ "metric_id": 1,
54
+ "metric": "Centreon-Server: rta",
55
+ "metric_legend": "Centreon-Server: rta",
56
+ "unit": "ms",
57
+ "min": 0.0,
58
+ "max": null,
59
+ "ds_data": {
60
+ "ds_color_line": "#29AFEE",
61
+ "ds_color_area": "#29AFEE",
62
+ "ds_filled": true,
63
+ "ds_invert": false,
64
+ "ds_legend": null,
65
+ "ds_stack": true,
66
+ "ds_order": 1,
67
+ "ds_transparency": 80.0,
68
+ "ds_color_line_mode": 0
69
+ },
70
+ "legend": "Centreon-Server: Round-Trip Average Time",
71
+ "stack": 0,
72
+ "warning_high_threshold": 200.0,
73
+ "critical_high_threshold": 400.0,
74
+ "warning_low_threshold": 0.0,
75
+ "critical_low_threshold": 0.0,
76
+ "ds_order": 1,
77
+ "data": [
78
+ 0.04508,
79
+ 0.0242,
80
+ 0.03592,
81
+ 0.01304,
82
+ 0.025,
83
+ 0.02748,
84
+ 0.05296,
85
+ 0.01864,
86
+ 0.02688,
87
+ 0.03676,
88
+ 0.03696,
89
+ null,
90
+ null
91
+ ],
92
+ "last_value": 0.04,
93
+ "minimum_value": null,
94
+ "maximum_value": null,
95
+ "average_value": 0.03
96
+ },
97
+ {
98
+ "metric_id": 3,
99
+ "metric": "Centreon-Server: rtmax",
100
+ "metric_legend": "Centreon-Server: rtmax",
101
+ "unit": "ms",
102
+ "min": null,
103
+ "max": null,
104
+ "ds_data": {
105
+ "ds_color_line": "#525256",
106
+ "ds_color_area": "#525256",
107
+ "ds_filled": false,
108
+ "ds_invert": false,
109
+ "ds_legend": null,
110
+ "ds_stack": true,
111
+ "ds_stack_key": "test",
112
+ "ds_order": 2,
113
+ "ds_transparency": 80.0,
114
+ "ds_color_line_mode": 0
115
+ },
116
+ "legend": "Centreon-Server: Round-Trip Maximum Time",
117
+ "stack": 0,
118
+ "warning_high_threshold": null,
119
+ "critical_high_threshold": null,
120
+ "warning_low_threshold": null,
121
+ "critical_low_threshold": null,
122
+ "ds_order": 2,
123
+ "data": [
124
+ 0.11372,
125
+ 0.05604,
126
+ 0.08556,
127
+ 0.02548,
128
+ 0.05352,
129
+ 0.05336,
130
+ 0.109,
131
+ 0.04112,
132
+ 0.05504,
133
+ 0.06812,
134
+ 0.08644,
135
+ null,
136
+ null
137
+ ],
138
+ "last_value": 0.09,
139
+ "minimum_value": null,
140
+ "maximum_value": 0.11,
141
+ "average_value": null
142
+ },
143
+ {
144
+ "metric_id": 4,
145
+ "metric": "Centreon-Server: rtmin",
146
+ "metric_legend": "Centreon-Server: rtmin",
147
+ "unit": "ms",
148
+ "min": null,
149
+ "max": null,
150
+ "ds_data": {
151
+ "ds_color_line": "#191777",
152
+ "ds_color_area": "#191777",
153
+ "ds_filled": false,
154
+ "ds_invert": false,
155
+ "ds_legend": null,
156
+ "ds_stack": true,
157
+ "ds_stack_key": "test",
158
+ "ds_order": 2,
159
+ "ds_transparency": 80.0,
160
+ "ds_color_line_mode": 0
161
+ },
162
+ "legend": "Centreon-Server: Round-Trip Minimum Time",
163
+ "stack": 0,
164
+ "warning_high_threshold": null,
165
+ "critical_high_threshold": null,
166
+ "warning_low_threshold": null,
167
+ "critical_low_threshold": null,
168
+ "ds_order": 2,
169
+ "data": [
170
+ 0.00984,
171
+ 0.008,
172
+ 0.00784,
173
+ 0.00624,
174
+ 0.00932,
175
+ 0.01348,
176
+ 0.01796,
177
+ 0.0064,
178
+ 0.01148,
179
+ 0.01644,
180
+ 0.01168,
181
+ null,
182
+ null
183
+ ],
184
+ "last_value": 0.01,
185
+ "minimum_value": 0.01,
186
+ "maximum_value": null,
187
+ "average_value": null
188
+ }
189
+ ],
190
+ "times": [
191
+ "2024-06-19T10:50:00+02:00",
192
+ "2024-06-19T10:55:00+02:00",
193
+ "2024-06-19T11:00:00+02:00",
194
+ "2024-06-19T11:05:00+02:00",
195
+ "2024-06-19T11:10:00+02:00",
196
+ "2024-06-19T11:15:00+02:00",
197
+ "2024-06-19T11:20:00+02:00",
198
+ "2024-06-19T11:25:00+02:00",
199
+ "2024-06-19T11:30:00+02:00",
200
+ "2024-06-19T11:35:00+02:00",
201
+ "2024-06-19T11:40:00+02:00",
202
+ "2024-06-19T11:45:00+02:00",
203
+ "2024-06-19T11:50:00+02:00"
204
+ ]
205
+ }
@@ -0,0 +1,20 @@
1
+ import { SvgIconProps } from '@mui/material';
2
+ import { ReactElement } from 'react';
3
+ import BaseIcon from './BaseIcon';
4
+
5
+ const icon = (
6
+ <g transform="translate(2 2)">
7
+ <path
8
+ d="M19.468,8.107,17.2,6.8l2.264-1.307a.8.8,0,0,0-.8-1.386L16.4,5.414V2.8a.8.8,0,1,0-1.6,0V5.414L12.54,4.107a.8.8,0,1,0-.8,1.386L14,6.8,11.74,8.107a.8.8,0,1,0,.8,1.386L14.8,8.186V10.8a.8.8,0,1,0,1.6,0V8.186l2.264,1.307a.789.789,0,0,0,.4.107.8.8,0,0,0,.4-1.493"
9
+ transform="translate(-1.868)"
10
+ />
11
+ <path
12
+ d="M5.2,20.4a3.2,3.2,0,1,1,3.2-3.2,3.2,3.2,0,0,1-3.2,3.2m0-4.8a1.6,1.6,0,1,0,1.6,1.6,1.6,1.6,0,0,0-1.6-1.6"
13
+ transform="translate(0 -2.4)"
14
+ />
15
+ </g>
16
+ );
17
+
18
+ export const RegexIcon = (props: SvgIconProps): ReactElement => (
19
+ <BaseIcon {...props} Icon={icon} dataTestId="RegexIcon" />
20
+ );
package/src/Icon/index.ts CHANGED
@@ -8,3 +8,4 @@ export { HostGroupIcon } from './HostGroupIcon';
8
8
  export { ServiceGroupIcon } from './ServiceGroupIcon';
9
9
  export { MetaServiceIcon } from './MetaServiceIcon';
10
10
  export { ContainerIcon } from './ContainerIcon';
11
+ export { RegexIcon } from './RegexIcon';
@@ -4,12 +4,40 @@ import {
4
4
  TestQueryProvider
5
5
  } from '@centreon/ui';
6
6
 
7
+ import i18next from 'i18next';
8
+ import { useState } from 'react';
9
+ import { initReactI18next } from 'react-i18next';
10
+ import { labelSelectAll, labelUnSelectAll } from '../../../../translatedLabels';
7
11
  import { baseEndpoint, getEndpoint, label, placeholder } from './utils';
8
12
 
9
13
  const optionOne = 'My Option 1';
10
14
 
15
+ const Component = () => {
16
+ const [values, setValues] = useState([]);
17
+ return (
18
+ <TestQueryProvider>
19
+ <div style={{ paddingTop: 20 }}>
20
+ <MultiConnectedAutocompleteField
21
+ field="host.name"
22
+ getEndpoint={getEndpoint}
23
+ label={label}
24
+ placeholder={placeholder}
25
+ value={values}
26
+ onChange={(_, item) => setValues(item)}
27
+ disableSelectAll={false}
28
+ />
29
+ </div>
30
+ </TestQueryProvider>
31
+ );
32
+ };
33
+
11
34
  describe('Multi connected autocomplete', () => {
12
35
  beforeEach(() => {
36
+ i18next.use(initReactI18next).init({
37
+ lng: 'en',
38
+ resources: {}
39
+ });
40
+
13
41
  cy.fixture('inputField/listOptions').then((optionsData) => {
14
42
  cy.interceptAPIRequest({
15
43
  alias: 'getListOptions',
@@ -35,18 +63,7 @@ describe('Multi connected autocomplete', () => {
35
63
  });
36
64
 
37
65
  cy.mount({
38
- Component: (
39
- <TestQueryProvider>
40
- <div style={{ paddingTop: 20 }}>
41
- <MultiConnectedAutocompleteField
42
- field="host.name"
43
- getEndpoint={getEndpoint}
44
- label={label}
45
- placeholder={placeholder}
46
- />
47
- </div>
48
- </TestQueryProvider>
49
- )
66
+ Component: <Component />
50
67
  });
51
68
  });
52
69
 
@@ -98,7 +115,7 @@ describe('Multi connected autocomplete', () => {
98
115
  cy.waitForRequest('@getSearchedOption');
99
116
 
100
117
  cy.fixture('inputField/searchedOption').then(() => {
101
- cy.get('@listOptions').find('li').should('have.length', 5);
118
+ cy.get('@listOptions').find('li').should('have.length', 6);
102
119
  });
103
120
 
104
121
  cy.get('[type="checkbox"]').eq(0).check();
@@ -115,7 +132,7 @@ describe('Multi connected autocomplete', () => {
115
132
  cy.fixture('inputField/listOptions').then((optionsData) => {
116
133
  cy.get('@listOptions')
117
134
  .find('li')
118
- .should('have.length', optionsData.result.length);
135
+ .should('have.length', optionsData.result.length + 1);
119
136
 
120
137
  cy.get('@listOptions').within(() => {
121
138
  optionsData.result.forEach((option) => {
@@ -124,4 +141,41 @@ describe('Multi connected autocomplete', () => {
124
141
  });
125
142
  });
126
143
  });
144
+
145
+ it('checks all options when Select all button is clicked', () => {
146
+ cy.get('[data-testid="Multi Connected Autocomplete"]').as('input');
147
+
148
+ cy.get('@input').click();
149
+
150
+ cy.contains('5 element(s) found');
151
+
152
+ cy.waitForRequest('@getListOptions');
153
+
154
+ cy.contains(labelSelectAll).click();
155
+
156
+ cy.contains(labelUnSelectAll).should('be.visible');
157
+
158
+ cy.get('[data-testid="CancelIcon"]').should('have.length', 5);
159
+
160
+ cy.makeSnapshot('checks all options when Select all button is clicked');
161
+ });
162
+
163
+ it('unchecks all options when unSelect all button is clicked', () => {
164
+ cy.get('[data-testid="Multi Connected Autocomplete"]').as('input');
165
+
166
+ cy.get('@input').click();
167
+
168
+ cy.contains('5 element(s) found');
169
+
170
+ cy.waitForRequest('@getListOptions');
171
+
172
+ cy.contains(labelSelectAll).click();
173
+ cy.contains(labelUnSelectAll).click();
174
+
175
+ cy.contains(labelSelectAll).should('be.visible');
176
+
177
+ cy.get('[data-testid="CancelIcon"]').should('have.length', 0);
178
+
179
+ cy.makeSnapshot('unchecks all options when unSelect all button is clicked');
180
+ });
127
181
  });
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { ReactElement, useCallback, useEffect, useState } from 'react';
2
2
 
3
3
  import {
4
4
  equals,
@@ -17,7 +17,7 @@ import {
17
17
  import { CircularProgress, useTheme } from '@mui/material';
18
18
 
19
19
  import { Props as AutocompleteFieldProps } from '..';
20
- import { ListingModel, SelectEntry } from '../../../..';
20
+ import { ListingMapModel, ListingModel, SelectEntry } from '../../../..';
21
21
  import {
22
22
  ConditionsSearchParameter,
23
23
  SearchParameter
@@ -30,6 +30,12 @@ import {
30
30
  } from '../../../../utils';
31
31
  import Option from '../../Option';
32
32
 
33
+ interface OptionResult<T> {
34
+ result: Array<T>;
35
+ limit: number;
36
+ total: number;
37
+ }
38
+
33
39
  export interface ConnectedAutoCompleteFieldProps<TData> {
34
40
  allowUniqOption?: boolean;
35
41
  baseEndpoint?: string;
@@ -37,7 +43,8 @@ export interface ConnectedAutoCompleteFieldProps<TData> {
37
43
  exclusionOptionProperty?: keyof SelectEntry;
38
44
  field: string;
39
45
  getEndpoint: ({ search, page }) => string;
40
- getRenderedOptionText: (option: TData) => string;
46
+ decoder?;
47
+ getRenderedOptionText?: (option: TData) => ReactElement | string;
41
48
  getRequestHeaders?: HeadersInit;
42
49
  initialPage: number;
43
50
  labelKey?: string;
@@ -46,12 +53,13 @@ export interface ConnectedAutoCompleteFieldProps<TData> {
46
53
  }
47
54
 
48
55
  const ConnectedAutocompleteField = (
49
- AutocompleteField: (props) => JSX.Element,
56
+ AutocompleteField: (props) => ReactElement,
50
57
  multiple: boolean
51
- ): ((props) => JSX.Element) => {
58
+ ): ((props) => ReactElement) => {
52
59
  const InnerConnectedAutocompleteField = <TData extends { name: string }>({
53
60
  initialPage = 1,
54
61
  getEndpoint,
62
+ decoder,
55
63
  field,
56
64
  labelKey,
57
65
  open,
@@ -66,7 +74,7 @@ const ConnectedAutocompleteField = (
66
74
  changeIdValue,
67
75
  ...props
68
76
  }: ConnectedAutoCompleteFieldProps<TData> &
69
- Omit<AutocompleteFieldProps, 'options'>): JSX.Element => {
77
+ Omit<AutocompleteFieldProps, 'options'>): ReactElement => {
70
78
  const [options, setOptions] = useState<Array<TData>>([]);
71
79
  const [page, setPage] = useState(1);
72
80
  const [maxPage, setMaxPage] = useState(initialPage);
@@ -88,9 +96,10 @@ const ConnectedAutocompleteField = (
88
96
 
89
97
  const theme = useTheme();
90
98
 
91
- const { fetchQuery, isFetching, prefetchNextPage } = useFetchQuery<
92
- ListingModel<TData>
99
+ const { fetchQuery, isFetching, prefetchNextPage, data } = useFetchQuery<
100
+ ListingModel<TData> | ListingMapModel<TData>
93
101
  >({
102
+ decoder,
94
103
  baseEndpoint,
95
104
  fetchHeaders: getRequestHeaders,
96
105
  getEndpoint: (params) => {
@@ -113,6 +122,32 @@ const ConnectedAutocompleteField = (
113
122
  }
114
123
  });
115
124
 
125
+ const getOptionResult = useCallback(
126
+ (
127
+ newOptions: ListingModel<TData> | ListingMapModel<TData>
128
+ ): OptionResult<TData> => {
129
+ if ('result' in newOptions)
130
+ return {
131
+ result: newOptions.result || [],
132
+ total: newOptions.meta.total || 1,
133
+ limit: newOptions.meta.limit || 1
134
+ };
135
+ if ('content' in newOptions)
136
+ return {
137
+ result: newOptions.content || [],
138
+ total: newOptions.totalElements || 1,
139
+ limit: newOptions.size || 1
140
+ };
141
+
142
+ return {
143
+ result: [],
144
+ total: 1,
145
+ limit: 1
146
+ };
147
+ },
148
+ []
149
+ );
150
+
116
151
  const lastOptionRef = useIntersectionObserver({
117
152
  action: () => setPage(page + 1),
118
153
  loading: isFetching,
@@ -186,7 +221,7 @@ const ConnectedAutocompleteField = (
186
221
  debounce(event.target.value);
187
222
  };
188
223
 
189
- const renderOptions = (renderProps, option, { selected }): JSX.Element => {
224
+ const renderOptions = (renderProps, option, { selected }): ReactElement => {
190
225
  const { value } = props;
191
226
 
192
227
  const lastValue = Array.isArray(value) ? last(value) : value;
@@ -242,12 +277,14 @@ const ConnectedAutocompleteField = (
242
277
 
243
278
  const moreOptions = page > 1 ? options : [];
244
279
 
280
+ const { result, limit, total } = getOptionResult(newOptions);
281
+
245
282
  const formattedList = changeIdValue
246
- ? newOptions.result.map((item) => ({
283
+ ? result.map((item) => ({
247
284
  ...item,
248
285
  id: changeIdValue(item)
249
286
  }))
250
- : newOptions.result;
287
+ : result;
251
288
 
252
289
  if (!isEmpty(labelKey) && !isNil(labelKey)) {
253
290
  const list = formattedList.map((item) =>
@@ -261,9 +298,6 @@ const ConnectedAutocompleteField = (
261
298
 
262
299
  setOptions(moreOptions.concat(formattedList as Array<TData>));
263
300
 
264
- const total = prop('total', newOptions.meta) || 1;
265
- const limit = prop('limit', newOptions.meta) || 1;
266
-
267
301
  const newMaxPage = Math.ceil(total / limit);
268
302
 
269
303
  setMaxPage(newMaxPage);
@@ -322,6 +356,7 @@ const ConnectedAutocompleteField = (
322
356
 
323
357
  return (
324
358
  <AutocompleteField
359
+ total={data?.meta?.total || data?.totalElements || 1}
325
360
  filterOptions={(opt): SelectEntry => opt}
326
361
  loading={isFetching}
327
362
  options={
@@ -0,0 +1,78 @@
1
+ import { ListSubheader, Typography } from '@mui/material';
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Button } from '../../../../components/Button';
4
+ import { useListboxStyles } from './Multi.styles';
5
+
6
+ import {
7
+ labelElementsFound,
8
+ labelSelectAll,
9
+ labelUnSelectAll
10
+ } from '../../../translatedLabels';
11
+
12
+ const CustomListbox = ({
13
+ children,
14
+ label,
15
+ labelTotal,
16
+ handleSelectAllToggle,
17
+ ...props
18
+ }) => {
19
+ const { classes } = useListboxStyles();
20
+
21
+ return (
22
+ <ul {...props}>
23
+ <ListSubheader sx={{ padding: 0 }}>
24
+ <div className={classes.lisSubHeader}>
25
+ <Typography variant="body2">{labelTotal}</Typography>
26
+ <Button variant="ghost" size="small" onClick={handleSelectAllToggle}>
27
+ {label}
28
+ </Button>
29
+ </div>
30
+ </ListSubheader>
31
+ <div className={classes.dropdown}>{children}</div>
32
+ </ul>
33
+ );
34
+ };
35
+
36
+ const ListboxComponent = ({
37
+ disableSelectAll,
38
+ options,
39
+ isOptionSelected,
40
+ onChange,
41
+ total
42
+ }) => {
43
+ if (disableSelectAll) {
44
+ return;
45
+ }
46
+
47
+ return (listboxProps): JSX.Element | undefined => {
48
+ const { t } = useTranslation();
49
+
50
+ const allSelected =
51
+ options.length > 0 && options.every((opt) => isOptionSelected(opt));
52
+
53
+ const handleSelectAllToggle = (): void => {
54
+ const syntheticEvent = {} as React.SyntheticEvent;
55
+
56
+ if (allSelected) {
57
+ onChange?.(syntheticEvent, [], 'selectOption');
58
+
59
+ return;
60
+ }
61
+
62
+ onChange?.(syntheticEvent, options, 'selectOption');
63
+ };
64
+
65
+ return (
66
+ <CustomListbox
67
+ {...listboxProps}
68
+ label={t(allSelected ? labelUnSelectAll : labelSelectAll)}
69
+ handleSelectAllToggle={handleSelectAllToggle}
70
+ labelTotal={t(labelElementsFound, {
71
+ total: total || options.length
72
+ })}
73
+ />
74
+ );
75
+ };
76
+ };
77
+
78
+ export default ListboxComponent;
@@ -0,0 +1,26 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()((theme) => ({
4
+ deleteIcon: {
5
+ height: theme.spacing(1.5),
6
+ width: theme.spacing(1.5)
7
+ },
8
+ tag: {
9
+ fontSize: theme.typography.caption.fontSize
10
+ }
11
+ }));
12
+
13
+ export const useListboxStyles = makeStyles()((theme) => ({
14
+ lisSubHeader: {
15
+ width: '100%',
16
+ background: theme.palette.background.default,
17
+ padding: theme.spacing(0.5, 1, 0.5, 1.5),
18
+ display: 'flex',
19
+ justifyContent: 'space-between',
20
+ alignItems: 'center'
21
+ },
22
+ dropdown: {
23
+ width: '100%',
24
+ background: theme.palette.background.paper
25
+ }
26
+ }));