orc-shared 1.2.0-dev.1 → 1.2.0-dev.5

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 (194) hide show
  1. package/dist/actions/applications.js +1 -1
  2. package/dist/actions/authentication.js +1 -1
  3. package/dist/actions/countries.js +1 -1
  4. package/dist/actions/locale.js +1 -1
  5. package/dist/actions/makeApiAction.js +1 -1
  6. package/dist/actions/metadata.js +1 -1
  7. package/dist/actions/modules.js +63 -1
  8. package/dist/actions/navigation.js +1 -1
  9. package/dist/actions/requestsApi.js +8 -8
  10. package/dist/actions/scopes.js +59 -22
  11. package/dist/actions/timezones.js +1 -1
  12. package/dist/actions/toasts.js +1 -1
  13. package/dist/actions/versionInfo.js +1 -1
  14. package/dist/actions/view.js +1 -1
  15. package/dist/buildStore.js +1 -1
  16. package/dist/components/AppFrame/About.js +4 -3
  17. package/dist/components/AppFrame/MenuItem.js +6 -15
  18. package/dist/components/AppFrame/Preferences.js +1 -1
  19. package/dist/components/AppFrame/Sidebar.js +20 -9
  20. package/dist/components/AppFrame/Topbar.js +1 -1
  21. package/dist/components/ApplicationModuleLoader.js +143 -0
  22. package/dist/components/Authenticate.js +13 -13
  23. package/dist/components/CategoryList.js +1 -1
  24. package/dist/components/Checkbox.js +1 -1
  25. package/dist/components/DropMenu/Menu.js +1 -1
  26. package/dist/components/DropMenu/index.js +1 -1
  27. package/dist/components/Form/FieldList.js +1 -1
  28. package/dist/components/Form/Form.js +1 -1
  29. package/dist/components/Form/Inputs/Button.js +1 -1
  30. package/dist/components/Form/Inputs/FieldButtons.js +1 -1
  31. package/dist/components/Form/Inputs/Number.js +1 -1
  32. package/dist/components/Form/Inputs/ReadOnly.js +1 -1
  33. package/dist/components/Form/Inputs/SmallButton.js +1 -1
  34. package/dist/components/Form/Inputs/Text.js +1 -1
  35. package/dist/components/Form/Inputs/Time.js +1 -1
  36. package/dist/components/Form/Inputs/Toggles.js +1 -1
  37. package/dist/components/Form/Inputs/Translation.js +1 -1
  38. package/dist/components/List/HeadCell.js +1 -1
  39. package/dist/components/List/List.js +1 -1
  40. package/dist/components/List/Row.js +1 -1
  41. package/dist/components/MaterialUI/DataDisplay/List.js +1 -1
  42. package/dist/components/MaterialUI/DataDisplay/NotificationProps.js +1 -1
  43. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Translations.js +1 -1
  44. package/dist/components/MaterialUI/DataDisplay/Table.js +1 -1
  45. package/dist/components/MaterialUI/DataDisplay/TableProps.js +1 -1
  46. package/dist/components/MaterialUI/DataDisplay/TransferList.js +1 -1
  47. package/dist/components/MaterialUI/DataDisplay/chipProps.js +1 -1
  48. package/dist/components/MaterialUI/DataDisplay/collapsableListProps.js +1 -1
  49. package/dist/components/MaterialUI/DataDisplay/dividerProps.js +1 -1
  50. package/dist/components/MaterialUI/DataDisplay/index.js +1 -1
  51. package/dist/components/MaterialUI/DataDisplay/modalProps.js +1 -1
  52. package/dist/components/MaterialUI/DataDisplay/useTableSelection.js +1 -1
  53. package/dist/components/MaterialUI/Feedback/useNotification.js +1 -1
  54. package/dist/components/MaterialUI/Inputs/AutocompleteProps.js +1 -1
  55. package/dist/components/MaterialUI/Inputs/CheckboxGroupProps.js +1 -1
  56. package/dist/components/MaterialUI/Inputs/CheckboxProps.js +1 -1
  57. package/dist/components/MaterialUI/Inputs/InputBase.js +1 -1
  58. package/dist/components/MaterialUI/Inputs/InputBaseProps.js +1 -1
  59. package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -1
  60. package/dist/components/MaterialUI/Inputs/RadioProps.js +1 -1
  61. package/dist/components/MaterialUI/Inputs/SelectProps.js +1 -1
  62. package/dist/components/MaterialUI/Inputs/Switch.js +1 -1
  63. package/dist/components/MaterialUI/Inputs/SwitchProps.js +1 -1
  64. package/dist/components/MaterialUI/Inputs/TimePicker.js +1 -1
  65. package/dist/components/MaterialUI/Inputs/createInput.js +1 -1
  66. package/dist/components/MaterialUI/Inputs/index.js +1 -1
  67. package/dist/components/MaterialUI/Inputs/standaloneRadioProps.js +1 -1
  68. package/dist/components/MaterialUI/Navigation/DropDownMenuProps.js +1 -1
  69. package/dist/components/MaterialUI/Navigation/ExternalLink.js +113 -0
  70. package/dist/components/MaterialUI/Surfaces/expansionPanelProps.js +1 -1
  71. package/dist/components/MaterialUI/Surfaces/paperProps.js +1 -1
  72. package/dist/components/MaterialUI/muiThemes.js +5 -0
  73. package/dist/components/MaterialUI/textProps.js +1 -1
  74. package/dist/components/Modules.js +126 -41
  75. package/dist/components/MultiSelector.js +1 -1
  76. package/dist/components/Navigation/Bar.js +1 -1
  77. package/dist/components/Navigation/Tab.js +1 -1
  78. package/dist/components/Navigation/useNavigationState.js +1 -1
  79. package/dist/components/Placeholder.js +1 -1
  80. package/dist/components/Routing/FullPage.js +3 -1
  81. package/dist/components/Routing/Page.js +5 -3
  82. package/dist/components/Routing/Segment.js +1 -1
  83. package/dist/components/Routing/SegmentPage.js +1 -1
  84. package/dist/components/Routing/withWaypointing.js +6 -2
  85. package/dist/components/Scope/ScopeNode.js +1 -1
  86. package/dist/components/Scope/Selector.js +1 -1
  87. package/dist/components/Scope/index.js +1 -1
  88. package/dist/components/Scope/useScopeConfirmationModalState.js +7 -16
  89. package/dist/components/Scope/useScopeData.js +4 -13
  90. package/dist/components/Scope/useScopeSelect.js +1 -1
  91. package/dist/components/Selector.js +1 -1
  92. package/dist/components/Spritesheet.js +1 -1
  93. package/dist/components/Switch.js +1 -1
  94. package/dist/components/Text.js +1 -1
  95. package/dist/components/ToastList.js +1 -1
  96. package/dist/components/Toolbar.js +1 -1
  97. package/dist/components/Treeview/Label.js +1 -1
  98. package/dist/components/Treeview/Leaf.js +1 -1
  99. package/dist/components/Treeview/Node.js +1 -1
  100. package/dist/components/Treeview/settings.js +1 -1
  101. package/dist/constants.js +19 -2
  102. package/dist/content/icons/orckestra-icon.svg +5 -0
  103. package/dist/content/iconsSheet.svg +8 -0
  104. package/dist/content/orckestra-logo-white.png +0 -0
  105. package/dist/hocs/withInfiniteScroll.js +1 -1
  106. package/dist/hooks/useDispatchWithModulesData.js +1 -1
  107. package/dist/hooks/useEditState.js +1 -1
  108. package/dist/hooks/useFullEntityEditState.js +1 -1
  109. package/dist/hooks/useLabelMessage.js +1 -1
  110. package/dist/hooks/useNavigationHandler.js +1 -1
  111. package/dist/hooks/useSelectorAndUnwrap.js +1 -1
  112. package/dist/reducers/metadata.js +1 -1
  113. package/dist/reducers/modules.js +39 -1
  114. package/dist/reducers/scopes.js +27 -0
  115. package/dist/reducers/settings.js +31 -2
  116. package/dist/schemas/countries.js +1 -1
  117. package/dist/schemas/definitions.js +1 -1
  118. package/dist/schemas/metadata.js +1 -1
  119. package/dist/schemas/productDefinitions.js +1 -1
  120. package/dist/schemas/timezones.js +1 -1
  121. package/dist/selectors/applications.js +1 -1
  122. package/dist/selectors/authentication.js +57 -18
  123. package/dist/selectors/countries.js +1 -1
  124. package/dist/selectors/locale.js +1 -1
  125. package/dist/selectors/metadata.js +1 -1
  126. package/dist/selectors/modules.js +15 -1
  127. package/dist/selectors/navigation.js +1 -1
  128. package/dist/selectors/requests.js +1 -1
  129. package/dist/selectors/scope.js +7 -1
  130. package/dist/selectors/settings.js +13 -1
  131. package/dist/selectors/versionInfo.js +1 -1
  132. package/dist/selectors/view.js +1 -1
  133. package/dist/spawnerMiddleware.js +1 -1
  134. package/dist/utils/displayModeHelper.js +1 -1
  135. package/dist/utils/localizationHelper.js +1 -1
  136. package/dist/utils/mapHelper.js +1 -1
  137. package/dist/utils/modelValidationHelper.js +1 -1
  138. package/dist/utils/parseHelper.js +1 -1
  139. package/dist/utils/propertyHelper.js +2 -2
  140. package/dist/utils/propertyValidator.js +1 -1
  141. package/dist/utils/setTranslationWithFallback.js +1 -1
  142. package/dist/utils/testUtils.js +2 -1
  143. package/dist/utils/unwrapImmutable.js +1 -1
  144. package/dist/utils/urlHelper.js +1 -1
  145. package/package.json +6 -5
  146. package/src/actions/modules.js +30 -0
  147. package/src/actions/modules.test.js +50 -1
  148. package/src/actions/scopes.js +33 -7
  149. package/src/actions/scopes.test.js +84 -14
  150. package/src/components/AppFrame/About.js +2 -2
  151. package/src/components/AppFrame/AppFrame.test.js +9 -0
  152. package/src/components/AppFrame/MenuItem.js +3 -5
  153. package/src/components/AppFrame/MenuItem.test.js +2 -24
  154. package/src/components/AppFrame/Sidebar.js +11 -12
  155. package/src/components/AppFrame/Sidebar.test.js +18 -0
  156. package/src/components/ApplicationModuleLoader.js +52 -0
  157. package/src/components/ApplicationModuleLoader.test.js +149 -0
  158. package/src/components/Authenticate.js +5 -4
  159. package/src/components/Authenticate.test.js +23 -4
  160. package/src/components/MaterialUI/Navigation/ExternalLink.js +25 -0
  161. package/src/components/MaterialUI/Navigation/ExternalLink.test.js +26 -0
  162. package/src/components/MaterialUI/muiThemes.js +5 -0
  163. package/src/components/Modules.js +103 -20
  164. package/src/components/Modules.test.js +315 -28
  165. package/src/components/Provision.test.js +34 -0
  166. package/src/components/Routing/FullPage.js +2 -1
  167. package/src/components/Routing/FullPage.test.js +23 -0
  168. package/src/components/Routing/Page.js +2 -2
  169. package/src/components/Routing/Page.test.js +20 -0
  170. package/src/components/Routing/Segment.js +1 -1
  171. package/src/components/Routing/withWaypointing.js +2 -2
  172. package/src/components/Routing/withWaypointing.test.js +33 -5
  173. package/src/components/Scope/useScopeConfirmationModalState.js +5 -16
  174. package/src/components/Scope/useScopeConfirmationModalState.test.js +39 -13
  175. package/src/components/Scope/useScopeData.js +0 -3
  176. package/src/components/Scope/useScopeData.test.js +0 -27
  177. package/src/constants.js +15 -0
  178. package/src/content/icons/orckestra-icon.svg +5 -0
  179. package/src/content/iconsSheet.svg +8 -0
  180. package/src/content/orckestra-logo-white.png +0 -0
  181. package/src/hocs/withScopeData.test.js +0 -31
  182. package/src/reducers/modules.js +48 -2
  183. package/src/reducers/modules.test.js +117 -2
  184. package/src/reducers/scopes.js +30 -0
  185. package/src/reducers/scopes.test.js +45 -1
  186. package/src/reducers/settings.js +26 -2
  187. package/src/reducers/settings.test.js +74 -6
  188. package/src/selectors/authentication.js +53 -27
  189. package/src/selectors/authentication.test.js +600 -12
  190. package/src/selectors/modules.js +7 -0
  191. package/src/selectors/modules.test.js +16 -1
  192. package/src/selectors/scope.js +2 -0
  193. package/src/selectors/scope.test.js +5 -0
  194. package/src/selectors/settings.js +6 -0
