@thednp/color-picker 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. package/README.md +4 -4
  2. package/dist/css/color-picker.css +1 -1
  3. package/dist/css/color-picker.min.css +1 -1
  4. package/dist/css/color-picker.rtl.css +1 -1
  5. package/dist/css/color-picker.rtl.min.css +1 -1
  6. package/dist/js/color-picker.cjs +2 -2
  7. package/dist/js/color-picker.cjs.map +1 -1
  8. package/dist/js/color-picker.js +2 -2
  9. package/dist/js/color-picker.js.map +1 -1
  10. package/dist/js/color-picker.mjs +243 -239
  11. package/dist/js/color-picker.mjs.map +1 -1
  12. package/package.json +23 -26
  13. package/src/ts/colorPalette.ts +2 -2
  14. package/src/ts/index.ts +20 -19
  15. package/test/color-palette.test.ts +137 -0
  16. package/test/color-picker.test.ts +705 -0
  17. package/{cypress → test}/fixtures/getMarkup.js +8 -7
  18. package/test/fixtures/swipe.ts +33 -0
  19. package/test/fixtures/write.ts +37 -0
  20. package/tsconfig.json +45 -27
  21. package/vite.config.mts +5 -13
  22. package/vitest.config-ui.ts +26 -0
  23. package/vitest.config.ts +26 -0
  24. package/cypress/e2e/color-palette.cy.ts +0 -128
  25. package/cypress/e2e/color-picker.cy.ts +0 -909
  26. package/cypress/plugins/esbuild-istanbul.ts +0 -50
  27. package/cypress/plugins/tsCompile.ts +0 -34
  28. package/cypress/support/commands.ts +0 -0
  29. package/cypress/support/e2e.ts +0 -21
  30. package/cypress/test.html +0 -23
  31. package/cypress.config.ts +0 -30
  32. /package/{cypress → test}/fixtures/colorNamesFrench.js +0 -0
  33. /package/{cypress → test}/fixtures/componentLabelsFrench.js +0 -0
  34. /package/{cypress → test}/fixtures/format.js +0 -0
  35. /package/{cypress → test}/fixtures/getRandomInt.js +0 -0
  36. /package/{cypress → test}/fixtures/sampleWebcolors.js +0 -0
  37. /package/{cypress → test}/fixtures/testSample.js +0 -0
