diff --git a/packages/material-ui/src/Select/Select.test.js b/packages/material-ui/src/Select/Select.test.js index 9db1aa24b77415..81c3c8cd98735d 100644 --- a/packages/material-ui/src/Select/Select.test.js +++ b/packages/material-ui/src/Select/Select.test.js @@ -116,6 +116,7 @@ describe('', () => { , ); - fireEvent.mouseDown(getByRole('button')); + const button = getByRole('button'); + fireEvent.mouseDown(button); + fireEvent.click(button); getAllByRole('option')[1].click(); expect(onChangeHandler.calledOnce).to.equal(true); @@ -265,7 +268,9 @@ describe(', ); - fireEvent.mouseDown(getByRole('button')); + const button = getByRole('button'); + fireEvent.mouseDown(button); + fireEvent.click(button); getAllByRole('option')[1].click(); expect(eventLog).to.deep.equal(['CHANGE_EVENT', 'CLOSE_EVENT']); @@ -724,7 +729,9 @@ describe('', () => { }); const { getByRole, getAllByRole } = render(); - fireEvent.mouseDown(getByRole('button')); + const button = getByRole('button'); + fireEvent.mouseDown(button); + fireEvent.click(button); const options = getAllByRole('option'); fireEvent.click(options[2]); diff --git a/packages/material-ui/src/Select/SelectInput.js b/packages/material-ui/src/Select/SelectInput.js index 9923a9b2a508f3..08a53e468fd972 100644 --- a/packages/material-ui/src/Select/SelectInput.js +++ b/packages/material-ui/src/Select/SelectInput.js @@ -74,6 +74,9 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { const [menuMinWidthState, setMenuMinWidthState] = React.useState(); const [openState, setOpenState] = React.useState(false); const handleRef = useForkRef(ref, inputRefProp); + const handleClick = React.useRef(null); + const menuRef = React.useRef(null); + const disablePointerEvents = React.useRef(false); const handleDisplayRef = React.useCallback((node) => { displayRef.current = node; @@ -83,6 +86,14 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { } }, []); + const handleMenuRef = React.useCallback((node) => { + menuRef.current = node; + + if (node && disablePointerEvents.current) { + node.style.pointerEvents = 'none'; + } + }, []); + React.useImperativeHandle( handleRef, () => ({ @@ -95,6 +106,15 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { [value], ); + React.useEffect(() => { + const doc = ownerDocument(inputRef.current); + return () => { + if (handleClick.current) { + doc.removeEventListener('click', handleClick.current); + } + }; + }, []); + React.useEffect(() => { if (autoFocus) { displayRef.current.focus(); @@ -141,6 +161,21 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { event.preventDefault(); displayRef.current.focus(); + const doc = ownerDocument(inputRef.current); + if (handleClick.current) { + doc.removeEventListener('click', handleClick.current); + } + + // Disable menu pointer events between `mousedown` and `click`. + disablePointerEvents.current = true; + handleClick.current = () => { + disablePointerEvents.current = false; + menuRef.current.style.pointerEvents = 'auto'; + doc.removeEventListener('click', handleClick.current); + handleClick.current = null; + }; + doc.addEventListener('click', handleClick.current); + update(true, event); }; @@ -426,6 +461,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) { })} /> integration', () => { // Let's open the select component // in the browser user click also focuses fireEvent.mouseDown(trigger); + fireEvent.click(trigger); const options = getAllByRole('option'); expect(options[1]).toHaveFocus(); @@ -82,6 +83,7 @@ describe(' + One + Two + Three + + + + ); +} diff --git a/test/e2e/index.test.ts b/test/e2e/index.test.ts index c4acb11bd69cf3..5bb3e5260c9fe8 100644 --- a/test/e2e/index.test.ts +++ b/test/e2e/index.test.ts @@ -98,4 +98,16 @@ describe('e2e', () => { expect(await page.evaluate(() => document.activeElement?.textContent)).to.equal('ok'); }); }); + + describe('', () => { + it('should not call onClickAway when opening a select', async () => { + await renderFixture('ClickAwayListener/WithSelect'); + expect(await page.textContent('#onClickAway')).to.equal('0'); + await page.click('body'); + expect(await page.textContent('#onClickAway')).to.equal('1'); + await page.click('[role="button"]'); + await page.click('"One"'); + expect(await page.textContent('#onClickAway')).to.equal('1'); + }); + }); });