@@ -7,17 +7,21 @@ import TabBar from "./MaterialUI/Navigation/TabBar";
7
7
  import { TestWrapper, createMuiTheme } from "./../utils/testUtils";
8
8
  import sinon from "sinon";
9
9
  import { createMemoryHistory } from "history";
10
+ import { INITIALIZE_FIRST_MODULE_SCOPE, SET_MODULE_AS_VISIBLE, SET_ROUTING_PERFORMED } from "../actions/modules";
11
+ import { resetLastScope } from "../selectors/navigation";
10
12
 
11
13
  describe("Modules", () => {
12
- let modules, Mod2, Mod3, Page1, Page2, Page3, store, state;
13
-
14
- const match = {
15
- url: "/TestScope/users/page1",
16
- path: "/:scope/users/page1",
17
- params: { scope: "TestScope" },
18
- };
14
+ let modules, Mod2, Mod3, Page1, Page2, Page3, store, state, match;
19
15
 
20
16
  beforeEach(() => {
17
+ resetLastScope();
18
+
19
+ match = {
20
+ url: "/TestScope/users/page1",
21
+ path: "/:scope/users/page1",
22
+ params: { scope: "TestScope" },
23
+ };
24
+
21
25
  Mod2 = () => <div id="Mod2" />;
22
26
  Mod3 = () => <div id="Mod3" />;
23
27
  Page1 = () => <div id="Page1" />;
@@ -68,9 +72,6 @@ describe("Modules", () => {
68
72
  router: {
69
73
  location: {},
70
74
  },
71
- modules: {
72
- tree: {},
73
- },
74
75
  view: {
75
76
  edit: {
76
77
  users: {},
@@ -83,6 +84,7 @@ describe("Modules", () => {
83
84
  },
84
85
  settings: {
85
86
  defaultScope: "myScope",
87
+ modules: ["users", "demos", "photos"],
86
88
  },
87
89
  scopes: {
88
90
  TestScope: {
@@ -93,6 +95,23 @@ describe("Modules", () => {
93
95
  children: ["test2"],
94
96
  isAuthorizedScope: true,
95
97
  },
98
+ TestScope1: {
99
+ id: "TestScope1",
100
+ name: { "en-CA": "Test 1" },
101
+ foo: false,
102
+ bar: false,
103
+ children: ["test2"],
104
+ isAuthorizedScope: false,
105
+ },
106
+ },
107
+ modules: {
108
+ tree: {},
109
+ visibleModules: ["users", "demos", "photos"],
110
+ lastScopeAndModuleSelection: {
111
+ scope: "TestScope",
112
+ moduleName: null,
113
+ routingPerformed: false,
114
+ },
96
115
  },
97
116
  locale: {
98
117
  locale: null,
@@ -104,7 +123,7 @@ describe("Modules", () => {
104
123
  });
105
124
  store = {
106
125
  subscribe: () => {},
107
- dispatch: () => {},
126
+ dispatch: sinon.spy().named("dispatch"),
108
127
  getState: () => state,
109
128
  };
110
129
  });
@@ -148,6 +167,144 @@ describe("Modules", () => {
148
167
  expect(mount(component).childNodes, "to satisfy", expected);
149
168
  });
150
169
 
170
+ it("renders a module table when routing is required for photos", () => {
171
+ const history = createMemoryHistory({ initialEntries: ["/TestScope/demos?arg=data"] });
172
+ const mockHistoryPush = sinon.spy(history, "push");
173
+
174
+ const component = (
175
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
176
+ <Modules modules={modules} scope="TestScope" />
177
+ </TestWrapper>
178
+ );
179
+
180
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "moduleName"], "photos");
181
+
182
+ const module = {
183
+ icon: "image",
184
+ label: "Module 2",
185
+ href: "/TestScope/photos",
186
+ mappedFrom: "/TestScope/photos",
187
+ active: true,
188
+ };
189
+
190
+ const expected = [
191
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
192
+ <TabBar module={module} pages={[]} />
193
+ </TestWrapper>,
194
+ <Mod2 />,
195
+ ];
196
+
197
+ expect(mount(component).childNodes, "to satisfy", expected);
198
+
199
+ expect(mockHistoryPush, "to have calls satisfying", [{ args: ["/TestScope/photos"] }]);
200
+
201
+ expect(store.dispatch, "to have a call satisfying", { args: [{ type: SET_ROUTING_PERFORMED }] });
202
+ });
203
+
204
+ it("renders a module table when routing is required for photos when already set correctly", () => {
205
+ match.url = "/TestScope/demos";
206
+ match.path = "/:scope/demos";
207
+
208
+ const component = (
209
+ <TestWrapper
210
+ provider={{ store }}
211
+ memoryRouter={{ initialEntries: ["/TestScope/demos"] }}
212
+ intlProvider
213
+ stylesProvider
214
+ muiThemeProvider={{ theme }}
215
+ >
216
+ <Modules modules={modules} scope="TestScope" />
217
+ </TestWrapper>
218
+ );
219
+
220
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "routingPerformed"], true);
221
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "moduleName"], "demos");
222
+
223
+ const module = {
224
+ icon: "cloud",
225
+ label: "Module 3",
226
+ href: "/TestScope/demos",
227
+ mappedFrom: "/TestScope/demos",
228
+ active: true,
229
+ };
230
+
231
+ const expected = [
232
+ <TestWrapper
233
+ provider={{ store }}
234
+ memoryRouter={{ initialEntries: ["/TestScope/demos"] }}
235
+ intlProvider
236
+ stylesProvider
237
+ muiThemeProvider={{ theme }}
238
+ >
239
+ <TabBar module={module} pages={[]} />
240
+ </TestWrapper>,
241
+ <Mod3 />,
242
+ ];
243
+
244
+ expect(mount(component).childNodes, "to satisfy", expected);
245
+ });
246
+
247
+ it("renders a module table on first available module if requested one not visible", () => {
248
+ const history = createMemoryHistory({ initialEntries: ["/TestScope/demos?arg=data"] });
249
+ const mockHistoryPush = sinon.spy(history, "push");
250
+
251
+ const component = (
252
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
253
+ <Modules modules={modules} scope="TestScope" />
254
+ </TestWrapper>
255
+ );
256
+
257
+ state = state.setIn(["modules", "visibleModules"], Immutable.fromJS(["demos", "users"]));
258
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "moduleName"], "photos");
259
+
260
+ const module = {
261
+ icon: "cloud",
262
+ label: "Module 3",
263
+ href: "/TestScope/demos",
264
+ mappedFrom: "/TestScope/demos",
265
+ active: true,
266
+ };
267
+
268
+ const expected = [
269
+ <TestWrapper
270
+ provider={{ store }}
271
+ memoryRouter={{ initialEntries: ["/TestScope/demos"] }}
272
+ intlProvider
273
+ stylesProvider
274
+ muiThemeProvider={{ theme }}
275
+ >
276
+ <TabBar module={module} pages={[]} />
277
+ </TestWrapper>,
278
+ <Mod3 />,
279
+ ];
280
+
281
+ expect(mount(component).childNodes, "to satisfy", expected);
282
+
283
+ expect(mockHistoryPush, "to have calls satisfying", [{ args: ["/TestScope/demos"] }]);
284
+
285
+ expect(store.dispatch, "to have a call satisfying", { args: [{ type: SET_ROUTING_PERFORMED }] });
286
+ });
287
+
288
+ it("renders a module table when routing cannot be performed without visible modules", () => {
289
+ const history = createMemoryHistory({ initialEntries: ["/TestScope/demos?arg=data"] });
290
+ const mockHistoryPush = sinon.spy(history, "push");
291
+
292
+ const component = (
293
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
294
+ <Modules modules={modules} scope="TestScope" />
295
+ </TestWrapper>
296
+ );
297
+
298
+ state = state.setIn(["modules", "visibleModules"], Immutable.fromJS([]));
299
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "moduleName"], "photos");
300
+
301
+ mount(component);
302
+
303
+ expect(mockHistoryPush, "not to have calls satisfying", [{ args: ["/TestScope/photos"] }]);
304
+
305
+ expect(store.dispatch, "not to have calls satisfying", [{ args: [{ type: SET_ROUTING_PERFORMED }] }]);
306
+ });
307
+
151
308
  it("renders a module table as a routing system (user route)", () => {
152
309
  const module = {
153
310
  icon: "user",
@@ -270,6 +427,80 @@ describe("Modules", () => {
270
427
  );
271
428
  });
272
429
 
430
+ it("properly sets visible modules and scope when not already set", () => {
431
+ modules.users.hide = () => () => true;
432
+ modules.demos.hide = false;
433
+
434
+ const component = (
435
+ <TestWrapper
436
+ provider={{ store }}
437
+ memoryRouter={{ initialEntries: ["/TestScope/demos"] }}
438
+ intlProvider
439
+ stylesProvider
440
+ muiThemeProvider={{ theme }}
441
+ >
442
+ <Modules modules={modules} scope="TestScope" />
443
+ </TestWrapper>
444
+ );
445
+
446
+ state = state.setIn(["modules", "visibleModules"], Immutable.fromJS([]));
447
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "moduleName"], null);
448
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "scope"], null);
449
+
450
+ mount(component);
451
+
452
+ expect(store.dispatch, "to have a call satisfying", { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "demos" }] });
453
+
454
+ expect(store.dispatch, "to have a call satisfying", { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "photos" }] });
455
+
456
+ expect(store.dispatch, "not to have calls satisfying", [
457
+ { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "users" }] },
458
+ ]);
459
+
460
+ expect(store.dispatch, "to have a call satisfying", {
461
+ args: [{ type: INITIALIZE_FIRST_MODULE_SCOPE, payload: "TestScope" }],
462
+ });
463
+ });
464
+
465
+ it("Does not set visible modules and scope when route is not yet initialized", () => {
466
+ modules.users.hide = () => () => true;
467
+ modules.demos.hide = false;
468
+
469
+ state = state.setIn(["navigation", "route", "match", "params", "scope"], null);
470
+
471
+ state = state.setIn(["modules", "visibleModules"], Immutable.fromJS([]));
472
+
473
+ const component = (
474
+ <TestWrapper
475
+ provider={{ store }}
476
+ memoryRouter={{ initialEntries: ["/TestScope/demos"] }}
477
+ intlProvider
478
+ stylesProvider
479
+ muiThemeProvider={{ theme }}
480
+ >
481
+ <Modules modules={modules} scope="TestScope" />
482
+ </TestWrapper>
483
+ );
484
+
485
+ mount(component);
486
+
487
+ expect(store.dispatch, "not to have calls satisfying", [
488
+ { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "demos" }] },
489
+ ]);
490
+
491
+ expect(store.dispatch, "not to have calls satisfying", [
492
+ { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "photos" }] },
493
+ ]);
494
+
495
+ expect(store.dispatch, "not to have calls satisfying", [
496
+ { args: [{ type: SET_MODULE_AS_VISIBLE, payload: "users" }] },
497
+ ]);
498
+
499
+ expect(store.dispatch, "not to have calls satisfying", [
500
+ { args: [{ type: INITIALIZE_FIRST_MODULE_SCOPE, payload: "TestScope" }] },
501
+ ]);
502
+ });
503
+
273
504
  describe("with custom href", () => {
274
505
  beforeEach(() => {
275
506
  state = Immutable.fromJS({
@@ -286,9 +517,16 @@ describe("Modules", () => {
286
517
  },
287
518
  settings: {
288
519
  defaultScope: "myScope",
520
+ modules: ["users", "demos", "photos"],
289
521
  },
290
522
  modules: {
291
523
  tree: {},
524
+ visibleModules: ["users", "demos", "photos"],
525
+ lastScopeAndModuleSelection: {
526
+ scope: "TestScope",
527
+ moduleName: null,
528
+ routingPerformed: false,
529
+ },
292
530
  },
293
531
  view: {
294
532
  edit: {
@@ -387,9 +625,16 @@ describe("Modules", () => {
387
625
  },
388
626
  settings: {
389
627
  defaultScope: "TestScope2",
628
+ modules: ["users", "demos", "photos"],
390
629
  },
391
630
  modules: {
392
631
  tree: {},
632
+ visibleModules: ["users", "demos", "photos"],
633
+ lastScopeAndModuleSelection: {
634
+ scope: "TestScope",
635
+ moduleName: null,
636
+ routingPerformed: false,
637
+ },
393
638
  },
394
639
  view: {
395
640
  edit: {
@@ -440,13 +685,7 @@ describe("Modules", () => {
440
685
  });
441
686
 
442
687
  describe("Module", () => {
443
- let config, Page1, store, state;
444
-
445
- const match = {
446
- url: "/TestScope/users/page1",
447
- path: "/:scope/users/page1",
448
- params: { scope: "TestScope" },
449
- };
688
+ let config, Page1, store, state, match;
450
689
 
451
690
  let history, pushSpy;
452
691
  beforeAll(() => {
@@ -456,6 +695,12 @@ describe("Module", () => {
456
695
  });
457
696
 
458
697
  beforeEach(() => {
698
+ match = {
699
+ url: "/TestScope/users/page1",
700
+ path: "/:scope/users/page1",
701
+ params: { scope: "TestScope" },
702
+ };
703
+
459
704
  config = {
460
705
  label: "Module 1",
461
706
  icon: "user",
@@ -482,6 +727,12 @@ describe("Module", () => {
482
727
  },
483
728
  modules: {
484
729
  tree: {},
730
+ visibleModules: ["firstModule", "module123"],
731
+ lastScopeAndModuleSelection: {
732
+ scope: "TestScope",
733
+ moduleName: "Profiles",
734
+ routingPerformed: true,
735
+ },
485
736
  },
486
737
  view: {
487
738
  edit: {
@@ -523,49 +774,85 @@ describe("Module", () => {
523
774
 
524
775
  const theme = createMuiTheme();
525
776
 
526
- it("Calls pushes to root when unauthorized user trying to access hidden module", () => {
527
- //config.hide = false;
777
+ it("Calls pushes to first module when unauthorized user trying to access hidden module", () => {
528
778
  const component = (
529
779
  <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
530
- <Module config={config} match={match} path={match.path} location={{}} />
780
+ <Module id="notVisibleModule" config={config} match={match} path={match.path} location={{}} />
531
781
  </TestWrapper>
532
782
  );
533
783
 
534
784
  mount(component);
535
785
 
536
- expect(history.push, "to have calls satisfying", [{ args: ["/"] }]);
786
+ expect(history.push, "to have calls satisfying", [{ args: ["/TestScope/firstModule"] }]);
537
787
 
538
788
  pushSpy.resetHistory();
539
789
  });
540
790
 
541
- it("Calls pushes to root when unauthorized user trying to access hidden module and hide property is a function which retrieves false", () => {
791
+ it("Does not call pushes to first module when unauthorized user trying to access hidden module and hide property is a function which retrieves false", () => {
542
792
  config.hide = () => false;
543
793
 
544
794
  const component = (
545
795
  <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
546
- <Module config={config} match={match} path={match.path} location={{}} />
796
+ <Module id="firstModule" config={config} match={match} path={match.path} location={{}} />
547
797
  </TestWrapper>
548
798
  );
549
799
 
550
800
  mount(component);
551
801
 
552
- expect(history.push, "not to have calls satisfying", [{ args: ["/"] }]);
802
+ expect(history.push, "not to have calls satisfying", [{ args: ["/TestScope/firstModule"] }]);
553
803
 
554
804
  pushSpy.resetHistory();
555
805
  });
556
806
 
557
- it("Calls pushes to root when unauthorized user trying to access hidden module and hide property is a function which retrieves true", () => {
807
+ it("Calls pushes to first module when unauthorized user trying to access hidden module and hide property is a function which retrieves true", () => {
808
+ config.hide = () => true;
809
+
810
+ const component = (
811
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
812
+ <Module id="notVisibleModule" config={config} match={match} path={match.path} location={{}} />
813
+ </TestWrapper>
814
+ );
815
+
816
+ mount(component);
817
+
818
+ expect(history.push, "to have calls satisfying", [{ args: ["/TestScope/firstModule"] }]);
819
+
820
+ pushSpy.resetHistory();
821
+ });
822
+
823
+ it("Does not call pushes to first module when already set to that location", () => {
824
+ state = state.setIn(["navigation", "route", "match", "url"], "/TestScope/firstModule");
825
+ state = state.setIn(["navigation", "route", "match", "path"], "/:scope/firstModule");
826
+
827
+ config.hide = () => true;
828
+
829
+ const component = (
830
+ <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
831
+ <Module id="notVisibleModule" config={config} match={match} path={match.path} location={{}} />
832
+ </TestWrapper>
833
+ );
834
+
835
+ mount(component);
836
+
837
+ expect(history.push, "not to have calls satisfying", [{ args: ["/TestScope/firstModule"] }]);
838
+
839
+ pushSpy.resetHistory();
840
+ });
841
+
842
+ it("Does not call pushes to first module when redirection is not yet completed to new scope", () => {
843
+ state = state.setIn(["modules", "lastScopeAndModuleSelection", "scope"], "newScope");
844
+
558
845
  config.hide = () => true;
559
846
 
560
847
  const component = (
561
848
  <TestWrapper provider={{ store }} router={{ history }} intlProvider stylesProvider muiThemeProvider={{ theme }}>
562
- <Module config={config} match={match} path={match.path} location={{}} />
849
+ <Module id="notVisibleModule" config={config} match={match} path={match.path} location={{}} />
563
850
  </TestWrapper>
564
851
  );
565
852
 
566
853
  mount(component);
567
854
 
568
- expect(history.push, "to have calls satisfying", [{ args: ["/"] }]);
855
+ expect(history.push, "not to have calls satisfying", [{ args: ["/TestScope/firstModule"] }]);
569
856
 
570
857
  pushSpy.resetHistory();
571
858
  });
@@ -6,6 +6,13 @@ import { spyOnConsole } from "../utils/testUtils";
6
6
  import Provision from "./Provision";
7
7
  import { createTheme } from "@material-ui/core/styles";
8
8
 
9
+ jest.mock("../utils/buildUrl", () => {
10
+ const modExport = {};
11
+ modExport.loadConfig = () => Promise.resolve({});
12
+ modExport.buildUrl = () => "URL";
13
+ return modExport;
14
+ });
15
+
9
16
  const fakeStore = {
10
17
  subscribe: listener => () => {},
11
18
  dispatch: action => action,
@@ -17,8 +24,35 @@ const fakeStore = {
17
24
  authentication: {
18
25
  name: "foo@bar.com",
19
26
  },
27
+ scopes: {
28
+ Global: {
29
+ name: { en: "Global", fr: "Global" },
30
+ id: "Global",
31
+ children: ["MyScope"],
32
+ currency: {
33
+ displayName: {
34
+ en: "Euro",
35
+ fr: "Euro",
36
+ },
37
+ },
38
+ defaultCulture: "en-US",
39
+ },
40
+ MyScope: {
41
+ name: { en: "First child", fr: "Premier fils" },
42
+ id: "FirstChild",
43
+ children: ["ChildScope"],
44
+ parentScopeId: "Global",
45
+ },
46
+ ChildScope: {
47
+ name: { en: "First grandchild", fr: "Premier petit-fils" },
48
+ id: "FirstGrandchild",
49
+ parentScopeId: "MyScope",
50
+ },
51
+ },
20
52
  settings: {
21
53
  defaultScope: "myScope",
54
+ loadedModulesScope: ["moduleA", "moduleB"],
55
+ modules: ["moduleA", "moduleB"],
22
56
  },
23
57
  }),
24
58
  replaceReducer: () => {},
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import Page from "./Page";
3
3
  import SegmentPage from "./SegmentPage";
4
4
 
5
- const FullPage = ({ path, config, location, match, modulePrependPath }) => {
5
+ const FullPage = ({ path, config, location, match, modulePrependPath, isVisible }) => {
6
6
  const { component, componentProps, pages = {}, segments, subpages, entityIdResolver } = config;
7
7
  if (segments) {
8
8
  return (
@@ -26,6 +26,7 @@ const FullPage = ({ path, config, location, match, modulePrependPath }) => {
26
26
  subpages={subpages}
27
27
  location={location}
28
28
  match={match}
29
+ isVisible={isVisible}
29
30
  modulePrependPath={modulePrependPath}
30
31
  />
31
32
  );
@@ -86,6 +86,29 @@ describe("Fullpage", () => {
86
86
  <View2 />,
87
87
  ));
88
88
 
89
+ it("does not show a page when not visible", () =>
90
+ expect(
91
+ <TestWrapper
92
+ provider={{ store }}
93
+ memoryRouter={{ initialEntries: ["/meep/snap/stuff"] }}
94
+ stylesProvider
95
+ muiThemeProvider={{ theme }}
96
+ >
97
+ <FullPage
98
+ path="/meep/snap"
99
+ config={{
100
+ component: View1,
101
+ }}
102
+ isVisible={false}
103
+ location={{ location: true }}
104
+ match={{ match: true }}
105
+ />
106
+ </TestWrapper>,
107
+ "when mounted",
108
+ "to be",
109
+ null,
110
+ ));
111
+
89
112
  it("shows a segment page if segments", () => {
90
113
  const location = {
91
114
  pathname: "/meep/snap/stuff",
@@ -5,8 +5,8 @@ import FullPage from "./FullPage";
5
5
  import SubPage from "./SubPage";
6
6
  import withWaypointing from "./withWaypointing";
7
7
 
8
- const Page = ({ component: View, path, pages = {}, subpages = {}, modulePrependPath }) => {
9
- const WrappedView = useMemo(() => withErrorBoundary(path)(withWaypointing(View)), [path, View]);
8
+ const Page = ({ component: View, path, pages = {}, subpages = {}, modulePrependPath, isVisible = true }) => {
9
+ const WrappedView = useMemo(() => withErrorBoundary(path)(withWaypointing(View, isVisible)), [path, View, isVisible]);
10
10
  return (
11
11
  <React.Fragment>
12
12
  <Switch>
@@ -70,6 +70,26 @@ describe("Page", () => {
70
70
  </TestWrapper>,
71
71
  ));
72
72
 
73
+ it("Does not show the page view when its path is matched but not visible", () =>
74
+ expect(
75
+ <TestWrapper provider={{ store }} intlProvider={intlProvider} stylesProvider muiThemeProvider={{ theme }}>
76
+ <MemoryRouter initialEntries={["/nabble"]}>
77
+ <Page
78
+ component={View}
79
+ path="/nabble"
80
+ isVisible={false}
81
+ pages={{
82
+ "/foo": { component: Sub1 },
83
+ "/bar": { component: Sub2 },
84
+ }}
85
+ />
86
+ </MemoryRouter>
87
+ </TestWrapper>,
88
+ "when mounted",
89
+ "to equal",
90
+ null,
91
+ ));
92
+
73
93
  it("shows nested page when its path is matched", () =>
74
94
  expect(
75
95
  <TestWrapper provider={{ store }} intlProvider={intlProvider} stylesProvider muiThemeProvider={{ theme }}>
@@ -6,7 +6,7 @@ const Segment = ({ location, match, config, root, modulePrependPath }) => {
6
6
  const { component, componentProps } = config;
7
7
  const path = location.pathname;
8
8
  const View = useMemo(
9
- () => withErrorBoundary(path)(withWaypointing(component, componentProps)),
9
+ () => withErrorBoundary(path)(withWaypointing(component, true, componentProps)),
10
10
  [path, component, componentProps],
11
11
  );
12
12
  return <View location={location} match={match} mapFrom={root} modulePrependPath={modulePrependPath} />;
@@ -6,7 +6,7 @@ import { setRoute, mapHref, setCurrentPrependPath } from "../../actions/navigati
6
6
  import useLoader from "../../hooks/useLoader";
7
7
 
8
8
  const withWaypointing =
9
- (Comp, componentProps = {}) =>
9
+ (Comp, isVisible = true, componentProps = {}) =>
10
10
  props => {
11
11
  const { match, mapFrom, modulePrependPath } = props;
12
12
  const location = useLocation();
@@ -24,7 +24,7 @@ const withWaypointing =
24
24
  }
25
25
  const cutout = state => selectRouteHref(state) === location.pathname;
26
26
  useLoader(loadActions, cutout);
27
- return <Comp {...props} {...componentProps} />;
27
+ return isVisible ? <Comp {...props} {...componentProps} /> : null;
28
28
  };
29
29
 
30
30
  export default withWaypointing;