Skip to content

Commit 9d37e24

Browse files
authored
Fix collection Escape handling (#6075)
Fix collection Escape handling (#6075)
1 parent f6a664b commit 9d37e24

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

packages/@react-aria/selection/src/useSelectableCollection.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,9 @@ export function useSelectableCollection(options: AriaSelectableCollectionOptions
254254
}
255255
break;
256256
case 'Escape':
257-
e.preventDefault();
258-
if (!disallowEmptySelection) {
257+
if (!disallowEmptySelection && manager.selectedKeys.size !== 0) {
258+
e.stopPropagation();
259+
e.preventDefault();
259260
manager.clearSelection();
260261
}
261262
break;

packages/react-aria-components/test/ListBox.test.js

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,18 @@
1111
*/
1212

1313
import {act, fireEvent, mockClickDefault, pointerMap, render, within} from '@react-spectrum/test-utils';
14-
import {DropIndicator, Header, ListBox, ListBoxContext, ListBoxItem, Section, Text, useDragAndDrop} from '../';
14+
import {
15+
Button, Dialog,
16+
DialogTrigger,
17+
DropIndicator,
18+
Header, Heading,
19+
ListBox,
20+
ListBoxContext,
21+
ListBoxItem, Modal,
22+
Section,
23+
Text,
24+
useDragAndDrop
25+
} from '../';
1526
import React, {useState} from 'react';
1627
import userEvent from '@testing-library/user-event';
1728

@@ -668,6 +679,51 @@ describe('ListBox', () => {
668679
});
669680
});
670681

682+
describe('inside modals', () => {
683+
it('should clear selection on first Escape, then allow the modal to close on the second Escape', async () => {
684+
let onOpenChange = jest.fn();
685+
let {getAllByRole, getByRole} = render(
686+
<DialogTrigger onOpenChange={onOpenChange}>
687+
<Button>Open Dialog</Button>
688+
<Modal>
689+
<Dialog>
690+
<form>
691+
<Heading slot="title">Sign up</Heading>
692+
<ListBox aria-label="Test" selectionMode="multiple">
693+
<ListBoxItem id="cat">Cat</ListBoxItem>
694+
<ListBoxItem id="dog">Dog</ListBoxItem>
695+
<ListBoxItem id="kangaroo">Kangaroo</ListBoxItem>
696+
</ListBox>
697+
</form>
698+
</Dialog>
699+
</Modal>
700+
</DialogTrigger>
701+
);
702+
await user.tab();
703+
expect(document.activeElement).toBe(getByRole('button'));
704+
await user.keyboard('{Enter}');
705+
expect(onOpenChange).toHaveBeenCalledTimes(1);
706+
707+
let options = getAllByRole('option');
708+
709+
await user.tab();
710+
expect(document.activeElement).toBe(options[0]);
711+
await user.keyboard('{Enter}');
712+
713+
expect(options[0]).toHaveAttribute('aria-selected', 'true');
714+
715+
keyPress('Escape');
716+
717+
expect(options[0]).toBeInTheDocument();
718+
expect(options[0]).toHaveAttribute('aria-selected', 'false');
719+
720+
keyPress('Escape');
721+
expect(options[0]).not.toBeInTheDocument();
722+
expect(onOpenChange).toHaveBeenCalledTimes(2);
723+
expect(onOpenChange).toHaveBeenCalledWith(false);
724+
});
725+
});
726+
671727
describe('links', function () {
672728
describe.each(['mouse', 'keyboard'])('%s', (type) => {
673729
let trigger = async (item) => {

0 commit comments

Comments
 (0)