[P6] Multi-board sidebar + switcher + first-run flow #2

Closed
kaykayyali wants to merge 3 commits from wt/redesign-boards into main
Owner

Phase 6 of the ultra-todo Trello-style redesign

Closes [P6] in the parent task graph.

What's in this PR

  • src/components/Sidebar.jsx + Sidebar.css: 240px dark Trello sidebar (#1d2125) with boards list, hover menu (Rename / Delete with confirm), inline rename input, mobile overlay with backdrop, first-run variant showing "+ Create your first board".
  • src/components/BoardListItem: small subcomponent embedded in Sidebar.jsx for clarity. Renders as a <button> with aria-current for keyboard nav.
  • src/components/TopBar.jsx + TopBar.css: shows active board name + mobile-only hamburger. Background rgba(255,255,255,.15) over the board gradient.
  • src/components/AppShell.jsx + AppShell.css: composes Sidebar + TopBar + content slot. Owns mobile-breakpoint detection via window.matchMedia. Pass-through for state + callbacks (decoupled from useBoardStore).
  • src/components/EmptyState.jsx + EmptyState.css: centered welcome (first-run) and "select or create a board" (no-board) variants.
  • src/App.jsx: holds multi-board state in useLocalStorage('ultra-todo-v2-state', ...) matching the spike data-model spec. Provides addBoard/renameBoard/deleteBoard/setActiveBoard to AppShell. State lives in App.jsx locally — P2's useBoardStore will replace this in P7 integration overlay.
  • src/App.css: removed .app { max-width: 500px; margin: 0 auto; } per spike.

Test framework setup (mirrors P2)

  • jest.config.cjs (jsdom + babel-jest + CSS stub mapper)
  • babel.config.cjs (@babel/preset-env + preset-react automatic runtime)
  • jest.setup.cjs (loads @testing-library/jest-dom + jsdom matchMedia polyfill)
  • __mocks__/styleMock.js (so JSX can import './Foo.css')
  • npm test script in package.json

Tests

25 tests across 3 files, all passing:

File Tests
src/components/Sidebar.test.jsx 17
src/components/TopBar.test.jsx 6
src/components/AppShell.test.jsx 2

TDD: every test was written first, watched fail (Sidebar not found), then implementation made them pass. Mobile responsive paths tested via showHamburger prop and isMobileOpen overlay toggle.

Pitfalls addressed (from the task body)

  • Sidebar lives OUTSIDE any DnD context — DnD remains in Board (P3/P5)
  • Delete-active-board fallback: picks first remaining board, or null if none
  • Hamburger overlay closes on board click AND backdrop click
  • All sidebar rows are <button> (free keyboard nav, ARIA-correct)

Acceptance

  • npm test passes (25/25)
  • npm run build succeeds
  • Manual smoke (create 2 boards, switch, rename, delete, verify localStorage persistence, first-run via localStorage.clear())
  • Mobile: hamburger toggles overlay; closes on board click or backdrop

Known merge collisions to resolve in P7

  • src/App.css: P3 also strips the max-width: 500px. Same change, identical line removal — git auto-resolves.
  • src/App.jsx: P3 will render <Board /> conditionally inside the legacy shell. Mine replaces the body with <AppShell />. P7 picks whichever works with the unified useBoardStore and the final layout (probably: App.jsx calls useBoardStore(), hands state to AppShell, AppShell renders <Board /> as children).
  • Test framework config: P2 may install Jest with different versions. P7 merges both package.json and resolves.
  • package-lock.json: large diff (8198 lines), all from npm install. Acceptable for first PR.

Manual smoke procedure

git fetch origin
git checkout wt/redesign-boards
npm install
npm run build && npm run preview
# open http://localhost:4173 in browser
# 1. First-run: see "+ Create your first board"
# 2. Click button, type "Personal", Create
# 3. Sidebar shows Personal as active; main area shows empty board
# 4. Click "+ Create board", add "Work"
# 5. Click Personal — switches active
# 6. Hover Work, click ⋯, Rename → "Office", Enter
# 7. Click ⋯ on Office, Delete, confirm → falls back to Personal
# 8. DevTools → Application → Local Storage → http://localhost:4173
#    confirm 'ultra-todo-v2-state' has schemaVersion:2, boards, boardOrder, activeBoardId
# 9. Reload — state persists
# 10. Mobile: DevTools → device toolbar → iPhone SE
#     - hamburger appears
#     - sidebar collapses; tap hamburger to open overlay
#     - tap a board or backdrop to close
## Phase 6 of the ultra-todo Trello-style redesign Closes [P6] in the parent task graph. ### What's in this PR - **`src/components/Sidebar.jsx` + `Sidebar.css`**: 240px dark Trello sidebar (`#1d2125`) with boards list, hover `⋯` menu (Rename / Delete with confirm), inline rename input, mobile overlay with backdrop, first-run variant showing "+ Create your first board". - **`src/components/BoardListItem`**: small subcomponent embedded in Sidebar.jsx for clarity. Renders as a `<button>` with `aria-current` for keyboard nav. - **`src/components/TopBar.jsx` + `TopBar.css`**: shows active board name + mobile-only hamburger. Background `rgba(255,255,255,.15)` over the board gradient. - **`src/components/AppShell.jsx` + `AppShell.css`**: composes Sidebar + TopBar + content slot. Owns mobile-breakpoint detection via `window.matchMedia`. Pass-through for state + callbacks (decoupled from `useBoardStore`). - **`src/components/EmptyState.jsx` + `EmptyState.css`**: centered welcome (first-run) and "select or create a board" (no-board) variants. - **`src/App.jsx`**: holds multi-board state in `useLocalStorage('ultra-todo-v2-state', ...)` matching the spike data-model spec. Provides `addBoard`/`renameBoard`/`deleteBoard`/`setActiveBoard` to AppShell. **State lives in App.jsx locally — P2's `useBoardStore` will replace this in P7 integration overlay.** - **`src/App.css`**: removed `.app { max-width: 500px; margin: 0 auto; }` per spike. ### Test framework setup (mirrors P2) - `jest.config.cjs` (jsdom + babel-jest + CSS stub mapper) - `babel.config.cjs` (@babel/preset-env + preset-react automatic runtime) - `jest.setup.cjs` (loads `@testing-library/jest-dom` + jsdom `matchMedia` polyfill) - `__mocks__/styleMock.js` (so JSX can `import './Foo.css'`) - `npm test` script in package.json ### Tests 25 tests across 3 files, all passing: | File | Tests | |---|---| | `src/components/Sidebar.test.jsx` | 17 | | `src/components/TopBar.test.jsx` | 6 | | `src/components/AppShell.test.jsx` | 2 | TDD: every test was written first, watched fail (`Sidebar` not found), then implementation made them pass. Mobile responsive paths tested via `showHamburger` prop and `isMobileOpen` overlay toggle. ### Pitfalls addressed (from the task body) - ✅ Sidebar lives OUTSIDE any DnD context — DnD remains in `Board` (P3/P5) - ✅ Delete-active-board fallback: picks first remaining board, or `null` if none - ✅ Hamburger overlay closes on board click AND backdrop click - ✅ All sidebar rows are `<button>` (free keyboard nav, ARIA-correct) ### Acceptance - [x] `npm test` passes (25/25) - [x] `npm run build` succeeds - [ ] Manual smoke (create 2 boards, switch, rename, delete, verify localStorage persistence, first-run via `localStorage.clear()`) - [ ] Mobile: hamburger toggles overlay; closes on board click or backdrop ### Known merge collisions to resolve in P7 - **`src/App.css`**: P3 also strips the `max-width: 500px`. Same change, identical line removal — git auto-resolves. - **`src/App.jsx`**: P3 will render `<Board />` conditionally inside the legacy shell. Mine replaces the body with `<AppShell />`. P7 picks whichever works with the unified `useBoardStore` and the final layout (probably: App.jsx calls `useBoardStore()`, hands state to AppShell, AppShell renders `<Board />` as children). - **Test framework config**: P2 may install Jest with different versions. P7 merges both `package.json` and resolves. - **`package-lock.json`**: large diff (8198 lines), all from `npm install`. Acceptable for first PR. ### Manual smoke procedure ```bash git fetch origin git checkout wt/redesign-boards npm install npm run build && npm run preview # open http://localhost:4173 in browser # 1. First-run: see "+ Create your first board" # 2. Click button, type "Personal", Create # 3. Sidebar shows Personal as active; main area shows empty board # 4. Click "+ Create board", add "Work" # 5. Click Personal — switches active # 6. Hover Work, click ⋯, Rename → "Office", Enter # 7. Click ⋯ on Office, Delete, confirm → falls back to Personal # 8. DevTools → Application → Local Storage → http://localhost:4173 # confirm 'ultra-todo-v2-state' has schemaVersion:2, boards, boardOrder, activeBoardId # 9. Reload — state persists # 10. Mobile: DevTools → device toolbar → iPhone SE # - hamburger appears # - sidebar collapses; tap hamburger to open overlay # - tap a board or backdrop to close ```
kaykayyali added 3 commits 2026-06-24 05:06:28 +00:00
Mirrors the test framework P2 will install. Adds:
- jest.config.cjs (jsdom env, babel-jest, CSS stub mapper)
- babel.config.cjs (@babel/preset-env + @babel/preset-react automatic runtime)
- jest.setup.cjs (loads @testing-library/jest-dom + matchMedia polyfill)
- __mocks__/styleMock.js (empty module so JSX can import .css)
- npm test script in package.json

If P2 lands with a different config, P7 integration overlay will resolve.
Sidebar (src/components/Sidebar.jsx):
- 240px dark Trello sidebar (#1d2125)
- "Boards" header + "+ Create board" inline form
- BoardListItem with hover ⋯ menu (Rename / Delete with confirm)
- Inline rename (input + Enter/Escape, blur saves)
- Mobile: collapses behind hamburger, full-screen overlay with backdrop
- First-run variant: shows "+ Create your first board" CTA

TopBar (src/components/TopBar.jsx):
- Renders active board name + hamburger on mobile only
- Subtle background rgba(255,255,255,.15) so board bg shows through

AppShell (src/components/AppShell.jsx):
- Composes Sidebar + TopBar + content slot
- Owns mobile breakpoint detection (window.matchMedia)
- Pass-through for state + callbacks (decoupled from useBoardStore)

EmptyState (src/components/EmptyState.jsx):
- first-run: "Welcome to Ultra Todo. Create your first board..."
- no-board: "Select a board... or create a new one"

Tests: 25 passing across Sidebar.test.jsx (17), TopBar.test.jsx (6),
AppShell.test.jsx (2). TDD — tests written first, watched fail,
implementation made them pass.

Pitfalls addressed:
- DnD lives in Board (P3); Sidebar is OUTSIDE any DnD context ✓
- Delete active board falls back to first remaining or null ✓
- Hamburger overlay closes on board click AND backdrop click ✓
- All sidebar rows are <button> for free keyboard nav ✓
- src/App.jsx: replaces flat-todo UI with multi-board state held in
  useLocalStorage('ultra-todo-v2-state') matching the spike spec
  ({ schemaVersion: 2, activeBoardId, boards, boardOrder, ... }).
  Provides addBoard / renameBoard / deleteBoard / setActiveBoard
  callbacks to AppShell. deleteBoard falls back to first remaining
  board or null when active is deleted.

  Local state lives in App.jsx (not a separate store file) to avoid
  colliding with P2's useBoardStore module. P7 integration overlay
  swaps this for the real hook.

- src/App.css: removes .app max-width: 500px + margin: 0 auto
  (per spike). Same change P3 will make; P7 merge resolves.
kaykayyali closed this pull request 2026-06-24 06:27:54 +00:00
kaykayyali deleted branch wt/redesign-boards 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#2