Input group
PortedAn input with icons, text or a button inside its frame, for example a search field. With label, it is wrapped in a Field.
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@etched/monet/input-group";
export function Preview() {
return (
<InputGroup>
<InputGroupAddon>
<MagnifyingGlassIcon />
</InputGroupAddon>
<InputGroupInput type="search" aria-label="Search candidates" placeholder="Search candidates" />
</InputGroup>
);
}Installation
import { InputGroup, InputGroupAddon, InputGroupInput, InputGroupSuffix, InputGroupTextarea } from "@etched/monet/input-group";Examples
Search
An icon before the field and a clear button after it. A press on the icon focuses the field.
import { MagnifyingGlassIcon, XIcon } from "@phosphor-icons/react";
import { useState } from "react";
import { Button } from "@etched/monet/button";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@etched/monet/input-group";
export function Search() {
const [query, setQuery] = useState("thermal");
return (
<div className="grid">
<InputGroup>
<InputGroupAddon>
<MagnifyingGlassIcon />
</InputGroupAddon>
<InputGroupInput aria-label="Search runs" placeholder="Search runs" value={query} onChange={(event) => setQuery(event.target.value)} />
{query && (
<InputGroupAddon align="end">
<Button size="sm" variant="ghost" shape="square" aria-label="Clear search" icon={<XIcon />} onClick={() => setQuery("")} />
</InputGroupAddon>
)}
</InputGroup>
</div>
);
}Text and sizes
https://
⌘K
import { MagnifyingGlassIcon } from "@phosphor-icons/react";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@etched/monet/input-group";
export function TextAndSizes() {
return (
<div className="grid">
<InputGroup>
<InputGroupAddon>https://</InputGroupAddon>
<InputGroupInput aria-label="Site" defaultValue="tracer.etched.com" />
</InputGroup>
<InputGroup size="sm">
<InputGroupAddon>
<MagnifyingGlassIcon />
</InputGroupAddon>
<InputGroupInput aria-label="Filter rows" placeholder="Filter rows" />
<InputGroupAddon align="end">⌘K</InputGroupAddon>
</InputGroup>
</div>
);
}Suffix
InputGroupSuffix follows the typed value, so the user sees the whole address as they type.
.etched.ai
Letters, numbers and dashes.
import { InputGroup, InputGroupInput, InputGroupSuffix } from "@etched/monet/input-group";
export function Suffix() {
return (
<div className="grid">
<InputGroup label="Workspace address" description="Letters, numbers and dashes.">
<InputGroupInput defaultValue="tracer" />
<InputGroupSuffix>.etched.ai</InputGroupSuffix>
</InputGroup>
</div>
);
}In a field
label, description and error wrap the group in a Field; the label names the field inside. required={false} adds the optional marker.
L-
Lots start with L-.
@etched.com
Enter an email address.
https://
Read-only: set by the scheduling tool.
import { LinkIcon } from "@phosphor-icons/react";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@etched/monet/input-group";
export function InAField() {
return (
<div className="grid">
<InputGroup label="Lot" description="Lots start with L-.">
<InputGroupAddon>L-</InputGroupAddon>
<InputGroupInput placeholder="7801" />
</InputGroup>
<InputGroup label="Owner email" error="Enter an email address.">
<InputGroupInput type="email" defaultValue="sam@" />
<InputGroupAddon align="end">@etched.com</InputGroupAddon>
</InputGroup>
<InputGroup label="Portfolio" required={false}>
<InputGroupAddon>https://</InputGroupAddon>
<InputGroupInput placeholder="example.com" />
</InputGroup>
<InputGroup label="Calendar link" description="Read-only: set by the scheduling tool.">
<InputGroupAddon>
<LinkIcon />
</InputGroupAddon>
<InputGroupInput defaultValue="cal.etched.ai/ana-ruiz" readOnly />
</InputGroup>
</div>
);
}Accessibility
- The group is a plain
divwith no role. The field inside is named by the group'slabel, or by its ownaria-label. - Text and the suffix in addons are not linked to the field, so screen readers do not hear them with it. Icons are hidden.
- Put needed addon text, such as a unit or the suffix, in the label or
descriptiontoo. Give an icon-only buttonaria-label. - Put
requiredon the field inside. The group'srequired={false}only adds the optional marker.
API reference
InputGroupAddon
| Prop | Type | Default | Description |
|---|---|---|---|
| align | "start" | "end" | "top" | "bottom" | "start" | Before or after the field, whatever its place in the code; above or below a textarea. |
| children * | ReactNode | – | An icon, short text, or a small ghost Button (an icon-only one needs aria-label). Above or below a textarea, a button that comes last goes to the end. |
| className | string | – |
InputGroup
| Prop | Type | Default | Description |
|---|---|---|---|
| label | ReactNode | – | Visible label. Without one, give the control an aria-label. |
| labelTooltip | ReactNode | – | More about the field, behind an info button after the label. |
| labelTooltipLabel | string | "More information" | Names the info button. |
| description | ReactNode | – | Help text below the control. Hidden while an error shows. |
| error | FieldError | – | |
| size | "sm" | "base" | "base" | |
| required | boolean | – | false adds the optional marker after the label. To make the value required, put the native required on the field inside. |
| children * | ReactNode | – | An InputGroupInput or InputGroupTextarea, with InputGroupAddons around it and an InputGroupSuffix after it. A Button placed directly in the group (not in an addon) is joined to the frame's end, with its own edge. |
| className | string | – |
InputGroupSuffix
| Prop | Type | Default | Description |
|---|---|---|---|
| children * | ReactNode | – | Text that follows the typed value, such as ".etched.ai". |
| className | string | – |
InputGroupTextarea
| Prop | Type | Default | Description |
|---|---|---|---|
| onValueChange | (value: string) => void | – | Called with the new text on each change. |
| minRows | number | 2 | The fewest lines it shows. |
| maxRows | number | – | The most lines it grows to; past them the text scrolls. |
Comment box
InputGroupTextareagrows with its text. Addons withalign="top"or"bottom"go above or below it; a button that comes last goes to the end.