|
|
@ -1,5 +1,6 @@
|
|
|
|
import clsx from 'clsx';
|
|
|
|
import clsx from 'clsx';
|
|
|
|
import type { ChangeEvent } from 'react';
|
|
|
|
import type { ChangeEvent } from 'react';
|
|
|
|
|
|
|
|
import { useId } from 'react';
|
|
|
|
|
|
|
|
|
|
|
|
import { RadioListContext } from './RadioListContext';
|
|
|
|
import { RadioListContext } from './RadioListContext';
|
|
|
|
import RadioListItem from './RadioListItem';
|
|
|
|
import RadioListItem from './RadioListItem';
|
|
|
@ -10,11 +11,13 @@ type Props<T> = Readonly<{
|
|
|
|
children: ReadonlyArray<React.ReactElement<typeof RadioListItem>>;
|
|
|
|
children: ReadonlyArray<React.ReactElement<typeof RadioListItem>>;
|
|
|
|
defaultValue?: T;
|
|
|
|
defaultValue?: T;
|
|
|
|
description?: string;
|
|
|
|
description?: string;
|
|
|
|
|
|
|
|
disabled?: boolean;
|
|
|
|
isLabelHidden?: boolean;
|
|
|
|
isLabelHidden?: boolean;
|
|
|
|
label: string;
|
|
|
|
label: string;
|
|
|
|
name?: string;
|
|
|
|
name?: string;
|
|
|
|
onChange?: (value: T, event: ChangeEvent<HTMLInputElement>) => void;
|
|
|
|
onChange?: (value: T, event: ChangeEvent<HTMLInputElement>) => void;
|
|
|
|
orientation?: RadioListOrientation;
|
|
|
|
orientation?: RadioListOrientation;
|
|
|
|
|
|
|
|
required?: boolean;
|
|
|
|
value?: T;
|
|
|
|
value?: T;
|
|
|
|
}>;
|
|
|
|
}>;
|
|
|
|
|
|
|
|
|
|
|
@ -24,35 +27,47 @@ export default function RadioList<T>({
|
|
|
|
children,
|
|
|
|
children,
|
|
|
|
defaultValue,
|
|
|
|
defaultValue,
|
|
|
|
description,
|
|
|
|
description,
|
|
|
|
|
|
|
|
disabled,
|
|
|
|
orientation = 'vertical',
|
|
|
|
orientation = 'vertical',
|
|
|
|
isLabelHidden,
|
|
|
|
isLabelHidden,
|
|
|
|
name,
|
|
|
|
name,
|
|
|
|
label,
|
|
|
|
label,
|
|
|
|
|
|
|
|
required,
|
|
|
|
value,
|
|
|
|
value,
|
|
|
|
onChange,
|
|
|
|
onChange,
|
|
|
|
}: Props<T>) {
|
|
|
|
}: Props<T>) {
|
|
|
|
|
|
|
|
const labelId = useId();
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
|
|
|
|
<RadioListContext.Provider
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
|
|
// @ts-ignore TODO: Figure out how to type the onChange.
|
|
|
|
// @ts-ignore TODO: Figure out how to type the onChange.
|
|
|
|
<RadioListContext.Provider value={{ defaultValue, name, onChange, value }}>
|
|
|
|
value={{ defaultValue, disabled, name, onChange, value }}>
|
|
|
|
<div>
|
|
|
|
<div>
|
|
|
|
<div className={clsx(isLabelHidden ? 'sr-only' : 'mb-4')}>
|
|
|
|
<div className={clsx(isLabelHidden ? 'sr-only' : 'mb-2')}>
|
|
|
|
<label className="text-base font-medium text-gray-900">{label}</label>
|
|
|
|
<label className="text-sm font-medium text-gray-900" id={labelId}>
|
|
|
|
|
|
|
|
{label}
|
|
|
|
|
|
|
|
{required && (
|
|
|
|
|
|
|
|
<span aria-hidden="true" className="text-danger-500">
|
|
|
|
|
|
|
|
{' '}
|
|
|
|
|
|
|
|
*
|
|
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
</label>
|
|
|
|
{description && (
|
|
|
|
{description && (
|
|
|
|
<p className="text-sm leading-5 text-gray-500">{description}</p>
|
|
|
|
<p className="text-xs leading-5 text-gray-500">{description}</p>
|
|
|
|
)}
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<fieldset>
|
|
|
|
|
|
|
|
<legend className="sr-only">TODO</legend>
|
|
|
|
|
|
|
|
<div
|
|
|
|
<div
|
|
|
|
|
|
|
|
aria-labelledby={labelId}
|
|
|
|
|
|
|
|
aria-required={required != null ? required : undefined}
|
|
|
|
className={clsx(
|
|
|
|
className={clsx(
|
|
|
|
'space-y-4',
|
|
|
|
'space-y-2',
|
|
|
|
orientation === 'horizontal' &&
|
|
|
|
orientation === 'horizontal' &&
|
|
|
|
'sm:flex sm:items-center sm:space-y-0 sm:space-x-10',
|
|
|
|
'sm:flex sm:items-center sm:space-y-0 sm:space-x-10',
|
|
|
|
)}>
|
|
|
|
)}
|
|
|
|
|
|
|
|
role="radiogroup">
|
|
|
|
{children}
|
|
|
|
{children}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</fieldset>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</RadioListContext.Provider>
|
|
|
|
</RadioListContext.Provider>
|
|
|
|
);
|
|
|
|
);
|
|
|
|