Skip to content

[scheduler] Complete ARIA grid semantics on EventTimeline and EventCalendar - #22266

Merged
rita-codes merged 11 commits into
mui:masterfrom
Anexus5919:fix/scheduler-aria-grid-semantics
May 11, 2026
Merged

rita-codes merged 11 commits into
mui:masterfrom
Anexus5919:fix/scheduler-aria-grid-semantics

Conversation

@Anexus5919

Copy link
Copy Markdown
Contributor

Closes #22265.

Summary

Wires the missing ARIA grid attributes that #22265 calls out across both Scheduler surfaces, so a screen-reader user always knows "row N of M, column N of M".

The data attributes required for this work were already in place after #22222 (data-level, data-index, data-unit-leaf, --span); this PR is the a11y follow-up the author of #22222 explicitly flagged as out of scope there. Keyboard navigation across cells stays out of scope here too, per the same issue (tracked in #21909).

What was missing before this PR (per #22265)

EventTimeline

EventCalendar

  • MiniCalendar already had the role hierarchy but no aria-colindex, aria-rowindex on cells.
  • DayTimeGrid (Day and Week views) had role="row", role="columnheader", role="gridcell" but no indices on most cells, and the time-grid container had no enclosing row.

Approach

Defaults are added to the headless primitives so consumers benefit automatically; styled wrappers then override the indices explicitly only where the layout has a fixed leftmost column (the all-day column in DayTimeGrid, the week-number column in MonthView). The override path uses the standard pattern of reading the value from elementProps first and falling back to a computed default when none is provided.

The outer header row in EventTimelinePremiumContent keeps its existing role="row" + aria-rowindex={1}, matching what #22265 lists in its current-state checklist; body row math (index + 2) is preserved so the keyboard-navigation tests continue to pass unchanged.

Changes

Timeline (headless premium)

packages/x-scheduler-headless-premium/src/timeline-grid/cell/TimelineGridCell.tsx

  • Default role switched from 'cell' to 'gridcell'. Consumers can override via the role prop, which is what lets the styled wrappers set columnheader and presentation further up.
  • Removed the obsolete TODO about aria-colindex via Composite; the styled layer now sets aria-colindex explicitly because the layout context (which logical column the cell sits in) is known there, not in the primitive.

packages/x-scheduler-headless-premium/src/timeline-grid/header/TimelineGridHeader.tsx

  • Each inner level row <div> now gets role="row".
  • Each header cell <div> now gets role="columnheader", aria-colindex={cell.index + 2} (column 1 is reserved for the resource title column), and aria-colspan={cell.spanInTicks} when spanInTicks > 1 (handles both grouping rows and clamped first/last cells).

packages/x-scheduler-headless-premium/src/timeline-grid/root/TimelineGridRoot.tsx

  • Sets aria-colcount={presetConfig.tickCount + 1} and aria-rowcount={resources.length + 1} on the root.

Timeline (styled premium)

packages/x-scheduler-premium/src/event-timeline-premium/content/EventTimelinePremiumContent.tsx

  • The resource title header cell is set to role="columnheader" with aria-colindex={1}.
  • The events header cell that wraps the multi-level header is set to role="presentation" so the inner level rows and column headers carry the grid semantics. The outer header row keeps role="row" and aria-rowindex={1} as before.

packages/x-scheduler-premium/src/event-timeline-premium/content/timeline-title-cell/EventTimelinePremiumTitleCell.tsx

  • The body title cell now sets aria-colindex={1}. The role="gridcell" default comes from the TimelineGridCell primitive change above.

Calendar (headless)

packages/x-scheduler-headless/src/calendar-grid/header-row/CalendarGridHeaderRow.tsx

  • Defaults aria-rowindex to 1; an explicit value passed via element props wins.

packages/x-scheduler-headless/src/calendar-grid/day-row/CalendarGridDayRow.tsx

  • Defaults aria-rowindex to rowIndex + 2 (header row reserved at index 1); an explicit value passed via element props wins.

packages/x-scheduler-headless/src/calendar-grid/header-cell/CalendarGridHeaderCell.tsx

  • Defaults aria-colindex to index + 1 (from useCompositeListItem); an explicit value passed via element props wins.

packages/x-scheduler-headless/src/calendar-grid/day-cell/CalendarGridDayCell.tsx

  • Defaults aria-colindex to index + 1; an explicit value passed via element props wins.

packages/x-scheduler-headless/src/calendar-grid/time-column/CalendarGridTimeColumn.tsx

  • Defaults aria-colindex to index + 1; an explicit value passed via element props wins.

Calendar (styled)

packages/x-scheduler/src/event-calendar/mini-calendar/MiniCalendar.tsx

  • Root: aria-rowcount={1 + weeks.length}, aria-colcount={7}.
  • Weekday header row: aria-rowindex={1}. Each weekday header cell: aria-colindex={dayIndex + 1}.
  • Each week row: aria-rowindex={weekIndex + 2}. Each day cell: aria-colindex={dayIndex + 1}.

packages/x-scheduler/src/internals/components/day-time-grid/DayTimeGrid.tsx

  • Root: aria-rowcount={3}, aria-colcount={1 + days.length}.
  • Header row: aria-rowindex={1}. Each day header cell: aria-colindex={index + 2} (column 1 is reserved for the all-day fixed cell).
  • All-day events row: aria-rowindex={2}. The all-day header cell on the left: aria-colindex={1}.
  • The time-grid container (DayTimeGridGrid) now has role="row" and aria-rowindex={3}. Without this, the TimeGridColumn cells (each role="gridcell") had no enclosing row, which is invalid ARIA.

packages/x-scheduler/src/internals/components/day-time-grid/DayGridCell.tsx

  • Accepts a colIndex prop and forwards it as aria-colindex.

packages/x-scheduler/src/internals/components/day-time-grid/TimeGridColumn.tsx

  • Accepts a colIndex prop and forwards it as aria-colindex.

packages/x-scheduler/src/month-view/MonthView.tsx

  • Root: aria-rowcount={1 + weeks.length}, aria-colcount={(showWeekNumber ? 1 : 0) + 7}.
  • Header row: aria-rowindex={1}. Week-number header cell (when present): role="columnheader", aria-colindex={1}. Day header cells: aria-colindex={dayIdx + (showWeekNumber ? 2 : 1)}.

packages/x-scheduler/src/month-view/month-view-row/MonthViewWeekRow.tsx

  • Computes colOffset = showWeekNumber ? 2 : 1. Week-number row header cell gets aria-colindex={1}. Each MonthViewCell is given colIndex={dayIdx + colOffset}.

packages/x-scheduler/src/month-view/month-view-row/MonthViewCell.tsx

  • Accepts a colIndex prop and forwards it as aria-colindex.

Tests

New assertions cover the additions, no existing test was modified.

packages/x-scheduler-premium/src/event-timeline-premium/content/timeline-header/EventTimelinePremiumHeader.test.tsx

  • role="row" on each inner level row.
  • role="columnheader" and aria-colindex (offset by 1 for the title column) on each leaf header cell of the dayAndHour preset.
  • aria-colspan="24" on grouping day cells in dayAndHour, and no aria-colspan on single-tick hour cells.
  • Clamped first/last month cells in dayAndMonth carry aria-colspan="29" and aria-colspan="27" respectively.
  • aria-colcount and aria-rowcount on the grid root.

packages/x-scheduler-premium/src/event-timeline-premium/EventTimelinePremium.test.tsx

  • Title header cell is role="columnheader" with aria-colindex="1".
  • Events header cell wrapper is role="presentation".
  • Each body title cell is role="gridcell" with aria-colindex="1".
  • aria-rowcount matches 1 + resources.length.

packages/x-scheduler/src/event-calendar/mini-calendar/MiniCalendar.test.tsx

  • aria-colcount="7" and aria-rowcount="7" on the grid.
  • aria-rowindex runs 1 through 7 across the weekday header row and the 6 week rows.
  • aria-colindex runs 1 through 7 across the weekday header cells.
  • aria-colindex cycles 1 through 7 across all 42 day cells.

packages/x-scheduler/src/week-view/tests/WeekView.test.tsx

  • aria-rowcount="3" and aria-colcount="8" on the day-time grid root.
  • Day header cells have aria-colindex from 2 to 8.
  • All-day header cell has aria-colindex="1".
  • All-day row has aria-rowindex="2"; time row has aria-rowindex="3".
  • Gridcells in both rows have aria-colindex from 2 to 8.

packages/x-scheduler/src/month-view/tests/MonthView.test.tsx

  • aria-colcount="7" on the grid and aria-rowcount greater than 1.
  • Header row is at aria-rowindex="1" with column headers at aria-colindex 1 to 7.
  • Each week row is at aria-rowindex={weekIdx + 2} with day cells at aria-colindex 1 to 7.

The pre-existing packages/x-scheduler-headless-premium/src/timeline-grid/tests/keyboard-navigation.TimelineGrid.test.tsx was not modified and still passes: the outer header row's aria-rowindex={1} and body row math index + 2 are preserved.

Out of scope

  • Keyboard interactions across cells (tracked in [scheduler] Keyboard interactions #21909). Existing keyboard navigation paths (useTimelineGridRowKeyboard, getNavigationTarget, the focused-cell context in TimelineGridRoot and CalendarGridRoot) are untouched.
  • Codemod. No public API was renamed or removed; existing primitives keep their props and gain only optional defaults that consumers can override via element props.

Verification

  • pnpm --filter "@mui/x-scheduler*" run typescript # 4 packages clean
  • pnpm eslint # exit 0
  • pnpm test:unit --project "x-scheduler*" --run # 95 files, 1325 passed, 12 skipped
  • pnpm prettier # changed files reformatted

The new aria assertions added in this PR run as part of the same pnpm test:unit invocation.

Test plan

  • Type check passes for the four scheduler packages.
  • Full repo ESLint passes with --max-warnings 0.
  • pnpm test:unit --project "x-scheduler*" --run is green (1325 passed, 12 pre-existing skips).
  • New aria-semantics tests cover Timeline header, Timeline body, MiniCalendar, WeekView (DayTimeGrid), and MonthView.
  • Existing keyboard-navigation.TimelineGrid.test.tsx still passes without modification.
  • Manual smoke with a screen reader (NVDA/VoiceOver) on the Scheduler docs demos: "row N of M, column N of M" announcements behave as expected when navigating cells.

@code-infra-dashboard

code-infra-dashboard Bot commented Apr 29, 2026 •

Copy link
Copy Markdown

Deploy preview

https://deploy-preview-22266--material-ui-x.netlify.app/

Bundle size

Bundle Parsed size Gzip size
@mui/x-data-grid 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-pro 0B(0.00%) 0B(0.00%)
@mui/x-data-grid-premium 0B(0.00%) 0B(0.00%)
@mui/x-charts 0B(0.00%) 0B(0.00%)
@mui/x-charts-pro 0B(0.00%) 0B(0.00%)
@mui/x-charts-premium 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers 0B(0.00%) 0B(0.00%)
@mui/x-date-pickers-pro 0B(0.00%) 0B(0.00%)
@mui/x-tree-view 0B(0.00%) 0B(0.00%)
@mui/x-tree-view-pro 0B(0.00%) 0B(0.00%)
@mui/x-license 0B(0.00%) 0B(0.00%)

Details of bundle changes


Check out the code infra dashboard for more information about this PR.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25afc96000

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

{
role: 'grid',
'aria-colcount': presetConfig.tickCount + 1,
'aria-rowcount': resources.length + 1,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count all header rows in grid aria-rowcount

TimelineGridHeader now emits one role="row" per header level, but TimelineGridRoot still reports aria-rowcount as resources.length + 1. In presets with multiple header levels, assistive tech sees more rows than the declared total, so row/position announcements become inconsistent (e.g., body rows indexed from 2 while extra header rows also exist). Update the rowcount calculation (and corresponding row indexing expectations) to include all rendered header rows.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in c379c9c

@Anexus5919

Copy link
Copy Markdown
Contributor Author

@rita-codes please have a review on this pr. Thanks!

@zannager zannager added the scope: scheduler Changes related to the scheduler. label Apr 30, 2026
key={cell.key}
className={[classNames?.cell, level.className].filter(Boolean).join(' ') || undefined}
role="columnheader"
aria-colindex={cell.index + 2}

@mj12albert mj12albert May 2, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review:

aria-colindex={cell.index + 2} uses the header-cell ordinal from iterate.ts, not the starting tick column. For dayAndHour, day headers span 24 columns, so the four day cells should start at columns 2, 26, 50, 74; this code emits 2, 3, 4, 5. Same problem appears for clamped month cells in dayAndMonth.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed in 10729f8

{
role: 'grid',
'aria-colcount': presetConfig.tickCount + 1,
'aria-rowcount': presetConfig.headers.length + resources.length,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TimelineGridRoot, TimelineGridTitleRow, and TimelineGridEventRow were changed to assume the grid always has presetConfig.headers.length header rows (which matches one of the styled versions). This breaks the headless API as it causes TitleRow/EventRow to start at aria-rowindex 3.

@Anexus5919 Anexus5919 May 2, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mj12albert addressed in 10729f8. Thanks for the review and I have implemented the suggested changes. Kindly do let me know if anything else is needed.
PTAL. ☺️

@Anexus5919
Anexus5919 requested a review from mj12albert May 3, 2026 09:39
@github-actions github-actions Bot added the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 4, 2026
@github-actions

github-actions Bot commented May 4, 2026

Copy link
Copy Markdown
Contributor

This pull request has conflicts, please resolve those before we can evaluate the pull request.

# Conflicts:
#	packages/x-scheduler-premium/src/event-timeline-premium/content/EventTimelinePremiumContent.tsx
@github-actions github-actions Bot removed the PR: out-of-date The pull request has merge conflicts and can't be merged. label May 4, 2026
@Anexus5919

Copy link
Copy Markdown
Contributor Author

@mj12albert @rita-codes PTAL.

@rita-codes rita-codes added the type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature. label May 6, 2026
@rita-codes rita-codes changed the title fix(scheduler): complete ARIA grid semantics on EventTimeline and EventCalendar [scheduler] Complete ARIA grid semantics on EventTimeline and EventCalendar May 6, 2026
@rita-codes

Copy link
Copy Markdown
Member

Hey @Anexus5919, thanks for picking this up. I'd suggest splitting this into two parts because EventCalendar and EventTimeline have different blockers.

EventCalendar (MiniCalendar, MonthView, DayTimeGrid)

The grid declares aria-colcount="8" but no row really has 8 cells:

  • Header row 1 has 7 columnheaders (cols 2-8); the first child is a placeholder div without role.
  • Row 2 (all-day) has 7 gridcells (cols 2-8). The "All day" cell with aria-colindex="1" lives outside the row, as a sibling.
  • Row 3 (time-grid) has 7 gridcells (cols 2-8). The time-axis is aria-hidden="true", so col 1 is missing here too.

MonthView with showWeekNumber=true has the same shape: a week-number column at col 1 that is not really part of the navigable grid. The "All day" label, the time-axis, and the week-number column are not navigable today, no tabindex, can't be reached with Tab or arrow keys. They are visual labels, not real columns. Declaring them as col 1 makes the grid claim a column the user can never reach.

Suggested fix (no DOM/CSS change): drop col 1 from the ARIA model. Use aria-colcount="7", shift all aria-colindex from 2-8 to 1-7, and turn the "All day" / week-number labels into aria-hidden elements still referenced via aria-labelledby from each row cell. Per ARIA spec, aria-labelledby resolves text from aria-hidden elements, so AT users still hear "Sunday 29, All day", they just can't focus the label as a separate target (which they can't do today either).
This matches what is actually navigable, removes the orphan columnheader, and doesn't touch layout, scroll, or keyboard handlers.

Two small unrelated fixes

  1. The PR removed the standard // Rendering props / // Props forwarded to the DOM element block in TimelineGridCell.tsx and shortened the tabIndex={0} comment in CalendarGridDayCell.tsx:111. Those are the convention used across primitives and explain non-obvious decisions (e.g. why all cells are tabbable instead of roving tabindex). Unrelated to this PR, suggest restoring.

  2. Test gap: MonthView with showWeekNumber=true is not covered. The colOffset math should have at least one assertion in that branch.

EventTimelinePremium

The Timeline part of this PR has three issues that depend on how horizontal body virtualization will be designed:

  1. aria-colcount is in tick units, not leaf header units. For monthAndYear this means aria-colcount = 1097 with year aria-colspan in days (184/365/365/182). DataGrid solved the same problem in leaf columns, and that already coexists with column virtualization. For monthAndYear this would mean aria-colcount = 37 and year aria-colspan = 6/12/12/6.

  2. aria-rowcount = 1 + resources.length in EventTimelinePremiumContent.tsx:426 undercounts. TimelineGridHeader emits one role="row" per level (2 for monthAndYear), and the outer EventTimelinePremiumHeaderRow is also role="row", that nests rows. Should be headers.length + resources.length, with the wrapper as role="presentation".

  3. useCompositeListItem fallback for aria-colindex registers cells in DOM mount order, not logical order. Under horizontal virtualization, cells mount/unmount during scroll → registration order won't match logical order. The PR already passes colIndex as a prop in DayGridCell / MonthViewCell / TimeGridColumn; extending that pattern and dropping the composite fallback keeps this safe.

Question for @romgrk

Two options, what do you prefer?

  • A) You already have the virtualization model clear → tell us the shape (cells per leaf unit vs sparse, aria-colindex source, X axis in timeResolution vs leaf units) and we align the EventTimeline part of this PR with it.
  • B) Model still in progress → we drop the EventTimeline changes from this PR for now and focus only on EventCalendar. Timeline a11y lands once your virtualization design is set.

Let me know what fits you better, I don't want to get in the way of your virtualization work 🙇‍♀️

Thanks!

@Anexus5919

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @rita-codes, the split makes sense.

Agreed on all EventCalendar points:

  1. Col 1 is not navigable today, declaring it in the ARIA model is misleading. I'll drop it from DayTimeGrid and MonthView, use aria-colcount=7, shift cells to 1-7, and keep the "All day" / week-number labels as aria-hidden referenced via aria-labelledby.
  2. Will restore the original comments in TimelineGridCell.tsx and CalendarGridDayCell.tsx.
  3. Will add a MonthView test for the showWeekNumber=true branch.

I'll also drop the useCompositeListItem fallback for aria-colindex and require explicit colIndex props (extending the pattern already used in DayGridCell / MonthViewCell / TimeGridColumn, plus adding it to MiniCalendar).

For EventTimeline, happy to go either way. I'll hold those changes until @romgrk weighs in on A vs B. If B, I'll revert the timeline-specific bits from this PR and let it land alongside the virtualization design.

return useRenderElement('div', componentProps, {
ref: [forwardedRef],
props: [elementProps, { role: 'cell' }],
props: [elementProps, { role }],

@flaviendelangle flaviendelangle May 7, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useRenderElement should automatically handle the priority here, we shouldn't do elementProps.xxx ?? 'customValue' 👍
But elementProps should be defined after the custom props @rita-codes (check all the other components)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @flaviendelangle, addressed in 9a4454f.
Dropped the ?? customValue fallback in the six primitives this PR touches and put elementProps after the custom props in each props array. CalendarGridDayCell destructures aria-labelledby out first to preserve the existing column-header join.

I left the rest of the codebase on the legacy order since the broader convention sweep is yours and @rita-codes' call. Happy to follow up in a separate PR if you'd like me to.

@romgrk

romgrk commented May 7, 2026

Copy link
Copy Markdown
Contributor

I'd go with B, I'm still trying out options for the virtualization layout.

@Anexus5919

Copy link
Copy Markdown
Contributor Author

Thanks @romgrk!

@rita-codes confirming before I proceed: should I revert the EventTimeline parts of this PR (the TimelineGridHeader aria additions, the TimelineGridCell role default, the EventTimelinePremium styled-wrapper aria attributes, and the related tests) so this PR stays scoped to EventCalendar? Timeline a11y can land in a follow-up alongside the virtualization design.

Holding off on the revert until you confirm.

@rita-codes

Copy link
Copy Markdown
Member

@romgrk perfect, makes sense 👍 thanks for confirming!

@Anexus5919 yes, let’s go with that. Please revert everything related to EventTimeline so we keep this PR focused only on EventCalendar.

We can tackle Timeline a11y separately once the virtualization design is clearer 🙌

@Anexus5919

Anexus5919 commented May 7, 2026 •

Copy link
Copy Markdown
Contributor Author

@Anexus5919 yes, let’s go with that. Please revert everything related to EventTimeline so we keep this PR focused only on EventCalendar.

@rita-codes Done in 1feca593. Timeline parts reverted; PR is now scoped to EventCalendar only (CalendarGrid primitives + MiniCalendar/DayTimeGrid/MonthView styled wrappers and tests). Will open the Timeline a11y as a separate PR once the virtualization design lands.

PTAL. ☺️

const isOtherMonth = !adapter.isSameMonth(day.value, displayedMonth);

// Create a full date label for accessibility
const fullDateLabel = adapter.formatByString(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, out of scope: the // Create a full date label for accessibility comment that used to sit on the line above was removed, but it doesn't relate to the ARIA grid semantics changes in this PR. Could you restore it to keep the diff focused?

@Anexus5919

Copy link
Copy Markdown
Contributor Author

Thanks @rita-codes, good catches. Pushed in 17e92f6:

  • Restored the full-date-label comment in MiniCalendar.
  • aria-colcount={weeks[0].length} in MonthView, dropped the optional chaining since weeks[0] is already dereferenced just below.
  • Removed the redundant role="row" and aria-rowindex={2} from DayTimeGridAllDayEventsRow. The CalendarGrid.DayRow primitive already covers both.

Happy to adjust anything else.

@rita-codes

rita-codes commented May 11, 2026 •

Copy link
Copy Markdown
Member

Nice iteration on this @Anexus5919! 👏
The split from Timeline + the aria-hidden + aria-labelledby pattern for non-navigable labels makes the grid model honest about what's actually reachable. Tests cover the new semantics well across all three views. LGTM 🚀

Thanks for working on this! 🙇‍♀️

@Anexus5919

Copy link
Copy Markdown
Contributor Author

Nice iteration on this @Anexus5919! 👏 The split from Timeline + the aria-hidden + aria-labelledby pattern for non-navigable labels makes the grid model honest about what's actually reachable. Tests cover the new semantics well across all three views. LGTM 🚀

Thanks for working on this! 🙇‍♀️

Thanks a lot, @rita-codes! 🙌 Really appreciate the thorough reviews on this one, learned a bunch about how the grid semantics fit together. Happy to pick up the Timeline a11y piece once the virtualization design settles, and I'd love to keep contributing to MUI X more broadly. Thanks again for the guidance throughout 🙏

@rita-codes
rita-codes merged commit 039cb0d into mui:master May 11, 2026
21 checks passed
@romgrk

romgrk commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

The virtualization rework is done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope: scheduler Changes related to the scheduler. type: enhancement It’s an improvement, but we can’t make up our mind whether it's a bug fix or a new feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[scheduler] Complete ARIA grid semantics on EventTimeline and EventCalendar

6 participants