Skip to content

Commit

Permalink
[Feat] pass initialUiState to prop (keplergl#1187)
Browse files Browse the repository at this point in the history
* [Feat] pass initialUiState as prop to KeplerGl component
Signed-off-by: Shan He <[email protected]>
  • Loading branch information
heshan0131 authored Jul 31, 2020
1 parent f8620a3 commit c942b9e
Show file tree
Hide file tree
Showing 16 changed files with 111 additions and 52 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ const mapStyles = [
];
```
#### `initialUiState` (object, optional)
- Default: `undefined`
Intial UI State applied to uiState reducer, value will be shallow merged with default [`INITIAL_UI_STATE`](https://docs.kepler.gl/docs/api-reference/reducers/ui-state#initial_ui_state)
### 3. Dispatch custom actions to `keplerGl` reducer.
One advantage of using the reducer over React component state to handle keplerGl state is the flexibility
Expand Down
8 changes: 3 additions & 5 deletions src/actions/actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {ParsedConfig} from '../schemas';
import {RGBColor} from 'reducers/types';
import {Bounds} from 'reducers/map-state-updaters';
import {MapInfo} from 'reducers/vis-state-updaters';
import {UiState} from 'reducers/ui-state-updaters';

/**
* Input dataest parsed to addDataToMap
Expand Down Expand Up @@ -84,13 +85,10 @@ export type KeplerGlInitPayload = {
mapboxApiAccessToken?: string;
mapboxApiUrl?: string;
mapStylesReplaceDefault?: boolean;
initialUiState?: Partial<UiState>;
};

export function keplerGlInit(options?: {
mapboxApiAccessToken?: string;
mapboxApiUrl?: string;
mapStylesReplaceDefault?: boolean;
}): {
export function keplerGlInit(options?: KeplerGlInitPayload): {
type: ActionTypes.INIT;
payload: KeplerGlInitPayload;
};
7 changes: 2 additions & 5 deletions src/actions/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,17 +153,14 @@ export const receiveMapConfig = createAction(ActionTypes.RECEIVE_MAP_CONFIG, (co
* @param payload.mapboxApiAccessToken - mapboxApiAccessToken to be saved to mapStyle reducer
* @param payload.mapboxApiUrl - mapboxApiUrl to be saved to mapStyle reducer.
* @param payload.mapStylesReplaceDefault - mapStylesReplaceDefault to be saved to mapStyle reducer
* @param payload.initialUiState - initial ui state
* @type {typeof import('./actions').keplerGlInit}
* @public
*/
export const keplerGlInit = createAction(
ActionTypes.INIT,
// @ts-ignore
({mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault} = {}) => ({
mapboxApiAccessToken,
mapboxApiUrl,
mapStylesReplaceDefault
})
payload => payload
);

/**
Expand Down
10 changes: 6 additions & 4 deletions src/actions/identity-actions.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
// THE SOFTWARE.

import ActionTypes from 'constants/action-types';
import {UiState} from 'reducers/ui-state-updaters';

export type RegisterEntryUpdaterAction = {
payload: {
id: string;
mint: boolean;
mapboxApiAccessToken: string;
mapboxApiUrl: string;
mapStylesReplaceDefault: boolean;
mint?: boolean;
mapboxApiAccessToken?: string;
mapboxApiUrl?: string;
mapStylesReplaceDefault?: boolean;
initialUiState?:Partial<UiState>;
};
};

Expand Down
25 changes: 9 additions & 16 deletions src/actions/identity-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,25 +27,18 @@ import ActionTypes from 'constants/action-types';
* Note that if you dispatch actions such as adding data to a kepler.gl instance before the React component is mounted, the action will not be
* performed. Instance reducer can only handle actions when it is instantiated.
* @memberof rootActions
* @param {Object} payload
* @param {string} payload.id - ***required** The id of the instance
* @param {boolean} payload.mint - Whether to use a fresh empty state, when `mint: true` it will *always* load a fresh state when the component is re-mounted.
* @param payload
* @param payload.id - ***required** The id of the instance
* @param payload.mint - Whether to use a fresh empty state, when `mint: true` it will *always* load a fresh state when the component is re-mounted.
* When `mint: false` it will register with existing instance state under the same `id`, when the component is unmounted then mounted again. Default: `true`
* @param {string} payload.mapboxApiAccessToken - mapboxApiAccessToken to be saved in `map-style` reducer.
* @param {string} payload.mapboxApiUrl - mapboxApiUrl to be saved in `map-style` reducer.
* @param {Boolean} payload.mapStylesReplaceDefault - mapStylesReplaceDefault to be saved in `map-style` reducer.
* @param payload.mapboxApiAccessToken - mapboxApiAccessToken to be saved in `map-style` reducer.
* @param payload.mapboxApiUrl - mapboxApiUrl to be saved in `map-style` reducer.
* @param payload.mapStylesReplaceDefault - mapStylesReplaceDefault to be saved in `map-style` reducer.
* @param payload.initialUiState - initial ui state
* @type {typeof import('./identity-actions').registerEntry}
* @public
*/
export const registerEntry = createAction(
ActionTypes.REGISTER_ENTRY,
({id, mint, mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault}) => ({
id,
mint,
mapboxApiAccessToken,
mapboxApiUrl,
mapStylesReplaceDefault
})
);
export const registerEntry = createAction(ActionTypes.REGISTER_ENTRY, payload => payload);

/**
*
Expand Down
14 changes: 12 additions & 2 deletions src/components/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function ContainerFactory(KeplerGl) {
* @param {string} props.mapboxApiAccessToken - _required_
* @param {string} props.mapboxApiUrl - _optional_
* @param {Boolean} props.mapStylesReplaceDefault - _optional_
* @param {object} props.initialUiState - _optional_
* You can create a free account at [www.mapbox.com](www.mapbox.com) and create a token at
* [www.mapbox.com/account/access-tokens](www.mapbox.com/account/access-tokens)
Expand Down Expand Up @@ -89,15 +90,24 @@ export function ContainerFactory(KeplerGl) {
}

componentDidMount() {
const {id, mint, mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault} = this.props;
const {
id,
mint,
mapboxApiAccessToken,
mapboxApiUrl,
mapStylesReplaceDefault,
initialUiState
} = this.props;

// add a new entry to reducer
this.props.dispatch(
registerEntry({
id,
mint,
mapboxApiAccessToken,
mapboxApiUrl,
mapStylesReplaceDefault
mapStylesReplaceDefault,
initialUiState
})
);
}
Expand Down
10 changes: 5 additions & 5 deletions src/reducers/map-style-updaters.js
Original file line number Diff line number Diff line change
Expand Up @@ -223,13 +223,13 @@ function getLayerGroupsFromStyle(style) {
* @type {typeof import('./map-style-updaters').initMapStyleUpdater}
* @public
*/
export const initMapStyleUpdater = (state, action) => ({
export const initMapStyleUpdater = (state, {payload = {}}) => ({
...state,
// save mapbox access token to map style state
mapboxApiAccessToken: (action.payload || {}).mapboxApiAccessToken,
mapboxApiUrl: (action.payload || {}).mapboxApiUrl || state.mapboxApiUrl,
mapStyles: action.payload && !action.payload.mapStylesReplaceDefault ? state.mapStyles : {},
mapStylesReplaceDefault: action.payload.mapStylesReplaceDefault || false
mapboxApiAccessToken: payload.mapboxApiAccessToken || state.mapboxApiAccessToken,
mapboxApiUrl: payload.mapboxApiUrl || state.mapboxApiUrl,
mapStyles: !payload.mapStylesReplaceDefault ? state.mapStyles : {},
mapStylesReplaceDefault: payload.mapStylesReplaceDefault || false
});
// });

Expand Down
13 changes: 11 additions & 2 deletions src/reducers/root.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,16 @@ export function provideInitialState(initialState) {

const handleRegisterEntry = (
state,
{payload: {id, mint, mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault}}
{
payload: {
id,
mint,
mapboxApiAccessToken,
mapboxApiUrl,
mapStylesReplaceDefault,
initialUiState
}
}
) => {
// by default, always create a mint state even if the same id already exist
// if state.id exist and mint=false, keep the existing state
Expand All @@ -44,7 +53,7 @@ export function provideInitialState(initialState) {
...state,
[id]: coreReducer(
previousState,
keplerGlInit({mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault})
keplerGlInit({mapboxApiAccessToken, mapboxApiUrl, mapStylesReplaceDefault, initialUiState})
)
};
};
Expand Down
4 changes: 4 additions & 0 deletions src/reducers/ui-state-updaters.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,3 +187,7 @@ export function loadFilesUpdater(state: UiState, action: LoadFilesUpdaterAction)
export function loadFilesErrUpdater(state: UiState, action: LoadFilesErrUpdaterAction): UiState;
export function loadFilesSuccessUpdater(state: UiState): UiState;
export function toggleSplitMapUpdater(state: UiState, action: ToggleSplitMapUpdaterAction): UiState;
export function initUiStateUpdater(state: UiState, action: {
type?: ActionTypes.INIT;
payload: KeplerGlInitPayload;
}): UiState;
9 changes: 9 additions & 0 deletions src/reducers/ui-state-updaters.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,15 @@ export const INITIAL_UI_STATE = {
};

/* Updaters */
/**
* @memberof uiStateUpdaters
*/
export const initUiStateUpdater = (state, action) => ({
...state,
...(action.payload || {}).initialUiState
});

/**
* Toggle active side panel
* @memberof uiStateUpdaters
Expand Down
1 change: 1 addition & 0 deletions src/reducers/ui-state.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import * as uiStateUpdaters from './ui-state-updaters';
* It is used to generate documentation
*/
const actionHandler = {
[ActionTypes.INIT]: uiStateUpdaters.initUiStateUpdater,
[ActionTypes.TOGGLE_SIDE_PANEL]: uiStateUpdaters.toggleSidePanelUpdater,
[ActionTypes.TOGGLE_MODAL]: uiStateUpdaters.toggleModalUpdater,
[ActionTypes.SHOW_EXPORT_DROPDOWN]: uiStateUpdaters.showExportDropdownUpdater,
Expand Down
12 changes: 8 additions & 4 deletions test/browser/components/container-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ test('Components -> Container -> Mount with mint:true', t => {
mint: true,
mapboxApiAccessToken: undefined,
mapboxApiUrl: undefined,
mapStylesReplaceDefault: undefined
mapStylesReplaceDefault: undefined,
initialUiState: undefined
}
};

Expand Down Expand Up @@ -130,7 +131,8 @@ test('Components -> Container -> Mount with mint:true', t => {
mint: true,
mapboxApiAccessToken: 'pk.smoothie',
mapboxApiUrl: undefined,
mapStylesReplaceDefault: undefined
mapStylesReplaceDefault: undefined,
initialUiState: undefined
}
};

Expand Down Expand Up @@ -206,7 +208,8 @@ test('Components -> Container -> Mount with mint:false', t => {
mint: false,
mapboxApiAccessToken: 'hello.world',
mapboxApiUrl: undefined,
mapStylesReplaceDefault: undefined
mapStylesReplaceDefault: undefined,
initialUiState: undefined
}
};

Expand Down Expand Up @@ -273,7 +276,8 @@ test('Components -> Container -> Mount then rename', t => {
mint: true,
mapboxApiAccessToken: 'hello.world',
mapboxApiUrl: undefined,
mapStylesReplaceDefault: undefined
mapStylesReplaceDefault: undefined,
initialUiState: undefined
}
};

Expand Down
5 changes: 0 additions & 5 deletions test/browser/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// required by enzymev3
const configure = require('enzyme').configure;
const Adapter = require('enzyme-adapter-react-16');
configure({adapter: new Adapter()});

import './injector-test';
import './container-test';
import './kepler-gl-test';
Expand Down
11 changes: 7 additions & 4 deletions test/browser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
const configure = require('enzyme').configure;
const Adapter = require('enzyme-adapter-react-16');
configure({adapter: new Adapter()});

// component tests
import './components';
require('./components');

// test layers
import './layer-tests';
require('./layer-tests');

// test reducers
import './reducers';
require('./reducers');

// test processors
import './file-handler';
require('./file-handler');
10 changes: 10 additions & 0 deletions test/node/reducers/map-style-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ test('#mapStyleReducer', t => {
});

test('#mapStyleReducer -> INIT', t => {
const initialState = reducer(InitialMapStyle, keplerGlInit());
t.deepEqual(
initialState,
{
...INITIAL_MAP_STYLE,
initialState: {}
},
'initialize map style with no argument'
);

const newState = reducer(
InitialMapStyle,
keplerGlInit({
Expand Down
18 changes: 18 additions & 0 deletions test/node/reducers/ui-state-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
startSaveStorage
} from 'actions/ui-state-actions';
import {loadFiles, loadFilesErr} from 'actions/vis-state-actions';
import {keplerGlInit} from 'actions/actions';
import reducer, {uiStateReducerFactory} from 'reducers/ui-state';
import {INITIAL_UI_STATE} from 'reducers/ui-state-updaters';
import {
Expand All @@ -55,6 +56,23 @@ test('#uiStateReducer', t => {
t.end();
});

test('#uiStateReducer -> INIT', t => {
const uiStateReducer = uiStateReducerFactory();

const newState = reducer(
uiStateReducer(undefined, {}),
keplerGlInit({
initialUiState: {readOnly: true}
})
);
t.deepEqual(
newState,
{...INITIAL_UI_STATE, readOnly: true, initialState: {}},
'should apply initialUiState'
);
t.end();
});

test('#uiStateReducerFactory', t => {
const uiStateReducer = uiStateReducerFactory({readOnly: true});

Expand Down

0 comments on commit c942b9e

Please sign in to comment.