Files
mastodon-sakyey/app/javascript/mastodon/components/form_fields/fieldset.tsx

86 lines
2.4 KiB
TypeScript

/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
import type { ReactNode, FC } from 'react';
import { createContext, useId } from 'react';
import { A11yLiveRegion } from 'mastodon/components/a11y_live_region';
import type { FieldStatus } from 'mastodon/components/callout_inline';
import { CalloutInline } from 'mastodon/components/callout_inline';
import classes from './fieldset.module.scss';
import { getFieldStatus } from './form_field_wrapper';
import formFieldWrapperClasses from './form_field_wrapper.module.scss';
interface FieldsetProps {
legend: ReactNode;
hint?: ReactNode;
name?: string;
status?: FieldStatus | FieldStatus['variant'];
layout?: 'vertical' | 'horizontal';
children: ReactNode;
}
export const FieldsetNameContext = createContext<string | undefined>(undefined);
/**
* A fieldset suitable for wrapping a group of checkboxes,
* radio buttons, or other grouped form controls.
*/
export const Fieldset: FC<FieldsetProps> = ({
legend,
hint,
name,
status,
layout,
children,
}) => {
const uniqueId = useId();
const labelId = `${uniqueId}-label`;
const hintId = `${uniqueId}-hint`;
const statusId = `${uniqueId}-status`;
const fieldsetName = name || `${uniqueId}-fieldset-name`;
const hasHint = !!hint;
const fieldStatus = getFieldStatus(status);
const hasStatusMessage = !!fieldStatus?.message;
const descriptionIds = [
hasHint ? hintId : '',
hasStatusMessage ? statusId : '',
]
.filter((id) => !!id)
.join(' ');
return (
<fieldset
className={classes.fieldset}
data-has-error={status === 'error'}
aria-labelledby={labelId}
aria-describedby={descriptionIds}
>
<div className={formFieldWrapperClasses.labelWrapper}>
<div id={labelId} className={formFieldWrapperClasses.label}>
{legend}
</div>
{hasHint && (
<p id={hintId} className={formFieldWrapperClasses.hint}>
{hint}
</p>
)}
</div>
<div className={classes.fieldsWrapper} data-layout={layout}>
<FieldsetNameContext.Provider value={fieldsetName}>
{children}
</FieldsetNameContext.Provider>
</div>
{/* Live region must be rendered even when empty */}
<A11yLiveRegion className={classes.status} id={statusId}>
{hasStatusMessage && <CalloutInline {...fieldStatus} />}
</A11yLiveRegion>
</fieldset>
);
};