I have a DatePicker component like this:
const MonthPanel = ({ setMode = () => {} }) => {
return (
<div>
<button
onClick={(e) => setMode("year")}
data-testid="datepicker-year-button"
>
2021
</button>
</div>
);
};
const YearPanel = () => {
return <div data-testid="datepicker-year-panel">YearPanel</div>;
};
const DatePicker = (props) => {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState("month");
const containerRef = useRef();
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleModeChange = (newMode) => {
setMode(newMode);
};
const generatePanel = () => {
switch (mode) {
case "month":
return <MonthPanel setMode={handleModeChange} />;
case "year":
return <YearPanel />;
default:
return null;
}
};
useEffect(() => {
const handleOutsideClick = (e) => {
if (containerRef.current && !containerRef.current.contains(e.target)) {
handleClose();
}
};
window.addEventListener("click", handleOutsideClick);
return () => {
window.removeEventListener("click", handleOutsideClick);
};
}, []);
return (
<div
ref={containerRef}
data-testid="datepicker-container"
>
<div onClick={handleOpen} data-testid="datepicker-input">
Select a date
</div>
{open && <div data-testid="datepicker-dialog">{generatePanel()}</div>}
</div>
);
};
And I have a test file like this:
it.only("should close DatePicker dialog when clicked only outside DatePicker", () => {
const { getByTestId, queryByTestId } = render(
<DatePicker />
);
userEvent.click(getByTestId("datepicker-input"));
userEvent.click(getByTestId("datepicker-year-button"));
expect(queryByTestId("datepicker-dialog")).toBeInTheDocument();
userEvent.click(document.body);
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
Desired state:
When you click outside DatePicker, it should close. And when you click [data-testid="datepicker-year-button"]
it should change DatePicker mode to "year", so the year panel will be shown.
Current state:
When you click [data-testid="datepicker-year-button"]
, it changes Datepicker mode to "year" and MonthPanel (and with it button itself) are removed. Because the button is event target and has already removed, containerRef.current.contains(e.target)
condition is false
and Dialog will be removed too. But the test is showing that dialog is in the document.
The question is how I should test this functionality correctly.
question from:
https://stackoverflow.com/questions/65644222/react-test-passes-but-component-does-not-work-properly-when-state-updated-in-doc 与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…