orc-shared 5.10.0-dev.2 → 5.10.0-dev.21

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 (186) hide show
  1. package/dist/actions/metadata.js +30 -11
  2. package/dist/actions/requestsApi.js +10 -1
  3. package/dist/components/AppFrame/About.js +136 -100
  4. package/dist/components/AppFrame/Anchor.js +45 -21
  5. package/dist/components/AppFrame/AppFrame.js +53 -31
  6. package/dist/components/AppFrame/Help.js +35 -15
  7. package/dist/components/AppFrame/MenuItem.js +148 -114
  8. package/dist/components/AppFrame/Preferences.js +136 -97
  9. package/dist/components/AppFrame/Sidebar.js +51 -28
  10. package/dist/components/AppFrame/Topbar.js +61 -36
  11. package/dist/components/ColumnWrapper.js +28 -5
  12. package/dist/components/Culture.js +33 -14
  13. package/dist/components/DropMenu/Menu.js +79 -45
  14. package/dist/components/DropMenu/index.js +34 -29
  15. package/dist/components/Form/Combination.js +45 -16
  16. package/dist/components/Form/Field.js +57 -38
  17. package/dist/components/Form/FieldElements.js +0 -11
  18. package/dist/components/Form/Fieldset.js +47 -19
  19. package/dist/components/Form/Form.js +22 -9
  20. package/dist/components/Form/FormElement.js +40 -7
  21. package/dist/components/Form/Inputs/Button.js +63 -18
  22. package/dist/components/Form/Inputs/ReadOnly.js +50 -27
  23. package/dist/components/{AppFrame/ApplicationSelector/Header.js → Form/Inputs/Selector.js} +30 -31
  24. package/dist/components/Form/Inputs/Text.js +20 -37
  25. package/dist/components/Form/Inputs/Toggles.js +39 -40
  26. package/dist/components/Form/Inputs/index.js +2 -13
  27. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.js +31 -11
  28. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/SectionToolbar.js +89 -0
  29. package/dist/components/MaterialUI/DataDisplay/Table.js +109 -18
  30. package/dist/components/MaterialUI/DataDisplay/TableProps.js +5 -1
  31. package/dist/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.js +198 -0
  32. package/dist/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +4 -1
  33. package/dist/components/MaterialUI/Inputs/DatePicker.js +14 -14
  34. package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -0
  35. package/dist/components/MaterialUI/Inputs/Select.js +2 -0
  36. package/dist/components/MaterialUI/Inputs/SelectProps.js +2 -0
  37. package/dist/components/MaterialUI/Inputs/Switch.js +17 -1
  38. package/dist/components/MaterialUI/Inputs/SwitchProps.js +2 -0
  39. package/dist/components/MaterialUI/Inputs/TimePicker.js +14 -21
  40. package/dist/components/MaterialUI/hocs/withDeferredTooltip.js +3 -1
  41. package/dist/components/MaterialUI/muiThemes.js +2 -1
  42. package/dist/components/Provision.js +1 -1
  43. package/dist/constants.js +2 -1
  44. package/dist/content/iconsSheet.svg +740 -116
  45. package/dist/hocs/withScrollBox.js +27 -12
  46. package/dist/hooks/useDaysAndMonthsLocalization.js +77 -0
  47. package/dist/hooks/useInMemoryPaging.js +135 -0
  48. package/dist/hooks/useMultipleFieldEditState.js +12 -3
  49. package/dist/reducers/metadata.js +6 -0
  50. package/dist/schemas/metadata.js +9 -1
  51. package/dist/selectors/locale.js +1 -0
  52. package/dist/selectors/metadata.js +14 -11
  53. package/dist/sharedMessages.js +184 -0
  54. package/dist/utils/ListHelper.js +271 -0
  55. package/dist/utils/comparisonHelper.js +185 -0
  56. package/dist/utils/propertyBagHelper.js +3 -1
  57. package/dist/utils/timezoneHelper.js +18 -31
  58. package/package.json +4 -3
  59. package/src/actions/metadata.js +11 -0
  60. package/src/actions/metadata.test.js +27 -0
  61. package/src/actions/requestsApi.js +6 -0
  62. package/src/components/AppFrame/About.js +97 -117
  63. package/src/components/AppFrame/About.test.js +128 -90
  64. package/src/components/AppFrame/Anchor.js +34 -36
  65. package/src/components/AppFrame/Anchor.test.js +5 -68
  66. package/src/components/AppFrame/AppFrame.js +31 -40
  67. package/src/components/AppFrame/AppFrame.test.js +424 -445
  68. package/src/components/AppFrame/Help.js +23 -20
  69. package/src/components/AppFrame/Help.test.js +3 -3
  70. package/src/components/AppFrame/MenuItem.js +106 -126
  71. package/src/components/AppFrame/MenuItem.test.js +78 -169
  72. package/src/components/AppFrame/Preferences.js +110 -98
  73. package/src/components/AppFrame/Preferences.test.js +115 -219
  74. package/src/components/AppFrame/Sidebar.js +39 -41
  75. package/src/components/AppFrame/Sidebar.test.js +88 -168
  76. package/src/components/AppFrame/Topbar.js +59 -52
  77. package/src/components/AppFrame/Topbar.test.js +31 -39
  78. package/src/components/ColumnWrapper.js +18 -9
  79. package/src/components/Culture.js +20 -10
  80. package/src/components/Culture.test.js +27 -16
  81. package/src/components/DropMenu/DropMenu.test.js +185 -224
  82. package/src/components/DropMenu/Menu.js +73 -80
  83. package/src/components/DropMenu/Menu.test.js +35 -86
  84. package/src/components/DropMenu/index.js +19 -15
  85. package/src/components/Form/Combination.js +35 -28
  86. package/src/components/Form/Combination.test.js +6 -19
  87. package/src/components/Form/Field.js +53 -66
  88. package/src/components/Form/Field.test.js +29 -51
  89. package/src/components/Form/FieldElements.js +0 -14
  90. package/src/components/Form/FieldElements.test.js +104 -111
  91. package/src/components/Form/Fieldset.js +42 -37
  92. package/src/components/Form/Fieldset.test.js +14 -7
  93. package/src/components/Form/Form.js +11 -7
  94. package/src/components/Form/Form.test.js +75 -56
  95. package/src/components/Form/FormElement.js +24 -16
  96. package/src/components/Form/InputField.test.js +24 -30
  97. package/src/components/Form/Inputs/Button.js +58 -14
  98. package/src/components/Form/Inputs/Button.test.js +32 -7
  99. package/src/components/Form/Inputs/Inputs.test.js +0 -7
  100. package/src/components/Form/Inputs/ReadOnly.js +34 -28
  101. package/src/components/Form/Inputs/ReadOnly.test.js +45 -7
  102. package/src/components/Form/Inputs/Selector.js +22 -0
  103. package/src/components/Form/Inputs/Selector.test.js +105 -0
  104. package/src/components/Form/Inputs/Text.js +15 -44
  105. package/src/components/Form/Inputs/Text.test.js +20 -29
  106. package/src/components/Form/Inputs/Toggles.js +27 -26
  107. package/src/components/Form/Inputs/Toggles.test.js +22 -28
  108. package/src/components/Form/Inputs/index.js +4 -15
  109. package/src/components/MaterialUI/DataDisplay/PredefinedElements/InformationItem.test.js +1 -4
  110. package/src/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.js +32 -6
  111. package/src/components/MaterialUI/DataDisplay/PredefinedElements/Placeholder.test.js +3 -1
  112. package/src/components/MaterialUI/DataDisplay/PredefinedElements/SectionToolbar.js +39 -0
  113. package/src/components/MaterialUI/DataDisplay/Table.js +190 -114
  114. package/src/components/MaterialUI/DataDisplay/Table.test.js +246 -1
  115. package/src/components/MaterialUI/DataDisplay/TableProps.js +4 -0
  116. package/src/components/MaterialUI/DataDisplay/TableProps.test.js +2 -0
  117. package/src/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.js +145 -0
  118. package/src/components/MaterialUI/DataDisplay/TableWithInMemoryPaging.test.js +457 -0
  119. package/src/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.js +5 -1
  120. package/src/components/MaterialUI/DataDisplay/TooltippedElements/MultipleLinesText.test.js +7 -1
  121. package/src/components/MaterialUI/Inputs/DatePicker.js +19 -20
  122. package/src/components/MaterialUI/Inputs/DatePicker.test.js +11 -6
  123. package/src/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -0
  124. package/src/components/MaterialUI/Inputs/Select.js +2 -0
  125. package/src/components/MaterialUI/Inputs/SelectProps.js +2 -0
  126. package/src/components/MaterialUI/Inputs/SelectProps.test.js +2 -0
  127. package/src/components/MaterialUI/Inputs/Switch.js +22 -1
  128. package/src/components/MaterialUI/Inputs/Switch.test.js +23 -0
  129. package/src/components/MaterialUI/Inputs/SwitchProps.js +2 -0
  130. package/src/components/MaterialUI/Inputs/SwitchProps.test.js +2 -0
  131. package/src/components/MaterialUI/Inputs/TimePicker.js +10 -19
  132. package/src/components/MaterialUI/Inputs/TimePicker.test.js +278 -117
  133. package/src/components/MaterialUI/hocs/withDeferredTooltip.js +4 -1
  134. package/src/components/MaterialUI/hocs/withDeferredTooltip.test.js +27 -0
  135. package/src/components/MaterialUI/muiThemes.js +1 -0
  136. package/src/components/Navigation/Bar.test.js +92 -87
  137. package/src/components/Provision.js +1 -1
  138. package/src/components/TaskDetailsModal.test.js +1 -3
  139. package/src/constants.js +1 -0
  140. package/src/content/iconsSheet.svg +740 -116
  141. package/src/hocs/withScrollBox.js +32 -19
  142. package/src/hocs/withScrollBox.test.js +15 -3
  143. package/src/hooks/useDaysAndMonthsLocalization.js +79 -0
  144. package/src/hooks/useDaysAndMonthsLocalization.test.js +107 -0
  145. package/src/hooks/useInMemoryPaging.js +78 -0
  146. package/src/hooks/useInMemoryPaging.test.js +515 -0
  147. package/src/hooks/useMultipleFieldEditState.js +11 -4
  148. package/src/hooks/useMultipleFieldEditState.test.js +49 -1
  149. package/src/reducers/metadata.js +6 -1
  150. package/src/reducers/metadata.test.js +31 -0
  151. package/src/requests +1 -0
  152. package/src/schemas/metadata.js +3 -0
  153. package/src/selectors/locale.js +1 -1
  154. package/src/selectors/metadata.js +12 -9
  155. package/src/selectors/metadata.test.js +92 -11
  156. package/src/sharedMessages.js +184 -0
  157. package/src/timezones.json +883 -0
  158. package/src/translations/en-US.json +46 -0
  159. package/src/translations/fr-CA.json +46 -0
  160. package/src/utils/ListHelper.js +203 -0
  161. package/src/utils/ListHelper.test.js +710 -0
  162. package/src/utils/comparisonHelper.js +135 -0
  163. package/src/utils/comparisonHelper.test.js +334 -0
  164. package/src/utils/propertyBagHelper.js +2 -0
  165. package/src/utils/propertyBagHelper.test.js +6 -0
  166. package/src/utils/timezoneHelper.js +10 -135
  167. package/src/utils/timezoneHelper.test.js +7 -7
  168. package/dist/components/Form/FieldList.js +0 -270
  169. package/dist/components/Form/Inputs/FieldButtons.js +0 -66
  170. package/dist/components/Form/Inputs/Number.js +0 -117
  171. package/dist/components/Form/Inputs/SmallButton.js +0 -91
  172. package/dist/components/Form/Inputs/Time.js +0 -86
  173. package/dist/components/Form/Inputs/Translation.js +0 -169
  174. package/src/components/AppFrame/ApplicationSelector/Header.js +0 -34
  175. package/src/components/AppFrame/ApplicationSelector/Header.test.js +0 -23
  176. package/src/components/Form/FieldList.js +0 -210
  177. package/src/components/Form/FieldList.test.js +0 -558
  178. package/src/components/Form/Inputs/FieldButtons.js +0 -90
  179. package/src/components/Form/Inputs/Number.js +0 -60
  180. package/src/components/Form/Inputs/Number.test.js +0 -435
  181. package/src/components/Form/Inputs/SmallButton.js +0 -37
  182. package/src/components/Form/Inputs/SmallButton.test.js +0 -65
  183. package/src/components/Form/Inputs/Time.js +0 -32
  184. package/src/components/Form/Inputs/Time.test.js +0 -41
  185. package/src/components/Form/Inputs/Translation.js +0 -93
  186. package/src/components/Form/Inputs/Translation.test.js +0 -204
