feat: Guided Tours Framework (Secrets)#2405
Conversation
🎩 PreviewA preview build has been created at: |
62653bf to
d7a841f
Compare
f541439 to
4b15ecd
Compare
d7a841f to
c7356f5
Compare
a386028 to
8035a04
Compare
8035a04 to
3538d7a
Compare
4b15ecd to
43ecf52
Compare
3538d7a to
ceba986
Compare
43ecf52 to
40d7a25
Compare
ceba986 to
564f6b9
Compare
a6562cb to
f997497
Compare
564f6b9 to
d2b59c0
Compare
f997497 to
2ea3d5e
Compare
d2b59c0 to
6b9c892
Compare
e8326e6 to
cc248e5
Compare
eec8848 to
46f3364
Compare
cc248e5 to
ea6118d
Compare
46f3364 to
2e917b7
Compare
ea6118d to
b44628b
Compare
2e917b7 to
d9f0b2a
Compare
b44628b to
6961ae0
Compare
d9f0b2a to
84a4fcc
Compare
84a4fcc to
c2bd919
Compare
c2bd919 to
bef69f1
Compare
2ce1124 to
723df5c
Compare
bef69f1 to
a095516
Compare
14e2536 to
e9e851a
Compare
a095516 to
9b2a265
Compare
e9e851a to
bc658d3
Compare
9b2a265 to
7bb124c
Compare
When a tour opts in (TourDefinition.mockBackend) and runs without a real backend, mock the secrets backend in-memory so its secret steps stay hands-on (open the picker, add/pick/assign a secret) against an ephemeral store cleared when the tour ends. Steps gate normally; the submit dialog opens to demonstrate assigning a secret to a run input, but real run submission stays gated on availability. - tourMockBackend: module store + useTourMockBackend hook + in-memory secrets - secretsStorage routes to the mock when active - TourMockBackendController activates it (tour.mockBackend && !available) - secrets list/picker + run-with-arguments button accept the mock; submit confirm stays disabled in mock mode - Replaces the earlier read-only degrade (drops requiresBackend gating relaxation, fallbackContent/BackendAwareStepContent); keeps the SecretsBackendUnavailable empty state for non-tour Settings -> Secrets Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bc658d3 to
bdaf9f1
Compare
7bb124c to
b7b4365
Compare
| } | ||
|
|
||
| if (interaction === "open-submit-dialog") { | ||
| const dialogSelector = '[data-tour="submit-arguments-dialog"]'; |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
The new open-submit-dialog and assign-secret-submit interactions watch for [data-tour="submit-arguments-dialog"], but SubmitTaskArgumentsDialog does not render that data-tour attribute. The Secrets tour will therefore stall when it asks the user to open or complete the submit-arguments dialog.
Suggestion: Add data-tour="submit-arguments-dialog" to the DialogContent in SubmitTaskArgumentsDialog, or update the bridge to use an existing stable selector that the dialog actually renders.
Rule: tangle-ui review: high-priority functional bugs must be flagged; E2E/tour selectors must match real rendered DOM.
| // settings shell (sidebar with Secrets active, other tabs disabled) and renders | ||
| // the real SecretsListView, but keeps add/replace in-dialog instead of using the | ||
| // router links the real page relies on, so the tour never navigates away. | ||
| export function TourSecretsDialog() { |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
This PR adds new components/hooks under directories that are not covered by REACT_COMPILER_ENABLED_DIRS (src/providers and src/components/shared/SecretsManagement are not enabled). New components and hooks must be compiler-covered when introduced; otherwise the stack expands uncompiled React code.
Suggestion: Add compiler entries for the new files that are compatible now, at minimum src/providers/TourProvider/TourSecretsDialog.tsx, src/providers/TourProvider/tourMockBackend.ts, and src/components/shared/SecretsManagement/components/SecretsBackendUnavailable.tsx, then validate.
Rule: react-patterns: new/moved/renamed components and hooks must be covered by react-compiler.config.js.
| Manage your secrets. | ||
| </DialogDescription> | ||
|
|
||
| <BlockStack className="h-full"> |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
The new settings stand-in styles BlockStack, InlineStack, and Button via className, and SecretsBackendUnavailable does the same for BlockStack/Icon. Project rules require semantic primitive props or a local/pattern component instead of using className as an escape hatch on primitives.
Suggestion: Move these layout styles into semantic primitive props or a small local primitive/pattern wrapper for the settings shell; use tone/size props for icon/text styling instead of primitive className.
Rule: ui-primitives: never pass className to a Tangle UI primitive; prefer semantic props or Layer-3/local primitive patterns.
| }; | ||
| } | ||
|
|
||
| if (interaction === "open-secret-dialog") { |
There was a problem hiding this comment.
🤖 This is an AI-generated code review comment.
The PR adds several near-identical MutationObserver branches for selector-based interactions. The file is already very large and FTA reports 85.7 with cyclomatic complexity 160, so adding repeated branches makes future tour interactions harder to maintain.
Suggestion: Extract a small helper such as waitForSelectorInteraction(selector, { skip, advance, stopFollow }) and reuse it for open-secret-dialog, open-settings-panel, open-submit-dialog, and assign-secret-submit.
Rule: static-analysis: high FTA/cyclomatic complexity should be tied to concrete extraction; tangle-ui review: duplicated logic should be extracted.

Description
The secret-specific interaction mechanics for the guided-tour framework, layered on the framework stack (#2348 / #2299 / #2347 / #2340) and the subgraphs mechanics (#2365). Foundation for the Using Secrets tour (#2406) — this PR adds the interaction types and bridge logic that detect them, the in-editor Settings → Secrets stand-in the tour opens, an in-memory mock backend so the secret steps stay hands-on without a real backend, and small reusability/behavior tweaks that let it all run without leaving the tour page. The tour content and DOM anchors land in #2406.
What changed
Interaction vocabulary (
src/components/Learn/tours/registry.ts)Five new
TourStep.interactionvalues, plus atourPanelfield, plus a tour-levelmockBackendflag (see below):assign-secret-argument— a task argument's value becomes a secret (MobXactiveSpecreaction viaisSecretArgument; optionally gated to atargetArgumentName)assign-secret-submit— a secret gets bound inside the submit dialog. The value lives in the dialog's local React state (not the spec), so this is detected from the DOM rather than a spec reactionopen-secret-dialog/open-settings-panel/open-submit-dialog— the respective dialog appears in the DOMtourPanel: "secrets-manager"— marks the steps during which the in-editor Settings stand-in should be openBridge handlers (
src/routes/v2/pages/Editor/components/EditorTourBridge.tsx)Each interaction marks the step complete in the shared tour-progress state (following #2299; it doesn't advance the tour itself). The three
open-*handlers areMutationObserverDOM-presence checks;assign-secret-argumentis a spec reaction;assign-secret-submitwatches for the secret display inside the submit dialog. Already-satisfied-on-entry states are marked complete immediately; gated "Next" / auto-advance (#2340) handle progression.Checklist labels (
src/providers/TourProvider/tourActionLabels.ts+ test)Copy for the five interactions, e.g.
assign-secret-argument→ "Assign a secret to the {targetArgumentName} argument",open-settings-panel→ "Open Settings to manage your secrets".In-editor Settings → Secrets stand-in (
TourSecretsDialog.tsx, new)Navigating to the real
/settings/secretsroute would unmount the tour page and tear the tour down. Instead, a non-modal dialog mirrors the real settings shell (sidebar with Secrets active, the other tabs disabled) and renders the realSecretsListView, keeping add/replace in-dialog rather than using the Settings router links. It opens when the active step is markedtourPanel: "secrets-manager"and the user clicks the gear (openTourSettings), and closes when the tour leaves those steps.AppMenuActions.tsx— in tour mode the Settings gear callsopenTourSettings()instead of routing away (keeps itsdata-tracking-idso the tour can anchor to it).EditorV2.tsx— mounts<TourSecretsDialog />alongside the existing tour dialogs.No-backend mock backend (
tourMockBackend.tsnew,secretsStorage.ts,Tour.tsx, secret/submit gates)Secrets live on the backend, so without one the secret steps would otherwise trap the user on a step they can't complete. When a tour opts in (
TourDefinition.mockBackend) and there's no real backend, the secrets backend is mocked in-memory so the steps stay fully hands-on — the user really opens the picker, adds a secret, picks it, and assigns it, against an ephemeral store that is cleared when the tour ends. Nothing is persisted or sent anywhere; with a real backend this is inert.tourMockBackend.ts(new) — a provider-free module store:setTourMockActive/isTourMockActive, auseTourMockBackend()hook, and an in-memorySecretmap (mockListSecrets/mockAddSecret/mockUpdateSecret/mockRemoveSecret).secretsStorage.ts—fetchSecretsList/addSecret/updateSecret/removeSecretroute to the in-memory store when the mock is active (the existingSecretsQueryKeys.All()invalidations keep the list fresh).Tour.tsx— aTourMockBackendControlleractivates the mock whiletour.mockBackend && !availableand clears it (and the store) on tour exit.SelectSecretDialog/SecretsListrender the real picker (available || mock); the run-with-arguments (split-arrows) button enables so the submit dialog can open. The primary Submit button and the submit-args confirm stay gated on real availability — the secret-to-input assignment is demonstrated, but launching a real run isn't wired in mock mode.Friendly no-backend empty state (
SecretsBackendUnavailable.tsx, new)SelectSecretDialog/SecretsListshow a "Backend not connected" state (Lock/DB icon + copy) instead of the generic error-boundary icon when there's genuinely no backend and the mock isn't active — which also improves the real Settings → Secrets page for backend-less setups.Reusable secrets components (
SecretsList.tsx,SecretsListView.tsx)Optional
onAddSecret/onEditSecretcallbacks. They default to the existing router links (real Settings page is unchanged), but let the stand-in reuse the real list/forms with in-dialog add/replace — so the tour isn't a hand-maintained copy of the secrets UI.Popover & dialog behavior
TourPopover.tsx—computeDefaultPopoverPositiongains a branch for a tall, centered modal: anchor the popover to its left, aligned to the dialog's top edge (rather than vertically centered).SubmitTaskArgumentsDialog.tsx— rendersmodal={!tourMode}; non-modal during a tour so the portaled tour popover (Next / Finish Tour) stays clickable while the dialog is open. Behavior outside tours is unchanged.Related Issue and Pull requests
Progresses https://github.com/Shopify/oasis-frontend/issues/583
Builds on the framework stack (#2348 / #2299 / #2347 / #2340 / #2365); consumed by the Using Secrets tour in #2406.
Type of Change
Checklist
Test Instructions
The registry is otherwise inert in this PR — the interactions are exercised end-to-end by the Using Secrets tour in #2406. To verify in isolation:
/api/secretscalls fire. Settings → Secrets with no backend shows the "Backend not connected" state.pnpm typecheck,pnpm lint, and thetourActionLabels/tourMockBackendunit tests pass.