Merge branch 'action-tests' into typescript
This commit is contained in:
commit
1a2b2df9ac
21
src/Updux.ts
21
src/Updux.ts
@ -5,9 +5,15 @@ import {
|
|||||||
DeepPartial,
|
DeepPartial,
|
||||||
Action,
|
Action,
|
||||||
} from 'redux';
|
} from 'redux';
|
||||||
import { configureStore, Reducer, createAction } from '@reduxjs/toolkit';
|
import {
|
||||||
|
configureStore,
|
||||||
|
Reducer,
|
||||||
|
createAction,
|
||||||
|
PrepareAction,
|
||||||
|
} from '@reduxjs/toolkit';
|
||||||
import { withPayload } from './actions.js';
|
import { withPayload } from './actions.js';
|
||||||
import { AggregateActions, Dux, UnionToIntersection } from './types.js';
|
import { AggregateActions, Dux, UnionToIntersection } from './types.js';
|
||||||
|
import { buildActions } from './buildActions.js';
|
||||||
|
|
||||||
type ActionCreator = ReturnType<typeof createAction>;
|
type ActionCreator = ReturnType<typeof createAction>;
|
||||||
|
|
||||||
@ -32,7 +38,9 @@ export default class Updux<
|
|||||||
> = {};
|
> = {};
|
||||||
#subduxes: SUBDUXES;
|
#subduxes: SUBDUXES;
|
||||||
|
|
||||||
#actions: Record<string, ActionCreator>;
|
#name: string;
|
||||||
|
|
||||||
|
#actions: AggregateActions<T_LocalActions, SUBDUXES>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
config: Partial<{
|
config: Partial<{
|
||||||
@ -45,13 +53,12 @@ export default class Updux<
|
|||||||
this.#localInitial = config.initial ?? ({} as T_LocalState);
|
this.#localInitial = config.initial ?? ({} as T_LocalState);
|
||||||
this.#localActions = config.actions ?? ({} as T_LocalActions);
|
this.#localActions = config.actions ?? ({} as T_LocalActions);
|
||||||
this.#subduxes = config.subduxes ?? ({} as SUBDUXES);
|
this.#subduxes = config.subduxes ?? ({} as SUBDUXES);
|
||||||
|
|
||||||
|
this.#actions = buildActions(this.#localActions, this.#subduxes);
|
||||||
}
|
}
|
||||||
|
|
||||||
get actions(): AggregateActions<T_LocalActions, SUBDUXES> {
|
get actions() {
|
||||||
return R.mergeAll([
|
return this.#actions;
|
||||||
this.#localActions,
|
|
||||||
...Object.values(this.#subduxes).map(R.pathOr(['actions'], {})),
|
|
||||||
]) as any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO memoize?
|
// TODO memoize?
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
import { test, expect } from 'vitest';
|
|
||||||
|
|
||||||
import { action } from './actions.js';
|
|
||||||
|
|
||||||
import { Updux } from './Updux.js';
|
|
||||||
|
|
||||||
|
|
||||||
test('Updux config accepts actions', () => {
|
|
||||||
const foo = new Updux({
|
|
||||||
actions: {
|
|
||||||
one: action('one', (x) => ({ x })),
|
|
||||||
two: action('two', (x) => x),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(Object.keys(foo.actions)).toHaveLength(2);
|
|
||||||
|
|
||||||
expect(foo.actions.one).toBeTypeOf('function');
|
|
||||||
expect(foo.actions.one('potato')).toEqual({
|
|
||||||
type: 'one',
|
|
||||||
payload: {
|
|
||||||
x: 'potato',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
test('throw if double action', () => {
|
|
||||||
expect(
|
|
||||||
() =>
|
|
||||||
new Updux({
|
|
||||||
actions: {
|
|
||||||
foo: action('foo'),
|
|
||||||
},
|
|
||||||
subduxes: {
|
|
||||||
beta: {
|
|
||||||
actions: {
|
|
||||||
foo: action('foo'),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
).toThrow(/action 'foo' already defined/);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('action definition shortcut', () => {
|
|
||||||
const foo = new Updux({
|
|
||||||
actions: {
|
|
||||||
foo: null,
|
|
||||||
bar: (x) => ({ x }),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(foo.actions.foo('hello')).toEqual({ type: 'foo', payload: 'hello' });
|
|
||||||
expect(foo.actions.bar('hello')).toEqual({
|
|
||||||
type: 'bar',
|
|
||||||
payload: { x: 'hello' },
|
|
||||||
});
|
|
||||||
});
|
|
@ -39,3 +39,79 @@ test('subduxes actions', () => {
|
|||||||
expect(foo.actions.bar(2)).toHaveProperty('type', 'bar');
|
expect(foo.actions.bar(2)).toHaveProperty('type', 'bar');
|
||||||
expect(foo.actions.baz()).toHaveProperty('type', 'baz');
|
expect(foo.actions.baz()).toHaveProperty('type', 'baz');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Updux config accepts actions', () => {
|
||||||
|
const foo = new Updux({
|
||||||
|
actions: {
|
||||||
|
one: createAction(
|
||||||
|
'one',
|
||||||
|
withPayload((x) => ({ x })),
|
||||||
|
),
|
||||||
|
two: createAction(
|
||||||
|
'two',
|
||||||
|
withPayload((x) => x),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(Object.keys(foo.actions)).toHaveLength(2);
|
||||||
|
|
||||||
|
expect(foo.actions.one).toBeTypeOf('function');
|
||||||
|
expect(foo.actions.one('potato')).toEqual({
|
||||||
|
type: 'one',
|
||||||
|
payload: {
|
||||||
|
x: 'potato',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('throw if double action', () => {
|
||||||
|
expect(
|
||||||
|
() =>
|
||||||
|
new Updux({
|
||||||
|
actions: {
|
||||||
|
foo: createAction('foo'),
|
||||||
|
},
|
||||||
|
subduxes: {
|
||||||
|
beta: {
|
||||||
|
actions: {
|
||||||
|
foo: createAction('foo'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toThrow(/action 'foo' defined both locally and in subdux 'beta'/);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() =>
|
||||||
|
new Updux({
|
||||||
|
subduxes: {
|
||||||
|
gamma: {
|
||||||
|
actions: {
|
||||||
|
foo: createAction('foo'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
beta: {
|
||||||
|
actions: {
|
||||||
|
foo: createAction('foo'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).toThrow(/action 'foo' defined both in subduxes 'gamma' and 'beta'/);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.todo('action definition shortcut', () => {
|
||||||
|
const foo = new Updux({
|
||||||
|
actions: {
|
||||||
|
foo: undefined,
|
||||||
|
bar: (x) => ({ x }),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(foo.actions.foo('hello')).toEqual({ type: 'foo', payload: 'hello' });
|
||||||
|
expect(foo.actions.bar('hello')).toEqual({
|
||||||
|
type: 'bar',
|
||||||
|
payload: { x: 'hello' },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
@ -3,7 +3,7 @@ import { createAction } from '@reduxjs/toolkit';
|
|||||||
export { createAction } from '@reduxjs/toolkit';
|
export { createAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
interface WithPayload {
|
interface WithPayload {
|
||||||
(): <P>(input: P) => { payload: P };
|
<P>(): (input: P) => { payload: P };
|
||||||
<P, A extends any[]>(prepare: (...args: A) => P): (...input: A) => {
|
<P, A extends any[]>(prepare: (...args: A) => P): (...input: A) => {
|
||||||
payload: P;
|
payload: P;
|
||||||
};
|
};
|
||||||
@ -11,5 +11,5 @@ interface WithPayload {
|
|||||||
|
|
||||||
export const withPayload: WithPayload = ((prepare) =>
|
export const withPayload: WithPayload = ((prepare) =>
|
||||||
(...input) => ({
|
(...input) => ({
|
||||||
payload: prepare ? prepare(...input) : input,
|
payload: prepare ? prepare(...input) : input[0],
|
||||||
})) as any;
|
})) as any;
|
||||||
|
33
src/buildActions.ts
Normal file
33
src/buildActions.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import * as R from 'remeda';
|
||||||
|
|
||||||
|
export function buildActions(localActions, subduxes) {
|
||||||
|
let actions: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const slice in subduxes) {
|
||||||
|
const subdux = subduxes[slice].actions;
|
||||||
|
|
||||||
|
if (!subdux) continue;
|
||||||
|
|
||||||
|
for (const a in subdux) {
|
||||||
|
if (actions[a]) {
|
||||||
|
throw new Error(
|
||||||
|
`action '${a}' defined both in subduxes '${actions[a]}' and '${slice}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
actions[a] = slice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const a in localActions) {
|
||||||
|
if (actions[a]) {
|
||||||
|
throw new Error(
|
||||||
|
`action '${a}' defined both locally and in subdux '${actions[a]}'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return R.mergeAll([
|
||||||
|
localActions,
|
||||||
|
...Object.values(subduxes).map(R.pathOr<any, any>(['actions'], {})),
|
||||||
|
]) as any;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user