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

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 (227) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +1 -1
  3. package/dist/actions/applications.js +1 -1
  4. package/dist/actions/authentication.js +1 -1
  5. package/dist/actions/countries.js +1 -1
  6. package/dist/actions/locale.js +1 -1
  7. package/dist/actions/makeApiAction.js +3 -3
  8. package/dist/actions/makeOrcApiAction.js +2 -2
  9. package/dist/actions/metadata.js +3 -3
  10. package/dist/actions/modules.js +63 -1
  11. package/dist/actions/navigation.js +3 -3
  12. package/dist/actions/requestsApi.js +8 -8
  13. package/dist/actions/scopes.js +59 -22
  14. package/dist/actions/timezones.js +1 -1
  15. package/dist/actions/toasts.js +1 -1
  16. package/dist/actions/versionInfo.js +1 -1
  17. package/dist/actions/view.js +1 -1
  18. package/dist/buildStore.js +3 -3
  19. package/dist/components/AppFrame/About.js +11 -6
  20. package/dist/components/AppFrame/MenuItem.js +6 -15
  21. package/dist/components/AppFrame/Preferences.js +3 -3
  22. package/dist/components/AppFrame/Sidebar.js +20 -9
  23. package/dist/components/AppFrame/Topbar.js +1 -1
  24. package/dist/components/ApplicationModuleLoader.js +143 -0
  25. package/dist/components/Authenticate.js +13 -13
  26. package/dist/components/CategoryList.js +1 -1
  27. package/dist/components/Checkbox.js +1 -1
  28. package/dist/components/DropMenu/Menu.js +1 -1
  29. package/dist/components/DropMenu/index.js +1 -1
  30. package/dist/components/Form/FieldList.js +3 -3
  31. package/dist/components/Form/Form.js +1 -1
  32. package/dist/components/Form/Inputs/Button.js +1 -1
  33. package/dist/components/Form/Inputs/FieldButtons.js +1 -1
  34. package/dist/components/Form/Inputs/Number.js +1 -1
  35. package/dist/components/Form/Inputs/ReadOnly.js +1 -1
  36. package/dist/components/Form/Inputs/SmallButton.js +1 -1
  37. package/dist/components/Form/Inputs/Text.js +1 -1
  38. package/dist/components/Form/Inputs/Time.js +1 -1
  39. package/dist/components/Form/Inputs/Toggles.js +1 -1
  40. package/dist/components/Form/Inputs/Translation.js +3 -3
  41. package/dist/components/List/HeadCell.js +1 -1
  42. package/dist/components/List/List.js +1 -1
  43. package/dist/components/List/Row.js +1 -1
  44. package/dist/components/List/enhanceColumnDefs.js +2 -2
  45. package/dist/components/MaterialUI/DataDisplay/List.js +1 -1
  46. package/dist/components/MaterialUI/DataDisplay/Notification.js +2 -2
  47. package/dist/components/MaterialUI/DataDisplay/NotificationProps.js +1 -1
  48. package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Translations.js +3 -3
  49. package/dist/components/MaterialUI/DataDisplay/Table.js +1 -1
  50. package/dist/components/MaterialUI/DataDisplay/TableProps.js +1 -1
  51. package/dist/components/MaterialUI/DataDisplay/TransferList.js +1 -1
  52. package/dist/components/MaterialUI/DataDisplay/chipProps.js +1 -1
  53. package/dist/components/MaterialUI/DataDisplay/collapsableListProps.js +1 -1
  54. package/dist/components/MaterialUI/DataDisplay/dividerProps.js +1 -1
  55. package/dist/components/MaterialUI/DataDisplay/index.js +1 -1
  56. package/dist/components/MaterialUI/DataDisplay/modalProps.js +1 -1
  57. package/dist/components/MaterialUI/DataDisplay/useTableSelection.js +3 -3
  58. package/dist/components/MaterialUI/Feedback/useNotification.js +1 -1
  59. package/dist/components/MaterialUI/Inputs/Autocomplete.js +2 -2
  60. package/dist/components/MaterialUI/Inputs/AutocompleteProps.js +1 -1
  61. package/dist/components/MaterialUI/Inputs/CheckboxGroupProps.js +1 -1
  62. package/dist/components/MaterialUI/Inputs/CheckboxProps.js +1 -1
  63. package/dist/components/MaterialUI/Inputs/InputBase.js +42 -5
  64. package/dist/components/MaterialUI/Inputs/InputBaseProps.js +1 -1
  65. package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -1
  66. package/dist/components/MaterialUI/Inputs/RadioProps.js +1 -1
  67. package/dist/components/MaterialUI/Inputs/Select.js +2 -2
  68. package/dist/components/MaterialUI/Inputs/SelectProps.js +1 -1
  69. package/dist/components/MaterialUI/Inputs/Switch.js +3 -3
  70. package/dist/components/MaterialUI/Inputs/SwitchProps.js +1 -1
  71. package/dist/components/MaterialUI/Inputs/TimePicker.js +1 -1
  72. package/dist/components/MaterialUI/Inputs/createInput.js +3 -3
  73. package/dist/components/MaterialUI/Inputs/index.js +1 -1
  74. package/dist/components/MaterialUI/Inputs/standaloneRadioProps.js +1 -1
  75. package/dist/components/MaterialUI/Navigation/DropDownMenuProps.js +1 -1
  76. package/dist/components/MaterialUI/Navigation/ExternalLink.js +113 -0
  77. package/dist/components/MaterialUI/ScopeSelector/ScopeSelector.js +8 -2
  78. package/dist/components/MaterialUI/Surfaces/expansionPanelProps.js +1 -1
  79. package/dist/components/MaterialUI/Surfaces/paperProps.js +1 -1
  80. package/dist/components/MaterialUI/muiThemes.js +7 -2
  81. package/dist/components/MaterialUI/textProps.js +1 -1
  82. package/dist/components/Modules.js +126 -41
  83. package/dist/components/MultiSelector.js +1 -1
  84. package/dist/components/Navigation/Bar.js +3 -3
  85. package/dist/components/Navigation/Tab.js +1 -1
  86. package/dist/components/Navigation/useNavigationState.js +3 -3
  87. package/dist/components/Placeholder.js +1 -1
  88. package/dist/components/Routing/FullPage.js +3 -1
  89. package/dist/components/Routing/Page.js +5 -3
  90. package/dist/components/Routing/Segment.js +1 -1
  91. package/dist/components/Routing/SegmentPage.js +1 -1
  92. package/dist/components/Routing/withWaypointing.js +8 -4
  93. package/dist/components/Scope/ScopeNode.js +1 -1
  94. package/dist/components/Scope/Selector.js +1 -1
  95. package/dist/components/Scope/index.js +3 -3
  96. package/dist/components/Scope/useScopeConfirmationModalState.js +7 -16
  97. package/dist/components/Scope/useScopeData.js +4 -13
  98. package/dist/components/Scope/useScopeSelect.js +1 -1
  99. package/dist/components/Selector.js +1 -1
  100. package/dist/components/Spritesheet.js +1 -1
  101. package/dist/components/Switch.js +1 -1
  102. package/dist/components/Text.js +1 -1
  103. package/dist/components/ToastList.js +1 -1
  104. package/dist/components/Toolbar.js +1 -1
  105. package/dist/components/Treeview/Label.js +1 -1
  106. package/dist/components/Treeview/Leaf.js +1 -1
  107. package/dist/components/Treeview/Node.js +3 -3
  108. package/dist/components/Treeview/index.js +2 -2
  109. package/dist/components/Treeview/settings.js +1 -1
  110. package/dist/constants.js +19 -2
  111. package/dist/content/icons/lock.svg +3 -0
  112. package/dist/content/icons/orckestra-icon.svg +5 -0
  113. package/dist/content/icons/unlock.svg +3 -0
  114. package/dist/content/iconsSheet.svg +11 -0
  115. package/dist/content/orckestra-logo-white.png +0 -0
  116. package/dist/getThemeOverrides.js +2 -2
  117. package/dist/hocs/withInfiniteScroll.js +1 -1
  118. package/dist/hocs/withUpdateHandler.js +2 -2
  119. package/dist/hooks/useDispatchWithModulesData.js +1 -1
  120. package/dist/hooks/useEditState.js +3 -3
  121. package/dist/hooks/useEntityLoader.js +2 -2
  122. package/dist/hooks/useFullEntityEditState.js +3 -3
  123. package/dist/hooks/useLabelMessage.js +3 -3
  124. package/dist/hooks/useMultipleFieldEditState.js +2 -2
  125. package/dist/hooks/useNavigationHandler.js +1 -1
  126. package/dist/hooks/useNotificationRequestState.js +2 -2
  127. package/dist/hooks/useSelectorAndUnwrap.js +1 -1
  128. package/dist/reducers/metadata.js +1 -1
  129. package/dist/reducers/modules.js +39 -1
  130. package/dist/reducers/scopes.js +27 -0
  131. package/dist/reducers/settings.js +31 -2
  132. package/dist/schemas/countries.js +1 -1
  133. package/dist/schemas/definitions.js +1 -1
  134. package/dist/schemas/metadata.js +1 -1
  135. package/dist/schemas/productDefinitions.js +1 -1
  136. package/dist/schemas/timezones.js +1 -1
  137. package/dist/selectors/applications.js +1 -1
  138. package/dist/selectors/authentication.js +57 -18
  139. package/dist/selectors/countries.js +1 -1
  140. package/dist/selectors/locale.js +1 -1
  141. package/dist/selectors/metadata.js +16 -6
  142. package/dist/selectors/modules.js +15 -1
  143. package/dist/selectors/navigation.js +1 -1
  144. package/dist/selectors/requests.js +1 -1
  145. package/dist/selectors/scope.js +8 -1
  146. package/dist/selectors/settings.js +13 -1
  147. package/dist/selectors/versionInfo.js +1 -1
  148. package/dist/selectors/view.js +1 -1
  149. package/dist/spawnerMiddleware.js +1 -1
  150. package/dist/utils/displayModeHelper.js +1 -1
  151. package/dist/utils/flatten.js +2 -2
  152. package/dist/utils/localizationHelper.js +1 -1
  153. package/dist/utils/mapHelper.js +1 -1
  154. package/dist/utils/modelValidationHelper.js +1 -1
  155. package/dist/utils/parseHelper.js +1 -1
  156. package/dist/utils/propertyHelper.js +2 -2
  157. package/dist/utils/propertyValidator.js +1 -1
  158. package/dist/utils/setTranslation.js +27 -1
  159. package/dist/utils/setTranslationWithFallback.js +31 -2
  160. package/dist/utils/testUtils.js +2 -1
  161. package/dist/utils/unwrapImmutable.js +1 -1
  162. package/dist/utils/urlHelper.js +1 -1
  163. package/package.json +6 -5
  164. package/src/actions/modules.js +30 -0
  165. package/src/actions/modules.test.js +50 -1
  166. package/src/actions/scopes.js +33 -7
  167. package/src/actions/scopes.test.js +84 -14
  168. package/src/components/AppFrame/About.js +10 -3
  169. package/src/components/AppFrame/AppFrame.test.js +9 -0
  170. package/src/components/AppFrame/MenuItem.js +3 -5
  171. package/src/components/AppFrame/MenuItem.test.js +2 -24
  172. package/src/components/AppFrame/Sidebar.js +11 -12
  173. package/src/components/AppFrame/Sidebar.test.js +18 -0
  174. package/src/components/ApplicationModuleLoader.js +52 -0
  175. package/src/components/ApplicationModuleLoader.test.js +149 -0
  176. package/src/components/Authenticate.js +5 -4
  177. package/src/components/Authenticate.test.js +23 -4
  178. package/src/components/MaterialUI/DataDisplay/PredefinedElements/Translations.test.js +7 -1
  179. package/src/components/MaterialUI/Inputs/InputBase.js +19 -2
  180. package/src/components/MaterialUI/Inputs/InputBase.test.js +68 -0
  181. package/src/components/MaterialUI/Navigation/ExternalLink.js +25 -0
  182. package/src/components/MaterialUI/Navigation/ExternalLink.test.js +26 -0
  183. package/src/components/MaterialUI/ScopeSelector/ScopeSelector.js +5 -1
  184. package/src/components/MaterialUI/muiThemes.js +5 -0
  185. package/src/components/Modules.js +103 -20
  186. package/src/components/Modules.test.js +315 -28
  187. package/src/components/Provision.test.js +34 -0
  188. package/src/components/Routing/FullPage.js +2 -1
  189. package/src/components/Routing/FullPage.test.js +23 -0
  190. package/src/components/Routing/Page.js +2 -2
  191. package/src/components/Routing/Page.test.js +20 -0
  192. package/src/components/Routing/Segment.js +1 -1
  193. package/src/components/Routing/withWaypointing.js +2 -2
  194. package/src/components/Routing/withWaypointing.test.js +33 -5
  195. package/src/components/Scope/Scope.test.js +4 -0
  196. package/src/components/Scope/useScopeConfirmationModalState.js +5 -16
  197. package/src/components/Scope/useScopeConfirmationModalState.test.js +39 -13
  198. package/src/components/Scope/useScopeData.js +0 -3
  199. package/src/components/Scope/useScopeData.test.js +0 -27
  200. package/src/constants.js +15 -0
  201. package/src/content/icons/lock.svg +3 -0
  202. package/src/content/icons/orckestra-icon.svg +5 -0
  203. package/src/content/icons/unlock.svg +3 -0
  204. package/src/content/iconsSheet.svg +11 -0
  205. package/src/content/orckestra-logo-white.png +0 -0
  206. package/src/hocs/withScopeData.test.js +0 -31
  207. package/src/reducers/modules.js +48 -2
  208. package/src/reducers/modules.test.js +117 -2
  209. package/src/reducers/scopes.js +30 -0
  210. package/src/reducers/scopes.test.js +45 -1
  211. package/src/reducers/settings.js +26 -2
  212. package/src/reducers/settings.test.js +74 -6
  213. package/src/selectors/authentication.js +53 -27
  214. package/src/selectors/authentication.test.js +600 -12
  215. package/src/selectors/metadata.js +18 -7
  216. package/src/selectors/metadata.test.js +221 -283
  217. package/src/selectors/modules.js +7 -0
  218. package/src/selectors/modules.test.js +16 -1
  219. package/src/selectors/scope.js +3 -1
  220. package/src/selectors/scope.test.js +5 -0
  221. package/src/selectors/settings.js +6 -0
  222. package/src/translations/en-US.json +1 -1
  223. package/src/translations/fr-CA.json +1 -1
  224. package/src/utils/setTranslation.js +16 -1
  225. package/src/utils/setTranslation.test.js +24 -0
  226. package/src/utils/setTranslationWithFallback.js +18 -1
  227. package/src/utils/setTranslationWithFallback.test.js +108 -0
