@peter.naydenov/shortcuts 3.3.1 → 3.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/Changelog.md +51 -1
  2. package/README.md +2 -0
  3. package/dist/main.d.ts +120 -0
  4. package/dist/methods/_normalizeWithPlugins.d.ts +2 -0
  5. package/dist/methods/_readShortcutWithPlugins.d.ts +2 -0
  6. package/dist/methods/_systemAction.d.ts +2 -0
  7. package/dist/methods/changeContext.d.ts +2 -0
  8. package/dist/methods/index.d.ts +17 -0
  9. package/dist/methods/listShortcuts.d.ts +17 -0
  10. package/dist/methods/load.d.ts +2 -0
  11. package/dist/methods/unload.d.ts +2 -0
  12. package/dist/plugins/click/_findTarget.d.ts +2 -0
  13. package/dist/plugins/click/_listenDOM.d.ts +5 -0
  14. package/dist/plugins/click/_normalizeShortcutName.d.ts +2 -0
  15. package/dist/plugins/click/_readClickEvent.d.ts +2 -0
  16. package/dist/plugins/click/_registerShortcutEvents.d.ts +2 -0
  17. package/dist/plugins/click/index.d.ts +15 -0
  18. package/dist/plugins/form/_defaults.d.ts +5 -0
  19. package/dist/plugins/form/_listenDOM.d.ts +5 -0
  20. package/dist/plugins/form/_normalizeShortcutName.d.ts +2 -0
  21. package/dist/plugins/form/_registerShortcutEvents.d.ts +2 -0
  22. package/dist/plugins/form/index.d.ts +10 -0
  23. package/dist/plugins/key/_listenDOM.d.ts +5 -0
  24. package/dist/plugins/key/_normalizeShortcutName.d.ts +2 -0
  25. package/dist/plugins/key/_readKeyEvent.d.ts +2 -0
  26. package/dist/plugins/key/_registerShortcutEvents.d.ts +2 -0
  27. package/dist/plugins/key/_specialChars.d.ts +32 -0
  28. package/dist/plugins/key/index.d.ts +15 -0
  29. package/dist/shortcuts.cjs +1 -1
  30. package/dist/shortcuts.esm.mjs +1 -1
  31. package/dist/shortcuts.umd.js +1 -1
  32. package/jsconfig.json +10 -0
  33. package/package.json +16 -7
  34. package/src/main.js +98 -28
  35. package/src/methods/_readShortcutWithPlugins.js +2 -1
  36. package/src/methods/changeContext.js +2 -1
  37. package/src/methods/listShortcuts.js +2 -1
  38. package/src/methods/load.js +10 -7
  39. package/src/methods/unload.js +3 -3
  40. package/src/plugins/click/_listenDOM.js +13 -3
  41. package/src/plugins/click/index.js +16 -6
  42. package/src/plugins/form/index.js +12 -17
  43. package/src/plugins/key/_listenDOM.js +13 -0
  44. package/src/plugins/key/index.js +11 -5
  45. package/test/01-general.test.js +158 -251
  46. package/test/02-key.test.js +272 -0
  47. package/test/03-click.test.js +352 -0
  48. package/test/04-form.test.js +90 -0
  49. package/test-helpers/setup.js +18 -0
  50. package/test-helpers/wait.js +8 -0
  51. package/tsconfig.json +23 -0
  52. package/vitest.config.js +21 -0
  53. package/vitest-example/HelloWorld.js +0 -9
  54. package/vitest-example/HelloWorld.test.js +0 -11
  55. package/vitest.workspace.js +0 -19
  56. /package/{test-components → test-helpers}/Block.jsx +0 -0
  57. /package/{test-components → test-helpers}/style.css +0 -0
@@ -1,37 +1,52 @@
1
- import { beforeEach, describe, it, test } from 'vitest'
1
+ import { beforeEach, afterEach, describe, it, expect } from 'vitest'
2
2
  import { userEvent } from '@vitest/browser/context'
3
+ import {
4
+ getByLabelText,
5
+ getByText,
6
+ getByTestId,
7
+ queryByTestId,
8
+ // Tip: all queries are also exposed on an object
9
+ // called "queries" which you could import here as well
10
+ waitFor
11
+ } from '@testing-library/dom'
12
+
3
13
 
