Skip to content

feat: Guided Tours Framework#2299

Draft
camielvs wants to merge 1 commit into
05-20-feat_learning_hub_guided_toursfrom
05-20-feat_learning_hub_tour_framework_and_first_tour
Draft

feat: Guided Tours Framework#2299
camielvs wants to merge 1 commit into
05-20-feat_learning_hub_guided_toursfrom
05-20-feat_learning_hub_tour_framework_and_first_tour

Conversation

@camielvs
Copy link
Copy Markdown
Collaborator

@camielvs camielvs commented May 21, 2026

Description

The guided-tour engine built on Reactour, exposed as a dedicated /tour/$tourId route so tours are refreshable, shareable, and own their lifecycle cleanly.

Supports forward/back controls, interactive activities (select-task, undock-window, redock-window), dynamic highlighting, and inline-markdown step copy (**bold**, _italic_, `code`, paragraph breaks). Interactive steps include fallback copy when the prompted state is already satisfied.

Tours are modal — once started, the user can only exit via the explicit Exit Tour button or the Done button on the final step. ESC, the close (X) button, and clicks on the mask are all disabled, so a tour can't be silently paused or dismissed mid-run.

Registry ships empty (tours: TourDefinition[] = []); the first tour lands in #2306. Until then every Learn-page card stays "Coming soon" and the framework merge is invisible to users.

Architecture

Route (src/routes/Dashboard/Learn/Tour.tsx)

  • Resolves the tourId param against the registry; falls back to /learn/tours if unknown.
  • Resolves or creates a temporary pipeline (__tour__<slug>) and renders the editor against it.
  • Always renders <TourModeProvider> + <EditorV2> from the first frame — the editor body shows a small loading skeleton while the temp pipeline resolves, so the menu bar stays mounted and there's no perceptible flicker on entry.
  • Bridges the URL's ?step=N and reactour's currentStep so browser back/forward navigates tour steps.
  • Defers reactour activation until the editor DOM mounts (waitForSelector('[data-testid="editor-v2"]')).
  • Snapshots the editor's window layout on entry, restores it on exit so the user's saved arrangement isn't disturbed.

Reactour wrapper (src/providers/TourProvider/TourProvider.tsx)

  • showCloseButton={false}, onClickMask={() => undefined}, disableKeyboardNavigation={["esc"]} — the only exits are explicit.
  • Custom Prev / Next render via renderNextButton in tourPopover.tsx. On the last step the next-button slot returns a hidden placeholder so the step-dots stay centered.
  • PopoverClampBridge keeps the popover inside the viewport so the step-number badge isn't clipped near screen edges.

Tour completion (tourPopover.tsx → TourCompletionActions)

  • A small <TourCompletionActions /> component (Done button + space for upstack extensions) gets injected into the last step's content via setSteps augmentation in TourReactourBridge. Done closes the popover and routes back to /learn/tours.

Tour mode context (src/providers/TourProvider/TourModeContext.tsx)

  • useTourMode() exposes { tour, tempPipelineName } to editor components.
  • EditorMenuBar reads this to render a Tour badge next to the pipeline name and the central Exit Tour button. File-menu destructive actions (rename / delete) are hidden in tour mode.
  • EditorV2Content reads it too: when pipelineRef is null and we're in tour mode, it shows PipelineEditorSkeleton instead of EmptyEditorState so the loading transition feels coherent.

Editor bridge (src/routes/v2/pages/Editor/components/EditorTourBridge.tsx)

  • Implements three interaction primitives a step can declare: interaction: "undock-window" | "redock-window" | "select-task".
  • Tracks window position so the highlight follows a floating panel as the user drags it.
  • select-task detects clicks on a stable [data-tour-node="task"] ancestor (not a library CSS class) so task selection only counts for true task nodes, not IO ports.

Editor integration (src/routes/v2/pages/Editor/EditorV2.tsx)

  • EditorV2 accepts an optional pipelineRef prop so the tour route can pass in the temp pipeline it resolved itself.
  • The body switches on three states: pipelineRef ? <PipelineEditor /> : tourMode ? <PipelineEditorSkeleton /> : <EmptyEditorState />.

Layout snapshot (src/routes/v2/shared/windows/windowPersistence.ts)

  • snapshotLayout / restoreLayout stash the editor's persisted window arrangement so a tour can mutate it freely and roll back on exit.

How to add a tour

  1. Create src/components/Learn/tours/<yourTour>.tour.json exporting a TourDefinition.
  2. Import + push into the tours array in registry.ts.
  3. Use stable data-* anchors on the UI elements your steps target. The codebase already exposes:
    • data-tour="..." for panels and bars you add as you go
    • data-dock-area, data-dock-window, data-dock-window-content, data-window-id for the dock/window system
    • data-tour-anchor="no-spotlight" to open a step with a centered popover and no highlight

Related Issue and Pull requests

Progresses https://github.com/Shopify/oasis-frontend/issues/583

Type of Change

  • New feature

Checklist

  • I have tested this does not break current pipelines / runs functionality
  • I have tested the changes on staging

Screenshots

image.png

image.png

Test Instructions

