Files
kite/.planning/milestones/v0.2-phases/10-ux-interaction/10-05-PLAN.md
T
curo1305andClaude Sonnet 4.6 123ae5b29b chore: archive v0.2 phase directories to milestones/v0.2-phases/
Moves phases 08–11 execution artifacts from .planning/phases/ to
.planning/milestones/v0.2-phases/ to keep .planning/phases/ clean
for the next milestone.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 14:34:52 +02:00

16 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
10-ux-interaction 05 execute 0
frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js
frontend/src/components/layout/__tests__/AppSidebar.empty.test.js
frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js
frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js
frontend/src/__tests__/keyboard.test.js
frontend/src/components/layout/__tests__/OsDragOverlay.test.js
frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js
frontend/src/components/ui/__tests__/dropdown.test.js
true
UX-02
UX-03
UX-04
UX-05
UX-06
UX-07
UX-08
UX-09
UX-11
UX-13
UX-14
truths artifacts key_links
Wave 0 xfail test stubs exist for every requirement that is implemented in Waves 1-4
Each stub is marked .skip or .todo so the test runner reports unrun tests instead of false greens
Each requirement ID is referenced by at least one stub
path provides
frontend/src/__tests__/keyboard.test.js Wave 0 xfail stubs for UX-05..08 keyboard shortcuts
path provides
frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js Wave 0 xfail stubs for UX-02 skeleton + UX-13 dropdown teleport
from to via pattern
Each stub describe() block Requirement ID in test title or comment describe('UX-XX: ...') describe.*UX-
Create Wave 0 test stubs (Nyquist pattern — RED tests before implementation) for the requirements that are wired in Waves 1-4. This ensures: 1. The test runner reports unrun tests for every requirement (no false greens) 2. Wave 1-4 implementations have a place to "promote" stubs to real assertions 3. Coverage of all 15 phase requirements is provable up-front

This plan covers the 11 requirements NOT covered by Wave 0 foundation plans (10-01..10-04 already include their tests):

  • Foundation already covered by their own plans: UX-01 (EmptyState — 10-02), UX-10 (Toast — 10-04), UX-12 (BreadcrumbBar — 10-03), CODE-05 (AppIcon — 10-01)
  • This plan covers: UX-02, UX-03, UX-04, UX-05, UX-06, UX-07, UX-08, UX-09, UX-11, UX-13, UX-14

Output: 8 stub test files, each with it.skip or it.todo placeholders annotating the requirement IDs and expected behavior. Wave 1-4 implementing plans will replace .skip/.todo with real tests.

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@CLAUDE.md @.planning/phases/10-ux-interaction/10-CONTEXT.md @.planning/phases/10-ux-interaction/10-RESEARCH.md @.planning/phases/10-ux-interaction/10-VALIDATION.md @frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js Vitest stubs use `it.todo('description')` or `it.skip('description', ...)`. Each stub file should `describe('UX-XX: requirement summary', ...)` with one or more `it.todo` placeholders for the behaviors that Wave 1-4 will implement.

Test file locations (mirror the source structure):

  • frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js — UX-02 (skeleton rows) + UX-13 (folder picker dropdown)
  • frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js — UX-11 (drag-to-move toast trigger + ring highlight)
  • frontend/src/components/layout/__tests__/AppSidebar.empty.test.js — UX-03 (sidebar skeleton + empty micro states) + UX-14 (sidebar New button removed)
  • frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js — UX-04 (audit log skeleton rows)
  • frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js — UX-04 (users table skeleton rows)
  • frontend/src/__tests__/keyboard.test.js — UX-05..08 (global keyboard shortcuts)
  • frontend/src/components/layout/__tests__/OsDragOverlay.test.js — UX-09 (OS file drag overlay depth-counter)
  • frontend/src/components/ui/__tests__/dropdown.test.js — UX-13 (Teleport+getBoundingClientRect dropdown positioning)
Task 1: Create skeleton + dropdown stubs (UX-02, UX-03, UX-04, UX-13, UX-14) frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js, frontend/src/components/layout/__tests__/AppSidebar.empty.test.js, frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js, frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js, frontend/src/components/ui/__tests__/dropdown.test.js - frontend/src/components/folders/__tests__/FolderBreadcrumb.test.js (style reference) - .planning/phases/10-ux-interaction/10-VALIDATION.md (test map) - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Pitfall 7: Skeleton Row Height" and §"Component Inventory §2: Dropdown Clipping Audit" Create 5 stub test files. Each file is a single `describe('UX-XX: requirement', ...)` block with `it.todo(...)` entries. Use `import { describe, it } from 'vitest'` at the top.
**File A — `frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-02: StorageBrowser shows skeleton rows during loading', () => {
  it.todo('renders 5+ skeleton rows when loading=true (replaces Loading… text)')
  it.todo('skeleton rows use grid-cols-[2rem_1fr_6rem_8rem_6rem] matching real row grid')
  it.todo('skeleton rows use animate-pulse')
  it.todo('Loading… text is absent when loading=true (skeleton replaces it)')
})

describe('UX-13: StorageBrowser folder picker uses Teleport + getBoundingClientRect', () => {
  it.todo('folder picker dropdown is teleported to body (escapes overflow container)')
  it.todo('dropdown position reflects getBoundingClientRect of the trigger button')
  it.todo('window scroll while open recalculates position')
})
```

