Provide a general summary of the issue here
When a RangeCalendar (including the calendar inside DateRangePicker from react-aria-components) is rendered inside a shadow root, clicking a single date immediately commits a range where start === end. It is impossible to select a range with two separate clicks.
The enableShadowDOM() flag is enabled and event retargeting is handled correctly in most of the codebase, but one line in useRangeCalendar still reads the raw e.target from a window-level listener.
🤔 Expected Behavior?
First click sets the anchor (start) date; second click on another day commits the range start → end — same behavior as in light DOM.
😯 Current Behavior
First click immediately commits { start: clickedDate, end: clickedDate }. Every subsequent click does the same, so a multi-day range can never be selected by clicking.
Drag-selection coincidentally still works, because the unintended commit on pointerup happens to be exactly what drag-selection wants.
💁 Possible Solution
useRangeCalendar registers a window-level pointerup listener (endDragging) to support drag selection:
// packages/react-aria/src/calendar/useRangeCalendar.ts
let endDragging = (e: PointerEvent) => {
// ...
state.setDragging(false);
if (!state.anchorDate) {
return;
}
let target = e.target as Element;
if (ref.current && isFocusWithin(ref.current) && (!nodeContains(ref.current, target) || !target.closest('button, [role="button"]'))) {
commitBehaviorMapping[commitBehavior]();
}
};
For a listener attached to window, events that originate inside a shadow root are retargeted: e.target is the shadow host, not the actual calendar cell button. Therefore nodeContains(ref.current, target) is always false, the hook believes the pointer was released outside the calendar, and commitSelection() runs on every single click — committing start === end right after the first click.
One-line fix — read the real target through the composed path:
let target = (typeof e.composedPath === 'function' ? e.composedPath()[0] : e.target) as Element;
(or use the existing getEventTarget shadow-DOM helper used elsewhere in the codebase.)
🔦 Context
We render React micro-frontends inside a shadow root (to isolate them from a host application's global CSS), with the enableShadowDOM() flag enabled via @react-stately/flags. Everything else in react-aria behaves correctly in this setup (focus management, presses, overlays via UNSAFE_PortalProvider) — only range selection by click is broken. We are currently shipping the one-line fix above via patch-package.
🖥️ Steps to Reproduce
- Enable shadow DOM support:
import {enableShadowDOM} from '@react-stately/flags'; enableShadowDOM();
- Attach a shadow root to a host element and render a
RangeCalendar inside it with createRoot(shadowRootContainer).render(...).
- Click one day in the calendar.
- Observe
onChange fires immediately with start === end. Clicking a second day never extends the range.
Minimal repro sketch:
import {enableShadowDOM} from '@react-stately/flags';
import {createRoot} from 'react-dom/client';
import {RangeCalendar, Heading, Button, CalendarGrid, CalendarCell} from 'react-aria-components';
enableShadowDOM();
const host = document.getElementById('host');
const shadow = host.attachShadow({mode: 'open'});
const container = document.createElement('div');
shadow.appendChild(container);
createRoot(container).render(
<RangeCalendar aria-label="Trip dates" onChange={(v) => console.log('committed', v)}>
<header>
<Button slot="previous">◀</Button>
<Heading />
<Button slot="next">▶</Button>
</header>
<CalendarGrid>
{(date) => <CalendarCell date={date} />}
</CalendarGrid>
</RangeCalendar>
);
Click any date → committed {start, end} logs immediately with the same date for both.
Version
react-aria 3.50.0 (latest); the unguarded e.target read is still present on current main (packages/react-aria/src/calendar/useRangeCalendar.ts)
What browsers are you seeing the problem on?
Chrome, Firefox, Microsoft Edge
If other, please specify.
No response
What operating system are you using?
Windows 11 (OS-independent)
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response
Provide a general summary of the issue here
When a
RangeCalendar(including the calendar insideDateRangePickerfrom react-aria-components) is rendered inside a shadow root, clicking a single date immediately commits a range wherestart === end. It is impossible to select a range with two separate clicks.The
enableShadowDOM()flag is enabled and event retargeting is handled correctly in most of the codebase, but one line inuseRangeCalendarstill reads the rawe.targetfrom a window-level listener.🤔 Expected Behavior?
First click sets the anchor (start) date; second click on another day commits the range
start → end— same behavior as in light DOM.😯 Current Behavior
First click immediately commits
{ start: clickedDate, end: clickedDate }. Every subsequent click does the same, so a multi-day range can never be selected by clicking.Drag-selection coincidentally still works, because the unintended commit on
pointeruphappens to be exactly what drag-selection wants.💁 Possible Solution
useRangeCalendarregisters a window-levelpointeruplistener (endDragging) to support drag selection:For a listener attached to
window, events that originate inside a shadow root are retargeted:e.targetis the shadow host, not the actual calendar cell button. ThereforenodeContains(ref.current, target)is alwaysfalse, the hook believes the pointer was released outside the calendar, andcommitSelection()runs on every single click — committingstart === endright after the first click.One-line fix — read the real target through the composed path:
(or use the existing
getEventTargetshadow-DOM helper used elsewhere in the codebase.)🔦 Context
We render React micro-frontends inside a shadow root (to isolate them from a host application's global CSS), with the
enableShadowDOM()flag enabled via@react-stately/flags. Everything else in react-aria behaves correctly in this setup (focus management, presses, overlays viaUNSAFE_PortalProvider) — only range selection by click is broken. We are currently shipping the one-line fix above viapatch-package.🖥️ Steps to Reproduce
import {enableShadowDOM} from '@react-stately/flags'; enableShadowDOM();RangeCalendarinside it withcreateRoot(shadowRootContainer).render(...).onChangefires immediately withstart === end. Clicking a second day never extends the range.Minimal repro sketch:
Click any date →
committed {start, end}logs immediately with the same date for both.Version
react-aria 3.50.0 (latest); the unguarded e.target read is still present on current main (packages/react-aria/src/calendar/useRangeCalendar.ts)
What browsers are you seeing the problem on?
Chrome, Firefox, Microsoft Edge
If other, please specify.
No response
What operating system are you using?
Windows 11 (OS-independent)
🧢 Your Company/Team
No response
🕷 Tracking Issue
No response