From 9ec599b51ae02aa3348372543f41f32e0691c273 Mon Sep 17 00:00:00 2001 From: Jonathan Beebe Date: Sun, 29 Mar 2026 10:54:46 -0500 Subject: [PATCH 1/2] fix: use meaningful fallback in delete announcement for screen readers When an item is not found during deletion (race condition), the announcement now says "item" instead of an empty string, avoiding unhelpful "Item "" deleted from ..." messages for screen reader users. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/__tests__/QuadrantCanvas.test.tsx | 19 ++++++++++++++++++- src/components/QuadrantCanvas.tsx | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/__tests__/QuadrantCanvas.test.tsx b/src/__tests__/QuadrantCanvas.test.tsx index 23c5939..7842de1 100644 --- a/src/__tests__/QuadrantCanvas.test.tsx +++ b/src/__tests__/QuadrantCanvas.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi } from 'vitest' -import { render, screen } from '@testing-library/react' +import { render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' import QuadrantCanvas from '../components/QuadrantCanvas' import type { Framework } from '../types' @@ -109,4 +109,21 @@ describe('QuadrantCanvas', () => { const updatedFw = onUpdate.mock.calls[0][0] expect(updatedFw.quadrants[0].items).toHaveLength(2) }) + + it('announces item text when deleting an item', async () => { + const user = userEvent.setup() + render() + + // Delete the existing item "Task A" via its delete button + const deleteBtn = screen.getByRole('button', { name: /delete item: task a/i }) + await user.click(deleteBtn) + + // The sr-only aria-live region should announce the deletion with the item name + await waitFor(() => { + const liveRegions = screen.getAllByRole('status') + const announcement = liveRegions.find((el) => el.textContent?.includes('deleted from')) + expect(announcement).toBeDefined() + expect(announcement!.textContent).toContain('Task A') + }) + }) }) diff --git a/src/components/QuadrantCanvas.tsx b/src/components/QuadrantCanvas.tsx index cd225b1..932202e 100644 --- a/src/components/QuadrantCanvas.tsx +++ b/src/components/QuadrantCanvas.tsx @@ -82,7 +82,7 @@ export default function QuadrantCanvas({ (quadrantIdx: number, itemId: string) => { const item = frameworkRef.current.quadrants[quadrantIdx].items.find((i) => i.id === itemId) updateFramework((fw) => removeItem(fw, quadrantIdx, itemId)) - announce(`Item "${item?.text ?? ''}" deleted from ${frameworkRef.current.quadrants[quadrantIdx].label}`) + announce(`Item "${item?.text ?? 'item'}" deleted from ${frameworkRef.current.quadrants[quadrantIdx].label}`) }, [updateFramework, announce], ) From 899c0941eba0729871942daad484246323463a49 Mon Sep 17 00:00:00 2001 From: Jonathan Beebe Date: Sun, 29 Mar 2026 11:16:58 -0500 Subject: [PATCH 2/2] fix: omit quoted placeholder when item not found in delete announcement When the item is missing (race condition), announce "Item deleted from ..." cleanly instead of including awkward placeholder text. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/QuadrantCanvas.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/QuadrantCanvas.tsx b/src/components/QuadrantCanvas.tsx index 932202e..3e70e21 100644 --- a/src/components/QuadrantCanvas.tsx +++ b/src/components/QuadrantCanvas.tsx @@ -82,7 +82,9 @@ export default function QuadrantCanvas({ (quadrantIdx: number, itemId: string) => { const item = frameworkRef.current.quadrants[quadrantIdx].items.find((i) => i.id === itemId) updateFramework((fw) => removeItem(fw, quadrantIdx, itemId)) - announce(`Item "${item?.text ?? 'item'}" deleted from ${frameworkRef.current.quadrants[quadrantIdx].label}`) + const label = frameworkRef.current.quadrants[quadrantIdx].label + const itemText = item ? `"${item.text}"` : '' + announce(item ? `Item ${itemText} deleted from ${label}` : `Item deleted from ${label}`) }, [updateFramework, announce], )