6.1 KiB
OpenSpec Change: enterprise-browser-react-tour
- Change ID: enterprise-browser-react-tour
- Package:
@suaveplan/react-tour - Category: browser
- Target path:
browser/react/react-tour - Status: IN PROGRESS
- Date: 2026-04-24
- Reference spec:
/mnt/s/web/suaveplan/enterprise/react-tour.md
Summary
Deliver @suaveplan/react-tour on the genesis branch at enterprise quality. The package provides a headless useTour hook for guided product tours with step navigation, spotlight targeting, scroll-into-view, and completion persistence. Genesis adds Zod-validated options, live targetRect tracking via ResizeObserver/IntersectionObserver, a PersistenceAdapter interface for injectable storage, @suaveplan/events lifecycle events, structured logging via @suaveplan/logger, SSR safety guards, Storybook stories, and co-located .md docs.
Current-state audit (genesis)
Fresh build — does not exist on genesis yet.
Available workspace deps on genesis
| Package | Role |
|---|---|
@suaveplan/error (workspace:^) |
TourConfigError, TOUR_STEP_NOT_FOUND |
@suaveplan/events (workspace:^) |
tour:start, tour:step-change, tour:complete, tour:skip, tour:error |
@suaveplan/logger (workspace:^) |
Structured logging for storage failures |
@suaveplan/schema-registry (workspace:^) |
Register TourStep schema |
zod (catalog:forms) |
TourConfig + TourStep Zod schemas |
@opentelemetry/api (catalog:observability) |
Step-view / completion metrics |
tslib (catalog:build) |
Runtime helpers |
Dev: vitest, @vitest/coverage-istanbul, happy-dom, @testing-library/react, @suaveplan/testing, @suaveplan/testing-dom.
Preserved public API surface
function useTour(config: TourConfig): [TourState, TourActions]
// Utils
function calculateSpotlightInset(rect: DOMRect, padding?: number): SpotlightInset
function calculateTooltipPosition(rect: DOMRect, placement: TooltipPlacement): TooltipPosition
function getTargetRect(selector: string): DOMRect | null
function scrollTargetIntoView(selector: string): Promise<void>
// Persistence (new injectable interface)
interface PersistenceAdapter { isCompleted(key: string): boolean; markCompleted(key: string): void; clear(key: string): void }
// Types
interface TourConfig { steps: TourStep[]; storageKey?: string; persistence?: PersistenceAdapter; onComplete?(): void; onSkip?(): void }
interface TourStep { id: string; target?: string; title: string; content: string; placement?: TooltipPlacement; shouldSkip?(ctx: TourState): boolean }
interface TourState { isActive: boolean; currentStep: number; totalSteps: number; isFirst: boolean; isLast: boolean; progress: number; targetRect: DOMRect | null }
interface TourActions { next(): void; prev(): void; goTo(n: number): void; skip(): void; reset(): void }
Subpath exports: ".", "./core/tour/tour", "./utils/positioning", "./persistence".
New additions for genesis
1. Zod-validated options
TourConfigSchema and TourStepSchema validated at hook entry; throws TourConfigError (extends @suaveplan/error) on invalid input. Registered in @suaveplan/schema-registry.
2. Live targetRect tracking
Replace the 100 ms setTimeout with a composite observer (ResizeObserver + IntersectionObserver + scroll listener) in src/utils/observe-target.ts so targetRect stays accurate during layout shifts.
3. PersistenceAdapter interface
LocalStoragePersistence (default), SessionStoragePersistence, MemoryPersistence (for tests), injectable via TourConfig.persistence. SSR-safe: typeof window guards everywhere.
4. Event lifecycle via @suaveplan/events
Emits tour:start, tour:step-change, tour:complete, tour:skip, tour:error so analytics and observability consumers can subscribe.
5. Structured logging
@suaveplan/logger replaces the three bare catch {} blocks in the original for storage and scroll errors.
6. Storybook stories
useTour.stories.tsx — basic tour, spotlight overlay, scroll-into-view, completion persistence, accessibility-compliant tooltip overlay.
7. Error codes
TOUR_CONFIG_INVALID, TOUR_STEP_NOT_FOUND.
8. OTel metrics
Counter tour.step_view_total, counter tour.complete_total, counter tour.skip_total, histogram tour.step_duration_ms.
File layout
browser/react/react-tour/
package.json
tsconfig.json
vitest.config.ts
src/
index.ts
error-codes.ts
core/
tour/
tour.ts (useTour hook, ≤250 lines)
tour-state.ts (state machine helpers)
tour-effects.ts (observer wiring + events)
tour-actions.ts (next/prev/goTo/skip/reset)
tour.test.ts
tour.md
tour.types.ts
index.ts
utils/
positioning/
positioning.ts (calculateSpotlightInset, calculateTooltipPosition, getTargetRect, scrollTargetIntoView)
positioning.test.ts
positioning.md
positioning.types.ts
index.ts
observe-target/
observe-target.ts (ResizeObserver + IntersectionObserver + scroll)
observe-target.test.ts
observe-target.md
index.ts
persistence/
persistence/
persistence.ts (PersistenceAdapter interface + implementations)
persistence.test.ts
persistence.md
index.ts
schema/
tour-schema/
tour-schema.ts (TourConfigSchema, TourStepSchema)
tour-schema.md
index.ts
stories/
use-tour.stories.tsx (basic, spotlight, scroll, persistence, a11y)
types/
common.ts
common.md
constants/
index.ts
tests/
integration.test.ts
e2e/
react-tour.spec.ts (targetRect tracks resize/scroll, completion persistence survives reload)
Impact
Downstream: any Suaveplan browser app using guided tours.
Breaking changes: None — all additions are backward-compatible; PersistenceAdapter is opt-in.
Prerequisites: @suaveplan/error, @suaveplan/events, @suaveplan/logger on Verdaccio.