Skip to content

Components · Forms

DurationInput

Free-text duration field ("1h 30m", "90m", "1.5h") with a minutes value and arrow-key steps.

@rookcom/monarch-react/schedulingComponent

Estimate

Type 1h 30m, 90m or 1.5h. Arrow keys step by 15 minutes.

Usage examples

Limits, days and small

Purpose

Enters a length of time the way people say it and stores whole minutes: estimates, time logged, meeting lengths, SLA targets. Import from @rookcom/monarch-react/scheduling.

Use when

  • A duration in minutes or hours: estimates, time tracking, buffers, response targets.

Avoid when

  • A time of day: use a time picker.
  • A date range: use DateRangePicker.
  • A handful of fixed lengths (15, 30, 60 minutes): use SegmentedControl or Select.

Design principles

  • Accept what people type: "90", "90m", "1h 30m", "1h30", "1.5h", "1,5h", "1:30", "2 hours", "1d 4h".
  • Show it back one way ("1h 30m") so the value is unambiguous.
  • Never save a guess: unreadable or out-of-range text stays, is flagged and explained.
  • The value is always whole minutes; formatting and parsing are replaceable for other conventions.

API reference

import { DurationInput, formatDuration, parseDuration } from '@rookcom/monarch-react/scheduling';
ExportKindRenderingRoot class
DurationInputcomponentclient.mn-duration-input-
formatDurationutilityclient–
parseDurationutilityclient–

Renders .mn-duration-input wrapping the input and its message. The ref and native input props go to the input; className and style go to the wrapper.

Helpers: parseDuration(text, { minutesPerDay }) returns minutes, null (empty) or NaN; formatDuration(minutes, { largestUnit, minutesPerDay }).

Generated from the TypeScript declarations of packages/react/src/components/forms/duration-input.

Properties

onValueChange fires on blur, Enter and arrow steps, never on every keystroke.

DurationInput

Free-text duration field with a minutes value. Reads "1h 30m", "90m", "1.5h" or "1:30", shows the value back as "1h 30m", and steps with the arrow keys. Unreadable or out-of-range text is kept, flagged and explained, never saved. The input is a `spinbutton` with a spoken value.

PropTypeDescription
childrenReactNode
classNamestring
defaultValuenumber | null
disabledboolean
format((minutes: number) => string)Custom display format. Default `formatDuration`.
idstring
invalidboolean
largestUnit"h" | "d"Largest unit when formatting: `h` (default) or `d`.
maxnumberLargest allowed value in minutes.
messagesPartial<DurationInputMessages>Interface text, for translation.
minnumberSmallest allowed value in minutes. Default 0.
minutesPerDaynumberMinutes in a "d" when parsing and formatting. Default 1440.
onValueChange((minutes: number | null) => void)Called with minutes (or `null` when cleared) on blur, Enter or an arrow-key step; never while typing.
parse((text: string) => number | null)Custom parser: minutes, `null` for empty, or `NaN` when unreadable. Default `parseDuration`.
size"sm" | "md" | "lg"
stepnumberMinutes added or removed by ArrowUp / ArrowDown. Default 15. PageUp / PageDown step by 60.
valuenumber | nullDuration in whole minutes (controlled). `null` is empty.

Variants

PropValuesDescription
size'sm''md''lg'Input height from --mn-control-*.
largestUnit'h''d'"26h" or "1d 2h". Use minutesPerDay={480} for 8-hour work days.

States

StateExposed asBehaviour
Unreadablearia-invalid="true""Use a duration like 1h 30m, 90m or 1.5h." under the field; the text is kept.
Out of rangearia-invalid="true""Enter between 15m and 2h." from min and max.
Empty–The value is null; aria-valuenow is removed.
Disabled / read-onlydisabled, readonlyKeys do nothing.

Interaction behaviour

  • Typing never changes the value; blur or Enter reads the text, saves minutes and reformats it.
  • Clearing the text saves null.
  • Escape restores the last saved value and clears the message.
  • Arrow steps snap to the step grid (50m + 15 → 1h) and clamp to min and max.

Keyboard support

KeysAction
ArrowUp / ArrowDownAdds or removes step minutes (default 15).
PageUp / PageDownMoves to the next or previous whole hour.
Home / EndSets min / max (End only with max).
EnterReads and saves the text.
EscapeRestores the saved value.

Accessibility

Role
spinbutton
Accessible name
A FieldLabel or aria-label.
  • aria-valuenow is the minutes, aria-valuetext a spoken form ("1 hour 30 minutes"), aria-valuemin / aria-valuemax the limits.
  • The error message is linked with aria-describedby and announced as an alert.
  • Field descriptions and errors are wired as for Input.

Responsive behaviour

  • Fills its container; set a width on the Field. Numbers are tabular so values do not jitter.

Theme support

  • Input tokens; the message uses --mn-color-danger-strong in every theme.

Do and don't

Do

  • Say in a FieldDescription which formats work when the field is new to people.
  • Set min and max when the product has limits, so the message can say them.

Don't

  • Do not use a number input with a separate unit select for durations people type.
  • Do not save on every keystroke; wait for onValueChange.
  • Input: Plain text and number input.
  • Field: Label, help and error wiring.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsField

Uses the .mn-input styles from text-field.

Testing coverage

  • Parses 13 input shapes, rejects unreadable text, honours minutesPerDay, formats h/m and days.
  • Commits on blur with spoken value; unreadable text is kept, flagged, explained and Escape restores.
  • Range message; arrow, page, Home and End steps with snapping and clamping.
  • Clearing to null, controlled updates, custom format and read-only.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/duration-input; Cross-browser axe: /components/duration-input

Tests: packages/react/src/components/forms/duration-input