Skip to content

Components · Date, time and scheduling

WeeklyHoursEditor

Edits weekly availability: days on or off, several intervals a day, copy and overlap checks.

@rookcom/monarch-react/schedulingComponent

Controlled with validation

Weekly hours
Times in Europe/London
  • Unavailable

  • Unavailable

37.5 hours a week

Usage examples

Uncontrolled, 12-hour clock, week from Sunday

Read-only

Purpose

Lets people set the hours they (or a team, or a support desk) are available each week. Needed by: calendar, tickets.

Use when

  • Booking availability, business hours, support or on-call hours.
  • Any setting that is "these times on these weekdays".

Avoid when

  • One-off dates (holidays, time off): use a date list next to it.
  • Shift rotas that differ week to week: that is a scheduling grid, not a weekly pattern.

Design principles

  • The value is plain data, { day, start, end }[] in minutes, a plain, serialisable shape, so it goes to the API and to TimeGrid workingHours unchanged.
  • Each day is switched on or off; switching off and on again restores the day’s intervals.
  • Times are picked from a list (step minutes), never typed free-hand, so there are no parse errors.
  • Validation is shown under the day and exported as validateWeeklyHours; the product decides whether to block saving.
  • The time zone is shown, not assumed: timeZoneLabel holds text or the product’s timezone picker.

API reference

import { WeeklyHoursEditor, validateWeeklyHours } from '@rookcom/monarch-react/scheduling';
ExportKindRenderingRoot class
WeeklyHoursEditorcomponentclient.mn-weekly-hours-editor
validateWeeklyHoursutilityclient–

Renders a <fieldset> (ref) with the legend, the zone slot and one row per weekday in weekStartsOn order (0 = Sunday … 6 = Saturday, as in the date pickers). Interval day values are ISO weekdays (1 = Monday … 7 = Sunday), Rook Calendar’s stored shape.

Controlled with value + onValueChange(value, errors), or uncontrolled with defaultValue. onValueChange emits intervals ordered by weekday, each day’s intervals in the order the user arranged them.

name adds a hidden input holding the value as JSON, for plain form posts.

validateWeeklyHours(value) returns { [weekday]: "range" | "order" | "overlap" } for invalid days.

Times use Monarch NativeSelect (the platform list works well on phones and with 97 options); a separate TimeInput/TimeSelect component is not part of this wave.

Generated from the TypeScript declarations of packages/react/src/components/scheduling/weekly-hours-editor.

Properties

WeeklyHoursEditor

Edits weekly availability: each day switched on or off, one or more time intervals per day, copying a day to the whole week and overlap checks. The value is a plain list of `{ day, start, end }` intervals in minutes.

PropTypeDescription
childrenReactNode
classNamestring
defaultInterval{ start: number; end: number; }The interval a day gets when switched on for the first time. Default 09:00–17:00.
defaultValueWeeklyHoursValueUncontrolled starting value. Default: no hours on any day.
disabledboolean
hourCycleHourCycleForce a 12- or 24-hour clock (`'12'` or `'24'`, as in the date pickers); by default the locale decides.
idstring
labelsWeeklyHoursEditorLabels
legendReactNodeVisible group title. Default "Weekly hours".
localestringBCP 47 locale for day names and times.
maxIntervalsnumberMost intervals per day. Default 6.
namestringSubmits the value as JSON in a hidden input with this name.
onValueChange((value: WeeklyHoursInterval[], errors: Partial<Record<number, WeeklyHoursError>>) => void)Called with every change, intervals ordered by ISO weekday, and the current validation result (see `validateWeeklyHours`) so the product can block saving while it is not empty.
readOnlybooleanShows the hours as text, without controls.
stepnumberMinutes between time options. Default 15.
timeZoneLabelReactNodeSlot beside the legend for the zone the hours are in: text such as "Times in Asia/Kolkata", or a timezone picker.
valueWeeklyHoursValueControlled value.
weekStartsOnWeekdayFirst row: 0 = Sunday … 6 = Saturday, as in MiniMonth and the date pickers. Default 1 (Monday). (Interval `day` values stay ISO 1–7.)

Variants

PropValuesDescription
readOnly'true''false'Shows each day’s hours as text ("09:00 – 17:00"), without controls.
disabled'true''false'Disables the whole fieldset.
step'5''10''15''30''60'Minutes between time options. Default 15.
hourCycle'12''24'Force a 12- or 24-hour clock; 24-hour shows "24:00" for the end of the day, 12-hour "Midnight".

States

StateExposed asBehaviour
Day ondata-on (row)Switch on, intervals shown, copy button available.
Day off–"Unavailable" in muted text.
Invaliddata-invalid (row), aria-invalid (selects)Danger-coloured message under the day (role="alert"), linked to its selects.
Read-onlydata-readonlyText only.
Disabled–Native fieldset disabling of every control.

Interaction behaviour

  • Switching a day on adds defaultInterval (09:00–17:00), or restores the intervals it had when switched off.
  • "+" adds an interval an hour after the last one (an hour long); "×" removes one. At most maxIntervals per day.
  • "Copy to all" copies that day’s intervals to every day of the week.
  • Errors appear as soon as a day is invalid: times outside the day, an interval ending before it starts, or overlapping intervals.

Keyboard support

KeysAction
Tab / Shift+TabThrough each day’s switch, selects, remove/add buttons and copy button, in reading order.
SpaceToggles a day’s switch.
Arrow keysChange a time in a focused select (platform behaviour).
Enter / SpaceActivate the add, remove and copy buttons.

Accessibility

Role
group (fieldset with legend); one group per day
Accessible name
The fieldset is named by legend; each switch and day group by the weekday; selects "Monday interval 1 start".
  • Buttons have full names: "Add Monday interval", "Remove Monday interval 2", "Copy to all days from Monday" (the visible "Copy to all" starts the name).
  • Errors use role="alert" and are referenced by the day’s selects through aria-describedby.
  • Day and time names come from Intl in locale; all fixed strings are in labels.
  • Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/weekly-hours-editor.

Responsive behaviour

  • A container query stacks each day below 34rem: the name and copy button on one line, intervals underneath.
  • Interval rows wrap, so two selects and their buttons fit at 320px.

Theme support

  • Built from Monarch Switch, NativeSelect and Button, so every theme and the product accent apply.
  • Errors use --mn-color-danger-strong.

Do and don't

Do

  • Show the time zone the hours are in.
  • Block saving while validateWeeklyHours returns errors, and validate again on the server.
  • Pass the workspace’s week start so rows match the calendar.

Don't

  • Do not store times as display strings; keep minutes from midnight.
  • Do not put holidays or one-off closures in weekly hours.
  • TimeGrid: Shades time outside the same value (workingHours).
  • TimezoneSelect: Goes in the timeZoneLabel slot.
  • Switch: The per-day on/off control.
  • Field: NativeSelect provides the time lists.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton, Field, Switch

Testing coverage

  • validateWeeklyHours: range, order and overlap per day.
  • Switching a day on adds the default interval (uncontrolled) and reports it.
  • Add, edit and remove intervals.
  • Overlap shows an alert linked to the selects with aria-invalid.
  • Copy to all days.
  • Switching a day off and on restores its intervals.
  • Controlled mode and the hidden form input.
  • Week start order, 12-hour labels and Tab order.
  • Read-only text, zone label and disabled fieldset; no axe violations; renders on the server.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/weekly-hours-editor; Cross-browser axe: /components/weekly-hours-editor

Tests: packages/react/src/components/scheduling/weekly-hours-editor