@schukai/monster 4.136.3 → 4.136.5
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/package.json +1 -1
- package/source/components/form/select.mjs +496 -113
- package/source/components/form/style/select.pcss +18 -0
- package/source/components/form/stylesheet/context-error.mjs +13 -6
- package/source/components/form/stylesheet/context-help.mjs +13 -6
- package/source/components/form/stylesheet/select.mjs +7 -14
- package/source/components/form/util/floating-ui.mjs +123 -23
- package/test/cases/components/form/floating-ui.mjs +30 -0
- package/test/cases/components/form/message-state-button.mjs +54 -32
- package/test/cases/components/form/select.mjs +235 -1
- package/test/util/intersection-mock.mjs +3 -1
|
@@ -33,7 +33,13 @@ let html2 = `
|
|
|
33
33
|
</div>
|
|
34
34
|
`;
|
|
35
35
|
|
|
36
|
-
let Select,
|
|
36
|
+
let Select,
|
|
37
|
+
getDefaultSelectPopperPositionProfile,
|
|
38
|
+
resolveSelectListDimension,
|
|
39
|
+
resolveSelectPopperWidthConstraints,
|
|
40
|
+
resolveSelectVisibleRect,
|
|
41
|
+
resolveSelectViewportMetrics,
|
|
42
|
+
fetchReference;
|
|
37
43
|
|
|
38
44
|
describe('Select', function () {
|
|
39
45
|
|
|
@@ -50,6 +56,11 @@ describe('Select', function () {
|
|
|
50
56
|
|
|
51
57
|
import("../../../../source/components/form/select.mjs").then((m) => {
|
|
52
58
|
Select = m['Select'];
|
|
59
|
+
getDefaultSelectPopperPositionProfile = m['getDefaultSelectPopperPositionProfile'];
|
|
60
|
+
resolveSelectListDimension = m['resolveSelectListDimension'];
|
|
61
|
+
resolveSelectPopperWidthConstraints = m['resolveSelectPopperWidthConstraints'];
|
|
62
|
+
resolveSelectVisibleRect = m['resolveSelectVisibleRect'];
|
|
63
|
+
resolveSelectViewportMetrics = m['resolveSelectViewportMetrics'];
|
|
53
64
|
done()
|
|
54
65
|
}).catch(e => done(e))
|
|
55
66
|
|
|
@@ -185,6 +196,189 @@ describe('Select', function () {
|
|
|
185
196
|
|
|
186
197
|
});
|
|
187
198
|
|
|
199
|
+
describe('Popper sizing', function () {
|
|
200
|
+
afterEach(() => {
|
|
201
|
+
let mocks = document.getElementById('mocks');
|
|
202
|
+
mocks.innerHTML = "";
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should allow the popper to become wider than a narrow control', function (done) {
|
|
206
|
+
const mocks = document.getElementById('mocks');
|
|
207
|
+
const select = document.createElement('monster-select');
|
|
208
|
+
|
|
209
|
+
select.setOption('options', [
|
|
210
|
+
{label: 'Alpha', value: 'alpha'},
|
|
211
|
+
{label: 'Beta', value: 'beta'}
|
|
212
|
+
]);
|
|
213
|
+
|
|
214
|
+
mocks.appendChild(select);
|
|
215
|
+
|
|
216
|
+
const shadowRoot = select.shadowRoot;
|
|
217
|
+
const control = shadowRoot.querySelector('[data-monster-role=control]');
|
|
218
|
+
const popper = shadowRoot.querySelector('[data-monster-role=popper]');
|
|
219
|
+
|
|
220
|
+
control.getBoundingClientRect = () => ({
|
|
221
|
+
width: 120,
|
|
222
|
+
height: 36,
|
|
223
|
+
top: 100,
|
|
224
|
+
left: 40,
|
|
225
|
+
right: 160,
|
|
226
|
+
bottom: 136,
|
|
227
|
+
x: 40,
|
|
228
|
+
y: 100
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
setTimeout(() => {
|
|
232
|
+
try {
|
|
233
|
+
shadowRoot.querySelector('[data-monster-role=container]').click();
|
|
234
|
+
setTimeout(() => {
|
|
235
|
+
try {
|
|
236
|
+
expect(popper.style.minWidth).to.equal('');
|
|
237
|
+
expect(popper.dataset.monsterPreferredWidth).to.equal('240');
|
|
238
|
+
done();
|
|
239
|
+
} catch (e) {
|
|
240
|
+
done(e);
|
|
241
|
+
}
|
|
242
|
+
}, 80);
|
|
243
|
+
} catch (e) {
|
|
244
|
+
done(e);
|
|
245
|
+
}
|
|
246
|
+
}, 20);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should keep the option list height tight for short lists', function () {
|
|
250
|
+
const result = resolveSelectListDimension({
|
|
251
|
+
visibleOptionHeights: [28, 28],
|
|
252
|
+
maxVisibleOptions: 20,
|
|
253
|
+
availableHeight: 500,
|
|
254
|
+
padding: 0,
|
|
255
|
+
margin: 0
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(result.desiredHeight).to.equal(56);
|
|
259
|
+
expect(result.maxHeight).to.equal(500);
|
|
260
|
+
expect(result.overflowY).to.equal('hidden');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should refresh the content max height when the available popper height grows again', function (done) {
|
|
264
|
+
const mocks = document.getElementById('mocks');
|
|
265
|
+
const select = document.createElement('monster-select');
|
|
266
|
+
|
|
267
|
+
select.setOption('options', Array.from({length: 24}, (_, index) => ({
|
|
268
|
+
label: `Option ${index + 1}`,
|
|
269
|
+
value: `${index + 1}`
|
|
270
|
+
})));
|
|
271
|
+
|
|
272
|
+
mocks.appendChild(select);
|
|
273
|
+
|
|
274
|
+
const shadowRoot = select.shadowRoot;
|
|
275
|
+
const control = shadowRoot.querySelector('[data-monster-role=control]');
|
|
276
|
+
const popper = shadowRoot.querySelector('[data-monster-role=popper]');
|
|
277
|
+
const content = shadowRoot.querySelector('[part=content]');
|
|
278
|
+
const options = shadowRoot.querySelector('[data-monster-role=options]');
|
|
279
|
+
|
|
280
|
+
control.getBoundingClientRect = () => ({
|
|
281
|
+
width: 200,
|
|
282
|
+
height: 36,
|
|
283
|
+
top: 100,
|
|
284
|
+
left: 40,
|
|
285
|
+
right: 240,
|
|
286
|
+
bottom: 136,
|
|
287
|
+
x: 40,
|
|
288
|
+
y: 100
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
try {
|
|
293
|
+
shadowRoot.querySelector('[data-monster-role=container]').click();
|
|
294
|
+
setTimeout(() => {
|
|
295
|
+
try {
|
|
296
|
+
content.style.maxHeight = '20px';
|
|
297
|
+
options.style.height = '20px';
|
|
298
|
+
options.style.maxHeight = '20px';
|
|
299
|
+
|
|
300
|
+
select.calcAndSetOptionsDimension();
|
|
301
|
+
|
|
302
|
+
expect(parseFloat(content.style.maxHeight)).to.be.greaterThan(20);
|
|
303
|
+
expect(content.style.maxHeight).to.equal(options.style.maxHeight);
|
|
304
|
+
expect(parseFloat(popper.style.maxHeight)).to.be.at.least(parseFloat(content.style.maxHeight));
|
|
305
|
+
done();
|
|
306
|
+
} catch (e) {
|
|
307
|
+
done(e);
|
|
308
|
+
}
|
|
309
|
+
}, 80);
|
|
310
|
+
} catch (e) {
|
|
311
|
+
done(e);
|
|
312
|
+
}
|
|
313
|
+
}, 20);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('should clamp preferred width to the visible viewport width', function () {
|
|
317
|
+
const result = resolveSelectPopperWidthConstraints({
|
|
318
|
+
controlWidth: 120,
|
|
319
|
+
availableWidth: 176
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
expect(result.preferredWidth).to.equal(240);
|
|
323
|
+
expect(result.maxWidth).to.equal(176);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
it('should keep start placement while enabling cross-axis shifting', function () {
|
|
327
|
+
const result = getDefaultSelectPopperPositionProfile();
|
|
328
|
+
|
|
329
|
+
expect(result.placement).to.equal('bottom-start');
|
|
330
|
+
expect(result.middleware).to.deep.equal([
|
|
331
|
+
'flip',
|
|
332
|
+
'offset:4',
|
|
333
|
+
'shift:crossAxis',
|
|
334
|
+
'size'
|
|
335
|
+
]);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
it('should prefer the larger live viewport metrics after a resize', function () {
|
|
339
|
+
const result = resolveSelectViewportMetrics({
|
|
340
|
+
layoutWidth: 1400,
|
|
341
|
+
layoutHeight: 900,
|
|
342
|
+
visualWidth: 1024,
|
|
343
|
+
visualHeight: 700,
|
|
344
|
+
offsetLeft: 20,
|
|
345
|
+
offsetTop: 30,
|
|
346
|
+
padding: 12
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
expect(result.width).to.equal(1400);
|
|
350
|
+
expect(result.height).to.equal(900);
|
|
351
|
+
expect(result.left).to.equal(20);
|
|
352
|
+
expect(result.top).to.equal(30);
|
|
353
|
+
expect(result.padding).to.equal(12);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('should intersect viewport and split container for the visible rect', function () {
|
|
357
|
+
const result = resolveSelectVisibleRect({
|
|
358
|
+
viewportMetrics: {
|
|
359
|
+
width: 1400,
|
|
360
|
+
height: 900,
|
|
361
|
+
left: 0,
|
|
362
|
+
top: 0,
|
|
363
|
+
padding: 12
|
|
364
|
+
},
|
|
365
|
+
boundaryRect: {
|
|
366
|
+
left: 100,
|
|
367
|
+
top: 80,
|
|
368
|
+
right: 620,
|
|
369
|
+
bottom: 700
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(result.left).to.equal(100);
|
|
374
|
+
expect(result.top).to.equal(80);
|
|
375
|
+
expect(result.right).to.equal(620);
|
|
376
|
+
expect(result.bottom).to.equal(700);
|
|
377
|
+
expect(result.width).to.equal(520);
|
|
378
|
+
expect(result.height).to.equal(620);
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
|
|
188
382
|
describe('Remote filter pagination', function () {
|
|
189
383
|
let requestCount = 0;
|
|
190
384
|
|
|
@@ -549,6 +743,46 @@ describe('Select', function () {
|
|
|
549
743
|
}, 50);
|
|
550
744
|
});
|
|
551
745
|
|
|
746
|
+
it('should not throw when IntersectionObserver is unavailable', function (done) {
|
|
747
|
+
this.timeout(2000);
|
|
748
|
+
|
|
749
|
+
let mocks = document.getElementById('mocks');
|
|
750
|
+
const savedIntersectionObserver = global.IntersectionObserver;
|
|
751
|
+
global.IntersectionObserver = undefined;
|
|
752
|
+
window.IntersectionObserver = undefined;
|
|
753
|
+
const failures = [];
|
|
754
|
+
const onError = (event) => {
|
|
755
|
+
failures.push(event?.error || event);
|
|
756
|
+
};
|
|
757
|
+
window.addEventListener('error', onError);
|
|
758
|
+
|
|
759
|
+
const select = document.createElement('monster-select');
|
|
760
|
+
select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
|
|
761
|
+
select.setOption('filter.mode', 'remote');
|
|
762
|
+
select.setOption('mapping.selector', 'items.*');
|
|
763
|
+
select.setOption('mapping.labelTemplate', '${name}');
|
|
764
|
+
select.setOption('mapping.valueTemplate', '${id}');
|
|
765
|
+
select.setOption('mapping.total', 'pagination.total');
|
|
766
|
+
select.setOption('mapping.currentPage', 'pagination.page');
|
|
767
|
+
select.setOption('mapping.objectsPerPage', 'pagination.perPage');
|
|
768
|
+
select.setOption('selection', [{value: 'alpha'}]);
|
|
769
|
+
mocks.appendChild(select);
|
|
770
|
+
|
|
771
|
+
setTimeout(() => {
|
|
772
|
+
try {
|
|
773
|
+
expect(failures).to.have.length(0);
|
|
774
|
+
} catch (e) {
|
|
775
|
+
return done(e);
|
|
776
|
+
} finally {
|
|
777
|
+
window.removeEventListener('error', onError);
|
|
778
|
+
global.IntersectionObserver = savedIntersectionObserver;
|
|
779
|
+
window.IntersectionObserver = savedIntersectionObserver;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
done();
|
|
783
|
+
}, 250);
|
|
784
|
+
});
|
|
785
|
+
|
|
552
786
|
});
|
|
553
787
|
|
|
554
788
|
|
|
@@ -14,6 +14,7 @@ export function setupIntersectionObserverMock(
|
|
|
14
14
|
} = {}) {
|
|
15
15
|
|
|
16
16
|
const savedImplementation = window.IntersectionObserver;
|
|
17
|
+
const savedGlobalImplementation = global.IntersectionObserver;
|
|
17
18
|
|
|
18
19
|
let lastObject;
|
|
19
20
|
|
|
@@ -61,9 +62,10 @@ export function setupIntersectionObserverMock(
|
|
|
61
62
|
return {
|
|
62
63
|
restore: function () {
|
|
63
64
|
window.IntersectionObserver = savedImplementation;
|
|
65
|
+
global.IntersectionObserver = savedGlobalImplementation;
|
|
64
66
|
},
|
|
65
67
|
getInstance: function () {
|
|
66
68
|
return lastObject;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
|
-
}
|
|
71
|
+
}
|