fix: address workbench inspector review findings

This commit is contained in:
2026-07-19 19:24:27 +08:00
parent 3194cbed29
commit 1a82a8b53d
3 changed files with 92 additions and 5 deletions

View File

@@ -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 <title>` 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.

View File

@@ -6,10 +6,10 @@
</script> </script>
<aside class="object-inspector" aria-label="Object inspector"> <aside class="object-inspector" aria-label="Object inspector">
<div class="inspector-tabs" role="tablist" aria-label="Inspector tabs"> <div class="inspector-tabs">
<button role="tab" aria-selected={activeTab === 'discussion'} on:click={() => (activeTab = 'discussion')}>Discussion</button> <button type="button" aria-pressed={activeTab === 'discussion'} on:click={() => (activeTab = 'discussion')}>Discussion</button>
<button role="tab" aria-selected={activeTab === 'properties'} on:click={() => (activeTab = 'properties')}>Properties</button> <button type="button" aria-pressed={activeTab === 'properties'} on:click={() => (activeTab = 'properties')}>Properties</button>
<button role="tab" aria-selected={activeTab === 'more'} on:click={() => (activeTab = 'more')}>More</button> <button type="button" aria-pressed={activeTab === 'more'} on:click={() => (activeTab = 'more')}>More</button>
</div> </div>
{#if item} {#if item}

View File

@@ -1,5 +1,5 @@
import '@testing-library/jest-dom/vitest'; 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 { describe, expect, it } from 'vitest';
import ProjectWorkbench from './ProjectWorkbench.svelte'; import ProjectWorkbench from './ProjectWorkbench.svelte';
@@ -23,4 +23,24 @@ describe('ProjectWorkbench', () => {
expect(screen.getByRole('button', { name: 'Project A2' })).toHaveAttribute('aria-pressed', 'true'); expect(screen.getByRole('button', { name: 'Project A2' })).toHaveAttribute('aria-pressed', 'true');
expect(screen.getByText('Ops Dashboard')).toBeInTheDocument(); 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();
});
}); });