**File B — `frontend/src/components/layout/__tests__/AppSidebar.empty.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-03: AppSidebar shows skeleton placeholders while loading', () => {
  it.todo('renders skeleton rows in folder section when loadingRoots=true')
  it.todo('renders skeleton rows in topics section when topicsStore.loading=true')
  it.todo('renders skeleton rows in cloud section when loadingCloudConnections=true')
  it.todo('Loading… text is removed from all three sections')
})

describe('UX-01 (sidebar micro): EmptyState size=sm appears when each section is empty', () => {
  it.todo('folders empty: EmptyState size=sm with icon=folder')
  it.todo('topics empty: EmptyState size=sm with icon=tag')
  it.todo('cloud empty: EmptyState size=sm with icon=cloud and a Settings link in #cta')
})

describe('UX-14: AppSidebar no longer renders an inline "New" folder button', () => {
  it.todo('no <button> with text "New" exists in the rendered template')
  it.todo('startNewFolder/cancelNewFolder/submitNewFolder methods are removed')
  it.todo('newFolderName/showNewFolderInput state is removed')
})
```

**File C — `frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-04: AdminAuditView shows skeleton table rows during loading', () => {
  it.todo('renders 5+ skeleton <tr> rows when loading=true')
  it.todo('skeleton rows have 5 columns matching the real table header')
  it.todo('animated spinner Loading audit log… text is removed')
})

describe('UX-01 (audit empty): EmptyState renders when entries is empty after load', () => {
  it.todo('EmptyState icon=clipboardList headline="No entries found" appears when entries.length===0 and not loading')
  it.todo('clear-filters button rendered in #cta slot')
})
```

**File D — `frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-04: AdminUsersView shows skeleton table rows during loading', () => {
  it.todo('renders 5+ skeleton <tr> rows when loading=true')
  it.todo('skeleton rows have 6 columns matching the real table header')
  it.todo('animated spinner Loading users… text is removed')
})
```

**File E — `frontend/src/components/ui/__tests__/dropdown.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-13: Teleport + getBoundingClientRect dropdown pattern across components', () => {
  it.todo('DocumentCard folder picker uses Teleport to body')
  it.todo('DocumentCard folder picker position matches trigger getBoundingClientRect')
  it.todo('FolderRow three-dot menu uses Teleport to body')
  it.todo('FolderRow three-dot menu repositions on window scroll')
})
```

Do not implement any real tests — these are stubs Wave 1/3 will promote.
cd frontend && npm run test -- --run skeleton.test && cd frontend && npm run test -- --run dropdown.test && cd frontend && npm run test -- --run AppSidebar.empty Expected: tests collected, all `.todo` are reported but not failing (Vitest reports `.todo` as "skipped/pending" — they do not fail the suite). - All 5 files exist at the specified paths - Each file contains at least one `describe('UX-XX: ...')` block whose name starts with the requirement ID - Total `it.todo(...)` count across the 5 files ≥ 20 - Running `cd frontend && npm run test -- --run` does NOT fail because of these files (they only contain `.todo`) - Grep verification: `grep -rE "describe\\('UX-0[234]" frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js frontend/src/components/layout/__tests__/AppSidebar.empty.test.js frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js | wc -l` ≥ 4 - Grep: `grep -rE "describe\\('UX-1[34]" frontend/src/components/ui/__tests__/dropdown.test.js frontend/src/components/layout/__tests__/AppSidebar.empty.test.js | wc -l` ≥ 2 5 skeleton/empty/dropdown stub files exist covering UX-02, UX-03, UX-04, UX-13, UX-14. Task 2: Create keyboard + OS-drag + drag-to-move stubs (UX-05..09, UX-11) frontend/src/__tests__/keyboard.test.js, frontend/src/components/layout/__tests__/OsDragOverlay.test.js, frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js - .planning/phases/10-ux-interaction/10-VALIDATION.md (behavioral contracts) - .planning/phases/10-ux-interaction/10-RESEARCH.md §"Common Pitfalls" 2, 3, 12 Create 3 stub test files:
**File F — `frontend/src/__tests__/keyboard.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-05: "/" focuses the search bar', () => {
  it.todo('dispatching keydown "/" calls focus() on SearchBar input via App.vue routeViewRef chain')
  it.todo('"/" does NOT redirect when an INPUT element has focus (guard: document.activeElement.tagName)')
})

describe('UX-06: "Escape" clears active search (when no input focused)', () => {
  it.todo('keydown Escape clears searchQuery via FileManagerView.clearSearch()')
  it.todo('Escape does NOT clear search while inside a focused INPUT')
  it.todo('Escape does NOT double-trigger when DocumentPreviewModal is open (modal owns its Escape handler)')
})

describe('UX-07: "U" triggers the upload picker', () => {
  it.todo('keydown "u" calls DropZone.triggerInput() through the ref chain (App→FileManagerView→StorageBrowser→DropZone)')
  it.todo('"U" does NOT fire when input is focused')
  it.todo('"U" is a no-op on routes that do not expose triggerUpload (optional chain)')
})

describe('UX-08: "N" starts new folder input', () => {
  it.todo('keydown "n" calls StorageBrowser.startNewFolder() via ref chain')
  it.todo('"N" does NOT fire when input is focused')
  it.todo('"N" is a no-op on CloudFolderView (does not expose startNewFolder)')
})
```

