Project onboarding
View on GitHubAdd an interactive tour step
Worked example: a tour step that asks the user to fill in a field
themselves. This is how the optional "create your first project"
chapter (PROJECT_CHAPTER_STEPS in resources/js/types/Tour.ts)
teaches the real "New project" form.
On a normal step the page is blocked and the arrow moves on. On an interactive step the spotlighted element stays usable — the rest of the page stays blocked — and the popover shows a hint such as "Type a project name".
Step 1 — Mark the elements
Wrap each field (label + input) in an element with data-tour. For
input steps the tour finds the <input>/<textarea> inside the
wrapper, focuses it and reads its value.
File: resources/js/Components/Organisms/NewProjectModal/NewProjectModal.tsx
<div data-tour="project-name" className="flex flex-col gap-1.5">
<label>Project name</label>
<Input value={data.name} onChange={...} />
</div>
Step 2 — Describe the interaction
File: resources/js/types/Tour.ts
{
id: 'project-name',
target: 'project-name',
placement: 'right',
backTo: 'project-open',
when: needsProjectChapter,
title: 'Name it',
description: 'Pick something your team will recognise.',
interaction: {
type: 'input',
required: true,
hint: 'Type a project name',
},
},
interaction.type is one of:
click— the user clicks the target; the arrow clicks it for them. The tour advances right after the click. Addadvance: 'external'pluscompleteWhento wait for something else instead — the "Create project" step advances only once thehasProjectsshared prop turnstrue, so a validation error doesn't skip ahead.input— typing into the field inside the target. Withrequired: truethe arrow stays disabled until the field has a value.free— the user may poke at the target (e.g. pick a color); the arrow simply moves on.
A step inside a form can also set completeWhen and skipTo: if the
form gets submitted early (the user presses Enter before reaching the
submit step), the tour jumps to skipTo instead of treating the vanished
form as a closed modal. All steps of the "New project" form do this.
backTo names an earlier step to jump back to if the target
disappears mid-step (for example the user closed the modal), so the
tour never strands on a step. Pressing the back arrow from such a
step also closes the modal it lives in.
Step 3 — Place it in a chapter
Steps in PROJECT_CHAPTER_STEPS use when: needsProjectChapter, so
they only appear for accounts with no project that haven't been
through the chapter. TOUR_STEPS spreads the chapter in before the
final step; getTourSteps() serves it on its own to accounts that
already finished the main tour.
Set sidebar: true on steps whose target is in the sidebar (opens
the mobile drawer) and leave it off for steps inside a modal.
Behaviors worth knowing
- Layers sit above
Modal(z-[1000]), atz-[1100]/z-[1101]. - Escape is ignored on interactive steps (the modal owns it), and ←/→ are ignored while typing so the caret keeps working.
- The popover doesn't steal focus on interactive steps. Instead of the popover's own trap, Tab cycles through the highlighted element and the tour's controls only, so covered fields can't be reached by keyboard.
- The target is scrolled into view once when an interactive step starts, since a field can be inside the window yet clipped by a scrollable modal.
Step 4 — Test it
resources/js/Components/Organisms/ProductTour/ProductTour.test.tsx(interactive steps) — click, input,completeWhen,backTo.resources/js/types/Tour.test.ts— which steps each kind of account gets, and that everybackTopoints at an earlier step.
