Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Add support for event capturing #1214

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ All options are optional and have a default value which you can override to chan
| `keydown` | `boolean` | `true` | Determines whether to listen to the browsers `keydown` event for triggering the callback. If you set both `keyup`and `keydown` to true, the callback will trigger on both events. |
| `preventDefault` | `boolean` or `(keyboardEvent: KeyboardEvent, hotkeysEvent: HotkeysEvent) => boolean` | `false` | Set this to a `true` if you want the hook to prevent the browsers default behavior on certain keystrokes like `meta+s` to save a page. NOTE: Certain keystrokes are not preventable, like `meta+w` to close a tab in chrome. |
| `description` | `string` | `undefined` | Use this option to describe what the hotkey does. this is helpful if you want to display a list of active hotkeys to the user. |
| `capture` | `boolean` | `undefined` | Set this option to have the hook use event capturing instead of event bubbling when registering event listeners. |


#### Overloads
Expand Down
3 changes: 2 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export type Options = {
preventDefault?: Trigger // Prevent default browser behavior? (Default: false)
description?: string // Use this option to describe what the hotkey does. (Default: undefined)
document?: Document // Listen to events on the document instead of the window. (Default: false)
ignoreModifiers?: boolean // Ignore modifiers when matching hotkeys. (Default: false)
ignoreModifiers?: boolean // Ignore modifiers when matching hotkeys. (Default: false),
capture?: boolean // Sets key event handlers to trigger on capturing phase. (Default: false)
}

export type OptionsOrDependencyArray = Options | DependencyList
8 changes: 4 additions & 4 deletions src/useHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ export default function useHotkeys<T extends HTMLElement>(
const domNode = ref || _options?.document || document

// @ts-ignore
domNode.addEventListener('keyup', handleKeyUp)
domNode.addEventListener('keyup', handleKeyUp, { capture: memoisedOptions?.capture })
// @ts-ignore
domNode.addEventListener('keydown', handleKeyDown)
domNode.addEventListener('keydown', handleKeyDown, { capture: memoisedOptions?.capture })

if (proxy) {
parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) =>
Expand All @@ -155,9 +155,9 @@ export default function useHotkeys<T extends HTMLElement>(

return () => {
// @ts-ignore
domNode.removeEventListener('keyup', handleKeyUp)
domNode.removeEventListener('keyup', handleKeyUp, { capture: memoisedOptions?.capture })
// @ts-ignore
domNode.removeEventListener('keydown', handleKeyDown)
domNode.removeEventListener('keydown', handleKeyDown, { capture: memoisedOptions?.capture })

if (proxy) {
parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) =>
Expand Down
19 changes: 19 additions & 0 deletions tests/useHotkeys.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1346,4 +1346,23 @@ test('Should listen to special chars with modifiers', async () => {
await user.keyboard(`{Shift>}-{/Shift}`)

expect(callback).toHaveBeenCalledTimes(1)
})

test('should use event capturing if option is set', async () => {
const user = userEvent.setup()
const callback = jest.fn()

const Component = ({ cb }: { cb: HotkeyCallback }) => {
useHotkeys<HTMLDivElement>('a', cb, { capture: true, enableOnFormTags: true })

return <input type={'text'} data-testid={'form-tag'} onKeyDown={(e) => e.stopPropagation()} />
}

const { getByTestId } = render(<Component cb={callback} />)

await user.click(getByTestId('form-tag'))
await user.keyboard('A')

expect(callback).toHaveBeenCalledTimes(1)
expect(getByTestId('form-tag')).toHaveValue('A')
})