Skip to content

Commit

Permalink
Fixes #1656: initialState override from localConfig (#1658)
Browse files Browse the repository at this point in the history
* Fixes #1656: initialState override from localConfig

* Added more documentation
  • Loading branch information
mbarto authored Mar 30, 2017
1 parent c0af4e2 commit dfe6d2e
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 49 deletions.
17 changes: 14 additions & 3 deletions docs/developer-guide/local-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ This is the main structure:
"useCORS": ["http://nominatim.openstreetmap.org", "https://nominatim.openstreetmap.org"]
},
// API keys for bing and mapquest services
"bingApiKey"
"mapquestApiKey"
"bingApiKey",
"mapquestApiKey",
// path to the translation files directory (if different from default)
"translationsPath"
"translationsPath",
// if true, every ajax and mapping request will be authenticated with the configurations if match a rule
"useAuthenticationRules": true
// the athentication rules to match
Expand All @@ -40,6 +40,17 @@ This is the main structure:
}, {
"urlPattern": "\\/geoserver.*",
"method": "authkey"
},
// optional state initializer (it will override the one defined in appConfig.js)
"initialState": {
// default initial state for every mode (will override initialState imposed by plugins reducers)
"defaultState": {
...
},
// mobile override (defined properties will overide default in mobile mode)
"mobile": {
...
}
}
"plugins": {
// plugins to load for the mobile mode
Expand Down
2 changes: 1 addition & 1 deletion project/static/js/app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const StandardRouter = connect((state) => ({
pages
}))(require('../MapStore2/web/client/components/app/StandardRouter'));

const appStore = require('../MapStore2/web/client/stores/StandardStore').bind(null, initialState, {});
const appStore = require('../MapStore2/web/client/stores/StandardStore').bind(null, initialState, {}, {}, {});

const appConfig = {
storeOpts,
Expand Down
63 changes: 38 additions & 25 deletions web/client/components/app/StandardApp.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,52 +40,65 @@ const StandardApp = React.createClass({
appComponent: () => <span/>
};
},
getInitialState() {
return {
store: null
};
},
componentWillMount() {
const onInit = () => {
const onInit = (config) => {
if (!global.Intl ) {
require.ensure(['intl', 'intl/locale-data/jsonp/en.js', 'intl/locale-data/jsonp/it.js'], (require) => {
global.Intl = require('intl');
require('intl/locale-data/jsonp/en.js');
require('intl/locale-data/jsonp/it.js');
this.init();
this.init(config);
});
} else {
this.init();
this.init(config);
}
};
const opts = assign({}, this.props.storeOpts, {
onPersist: onInit
});
this.store = this.props.appStore(this.props.pluginsDef.plugins, opts);
if (!opts.persist) {
onInit();

if (urlQuery.localConfig) {
ConfigUtils.setLocalConfigurationFile(urlQuery.localConfig + '.json');
}
ConfigUtils.loadConfiguration().then((config) => {
const opts = assign({}, this.props.storeOpts, {
onPersist: onInit.bind(null, config)
}, {
initialState: config.initialState || {defaultState: {}, mobile: {}}
});
this.store = this.props.appStore(this.props.pluginsDef.plugins, opts);
this.setState({
store: this.store
});

if (!opts.persist) {
onInit(config);
}
});

},
render() {
const {plugins, requires} = this.props.pluginsDef;
const {pluginsDef, appStore, initialActions, appComponent, ...other} = this.props;
const App = this.props.appComponent;
return (
<Provider store={this.store}>
return this.state.store ? (
<Provider store={this.state.store}>
<App {...other} plugins={assign(PluginsUtils.getPlugins(plugins), {requires})}/>
</Provider>
);
) : null;
},
init() {
init(config) {
this.store.dispatch(changeBrowserProperties(ConfigUtils.getBrowserProperties()));
if (urlQuery.localConfig) {
ConfigUtils.setLocalConfigurationFile(urlQuery.localConfig + '.json');
this.store.dispatch(localConfigLoaded(config));
const locale = LocaleUtils.getUserLocale();
this.store.dispatch(loadLocale(null, locale));
if (this.props.printingEnabled) {
this.store.dispatch(loadPrintCapabilities(ConfigUtils.getConfigProp('printUrl')));
}
ConfigUtils.loadConfiguration().then((config) => {
this.store.dispatch(localConfigLoaded(config));
const locale = LocaleUtils.getUserLocale();
this.store.dispatch(loadLocale(null, locale));
if (this.props.printingEnabled) {
this.store.dispatch(loadPrintCapabilities(ConfigUtils.getConfigProp('printUrl')));
}
this.props.initialActions.forEach((action) => {
this.store.dispatch(action());
});
this.props.initialActions.forEach((action) => {
this.store.dispatch(action());
});
}
});
Expand Down
69 changes: 53 additions & 16 deletions web/client/components/app/__tests__/StandardApp-test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,18 @@ describe('StandardApp', () => {
expect(app).toExist();
});

it('creates a default app with the given store creator', () => {
it('creates a default app with the given store creator', (done) => {
let dispatched = 0;
const store = () => ({
dispatch() {
dispatched++;
done();
}
});


const app = ReactDOM.render(<StandardApp appStore={store}/>, document.getElementById("container"));
expect(app).toExist();

expect(dispatched > 0).toBe(true);
});

it('creates a default app and runs the initial actions', (done) => {
Expand All @@ -83,20 +82,49 @@ describe('StandardApp', () => {
expect(app).toExist();
});

it('creates a default app and reads initialState from localConfig', (done) => {
const store = (plugins, storeOpts) => {
expect(storeOpts.initialState.defaultState.test).toExist();
done();
return {
dispatch() {
}
};
};

const storeOpts = {
initialState: {
defaultState: {
test: "test"
},
mobile: {}
}
};
const app = ReactDOM.render(<StandardApp appStore={store} storeOpts={storeOpts}/>, document.getElementById("container"));
expect(app).toExist();
});

it('creates a default app and renders the given component', () => {
const store = () => ({
dispatch: () => {},
subscribe: () => {},
getState: () => ({})
});


const app = ReactDOM.render(<StandardApp appStore={store} appComponent={mycomponent}/>, document.getElementById("container"));
expect(app).toExist();

const dom = ReactDOM.findDOMNode(app);

expect(dom.className).toBe('mycomponent');
const oldLoad = ConfigUtils.loadConfiguration;
try {
ConfigUtils.loadConfiguration = () => ({
then: (callback) => {
callback({});
}
});
const app = ReactDOM.render(<StandardApp appStore={store} appComponent={mycomponent}/>, document.getElementById("container"));
expect(app).toExist();

const dom = ReactDOM.findDOMNode(app);
expect(dom.className).toBe('mycomponent');
} finally {
ConfigUtils.loadConfiguration = oldLoad;
}
});

it('creates a default app and configures plugins', () => {
Expand All @@ -115,12 +143,21 @@ describe('StandardApp', () => {
subscribe: () => {},
getState: () => ({})
});
const oldLoad = ConfigUtils.loadConfiguration;
try {
ConfigUtils.loadConfiguration = () => ({
then: (callback) => {
callback({});
}
});

const app = ReactDOM.render(<StandardApp appStore={store} appComponent={mycomponent} pluginsDef={pluginsDef}/>, document.getElementById("container"));
expect(app).toExist();

const dom = ReactDOM.findDOMNode(app);
const app = ReactDOM.render(<StandardApp appStore={store} appComponent={mycomponent} pluginsDef={pluginsDef}/>, document.getElementById("container"));
expect(app).toExist();

expect(dom.getElementsByClassName('MyPlugin').length).toBe(1);
const dom = ReactDOM.findDOMNode(app);
expect(dom.getElementsByClassName('MyPlugin').length).toBe(1);
} finally {
ConfigUtils.loadConfiguration = oldLoad;
}
});
});
7 changes: 4 additions & 3 deletions web/client/stores/StandardStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const {createEpicMiddleware} = require('redux-observable');
const SecurityUtils = require('../utils/SecurityUtils');
const ListenerEnhancer = require('@carnesen/redux-add-action-listener-enhancer').default;

module.exports = (initialState = {defaultState: {}, mobile: {}}, appReducers = {}, appEpics = {}, plugins, storeOpts) => {
module.exports = (initialState = {defaultState: {}, mobile: {}}, appReducers = {}, appEpics = {}, plugins, storeOpts = {}) => {
const allReducers = combineReducers(plugins, {
...appReducers,
localConfig: require('../reducers/localConfig'),
Expand All @@ -40,8 +40,9 @@ module.exports = (initialState = {defaultState: {}, mobile: {}}, appReducers = {
layers: () => {return null; }
});
const rootEpic = combineEpics(plugins, appEpics);
const defaultState = initialState.defaultState;
const mobileOverride = initialState.mobile;
const optsState = storeOpts.initialState || {defaultState: {}, mobile: {}};
const defaultState = assign({}, initialState.defaultState, optsState.defaultState);
const mobileOverride = assign({}, initialState.mobile, optsState.mobile);
const epicMiddleware = createEpicMiddleware(rootEpic);
const rootReducer = (state, action) => {
let mapState = createHistory(LayersUtils.splitMapAndLayers(mapConfig(state, action)));
Expand Down
9 changes: 8 additions & 1 deletion web/client/test-resources/localConfig.json
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
{}
{
"initialState": {
"defaultState": {
"test": "test"
},
"mobile": {}
}
}

0 comments on commit dfe6d2e

Please sign in to comment.