@@ -0,0 +1,515 @@
1
+ import React, { useRef } from "react";
2
+ import { Provider } from "react-redux";
3
+ import Immutable from "immutable";
4
+ import sinon from "sinon";
5
+ import { spyOnConsole } from "../utils/testUtils";
6
+ import { mount } from "enzyme";
7
+ import useInMemoryPaging from "./useInMemoryPaging";
8
+ import { buildHeaderAndRowFromConfig, Table, TableProps } from "../components/MaterialUI/DataDisplay";
9
+
10
+ const getColumnDefs = () => [
11
+ {
12
+ fieldName: "name",
13
+ label: "name",
14
+ },
15
+ ];
16
+
17
+ const TestComp = ({
18
+ stateName,
19
+ allRecords,
20
+ sortAndFilterFn,
21
+ initialSort = {},
22
+ initialFilters = {},
23
+ tableRef = undefined,
24
+ }) => {
25
+ const internalTableRef = useRef(null);
26
+
27
+ const params = {
28
+ viewStateName: stateName,
29
+ tableRef: tableRef === undefined ? internalTableRef : tableRef,
30
+ records: allRecords,
31
+ pageSize: 20,
32
+ initialSort: initialSort,
33
+ initialFilters: initialFilters,
34
+ sortAndFilterFn: sortAndFilterFn,
35
+ };
36
+
37
+ if (params.initialSort === null) {
38
+ delete params.initialSort;
39
+ }
40
+
41
+ if (params.initialFilters === null) {
42
+ delete params.initialFilters;
43
+ }
44
+
45
+ const {
46
+ rows: pagedRows,
47
+ scrollLoader,
48
+ currentPage,
49
+ filters,
50
+ sorting,
51
+ setFilter,
52
+ setSort,
53
+ totalCount,
54
+ } = useInMemoryPaging(params);
55
+
56
+ const onInternalFilter = () => {
57
+ setFilter({ search: "n10" });
58
+ };
59
+
60
+ const onInternalSort = () => {
61
+ setSort({ sortBy: "name" });
62
+ };
63
+
64
+ const onScrollPage1 = () => {
65
+ scrollLoader(1);
66
+ };
67
+
68
+ const onScrollPage2 = () => {
69
+ scrollLoader(2);
70
+ };
71
+
72
+ const tableProps = new TableProps();
73
+ tableProps.set(TableProps.propNames.stickyHeader, true);
74
+ tableProps.set(TableProps.propNames.withoutTopBorder, true);
75
+ const columnDefs = getColumnDefs();
76
+
77
+ const { headers, rows } = buildHeaderAndRowFromConfig(columnDefs, pagedRows, true, "id");
78
+
79
+ return (
80
+ <div>
81
+ <div data-qa="totalCount">{totalCount}</div>
82
+ <div data-qa="filters">{JSON.stringify(filters)}</div>
83
+ <div data-qa="sorting">{JSON.stringify(sorting)}</div>
84
+ <div data-qa="pagedRows">{JSON.stringify(pagedRows)}</div>
85
+
86
+ <input type="button" data-qa="filter" value="filter" onClick={onInternalFilter} />
87
+ <input type="button" data-qa="sort" value="sort" onClick={onInternalSort} />
88
+ <input type="button" data-qa="scrollPage1" value="scrollPage1" onClick={onScrollPage1} />
89
+ <input type="button" data-qa="scrollPage2" value="scrollPage2" onClick={onScrollPage2} />
90
+
91
+ <Table
92
+ headers={headers}
93
+ rows={rows}
94
+ tableProps={tableProps}
95
+ scrollLoader={scrollLoader}
96
+ latestPage={currentPage}
97
+ pageLength={20}
98
+ />
99
+ </div>
100
+ );
101
+ };
102
+
103
+ const sortAndFilter = ({ list, filters, sorting }) => {
104
+ const { searchTerm } = filters;
105
+ const { sortBy = "name" } = sorting;
106
+
107
+ list = [...list];
108
+ list.sort((a, b) => a[sortBy]?.localeCompare(b[sortBy]));
109
+
110
+ if (searchTerm) {
111
+ list = list.filter(c => c.name?.toLowerCase()?.includes(searchTerm));
112
+ }
113
+
114
+ return list;
115
+ };
116
+
117
+ const standardListRecords = Array.from(Array(50).keys()).map(k => ({ name: "n" + k }));
118
+ const immutableListRecord = Immutable.fromJS(standardListRecords);
119
+
120
+ describe.each([
121
+ ["useInMemoryPaging with standard list", standardListRecords],
122
+ ["useInMemoryPaging with Immutable list", immutableListRecord],
123
+ ])("%s", (title, listValues) => {
124
+ spyOnConsole(["warn", "error"]);
125
+
126
+ let state, store;
127
+
128
+ beforeEach(() => {
129
+ state = Immutable.fromJS({});
130
+ store = {
131
+ getState: () => state,
132
+ subscribe: () => {},
133
+ dispatch: sinon.spy().named("dispatch"),
134
+ };
135
+ });
136
+
137
+ it("initial state", () => {
138
+ const component = (
139
+ <Provider store={store}>
140
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} />
141
+ </Provider>
142
+ );
143
+
144
+ const mountedComponent = mount(component);
145
+ const totalCountDiv = mountedComponent.find("[data-qa='totalCount']").at(0);
146
+ const filtersDiv = mountedComponent.find("[data-qa='filters']").at(0);
147
+ const sortingDiv = mountedComponent.find("[data-qa='sorting']").at(0);
148
+ const pagedRowsDiv = mountedComponent.find("[data-qa='pagedRows']").at(0);
149
+
150
+ expect(totalCountDiv.text(), "to equal", "50");
151
+ expect(filtersDiv.text(), "to equal", "{}");
152
+ expect(sortingDiv.text(), "to equal", "{}");
153
+ expect(
154
+ pagedRowsDiv.text(),
155
+ "to equal",
156
+ '[{"name":"n0"},{"name":"n1"},{"name":"n10"},{"name":"n11"},{"name":"n12"},{"name":"n13"},{"name":"n14"},{"name":"n15"},{"name":"n16"},{"name":"n17"},{"name":"n18"},{"name":"n19"},{"name":"n2"},{"name":"n20"},{"name":"n21"},{"name":"n22"},{"name":"n23"},{"name":"n24"},{"name":"n25"},{"name":"n26"}]',
157
+ );
158
+ });
159
+
160
+ it("scrollLoader changes nothing if page is less than next page", () => {
161
+ state = state.setIn(
162
+ ["view", "inMemory"],
163
+ Immutable.fromJS({
164
+ currentPage: 1,
165
+ nextPageToLoad: 1,
166
+ }),
167
+ );
168
+
169
+ const component = (
170
+ <Provider store={store}>
171
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} />
172
+ </Provider>
173
+ );
174
+
175
+ const mountedComponent = mount(component);
176
+ const onScrollPage1Btn = mountedComponent.find("[data-qa='scrollPage1']").at(0);
177
+ onScrollPage1Btn.simulate("click");
178
+
179
+ expect(store.dispatch, "was not called");
180
+ });
181
+
182
+ it("scrollLoader changes view state if page is greater than next page", () => {
183
+ state = state.setIn(
184
+ ["view", "inMemory"],
185
+ Immutable.fromJS({
186
+ currentPage: 1,
187
+ nextPageToLoad: 1,
188
+ }),
189
+ );
190
+
191
+ const component = (
192
+ <Provider store={store}>
193
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} />
194
+ </Provider>
195
+ );
196
+
197
+ const mountedComponent = mount(component);
198
+ const onScrollPage2Btn = mountedComponent.find("[data-qa='scrollPage2']").at(0);
199
+ onScrollPage2Btn.simulate("click");
200
+
201
+ expect(store.dispatch, "to have a call satisfying", {
202
+ args: [
203
+ {
204
+ type: "VIEW_STATE_SET_FIELD",
205
+ payload: { name: "inMemory", field: "currentPage", value: 2 },
206
+ },
207
+ ],
208
+ });
209
+ expect(store.dispatch, "to have a call satisfying", {
210
+ args: [
211
+ {
212
+ type: "VIEW_STATE_SET_FIELD",
213
+ payload: { name: "inMemory", field: "nextPageToLoad", value: 2 },
214
+ },
215
+ ],
216
+ });
217
+ });
218
+
219
+ it("validate default values for filters and sorting", () => {
220
+ state = state.setIn(
221
+ ["view", "inMemory"],
222
+ Immutable.fromJS({
223
+ currentPage: 1,
224
+ nextPageToLoad: 1,
225
+ }),
226
+ );
227
+
228
+ const component = (
229
+ <Provider store={store}>
230
+ <TestComp
231
+ allRecords={listValues}
232
+ stateName={"inMemory"}
233
+ sortAndFilterFn={sortAndFilter}
234
+ initialSort={null}
235
+ initialFilters={null}
236
+ />
237
+ </Provider>
238
+ );
239
+
240
+ const mountedComponent = mount(component);
241
+ const totalCountDiv = mountedComponent.find("[data-qa='totalCount']").at(0);
242
+ const filtersDiv = mountedComponent.find("[data-qa='filters']").at(0);
243
+ const sortingDiv = mountedComponent.find("[data-qa='sorting']").at(0);
244
+ const pagedRowsDiv = mountedComponent.find("[data-qa='pagedRows']").at(0);
245
+
246
+ expect(totalCountDiv.text(), "to equal", "50");
247
+ expect(filtersDiv.text(), "to equal", "{}");
248
+ expect(sortingDiv.text(), "to equal", "{}");
249
+ expect(
250
+ pagedRowsDiv.text(),
251
+ "to equal",
252
+ '[{"name":"n0"},{"name":"n1"},{"name":"n10"},{"name":"n11"},{"name":"n12"},{"name":"n13"},{"name":"n14"},{"name":"n15"},{"name":"n16"},{"name":"n17"},{"name":"n18"},{"name":"n19"},{"name":"n2"},{"name":"n20"},{"name":"n21"},{"name":"n22"},{"name":"n23"},{"name":"n24"},{"name":"n25"},{"name":"n26"}]',
253
+ );
254
+ });
255
+
256
+ it("displays first page with a real sortAndFilterFn", () => {
257
+ state = state.setIn(
258
+ ["view", "inMemory"],
259
+ Immutable.fromJS({
260
+ currentPage: 1,
261
+ nextPageToLoad: 1,
262
+ }),
263
+ );
264
+
265
+ const sortAndFilterCustom = ({ list, filters, sorting }) => {
266
+ const { searchTerm } = filters;
267
+ const { sortBy = "name" } = sorting;
268
+
269
+ list = [...list];
270
+ list.sort((a, b) => a[sortBy]?.localeCompare(b[sortBy]));
271
+
272
+ if (searchTerm) {
273
+ list = list.filter(c => c.name?.toLowerCase()?.includes(searchTerm));
274
+ }
275
+
276
+ return list.slice(0, 10);
277
+ };
278
+
279
+ const component = (
280
+ <Provider store={store}>
281
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilterCustom} />
282
+ </Provider>
283
+ );
284
+
285
+ const mountedComponent = mount(component);
286
+ const totalCountDiv = mountedComponent.find("[data-qa='totalCount']").at(0);
287
+ const filtersDiv = mountedComponent.find("[data-qa='filters']").at(0);
288
+ const sortingDiv = mountedComponent.find("[data-qa='sorting']").at(0);
289
+ const pagedRowsDiv = mountedComponent.find("[data-qa='pagedRows']").at(0);
290
+
291
+ expect(totalCountDiv.text(), "to equal", "10");
292
+ expect(filtersDiv.text(), "to equal", "{}");
293
+ expect(sortingDiv.text(), "to equal", "{}");
294
+ expect(
295
+ pagedRowsDiv.text(),
296
+ "to equal",
297
+ '[{"name":"n0"},{"name":"n1"},{"name":"n10"},{"name":"n11"},{"name":"n12"},{"name":"n13"},{"name":"n14"},{"name":"n15"},{"name":"n16"},{"name":"n17"}]',
298
+ );
299
+ });
300
+
301
+ it("displays 2nd page", () => {
302
+ state = state.setIn(
303
+ ["view", "inMemory"],
304
+ Immutable.fromJS({
305
+ currentPage: 2,
306
+ nextPageToLoad: 2,
307
+ }),
308
+ );
309
+
310
+ const component = (
311
+ <Provider store={store}>
312
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} />
313
+ </Provider>
314
+ );
315
+
316
+ const mountedComponent = mount(component);
317
+ const totalCountDiv = mountedComponent.find("[data-qa='totalCount']").at(0);
318
+ const filtersDiv = mountedComponent.find("[data-qa='filters']").at(0);
319
+ const sortingDiv = mountedComponent.find("[data-qa='sorting']").at(0);
320
+ const pagedRowsDiv = mountedComponent.find("[data-qa='pagedRows']").at(0);
321
+
322
+ expect(totalCountDiv.text(), "to equal", "50");
323
+ expect(filtersDiv.text(), "to equal", "{}");
324
+ expect(sortingDiv.text(), "to equal", "{}");
325
+ expect(
326
+ pagedRowsDiv.text(),
327
+ "to equal",
328
+ '[{"name":"n0"},{"name":"n1"},{"name":"n10"},{"name":"n11"},{"name":"n12"},{"name":"n13"},{"name":"n14"},{"name":"n15"},{"name":"n16"},{"name":"n17"},{"name":"n18"},{"name":"n19"},{"name":"n2"},{"name":"n20"},{"name":"n21"},{"name":"n22"},{"name":"n23"},{"name":"n24"},{"name":"n25"},{"name":"n26"},{"name":"n27"},{"name":"n28"},{"name":"n29"},{"name":"n3"},{"name":"n30"},{"name":"n31"},{"name":"n32"},{"name":"n33"},{"name":"n34"},{"name":"n35"},{"name":"n36"},{"name":"n37"},{"name":"n38"},{"name":"n39"},{"name":"n4"},{"name":"n40"},{"name":"n41"},{"name":"n42"},{"name":"n43"},{"name":"n44"}]',
329
+ );
330
+ });
331
+
332
+ it("scrollTableRef raises warning if tableRef is null", () => {
333
+ state = state.setIn(
334
+ ["view", "inMemory"],
335
+ Immutable.fromJS({
336
+ currentPage: 2,
337
+ nextPageToLoad: 2,
338
+ }),
339
+ );
340
+
341
+ const component = (
342
+ <Provider store={store}>
343
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} tableRef={null} />
344
+ </Provider>
345
+ );
346
+
347
+ const mountedComponent = mount(component);
348
+ const filterBtn = mountedComponent.find("[data-qa='filter']").at(0);
349
+ filterBtn.simulate("click");
350
+
351
+ expect(console.error, "to have a call satisfying", {
352
+ args: ["[useInMemoryPaging]: tableRef was not specified or tableRef.current is null."],
353
+ });
354
+ });
355
+
356
+ it("scrollTableRef raises warning if tableRef.current is null", () => {
357
+ state = state.setIn(
358
+ ["view", "inMemory"],
359
+ Immutable.fromJS({
360
+ currentPage: 2,
361
+ nextPageToLoad: 2,
362
+ }),
363
+ );
364
+
365
+ const tableRef = {
366
+ current: null,
367
+ };
368
+
369
+ const component = (
370
+ <Provider store={store}>
371
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} tableRef={tableRef} />
372
+ </Provider>
373
+ );
374
+
375
+ const mountedComponent = mount(component);
376
+ const filterBtn = mountedComponent.find("[data-qa='filter']").at(0);
377
+ filterBtn.simulate("click");
378
+
379
+ expect(console.error, "to have a call satisfying", {
380
+ args: ["[useInMemoryPaging]: tableRef was not specified or tableRef.current is null."],
381
+ });
382
+ });
383
+
384
+ it("scrollTableRef scrolls to top if tableRef.current is not null", () => {
385
+ state = state.setIn(
386
+ ["view", "inMemory"],
387
+ Immutable.fromJS({
388
+ currentPage: 2,
389
+ nextPageToLoad: 2,
390
+ }),
391
+ );
392
+
393
+ const tableRef = {
394
+ current: {
395
+ scrollToTop: sinon.spy().named("tableRefcurrent"),
396
+ },
397
+ };
398
+
399
+ const component = (
400
+ <Provider store={store}>
401
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} tableRef={tableRef} />
402
+ </Provider>
403
+ );
404
+
405
+ const mountedComponent = mount(component);
406
+ const filterBtn = mountedComponent.find("[data-qa='filter']").at(0);
407
+ filterBtn.simulate("click");
408
+
409
+ expect(tableRef.current.scrollToTop, "was called");
410
+ });
411
+
412
+ it("changing filter reset state and tableRef", () => {
413
+ state = state.setIn(
414
+ ["view", "inMemory"],
415
+ Immutable.fromJS({
416
+ currentPage: 2,
417
+ nextPageToLoad: 2,
418
+ }),
419
+ );
420
+
421
+ const tableRef = {
422
+ current: {
423
+ scrollToTop: sinon.spy().named("tableRefcurrent"),
424
+ },
425
+ };
426
+
427
+ const component = (
428
+ <Provider store={store}>
429
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} tableRef={tableRef} />
430
+ </Provider>
431
+ );
432
+
433
+ const mountedComponent = mount(component);
434
+ const filterBtn = mountedComponent.find("[data-qa='filter']").at(0);
435
+ filterBtn.simulate("click");
436
+
437
+ expect(tableRef.current.scrollToTop, "was called");
438
+ expect(store.dispatch, "to have a call satisfying", {
439
+ args: [
440
+ {
441
+ type: "VIEW_STATE_SET_FIELD",
442
+ payload: { name: "inMemory", field: "filters", value: { search: "n10" } },
443
+ },
444
+ ],
445
+ });
446
+ expect(store.dispatch, "to have a call satisfying", {
447
+ args: [
448
+ {
449
+ type: "VIEW_STATE_SET_FIELD",
450
+ payload: { name: "inMemory", field: "currentPage", value: 1 },
451
+ },
452
+ ],
453
+ });
454
+ expect(store.dispatch, "to have a call satisfying", {
455
+ args: [
456
+ {
457
+ type: "VIEW_STATE_SET_FIELD",
458
+ payload: { name: "inMemory", field: "nextPageToLoad", value: 1 },
459
+ },
460
+ ],
461
+ });
462
+ });
463
+
464
+ it("changing sorting reset state and tableRef", () => {
465
+ state = state.setIn(
466
+ ["view", "inMemory"],
467
+ Immutable.fromJS({
468
+ currentPage: 2,
469
+ nextPageToLoad: 2,
470
+ }),
471
+ );
472
+
473
+ const tableRef = {
474
+ current: {
475
+ scrollToTop: sinon.spy().named("tableRefcurrent"),
476
+ },
477
+ };
478
+
479
+ const component = (
480
+ <Provider store={store}>
481
+ <TestComp allRecords={listValues} stateName={"inMemory"} sortAndFilterFn={sortAndFilter} tableRef={tableRef} />
482
+ </Provider>
483
+ );
484
+
485
+ const mountedComponent = mount(component);
486
+ const sortingBtn = mountedComponent.find("[data-qa='sort']").at(0);
487
+ sortingBtn.simulate("click");
488
+
489
+ expect(tableRef.current.scrollToTop, "was called");
490
+ expect(store.dispatch, "to have a call satisfying", {
491
+ args: [
492
+ {
493
+ type: "VIEW_STATE_SET_FIELD",
494
+ payload: { name: "inMemory", field: "sorting", value: { sortBy: "name" } },
495
+ },
496
+ ],
497
+ });
498
+ expect(store.dispatch, "to have a call satisfying", {
499
+ args: [
500
+ {
501
+ type: "VIEW_STATE_SET_FIELD",
502
+ payload: { name: "inMemory", field: "currentPage", value: 1 },
503
+ },
504
+ ],
505
+ });
506
+ expect(store.dispatch, "to have a call satisfying", {
507
+ args: [
508
+ {
509
+ type: "VIEW_STATE_SET_FIELD",
510
+ payload: { name: "inMemory", field: "nextPageToLoad", value: 1 },
511
+ },
512
+ ],
513
+ });
514
+ });
515
+ });
@@ -1,7 +1,7 @@
1
1
  import { useSelector } from "react-redux";
