mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Added reducerFactory and tests
This commit is contained in:
parent
62341ffe56
commit
0ddaa95d0e
@ -11,7 +11,7 @@ interface Dummy {
|
||||
b: boolean;
|
||||
}
|
||||
|
||||
const setup = payload => {
|
||||
const setup = (payload: Dummy) => {
|
||||
resetAllActionCreatorTypes();
|
||||
const actionCreator = actionCreatorFactory<Dummy>('dummy').create();
|
||||
const result = actionCreator(payload);
|
||||
|
99
public/app/core/redux/reducerFactory.test.ts
Normal file
99
public/app/core/redux/reducerFactory.test.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import { reducerFactory } from './reducerFactory';
|
||||
import { actionCreatorFactory, GrafanaAction } from './actionCreatorFactory';
|
||||
|
||||
interface DummyReducerState {
|
||||
n: number;
|
||||
s: string;
|
||||
b: boolean;
|
||||
o: {
|
||||
n: number;
|
||||
s: string;
|
||||
b: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const dummyReducerIntialState: DummyReducerState = {
|
||||
n: 1,
|
||||
s: 'One',
|
||||
b: true,
|
||||
o: {
|
||||
n: 2,
|
||||
s: 'two',
|
||||
b: false,
|
||||
},
|
||||
};
|
||||
|
||||
const dummyActionCreator = actionCreatorFactory<DummyReducerState>('dummy').create();
|
||||
|
||||
const dummyReducer = reducerFactory(dummyReducerIntialState)
|
||||
.addHandler({
|
||||
creator: dummyActionCreator,
|
||||
handler: ({ state, action }) => {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
})
|
||||
.create();
|
||||
|
||||
describe('reducerFactory', () => {
|
||||
describe('given it is created with a defined handler', () => {
|
||||
describe('when reducer is called with no state', () => {
|
||||
describe('and with an action that the handler can not handle', () => {
|
||||
it('then the resulting state should be intial state', () => {
|
||||
const result = dummyReducer(undefined as DummyReducerState, {} as GrafanaAction<any>);
|
||||
|
||||
expect(result).toEqual(dummyReducerIntialState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with an action that the handler can handle', () => {
|
||||
it('then the resulting state should correct', () => {
|
||||
const payload = { n: 10, s: 'ten', b: false, o: { n: 20, s: 'twenty', b: true } };
|
||||
const result = dummyReducer(undefined as DummyReducerState, dummyActionCreator(payload));
|
||||
|
||||
expect(result).toEqual(payload);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when reducer is called with a state', () => {
|
||||
describe('and with an action that the handler can not handle', () => {
|
||||
it('then the resulting state should be intial state', () => {
|
||||
const result = dummyReducer(dummyReducerIntialState, {} as GrafanaAction<any>);
|
||||
|
||||
expect(result).toEqual(dummyReducerIntialState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('and with an action that the handler can handle', () => {
|
||||
it('then the resulting state should correct', () => {
|
||||
const payload = { n: 10, s: 'ten', b: false, o: { n: 20, s: 'twenty', b: true } };
|
||||
const result = dummyReducer(dummyReducerIntialState, dummyActionCreator(payload));
|
||||
|
||||
expect(result).toEqual(payload);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('given a handler is added', () => {
|
||||
describe('when a handler with the same creator is added', () => {
|
||||
it('then is should throw', () => {
|
||||
const faultyReducer = reducerFactory(dummyReducerIntialState).addHandler({
|
||||
creator: dummyActionCreator,
|
||||
handler: ({ state, action }) => {
|
||||
return { ...state, ...action.payload };
|
||||
},
|
||||
});
|
||||
|
||||
expect(() => {
|
||||
faultyReducer.addHandler({
|
||||
creator: dummyActionCreator,
|
||||
handler: ({ state }) => {
|
||||
return state;
|
||||
},
|
||||
});
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
55
public/app/core/redux/reducerFactory.ts
Normal file
55
public/app/core/redux/reducerFactory.ts
Normal file
@ -0,0 +1,55 @@
|
||||
import { GrafanaAction, GrafanaActionCreator } from './actionCreatorFactory';
|
||||
import { Reducer } from 'redux';
|
||||
|
||||
export interface ActionHandler<State, Payload> {
|
||||
state: State;
|
||||
action: GrafanaAction<Payload>;
|
||||
}
|
||||
|
||||
export interface ActionHandlerConfig<State, Payload> {
|
||||
creator: GrafanaActionCreator<Payload>;
|
||||
handler: (handler: ActionHandler<State, Payload>) => State;
|
||||
}
|
||||
|
||||
export interface AddActionHandler<State> {
|
||||
addHandler: <Payload>(config: ActionHandlerConfig<State, Payload>) => CreateReducer<State>;
|
||||
}
|
||||
|
||||
export interface CreateReducer<State> extends AddActionHandler<State> {
|
||||
create: () => Reducer<State, GrafanaAction<any>>;
|
||||
}
|
||||
|
||||
export const reducerFactory = <State>(initialState: State): AddActionHandler<State> => {
|
||||
const allHandlerConfigs: Array<ActionHandlerConfig<State, any>> = [];
|
||||
|
||||
const addHandler = <Payload>(config: ActionHandlerConfig<State, Payload>): CreateReducer<State> => {
|
||||
if (allHandlerConfigs.some(c => c.creator.type === config.creator.type)) {
|
||||
throw new Error(`There is already a handlers defined with the type ${config.creator.type}`);
|
||||
}
|
||||
|
||||
allHandlerConfigs.push(config);
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
const create = (): Reducer<State, GrafanaAction<any>> => {
|
||||
const reducer: Reducer<State, GrafanaAction<any>> = (state: State = initialState, action: GrafanaAction<any>) => {
|
||||
const validHandlers = allHandlerConfigs
|
||||
.filter(config => config.creator.type === action.type)
|
||||
.map(config => config.handler);
|
||||
|
||||
return validHandlers.reduce((currentState, handler) => {
|
||||
return handler({ state: currentState, action });
|
||||
}, state || initialState);
|
||||
};
|
||||
|
||||
return reducer;
|
||||
};
|
||||
|
||||
const instance: CreateReducer<State> = {
|
||||
addHandler,
|
||||
create,
|
||||
};
|
||||
|
||||
return instance;
|
||||
};
|
Loading…
Reference in New Issue
Block a user