All components (204)
VAT rate
Fields · VatRateSelect
What it is and when to use it
A VAT rate selector: 21 % standard, 10 % reduced, 4 % super-reduced and 0 % exempt.
When to use it
- Invoice lines, quotes, product records: anywhere a VAT rate is chosen.
- It delivers the percentage as a number (
21), which is what is multiplied later. - With
ratesother rates are offered (a Canary Islands IGIC, a rate from another country): those without a name show only their percentage.
When not to
- A free percentage:
NumberField.
Keyboard: that of Select: Down opens, the arrow keys move through, Enter chooses, Escape closes.
Accessibility: it is a Select, with its label ("VAT rate"), help text and error linked.
Fine styling: no variables of its own: those of Select and the field.
What it looks like
21% · standard
10% · reduced
4% · super-reduced
0% · exempt
Base €129.90 + VAT €27.28 = €157.18.
<MoneyField label="Sale price" value={precio} onChange={setPrecio} max={5000} />
<VatRateSelect value={iva} onChange={setIva} />Props
Import it with import { VatRateSelect } from '@amset/ui'. Required props are marked.
| Prop | Type | What it does |
|---|---|---|
className | string | Merged with the library classes; it does not replace them. |
defaultOpen | boolean | The list starts open. To photograph the panel on the test bench. |
disabled | boolean | — |
error | string | — |
excludeFromTabOrder | boolean | Out of the tab order. It can still be focused by hand (`.focus()`), which is the difference with disabling it: a disabled control cannot be focused in any way. |
hiddenLabel | boolean | The label for the screen reader only. For when the field sits inside another one that already explains itself (the prefix of a phone), never to save space: without a visible label you cannot tell what is being chosen. |
hint | string | — |
id | string | It goes to the TRIGGER, which is the focusable node and the one carrying the role of the field: it is the one that must be pointable from outside with an `aria-controls`, a step of a guided tour or a shortcut. The inner parts (the label, the list, the hint) keep their own `id`s, derived from this one when it is passed, so they do not repeat with two selectors on the same page. |
initialValue | number | null | — |
invalid | boolean | — |
label | string | — |
loading | boolean | The options are arriving. Marks the field as busy, shows a ring and a `loadingText` row in the list; the options that were already there stay. |
loadingText | string | What the loading row says. "Loading…" by default. |
name | string | So that the value travels in a native form. |
native | boolean | A SYSTEM `<select>` instead of the own panel. On a phone the operating system's picker appears, which is what is wanted when the list is long or is filled in with a finger. The icons and the two lines per option are lost (`renderOption`, `renderValue`, `icon`, `description`); groups (`group`) come out as `<optgroup>`. Same field on the outside and same props. |
offset | number | Separation in pixels between the field and the list. None by default. |
onBlur | () => void | On leaving the field. The form uses it to mark that it has been touched. |
onChange | (rate: number | null) => void | — |
onChangeEvent | (event: ChangeEvent<HTMLSelectElement>) => void | With `native`: the `<select>` event, to migrate a handler that reads `e.target.value`. `onChange` still delivers the key, and both are called. |
onOpenChange | (open: boolean) => void | — |
open | boolean | Open or closed, driven from OUTSIDE. With this the state stops living here: whoever passes it decides, and `onOpenChange` notifies of each attempt. It is needed to open the list from somewhere else (a keyboard shortcut, a step of a guided tour) without touching the trigger. |
panelWidth | "content" | "trigger" | How wide the list is. `trigger` (the default) leaves it the same as the field, which is what is expected of a normal selector. `content` lets it grow until the options fit. It is needed when the trigger is deliberately NARROWER than the options: a phone prefix shows "+34" and the list says "+34 · Spain". Measured before this existed: the panel came out 69 px wide and the 19 options appeared cut mid-word ("Spai", "Portu"). |
placeholder | string | Text when nothing is chosen. It does not replace the label. |
placement | Placement | Where the list appears. By default, attached right below the field. |
rates | readonly number[] | The rates on offer, from highest to lowest. By default, those of Spanish VAT (21, 10, 4 and 0). |
readOnly | boolean | — |
renderOption | OptionRender<ListOption> | Paints the inside of each option in the list: an avatar, two lines, a price. The name the screen reader reads is still `label`. |
renderValue | (option: SelectOption) => ReactNode | Paints what is chosen inside the closed field. Without this the icon and the label (or `shortLabel`) show. What is announced is what is painted, so it should carry the text. |
required | boolean | — |
shouldCloseOnSelect | boolean | Choosing an option does NOT close the list. By default it closes it, which is what anyone expects in a single-option dropdown; this is for whoever builds on top something that lets you keep looking. |
size | "sm" | "md" | "lg" | — |
validationBehavior | "aria" | "native" | — |
value | number | null | The chosen rate (`21`), controlled from outside. |
variant | FieldVariant | — |
It also accepts the attributes of the element it renders: className, style, data-*, aria-* and its events.
In the test bench
Every state and variant, to try one at a time, on its page at ui.amset.tech:
