Skip to content

Commit

Permalink
Merge pull request #1219 from prostoandrei/addedHotkeyTrigger
Browse files Browse the repository at this point in the history
Feat: Helps to identify which Shortcut was triggered exactly
  • Loading branch information
JohannesKlauss authored Nov 2, 2024
2 parents 2aae06d + 714ebaa commit 51186bd
Show file tree
Hide file tree
Showing 7 changed files with 69 additions and 25 deletions.
5 changes: 4 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [4.6.0] - 01-Nov-2024
* Added 'hotkey' field, which shows which combination triggered the handler

## [2.2.1] - 23-Jul-2020
* Remove deprecated findDOMNode
* Added useIsHotkeyPressed hook.
Expand Down Expand Up @@ -94,4 +97,4 @@
* Cleaned up repository

## [0.1.2] - 15-Jan-2019
* Initial release
* Initial release
18 changes: 18 additions & 0 deletions documentation/docs/api/use-hotkeys.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,24 @@ useHotkeys(['ctrl+a', 'shift+b', 'r', 'f'], (_, handler) => {
})
```

Also you could work directly with hotkey trigger:

```jsx
const HOTKEYS = [
ACTION_A: 'ctrl+a',
ACTION_B: 'shift+b',
]
useHotkeys(HOTKEYS, (_, { hotkey }) => {
switch (hotkey) {
case HOTKEYS.ACTION_A: alert('You pressed ctrl+a!');
break;
case HOTKEYS.ACTION_B: alert('You pressed shift+b!');
break;
}
})
```

***

### `callback`
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "react-hotkeys-hook",
"description": "React hook for handling keyboard shortcuts",
"version": "4.5.1",
"version": "4.6.0",
"repository": {
"type": "git",
"url": "https://github.com/JohannesKlauss/react-keymap-hook.git"
Expand Down
1 change: 1 addition & 0 deletions src/parseHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ export function parseHotkey(hotkey: string, combinationKey = '+', description?:
...modifiers,
keys: singleCharKeys,
description,
hotkey,
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type Hotkey = KeyboardModifiers & {
keys?: readonly string[]
scopes?: Scopes
description?: string
hotkey: string
}

export type HotkeysEvent = Hotkey
Expand Down
63 changes: 42 additions & 21 deletions tests/useHotkeys.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import {
import { createEvent, fireEvent, render, screen, renderHook } from '@testing-library/react'

const wrapper =
(initialScopes: string[]): JSXElementConstructor<{children: ReactElement}> =>
({ children }: { children?: ReactNode }) =>
<HotkeysProvider initiallyActiveScopes={initialScopes}>{children}</HotkeysProvider>
(initialScopes: string[]): JSXElementConstructor<{ children: ReactElement }> =>
({ children }: { children?: ReactNode }) =>
<HotkeysProvider initiallyActiveScopes={initialScopes}>{children}</HotkeysProvider>

type HookParameters = {
keys: Keys
Expand Down Expand Up @@ -59,7 +59,7 @@ test('should log a warning when trying to set a scope without a wrapped provider
renderHook(() => useHotkeys('a', callback, { scopes: 'foo' }))

expect(console.warn).toHaveBeenCalledWith(
'A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>',
'A hotkey has the "scopes" option set, however no active scopes were found. If you want to use the global scopes feature, you need to wrap your app in a <HotkeysProvider>'
)
expect(callback).not.toHaveBeenCalled()
})
Expand Down Expand Up @@ -267,7 +267,6 @@ test('should be able to always output correct keys on multiple hotkeys', async (
await user.keyboard('{/A}')
expect(callbackA).toHaveBeenCalledTimes(1)
expect(callbackB).toHaveBeenCalledTimes(2)

})

test('should be able to parse first argument as string, array or readonly array', async () => {
Expand Down Expand Up @@ -376,7 +375,7 @@ test('should reflect set splitKey character', async () => {
({ keys, options }) => useHotkeys(keys, callback, options),
{
initialProps: { keys: 'a, b', options: undefined },
},
}
)

await user.keyboard('A')
Expand Down Expand Up @@ -536,13 +535,19 @@ test('should ignore case of form tags', async () => {
expect(getByTestId('form-tag')).toHaveValue('A')
})

test('should ignore event when ignoreEventWhen\'s condition matches', async () => {
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 }) => {
const Component = ({
cb,
ignoreEventWhen,
}: {
cb: HotkeyCallback
ignoreEventWhen?: (e: KeyboardEvent) => boolean
}) => {
useHotkeys<HTMLDivElement>('a', cb, { ignoreEventWhen })

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

const eventCondition = (e: KeyboardEvent) => {
Expand All @@ -563,13 +568,19 @@ test('should ignore event when ignoreEventWhen\'s condition matches', async () =
expect(callback).toHaveBeenCalledTimes(1)
})

test('shouldn\'t ignore event when ignoreEventWhen\'s condition doesn\'t match', async () => {
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 }) => {
const Component = ({
cb,
ignoreEventWhen,
}: {
cb: HotkeyCallback
ignoreEventWhen?: (e: KeyboardEvent) => boolean
}) => {
useHotkeys<HTMLDivElement>('a', cb, { ignoreEventWhen })

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

const eventCondition = (e: KeyboardEvent) => {
Expand All @@ -593,10 +604,16 @@ test('shouldn\'t ignore event when ignoreEventWhen\'s condition doesn\'t match',
test('should call ignoreEventWhen callback only when event is a hotkey match', async () => {
const user = userEvent.setup()
const callback = jest.fn()
const Component = ({ cb, ignoreEventWhen }: { cb: HotkeyCallback; ignoreEventWhen?: (e: KeyboardEvent) => boolean }) => {
const Component = ({
cb,
ignoreEventWhen,
}: {
cb: HotkeyCallback
ignoreEventWhen?: (e: KeyboardEvent) => boolean
}) => {
useHotkeys<HTMLDivElement>('a', cb, { ignoreEventWhen })

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

const { getByTestId } = render(<Component cb={jest.fn()} ignoreEventWhen={callback} />)
Expand Down Expand Up @@ -886,8 +903,7 @@ test('should allow named keys like arrow keys, space, enter, backspace, etc.', a
expect(callback).toHaveBeenCalledTimes(7)
})

test.skip('should trigger when used in portals', async () => {
})
test.skip('should trigger when used in portals', async () => {})

test('should parse options and dependencies correctly no matter their position', async () => {
const user = userEvent.setup()
Expand Down Expand Up @@ -929,6 +945,7 @@ test('should pass keyboard event and hotkey object to callback', async () => {
keys: ['a'],
shift: false,
ctrl: false,
hotkey: 'a',
alt: false,
meta: false,
mod: false,
Expand All @@ -948,6 +965,7 @@ test('should set shift to true in hotkey object if listening to shift', async ()
keys: ['a'],
shift: true,
ctrl: false,
hotkey: 'shift+a',
alt: false,
meta: false,
mod: false,
Expand All @@ -967,6 +985,7 @@ test('should set ctrl to true in hotkey object if listening to ctrl', async () =
keys: ['a'],
shift: false,
ctrl: true,
hotkey: 'ctrl+a',
alt: false,
meta: false,
mod: false,
Expand All @@ -986,6 +1005,7 @@ test('should set alt to true in hotkey object if listening to alt', async () =>
keys: ['a'],
shift: false,
ctrl: false,
hotkey: 'alt+a',
alt: true,
meta: false,
mod: false,
Expand All @@ -1005,6 +1025,7 @@ test('should set mod to true in hotkey object if listening to mod', async () =>
keys: ['a'],
shift: false,
ctrl: false,
hotkey: 'mod+a',
alt: false,
meta: false,
mod: true,
Expand All @@ -1024,6 +1045,7 @@ test('should set meta to true in hotkey object if listening to meta', async () =
keys: ['a'],
shift: false,
ctrl: false,
hotkey: 'meta+a',
alt: false,
meta: true,
mod: false,
Expand All @@ -1043,6 +1065,7 @@ test('should set multiple modifiers to true in hotkey object if listening to mul
keys: ['a'],
shift: true,
alt: false,
hotkey: 'mod+shift+a',
ctrl: false,
meta: false,
mod: true,
Expand Down Expand Up @@ -1132,8 +1155,7 @@ test('should call preventDefault option function with hotkey and keyboard event'
const user = userEvent.setup()
const preventDefault = jest.fn()

renderHook(() => useHotkeys('a', async () => {
}, { preventDefault }))
renderHook(() => useHotkeys('a', async () => {}, { preventDefault }))

await user.keyboard('A')

Expand All @@ -1142,6 +1164,7 @@ test('should call preventDefault option function with hotkey and keyboard event'
keys: ['a'],
shift: false,
alt: false,
hotkey: 'a',
ctrl: false,
meta: false,
mod: false,
Expand Down Expand Up @@ -1251,7 +1274,6 @@ test('Should ignore modifiers if option is set', async () => {
expect(callback).toHaveBeenCalledTimes(2)
})


test('should respect dependencies array if they are passed', async () => {
function Fixture() {
const [count, setCount] = useState(0)
Expand All @@ -1277,7 +1299,6 @@ test('should respect dependencies array if they are passed', async () => {
expect(getByText('1')).not.toBeNull()
})


test('should use updated callback if no dependencies are passed', async () => {
function Fixture() {
const [count, setCount] = useState(0)
Expand Down Expand Up @@ -1346,4 +1367,4 @@ test('Should listen to special chars with modifiers', async () => {
await user.keyboard(`{Shift>}-{/Shift}`)

expect(callback).toHaveBeenCalledTimes(1)
})
})

0 comments on commit 51186bd

Please sign in to comment.