Skip to content

Commit

Permalink
tests
Browse files Browse the repository at this point in the history
  • Loading branch information
scurker committed Sep 20, 2024
1 parent 3b5d594 commit 63493f7
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 14 deletions.
4 changes: 2 additions & 2 deletions packages/react/src/components/ClickOutsideListener/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef, useEffect } from 'react';
import setRef from '../../utils/setRef';
import getElementOrRef from '../../utils/getElementOrRef';
import resolveElement from '../../utils/resolveElement';

export interface ClickOutsideListenerProps<
T extends HTMLElement = HTMLElement
Expand Down Expand Up @@ -30,7 +30,7 @@ function ClickOutsideListener(
}

const eventTarget = event.target as HTMLElement;
const elementTarget = getElementOrRef(target);
const elementTarget = resolveElement(target);

if (target && !elementTarget?.contains(eventTarget)) {
onClickOutside(event);
Expand Down
15 changes: 10 additions & 5 deletions packages/react/src/components/Drawer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import ClickOutsideListener from '../ClickOutsideListener';
import useEscapeKey from '../../utils/useEscapeKey';
import useSharedRef from '../../utils/useSharedRef';
import focusableSelector from '../../utils/focusable-selector';
import getElementOrRef from '../../utils/getElementOrRef';
import resolveElement from '../../utils/resolveElement';

interface DrawerProps<T extends HTMLElement = HTMLElement>
extends React.HTMLAttributes<HTMLDivElement> {
Expand Down Expand Up @@ -49,6 +49,7 @@ const Drawer = forwardRef<HTMLDivElement, DrawerProps>(
ref
) => {
const drawerRef = useSharedRef(ref);
const openRef = useRef(!!open);
const previousActiveElementRef = useRef<HTMLElement>(
null
) as React.MutableRefObject<HTMLElement | null>;
Expand All @@ -72,15 +73,19 @@ const Drawer = forwardRef<HTMLDivElement, DrawerProps>(
}, [setIsTransitioning]);

useEffect(() => {
setIsTransitioning(true);
}, [open]);
if (openRef.current !== open) {
setIsTransitioning(true);
}

openRef.current = open;
}, [open, setIsTransitioning]);

useLayoutEffect(() => {
if (open) {
previousActiveElementRef.current =
document.activeElement as HTMLElement;

const initialFocusElement = getElementOrRef(focusInitial);
const initialFocusElement = resolveElement(focusInitial);
if (initialFocusElement) {
initialFocusElement.focus();
} else {
Expand All @@ -95,7 +100,7 @@ const Drawer = forwardRef<HTMLDivElement, DrawerProps>(
}
}
} else if (previousActiveElementRef.current) {
const returnFocusElement = getElementOrRef(focusReturn);
const returnFocusElement = resolveElement(focusReturn);
if (returnFocusElement) {
returnFocusElement.focus();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type { RefObject, MutableRefObject } from 'react';
* When an element can be passed as a value that is either an element or an
* elementRef, this will resolve the property down to the resulting element
*/
export default function getElementOrRef<T extends Element = Element>(
export default function resolveElement<T extends Element = Element>(
elementOrRef: T | RefObject<T> | MutableRefObject<T> | undefined
): T | null {
if (elementOrRef instanceof Element) {
Expand Down
168 changes: 168 additions & 0 deletions packages/react/src/utils/useEscapeKey.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import React from 'react';
import { render, fireEvent, createEvent } from '@testing-library/react';
import useEscapeKey from './useEscapeKey';

const renderHook = (hookFn: () => void) => {
const RenderHook = () => {
hookFn();
return null;
};

render(<RenderHook />);
};

afterEach(() => {
jest.restoreAllMocks();
});

it('should listen to event in bubble phase', () => {
const callback = jest.fn();
const addEventListener = jest.spyOn(document.body, 'addEventListener');
renderHook(() =>
useEscapeKey({
callback,
capture: false
})
);

expect(addEventListener.mock.lastCall?.[2]).toEqual(false);
});

it('should call callback with escape', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyUp(document.body, { key: 'Escape' });
expect(callback).toBeCalled();
});

it('should call callback with esc', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyUp(document.body, { key: 'Esc' });
expect(callback).toBeCalled();
});

it('should call callback with keyCode', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyUp(document.body, { keyCode: 27 });
expect(callback).toBeCalled();
});

it('should listen to keydown event', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback,
event: 'keydown'
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyDown(document.body, { key: 'Escape' });
expect(callback).toBeCalled();
});

it('should listen to keypress event', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback,
event: 'keypress'
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyPress(document.body, { key: 'Escape' });
expect(callback).toBeCalled();
});

it('should listen to keypress event', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback,
event: 'keyup'
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyUp(document.body, { key: 'Escape' });
expect(callback).toBeCalled();
});

it('should listen to target', async () => {
const target = document.createElement('div');
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback,
target
})
);

expect(callback).not.toBeCalled();
await fireEvent.keyUp(target, { key: 'Escape' });
expect(callback).toBeCalled();
});

it('should use capture', () => {
const callback = jest.fn();
const addEventListener = jest.spyOn(document.body, 'addEventListener');
renderHook(() =>
useEscapeKey({
callback,
capture: true
})
);

expect(addEventListener.mock.lastCall?.[2]).toEqual(true);
});

// TODO: fix this test
it.skip('should check for default prevented', async () => {
const callback = jest.fn();
renderHook(() =>
useEscapeKey({
callback,
defaultPrevented: true
})
);

const fireKeyUpEvent = (preventDefault: boolean) => {
const event = createEvent.keyUp(document.body, { key: 'Escape' });
// rtl doesn't let us mock preventDefault
// see: https://github.com/testing-library/react-testing-library/issues/572
// @ts-expect-error not mocking function but
event.preventDefault = preventDefault;
fireEvent(document.body, event);
};

await fireEvent.keyUp(document.body, {
key: 'Escape',
defaultPrevented: true
});
// fireKeyUpEvent(true)
expect(callback).not.toBeCalled();
// fireKeyUpEvent(false)
await fireEvent.keyUp(document.body, { key: 'Escape' });
expect(callback).toBeCalled();
});
8 changes: 2 additions & 6 deletions packages/react/src/utils/useEscapeKey.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, type DependencyList } from 'react';
import resolveElement from './resolveElement';

const isEscapeKey = (event: KeyboardEvent) =>
event.key === 'Escape' || event.key === 'Esc' || event.keyCode === 27;
Expand All @@ -24,12 +25,7 @@ export default function useEscapeKey<T extends HTMLElement = HTMLElement>(
) {
const callback = options.callback;
const event = options.event || 'keyup';
const target = options.target
? 'current' in options.target
? options.target.current
: options.target
: document.body;

const target = resolveElement(options.target) || document.body;
const active = typeof options.active === 'boolean' ? options.active : true;

useEffect(() => {
Expand Down

0 comments on commit 63493f7

Please sign in to comment.