Skip to content

Commit

Permalink
[pickers] Fix picker components not opening on click in React 17 (#24981
Browse files Browse the repository at this point in the history
)
  • Loading branch information
eps1lon committed Feb 19, 2021
1 parent 3ec5046 commit ae62f9c
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 2 deletions.
@@ -1,8 +1,9 @@
import * as React from 'react';
import TextField from '@material-ui/core/TextField';
import { spy } from 'sinon';
import { spy, useFakeTimers } from 'sinon';
import { expect } from 'chai';
import { describeConformance, fireEvent, screen } from 'test/utils';
import { describeConformance, fireEvent, fireDiscreteEvent, screen } from 'test/utils';
import { TransitionProps } from '@material-ui/core/transitions';
import { TimePickerProps } from '@material-ui/lab/TimePicker';
import DesktopTimePicker from '@material-ui/lab/DesktopTimePicker';
import {
Expand All @@ -12,6 +13,15 @@ import {
} from '../internal/pickers/test-utils';

describe('<DesktopTimePicker />', () => {
let clock: ReturnType<typeof useFakeTimers>;
beforeEach(() => {
clock = useFakeTimers();
});

afterEach(() => {
clock.restore();
});

const render = createPickerRender();
const mount = createPickerMount();

Expand All @@ -30,6 +40,42 @@ describe('<DesktopTimePicker />', () => {
}),
);

const NoTransition = React.forwardRef(function NoTransition(
props: TransitionProps & { children?: React.ReactNode },
ref: React.Ref<HTMLDivElement>,
) {
const { children, in: inProp } = props;

if (!inProp) {
return null;
}
return (
<div ref={ref} tabIndex={-1}>
{children}
</div>
);
});

it('opens on click', () => {
const handleClose = spy();
const handleOpen = spy();
render(
<DesktopTimePicker
value={null}
onChange={() => {}}
onClose={handleClose}
onOpen={handleOpen}
renderInput={(params) => <TextField {...params} />}
TransitionComponent={NoTransition}
/>,
);

fireDiscreteEvent.click(screen.getByLabelText(/choose time/i));

expect(handleClose.callCount).to.equal(0);
expect(handleOpen.callCount).to.equal(1);
});

it('closes on clickaway', () => {
const handleClose = spy();
render(
Expand Down
22 changes: 22 additions & 0 deletions packages/material-ui-lab/src/internal/pickers/PickersPopper.tsx
Expand Up @@ -73,13 +73,35 @@ function useClickAwayListener(

const nodeRef = React.useRef<Element>(null);

const activatedRef = React.useRef(false);
React.useEffect(() => {
if (!active) {
return undefined;
}

function handleClickCapture() {
activatedRef.current = true;
}

document.addEventListener('click', handleClickCapture, { capture: true, once: true });

return () => {
activatedRef.current = false;
document.removeEventListener('click', handleClickCapture, { capture: true });
};
}, [active]);

// The handler doesn't take event.defaultPrevented into account:
//
// event.preventDefault() is meant to stop default behaviors like
// clicking a checkbox to check it, hitting a button to submit a form,
// and hitting left arrow to move the cursor in a text input etc.
// Only special HTML elements have these default behaviors.
const handleClickAway = useEventCallback((event: MouseEvent | TouchEvent) => {
if (!activatedRef.current) {
return;
}

// Given developers can stop the propagation of the synthetic event,
// we can only be confident with a positive value.
const insideReactTree = syntheticEventRef.current;
Expand Down

0 comments on commit ae62f9c

Please sign in to comment.