diff --git a/e2e/board-flow.spec.ts b/e2e/board-flow.spec.ts index 8dd2034..37c2798 100644 --- a/e2e/board-flow.spec.ts +++ b/e2e/board-flow.spec.ts @@ -31,10 +31,11 @@ test.describe('board flow', () => { await titleInput.press('Enter'); await expect(issueDrawer.getByLabel('Issue title')).toHaveValue(updatedTitle); + await issueDrawer.getByLabel('Edit description').click(); const descriptionInput = issueDrawer.getByLabel('Issue description'); await descriptionInput.fill(updatedDescription); - await descriptionInput.blur(); - await expect(issueDrawer.getByLabel('Issue description')).toHaveValue(updatedDescription); + await issueDrawer.getByRole('button', { name: 'Save' }).click(); + await expect(issueDrawer.getByText(updatedDescription)).toBeVisible(); await issueDrawer.getByLabel('Issue state').selectOption({ label: 'Done' }); await expect(page.locator('[data-testid="column-Done"]')).toContainText(updatedTitle); diff --git a/packages/web/src/App.board-drawer.test.tsx b/packages/web/src/App.board-drawer.test.tsx index a9175cc..4b83dc2 100644 --- a/packages/web/src/App.board-drawer.test.tsx +++ b/packages/web/src/App.board-drawer.test.tsx @@ -16,7 +16,7 @@ describe('App board drawer flows', () => { const drawer = await screen.findByRole('dialog', { name: 'Issue detail drawer' }); expect(within(drawer).getByLabelText('Issue title')).toHaveValue('Ready item'); - expect(within(drawer).getByLabelText('Issue description')).toHaveValue('Ready description'); + expect(within(drawer).getByText('Ready description')).toBeInTheDocument(); expect(within(drawer).getByText('INV-1 — Backlog item')).toBeInTheDocument(); expect(within(drawer).getByText('No child issues.')).toBeInTheDocument(); }); diff --git a/packages/web/src/App.issue-detail-edit.test.tsx b/packages/web/src/App.issue-detail-edit.test.tsx index eefa78f..9627b08 100644 --- a/packages/web/src/App.issue-detail-edit.test.tsx +++ b/packages/web/src/App.issue-detail-edit.test.tsx @@ -117,9 +117,10 @@ describe('App issue detail editing', () => { fireEvent.click(await screen.findByRole('button', { name: 'Open INV-1' })); const drawer = await screen.findByLabelText('Issue detail drawer'); + fireEvent.click(within(drawer).getByLabelText('Edit description')); const descriptionInput = within(drawer).getByLabelText('Issue description'); fireEvent.change(descriptionInput, { target: { value: 'Updated description' } }); - fireEvent.blur(descriptionInput); + fireEvent.click(within(drawer).getByText('Save')); await waitFor(() => expect(mutate).toHaveBeenCalledWith({ @@ -131,7 +132,7 @@ describe('App issue detail editing', () => { ); await waitFor(() => - expect(within(drawer).getByLabelText('Issue description')).toHaveValue('Updated description'), + expect(within(drawer).getByText('Updated description')).toBeInTheDocument(), ); }); @@ -154,9 +155,10 @@ describe('App issue detail editing', () => { fireEvent.click(await screen.findByRole('button', { name: 'Open INV-1' })); const drawer = await screen.findByLabelText('Issue detail drawer'); + fireEvent.click(within(drawer).getByLabelText('Edit description')); const descriptionInput = within(drawer).getByLabelText('Issue description'); fireEvent.change(descriptionInput, { target: { value: 'Locally edited draft' } }); - fireEvent.blur(descriptionInput); + fireEvent.click(within(drawer).getByText('Save')); await waitFor(() => expect(mutate).toHaveBeenCalledWith({ @@ -168,9 +170,7 @@ describe('App issue detail editing', () => { ); await waitFor(() => - expect(within(drawer).getByLabelText('Issue description')).toHaveValue( - 'Persisted description from server', - ), + expect(within(drawer).getByText('Persisted description from server')).toBeInTheDocument(), ); }); @@ -188,7 +188,7 @@ describe('App issue detail editing', () => { const secondDrawer = await screen.findByLabelText('Issue detail drawer'); expect(within(secondDrawer).getByLabelText('Issue title')).toHaveValue('Ready item'); - expect(within(secondDrawer).getByLabelText('Issue description')).toHaveValue('Ready description'); + expect(within(secondDrawer).getByText('Ready description')).toBeInTheDocument(); }); it('shows the updated title after closing and reopening the same issue', async () => { diff --git a/packages/web/src/App.issue-meta.test.tsx b/packages/web/src/App.issue-meta.test.tsx index 209a2ee..b43e70d 100644 --- a/packages/web/src/App.issue-meta.test.tsx +++ b/packages/web/src/App.issue-meta.test.tsx @@ -153,8 +153,8 @@ describe('App issue metadata flows', () => { expect(await screen.findByText('No issues in Backlog yet.')).toBeInTheDocument(); // Canceled column is collapsed by default, so the empty message is hidden; - // just verify the collapsed column header is rendered - expect(screen.getByLabelText(/Canceled column/i)).toBeInTheDocument(); + // just verify at least one element references the Canceled column + expect(screen.getAllByLabelText(/Canceled column/i).length).toBeGreaterThan(0); }); it('shows "No labels available" message when labels array is empty', async () => { diff --git a/packages/web/src/App.navigation.test.tsx b/packages/web/src/App.navigation.test.tsx index 96b67d5..f0b5045 100644 --- a/packages/web/src/App.navigation.test.tsx +++ b/packages/web/src/App.navigation.test.tsx @@ -32,7 +32,7 @@ describe('App navigation and backlog flows', () => { expect(await screen.findByRole('heading', { name: 'Issue detail' })).toBeInTheDocument(); expect(screen.getByText('INV-2')).toBeInTheDocument(); await waitFor(() => expect(screen.getByLabelText('Issue title')).toHaveValue('Ready item')); - await waitFor(() => expect(screen.getByLabelText('Issue description')).toHaveValue('Ready description')); + await waitFor(() => expect(screen.getByText('Ready description')).toBeInTheDocument()); expect(screen.getByText('INV-1 — Backlog item')).toBeInTheDocument(); }); diff --git a/packages/web/src/App.routing.test.tsx b/packages/web/src/App.routing.test.tsx index 205c6a9..77c2fe4 100644 --- a/packages/web/src/App.routing.test.tsx +++ b/packages/web/src/App.routing.test.tsx @@ -27,7 +27,7 @@ describe('App routing', () => { expect(await screen.findByRole('heading', { name: 'Issue detail' })).toBeInTheDocument(); expect(screen.getByText('INV-2')).toBeInTheDocument(); await waitFor(() => expect(screen.getByLabelText('Issue title')).toHaveValue('Ready item')); - await waitFor(() => expect(screen.getByLabelText('Issue description')).toHaveValue('Ready description')); + await waitFor(() => expect(screen.getByText('Ready description')).toBeInTheDocument()); expect(screen.getByText('INV-1 — Backlog item')).toBeInTheDocument(); }); diff --git a/packages/web/src/components/IssueCard.tsx b/packages/web/src/components/IssueCard.tsx index a6d584b..d51d79a 100644 --- a/packages/web/src/components/IssueCard.tsx +++ b/packages/web/src/components/IssueCard.tsx @@ -167,9 +167,11 @@ export function IssueCard({
- + {issue.assignee ? ( + + ) : null} {issue.assignee?.name ?? 'Unassigned'}
diff --git a/packages/web/src/components/IssueDetailDrawer.tsx b/packages/web/src/components/IssueDetailDrawer.tsx index 266e7bb..401355c 100644 --- a/packages/web/src/components/IssueDetailDrawer.tsx +++ b/packages/web/src/components/IssueDetailDrawer.tsx @@ -64,6 +64,7 @@ export function IssueDetailDrawer({ const [selectedLabelIds, setSelectedLabelIds] = useState([]); const [selectedAssigneeId, setSelectedAssigneeId] = useState(''); const [isEditingTitle, setIsEditingTitle] = useState(false); + const [isEditingDescription, setIsEditingDescription] = useState(false); const [commentBody, setCommentBody] = useState(''); const isSavingTitleRef = useRef(false); const titleTextareaRef = useRef(null); @@ -320,18 +321,48 @@ export function IssueDetailDrawer({
- -