167 lines
6.1 KiB
Markdown
167 lines
6.1 KiB
Markdown
# 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
|
|
|
|
```ts
|
|
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
|
|
|
|
```text
|
|
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.
|