diff --git a/.changeset/happy-spiders-bathe.md b/.changeset/happy-spiders-bathe.md new file mode 100644 index 0000000000..0febcf26ef --- /dev/null +++ b/.changeset/happy-spiders-bathe.md @@ -0,0 +1,5 @@ +--- +'xstate': patch +--- + +Fixed an issue with ancestors of the default history target that lie outside of the transition domain being incorrectly entered. diff --git a/packages/core/src/stateUtils.ts b/packages/core/src/stateUtils.ts index 698ef00fa4..4c681fc31c 100644 --- a/packages/core/src/stateUtils.ts +++ b/packages/core/src/stateUtils.ts @@ -1294,7 +1294,7 @@ function addDescendantStatesToEnter< for (const s of historyDefaultTransition.target) { addProperAncestorStatesToEnter( s, - stateNode, + stateNode.parent!, statesToEnter, historyValue, statesForDefaultEntry diff --git a/packages/core/test/history.test.ts b/packages/core/test/history.test.ts index fc2744978b..4a99b205d7 100644 --- a/packages/core/test/history.test.ts +++ b/packages/core/test/history.test.ts @@ -1,4 +1,11 @@ -import { createActor, createMachine, fromCallback } from '../src/index'; +import { + assign, + createActor, + createMachine, + fromCallback, + fromPromise +} from '../src/index'; +import { trackEntries } from './utils'; describe('history states', () => { it('should go to the most recently visited state (explicit shallow history type)', () => { @@ -528,6 +535,92 @@ describe('history states', () => { expect(spy).toHaveBeenCalledTimes(1); }); + + it('should not enter ancestors of the entered history state that lie outside of the transition domain when entering the default history configuration', () => { + const machine = createMachine({ + initial: 'closed', + states: { + closed: { + on: { + 'BUTTON.CLICK': 'open.hist' + } + }, + open: { + on: { + 'BUTTON.CLICK': 'closed' + }, + initial: 'first', + states: { + hist: { type: 'history' }, + first: {}, + second: {} + } + } + } + }); + + const flushTracked = trackEntries(machine); + + const actorRef = createActor(machine).start(); + flushTracked(); + + actorRef.send({ type: 'BUTTON.CLICK' }); + expect(flushTracked()).toEqual([ + 'exit: closed', + 'enter: open', + 'enter: open.first' + ]); + }); + + it('should not enter ancestors of the entered history state that lie outside of the transition domain when retoring the stored history configuration', () => { + const machine = createMachine({ + initial: 'closed', + states: { + closed: { + id: 'closed', + on: { + 'BUTTON.CLICK': 'open.hist' + } + }, + open: { + on: { + 'BUTTON.CLICK': 'closed' + }, + initial: 'first', + states: { + hist: { type: 'history' }, + first: { + on: { + NEXT: 'second' + } + }, + second: { + on: { + CLOSE: '#closed' + } + } + } + } + } + }); + + const flushTracked = trackEntries(machine); + + const actorRef = createActor(machine).start(); + + actorRef.send({ type: 'BUTTON.CLICK' }); + actorRef.send({ type: 'NEXT' }); + actorRef.send({ type: 'CLOSE' }); + + flushTracked(); + + actorRef.send({ type: 'BUTTON.CLICK' }); + expect(flushTracked()).toEqual([ + 'exit: closed', + 'enter: open', + 'enter: open.second' + ]); + }); }); describe('deep history states', () => {