chore: fix aria label warnings
This commit is contained in:
parent
771b87ae41
commit
347c6698ee
@ -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',
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user