@@ -3,49 +3,122 @@ import { useHistory, useLocation } from "react-router-dom";
3
3
  import { useSelector, useDispatch } from "react-redux";
4
4
  import { Route, Switch, Redirect } from "react-router-dom";
5
5
  import withErrorBoundary from "../hocs/withErrorBoundary";
6
- import { getCurrentScope, selectRouteHref } from "../selectors/navigation";
6
+ import { getCurrentScope, getCurrentScopeFromRoute, selectRouteHref } from "../selectors/navigation";
7
7
  import { isCurrentScopeAuthorizedSelector } from "../selectors/scope";
8
8
  import Navigation from "./Navigation";
9
9
  import FullPage from "./Routing/FullPage";
10
10
  import { setHrefConfig } from "../actions/navigation";
11
- import { setModulesStructure } from "../actions/modules";
12
- import { defaultScopeSelector } from "../selectors/settings";
11
+ import {
12
+ setRoutingPerformed,
13
+ setModuleAsVisible,
14
+ setModulesStructure,
15
+ initializeFirstModuleScope,
16
+ } from "../actions/modules";
17
+ import { defaultScopeSelector, getApplicationModulesSelector } from "../selectors/settings";
18
+ import { getScopeModuleInformationSelector } from "../selectors/modules";
19
+ import UrlPattern from "url-pattern";
20
+ import { overtureModule } from "../constants";
13
21
 
