diff --git a/.superpowers/sdd/task-4-report.md b/.superpowers/sdd/task-4-report.md new file mode 100644 index 0000000..b743d8d --- /dev/null +++ b/.superpowers/sdd/task-4-report.md @@ -0,0 +1,67 @@ +# Task 4 Report: Channel Content Templates And Object Inspector + +## Status + +Implemented and committed the Task 4 workbench channel templates and object inspector. + +## What Changed + +- Added `ChannelContent.svelte`, which dispatches `ProjectWorkspace` data to the selected system or custom channel template. +- Added seven channel templates for overview, inbox, tasks, AI sessions, notes and sources, cron plans, and external custom links. +- Made all applicable records selectable through an `Inspect ` action that supplies an `InspectorItem` to the workbench. +- Added `ObjectInspector.svelte` with Discussion, Properties, and More tabs, including empty-state behavior and object properties. +- Wired the selected channel and selected inspector item into `ProjectWorkbench.svelte`. +- Added the required channel dispatcher test, including the task-to-inspector callback assertion. +- Kept visual-system CSS untouched for Task 5. + +## TDD Evidence + +1. Added `apps/web/src/features/workbench/ChannelContent.test.ts` before creating any production channel component. +2. Ran `npm test -- --run src/features/workbench/ChannelContent.test.ts` from `apps/web`. +3. Observed the expected red failure: Vite could not resolve `./ChannelContent.svelte` because it did not exist. +4. Implemented the dispatcher and channel components, then ran the focused channel and workbench tests. +5. Corrected the inspector tab semantics after the green run exposed Svelte accessibility warnings, then reran verification with no warnings. + +## Tests + +- `npm test -- --run src/features/workbench/ChannelContent.test.ts src/features/workbench/ProjectWorkbench.test.ts` + - Passed: 2 test files, 4 tests. +- `npm run build` + - Passed: Vite production build completed successfully. +- `git diff --check` + - Passed: no whitespace errors. + +## Files Changed + +- Created `apps/web/src/features/workbench/ChannelContent.svelte` +- Created `apps/web/src/features/workbench/ChannelContent.test.ts` +- Created `apps/web/src/features/workbench/ObjectInspector.svelte` +- Created `apps/web/src/features/workbench/channels/OverviewChannel.svelte` +- Created `apps/web/src/features/workbench/channels/InboxChannel.svelte` +- Created `apps/web/src/features/workbench/channels/TasksChannel.svelte` +- Created `apps/web/src/features/workbench/channels/AISessionsChannel.svelte` +- Created `apps/web/src/features/workbench/channels/NotesSourcesChannel.svelte` +- Created `apps/web/src/features/workbench/channels/CronChannel.svelte` +- Created `apps/web/src/features/workbench/channels/CustomLinkChannel.svelte` +- Modified `apps/web/src/features/workbench/ProjectWorkbench.svelte` +- Created this report: `.superpowers/sdd/task-4-report.md` + +## Self-Review + +- Confirmed every `WorkbenchChannel` type has a dedicated rendered template through the dispatcher. +- Confirmed the custom link uses the required `Open external channel` text and provides a copy-style action. +- Confirmed task inspection produces the exact task title required by the test. +- Confirmed the inspector remains visible through its existing `aria-label` and exposes semantic tab state using `role="tab"` with `aria-selected`. +- Confirmed no React, shadcn/ui dependency, or index.css modification was introduced. +- Confirmed unrelated existing `.superpowers/sdd` files were not staged. + +## Concerns + +- The copy URL and inspector More-tab actions are present as MVP UI controls; beyond writing the custom URL to the clipboard, they intentionally do not persist or mutate workbench data. +- Visual styling is limited to semantic class hooks by design; Task 5 owns the visual-system CSS work. + +## Review Fixes + +- Replaced the incomplete ARIA tabs pattern in `ObjectInspector.svelte` with ordinary pressed view-switcher buttons, removing tab and tablist roles that required a full tab-panel keyboard interaction model. +- Extended `ProjectWorkbench.test.ts` to select the Work Plan channel, inspect a task, and assert the rendered inspector title plus Properties values within the object inspector. +- Focused workbench coverage passed after the changes: 2 test files, 5 tests. diff --git a/apps/web/src/features/workbench/ObjectInspector.svelte b/apps/web/src/features/workbench/ObjectInspector.svelte index f36f387..79fa781 100644 --- a/apps/web/src/features/workbench/ObjectInspector.svelte +++ b/apps/web/src/features/workbench/ObjectInspector.svelte @@ -6,10 +6,10 @@ </script> <aside class="object-inspector" aria-label="Object inspector"> - <div class="inspector-tabs" role="tablist" aria-label="Inspector tabs"> - <button role="tab" aria-selected={activeTab === 'discussion'} on:click={() => (activeTab = 'discussion')}>Discussion</button> - <button role="tab" aria-selected={activeTab === 'properties'} on:click={() => (activeTab = 'properties')}>Properties</button> - <button role="tab" aria-selected={activeTab === 'more'} on:click={() => (activeTab = 'more')}>More</button> + <div class="inspector-tabs"> + <button type="button" aria-pressed={activeTab === 'discussion'} on:click={() => (activeTab = 'discussion')}>Discussion</button> + <button type="button" aria-pressed={activeTab === 'properties'} on:click={() => (activeTab = 'properties')}>Properties</button> + <button type="button" aria-pressed={activeTab === 'more'} on:click={() => (activeTab = 'more')}>More</button> </div> {#if item} diff --git a/apps/web/src/features/workbench/ProjectWorkbench.test.ts b/apps/web/src/features/workbench/ProjectWorkbench.test.ts index cf251b3..61e15b1 100644 --- a/apps/web/src/features/workbench/ProjectWorkbench.test.ts +++ b/apps/web/src/features/workbench/ProjectWorkbench.test.ts @@ -1,5 +1,5 @@ import '@testing-library/jest-dom/vitest'; -import { fireEvent, render, screen } from '@testing-library/svelte'; +import { fireEvent, render, screen, within } from '@testing-library/svelte'; import { describe, expect, it } from 'vitest'; import ProjectWorkbench from './ProjectWorkbench.svelte'; @@ -23,4 +23,24 @@ describe('ProjectWorkbench', () => { expect(screen.getByRole('button', { name: 'Project A2' })).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByText('Ops Dashboard')).toBeInTheDocument(); }); + + it('shows inspected task details in the inspector', async () => { + render(ProjectWorkbench, { props: { currentUser: { account: 'david@example.com' } } }); + const inspector = screen.getByLabelText('Object inspector'); + + expect(within(inspector).getByRole('button', { name: 'Discussion' })).toHaveAttribute('aria-pressed', 'true'); + + await fireEvent.click(screen.getByRole('button', { name: 'Work Plan 8' })); + await fireEvent.click(screen.getByRole('button', { name: 'Inspect Confirm homepage information architecture' })); + + expect(within(inspector).getByRole('heading', { name: 'Confirm homepage information architecture' })).toBeInTheDocument(); + + await fireEvent.click(within(inspector).getByRole('button', { name: 'Properties' })); + + expect(within(inspector).getByRole('button', { name: 'Properties' })).toHaveAttribute('aria-pressed', 'true'); + expect(within(inspector).getByText('Status')).toBeInTheDocument(); + expect(within(inspector).getByText('Open')).toBeInTheDocument(); + expect(within(inspector).getByText('Owner')).toBeInTheDocument(); + expect(within(inspector).getByText('David')).toBeInTheDocument(); + }); });