With the registry empty, the visible behavior is:

  • /learn/tours and the dashboard Featured Tours tile render every tour card as "Coming soon"
  • No tour can be started; no tour UI appears anywhere in the editor
  • Navigating directly to /tour/anything redirects to /learn/tours
  • Existing editor / runs / dashboard flows are unaffected

Regression check: confirm pipelines list, editor menu bar, dockable / floating windows, and task nodes behave identically to master. None of the tour engine should be visible until a tour registers.

Additional Comments

More tours intended to follow the first one (#2306), covering a range of topics.

Copy link
Copy Markdown
Collaborator Author

camielvs commented May 21, 2026

@camielvs camielvs mentioned this pull request May 21, 2026
3 tasks
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 21, 2026

🎩 Preview

A preview build has been created at: 05-20-feat_learning_hub_tour_framework_and_first_tour/d9c0e95

@camielvs camielvs added the #gsd:50583 Learning Hub label May 21, 2026 — with Graphite App
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 3 times, most recently from 08e1624 to 90d5b9d Compare May 21, 2026 22:07
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from 0f30f2c to 4cc74f0 Compare May 21, 2026 22:07
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from 90d5b9d to d89055f Compare May 21, 2026 22:36
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from 4cc74f0 to fd9690e Compare May 21, 2026 22:37
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 4 times, most recently from a0e7c17 to a98fb46 Compare May 22, 2026 18:40
@camielvs camielvs changed the title feat: Learning Hub Tour Framework and First Tour feat: Learning Hub Tour Framework May 22, 2026
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from a98fb46 to 9f459c8 Compare May 22, 2026 19:29
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from fd9690e to 8c23f4e Compare May 22, 2026 21:53
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from 9f459c8 to e7770ca Compare May 22, 2026 21:53
@camielvs camielvs mentioned this pull request May 22, 2026
8 tasks
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from e7770ca to e26abd3 Compare May 23, 2026 00:09
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from 8c23f4e to b781af4 Compare May 23, 2026 00:12
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 2 times, most recently from d95e8da to 6246686 Compare May 23, 2026 00:21
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from b781af4 to 12125e7 Compare May 23, 2026 00:21
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 2 times, most recently from ecfc28a to cecf1a3 Compare May 28, 2026 20:54
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from 51800ff to b73fea7 Compare May 28, 2026 20:54
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 2 times, most recently from 5c82cee to 07be379 Compare May 28, 2026 21:07
@camielvs camielvs mentioned this pull request May 28, 2026
3 tasks
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch 11 times, most recently from bd097db to 84f39e0 Compare May 29, 2026 18:33
@camielvs camielvs changed the title feat: Learning Hub Tour Framework feat: Guided TOurs Framework May 29, 2026
@camielvs camielvs changed the title feat: Guided TOurs Framework feat: Guided Tours Framework May 29, 2026
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from 84f39e0 to d6d13ae Compare May 29, 2026 19:24
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from b73fea7 to faca533 Compare May 29, 2026 21:22
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from d6d13ae to e97b036 Compare May 29, 2026 21:22
@camielvs camielvs mentioned this pull request May 29, 2026
3 tasks
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from e97b036 to d308ae7 Compare May 29, 2026 21:29
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from faca533 to 879e09d Compare May 29, 2026 21:29
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_tour_framework_and_first_tour branch from d308ae7 to 95b9b2a Compare May 29, 2026 22:33
@camielvs camielvs force-pushed the 05-20-feat_learning_hub_guided_tours branch from 879e09d to 4bd1952 Compare May 29, 2026 22:33
Introduces the guided tour framework using [Reactour](https://docs.reactour.dev/),
exposed as a dedicated `/tour/$tourId` route rather than an imperative
provider call. The route owns:

- The temp tour pipeline lifecycle (create on mount, delete on unmount,
  no persisted "save state" — tours always start fresh)
- Editor layout snapshot/restore around the tour
- URL ↔ reactour step sync (`?step=N` is a stable, shareable deep-link
  and survives same-tab refresh via the URL itself)
- A `TourModeContext` consumed by `EditorMenuBar` and `FileMenu` to show
  tour-specific UI (badge, Resume/Save-as/Exit buttons when the popover
  is closed) and hide destructive actions

Engine bits live under `src/providers/TourProvider/`:
- `TourProvider.tsx` — slim reactour wrapper, app-wide
- `tourPopover.tsx` — styles, position fn, FinishButton, clamp bridge
- `tourPipelineLifecycle.ts` — create/delete/promote/cleanup helpers
- `TourOrphanCleanup.tsx` — sweeps tour pipelines on app load (catches
  tab-close orphans), skipped while user is on a tour route
- `TourModeContext.tsx` — context hook for editor components
- `waitForSelector.ts` — DOM utility

`EditorV2` accepts an optional `pipelineRef` prop so the tour route can
render the same editor against a temp pipeline it resolved itself.

Layout snapshot/restore is exposed from `windowPersistence` so the tour
doesn't reach into localStorage directly.

`registry.ts` ships with `tours: TourDefinition[] = []` — the first
tour lands in the stacked PR.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

#gsd:50583 Learning Hub

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant