Skip to content

Commit

Permalink
Update event listener hooks
Browse files Browse the repository at this point in the history
- Remove useEventListener function overloads
- Remove useDocument hook
- Remove useWindow hook
- Add useDocument hook
- Add useWindow hook
- Add RefObjectOption to useEventListener for ref support

#101 #102
  • Loading branch information
leroykorterink committed Mar 31, 2023
1 parent 2f6f3da commit 9c1957f
Show file tree
Hide file tree
Showing 26 changed files with 286 additions and 1,889 deletions.
28 changes: 28 additions & 0 deletions src/hooks/useDocument/useDocument.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Meta } from '@storybook/blocks';

<Meta title="hooks/useDocument" />

# useDocument

This hook returns the Document instance when it's availbable in the environment. If the Document is
not available, it will return `undefined`.

## Reference

```ts
function useDocument(): Document | undefined;
```

## Usage

```tsx
function DemoComponent(): ReactNode {
const document = useDocument(ref, ':focus-within');

useEventListener(document, 'focusin', (event) => {
// do something
});

return null;
}
```
10 changes: 10 additions & 0 deletions src/hooks/useDocument/useDocument.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { renderHook } from '@testing-library/react';
import { useDocument } from './useDocument.js';

describe('useEventListener', () => {
it('Should return the document', () => {
const result = renderHook(useDocument);

expect(result.result.current).toBeInstanceOf(Document);
});
});
9 changes: 9 additions & 0 deletions src/hooks/useDocument/useDocument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Returns undefined when in a server environment, otherwise returns document.
*
* Note: be careful with this hook as it returns a different value on on server
* and client.
*/
export function useDocument(): Document | undefined {
return typeof document === 'undefined' ? undefined : document;
}

This file was deleted.

This file was deleted.

This file was deleted.

12 changes: 0 additions & 12 deletions src/hooks/useDocumentEventListener/useDocumentEventListener.ts

This file was deleted.

7 changes: 5 additions & 2 deletions src/hooks/useEventListener/useEventListener.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable react/jsx-no-literals */
import type { StoryObj } from '@storybook/react';
import { type ReactElement, useState } from 'react';
import { useState, type ReactElement } from 'react';
import { useDocument } from '../useDocument/useDocument.js';
import { useEventListener } from './useEventListener.js';

export default {
Expand All @@ -10,7 +11,9 @@ export default {
function DemoComponent(): ReactElement {
const [text, setText] = useState<ReadonlyArray<string>>([]);

useEventListener(typeof document === 'undefined' ? undefined : document, 'focusin', (event) => {
const document = useDocument();

useEventListener(document, 'focusin', (event) => {
// eslint-disable-next-line no-console
setText((previous) => [
...previous,
Expand Down
56 changes: 43 additions & 13 deletions src/hooks/useEventListener/useEventListener.test.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
import { renderHook } from '@testing-library/react';
/* eslint-disable react/no-multi-comp */
import { jest } from '@jest/globals';
import { render } from '@testing-library/react';
import { createRef, useEffect, useState, type ReactElement } from 'react';
import { useEventListener } from './useEventListener.js';

describe('useEventListener', () => {
it('should not crash', () => {
renderHook(
() => {
useEventListener(typeof document === 'undefined' ? undefined : document, 'focusin', () => {
// eslint-disable-next-line no-console
console.log(document.activeElement);
});
},
{
initialProps: undefined,
},
);
it('Should listen to event attached to element from RefObject', () => {
const spy = jest.fn();
const ref = createRef<HTMLDivElement>();

function Test(): ReactElement {
useEventListener(ref, 'click', spy);

return <div ref={ref} />;
}

render(<Test />);

ref.current?.click();

expect(spy).toBeCalledTimes(1);
});

it('Should listen to event attached to element from state', async () => {
const spy = jest.fn();
let exposedRef: HTMLDivElement | null = null;

function Test(): ReactElement {
const [ref, setRef] = useState<HTMLDivElement | null>(null);

useEffect(() => {
exposedRef = ref;
}, [ref]);

useEventListener(ref, 'click', spy);

return <div ref={setRef} />;
}

render(<Test />);

// @ts-expect-error typescript doesn't infer type for exposedRef correctly
exposedRef?.click();

expect(spy).toBeCalledTimes(1);
});
});
Loading

0 comments on commit 9c1957f

Please sign in to comment.