4
- import Block from '../test-components/Block.jsx'
5
- import VisaulController from '@peter.naydenov/visual-controller-for-react'
6
- import '../test-components/style.css'
7
14
 
15
+ import '../test-helpers/style.css'
16
+ import Block from '../test-helpers/Block.jsx'
17
+ import VisaulController from '@peter.naydenov/visual-controller-for-react'
18
+ import wait from '../test-helpers/wait.js'
8
19
  import {
9
20
  pluginClick,
10
21
  pluginKey
11
22
  , pluginForm
12
23
  , shortcuts
13
24
  } from '../src/main.js'
14
- import { expect } from 'chai'
25
+
15
26
 
16
27
  import askForPromise from 'ask-for-promise'
17
28
 
18
29
  const html = new VisaulController ();
19
-
20
30
  let
21
31
  a = false
22
32
  , b = false
33
+ , c = null
23
34
  ;
24
-
25
-
26
- function wait (ms) {
27
- return new Promise((resolve) => setTimeout(resolve, ms));
28
- }
29
-
30
- const short = shortcuts ({onShortcut : ( shortcut, {context,note,type}) => console.log (shortcut, context, note, type) });
31
-
32
- short.load ({
35
+ const contextDefinition = {
33
36
  general : {
34
- ' key : shift+a': [ () => a = true ]
37
+ ' key : shift+a': [
38
+ () => a = true,
39
+ () => c = 'triggered'
40
+ ],
41
+ '@shortcuts-error' : ( m ) => c = m
42
+ }
43
+ , touch : {
44
+ // Single click with left button
45
+ 'click: left-1': () => b = true,
46
+ // Double click with left button
47
+ 'click: left-2': () => b = true,
48
+ // Single click with right button
49
+ 'click: right-1': () => b = true
35
50
  }
36
51
  , extra : {
37
52
  'key : p,r,o,b,a': () => b = true
@@ -47,253 +62,145 @@ short.load ({
47
62
  }
48
63
  ]
49
64
  }
50
- })
51
-
52
- beforeEach ( () => {
53
- let container = document.createElement ( 'div' )
54
- container.id = 'app'
55
- document.body.appendChild ( container )
56
- html.publish ( Block, {}, 'app' )
57
- a = false, b = false
58
- }) // beforeEach
65
+ }
59
66
 
60
67
 
68
+
69
+ let short = shortcuts ();
61
70
 
62
71
 
63
72
 
