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]: ignoreEventCondition option #980

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
27 changes: 27 additions & 0 deletions documentation/docs/api/use-hotkeys.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,33 @@ this might desirable. We can enable the callback trigger for an input tag using

`INPUT`, `TEXTAREA`, `SELECT`

##### `enableOnFormTags`

```ts
enableOnFormTags: string[] // default: undefined
```

Normally we do not want a hotkey being triggered while a user types something into an input field. In some cases however
this might desirable. We can enable the callback trigger for an input tag using the following values:

`INPUT`, `TEXTAREA`, `SELECT`

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This text is now doubled

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed 👍

##### `ignoreEventWhen`

```ts
ignoreEventWhen: (e: KeyboardEvent) => boolean // default: undefined
```

Provides a fine control over what events to ignore. Can be used in special cases, for example

```js
useHotkeys('a', someCallback, {
ignoreEventWhen: (e) => {
return e.target.className.includes('special-element')
},
})
```

##### `enabled`

```ts
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export type Options = {
enabled?: Trigger // Main setting that determines if the hotkey is enabled or not. (Default: true)
enableOnFormTags?: FormTags[] | boolean // Enable hotkeys on a list of tags. (Default: false)
enableOnContentEditable?: boolean // Enable hotkeys on tags with contentEditable props. (Default: false)
ignoreEventWhen?: (e: KeyboardEvent) => boolean // Ignore evenets based on a condition (Default: undefined)
combinationKey?: string // Character to split keys in hotkeys combinations. (Default: +)
splitKey?: string // Character to separate different hotkeys. (Default: ,)
scopes?: Scopes // Scope
Expand Down
4 changes: 4 additions & 0 deletions src/useHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export default function useHotkeys<T extends HTMLElement>(
return
}

if (memoisedOptions?.ignoreEventWhen?.(e)) {
return;
}

// TODO: SINCE THE EVENT IS NOW ATTACHED TO THE REF, THE ACTIVE ELEMENT CAN NEVER BE INSIDE THE REF. THE HOTKEY ONLY TRIGGERS IF THE
// REF IS THE ACTIVE ELEMENT. THIS IS A PROBLEM SINCE FOCUSED SUB COMPONENTS WON'T TRIGGER THE HOTKEY.
if (
Expand Down
54 changes: 54 additions & 0 deletions tests/useHotkeys.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,60 @@ test('should ignore case of form tags', async () => {
expect(getByTestId('form-tag')).toHaveValue('A')
})

test('should ignore event when ignoreEventWhen\'s condition matches', async () => {
const user = userEvent.setup()
const callback = jest.fn()
const Component = ({ cb, ignoreEventWhen }: { cb: HotkeyCallback; ignoreEventWhen?: (e: KeyboardEvent) => boolean }) => {
useHotkeys<HTMLDivElement>('a', cb, { ignoreEventWhen })

return <button className='ignore' data-testid={'test-button'} />
}

const eventCondition = (e: KeyboardEvent) => {
const element = e.target as HTMLElement

return element.className === 'ignore'
}

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

await user.keyboard('A')

expect(callback).toHaveBeenCalledTimes(1)

await user.click(getByTestId('test-button'))
await user.keyboard('A')

expect(callback).toHaveBeenCalledTimes(1)
JohannesKlauss marked this conversation as resolved.
Show resolved Hide resolved
})

test('shouldn\'t ignore event when ignoreEventWhen\'s condition doesn\'t match', async () => {
const user = userEvent.setup()
const callback = jest.fn()
const Component = ({ cb, ignoreEventWhen }: { cb: HotkeyCallback; ignoreEventWhen?: (e: KeyboardEvent) => boolean }) => {
useHotkeys<HTMLDivElement>('a', cb, { ignoreEventWhen })

return <button className='dont-ignore' data-testid={'test-button'} />
}

const eventCondition = (e: KeyboardEvent) => {
const element = e.target as HTMLElement

return element.className === 'ignore'
}

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

await user.keyboard('A')

expect(callback).toHaveBeenCalledTimes(1)

await user.click(getByTestId('test-button'))
await user.keyboard('A')

expect(callback).toHaveBeenCalledTimes(2)
})

test('enabledOnTags should accept boolean', async () => {
const user = userEvent.setup()
const callback = jest.fn()
Expand Down