2
2
  import { validationRules } from "../utils/modelValidationHelper";
3
3
  import { getModifiedModels } from "../selectors/view";
4
- import { setEditModelField, setEditModelFieldError } from "../actions/view";
4
+ import { setEditModelField, setEditModelFieldError, removeEditModelFieldError } from "../actions/view";
5
5
  import { useDispatchWithModulesData } from "./../hooks/useDispatchWithModulesData";
6
6
 
7
7
  /* This hook is used when a component has a dynamic number of fields that can be edited (e.g.: Orders' Custom Configuration Parameters).
@@ -25,7 +25,7 @@ const useMultipleFieldEditState = (entityId, sectionName, initialValues, extende
25
25
  const mergedValidationRules = { ...validationRules, ...extendedValidationRules };
26
26
  const modifiedStates = useSelector(getModifiedModels(entityId))[sectionName] || {};
27
27
 
28
- const useDynamicFieldState = (id, fieldName, errorTypes = []) => {
28
+ const useDynamicFieldState = (id, fieldName, errorTypes = [], fieldDependencies = {}) => {
29
29
  const keys = [id, fieldName];
30
30
 
31
31
  const initialValue = initialValues[id]?.[fieldName] ?? "";
@@ -42,12 +42,15 @@ const useMultipleFieldEditState = (entityId, sectionName, initialValues, extende
42
42
  dispatch(setEditModelField, [keys, initialValue, initialValue, entityId, sectionName]);
43
43
  };
44
44
 
45
- const isEditStateValid = value => {
45
+ const isEditStateValid = (value, dependencies = {}) => {
46
46
  const valueToValidate = value ?? editState.value;
47
47
 
48
48
  let hasAnyValidationErrors = false;
49
49
  errorTypes.forEach(errorType => {
50
- const isValid = mergedValidationRules[errorType](valueToValidate, id, fieldName);
50
+ const isValid = mergedValidationRules[errorType](valueToValidate, id, fieldName, {
51
+ ...fieldDependencies,
52
+ ...dependencies,
53
+ });
51
54
 
52
55
  if (isValid === false) {
53
56
  dispatch(setEditModelFieldError, [keys, errorType, entityId, sectionName]);
@@ -57,6 +60,10 @@ const useMultipleFieldEditState = (entityId, sectionName, initialValues, extende
57
60
  }
58
61
  });
59
62
 
63
+ if (!hasAnyValidationErrors) {
64
+ dispatch(removeEditModelFieldError, [keys, entityId, sectionName]);
65
+ }
66
+
60
67
  return !hasAnyValidationErrors;
61
68
  };
62
69
 
@@ -5,7 +5,7 @@ import Immutable from "immutable";
5
5
  import sinon from "sinon";
6
6
  import { mount } from "enzyme";
7
7
  import * as useDispatchWithModulesDataMock from "./useDispatchWithModulesData";
8
- import { setEditModelField, setEditModelFieldError } from "./../actions/view";
8
+ import { removeEditModelFieldError, setEditModelField, setEditModelFieldError } from "./../actions/view";
9
9
  import { validationErrorTypes } from "./../constants";
10
10
  import _ from "lodash";
11
11
 
@@ -404,6 +404,54 @@ describe("useMultipleFieldEditState", () => {
404
404
  }
405
405
  });
406
406
 
407
+ it("Updates edit view value and reset error correctly with custom validation rules when validation was passed", () => {
408
+ const useDispatchWithModulesDataSpy = sinon.spy();
409
+ const useDispatchWithModulesDataStub = sinon
410
+ .stub(useDispatchWithModulesDataMock, "useDispatchWithModulesData")
411
+ .returns(useDispatchWithModulesDataSpy);
412
+
413
+ try {
414
+ // TODOJOC
415
+ const mountedComponent = mountComponent();
416
+
417
+ const fieldComponent = mountedComponent.find(`#id1-prop1-update`);
418
+
419
+ const event = {
420
+ target: {
421
+ value: "anotherValue",
422
+ },
423
+ };
424
+
425
+ fieldComponent.invoke("onClick")(event);
426
+
427
+ const resetEvent = {
428
+ target: {
429
+ value: "custom",
430
+ },
431
+ };
432
+
433
+ fieldComponent.invoke("onClick")(resetEvent);
434
+
435
+ const id = "id1";
436
+ const fieldName = "prop1";
437
+ const initialFieldValue = fieldInitialValues[id][fieldName];
438
+
439
+ expect(useDispatchWithModulesDataSpy, "to have a call satisfying", {
440
+ args: [setEditModelField, [[id, fieldName], "anotherValue", initialFieldValue, entityId, sectionName]],
441
+ });
442
+
443
+ expect(useDispatchWithModulesDataSpy, "to have a call satisfying", {
444
+ args: [setEditModelFieldError, [[id, fieldName], "customRule", entityId, sectionName]],
445
+ });
446
+
447
+ expect(useDispatchWithModulesDataSpy, "to have a call satisfying", {
448
+ args: [removeEditModelFieldError, [[id, fieldName], entityId, sectionName]],
449
+ });
450
+ } finally {
451
+ useDispatchWithModulesDataStub.restore();
452
+ }
453
+ });
454
+
407
455
  it.each([
408
456
  ["id1", "prop1"],
409
457
  ["id1", "prop2"],
@@ -1,7 +1,7 @@
1
1
  import Immutable from "immutable";
2
2
  import { normalize } from "normalizr";
3
3
  import { definitionsListSchema } from "../schemas/definitions";
4
- import { profileAttributeGroupsListSchema } from "../schemas/metadata";
4
+ import { orderAttributeGroupsListSchema, profileAttributeGroupsListSchema } from "../schemas/metadata";
5
5
  import { productDefinitionsListSchema } from "../schemas/productDefinitions";
6
6
  import {
7
7
  GET_ORDER_LOOKUPS_SUCCESS,
@@ -26,6 +26,7 @@ import {
26
26
  UPDATE_PROFILE_DEFINITION_SUCCESS,
27
27
  GET_ORDER_LOOKUP_SUCCESS,
28
28
  GET_CUSTOMER_LOOKUP_SUCCESS,
29
+ GET_ORDER_ATTRIBUTE_GROUPS_SUCCESS,
29
30
  } from "../actions/metadata";
30
31
 
31
32
  export const ORDER_MODULE_NAME = "order";
@@ -154,6 +155,10 @@ const metadataReducer = (state = initialState, action) => {
154
155
  const normalizedData = normalize(action.payload?.profileAttributeGroups, profileAttributeGroupsListSchema);
155
156
  return state.set("profileAttributeGroups", Immutable.fromJS(normalizedData.entities.metadata));
156
157
  }
158
+ case GET_ORDER_ATTRIBUTE_GROUPS_SUCCESS: {
159
+ const normalizedData = normalize(action.payload?.orderAttributeGroups, orderAttributeGroupsListSchema);
160
+ return state.set("orderAttributeGroups", Immutable.fromJS(normalizedData.entities.metadata));
161
+ }
157
162
  case SAVE_ORDER_LOOKUP_SUCCESS:
158
163
  case GET_ORDER_LOOKUP_SUCCESS:
159
164
  return lookupReducerHelper.getLookupsQuerySuccess(ORDER_MODULE_NAME, state, {
@@ -22,6 +22,7 @@ import {
22
22
  ADD_CUSTOMER_LOOKUP_SUCCESS,
23
23
  GET_ORDER_LOOKUP_SUCCESS,
24
24
  GET_CUSTOMER_LOOKUP_SUCCESS,
25
+ GET_ORDER_ATTRIBUTE_GROUPS_SUCCESS,
25
26
  } from "../actions/metadata";
26
27
  import reducer from "./metadata";
27
28
 
@@ -1553,3 +1554,33 @@ describe("profileAttributeGroups", () => {
1553
1554
  );
1554
1555
  });
1555
1556
  });
1557
+
1558
+ describe("orderAttributeGroups", () => {
1559
+ it("saves orderAttributeGroups metadata", () => {
1560
+ const orderAttributeGroupsPayload = {
1561
+ displayOrder: 1,
1562
+ name: "Default",
1563
+ displayName: {
1564
+ "en-US": "Default",
1565
+ "fr-CA": "Défaut",
1566
+ },
1567
+ isSystem: false,
1568
+ };
1569
+ const oldState = Immutable.fromJS({
1570
+ orderAttributeGroups: {},
1571
+ });
1572
+ const action = {
1573
+ type: GET_ORDER_ATTRIBUTE_GROUPS_SUCCESS,
1574
+ payload: { orderAttributeGroups: [orderAttributeGroupsPayload] },
1575
+ };
1576
+ const newState = reducer(oldState, action);
1577
+ return expect(newState, "not to be", oldState).and(
1578
+ "to equal",
1579
+ Immutable.fromJS({
1580
+ orderAttributeGroups: {
1581
+ Default: orderAttributeGroupsPayload,
1582
+ },
1583
+ }),
1584
+ );
1585
+ });
1586
+ });