Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
359 views
in Technique[技术] by (71.8m points)

javascript - React test passes but component does not work properly when state updated in document.addListener handler

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You could call e.stopPropagation() on your button click handler in <MonthPanel>, to prevent the event bubbling up and being caught by the window's eventListener.

<button
    onClick={(e) => {
        e.stopPropagation();
        setMode("year");
    }}
    data-testid="datepicker-year-button"
>

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...