Skip to content

Commit 7a58f33

Browse files
committed
fix(Select): resolve pointer cancellation issues for WCAG 2.5.2 compliance (#45301)
1 parent b0a32f0 commit 7a58f33

File tree

2 files changed

+50
-24
lines changed

2 files changed

+50
-24
lines changed

Diff for: packages/mui-material/src/Select/Select.test.js

+48
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,54 @@ describe('<Select />', () => {
3333
skip: ['componentProp', 'componentsProp', 'themeVariants', 'themeStyleOverrides'],
3434
}));
3535

36+
describe('WCAG 2.5.2 - Pointer Cancellation', () => {
37+
it('should close the menu when dragging away and releasing', () => {
38+
const { getByRole, queryByRole } = render(
39+
<Select value="">
40+
<MenuItem value="">none</MenuItem>
41+
<MenuItem value={10}>Ten</MenuItem>
42+
</Select>,
43+
);
44+
const trigger = getByRole('combobox');
45+
46+
// Open the menu with left mouse button
47+
fireEvent.mouseDown(trigger, { button: 0 });
48+
expect(getByRole('listbox')).not.to.equal(null);
49+
50+
// Simulate mouse move to initiate drag
51+
fireEvent.mouseMove(document.body);
52+
53+
// Simulate mouse up outside any menu items
54+
fireEvent.mouseUp(document.body);
55+
56+
// Menu should be closed now
57+
expect(queryByRole('listbox', { hidden: false })).to.equal(null);
58+
});
59+
60+
it('should not close the menu when releasing on a menu item after dragging', () => {
61+
const { getByRole, getAllByRole } = render(
62+
<Select value="">
63+
<MenuItem value="">none</MenuItem>
64+
<MenuItem value={10}>Ten</MenuItem>
65+
</Select>,
66+
);
67+
const trigger = getByRole('combobox');
68+
69+
// Open the menu
70+
fireEvent.mouseDown(trigger, { button: 0 });
71+
const options = getAllByRole('option');
72+
73+
// Simulate mouse move to initiate drag
74+
fireEvent.mouseMove(document.body);
75+
76+
// Simulate mouse up on a menu item
77+
fireEvent.mouseUp(options[0]);
78+
79+
// Menu should still be open
80+
expect(getByRole('listbox')).not.to.equal(null);
81+
});
82+
});
83+
3684
describe('prop: inputProps', () => {
3785
it('should be able to provide a custom classes property', () => {
3886
render(

Diff for: packages/mui-material/src/Select/SelectInput.js

+2-24
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,6 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
168168
startedOn: null,
169169
});
170170

171-
const focusTrackingRef = React.useRef({
172-
isFocused: false,
173-
pendingBlur: false,
174-
});
175-
176171
React.useImperativeHandle(
177172
handleRef,
178173
() => ({
@@ -302,10 +297,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
302297
current = current.parentElement;
303298
}
304299

305-
if (menuItem) {
306-
// Simulate a click on the menu item if we released on one
307-
menuItem.click();
308-
} else {
300+
if (!menuItem) {
309301
// If released outside menu items, close the menu
310302
update(false, event);
311303
}
@@ -398,23 +390,9 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
398390

399391
const open = displayNode !== null && openState;
400392

401-
const handleFocus = (event) => {
402-
// Skip duplicate focus events
403-
if (!focusTrackingRef.current.isFocused) {
404-
focusTrackingRef.current.isFocused = true;
405-
focusTrackingRef.current.pendingBlur = false;
406-
407-
if (onFocus) {
408-
onFocus(event);
409-
}
410-
}
411-
};
412-
413393
const handleBlur = (event) => {
414394
// if open event.stopImmediatePropagation
415395
if (!open && onBlur) {
416-
focusTrackingRef.current.pendingBlur = false;
417-
focusTrackingRef.current.isFocused = false;
418396
// Preact support, target is read only property on a native event.
419397
Object.defineProperty(event, 'target', { writable: true, value: { value, name } });
420398
onBlur(event);
@@ -595,7 +573,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
595573
onKeyDown={handleKeyDown}
596574
onMouseDown={disabled || readOnly ? null : handleMouseDown}
597575
onBlur={handleBlur}
598-
onFocus={handleFocus}
576+
onFocus={onFocus}
599577
{...SelectDisplayProps}
600578
ownerState={ownerState}
601579
className={clsx(SelectDisplayProps.className, classes.select, className)}

0 commit comments

Comments
 (0)