Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
# 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.

View File

@@ -0,0 +1,136 @@
# Tasks: enterprise-browser-react-tour
**Change ID**: enterprise-browser-react-tour
**Package**: `@suaveplan/react-tour`
**Target path**: `browser/react/react-tour`
**Prerequisite packages on Verdaccio**: `@suaveplan/error`, `@suaveplan/events`, `@suaveplan/logger`, `@suaveplan/schema-registry`
---
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
## 1. Scaffold
- [ ] Create directory tree per `proposal.md`.
- [ ] `package.json` v0.0.0, `"sideEffects": false`.
- `dependencies`: `@suaveplan/error workspace:^`, `@suaveplan/events workspace:^`, `@suaveplan/logger workspace:^`, `@suaveplan/schema-registry workspace:^`, `zod catalog:forms`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`, `react catalog:react`
- `peerDependencies`: `react`
- `devDependencies`: `vitest catalog:testing`, `@vitest/coverage-istanbul catalog:testing`, `happy-dom catalog:testing`, `@testing-library/react catalog:testing`, `@suaveplan/testing workspace:^`, `@suaveplan/testing-dom workspace:^`
- Subpath exports: `"."`, `"./core/tour/tour"`, `"./utils/positioning"`, `"./persistence"`
- [ ] `tsconfig.json`, `vitest.config.ts` (pool: forks, happy-dom environment).
---
## 2. Error codes
- [ ] `TOUR_CONFIG_INVALID`, `TOUR_STEP_NOT_FOUND`.
- [ ] `registerTourErrorCodes()` — call eagerly.
---
## 3. Types (`src/types/common.ts`)
- [ ] `TourConfig`, `TourStep`, `TourState`, `TourActions`, `TooltipPlacement`, `TooltipPosition`, `SpotlightInset`.
- [ ] Write `common.md`.
---
## 4. `TourSchema` (`src/schema/tour-schema/`)
- [ ] `tour-schema.ts` — `TourConfigSchema`, `TourStepSchema` (Zod); `registerTourSchemas()`.
- [ ] Write `tour-schema.md`.
---
## 5. Positioning utilities (`src/utils/positioning/`)
- [ ] `positioning.ts` — `calculateSpotlightInset`, `calculateTooltipPosition`, `getTargetRect`, `scrollTargetIntoView`.
- [ ] All functions SSR-safe (`typeof document` guard).
- [ ] Write `positioning.test.ts` (happy-dom geometry, SSR import smoke test). Write `positioning.md`.
---
## 6. Observer utility (`src/utils/observe-target/`)
- [ ] `observe-target.ts` — composite `observeTarget(selector, callback)` using `ResizeObserver` + `IntersectionObserver` + scroll listener; returns cleanup.
- [ ] Write `observe-target.test.ts` (observer fires on resize, fires on scroll). Write `observe-target.md`.
---
## 7. Persistence adapters (`src/persistence/persistence/`)
- [ ] `persistence.ts` — `PersistenceAdapter` interface; `LocalStoragePersistence` (SSR-safe), `SessionStoragePersistence`, `MemoryPersistence`.
- [ ] Write `persistence.test.ts` (each adapter: isCompleted, markCompleted, clear, SSR no-throw). Write `persistence.md`.
---
## 8. Tour state helpers (`src/core/tour/tour-state.ts`)
- [ ] `computeProgress(step, total): number`, boundary guards, `shouldSkip` evaluation.
---
## 9. Tour effects (`src/core/tour/tour-effects.ts`)
- [ ] Observer wiring: call `observeTarget` on step change, update `targetRect` on callback.
- [ ] Emit `@suaveplan/events` lifecycle events (`tour:start`, `tour:step-change`, `tour:complete`, `tour:skip`, `tour:error`).
- [ ] Wire OTel metrics.
---
## 10. Tour actions (`src/core/tour/tour-actions.ts`)
- [ ] `next()`, `prev()`, `goTo(n)`, `skip()`, `reset()` — with boundary checks and `shouldSkip` evaluation.
---
## 11. `useTour` hook (`src/core/tour/tour.ts`)
- [ ] Compose state + effects + actions.
- [ ] Validate `TourConfig` via `TourConfigSchema` on entry; throw `TOUR_CONFIG_INVALID`.
- [ ] Log storage errors via `@suaveplan/logger` instead of swallowing.
- [ ] Write `tour.test.ts` (config validation, next/prev/goTo/skip, persistence, SSR no-throw). Write `tour.md`.
---
## 12. Storybook stories (`src/stories/use-tour.stories.tsx`)
- [ ] Default story: basic 3-step tour.
- [ ] Spotlight overlay: CSS spotlight with `calculateSpotlightInset`.
- [ ] Scroll-into-view: target below the fold.
- [ ] Completion persistence: reload and confirm tour does not restart.
- [ ] Accessibility-compliant tooltip: `role="dialog"`, focus trap, `aria-live`.
---
## 13. Integration test (`tests/integration.test.ts`)
- [ ] Full tour flow: start, navigate, complete, verify persistence adapter called.
- [ ] `MemoryPersistence` swap: no `localStorage` touched.
- [ ] `shouldSkip` step evaluated; step skipped automatically.
---
## 14. Playwright E2E (`e2e/react-tour.spec.ts`)
- [ ] `targetRect` stays aligned after element resizes (Playwright + Chromium).
- [ ] Completion persists across reload (Playwright localStorage).
- [ ] SSR smoke: import from Node without throwing.
---
## 15. Docs + verify
- [ ] Write `.md` per module (≥200 words, API reference, examples).
- [ ] Write `README.md` with subpath table, persistence-adapter guide, SSR guide, a11y notes.
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/react-tour` — green.
- [ ] `bunx turbo test:coverage` — 100%.
- [ ] No `@ts-expect-error` in `src/` or tests. No `vi.stubGlobal`.
- [ ] Storybook stories render and pass axe-core.
- [ ] Playwright E2E green on Chromium.
- [ ] `bun scripts/publish.ts --package @suaveplan/react-tour`.
- [ ] Archive to `openspec/archive/2026-04-24-enterprise-browser-react-tour/`.