feat(tour): highlight each step, and make the comparing steps try both looks

Owner feedback on the forced tour. Each step rings what it is about and,
while forced, dims the rest of the page around it. "Pick a layout" and the
Timeline step hold Continue until both options have been on screen, scroll
their subject to the top of the window and, on wide screens, move the card
to the right edge so the page has room to show the difference.

Guards for the reported snap-back and endless tour, which did not
reproduce: the effect that moves an unseen reader to Relaxed fires at most
once per page load and never after the tab saw the tour done, and setup
saves only the prefs it changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Lucas WintherandClaude Opus 5.5 committed 2026-10-18 05:10:42 +02:00
1 parent 94fba4d7b5
commit 5be04a7439
8 files changed
+307 -72

No files matched your search

+17
View File
@@ -27,6 +27,7 @@ import {
TOUR_DONE,
TOUR_STEPS,
tourMode,
untriedOptions,
} from "../src/client/components/redesign/ReturningTour.tsx";
import { setupReader } from "../src/client/state/reader/onboarding.ts";
@@ -65,6 +66,22 @@ test("the tour lets the reader try both layouts and both Timeline looks", () =>
["timeline", "timelineLook"],
]);
});
test("a choice holds Continue until both options have been tried", () => {
expect(
untriedOptions("interface", ["structured"]).map((o) => o.label),
).toEqual(["Relaxed"]);
expect(untriedOptions("interface", ["structured", "relaxed"])).toEqual([]);
expect(untriedOptions("timelineLook", []).map((o) => o.value)).toEqual([
"new",
"classic",
]);
});
test("every step highlights what it is about", () => {
for (const step of TOUR_STEPS) expect(step.target).toBeTruthy();
expect(TOUR_STEPS.find((s) => s.stop === "routines")!.target).toBe(
'[data-page="routines"]',
);
});
test("the tour introduces the Timeline right after the Checklist, and codes", () => {
const stops = TOUR_STEPS.map((s) => s.stop);
expect(stops.indexOf("timeline")).toBe(stops.lastIndexOf("soon") + 1);