@thednp/color-picker 1.0.0 → 2.0.0-alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. package/.eslintrc.cjs +199 -0
  2. package/.lgtm.yml +9 -0
  3. package/.prettierrc.json +15 -0
  4. package/.stylelintrc.json +236 -0
  5. package/LICENSE +0 -0
  6. package/README.md +63 -80
  7. package/compile.js +48 -0
  8. package/cypress/downloads/downloads.htm +0 -0
  9. package/cypress/e2e/color-palette.cy.ts +128 -0
  10. package/cypress/e2e/color-picker.cy.ts +920 -0
  11. package/cypress/fixtures/colorNamesFrench.js +3 -0
  12. package/cypress/fixtures/componentLabelsFrench.js +21 -0
  13. package/cypress/fixtures/format.js +3 -0
  14. package/cypress/fixtures/getCEMarkup.js +29 -0
  15. package/cypress/fixtures/getMarkup.js +28 -0
  16. package/cypress/fixtures/getRandomInt.js +6 -0
  17. package/cypress/fixtures/sampleWebcolors.js +18 -0
  18. package/cypress/fixtures/testSample.js +8 -0
  19. package/cypress/plugins/esbuild-istanbul.ts +50 -0
  20. package/cypress/plugins/tsCompile.ts +34 -0
  21. package/cypress/support/commands.ts +0 -0
  22. package/cypress/support/e2e.ts +21 -0
  23. package/cypress/test.html +23 -0
  24. package/cypress.config.ts +29 -0
  25. package/dist/css/color-picker.css +15 -39
  26. package/dist/css/color-picker.min.css +2 -2
  27. package/dist/css/color-picker.rtl.css +15 -39
  28. package/dist/css/color-picker.rtl.min.css +2 -2
  29. package/dist/js/color-picker.cjs +8 -0
  30. package/dist/js/color-picker.cjs.map +1 -0
  31. package/dist/js/color-picker.d.ts +278 -0
  32. package/dist/js/color-picker.js +5 -3583
  33. package/dist/js/color-picker.js.map +1 -0
  34. package/dist/js/color-picker.mjs +2631 -0
  35. package/dist/js/color-picker.mjs.map +1 -0
  36. package/dts.config.ts +15 -0
  37. package/package.json +64 -74
  38. package/src/scss/_variables.scss +0 -1
  39. package/src/scss/color-picker.rtl.scss +4 -0
  40. package/src/scss/color-picker.scss +75 -39
  41. package/src/ts/colorPalette.ts +89 -0
  42. package/src/{js/color-picker.js → ts/index.ts} +492 -502
  43. package/src/ts/interface/colorPickerLabels.ts +20 -0
  44. package/src/ts/interface/colorPickerOptions.ts +11 -0
  45. package/src/ts/interface/paletteOptions.ts +6 -0
  46. package/src/ts/util/colorNames.ts +21 -0
  47. package/src/{js/util/colorPickerLabels.js → ts/util/colorPickerLabels.ts} +4 -2
  48. package/src/ts/util/getColorControls.ts +90 -0
  49. package/src/{js/util/getColorForm.js → ts/util/getColorForm.ts} +28 -18
  50. package/src/{js/util/getColorMenu.js → ts/util/getColorMenu.ts} +21 -30
  51. package/src/ts/util/isValidJSON.ts +19 -0
  52. package/src/{js/util/setMarkup.js → ts/util/setMarkup.ts} +57 -48
  53. package/src/{js/util/vHidden.js → ts/util/vHidden.ts} +0 -0
  54. package/tsconfig.json +29 -0
  55. package/vite.config.ts +34 -0
  56. package/dist/js/color-esm.js +0 -1167
  57. package/dist/js/color-esm.min.js +0 -2
  58. package/dist/js/color-palette-esm.js +0 -1238
  59. package/dist/js/color-palette-esm.min.js +0 -2
  60. package/dist/js/color-palette.js +0 -1246
  61. package/dist/js/color-palette.min.js +0 -2
  62. package/dist/js/color-picker-element-esm.js +0 -3739
  63. package/dist/js/color-picker-element-esm.min.js +0 -2
  64. package/dist/js/color-picker-element.js +0 -3747
  65. package/dist/js/color-picker-element.min.js +0 -2
  66. package/dist/js/color-picker-esm.js +0 -3578
  67. package/dist/js/color-picker-esm.min.js +0 -2
  68. package/dist/js/color-picker.min.js +0 -2
  69. package/dist/js/color.js +0 -1175
  70. package/dist/js/color.min.js +0 -2
  71. package/src/js/color-palette.js +0 -75
  72. package/src/js/color-picker-element.js +0 -110
  73. package/src/js/color.js +0 -1107
  74. package/src/js/index.js +0 -3
  75. package/src/js/util/colorNames.js +0 -6
  76. package/src/js/util/getColorControls.js +0 -103
  77. package/src/js/util/isValidJSON.js +0 -13
  78. package/src/js/util/nonColors.js +0 -5
  79. package/src/js/util/roundPart.js +0 -9
  80. package/src/js/util/setCSSProperties.js +0 -12
  81. package/src/js/util/tabindex.js +0 -3
  82. package/src/js/util/toggleCEAttr.js +0 -70
  83. package/src/js/util/version.js +0 -6
  84. package/src/js/version.js +0 -6
  85. package/types/cp.d.ts +0 -544
  86. package/types/index.d.ts +0 -48
  87. package/types/source/source.ts +0 -5
  88. package/types/source/types.d.ts +0 -92
