initial commit
This commit is contained in:
@ -0,0 +1,4 @@
|
||||
export const ACTION_TYPES = {
|
||||
SET_QUERY_KEY_VALUE: 'SET_QUERY_KEY_VALUE',
|
||||
SET_QUERY_CONTEXT_VALUE: 'SET_QUERY_CONTEXT_VALUE',
|
||||
};
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ACTION_TYPES as types } from './action-types';
|
||||
|
||||
/**
|
||||
* Action creator for setting a single query-state value for a given context.
|
||||
*
|
||||
* @param {string} context Context for query state being stored.
|
||||
* @param {string} queryKey Key for query item.
|
||||
* @param {*} value The value for the query item.
|
||||
*
|
||||
* @return {Object} The action object.
|
||||
*/
|
||||
export const setQueryValue = ( context, queryKey, value ) => {
|
||||
return {
|
||||
type: types.SET_QUERY_KEY_VALUE,
|
||||
context,
|
||||
queryKey,
|
||||
value,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Action creator for setting query-state for a given context.
|
||||
*
|
||||
* @param {string} context Context for query state being stored.
|
||||
* @param {*} value Query state being stored for the given context.
|
||||
*
|
||||
* @return {Object} The action object.
|
||||
*/
|
||||
export const setValueForQueryContext = ( context, value ) => {
|
||||
return {
|
||||
type: types.SET_QUERY_CONTEXT_VALUE,
|
||||
context,
|
||||
value,
|
||||
};
|
||||
};
|
@ -0,0 +1 @@
|
||||
export const STORE_KEY = 'wc/store/query-state';
|
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './constants';
|
||||
import * as selectors from './selectors';
|
||||
import * as actions from './actions';
|
||||
import reducer from './reducers';
|
||||
|
||||
registerStore( STORE_KEY, {
|
||||
reducer,
|
||||
actions,
|
||||
selectors,
|
||||
} );
|
||||
|
||||
export const QUERY_STATE_STORE_KEY = STORE_KEY;
|
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { ACTION_TYPES as types } from './action-types';
|
||||
import { getStateForContext } from './utils';
|
||||
|
||||
/**
|
||||
* Reducer for processing actions related to the query state store.
|
||||
*
|
||||
* @param {Object} state Current state in store.
|
||||
* @param {Object} action Action being processed.
|
||||
*/
|
||||
const queryStateReducer = ( state = {}, action ) => {
|
||||
const { type, context, queryKey, value } = action;
|
||||
const prevState = getStateForContext( state, context );
|
||||
let newState;
|
||||
switch ( type ) {
|
||||
case types.SET_QUERY_KEY_VALUE:
|
||||
const prevStateObject =
|
||||
prevState !== null ? JSON.parse( prevState ) : {};
|
||||
|
||||
// mutate it and JSON.stringify to compare
|
||||
prevStateObject[ queryKey ] = value;
|
||||
newState = JSON.stringify( prevStateObject );
|
||||
|
||||
if ( prevState !== newState ) {
|
||||
state = {
|
||||
...state,
|
||||
[ context ]: newState,
|
||||
};
|
||||
}
|
||||
break;
|
||||
case types.SET_QUERY_CONTEXT_VALUE:
|
||||
newState = JSON.stringify( value );
|
||||
if ( prevState !== newState ) {
|
||||
state = {
|
||||
...state,
|
||||
[ context ]: newState,
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export default queryStateReducer;
|
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getStateForContext } from './utils';
|
||||
|
||||
/**
|
||||
* Selector for retrieving a specific query-state for the given context.
|
||||
*
|
||||
* @param {Object} state Current state.
|
||||
* @param {string} context Context for the query-state being retrieved.
|
||||
* @param {string} queryKey Key for the specific query-state item.
|
||||
* @param {*} defaultValue Default value for the query-state key if it doesn't
|
||||
* currently exist in state.
|
||||
*
|
||||
* @return {*} The currently stored value or the defaultValue if not present.
|
||||
*/
|
||||
export const getValueForQueryKey = (
|
||||
state,
|
||||
context,
|
||||
queryKey,
|
||||
defaultValue = {}
|
||||
) => {
|
||||
let stateContext = getStateForContext( state, context );
|
||||
if ( stateContext === null ) {
|
||||
return defaultValue;
|
||||
}
|
||||
stateContext = JSON.parse( stateContext );
|
||||
return typeof stateContext[ queryKey ] !== 'undefined'
|
||||
? stateContext[ queryKey ]
|
||||
: defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Selector for retrieving the query-state for the given context.
|
||||
*
|
||||
* @param {Object} state The current state.
|
||||
* @param {string} context The context for the query-state being retrieved.
|
||||
* @param {*} defaultValue The default value to return if there is no state for
|
||||
* the given context.
|
||||
*
|
||||
* @return {*} The currently stored query-state for the given context or
|
||||
* defaultValue if not present in state.
|
||||
*/
|
||||
export const getValueForQueryContext = (
|
||||
state,
|
||||
context,
|
||||
defaultValue = {}
|
||||
) => {
|
||||
const stateContext = getStateForContext( state, context );
|
||||
return stateContext === null ? defaultValue : JSON.parse( stateContext );
|
||||
};
|
@ -0,0 +1,136 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import queryStateReducer from '../reducers';
|
||||
import { setQueryValue, setValueForQueryContext } from '../actions';
|
||||
|
||||
describe( 'queryStateReducer', () => {
|
||||
const originalState = deepFreeze( {
|
||||
contexta: JSON.stringify( {
|
||||
foo: 'bar',
|
||||
cheese: 'pizza',
|
||||
} ),
|
||||
} );
|
||||
it(
|
||||
'returns original state when the action is not of the type being ' +
|
||||
'processed',
|
||||
() => {
|
||||
expect(
|
||||
queryStateReducer( originalState, { type: 'invalid' } )
|
||||
).toBe( originalState );
|
||||
}
|
||||
);
|
||||
describe( 'SET_QUERY_KEY_VALUE action', () => {
|
||||
it(
|
||||
'returns original state when incoming query-state key value ' +
|
||||
'matches what is already in the state',
|
||||
() => {
|
||||
expect(
|
||||
queryStateReducer(
|
||||
originalState,
|
||||
setQueryValue( 'contexta', 'foo', 'bar' )
|
||||
)
|
||||
).toBe( originalState );
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns new state when incoming query-state key exist ' +
|
||||
'but the value is a new value',
|
||||
() => {
|
||||
const newState = queryStateReducer(
|
||||
originalState,
|
||||
setQueryValue( 'contexta', 'foo', 'zed' )
|
||||
);
|
||||
expect( newState ).not.toBe( originalState );
|
||||
expect( newState ).toEqual( {
|
||||
contexta: JSON.stringify( {
|
||||
foo: 'zed',
|
||||
cheese: 'pizza',
|
||||
} ),
|
||||
} );
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns new state when incoming query-state key does not ' +
|
||||
'exist',
|
||||
() => {
|
||||
const newState = queryStateReducer(
|
||||
originalState,
|
||||
setQueryValue( 'contexta', 'burger', 'pizza' )
|
||||
);
|
||||
expect( newState ).not.toBe( originalState );
|
||||
expect( newState ).toEqual( {
|
||||
contexta: JSON.stringify( {
|
||||
foo: 'bar',
|
||||
cheese: 'pizza',
|
||||
burger: 'pizza',
|
||||
} ),
|
||||
} );
|
||||
}
|
||||
);
|
||||
} );
|
||||
describe( 'SET_QUERY_CONTEXT_VALUE action', () => {
|
||||
it(
|
||||
'returns original state when incoming context value matches ' +
|
||||
'what is already in the state',
|
||||
() => {
|
||||
expect(
|
||||
queryStateReducer(
|
||||
originalState,
|
||||
setValueForQueryContext( 'contexta', {
|
||||
foo: 'bar',
|
||||
cheese: 'pizza',
|
||||
} )
|
||||
)
|
||||
).toBe( originalState );
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns new state when incoming context value is different ' +
|
||||
'than what is already in the state',
|
||||
() => {
|
||||
const newState = queryStateReducer(
|
||||
originalState,
|
||||
setValueForQueryContext( 'contexta', {
|
||||
bar: 'foo',
|
||||
pizza: 'cheese',
|
||||
} )
|
||||
);
|
||||
expect( newState ).not.toBe( originalState );
|
||||
expect( newState ).toEqual( {
|
||||
contexta: JSON.stringify( {
|
||||
bar: 'foo',
|
||||
pizza: 'cheese',
|
||||
} ),
|
||||
} );
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns new state when incoming context does not exist in the ' +
|
||||
'state',
|
||||
() => {
|
||||
const newState = queryStateReducer(
|
||||
originalState,
|
||||
setValueForQueryContext( 'contextb', {
|
||||
foo: 'bar',
|
||||
} )
|
||||
);
|
||||
expect( newState ).not.toBe( originalState );
|
||||
expect( newState ).toEqual( {
|
||||
contexta: JSON.stringify( {
|
||||
foo: 'bar',
|
||||
cheese: 'pizza',
|
||||
} ),
|
||||
contextb: JSON.stringify( {
|
||||
foo: 'bar',
|
||||
} ),
|
||||
} );
|
||||
}
|
||||
);
|
||||
} );
|
||||
} );
|
@ -0,0 +1,63 @@
|
||||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import deepFreeze from 'deep-freeze';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { getValueForQueryKey, getValueForQueryContext } from '../selectors';
|
||||
|
||||
const testState = deepFreeze( {
|
||||
contexta: JSON.stringify( {
|
||||
foo: 'bar',
|
||||
cheese: 'pizza',
|
||||
} ),
|
||||
} );
|
||||
|
||||
describe( 'getValueForQueryKey', () => {
|
||||
it(
|
||||
'returns provided default value when there is no state for the ' +
|
||||
'given context',
|
||||
() => {
|
||||
expect(
|
||||
getValueForQueryKey( testState, 'invalid', 'foo', 42 )
|
||||
).toBe( 42 );
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns provided default value when there is no value for the ' +
|
||||
'given context and queryKey',
|
||||
() => {
|
||||
expect(
|
||||
getValueForQueryKey( testState, 'contexta', 'pizza', 42 )
|
||||
).toBe( 42 );
|
||||
}
|
||||
);
|
||||
it( 'returns expected value when context and queryKey exist', () => {
|
||||
expect( getValueForQueryKey( testState, 'contexta', 'foo', 42 ) ).toBe(
|
||||
'bar'
|
||||
);
|
||||
} );
|
||||
} );
|
||||
|
||||
describe( 'getValueForQueryContext', () => {
|
||||
it(
|
||||
'returns provided default value when there is no state for the ' +
|
||||
'given context',
|
||||
() => {
|
||||
expect( getValueForQueryContext( testState, 'invalid', 42 ) ).toBe(
|
||||
42
|
||||
);
|
||||
}
|
||||
);
|
||||
it(
|
||||
'returns expected value when selecting a context that exists in ' +
|
||||
'state',
|
||||
() => {
|
||||
expect(
|
||||
getValueForQueryContext( testState, 'contexta', 42 )
|
||||
).toEqual( JSON.parse( testState.contexta ) );
|
||||
}
|
||||
);
|
||||
} );
|
@ -0,0 +1,3 @@
|
||||
export const getStateForContext = ( state, context ) => {
|
||||
return typeof state[ context ] === 'undefined' ? null : state[ context ];
|
||||
};
|
Reference in New Issue
Block a user