From af8a9ef940f37865d211a0c0e851a75c5ccb3722 Mon Sep 17 00:00:00 2001 From: Chuck Carpenter Date: Wed, 12 Aug 2026 11:04:54 +0200 Subject: [PATCH 1/2] fix: pass the actual previous step in the Tour `show` event `show()` assigned `this.currentStep = step` before building the event payload, so `previous` read back the step being shown and was always the same object as `step`. Capture `currentStep` before the reassignment. The documented contract (docs-src/src/content/docs/guides/usage.md, "Tour Events") is that `show` is "Triggered with a hash of the `step` and the `previous` step", so `previous` never reflecting the prior step contradicted the docs. Co-Authored-By: Claude Opus 5 --- shepherd.js/src/tour.ts | 4 +++- shepherd.js/test/unit/tour.spec.js | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/shepherd.js/src/tour.ts b/shepherd.js/src/tour.ts index 6255ea691..beb574338 100644 --- a/shepherd.js/src/tour.ts +++ b/shepherd.js/src/tour.ts @@ -400,10 +400,12 @@ export class Tour extends Evented { * @private */ _showStep(step: Step) { + const previous = this.currentStep; + this.currentStep = step; this.trigger('show', { step, - previous: this.currentStep + previous }); step.show(); diff --git a/shepherd.js/test/unit/tour.spec.js b/shepherd.js/test/unit/tour.spec.js index 28f670b60..b4a3640de 100644 --- a/shepherd.js/test/unit/tour.spec.js +++ b/shepherd.js/test/unit/tour.spec.js @@ -533,6 +533,34 @@ describe('Tour | Top-Level Class', function () { ).toBeFalsy(); }); + it('triggers show with the step that was showing before as `previous`', function () { + const shows = []; + instance.on('show', ({ step, previous }) => + shows.push({ step, previous }) + ); + + instance.start(); + instance.next(); + + const [firstShow, secondShow] = shows; + + expect(firstShow.step.id).toBe('test'); + expect( + firstShow.previous, + 'there is no previous step on the first show' + ).toBeNull(); + + expect(secondShow.step.id).toBe('test2'); + expect( + secondShow.previous.id, + '`previous` is the step that was showing before' + ).toBe('test'); + expect( + secondShow.previous, + '`previous` is not the same object as `step`' + ).not.toBe(secondShow.step); + }); + it('showOn determines which steps to skip', function () { instance.start(); expect(instance.getCurrentStep().id).toBe('test'); From 488a5108e445bcba2a319310a4c20af72fcb785a Mon Sep 17 00:00:00 2001 From: Chuck Carpenter Date: Wed, 12 Aug 2026 11:51:13 +0200 Subject: [PATCH 2/2] fix: normalize an uninitialized `currentStep` to null in the `show` payload `currentStep` is `Step | null | undefined`, and only `start()` initializes it to `null`. `removeStep()` sets it to `undefined` before showing the next step, so that path reported `previous: undefined` while the start path reported `null`. Normalize with `?? null` so `previous` is consistent across every route into `show()`. Co-Authored-By: Claude Opus 5 --- shepherd.js/src/tour.ts | 4 +++- shepherd.js/test/unit/tour.spec.js | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/shepherd.js/src/tour.ts b/shepherd.js/src/tour.ts index beb574338..d1e4824df 100644 --- a/shepherd.js/src/tour.ts +++ b/shepherd.js/src/tour.ts @@ -400,7 +400,9 @@ export class Tour extends Evented { * @private */ _showStep(step: Step) { - const previous = this.currentStep; + // `currentStep` is `undefined` until a step has shown, so normalize to + // `null` to keep `previous` consistent across every path into `show` + const previous = this.currentStep ?? null; this.currentStep = step; this.trigger('show', { diff --git a/shepherd.js/test/unit/tour.spec.js b/shepherd.js/test/unit/tour.spec.js index b4a3640de..4babff03f 100644 --- a/shepherd.js/test/unit/tour.spec.js +++ b/shepherd.js/test/unit/tour.spec.js @@ -561,6 +561,23 @@ describe('Tour | Top-Level Class', function () { ).not.toBe(secondShow.step); }); + it('reports `previous` as null when no step was showing before', function () { + // `removeStep` clears `currentStep` to `undefined` before showing the + // next step, so `previous` has to be normalized on that path too + instance.start(); + + let previousStep; + instance.on('show', ({ previous }) => (previousStep = previous)); + + instance.removeStep('test'); + + expect(instance.getCurrentStep().id).toBe('test2'); + expect( + previousStep, + '`previous` is null rather than undefined when nothing was showing' + ).toBeNull(); + }); + it('showOn determines which steps to skip', function () { instance.start(); expect(instance.getCurrentStep().id).toBe('test');