Components · Date, time and scheduling
AgendaList
Events grouped by day with sticky day headings, a Today marker and an empty state.
Agenda
Wednesday 4 November, Today, 3 events
Thursday 5 November, 1 event
- Time off · Priya
Friday 6 November, 1 event
import { AgendaList, type CalendarEvent } from '@rookcom/monarch-react/scheduling';
const at = (day: string, time: string) => `${day}T${time}:00Z`;
const EVENTS: CalendarEvent[] = [
{ id: 'e1', title: 'Team offsite', start: '2026-11-04', allDay: true, color: '#16a34a' },
{
id: 'e2',
title: 'Stand-up',
start: at('2026-11-04', '09:00'),
end: at('2026-11-04', '09:15'),
color: '#0f766e',
},
{
id: 'e3',
title: 'Design review',
start: at('2026-11-04', '11:00'),
end: at('2026-11-04', '12:00'),
detail: 'Room 4',
color: '#7c3aed',
},
{
id: 'e4',
title: 'Vendor call',
start: at('2026-11-05', '14:00'),
end: at('2026-11-05', '14:30'),
status: 'tentative',
detail: 'Video call',
color: '#2563eb',
},
{
id: 'e5',
title: 'Retrospective',
start: at('2026-11-06', '16:00'),
end: at('2026-11-06', '17:00'),
status: 'cancelled',
color: '#c2410c',
},
];
const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;
<div style={{ width: '100%', maxWidth: 560 }}>
<AgendaList
{...base}
events={EVENTS}
aria-label="Agenda"
dayNotes={(day) => (day === '2026-11-05' ? ['Time off · Priya'] : undefined)}
style={{ maxHeight: 360 }}
/>
</div>Usage examples
Empty
import { AgendaList } from '@rookcom/monarch-react/scheduling';
const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;
<div style={{ width: '100%', maxWidth: 560 }}>
<AgendaList {...base} events={[]} />
</div>Custom empty state and range
import { Button } from '@rookcom/monarch-react';
import { AgendaList } from '@rookcom/monarch-react/scheduling';
const base = { timeZone: 'UTC', locale: 'en-GB', today: '2026-11-04' } as const;
<div style={{ width: '100%', maxWidth: 560 }}>
<AgendaList
{...base}
events={[]}
from="2026-11-09"
days={7}
empty={
<div style={{ display: 'grid', gap: 8, justifyItems: 'start', padding: 16 }}>
<p style={{ margin: 0 }}>No bookings next week.</p>
<Button size="sm">Share booking link</Button>
</div>
}
/>
</div>Purpose
Lists upcoming events in time order, one section per day, for an agenda view, a dashboard card or a narrow screen where a grid does not fit. Needed by: calendar.
Use when
- The agenda view of a calendar, or the phone layout of a week.
- An "Upcoming" card listing the next bookings, interviews or deadlines.
Avoid when
- Overlaps and free time matter: use TimeGrid.
- The user scans a whole month: use CalendarMonthView.
- The rows are not events (a task list, a feed): use a plain list.
Design principles
- Days are sections with a real heading, so the outline and screen-reader heading navigation follow the days.
- All-day events come first in a day, then timed events by start time.
- A time column on the left keeps titles aligned; below 24rem it moves above the title.
- Today is marked by text ("Today") and colour, never colour alone.
- The product supplies data and callbacks; the list only orders, groups and formats.
API reference
import { AgendaList } from '@rookcom/monarch-react/scheduling';| Export | Kind | Rendering | Root class |
|---|---|---|---|
AgendaList | component | client | .mn-agenda-list |
Renders a <div> (ref) containing one <section aria-labelledby> per day with an h3 (see headingLevel) and a <ul> of EventChip bar chips.
Days come from the events (from to the last event) or from from + days; showEmptyDays lists days with nothing.
Times are formatted with Intl in timeZone and locale; pass both (and today) for identical server and client output.
Generated from the TypeScript declarations of packages/react/src/components/scheduling/agenda-list.
Properties
AgendaList
Events grouped by day, in order: sticky day headings with a count and a "Today" marker, all-day events first, an empty state when nothing is scheduled, and arrow keys to move between events.
| Prop | Type | Description |
|---|---|---|
events required | readonly CalendarEvent[] | |
className | string | |
dayNotes | ((day: string) => readonly ReactNode[] | undefined) | Informational rows shown first in a day, such as time off or a holiday. A day with notes is listed even when it has no events. |
days | number | How many days from `from` to cover. Default: through the last event. |
empty | ReactNode | Replaces the default empty state. |
from | string | First day listed, `'2026-11-04'`. Default: the first day with an event. Events that started earlier but are still running appear on it. |
headingLevel | 3 | 4 | 6 | 2 | 5 | Level of the day headings. Default 3. |
hourCycle | HourCycle | Force a 12- or 24-hour clock (`'12'` or `'24'`, as in the date pickers); by default the locale decides. |
id | string | |
labels | AgendaListLabels | |
locale | string | |
onEventClick | ((event: CalendarEvent) => void) | |
renderEvent | ((event: CalendarEvent, chip: ReactElement) => ReactNode) | Wrap or replace each chip (for example in a PopoverTrigger). Keep one focusable element. |
showEmptyDays | boolean | List days in the range that have nothing. Default `false`. |
timeZone | string | IANA zone events are grouped in. Defaults to the runtime's; pass it for server rendering. |
today | string | null | Today, for the marker. Inject for server rendering; read from the clock after mount when left out; `null` for none. |
Variants
| Prop | Values | Description |
|---|---|---|
headingLevel | '2''3''4''5''6' | Level of the day headings, to fit the page outline. Default 3. |
showEmptyDays | 'true''false' | List every day in the range, with a zero count, instead of only days with events or notes. |
States
| State | Exposed as | Behaviour |
|---|---|---|
| Today | data-today (section) | Link-coloured date and a "Today" pill in the heading. |
| Empty | data-empty (root) | EmptyState "Nothing scheduled", or your empty node. |
| Note rows | – | Hatched informational rows from dayNotes (time off, holidays), shown first. |
| Event status | – | Each row is an EventChip, so tentative, cancelled and declined read as in every view. |
Interaction behaviour
- Activating a row calls
onEventClick(event), or follows the event’shref. renderEvent(event, chip)wraps a row, for example in a PopoverTrigger.- Give the list a height or max-height to scroll inside a card; the day headings stick to the top.
- A multi-day event appears on each day it touches; a timed event continuing from an earlier day shows "– end".
Keyboard support
| Keys | Action |
|---|---|
| Tab | Enters the list at today’s first event (or the first event), then leaves it: one tab stop. |
| ArrowDown / ArrowUp | Next or previous event, across days. |
| Page Down / Page Up | First event of the next or previous day. |
| Home / End | First or last event. |
| Enter / Space | Activates the focused event. |
Accessibility
- Role
- region per day (section labelled by its heading)
- Accessible name
- Each day section is named by its heading: "Wednesday 4 November, Today, 3 events".
- Rows are EventChip buttons named "title, time, status, detail".
- Roving tabindex keeps a long agenda to one tab stop while every event stays reachable.
- Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/agenda-list.
Responsive behaviour
- Uses a container query: below 24rem the time sits above the title and the status beside it.
- Titles and details truncate; nothing forces horizontal scrolling at 320px.
Theme support
- Headings use
--mn-color-bg-surface, so they cover scrolled rows in every theme. - The Today pill uses
--mn-color-accent-subtlewith default text, which meets contrast in every theme. - Note rows are hatched with
--mn-color-bg-muted/--mn-color-bg-subtle.
Do and don't
Do
- Pass
timeZone,localeandtodayfrom the user’s settings so grouping matches the rest of the product. - Give it a bounded height inside cards so the sticky headings work.
- Use
dayNotesfor time off and holidays instead of fake events.
Don't
- Do not sort or group the events yourself; pass them in any order.
- Do not nest interactive controls inside a row; open details in a popover or page.
Related components
- EventChip: Each row is a
barEventChip. - CalendarMonthView: The month layout of the same events.
- TimeGrid: The day and week layout, with overlaps and free time.
- EmptyState: The default empty state.
Dependencies
| Kind | Depends on |
|---|---|
| npm packages | react |
| Monarch components | EmptyState, EventChip |
Testing coverage
- Groups by day with headings, counts and a Today marker; all-day events first, then by start time.
- Arrow keys, Page Up/Down and Home/End move between events across days; one tab stop; Enter activates.
- The tab stop starts at today or later.
- Groups in the given time zone; continuing events show their end only.
- Fixed ranges with
showEmptyDays,dayNotes, heading level and labels. - Default and custom empty states;
renderEventwrapping. - No axe violations in jsdom; renders on the server.
| Check | Covered |
|---|---|
| Axe in jsdom | Yes |
| Server rendering | Yes |
| Browser suites | Visual baselines: /__gallery/agenda-list; Cross-browser axe: /components/agenda-list |
Tests: packages/react/src/components/scheduling/agenda-list