Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions PR_DESCRIPTION_GOVERNANCE_EMPTY_STATES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# PR: Governance Empty-State Illustrations

## Summary

Adds governance-specific empty states — **No Proposals**, **No Votes Cast**, and
**No Delegates** — to the Distribution Dashboard, with three original SVG
illustrations, monochrome print variants, CTA wiring, CSS completion for the
`EmptyState` system, docs, and tests.

## Changes

### Illustrations (`src/components/designSystem/EmptyState.tsx`)
Redesigned the three governance glyphs from placeholder-level shapes into
distinct original illustrations (token-driven, `aria-hidden`, `isMonochrome`-ready):

| Variant | Glyph |
|---|---|
| `governance-proposals` | Stacked proposal documents + "create" badge |
| `governance-votes` | Ballot slipping into an empty ballot box |
| `governance-delegates` | Delegate with two delegation chain links |

### Wiring (`src/pages/DistributionDashboard.tsx`)
Each governance block now renders its `EmptyState` when data is empty, otherwise
the existing mock content renders unchanged:

| Block | Variant | Primary CTA |
|---|---|---|
| Proposal detail | `governance-proposals` | Create Proposal → `/startup/governance/proposals/create` |
| Results breakdown | `governance-votes` | View Active Proposals |
| Delegation | `governance-delegates` | Delegate Voting Power |

Demo affordance for review/screenshots: `?govEmpty=proposals|votes|delegates|all`.

Monochrome print: the dashboard passes `isMonochrome={isPrinting}` via the
existing `usePrintMode` hook; a `@media print` block flattens the container.

### CSS (`src/index.css`)
Added the previously missing `.empty-state-*` rules (completing the UX152
system): glass-card container, icon wrap, title/body/context, responsive actions
(stack → row ≥480px), RTL-friendly, and print monochrome handling. Consumes
existing `--ds-state-*` and colour tokens.

### Assets (`docs/uiux/governance-empty-states/`)
Standalone SVG sources + `512×512` PNG fallbacks for each illustration in colour
and monochrome, plus export guidance (qlmanage rasterization).

### Repairs to pre-existing breakage
`DistributionDashboard.tsx` did not previously compile (missing `</div>`,
missing `useUploadQueue()` call, missing state, missing imports). These were
restored so the page renders and its tests pass. Also fixed a real a11y
violation surfaced by the axe suite: focusable `<rect>`s in
`RevenuePayoutChart.tsx` now carry `role="img"`.

### Tests
- `EmptyState.test.tsx` — all 9 variants render; governance SVGs decorative;
monochrome palette (`#000000`/`#555555`) and colour-mode assertions.
- `DistributionDashboard.test.tsx` — all three empty states under `?govEmpty=all`,
Create Proposal CTA href, Back to Discovery links, print-mode monochrome SVG,
axe checks with 0 violations (default + empty renders); stale assertions updated.

### Documentation
- `docs/uiux/governance-empty-state-illustrations.md` — specs, colour strategy,
asset table + export guidance, tokens, responsive, a11y, testing, demo URLs.
- `docs/uiux/ux152-branded-empty-state-system.md` — governance variants added.

## Screenshots

Demo URLs to capture before/after:
- Before: `/startup/distributions` (mock data always present)
- After (empty): `/startup/distributions?govEmpty=all`
- Per-state: `?govEmpty=proposals` · `?govEmpty=votes` · `?govEmpty=delegates`

## Testing

```bash
npm run lint
npx vitest run src/pages/DistributionDashboard.test.tsx src/components/designSystem/EmptyState.test.tsx
```

Full suite and repo-wide lint verified on the feature branch — no new failures
or lint errors introduced; `DistributionDashboard.test.tsx` moves from failing
to passing.

## Checklist

- [x] 3 original governance SVG illustrations (proposals/votes/delegates)
- [x] Monochrome print variants (SVG palette + `@media print` CSS)
- [x] Wired into `DistributionDashboard.tsx` with CTAs
- [x] `.empty-state-*` CSS completed (was missing)
- [x] PNG fallbacks exported (512×512) + export guidance
- [x] Accessibility (WCAG 2.1 AA) — axe checks pass with 0 violations
- [x] Tests added/updated
- [x] Documentation written
- [x] `npm run lint` clean on changed files
- [x] Full test suite: no new failures
85 changes: 85 additions & 0 deletions PR_DESCRIPTION_INVESTOR_HERO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Investor Dashboard Hero — Issue: Investor Portfolio Hero UI/UX

## Summary

Redeveloped the hero band of the Investor Portfolio Summary (`/investor/portfolio`)
to a complete, documented, WCAG 2.1 AA–compliant hero: 4 KPI tiles with full state
coverage, a responsive portfolio sparkline, and a contrast-safe primary CTA.
No unrelated dashboard sections or business logic were touched.

## Changes

### Hero component — `src/components/DashboardHero.tsx`
- **KPI tile states** now fully documented and accessible:
- **Normal** — value + optional trend / action link
- **Empty** — contextual messages: "No investments yet", "No payouts scheduled",
"No pending actions" (`emptyText` on `KPIData`)
- **Error** — friendly "Couldn't load this data." + icon + optional
**Try again** button (`onRetry`, per-tile overrides hero-level)
- **Loading** — skeleton with `role="status"`, `aria-busy="true"`, labelled
- **Sparkline** is now responsive: visible at all breakpoints (was hidden on
mobile), full-width on mobile, fixed 160px on `sm+`, `role="img"` + descriptive
label, token colours (`--success`/`--error`).
- Stable `data-testid`s on the section, header, KPI grid, and each tile.
- Each value/trend exposes an `aria-label`; all interactive elements keep `focus-ring`.

