From da91d19b2d648a447df3e17ca192635afeabd00c Mon Sep 17 00:00:00 2001 From: KANISHKA GUPTA Date: Fri, 12 Jun 2026 20:41:43 +0530 Subject: [PATCH] W3C accordian --- .../EditorPanel.accessibility.test.tsx | 24 ++++---- .../SectionCard.accessibility.test.tsx | 56 +------------------ app/generator/components/SectionCard.tsx | 3 +- 3 files changed, 17 insertions(+), 66 deletions(-) diff --git a/app/generator/components/EditorPanel.accessibility.test.tsx b/app/generator/components/EditorPanel.accessibility.test.tsx index 458d7aadb..419224cf8 100644 --- a/app/generator/components/EditorPanel.accessibility.test.tsx +++ b/app/generator/components/EditorPanel.accessibility.test.tsx @@ -48,14 +48,16 @@ describe('EditorPanel Component Accessibility Tests', () => { expect(form).toBeInTheDocument(); // Section card heading hierarchy (h3) and regions - const nameHeading = screen.getByRole('heading', { level: 3, name: /^name$/i }); - expect(nameHeading).toBeInTheDocument(); - - const nameRegion = screen.getByRole('region', { name: /^name$/i }); - expect(nameRegion).toBeInTheDocument(); - const headingId = nameHeading.getAttribute('id'); - expect(headingId).toBeTruthy(); - expect(nameRegion).toHaveAttribute('aria-labelledby', headingId!); + + const headerButton = screen.getByRole('button', { + name: /your display name for the readme header/i, + }); + + const nameRegion = screen.getByRole('region', { + name: /your display name for the readme header/i, + }); + + expect(nameRegion).toHaveAttribute('aria-labelledby', headerButton.id); }); // Test 2: Label to Input Pairings @@ -131,7 +133,9 @@ describe('EditorPanel Component Accessibility Tests', () => { it('5. verifies that the SectionCard header updates aria-expanded states correctly when toggled', () => { render(); - const headerButton = screen.getByRole('button', { name: /^name$/i }); + const headerButton = screen.getByRole('button', { + name: /your display name for the readme header/i, + }); expect(headerButton).toHaveAttribute('aria-expanded', 'true'); // Collapse panel @@ -139,7 +143,7 @@ describe('EditorPanel Component Accessibility Tests', () => { expect(headerButton).toHaveAttribute('aria-expanded', 'false'); // Confirm that region content panel is removed or hidden - const nameRegion = screen.queryByRole('region', { name: /^name$/i }); + const nameRegion = screen.queryByRole('region', { name: /name/i }); expect(nameRegion).toBeNull(); }); }); diff --git a/app/generator/components/SectionCard.accessibility.test.tsx b/app/generator/components/SectionCard.accessibility.test.tsx index 9c3bb6e4d..2072e73de 100644 --- a/app/generator/components/SectionCard.accessibility.test.tsx +++ b/app/generator/components/SectionCard.accessibility.test.tsx @@ -6,20 +6,6 @@ import { describe, expect, it } from 'vitest'; import { SectionCard, FieldLabel } from './SectionCard'; describe('SectionCard Accessibility', () => { - /** - * Test Case 1: ARIA & Accessible Markup Validation - * - * Verifies that the interactive controls are identified correctly by screen readers, - * and validates the presence of expected roles and labels. - * - * NOTE: - * During testing, we found that SectionCard lacks standard dynamic ARIA properties. - * TODO: Implement the following improvements in SectionCard.tsx: - * - Add `aria-expanded={open}` on the main toggle button. - * - Add `aria-controls={contentId}` pointing to the collapsible content region. - * - Add `role="region"` or `role="group"` with `aria-labelledby` referencing the button/title to the children wrapper. - * - Add `aria-describedby` referencing the description paragraph. - */ it('1. ARIA & Accessible Markup Validation: verifies interactive elements expose correct accessibility attributes', () => { // Arrange const titleText = 'Profile Details'; @@ -64,9 +50,8 @@ describe('SectionCard Accessibility', () => { // Verify the content region exists, carries the correct role, ID, and label const contentRegion = screen.getByRole('region', { name: new RegExp(titleText, 'i') }); expect(contentRegion).toHaveAttribute('id', contentId!); - - const titleElement = screen.getByText(titleText); - expect(titleElement).toHaveAttribute('id', contentRegion.getAttribute('aria-labelledby')!); + expect(descElement.id).toBe(descriptionId); + expect(contentRegion).toHaveAttribute('aria-labelledby', toggleButton.id); // Verify children containing fields using ARIA attributes map correctly const textInput = screen.getByRole('textbox', { name: 'Full Name' }); @@ -77,12 +62,6 @@ describe('SectionCard Accessibility', () => { expect(helperText).toBeInTheDocument(); }); - /** - * Test Case 2: Keyboard Focus Visibility - * - * Verifies focusable controls receive focus via keyboard tab navigation, and that - * the custom ring/focus outline styles are properly applied without losing focus. - */ it('2. Keyboard Focus Visibility: verifies focusable controls receive focus and focus indicators are preserved', async () => { // Arrange const user = userEvent.setup(); @@ -112,16 +91,7 @@ describe('SectionCard Accessibility', () => { expect(document.body).toHaveFocus(); }); - /** - * Test Case 3: Tooltip Accessibility - * - * Verifies tooltip triggers inside SectionCard expose accessible descriptions, - * and that screen readers can announce the tooltip contents via correct relations. - */ it('3. Tooltip Accessibility: verifies tooltip trigger associates with content via ARIA attributes', () => { - // Arrange & Act - // Render a mock tooltip implementation in SectionCard's children to verify - // the wrapper component handles accessible tooltip patterns correctly. render(
@@ -141,18 +111,8 @@ describe('SectionCard Accessibility', () => { // Assert expect(trigger).toHaveAttribute('aria-describedby', 'info-tooltip'); expect(tooltip).toHaveTextContent('This explains the section settings in detail.'); - - // TODO: Standardize tooltips inside SectionCard by packaging a reusable, - // fully compliant tooltip component in the generator components directory. }); - /** - * Test Case 4: Keyboard Navigation Order - * - * Verifies the tab sequence flows in a natural order across interactive controls. - * Also ensures that when the SectionCard is collapsed, the child inputs - * are unmounted and therefore skipped entirely in keyboard navigation. - */ it('4. Keyboard Navigation Order: verifies sequential keyboard navigation and that closed card controls are unreachable', async () => { // Arrange const user = userEvent.setup(); @@ -206,16 +166,7 @@ describe('SectionCard Accessibility', () => { expect(document.body).toHaveFocus(); }); - /** - * Test Case 5: Heading Hierarchy Validation - * - * Verifies that the SectionCard title uses a semantic heading level (h3) - * to align with screen reader expectations and allow landmark navigation. - * Also verifies headings inside children follow proper layout nesting rules. - */ it('5. Heading Hierarchy Validation: verifies heading structure logical flow and checks for violations', () => { - // Arrange & Act - // Render SectionCard alongside correct heading structures in children render(
@@ -239,8 +190,5 @@ describe('SectionCard Accessibility', () => { expect(h2El).toHaveTextContent('Primary Section Level 2 Heading'); expect(h3El).toHaveTextContent('Secondary Subsection Level 3 Heading'); - - // TODO: Consider introducing a heading-level prop (e.g. `titleAs="h2" | "h3" | "span"`) - // to allow callers to align the SectionCard header title semantically with page layout outlines. }); }); diff --git a/app/generator/components/SectionCard.tsx b/app/generator/components/SectionCard.tsx index 6d5eed8df..783461612 100644 --- a/app/generator/components/SectionCard.tsx +++ b/app/generator/components/SectionCard.tsx @@ -33,7 +33,6 @@ export function SectionCard({ id={headerId} aria-expanded={open} aria-controls={contentId} - aria-labelledby={titleId} aria-describedby={description ? descriptionId : undefined} onClick={() => setOpen((p) => !p)} className="w-full flex items-center gap-3 px-5 py-4 text-left group focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-emerald-500/50" @@ -68,7 +67,7 @@ export function SectionCard({ {open && ( -
+
{children}