**File G — `frontend/src/components/layout/__tests__/OsDragOverlay.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-09: OsDragOverlay shows when OS files are dragged over the window', () => {
  it.todo('overlay hidden by default (dragDepth=0)')
  it.todo('dragenter with dataTransfer.types including "Files" shows overlay')
  it.todo('dragenter without "Files" type is ignored (in-app element drag)')
  it.todo('dragleave decrements depth; overlay hides when depth reaches 0')
  it.todo('drop event emits files-dropped with the file list')
  it.todo('drop resets dragDepth to 0 and hides overlay')
  it.todo('overlay is teleported to body with z-[9998] (below toast z-[9999])')
})
```

**File H — `frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js`:**
```
import { describe, it } from 'vitest'

describe('UX-11: Drag-to-move document onto folder row', () => {
  it.todo('dragOverFolderId set on @dragover applies bg-amber-50 ring-2 ring-inset ring-amber-300 to that folder row')
  it.todo('drop on folder emits file-move with { fileId, folderId }')
  it.todo('dragend resets draggingFile to null after nextTick (click-after-drag guard)')
  it.todo('click on file row is suppressed when draggingFile is non-null')
})

describe('UX-11 (toast wiring): doMove emits toast', () => {
  it.todo('successful moveToFolder triggers useToastStore.show("Document moved", "success")')
  it.todo('failed moveToFolder triggers useToastStore.show("Move failed: ...", "error")')
})
```
cd frontend && npm run test -- --run keyboard.test && cd frontend && npm run test -- --run OsDragOverlay.test && cd frontend && npm run test -- --run dragmove.test Expected: tests collected; all .todo entries reported as pending; suite does not fail. - All 3 files exist at the specified paths - `keyboard.test.js` contains describe blocks for UX-05, UX-06, UX-07, UX-08 (4 distinct requirement IDs) - `OsDragOverlay.test.js` contains describe for UX-09 - `StorageBrowser.dragmove.test.js` contains describe for UX-11 - Total `it.todo` count across the 3 files ≥ 18 - `cd frontend && npm run test -- --run` does not fail because of these files - Grep: `grep -E "describe\\('UX-0[5678]" frontend/src/__tests__/keyboard.test.js | wc -l` returns 4 3 stub files exist covering UX-05..09 + UX-11. - All 8 stub test files exist at the specified paths (5 from Task 1 + 3 from Task 2) - `cd frontend && npm run test -- --run` continues to pass (no `.todo` produces a failure) - Each of UX-02, UX-03, UX-04, UX-05, UX-06, UX-07, UX-08, UX-09, UX-11, UX-13, UX-14 appears in at least one stub `describe(...)` block - Coverage check: `grep -rE "UX-0[23456789]|UX-1[1345]|UX-14" frontend/src/__tests__/keyboard.test.js frontend/src/components/storage/__tests__/StorageBrowser.skeleton.test.js frontend/src/components/storage/__tests__/StorageBrowser.dragmove.test.js frontend/src/components/layout/__tests__/AppSidebar.empty.test.js frontend/src/views/admin/__tests__/AdminAuditView.skeleton.test.js frontend/src/views/admin/__tests__/AdminUsersView.skeleton.test.js frontend/src/components/layout/__tests__/OsDragOverlay.test.js frontend/src/components/ui/__tests__/dropdown.test.js | wc -l` returns ≥ 11

<success_criteria> Every requirement implemented in Waves 1-4 has a placeholder test that will be promoted to a real assertion when the implementing wave runs. Wave verification can detect when a requirement has been forgotten by checking that the corresponding .todo was upgraded to a real it(...). </success_criteria>

Create `.planning/phases/10-ux-interaction/10-05-SUMMARY.md` when done. Include the total count of `it.todo` stubs created.