14
- export const Module = withErrorBoundary("Module")(({ config, path, error, location, match, modulePrependPath }) => {
15
- const currentRoute = useSelector(selectRouteHref);
22
+ const rerouteOnScopeAndModule = (history, currentRoute, scope, module) => {
23
+ const params = {
24
+ scope: scope,
25
+ };
26
+
27
+ const pattern = new UrlPattern(`/:scope/${module}`);
28
+ const href = pattern.stringify(params);
29
+
30
+ if (currentRoute !== href) {
31
+ history.push(href);
32
+ }
33
+ };
34
+
35
+ export const Module = withErrorBoundary("Module")(({ id, config, path, error, location, match, modulePrependPath }) => {
16
36
  const history = useHistory();
17
- let hideSelector = state => (typeof config.hide === "function" ? config.hide(state) : config.hide ?? false);
18
- const isHidden = useSelector(hideSelector);
37
+ const scope = useSelector(getCurrentScopeFromRoute);
38
+ const currentRoute = useSelector(selectRouteHref);
39
+ const moduleInfo = useSelector(getScopeModuleInformationSelector);
40
+
41
+ const isVisible =
42
+ moduleInfo.scope != null && moduleInfo.visibleModules.length > 0 && moduleInfo.visibleModules.includes(id);
19
43
 
20
44
  React.useEffect(() => {
21
- if (isHidden === true && currentRoute.includes(match.url)) {
22
- history.push("/");
45
+ if (moduleInfo.scope != null && scope === moduleInfo.scope && moduleInfo.visibleModules.length > 0 && !isVisible) {
46
+ rerouteOnScopeAndModule(history, currentRoute, moduleInfo.scope, moduleInfo.visibleModules[0]);
23
47
  }
24
- // eslint-disable-next-line react-hooks/exhaustive-deps
25
- }, [isHidden, currentRoute, match.url]);
48
+ }, [history, scope, isVisible, currentRoute, moduleInfo.scope, moduleInfo.visibleModules]);
26
49
 
27
50
  return (
28
- <FullPage path={path} config={config} location={location} match={match} modulePrependPath={modulePrependPath} />
51
+ <FullPage
52
+ path={path}
53
+ config={config}
54
+ location={location}
55
+ match={match}
56
+ modulePrependPath={modulePrependPath}
57
+ isVisible={isVisible}
58
+ />
29
59
  );
30
60
  });
31
61
 
32
62
  const getHrefFromPath = (path, scope) => path.replace(":scope", scope);
33
63
 
64
+ const CheckModuleVisibility = ({ id, config, moduleInfo }) => {
65
+ const dispatch = useDispatch();
66
+ const applicationModules = useSelector(getApplicationModulesSelector);
67
+ const scopeFromRoute = useSelector(getCurrentScopeFromRoute);
68
+
69
+ const hideSelector = state => (typeof config.hide === "function" ? config.hide(state) : () => config.hide ?? false);
70
+
71
+ const isHidden = useSelector(hideSelector(moduleInfo.scope ?? scopeFromRoute));
72
+
73
+ const moduleScope = moduleInfo.scope;
74
+ const moduleIsVisible = isHidden === false && !moduleInfo.visibleModules.includes(id);
75
+
76
+ React.useEffect(() => {
77
+ // We need to wait for the ROUTE to be set the first time in the Redux/Store before to set module's visibility
78
+ if (scopeFromRoute !== null || applicationModules.includes(overtureModule.System)) {
79
+ if (moduleScope == null) {
80
+ dispatch(initializeFirstModuleScope(scopeFromRoute));
81
+ }
82
+
83
+ if (moduleIsVisible) {
84
+ dispatch(setModuleAsVisible(id));
85
+ }
86
+ }
87
+ }, [moduleScope, moduleIsVisible, dispatch, id, scopeFromRoute, applicationModules]);
88
+
89
+ return <React.Fragment />;
90
+ };
91
+
34
92
  export const Modules = ({ modules, pathConfig: { customPath, ...otherConfigs } = {} }) => {
35
93
  const dispatch = useDispatch();
36
- const scope = useSelector(getCurrentScope);
94
+ const currentScope = useSelector(getCurrentScope);
37
95
  const isAuthorizedScope = useSelector(isCurrentScopeAuthorizedSelector);
38
96
  const defaultScope = useSelector(defaultScopeSelector);
39
97
  const history = useHistory();
40
98
  const location = useLocation();
99
+ const currentRoute = useSelector(selectRouteHref);
100
+
101
+ const moduleInfo = useSelector(getScopeModuleInformationSelector);
102
+
103
+ const firstModuleName = Object.keys(modules)[0];
104
+
105
+ const destinationModule =
106
+ moduleInfo.routingPerformed === false &&
107
+ moduleInfo.scope != null &&
108
+ moduleInfo.visibleModules.length > 0 &&
109
+ moduleInfo.moduleName != null
110
+ ? moduleInfo.visibleModules.includes(moduleInfo.moduleName)
111
+ ? moduleInfo.moduleName
112
+ : moduleInfo.visibleModules[0]
113
+ : null;
41
114
 
42
115
  const scopePath = "/:scope/";
43
116
  const prependPath = customPath || scopePath;
44
- const prependHref = getHrefFromPath(prependPath, scope);
117
+ const prependHref = getHrefFromPath(prependPath, currentScope);
45
118
 
46
119
  Object.keys(otherConfigs).forEach(key => {
47
120
  const moduleConfig = otherConfigs[key];
48
- moduleConfig.prependHref = getHrefFromPath(moduleConfig.prependPath, scope);
121
+ moduleConfig.prependHref = getHrefFromPath(moduleConfig.prependPath, currentScope);
49
122
  });
50
123
 
51
124
  useEffect(() => {
@@ -53,23 +126,33 @@ export const Modules = ({ modules, pathConfig: { customPath, ...otherConfigs } =
53
126
  }, [dispatch, prependPath, prependHref, otherConfigs]);
54
127
 
55
128
  const getModuleConfig = name => (otherConfigs && otherConfigs[name]) || { prependPath, prependHref };
56
- const firstModuleName = Object.keys(modules)[0];
57
129
 
58
130
  useEffect(() => {
59
131
  const { pathname, search } = location;
60
- if (!isAuthorizedScope && pathname.includes(scope) && defaultScope) {
61
- history.push(pathname.replace(scope, defaultScope) + search);
132
+ if (!isAuthorizedScope && pathname.includes(currentScope) && defaultScope) {
133
+ history.push(pathname.replace(currentScope, defaultScope) + search);
62
134
  }
63
135
  // eslint-disable-next-line react-hooks/exhaustive-deps
64
- }, [isAuthorizedScope, defaultScope, history, scope]);
136
+ }, [isAuthorizedScope, defaultScope, history, currentScope]);
65
137
 
66
138
  React.useEffect(() => {
67
139
  dispatch(setModulesStructure(modules));
68
140
  }, [dispatch, modules]);
69
141
 
142
+ useEffect(() => {
143
+ if (destinationModule != null) {
144
+ rerouteOnScopeAndModule(history, currentRoute, moduleInfo.scope, destinationModule);
145
+
146
+ dispatch(setRoutingPerformed());
147
+ }
148
+ }, [dispatch, history, moduleInfo.scope, destinationModule, currentRoute]);
149
+
70
150
  return (
71
151
  <React.Fragment>
72
152
  <Navigation modules={modules} />
153
+ {Object.entries(modules).map(([name, module]) => (
154
+ <CheckModuleVisibility key={name} id={name} config={module} moduleInfo={moduleInfo} />
155
+ ))}
73
156
  <Switch>
74
157
  {Object.entries(modules).map(([name, module]) => {
75
158
  const moduleConfig = getModuleConfig(name);
@@ -79,7 +162,7 @@ export const Modules = ({ modules, pathConfig: { customPath, ...otherConfigs } =
79
162
  key={name}
80
163
  path={path}
81
164
  render={route => (
82
- <Module modulePrependPath={moduleConfig.prependPath} config={module} path={path} {...route} />
165
+ <Module id={name} modulePrependPath={moduleConfig.prependPath} config={module} path={path} {...route} />
83
166
  )}
84
167
  />
85
168
  );
@@ -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
  });