Skip to content

Commit 8cb98be

Browse files
feat(overmind): allow transition callback
1 parent c864a89 commit 8cb98be

File tree

2 files changed

+104
-11
lines changed

2 files changed

+104
-11
lines changed

packages/node_modules/overmind/src/statemachine.test.ts

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,42 @@ describe('Statemachine', () => {
184184

185185
const state = statemachine<States>({
186186
FOO: ['BAR'],
187-
BAR: (_, state) => state.transition({
188-
current: 'FOO'
187+
BAR: () => 'FOO'
188+
}).create({
189+
current: 'FOO'
190+
})
191+
const transition: Action = ({ state }) => {
192+
state.transition({
193+
current: 'BAR'
189194
})
195+
}
196+
197+
const config = {
198+
state,
199+
actions: {
200+
transition
201+
}
202+
}
203+
204+
interface Action extends IAction<typeof config, void, void> {}
205+
206+
const overmind = createOvermindMock(config)
207+
208+
209+
overmind.actions.transition()
210+
expect(overmind.state.current).toBe('FOO')
211+
})
212+
213+
test('should allow back transition', () => {
214+
type States = {
215+
current: 'FOO'
216+
} | {
217+
current: 'BAR'
218+
}
219+
220+
const state = statemachine<States>({
221+
FOO: ['BAR'],
222+
BAR: () => true
190223
}).create({
191224
current: 'FOO'
192225
})
@@ -212,6 +245,43 @@ describe('Statemachine', () => {
212245
expect(overmind.state.current).toBe('FOO')
213246
})
214247

248+
test('should allow using callback for transition', () => {
249+
type States = {
250+
current: 'FOO'
251+
} | {
252+
current: 'BAR'
253+
bar: string
254+
}
255+
256+
const state = statemachine<States>({
257+
FOO: ['BAR'],
258+
BAR: []
259+
}).create({
260+
current: 'FOO'
261+
})
262+
const transition: Action = ({ state }) => {
263+
state.transition('BAR', (state) => {
264+
state.bar = 'bar2'
265+
})
266+
}
267+
268+
const config = {
269+
state,
270+
actions: {
271+
transition
272+
}
273+
}
274+
275+
interface Action extends IAction<typeof config, void, void> {}
276+
277+
const overmind = createOvermindMock(config)
278+
279+
280+
overmind.actions.transition()
281+
expect(overmind.state.current).toBe('BAR')
282+
expect(overmind.state.matches('BAR')?.bar).toBe('bar2')
283+
})
284+
215285
test('should flush changes to transitions', () => {
216286
expect.assertions(1)
217287

packages/node_modules/overmind/src/statemachine.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ type TStates = {
1010
}
1111

1212
export type StatemachineTransitions<States extends TStates, BaseState extends IState> = {
13-
[State in States["current"]]: Array<States["current"]> | ((payload: Omit<States extends { current: State } ? States : never, 'current'>, state: Statemachine<States, BaseState>) => undefined | Statemachine<States, BaseState> | Array<States["current"]> | boolean)
13+
[State in States["current"]]: Array<States["current"]> | ((payload: Omit<States extends { current: State } ? States : never, 'current'>, state: Statemachine<States, BaseState>) => States["current"] | Array<States["current"]> | boolean)
1414
}
1515

1616
export interface MachineMethods<States extends TStates, Base extends IState> {
@@ -20,6 +20,10 @@ export interface MachineMethods<States extends TStates, Base extends IState> {
2020
transition<T extends States["current"]>(
2121
newState: Partial<Base> & States extends { current: T } ? States : never
2222
): Statemachine<States extends { current: T} ? States : never, Base> | undefined
23+
transition<T extends States["current"]>(
24+
newState: T,
25+
cb: (state: Statemachine<States extends { current: T} ? States : never, Base>) => void
26+
): Statemachine<States extends { current: T} ? States : never, Base> | undefined
2327
}
2428

2529
export type Statemachine<States extends TStates, Base extends IState = never> = ([Base] extends [never] ? MachineMethods<States, {}> : Base & MachineMethods<States, Base>) & States
@@ -54,7 +58,7 @@ export class StateMachine<Base extends IState, States extends TStates> {
5458
this[TRANSITIONS] = transitions
5559
Object.assign(this, state)
5660
}
57-
transition(newState: any) {
61+
transition(...args) {
5862
if (this[VALUE][IS_DISPOSED]) {
5963
if (process.env.NODE_ENV === 'development') {
6064
console.warn(`Overmind - The statemachine at "${this[PATH]}" has been disposed, but you tried to transition on it`)
@@ -64,13 +68,15 @@ export class StateMachine<Base extends IState, States extends TStates> {
6468

6569
const transition = this[VALUE][CURRENT_DYNAMIC_TRANSITION] || this[VALUE][TRANSITIONS][this.current]
6670

67-
if (transition.includes(newState.current)) {
71+
const newState = args.length === 2 ? args[0] : args[0].current
72+
73+
if (transition.includes(newState)) {
6874
let dynamicResult: any
6975

7076
this[VALUE][CURRENT_DYNAMIC_TRANSITION] = null
7177

72-
if (typeof this[VALUE][TRANSITIONS][newState.current] === 'function') {
73-
dynamicResult = this[VALUE][TRANSITIONS][newState.current](newState, this)
78+
if (typeof this[VALUE][TRANSITIONS][newState] === 'function') {
79+
dynamicResult = this[VALUE][TRANSITIONS][newState](newState, this)
7480

7581
if (!dynamicResult) {
7682
return
@@ -81,13 +87,30 @@ export class StateMachine<Base extends IState, States extends TStates> {
8187
}
8288
}
8389

84-
Object.assign(this, newState)
90+
const existingState = this.current
91+
if (args.length === 2) {
92+
this.current = newState
93+
args[1](this)
94+
} else {
95+
Object.assign(this, args[0])
96+
}
97+
98+
99+
if (typeof dynamicResult === 'string') {
100+
this[VALUE][CURRENT_DYNAMIC_TRANSITION] = [dynamicResult]
101+
return this.transition({ current: dynamicResult })
102+
}
103+
104+
if (dynamicResult === true) {
105+
this[VALUE][CURRENT_DYNAMIC_TRANSITION] = [existingState]
106+
return this.transition({ current: existingState })
107+
}
85108

86109
return this
87-
} else if (process.env.NODE_ENV === 'development' && newState.current !== this.current) {
110+
} else if (process.env.NODE_ENV === 'development' && newState !== this.current) {
88111
console.warn(`Overmind Statemachine - You tried to transition into "${newState.current}", but it is not a valid transition. The valid transitions are ${JSON.stringify(transition)}`)
89-
} else if (process.env.NODE_ENV === 'development' && newState.current === this.current) {
90-
console.warn(`Overmind Statemachine - You tried to transition into "${newState.current}", but you are already in this state. Do a "match" before running this piece of logic or add it as a valid state transition for this state`)
112+
} else if (process.env.NODE_ENV === 'development' && newState === this.current) {
113+
console.warn(`Overmind Statemachine - You tried to transition into "${newState}", but you are already in this state. Do a "match" before running this piece of logic or add it as a valid state transition for this state`)
91114
}
92115

93116
return

0 commit comments

Comments
 (0)