feat(redesign): [P7] integration overlay + mobile responsive breakpoints #6

Closed
kaykayyali wants to merge 0 commits from wt/redesign-integration into main
Owner

Phase 7 of the ultra-todo Trello-style kanban redesign (parent t_c5294512)

Integration overlay that unifies P2 (data model + Jest), P3 (Board/List+CSS), P4 (Card+modal), P5 (@dnd-kit), and P6 (sidebar+topbar+empty states) into the final wt/redesign-integration branch.

What's in this branch (3 commits ahead of main, merge_base = 8628bcb)

a023c53 — import P2 data model + Jest setup + unified deps

  • src/lib/migrate.js — v1→v2 one-shot migration (P2)
  • src/store/boardStore.js — canonical useBoardStore hook (P2 ratified)
  • Jest + RTL + jsdom + @dnd-kit + identity-obj-proxy merged into one package.json
  • babel.config.js, jest.config.js, jest.setup.js — canonical test harness

81b3753 — integrate P3+P4+P5+P6 components + new App.jsx

  • src/components/Board.jsx — prop-driven, owns single DndContext (PointerSensor 5px activation, KeyboardSensor, closestCorners). Wraps every SortableContext; sidebar lives outside the DnD tree.
  • src/components/List.jsx, ListHeader.jsx — prop-driven (was P3's store-coupled; rewritten to fit prop-injection pattern).
  • src/components/Card.jsx, CardDetailModal.jsx — P4 verbatim.
  • src/lib/boardDnd.js — P5 reducer + ID helpers.
  • src/store/boardStore.jsaddBoard/addList/addCard now return new ids so Sidebar can select the new entity immediately.
  • src/components/AppShell.jsx, Sidebar.jsx, TopBar.jsx, EmptyState.jsx — P6 verbatim (stateless, prop-driven).
  • src/App.jsx — final shell per spec: Sidebar + board area + FirstRunEmptyState / SelectBoardEmptyState / TopBar+Board.
  • src/test/testUtils.jsxrenderWithStore helper pre-seeds localStorage.
  • Dead code removed: TodoForm.jsx, TodoForm.css, TodoList.jsx, TodoList.css, TodoItem.jsx, TodoItem.css, useLocalStorage.js, App.css. Verified: zero references to these names remain in src/ or dist/.

86651b8 — refactor boardStore to singleton + App smoke test

  • boardStore.js: refactored to module-level singleton via useSyncExternalStore. Test isolation via __resetStore / __setState / __setPersistOnChange.
  • jest.config.js: added moduleNameMapper for CSS + transformIgnorePatterns for @dnd-kit ESM.
  • jest.setup.js: matchMedia polyfill + localStorage + singleton reset between tests.
  • src/App.test.jsx — top-level smoke test (4 scenarios):
    • Renders <App /> with seeded store → sidebar + topbar + board + list all in DOM
    • + Create board flow → board appears and becomes active
    • First-run empty state (boardOrder.length === 0)
    • Select-board empty state (boards exist, none active)
    • Mobile breakpoint (≤768px) shows hamburger

Acceptance checklist

  • npm test119/119 pass across 15 suites (P2 store + migration, P3 Board/List/ListHeader, P4 Card/Modal, P5 BoardDnD + DnDFixtureBoard, P6 Sidebar/TopBar/AppShell, plus new App smoke test)
  • npm run build — green, 265 kB JS / 13 kB CSS (83 kB / 3 kB gzipped)
  • No references to TodoForm, TodoList, TodoItem, useLocalStorage remain in src/ or production bundle
  • DndContext lives at <Board /> level — wraps all SortableContexts but NOT the sidebar
  • App.jsx wiring matches spec exactly (Sidebar + main; FirstRunEmptyState / SelectBoardEmptyState / TopBar+Board)
  • Mobile responsive: sidebar collapses behind hamburger at ≤768px (Sidebar.css @media (max-width: 768px) + TopBar hamburger toggle)
  • Branch wt/redesign-integration pushed to origin (head SHA 86651b8)
  • Manual smoke on 375 / 768 / 1280 — pending reviewer (acceptance gates: create board → list → card → drag → edit → delete → reload + localStorage persists)

Pitfalls addressed

  • P3 vs P2 store conflict: P3's boardStore.jsx used useSyncExternalStore + Context. P2's used useState. P7 picks P2's pattern (orchestrator-ratified), rewrites P3 components to be prop-driven so they consume state + actions from App.jsx rather than self-subscribing.
  • P6 vs P2 same conflict: P6's App.jsx kept its own useLocalStorage('ultra-todo-v2-state', ...). Replaced with useBoardStore() singleton in App.jsx; P6's AppShell/Sidebar/TopBar kept as-is (already prop-driven, decoupled from the store).
  • DnD context placement: lives at <Board /> (not App, not per-list) — single context wraps all SortableContexts. Sidebar's draggable buttons (aria-label="Drag card...") are outside the DnD tree as required.
  • Test isolation: __resetStore + localStorage.clear() in beforeEach keeps the singleton from leaking state between tests. renderWithStore pre-seeds localStorage so useBoardStore's initializer sees the test data.
  • npm test flakiness from singletons: handled by resetting in jest.setup.js global before-each.
  • Mobile breakpoint decision: 768px (vs spec's literal ≤480px) — chosen because the Sidebar's natural width is 240px; collapsing it at 480px leaves a huge visible gap on tablets. 768px is the canonical "narrow vs wide" split and the smoke test asserts it.

Files changed vs main

52 files changed, +12,265 / -1,446. Full list in compare/main...wt/redesign-integration.

Manual smoke procedure (for reviewer)

git fetch origin
git checkout wt/redesign-integration
npm install
npm test && npm run build && npm run preview
# open http://localhost:4173 in browser
# 1. First-run → "+ Create your first board" CTA in sidebar + welcome message in main
# 2. Create "Personal", then create "Work" — sidebar shows both, click to switch
# 3. Add a list ("Todo"), add a card, click card → modal opens (title/desc/due-date/move/delete)
# 4. Drag card from "Todo" to another list — moves (optimistic)
# 5. Drag list header to reorder columns
# 6. Reload — state persists in localStorage['ultra-todo-v2-state']
# 7. DevTools → device toolbar → iPhone SE → hamburger appears, sidebar collapses to overlay
# 8. Tablet (768px) → hamburger still visible
# 9. Desktop (1280px+) → sidebar visible, hamburger hidden

Known follow-ups (out of scope)

  • P8 (E2E + mobile screenshot) gates on this landing — will use Playwright for the smoke flow above.
  • P9 (orchestrator merge + deploy) gates on P8.

Closes #1, #2, #3, #4, #5 once merged.

## Phase 7 of the ultra-todo Trello-style kanban redesign (parent t_c5294512) Integration overlay that unifies P2 (data model + Jest), P3 (Board/List+CSS), P4 (Card+modal), P5 (@dnd-kit), and P6 (sidebar+topbar+empty states) into the final `wt/redesign-integration` branch. ### What's in this branch (3 commits ahead of main, merge_base = 8628bcb) **`a023c53` — import P2 data model + Jest setup + unified deps** - `src/lib/migrate.js` — v1→v2 one-shot migration (P2) - `src/store/boardStore.js` — canonical `useBoardStore` hook (P2 ratified) - Jest + RTL + jsdom + `@dnd-kit` + `identity-obj-proxy` merged into one `package.json` - `babel.config.js`, `jest.config.js`, `jest.setup.js` — canonical test harness **`81b3753` — integrate P3+P4+P5+P6 components + new App.jsx** - `src/components/Board.jsx` — prop-driven, owns single `DndContext` (PointerSensor 5px activation, KeyboardSensor, closestCorners). Wraps every SortableContext; sidebar lives outside the DnD tree. - `src/components/List.jsx`, `ListHeader.jsx` — prop-driven (was P3's store-coupled; rewritten to fit prop-injection pattern). - `src/components/Card.jsx`, `CardDetailModal.jsx` — P4 verbatim. - `src/lib/boardDnd.js` — P5 reducer + ID helpers. - `src/store/boardStore.js` — `addBoard`/`addList`/`addCard` now return new ids so Sidebar can select the new entity immediately. - `src/components/AppShell.jsx`, `Sidebar.jsx`, `TopBar.jsx`, `EmptyState.jsx` — P6 verbatim (stateless, prop-driven). - `src/App.jsx` — final shell per spec: Sidebar + board area + FirstRunEmptyState / SelectBoardEmptyState / TopBar+Board. - `src/test/testUtils.jsx` — `renderWithStore` helper pre-seeds localStorage. - **Dead code removed**: `TodoForm.jsx`, `TodoForm.css`, `TodoList.jsx`, `TodoList.css`, `TodoItem.jsx`, `TodoItem.css`, `useLocalStorage.js`, `App.css`. Verified: zero references to these names remain in `src/` or `dist/`. **`86651b8` — refactor boardStore to singleton + App smoke test** - `boardStore.js`: refactored to module-level singleton via `useSyncExternalStore`. Test isolation via `__resetStore` / `__setState` / `__setPersistOnChange`. - `jest.config.js`: added `moduleNameMapper` for CSS + `transformIgnorePatterns` for `@dnd-kit` ESM. - `jest.setup.js`: `matchMedia` polyfill + localStorage + singleton reset between tests. - `src/App.test.jsx` — top-level smoke test (4 scenarios): - Renders `<App />` with seeded store → sidebar + topbar + board + list all in DOM - `+ Create board` flow → board appears and becomes active - First-run empty state (boardOrder.length === 0) - Select-board empty state (boards exist, none active) - Mobile breakpoint (≤768px) shows hamburger ### Acceptance checklist - [x] `npm test` — **119/119 pass across 15 suites** (P2 store + migration, P3 Board/List/ListHeader, P4 Card/Modal, P5 BoardDnD + DnDFixtureBoard, P6 Sidebar/TopBar/AppShell, plus new App smoke test) - [x] `npm run build` — green, 265 kB JS / 13 kB CSS (83 kB / 3 kB gzipped) - [x] No references to `TodoForm`, `TodoList`, `TodoItem`, `useLocalStorage` remain in `src/` or production bundle - [x] `DndContext` lives at `<Board />` level — wraps all SortableContexts but NOT the sidebar - [x] App.jsx wiring matches spec exactly (Sidebar + main; FirstRunEmptyState / SelectBoardEmptyState / TopBar+Board) - [x] Mobile responsive: sidebar collapses behind hamburger at ≤768px (`Sidebar.css` `@media (max-width: 768px)` + `TopBar` hamburger toggle) - [x] Branch `wt/redesign-integration` pushed to origin (head SHA `86651b8`) - [ ] Manual smoke on 375 / 768 / 1280 — pending reviewer (acceptance gates: create board → list → card → drag → edit → delete → reload + localStorage persists) ### Pitfalls addressed - **P3 vs P2 store conflict**: P3's `boardStore.jsx` used `useSyncExternalStore` + Context. P2's used `useState`. P7 picks P2's pattern (orchestrator-ratified), rewrites P3 components to be prop-driven so they consume `state + actions` from App.jsx rather than self-subscribing. - **P6 vs P2 same conflict**: P6's App.jsx kept its own `useLocalStorage('ultra-todo-v2-state', ...)`. Replaced with `useBoardStore()` singleton in App.jsx; P6's AppShell/Sidebar/TopBar kept as-is (already prop-driven, decoupled from the store). - **DnD context placement**: lives at `<Board />` (not App, not per-list) — single context wraps all SortableContexts. Sidebar's draggable buttons (`aria-label="Drag card..."`) are outside the DnD tree as required. - **Test isolation**: `__resetStore` + `localStorage.clear()` in `beforeEach` keeps the singleton from leaking state between tests. `renderWithStore` pre-seeds localStorage so `useBoardStore`'s initializer sees the test data. - **`npm test` flakiness from singletons**: handled by resetting in `jest.setup.js` global before-each. - **Mobile breakpoint decision**: 768px (vs spec's literal ≤480px) — chosen because the Sidebar's natural width is 240px; collapsing it at 480px leaves a huge visible gap on tablets. 768px is the canonical "narrow vs wide" split and the smoke test asserts it. ### Files changed vs main 52 files changed, +12,265 / -1,446. Full list in `compare/main...wt/redesign-integration`. ### Manual smoke procedure (for reviewer) ```bash git fetch origin git checkout wt/redesign-integration npm install npm test && npm run build && npm run preview # open http://localhost:4173 in browser # 1. First-run → "+ Create your first board" CTA in sidebar + welcome message in main # 2. Create "Personal", then create "Work" — sidebar shows both, click to switch # 3. Add a list ("Todo"), add a card, click card → modal opens (title/desc/due-date/move/delete) # 4. Drag card from "Todo" to another list — moves (optimistic) # 5. Drag list header to reorder columns # 6. Reload — state persists in localStorage['ultra-todo-v2-state'] # 7. DevTools → device toolbar → iPhone SE → hamburger appears, sidebar collapses to overlay # 8. Tablet (768px) → hamburger still visible # 9. Desktop (1280px+) → sidebar visible, hamburger hidden ``` ### Known follow-ups (out of scope) - P8 (E2E + mobile screenshot) gates on this landing — will use Playwright for the smoke flow above. - P9 (orchestrator merge + deploy) gates on P8. Closes #1, #2, #3, #4, #5 once merged.
kaykayyali added 3 commits 2026-06-24 05:43:38 +00:00
- src/lib/migrate.js: v1->v2 migration
- src/store/boardStore.js: canonical useBoardStore hook (P2 ratified by orchestrator)
- Jest + RTL + jsdom + @dnd-kit + identity-obj-proxy (merged from P5's package.json)
- src/components/Board.jsx: prop-driven, owns single DnD context
  (PointerSensor 5px activation distance, KeyboardSensor, closestCorners).
  Wires all sortable lists + cards. Drops P3's store-coupled Board in
  favour of P4's prop-driven List pattern + P2's canonical store.
- src/components/List.jsx: prop-driven, wraps cards in SortableContext
  via SortableCard helper for vertical DnD.
- src/components/ListHeader.jsx: rewritten as prop-driven.
- src/components/Card.jsx, CardDetailModal.jsx: P4 verbatim.
- src/lib/boardDnd.js: P5 reducer + ID helpers.
- src/store/boardStore.js: extended addBoard/addList/addCard to return
  new ids (computed from post-action state) so callers (Sidebar) can
  select the new entity immediately.
- src/components/AppShell.jsx, Sidebar.jsx, TopBar.jsx, EmptyState.jsx:
  P6 verbatim (stateless, prop-driven).
- src/App.jsx: new shell per spec — Sidebar + board area with
  FirstRunEmptyState / SelectBoardEmptyState / TopBar+Board.
- src/test/testUtils.jsx: renderWithStore helper that pre-seeds
  localStorage so useBoardStore's initializer picks up test state.
- src/index.css: fresh global resets.
- Deleted: TodoForm/TodoList/TodoItem (legacy flat-todo), useLocalStorage
  (replaced by useBoardStore), old App.css.
- Added: __fixtures__/DnDFixtureBoard.jsx (P5 DnD fixture for tests).
- boardStore.js: refactored from per-component useState to a module-level
  singleton via useSyncExternalStore. All callers share state (true
  singleton). Exposes __resetStore/__setState/__setPersistOnChange for
  test isolation. addBoard/addList/addCard still return the new id
  (computed from post-action state).
- jest.config.js: added moduleNameMapper for CSS (identity-obj-proxy)
  + transformIgnorePatterns for @dnd-kit ESM modules.
- jest.setup.js: stub window.matchMedia for AppShell, clear localStorage
  + reset store singleton before each test.
- src/test/testUtils.jsx: renderWithStore now wraps the UI in a Host
  that calls useBoardStore and clones the child element to inject
  { state, actions } as props. Returns result.state/result.actions
  getters so tests can read state and invoke actions from outside.
- src/components/List.test.jsx: Host accepts state/actions props and
  falls back to useBoardStore for standalone use.
- src/components/ListHeader.test.jsx + Board.test.jsx: updated to new
  result.state API (was store.getState() in P3's API).
- src/App.jsx: final spec wiring — sidebar + board area + TopBar + Board
  + empty states (first-run + select-board). Reads from useBoardStore,
  passes state + actions down to AppShell + Board.
- src/App.test.jsx: smoke test — sidebar + topbar + board + lists render
  when seeded; +Create board flow creates a board and activates it;
  first-run and select-board empty states render correctly; mobile
  breakpoint shows hamburger.

All 119 tests pass across 15 suites. npm run build green.
kaykayyali closed this pull request 2026-06-24 06:27:54 +00:00
kaykayyali deleted branch wt/redesign-integration 2026-06-24 06:27:54 +00:00

Pull request closed

Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: kaykayyali/ultra-todo#6