orc-shared 1.2.0-dev.2 → 1.2.0-dev.6

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 (74) hide show
  1. package/dist/actions/modules.js +63 -1
  2. package/dist/actions/scopes.js +59 -22
  3. package/dist/components/AppFrame/MenuItem.js +5 -14
  4. package/dist/components/AppFrame/Sidebar.js +11 -5
  5. package/dist/components/ApplicationModuleLoader.js +143 -0
  6. package/dist/components/Authenticate.js +12 -12
  7. package/dist/components/MaterialUI/DataDisplay/SelectionList.js +1 -1
  8. package/dist/components/MaterialUI/Navigation/ExternalLink.js +113 -0
  9. package/dist/components/MaterialUI/muiThemes.js +5 -0
  10. package/dist/components/Modules.js +126 -41
  11. package/dist/components/Routing/FullPage.js +3 -1
  12. package/dist/components/Routing/Page.js +5 -3
  13. package/dist/components/Routing/Segment.js +1 -1
  14. package/dist/components/Routing/withWaypointing.js +6 -2
  15. package/dist/components/Scope/useScopeConfirmationModalState.js +7 -16
  16. package/dist/components/Scope/useScopeData.js +4 -13
  17. package/dist/constants.js +19 -2
  18. package/dist/content/iconsSheet.svg +3 -0
  19. package/dist/reducers/modules.js +39 -1
  20. package/dist/reducers/scopes.js +27 -0
  21. package/dist/reducers/settings.js +31 -2
  22. package/dist/selectors/authentication.js +57 -18
  23. package/dist/selectors/modules.js +15 -1
  24. package/dist/selectors/scope.js +7 -1
  25. package/dist/selectors/settings.js +13 -1
  26. package/package.json +6 -5
  27. package/src/actions/modules.js +30 -0
  28. package/src/actions/modules.test.js +50 -1
  29. package/src/actions/scopes.js +33 -7
  30. package/src/actions/scopes.test.js +84 -14
  31. package/src/components/AppFrame/AppFrame.test.js +9 -0
  32. package/src/components/AppFrame/MenuItem.js +3 -5
  33. package/src/components/AppFrame/MenuItem.test.js +2 -24
  34. package/src/components/AppFrame/Sidebar.js +8 -1
  35. package/src/components/AppFrame/Sidebar.test.js +18 -0
  36. package/src/components/ApplicationModuleLoader.js +52 -0
  37. package/src/components/ApplicationModuleLoader.test.js +149 -0
  38. package/src/components/Authenticate.js +5 -4
  39. package/src/components/Authenticate.test.js +23 -4
  40. package/src/components/MaterialUI/DataDisplay/SelectionList.js +1 -1
  41. package/src/components/MaterialUI/DataDisplay/SelectionList.test.js +2 -2
  42. package/src/components/MaterialUI/Navigation/ExternalLink.js +25 -0
  43. package/src/components/MaterialUI/Navigation/ExternalLink.test.js +26 -0
  44. package/src/components/MaterialUI/muiThemes.js +5 -0
  45. package/src/components/Modules.js +103 -20
  46. package/src/components/Modules.test.js +315 -28
  47. package/src/components/Provision.test.js +34 -0
  48. package/src/components/Routing/FullPage.js +2 -1
  49. package/src/components/Routing/FullPage.test.js +23 -0
  50. package/src/components/Routing/Page.js +2 -2
  51. package/src/components/Routing/Page.test.js +20 -0
  52. package/src/components/Routing/Segment.js +1 -1
  53. package/src/components/Routing/withWaypointing.js +2 -2
  54. package/src/components/Routing/withWaypointing.test.js +33 -5
  55. package/src/components/Scope/useScopeConfirmationModalState.js +5 -16
  56. package/src/components/Scope/useScopeConfirmationModalState.test.js +39 -13
  57. package/src/components/Scope/useScopeData.js +0 -3
  58. package/src/components/Scope/useScopeData.test.js +0 -27
  59. package/src/constants.js +15 -0
  60. package/src/content/iconsSheet.svg +3 -0
  61. package/src/hocs/withScopeData.test.js +0 -31
  62. package/src/reducers/modules.js +48 -2
  63. package/src/reducers/modules.test.js +117 -2
  64. package/src/reducers/scopes.js +30 -0
  65. package/src/reducers/scopes.test.js +45 -1
  66. package/src/reducers/settings.js +26 -2
  67. package/src/reducers/settings.test.js +74 -6
  68. package/src/selectors/authentication.js +53 -27
  69. package/src/selectors/authentication.test.js +600 -12
  70. package/src/selectors/modules.js +7 -0
  71. package/src/selectors/modules.test.js +16 -1
  72. package/src/selectors/scope.js +2 -0
  73. package/src/selectors/scope.test.js +5 -0
  74. package/src/selectors/settings.js +6 -0
