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.
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/dist/actions/applications.js +1 -1
- package/dist/actions/authentication.js +1 -1
- package/dist/actions/countries.js +1 -1
- package/dist/actions/locale.js +1 -1
- package/dist/actions/makeApiAction.js +3 -3
- package/dist/actions/makeOrcApiAction.js +2 -2
- package/dist/actions/metadata.js +3 -3
- package/dist/actions/modules.js +63 -1
- package/dist/actions/navigation.js +3 -3
- package/dist/actions/requestsApi.js +8 -8
- package/dist/actions/scopes.js +59 -22
- package/dist/actions/timezones.js +1 -1
- package/dist/actions/toasts.js +1 -1
- package/dist/actions/versionInfo.js +1 -1
- package/dist/actions/view.js +1 -1
- package/dist/buildStore.js +3 -3
- package/dist/components/AppFrame/About.js +11 -6
- package/dist/components/AppFrame/MenuItem.js +6 -15
- package/dist/components/AppFrame/Preferences.js +3 -3
- package/dist/components/AppFrame/Sidebar.js +20 -9
- package/dist/components/AppFrame/Topbar.js +1 -1
- package/dist/components/ApplicationModuleLoader.js +143 -0
- package/dist/components/Authenticate.js +13 -13
- package/dist/components/CategoryList.js +1 -1
- package/dist/components/Checkbox.js +1 -1
- package/dist/components/DropMenu/Menu.js +1 -1
- package/dist/components/DropMenu/index.js +1 -1
- package/dist/components/Form/FieldList.js +3 -3
- package/dist/components/Form/Form.js +1 -1
- package/dist/components/Form/Inputs/Button.js +1 -1
- package/dist/components/Form/Inputs/FieldButtons.js +1 -1
- package/dist/components/Form/Inputs/Number.js +1 -1
- package/dist/components/Form/Inputs/ReadOnly.js +1 -1
- package/dist/components/Form/Inputs/SmallButton.js +1 -1
- package/dist/components/Form/Inputs/Text.js +1 -1
- package/dist/components/Form/Inputs/Time.js +1 -1
- package/dist/components/Form/Inputs/Toggles.js +1 -1
- package/dist/components/Form/Inputs/Translation.js +3 -3
- package/dist/components/List/HeadCell.js +1 -1
- package/dist/components/List/List.js +1 -1
- package/dist/components/List/Row.js +1 -1
- package/dist/components/List/enhanceColumnDefs.js +2 -2
- package/dist/components/MaterialUI/DataDisplay/List.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/Notification.js +2 -2
- package/dist/components/MaterialUI/DataDisplay/NotificationProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/PredefinedElements/Translations.js +3 -3
- package/dist/components/MaterialUI/DataDisplay/Table.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/TableProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/TransferList.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/chipProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/collapsableListProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/dividerProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/index.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/modalProps.js +1 -1
- package/dist/components/MaterialUI/DataDisplay/useTableSelection.js +3 -3
- package/dist/components/MaterialUI/Feedback/useNotification.js +1 -1
- package/dist/components/MaterialUI/Inputs/Autocomplete.js +2 -2
- package/dist/components/MaterialUI/Inputs/AutocompleteProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/CheckboxGroupProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/CheckboxProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/InputBase.js +42 -5
- package/dist/components/MaterialUI/Inputs/InputBaseProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/PredefinedElements/SearchControl.js +1 -1
- package/dist/components/MaterialUI/Inputs/RadioProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/Select.js +2 -2
- package/dist/components/MaterialUI/Inputs/SelectProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/Switch.js +3 -3
- package/dist/components/MaterialUI/Inputs/SwitchProps.js +1 -1
- package/dist/components/MaterialUI/Inputs/TimePicker.js +1 -1
- package/dist/components/MaterialUI/Inputs/createInput.js +3 -3
- package/dist/components/MaterialUI/Inputs/index.js +1 -1
- package/dist/components/MaterialUI/Inputs/standaloneRadioProps.js +1 -1
- package/dist/components/MaterialUI/Navigation/DropDownMenuProps.js +1 -1
- package/dist/components/MaterialUI/Navigation/ExternalLink.js +113 -0
- package/dist/components/MaterialUI/ScopeSelector/ScopeSelector.js +8 -2
- package/dist/components/MaterialUI/Surfaces/expansionPanelProps.js +1 -1
- package/dist/components/MaterialUI/Surfaces/paperProps.js +1 -1
- package/dist/components/MaterialUI/muiThemes.js +7 -2
- package/dist/components/MaterialUI/textProps.js +1 -1
- package/dist/components/Modules.js +126 -41
- package/dist/components/MultiSelector.js +1 -1
- package/dist/components/Navigation/Bar.js +3 -3
- package/dist/components/Navigation/Tab.js +1 -1
- package/dist/components/Navigation/useNavigationState.js +3 -3
- package/dist/components/Placeholder.js +1 -1
- package/dist/components/Routing/FullPage.js +3 -1
- package/dist/components/Routing/Page.js +5 -3
- package/dist/components/Routing/Segment.js +1 -1
- package/dist/components/Routing/SegmentPage.js +1 -1
- package/dist/components/Routing/withWaypointing.js +8 -4
- package/dist/components/Scope/ScopeNode.js +1 -1
- package/dist/components/Scope/Selector.js +1 -1
- package/dist/components/Scope/index.js +3 -3
- package/dist/components/Scope/useScopeConfirmationModalState.js +7 -16
- package/dist/components/Scope/useScopeData.js +4 -13
- package/dist/components/Scope/useScopeSelect.js +1 -1
- package/dist/components/Selector.js +1 -1
- package/dist/components/Spritesheet.js +1 -1
- package/dist/components/Switch.js +1 -1
- package/dist/components/Text.js +1 -1
- package/dist/components/ToastList.js +1 -1
- package/dist/components/Toolbar.js +1 -1
- package/dist/components/Treeview/Label.js +1 -1
- package/dist/components/Treeview/Leaf.js +1 -1
- package/dist/components/Treeview/Node.js +3 -3
- package/dist/components/Treeview/index.js +2 -2
- package/dist/components/Treeview/settings.js +1 -1
- package/dist/constants.js +19 -2
- package/dist/content/icons/lock.svg +3 -0
- package/dist/content/icons/orckestra-icon.svg +5 -0
- package/dist/content/icons/unlock.svg +3 -0
- package/dist/content/iconsSheet.svg +11 -0
- package/dist/content/orckestra-logo-white.png +0 -0
- package/dist/getThemeOverrides.js +2 -2
- package/dist/hocs/withInfiniteScroll.js +1 -1
- package/dist/hocs/withUpdateHandler.js +2 -2
- package/dist/hooks/useDispatchWithModulesData.js +1 -1
- package/dist/hooks/useEditState.js +3 -3
- package/dist/hooks/useEntityLoader.js +2 -2
- package/dist/hooks/useFullEntityEditState.js +3 -3
- package/dist/hooks/useLabelMessage.js +3 -3
- package/dist/hooks/useMultipleFieldEditState.js +2 -2
- package/dist/hooks/useNavigationHandler.js +1 -1
- package/dist/hooks/useNotificationRequestState.js +2 -2
- package/dist/hooks/useSelectorAndUnwrap.js +1 -1
- package/dist/reducers/metadata.js +1 -1
- package/dist/reducers/modules.js +39 -1
- package/dist/reducers/scopes.js +27 -0
- package/dist/reducers/settings.js +31 -2
- package/dist/schemas/countries.js +1 -1
- package/dist/schemas/definitions.js +1 -1
- package/dist/schemas/metadata.js +1 -1
- package/dist/schemas/productDefinitions.js +1 -1
- package/dist/schemas/timezones.js +1 -1
- package/dist/selectors/applications.js +1 -1
- package/dist/selectors/authentication.js +57 -18
- package/dist/selectors/countries.js +1 -1
- package/dist/selectors/locale.js +1 -1
- package/dist/selectors/metadata.js +16 -6
- package/dist/selectors/modules.js +15 -1
- package/dist/selectors/navigation.js +1 -1
- package/dist/selectors/requests.js +1 -1
- package/dist/selectors/scope.js +8 -1
- package/dist/selectors/settings.js +13 -1
- package/dist/selectors/versionInfo.js +1 -1
- package/dist/selectors/view.js +1 -1
- package/dist/spawnerMiddleware.js +1 -1
- package/dist/utils/displayModeHelper.js +1 -1
- package/dist/utils/flatten.js +2 -2
- package/dist/utils/localizationHelper.js +1 -1
- package/dist/utils/mapHelper.js +1 -1
- package/dist/utils/modelValidationHelper.js +1 -1
- package/dist/utils/parseHelper.js +1 -1
- package/dist/utils/propertyHelper.js +2 -2
- package/dist/utils/propertyValidator.js +1 -1
- package/dist/utils/setTranslation.js +27 -1
- package/dist/utils/setTranslationWithFallback.js +31 -2
- package/dist/utils/testUtils.js +2 -1
- package/dist/utils/unwrapImmutable.js +1 -1
- package/dist/utils/urlHelper.js +1 -1
- package/package.json +6 -5
- package/src/actions/modules.js +30 -0
- package/src/actions/modules.test.js +50 -1
- package/src/actions/scopes.js +33 -7
- package/src/actions/scopes.test.js +84 -14
- package/src/components/AppFrame/About.js +10 -3
- package/src/components/AppFrame/AppFrame.test.js +9 -0
- package/src/components/AppFrame/MenuItem.js +3 -5
- package/src/components/AppFrame/MenuItem.test.js +2 -24
- package/src/components/AppFrame/Sidebar.js +11 -12
- package/src/components/AppFrame/Sidebar.test.js +18 -0
- package/src/components/ApplicationModuleLoader.js +52 -0
- package/src/components/ApplicationModuleLoader.test.js +149 -0
- package/src/components/Authenticate.js +5 -4
- package/src/components/Authenticate.test.js +23 -4
- package/src/components/MaterialUI/DataDisplay/PredefinedElements/Translations.test.js +7 -1
- package/src/components/MaterialUI/Inputs/InputBase.js +19 -2
- package/src/components/MaterialUI/Inputs/InputBase.test.js +68 -0
- package/src/components/MaterialUI/Navigation/ExternalLink.js +25 -0
- package/src/components/MaterialUI/Navigation/ExternalLink.test.js +26 -0
- package/src/components/MaterialUI/ScopeSelector/ScopeSelector.js +5 -1
- package/src/components/MaterialUI/muiThemes.js +5 -0
- package/src/components/Modules.js +103 -20
- package/src/components/Modules.test.js +315 -28
- package/src/components/Provision.test.js +34 -0
- package/src/components/Routing/FullPage.js +2 -1
- package/src/components/Routing/FullPage.test.js +23 -0
- package/src/components/Routing/Page.js +2 -2
- package/src/components/Routing/Page.test.js +20 -0
- package/src/components/Routing/Segment.js +1 -1
- package/src/components/Routing/withWaypointing.js +2 -2
- package/src/components/Routing/withWaypointing.test.js +33 -5
- package/src/components/Scope/Scope.test.js +4 -0
- package/src/components/Scope/useScopeConfirmationModalState.js +5 -16
- package/src/components/Scope/useScopeConfirmationModalState.test.js +39 -13
- package/src/components/Scope/useScopeData.js +0 -3
- package/src/components/Scope/useScopeData.test.js +0 -27
- package/src/constants.js +15 -0
- package/src/content/icons/lock.svg +3 -0
- package/src/content/icons/orckestra-icon.svg +5 -0
- package/src/content/icons/unlock.svg +3 -0
- package/src/content/iconsSheet.svg +11 -0
- package/src/content/orckestra-logo-white.png +0 -0
- package/src/hocs/withScopeData.test.js +0 -31
- package/src/reducers/modules.js +48 -2
- package/src/reducers/modules.test.js +117 -2
- package/src/reducers/scopes.js +30 -0
- package/src/reducers/scopes.test.js +45 -1
- package/src/reducers/settings.js +26 -2
- package/src/reducers/settings.test.js +74 -6
- package/src/selectors/authentication.js +53 -27
- package/src/selectors/authentication.test.js +600 -12
- package/src/selectors/metadata.js +18 -7
- package/src/selectors/metadata.test.js +221 -283
- package/src/selectors/modules.js +7 -0
- package/src/selectors/modules.test.js +16 -1
- package/src/selectors/scope.js +3 -1
- package/src/selectors/scope.test.js +5 -0
- package/src/selectors/settings.js +6 -0
- package/src/translations/en-US.json +1 -1
- package/src/translations/fr-CA.json +1 -1
- package/src/utils/setTranslation.js +16 -1
- package/src/utils/setTranslation.test.js +24 -0
- package/src/utils/setTranslationWithFallback.js +18 -1
- package/src/utils/setTranslationWithFallback.test.js +108 -0
|
@@ -24,6 +24,15 @@ describe("Sidebar", () => {
|
|
|
24
24
|
state = Immutable.fromJS({
|
|
25
25
|
navigation: { route: { match: { params: { scope: "Global" } } }, config: { prependHref: "/Global/" } },
|
|
26
26
|
settings: { defaultScope: "myScope" },
|
|
27
|
+
modules: {
|
|
28
|
+
tree: "modulesTree",
|
|
29
|
+
visibleModules: ["a", "module123"],
|
|
30
|
+
lastScopeAndModuleSelection: {
|
|
31
|
+
scope: "Norway",
|
|
32
|
+
moduleName: "Profiles",
|
|
33
|
+
routingPerformed: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
27
36
|
});
|
|
28
37
|
store = {
|
|
29
38
|
subscribe: () => {},
|
|
@@ -153,6 +162,15 @@ describe("EnhancedMenuItem", () => {
|
|
|
153
162
|
},
|
|
154
163
|
config: { prependHref: "/Global/" },
|
|
155
164
|
},
|
|
165
|
+
modules: {
|
|
166
|
+
tree: "modulesTree",
|
|
167
|
+
visibleModules: ["route"],
|
|
168
|
+
lastScopeAndModuleSelection: {
|
|
169
|
+
scope: "Norway",
|
|
170
|
+
moduleName: "Profiles",
|
|
171
|
+
routingPerformed: false,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
156
174
|
settings: { defaultScope: "myScope" },
|
|
157
175
|
}),
|
|
158
176
|
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { useDispatch, useSelector } from "react-redux";
|
|
3
|
+
import { getDefaultScope, getScopes } from "../actions/scopes";
|
|
4
|
+
import {
|
|
5
|
+
defaultScopeSelector,
|
|
6
|
+
getApplicationModulesSelector,
|
|
7
|
+
getLoadedModulesScopeSelector,
|
|
8
|
+
} from "../selectors/settings";
|
|
9
|
+
import { Loader } from "./Authenticate";
|
|
10
|
+
import { overtureModule, scopeTypes } from "../constants";
|
|
11
|
+
import { initializeFirstModuleScope } from "../actions/modules";
|
|
12
|
+
|
|
13
|
+
const ApplicationModuleLoader = ({ children }) => {
|
|
14
|
+
const dispatch = useDispatch();
|
|
15
|
+
|
|
16
|
+
const [scopesLoaded, setScopesLoaded] = useState(false);
|
|
17
|
+
|
|
18
|
+
const loadedModules = useSelector(getLoadedModulesScopeSelector);
|
|
19
|
+
const applicationModules = useSelector(getApplicationModulesSelector);
|
|
20
|
+
const defaultScope = useSelector(defaultScopeSelector);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (applicationModules.length > 0 && scopesLoaded === false) {
|
|
24
|
+
if (applicationModules.includes(overtureModule.System)) {
|
|
25
|
+
dispatch(initializeFirstModuleScope(scopeTypes.global));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
applicationModules.forEach(x => {
|
|
29
|
+
// For the default scope, the latest that will be returned will be the chosen one
|
|
30
|
+
dispatch(getDefaultScope(x));
|
|
31
|
+
// For scopes, they need to be merged
|
|
32
|
+
dispatch(getScopes(x));
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
setScopesLoaded(true);
|
|
36
|
+
}
|
|
37
|
+
}, [dispatch, applicationModules, scopesLoaded]);
|
|
38
|
+
|
|
39
|
+
const scopeLoadedFromAllModules =
|
|
40
|
+
applicationModules.length > 0 &&
|
|
41
|
+
applicationModules.reduce((prev, current) => prev && loadedModules.includes(current), true);
|
|
42
|
+
|
|
43
|
+
const applicationModuleReady = scopeLoadedFromAllModules && defaultScope != null;
|
|
44
|
+
|
|
45
|
+
if (!applicationModuleReady) {
|
|
46
|
+
return <Loader />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return React.Children.only(children);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export default ApplicationModuleLoader;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Immutable from "immutable";
|
|
3
|
+
import { Provider } from "react-redux";
|
|
4
|
+
import { ThemeProvider } from "styled-components";
|
|
5
|
+
import { Loader } from "./Authenticate";
|
|
6
|
+
import ApplicationModuleLoader from "./ApplicationModuleLoader";
|
|
7
|
+
import { mount } from "enzyme";
|
|
8
|
+
import { getDefaultScope, getScopes } from "../actions/scopes";
|
|
9
|
+
import sinon from "sinon";
|
|
10
|
+
import { initializeFirstModuleScope } from "../actions/modules";
|
|
11
|
+
import { scopeTypes } from "../constants";
|
|
12
|
+
|
|
13
|
+
const TestComp = () => {
|
|
14
|
+
return <div className="test" />;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
jest.mock("../utils/buildUrl", () => {
|
|
18
|
+
const modExport = {};
|
|
19
|
+
modExport.loadConfig = () => Promise.resolve({});
|
|
20
|
+
modExport.buildUrl = (path = [], params = "") => "URL: " + path.join("/") + " " + JSON.stringify(params);
|
|
21
|
+
return modExport;
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("ApplicationModuleLoader", () => {
|
|
25
|
+
let state, store;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
state = Immutable.fromJS({
|
|
29
|
+
requests: {},
|
|
30
|
+
scopes: {
|
|
31
|
+
test1: {
|
|
32
|
+
id: "test1",
|
|
33
|
+
name: { "en-CA": "Test 1", "en-US": "Test 1" },
|
|
34
|
+
children: ["test2"],
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
authentication: {
|
|
38
|
+
name: "foo@bar.com",
|
|
39
|
+
},
|
|
40
|
+
settings: {
|
|
41
|
+
defaultScope: "aDefaultScope",
|
|
42
|
+
loadedModulesScope: ["moduleA", "moduleB"],
|
|
43
|
+
modules: ["moduleA", "moduleB"],
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
store = state => ({
|
|
47
|
+
subscribe: () => {},
|
|
48
|
+
getState: () => state,
|
|
49
|
+
dispatch: sinon.spy().named("dispatch"),
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("shows the component when scopes configuration are all loaded", () =>
|
|
54
|
+
expect(
|
|
55
|
+
<Provider store={store(state)}>
|
|
56
|
+
<ApplicationModuleLoader>
|
|
57
|
+
<TestComp />
|
|
58
|
+
</ApplicationModuleLoader>
|
|
59
|
+
</Provider>,
|
|
60
|
+
"when mounted",
|
|
61
|
+
"to exhaustively satisfy",
|
|
62
|
+
<TestComp />,
|
|
63
|
+
));
|
|
64
|
+
|
|
65
|
+
it("shows a load indicator component when default scope is still unknown", () => {
|
|
66
|
+
state = state.setIn(["settings", "defaultScope"], null);
|
|
67
|
+
return expect(
|
|
68
|
+
<Provider store={store(state)}>
|
|
69
|
+
<ThemeProvider theme={{}}>
|
|
70
|
+
<ApplicationModuleLoader>
|
|
71
|
+
<TestComp />
|
|
72
|
+
</ApplicationModuleLoader>
|
|
73
|
+
</ThemeProvider>
|
|
74
|
+
</Provider>,
|
|
75
|
+
"when mounted",
|
|
76
|
+
"to exhaustively satisfy",
|
|
77
|
+
<ThemeProvider theme={{}}>
|
|
78
|
+
<Loader />
|
|
79
|
+
</ThemeProvider>,
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("shows a load indicator component when scopes trees are not loaded for all modules", () => {
|
|
84
|
+
state = state.setIn(["settings", "loadedModulesScope"], Immutable.fromJS(["moduleA"]));
|
|
85
|
+
return expect(
|
|
86
|
+
<Provider store={store(state)}>
|
|
87
|
+
<ThemeProvider theme={{}}>
|
|
88
|
+
<ApplicationModuleLoader>
|
|
89
|
+
<TestComp />
|
|
90
|
+
</ApplicationModuleLoader>
|
|
91
|
+
</ThemeProvider>
|
|
92
|
+
</Provider>,
|
|
93
|
+
"when mounted",
|
|
94
|
+
"to exhaustively satisfy",
|
|
95
|
+
<ThemeProvider theme={{}}>
|
|
96
|
+
<Loader />
|
|
97
|
+
</ThemeProvider>,
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("scopes tree and default scope should be loaded when rendering the component", () => {
|
|
102
|
+
const theStore = store(state);
|
|
103
|
+
|
|
104
|
+
const component = (
|
|
105
|
+
<Provider store={theStore}>
|
|
106
|
+
<ThemeProvider theme={{}}>
|
|
107
|
+
<ApplicationModuleLoader>
|
|
108
|
+
<TestComp />
|
|
109
|
+
</ApplicationModuleLoader>
|
|
110
|
+
</ThemeProvider>
|
|
111
|
+
</Provider>
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
mount(component);
|
|
115
|
+
|
|
116
|
+
expect(theStore.dispatch, "to have calls satisfying", [
|
|
117
|
+
{ args: [getDefaultScope("moduleA")] },
|
|
118
|
+
{ args: [getScopes("moduleA")] },
|
|
119
|
+
{ args: [getDefaultScope("moduleB")] },
|
|
120
|
+
{ args: [getScopes("moduleB")] },
|
|
121
|
+
]);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("First module scope should be initialized when System is part application modules", () => {
|
|
125
|
+
state = state.setIn(["settings", "modules"], Immutable.fromJS(["moduleA", "System"]));
|
|
126
|
+
|
|
127
|
+
const theStore = store(state);
|
|
128
|
+
|
|
129
|
+
const component = (
|
|
130
|
+
<Provider store={theStore}>
|
|
131
|
+
<ThemeProvider theme={{}}>
|
|
132
|
+
<ApplicationModuleLoader>
|
|
133
|
+
<TestComp />
|
|
134
|
+
</ApplicationModuleLoader>
|
|
135
|
+
</ThemeProvider>
|
|
136
|
+
</Provider>
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
mount(component);
|
|
140
|
+
|
|
141
|
+
expect(theStore.dispatch, "to have calls satisfying", [
|
|
142
|
+
{ args: [initializeFirstModuleScope(scopeTypes.global)] },
|
|
143
|
+
{ args: [getDefaultScope("moduleA")] },
|
|
144
|
+
{ args: [getScopes("moduleA")] },
|
|
145
|
+
{ args: [getDefaultScope("System")] },
|
|
146
|
+
{ args: [getScopes("System")] },
|
|
147
|
+
]);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -6,10 +6,10 @@ import { useSelector } from "react-redux";
|
|
|
6
6
|
import { unwrapImmutable } from "../utils";
|
|
7
7
|
import { GET_AUTHENTICATION_PROFILE } from "../actions/authentication";
|
|
8
8
|
import { ERROR, LOGOUT } from "../reducers/request";
|
|
9
|
+
import ApplicationModuleLoader from "./ApplicationModuleLoader";
|
|
9
10
|
|
|
10
11
|
export const useAuthenticationData = () => ({
|
|
11
12
|
loading: useSelector(state => state.getIn(["requests", "actives", GET_AUTHENTICATION_PROFILE])),
|
|
12
|
-
defaultScope: useSelector(state => state.getIn(["settings", "defaultScope"])) || null,
|
|
13
13
|
authedUser: useSelector(state => state.getIn(["authentication", "name"])),
|
|
14
14
|
requestError: unwrapImmutable(useSelector(state => state.getIn(["requests", ERROR]) || null)),
|
|
15
15
|
needLogin: useSelector(state => state.getIn(["requests", LOGOUT])),
|
|
@@ -47,14 +47,15 @@ export const Error = ({ requestError, needLogin }) => {
|
|
|
47
47
|
};
|
|
48
48
|
|
|
49
49
|
const Authenticate = ({ children }) => {
|
|
50
|
-
const { loading,
|
|
51
|
-
|
|
50
|
+
const { loading, authedUser, requestError, needLogin } = useAuthenticationData();
|
|
51
|
+
|
|
52
|
+
if (loading) {
|
|
52
53
|
return <Loader />;
|
|
53
54
|
}
|
|
54
55
|
if (!authedUser) {
|
|
55
56
|
return <Error {...{ requestError, needLogin }} />;
|
|
56
57
|
} else {
|
|
57
|
-
return
|
|
58
|
+
return <ApplicationModuleLoader children={children} />;
|
|
58
59
|
}
|
|
59
60
|
};
|
|
60
61
|
|
|
@@ -7,19 +7,36 @@ import { ERROR, LOGOUT } from "../reducers/request";
|
|
|
7
7
|
import { GET_AUTHENTICATION_PROFILE } from "../actions/authentication";
|
|
8
8
|
|
|
9
9
|
const TestComp = () => {
|
|
10
|
-
return <div className="test"
|
|
10
|
+
return <div className="test" />;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
|
+
jest.mock("../utils/buildUrl", () => {
|
|
14
|
+
const modExport = {};
|
|
15
|
+
modExport.loadConfig = () => Promise.resolve({});
|
|
16
|
+
modExport.buildUrl = () => "URL";
|
|
17
|
+
return modExport;
|
|
18
|
+
});
|
|
19
|
+
|
|
13
20
|
describe("Authenticate", () => {
|
|
14
21
|
let state, store;
|
|
22
|
+
|
|
15
23
|
beforeEach(() => {
|
|
16
24
|
state = Immutable.fromJS({
|
|
17
25
|
requests: {},
|
|
26
|
+
scopes: {
|
|
27
|
+
test1: {
|
|
28
|
+
id: "test1",
|
|
29
|
+
name: { "en-CA": "Test 1", "en-US": "Test 1" },
|
|
30
|
+
children: ["test2"],
|
|
31
|
+
},
|
|
32
|
+
},
|
|
18
33
|
authentication: {
|
|
19
34
|
name: "foo@bar.com",
|
|
20
35
|
},
|
|
21
36
|
settings: {
|
|
22
37
|
defaultScope: "aDefaultScope",
|
|
38
|
+
loadedModulesScope: ["moduleA", "moduleB"],
|
|
39
|
+
modules: ["moduleA", "moduleB"],
|
|
23
40
|
},
|
|
24
41
|
});
|
|
25
42
|
store = state => ({
|
|
@@ -38,7 +55,9 @@ describe("Authenticate", () => {
|
|
|
38
55
|
</Provider>,
|
|
39
56
|
"when mounted",
|
|
40
57
|
"to exhaustively satisfy",
|
|
41
|
-
<
|
|
58
|
+
<Provider store={store(state)}>
|
|
59
|
+
<TestComp />
|
|
60
|
+
</Provider>,
|
|
42
61
|
));
|
|
43
62
|
|
|
44
63
|
it("shows a load indicator component if authentication is ongoing", () => {
|
|
@@ -59,12 +78,12 @@ describe("Authenticate", () => {
|
|
|
59
78
|
);
|
|
60
79
|
});
|
|
61
80
|
|
|
62
|
-
it("shows a load indicator component
|
|
81
|
+
it("shows a load indicator component when application modules not ready", () => {
|
|
63
82
|
state = state.setIn(["settings", "defaultScope"], null);
|
|
64
83
|
return expect(
|
|
65
84
|
<Provider store={store(state)}>
|
|
66
85
|
<ThemeProvider theme={{}}>
|
|
67
|
-
<Authenticate>
|
|
86
|
+
<Authenticate applicationModuleReady={false}>
|
|
68
87
|
<TestComp />
|
|
69
88
|
</Authenticate>
|
|
70
89
|
</ThemeProvider>
|
|
@@ -18,6 +18,8 @@ describe("Translations ", () => {
|
|
|
18
18
|
const theme = createMuiTheme();
|
|
19
19
|
|
|
20
20
|
beforeEach(() => {
|
|
21
|
+
window.bypassDebounce = true;
|
|
22
|
+
|
|
21
23
|
collapsibleListProps = new CollapsibleListProps();
|
|
22
24
|
collapsibleListProps.set(CollapsibleListProps.propNames.hasMessage, true);
|
|
23
25
|
collapsibleListProps.set(
|
|
@@ -32,6 +34,10 @@ describe("Translations ", () => {
|
|
|
32
34
|
collapsibleListProps.set(CollapsibleListProps.propNames.containerWidth, "unset");
|
|
33
35
|
});
|
|
34
36
|
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
delete window.bypassDebounce;
|
|
39
|
+
});
|
|
40
|
+
|
|
35
41
|
it("Renders Translations correctly", () => {
|
|
36
42
|
const component = (
|
|
37
43
|
<TestWrapper stylesProvider muiThemeProvider={{ theme }} intlProvider={{ messages }}>
|
|
@@ -135,7 +141,7 @@ describe("Translations ", () => {
|
|
|
135
141
|
expect(component, "when mounted", "to satisfy", expected);
|
|
136
142
|
});
|
|
137
143
|
|
|
138
|
-
it("Translations
|
|
144
|
+
it("Translations component handles change", () => {
|
|
139
145
|
const aValue = "aValue";
|
|
140
146
|
const update = sinon.spy().named("update");
|
|
141
147
|
|
|
@@ -111,12 +111,29 @@ const InputBase = ({ inputProps }) => {
|
|
|
111
111
|
|
|
112
112
|
const onChangeHandler = event => {
|
|
113
113
|
event.persist();
|
|
114
|
-
|
|
114
|
+
|
|
115
|
+
if (!event.target.value || window.bypassDebounce === true) {
|
|
116
|
+
update(event.target.value, metadata);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
setInputText(event.target.value);
|
|
115
120
|
};
|
|
116
121
|
|
|
117
122
|
const inputBaseInputStyle = inputProps?.getStyle(InputBaseProps.ruleNames.input);
|
|
118
123
|
const errorTextStyle = inputProps?.getStyle(InputBaseProps.ruleNames.errorText);
|
|
119
124
|
|
|
125
|
+
const [inputText, setInputText] = React.useState(null);
|
|
126
|
+
|
|
127
|
+
const textToDisplay = inputText ?? value;
|
|
128
|
+
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
if (inputText !== value && inputText != null && window.bypassDebounce !== true) {
|
|
131
|
+
const timeOutId = setTimeout(() => update(inputText, metadata), 100);
|
|
132
|
+
return () => clearTimeout(timeOutId);
|
|
133
|
+
}
|
|
134
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
135
|
+
}, [inputText, value]);
|
|
136
|
+
|
|
120
137
|
return (
|
|
121
138
|
<div className={classes.container}>
|
|
122
139
|
<div className={classes.inputContainer}>
|
|
@@ -133,7 +150,7 @@ const InputBase = ({ inputProps }) => {
|
|
|
133
150
|
onClick={onClick}
|
|
134
151
|
type={type}
|
|
135
152
|
placeholder={placeholder}
|
|
136
|
-
value={
|
|
153
|
+
value={textToDisplay}
|
|
137
154
|
fullWidth={true}
|
|
138
155
|
onChange={event => onChangeHandler(event)}
|
|
139
156
|
error={!!error}
|
|
@@ -5,15 +5,19 @@ import InputBaseMUI from "@material-ui/core/InputBase";
|
|
|
5
5
|
import sinon from "sinon";
|
|
6
6
|
import { ignoreConsoleError } from "../../../utils/testUtils";
|
|
7
7
|
import InputBaseProps from "./InputBaseProps";
|
|
8
|
+
import { act } from "unexpected-reaction";
|
|
8
9
|
|
|
9
10
|
describe("InputBase Component", () => {
|
|
10
11
|
let update, container;
|
|
11
12
|
beforeEach(() => {
|
|
13
|
+
window.bypassDebounce = true;
|
|
14
|
+
|
|
12
15
|
container = document.createElement("div");
|
|
13
16
|
document.body.appendChild(container);
|
|
14
17
|
update = sinon.spy().named("update");
|
|
15
18
|
});
|
|
16
19
|
afterEach(() => {
|
|
20
|
+
delete window.bypassDebounce;
|
|
17
21
|
document.body.removeChild(container);
|
|
18
22
|
container = null;
|
|
19
23
|
});
|
|
@@ -243,3 +247,67 @@ describe("InputBase Component", () => {
|
|
|
243
247
|
expect(mountedComponent.containsMatchingElement(expected), "to be truthy");
|
|
244
248
|
});
|
|
245
249
|
});
|
|
250
|
+
|
|
251
|
+
describe("InputBase component debouce", () => {
|
|
252
|
+
const clock = sinon.useFakeTimers();
|
|
253
|
+
let update, container;
|
|
254
|
+
|
|
255
|
+
beforeEach(() => {
|
|
256
|
+
container = document.createElement("div");
|
|
257
|
+
document.body.appendChild(container);
|
|
258
|
+
update = sinon.spy().named("update");
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
afterEach(() => {
|
|
262
|
+
clock.restore();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it("InputBase component updates when debounced update", async () => {
|
|
266
|
+
const inputProps = new InputBaseProps();
|
|
267
|
+
const aLabel = "aLabel";
|
|
268
|
+
const aValue = "value";
|
|
269
|
+
|
|
270
|
+
const metadata = {
|
|
271
|
+
test: "value",
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
inputProps.set(InputBaseProps.propNames.value, "");
|
|
275
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
276
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
277
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
278
|
+
|
|
279
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
280
|
+
const mountedComponent = mount(component);
|
|
281
|
+
|
|
282
|
+
const input = mountedComponent.find("input");
|
|
283
|
+
input.simulate("change", { target: { value: aValue } });
|
|
284
|
+
|
|
285
|
+
act(() => {
|
|
286
|
+
clock.tick(200);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
input.simulate("change", { target: { value: "differentValue" } });
|
|
290
|
+
expect(update, "to have calls satisfying", [{ args: [aValue, metadata] }]);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("InputBase component update to empty when text to display is null", async () => {
|
|
294
|
+
const inputProps = new InputBaseProps();
|
|
295
|
+
const aLabel = "aLabel";
|
|
296
|
+
|
|
297
|
+
const metadata = {
|
|
298
|
+
test: "value",
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
inputProps.set(InputBaseProps.propNames.value, null);
|
|
302
|
+
inputProps.set(InputBaseProps.propNames.update, update);
|
|
303
|
+
inputProps.set(InputBaseProps.propNames.label, aLabel);
|
|
304
|
+
inputProps.set(InputBaseProps.propNames.metadata, metadata);
|
|
305
|
+
|
|
306
|
+
const component = <InputBase inputProps={inputProps} />;
|
|
307
|
+
const mountedComponent = mount(component);
|
|
308
|
+
|
|
309
|
+
const input = mountedComponent.find("input");
|
|
310
|
+
input.simulate("change", { target: { value: null } });
|
|
311
|
+
expect(input.get(0).props.value, "to equal", "");
|
|
312
|
+
});
|
|
313
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Link from "@material-ui/core/Link";
|
|
3
|
+
import Icon from "../DataDisplay/Icon";
|
|
4
|
+
import { makeStyles } from "@material-ui/core/styles";
|
|
5
|
+
|
|
6
|
+
const useStyles = makeStyles(theme => ({
|
|
7
|
+
icon: {
|
|
8
|
+
paddingLeft: theme.spacing(1),
|
|
9
|
+
width: theme.spacing(1.2),
|
|
10
|
+
color: theme.palette.primary.main,
|
|
11
|
+
},
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
const ExternalLink = ({ id, url, children }) => {
|
|
15
|
+
const classes = useStyles();
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<Link id={id} href={url} rel="noreferrer" target="_blank" underline="always">
|
|
19
|
+
{children}
|
|
20
|
+
<Icon id="open-in-new-tab" className={classes.icon} />
|
|
21
|
+
</Link>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default ExternalLink;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Link from "@material-ui/core/Link";
|
|
3
|
+
import Icon from "../DataDisplay/Icon";
|
|
4
|
+
import ExternalLink from "./ExternalLink";
|
|
5
|
+
|
|
6
|
+
describe("ExternalLink", () => {
|
|
7
|
+
it("render ExternalLink without errors", () => {
|
|
8
|
+
const children = <div>a div element</div>;
|
|
9
|
+
|
|
10
|
+
expect(
|
|
11
|
+
<ExternalLink id="an_Id" url="https://www.npmjs.com/package/orc-shared" children={children} />,
|
|
12
|
+
"when mounted",
|
|
13
|
+
"to satisfy",
|
|
14
|
+
<Link
|
|
15
|
+
id="an_Id"
|
|
16
|
+
href="https://www.npmjs.com/package/orc-shared"
|
|
17
|
+
rel="noreferrer"
|
|
18
|
+
target="_blank"
|
|
19
|
+
underline="always"
|
|
20
|
+
>
|
|
21
|
+
{children}
|
|
22
|
+
<Icon id="open-in-new-tab" />
|
|
23
|
+
</Link>,
|
|
24
|
+
);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -16,6 +16,10 @@ const useStyles = makeStyles(theme => ({
|
|
|
16
16
|
border: `1px solid ${theme.palette.grey.borders}`,
|
|
17
17
|
boxShadow: "0 2px 4px rgba(0,0,0,0.5)",
|
|
18
18
|
width: theme.spacing(50),
|
|
19
|
+
display: "flex",
|
|
20
|
+
},
|
|
21
|
+
scopeContainer: {
|
|
22
|
+
width: "100%",
|
|
19
23
|
},
|
|
20
24
|
scopeSelector: {
|
|
21
25
|
display: "flex",
|
|
@@ -64,7 +68,7 @@ const ScopeSelector = ({ show, getScope, selectedScope, closeSelector, filter, u
|
|
|
64
68
|
<Sidepanel className={classes.container} in={show} timeout={300}>
|
|
65
69
|
<ClickAwayListener onClickAway={e => closeSelector(e)}>
|
|
66
70
|
{/* this div is required since ClickAwayListener child element should be able to hold ref */}
|
|
67
|
-
<div>{show ? scopeSelectorContent : null}</div>
|
|
71
|
+
<div className={classes.scopeContainer}>{show ? scopeSelectorContent : null}</div>
|
|
68
72
|
</ClickAwayListener>
|
|
69
73
|
</Sidepanel>
|
|
70
74
|
);
|