chore: fix aria label warnings

This commit is contained in:
Aarnav Tale 2025-02-04 11:27:29 -05:00
parent 771b87ae41
commit 347c6698ee
No known key found for this signature in database
4 changed files with 65 additions and 24 deletions

View File

@ -3,12 +3,14 @@ import { type AriaTextFieldProps, useId, useTextField } from 'react-aria';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
export interface InputProps extends AriaTextFieldProps<HTMLInputElement> { export interface InputProps extends AriaTextFieldProps<HTMLInputElement> {
label: string;
labelHidden?: boolean;
isRequired?: boolean; isRequired?: boolean;
className?: string; className?: string;
} }
export default function Input(props: InputProps) { export default function Input(props: InputProps) {
const { label, className } = props; const { label, labelHidden, className } = props;
const ref = useRef<HTMLInputElement | null>(null); const ref = useRef<HTMLInputElement | null>(null);
const id = useId(props.id); const id = useId(props.id);
@ -19,16 +21,24 @@ export default function Input(props: InputProps) {
errorMessageProps, errorMessageProps,
isInvalid, isInvalid,
validationErrors, validationErrors,
} = useTextField(props, ref); } = useTextField(
{
...props,
label,
'aria-label': label,
},
ref,
);
return ( return (
<div className="flex flex-col"> <div className="flex flex-col w-full" aria-label={label}>
<label <label
{...labelProps} {...labelProps}
htmlFor={id} htmlFor={id}
className={cn( className={cn(
'text-xs font-medium px-3 mb-0.5', 'text-xs font-medium px-3 mb-0.5',
'text-headplane-700 dark:text-headplane-100', 'text-headplane-700 dark:text-headplane-100',
labelHidden && 'sr-only',
)} )}
> >
{label} {label}
@ -37,7 +47,6 @@ export default function Input(props: InputProps) {
{...inputProps} {...inputProps}
required={props.isRequired} required={props.isRequired}
ref={ref} ref={ref}
id={id}
className={cn( className={cn(
'rounded-xl px-3 py-2', 'rounded-xl px-3 py-2',
'focus:outline-none focus:ring', 'focus:outline-none focus:ring',

View File

@ -2,6 +2,7 @@ import { Minus, Plus } from 'lucide-react';
import { useRef } from 'react'; import { useRef } from 'react';
import { import {
type AriaNumberFieldProps, type AriaNumberFieldProps,
useId,
useLocale, useLocale,
useNumberField, useNumberField,
} from 'react-aria'; } from 'react-aria';
@ -19,6 +20,7 @@ export default function NumberInput(props: InputProps) {
const { locale } = useLocale(); const { locale } = useLocale();
const state = useNumberFieldState({ ...props, locale }); const state = useNumberFieldState({ ...props, locale });
const ref = useRef<HTMLInputElement | null>(null); const ref = useRef<HTMLInputElement | null>(null);
const id = useId(props.id);
const { const {
labelProps, labelProps,
@ -36,8 +38,7 @@ export default function NumberInput(props: InputProps) {
<div className="flex flex-col"> <div className="flex flex-col">
<label <label
{...labelProps} {...labelProps}
// TODO: This is WRONG use useId htmlFor={id}
htmlFor={name}
className={cn( className={cn(
'text-xs font-medium px-3 mb-0.5', 'text-xs font-medium px-3 mb-0.5',
'text-headplane-700 dark:text-headplane-100', 'text-headplane-700 dark:text-headplane-100',
@ -56,6 +57,7 @@ export default function NumberInput(props: InputProps) {
> >
<input <input
{...inputProps} {...inputProps}
id={id}
required={props.isRequired} required={props.isRequired}
name={name} name={name}
ref={ref} ref={ref}

View File

@ -6,6 +6,7 @@ import {
useButton, useButton,
useComboBox, useComboBox,
useFilter, useFilter,
useId,
useListBox, useListBox,
useOption, useOption,
} from 'react-aria'; } from 'react-aria';
@ -18,6 +19,7 @@ export interface SelectProps extends AriaComboBoxProps<object> {}
function Select(props: SelectProps) { function Select(props: SelectProps) {
const { contains } = useFilter({ sensitivity: 'base' }); const { contains } = useFilter({ sensitivity: 'base' });
const state = useComboBoxState({ ...props, defaultFilter: contains }); const state = useComboBoxState({ ...props, defaultFilter: contains });
const id = useId(props.id);
const buttonRef = useRef<HTMLButtonElement | null>(null); const buttonRef = useRef<HTMLButtonElement | null>(null);
const inputRef = useRef<HTMLInputElement | null>(null); const inputRef = useRef<HTMLInputElement | null>(null);
@ -46,8 +48,7 @@ function Select(props: SelectProps) {
<div className="flex flex-col"> <div className="flex flex-col">
<label <label
{...labelProps} {...labelProps}
// TODO: THIS IS WRONG, use useId htmlFor={id}
htmlFor={props['aria-labelledby']}
className={cn( className={cn(
'text-xs font-medium px-3 mb-0.5', 'text-xs font-medium px-3 mb-0.5',
'text-headplane-700 dark:text-headplane-100', 'text-headplane-700 dark:text-headplane-100',
@ -65,7 +66,9 @@ function Select(props: SelectProps) {
<input <input
{...inputProps} {...inputProps}
ref={inputRef} ref={inputRef}
id={id}
className="outline-none px-3 py-2 rounded-l-xl w-full bg-transparent" className="outline-none px-3 py-2 rounded-l-xl w-full bg-transparent"
data-1p-ignore
/> />
<button <button
{...buttonProps} {...buttonProps}

View File

@ -1,34 +1,61 @@
import { Switch as AriaSwitch } from 'react-aria-components'; import { useRef } from 'react';
import {
AriaSwitchProps,
VisuallyHidden,
useFocusRing,
useSwitch,
} from 'react-aria';
import { useToggleState } from 'react-stately';
import cn from '~/utils/cn'; import cn from '~/utils/cn';
type SwitchProps = Parameters<typeof AriaSwitch>[0] & { export interface SwitchProps extends AriaSwitchProps {
readonly label: string; label: string;
}; className?: string;
}
export default function Switch(props: SwitchProps) { export default function Switch(props: SwitchProps) {
const state = useToggleState(props);
const ref = useRef<HTMLInputElement | null>(null);
const { focusProps, isFocusVisible } = useFocusRing();
const { inputProps } = useSwitch(
{
...props,
'aria-label': props.label,
},
state,
ref,
);
return ( return (
<AriaSwitch <label className="flex items-center gap-x-2">
{...props} <VisuallyHidden elementType="span">
aria-label={props.label} <input
className="group flex gap-2 items-center" {...inputProps}
> {...focusProps}
aria-label={props.label}
ref={ref}
/>
</VisuallyHidden>
<div <div
aria-hidden
className={cn( className={cn(
'flex h-[26px] w-[44px] p-[4px] shrink-0', 'flex h-[28px] w-[46px] p-[4px] shrink-0 rounded-full',
'rounded-full outline-none group-focus-visible:ring-2', 'bg-headplane-300 dark:bg-headplane-700',
'bg-main-600/50 dark:bg-main-600/20 group-selected:bg-main-700', 'border border-transparent dark:border-headplane-800',
props.isDisabled && 'opacity-50 cursor-not-allowed', state.isSelected && 'bg-headplane-900 dark:bg-headplane-950',
props.className, isFocusVisible && 'ring-2',
props.isDisabled && 'opacity-50',
)} )}
> >
<span <span
className={cn( className={cn(
'h-[18px] w-[18px] transform rounded-full', 'h-[18px] w-[18px] transform rounded-full',
'bg-white transition duration-100 ease-in-out', 'bg-white transition duration-50 ease-in-out',
'translate-x-0 group-selected:translate-x-[100%]', 'translate-x-0 group-selected:translate-x-[100%]',
state.isSelected && 'translate-x-[100%]',
)} )}
/> />
</div> </div>
</AriaSwitch> </label>
); );
} }