53 lines
1.3 KiB
React
53 lines
1.3 KiB
React
import { useId } from "react";
|
|
|
|
/**
|
|
* @param {Omit<React.InputHTMLAttributes<HTMLInputElement>, "id"> & {
|
|
* id?: string,
|
|
* label: string,
|
|
* description?: string,
|
|
* error?: string
|
|
* }} props
|
|
*/
|
|
export function TextField({
|
|
id,
|
|
label,
|
|
description,
|
|
error,
|
|
className = "",
|
|
required,
|
|
...inputProps
|
|
}) {
|
|
const generatedId = useId();
|
|
const inputId = id ?? `field-${generatedId}`;
|
|
const descriptionId = description ? `${inputId}-description` : undefined;
|
|
const errorId = error ? `${inputId}-error` : undefined;
|
|
const describedBy = [descriptionId, errorId].filter(Boolean).join(" ");
|
|
|
|
return (
|
|
<div className={`ui-field ${className}`.trim()}>
|
|
<label className="ui-field__label" htmlFor={inputId}>
|
|
{label}
|
|
{required ? <span aria-hidden="true"> *</span> : null}
|
|
</label>
|
|
{description ? (
|
|
<p className="ui-field__description" id={descriptionId}>
|
|
{description}
|
|
</p>
|
|
) : null}
|
|
<input
|
|
{...inputProps}
|
|
className="ui-field__input"
|
|
id={inputId}
|
|
required={required}
|
|
aria-describedby={describedBy || undefined}
|
|
aria-invalid={error ? "true" : undefined}
|
|
/>
|
|
{error ? (
|
|
<p className="ui-field__error" id={errorId}>
|
|
{error}
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
);
|
|
}
|