@aquera/nile-elements 2.0.12 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/index.js +132 -134
- package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.css.cjs.js.map +1 -1
- package/dist/nile-auto-complete/nile-auto-complete.css.esm.js +5 -2
- package/dist/nile-chip/nile-chip.css.cjs.js +1 -1
- package/dist/nile-chip/nile-chip.css.cjs.js.map +1 -1
- package/dist/nile-chip/nile-chip.css.esm.js +2 -0
- package/dist/nile-combobox/nile-combobox.css.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.css.cjs.js.map +1 -1
- package/dist/nile-combobox/nile-combobox.css.esm.js +9 -9
- package/dist/nile-command-menu/nile-command-menu.css.cjs.js +1 -1
- package/dist/nile-command-menu/nile-command-menu.css.cjs.js.map +1 -1
- package/dist/nile-command-menu/nile-command-menu.css.esm.js +5 -5
- package/dist/nile-command-menu-item/nile-command-menu-item.css.cjs.js +1 -1
- package/dist/nile-command-menu-item/nile-command-menu-item.css.cjs.js.map +1 -1
- package/dist/nile-command-menu-item/nile-command-menu-item.css.esm.js +2 -2
- package/dist/nile-option/nile-option.css.cjs.js +1 -1
- package/dist/nile-option/nile-option.css.cjs.js.map +1 -1
- package/dist/nile-option/nile-option.css.esm.js +1 -1
- package/dist/nile-select/nile-select.cjs.js +2 -2
- package/dist/nile-select/nile-select.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.css.cjs.js +1 -1
- package/dist/nile-select/nile-select.css.cjs.js.map +1 -1
- package/dist/nile-select/nile-select.css.esm.js +19 -24
- package/dist/nile-select/nile-select.esm.js +43 -42
- package/dist/nile-select/virtual-scroll-helper.cjs.js +1 -1
- package/dist/nile-select/virtual-scroll-helper.cjs.js.map +1 -1
- package/dist/nile-select/virtual-scroll-helper.esm.js +2 -0
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js +4 -4
- package/dist/nile-virtual-select/nile-virtual-select.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.cjs.js.map +1 -1
- package/dist/nile-virtual-select/nile-virtual-select.css.esm.js +20 -25
- package/dist/nile-virtual-select/nile-virtual-select.esm.js +26 -26
- package/dist/src/nile-auto-complete/nile-auto-complete.css.js +3 -0
- package/dist/src/nile-auto-complete/nile-auto-complete.css.js.map +1 -1
- package/dist/src/nile-chip/nile-chip.css.js +2 -0
- package/dist/src/nile-chip/nile-chip.css.js.map +1 -1
- package/dist/src/nile-combobox/nile-combobox.css.js +9 -9
- package/dist/src/nile-combobox/nile-combobox.css.js.map +1 -1
- package/dist/src/nile-command-menu/nile-command-menu.css.js +5 -5
- package/dist/src/nile-command-menu/nile-command-menu.css.js.map +1 -1
- package/dist/src/nile-command-menu-item/nile-command-menu-item.css.js +2 -2
- package/dist/src/nile-command-menu-item/nile-command-menu-item.css.js.map +1 -1
- package/dist/src/nile-option/nile-option.css.js +1 -1
- package/dist/src/nile-option/nile-option.css.js.map +1 -1
- package/dist/src/nile-select/nile-select.css.js +19 -24
- package/dist/src/nile-select/nile-select.css.js.map +1 -1
- package/dist/src/nile-select/nile-select.d.ts +18 -2
- package/dist/src/nile-select/nile-select.js +226 -64
- package/dist/src/nile-select/nile-select.js.map +1 -1
- package/dist/src/nile-select/nile-select.test.d.ts +1 -0
- package/dist/src/nile-select/nile-select.test.js +896 -17
- package/dist/src/nile-select/nile-select.test.js.map +1 -1
- package/dist/src/nile-select/virtual-scroll-helper.js +2 -0
- package/dist/src/nile-select/virtual-scroll-helper.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js +20 -25
- package/dist/src/nile-virtual-select/nile-virtual-select.css.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.d.ts +10 -2
- package/dist/src/nile-virtual-select/nile-virtual-select.js +142 -30
- package/dist/src/nile-virtual-select/nile-virtual-select.js.map +1 -1
- package/dist/src/nile-virtual-select/nile-virtual-select.test.d.ts +1 -0
- package/dist/src/nile-virtual-select/nile-virtual-select.test.js +331 -16
- package/dist/src/nile-virtual-select/nile-virtual-select.test.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/nile-auto-complete/nile-auto-complete.css.ts +3 -0
- package/src/nile-chip/nile-chip.css.ts +2 -0
- package/src/nile-combobox/nile-combobox.css.ts +9 -9
- package/src/nile-command-menu/nile-command-menu.css.ts +5 -5
- package/src/nile-command-menu-item/nile-command-menu-item.css.ts +2 -2
- package/src/nile-option/nile-option.css.ts +1 -1
- package/src/nile-select/nile-select.css.ts +19 -24
- package/src/nile-select/nile-select.test.ts +1112 -17
- package/src/nile-select/nile-select.ts +237 -67
- package/src/nile-select/virtual-scroll-helper.ts +2 -0
- package/src/nile-virtual-select/nile-virtual-select.css.ts +20 -25
- package/src/nile-virtual-select/nile-virtual-select.test.ts +404 -16
- package/src/nile-virtual-select/nile-virtual-select.ts +200 -79
- package/vscode-html-custom-data.json +9 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { aTimeout, expect, fixture, html, nextFrame } from '@open-wc/testing';
|
|
1
|
+
import { aTimeout, expect, fixture, html, nextFrame, waitUntil } from '@open-wc/testing';
|
|
2
2
|
import './nile-virtual-select';
|
|
3
|
+
import '../nile-input/nile-input';
|
|
3
4
|
import type { NileVirtualSelect } from './nile-virtual-select';
|
|
4
5
|
|
|
5
6
|
describe('NileVirtualSelect', () => {
|
|
@@ -140,7 +141,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
140
141
|
it('V1. multiple + no max-options-visible + autoResize=false renders fitted chips', async () => {
|
|
141
142
|
const host = await fixture<HTMLElement>(
|
|
142
143
|
html`<div style="width: 320px">
|
|
143
|
-
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}>
|
|
144
|
+
<nile-virtual-select showtooltip multiple .data=${DATA} .value=${LABELS}>
|
|
144
145
|
</nile-virtual-select>
|
|
145
146
|
</div>`
|
|
146
147
|
);
|
|
@@ -156,7 +157,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
156
157
|
it('V2. multiple + no max-options-visible + autoResize=true refits when the container resizes', async () => {
|
|
157
158
|
const host = await fixture<HTMLElement>(
|
|
158
159
|
html`<div style="width: 480px">
|
|
159
|
-
<nile-virtual-select multiple autoresize .data=${DATA} .value=${LABELS}>
|
|
160
|
+
<nile-virtual-select showtooltip multiple autoresize .data=${DATA} .value=${LABELS}>
|
|
160
161
|
</nile-virtual-select>
|
|
161
162
|
</div>`
|
|
162
163
|
);
|
|
@@ -177,7 +178,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
177
178
|
it('V3. explicit max-options-visible switches to fixed-count layout', async () => {
|
|
178
179
|
const host = await fixture<HTMLElement>(
|
|
179
180
|
html`<div style="width: 320px">
|
|
180
|
-
<nile-virtual-select
|
|
181
|
+
<nile-virtual-select showtooltip
|
|
181
182
|
multiple
|
|
182
183
|
max-options-visible="2"
|
|
183
184
|
.data=${DATA}
|
|
@@ -197,7 +198,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
197
198
|
it('V3b. max-options-visible set as a property also switches to fixed-count layout', async () => {
|
|
198
199
|
const host = await fixture<HTMLElement>(
|
|
199
200
|
html`<div style="width: 320px">
|
|
200
|
-
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}>
|
|
201
|
+
<nile-virtual-select showtooltip multiple .data=${DATA} .value=${LABELS}>
|
|
201
202
|
</nile-virtual-select>
|
|
202
203
|
</div>`
|
|
203
204
|
);
|
|
@@ -215,7 +216,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
215
216
|
it('V3c. the default max-options-visible is still 3 and reading it does not opt out', async () => {
|
|
216
217
|
const host = await fixture<HTMLElement>(
|
|
217
218
|
html`<div style="width: 320px">
|
|
218
|
-
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}>
|
|
219
|
+
<nile-virtual-select showtooltip multiple .data=${DATA} .value=${LABELS}>
|
|
219
220
|
</nile-virtual-select>
|
|
220
221
|
</div>`
|
|
221
222
|
);
|
|
@@ -272,9 +273,9 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
272
273
|
it('V7. nile-select and nile-virtual-select agree on when fitting is active', async () => {
|
|
273
274
|
const host = await fixture<HTMLElement>(
|
|
274
275
|
html`<div style="width: 320px">
|
|
275
|
-
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}>
|
|
276
|
+
<nile-virtual-select showtooltip multiple .data=${DATA} .value=${LABELS}>
|
|
276
277
|
</nile-virtual-select>
|
|
277
|
-
<nile-virtual-select
|
|
278
|
+
<nile-virtual-select showtooltip
|
|
278
279
|
multiple
|
|
279
280
|
max-options-visible="2"
|
|
280
281
|
.data=${DATA}
|
|
@@ -321,7 +322,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
321
322
|
}).length;
|
|
322
323
|
}
|
|
323
324
|
|
|
324
|
-
it('V12. without showtooltip
|
|
325
|
+
it('V12. without showtooltip the pre-2.0.9 chip row is used', async () => {
|
|
325
326
|
const host = await fixture<HTMLElement>(
|
|
326
327
|
html`<div style="width: 480px">
|
|
327
328
|
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}></nile-virtual-select>
|
|
@@ -329,11 +330,16 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
329
330
|
);
|
|
330
331
|
const el = host.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
331
332
|
await settle(el);
|
|
333
|
+
await aTimeout(20);
|
|
334
|
+
await settle(el);
|
|
332
335
|
|
|
333
336
|
expect(el.showTooltip).to.be.false;
|
|
334
|
-
expect(
|
|
335
|
-
|
|
336
|
-
expect(
|
|
337
|
+
expect(tagsRoot(el)!.classList.contains('select__tags--fitted')).to.be.false;
|
|
338
|
+
expect(el.shadowRoot!.querySelector('button.select__tags-count')).to.not.exist;
|
|
339
|
+
expect(chips(el).every(chip => !chip.style.maxWidth)).to.be.true;
|
|
340
|
+
const overflowCount = el.shadowRoot!.querySelector('span.select__tags-count');
|
|
341
|
+
expect(overflowCount).to.exist;
|
|
342
|
+
expect(overflowCount!.textContent).to.contain('More');
|
|
337
343
|
});
|
|
338
344
|
|
|
339
345
|
it('V13. showtooltip is what allows a fitted chip to be truncated', async () => {
|
|
@@ -354,7 +360,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
354
360
|
expect(clippedChipCount(el)).to.be.greaterThan(0);
|
|
355
361
|
});
|
|
356
362
|
|
|
357
|
-
it('V8. no showtooltip attribute
|
|
363
|
+
it('V8. no showtooltip attribute renders no chip tooltips', async () => {
|
|
358
364
|
const host = await fixture<HTMLElement>(
|
|
359
365
|
html`<div style="width: 200px">
|
|
360
366
|
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}></nile-virtual-select>
|
|
@@ -365,8 +371,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
365
371
|
|
|
366
372
|
expect(el.showTooltip).to.be.false;
|
|
367
373
|
expect(el.hasAttribute('showtooltip')).to.be.false;
|
|
368
|
-
expect(tips(el).length).to.
|
|
369
|
-
expect(liveTips(el).length).to.equal(0);
|
|
374
|
+
expect(tips(el).length).to.equal(0);
|
|
370
375
|
});
|
|
371
376
|
|
|
372
377
|
it('V9. showtooltip shows the chip tooltip', async () => {
|
|
@@ -411,7 +416,7 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
411
416
|
it('V11. the overflow button label and text never disagree, including first paint', async () => {
|
|
412
417
|
const host = await fixture<HTMLElement>(
|
|
413
418
|
html`<div style="width: 220px">
|
|
414
|
-
<nile-virtual-select multiple .data=${DATA} .value=${LABELS}>
|
|
419
|
+
<nile-virtual-select showtooltip multiple .data=${DATA} .value=${LABELS}>
|
|
415
420
|
</nile-virtual-select>
|
|
416
421
|
</div>`
|
|
417
422
|
);
|
|
@@ -454,4 +459,387 @@ describe('NileVirtualSelect chip fitting behaviour matrix', () => {
|
|
|
454
459
|
expect(el.shadowRoot!.querySelector('button.select__tags-count')).to.not
|
|
455
460
|
.exist;
|
|
456
461
|
});
|
|
462
|
+
|
|
463
|
+
it('G1. default chips are not capped by the 2.0.9 chip styles; showtooltip caps them', async () => {
|
|
464
|
+
const host = await fixture<HTMLElement>(
|
|
465
|
+
html`<div style="width: 480px">
|
|
466
|
+
<nile-virtual-select multiple .data=${DATA} .value=${LABELS.slice(0, 2)}></nile-virtual-select>
|
|
467
|
+
<nile-virtual-select multiple showtooltip .data=${DATA} .value=${LABELS.slice(0, 2)}></nile-virtual-select>
|
|
468
|
+
</div>`
|
|
469
|
+
);
|
|
470
|
+
const [plain, tooltip] = Array.from(
|
|
471
|
+
host.querySelectorAll('nile-virtual-select')
|
|
472
|
+
) as NileVirtualSelect[];
|
|
473
|
+
await settle(plain);
|
|
474
|
+
await settle(tooltip);
|
|
475
|
+
|
|
476
|
+
const [plainChip] = chips(plain);
|
|
477
|
+
const [tooltipChip] = chips(tooltip);
|
|
478
|
+
expect(plainChip, 'plain select rendered no chips').to.exist;
|
|
479
|
+
expect(tooltipChip, 'tooltip select rendered no chips').to.exist;
|
|
480
|
+
expect(getComputedStyle(plainChip).maxWidth).to.equal('none');
|
|
481
|
+
expect(getComputedStyle(tooltipChip).maxWidth).to.not.equal('none');
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
describe('NileVirtualSelect chipMinWidth / chipMaxWidth attributes', () => {
|
|
486
|
+
const LABELS = ['Engineering Operations Team', 'Human Resources Department'];
|
|
487
|
+
const DATA = LABELS.map(label => ({ label, value: label }));
|
|
488
|
+
|
|
489
|
+
async function settle(el: NileVirtualSelect) {
|
|
490
|
+
await el.updateComplete;
|
|
491
|
+
await nextFrame();
|
|
492
|
+
await nextFrame();
|
|
493
|
+
await el.updateComplete;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
const firstChip = (el: NileVirtualSelect) =>
|
|
497
|
+
el.shadowRoot!.querySelector('.select__tags nile-tag') as HTMLElement;
|
|
498
|
+
|
|
499
|
+
it('R4a. defaults are 110 / 180 and are not written to the host', async () => {
|
|
500
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple .data=${DATA}></nile-virtual-select>`);
|
|
501
|
+
await settle(el);
|
|
502
|
+
|
|
503
|
+
expect(el.chipMinWidth).to.equal(110);
|
|
504
|
+
expect(el.chipMaxWidth).to.equal(180);
|
|
505
|
+
expect(el.hasAttribute('chipminwidth')).to.be.false;
|
|
506
|
+
expect(el.hasAttribute('chipmaxwidth')).to.be.false;
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('R4b. attributes still set the properties; properties do not add attributes', async () => {
|
|
510
|
+
const el = await fixture<NileVirtualSelect>(
|
|
511
|
+
html`<nile-virtual-select multiple .data=${DATA} chipminwidth="90" chipmaxwidth="150"></nile-virtual-select>`
|
|
512
|
+
);
|
|
513
|
+
await settle(el);
|
|
514
|
+
expect(el.chipMinWidth).to.equal(90);
|
|
515
|
+
expect(el.chipMaxWidth).to.equal(150);
|
|
516
|
+
|
|
517
|
+
const plain = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple .data=${DATA}></nile-virtual-select>`);
|
|
518
|
+
plain.chipMaxWidth = 140;
|
|
519
|
+
await settle(plain);
|
|
520
|
+
expect(plain.chipMaxWidth).to.equal(140);
|
|
521
|
+
expect(plain.hasAttribute('chipmaxwidth')).to.be.false;
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
it('R4c. showtooltip still caps squeezed chips at chipMaxWidth', async () => {
|
|
525
|
+
const host = await fixture<HTMLElement>(html`<div style="width: 300px">
|
|
526
|
+
<nile-virtual-select multiple showtooltip chipmaxwidth="150" .data=${DATA} .value=${LABELS}></nile-virtual-select>
|
|
527
|
+
</div>`);
|
|
528
|
+
const el = host.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
529
|
+
await settle(el);
|
|
530
|
+
|
|
531
|
+
const chip = firstChip(el);
|
|
532
|
+
expect(chip, 'select rendered no chips').to.exist;
|
|
533
|
+
expect(chip.getBoundingClientRect().width).to.be.within(1, 150.5);
|
|
534
|
+
|
|
535
|
+
el.chipMaxWidth = 120;
|
|
536
|
+
await settle(el);
|
|
537
|
+
await nextFrame();
|
|
538
|
+
expect(firstChip(el).getBoundingClientRect().width).to.be.within(1, 120.5);
|
|
539
|
+
});
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
describe('NileVirtualSelect footer width with and without noWidthSync', () => {
|
|
543
|
+
const DATA = [{ label: 'a', value: 'a' }, { label: 'b', value: 'b' }];
|
|
544
|
+
|
|
545
|
+
async function openSelect(width: number, noWidthSync: boolean, portal = false) {
|
|
546
|
+
const host = await fixture<HTMLElement>(html`<div style="width: ${width}px">
|
|
547
|
+
<nile-virtual-select multiple .noWidthSync=${noWidthSync} .portal=${portal} .data=${DATA} .value=${['a']}></nile-virtual-select>
|
|
548
|
+
</div>`);
|
|
549
|
+
const el = host.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
550
|
+
await el.updateComplete;
|
|
551
|
+
el.open = true;
|
|
552
|
+
const listbox = () =>
|
|
553
|
+
(portal
|
|
554
|
+
? document.body.querySelector('.nile-virtual-select-portal-append #listbox')
|
|
555
|
+
: el.shadowRoot!.querySelector('#listbox')) as HTMLElement | null;
|
|
556
|
+
await waitUntil(() => (listbox()?.getBoundingClientRect().width ?? 0) > 0, 'listbox never opened');
|
|
557
|
+
await nextFrame();
|
|
558
|
+
const box = listbox()!;
|
|
559
|
+
const footer = box.querySelector('.select__footer') as HTMLElement;
|
|
560
|
+
const combobox = el.shadowRoot!.querySelector('[part~="combobox"]') as HTMLElement;
|
|
561
|
+
return { el, listbox: box, footer, combobox };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const labelShown = (footer: HTMLElement, selector: string) =>
|
|
565
|
+
getComputedStyle(footer.querySelector(selector) as HTMLElement).display !== 'none';
|
|
566
|
+
|
|
567
|
+
before(() => document.documentElement.style.setProperty('--nile-display-flex', 'flex'));
|
|
568
|
+
after(() => document.documentElement.style.removeProperty('--nile-display-flex'));
|
|
569
|
+
|
|
570
|
+
afterEach(async () => {
|
|
571
|
+
document.body.querySelectorAll('.nile-virtual-select-portal-append').forEach(n => n.remove());
|
|
572
|
+
document.head.querySelectorAll('style[id^="nile-virtual-select-styles-"]').forEach(n => n.remove());
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
for (const width of [120, 400]) {
|
|
576
|
+
it(`R3a. noWidthSync + multiple at ${width}px does not collapse and shows the whole footer`, async () => {
|
|
577
|
+
const { listbox, footer } = await openSelect(width, true);
|
|
578
|
+
|
|
579
|
+
expect(listbox.offsetWidth).to.be.greaterThan(60);
|
|
580
|
+
expect(listbox.offsetWidth).to.be.at.least(footer.offsetWidth);
|
|
581
|
+
expect(footer.scrollWidth).to.be.at.most(footer.clientWidth + 1);
|
|
582
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.true;
|
|
583
|
+
expect(labelShown(footer, '.select__footer-action .select__footer-label')).to.be.true;
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
it('R3b. width sync at normal width keeps the dropdown as wide as the trigger', async () => {
|
|
588
|
+
const { listbox, combobox, footer } = await openSelect(400, false);
|
|
589
|
+
|
|
590
|
+
expect(Math.abs(listbox.offsetWidth - combobox.offsetWidth)).to.be.below(2);
|
|
591
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.true;
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
it('R3c. width sync at a narrow width still collapses the footer labels (container queries kept)', async () => {
|
|
595
|
+
const { listbox, combobox, footer } = await openSelect(130, false);
|
|
596
|
+
|
|
597
|
+
expect(Math.abs(listbox.offsetWidth - combobox.offsetWidth)).to.be.below(2);
|
|
598
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.false;
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
for (const noWidthSync of [false, true]) {
|
|
602
|
+
it(`R3d. portal (noWidthSync=${noWidthSync}) pins the width and keeps the narrow-footer queries`, async () => {
|
|
603
|
+
const { el, listbox, combobox, footer } = await openSelect(130, noWidthSync, true);
|
|
604
|
+
|
|
605
|
+
expect(listbox.offsetWidth).to.be.within(combobox.offsetWidth - 8, combobox.offsetWidth);
|
|
606
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.false;
|
|
607
|
+
|
|
608
|
+
el.open = false;
|
|
609
|
+
await waitUntil(() => !document.body.querySelector('.nile-virtual-select-portal-append'), 'portal not cleaned up');
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
});
|
|
613
|
+
|
|
614
|
+
describe('NileVirtualSelect showtooltip chip layout taken while hidden', () => {
|
|
615
|
+
const LABELS = ['Engineering Operations Team', 'Human Resources Department'];
|
|
616
|
+
const DATA = LABELS.map(label => ({ label, value: label }));
|
|
617
|
+
|
|
618
|
+
async function frames(n = 3) {
|
|
619
|
+
for (let i = 0; i < n; i++) await nextFrame();
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const chipsOf = (el: NileVirtualSelect) =>
|
|
623
|
+
Array.from(el.shadowRoot!.querySelectorAll('.select__tags nile-tag')) as HTMLElement[];
|
|
624
|
+
|
|
625
|
+
async function hiddenSelect(showTooltip: boolean) {
|
|
626
|
+
const host = await fixture<HTMLElement>(html`<div style="display: none; width: 600px">
|
|
627
|
+
<nile-virtual-select multiple ?showtooltip=${showTooltip} .data=${DATA} .value=${LABELS}></nile-virtual-select>
|
|
628
|
+
</div>`);
|
|
629
|
+
const el = host.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
630
|
+
await el.updateComplete;
|
|
631
|
+
await frames();
|
|
632
|
+
await el.updateComplete;
|
|
633
|
+
return { host, el };
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
it('R5a. showtooltip chips measured while hidden get real widths once shown', async () => {
|
|
637
|
+
const { host, el } = await hiddenSelect(true);
|
|
638
|
+
|
|
639
|
+
host.style.display = 'block';
|
|
640
|
+
await frames(4);
|
|
641
|
+
await el.updateComplete;
|
|
642
|
+
|
|
643
|
+
const chips = chipsOf(el);
|
|
644
|
+
expect(chips.length).to.equal(2);
|
|
645
|
+
for (const chip of chips) {
|
|
646
|
+
expect(chip.style.width).to.not.equal('0px');
|
|
647
|
+
expect(chip.offsetWidth).to.be.greaterThan(50);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
it('R5c. without showtooltip a hidden-then-shown select is unaffected (no inline widths)', async () => {
|
|
652
|
+
const { host, el } = await hiddenSelect(false);
|
|
653
|
+
|
|
654
|
+
host.style.display = 'block';
|
|
655
|
+
await frames(4);
|
|
656
|
+
|
|
657
|
+
for (const chip of chipsOf(el)) {
|
|
658
|
+
expect(chip.style.width).to.equal('');
|
|
659
|
+
expect(chip.offsetWidth).to.be.greaterThan(50);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
describe('NileVirtualSelect 2.0.8 compatibility matrix', () => {
|
|
665
|
+
const DATA = ['a', 'b', 'c'].map(v => ({ label: `L${v}`, value: v }));
|
|
666
|
+
|
|
667
|
+
async function settle(el: NileVirtualSelect) {
|
|
668
|
+
await el.updateComplete;
|
|
669
|
+
await nextFrame();
|
|
670
|
+
await nextFrame();
|
|
671
|
+
await el.updateComplete;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
const chipCount = (el: NileVirtualSelect) => el.shadowRoot!.querySelectorAll('.select__tags nile-tag').length;
|
|
675
|
+
|
|
676
|
+
function record(el: EventTarget, names: string[]) {
|
|
677
|
+
const seen: string[] = [];
|
|
678
|
+
names.forEach(name => el.addEventListener(name, () => seen.push(name)));
|
|
679
|
+
return seen;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
it('CV1. a value set before data arrives is kept, and survives the data being replaced', async () => {
|
|
683
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple .value=${['b']}></nile-virtual-select>`);
|
|
684
|
+
await settle(el);
|
|
685
|
+
el.data = DATA;
|
|
686
|
+
await settle(el);
|
|
687
|
+
expect(el.value).to.deep.equal(['b']);
|
|
688
|
+
expect(chipCount(el)).to.equal(1);
|
|
689
|
+
|
|
690
|
+
el.data = [...DATA];
|
|
691
|
+
await settle(el);
|
|
692
|
+
expect(el.value).to.deep.equal(['b']);
|
|
693
|
+
expect(chipCount(el)).to.equal(1);
|
|
694
|
+
});
|
|
695
|
+
|
|
696
|
+
it('CV2. programmatic value updates the chips', async () => {
|
|
697
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple .data=${DATA}></nile-virtual-select>`);
|
|
698
|
+
await settle(el);
|
|
699
|
+
el.value = ['a', 'c'];
|
|
700
|
+
await settle(el);
|
|
701
|
+
expect(chipCount(el)).to.equal(2);
|
|
702
|
+
el.value = [];
|
|
703
|
+
await settle(el);
|
|
704
|
+
expect(chipCount(el)).to.equal(0);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it('CV3. removing a chip updates the value and emits tag-remove', async () => {
|
|
708
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple .data=${DATA} .value=${['a', 'b']}></nile-virtual-select>`);
|
|
709
|
+
await settle(el);
|
|
710
|
+
const seen = record(el, ['nile-tag-remove']);
|
|
711
|
+
|
|
712
|
+
const chip = el.shadowRoot!.querySelector('.select__tags nile-tag') as HTMLElement;
|
|
713
|
+
expect(chip, 'select rendered no chips').to.exist;
|
|
714
|
+
chip.dispatchEvent(new CustomEvent('nile-remove', { bubbles: true, composed: true }));
|
|
715
|
+
await settle(el);
|
|
716
|
+
|
|
717
|
+
expect(el.value).to.deep.equal(['b']);
|
|
718
|
+
expect(seen).to.deep.equal(['nile-tag-remove']);
|
|
719
|
+
});
|
|
720
|
+
|
|
721
|
+
it('CV4. the clear button empties the value and emits nile-clear', async () => {
|
|
722
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple clearable .data=${DATA} .value=${['a', 'b']}></nile-virtual-select>`);
|
|
723
|
+
await settle(el);
|
|
724
|
+
const seen = record(el, ['nile-clear']);
|
|
725
|
+
|
|
726
|
+
const clear = el.shadowRoot!.querySelector('[part~="clear-button"]') as HTMLElement;
|
|
727
|
+
expect(clear, 'clear button rendered').to.exist;
|
|
728
|
+
clear.click();
|
|
729
|
+
await settle(el);
|
|
730
|
+
|
|
731
|
+
expect(el.value).to.deep.equal([]);
|
|
732
|
+
expect(chipCount(el)).to.equal(0);
|
|
733
|
+
expect(seen).to.include('nile-clear');
|
|
734
|
+
});
|
|
735
|
+
|
|
736
|
+
it('CV5. form reset restores the value attribute', async () => {
|
|
737
|
+
const form = await fixture<HTMLFormElement>(html`<form>
|
|
738
|
+
<nile-virtual-select name="s" multiple .data=${DATA} value="a"></nile-virtual-select>
|
|
739
|
+
</form>`);
|
|
740
|
+
const el = form.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
741
|
+
await settle(el);
|
|
742
|
+
|
|
743
|
+
el.value = ['c'];
|
|
744
|
+
await settle(el);
|
|
745
|
+
form.reset();
|
|
746
|
+
await settle(el);
|
|
747
|
+
|
|
748
|
+
expect(el.value).to.deep.equal(['a']);
|
|
749
|
+
});
|
|
750
|
+
|
|
751
|
+
it('CV6. portal + search: typing in the portalled search keeps the value', async () => {
|
|
752
|
+
const el = await fixture<NileVirtualSelect>(html`<nile-virtual-select multiple portal searchEnabled .data=${DATA} .value=${['a']}></nile-virtual-select>`);
|
|
753
|
+
await settle(el);
|
|
754
|
+
el.open = true;
|
|
755
|
+
await waitUntil(() => document.body.querySelector('.nile-virtual-select-portal-append .select__search nile-input'), 'portal search never rendered');
|
|
756
|
+
const search = document.body.querySelector('.nile-virtual-select-portal-append .select__search nile-input') as any;
|
|
757
|
+
await search.updateComplete;
|
|
758
|
+
|
|
759
|
+
const input = search.shadowRoot.querySelector('input') as HTMLInputElement;
|
|
760
|
+
input.value = 'Lb';
|
|
761
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
762
|
+
await settle(el);
|
|
763
|
+
expect(el.value).to.deep.equal(['a']);
|
|
764
|
+
|
|
765
|
+
el.open = false;
|
|
766
|
+
await waitUntil(() => !document.body.querySelector('.nile-virtual-select-portal-append'), 'portal not cleaned up');
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
it('CV7. reconnecting without showtooltip keeps value and chips', async () => {
|
|
770
|
+
const host = await fixture<HTMLElement>(html`<div style="width: 500px">
|
|
771
|
+
<nile-virtual-select multiple .data=${DATA} .value=${['a', 'b']}></nile-virtual-select>
|
|
772
|
+
</div>`);
|
|
773
|
+
const el = host.querySelector('nile-virtual-select') as NileVirtualSelect;
|
|
774
|
+
await settle(el);
|
|
775
|
+
|
|
776
|
+
el.remove();
|
|
777
|
+
host.appendChild(el);
|
|
778
|
+
await settle(el);
|
|
779
|
+
|
|
780
|
+
expect(el.value).to.deep.equal(['a', 'b']);
|
|
781
|
+
expect(chipCount(el)).to.equal(2);
|
|
782
|
+
});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
describe('NileVirtualSelect showtooltip tooltip on sub-pixel truncation', () => {
|
|
786
|
+
const A = 'Engineering Operations';
|
|
787
|
+
const B = 'Human Resources Department And Payroll';
|
|
788
|
+
|
|
789
|
+
async function frames(n = 5) {
|
|
790
|
+
for (let i = 0; i < n; i++) await nextFrame();
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
async function mount(width: number, chipMaxWidth: number) {
|
|
794
|
+
const host = await fixture<HTMLElement>(html`<div style="width: ${width}px">
|
|
795
|
+
<nile-virtual-select multiple showtooltip chipminwidth="40" chipmaxwidth=${chipMaxWidth} .data=${[A, B].map(v => ({ label: v, value: v }))} .value=${[A, B]}></nile-virtual-select>
|
|
796
|
+
</div>`);
|
|
797
|
+
const el = host.querySelector('nile-virtual-select') as any;
|
|
798
|
+
await el.updateComplete;
|
|
799
|
+
await frames();
|
|
800
|
+
await el.updateComplete;
|
|
801
|
+
await frames(2);
|
|
802
|
+
return el;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function visiblyCut(chip: HTMLElement) {
|
|
806
|
+
const content = chip.shadowRoot!.querySelector('[part="content"]') as HTMLSlotElement;
|
|
807
|
+
const text = content.assignedNodes({ flatten: true }).find(n => n.textContent?.trim())!;
|
|
808
|
+
const range = document.createRange();
|
|
809
|
+
range.selectNodeContents(text);
|
|
810
|
+
const over = range.getBoundingClientRect().right - content.getBoundingClientRect().right;
|
|
811
|
+
return { cut: over > 1 / 64, over, pxGap: content.scrollWidth - content.clientWidth };
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function chipsAndTips(el: any) {
|
|
815
|
+
const chips = Array.from(el.shadowRoot.querySelectorAll('.select__tags nile-tag')) as HTMLElement[];
|
|
816
|
+
const tips = Array.from(el.shadowRoot.querySelectorAll('.select__tags nile-lite-tooltip')) as HTMLElement[];
|
|
817
|
+
return chips
|
|
818
|
+
.map((chip, i) => ({ chip, tipOn: !!tips[i] && !tips[i].hasAttribute('disabled') }))
|
|
819
|
+
.filter(({ chip }) => chip.style.display !== 'none');
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
it('TT1. every visibly cut chip has its tooltip, every uncut chip has none (sweep across the edge)', async () => {
|
|
823
|
+
const wide = await mount(900, 400);
|
|
824
|
+
const natural = chipsAndTips(wide)[0].chip.offsetWidth;
|
|
825
|
+
|
|
826
|
+
let subPixelCuts = 0;
|
|
827
|
+
for (let max = natural - 4; max <= natural + 1; max += 0.25) {
|
|
828
|
+
const el = await mount(330, max);
|
|
829
|
+
for (const { chip, tipOn } of chipsAndTips(el)) {
|
|
830
|
+
const { cut, pxGap } = visiblyCut(chip);
|
|
831
|
+
expect(tipOn, `chipMaxWidth ${max}: tooltip ${tipOn} but cut ${cut}`).to.equal(cut);
|
|
832
|
+
if (cut && pxGap <= 1) subPixelCuts++;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
expect(subPixelCuts, 'sweep reached the sub-pixel truncation window').to.be.greaterThan(0);
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
it('TT2. chips at their natural width get no tooltip', async () => {
|
|
839
|
+
const el = await mount(900, 400);
|
|
840
|
+
for (const { chip, tipOn } of chipsAndTips(el)) {
|
|
841
|
+
expect(visiblyCut(chip).cut).to.be.false;
|
|
842
|
+
expect(tipOn).to.be.false;
|
|
843
|
+
}
|
|
844
|
+
});
|
|
457
845
|
});
|