feat: Guided Tours End Screen#2339
Conversation
🎩 PreviewA preview build has been created at: |
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
e1a0ce6 to
98d9144
Compare
288fe40 to
1733fc8
Compare
2805208 to
931f929
Compare
931f929 to
d56854f
Compare
1733fc8 to
444aa16
Compare
d56854f to
eed0e9a
Compare
444aa16 to
38b4021
Compare
eed0e9a to
a906d43
Compare
38b4021 to
b2e2959
Compare
a906d43 to
9d3d7e9
Compare
b2e2959 to
63df8cc
Compare
9d3d7e9 to
ea34dec
Compare
63df8cc to
e33f721
Compare
d9d8939 to
4c7d30d
Compare
a8ed23c to
811dd19
Compare
4c7d30d to
0031f7f
Compare
811dd19 to
163f396
Compare
0031f7f to
ea9f210
Compare
163f396 to
2f4a4d3
Compare
ea9f210 to
ebf3782
Compare
2f4a4d3 to
0dec8db
Compare
ebf3782 to
42bc0ad
Compare
cd00ca2 to
4cb82fa
Compare
| import { usePipelineActions } from "@/routes/v2/pages/Editor/store/actions/usePipelineActions"; | ||
| import { useSharedStores } from "@/routes/v2/shared/store/SharedStoreContext"; | ||
|
|
||
| export function TourSaveExploreDialog() { |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
This PR adds a new React component under src/providers, but react-compiler.config.js only enables src/routes and selected provider subdirectories; src/providers/TourProvider is still outside compiler coverage. The project review standard treats missing React Compiler registration for new components/hooks as a High-severity adoption gap.
Suggestion: Add this file explicitly to REACT_COMPILER_ENABLED_DIRS, or enable the appropriate TourProvider subdirectory after confirming it is compiler-compatible.
Rule: react-patterns / tangle-ui-review — new components and hooks must be covered by react-compiler.config.js; new files under non-enabled directories are not covered automatically.
| return "bottom"; | ||
| } | ||
|
|
||
| let saveExploreHandler: (() => void) | null = null; |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
The save/explore action is registered by mutating a module-level variable and then read during TourCompletionActions render. Because registering the handler is not React state or context, changes do not cause a re-render; if the completion actions render before the effect in TourSaveExploreDialog registers, the Save demo pipeline button is omitted until some unrelated render happens. This also makes the feature depend on mount/effect ordering instead of explicit React data flow.
Suggestion: Move the save-dialog opener into React-owned state/context, e.g. include an openSavePipelineDialog callback or capability flag in TourModeValue/TourProvider, and render TourCompletionActions from that context instead of reading a mutable module singleton.
Rule: docs/react-best-practices.md / react-patterns — context/provider values should model shared state explicitly; avoid unstable/non-reactive values that bypass React data flow.

Description
Adds a Save this pipeline affordance at the end of every tour. Curious users who want to keep poking at the pipeline they just walked through (or use it as a starting point) can convert the ephemeral tour pipeline into a real, durable one.
Surfaces only on the last tour step, as a small link-style button below the primary Done action, under a "Continue exploring:" label. Clicking it:
/editor/$nameroute with the new file open.If the user just clicks Done, behavior is unchanged: tour ends, temp pipeline stays in session storage until the next tour starts (which resets it).
Architecture
TourCompletionActionsextension (src/providers/TourProvider/tourPopover.tsx)TourCompletionActions) is now joined by a conditional "Save this pipeline" button whensaveExploreHandleris registered. Both render in the last step's content area.saveExploreHandlerslot +registerSaveExploreHandler(cb)API bridges the React tree gap between the reactour popover (rendered aboveTourModeProviderin the tree, no access to it) and the dialog host (rendered belowSharedStoreProvider, has the spec access we need). The popover only knows "is something registered? if so, call it". The dialog host is what registers itself.TourSaveExploreDialog(src/providers/TourProvider/TourSaveExploreDialog.tsx, mounted insideEditorV2Content)setOpen(true)handler withregisterSaveExploreHandleron mount whentourModeis active; clears it on unmount.useSharedStores().navigation, renames it in-memory viausePipelineActions().renamePipeline, serializes to YAML, and callstourMode.promoteToPipeline(name, yaml).promoteToPipeline(src/providers/TourProvider/TourModeContext.tsx+Tour.tsx)TourModeContextvalue. The implementation lives inTourPage(above the storage override) whereusePipelineStorage()resolves to the durable IndexedDB service./editor/$name. Errors surface viauseToastNotification.Why the indirection through a module-level handler
The natural place to render the dialog is inside
EditorV2, whereSharedStoreProviderexposes the live spec. The natural place to render the trigger button is the popover, which reactour mounts above ourTourModeProvider. React Context can't bridge that gap (the popover renders outside the provider's subtree). The module-level slot is the smallest possible bridge: one writer, one reader, scoped by lifecycle of the dialog host.Related Issue and Pull requests
Progresses https://github.com/Shopify/oasis-frontend/issues/583
Type of Change
Checklist
Screenshots
Test Instructions
Pre-req: a registered tour (use this branch with #2306 on top).
Happy path
/editor/<name>with the pipeline open and editable. The file appears in the regular pipelines list.__tour__<slug>) remains in session storage; it'll be reset on the next tour start.Cancel / dismiss
Name collision
Regression
/learn/tours, nothing is added to the pipelines list.Additional Comments
The "Continue exploring:" framing is intentional — users who walk through a guided tour and walk away should not feel obligated to save anything. Save is opt-in for the curious.