-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Add support for keyboard shortcuts to <MenuItemLink>
#10790
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
Changes from 4 commits
25deb15
212100c
9599edf
caf750d
5a4bb64
1a66cc1
8f81a7d
0560b25
eb48bad
eeb1502
07445ad
1397d2e
e6a59e8
2d66154
88374b6
e242533
707583e
eb5452b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { | ||
| type HotkeyCallback, | ||
| type Keys, | ||
| type Options, | ||
| useHotkeys, | ||
| } from 'react-hotkeys-hook'; | ||
|
|
||
| export const KeyboardShortcut = (props: KeyboardShortcutProps) => { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please add jsDoc to explain why we need that (to add a keyboard shortcut conditionally? I so, why don't we use the "enabled" option?) |
||
| const { callback, dependencies, keys, options } = props; | ||
| useHotkeys(keys, callback, options, dependencies); | ||
| return null; | ||
| }; | ||
|
|
||
| export interface KeyboardShortcutProps { | ||
| keys: Keys; | ||
| callback: HotkeyCallback; | ||
| options?: Options; | ||
| dependencies?: readonly unknown[]; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { Keys } from 'react-hotkeys-hook'; | ||
|
|
||
| export const getKeyboardShortcutLabel = (keyboardShortcut: Keys) => { | ||
| if (typeof keyboardShortcut === 'string') { | ||
| return keyboardShortcut.split('+').join(' + '); | ||
| } | ||
| return keyboardShortcut | ||
| .map(shortcut => getKeyboardShortcutLabel(shortcut)) | ||
| .join(', '); | ||
| }; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit short, we'd need to be smarter than that and show icons for key modifiers dependent on the platform (e.g. command / windows) |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| import * as React from 'react'; | ||
| import { fireEvent, render, screen } from '@testing-library/react'; | ||
| import { Default, WithDashboard, WithKeyboardShortcuts } from './Menu.stories'; | ||
|
|
||
| describe('<Menu>', () => { | ||
| it('should render a default menu with items for all registered resources', async () => { | ||
| render(<Default />); | ||
| await screen.findByText('Posts', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Comments', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Tags', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Users', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Orders', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Reviews', { selector: '[role="menuitem"]' }); | ||
| }); | ||
|
|
||
| it('should render a default menu with items for all registered resources and the dashboard', async () => { | ||
| render(<WithDashboard />); | ||
| await screen.findByText('Dashboard', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Posts', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Comments', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Tags', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Users', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Orders', { selector: '[role="menuitem"]' }); | ||
| await screen.findByText('Reviews', { selector: '[role="menuitem"]' }); | ||
| }); | ||
|
|
||
| it('should support keyboard shortcuts', async () => { | ||
| render(<WithKeyboardShortcuts />); | ||
| await screen.findByText('Dashboard', { selector: '[role="menuitem"]' }); | ||
| fireEvent.keyDown(global.document, { | ||
| key: 'c', | ||
| code: 'KeyC', | ||
| ctrlKey: true, | ||
| altKey: true, | ||
| }); | ||
| expect(await screen.findAllByText('Customers')).toHaveLength(2); | ||
| fireEvent.keyDown(global.document, { | ||
| key: 's', | ||
| code: 'KeyS', | ||
| ctrlKey: true, | ||
| altKey: true, | ||
| }); | ||
| expect(await screen.findAllByText('Sales')).toHaveLength(2); | ||
| fireEvent.keyDown(global.document, { | ||
| key: 'p', | ||
| code: 'KeyP', | ||
| ctrlKey: true, | ||
| altKey: true, | ||
| }); | ||
| expect(await screen.findAllByText('Products')).toHaveLength(2); | ||
| fireEvent.keyDown(global.document, { | ||
| key: 'd', | ||
| code: 'KeyD', | ||
| ctrlKey: true, | ||
| altKey: true, | ||
| }); | ||
| expect(await screen.findAllByText('Dashboard')).toHaveLength(2); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -44,7 +44,12 @@ export const Menu = (inProps: MenuProps) => { | |
| props: inProps, | ||
| name: PREFIX, | ||
| }); | ||
| const { children, className, ...rest } = props; | ||
| const { | ||
| children, | ||
| className, | ||
| hasDashboard: hasDashboardProp, | ||
| ...rest | ||
| } = props; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you just want to discard
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's how we usually do it |
||
| const hasDashboard = useHasDashboard(); | ||
| const [open] = useSidebarState(); | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.