Text input component #30

Merged
stne3960 merged 35 commits from text_input into main 2025-12-16 17:59:33 +01:00
2 changed files with 22 additions and 12 deletions
Showing only changes of commit a4657951fb - Show all commits

View File

@ -12,7 +12,8 @@ export interface TextInputProps
error?: boolean; error?: boolean;
stne3960 marked this conversation as resolved
Review

Without having seen an example of a full form with validation it's hard to tell if this is the path forward. We can probably leave it for now.

I am wondering about using built-in form validation and the :valid and related pseudo-selectors and how it works with accessibility without it.

Without having seen an example of a full form with validation it's hard to tell if this is the path forward. We can probably leave it for now. I am wondering about using built-in form validation and the `:valid` and related pseudo-selectors and how it works with accessibility without it.
Review

I’d keep the error prop for now. Native validation works for some cases but gets awkward once you have server errors or custom validation. An explicit error state keeps the component predictable.

I’d keep the error prop for now. Native validation works for some cases but gets awkward once you have server errors or custom validation. An explicit error state keeps the component predictable.
fullWidth?: boolean; fullWidth?: boolean;
customWidth?: string; customWidth?: string;
label?: string; label: string;
stne3960 marked this conversation as resolved Outdated

Should label really be optional? Will that not lead to accessibility issues?

Should label really be optional? Will that not lead to accessibility issues?

In practical implementation, probably not, that was how the component was designed in the specification.

In practical implementation, probably not, that was how the component was designed in the specification.
hideLabel?: boolean;
message?: string; message?: string;
} }
@ -70,6 +71,7 @@ export default function TextInput({
fullWidth = false, fullWidth = false,
customWidth, customWidth,
label, label,
hideLabel = false,
message, message,
className = "", className = "",
...props ...props
@ -78,6 +80,7 @@ export default function TextInput({
const widthStyle = customWidth ? { width: customWidth } : undefined; const widthStyle = customWidth ? { width: customWidth } : undefined;
const stateClasses = error ? errorStateClasses : defaultStateClasses; const stateClasses = error ? errorStateClasses : defaultStateClasses;
const inputPadding = Icon ? "pr-(--padding-md)" : "px-(--padding-md)"; const inputPadding = Icon ? "pr-(--padding-md)" : "px-(--padding-md)";
const showVisibleLabel = !hideLabel;
const inputField = ( const inputField = (
<div <div
@ -103,6 +106,7 @@ export default function TextInput({
)} )}
<input <input
id={inputId} id={inputId}
aria-label={hideLabel ? label : undefined}
className={clsx( className={clsx(
"flex-1 min-w-0 h-full bg-transparent border-none outline-none", "flex-1 min-w-0 h-full bg-transparent border-none outline-none",
"text-base-ink-max placeholder:text-base-ink-placeholder", "text-base-ink-max placeholder:text-base-ink-placeholder",
@ -114,13 +118,13 @@ export default function TextInput({
</div> </div>
); );
if (!label && !message) { if (!showVisibleLabel && !message) {
return inputField; return inputField;
} }
return ( return (
<div className="flex flex-col gap-(--spacing-sm)"> <div className="flex flex-col gap-(--spacing-sm)">
{label && ( {showVisibleLabel && (
<label htmlFor={inputId} className="body-bold-md text-base-ink-strong"> <label htmlFor={inputId} className="body-bold-md text-base-ink-strong">
{label} {label}
</label> </label>

View File

@ -49,9 +49,9 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input Sizes</h2> <h2 className="mb-md">Text Input Sizes</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput size="sm" placeholder="Small" /> <TextInput size="sm" placeholder="Small" label="Small input" hideLabel />
<TextInput size="md" placeholder="Medium" /> <TextInput size="md" placeholder="Medium" label="Medium input" hideLabel />
<TextInput size="lg" placeholder="Large" /> <TextInput size="lg" placeholder="Large" label="Large input" hideLabel />
</div> </div>
</section> </section>
@ -62,16 +62,22 @@ export default function ComponentLibrary() {
size="sm" size="sm"
placeholder="Small with icon" placeholder="Small with icon"
Icon={SearchIcon} Icon={SearchIcon}
label="Small search"
hideLabel
/> />
<TextInput <TextInput
size="md" size="md"
placeholder="Medium with icon" placeholder="Medium with icon"
Icon={SearchIcon} Icon={SearchIcon}
label="Medium search"
hideLabel
/> />
<TextInput <TextInput
size="lg" size="lg"
placeholder="Large with icon" placeholder="Large with icon"
Icon={SearchIcon} Icon={SearchIcon}
label="Large search"
hideLabel
/> />
</div> </div>
</section> </section>
@ -79,24 +85,24 @@ export default function ComponentLibrary() {
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input States</h2> <h2 className="mb-md">Text Input States</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput placeholder="Default" /> <TextInput placeholder="Default" label="Default state" hideLabel />
<TextInput placeholder="Error state" error /> <TextInput placeholder="Error state" error label="Error state" hideLabel />
</div> </div>
</section> </section>
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input With/Without Placeholder</h2> <h2 className="mb-md">Text Input With/Without Placeholder</h2>
<div className="flex flex-wrap items-center gap-md"> <div className="flex flex-wrap items-center gap-md">
<TextInput placeholder="Placeholder" /> <TextInput placeholder="Placeholder" label="With placeholder" hideLabel />
<TextInput /> <TextInput label="Without placeholder" hideLabel />
</div> </div>
</section> </section>
<section className="mt-lg"> <section className="mt-lg">
<h2 className="mb-md">Text Input Width Options</h2> <h2 className="mb-md">Text Input Width Options</h2>
<div className="flex flex-col gap-md"> <div className="flex flex-col gap-md">
<TextInput placeholder="Full width" fullWidth /> <TextInput placeholder="Full width" fullWidth label="Full width input" hideLabel />
<TextInput placeholder="Custom width" customWidth="300px" /> <TextInput placeholder="Custom width" customWidth="300px" label="Custom width input" hideLabel />
</div> </div>
</section> </section>