Skip to content

Commit

Permalink
add tests for useFocusChange and fix errors
Browse files Browse the repository at this point in the history
  • Loading branch information
dartess committed Aug 25, 2022
1 parent e62e04f commit 030ce69
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 9 deletions.
2 changes: 1 addition & 1 deletion internal/test/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,5 @@ export {
act as actHooks,
} from "@testing-library/react-hooks";
export { cleanup, fireEvent, screen, act } from "@testing-library/react";
export * as userEvent from "@testing-library/user-event";
export { default as userEvent } from "@testing-library/user-event";
export type { RenderOptions, RenderResult };
106 changes: 106 additions & 0 deletions packages/utils/__tests__/use-focus-change.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import * as React from "react";
import { render, cleanup, userEvent } from "@reach-internal/test/utils";
import { afterEach, describe, expect, it, vi } from "vitest";
import { useFocusChange } from "@reach/utils";

afterEach(cleanup);

describe("useFocusChange", () => {
const Test = ({
onChange,
when,
}: {
onChange: () => void;
when?: "focus" | "blur";
}) => {
useFocusChange(onChange, when);
return (
<>
<input type="text" placeholder="first" />
<input type="text" placeholder="second" />
<div>just div</div>
</>
);
};

const renderTest = (when?: "focus" | "blur") => {
const handleChange = vi.fn();
const { getByPlaceholderText, getByText } = render(
<Test onChange={handleChange} when={when} />
);
const firstInput = getByPlaceholderText("first");
const secondInput = getByPlaceholderText("second");
const div = getByText("just div");
return {
firstInput,
secondInput,
div,
handleChange,
};
};

/**
* WARNING: The order of the tests is important:
* the blur test should come first.
* If this is not the case, the activeElement will be dirty
* and the blur event will fire when the input is clicked.
*/

it("should call handler on blur", async () => {
const {
firstInput,
secondInput,
div,
handleChange: handleBlur,
} = renderTest("blur");

await userEvent.click(firstInput);
expect(handleBlur).not.toHaveBeenCalled();

await userEvent.click(secondInput);
expect(handleBlur).toHaveBeenCalledTimes(1);
expect(handleBlur).toHaveBeenCalledWith(
document.body,
document.body,
expect.any(FocusEvent)
);

await userEvent.click(div);
expect(handleBlur).toHaveBeenCalledTimes(2);
expect(handleBlur).toHaveBeenCalledWith(
document.body,
document.body,
expect.any(FocusEvent)
);
});

it("should call handler on focus", async () => {
const { firstInput, secondInput, handleChange: handleFocus } = renderTest();

await userEvent.click(firstInput);
expect(handleFocus).toHaveBeenCalledTimes(1);
expect(handleFocus).toHaveBeenCalledWith(
firstInput,
document.body,
expect.any(FocusEvent)
);

await userEvent.click(secondInput);
expect(handleFocus).toHaveBeenCalledTimes(2);
expect(handleFocus).toHaveBeenCalledWith(
secondInput,
firstInput,
expect.any(FocusEvent)
);
});

it("should do not call handler on focus at the same node", async () => {
const { firstInput, handleChange: handleFocus } = renderTest();

await userEvent.click(firstInput);
expect(handleFocus).toHaveBeenCalledOnce();

await userEvent.click(firstInput);
expect(handleFocus).toHaveBeenCalledOnce();
});
});
20 changes: 12 additions & 8 deletions packages/utils/src/use-focus-change.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,24 @@ export function useFocusChange(
lastActiveElement.current = ownerDocument.activeElement;

function onChange(event: FocusEvent) {
if (lastActiveElement.current !== ownerDocument.activeElement) {
handleChange(
ownerDocument.activeElement,
lastActiveElement.current,
event
);
lastActiveElement.current = ownerDocument.activeElement;
if (
when === "focus" &&
lastActiveElement.current === ownerDocument.activeElement
) {
return;
}
handleChange(
ownerDocument.activeElement,
lastActiveElement.current,
event
);
lastActiveElement.current = ownerDocument.activeElement;
}

ownerDocument.addEventListener(when, onChange, true);

return () => {
ownerDocument.removeEventListener(when, onChange);
ownerDocument.removeEventListener(when, onChange, true);
};
}, [when, handleChange, ownerDocument]);
}

0 comments on commit 030ce69

Please sign in to comment.