From ca440c837bd602c82058a44f713adb6c147b7f78 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 30 Aug 2026 23:18:33 +0000 Subject: [PATCH] docs: add monorepo, UI components, state management and testing guides Adds docs/monorepo.md (workspace layout, Turbo task graph, shared tooling), apps/web/docs/components-ui.md (shared UI primitives reference), apps/web/docs/concepts-state-management.md (provider tree, Auth/Wallet contexts, ProtectedRoute), and apps/web/docs/testing.md (Vitest setup, fake-indexeddb, WebCrypto, socket mocking). Closes #544 Closes #568 Closes #572 Closes #573 Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01R8SebNynWtmCebVq8tpgxS --- apps/web/docs/components-ui.md | 242 +++++++++++++++++++++ apps/web/docs/concepts-state-management.md | 179 +++++++++++++++ apps/web/docs/testing.md | 211 ++++++++++++++++++ docs/monorepo.md | 95 ++++++++ 4 files changed, 727 insertions(+) create mode 100644 apps/web/docs/components-ui.md create mode 100644 apps/web/docs/concepts-state-management.md create mode 100644 apps/web/docs/testing.md create mode 100644 docs/monorepo.md diff --git a/apps/web/docs/components-ui.md b/apps/web/docs/components-ui.md new file mode 100644 index 0000000..b44cc15 --- /dev/null +++ b/apps/web/docs/components-ui.md @@ -0,0 +1,242 @@ +# UI Primitives (`apps/web/src/components/ui/`) + +This document covers the shared UI primitives in `apps/web/src/components/ui/`: `Avatar`, `Badge`, `CopyButton`, `EmptyState`, `Modal`, `ProposalCard`, `SkeletonLoader`, and `Spinner`. For each component: its props, a usage example, whether it's presentational or stateful, any accessibility behavior implemented, and current test coverage. Test coverage was verified by grepping the whole repo for each component name inside `*.test.*` files — `ProposalCard.test.tsx` is the only test file found for any of these eight components. + +--- + +## Avatar + +`apps/web/src/components/ui/Avatar.tsx` + +**Props** + +| Name | Type | Required | Default | +|---|---|---|---| +| `src` | `string` | No | `undefined` | +| `fallback` | `string` | Yes | — | +| `size` | `'sm' \| 'md' \| 'lg'` (maps to 24 / 36 / 48px) | Yes | — | +| `online` | `boolean` | No | `undefined` (falsy) | + +`fallback` is also used to derive initials (via `getInitials`) and a deterministic background color (via a simple string hash → HSL hue) when no image is shown. + +**Usage** + +```tsx + +``` + +**Type**: Stateful. Uses `useState` to track a `failedSrc` (so that if the image `onError`s, it falls back to the initials avatar instead of retrying), and `useMemo` to compute initials/color. + +**Accessibility**: The outer wrapper has `aria-label="Avatar for {fallback}"`. When an image is shown, its `alt` text is the same `ariaLabel`. The optional online indicator dot has its own `aria-label="Online"`. + +**Test coverage**: No test file today. + +--- + +## Badge + +`apps/web/src/components/ui/Badge.tsx` + +**Props** + +| Name | Type | Required | Default | +|---|---|---|---| +| `variant` | `'default' \| 'success' \| 'warning' \| 'danger'` | No | `'default'` | +| `children` | `React.ReactNode` | Yes | — | +| `className` | `string` | No | `undefined` | + +**Usage** + +```tsx +Active +``` + +**Type**: Presentational. A pure function component with no state, effects, or refs — just a `` with variant-based Tailwind classes. + +**Accessibility**: None implemented beyond it being a plain inline text element (no `role` or `aria-*` attributes). + +**Test coverage**: No test file today. + +--- + +## CopyButton + +`apps/web/src/components/ui/CopyButton.tsx` + +**Props** + +| Name | Type | Required | Default | +|---|---|---|---| +| `value` | `string` | Yes | — | +| `className` | `string` | No | `''` | + +**Usage** + +```tsx + +``` + +**Type**: Stateful. Uses `useState` to track a `copied` boolean. + +**Accessibility / copied feedback**: The button's `aria-label` and `title` swap between `"Copy address"` / `"Copy to clipboard"` and `"Copied address"` / `"Copied!"` based on the `copied` state. Clicking calls `navigator.clipboard.writeText(value)`; on success it sets `copied` to `true` and shows an animated checkmark icon (via `framer-motion`'s `AnimatePresence`, cross-fading from a Copy icon to a Check icon) in place of the copy icon. The `copied` state is reset back to `false` after **2000ms** (`setTimeout`), reverting to the copy icon/labels. While `copied` is `true`, clicking again is a no-op (`if (copied) return`). Clicks call `e.stopPropagation()` so the button can sit inside a clickable parent (e.g. a card) without triggering the parent's click handler. Clipboard failures are silently swallowed (empty `catch`). + +**Test coverage**: No test file today. + +--- + +## EmptyState + +`apps/web/src/components/ui/EmptyState.tsx` + +**Props** + +| Name | Type | Required | Default | +|---|---|---|---| +| `icon` | `string` | Yes | — | +| `title` | `string` | Yes | — | +| `description` | `string` | Yes | — | +| `action` | `{ label: string; onClick: () => void }` | No | `undefined` | + +**Usage** + +```tsx + +``` + +**Type**: Presentational. Pure function component, no state or effects. + +**Accessibility**: The icon `
` is marked `aria-hidden="true"` since it's decorative; the title/description are rendered as a semantic `

`/`

` pair. The optional action renders as a real `