mirror of
https://github.com/johndoe6345789/metabuilder.git
synced 2026-04-24 13:54:57 +00:00
Complete implementation of accessibility utilities across fakemui components: **New Files**: - src/utils/accessibility.ts - Core accessibility utilities (moved from legacy) - src/utils/accessibility.module.scss - Accessibility SCSS styles - src/utils/useAccessible.ts - React hooks for accessibility: * useAccessible() - Generate test IDs and ARIA attributes * useKeyboardNavigation() - Handle keyboard events * useFocusManagement() - Programmatic focus control * useLiveRegion() - Screen reader announcements * useFocusTrap() - Focus trapping for modals **Component Updates**: - Button.tsx - Added data-testid and ARIA support via useAccessible hook - TextField.tsx - Added data-testid, aria-invalid, aria-describedby support **Documentation**: - docs/ACCESSIBILITY_INTEGRATION.md - Complete integration guide with examples **Features**: - 50+ preset test ID generators (form, canvas, settings, navigation, etc.) - ARIA attribute patterns for buttons, toggles, dialogs, tabs, live regions - Keyboard navigation helpers (Enter, Escape, Arrow keys, Tab) - Accessibility validators (hasLabel, isKeyboardAccessible, etc.) - Fully typed TypeScript with AccessibilityFeature, Component, Action types All components now support reliable testing via data-testid and screen reader access via ARIA attributes. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
import React, { forwardRef, useId } from 'react'
|
|
import { FormLabel } from './FormLabel'
|
|
import { FormHelperText } from './FormHelperText'
|
|
import { Input, InputProps } from './Input'
|
|
import { Select } from './Select'
|
|
import { useAccessible } from '../../../src/utils/useAccessible'
|
|
|
|
export interface TextFieldProps extends Omit<InputProps, 'size' | 'label' | 'helperText'> {
|
|
label?: React.ReactNode
|
|
helperText?: React.ReactNode
|
|
error?: boolean
|
|
/** Render as a select dropdown instead of input */
|
|
select?: boolean
|
|
/** Children (for select mode - MenuItem components) */
|
|
children?: React.ReactNode
|
|
/** Input size */
|
|
size?: 'small' | 'medium'
|
|
/** Unique identifier for testing and accessibility */
|
|
testId?: string
|
|
}
|
|
|
|
export const TextField = forwardRef<HTMLInputElement | HTMLSelectElement, TextFieldProps>(
|
|
({ label, helperText, error, className = '', id: providedId, select, children, size, testId: customTestId, ...props }, ref) => {
|
|
const generatedId = useId()
|
|
const id = providedId ?? generatedId
|
|
const helperTextId = `${id}-helper-text`
|
|
|
|
// Convert size prop to Input's expected format
|
|
const inputSize = size === 'small' ? 'sm' : size === 'medium' ? 'md' : undefined
|
|
|
|
const accessible = useAccessible({
|
|
feature: 'form',
|
|
component: select ? 'select' : 'input',
|
|
identifier: customTestId || String(label)?.substring(0, 20),
|
|
ariaDescribedBy: helperText ? helperTextId : undefined,
|
|
})
|
|
|
|
return (
|
|
<div className={`text-field ${error ? 'text-field--error' : ''} ${className}`}>
|
|
{label && <FormLabel htmlFor={id}>{label}</FormLabel>}
|
|
{select ? (
|
|
<Select
|
|
ref={ref as React.Ref<HTMLSelectElement>}
|
|
id={id}
|
|
error={error}
|
|
className="select--full-width"
|
|
data-testid={accessible['data-testid']}
|
|
aria-invalid={error}
|
|
aria-describedby={helperText ? helperTextId : undefined}
|
|
{...(props as unknown as React.SelectHTMLAttributes<HTMLSelectElement>)}
|
|
>
|
|
{children}
|
|
</Select>
|
|
) : (
|
|
<Input
|
|
ref={ref as React.Ref<HTMLInputElement>}
|
|
id={id}
|
|
error={error}
|
|
size={inputSize}
|
|
data-testid={accessible['data-testid']}
|
|
aria-invalid={error}
|
|
aria-describedby={helperText ? helperTextId : undefined}
|
|
{...props}
|
|
/>
|
|
)}
|
|
{helperText && (
|
|
<FormHelperText error={error} id={helperTextId} role="status">
|
|
{helperText}
|
|
</FormHelperText>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|
|
)
|
|
|
|
TextField.displayName = 'TextField'
|