Public Access
feat(ui): Sprint 9 — F1 onboarding tour (4-step welcome)
Hand-rolled 4-step tour (no react-joyride) anchors to existing [data-tour="<id>"] attributes. localStorage key mealplanner:onboarding-complete is the source of truth; ?reset-tour=1 clears the key and re-shows. Steps: Dashboard / Pantry / Recipes / Shopping List. Keyboard: 1-4 jump, ←/→ step, Esc dismiss. Off-route fallback renders a centered card with an 'Open <page>' CTA. A11y: role=dialog, aria-modal=true, focus captured on open and restored on close. 5 lines of code across 4 pages; 1 new component (~420 lines). No new dependencies. No backend changes. No migration. Frontend-only deploy. Tracking: Review/sprint9-verification.md (8-step browser smoke + a11y check + reset-link test).
This commit is contained in:
@@ -2,6 +2,7 @@ import { BrowserRouter, Routes, Route, Link, useLocation, useNavigate, Navigate
|
||||
import { QueryClient, QueryClientProvider, QueryCache, MutationCache } from '@tanstack/react-query'
|
||||
import { ErrorBoundary } from './components/ErrorBoundary'
|
||||
import { ShortcutHelpBanner, SHOW_SHORTCUT_HELP_EVENT } from './components/ShortcutHelpBanner'
|
||||
import { OnboardingTour, useOnboarding } from './components/OnboardingTour'
|
||||
import { showApiError } from './lib/toast'
|
||||
import { useKeyboardShortcuts } from './hooks/useKeyboardShortcuts'
|
||||
import { requestFocusSearch } from './hooks/useFocusSearch'
|
||||
@@ -73,6 +74,10 @@ function GlobalShortcuts() {
|
||||
}
|
||||
|
||||
function App() {
|
||||
// Onboarding tour state lives at the App root so the localStorage
|
||||
// key is read once on mount. The tour itself is mounted inside the
|
||||
// router (it needs useLocation / useNavigate).
|
||||
const onboarding = useOnboarding()
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
@@ -94,6 +99,15 @@ function App() {
|
||||
</Routes>
|
||||
</main>
|
||||
<ShortcutHelpBanner />
|
||||
<OnboardingTour
|
||||
isComplete={onboarding.isComplete}
|
||||
onComplete={() => {
|
||||
// The tour already wrote the localStorage key via
|
||||
// writeComplete(); flip the App-level flag so it stays
|
||||
// hidden after the next render.
|
||||
onboarding.reset()
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</BrowserRouter>
|
||||
</QueryClientProvider>
|
||||
|
||||
Reference in New Issue
Block a user