@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,6 +1,7 @@
|
|
|
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-select';
|
|
3
3
|
import '../nile-option/nile-option';
|
|
4
|
+
import '../nile-input/nile-input';
|
|
4
5
|
describe('NileSelect', () => {
|
|
5
6
|
it('1. renders', async () => { const el = await fixture(html `<nile-select></nile-select>`); expect(el).to.exist; });
|
|
6
7
|
it('2. shadow root', async () => { const el = await fixture(html `<nile-select></nile-select>`); expect(el.shadowRoot).to.not.be.null; });
|
|
@@ -132,7 +133,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
132
133
|
}
|
|
133
134
|
it('M1. multiple + no max-options-visible + autoResize=false renders fitted chips', async () => {
|
|
134
135
|
const host = await fixture(html `<div style="width: 320px">
|
|
135
|
-
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
136
|
+
<nile-select showtooltip multiple .value=${LABELS}>${options()}</nile-select>
|
|
136
137
|
</div>`);
|
|
137
138
|
const el = host.querySelector('nile-select');
|
|
138
139
|
await settle(el);
|
|
@@ -143,7 +144,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
143
144
|
});
|
|
144
145
|
it('M2. multiple + no max-options-visible + autoResize=true refits when the container resizes', async () => {
|
|
145
146
|
const host = await fixture(html `<div style="width: 480px">
|
|
146
|
-
<nile-select multiple autoresize .value=${LABELS}>${options()}</nile-select>
|
|
147
|
+
<nile-select showtooltip multiple autoresize .value=${LABELS}>${options()}</nile-select>
|
|
147
148
|
</div>`);
|
|
148
149
|
const el = host.querySelector('nile-select');
|
|
149
150
|
await settle(el);
|
|
@@ -157,7 +158,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
157
158
|
});
|
|
158
159
|
it('M3. explicit max-options-visible switches to fixed-count layout', async () => {
|
|
159
160
|
const host = await fixture(html `<div style="width: 320px">
|
|
160
|
-
<nile-select multiple max-options-visible="2" .value=${LABELS}>
|
|
161
|
+
<nile-select showtooltip multiple max-options-visible="2" .value=${LABELS}>
|
|
161
162
|
${options()}
|
|
162
163
|
</nile-select>
|
|
163
164
|
</div>`);
|
|
@@ -170,7 +171,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
170
171
|
});
|
|
171
172
|
it('M3b. max-options-visible set as a property also switches to fixed-count layout', async () => {
|
|
172
173
|
const host = await fixture(html `<div style="width: 320px">
|
|
173
|
-
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
174
|
+
<nile-select showtooltip multiple .value=${LABELS}>${options()}</nile-select>
|
|
174
175
|
</div>`);
|
|
175
176
|
const el = host.querySelector('nile-select');
|
|
176
177
|
await settle(el);
|
|
@@ -182,7 +183,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
182
183
|
});
|
|
183
184
|
it('M3c. the default max-options-visible is still 3 and reading it does not opt out', async () => {
|
|
184
185
|
const host = await fixture(html `<div style="width: 320px">
|
|
185
|
-
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
186
|
+
<nile-select showtooltip multiple .value=${LABELS}>${options()}</nile-select>
|
|
186
187
|
</div>`);
|
|
187
188
|
const el = host.querySelector('nile-select');
|
|
188
189
|
await settle(el);
|
|
@@ -201,7 +202,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
201
202
|
});
|
|
202
203
|
it('M8. virtual-scroll mode keeps fitted chips when max-options-visible was never set', async () => {
|
|
203
204
|
const host = await fixture(html `<div style="width: 320px">
|
|
204
|
-
<nile-select
|
|
205
|
+
<nile-select showtooltip
|
|
205
206
|
multiple
|
|
206
207
|
enable-virtual-scroll
|
|
207
208
|
.enableVirtualScroll=${true}
|
|
@@ -216,11 +217,12 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
216
217
|
await inner.updateComplete;
|
|
217
218
|
await nextFrame();
|
|
218
219
|
const innerTags = inner.shadowRoot.querySelector('.select__tags');
|
|
220
|
+
expect(innerTags, 'inner .select__tags should render').to.exist;
|
|
219
221
|
expect(innerTags.classList.contains('select__tags--fitted')).to.be.true;
|
|
220
222
|
});
|
|
221
223
|
it('M9. virtual-scroll mode forwards an explicit max-options-visible', async () => {
|
|
222
224
|
const host = await fixture(html `<div style="width: 320px">
|
|
223
|
-
<nile-select
|
|
225
|
+
<nile-select showtooltip
|
|
224
226
|
multiple
|
|
225
227
|
max-options-visible="2"
|
|
226
228
|
.enableVirtualScroll=${true}
|
|
@@ -251,7 +253,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
251
253
|
});
|
|
252
254
|
it('M11. toggling max-options-visible does not leak resize observers or lose the observed container', async () => {
|
|
253
255
|
const host = await fixture(html `<div style="width: 480px">
|
|
254
|
-
<nile-select multiple autoresize .value=${LABELS}>
|
|
256
|
+
<nile-select showtooltip multiple autoresize .value=${LABELS}>
|
|
255
257
|
${options()}
|
|
256
258
|
</nile-select>
|
|
257
259
|
</div>`);
|
|
@@ -288,7 +290,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
288
290
|
function liveTips(el) {
|
|
289
291
|
return tips(el).filter(tip => !tip.hasAttribute('disabled'));
|
|
290
292
|
}
|
|
291
|
-
it('M12. no showtooltip attribute
|
|
293
|
+
it('M12. no showtooltip attribute renders no chip tooltips', async () => {
|
|
292
294
|
const host = await fixture(html `<div style="width: 200px">
|
|
293
295
|
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
294
296
|
</div>`);
|
|
@@ -296,8 +298,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
296
298
|
await settle(el);
|
|
297
299
|
expect(el.showTooltip).to.be.false;
|
|
298
300
|
expect(el.hasAttribute('showtooltip')).to.be.false;
|
|
299
|
-
expect(tips(el).length).to.
|
|
300
|
-
expect(liveTips(el).length).to.equal(0);
|
|
301
|
+
expect(tips(el).length).to.equal(0);
|
|
301
302
|
});
|
|
302
303
|
it('M13. showtooltip shows the chip tooltip', async () => {
|
|
303
304
|
const host = await fixture(html `<div style="width: 200px">
|
|
@@ -324,16 +325,21 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
324
325
|
return content.scrollWidth > content.clientWidth + 1;
|
|
325
326
|
}).length;
|
|
326
327
|
}
|
|
327
|
-
it('M16. without showtooltip
|
|
328
|
+
it('M16. without showtooltip the pre-2.0.9 chip row is used', async () => {
|
|
328
329
|
const host = await fixture(html `<div style="width: 480px">
|
|
329
330
|
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
330
331
|
</div>`);
|
|
331
332
|
const el = host.querySelector('nile-select');
|
|
332
333
|
await settle(el);
|
|
334
|
+
await aTimeout(20);
|
|
335
|
+
await settle(el);
|
|
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
|
it('M17. showtooltip is what allows a fitted chip to be truncated', async () => {
|
|
339
345
|
const host = await fixture(html `<div style="width: 480px">
|
|
@@ -366,7 +372,7 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
366
372
|
});
|
|
367
373
|
it('M15. the overflow button label and text never disagree, including first paint', async () => {
|
|
368
374
|
const host = await fixture(html `<div style="width: 220px">
|
|
369
|
-
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
375
|
+
<nile-select showtooltip multiple .value=${LABELS}>${options()}</nile-select>
|
|
370
376
|
</div>`);
|
|
371
377
|
const el = host.querySelector('nile-select');
|
|
372
378
|
// Before the layout frame runs, no button may claim a count it cannot back up.
|
|
@@ -445,5 +451,878 @@ describe('NileSelect chip fitting behaviour matrix', () => {
|
|
|
445
451
|
expect(inner.showTooltip).to.be.true;
|
|
446
452
|
expect(inner.chipMaxWidth).to.equal(240);
|
|
447
453
|
});
|
|
454
|
+
it('G1. default chips are not capped or squeezed by the 2.0.9 chip styles', async () => {
|
|
455
|
+
const host = await fixture(html `<div style="width: 480px">
|
|
456
|
+
<nile-select multiple .value=${LABELS.slice(0, 2)}>${options()}</nile-select>
|
|
457
|
+
</div>`);
|
|
458
|
+
const el = host.querySelector('nile-select');
|
|
459
|
+
await settle(el);
|
|
460
|
+
const [chip] = chips(el);
|
|
461
|
+
expect(chip, 'select rendered no chips').to.exist;
|
|
462
|
+
expect(getComputedStyle(chip).maxWidth).to.equal('none');
|
|
463
|
+
expect(getComputedStyle(chip).minWidth).to.not.equal('0px');
|
|
464
|
+
});
|
|
465
|
+
it('G2. showtooltip caps chips at the chip max width', async () => {
|
|
466
|
+
const host = await fixture(html `<div style="width: 480px">
|
|
467
|
+
<nile-select multiple showtooltip .value=${LABELS.slice(0, 2)}>${options()}</nile-select>
|
|
468
|
+
</div>`);
|
|
469
|
+
const el = host.querySelector('nile-select');
|
|
470
|
+
await settle(el);
|
|
471
|
+
const [firstChip] = chips(el);
|
|
472
|
+
expect(firstChip, 'select rendered no chips').to.exist;
|
|
473
|
+
expect(getComputedStyle(firstChip).maxWidth).to.not.equal('none');
|
|
474
|
+
});
|
|
475
|
+
it('G3. autoresize without showtooltip refits with the 2.0.8 width fit', async () => {
|
|
476
|
+
const host = await fixture(html `<div style="width: 1200px">
|
|
477
|
+
<nile-select multiple autoresize .value=${LABELS}>${options()}</nile-select>
|
|
478
|
+
</div>`);
|
|
479
|
+
const el = host.querySelector('nile-select');
|
|
480
|
+
await settle(el);
|
|
481
|
+
await aTimeout(20);
|
|
482
|
+
const wide = el.maxOptionsVisible;
|
|
483
|
+
host.style.width = '300px';
|
|
484
|
+
await aTimeout(200);
|
|
485
|
+
await settle(el);
|
|
486
|
+
expect(tagsRoot(el).classList.contains('select__tags--fitted')).to.be.false;
|
|
487
|
+
expect(wide).to.equal(0);
|
|
488
|
+
expect(el.maxOptionsVisible).to.be.greaterThan(0);
|
|
489
|
+
expect(el.shadowRoot.querySelector('span.select__tags-count')).to.exist;
|
|
490
|
+
});
|
|
491
|
+
it('G4. toggling showTooltip switches between the fitted and the 2.0.8 chip row', async () => {
|
|
492
|
+
const host = await fixture(html `<div style="width: 320px">
|
|
493
|
+
<nile-select multiple .value=${LABELS}>${options()}</nile-select>
|
|
494
|
+
</div>`);
|
|
495
|
+
const el = host.querySelector('nile-select');
|
|
496
|
+
await settle(el);
|
|
497
|
+
expect(tagsRoot(el).classList.contains('select__tags--fitted')).to.be.false;
|
|
498
|
+
el.showTooltip = true;
|
|
499
|
+
await settle(el);
|
|
500
|
+
expect(tagsRoot(el).classList.contains('select__tags--fitted')).to.be.true;
|
|
501
|
+
expect(el.shadowRoot.querySelector('button.select__tags-count')).to.exist;
|
|
502
|
+
el.showTooltip = false;
|
|
503
|
+
await settle(el);
|
|
504
|
+
await aTimeout(20);
|
|
505
|
+
await settle(el);
|
|
506
|
+
expect(tagsRoot(el).classList.contains('select__tags--fitted')).to.be.false;
|
|
507
|
+
expect(el.shadowRoot.querySelector('button.select__tags-count')).to.not.exist;
|
|
508
|
+
});
|
|
509
|
+
});
|
|
510
|
+
describe('NileSelect enableVirtualScroll value sync', () => {
|
|
511
|
+
const ROLES = ['Administrators', 'Users', 'Guests', 'Print Operators'];
|
|
512
|
+
const DATA = ROLES.map(role => ({ label: role, value: role }));
|
|
513
|
+
async function virtualSelect(tpl = html `<nile-select
|
|
514
|
+
multiple
|
|
515
|
+
.enableVirtualScroll=${true}
|
|
516
|
+
.data=${DATA}
|
|
517
|
+
.value=${[]}
|
|
518
|
+
></nile-select>`) {
|
|
519
|
+
const el = await fixture(tpl);
|
|
520
|
+
await el.updateComplete;
|
|
521
|
+
const inner = el.shadowRoot.querySelector('nile-virtual-select');
|
|
522
|
+
await inner.updateComplete;
|
|
523
|
+
return { el, inner };
|
|
524
|
+
}
|
|
525
|
+
async function settle(el, inner) {
|
|
526
|
+
await inner.updateComplete;
|
|
527
|
+
await el.updateComplete;
|
|
528
|
+
await inner.updateComplete;
|
|
529
|
+
}
|
|
530
|
+
it('V1. picking an option updates the wrapper value', async () => {
|
|
531
|
+
const { el, inner } = await virtualSelect();
|
|
532
|
+
inner.handleOptionSelection({ value: 'Administrators' });
|
|
533
|
+
await settle(el, inner);
|
|
534
|
+
expect(el.value).to.deep.equal(['Administrators']);
|
|
535
|
+
});
|
|
536
|
+
it('V2. the first pick survives a wrapper re-render', async () => {
|
|
537
|
+
const { el, inner } = await virtualSelect();
|
|
538
|
+
inner.handleOptionSelection({ value: 'Administrators' });
|
|
539
|
+
await settle(el, inner);
|
|
540
|
+
el.requestUpdate();
|
|
541
|
+
await settle(el, inner);
|
|
542
|
+
expect(inner.value).to.deep.equal(['Administrators']);
|
|
543
|
+
expect(el.value).to.deep.equal(['Administrators']);
|
|
544
|
+
});
|
|
545
|
+
it('V3. a removed chip stays removed after a wrapper re-render', async () => {
|
|
546
|
+
const { el, inner } = await virtualSelect(html `<nile-select
|
|
547
|
+
multiple
|
|
548
|
+
.enableVirtualScroll=${true}
|
|
549
|
+
.data=${DATA}
|
|
550
|
+
.value=${['Administrators', 'Users', 'Guests']}
|
|
551
|
+
></nile-select>`);
|
|
552
|
+
inner.handleTagRemove(new CustomEvent('nile-remove'), { value: 'Administrators' });
|
|
553
|
+
await settle(el, inner);
|
|
554
|
+
inner.handleTagRemove(new CustomEvent('nile-remove'), { value: 'Users' });
|
|
555
|
+
await settle(el, inner);
|
|
556
|
+
el.requestUpdate();
|
|
557
|
+
await settle(el, inner);
|
|
558
|
+
expect(el.value).to.deep.equal(['Guests']);
|
|
559
|
+
expect(inner.value).to.deep.equal(['Guests']);
|
|
560
|
+
});
|
|
561
|
+
it('V4. nile-change listeners on the wrapper already see the new value', async () => {
|
|
562
|
+
const { el, inner } = await virtualSelect();
|
|
563
|
+
const seen = [];
|
|
564
|
+
el.addEventListener('nile-change', () => seen.push(el.value));
|
|
565
|
+
inner.handleOptionSelection({ value: 'Guests' });
|
|
566
|
+
await settle(el, inner);
|
|
567
|
+
expect(seen).to.deep.equal([['Guests']]);
|
|
568
|
+
});
|
|
569
|
+
it('V5. single-select virtual mode keeps the wrapper value in sync', async () => {
|
|
570
|
+
const { el, inner } = await virtualSelect(html `<nile-select
|
|
571
|
+
.enableVirtualScroll=${true}
|
|
572
|
+
.data=${DATA}
|
|
573
|
+
></nile-select>`);
|
|
574
|
+
inner.handleOptionSelection({ value: 'Users' });
|
|
575
|
+
await settle(el, inner);
|
|
576
|
+
el.requestUpdate();
|
|
577
|
+
await settle(el, inner);
|
|
578
|
+
expect(el.value).to.equal('Users');
|
|
579
|
+
expect(inner.value).to.equal('Users');
|
|
580
|
+
});
|
|
581
|
+
it('V6. resizing the host does not re-render the wrapper without autoResize', async () => {
|
|
582
|
+
const host = await fixture(html `<div style="width: 400px">
|
|
583
|
+
<nile-select multiple .enableVirtualScroll=${true} .data=${DATA}></nile-select>
|
|
584
|
+
</div>`);
|
|
585
|
+
const el = host.querySelector('nile-select');
|
|
586
|
+
await el.updateComplete;
|
|
587
|
+
await nextFrame();
|
|
588
|
+
await nextFrame();
|
|
589
|
+
let updates = 0;
|
|
590
|
+
const update = el.update;
|
|
591
|
+
el.update = function (changed) {
|
|
592
|
+
updates++;
|
|
593
|
+
return update.call(this, changed);
|
|
594
|
+
};
|
|
595
|
+
host.style.width = '250px';
|
|
596
|
+
await nextFrame();
|
|
597
|
+
await nextFrame();
|
|
598
|
+
await aTimeout(50);
|
|
599
|
+
expect(updates).to.equal(0);
|
|
600
|
+
});
|
|
601
|
+
});
|
|
602
|
+
describe('NileSelect enableVirtualScroll + searchEnabled value sync', () => {
|
|
603
|
+
const ROLES = ['Administrators', 'Users', 'Guests', 'Print Operators'];
|
|
604
|
+
const DATA = ROLES.map(role => ({ label: role, value: role }));
|
|
605
|
+
async function settle(el, inner) {
|
|
606
|
+
await inner.updateComplete;
|
|
607
|
+
await el.updateComplete;
|
|
608
|
+
await inner.updateComplete;
|
|
609
|
+
await nextFrame();
|
|
610
|
+
await el.updateComplete;
|
|
611
|
+
await inner.updateComplete;
|
|
612
|
+
}
|
|
613
|
+
async function searchableVirtual({ multiple = true, portal = false, value = ['Users'] } = {}) {
|
|
614
|
+
const el = await fixture(html `<nile-select
|
|
615
|
+
?multiple=${multiple}
|
|
616
|
+
.searchEnabled=${true}
|
|
617
|
+
.enableVirtualScroll=${true}
|
|
618
|
+
.portal=${portal}
|
|
619
|
+
.data=${DATA}
|
|
620
|
+
.value=${value}
|
|
621
|
+
></nile-select>`);
|
|
622
|
+
await el.updateComplete;
|
|
623
|
+
const inner = el.shadowRoot.querySelector('nile-virtual-select');
|
|
624
|
+
await inner.updateComplete;
|
|
625
|
+
inner.open = true;
|
|
626
|
+
await settle(el, inner);
|
|
627
|
+
if (portal) {
|
|
628
|
+
await waitUntil(() => document.body.querySelector('.nile-virtual-select-portal-append #listbox'), 'listbox never portalled');
|
|
629
|
+
}
|
|
630
|
+
await waitUntil(() => searchInput(inner)?.shadowRoot?.querySelector('input'), 'search box never rendered');
|
|
631
|
+
return { el, inner };
|
|
632
|
+
}
|
|
633
|
+
function searchInput(inner) {
|
|
634
|
+
const inShadow = inner.shadowRoot.querySelector('.select__search nile-input');
|
|
635
|
+
const inPortal = document.body.querySelector('.nile-virtual-select-portal-append .select__search nile-input');
|
|
636
|
+
return (inShadow ?? inPortal);
|
|
637
|
+
}
|
|
638
|
+
async function typeSearch(el, inner, text) {
|
|
639
|
+
const input = searchInput(inner).shadowRoot.querySelector('input');
|
|
640
|
+
input.value = text;
|
|
641
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
642
|
+
await settle(el, inner);
|
|
643
|
+
}
|
|
644
|
+
afterEach(() => {
|
|
645
|
+
document.body.querySelectorAll('.nile-virtual-select-portal-append').forEach(n => n.remove());
|
|
646
|
+
});
|
|
647
|
+
it('R1a. typing in the search box keeps the selected values (multiple)', async () => {
|
|
648
|
+
const { el, inner } = await searchableVirtual();
|
|
649
|
+
expect(searchInput(inner), 'search box rendered').to.exist;
|
|
650
|
+
await typeSearch(el, inner, 'Adm');
|
|
651
|
+
expect(el.value).to.deep.equal(['Users']);
|
|
652
|
+
expect(inner.value).to.deep.equal(['Users']);
|
|
653
|
+
});
|
|
654
|
+
it('R1b. typing in the search box keeps the selected value (single)', async () => {
|
|
655
|
+
const { el, inner } = await searchableVirtual({ multiple: false, value: 'Users' });
|
|
656
|
+
await typeSearch(el, inner, 'zz');
|
|
657
|
+
expect(el.value).to.equal('Users');
|
|
658
|
+
expect(inner.value).to.equal('Users');
|
|
659
|
+
});
|
|
660
|
+
it('R1c. clearing the search text does not clear the selection', async () => {
|
|
661
|
+
const { el, inner } = await searchableVirtual({ value: ['Users', 'Guests'] });
|
|
662
|
+
await typeSearch(el, inner, 'Gu');
|
|
663
|
+
await typeSearch(el, inner, '');
|
|
664
|
+
expect(el.value).to.deep.equal(['Users', 'Guests']);
|
|
665
|
+
await typeSearch(el, inner, 'Gu');
|
|
666
|
+
const clear = searchInput(inner).shadowRoot.querySelector('.input__clear');
|
|
667
|
+
expect(clear, 'search clear button rendered').to.exist;
|
|
668
|
+
clear.click();
|
|
669
|
+
await settle(el, inner);
|
|
670
|
+
expect(el.value).to.deep.equal(['Users', 'Guests']);
|
|
671
|
+
expect(inner.value).to.deep.equal(['Users', 'Guests']);
|
|
672
|
+
});
|
|
673
|
+
it('R1d. a real selection while searching still updates the wrapper value', async () => {
|
|
674
|
+
const { el, inner } = await searchableVirtual();
|
|
675
|
+
await typeSearch(el, inner, 'Gu');
|
|
676
|
+
inner.handleOptionSelection({ value: 'Guests' });
|
|
677
|
+
await settle(el, inner);
|
|
678
|
+
expect(el.value).to.deep.equal(['Users', 'Guests']);
|
|
679
|
+
el.requestUpdate();
|
|
680
|
+
await settle(el, inner);
|
|
681
|
+
expect(inner.value).to.deep.equal(['Users', 'Guests']);
|
|
682
|
+
});
|
|
683
|
+
it('R1e. removing a chip still updates the wrapper value', async () => {
|
|
684
|
+
const { el, inner } = await searchableVirtual({ value: ['Users', 'Guests'] });
|
|
685
|
+
await typeSearch(el, inner, 'U');
|
|
686
|
+
inner.handleTagRemove(new CustomEvent('nile-remove'), { value: 'Users' });
|
|
687
|
+
await settle(el, inner);
|
|
688
|
+
expect(el.value).to.deep.equal(['Guests']);
|
|
689
|
+
});
|
|
690
|
+
it('R1f. nile-input and nile-change from the inner select sync; nested ones do not', async () => {
|
|
691
|
+
const { el, inner } = await searchableVirtual();
|
|
692
|
+
const nested = inner.shadowRoot.querySelector('.select__search');
|
|
693
|
+
const fire = (target, name, value) => target.dispatchEvent(new CustomEvent(name, { detail: { value }, bubbles: true, composed: true }));
|
|
694
|
+
fire(nested, 'nile-input', 'nested-input');
|
|
695
|
+
fire(nested, 'nile-change', 'nested-change');
|
|
696
|
+
expect(el.value).to.deep.equal(['Users']);
|
|
697
|
+
fire(inner, 'nile-input', ['Guests']);
|
|
698
|
+
expect(el.value).to.deep.equal(['Guests']);
|
|
699
|
+
fire(inner, 'nile-change', ['Administrators']);
|
|
700
|
+
expect(el.value).to.deep.equal(['Administrators']);
|
|
701
|
+
});
|
|
702
|
+
it('R1g. portal mode: list is portalled, search keeps the value, selection syncs', async () => {
|
|
703
|
+
const { el, inner } = await searchableVirtual({ portal: true });
|
|
704
|
+
const listbox = document.body.querySelector('.nile-virtual-select-portal-append #listbox');
|
|
705
|
+
expect(listbox, 'listbox moved under document.body').to.exist;
|
|
706
|
+
expect(inner.shadowRoot.querySelector('#listbox')).to.equal(null);
|
|
707
|
+
await typeSearch(el, inner, 'Adm');
|
|
708
|
+
expect(el.value).to.deep.equal(['Users']);
|
|
709
|
+
inner.handleOptionSelection({ value: 'Administrators' });
|
|
710
|
+
await settle(el, inner);
|
|
711
|
+
expect(el.value).to.deep.equal(['Users', 'Administrators']);
|
|
712
|
+
inner.open = false;
|
|
713
|
+
await settle(el, inner);
|
|
714
|
+
await waitUntil(() => !document.body.querySelector('.nile-virtual-select-portal-append'), 'portal not cleaned up');
|
|
715
|
+
});
|
|
716
|
+
});
|
|
717
|
+
describe('NileSelect chipMinWidth / chipMaxWidth attributes', () => {
|
|
718
|
+
const LABELS = ['Engineering Operations Team', 'Human Resources Department'];
|
|
719
|
+
const options = () => LABELS.map(label => html `<nile-option value=${label}>${label}</nile-option>`);
|
|
720
|
+
async function settle(el) {
|
|
721
|
+
await el.updateComplete;
|
|
722
|
+
await nextFrame();
|
|
723
|
+
await nextFrame();
|
|
724
|
+
await el.updateComplete;
|
|
725
|
+
}
|
|
726
|
+
const firstChip = (el) => el.shadowRoot.querySelector('.select__tags nile-tag');
|
|
727
|
+
it('R4a. defaults are 110 / 180 and are not written to the host', async () => {
|
|
728
|
+
const el = await fixture(html `<nile-select multiple>${options()}</nile-select>`);
|
|
729
|
+
await settle(el);
|
|
730
|
+
expect(el.chipMinWidth).to.equal(110);
|
|
731
|
+
expect(el.chipMaxWidth).to.equal(180);
|
|
732
|
+
expect(el.hasAttribute('chipminwidth')).to.be.false;
|
|
733
|
+
expect(el.hasAttribute('chipmaxwidth')).to.be.false;
|
|
734
|
+
});
|
|
735
|
+
it('R4b. the chipminwidth / chipmaxwidth attributes still set the properties', async () => {
|
|
736
|
+
const el = await fixture(html `<nile-select multiple chipminwidth="90" chipmaxwidth="150">${options()}</nile-select>`);
|
|
737
|
+
await settle(el);
|
|
738
|
+
expect(el.chipMinWidth).to.equal(90);
|
|
739
|
+
expect(el.chipMaxWidth).to.equal(150);
|
|
740
|
+
el.setAttribute('chipmaxwidth', '160');
|
|
741
|
+
await settle(el);
|
|
742
|
+
expect(el.chipMaxWidth).to.equal(160);
|
|
743
|
+
});
|
|
744
|
+
it('R4c. setting the properties programmatically works and does not add attributes', async () => {
|
|
745
|
+
const el = await fixture(html `<nile-select multiple>${options()}</nile-select>`);
|
|
746
|
+
await settle(el);
|
|
747
|
+
el.chipMinWidth = 95;
|
|
748
|
+
el.chipMaxWidth = 140;
|
|
749
|
+
await settle(el);
|
|
750
|
+
expect(el.chipMinWidth).to.equal(95);
|
|
751
|
+
expect(el.chipMaxWidth).to.equal(140);
|
|
752
|
+
expect(el.hasAttribute('chipmaxwidth')).to.be.false;
|
|
753
|
+
});
|
|
754
|
+
it('R4d. showtooltip still caps squeezed chips at chipMaxWidth (attribute and property)', async () => {
|
|
755
|
+
const host = await fixture(html `<div style="width: 300px">
|
|
756
|
+
<nile-select multiple showtooltip chipmaxwidth="150" .value=${LABELS}>${options()}</nile-select>
|
|
757
|
+
</div>`);
|
|
758
|
+
const el = host.querySelector('nile-select');
|
|
759
|
+
await settle(el);
|
|
760
|
+
const chip = firstChip(el);
|
|
761
|
+
expect(chip, 'select rendered no chips').to.exist;
|
|
762
|
+
expect(chip.getBoundingClientRect().width).to.be.within(1, 150.5);
|
|
763
|
+
el.chipMaxWidth = 120;
|
|
764
|
+
await settle(el);
|
|
765
|
+
await nextFrame();
|
|
766
|
+
expect(firstChip(el).getBoundingClientRect().width).to.be.within(1, 120.5);
|
|
767
|
+
});
|
|
768
|
+
it('R4e. virtual mode forwards explicit values without reflecting defaults on either host', async () => {
|
|
769
|
+
const data = LABELS.map(label => ({ label, value: label }));
|
|
770
|
+
const el = await fixture(html `<nile-select multiple .enableVirtualScroll=${true} .data=${data} chipmaxwidth="150"></nile-select>`);
|
|
771
|
+
await settle(el);
|
|
772
|
+
const inner = el.shadowRoot.querySelector('nile-virtual-select');
|
|
773
|
+
await inner.updateComplete;
|
|
774
|
+
expect(inner.chipMaxWidth).to.equal(150);
|
|
775
|
+
expect(inner.chipMinWidth).to.equal(110);
|
|
776
|
+
expect(el.hasAttribute('chipminwidth')).to.be.false;
|
|
777
|
+
expect(inner.hasAttribute('chipminwidth')).to.be.false;
|
|
778
|
+
expect(inner.hasAttribute('chipmaxwidth')).to.be.false;
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
describe('NileSelect footer width with and without noWidthSync', () => {
|
|
782
|
+
async function openSelect(width, noWidthSync) {
|
|
783
|
+
const host = await fixture(html `<div style="width: ${width}px">
|
|
784
|
+
<nile-select multiple .noWidthSync=${noWidthSync} .value=${['a']}>
|
|
785
|
+
<nile-option value="a">a</nile-option>
|
|
786
|
+
<nile-option value="b">b</nile-option>
|
|
787
|
+
</nile-select>
|
|
788
|
+
</div>`);
|
|
789
|
+
const el = host.querySelector('nile-select');
|
|
790
|
+
await el.updateComplete;
|
|
791
|
+
await el.show();
|
|
792
|
+
await nextFrame();
|
|
793
|
+
const listbox = el.shadowRoot.querySelector('#listbox');
|
|
794
|
+
const footer = el.shadowRoot.querySelector('.select__footer');
|
|
795
|
+
const combobox = el.shadowRoot.querySelector('[part~="combobox"]');
|
|
796
|
+
return { el, listbox, footer, combobox };
|
|
797
|
+
}
|
|
798
|
+
const labelShown = (footer, selector) => getComputedStyle(footer.querySelector(selector)).display !== 'none';
|
|
799
|
+
before(() => document.documentElement.style.setProperty('--nile-display-flex', 'flex'));
|
|
800
|
+
after(() => document.documentElement.style.removeProperty('--nile-display-flex'));
|
|
801
|
+
for (const width of [120, 400]) {
|
|
802
|
+
it(`R3a. noWidthSync + multiple at ${width}px does not collapse and shows the whole footer`, async () => {
|
|
803
|
+
const { listbox, footer } = await openSelect(width, true);
|
|
804
|
+
expect(listbox.offsetWidth).to.be.greaterThan(60);
|
|
805
|
+
expect(listbox.offsetWidth).to.be.at.least(footer.offsetWidth);
|
|
806
|
+
expect(footer.scrollWidth).to.be.at.most(footer.clientWidth + 1);
|
|
807
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.true;
|
|
808
|
+
expect(labelShown(footer, '.select__footer-action .select__footer-label')).to.be.true;
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
it('R3b. width sync at normal width keeps the dropdown as wide as the trigger', async () => {
|
|
812
|
+
const { listbox, combobox, footer } = await openSelect(400, false);
|
|
813
|
+
expect(Math.abs(listbox.offsetWidth - combobox.offsetWidth)).to.be.below(2);
|
|
814
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.true;
|
|
815
|
+
});
|
|
816
|
+
it('R3c. width sync at a narrow width still collapses the footer labels (container queries kept)', async () => {
|
|
817
|
+
const { listbox, combobox, footer } = await openSelect(130, false);
|
|
818
|
+
expect(Math.abs(listbox.offsetWidth - combobox.offsetWidth)).to.be.below(2);
|
|
819
|
+
expect(labelShown(footer, '.select__clear .select__footer-label')).to.be.false;
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
describe('NileSelect showtooltip chip fit recovery (hidden, reconnect, re-parent)', () => {
|
|
823
|
+
const LABELS = [
|
|
824
|
+
'Engineering Operations Team',
|
|
825
|
+
'Human Resources Department',
|
|
826
|
+
'Finance And Accounting Group',
|
|
827
|
+
'Customer Success Organisation',
|
|
828
|
+
'Product Design Studio Unit'
|
|
829
|
+
];
|
|
830
|
+
const options = () => LABELS.map(label => html `<nile-option value=${label}>${label}</nile-option>`);
|
|
831
|
+
const NativeRO = window.ResizeObserver;
|
|
832
|
+
const active = new Map();
|
|
833
|
+
const liveCount = () => Array.from(active.values()).filter(targets => targets.size > 0).length;
|
|
834
|
+
before(() => {
|
|
835
|
+
window.ResizeObserver = class extends NativeRO {
|
|
836
|
+
observe(target, options) {
|
|
837
|
+
if (!active.has(this))
|
|
838
|
+
active.set(this, new Set());
|
|
839
|
+
active.get(this).add(target);
|
|
840
|
+
super.observe(target, options);
|
|
841
|
+
}
|
|
842
|
+
unobserve(target) {
|
|
843
|
+
active.get(this)?.delete(target);
|
|
844
|
+
super.unobserve(target);
|
|
845
|
+
}
|
|
846
|
+
disconnect() {
|
|
847
|
+
active.get(this)?.clear();
|
|
848
|
+
super.disconnect();
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
});
|
|
852
|
+
after(() => {
|
|
853
|
+
window.ResizeObserver = NativeRO;
|
|
854
|
+
});
|
|
855
|
+
async function frames(n = 4) {
|
|
856
|
+
for (let i = 0; i < n; i++)
|
|
857
|
+
await nextFrame();
|
|
858
|
+
}
|
|
859
|
+
async function settle(el) {
|
|
860
|
+
await el.updateComplete;
|
|
861
|
+
await frames();
|
|
862
|
+
await el.updateComplete;
|
|
863
|
+
await frames(2);
|
|
864
|
+
}
|
|
865
|
+
const chipsOf = (el) => Array.from(el.shadowRoot.querySelectorAll('.select__tags nile-tag'));
|
|
866
|
+
const watchActive = (el) => !!el.measurableWatch;
|
|
867
|
+
function fitOf(el) {
|
|
868
|
+
const chips = chipsOf(el);
|
|
869
|
+
const visible = chips.filter(chip => chip.style.display !== 'none');
|
|
870
|
+
const button = el.shadowRoot.querySelector('button.select__tags-count');
|
|
871
|
+
return {
|
|
872
|
+
chips: chips.length,
|
|
873
|
+
visible: visible.length,
|
|
874
|
+
more: button ? button.textContent.trim() : '',
|
|
875
|
+
widths: visible.map(chip => chip.offsetWidth)
|
|
876
|
+
};
|
|
877
|
+
}
|
|
878
|
+
function box(width, display = 'block') {
|
|
879
|
+
const div = document.createElement('div');
|
|
880
|
+
div.style.width = `${width}px`;
|
|
881
|
+
div.style.display = display;
|
|
882
|
+
document.body.appendChild(div);
|
|
883
|
+
boxes.push(div);
|
|
884
|
+
return div;
|
|
885
|
+
}
|
|
886
|
+
let boxes = [];
|
|
887
|
+
afterEach(() => {
|
|
888
|
+
boxes.forEach(b => b.remove());
|
|
889
|
+
boxes = [];
|
|
890
|
+
});
|
|
891
|
+
async function makeSelect(parent, { showTooltip = true, autoResize = false, value = LABELS } = {}) {
|
|
892
|
+
const el = document.createElement('nile-select');
|
|
893
|
+
el.multiple = true;
|
|
894
|
+
el.showTooltip = showTooltip;
|
|
895
|
+
el.autoResize = autoResize;
|
|
896
|
+
for (const label of LABELS) {
|
|
897
|
+
const option = document.createElement('nile-option');
|
|
898
|
+
option.value = label;
|
|
899
|
+
option.textContent = label;
|
|
900
|
+
el.appendChild(option);
|
|
901
|
+
}
|
|
902
|
+
el.value = value;
|
|
903
|
+
parent.appendChild(el);
|
|
904
|
+
await settle(el);
|
|
905
|
+
return el;
|
|
906
|
+
}
|
|
907
|
+
async function reference(width, opts = {}) {
|
|
908
|
+
const el = await makeSelect(box(width), opts);
|
|
909
|
+
const fit = fitOf(el);
|
|
910
|
+
el.parentElement.remove();
|
|
911
|
+
return fit;
|
|
912
|
+
}
|
|
913
|
+
function expectFitted(actual, expected, el) {
|
|
914
|
+
expect(actual.chips).to.equal(expected.chips);
|
|
915
|
+
expect(actual.visible, 'visible chips').to.equal(expected.visible);
|
|
916
|
+
expect(actual.more, '+N button').to.equal(expected.more);
|
|
917
|
+
actual.widths.forEach((w, i) => {
|
|
918
|
+
expect(w, `chip ${i} width`).to.be.within(el.chipMinWidth - 1, el.chipMaxWidth + 1);
|
|
919
|
+
expect(Math.abs(w - expected.widths[i]), `chip ${i} matches reference`).to.be.at.most(2);
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
it('R5-ref. the 300px reference really is a fitted row (so a stale row cannot match it)', async () => {
|
|
923
|
+
const ref = await reference(300);
|
|
924
|
+
expect(ref.chips).to.equal(5);
|
|
925
|
+
expect(ref.visible).to.be.within(1, 4);
|
|
926
|
+
expect(ref.more).to.equal(`+${5 - ref.visible}`);
|
|
927
|
+
});
|
|
928
|
+
it('R5a. hidden -> visible without reconnect recovers the fitted row, then the watch ends', async () => {
|
|
929
|
+
const ref = await reference(300);
|
|
930
|
+
const parent = box(300, 'none');
|
|
931
|
+
const el = await makeSelect(parent);
|
|
932
|
+
expect(watchActive(el), 'watch armed while hidden').to.be.true;
|
|
933
|
+
parent.style.display = 'block';
|
|
934
|
+
await settle(el);
|
|
935
|
+
expectFitted(fitOf(el), ref, el);
|
|
936
|
+
expect(watchActive(el)).to.be.false;
|
|
937
|
+
});
|
|
938
|
+
it('R5b. hidden -> reconnected into a visible 300px parent re-fits, twice, without extra observers', async () => {
|
|
939
|
+
const ref = await reference(300);
|
|
940
|
+
const el = await makeSelect(box(300, 'none'));
|
|
941
|
+
expect(watchActive(el)).to.be.true;
|
|
942
|
+
el.remove();
|
|
943
|
+
expect(watchActive(el), 'disconnect stops the watch').to.be.false;
|
|
944
|
+
box(300).appendChild(el);
|
|
945
|
+
await settle(el);
|
|
946
|
+
expectFitted(fitOf(el), ref, el);
|
|
947
|
+
expect(watchActive(el)).to.be.false;
|
|
948
|
+
const liveAfterFirst = liveCount();
|
|
949
|
+
el.remove();
|
|
950
|
+
box(300).appendChild(el);
|
|
951
|
+
await settle(el);
|
|
952
|
+
expectFitted(fitOf(el), ref, el);
|
|
953
|
+
expect(watchActive(el)).to.be.false;
|
|
954
|
+
expect(liveCount(), 'no observer growth across reconnects').to.equal(liveAfterFirst);
|
|
955
|
+
});
|
|
956
|
+
it('R5c. visible -> disconnect -> reconnect into a narrower parent re-fits to the new width', async () => {
|
|
957
|
+
const ref = await reference(300);
|
|
958
|
+
const el = await makeSelect(box(700));
|
|
959
|
+
expect(fitOf(el).visible).to.be.greaterThan(ref.visible);
|
|
960
|
+
el.remove();
|
|
961
|
+
box(300).appendChild(el);
|
|
962
|
+
await settle(el);
|
|
963
|
+
expectFitted(fitOf(el), ref, el);
|
|
964
|
+
expect(watchActive(el)).to.be.false;
|
|
965
|
+
});
|
|
966
|
+
it('R5d. value changed while hidden and detached, then reconnected, fits the new value', async () => {
|
|
967
|
+
const four = LABELS.slice(0, 4);
|
|
968
|
+
const ref = await reference(300, { value: four });
|
|
969
|
+
const el = await makeSelect(box(300, 'none'));
|
|
970
|
+
el.remove();
|
|
971
|
+
el.value = four;
|
|
972
|
+
await settle(el);
|
|
973
|
+
expect(watchActive(el), 'no watch left on a detached select').to.be.false;
|
|
974
|
+
box(300).appendChild(el);
|
|
975
|
+
await settle(el);
|
|
976
|
+
expectFitted(fitOf(el), ref, el);
|
|
977
|
+
expect(watchActive(el)).to.be.false;
|
|
978
|
+
});
|
|
979
|
+
it('R5e. reconnect into another hidden parent keeps one watch, and recovers when shown', async () => {
|
|
980
|
+
const ref = await reference(300);
|
|
981
|
+
const el = await makeSelect(box(300, 'none'));
|
|
982
|
+
const liveHidden = liveCount();
|
|
983
|
+
el.remove();
|
|
984
|
+
const second = box(300, 'none');
|
|
985
|
+
second.appendChild(el);
|
|
986
|
+
await settle(el);
|
|
987
|
+
expect(watchActive(el), 're-armed in the new hidden parent').to.be.true;
|
|
988
|
+
expect(liveCount(), 'the old watch was replaced, not duplicated').to.equal(liveHidden);
|
|
989
|
+
second.style.display = 'block';
|
|
990
|
+
await settle(el);
|
|
991
|
+
expectFitted(fitOf(el), ref, el);
|
|
992
|
+
expect(watchActive(el)).to.be.false;
|
|
993
|
+
});
|
|
994
|
+
it('R5f. showTooltip true -> false before reconnect gives the untouched 2.0.8 row', async () => {
|
|
995
|
+
const legacy = await makeSelect(box(300), { showTooltip: false });
|
|
996
|
+
const legacyText = legacy.shadowRoot.querySelector('.select__tags').textContent.replace(/\s+/g, ' ').trim();
|
|
997
|
+
const el = await makeSelect(box(300, 'none'));
|
|
998
|
+
el.remove();
|
|
999
|
+
el.showTooltip = false;
|
|
1000
|
+
box(300).appendChild(el);
|
|
1001
|
+
await settle(el);
|
|
1002
|
+
expect(watchActive(el)).to.be.false;
|
|
1003
|
+
expect(el.shadowRoot.querySelector('.select__tags--fitted')).to.equal(null);
|
|
1004
|
+
chipsOf(el).forEach(chip => expect(chip.style.width, 'no fitted width on the 2.0.8 row').to.equal(''));
|
|
1005
|
+
expect(el.shadowRoot.querySelector('.select__tags').textContent.replace(/\s+/g, ' ').trim()).to.equal(legacyText);
|
|
1006
|
+
});
|
|
1007
|
+
it('R5g. showtooltip=false: hidden -> shown and reconnect never arm a watch or write chip widths', async () => {
|
|
1008
|
+
const parent = box(300, 'none');
|
|
1009
|
+
const el = await makeSelect(parent, { showTooltip: false });
|
|
1010
|
+
expect(watchActive(el)).to.be.false;
|
|
1011
|
+
parent.style.display = 'block';
|
|
1012
|
+
await settle(el);
|
|
1013
|
+
el.remove();
|
|
1014
|
+
box(300).appendChild(el);
|
|
1015
|
+
await settle(el);
|
|
1016
|
+
expect(watchActive(el)).to.be.false;
|
|
1017
|
+
chipsOf(el).forEach(chip => expect(chip.style.width).to.equal(''));
|
|
1018
|
+
});
|
|
1019
|
+
it('R5h. a visible showtooltip select never arms the watch', async () => {
|
|
1020
|
+
const el = await makeSelect(box(300));
|
|
1021
|
+
expect(watchActive(el)).to.be.false;
|
|
1022
|
+
});
|
|
1023
|
+
it('R5i. repeated disconnect/reconnect cycles stay stable', async () => {
|
|
1024
|
+
const ref = await reference(300);
|
|
1025
|
+
const el = await makeSelect(box(300));
|
|
1026
|
+
const baseline = liveCount();
|
|
1027
|
+
for (let i = 0; i < 5; i++) {
|
|
1028
|
+
el.remove();
|
|
1029
|
+
box(300).appendChild(el);
|
|
1030
|
+
await settle(el);
|
|
1031
|
+
}
|
|
1032
|
+
expectFitted(fitOf(el), ref, el);
|
|
1033
|
+
expect(watchActive(el)).to.be.false;
|
|
1034
|
+
expect(liveCount(), 'no observer growth across cycles').to.equal(baseline);
|
|
1035
|
+
});
|
|
1036
|
+
async function updatesOver(el, ms) {
|
|
1037
|
+
let updates = 0;
|
|
1038
|
+
const update = el.update;
|
|
1039
|
+
el.update = function (changed) {
|
|
1040
|
+
updates++;
|
|
1041
|
+
return update.call(this, changed);
|
|
1042
|
+
};
|
|
1043
|
+
await aTimeout(ms);
|
|
1044
|
+
el.update = update;
|
|
1045
|
+
return updates;
|
|
1046
|
+
}
|
|
1047
|
+
it('R5j. autoResize + showtooltip: hidden -> reconnect fits, resizes refit, and it settles', async () => {
|
|
1048
|
+
const ref300 = await reference(300);
|
|
1049
|
+
const ref500 = await reference(500);
|
|
1050
|
+
const el = await makeSelect(box(300, 'none'), { autoResize: true });
|
|
1051
|
+
el.remove();
|
|
1052
|
+
const parent = box(300);
|
|
1053
|
+
parent.appendChild(el);
|
|
1054
|
+
await settle(el);
|
|
1055
|
+
expectFitted(fitOf(el), ref300, el);
|
|
1056
|
+
parent.style.width = '500px';
|
|
1057
|
+
await settle(el);
|
|
1058
|
+
expectFitted(fitOf(el), ref500, el);
|
|
1059
|
+
expect(await updatesOver(el, 300), 'no render loop with showtooltip').to.equal(0);
|
|
1060
|
+
});
|
|
1061
|
+
it('R5k. autoResize: turning showtooltip on over the 2.0.8 row gives the fitted row and settles', async () => {
|
|
1062
|
+
const ref = await reference(300);
|
|
1063
|
+
const el = await makeSelect(box(300), { showTooltip: false, autoResize: true });
|
|
1064
|
+
el.showTooltip = true;
|
|
1065
|
+
await settle(el);
|
|
1066
|
+
await aTimeout(20);
|
|
1067
|
+
await settle(el);
|
|
1068
|
+
expectFitted(fitOf(el), ref, el);
|
|
1069
|
+
expect(await updatesOver(el, 300), 'no render loop once showtooltip is on').to.equal(0);
|
|
1070
|
+
});
|
|
1071
|
+
});
|
|
1072
|
+
class R7PrefixProbe extends HTMLElement {
|
|
1073
|
+
constructor() {
|
|
1074
|
+
super();
|
|
1075
|
+
R7PrefixProbe.created++;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
R7PrefixProbe.created = 0;
|
|
1079
|
+
if (!customElements.get('r7-prefix-probe'))
|
|
1080
|
+
customElements.define('r7-prefix-probe', R7PrefixProbe);
|
|
1081
|
+
describe('NileSelect option prefix copies across re-renders', () => {
|
|
1082
|
+
async function settle(el) {
|
|
1083
|
+
await el.updateComplete;
|
|
1084
|
+
await nextFrame();
|
|
1085
|
+
await nextFrame();
|
|
1086
|
+
await el.updateComplete;
|
|
1087
|
+
}
|
|
1088
|
+
const prefixIn = (el, selector) => el.shadowRoot.querySelector(`${selector} r7-prefix-probe`);
|
|
1089
|
+
const withPrefix = (value) => html `<nile-option value=${value}><r7-prefix-probe slot="prefix" data-v=${value}></r7-prefix-probe>${value}</nile-option>`;
|
|
1090
|
+
it('R7a. single select: the trigger prefix node is reused across re-renders', async () => {
|
|
1091
|
+
const el = await fixture(html `<nile-select .value=${'a'}>${withPrefix('a')}${withPrefix('b')}</nile-select>`);
|
|
1092
|
+
await settle(el);
|
|
1093
|
+
const first = prefixIn(el, '.select__prefix--from-options');
|
|
1094
|
+
expect(first, 'prefix copied into the trigger').to.exist;
|
|
1095
|
+
const created = R7PrefixProbe.created;
|
|
1096
|
+
for (let i = 0; i < 3; i++) {
|
|
1097
|
+
el.requestUpdate();
|
|
1098
|
+
await settle(el);
|
|
1099
|
+
}
|
|
1100
|
+
expect(prefixIn(el, '.select__prefix--from-options')).to.equal(first);
|
|
1101
|
+
expect(R7PrefixProbe.created).to.equal(created);
|
|
1102
|
+
});
|
|
1103
|
+
for (const showTooltip of [false, true]) {
|
|
1104
|
+
it(`R7b. multiple (showtooltip=${showTooltip}): chip prefix nodes are reused across re-renders`, async () => {
|
|
1105
|
+
const el = await fixture(html `<nile-select multiple ?showtooltip=${showTooltip} .value=${['a', 'b']}>
|
|
1106
|
+
${withPrefix('a')}${withPrefix('b')}
|
|
1107
|
+
</nile-select>`);
|
|
1108
|
+
await settle(el);
|
|
1109
|
+
const before = Array.from(el.shadowRoot.querySelectorAll('.select__tags r7-prefix-probe'));
|
|
1110
|
+
expect(before.length).to.equal(2);
|
|
1111
|
+
const created = R7PrefixProbe.created;
|
|
1112
|
+
el.requestUpdate();
|
|
1113
|
+
await settle(el);
|
|
1114
|
+
const after = Array.from(el.shadowRoot.querySelectorAll('.select__tags r7-prefix-probe'));
|
|
1115
|
+
expect(after.length).to.equal(2);
|
|
1116
|
+
after.forEach((node, i) => expect(node === before[i], `chip ${i} prefix replaced`).to.be.true);
|
|
1117
|
+
expect(R7PrefixProbe.created).to.equal(created);
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
it('R7c. changing the selection or the option prefix still updates the copy', async () => {
|
|
1121
|
+
const el = await fixture(html `<nile-select .value=${'a'}>${withPrefix('a')}${withPrefix('b')}</nile-select>`);
|
|
1122
|
+
await settle(el);
|
|
1123
|
+
el.value = 'b';
|
|
1124
|
+
await settle(el);
|
|
1125
|
+
expect(prefixIn(el, '.select__prefix--from-options').dataset.v).to.equal('b');
|
|
1126
|
+
const source = el.querySelector('nile-option[value="b"] r7-prefix-probe');
|
|
1127
|
+
source.setAttribute('data-v', 'b2');
|
|
1128
|
+
el.requestUpdate();
|
|
1129
|
+
await settle(el);
|
|
1130
|
+
expect(prefixIn(el, '.select__prefix--from-options').dataset.v).to.equal('b2');
|
|
1131
|
+
});
|
|
1132
|
+
});
|
|
1133
|
+
describe('NileSelect 2.0.8 compatibility matrix', () => {
|
|
1134
|
+
const OPTS = ['a', 'b', 'c'];
|
|
1135
|
+
const options = () => OPTS.map(v => html `<nile-option value=${v}>L${v}</nile-option>`);
|
|
1136
|
+
const DATA = OPTS.map(v => ({ label: `L${v}`, value: v }));
|
|
1137
|
+
async function settle(el) {
|
|
1138
|
+
await el.updateComplete;
|
|
1139
|
+
await nextFrame();
|
|
1140
|
+
await nextFrame();
|
|
1141
|
+
await el.updateComplete;
|
|
1142
|
+
}
|
|
1143
|
+
const selectedValues = (el) => Array.from(el.querySelectorAll('nile-option'))
|
|
1144
|
+
.filter(o => o.selected)
|
|
1145
|
+
.map(o => o.value);
|
|
1146
|
+
const chipCount = (el) => el.shadowRoot.querySelectorAll('.select__tags nile-tag').length;
|
|
1147
|
+
function record(el, names) {
|
|
1148
|
+
const seen = [];
|
|
1149
|
+
names.forEach(name => el.addEventListener(name, e => seen.push([name, JSON.parse(JSON.stringify(e.detail ?? null))])));
|
|
1150
|
+
return seen;
|
|
1151
|
+
}
|
|
1152
|
+
it('C1. a value in the template is applied when the options arrive later (multiple and single)', async () => {
|
|
1153
|
+
const multi = await fixture(html `<nile-select multiple .value=${['a', 'b']}></nile-select>`);
|
|
1154
|
+
await settle(multi);
|
|
1155
|
+
OPTS.forEach(v => { const o = document.createElement('nile-option'); o.value = v; o.textContent = v; multi.appendChild(o); });
|
|
1156
|
+
await settle(multi);
|
|
1157
|
+
expect(multi.value).to.deep.equal(['a', 'b']);
|
|
1158
|
+
expect(selectedValues(multi)).to.deep.equal(['a', 'b']);
|
|
1159
|
+
expect(chipCount(multi)).to.equal(2);
|
|
1160
|
+
const single = await fixture(html `<nile-select .value=${'b'}></nile-select>`);
|
|
1161
|
+
await settle(single);
|
|
1162
|
+
OPTS.forEach(v => { const o = document.createElement('nile-option'); o.value = v; o.textContent = v; single.appendChild(o); });
|
|
1163
|
+
await settle(single);
|
|
1164
|
+
expect(single.value).to.equal('b');
|
|
1165
|
+
expect(selectedValues(single)).to.deep.equal(['b']);
|
|
1166
|
+
});
|
|
1167
|
+
it('C2. programmatic value updates reach options, chips and the form value', async () => {
|
|
1168
|
+
const el = await fixture(html `<nile-select multiple name="s">${options()}</nile-select>`);
|
|
1169
|
+
await settle(el);
|
|
1170
|
+
el.value = ['b', 'c'];
|
|
1171
|
+
await settle(el);
|
|
1172
|
+
expect(selectedValues(el)).to.deep.equal(['b', 'c']);
|
|
1173
|
+
expect(chipCount(el)).to.equal(2);
|
|
1174
|
+
el.value = [];
|
|
1175
|
+
await settle(el);
|
|
1176
|
+
expect(selectedValues(el)).to.deep.equal([]);
|
|
1177
|
+
expect(chipCount(el)).to.equal(0);
|
|
1178
|
+
});
|
|
1179
|
+
it('C3. removing a chip updates the value and emits input, change, tag-remove in order', async () => {
|
|
1180
|
+
const el = await fixture(html `<nile-select multiple .value=${['a', 'b']}>${options()}</nile-select>`);
|
|
1181
|
+
await settle(el);
|
|
1182
|
+
const seen = record(el, ['nile-input', 'nile-change', 'nile-tag-remove']);
|
|
1183
|
+
const chip = el.shadowRoot.querySelector('.select__tags nile-tag');
|
|
1184
|
+
expect(chip, 'select rendered no chips').to.exist;
|
|
1185
|
+
chip.dispatchEvent(new CustomEvent('nile-remove', { bubbles: true, composed: true }));
|
|
1186
|
+
await settle(el);
|
|
1187
|
+
expect(el.value).to.deep.equal(['b']);
|
|
1188
|
+
expect(seen.map(([name]) => name)).to.deep.equal(['nile-input', 'nile-change', 'nile-tag-remove']);
|
|
1189
|
+
expect(seen[2][1].removedtagvalue).to.equal('a');
|
|
1190
|
+
});
|
|
1191
|
+
it('C4. the clear button empties the value and emits clear, input, change', async () => {
|
|
1192
|
+
const el = await fixture(html `<nile-select multiple clearable .value=${['a', 'b']}>${options()}</nile-select>`);
|
|
1193
|
+
await settle(el);
|
|
1194
|
+
const seen = record(el, ['nile-clear', 'nile-input', 'nile-change']);
|
|
1195
|
+
const clear = el.shadowRoot.querySelector('[part~="clear-button"]');
|
|
1196
|
+
expect(clear, 'clear button rendered').to.exist;
|
|
1197
|
+
clear.click();
|
|
1198
|
+
await settle(el);
|
|
1199
|
+
expect(el.value).to.deep.equal([]);
|
|
1200
|
+
expect(chipCount(el)).to.equal(0);
|
|
1201
|
+
expect(seen.map(([name]) => name)).to.deep.equal(['nile-clear', 'nile-input', 'nile-change']);
|
|
1202
|
+
});
|
|
1203
|
+
it('C5. form reset restores the value attribute', async () => {
|
|
1204
|
+
const form = await fixture(html `<form>
|
|
1205
|
+
<nile-select name="s" multiple value="a b">${options()}</nile-select>
|
|
1206
|
+
</form>`);
|
|
1207
|
+
const el = form.querySelector('nile-select');
|
|
1208
|
+
await settle(el);
|
|
1209
|
+
el.value = ['c'];
|
|
1210
|
+
await settle(el);
|
|
1211
|
+
form.reset();
|
|
1212
|
+
await settle(el);
|
|
1213
|
+
expect(el.value).to.deep.equal(['a', 'b']);
|
|
1214
|
+
expect(selectedValues(el)).to.deep.equal(['a', 'b']);
|
|
1215
|
+
});
|
|
1216
|
+
it('C6. portal + search: typing filters without touching the value', async () => {
|
|
1217
|
+
const el = await fixture(html `<nile-select multiple portal searchEnabled .value=${['a']}>${options()}</nile-select>`);
|
|
1218
|
+
await settle(el);
|
|
1219
|
+
await el.show();
|
|
1220
|
+
await aTimeout(50);
|
|
1221
|
+
const search = Array.from(document.body.querySelectorAll('nile-input')).find(n => !el.shadowRoot.contains(n));
|
|
1222
|
+
expect(search, 'portal search box rendered').to.exist;
|
|
1223
|
+
await search.updateComplete;
|
|
1224
|
+
const input = search.shadowRoot.querySelector('input');
|
|
1225
|
+
input.value = 'Lb';
|
|
1226
|
+
input.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
|
|
1227
|
+
await settle(el);
|
|
1228
|
+
expect(el.value).to.deep.equal(['a']);
|
|
1229
|
+
await el.hide();
|
|
1230
|
+
});
|
|
1231
|
+
it('C7. reconnecting a multiple select without showtooltip keeps value and chips', async () => {
|
|
1232
|
+
const host = await fixture(html `<div style="width: 500px">
|
|
1233
|
+
<nile-select multiple .value=${['a', 'b']}>${options()}</nile-select>
|
|
1234
|
+
</div>`);
|
|
1235
|
+
const el = host.querySelector('nile-select');
|
|
1236
|
+
await settle(el);
|
|
1237
|
+
el.remove();
|
|
1238
|
+
host.appendChild(el);
|
|
1239
|
+
await settle(el);
|
|
1240
|
+
expect(el.value).to.deep.equal(['a', 'b']);
|
|
1241
|
+
expect(chipCount(el)).to.equal(2);
|
|
1242
|
+
});
|
|
1243
|
+
it('C8. enableVirtualScroll: a value set before data arrives is kept and shown', async () => {
|
|
1244
|
+
const el = await fixture(html `<nile-select multiple .enableVirtualScroll=${true} .value=${['b']}></nile-select>`);
|
|
1245
|
+
await settle(el);
|
|
1246
|
+
el.data = DATA;
|
|
1247
|
+
await settle(el);
|
|
1248
|
+
const inner = el.shadowRoot.querySelector('nile-virtual-select');
|
|
1249
|
+
await inner.updateComplete;
|
|
1250
|
+
expect(el.value).to.deep.equal(['b']);
|
|
1251
|
+
expect(inner.value).to.deep.equal(['b']);
|
|
1252
|
+
expect(inner.shadowRoot.querySelectorAll('.select__tags nile-tag').length).to.equal(1);
|
|
1253
|
+
});
|
|
1254
|
+
it('C9. enableVirtualScroll: programmatic value and clear-all reach both selects', async () => {
|
|
1255
|
+
const el = await fixture(html `<nile-select multiple .enableVirtualScroll=${true} .data=${DATA}></nile-select>`);
|
|
1256
|
+
await settle(el);
|
|
1257
|
+
const inner = el.shadowRoot.querySelector('nile-virtual-select');
|
|
1258
|
+
el.value = ['a', 'c'];
|
|
1259
|
+
await settle(el);
|
|
1260
|
+
await inner.updateComplete;
|
|
1261
|
+
expect(inner.value).to.deep.equal(['a', 'c']);
|
|
1262
|
+
inner.unSelectAll();
|
|
1263
|
+
await inner.updateComplete;
|
|
1264
|
+
await settle(el);
|
|
1265
|
+
expect(inner.value).to.deep.equal([]);
|
|
1266
|
+
expect(el.value).to.deep.equal([]);
|
|
1267
|
+
});
|
|
1268
|
+
});
|
|
1269
|
+
describe('NileSelect showtooltip tooltip on sub-pixel truncation', () => {
|
|
1270
|
+
const A = 'Engineering Operations';
|
|
1271
|
+
const B = 'Human Resources Department And Payroll';
|
|
1272
|
+
async function frames(n = 5) {
|
|
1273
|
+
for (let i = 0; i < n; i++)
|
|
1274
|
+
await nextFrame();
|
|
1275
|
+
}
|
|
1276
|
+
async function mount(width, chipMaxWidth) {
|
|
1277
|
+
const host = await fixture(html `<div style="width: ${width}px">
|
|
1278
|
+
<nile-select multiple showtooltip chipminwidth="40" chipmaxwidth=${chipMaxWidth} .value=${[A, B]}>
|
|
1279
|
+
<nile-option value=${A}>${A}</nile-option>
|
|
1280
|
+
<nile-option value=${B}>${B}</nile-option>
|
|
1281
|
+
</nile-select>
|
|
1282
|
+
</div>`);
|
|
1283
|
+
const el = host.querySelector('nile-select');
|
|
1284
|
+
await el.updateComplete;
|
|
1285
|
+
await frames();
|
|
1286
|
+
await el.updateComplete;
|
|
1287
|
+
await frames(2);
|
|
1288
|
+
return el;
|
|
1289
|
+
}
|
|
1290
|
+
function visiblyCut(chip) {
|
|
1291
|
+
const content = chip.shadowRoot.querySelector('[part="content"]');
|
|
1292
|
+
const text = content.assignedNodes({ flatten: true }).find(n => n.textContent?.trim());
|
|
1293
|
+
const range = document.createRange();
|
|
1294
|
+
range.selectNodeContents(text);
|
|
1295
|
+
const over = range.getBoundingClientRect().right - content.getBoundingClientRect().right;
|
|
1296
|
+
return { cut: over > 1 / 64, over, pxGap: content.scrollWidth - content.clientWidth };
|
|
1297
|
+
}
|
|
1298
|
+
function chipsAndTips(el) {
|
|
1299
|
+
const chips = Array.from(el.shadowRoot.querySelectorAll('.select__tags nile-tag'));
|
|
1300
|
+
const tips = Array.from(el.shadowRoot.querySelectorAll('.select__tags nile-lite-tooltip'));
|
|
1301
|
+
return chips
|
|
1302
|
+
.map((chip, i) => ({ chip, tipOn: !!tips[i] && !tips[i].hasAttribute('disabled') }))
|
|
1303
|
+
.filter(({ chip }) => chip.style.display !== 'none');
|
|
1304
|
+
}
|
|
1305
|
+
it('TT1. every visibly cut chip has its tooltip, every uncut chip has none (sweep across the edge)', async () => {
|
|
1306
|
+
const wide = await mount(900, 400);
|
|
1307
|
+
const natural = chipsAndTips(wide)[0].chip.offsetWidth;
|
|
1308
|
+
let subPixelCuts = 0;
|
|
1309
|
+
for (let max = natural - 4; max <= natural + 1; max += 0.25) {
|
|
1310
|
+
const el = await mount(330, max);
|
|
1311
|
+
for (const { chip, tipOn } of chipsAndTips(el)) {
|
|
1312
|
+
const { cut, pxGap } = visiblyCut(chip);
|
|
1313
|
+
expect(tipOn, `chipMaxWidth ${max}: tooltip ${tipOn} but cut ${cut}`).to.equal(cut);
|
|
1314
|
+
if (cut && pxGap <= 1)
|
|
1315
|
+
subPixelCuts++;
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
expect(subPixelCuts, 'sweep reached the sub-pixel truncation window').to.be.greaterThan(0);
|
|
1319
|
+
});
|
|
1320
|
+
it('TT2. chips at their natural width get no tooltip', async () => {
|
|
1321
|
+
const el = await mount(900, 400);
|
|
1322
|
+
for (const { chip, tipOn } of chipsAndTips(el)) {
|
|
1323
|
+
expect(visiblyCut(chip).cut).to.be.false;
|
|
1324
|
+
expect(tipOn).to.be.false;
|
|
1325
|
+
}
|
|
1326
|
+
});
|
|
448
1327
|
});
|
|
449
1328
|
//# sourceMappingURL=nile-select.test.js.map
|