Skip to content

Commit

Permalink
[v5] Rename external transitions to reentering ones (#3900)
Browse files Browse the repository at this point in the history
* Rename external transitions to reentering ones and adjust semantics slightly

* fixed the missed spot

* tweak tests

* bring back the inability to use reenter:false on parallel states transitions

* add a changeset

* fixed leftover instances of external property
  • Loading branch information
Andarist committed Apr 9, 2023
1 parent 0b38a64 commit 7d1a8ff
Show file tree
Hide file tree
Showing 18 changed files with 98 additions and 43 deletions.
5 changes: 5 additions & 0 deletions .changeset/cool-hairs-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'xstate': major
---

`external` property on transitions has been renamed to `reenter`
4 changes: 2 additions & 2 deletions packages/core/src/StateNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ export class StateNode<
source: this,
actions: this.initial.actions,
eventType: null as any,
external: false,
reenter: false,
toJSON: () => ({
target: this.initial!.target!.map((t) => `#${t.id}`),
source: `#${this.id}`,
Expand Down Expand Up @@ -451,7 +451,7 @@ export class StateNode<
return !(
!transition.target &&
!transition.actions.length &&
!transition.external
!transition.reenter
);
})
.map((transition) => transition.eventType)
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/scxml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ function toConfig(
target: getTargets(targets),
...(value.elements ? executableContent(value.elements) : undefined),
...guardObject,
external: !internal
...(!internal && { reenter: true })
};

if (eventType === NULL_EVENT) {
Expand Down
12 changes: 6 additions & 6 deletions packages/core/src/stateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ export function formatTransition<
}
): AnyTransitionDefinition {
const normalizedTarget = normalizeTarget(transitionConfig.target);
const external = transitionConfig.external ?? false;
const reenter = transitionConfig.reenter ?? false;
const { guards } = stateNode.machine.options;
const target = resolveTarget(stateNode, normalizedTarget);

Expand All @@ -381,7 +381,7 @@ export function formatTransition<
: undefined,
target,
source: stateNode,
external,
reenter,
eventType: transitionConfig.event,
toJSON: () => ({
...transition,
Expand Down Expand Up @@ -524,7 +524,7 @@ export function formatInitialTransition<
source: stateNode,
actions: [],
eventType: null as any,
external: false,
reenter: false,
target: resolvedTarget!,
toJSON: () => ({
...transition,
Expand Down Expand Up @@ -990,8 +990,8 @@ function getTransitionDomain(
}

if (
!transition.external &&
transition.source.type === 'compound' &&
!transition.reenter &&
transition.source.type !== 'parallel' &&
targetStates.every((targetStateNode) =>
isDescendant(targetStateNode, transition.source)
)
Expand Down Expand Up @@ -1069,7 +1069,7 @@ export function microstep<
{
target: [...currentState.configuration].filter(isAtomicStateNode),
source: machine.root,
external: true,
reenter: true,
actions: [],
eventType: null as any,
toJSON: null as any // TODO: fix
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ export interface TransitionConfig<
> {
guard?: GuardConfig<TContext, TExpressionEvent>;
actions?: BaseActions<TContext, TExpressionEvent, TEvent, TAction>;
external?: boolean;
reenter?: boolean;
target?: TransitionTarget | undefined;
meta?: Record<string, any>;
description?: string;
Expand Down Expand Up @@ -1437,7 +1437,7 @@ export interface TransitionDefinition<
target: Array<StateNode<TContext, TEvent>> | undefined;
source: StateNode<TContext, TEvent>;
actions: BaseActionObject[];
external: boolean;
reenter: boolean;
guard?: GuardDefinition<TContext, TEvent>;
eventType: TEvent['type'] | '*';
toJSON: () => {
Expand Down
66 changes: 58 additions & 8 deletions packages/core/test/actions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ describe('entry/exit actions', () => {
on: {
RESTART: {
target: 'green',
external: true
reenter: true
}
}
}
Expand All @@ -256,7 +256,7 @@ describe('entry/exit actions', () => {
on: {
RESTART: {
target: 'green',
external: true
reenter: true
}
},
initial: 'walk',
Expand Down Expand Up @@ -599,7 +599,7 @@ describe('entry/exit actions', () => {
expect(called).toBe(true);
});

it('root entry/exit actions should be called on root external transitions', () => {
it('root entry/exit actions should be called on root reentering transitions', () => {
let entrySpy = jest.fn();
let exitSpy = jest.fn();

Expand All @@ -610,7 +610,7 @@ describe('entry/exit actions', () => {
on: {
EVENT: {
target: '#two',
external: true
reenter: true
}
},
initial: 'one',
Expand Down Expand Up @@ -858,7 +858,7 @@ describe('entry/exit actions', () => {
on: {
RESTART: {
target: 'green',
external: true
reenter: true
}
},
initial: 'walk',
Expand Down Expand Up @@ -966,15 +966,15 @@ describe('entry/exit actions', () => {
]);
});

it('should enter all descendents when target is a descendent of the source when using an external transition', () => {
it('should enter all descendents when target is a descendent of the source when using an reentering transition', () => {
const machine = createMachine({
initial: 'A',
states: {
A: {
initial: 'A1',
on: {
NEXT: {
external: true,
reenter: true,
target: '.A2'
}
},
Expand Down Expand Up @@ -1194,7 +1194,57 @@ describe('entry/exit actions', () => {
ready: {
type: 'parallel',
on: {
FOO: '#cameraOff'
FOO: {
target: '#cameraOff',
reenter: true
}
},
states: {
devicesInfo: {},
camera: {
initial: 'on',
states: {
on: {},
off: {
id: 'cameraOff'
}
}
}
}
}
}
});

const flushTracked = trackEntries(machine);

const service = interpret(machine).start();

flushTracked();
service.send({ type: 'FOO' });

expect(flushTracked()).toEqual([
'exit: ready.camera.on',
'exit: ready.camera',
'exit: ready.devicesInfo',
'exit: ready',
'enter: ready',
'enter: ready.devicesInfo',
'enter: ready.camera',
'enter: ready.camera.off'
]);
});

it('should reenter parallel region when a parallel state is reentered while targeting another region', () => {
const machine = createMachine({
initial: 'ready',
states: {
ready: {
type: 'parallel',
on: {
FOO: {
target: '#cameraOff',
reenter: true
}
},
states: {
devicesInfo: {},
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/activities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ describe('invocations (activities)', () => {
expect(actual).toEqual(['start 0', 'stop 0', 'start 1']);
});

it('should start a new actor when reentering the invoking state during an external self transition', () => {
it('should start a new actor when reentering the invoking state during a reentering self transition', () => {
let counter = 0;
const actual: string[] = [];
const machine = createMachine(
Expand All @@ -371,7 +371,7 @@ describe('invocations (activities)', () => {
on: {
NEXT: {
target: 'a',
external: true
reenter: true
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/history.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ describe('history states', () => {
expect(service.getSnapshot().value).toEqual({ idle: 'absent' });
});

it('should reenter persisted state during external transition targeting a history state', () => {
it('should reenter persisted state during reentering transition targeting a history state', () => {
const actual: string[] = [];

const machine = createMachine({
Expand All @@ -132,7 +132,7 @@ describe('history states', () => {
on: {
REENTER: {
target: '#b_hist',
external: true
reenter: true
}
},
initial: 'a1',
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/internalTransitions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ describe('internal transitions', () => {
on: {
NEXT: {
target: '.right',
external: true
reenter: true
}
}
}
Expand Down Expand Up @@ -85,7 +85,7 @@ describe('internal transitions', () => {
on: {
RESET: {
target: 'foo',
external: true
reenter: true
}
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ describe('internal transitions', () => {
on: {
RESET_TO_B: {
target: 'foo.b',
external: true
reenter: true
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions packages/core/test/invoke.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3053,7 +3053,7 @@ describe('invoke', () => {
expect(disposed).toBe(true);
});

it('root invocations should restart on root external transitions', () => {
it('root invocations should restart on root reentering transitions', () => {
let count = 0;

const machine = createMachine({
Expand All @@ -3067,7 +3067,7 @@ describe('invoke', () => {
on: {
EVENT: {
target: '#two',
external: true
reenter: true
}
},
initial: 'one',
Expand Down
6 changes: 3 additions & 3 deletions packages/core/test/json.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,8 @@ describe('json', () => {
"actions": [],
"event": "done.invoke.active:invocation[0]",
"eventType": "done.invoke.active:invocation[0]",
"external": false,
"guard": undefined,
"reenter": false,
"source": "#active",
"target": [
"#(machine).foo",
Expand All @@ -157,8 +157,8 @@ describe('json', () => {
"actions": [],
"event": "error.platform.active:invocation[0]",
"eventType": "error.platform.active:invocation[0]",
"external": false,
"guard": undefined,
"reenter": false,
"source": "#active",
"target": [
"#(machine).bar",
Expand All @@ -169,8 +169,8 @@ describe('json', () => {
"actions": [],
"event": "EVENT",
"eventType": "EVENT",
"external": false,
"guard": undefined,
"reenter": false,
"source": "#active",
"target": [
"#(machine).foo",
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/parallel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,7 @@ describe('parallel states', () => {
});

// https://github.com/statelyai/xstate/issues/531
it('should calculate the entry set for external transitions in parallel states', () => {
it('should calculate the entry set for reentering transitions in parallel states', () => {
const testMachine = createMachine<{ log: string[] }>({
id: 'test',
context: { log: [] },
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/predictableExec.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ describe('predictableExec', () => {
on: {
REENTER: {
target: 'active',
external: true
reenter: true
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/core/test/scxml.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ const testGroups: Record<string, string[]> = {
// 'test530.txml', // https://github.com/davidkpiano/xstate/pull/1811#discussion_r551897693
// 'test531.txml', // Basic HTTP Event I/O processor not implemented
// 'test532.txml', // Basic HTTP Event I/O processor not implemented
'test533.txml',
// 'test533.txml', // we allow `reenter: false` to not leave the source state even if that source state is not compound
// 'test534.txml', // Basic HTTP Event I/O processor not implemented
// 'test550.txml', // non-root datamodel with early binding not implemented yet
// 'test551.txml', // non-root datamodel with early binding not implemented yet
Expand Down
Loading

0 comments on commit 7d1a8ff

Please sign in to comment.