diff --git a/.Jules/palette.md b/.Jules/palette.md index bbbd06dfd..bfe233e69 100644 --- a/.Jules/palette.md +++ b/.Jules/palette.md @@ -29,3 +29,7 @@ ## 2026-01-22 - Replacing Text Characters with Semantic Icons **Learning:** In `AppSidebar.tsx`, a text character "▼" was used for the expand/collapse arrow. Screen readers might announce this as "Black Down-Pointing Triangle" or similar, which is distracting. Text characters also don't scale or style as consistently as SVGs. **Action:** Replace decorative text characters with `CyberIcon` (or proper SVG icons) and add `aria-hidden="true"` to ensure they are treated as visual decoration only. + +## 2026-05-24 - Interactive List Items as Buttons +**Learning:** In `GlobalSearchModal.tsx`, search results were implemented as clickable `div`s, making them inaccessible to keyboard users (no tab focus). Also, `Dialog` components from `@radix-ui` require a `DialogDescription` for proper accessibility, even if visually hidden. +**Action:** Always use ` ))} ) : searchQuery.trim() ? ( diff --git a/src/tests/components/GlobalSearchModal.test.tsx b/src/tests/components/GlobalSearchModal.test.tsx new file mode 100644 index 000000000..df7146bcc --- /dev/null +++ b/src/tests/components/GlobalSearchModal.test.tsx @@ -0,0 +1,36 @@ +import { render, screen } from '@testing-library/react'; +import { GlobalSearchModal } from '../../components/GlobalSearchModal'; +import { MemoryRouter } from 'react-router-dom'; +import { vi } from 'vitest'; + +// Mock apiClient +vi.mock('@/services/api-client', () => ({ + default: { + get: vi.fn().mockResolvedValue({ success: true, data: [] }), + }, + apiClient: { + get: vi.fn().mockResolvedValue({ success: true, data: [] }), + } +})); + +describe('GlobalSearchModal', () => { + it('renders search results as buttons for accessibility', () => { + render( + + {}} /> + + ); + + // Default results (pages) should be visible + // We use a specific page title we know exists in PAGES_SEARCH + const dashboardResult = screen.getByText('Dashboard'); + expect(dashboardResult).toBeInTheDocument(); + + // Check if the container of the result is a button + // We look for the closest button to the text "Dashboard" + // Note: In the current implementation, this is a div, so this test should fail + const button = dashboardResult.closest('button'); + + expect(button).toBeInTheDocument(); + }); +});