64
73
  describe ( "Shortcuts", () => {
65
74
 
75
+ beforeEach ( async () => {
76
+ short.load ( contextDefinition )
77
+ let container = document.createElement ( 'div' )
78
+ container.id = 'app'
79
+ document.body.appendChild ( container )
80
+ await html.publish ( Block, {}, 'app' )
81
+ a = false, b = false
82
+ }) // beforeEach
66
83
 
67
84
 
68
- test ( 'Shortcut if no plugin installed', () => {
69
- let res = new Promise ( (resolve,reject) => {
70
- short.changeContext ( 'general' )
71
- let r = short.listShortcuts ('general')
72
- expect ( r[0]).to.equal ( ' key : shift+a' ) // Shortcut name is the same as it was set
73
- resolve ('success')
74
- })
75
- return res
76
- }) // test no plugin installed
77
-
78
-
79
-
80
- test ( 'Key plugin, no context selected', () => {
81
- const res = new Promise ( ( resolve ) => {
82
- short.enablePlugin ( pluginKey )
83
- const r = short.listShortcuts ( 'general' )
84
- expect ( r[0] ).to.equal ( 'KEY:A+SHIFT' ) // Shortcut name is recognized by plugin and is normalized
85
- resolve ('success')
86
- })
87
- return res
88
- }) // test key plugin installed, no context selected
89
-
90
-
91
-
92
- test ( 'Key plugin with context selected', () => {
93
- const res = new Promise ( (resolve) => {
94
- short.enablePlugin ( pluginKey )
95
- short.changeContext ( 'general' )
96
- const r = short.listShortcuts ('general')
97
- expect ( r[0] ).to.equal ( 'KEY:A+SHIFT' ) // Shortcut name is recognized by plugin and is normalized
98
- resolve ( 'success' )
99
- })
100
- return res
101
- }) // test key plugin installed with context selected
102
-
103
-
104
-
105
- test ( 'Simple shortcut', () => {
106
- const res = new Promise ( (resolve) => {
107
- short.enablePlugin ( pluginKey )
108
- short.changeContext ()
109
- short.changeContext ( 'general' )
110
- userEvent.keyboard ( '{Shift>}a</Shift>' )
111
- expect ( a ).to.be.false
112
- wait ( 1000 )
113
- .then ( () => { // Default wait sequence timeout is 480 ms, but maxSequence is 1, so we don't need to wait for timeout
114
- expect ( a ).to.be.true
115
- resolve ( 'success' )
116
- })
117
- })
118
- return res
119
- }) // test simple shortcut
120
-
121
-
122
-
123
- test ( 'Call sequence shortcut', async () => {
124
- let res = new Promise ( async (resolve) => {
125
- b = false
126
- short.enablePlugin ( pluginKey )
127
- short.changeContext ()
128
- short.changeContext ( 'extra' )
129
- await userEvent.keyboard ( 'proba' )
130
- expect ( b ).to.be.true
131
- resolve ( 'success' )
132
- })
133
- return res
134
- }) // test call sequence shortcut
135
-
136
-
137
-
138
- test ( 'Single mouse click', done => {
139
- const res = new Promise ( async (resolve) => {
140
- expect ( a ).to.be.false
141
- expect ( b ).to.be.false
142
- short.enablePlugin ( pluginClick )
143
-
144
- short.load ({ 'extra' : {
145
- ' cLIck : left - 1 ' : () => a = true // Check if spaces, letter case can break the shortcut recognition
146
- }
147
- })
148
- short.changeContext ()
149
- short.changeContext ( 'extra' )
150
- wait ( 100 )
151
- .then ( async () => {
152
- // Default wait mouse timeout is 320 ms, but maxClicks is 1, so we don't need to wait for timeout
153
- let loc = document.querySelector('#rspan') || false
154
- if ( loc ) await userEvent.click ( loc )
155
- expect ( a ).to.be.true
156
- resolve ( 'success' )
157
- })
158
-
159
-
160
- })
161
- return res
162
- }) // test single mouse click
163
-
164
-
165
-
166
- test ( 'Double mouse click', () => {
167
- let res = new Promise ( async (resolve) => {
168
- expect ( a ).to.be.false
169
- expect ( b ).to.be.false
170
-
171
- short.enablePlugin ( pluginClick )
172
- short.changeContext ( 'extra' )
173
- short.load ({ // load will restart the selected context
174
- 'extra' : { // load will overwrite existing 'extra' context definition
175
- 'click: left-2' : () => a = true
176
- }
177
- })
178
- wait ( 350 )
179
- .then ( async () => { // Default wait mouse timeout is 320 ms
180
- let loc = document.querySelector ( '#rspan' ) || false
181
- // Third click is ignored. Max clicks according definition is 2.
182
- if ( loc ) await userEvent.tripleClick ( loc )
183
- expect ( a ).to.be.true
184
- resolve ( 'success' )
185
- })
186
- })
187
- return res
188
- }) // test double mouse click
189
-
190
-
191
-
192
- test ( 'Dependencies on shortcuts', () => {
193
- const res = new Promise ( async (resolve) => {
194
- const task = askForPromise ();
195
- expect ( a ).to.be.false
196
- expect ( b ).to.be.false
197
85
 
198
- short.enablePlugin ( pluginClick )
199
- short.setDependencies ({ task })
200
-
201
- short.load ({
202
- 'extra' : { // load will overwrite existing 'extra' context definition
203
- 'click: left-1' : ({dependencies}) => {
204
- const { task } = dependencies;
205
- expect ( task ).to.have.property ( 'done' )
206
- expect ( task ).to.have.property ( 'promise' )
207
- a = true
208
- }
209
- }
210
- }) // load will restart the selected context
211
- short.changeContext ( 'extra' )
212
- wait ( 350 ) // Default wait mouse timeout is 320 ms
213
- .then ( async () => {
214
- let loc = document.querySelector ( '#rspan' ) || false
215
- if ( loc ) await userEvent.click ( loc )
216
- resolve ( 'success' )
217
- })
218
- }) // res promise
219
- return res
220
- }) // test dependencies on shortcuts
221
-
222
-
223
-
224
- test ( 'Emit custom event', () => {
225
- const res = new Promise ( async (resolve) => {
226
- let result = null;
227
- short.changeContext ()
228
- short.enablePlugin ( pluginClick )
229
- const myAllContext = {
230
- myAll: {
231
- 'click : leff-1' : () => console.log ( 'nothing' )
232
- , 'yo' : ({msg}) => result = msg
233
- }}
234
- short.load ( myAllContext )
235
- short.changeContext ( 'myAll' )
236
- short.emit ( 'yo', { context: short.getContext(), note: 'tt', type:'custom', msg:'hello' })
237
- expect ( result ).to.be.equal ( 'hello' )
238
- short.changeContext ( 'general' )
239
- short.unload ( 'myAll' )
240
- resolve ( 'success' )
241
- })
242
- return res
243
- }) // test emit custom event
244
-
245
-
246
-
247
- test ( 'List shortcuts', () => {
248
- short.enablePlugin ( pluginKey )
249
- let general = short.listShortcuts ('general');
250
- expect ( general ).to.be.an ( 'array' )
251
- expect ( general ).to.have.lengthOf ( 1 )
252
- expect ( general[0] ).to.be.equal ( 'KEY:A+SHIFT' )
86
+ afterEach ( async () => {
87
+ short.reset ()
88
+ a = false, b = false, c = null;
89
+ }) // afterEach
90
+
91
+
92
+
93
+ it ( 'Load and unload context', async () => {
94
+ let ls = short.listContexts ()
95
+ expect ( ls ).to.includes ( 'general' )
96
+
97
+ short.changeContext ( 'general' )
98
+ // Can't not to unload current active context - sends an error on @shortcuts-error
99
+ // @shortcuts-error is a system error event used accross the library
100
+ short.unload ( 'general' )
101
+ expect ( c ).to.be.equal ( `Context 'general' can't be removed during is current active context. Change the context first` )
102
+
103
+ // Try to change context to non-existent one
104
+ // Message goes to @shortcuts-error
105
+ short.changeContext ( 'hhm' )
106
+ expect ( c ).to.be.equal ( `Context 'hhm' does not exist` )
107
+
108
+ // Change to something that exists
109
+ short.changeContext ( 'extra' )
110
+ // Free to unload 'general'
111
+ short.unload ( 'general' )
112
+ ls = short.listContexts ()
113
+ // Unload success
114
+ expect ( ls ).to.not.includes ( 'general' )
115
+ }) // it load and unload context
116
+
117
+
118
+
119
+ it ( 'List enabled plugins. Disable and enable plugins', async () => {
120
+ expect ( short.listPlugins () ).to.be.deep.equal ( [] )
121
+ // Enable list of plugins
122
+ let myPlugins = [ pluginKey, pluginClick ]
123
+ myPlugins.forEach ( plugin => short.enablePlugin ( plugin ) )
124
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
125
+ // Method disablePlugin require plugin name (prefix)
126
+ short.disablePlugin ( 'click' )
127
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key' ] )
128
+ // Method enablePlugin require the plugin as a function
129
+ short.enablePlugin ( pluginClick )
130
+ expect ( short.listPlugins () ).to.be.deep.equal ( [ 'key', 'click' ] )
131
+ }) // it list enabled plugins
132
+
133
+
134
+
135
+ it ( 'Unload non existing context', () => {
136
+ let change = false;
137
+ let ls = short.listContexts ()
138
+ short.load ( {
139
+ local : {
140
+ 'click : leff-1' : () => console.log ( 'nothing' ),
141
+ '@shortcuts-error': () => change = true
142
+ }
143
+ })
144
+ short.changeContext ( 'local' )
145
+ short.unload ( 'unknown' )
146
+ expect ( change ).to.be.true
147
+ }) // it unload non existing context
148
+
253
149
 
254
- let fail = short.listShortcuts ( 'somethingNotExisting' );
255
- expect ( fail ).to.be.null
256
-
257
- let all = short.listShortcuts ();
258
- expect ( all ).to.be.an ( 'array' )
259
150
 
260
- expect ( all ).to.have.lengthOf ( 3 )
261
- expect ( all[0] ).to.have.property ( 'context' )
262
- expect ( all[0] ).to.have.property ( 'shortcuts' )
263
- expect ( all[0].shortcuts ).to.be.an('array')
264
- expect ( all[0].shortcuts ).to.have.lengthOf ( 1 )
265
- expect ( all[0].shortcuts[0] ).to.be.equal ( 'KEY:A+SHIFT' )
266
- expect ( all[0].context ).to.be.equal ( 'general' )
267
- }) // test list shortcuts
268
-
269
-
270
-
271
- test ( 'Click on anchor', () => {
272
- const res = new Promise ( async (resolve) => {
273
- // Click on anchor that don't have click-data attribute.
274
- let result = 'none';
275
- short.enablePlugin ( pluginClick )
276
- short.load ({ 'extra' : {
277
- 'click: 1 - left' : ({target, context, event }) => { // Order of button name and number of click is not important
278
- event.preventDefault ()
279
- expect ( context ).to.be.equal ( 'extra' )
280
- expect ( target.nodeName ).to.be.equal ( 'A' )
281
- result = target.nodeName
282
- }
283
- }
284
- })
285
- short.changeContext ( 'extra' )
286
- wait ( 10 )
287
- .then ( async () => {
288
- let loc = document.querySelector ( '#anchor' ) || false;
289
- if ( loc ) await userEvent.click ( loc )
290
- expect ( result ).to.be.equal ( 'A' )
291
- short.changeContext ( 'general' )
292
- resolve ( 'success' )
293
- })
294
- })
295
- return res
296
- }) // test click on anchor
151
+ it ( 'Emit custom event', () => {
152
+ // TODO: Check arguments for the custom event handlers
153
+ let result = null;
154
+ short.enablePlugin ( pluginClick )
155
+ const myAllContext = {
156
+ myAll: {
157
+ 'click : leff-1' : () => console.log ( 'nothing' )
158
+ , 'yo' : ({msg}) => result = msg
159
+ }}
160
+ short.load ( myAllContext )
161
+ short.changeContext ( 'myAll' )
162
+ short.emit ( 'yo', { context: short.getContext(), note: 'tt', type:'custom', msg:'hello' })
163
+ expect ( result ).to.be.equal ( 'hello' )
164
+ short.changeContext ( 'general' )
165
+ short.unload ( 'myAll' )
166
+ }) // it emit custom event
167
+
168
+
169
+
170
+ it ( 'List shortcuts', () => {
171
+ short.enablePlugin ( pluginKey )
172
+
173
+ let general = short.listShortcuts ('general');
174
+ expect ( general ).to.be.an ( 'array' )
175
+ expect ( general ).to.have.lengthOf ( 2 )
176
+ expect ( general ).to.include ( 'KEY:A+SHIFT' )
177
+
178
+ let fail = short.listShortcuts ( 'somethingNotExisting' );
179
+ expect ( fail ).to.be.null
180
+
181
+ let all = short.listShortcuts ();
182
+ expect ( all ).to.be.an ( 'array' )
183
+
184
+ expect ( all ).to.have.lengthOf ( 4 )
185
+ // Property 'context' is a context name - string
186
+ expect ( all[0] ).to.have.property ( 'context' )
187
+ expect ( all[0] ).to.have.property ( 'shortcuts' )
188
+ expect ( all[0].shortcuts ).to.be.an ( 'array' )
189
+ expect ( all[0].shortcuts ).to.have.lengthOf ( 2 )
190
+ expect ( all[0].shortcuts ).to.include ( 'KEY:A+SHIFT' )
191
+ expect ( all[0].context ).to.be.equal ( 'general' )
192
+ }) // it list shortcuts
193
+
194
+
195
+
196
+ it ( 'Reset', () => {
197
+ short.enablePlugin ( pluginKey )
198
+ short.reset ()
199
+ expect ( short.listShortcuts () ).to.have.lengthOf ( 0 )
200
+ expect ( short.listShortcuts () ).to.have.lengthOf ( 0 )
201
+ expect ( short.getContext () ).to.be.null
202
+ }) // it Reset
203
+
297
204
 
298
205
  }) // describe
299
206