UNPKG

4.54 kBMarkdownView Raw
1# `@shopify/react-shortcuts`
2
3[![CircleCI](https://circleci.com/gh/Shopify/quilt.svg?style=svg&circle-token=8dafbec2d33dcb489dfce1e82ed37c271b26aeba)](https://circleci.com/gh/Shopify/quilt)
4[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE.md) [![npm version](https://badge.fury.io/js/%40shopify%2Freact-shortcuts.svg)](https://badge.fury.io/js/%40shopify%2Freact-shortcuts) ![bundle size badge](https://img.shields.io/bundlephobia/minzip/@shopify/react-shortcuts.svg)
5
6Declarative and performant library for matching shortcut combinations in React applications.
7
8## Installation
9
10```bash
11$ yarn add @shopify/react-shortcuts
12```
13
14## Usage
15
16The library exposes three main parts, `<ShortcutProvider />`, `<Shortcut />` and `ShortcutManager`.
17
18### ShortcutProvider
19
20Wrapping your application in a `<ShortcutProvider />` allows you to use `<Shortcut />` components anywhere in your application, internally sharing a single `ShortcutManager` instance to minimize listeners and collisions.
21
22```ts
23// App.ts
24
25import React from 'react';
26import {ShortcutProvider} from '@shopify/react-shortcuts';
27
28export default function App() {
29 <ShortcutProvider>{/* the rest of your app */}</ShortcutProvider>;
30}
31```
32
33### Shortcut
34
35Shortcut is used to register a new keyboard shortcut to `ShortcutManager`. It can be triggered globally or only when a node is focused.
36
37**Note: a `<Shortcut />` must have a `<ShortcutProvider />` somewhere above it in the tree.**
38
39#### API
40
41```ts
42export interface Props {
43 /*
44 keys that, when pressed sequentially, will trigger `onMatch`
45 */
46 keys: Key[];
47 /*
48 a callback that will trigger when the key combination is pressed
49 */
50 onMatch(keys: Key[]): void;
51 /*
52 a node that, when supplied, will be used to only fire `onMatch` when it is focused
53 */
54 node?: HTMLElement | null;
55 /*
56 a boolean that lets you temporarily disable the shortcut
57 */
58 ignoreInput?: boolean;
59 /*
60 a boolean that lets you opt out of swalloing the key event and let it propagate
61 */
62 allowDefault?: boolean;
63}
64```
65
66#### Basic example
67
68```ts
69// MyComponent.tsx
70
71import React from 'react';
72import {Shortcut} from '@shopify/react-shortcuts';
73
74export default function MyComponent() {
75 return (
76 <div>
77 {/* some app markup here */}
78 <Shortcut key={['f', 'o', 'o']} onMatch={() => console.log('foo')} />
79 </div>
80 );
81}
82```
83
84#### With modifier keys
85
86```ts
87// MyComponent.tsx
88
89import React from 'react';
90import {Shortcut} from '@shopify/react-shortcuts';
91
92export default function MyComponent() {
93 return (
94 <div>
95 {/* some app markup here */}
96 <Shortcut
97 key={['ctrlKey', 'shiftKey', 'b']}
98 onMatch={() => console.log('bar!')}
99 />
100 </div>
101 );
102}
103```
104
105#### On a focused node
106
107Provide a node in the form of a `ref`. `<Shortcut />` will automatically subscribe to the `ShortcutManager` once the node is available.
108
109```ts
110// MyComponent.tsx
111
112import React from 'react';
113import {Shortcut} from '@shopify/react-shortcuts';
114
115class MyComponent extends React.Component {
116 state = {};
117
118 render() {
119 const {fooNode} = this.state;
120 return (
121 <div>
122 <button ref={node => this.setState({fooNode: node})} />
123 <Shortcut
124 node={fooNode}
125 key={['f', 'o', 'o']}
126 onMatch={() => console.log('foo')}
127 />
128 </div>
129 );
130 }
131}
132```
133
134### ShortcutManager
135
136`ShortcutManager` is created by `ShortcutProvider` and handles the logic for calling the appropriate shortcut callbacks and avoiding conflicts. You should never need to use it directly in application code, though you may want access to it in tests.
137
138#### Example jest test
139
140Given a component implementing a `<Shortcut />`
141
142```ts
143// MyComponent.tsx
144
145export default function MyComponent() {
146 return (
147 <div>
148 {/* some app markup here */}
149 <Shortcut key={['f', 'o', 'o']} onMatch={() => console.log('foo')} />
150 </div>
151 );
152}
153```
154
155you might want to add a `ShortcutManager` to it's context in an enzyme test to prevent errors
156
157```ts
158// MyComponent.test.tsx
159
160import React from 'react';
161import {mount} from 'enzyme';
162import {ShortcutManager, Shortcut} from '@shopify/react-shortcuts';
163
164import MyComponent from './MyComponent';
165
166describe('my-component', () => {
167 it('renders a shortcut for f,o,o', () => {
168 const component = mount(<MyComponent />, {
169 context: {shortcutManager: new ShortcutManager()},
170 });
171
172 const shortcut = component.find(Shortcut);
173
174 expect(shortcut.prop('key')).toEqual(['f', 'o', 'o']);
175 });
176});
177```