diff --git a/packages/react-aria-components/test/DateField.test.js b/packages/react-aria-components/test/DateField.test.js
index 43d0e94ff00..cbe98cb187f 100644
--- a/packages/react-aria-components/test/DateField.test.js
+++ b/packages/react-aria-components/test/DateField.test.js
@@ -562,4 +562,32 @@ describe('DateField', () => {
expect(segements[1]).toHaveTextContent('dd');
expect(segements[2]).toHaveTextContent('yyyy');
});
+
+ // Regression test for #10259: in Firefox a stale selection anchor can remain inside a segment
+ // after focus moves away, and the selectionchange handler would collapse onto it, stealing focus.
+ it('does not collapse the selection onto a segment while another element is focused', () => {
+ let {getByRole} = render(
+ <>
+
+
+
+ {segment => }
+
+ >
+ );
+
+ let button = getByRole('button');
+ let segment = within(getByRole('group')).getAllByRole('spinbutton').at(-1);
+ act(() => button.focus());
+ expect(document.activeElement).toBe(button);
+
+ let collapse = jest.fn();
+ jest.spyOn(window, 'getSelection').mockReturnValue({anchorNode: segment.firstChild, collapse});
+ act(() => {
+ document.dispatchEvent(new Event('selectionchange'));
+ });
+
+ expect(collapse).not.toHaveBeenCalled();
+ jest.restoreAllMocks();
+ });
});
diff --git a/packages/react-aria/src/datepicker/useDateSegment.ts b/packages/react-aria/src/datepicker/useDateSegment.ts
index 9119e2b7d76..963fc4659ff 100644
--- a/packages/react-aria/src/datepicker/useDateSegment.ts
+++ b/packages/react-aria/src/datepicker/useDateSegment.ts
@@ -264,7 +264,13 @@ export function useDateSegment(
// Otherwise, when tapping on a segment in Android Chrome and then entering text,
// composition events will be fired that break the DOM structure and crash the page.
let selection = window.getSelection();
- if (selection?.anchorNode && nodeContains(ref.current, selection?.anchorNode)) {
+ // Only collapse while focused, otherwise a stale anchor left in the segment (e.g. on Firefox)
+ // steals focus back into it on selectionchange. See #10259.
+ if (
+ selection?.anchorNode &&
+ nodeContains(ref.current, selection?.anchorNode) &&
+ getActiveElement() === ref.current
+ ) {
selection.collapse(ref.current);
}
});