diff --git a/src/actions/index.js b/src/actions/index.js index 15043442..2c3276f4 100644 --- a/src/actions/index.js +++ b/src/actions/index.js @@ -63,9 +63,9 @@ export function setPageSize(pageSize) { } } -export function updateState({ data, pageProperties = {}, sortProperties = {} }) { +export function updateState(newState) { return { type: GRIDDLE_UPDATE_STATE, - newState: { data, pageProperties, sortProperties } + newState } } diff --git a/src/module.d.ts b/src/module.d.ts index 283c9a76..2325a8f2 100644 --- a/src/module.d.ts +++ b/src/module.d.ts @@ -315,6 +315,7 @@ export interface GriddleSortKey { export interface GriddleStyleElements { Cell?: T; Filter?: T; + Layout?: T; Loading?: T; NextButton?: T; NoResults?: T; diff --git a/src/reducers/__tests__/dataReducerTest.js b/src/reducers/__tests__/dataReducerTest.js index d53dc8aa..82e88524 100644 --- a/src/reducers/__tests__/dataReducerTest.js +++ b/src/reducers/__tests__/dataReducerTest.js @@ -183,3 +183,71 @@ test('toggle column works when there is no visible property', (t) => { }); + +test('update state merges non-data', (t) => { + const initialState = Immutable.fromJS({ + changed: 1, + unchanged: 2, + nested: { + changed: 3, + unchanged: 4, + }, + data: [], + lookup: {}, + }); + const newState = { + changed: -1, + nested: { + changed: -3, + }, + }; + + const state = reducers.GRIDDLE_UPDATE_STATE(initialState, { newState }); + + t.deepEqual(state.toJSON(), { + changed: -1, + unchanged: 2, + nested: { + changed: -3, + unchanged: 4, + }, + data: [], + lookup: {}, + }); +}); + +test('update state transforms data', (t) => { + const initialState = Immutable.fromJS({ + unchanged: 2, + nested: { + unchanged: 4, + }, + data: [ + {name: "one", griddleKey: 0}, + {name: "two", griddleKey: 1}, + ], + lookup: { 0: 0, 1: 1 }, + }); + const newState = { + data: [ + { name: 'uno' }, + { name: 'dos' }, + { name: 'tre' }, + ] + }; + + const state = reducers.GRIDDLE_UPDATE_STATE(initialState, { newState }); + + t.deepEqual(state.toJSON(), { + unchanged: 2, + nested: { + unchanged: 4, + }, + data: [ + {name: "uno", griddleKey: 0}, + {name: "dos", griddleKey: 1}, + {name: "tre", griddleKey: 2}, + ], + lookup: { 0: 0, 1: 1, 2: 2 }, + }); +}); diff --git a/src/reducers/dataReducer.js b/src/reducers/dataReducer.js index 26d7c44f..c2fb4b2d 100644 --- a/src/reducers/dataReducer.js +++ b/src/reducers/dataReducer.js @@ -132,11 +132,19 @@ export function GRIDDLE_TOGGLE_COLUMN(state, action) { new Immutable.Map({ id: action.columnId, visible: true })); } +const defaultRenderProperties = Immutable.fromJS({}); export function GRIDDLE_UPDATE_STATE(state, action) { const { data, ...newState } = action.newState; - const transformedData = transformData(data, state.get('renderProperties').toJSON()); - return state.mergeDeep(Immutable.fromJS(newState)) + var mergedState = state.mergeDeep(Immutable.fromJS(newState)); + if (!data) { + return mergedState; + } + + const renderProperties = state.get('renderProperties', defaultRenderProperties).toJSON(); + const transformedData = transformData(data, renderProperties); + + return mergedState .set('data', transformedData.data) .set('lookup', transformedData.lookup); } diff --git a/src/utils/__tests__/initilizerTests.js b/src/utils/__tests__/initilizerTests.js index 31522b38..7309c09c 100644 --- a/src/utils/__tests__/initilizerTests.js +++ b/src/utils/__tests__/initilizerTests.js @@ -60,7 +60,7 @@ test('init succeeds given empty defaults and props', (assert) => { test('init returns defaults given minimum props', (assert) => { const ctx = { props: { data: [] } }; const defaults = { - reducers: { REDUCE: () => ({ reduced: true }) }, + reducer: { REDUCE: () => ({ reduced: true }) }, components: { Layout: () => null }, settingsComponentObjects: { mySettings: { order: 10 } }, selectors: { aSelector: () => null }, @@ -82,7 +82,7 @@ test('init returns defaults given minimum props', (assert) => { }); assert.is(typeof res.reducers, 'function'); - assert.deepEqual(Object.keys(res.reducers), Object.keys(defaults.reducers)); + assert.deepEqual(Object.keys(res.reducers), Object.keys(defaults.reducer)); assert.deepEqual(res.reducers({}, { type: 'REDUCE' }), { reduced: true }); assert.deepEqual(res.reduxMiddleware, []); @@ -244,7 +244,7 @@ test('init returns composed reducer given plugins', (assert) => { }, }; const defaults = { - reducers: { + reducer: { DEFAULTS: () => ({ defaults: true }), PLUGIN: () => ({ plugin: false }), }, diff --git a/src/utils/initializer.js b/src/utils/initializer.js index 193c7e86..2238b8ab 100644 --- a/src/utils/initializer.js +++ b/src/utils/initializer.js @@ -7,7 +7,7 @@ module.exports = function initializer(defaults) { if (!this) throw new Error('this missing!'); const { - reducers: dataReducers, + reducer: dataReducers, components, settingsComponentObjects, selectors, diff --git a/stories/index.tsx b/stories/index.tsx index 67ada22e..b8e511d6 100644 --- a/stories/index.tsx +++ b/stories/index.tsx @@ -105,6 +105,58 @@ storiesOf('Griddle main', module) ) }) + .add('with external prop changes', () => { + const NoResultsWithN = connect( + (state: any) => ({ + n: state.get('n'), + addTen: state.get('addTen'), + }), + () => {} + )(({ n, addTen }) => ( +
+

n = {n}

+ +
+ )); + + class Stateful extends React.Component<{}, { n: number }> { + constructor(props) { + super(props); + this.state = { n: 0 }; + } + + render() { + const { n } = this.state; + return ( +
+

+ Click to change Griddle props:{' '} + + +

+ this.setState(({ n }) => ({ n: n + 10 }))} + plugins={[LocalPlugin]} + data={fakeData.filter((d,i) => i % n === 0)} + components={{ + NoResults: NoResultsWithN + }} + styleConfig={{ + styles: { + Layout: { color: n % 3 ? 'blue' : 'inherit' }, + }, + }} + textProperties={{ + settingsToggle: `Settings (${n})` + }} + /> +
+ ); + } + } + return ; + }) .add('with local, delayed data', () => { class DeferredGriddle extends React.Component, { data?: FakeData[] }> { private timeout;