@@ -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;
@@ -6,7 +6,7 @@ import { Router, Route } from "react-router-dom";
6
6
  import { mount, act } from "unexpected-reaction";
7
7
  import sinon from "sinon";
8
8
  import { PropStruct } from "../../utils/testUtils";
9
- import { setRoute, mapHref } from "../../actions/navigation";
9
+ import { setRoute, mapHref, setCurrentPrependPath } from "../../actions/navigation";
10
10
  import withWaypointing from "./withWaypointing";
11
11
 
12
12
  describe("withWaypointing", () => {
@@ -22,6 +22,7 @@ describe("withWaypointing", () => {
22
22
  params: { some: "meep" },
23
23
  isExact: true,
24
24
  },
25
+ modulePrependPath: "foo",
25
26
  },
26
27
  },
27
28
  requests: {
@@ -94,6 +95,21 @@ describe("withWaypointing", () => {
94
95
  })
95
96
  .then(() => expect(store.dispatch, "to have calls satisfying", [])));
96
97
 
98
+ it("does not render the component if the component is not visible", () =>
99
+ expect(withWaypointing, "called with", [PropStruct, false])
100
+ .then(EnhancedView => {
101
+ return expect(
102
+ <Provider store={store}>
103
+ <Router history={history}>
104
+ <Route path="/feep/:some" component={EnhancedView} />
105
+ </Router>
106
+ </Provider>,
107
+ "when mounted",
108
+ "to be null",
109
+ );
110
+ })
111
+ .then(() => expect(store.dispatch, "to have calls satisfying", [])));
112
+
97
113
  it("does fire action if path parameters different", () =>
98
114
  expect(withWaypointing, "called with", [PropStruct])
99
115
  .then(EnhancedView => {
@@ -128,7 +144,7 @@ describe("withWaypointing", () => {
128
144
  ));
129
145
 
130
146
  it("does fire action if path parameters different and with custom props", () =>
131
- expect(withWaypointing, "called with", [PropStruct, { foo: 123 }])
147
+ expect(withWaypointing, "called with", [PropStruct, true, { foo: 123 }])
132
148
  .then(EnhancedView => {
133
149
  history.replace("/feep/moof");
134
150
  return expect(
@@ -161,7 +177,7 @@ describe("withWaypointing", () => {
161
177
  ));
162
178
 
163
179
  it("does not fire action if route match is not exact with custom props", () =>
164
- expect(withWaypointing, "called with", [PropStruct, { foo: 123 }])
180
+ expect(withWaypointing, "called with", [PropStruct, true, { foo: 123 }])
165
181
  .then(EnhancedView => {
166
182
  history.replace("/feep/meep/mef");
167
183
  return expect(
@@ -201,16 +217,28 @@ describe("withWaypointing", () => {
201
217
  return expect(
202
218
  <Provider store={store}>
203
219
  <Router history={history}>
204
- <Route path="/foo/bar" render={props => <EnhancedView {...props} mapFrom="/foo" />} />
220
+ <Route
221
+ path="/foo/bar"
222
+ render={props => <EnhancedView {...props} mapFrom="/foo" modulePrependPath="foo" />}
223
+ />
205
224
  </Router>
206
225
  </Provider>,
207
226
  "when mounted",
208
227
  "to satisfy",
209
- <PropStruct history="__ignore" location="__ignore" match="__ignore" mapFrom="/foo" />,
228
+ <PropStruct
229
+ history="__ignore"
230
+ location="__ignore"
231
+ match="__ignore"
232
+ mapFrom="/foo"
233
+ modulePrependPath="__ignore"
234
+ />,
210
235
  );
211
236
  })
212
237
  .then(() =>
213
238
  expect(store.dispatch, "to have calls satisfying", [
239
+ {
240
+ args: [setCurrentPrependPath("foo")],
241
+ },
214
242
  {
215
243
  args: [mapHref("/foo", "/foo/bar")],
216
244
  },
@@ -6,8 +6,7 @@ import { applicationScopeHasChanged } from "../../actions/scopes";
6
6
  import useScopeData from "./useScopeData";
7
7
  import { hasUnsavedDataSelector } from "../../selectors/view";
8
8
  import { scopeConfirmationDialogTypes } from "../../constants";
9
- import { useHistory } from "react-router-dom";
10
- import UrlPattern from "url-pattern";
9
+ import { setNewScopeAndModuleName } from "../../actions/modules";
11
10
 
12
11
  const ExecuteClosingTabHandlerActions = async closingTabHandlerActions => {
13
12
  if (closingTabHandlerActions.length <= 0) return Promise.resolve();
@@ -19,23 +18,13 @@ const ExecuteClosingTabHandlerActions = async closingTabHandlerActions => {
19
18
 
20
19
  const useApplicationScopeChanger = closingTabHandlerActions => {
21
20
  const dispatch = useDispatch();
22
- const history = useHistory();
23
21
  const moduleName = useSelectorAndUnwrap(selectCurrentModuleName);
24
22
 
25
23
  return (previousScope, newScope) => {
26
- const params = {
27
- scope: newScope,
28
- };
29
-
30
- // did not find a more reliable way to get the URL for the first tab of the module
31
- const pattern = new UrlPattern(`/:scope/${moduleName}`);
32
- const href = pattern.stringify(params);
33
-
34
- history.push(href);
35
-
36
- ExecuteClosingTabHandlerActions(closingTabHandlerActions).then(() =>
37
- dispatch(applicationScopeHasChanged(previousScope, newScope)),
38
- );
24
+ ExecuteClosingTabHandlerActions(closingTabHandlerActions).then(() => {
25
+ dispatch(setNewScopeAndModuleName(newScope, moduleName));
26
+ dispatch(applicationScopeHasChanged(previousScope, newScope));
27
+ });
39
28
  };
40
29
  };
41
30
 
@@ -4,8 +4,8 @@ import sinon from "sinon";
4
4
  import { TestWrapper } from "../../utils/testUtils";
5
5
  import useScopeConfirmationModalState from "./useScopeConfirmationModalState";
6
6
  import { mount } from "enzyme";
7
- import { createMemoryHistory } from "history";
8
7
  import { APPLICATION_SCOPE_HAS_CHANGED } from "../../actions/scopes";
8
+ import { SET_NEW_SCOPE_AND_MODULE_NAME } from "../../actions/modules";
9
9
 
10
10
  jest.mock("../../utils/buildUrl", () => {
11
11
  const modExport = {};
@@ -35,13 +35,9 @@ const TestComp = () => {
35
35
  };
36
36
 
37
37
  describe("useScopeConfirmationModalState", () => {
38
- let state, store, history;
38
+ let state, store;
39
39
 
40
40
  beforeEach(() => {
41
- history = createMemoryHistory({ initialEntries: ["/TestScope/demos?arg=data"] });
42
- sinon.spy(history, "push");
43
- history.push.named("history.push");
44
-
45
41
  state = Immutable.fromJS({
46
42
  input: {},
47
43
  locale: {
@@ -170,7 +166,7 @@ describe("useScopeConfirmationModalState", () => {
170
166
 
171
167
  it("selecting a new scope without active tabs changes the scope", async () => {
172
168
  const component = (
173
- <TestWrapper provider={{ store }} router={{ history }}>
169
+ <TestWrapper provider={{ store }}>
174
170
  <TestComp />
175
171
  </TestWrapper>
176
172
  );
@@ -183,8 +179,18 @@ describe("useScopeConfirmationModalState", () => {
183
179
 
184
180
  await new Promise(resolve => setTimeout(resolve, 10));
185
181
 
186
- expect(history.push, "to have calls satisfying", [{ args: ["/newScope/TheModuleName"] }]);
187
182
  expect(store.dispatch, "to have calls satisfying", [
183
+ {
184
+ args: [
185
+ {
186
+ type: SET_NEW_SCOPE_AND_MODULE_NAME,
187
+ payload: {
188
+ scope: "newScope",
189
+ moduleName: "TheModuleName",
190
+ },
191
+ },
192
+ ],
193
+ },
188
194
  {
189
195
  args: [
190
196
  {
@@ -227,7 +233,7 @@ describe("useScopeConfirmationModalState", () => {
227
233
  openTabs();
228
234
 
229
235
  const component = (
230
- <TestWrapper provider={{ store }} router={{ history }}>
236
+ <TestWrapper provider={{ store }}>
231
237
  <TestComp />
232
238
  </TestWrapper>
233
239
  );
@@ -248,8 +254,18 @@ describe("useScopeConfirmationModalState", () => {
248
254
  isModalOpened = mountedComponent.find("#isModalOpened").text();
249
255
  expect(isModalOpened, "to equal", "false");
250
256
 
251
- expect(history.push, "to have calls satisfying", [{ args: ["/newScope/TheModuleName"] }]);
252
257
  expect(store.dispatch, "to have calls satisfying", [
258
+ {
259
+ args: [
260
+ {
261
+ type: SET_NEW_SCOPE_AND_MODULE_NAME,
262
+ payload: {
263
+ scope: "newScope",
264
+ moduleName: "TheModuleName",
265
+ },
266
+ },
267
+ ],
268
+ },
253
269
  {
254
270
  args: [
255
271
  {
@@ -281,7 +297,7 @@ describe("useScopeConfirmationModalState", () => {
281
297
  openTabs();
282
298
 
283
299
  const component = (
284
- <TestWrapper provider={{ store }} router={{ history }}>
300
+ <TestWrapper provider={{ store }}>
285
301
  <TestComp />
286
302
  </TestWrapper>
287
303
  );
@@ -309,9 +325,19 @@ describe("useScopeConfirmationModalState", () => {
309
325
  { args: [null, true] },
310
326
  ]);
311
327
 
312
- expect(history.push, "to have calls satisfying", [{ args: ["/newScope/TheModuleName"] }]);
313
- expect(store.dispatch.callCount, "to be", 1);
328
+ expect(store.dispatch.callCount, "to be", 2);
314
329
  expect(store.dispatch, "to have calls satisfying", [
330
+ {
331
+ args: [
332
+ {
333
+ type: SET_NEW_SCOPE_AND_MODULE_NAME,
334
+ payload: {
335
+ scope: "newScope",
336
+ moduleName: "TheModuleName",
337
+ },
338
+ },
339
+ ],
340
+ },
315
341
  {
316
342
  args: [
317
343
  {
@@ -1,8 +1,6 @@
1
1
  import { useSelector } from "react-redux";
2
- import useLoader from "../../hooks/useLoader";
3
2
  import { unwrapImmutable } from "../../utils";
4
3
  import { currentScopeSelector, scopeGetter } from "../../selectors/scope";
5
- import { getScopes } from "../../actions/scopes";
6
4
 
7
5
  const buildDefaultNodeState = (current, getScope) => {
8
6
  let node = current;
@@ -16,7 +14,6 @@ const buildDefaultNodeState = (current, getScope) => {
16
14
  const useScopeData = () => {
17
15
  const currentScope = unwrapImmutable(useSelector(currentScopeSelector));
18
16
  const getScope = useSelector(scopeGetter);
19
- useLoader(getScopes(), () => currentScope.name);
20
17
  const defaultNodeState = buildDefaultNodeState(currentScope, getScope);
21
18
  return [currentScope, defaultNodeState, getScope];
22
19
  };
@@ -2,7 +2,6 @@ import React from "react";
2
2
  import { Provider } from "react-redux";
3
3
  import { MemoryRouter } from "react-router-dom";
4
4
  import Immutable from "immutable";
5
- import { RSAA } from "redux-api-middleware";
6
5
  import sinon from "sinon";
7
6
  import { PropStruct } from "../../utils/testUtils";
8
7
  import useScopeData from "./useScopeData";
@@ -122,30 +121,4 @@ describe("useScopeData", () => {
122
121
  )
123
122
 
124
123
  .then(() => expect(store.dispatch, "was not called")));
125
-
126
- it("loads scopes if it has none", () => {
127
- state = state.setIn(["scopes"], Immutable.Map());
128
- return expect(
129
- <Provider store={store}>
130
- <MemoryRouter>
131
- <TestComp />
132
- </MemoryRouter>
133
- </Provider>,
134
- "when mounted",
135
- "to be a",
136
- "DOMElement",
137
- ).then(() =>
138
- expect(store.dispatch, "to have calls satisfying", [
139
- {
140
- args: [
141
- {
142
- [RSAA]: {
143
- types: ["GET_SCOPES_REQUEST", "GET_SCOPES_SUCCESS", "GET_SCOPES_FAILURE"],
144
- },
145
- },
146
- ],
147
- },
148
- ]),
149
- );
150
- });
151
124
  });
package/src/constants.js CHANGED
@@ -23,6 +23,7 @@ export const roleGroups = {
23
23
  Shopping: "Shopping",
24
24
  Search: "Search",
25
25
  Profiles: "Profiles",
26
+ Locations: "Locations",
26
27
  Orchestrator: "Orchestrator",
27
28
  PriceManagement: "PriceManagement",
28
29
  UserManagement: "UserManagement",
@@ -73,6 +74,20 @@ export const attributeDataType = {
73
74
  customType: "CustomType",
74
75
  };
75
76
 
77
+ // It is intended that some of them have a different value of its keys
78
+ export const overtureModule = {
79
+ System: "System",
80
+ Products: "Product",
81
+ Customers: "Customer",
82
+ Orders: "Order",
83
+ Marketing: "Marketing",
84
+ Report: "Reports",
85
+ Administration: "Administration",
86
+ UserManagement: "UserManagement",
87
+ PriceManagement: "PriceManagement",
88
+ Locations: "Location",
89
+ };
90
+
76
91
  export const definitionType = {
77
92
  shared: "Shared",
78
93
  embedded: "Embedded",
@@ -122,6 +122,9 @@
122
122
  <symbol id="icon-dropdown-chevron-down" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
123
123
  <polyline fill="none" stroke-linecap="round" stroke-linejoin="round" points="9 3 5 7 1 3"/>
124
124
  </symbol>
125
+ <symbol id="icon-open-in-new-tab" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
126
+ <path stroke="none" fill-rule="evenodd" d="M8.4375,6.25 L7.8125,6.25 C7.63991102,6.25 7.5,6.38991102 7.5,6.5625 L7.5,8.75 L1.25,8.75 L1.25,2.5 L4.0625,2.5 C4.23508898,2.5 4.375,2.36008898 4.375,2.1875 L4.375,1.5625 C4.375,1.38991102 4.23508898,1.25 4.0625,1.25 L0.9375,1.25 C0.419733047,1.25 0,1.66973305 0,2.1875 L0,9.0625 C0,9.58026695 0.419733047,10 0.9375,10 L7.8125,10 C8.33026695,10 8.75,9.58026695 8.75,9.0625 L8.75,6.5625 C8.75,6.38991102 8.61008898,6.25 8.4375,6.25 Z M9.53125,0 L7.03125,0 C6.61386719,0 6.40527344,0.506054688 6.69921875,0.80078125 L7.39707031,1.49863281 L2.63671875,6.25722656 C2.54845882,6.34518057 2.49884911,6.46465556 2.49884911,6.58925781 C2.49884911,6.71386007 2.54845882,6.83333506 2.63671875,6.92128906 L3.07949219,7.36328125 C3.16744619,7.45154118 3.28692118,7.50115089 3.41152344,7.50115089 C3.53612569,7.50115089 3.65560068,7.45154118 3.74355469,7.36328125 L8.5015625,2.60390625 L9.19921875,3.30078125 C9.4921875,3.59375 10,3.38867188 10,2.96875 L10,0.46875 C10,0.209866524 9.79013348,0 9.53125,0 Z"/>
127
+ </symbol>
125
128
  <symbol id="icon-dropdown-chevron-right" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
126
129
  <polyline fill="none" stroke-linecap="round" stroke-linejoin="round" points="9 3 5 7 1 3" transform="rotate(-90 5 5)"/>
127
130
  </symbol>
@@ -2,7 +2,6 @@ import React from "react";
2
2
  import { Provider } from "react-redux";
3
3
  import { MemoryRouter } from "react-router-dom";
4
4
  import Immutable from "immutable";
5
- import { RSAA } from "redux-api-middleware";
6
5
  import sinon from "sinon";
7
6
  import { PropStruct, spyOnConsole } from "../utils/testUtils";
8
7
  import withScopeData from "./withScopeData";
@@ -142,34 +141,4 @@ describe("withScopeData", () => {
142
141
  ),
143
142
  )
144
143
  .then(() => expect(store.dispatch, "was not called")));
145
-
146
- it("loads scopes if it has none", () => {
147
- state = state.setIn(["scopes"], Immutable.Map());
148
- return expect(withScopeData, "when called with", [TestComp])
149
- .then(Comp =>
150
- expect(
151
- <Provider store={store}>
152
- <MemoryRouter>
153
- <Comp />
154
- </MemoryRouter>
155
- </Provider>,
156
- "when mounted",
157
- "to be a",
158
- "DOMElement",
159
- ),
160
- )
161
- .then(() =>
162
- expect(store.dispatch, "to have calls satisfying", [
163
- {
164
- args: [
165
- {
166
- [RSAA]: {
167
- types: ["GET_SCOPES_REQUEST", "GET_SCOPES_SUCCESS", "GET_SCOPES_FAILURE"],
168
- },
169
- },
170
- ],
171
- },
172
- ]),
173
- );
174
- });
175
144
  });
@@ -1,8 +1,22 @@
1
1
  import Immutable from "immutable";
2
- import { SET_MODULES_STRUCTURE } from "../actions/modules";
2
+ import {
3
+ INITIALIZE_FIRST_MODULE_SCOPE,
4
+ SET_MODULE_AS_VISIBLE,
5
+ SET_MODULES_STRUCTURE,
6
+ SET_NEW_SCOPE_AND_MODULE_NAME,
7
+ SET_ROUTING_PERFORMED,
8
+ } from "../actions/modules";
3
9
  import { infoBar } from "./../constants";
4
10
 
5
- const initialState = Immutable.Map({});
11
+ const initialState = Immutable.fromJS({
12
+ tree: {},
13
+ visibleModules: [],
14
+ lastScopeAndModuleSelection: {
15
+ scope: null,
16
+ moduleName: null,
17
+ routingPerformed: true,
18
+ },
19
+ });
6
20
 
7
21
  const viewStateReducer = (state = initialState, action) => {
8
22
  switch (action.type) {
@@ -32,6 +46,38 @@ const viewStateReducer = (state = initialState, action) => {
32
46
 
33
47
  return state.set("tree", Immutable.fromJS(modulesTree));
34
48
  }
49
+
50
+ case INITIALIZE_FIRST_MODULE_SCOPE: {
51
+ return state.setIn(["lastScopeAndModuleSelection", "scope"], action.payload);
52
+ }
53
+
54
+ case SET_MODULE_AS_VISIBLE: {
55
+ const visibleModules = state.get("visibleModules").toJS();
56
+ visibleModules.push(action.payload);
57
+
58
+ return state.set("visibleModules", Immutable.fromJS(visibleModules));
59
+ }
60
+
61
+ case SET_NEW_SCOPE_AND_MODULE_NAME: {
62
+ const lastScopeAndModuleSelection = state.get("lastScopeAndModuleSelection").toJS();
63
+
64
+ if (action.payload.scope !== lastScopeAndModuleSelection.scope) {
65
+ state = state.set("visibleModules", Immutable.fromJS([]));
66
+ }
67
+
68
+ return state.set(
69
+ "lastScopeAndModuleSelection",
70
+ Immutable.fromJS({
71
+ moduleName: action.payload.moduleName,
72
+ scope: action.payload.scope,
73
+ routingPerformed: false,
74
+ }),
75
+ );
76
+ }
77
+
78
+ case SET_ROUTING_PERFORMED:
79
+ return state.setIn(["lastScopeAndModuleSelection", "routingPerformed"], true);
80
+
35
81
  default:
36
82
  return state;
37
83
  }