@thednp/color-picker 1.0.1 → 2.0.0-alpha10

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