@@ -0,0 +1,920 @@
1
+ /// <reference types="cypress" />
2
+
3
+ import Color from '@thednp/color';
4
+ import ColorPalette from '../../src/ts/colorPalette';
5
+ import ColorPicker from '../../src/ts/index';
6
+ // import roundPart from '@thednp/color/src/util/roundPart';
7
+
8
+ import getRandomInt from '../fixtures/getRandomInt';
9
+ import FORMAT from '../fixtures/format';
10
+ import getMarkup from '../fixtures/getMarkup';
11
+ import componentLabelsFrench from '../fixtures/componentLabelsFrench';
12
+ import colorNamesFrench from '../fixtures/colorNamesFrench';
13
+
14
+ const colorNames = ['white', 'black', 'grey', 'red', 'orange', 'brown', 'gold', 'olive', 'yellow', 'lime', 'green', 'teal', 'cyan', 'blue', 'violet', 'magenta', 'pink'];
15
+ const colorNameValues = ['#fff', '#000', '#808080', '#f00', '#ffa500', '#653c24', '#c8af00', '#808000', '#ff0', '#0f0', '#080', '#075', '#0ff', '#05f', '#a7f', '#b0f', '#f0d'];
16
+ const colorPresets = '#470000,#750000,#a30000,#d10000,#ff0000,#ff2e2e,#ff5c5c,#ff8a8a,#ffb8b8,#ffe6e6,#004700,#007500,#00a300,#00d100,#00ff00,#2eff2e,#5cff5c,#8aff8a,#b8ffb8,#e6ffe6,#000047,#000075,#0000a3,#0000d1,#0000ff,#2e2eff,#5c5cff,#8a8aff,#b8b8ff,#e6e6ff';
17
+ const { roundPart } = Color;
18
+
19
+ describe('ColorPicker Class Test', () => {
20
+
21
+ beforeEach(() => {
22
+ cy.visit('cypress/test.html')
23
+ .wait(200)
24
+ });
25
+
26
+ it('initialize with no parameter throws error', () => {
27
+ // const args = [];
28
+ try {
29
+ // @ts-ignore
30
+ new ColorPicker();
31
+ } catch (error) {
32
+ expect(error).to.be.instanceOf(TypeError);
33
+ expect(error).to.have.property('message', `ColorPicker target not specified.`);
34
+ }
35
+ });
36
+
37
+ it('inject incomplete markup and initialize, throws error', () => {
38
+ cy.document().its('body').then((body) => {
39
+ const cp = document.createElement('div');
40
+ const input = document.createElement('input');
41
+ cp.append(input)
42
+ body.append(cp);
43
+
44
+ cy.get('input').then((input) => {
45
+ if (input.length) {
46
+ try {
47
+ new ColorPicker(input[0]);
48
+ } catch (error) {
49
+ expect(error).to.be.instanceOf(TypeError);
50
+ expect(error).to.have.property('message', 'ColorPicker requires a specific markup to work.');
51
+ }
52
+ }
53
+ })
54
+ })
55
+ });
56
+
57
+ it('inject markup and initialize with all DATA API', () => {
58
+ const id = getRandomInt(0,999);
59
+ const format = FORMAT[getRandomInt(0,3)];
60
+ const { init, selector } = ColorPicker;
61
+ let value;
62
+
63
+ cy.document().its('body').then((body) => {
64
+ ({value} = getMarkup(body, id, format));
65
+ });
66
+
67
+ // cy.get(`#color-picker-${id}`).then(($input) => {
68
+ cy.get('input').then(($input) => {
69
+ cy.wrap($input[0]).as('input');
70
+ });
71
+
72
+ cy.get(`@input`).then(($input) => {
73
+ const input = $input[0];
74
+ input.setAttribute('value', "currentColor");
75
+ input.setAttribute('data-function', "color-picker");
76
+ input.setAttribute('data-color-presets', '{"hue": 120, "hueSteps": 6, "lightSteps": 10}');
77
+ input.setAttribute('data-color-keywords', 'magenta, olive, yellow, red:default, currentColor:textColor');
78
+ input.setAttribute('data-color-labels', colorNamesFrench);
79
+ input.setAttribute('data-component-labels', '{"pickerLabel": "Couleur Sélection", "appearanceLabel": "Apparence de la couleur", "valueLabel": "Valeur de couleur", "toggleLabel": "Sélectionner la couleur", "presetsLabel": "Préréglages de couleur", "defaultsLabel": "Couleur par défaut", "formatLabel": "Format", "alphaLabel": "Alpha", "hexLabel": "Hexadécimal", "hueLabel": "Nuance", "whitenessLabel": "Blancheur", "blacknessLabel": "Noirceur", "saturationLabel": "Saturation", "lightnessLabel": "Légèreté", "redLabel": "Rouge", "greenLabel": "Vert", "blueLabel": "Bleu"}');
80
+ });
81
+
82
+ cy.document().should('not.be.undefined')
83
+ .get('@input').should('exist')
84
+ cy.document().invoke('querySelectorAll', selector)
85
+ .then((nodelist ) => {
86
+ [...nodelist as NodeListOf<HTMLInputElement>].forEach(init);
87
+ })
88
+
89
+ cy.get('@input').then(($input) => {
90
+ if ($input.length) {
91
+ const cp = ColorPicker.getInstance($input[0] as HTMLInputElement);
92
+ cy.wrap(cp).as('cp');
93
+ }
94
+ })
95
+
96
+ cy.get('@cp').should('exist')
97
+ .get('@cp').then(() => {
98
+ cy.get('@cp').its('colorLabels').then(({white,black,grey,red,orange,brown,gold,olive,yellow,lime,green,teal,cyan,blue,violet,magenta,pink}) => {
99
+ expect([white,black,grey,red,orange,brown,gold,olive,yellow,lime,green,teal,cyan,blue,violet,magenta,pink])
100
+ .to.deep.equal(colorNamesFrench.split(','));
101
+ })
102
+
103
+ // .get('@input').should('be.instanceOf', HTMLInputElement)
104
+ .get('@cp').its('format').should('equal', format)
105
+ .get('@cp').its('value').should('not.equal', value) // not `value`
106
+ .get('@cp').its('value').should('equal', new Color('#fff', format).toString(true)) // <- currentColor
107
+ .get('@cp').its('colorKeywords').should('deep.equal', ['magenta', 'olive', 'yellow', 'red:default', 'currentColor:textColor'])
108
+ .get('@cp').its('colorPresets').should('be.instanceOf', ColorPalette)
109
+ .get('@cp').its('colorPresets').its('colors').each((c) => {
110
+ expect(c).to.be.instanceOf(Color);
111
+ })
112
+ .get('@cp').its('colorPicker').should('be.instanceOf', HTMLDivElement)
113
+ .get('@cp').its('colorMenu').should('be.instanceOf', HTMLDivElement);
114
+ });
115
+ });
116
+
117
+ it('inject markup and initialize via JavaScript API', () => {
118
+ cy.get('body').then((body) => {
119
+ const id = getRandomInt(0,999);
120
+ const format = FORMAT[getRandomInt(0,3)];
121
+ const {value} = getMarkup(body, id, format);
122
+
123
+ // cy.get(`#color-picker-${id}`).then((input) => {
124
+ cy.get('input').then((input) => {
125
+
126
+ if (input.length) {
127
+ const cp = new ColorPicker(input[0], {
128
+ colorKeywords: ['orange', 'lime', 'darkred'],
129
+ colorPresets: '#330033, #990099, #ff00ff, #ff66ff, #ffccff',
130
+ colorLabels: colorNamesFrench,
131
+ componentLabels: componentLabelsFrench,
132
+ });
133
+ console.log(cp)
134
+ cy.wrap(cp).as('cp');
135
+ cy.get('@cp').then(() => {
136
+ cy.get('@cp').its('input').should('equal', input[0]);
137
+ cy.get('@cp').its('format').should('equal', format);
138
+ cy.get('@cp').its('value').should('equal', value);
139
+ cy.get('@cp').its('colorPresets').should('be.instanceOf', Array);
140
+ cy.get('@cp').its('colorLabels')
141
+ .then(({white,black,grey,red,orange,brown,gold,olive,yellow,lime,green,teal,cyan,blue,violet,magenta,pink}) => {
142
+ expect([white,black,grey,red,orange,brown,gold,olive,yellow,lime,green,teal,cyan,blue,violet,magenta,pink]).to.deep.equal(colorNamesFrench.split(','));
143
+ });
144
+ cy.get('@cp').its('colorKeywords').should('deep.equal', ['orange', 'lime', 'darkred']);
145
+ cy.get('@cp').its('colorPicker').should('be.instanceOf', HTMLDivElement);
146
+ cy.get('@cp').its('colorMenu').should('be.instanceOf', HTMLDivElement);
147
+ });
148
+ }
149
+
150
+ });
151
+ });
152
+ });
153
+
154
+ it('inject markup and initialize with `colorKeywords`', () => {
155
+ cy.get('body').then((body) => {
156
+ const id = getRandomInt(0,999);
157
+ const format = FORMAT[getRandomInt(0,3)];
158
+ getMarkup(body, id, format);
159
+ cy.get('input').then((input) => {
160
+ if (input.length) {
161
+ cy.wrap(new ColorPicker(input[0], {
162
+ colorKeywords: 'orange, lime, darkred',
163
+ })).as('cp');
164
+ cy.get('@cp').then(() => {
165
+ cy.get('@cp').its('colorKeywords').should('be.instanceOf', Array);
166
+ });
167
+ }
168
+ });
169
+ });
170
+ });
171
+
172
+ it('inject markup and initialize with `colorPresets`', () => {
173
+ cy.get('body').then((body) => {
174
+ const id = getRandomInt(0,999);
175
+ const format = FORMAT[getRandomInt(0,3)];
176
+ getMarkup(body, id, format);
177
+
178
+ cy.get('input').then((input) => {
179
+ if (input.length) {
180
+ cy.wrap(new ColorPicker(input[0], {
181
+ colorPresets: '#330033, #990099, #ff00ff, #ff66ff, #ffccff'.split(','),
182
+ })).as('cp');
183
+ // cy.get('@cp')
184
+ // .then(() => {
185
+ cy.get('@cp').its('colorPresets').should('be.instanceOf', Array);
186
+ // });
187
+ }
188
+ });
189
+ });
190
+ });
191
+
192
+ it('Test `pointer` event listeners', function() {
193
+ cy.document().its('body').then((body) => {
194
+ const id = getRandomInt(0,999);
195
+ const format = FORMAT[getRandomInt(0,3)];
196
+ getMarkup(body, id, format);
197
+
198
+ cy.get(`input`).then((input) => {
199
+ if (input.length) {
200
+ cy.wrap(new ColorPicker(input[0])).as('cp');
201
+
202
+ cy.log('Testing touch event listeners on `visuals`');
203
+ cy.wrap(input[0]).focus().clear().type('hsl 0 100 50{enter}')
204
+ cy.wait(50)
205
+ cy.get('@cp').its('rgb').then((rgb) => {
206
+ cy.get('.visual-control').eq(0).trigger('pointerdown', 0, 0, { force: true, pointerType: 'touch' })
207
+ cy.get('.visual-control').eq(0).trigger('pointermove', -5, -5, { force: true, pointerType: 'touch' })
208
+ cy.get('.visual-control').eq(0).trigger('pointermove', 500, 500, { force: true, pointerType: 'touch' })
209
+ cy.get('.visual-control').eq(0).trigger('pointermove', 100, 100, { force: true, pointerType: 'touch' })
210
+ cy.get('.visual-control').eq(0).trigger('pointerup', 100, 100, { force: true, pointerType: 'touch' }).then(() => {
211
+ cy.wait(17)
212
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
213
+ })
214
+ });
215
+
216
+ cy.wrap(input[0]).focus().clear().type('hsl 0 100 50{enter}')
217
+ .get('@cp').its('rgb').then((rgb) => {
218
+ cy.get('.visual-control').eq(1)
219
+ .trigger('pointerdown', 3, 0, { force: true, pointerType: 'touch' })
220
+ .trigger('pointermove', 0, -5, { force: true, pointerType: 'touch' })
221
+ .trigger('pointermove', 3, 500, { force: true, pointerType: 'touch' })
222
+ .trigger('pointermove', 3, 100, { force: true, pointerType: 'touch' })
223
+ .trigger('pointerup', 3, 100, { force: true, pointerType: 'touch' })
224
+ .then(() => {
225
+ cy.wait(17)
226
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
227
+ })
228
+ });
229
+
230
+ cy.wrap(input[0]).focus().clear().type('hsl 0 100 50{enter}')
231
+ .get('@cp').its('rgb').then((rgb) => {
232
+ cy.get('.visual-control').eq(2)
233
+ .trigger('pointerdown', 3, 0, { force: true, pointerType: 'touch' })
234
+ .trigger('pointermove', 3, -5, { force: true, pointerType: 'touch' })
235
+ .trigger('pointermove', 3, 200, { force: true, pointerType: 'touch' })
236
+ .trigger('pointermove', 3, 500, { force: true, pointerType: 'touch' })
237
+ .trigger('pointermove', 3, 100, { force: true, pointerType: 'touch' })
238
+ .trigger('pointerup', 3, 100, { force: true, pointerType: 'touch' })
239
+ .then(() => {
240
+ cy.wait(17)
241
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
242
+ })
243
+ });
244
+ }
245
+ });
246
+ });
247
+
248
+ });
249
+
250
+ it('Test showing & hiding `colorPicker` / `presetsMenu`', function() {
251
+ cy.document().its('body').then((body) => {
252
+ const id = getRandomInt(0,999);
253
+ const format = FORMAT[getRandomInt(0,3)];
254
+ getMarkup(body, id, format);
255
+
256
+ const a = document.createElement('button');
257
+ // a.setAttribute('href', '#');
258
+ a.innerText = 'Some link';
259
+ body.append(a);
260
+ cy.wrap(a).as('a');
261
+ // cy.get(`#color-picker-${id}`).then((input) => {
262
+ cy.get('input').then((input) => {
263
+ cy.wrap(input[0]).as('input');
264
+ });
265
+ });
266
+
267
+ cy.wait(200)
268
+ cy.get('@input').then(($input) => {
269
+ let cp;
270
+ if ($input.length) {
271
+ cp = new ColorPicker($input[0] as HTMLInputElement, {
272
+ colorKeywords: 'olive,green,red,:empty,transparent',
273
+ colorPresets: colorPresets,
274
+ colorLabels: colorNamesFrench,
275
+ });
276
+ }
277
+ cy.wrap(cp).as('cp');
278
+ })
279
+ cy.wait(200);
280
+
281
+ cy.log('Testing `click` on `pickerToggle`');
282
+ cy.get('.picker-toggle').click()
283
+ cy.get('.color-dropdown.picker').should('have.class', 'show')
284
+ cy.get('@cp').invoke('hide');
285
+
286
+ cy.wait(17);
287
+
288
+ cy.log('Testing `click` on `menuToggle`');
289
+ cy.get('.menu-toggle').click()
290
+ cy.get('.color-dropdown.menu').should('have.class', 'show')
291
+ cy.wait(17)
292
+ cy.get('.menu-toggle').click()
293
+ cy.get('.color-dropdown.menu').should('not.have.class', 'show');
294
+
295
+ cy.wait(17);
296
+
297
+ cy.log('Testing `keydown` on `pickerToggle`');
298
+ // trigger('keydown') won't work with <div> elements
299
+ cy.get('.picker-toggle').focus().type('{enter}')
300
+ cy.wait(17)
301
+ cy.get('.color-dropdown.picker').should('have.class', 'show')
302
+ cy.wait(17)
303
+ cy.get('.picker-toggle').type('{enter}')
304
+ cy.wait(17)
305
+ cy.get('.color-dropdown.picker').should('not.have.class', 'show');
306
+
307
+ cy.wait(17);
308
+
309
+ cy.log('Testing `keydown` on `menuToggle`');
310
+ // `menuToggle` is not focusable until open
311
+ cy.get('@cp').invoke('showPicker')
312
+ cy.wait(17)
313
+ // trigger('keydown') won't work with <div> elements
314
+ cy.get('.menu-toggle').focus().type('{enter}')
315
+ cy.wait(17)
316
+ cy.get('.color-dropdown.menu').should('have.class', 'show')
317
+ cy.wait(17)
318
+ cy.get('.menu-toggle').type('{enter}')
319
+ cy.wait(17)
320
+ cy.get('.color-dropdown.menu').should('not.have.class', 'show');
321
+
322
+ cy.wait(17);
323
+
324
+ cy.log('Testing `focusin` on `input`');
325
+ cy.get('@input').focus().then(() => {
326
+ cy.get('.color-dropdown.picker').should('have.class', 'show');
327
+ });
328
+
329
+ cy.wait(17);
330
+
331
+ cy.log('Testing `focusout` on `input`');
332
+ cy.get('@a').focus()
333
+ cy.get('@a').blur()
334
+
335
+ // cy.get('@a').focus();
336
+ cy.wait(17)
337
+ // cy.get('@a').trigger('focusin', {force: true})
338
+ // cy.get('@a').trigger('focusout', {force: true})
339
+ // .then(() => {
340
+ cy.get('.color-dropdown.picker').should('not.have.class', 'show')
341
+ // });
342
+
343
+ cy.wait(17);
344
+
345
+ cy.log('Testing `pointerup` on body while `colorPicker` is open');
346
+ cy.get('@cp').invoke('togglePicker')
347
+ cy.wait(17)
348
+ cy.get('body').click('topRight').then(() => {
349
+ cy.wait(17);
350
+ cy.get('.color-dropdown.picker').should('not.have.class', 'show');
351
+ });
352
+
353
+ cy.wait(17);
354
+ cy.log('Type in `transparent` and press `enter`');
355
+ cy.get('@input').focus().clear().type('transparent{enter}')
356
+ cy.get('@cp').its('rgb').then(({r,g,b,a}) => {
357
+ expect([...[r,g,b].map(roundPart), a]).to.deep.equal([0,0,0,0]);
358
+ });
359
+
360
+ cy.log('type in `currentColor` and press `enter`');
361
+ cy.get('@input').focus().clear().type('currentColor{enter}')
362
+ cy.get('@cp').its('rgb').then(({r,g,b,a}) => {
363
+ expect([...[r,g,b].map(roundPart), a]).to.deep.equal([0,0,0,1]);
364
+ });
365
+
366
+ cy.log('Type in an invalid value and press `enter`, should reset value')
367
+ cy.wait(17)
368
+ cy.get('@cp').invoke('showPicker')
369
+ cy.get('@input').focus().clear().type('wombat{enter}').then(() => {
370
+ cy.get('@cp').its('value').should('not.equal', 'wombat');
371
+ });
372
+
373
+ cy.log('Type in a valid value and call `hide`, should keep previous value');
374
+ cy.get('@cp').invoke('showPicker')
375
+ cy.get('@input').focus().clear().type('hsl 0 0 100')
376
+ cy.wait(17)
377
+ cy.get('@cp').invoke('hide')
378
+ cy.wait(17)
379
+ cy.get('@cp').its('rgb').should(({ r,g,b }) => {
380
+ expect([r,g,b].map(roundPart)).to.not.deep.equal([255,255,255]);
381
+ })
382
+ cy.get('@cp').its('value').should('not.equal', 'hsl 0 0 100');
383
+ });
384
+
385
+
386
+ it('Test `keyboard` event listeners', function() {
387
+ let value: unknown;
388
+ cy.document().its('body').then((body) => {
389
+ const id = getRandomInt(0,999);
390
+ const format = FORMAT[getRandomInt(0,3)];
391
+
392
+ ({value} = getMarkup(body, id, format));
393
+
394
+ cy.get(`#color-picker-${id}`).then((input) => {
395
+ // cy.get('input').then((input) => {
396
+ cy.wrap(input[0]).as('input');
397
+ });
398
+ });
399
+
400
+ cy.get('@input').then(($input) => {
401
+ let cp;
402
+ if ($input.length) {
403
+ cp = new ColorPicker($input[0] as HTMLInputElement, {
404
+ colorKeywords: 'olive,green,red,:empty,transparent',
405
+ colorPresets: colorPresets,
406
+ colorLabels: colorNamesFrench,
407
+ });
408
+ }
409
+ cy.wrap(cp).as('cp');
410
+ });
411
+
412
+ cy.log('Type in a valid value and press `Escape`, should keep previous value');
413
+ cy.get('@cp').invoke('showPicker')
414
+ .get('@input').focus().clear().type('hsl 0 0 100')
415
+ .wait(17)
416
+ .get('@input').trigger('keyup', { code: 'Escape' })
417
+ .wait(17)
418
+ .get('@cp').its('rgb').should(({ r,g,b }) => {
419
+ expect([r,g,b].map(roundPart)).to.not.deep.equal([255,255,255]);
420
+ })
421
+ .get('@cp').its('value').should('not.equal', 'hsl 0 0 100');
422
+
423
+ cy.wait(17);
424
+
425
+ cy.log('Testing `keyboard` events on `colorKeywords`');
426
+ cy.get('@cp').invoke('toggleMenu');
427
+ cy.wait(17);
428
+ cy.get('.color-defaults li').eq(0).focus().trigger('keydown', { code: 'ArrowRight' }).then(() => {
429
+ cy.get('.color-defaults li').eq(1).should('be.focused');
430
+ });
431
+
432
+ cy.get('.color-defaults li').eq(1).trigger('keydown', { code: 'ArrowLeft' }).then(() => {
433
+ cy.get('.color-defaults li').eq(0).should('be.focused');
434
+ });;
435
+ cy.get('.color-defaults li').eq(0).focus().trigger('keydown', { code: 'ArrowDown' }).then(() => {
436
+ cy.get('.color-defaults li').eq(1).should('be.focused');
437
+ });
438
+ cy.get('.color-defaults li').eq(1).trigger('keydown', { code: 'ArrowUp' }).then(() => {
439
+ cy.get('.color-defaults li').eq(0).should('be.focused');
440
+ });
441
+ cy.log('set a color keyword as active');
442
+ cy.get('@cp').its('value').then((value) => {
443
+ cy.get('.color-defaults li').eq(0).trigger('keydown', { code: 'Enter' }).then(() => {
444
+ cy.get('.color-defaults li').eq(0).should('have.class', 'active');
445
+ cy.get('@cp').its('value').should('not.equal', value);
446
+ });
447
+ });
448
+ cy.contains('transparent').click().then(() => {
449
+ cy.get('.color-defaults li').eq(0).should('not.have.class', 'active');
450
+ cy.contains('transparent').should('have.class', 'active');
451
+ cy.get('@cp').its('value').should('equal', 'transparent');
452
+ });
453
+ cy.contains('empty').click().then(() => {
454
+ cy.contains('transparent').should('have.class', 'active');
455
+ cy.get('@cp').its('value').should('not.equal', 'empty');
456
+ });
457
+
458
+ cy.log('Testing `keyboard` events on `colorOptions`');
459
+ cy.get('.color-options li').eq(0).focus().trigger('keydown', { code: 'ArrowRight' }).then(() => {
460
+ cy.get('.color-options li').eq(1).should('be.focused');
461
+ });
462
+
463
+ cy.get('.color-options li').eq(1).trigger('keydown', { code: 'ArrowLeft' }).then(() => {
464
+ cy.get('.color-options li').eq(0).should('be.focused');
465
+ });;
466
+ cy.get('.color-options li').eq(0).focus().trigger('keydown', { code: 'ArrowDown' }).then(() => {
467
+ cy.get('.color-options li').eq(10).should('be.focused');
468
+ });
469
+ cy.get('.color-options li').eq(10).trigger('keydown', { code: 'ArrowUp' }).then(() => {
470
+ cy.get('.color-options li').eq(0).should('be.focused');
471
+ });;
472
+
473
+ cy.log('Test setting a color option as active');
474
+ cy.get('.color-options li').eq(0).trigger('keydown', { code: 'Enter' }).then(() => {
475
+ cy.get('.color-options li').eq(0).should('have.class', 'active');
476
+ cy.get('@cp').its('value').should('not.equal', value);
477
+ });
478
+ cy.get('.color-options li').eq(1).click().then(() => {
479
+ cy.get('.color-options li').eq(0).should('not.have.class', 'active');
480
+ cy.get('.color-options li').eq(1).should('have.class', 'active');
481
+ cy.get('@cp').its('value').should('not.equal', value);
482
+ });
483
+
484
+ cy.log('Test removing active class from color option');
485
+ cy.get('@cp').invoke('togglePicker');
486
+ cy.wait(17);
487
+ cy.get('.visual-control').eq(0).click().then(() => {
488
+ cy.get('.color-options li').eq(1).should('not.have.class', 'active');
489
+ });
490
+
491
+
492
+ cy.log('Test dismiss on `Escape`');
493
+ cy.get('@input').focus().then(() => {
494
+ cy.wait(17);
495
+ cy.get('@cp').its('colorPicker').should('have.class', 'show');
496
+ })
497
+ .wait(17)
498
+ .document().trigger('keyup', { code: 'Escape' })
499
+ .wait(17)
500
+ .get('@cp').its('colorPicker').should('not.have.class', 'show');
501
+ });
502
+
503
+
504
+ it('Test `scroll` event listeners', function() {
505
+ const id = getRandomInt(0,999);
506
+ const format = FORMAT[getRandomInt(0,3)];
507
+
508
+ cy.get('body').should('exist').then((body) => {
509
+ // const win = body[0].ownerDocument.defaultView;
510
+ cy.log('Test repositioning dropdown on scroll');
511
+ if (body) {
512
+ getMarkup(body, id, format);
513
+ }
514
+ })
515
+ cy.get(`input`).should('exist').then((input) => {
516
+ if (!input.length || !input[0]) return;
517
+ const cp = new ColorPicker(input[0] as HTMLInputElement, {
518
+ colorKeywords: 'olive,green,red,:empty,transparent',
519
+ colorPresets: colorPresets,
520
+ colorLabels: colorNamesFrench,
521
+ });
522
+
523
+ cp.togglePicker();
524
+
525
+ cy.scrollTo('top')
526
+ .wait(100)
527
+ .then(() => {
528
+ cy.log(`html.scrollY is expected to be 0`)
529
+ expect(input[0].ownerDocument.defaultView?.scrollY).to.equal(0)
530
+
531
+ expect(cp.colorPicker).to.not.have.class('bottom');
532
+ expect(cp.colorPicker).to.have.class('top');
533
+ })
534
+
535
+ // TO DO - validate a switch
536
+ // cy.scrollTo('bottom')
537
+ // .wait(100)
538
+ // .then((win) => {
539
+ // cy.log(`html.scrollY is expected to not be 0`)
540
+ // // cy.wait(200);
541
+ // expect(win.scrollY).to.not.equal(0)
542
+
543
+ // expect(cp.colorPicker).to.have.class('bottom');
544
+ // expect(cp.colorPicker).to.not.have.class('top');
545
+ // })
546
+ })
547
+ });
548
+
549
+
550
+ it('Test controls mouse & keyboard events', function() {
551
+ cy.get('body').then((body) => {
552
+ const id = getRandomInt(0,999);
553
+ const format = FORMAT[getRandomInt(0,3)];
554
+
555
+ getMarkup(body, id, format);
556
+
557
+ // cy.get(`#color-picker-${id}`).then((input) => {
558
+ cy.get('input').then((input) => {
559
+ let cp;
560
+ if (input.length) {
561
+ cp = new ColorPicker(input[0], {
562
+ colorKeywords: 'green,green,red,transparent',
563
+ colorPresets: colorPresets,
564
+ });
565
+ }
566
+ cy.wrap(input[0]).as('input');
567
+ cy.wrap(cp).as('cp');
568
+ });
569
+
570
+ cy.log('Testing `click` on `visuals`');
571
+ cy.get('@input').focus().clear().type('hsl 0 100 50{enter}').then(() => {
572
+ cy.get('.visual-control').eq(0).click()
573
+ .wait(17)
574
+ .get('@cp').its('rgb').should(({ r,g,b }) => {
575
+ expect([r,g,b].map(roundPart)).to.not.deep.equal([255, 0, 0]);
576
+ });
577
+ });
578
+
579
+ cy.get('@input').focus().clear().type('hsl 300 100 50{enter}').then(() => {
580
+ // cy.get('@cp').its('rgb').then(rgb => console.log(rgb))
581
+
582
+ cy.get('.visual-control').eq(1).click(0, -0.6, { force: true })
583
+ .wait(17)
584
+ .get('@cp').its('rgb').should(({ r,g,b }) => {
585
+ expect([r,g,b].map(roundPart)).to.not.deep.equal([255,0,255]);
586
+ });
587
+ })
588
+
589
+ cy.get('@input').focus().clear().type('hsl 120 100 50{enter}').then(() => {
590
+ cy.get('.visual-control').eq(2).click({ force: true })
591
+ .wait(17)
592
+ .get('@cp').its('rgb').should(({ r,g,b,a }) => {
593
+ expect([...[r,g,b].map(roundPart), a]).to.deep.equal([0,255,0, 0.5]);
594
+ });
595
+ });
596
+
597
+ cy.log('Testing mouse event listeners on `controlKnobs`');
598
+ cy.get('@input').focus().clear().type('hsl 0 100 50{enter}')
599
+ cy.get('@cp').its('rgb').then((rgb) => {
600
+ cy.get('.visual-control').eq(0).then((visual) => {
601
+ const { width, height} = visual[0].getBoundingClientRect();
602
+
603
+ cy.get('.knob').eq(0)
604
+ .trigger('pointerdown', { force: true })
605
+ .trigger('pointermove', -width, -height, { force: true })
606
+ .trigger('pointermove', -width - 100, -height - 100, { force: true })
607
+ .trigger('pointermove', 300, 300, { force: true })
608
+ .trigger('pointermove', -width + 50, -height + 50, { force: true })
609
+ .wait(17)
610
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
611
+ });
612
+ });
613
+
614
+ cy.get('@input').focus().clear().type('hsl 0 100 50{enter}')
615
+ .get('@cp').its('rgb').then((rgb) => {
616
+ cy.get('.visual-control').eq(1).then((visual) => {
617
+ const { width, height } = visual[0].getBoundingClientRect();
618
+
619
+ cy.get('.knob').eq(1)
620
+ .trigger('pointerdown', { force: true })
621
+ .trigger('pointermove', width / 2, -height - 100, { force: true })
622
+ .trigger('pointermove', width / 2, 300, { force: true })
623
+ .trigger('pointermove', width / 2, -height + 20, { force: true })
624
+ .wait(17)
625
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
626
+ });
627
+ });
628
+
629
+ cy.get('@input').focus().clear().type('hsl 0 100 50{enter}')
630
+ .get('@cp').its('rgb').then((rgb) => {
631
+ cy.get('.visual-control').eq(2).then((visual) => {
632
+ const { width, height } = visual[0].getBoundingClientRect();
633
+
634
+ cy.get('.knob').eq(2)
635
+ .trigger('pointerdown', { force: true })
636
+ .trigger('pointermove', width / 2, -height - 100, { force: true })
637
+ .trigger('pointermove', width / 2, 300, { force: true })
638
+ .trigger('pointermove', width / 2, -height + 20, { force: true })
639
+ .wait(17)
640
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
641
+ });
642
+ });
643
+
644
+
645
+ cy.log('Testing keyboard event listeners on `controlKnobs`');
646
+ cy.get('@input').focus().clear().type('hsl 300 100 50{enter}').then(() => {
647
+ const rgb = cy.get('@cp').its('rgb');
648
+ cy.get('.knob').eq(0).focus()
649
+ .trigger('keydown', { code: 'ArrowDown' })
650
+ .trigger('keydown', { code: 'ArrowDown' })
651
+ .trigger('keydown', { code: 'a' }) // edge case
652
+ .trigger('keydown', { code: 'ArrowUp' })
653
+ .trigger('keydown', { code: 'ArrowRight' })
654
+ .trigger('keydown', { code: 'ArrowRight' })
655
+ .trigger('keydown', { code: 'ArrowLeft' })
656
+ .wait(17)
657
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
658
+ });
659
+
660
+ cy.get('@input').focus().clear().type('hsl 180 100 50{enter}').then(() => {
661
+ const rgb = cy.get('@cp').its('rgb');
662
+ cy.get('.knob').eq(1).focus()
663
+ .trigger('keydown', { code: 'ArrowDown' })
664
+ .trigger('keydown', { code: 'ArrowDown' })
665
+ .trigger('keydown', { code: 'b' }) // edge case
666
+ .trigger('keydown', { code: 'ArrowUp' })
667
+ .trigger('keydown', { code: 'ArrowLeft' })
668
+ .trigger('keydown', { code: 'ArrowLeft' })
669
+ .trigger('keydown', { code: 'ArrowRight' })
670
+ .wait(17)
671
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
672
+ });
673
+
674
+ cy.get('@input').focus().clear().type('hsl 0 100 50{enter}').then(() => {
675
+ const rgb = cy.get('@cp').its('rgb');
676
+ cy.get('.knob').eq(2).focus()
677
+ .trigger('keydown', { code: 'ArrowDown' })
678
+ .trigger('keydown', { code: 'ArrowDown' })
679
+ .trigger('keydown', { code: 'C' }) // edge case
680
+ .trigger('keydown', { code: 'ArrowUp' })
681
+ .trigger('keydown', { code: 'ArrowLeft' })
682
+ .trigger('keydown', { code: 'ArrowLeft' })
683
+ .trigger('keydown', { code: 'ArrowRight' })
684
+ .wait(17)
685
+ .get('@cp').its('rgb').should('not.deep.equal', rgb);
686
+ });
687
+ });
688
+ });
689
+
690
+
691
+ it('Test private methods', function() {
692
+ cy.get('body').then((body) => {
693
+ const id = getRandomInt(0,999);
694
+ const format = FORMAT[getRandomInt(0,3)];
695
+
696
+ getMarkup(body, id, format);
697
+
698
+ // cy.get(`#color-picker-${id}`).then((input) => {
699
+ cy.get('input').then((input) => {
700
+ let cp;
701
+ if (input.length) {
702
+ cp = new ColorPicker(input[0], {
703
+ colorKeywords: 'green,green,red,transparent',
704
+ colorPresets: colorPresets,
705
+ });
706
+ }
707
+ cy.wrap(cp).as('cp');
708
+ });
709
+
710
+ cy.log('Testing `togglePicker`');
711
+ cy.get('@cp').invoke('togglePicker')
712
+ // .then(() => {
713
+ cy.get('.color-dropdown')
714
+ .should('have.class', 'picker')
715
+ .and('have.class', 'show');
716
+ // });
717
+ // cy.wait(17);
718
+ cy.get('@cp').invoke('togglePicker')
719
+ // .then(() => {
720
+ cy.get('.color-dropdown')
721
+ .should('have.class', 'picker')
722
+ .and('not.have.class', 'show');
723
+ // });
724
+
725
+ // cy.wait(17);
726
+
727
+ cy.log('Testing `toggleMenu`');
728
+ cy.get('@cp').invoke('toggleMenu')
729
+ // .then(() => {
730
+ cy.get('.color-dropdown')
731
+ .should('have.class', 'menu')
732
+ .and('have.class', 'show');
733
+ // });
734
+ // cy.wait(17);
735
+ cy.get('@cp').invoke('toggleMenu')
736
+ // .then(() => {
737
+ cy.get('.color-dropdown')
738
+ .should('have.class', 'menu')
739
+ .and('not.have.class', 'show');
740
+ // });
741
+
742
+ // cy.wait(17);
743
+
744
+ cy.log('Testing `showPicker`');
745
+ cy.get('@cp').invoke('showPicker')
746
+ // .then(() => {
747
+ cy.get('.color-dropdown')
748
+ .should('have.class', 'picker')
749
+ .and('have.class', 'show');
750
+ // });
751
+
752
+ // cy.wait(17);
753
+
754
+ cy.log('Testing `hide`');
755
+ cy.get('@cp').invoke('hide')
756
+ // .then(() => {
757
+ cy.get('.color-dropdown')
758
+ .and('not.have.class', 'show');
759
+ // });
760
+
761
+ // cy.wait(17);
762
+
763
+ cy.log('Testing `dispose`');
764
+ cy.get('@cp').invoke('dispose')
765
+ // .then(() => {
766
+ cy.get('.color-dropdown')
767
+ .should((dropdown) => {
768
+ expect(dropdown).to.not.exist;
769
+ });
770
+ // });
771
+ });
772
+ });
773
+
774
+ it('Test color appearance and color luminance', () => {
775
+ const frenchColors = colorNamesFrench.split(',');
776
+
777
+ cy.document().its('body').then((body) => {
778
+ const id = getRandomInt(0,999);
779
+ const format = FORMAT[getRandomInt(0,3)];
780
+
781
+ getMarkup(body, id, format);
782
+
783
+ cy.get(`input`).then(($input) => {
784
+ let cp;
785
+
786
+ if ($input.length) {
787
+ cp = new ColorPicker($input[0] as HTMLInputElement, {
788
+ colorKeywords: 'olive,green,red,transparent',
789
+ colorPresets: colorPresets,
790
+ colorLabels: colorNamesFrench,
791
+ });
792
+ }
793
+ cy.wrap(cp).as('cp');
794
+ })
795
+ });
796
+
797
+ frenchColors.forEach((color) => {
798
+ const webcolor = colorNameValues[frenchColors.indexOf(color)];
799
+ cy.get('input').eq(0).focus().clear().type(`${webcolor}`)
800
+ .get('@cp').its('isValid').should('be.true')
801
+ .get('input').eq(0).type('{enter}')
802
+ .get('@cp').its('appearance').should('be.equal', color);
803
+
804
+ if (colorNames[frenchColors.indexOf(color)] === 'white') {
805
+ cy.get('@cp').its('luminance').should('be.greaterThan', 0.99);
806
+ } else {
807
+ cy.get('@cp').its('luminance').should('be.lessThan', 0.99);
808
+ }
809
+ cy.wait(17);
810
+ });
811
+ });
812
+
813
+ FORMAT.forEach((format) => {
814
+ it(`Test format specific event listeners - ${format.toUpperCase()}`, function() {
815
+ cy.get('body').then((body) => {
816
+ const id = getRandomInt(0,999);
817
+
818
+ getMarkup(body, id, format);
819
+
820
+ cy.get('input').then(($input) => {
821
+ let cp: unknown | ColorPicker;
822
+ if ($input.length) {
823
+ const [input] = $input;
824
+ input.value = '';
825
+ cp = new ColorPicker(input, {
826
+ colorKeywords: ['green','green','red','transparent','currentColor'],
827
+ colorPresets: colorPresets.split(','),
828
+ colorLabels: colorNamesFrench.split(','),
829
+ });
830
+ }
831
+ cy.wrap(cp).as('cp');
832
+ });
833
+
834
+ cy.log('Test typing a valid value and press `enter`');
835
+ cy.get('input').eq(0).focus().clear().type('hsl 0 100 50{enter}')
836
+ cy.get('@cp').its('value').then((value) => {
837
+ if (format === 'hsl') {
838
+ expect(value).to.be.equal('hsl(0, 100%, 50%)');
839
+ } else if (format === 'rgb') {
840
+ expect(value).to.be.equal('rgb(255, 0, 0)');
841
+ } else if (format === 'hex') {
842
+ expect(value).to.be.equal('#f00');
843
+ } else if (format === 'hwb') {
844
+ expect(value).to.be.equal('hwb(0deg 0% 0%)');
845
+ }
846
+ });
847
+
848
+ cy.log('Test keyboard event listeners on `inputs`');
849
+ if (format === 'hex') {
850
+ cy.log('test hex');
851
+
852
+ cy.get('input').eq(0).focus().clear().type('hsl 300 100 50{enter}')
853
+ cy.get('@cp').its('rgb').then((rgb) => {
854
+ cy.get('.color-input').eq(0).focus().clear().type('hsl 100 100 50{enter}')
855
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
856
+ });
857
+ } else if (format === 'rgb') {
858
+ cy.log('test rgb');
859
+
860
+ cy.get('input').eq(0).focus().clear().type('hsl 300 100 50{enter}')
861
+ cy.get('@cp').its('rgb').then((rgb) => {
862
+ cy.get('.color-input').eq(0).focus().clear().type('150{enter}')
863
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
864
+ });
865
+ cy.get('input').eq(0).focus().clear().type('hsl 100 100 50{enter}')
866
+ cy.get('@cp').its('rgb').then((rgb) => {
867
+ cy.get('.color-input').eq(1).focus().clear().type('150{enter}')
868
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
869
+ });
870
+ cy.get('input').eq(0).focus().clear().type('hsl 300 100 50{enter}')
871
+ .get('@cp').its('rgb').then((rgb) => {
872
+ cy.get('.color-input').eq(2).focus().clear().type('150{enter}')
873
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
874
+ // });
875
+ });
876
+ } else if (format === 'hsl' || format === 'hwb') {
877
+ cy.log('test hsl / hwb');
878
+
879
+ cy.get('input').eq(0).focus().clear().type('hsl 270 100 50{enter}')
880
+ .get('@cp').its('rgb').then((rgb) => {
881
+ cy.get('.color-input').eq(0).focus().clear().type('0{enter}')
882
+ // .wait(17)
883
+ // .then(() => {
884
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
885
+ // });
886
+ });
887
+ cy.get('input').eq(0).focus().clear().type('hsl 330 100 50{enter}')
888
+ .get('@cp').its('rgb').then((rgb) => {
889
+ cy.get('.color-input').eq(1).focus().clear().type('50{enter}')
890
+ // .wait(17)
891
+ // .then(() => {
892
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
893
+ // });
894
+ });
895
+ cy.get('input').eq(0).focus().clear().type('hsl 300 100 50{enter}')
896
+ .get('@cp').its('rgb').then((rgb) => {
897
+ cy.get('.color-input').eq(2).focus().clear().type('25{enter}')
898
+ // .wait(17)
899
+ // .then(() => {
900
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
901
+ // });
902
+ });
903
+ }
904
+
905
+ if (format !== 'hex') {
906
+ cy.log('test alpha chanel input');
907
+ cy.get('input').eq(0).focus().clear().type('hsl 240 100 50{enter}')
908
+ .get('@cp').its('rgb').then((rgb) => {
909
+ cy.get('.color-input').eq(3).focus().clear().type('25{enter}')
910
+ // .wait(17)
911
+ // .then(() => {
912
+ cy.get('@cp').its('rgb').should('not.deep.equal', rgb);
913
+ // });
914
+ });
915
+ }
916
+ });
917
+ });
918
+ });
919
+
920
+ });