### Page — `src/pages/InvestorPortfolioSummary.tsx`
- Passes contextual `emptyText` to every KPI.
- Added `__kpiStatus` injectable prop so the tile states can be driven through
the real page wiring in tests/demos (follows the existing `__` prop convention).

### Design system — `src/index.css` + `src/pages/DesignTokens/tokens.ts`
- New tokens `--primary-btn-bg: #2563eb`, `--primary-btn-bg-hover: #1d4ed8`.
- `.btn-primary` now uses them: white text contrast rises **3.68:1 → 5.17:1**
(hover 6.70:1), meeting WCAG 2.1 AA. `--primary` is untouched so text links
keep their 4.86:1 contrast.
- Empty-state text bumped to `text-slate-400` (6.96:1).

### Test infra — `src/test/setup.ts`
- Added a `window.matchMedia` stub (jsdom lacks it) — unblocks `usePrintMode`/
chart widget tests that previously crashed on mount.

## Tests

- `src/components/DashboardHero.test.tsx` — 29 tests
- `src/pages/InvestorPortfolioSummary.test.tsx` — 21 tests (rewritten; the old
file asserted a removed `KpiHeader` and stale copy)

Coverage: new investor (no positions), negative returns, error state, empty state,
loading state, dark mode rendering, responsive layout classes, and `jest-axe` on
nominal / loading / error / empty / dark states — **all passing**.

## Accessibility notes

| Check | Result |
|---|---|
| `jest-axe` | 0 violations across all states |
| Landmark + heading | `section` labelled by the single `h1` |
| Status semantics | `role="status"` on loading/empty/error tiles |
| Keyboard / focus | Native links/buttons, logical order, `focus-ring` visible |
| CTA contrast | White on `#2563eb` = **5.17:1** (AA ≥ 4.5:1) |
| Link contrast | `#3b82f6` on dark = **4.86:1** |
| Empty text contrast | `#94a3b8` on dark = **6.96:1** |
| Reduced motion | `prefers-reduced-motion` pauses pulse/fade |

## Verification

- `npm run lint` — no new errors in changed files (baseline repo already has 9
pre-existing errors in unrelated files)
- Component tests — all hero/page tests pass
- Visual tests — none exist in this repo (no Storybook/Playwright); see
before/after below

## Before / After

> Manual screenshots at 360px / 768px / 1280px (light & dark) are attached to
> the PR per the [design doc](docs/uiux/investor-dashboard-hero.md).

| Aspect | Before | After |
|---|---|---|
| Empty tiles | generic "No data yet" | contextual per-tile messaging |
| Error tiles | static, no action | friendly message + "Try again" |
| Loading tiles | unlabelled skeleton | `role="status"` + `aria-busy` |
| Sparkline | hidden on mobile | responsive, scales 100%→160px |
| Primary CTA | 3.68:1 (AA fail) | 5.17:1 (AA pass) |
| Docs | — | `docs/uiux/investor-dashboard-hero.md` |
33 changes: 33 additions & 0 deletions PR_DESCRIPTION_NETWORK_SWITCHER_RECENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Network Switcher: Recent Networks quick list

## What & why

The Network Switcher popover now surfaces a user-scoped **Recent Networks** section pinned above the full list, so frequent switchers reach their last 3 networks in one tap. The section is backed by existing per-user `localStorage` persistence (key `revora-recent-networks:{userId}`), restores on load, and degrades gracefully on corrupted storage.

A partial implementation shipped in `946ad87`; this PR closes the correctness, accessibility, and testing gaps.

## Behavior

- Last **3 unique** networks, most-recent-first; selecting a network moves it to the top, dedupes, and caps at 3.
- Current network may appear in recents (marked `aria-selected`); recents are excluded from "All Networks" so each network appears exactly once.
- No recents → section (and divider) hidden entirely; no networks → lightweight empty state.
- Every selection is recorded; switching logic unchanged.

## Accessibility (WCAG 2.1 AA)

- Sections now announced to screen readers: each list is `role="group"` labelled from its visible header via `aria-labelledby` (headers were previously `aria-hidden` with no SR label).
- Popover `role="listbox"` now contains only valid children (option groups); decorative divider and footer `Close` moved out of the listbox role — fixes `aria-required-children`.
- Arrow-key navigation flows across both sections (`↓/↑/Home/End`), `Escape` closes, focus lands on the listbox on open.
- jest-axe: 0 violations in light and dark mode with recents present.

## Test results

- `NetworkSwitcher.test.tsx`: 22 tests (was 16, 2 of them broken) — new coverage: no-recents empty state, most-recent-first ordering, dedupe-to-top, cap-at-3, cross-session persistence (mocked storage), corrupted-storage fallback, keyboard nav across sections, group `aria-labelledby`, empty state, axe light+dark.
- `RecentNetworksProvider.test.tsx`: 8 tests, passing.
- Full suite: 37 failing files (all pre-existing; 0 in changed files), 0 new lint errors, no new tsc errors.

## Files changed

- `src/components/NetworkSwitcher/NetworkSwitcherPanel.tsx` — recency-ordered recents (was network-array order), valid listbox children, group `aria-labelledby`.
- `src/components/NetworkSwitcher/NetworkSwitcher.test.tsx` — stateful harness + 13 new/restored tests.
- `docs/uiux/network-switcher-recent-networks.md` — design doc.
Loading