Skip to content

Commit

Permalink
Merge pull request #972 from sirvine/hotkeys-context-passes-options
Browse files Browse the repository at this point in the history
Feat: Expose descriptions for currently-bound hotkeys.
  • Loading branch information
JohannesKlauss authored Mar 24, 2023
2 parents 668ac7e + 0d0fd59 commit 0ec0fa5
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 24 deletions.
5 changes: 3 additions & 2 deletions src/parseHotkeys.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Hotkey, KeyboardModifiers, Keys } from './types'
import { Hotkey, KeyboardModifiers, Keys, Options } from './types'

const reservedModifierKeywords = ['shift', 'alt', 'meta', 'mod', 'ctrl']

Expand Down Expand Up @@ -39,7 +39,7 @@ export function parseKeysHookInput(keys: string, splitKey: string = ','): string
return keys.split(splitKey)
}

export function parseHotkey(hotkey: string, combinationKey = '+'): Hotkey {
export function parseHotkey(hotkey: string, combinationKey = '+', description?: string): Hotkey {
const keys = hotkey
.toLocaleLowerCase()
.split(combinationKey)
Expand All @@ -58,5 +58,6 @@ export function parseHotkey(hotkey: string, combinationKey = '+'): Hotkey {
return {
...modifiers,
keys: singleCharKeys,
description,
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type KeyboardModifiers = {
export type Hotkey = KeyboardModifiers & {
keys?: string[]
scopes?: Scopes
description?: string
}

export type HotkeysEvent = Hotkey
Expand Down
4 changes: 2 additions & 2 deletions src/useHotkeys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ export default function useHotkeys<T extends HTMLElement>(

if (proxy) {
parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) =>
proxy.addHotkey(parseHotkey(key, memoisedOptions?.combinationKey))
proxy.addHotkey(parseHotkey(key, memoisedOptions?.combinationKey, memoisedOptions?.description))
)
}

Expand All @@ -155,7 +155,7 @@ export default function useHotkeys<T extends HTMLElement>(

if (proxy) {
parseKeysHookInput(_keys, memoisedOptions?.splitKey).forEach((key) =>
proxy.removeHotkey(parseHotkey(key, memoisedOptions?.combinationKey))
proxy.removeHotkey(parseHotkey(key, memoisedOptions?.combinationKey, memoisedOptions?.description))
)
}
}
Expand Down
59 changes: 39 additions & 20 deletions tests/HotkeysProvider.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ test('should render children', () => {
const { getByText } = render(
<HotkeysProvider>
<div>Hello</div>
</HotkeysProvider>,
</HotkeysProvider>
)

expect(getByText('Hello')).toBeInTheDocument()
Expand All @@ -23,8 +23,9 @@ test('should default to wildcard scope', () => {
})

test('should default to wildcard scope if empty array is provided as initialActiveScopes', () => {
const wrapper = ({ children }: { children: ReactNode }) => <HotkeysProvider
initiallyActiveScopes={[]}>{children}</HotkeysProvider>
const wrapper = ({ children }: { children: ReactNode }) => (
<HotkeysProvider initiallyActiveScopes={[]}>{children}</HotkeysProvider>
)
const { result } = renderHook(() => useHotkeysContext(), {
wrapper,
})
Expand Down Expand Up @@ -150,8 +151,9 @@ test('should keep wildcard scope active when all is the only active scope and ge
})

test('should return initially set scopes', () => {
const wrapper = ({ children }: { children: ReactNode }) => <HotkeysProvider
initiallyActiveScopes={['foo', 'bar']}>{children}</HotkeysProvider>
const wrapper = ({ children }: { children: ReactNode }) => (
<HotkeysProvider initiallyActiveScopes={['foo', 'bar']}>{children}</HotkeysProvider>
)
const { result } = renderHook(() => useHotkeysContext(), {
wrapper,
})
Expand All @@ -166,8 +168,9 @@ test('should return all bound hotkeys', () => {
return useHotkeysContext()
}

const wrapper = ({ children }: { children: ReactNode }) => <HotkeysProvider
initiallyActiveScopes={['foo']}>{children}</HotkeysProvider>
const wrapper = ({ children }: { children: ReactNode }) => (
<HotkeysProvider initiallyActiveScopes={['foo']}>{children}</HotkeysProvider>
)
const { result } = renderHook(useIntegratedHotkeys, {
wrapper,
})
Expand All @@ -183,20 +186,19 @@ test('should update bound hotkeys when useHotkeys changes its scopes', () => {
}

const wrapper = ({ children }: { children: ReactNode }) => {
return (
<HotkeysProvider initiallyActiveScopes={['foo']}>
{children}
</HotkeysProvider>
)
return <HotkeysProvider initiallyActiveScopes={['foo']}>{children}</HotkeysProvider>
}

const { result, rerender } = renderHook<{ scopes: string[] }, HotkeysContextType>(({ scopes }) => useIntegratedHotkeys(scopes), {
// @ts-ignore
wrapper,
initialProps: {
scopes: ['foo'],
},
})
const { result, rerender } = renderHook<{ scopes: string[] }, HotkeysContextType>(
({ scopes }) => useIntegratedHotkeys(scopes),
{
// @ts-ignore
wrapper,
initialProps: {
scopes: ['foo'],
},
}
)

expect(result.current.hotkeys).toHaveLength(1)

Expand All @@ -220,4 +222,21 @@ test('should return bound hotkeys when defined as a string array', () => {
})
expect(result.current.hotkeys[0].keys).toEqual(['a', 'c'])
expect(result.current.hotkeys[1].keys).toEqual(['b'])
})
})

test('should return descriptions for bound hotkeys', () => {
const useIntegratedHotkeys = () => {
useHotkeys('a', () => null, { scopes: ['foo'], description: 'bar' })

return useHotkeysContext()
}

const wrapper = ({ children }: { children: ReactNode }) => (
<HotkeysProvider initiallyActiveScopes={['foo']}>{children}</HotkeysProvider>
)
const { result } = renderHook(useIntegratedHotkeys, {
wrapper,
})

expect(result.current.hotkeys[0].description).toEqual('bar')
})

1 comment on commit 0ec0fa5

@vercel
Copy link

@vercel vercel bot commented on 0ec0fa5 Mar 24, 2023

Choose a reason for hiding this comment

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

Please sign in to comment.