@@ -0,0 +1,705 @@
1
+ import { expect, it, describe, beforeEach, vi } from 'vitest';
2
+ import Color from '@thednp/color';
3
+ import ColorPalette from '~/ts/colorPalette';
4
+ import ColorPicker from '~/ts/index';
5
+ import getRandomInt from './fixtures/getRandomInt';
6
+ import FORMAT from './fixtures/format';
7
+ import getMarkup from './fixtures/getMarkup';
8
+ import componentLabelsFrench from './fixtures/componentLabelsFrench';
9
+ import colorNamesFrench from './fixtures/colorNamesFrench';
10
+ import write from './fixtures/write';
11
+ import swipe from './fixtures/swipe';
12
+ import "~/scss/color-picker.scss";
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
+
18
+ describe('ColorPicker Class Test', () => {
19
+ const wrapper = document.createElement('div');
20
+ document.body.append(wrapper);
21
+
22
+ beforeEach(async () => {
23
+ wrapper.innerHTML = '';
24
+ });
25
+
26
+ it('initialize with no parameter throws error', () => {
27
+ try {
28
+ // @ts-expect-error
29
+ new ColorPicker();
30
+ } catch (error) {
31
+ expect(error).to.be.instanceOf(TypeError);
32
+ expect(error).to.have.property('message', `ColorPicker target not specified.`);
33
+ }
34
+ });
35
+ it('inject incomplete markup and initialize, throws error', () => {
36
+ const cp = document.createElement('div');
37
+ const input = document.createElement('input');
38
+ cp.append(input)
39
+ document.body.append(cp);
40
+
41
+ try {
42
+ new ColorPicker(input);
43
+ } catch (error) {
44
+ expect(error).to.be.instanceOf(TypeError);
45
+ expect(error).to.have.property('message', 'ColorPicker requires a specific markup to work.');
46
+ }
47
+ });
48
+ it('inject markup and initialize with all DATA API', async () => {
49
+ const id = getRandomInt(0, 999);
50
+ const format = FORMAT[getRandomInt(0, 3)];
51
+ const { init, selector } = ColorPicker;
52
+ const { container, value } = await vi.waitFor(() => getMarkup(id, format), { timeout: 150 });
53
+ wrapper.append(container);
54
+ const input = await vi.waitFor(() => document.querySelector(selector) as HTMLInputElement, { timeout: 200 });
55
+
56
+ if (!input) return;
57
+ input.setAttribute('value', value);
58
+ input.setAttribute('data-function', "color-picker");
59
+ input.setAttribute('data-color-presets', '{"hue": 120, "hueSteps": 6, "lightSteps": 10}');
60
+ input.setAttribute('data-color-keywords', 'magenta, olive, yellow, red:default, currentColor:textColor');
61
+ input.setAttribute('data-color-labels', colorNamesFrench);
62
+ 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"}');
63
+
64
+ init(input);
65
+
66
+ const cp = ColorPicker.getInstance(input as HTMLInputElement);
67
+ expect(Object.values(cp?.colorLabels!)).to.deep.equal(colorNamesFrench.split(','));
68
+ expect(cp?.format).to.equal(format);
69
+ expect(cp?.value).to.equal(value);
70
+ expect(cp?.value).to.equal(new Color(value, format).toString(true));
71
+ expect(cp?.colorKeywords).to.deep.equal(['magenta', 'olive', 'yellow', 'red:default', 'currentColor:textColor']);
72
+ expect(cp?.colorPresets).to.be.instanceOf(ColorPalette);
73
+ (cp?.colorPresets as ColorPalette).colors.forEach(c => expect(c).to.be.instanceOf(Color));
74
+ expect(cp?.colorPicker).to.be.instanceOf(HTMLDivElement);
75
+ expect(cp?.colorMenu).to.be.instanceOf(HTMLDivElement);
76
+ });
77
+ it('inject markup and initialize via JavaScript API', async () => {
78
+ const id = getRandomInt(0, 999);
79
+ const format = FORMAT[getRandomInt(0, 3)];
80
+ const { container, value } = await vi.waitFor(() => getMarkup(id, format), { timeout: 150 });
81
+ wrapper.append(container); const input = await vi.waitFor(() => document.querySelector('input') as HTMLInputElement, { timeout: 200 });
82
+ if (!input) return;
83
+
84
+ const cp = new ColorPicker(input, {
85
+ colorKeywords: ['orange', 'lime', 'darkred'],
86
+ colorPresets: '#330033, #990099, #ff00ff, #ff66ff, #ffccff',
87
+ colorLabels: colorNamesFrench,
88
+ componentLabels: componentLabelsFrench,
89
+ });
90
+ expect(cp.input).to.be.eq(input);
91
+ expect(cp.format).to.be.eq(format);
92
+ expect(cp?.value).to.equal(value);
93
+ expect(cp?.colorPresets).to.be.instanceOf(Array);
94
+ expect(cp?.colorPresets as string[]).to.deep.equal('#330033,#990099,#ff00ff,#ff66ff,#ffccff'.split(','));
95
+ expect(cp?.colorPicker).to.be.instanceOf(HTMLDivElement);
96
+ expect(Object.values(cp?.colorLabels)).to.deep.equal(colorNamesFrench.split(','));
97
+ expect(cp?.colorKeywords).to.deep.equal(['orange', 'lime', 'darkred']);
98
+ });
99
+ it('inject markup and initialize with `colorKeywords`', async () => {
100
+ const id = getRandomInt(0, 999);
101
+ const format = FORMAT[getRandomInt(0, 3)];
102
+
103
+ const { container } = await vi.waitFor(() => getMarkup(id, format), { timeout: 150 });
104
+ wrapper.append(container); const input = await vi.waitFor(() => document.querySelector('input') as HTMLInputElement, { timeout: 200 });
105
+ if (!input) return;
106
+
107
+ const cp = new ColorPicker(input, {
108
+ colorKeywords: 'orange, lime, darkred',
109
+ });
110
+ expect(cp.colorKeywords).to.deep.equal('orange,lime,darkred'.split(','))
111
+ });
112
+ it('inject markup and initialize with `colorPresets`', async () => {
113
+ const id = getRandomInt(0, 999);
114
+ const format = FORMAT[getRandomInt(0, 3)];
115
+ const { container } = await vi.waitFor(() => getMarkup(id, format), { timeout: 150 });
116
+ wrapper.append(container);
117
+ const input = await vi.waitFor(() => document.querySelector('input') as HTMLInputElement, { timeout: 200 });
118
+ if (!input) return;
119
+
120
+ const cp = new ColorPicker(input, {
121
+ colorPresets: '#330033, #990099, #ff00ff, #ff66ff, #ffccff'.split(',').map(x => x.trim()),
122
+ });
123
+ expect(cp.colorPresets).to.deep.equal('#330033,#990099,#ff00ff,#ff66ff,#ffccff'.split(',').map(x => x.trim()));
124
+ });
125
+ it('Test showing & hiding `colorPicker` / `presetsMenu`', async function () {
126
+ vi.useFakeTimers();
127
+ const id = getRandomInt(0, 999);
128
+ const format = FORMAT[getRandomInt(0, 3)];
129
+
130
+ const { container } = await vi.waitUntil(() => getMarkup(id, format), { timeout: 150 });
131
+ wrapper.append(container);
132
+ const mylink = await vi.waitUntil(() => container.getElementsByClassName('my-link')[0] as HTMLAnchorElement, { timeout: 17 });
133
+ const input = await vi.waitUntil(() => container.querySelector('input') as HTMLInputElement, { timeout: 200 });
134
+ if (!input) return;
135
+
136
+ const cp = new ColorPicker(input, {
137
+ colorKeywords: 'olive,green,red,:empty,transparent',
138
+ colorPresets: colorPresets,
139
+ colorLabels: colorNamesFrench,
140
+ });
141
+
142
+ cp.pickerToggle.click();
143
+ vi.advanceTimersByTime(350);
144
+ expect(cp.colorPicker.className).to.contain('show');
145
+
146
+ cp.hide();
147
+ vi.advanceTimersByTime(350);
148
+ expect(cp.colorPicker.className).to.not.contain('show');
149
+
150
+ cp.menuToggle.click();
151
+ vi.advanceTimersByTime(350);
152
+ expect(cp.colorPicker.className).to.not.contain('show');
153
+ expect(cp.colorMenu.className).to.contain('show');
154
+
155
+ cp.hide();
156
+ vi.advanceTimersByTime(350);
157
+
158
+ write(cp.pickerToggle, "Space");
159
+ vi.advanceTimersByTime(50);
160
+ expect(cp.colorPicker.className).to.contain('show');
161
+ expect(cp.colorMenu.className).to.not.contain('show');
162
+
163
+ write(cp.menuToggle, 'Enter');
164
+ vi.advanceTimersByTime(350);
165
+ expect(cp.colorMenu.className).to.contain('show');
166
+
167
+ write(cp.menuToggle, 'Space');
168
+ vi.advanceTimersByTime(350);
169
+ expect(cp.colorMenu.className).to.not.contain('show');
170
+
171
+ cp.togglePicker();
172
+ vi.advanceTimersByTime(350);
173
+ expect(cp.colorPicker.className).to.contain('show');
174
+
175
+ if (!mylink) return;
176
+ // mylink.click();
177
+ mylink.dispatchEvent(new PointerEvent('pointerup', { bubbles: true }))
178
+
179
+ vi.advanceTimersByTime(350);
180
+ expect(cp.colorPicker.className).to.not.contain('show');
181
+
182
+ vi.advanceTimersByTime(350);
183
+ cp.togglePicker();
184
+ vi.advanceTimersByTime(350);
185
+ expect(cp.colorPicker.className).to.contain('show');
186
+ document.body.dispatchEvent(new PointerEvent('pointerup', { bubbles: true, clientX: 200, clientY: 550 }))
187
+ vi.advanceTimersByTime(350);
188
+ expect(cp.colorPicker.className).to.not.contain('show');
189
+
190
+ cp.showPicker();
191
+ vi.advanceTimersByTime(350);
192
+ expect(cp.colorPicker.className).to.contain('show');
193
+ vi.advanceTimersByTime(350);
194
+
195
+ write(cp.input, 'transparentEnter');
196
+ vi.advanceTimersByTime(350);
197
+ expect(cp.rgb).to.deep.equal({ r: 0, g: 0, b: 0, a: 0 });
198
+
199
+ write(cp.input, 'currentColorEnter');
200
+ vi.advanceTimersByTime(350);
201
+ expect(cp.rgb).to.deep.equal({ r: 0, g: 0, b: 0, a: 1 });
202
+
203
+ write(cp.input, 'wombatEnter');
204
+ vi.advanceTimersByTime(350);
205
+ expect(cp.value).to.not.equal('wombat');
206
+
207
+ write(cp.input, 'hsl 0 0 100');
208
+ vi.advanceTimersByTime(350);
209
+ cp.hide();
210
+ vi.advanceTimersByTime(350);
211
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 255, b: 255, a: 1 });
212
+ expect(cp.value).to.not.equal('hsl 0 0 100');
213
+ });
214
+ it('Test `pointer` event listeners', async () => {
215
+ vi.useFakeTimers();
216
+ const id = getRandomInt(0, 999);
217
+ const format = FORMAT[getRandomInt(0, 3)];
218
+
219
+ const { container } = await vi.waitFor(() => getMarkup(id, format), { timeout: 150 });
220
+ wrapper.append(container)
221
+ const input = await vi.waitFor(() => container.querySelector('input') as HTMLInputElement, { timeout: 200 });
222
+ if (!input) return;
223
+
224
+ const cp = new ColorPicker(input);
225
+ let lastRgb = cp.rgb;
226
+
227
+ cp.showPicker();
228
+ vi.advanceTimersByTime(350);
229
+ write(input, 'hsl 0 100 50 Enter');
230
+ vi.advanceTimersByTime(350);
231
+ expect(cp.colorPicker.className, 'dropdown is open').to.include('show');
232
+ expect(input.value, 'write check').to.equal(cp.value);
233
+ lastRgb = cp.rgb;
234
+
235
+ vi.advanceTimersByTime(350);
236
+ swipe(cp.visuals[0], [[5, 5], [-5, -5], [500, 500], [100, 100], [100, 100]]);
237
+ vi.advanceTimersByTime(350);
238
+ expect(cp.rgb, 'swipe check').to.not.deep.equal(lastRgb);
239
+ vi.advanceTimersByTime(350);
240
+ lastRgb = cp.rgb;
241
+
242
+ vi.advanceTimersByTime(550);
243
+ swipe(cp.visuals[1], [[5, 5], [5, 0], [5, -5], [5, 500], [5, 100], [5, 100]]);
244
+ vi.advanceTimersByTime(350);
245
+ expect(cp.rgb, 'swipe check').to.not.deep.equal(lastRgb);
246
+ vi.advanceTimersByTime(350);
247
+ lastRgb = cp.rgb;
248
+
249
+ vi.advanceTimersByTime(550);
250
+ swipe(cp.visuals[2], [[5, 5], [5, -5], [5, 200], [5, 500], [5, 100], [5, 100]]);
251
+ vi.advanceTimersByTime(350);
252
+ expect(cp.rgb, 'swipe check').to.not.deep.equal(lastRgb);
253
+ vi.advanceTimersByTime(350);
254
+ lastRgb = cp.rgb;
255
+ vi.advanceTimersByTime(350);
256
+ });
257
+ it('Test `keyboard` event listeners', async () => {
258
+ vi.useFakeTimers();
259
+ const id = getRandomInt(0, 999);
260
+ const format = FORMAT[getRandomInt(0, 3)];
261
+ const { container } = getMarkup(id, format);
262
+ wrapper.append(container);
263
+ const input = container.querySelector('input');
264
+ if (!input) return;
265
+
266
+ const cp = new ColorPicker(input, {
267
+ colorKeywords: 'olive,green,red,:empty,transparent',
268
+ colorPresets: colorPresets,
269
+ colorLabels: colorNamesFrench,
270
+ });
271
+
272
+ cp.togglePicker();
273
+ vi.advanceTimersByTime(350);
274
+
275
+ input.focus();
276
+ input.select();
277
+ write(input, "hsl 0 0 100");
278
+ vi.advanceTimersByTime(350);
279
+ // input.addEventListener('keyup', (e) => console.log(e));
280
+ input.dispatchEvent(new KeyboardEvent('keyup', { code: "Escape", key: "Escape", bubbles: true }));
281
+ vi.advanceTimersByTime(350);
282
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 255, b: 255 });
283
+ expect(cp.value).to.not.equal("hsl 0 0 100");
284
+
285
+ cp.toggleMenu();
286
+ vi.advanceTimersByTime(350);
287
+ const defaults = cp.colorMenu.children[1].getElementsByTagName('LI') as HTMLCollectionOf<HTMLLIElement>;
288
+ defaults[0].focus();
289
+ defaults[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowRight', code: 'ArrowRight' }));
290
+ vi.advanceTimersByTime(350);
291
+ // console.log(document.activeElement);
292
+ expect(defaults[1]).to.equal(document.activeElement);
293
+
294
+ vi.advanceTimersByTime(350);
295
+ defaults[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowLeft', code: 'ArrowLeft' }));
296
+ vi.advanceTimersByTime(350);
297
+ expect(defaults[0]).to.equal(document.activeElement);
298
+
299
+ vi.advanceTimersByTime(350);
300
+ defaults[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowDown', code: 'ArrowDown' }));
301
+ vi.advanceTimersByTime(350);
302
+ expect(defaults[1]).to.equal(document.activeElement);
303
+
304
+ vi.advanceTimersByTime(350);
305
+ defaults[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowUp', code: 'ArrowUp' }));
306
+ vi.advanceTimersByTime(350);
307
+ expect(defaults[0]).to.equal(document.activeElement);
308
+
309
+ let prevValue = cp.value;
310
+ vi.advanceTimersByTime(350);
311
+ defaults[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter', code: 'Enter' }));
312
+ vi.advanceTimersByTime(350);
313
+ expect(defaults[0].className).to.contain('active');
314
+ expect(cp.value).to.not.equal(prevValue);
315
+
316
+ vi.advanceTimersByTime(350);
317
+ const transparent = cp.colorMenu.querySelector<HTMLLIElement>('[data-value="transparent"]');
318
+ if (transparent) {
319
+ transparent.click();
320
+ vi.advanceTimersByTime(350);
321
+ prevValue = cp.value;
322
+
323
+ expect(transparent.className).to.contain('active');
324
+ expect(cp.value).to.equal('transparent');
325
+ }
326
+
327
+ vi.advanceTimersByTime(350);
328
+ const empty = cp.colorMenu.querySelector<HTMLLIElement>('[data-value="empty"]');
329
+ if (empty) {
330
+ empty.click();
331
+ vi.advanceTimersByTime(350);
332
+ prevValue = cp.value;
333
+
334
+ expect(empty.className).to.contain('active');
335
+ expect(cp.value).to.not.equal('empty');
336
+ }
337
+
338
+ vi.advanceTimersByTime(350);
339
+ const options = cp.colorMenu.children[0].getElementsByTagName('LI') as HTMLCollectionOf<HTMLLIElement>;
340
+ options[0].focus();
341
+ // options[0].addEventListener('keydown', e => console.log(e))
342
+ options[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowRight', code: 'ArrowRight' }));
343
+ vi.advanceTimersByTime(350);
344
+ expect(options[1]).to.equal(document.activeElement);
345
+
346
+ vi.advanceTimersByTime(350);
347
+ options[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowLeft', code: 'ArrowLeft' }));
348
+ vi.advanceTimersByTime(350);
349
+ expect(options[0]).to.equal(document.activeElement);
350
+
351
+ vi.advanceTimersByTime(350);
352
+ options[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowDown', code: 'ArrowDown' }));
353
+ vi.advanceTimersByTime(350);
354
+ // console.log(document.activeElement);
355
+ expect(options[10]).to.equal(document.activeElement);
356
+
357
+ vi.advanceTimersByTime(350);
358
+ options[10].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'ArrowUp', code: 'ArrowUp' }));
359
+ vi.advanceTimersByTime(350);
360
+ expect(options[0]).to.equal(document.activeElement);
361
+
362
+ prevValue = cp.value;
363
+ options[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: 'Enter', code: 'Enter' }));
364
+ vi.advanceTimersByTime(350);
365
+ expect(options[0].className).to.contain('active');
366
+ expect(cp.value).to.not.equal(prevValue);
367
+
368
+ prevValue = cp.value;
369
+ options[1].click();
370
+ vi.advanceTimersByTime(350);
371
+ expect(options[1].className).to.contain('active');
372
+ expect(cp.value).to.not.equal(prevValue);
373
+
374
+ document.dispatchEvent(new KeyboardEvent('keyup', { bubbles: true, code: 'Escape' }))
375
+ vi.advanceTimersByTime(350);
376
+ expect(cp.colorMenu.className).to.not.contain('show');
377
+ });
378
+ it('Test `scroll` event listeners', async () => {
379
+ vi.useFakeTimers();
380
+ const id = getRandomInt(0, 999);
381
+ const format = FORMAT[getRandomInt(0, 3)];
382
+ const { container } = getMarkup(id, format);
383
+ Object.assign(container.style, { margin: '750px 0' });
384
+ wrapper.append(container);
385
+ const input = container.querySelector('input');
386
+ if (!input) return;
387
+
388
+ const win = input.ownerDocument.defaultView!;
389
+ const cp = new ColorPicker(input, {
390
+ colorKeywords: 'olive,green,red,:empty,transparent',
391
+ colorPresets: colorPresets,
392
+ colorLabels: colorNamesFrench,
393
+ });
394
+
395
+ cp.showPicker();
396
+ vi.advanceTimersByTime(350);
397
+ expect(cp.colorPicker.className).to.contain('show');
398
+
399
+ vi.advanceTimersByTime(350);
400
+ win.scrollTo({ left: 0, top: 10, behavior: "smooth" });
401
+ win.dispatchEvent(new Event('scroll'));
402
+ await Promise.resolve(() =>
403
+ new Promise((resolve) => {
404
+ setTimeout(() => {
405
+ expect(cp.colorPicker.className).to.contain('top');
406
+ resolve('done')
407
+ }, 550)
408
+ vi.advanceTimersByTime(550);
409
+ })
410
+ );
411
+
412
+ win.scrollTo({ left: 0, top: document.body.scrollHeight, behavior: "smooth" });
413
+ win.dispatchEvent(new Event('scroll'));
414
+ await Promise.resolve(() =>
415
+ new Promise((resolve) => {
416
+ setTimeout(() => {
417
+ expect(cp.colorPicker.className).to.not.contain('top');
418
+ resolve('done')
419
+ }, 550)
420
+ vi.advanceTimersByTime(550);
421
+ })
422
+ );
423
+ });
424
+ it('Test controls mouse & keyboard events', async () => {
425
+ vi.useFakeTimers();
426
+ const id = getRandomInt(0, 999);
427
+ const format = FORMAT[getRandomInt(0, 3)];
428
+ const { container } = getMarkup(id, format);
429
+ wrapper.append(container);
430
+ const input = container.querySelector('input');
431
+ if (!input) return;
432
+
433
+ const cp = new ColorPicker(input, {
434
+ colorKeywords: 'green,green,red,transparent',
435
+ colorPresets: colorPresets,
436
+ });
437
+
438
+ cp.togglePicker();
439
+ vi.advanceTimersByTime(350);
440
+ write(input, "hsl 0 100 50 Enter");
441
+ vi.advanceTimersByTime(350);
442
+
443
+ // test visuals click, but we're using pointerdown more reliably
444
+ const v0rect = cp.visuals[0].getBoundingClientRect();
445
+ cp.visuals[0].dispatchEvent(new PointerEvent('pointerdown', {
446
+ bubbles: true, cancelable: true,
447
+ clientX: v0rect.left + v0rect.width / 2,
448
+ clientY: v0rect.top + v0rect.height / 2,
449
+ }));
450
+ vi.advanceTimersByTime(350);
451
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 0, b: 0, a: 1 });
452
+
453
+ write(input, "hsl 300 100 50 Enter");
454
+ vi.advanceTimersByTime(350);
455
+ const v1rect = cp.visuals[1].getBoundingClientRect();
456
+ cp.visuals[1].dispatchEvent(new PointerEvent('pointerdown', {
457
+ bubbles: true, cancelable: true,
458
+ clientX: v1rect.left + v1rect.width / 2,
459
+ clientY: v1rect.top + v1rect.height / 2,
460
+ }));
461
+ vi.advanceTimersByTime(350);
462
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 0, b: 255, a: 1 });
463
+
464
+ write(input, "hsl 120 100 50 Enter");
465
+ vi.advanceTimersByTime(350);
466
+ const v2rect = cp.visuals[2].getBoundingClientRect();
467
+ cp.visuals[2].dispatchEvent(new PointerEvent('pointerdown', {
468
+ bubbles: true, cancelable: true,
469
+ clientX: v2rect.left + v2rect.width / 2,
470
+ clientY: v2rect.top + v2rect.height / 2,
471
+ }));
472
+ vi.advanceTimersByTime(350);
473
+ expect(cp.rgb).to.deep.equal({ r: 0, g: 255, b: 0, a: 0.5 });
474
+
475
+ // test control knobs pointer events
476
+ write(input, "hsl 0 100 100 Enter");
477
+ vi.advanceTimersByTime(350);
478
+ swipe(cp.controlKnobs[0], [[2, 2], [-v0rect.left, -v0rect.top], [300, 300], [v0rect.width / 2, v0rect.height / 2]], { x: v0rect.left, y: v0rect.top });
479
+ vi.advanceTimersByTime(350);
480
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 0, b: 0, a: 1 });
481
+
482
+ write(input, "hsl 0 100 100 Enter");
483
+ vi.advanceTimersByTime(350);
484
+ swipe(cp.controlKnobs[1], [[2, 2], [-v1rect.left, -v1rect.top], [2, 300], [v1rect.width / 2, v1rect.height / 2]], { x: v1rect.left, y: v1rect.top });
485
+ vi.advanceTimersByTime(350);
486
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 0, b: 0, a: 1 });
487
+
488
+ write(input, "hsl 0 100 100 Enter");
489
+ vi.advanceTimersByTime(350);
490
+ swipe(cp.controlKnobs[2], [[2, 2], [-v2rect.left, -v2rect.top], [2, 300], [v2rect.width / 2, v2rect.height / 2]], { x: v2rect.left, y: v2rect.top });
491
+ vi.advanceTimersByTime(350);
492
+ expect(cp.rgb).to.not.deep.equal({ r: 255, g: 0, b: 0, a: 1 });
493
+
494
+ // test control knobs keyboard events
495
+ write(input, 'hsl 300 100 50 Enter');
496
+ vi.advanceTimersByTime(350);
497
+ let currentRgb = cp.rgb;
498
+ cp.controlKnobs[0].focus();
499
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
500
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
501
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "a", code: 'a' })); // adge case, produces no effect
502
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
503
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
504
+ cp.controlKnobs[0].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowLeft", code: 'ArrowLeft' }));
505
+ vi.advanceTimersByTime(350);
506
+ expect(cp.rgb).to.not.deep.equal(currentRgb);
507
+
508
+ write(input, 'hsl 180 100 50 Enter');
509
+ vi.advanceTimersByTime(350);
510
+ currentRgb = cp.rgb;
511
+ cp.controlKnobs[1].focus();
512
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
513
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
514
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "b", code: 'b' }));
515
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
516
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
517
+ cp.controlKnobs[1].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowLeft", code: 'ArrowLeft' }));
518
+ vi.advanceTimersByTime(350);
519
+ expect(cp.rgb).to.not.deep.equal(currentRgb);
520
+
521
+ write(input, 'hsl 0 100 50');
522
+ vi.advanceTimersByTime(350);
523
+ currentRgb = cp.rgb;
524
+ cp.controlKnobs[2].focus();
525
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
526
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowDown", code: 'ArrowDown' }));
527
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "b", code: 'c' }));
528
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
529
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowRight", code: 'ArrowRight' }));
530
+ cp.controlKnobs[2].dispatchEvent(new KeyboardEvent('keydown', { bubbles: true, key: "ArrowLeft", code: 'ArrowLeft' }));
531
+ vi.advanceTimersByTime(350);
532
+ expect(cp.rgb).to.not.deep.equal(currentRgb);
533
+ });
534
+ it('Test private methods', function () {
535
+ vi.useFakeTimers();
536
+ const id = getRandomInt(0, 999);
537
+ const format = FORMAT[getRandomInt(0, 3)];
538
+ const { container } = getMarkup(id, format);
539
+ wrapper.append(container);
540
+ const input = container.querySelector('input');
541
+ if (!input) return;
542
+
543
+ const cp = new ColorPicker(input, {
544
+ colorKeywords: 'green,green,red,transparent',
545
+ colorPresets: colorPresets,
546
+ });
547
+
548
+ cp.togglePicker();
549
+ vi.advanceTimersByTime(350);
550
+ expect(cp.colorPicker.className).to.contain('show');
551
+
552
+ cp.togglePicker();
553
+ vi.advanceTimersByTime(350);
554
+ expect(cp.colorPicker.className).to.not.contain('show');
555
+
556
+ cp.toggleMenu();
557
+ vi.advanceTimersByTime(350);
558
+ expect(cp.colorMenu.className).to.contain('show');
559
+
560
+ cp.toggleMenu();
561
+ vi.advanceTimersByTime(350);
562
+ expect(cp.colorMenu.className).to.not.contain('show');
563
+
564
+ cp.showPicker();
565
+ vi.advanceTimersByTime(350);
566
+ expect(cp.colorPicker.className).to.contain('show');
567
+
568
+ cp.hide();
569
+ vi.advanceTimersByTime(350);
570
+ expect(cp.colorPicker.className).to.not.contain('show');
571
+
572
+ cp.dispose();
573
+ vi.advanceTimersByTime(350);
574
+ expect(cp.colorPicker).to.not.undefined;
575
+ });
576
+
577
+ const frenchColors = colorNamesFrench.split(',');
578
+ frenchColors.forEach((color) => {
579
+ it(`Test color appearance and color luminance ${color}`, () => {
580
+ vi.useFakeTimers();
581
+ const id = getRandomInt(0, 999);
582
+ const format = FORMAT[getRandomInt(0, 3)];
583
+ const { container } = getMarkup(id, format);
584
+ wrapper.append(container);
585
+ const input = container.querySelector('input');
586
+ if (!input) return;
587
+
588
+ const cp = new ColorPicker(input, {
589
+ colorKeywords: 'olive,green,red,transparent',
590
+ colorPresets: colorPresets,
591
+ colorLabels: colorNamesFrench,
592
+ });
593
+
594
+ const webcolor = colorNameValues[frenchColors.indexOf(color)];
595
+
596
+ vi.advanceTimersByTime(350);
597
+ write(input, webcolor + "Enter");
598
+ vi.advanceTimersByTime(50);
599
+ expect(cp.isValid).to.be.true;
600
+ expect(cp.appearance).to.equal(color);
601
+
602
+ if (colorNames[frenchColors.indexOf(color)] === 'white') {
603
+ expect(cp.luminance).to.be.greaterThan(0.99);
604
+ } else {
605
+ expect(cp.luminance).to.be.lessThan(0.99);
606
+ }
607
+ vi.advanceTimersByTime(350);
608
+
609
+ });
610
+ });
611
+
612
+ FORMAT.forEach((format) => {
613
+ it(`Test format specific event listeners - ${format.toUpperCase()}`, async () => {
614
+ vi.useFakeTimers();
615
+ const id = getRandomInt(0, 999);
616
+ const { container } = getMarkup(id, format);
617
+ wrapper.append(container);
618
+ const input = container.querySelector('input');
619
+ if (!input) return;
620
+
621
+ const cp = new ColorPicker(input, {
622
+ colorKeywords: ['green', 'green', 'red', 'transparent', 'currentColor'],
623
+ colorPresets: colorPresets.split(','),
624
+ colorLabels: colorNamesFrench.split(','),
625
+ });
626
+ cp.showPicker();
627
+ vi.advanceTimersByTime(350);
628
+
629
+ // Test typing a valid value and press `Enter`
630
+ write(input, 'hsl 0 100 50 Enter');
631
+ vi.advanceTimersByTime(350);
632
+ if (format === 'hsl') {
633
+ expect(cp.value).to.be.equal('hsl(0, 100%, 50%)');
634
+ } else if (format === 'rgb') {
635
+ expect(cp.value).to.be.equal('rgb(255, 0, 0)');
636
+ } else if (format === 'hex') {
637
+ expect(cp.value).to.be.equal('#f00');
638
+ } else if (format === 'hwb') {
639
+ expect(cp.value).to.be.equal('hwb(0deg 0% 0%)');
640
+ }
641
+
642
+ // Test keyboard event listeners on `inputs`
643
+ if (format === 'hex') {
644
+ write(input, "hsl 300 100 50 Enter");
645
+ vi.advanceTimersByTime(350);
646
+ const rgb = cp.rgb;
647
+ write(cp.inputs[0], 'hsl 100 100 50 Enter');
648
+ vi.advanceTimersByTime(350);
649
+ expect(cp.rgb).to.not.deep.equal(rgb);
650
+ } else if (format === 'rgb') {
651
+ write(input, "hsl 300 100 50 Enter");
652
+ vi.advanceTimersByTime(350);
653
+ let rgb = cp.rgb;
654
+ write(cp.inputs[0], '150Enter');
655
+ vi.advanceTimersByTime(350);
656
+ expect(cp.rgb).to.not.deep.equal(rgb);
657
+
658
+ write(input, "hsl 100 100 50 Enter");
659
+ vi.advanceTimersByTime(350);
660
+ rgb = cp.rgb;
661
+ write(cp.inputs[1], '150Enter');
662
+ vi.advanceTimersByTime(350);
663
+ expect(cp.rgb).to.not.deep.equal(rgb);
664
+
665
+ write(input, "hsl 300 100 50 Enter");
666
+ vi.advanceTimersByTime(350);
667
+ rgb = cp.rgb;
668
+ write(cp.inputs[2], '150Enter');
669
+ vi.advanceTimersByTime(350);
670
+ expect(cp.rgb).to.not.deep.equal(rgb);
671
+
672
+ } else if (format === 'hsl' || format === 'hwb') {
673
+ write(input, "hsl 270 100 50 Enter");
674
+ vi.advanceTimersByTime(350);
675
+ let rgb = cp.rgb;
676
+ write(cp.inputs[0], '0Enter');
677
+ vi.advanceTimersByTime(350);
678
+ expect(cp.rgb).to.not.deep.equal(rgb);
679
+
680
+ write(input, "hsl 330 100 50 Enter");
681
+ vi.advanceTimersByTime(350);
682
+ rgb = cp.rgb;
683
+ write(cp.inputs[1], '50Enter');
684
+ vi.advanceTimersByTime(350);
685
+ expect(cp.rgb).to.not.deep.equal(rgb);
686
+
687
+ write(input, "hsl 300 100 50 Enter");
688
+ vi.advanceTimersByTime(350);
689
+ rgb = cp.rgb;
690
+ write(cp.inputs[2], '25Enter');
691
+ vi.advanceTimersByTime(350);
692
+ expect(cp.rgb).to.not.deep.equal(rgb);
693
+ }
694
+
695
+ if (format !== 'hex') {
696
+ write(input, "hsl 240 100 50 Enter");
697
+ vi.advanceTimersByTime(350);
698
+ const rgb = cp.rgb;
699
+ write(cp.inputs[3], '25Enter');
700
+ vi.advanceTimersByTime(350);
701
+ expect(cp.rgb).to.not.deep.equal(rgb);
702
+ }
703
+ });
704
+ });
705
+ });