feat(sf-fe-a3): UI primitives — HeadlineBar + Badge + Drawer #341

Merged
lekss361 merged 1 commit from feat/sf-fe-a3-primitives into main 2026-05-17 22:06:53 +00:00
Owner

Summary

SF Frontend Migration Wave 1 parallel со A1. 3 reusable UI primitives для Wave 2-4.

Files (6 NEW)

  • frontend/src/components/ui/HeadlineBar.tsx — dark verdict strip; props title, subtitle?, rightSlot?. Server component. CSS tokens (--bg-headline, --fg-on-dark, --fg-on-dark-muted)
  • frontend/src/components/ui/Badge.tsx — inline-flex semantic badge. Variants: success | warning | danger | info | neutral. Sizes: sm | md. Icon slot (Lucide).
  • frontend/src/components/ui/Drawer.tsx — accessible slide-in panel. side="right" (360px default) или side="bottom" (mobile bottom-sheet). Click-outside, Escape, focus trap, body scroll lock, 180ms transition. "use client".
  • 3 test files (4 + 6 + 5 = 15 unit tests)

Constraints соблюдены

  • TS strict
  • CSS tokens, zero inline hex
  • Lucide icons, no emoji
  • Tailwind utilities
  • pre-commit pass

Open questions (non-blocking, можно отдельно)

  1. CSS tokens (--bg-headline, --success-soft, etc.) must be defined в globals.css — UI Brief spec'ит их, но проверить что они в проекте. Если нет — fallback transparent.
  2. Test runner (jest + @testing-library/react) ещё не в package.json — tests созданы, but won't run пока. Setup — отдельный PR.

Next

Components готовы для use в A2 (EntryMap+Drawer для preview), A4 (HeadlineBar для analysis page header), Sections (Badge для статусов "Свежий", "Низкое покрытие" etc).

Part of plan vault code/patterns/SiteFinder_Frontend_Migration_Plan_May18.md §A3.

## Summary SF Frontend Migration Wave 1 parallel со A1. 3 reusable UI primitives для Wave 2-4. ## Files (6 NEW) - `frontend/src/components/ui/HeadlineBar.tsx` — dark verdict strip; props `title`, `subtitle?`, `rightSlot?`. Server component. CSS tokens (`--bg-headline`, `--fg-on-dark`, `--fg-on-dark-muted`) - `frontend/src/components/ui/Badge.tsx` — inline-flex semantic badge. Variants: `success | warning | danger | info | neutral`. Sizes: `sm | md`. Icon slot (Lucide). - `frontend/src/components/ui/Drawer.tsx` — accessible slide-in panel. `side="right"` (360px default) или `side="bottom"` (mobile bottom-sheet). Click-outside, Escape, focus trap, body scroll lock, 180ms transition. `"use client"`. - 3 test files (4 + 6 + 5 = 15 unit tests) ## Constraints соблюдены - ✅ TS strict - ✅ CSS tokens, zero inline hex - ✅ Lucide icons, no emoji - ✅ Tailwind utilities - ✅ pre-commit pass ## Open questions (non-blocking, можно отдельно) 1. CSS tokens (`--bg-headline`, `--success-soft`, etc.) must be defined в `globals.css` — UI Brief spec'ит их, но проверить что они в проекте. Если нет — fallback transparent. 2. Test runner (jest + @testing-library/react) ещё не в `package.json` — tests созданы, but won't run пока. Setup — отдельный PR. ## Next Components готовы для use в A2 (EntryMap+Drawer для preview), A4 (HeadlineBar для analysis page header), Sections (Badge для статусов "Свежий", "Низкое покрытие" etc). Part of plan vault `code/patterns/SiteFinder_Frontend_Migration_Plan_May18.md` §A3.
lekss361 added 1 commit 2026-05-17 21:59:19 +00:00
Author
Owner

Deep Code Review — APPROVE

Drawer a11y completeness: focus trap (Tab/Shift+Tab wrap), Escape, click-outside via target === currentTarget, body scroll lock with prev-value restore, focus save/restore via previousFocusRef, requestAnimationFrame + cancelAnimationFrame cleanup. All useEffect hooks have proper cleanups — no leaks. "use client" present. Minor: role="dialog" aria-modal="true" sit on the backdrop, not the panel (unusual but harmless); aria-labelledby is left to callers — acceptable for a primitive.

HeadlineBar: server component (no "use client"), pure prop rendering. role="banner" is a small nitpick (site-level role) but not blocking.

Badge: 5×2 lookup objects, no any, no dangerouslySetInnerHTML. icon?: ReactNode is broader than typed LucideIcon but more flexible — fine for a primitive slot.

CSS tokens (MINOR, follow-up): confirmed no globals.css in frontend/src/** and no :root { --bg-headline ... } block anywhere except frontend/public/*.html static prototypes. Until a follow-up PR adds the tokens to a global stylesheet imported from app/layout.tsx, Drawer panels and HeadlineBar will render with transparent fallback (background: var(--bg-headline) → unset). This isn't a regression — existing Section.tsx/KpiCard.tsx hardcode inline hex, so the tokens pattern was never wired. PR is correctly forward-looking. MarketTab.tsx uses two-arg var(--bg-headline, #0F172A) fallback — worth considering for this PR too, but not blocking.

Tests (MINOR, follow-up): 15 tests in valid TS strict (would pass tsc --noEmit). jest/@testing-library/react not in package.json, no test script — tests unrunnable until setup PR. Acceptable per author's open question.

Cross-file impact: zero callers yet, no signature/contract regressions. Safe to merge.

Merging.

## Deep Code Review — APPROVE **Drawer a11y completeness**: focus trap (Tab/Shift+Tab wrap), Escape, click-outside via `target === currentTarget`, body scroll lock with prev-value restore, focus save/restore via `previousFocusRef`, `requestAnimationFrame` + `cancelAnimationFrame` cleanup. All `useEffect` hooks have proper cleanups — no leaks. `"use client"` present. Minor: `role="dialog" aria-modal="true"` sit on the backdrop, not the panel (unusual but harmless); `aria-labelledby` is left to callers — acceptable for a primitive. **HeadlineBar**: server component (no `"use client"`), pure prop rendering. `role="banner"` is a small nitpick (site-level role) but not blocking. **Badge**: 5×2 lookup objects, no `any`, no `dangerouslySetInnerHTML`. `icon?: ReactNode` is broader than typed `LucideIcon` but more flexible — fine for a primitive slot. **CSS tokens (MINOR, follow-up)**: confirmed no `globals.css` in `frontend/src/**` and no `:root { --bg-headline ... }` block anywhere except `frontend/public/*.html` static prototypes. Until a follow-up PR adds the tokens to a global stylesheet imported from `app/layout.tsx`, Drawer panels and HeadlineBar will render with transparent fallback (`background: var(--bg-headline)` → unset). This isn't a regression — existing `Section.tsx`/`KpiCard.tsx` hardcode inline hex, so the tokens pattern was never wired. PR is correctly forward-looking. `MarketTab.tsx` uses two-arg `var(--bg-headline, #0F172A)` fallback — worth considering for this PR too, but not blocking. **Tests (MINOR, follow-up)**: 15 tests in valid TS strict (would pass `tsc --noEmit`). `jest`/`@testing-library/react` not in `package.json`, no `test` script — tests unrunnable until setup PR. Acceptable per author's open question. **Cross-file impact**: zero callers yet, no signature/contract regressions. Safe to merge. Merging.
lekss361 merged commit 2548f71f40 into main 2026-05-17 22:06:53 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#341
No description provided.