@schukai/monster 4.145.2 → 4.146.0
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/choice-cards.mjs +13 -2
- package/source/components/form/control-bar-spacer.mjs +3 -0
- package/source/components/form/control-bar.mjs +19 -2
- package/source/components/layout/style/tabs.pcss +13 -0
- package/source/components/layout/stylesheet/tabs.mjs +1 -1
- package/source/components/layout/tabs.mjs +808 -30
- package/test/cases/components/form/select.mjs +46 -0
- package/test/cases/components/layout/tabs.mjs +872 -463
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {getGlobal} from "../../../../source/types/global.mjs";
|
|
2
|
-
import * as chai from
|
|
3
|
-
import {chaiDom} from "../../../util/chai-dom.mjs";
|
|
4
|
-
import {initJSDOM} from "../../../util/jsdom.mjs";
|
|
5
|
-
import {ResizeObserverMock} from "../../../util/resize-observer.mjs";
|
|
1
|
+
import { getGlobal } from "../../../../source/types/global.mjs";
|
|
2
|
+
import * as chai from "chai";
|
|
3
|
+
import { chaiDom } from "../../../util/chai-dom.mjs";
|
|
4
|
+
import { initJSDOM } from "../../../util/jsdom.mjs";
|
|
5
|
+
import { ResizeObserverMock } from "../../../util/resize-observer.mjs";
|
|
6
6
|
|
|
7
7
|
let expect = chai.expect;
|
|
8
8
|
chai.use(chaiDom);
|
|
9
9
|
|
|
10
10
|
const global = getGlobal();
|
|
11
11
|
|
|
12
|
-
|
|
13
12
|
// language=html
|
|
14
13
|
let html1 = `
|
|
15
14
|
<monster-tabs id="mytabs">
|
|
@@ -145,7 +144,8 @@ let htmlNoDerivedLabel = `
|
|
|
145
144
|
</monster-tabs>
|
|
146
145
|
`;
|
|
147
146
|
|
|
148
|
-
const svgTabIcon =
|
|
147
|
+
const svgTabIcon =
|
|
148
|
+
"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 16 16'%3E%3Cpath d='M8 1 15 15H1z'/%3E%3C/svg%3E";
|
|
149
149
|
const pngTabIcon = "data:image/png;base64,iVBORw0KGgo=";
|
|
150
150
|
|
|
151
151
|
// language=html
|
|
@@ -180,7 +180,7 @@ let VerticalTabs;
|
|
|
180
180
|
let restoreBoundingClientRect = null;
|
|
181
181
|
let restoreResizeObserver = null;
|
|
182
182
|
|
|
183
|
-
function waitForCondition(check, {timeout = 4000, interval = 10} = {}) {
|
|
183
|
+
function waitForCondition(check, { timeout = 4000, interval = 10 } = {}) {
|
|
184
184
|
const startedAt = Date.now();
|
|
185
185
|
|
|
186
186
|
return new Promise((resolve, reject) => {
|
|
@@ -196,7 +196,7 @@ function waitForCondition(check, {timeout = 4000, interval = 10} = {}) {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
if (Date.now() - startedAt > timeout) {
|
|
199
|
-
reject(new Error(
|
|
199
|
+
reject(new Error("condition timed out"));
|
|
200
200
|
return;
|
|
201
201
|
}
|
|
202
202
|
|
|
@@ -207,39 +207,129 @@ function waitForCondition(check, {timeout = 4000, interval = 10} = {}) {
|
|
|
207
207
|
});
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
|
|
210
|
+
function createRect({ width, height = 40, x = 0, y = 0 }) {
|
|
211
|
+
return {
|
|
212
|
+
width,
|
|
213
|
+
height,
|
|
214
|
+
top: y,
|
|
215
|
+
left: x,
|
|
216
|
+
right: x + width,
|
|
217
|
+
bottom: y + height,
|
|
218
|
+
x,
|
|
219
|
+
y,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function installOverflowTabGeometryMock({
|
|
224
|
+
navWidth = 480,
|
|
225
|
+
switchWidth = 44,
|
|
226
|
+
} = {}) {
|
|
227
|
+
const originalGetBoundingClientRect =
|
|
228
|
+
global.HTMLElement.prototype.getBoundingClientRect;
|
|
229
|
+
|
|
230
|
+
global.HTMLElement.prototype.getBoundingClientRect = function () {
|
|
231
|
+
const role = this.getAttribute?.("data-monster-role");
|
|
232
|
+
const label = this.textContent?.trim() || "";
|
|
233
|
+
|
|
234
|
+
if (role === "nav") {
|
|
235
|
+
return createRect({ width: navWidth });
|
|
236
|
+
}
|
|
211
237
|
|
|
212
|
-
|
|
213
|
-
|
|
238
|
+
if (role === "button") {
|
|
239
|
+
return createRect({ width: Math.max(70, label.length * 11) });
|
|
240
|
+
}
|
|
214
241
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
242
|
+
if (role === "switch") {
|
|
243
|
+
return createRect({ width: switchWidth });
|
|
244
|
+
}
|
|
218
245
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const Crypto = m['Crypto'];
|
|
222
|
-
global['crypto'] = new Crypto();
|
|
223
|
-
}));
|
|
224
|
-
}
|
|
246
|
+
return originalGetBoundingClientRect.call(this);
|
|
247
|
+
};
|
|
225
248
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}))
|
|
249
|
+
return () => {
|
|
250
|
+
global.HTMLElement.prototype.getBoundingClientRect =
|
|
251
|
+
originalGetBoundingClientRect;
|
|
252
|
+
};
|
|
253
|
+
}
|
|
232
254
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
255
|
+
function createDragEvent(type, reference) {
|
|
256
|
+
const event = new Event(type, {
|
|
257
|
+
bubbles: true,
|
|
258
|
+
cancelable: true,
|
|
259
|
+
composed: true,
|
|
260
|
+
});
|
|
261
|
+
event.dataTransfer = {
|
|
262
|
+
effectAllowed: "",
|
|
263
|
+
dropEffect: "",
|
|
264
|
+
setData(format, value) {
|
|
265
|
+
this[format] = value;
|
|
266
|
+
},
|
|
267
|
+
getData(format) {
|
|
268
|
+
return this[format] || "";
|
|
269
|
+
},
|
|
270
|
+
};
|
|
271
|
+
if (reference) {
|
|
272
|
+
event.dataTransfer.setData("text/plain", reference);
|
|
273
|
+
}
|
|
274
|
+
return event;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function dragTab(tabs, sourceReference, targetReference) {
|
|
278
|
+
const sourceButton = tabs.shadowRoot.querySelector(
|
|
279
|
+
`button[part=button][data-monster-tab-reference="${sourceReference}"]`,
|
|
280
|
+
);
|
|
281
|
+
const targetButton = tabs.shadowRoot.querySelector(
|
|
282
|
+
`button[part=button][data-monster-tab-reference="${targetReference}"]`,
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
expect(sourceButton).to.not.equal(null);
|
|
286
|
+
expect(targetButton).to.not.equal(null);
|
|
287
|
+
|
|
288
|
+
sourceButton.dispatchEvent(createDragEvent("dragstart", sourceReference));
|
|
289
|
+
targetButton.dispatchEvent(createDragEvent("dragover", sourceReference));
|
|
290
|
+
targetButton.dispatchEvent(createDragEvent("drop", sourceReference));
|
|
291
|
+
sourceButton.dispatchEvent(createDragEvent("dragend", sourceReference));
|
|
292
|
+
}
|
|
236
293
|
|
|
237
|
-
|
|
238
|
-
|
|
294
|
+
describe("Tabs", function () {
|
|
295
|
+
before(function (done) {
|
|
296
|
+
initJSDOM().then(() => {
|
|
297
|
+
import("element-internals-polyfill").catch((e) => done(e));
|
|
239
298
|
|
|
240
|
-
|
|
299
|
+
let promises = [];
|
|
241
300
|
|
|
242
|
-
|
|
301
|
+
if (!global["crypto"]) {
|
|
302
|
+
promises.push(
|
|
303
|
+
import("@peculiar/webcrypto").then((m) => {
|
|
304
|
+
const Crypto = m["Crypto"];
|
|
305
|
+
global["crypto"] = new Crypto();
|
|
306
|
+
}),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
promises.push(
|
|
311
|
+
import("../../../../source/components/layout/tabs.mjs").then((m) => {
|
|
312
|
+
Tabs = m["Tabs"];
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
315
|
+
promises.push(
|
|
316
|
+
import("../../../../source/components/layout/vertical-tabs.mjs").then(
|
|
317
|
+
(m) => {
|
|
318
|
+
VerticalTabs = m["VerticalTabs"];
|
|
319
|
+
},
|
|
320
|
+
),
|
|
321
|
+
);
|
|
322
|
+
|
|
323
|
+
Promise.all(promises)
|
|
324
|
+
.then(() => {
|
|
325
|
+
done();
|
|
326
|
+
})
|
|
327
|
+
.catch((e) => done(e));
|
|
328
|
+
});
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
describe("document.createElement()", function () {
|
|
332
|
+
afterEach(() => {
|
|
243
333
|
if (restoreBoundingClientRect instanceof Function) {
|
|
244
334
|
restoreBoundingClientRect();
|
|
245
335
|
restoreBoundingClientRect = null;
|
|
@@ -248,136 +338,147 @@ describe('Tabs', function () {
|
|
|
248
338
|
restoreResizeObserver();
|
|
249
339
|
restoreResizeObserver = null;
|
|
250
340
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
it('should have buttons and tabs', function (done) {
|
|
256
|
-
|
|
257
|
-
let mocks = document.getElementById('mocks');
|
|
258
|
-
mocks.innerHTML = html1;
|
|
259
|
-
|
|
260
|
-
setTimeout(() => {
|
|
261
|
-
try {
|
|
262
|
-
const tabs = document.getElementById('mytabs')
|
|
263
|
-
expect(tabs).is.instanceof(Tabs);
|
|
264
|
-
|
|
265
|
-
setTimeout(() => {
|
|
266
|
-
let nav = tabs.shadowRoot.querySelector('nav');
|
|
267
|
-
const buttons = tabs.shadowRoot.querySelectorAll('button[part=button]');
|
|
268
|
-
expect(buttons[0]).is.instanceof(HTMLButtonElement);
|
|
269
|
-
expect(nav.hasChildNodes()).to.be.true;
|
|
270
|
-
expect(buttons.length).to.be.equal(4);
|
|
271
|
-
done();
|
|
272
|
-
}, 100)
|
|
273
|
-
|
|
274
|
-
} catch (e) {
|
|
275
|
-
return done(e);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
}, 0)
|
|
341
|
+
let mocks = document.getElementById("mocks");
|
|
342
|
+
mocks.innerHTML = "";
|
|
343
|
+
});
|
|
279
344
|
|
|
345
|
+
it("should have buttons and tabs", function (done) {
|
|
346
|
+
let mocks = document.getElementById("mocks");
|
|
347
|
+
mocks.innerHTML = html1;
|
|
280
348
|
|
|
281
|
-
|
|
349
|
+
setTimeout(() => {
|
|
350
|
+
try {
|
|
351
|
+
const tabs = document.getElementById("mytabs");
|
|
352
|
+
expect(tabs).is.instanceof(Tabs);
|
|
282
353
|
|
|
283
|
-
|
|
354
|
+
setTimeout(() => {
|
|
355
|
+
let nav = tabs.shadowRoot.querySelector("nav");
|
|
356
|
+
const buttons = tabs.shadowRoot.querySelectorAll(
|
|
357
|
+
"button[part=button]",
|
|
358
|
+
);
|
|
359
|
+
expect(buttons[0]).is.instanceof(HTMLButtonElement);
|
|
360
|
+
expect(nav.hasChildNodes()).to.be.true;
|
|
361
|
+
expect(buttons.length).to.be.equal(4);
|
|
362
|
+
done();
|
|
363
|
+
}, 100);
|
|
364
|
+
} catch (e) {
|
|
365
|
+
return done(e);
|
|
366
|
+
}
|
|
367
|
+
}, 0);
|
|
368
|
+
});
|
|
284
369
|
|
|
285
|
-
|
|
286
|
-
|
|
370
|
+
it("should shorten label from content when no explicit label is provided", function (done) {
|
|
371
|
+
let mocks = document.getElementById("mocks");
|
|
372
|
+
mocks.innerHTML = html1;
|
|
287
373
|
|
|
288
374
|
setTimeout(() => {
|
|
289
375
|
try {
|
|
290
|
-
const tabs = document.getElementById(
|
|
376
|
+
const tabs = document.getElementById("mytabs");
|
|
291
377
|
expect(tabs).is.instanceof(Tabs);
|
|
292
378
|
|
|
293
379
|
setTimeout(() => {
|
|
294
380
|
const thirdTab = tabs.children[2];
|
|
295
381
|
expect(thirdTab).to.not.equal(undefined);
|
|
296
|
-
const reference = thirdTab.getAttribute(
|
|
382
|
+
const reference = thirdTab.getAttribute("id");
|
|
297
383
|
expect(reference).to.not.equal(null);
|
|
298
384
|
const button = tabs.shadowRoot.querySelector(
|
|
299
385
|
`button[part=button][data-monster-tab-reference="${reference}"]`,
|
|
300
386
|
);
|
|
301
387
|
expect(button).to.not.equal(null);
|
|
302
|
-
const labelSpan = button.querySelector(
|
|
388
|
+
const labelSpan = button.querySelector(
|
|
389
|
+
"span[data-monster-replace]",
|
|
390
|
+
);
|
|
303
391
|
expect(labelSpan).to.not.equal(null);
|
|
304
|
-
expect(labelSpan.textContent.trim()).to.equal(
|
|
392
|
+
expect(labelSpan.textContent.trim()).to.equal("Das ist tab…");
|
|
305
393
|
done();
|
|
306
394
|
}, 100);
|
|
307
395
|
} catch (e) {
|
|
308
396
|
return done(e);
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
it('should open the first tab by default when no tab is predefined as active', function (done) {
|
|
397
|
+
}
|
|
398
|
+
}, 0);
|
|
399
|
+
});
|
|
314
400
|
|
|
315
|
-
|
|
316
|
-
|
|
401
|
+
it("should open the first tab by default when no tab is predefined as active", function (done) {
|
|
402
|
+
let mocks = document.getElementById("mocks");
|
|
403
|
+
mocks.innerHTML = html1;
|
|
317
404
|
|
|
318
405
|
setTimeout(() => {
|
|
319
406
|
try {
|
|
320
|
-
const tabs = document.getElementById(
|
|
407
|
+
const tabs = document.getElementById("mytabs");
|
|
321
408
|
expect(tabs).is.instanceof(Tabs);
|
|
322
409
|
|
|
323
410
|
setTimeout(() => {
|
|
324
|
-
expect(tabs.children[0].classList.contains(
|
|
325
|
-
|
|
326
|
-
|
|
411
|
+
expect(tabs.children[0].classList.contains("active")).to.equal(
|
|
412
|
+
true,
|
|
413
|
+
);
|
|
414
|
+
expect(tabs.children[1].classList.contains("active")).to.equal(
|
|
415
|
+
false,
|
|
416
|
+
);
|
|
417
|
+
expect(tabs.getActiveTab()).to.equal(
|
|
418
|
+
tabs.children[0].getAttribute("id"),
|
|
419
|
+
);
|
|
327
420
|
done();
|
|
328
421
|
}, 100);
|
|
329
422
|
} catch (e) {
|
|
330
423
|
return done(e);
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
it('should keep a predefined active tab instead of opening the first tab', function (done) {
|
|
424
|
+
}
|
|
425
|
+
}, 0);
|
|
426
|
+
});
|
|
336
427
|
|
|
337
|
-
|
|
338
|
-
|
|
428
|
+
it("should keep a predefined active tab instead of opening the first tab", function (done) {
|
|
429
|
+
let mocks = document.getElementById("mocks");
|
|
430
|
+
mocks.innerHTML = htmlActiveSecond;
|
|
339
431
|
|
|
340
432
|
setTimeout(() => {
|
|
341
433
|
try {
|
|
342
|
-
const tabs = document.getElementById(
|
|
434
|
+
const tabs = document.getElementById("mytabs");
|
|
343
435
|
expect(tabs).is.instanceof(Tabs);
|
|
344
436
|
|
|
345
437
|
setTimeout(() => {
|
|
346
|
-
expect(tabs.children[0].classList.contains(
|
|
347
|
-
|
|
348
|
-
|
|
438
|
+
expect(tabs.children[0].classList.contains("active")).to.equal(
|
|
439
|
+
false,
|
|
440
|
+
);
|
|
441
|
+
expect(tabs.children[1].classList.contains("active")).to.equal(
|
|
442
|
+
true,
|
|
443
|
+
);
|
|
444
|
+
expect(tabs.getActiveTab()).to.equal(
|
|
445
|
+
tabs.children[1].getAttribute("id"),
|
|
446
|
+
);
|
|
349
447
|
done();
|
|
350
448
|
}, 100);
|
|
351
449
|
} catch (e) {
|
|
352
450
|
return done(e);
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
it('should configure flip and shift middleware for the overflow popper', function (done) {
|
|
451
|
+
}
|
|
452
|
+
}, 0);
|
|
453
|
+
});
|
|
358
454
|
|
|
359
|
-
|
|
360
|
-
|
|
455
|
+
it("should configure flip and shift middleware for the overflow popper", function (done) {
|
|
456
|
+
let mocks = document.getElementById("mocks");
|
|
457
|
+
mocks.innerHTML = html1;
|
|
361
458
|
|
|
362
459
|
setTimeout(() => {
|
|
363
460
|
try {
|
|
364
|
-
const tabs = document.getElementById(
|
|
461
|
+
const tabs = document.getElementById("mytabs");
|
|
365
462
|
expect(tabs).is.instanceof(Tabs);
|
|
366
463
|
|
|
367
|
-
const modifiers = tabs.getOption(
|
|
368
|
-
expect(modifiers.some((entry) => entry?.name ===
|
|
369
|
-
|
|
464
|
+
const modifiers = tabs.getOption("popper.modifiers");
|
|
465
|
+
expect(modifiers.some((entry) => entry?.name === "flip")).to.equal(
|
|
466
|
+
true,
|
|
467
|
+
);
|
|
468
|
+
expect(modifiers.some((entry) => entry?.name === "shift")).to.equal(
|
|
469
|
+
true,
|
|
470
|
+
);
|
|
370
471
|
done();
|
|
371
472
|
} catch (e) {
|
|
372
473
|
return done(e);
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
474
|
+
}
|
|
475
|
+
}, 0);
|
|
476
|
+
});
|
|
376
477
|
|
|
377
|
-
it(
|
|
478
|
+
it("should move overflowing tabs into the popper without losing them", function (done) {
|
|
378
479
|
this.timeout(5000);
|
|
379
480
|
|
|
380
|
-
|
|
481
|
+
let mocks = document.getElementById("mocks");
|
|
381
482
|
const OriginalResizeObserver = window.ResizeObserver;
|
|
382
483
|
const originalGlobalResizeObserver = global.ResizeObserver;
|
|
383
484
|
class TrackingResizeObserver extends ResizeObserverMock {
|
|
@@ -398,54 +499,17 @@ describe('Tabs', function () {
|
|
|
398
499
|
window.ResizeObserver = OriginalResizeObserver;
|
|
399
500
|
global.ResizeObserver = originalGlobalResizeObserver;
|
|
400
501
|
};
|
|
401
|
-
|
|
402
|
-
global.HTMLElement.prototype.getBoundingClientRect;
|
|
403
|
-
restoreBoundingClientRect = () => {
|
|
404
|
-
global.HTMLElement.prototype.getBoundingClientRect =
|
|
405
|
-
originalGetBoundingClientRect;
|
|
406
|
-
};
|
|
407
|
-
global.HTMLElement.prototype.getBoundingClientRect = function () {
|
|
408
|
-
const role = this.getAttribute?.('data-monster-role');
|
|
409
|
-
const label = this.textContent?.trim() || '';
|
|
410
|
-
|
|
411
|
-
if (role === 'nav') {
|
|
412
|
-
return {
|
|
413
|
-
width: 480,
|
|
414
|
-
height: 40,
|
|
415
|
-
top: 0,
|
|
416
|
-
left: 0,
|
|
417
|
-
right: 480,
|
|
418
|
-
bottom: 40,
|
|
419
|
-
x: 0,
|
|
420
|
-
y: 0,
|
|
421
|
-
};
|
|
422
|
-
}
|
|
502
|
+
restoreBoundingClientRect = installOverflowTabGeometryMock();
|
|
423
503
|
|
|
424
|
-
|
|
425
|
-
return {
|
|
426
|
-
width: Math.max(70, label.length * 11),
|
|
427
|
-
height: 40,
|
|
428
|
-
top: 0,
|
|
429
|
-
left: 0,
|
|
430
|
-
right: 0,
|
|
431
|
-
bottom: 40,
|
|
432
|
-
x: 0,
|
|
433
|
-
y: 0,
|
|
434
|
-
};
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
return originalGetBoundingClientRect.call(this);
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
mocks.innerHTML = htmlOverflow;
|
|
504
|
+
mocks.innerHTML = htmlOverflow;
|
|
441
505
|
|
|
442
506
|
waitForCondition(() => {
|
|
443
|
-
const tabs = document.getElementById(
|
|
507
|
+
const tabs = document.getElementById("overflow-tabs");
|
|
444
508
|
const switchButton = tabs?.shadowRoot?.querySelector(
|
|
445
509
|
'[data-monster-role="switch"]',
|
|
446
510
|
);
|
|
447
|
-
const standardButtons = tabs?.getOption?.(
|
|
448
|
-
const popperButtons = tabs?.getOption?.(
|
|
511
|
+
const standardButtons = tabs?.getOption?.("buttons.standard") || [];
|
|
512
|
+
const popperButtons = tabs?.getOption?.("buttons.popper") || [];
|
|
449
513
|
|
|
450
514
|
return (
|
|
451
515
|
tabs instanceof Tabs &&
|
|
@@ -453,406 +517,751 @@ describe('Tabs', function () {
|
|
|
453
517
|
standardButtons.length > 0 &&
|
|
454
518
|
popperButtons.length > 0 &&
|
|
455
519
|
standardButtons.length + popperButtons.length === 11 &&
|
|
456
|
-
switchButton.classList.contains(
|
|
520
|
+
switchButton.classList.contains("hidden") === false
|
|
457
521
|
);
|
|
458
|
-
})
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
522
|
+
})
|
|
523
|
+
.then(() => {
|
|
524
|
+
let tabs;
|
|
525
|
+
let originalSetOption;
|
|
526
|
+
try {
|
|
527
|
+
tabs = document.getElementById("overflow-tabs");
|
|
528
|
+
expect(tabs).is.instanceof(Tabs);
|
|
529
|
+
const switchButton = tabs.shadowRoot.querySelector(
|
|
530
|
+
'[data-monster-role="switch"]',
|
|
531
|
+
);
|
|
532
|
+
let standardButtons = tabs.getOption("buttons.standard");
|
|
533
|
+
let popperButtons = tabs.getOption("buttons.popper");
|
|
534
|
+
|
|
535
|
+
expect(switchButton).to.not.equal(null);
|
|
536
|
+
expect(standardButtons.length).to.be.greaterThan(0);
|
|
537
|
+
expect(popperButtons.length).to.be.greaterThan(0);
|
|
538
|
+
expect(standardButtons.length + popperButtons.length).to.equal(11);
|
|
539
|
+
expect(switchButton.classList.contains("hidden")).to.equal(false);
|
|
540
|
+
|
|
541
|
+
const resizeObserver = TrackingResizeObserver.instances.find(
|
|
542
|
+
(observer) =>
|
|
543
|
+
observer.elements.includes(
|
|
544
|
+
tabs.shadowRoot.querySelector('[data-monster-role="nav"]'),
|
|
545
|
+
),
|
|
546
|
+
);
|
|
547
|
+
expect(resizeObserver).to.not.equal(undefined);
|
|
548
|
+
|
|
549
|
+
originalSetOption = tabs.setOption.bind(tabs);
|
|
550
|
+
let resetAllTabsDuringResize = false;
|
|
551
|
+
tabs.setOption = function (path, value) {
|
|
552
|
+
if (
|
|
553
|
+
path === "buttons" &&
|
|
554
|
+
value?.standard?.length === 11 &&
|
|
555
|
+
value?.popper?.length === 0
|
|
556
|
+
) {
|
|
557
|
+
resetAllTabsDuringResize = true;
|
|
558
|
+
}
|
|
559
|
+
return originalSetOption(path, value);
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
resizeObserver.triggerResize([]);
|
|
467
563
|
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
564
|
+
return waitForCondition(() => {
|
|
565
|
+
return TrackingResizeObserver.callbackCount > 0;
|
|
566
|
+
})
|
|
567
|
+
.then(() => {
|
|
568
|
+
return new Promise((resolve) => setTimeout(resolve, 260));
|
|
569
|
+
})
|
|
570
|
+
.then(() => {
|
|
571
|
+
return new Promise((resolve) => requestAnimationFrame(resolve));
|
|
572
|
+
})
|
|
573
|
+
.then(() => {
|
|
574
|
+
tabs.setOption = originalSetOption;
|
|
575
|
+
expect(resetAllTabsDuringResize).to.equal(false);
|
|
576
|
+
standardButtons = tabs.getOption("buttons.standard");
|
|
577
|
+
popperButtons = tabs.getOption("buttons.popper");
|
|
578
|
+
expect(standardButtons.length).to.be.greaterThan(0);
|
|
579
|
+
expect(popperButtons.length).to.be.greaterThan(0);
|
|
580
|
+
expect(standardButtons.length + popperButtons.length).to.equal(
|
|
581
|
+
11,
|
|
582
|
+
);
|
|
583
|
+
expect(
|
|
584
|
+
new Set(
|
|
585
|
+
standardButtons
|
|
586
|
+
.concat(popperButtons)
|
|
587
|
+
.map((button) => button.reference),
|
|
588
|
+
).size,
|
|
589
|
+
).to.equal(11);
|
|
590
|
+
})
|
|
591
|
+
.then(() => {
|
|
592
|
+
restoreBoundingClientRect();
|
|
593
|
+
restoreBoundingClientRect = null;
|
|
594
|
+
restoreResizeObserver();
|
|
595
|
+
restoreResizeObserver = null;
|
|
596
|
+
done();
|
|
597
|
+
});
|
|
598
|
+
} catch (e) {
|
|
599
|
+
if (tabs && originalSetOption) {
|
|
600
|
+
tabs.setOption = originalSetOption;
|
|
601
|
+
}
|
|
602
|
+
restoreBoundingClientRect();
|
|
603
|
+
restoreBoundingClientRect = null;
|
|
604
|
+
restoreResizeObserver();
|
|
605
|
+
restoreResizeObserver = null;
|
|
606
|
+
return done(e);
|
|
607
|
+
}
|
|
608
|
+
})
|
|
609
|
+
.catch((e) => {
|
|
610
|
+
const tabs = document.getElementById("overflow-tabs");
|
|
611
|
+
if (tabs && Object.prototype.hasOwnProperty.call(tabs, "setOption")) {
|
|
612
|
+
delete tabs.setOption;
|
|
613
|
+
}
|
|
614
|
+
if (restoreBoundingClientRect instanceof Function) {
|
|
615
|
+
restoreBoundingClientRect();
|
|
616
|
+
restoreBoundingClientRect = null;
|
|
617
|
+
}
|
|
618
|
+
if (restoreResizeObserver instanceof Function) {
|
|
619
|
+
restoreResizeObserver();
|
|
620
|
+
restoreResizeObserver = null;
|
|
621
|
+
}
|
|
622
|
+
done(e);
|
|
623
|
+
});
|
|
624
|
+
});
|
|
473
625
|
|
|
474
|
-
|
|
475
|
-
|
|
626
|
+
it("should preserve the overflow split and recalculate widths while rebuilding tab buttons", async function () {
|
|
627
|
+
this.timeout(5000);
|
|
628
|
+
|
|
629
|
+
const mocks = document.getElementById("mocks");
|
|
630
|
+
restoreBoundingClientRect = installOverflowTabGeometryMock();
|
|
631
|
+
|
|
632
|
+
let tabs;
|
|
633
|
+
let originalSetOption;
|
|
634
|
+
|
|
635
|
+
try {
|
|
636
|
+
mocks.innerHTML = htmlOverflow;
|
|
637
|
+
|
|
638
|
+
await waitForCondition(() => {
|
|
639
|
+
tabs = document.getElementById("overflow-tabs");
|
|
640
|
+
const standardButtons = tabs?.getOption?.("buttons.standard") || [];
|
|
641
|
+
const popperButtons = tabs?.getOption?.("buttons.popper") || [];
|
|
642
|
+
|
|
643
|
+
return (
|
|
644
|
+
tabs instanceof Tabs &&
|
|
645
|
+
standardButtons.length > 0 &&
|
|
646
|
+
popperButtons.length > 0 &&
|
|
647
|
+
standardButtons.length + popperButtons.length === 11
|
|
476
648
|
);
|
|
477
|
-
|
|
478
|
-
resizeObserver.triggerResize([]);
|
|
649
|
+
});
|
|
479
650
|
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
651
|
+
const initialPopperCount = tabs.getOption("buttons.popper").length;
|
|
652
|
+
const standardReference = tabs.getOption(
|
|
653
|
+
"buttons.standard.0.reference",
|
|
654
|
+
);
|
|
655
|
+
const panel = document.getElementById(standardReference);
|
|
656
|
+
expect(panel).to.not.equal(null);
|
|
657
|
+
|
|
658
|
+
originalSetOption = tabs.setOption.bind(tabs);
|
|
659
|
+
let resetAllTabsDuringRebuild = false;
|
|
660
|
+
tabs.setOption = function (path, value) {
|
|
661
|
+
if (
|
|
662
|
+
path === "buttons" &&
|
|
663
|
+
value?.standard?.length === 11 &&
|
|
664
|
+
value?.popper?.length === 0
|
|
665
|
+
) {
|
|
666
|
+
resetAllTabsDuringRebuild = true;
|
|
667
|
+
}
|
|
668
|
+
return originalSetOption(path, value);
|
|
669
|
+
};
|
|
670
|
+
|
|
671
|
+
panel.setAttribute(
|
|
672
|
+
"data-monster-button-label",
|
|
673
|
+
"Aktualisierte sehr lange Tab Beschriftung mit viel mehr Platzbedarf",
|
|
674
|
+
);
|
|
675
|
+
|
|
676
|
+
await waitForCondition(() => {
|
|
677
|
+
return tabs
|
|
678
|
+
.getOption("buttons.standard", [])
|
|
679
|
+
.concat(tabs.getOption("buttons.popper", []))
|
|
680
|
+
.some((button) => {
|
|
490
681
|
return (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
standardButtons.length + popperButtons.length === 11 &&
|
|
494
|
-
new Set(
|
|
495
|
-
standardButtons
|
|
496
|
-
.concat(popperButtons)
|
|
497
|
-
.map((button) => button.reference),
|
|
498
|
-
).size === 11
|
|
682
|
+
button.reference === standardReference &&
|
|
683
|
+
button.label.includes("Aktualisierte")
|
|
499
684
|
);
|
|
500
685
|
});
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
restoreBoundingClientRect();
|
|
518
|
-
restoreBoundingClientRect = null;
|
|
519
|
-
}
|
|
520
|
-
if (restoreResizeObserver instanceof Function) {
|
|
521
|
-
restoreResizeObserver();
|
|
522
|
-
restoreResizeObserver = null;
|
|
686
|
+
});
|
|
687
|
+
await waitForCondition(() => {
|
|
688
|
+
return tabs.getOption("buttons.popper").length > initialPopperCount;
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
tabs.setOption = originalSetOption;
|
|
692
|
+
expect(resetAllTabsDuringRebuild).to.equal(false);
|
|
693
|
+
expect(tabs.getOption("buttons.popper").length).to.be.greaterThan(0);
|
|
694
|
+
expect(
|
|
695
|
+
tabs
|
|
696
|
+
.getOption("buttons.standard")
|
|
697
|
+
.concat(tabs.getOption("buttons.popper")).length,
|
|
698
|
+
).to.equal(11);
|
|
699
|
+
} finally {
|
|
700
|
+
if (tabs && originalSetOption) {
|
|
701
|
+
tabs.setOption = originalSetOption;
|
|
523
702
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
703
|
+
restoreBoundingClientRect();
|
|
704
|
+
restoreBoundingClientRect = null;
|
|
705
|
+
}
|
|
706
|
+
});
|
|
527
707
|
|
|
528
|
-
it(
|
|
529
|
-
let mocks = document.getElementById(
|
|
708
|
+
it("should ignore unavailable panels when creating tabs and buttons", function (done) {
|
|
709
|
+
let mocks = document.getElementById("mocks");
|
|
530
710
|
mocks.innerHTML = htmlAvailability;
|
|
531
711
|
|
|
532
712
|
waitForCondition(() => {
|
|
533
|
-
const tabs = document.getElementById(
|
|
713
|
+
const tabs = document.getElementById("availability-tabs");
|
|
534
714
|
return (
|
|
535
715
|
tabs instanceof Tabs &&
|
|
536
|
-
tabs.shadowRoot.querySelectorAll(
|
|
537
|
-
|
|
716
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length ===
|
|
717
|
+
2 &&
|
|
718
|
+
tabs.getActiveTab() === "overview"
|
|
538
719
|
);
|
|
539
|
-
})
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
720
|
+
})
|
|
721
|
+
.then(() => {
|
|
722
|
+
try {
|
|
723
|
+
const tabs = document.getElementById("availability-tabs");
|
|
724
|
+
const vacation = document.getElementById("vacation-panel");
|
|
725
|
+
const buttons = tabs.shadowRoot.querySelectorAll(
|
|
726
|
+
"button[part=button]",
|
|
727
|
+
);
|
|
728
|
+
|
|
729
|
+
expect(tabs.getTabs().length).to.equal(2);
|
|
730
|
+
expect(tabs.getTabs().includes(vacation)).to.equal(false);
|
|
731
|
+
expect(
|
|
732
|
+
tabs.shadowRoot.querySelector(
|
|
733
|
+
`button[part=button][data-monster-tab-reference="${vacation.getAttribute("id")}"]`,
|
|
734
|
+
),
|
|
735
|
+
).to.equal(null);
|
|
736
|
+
expect(buttons[0].textContent.trim()).to.equal("Overview");
|
|
737
|
+
expect(buttons[1].textContent.trim()).to.equal("Details");
|
|
738
|
+
expect(tabs.getActiveTab()).to.equal("overview");
|
|
739
|
+
done();
|
|
740
|
+
} catch (e) {
|
|
741
|
+
done(e);
|
|
742
|
+
}
|
|
743
|
+
})
|
|
744
|
+
.catch(done);
|
|
558
745
|
});
|
|
559
746
|
|
|
560
|
-
it(
|
|
561
|
-
let mocks = document.getElementById(
|
|
747
|
+
it("should ignore unavailable tabs in activeTab and rebuild when they become available", function (done) {
|
|
748
|
+
let mocks = document.getElementById("mocks");
|
|
562
749
|
mocks.innerHTML = htmlAvailability;
|
|
563
750
|
|
|
564
751
|
waitForCondition(() => {
|
|
565
|
-
const tabs = document.getElementById(
|
|
566
|
-
return tabs instanceof Tabs && tabs.getActiveTab() ===
|
|
567
|
-
})
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
752
|
+
const tabs = document.getElementById("availability-tabs");
|
|
753
|
+
return tabs instanceof Tabs && tabs.getActiveTab() === "overview";
|
|
754
|
+
})
|
|
755
|
+
.then(() => {
|
|
756
|
+
const tabs = document.getElementById("availability-tabs");
|
|
757
|
+
const vacation = document.getElementById("vacation-panel");
|
|
758
|
+
let availabilityEvent = null;
|
|
759
|
+
tabs.addEventListener("monster-tab-availability-changed", (event) => {
|
|
760
|
+
availabilityEvent = event;
|
|
761
|
+
});
|
|
574
762
|
|
|
575
|
-
|
|
576
|
-
|
|
763
|
+
tabs.activeTab("vacation");
|
|
764
|
+
expect(tabs.getActiveTab()).to.equal("overview");
|
|
577
765
|
|
|
578
|
-
|
|
766
|
+
vacation.setAttribute("data-monster-tab-available", "true");
|
|
579
767
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
768
|
+
return waitForCondition(() => {
|
|
769
|
+
return (
|
|
770
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length ===
|
|
771
|
+
3
|
|
772
|
+
);
|
|
773
|
+
}).then(() => {
|
|
774
|
+
try {
|
|
775
|
+
expect(availabilityEvent).to.not.equal(null);
|
|
776
|
+
expect(availabilityEvent.detail.name).to.equal("vacation");
|
|
777
|
+
expect(availabilityEvent.detail.available).to.equal(true);
|
|
587
778
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
779
|
+
tabs.activeTab("vacation");
|
|
780
|
+
expect(tabs.getActiveTab()).to.equal("vacation");
|
|
781
|
+
done();
|
|
782
|
+
} catch (e) {
|
|
783
|
+
done(e);
|
|
784
|
+
}
|
|
785
|
+
}, 100);
|
|
786
|
+
})
|
|
787
|
+
.catch(done);
|
|
596
788
|
});
|
|
597
789
|
|
|
598
|
-
it(
|
|
599
|
-
let mocks = document.getElementById(
|
|
790
|
+
it("should fallback when the active tab becomes unavailable", function (done) {
|
|
791
|
+
let mocks = document.getElementById("mocks");
|
|
600
792
|
mocks.innerHTML = htmlActiveUnavailable;
|
|
601
793
|
|
|
602
794
|
waitForCondition(() => {
|
|
603
|
-
const tabs = document.getElementById(
|
|
604
|
-
return tabs instanceof Tabs && tabs.getActiveTab() ===
|
|
605
|
-
})
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
795
|
+
const tabs = document.getElementById("fallback-tabs");
|
|
796
|
+
return tabs instanceof Tabs && tabs.getActiveTab() === "second";
|
|
797
|
+
})
|
|
798
|
+
.then(() => {
|
|
799
|
+
const tabs = document.getElementById("fallback-tabs");
|
|
800
|
+
const activePanel = document.getElementById("second-panel");
|
|
801
|
+
activePanel.setAttribute("data-monster-tab-available", "false");
|
|
802
|
+
|
|
803
|
+
return waitForCondition(() => tabs.getActiveTab() === "first").then(
|
|
804
|
+
() => {
|
|
805
|
+
try {
|
|
806
|
+
expect(activePanel.classList.contains("active")).to.equal(
|
|
807
|
+
false,
|
|
808
|
+
);
|
|
809
|
+
expect(
|
|
810
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]")
|
|
811
|
+
.length,
|
|
812
|
+
).to.equal(2);
|
|
813
|
+
done();
|
|
814
|
+
} catch (e) {
|
|
815
|
+
done(e);
|
|
816
|
+
}
|
|
817
|
+
},
|
|
818
|
+
);
|
|
819
|
+
})
|
|
820
|
+
.catch(done);
|
|
620
821
|
});
|
|
621
822
|
|
|
622
|
-
it(
|
|
623
|
-
let mocks = document.getElementById(
|
|
823
|
+
it("should fallback when the active tab is removed directly", function (done) {
|
|
824
|
+
let mocks = document.getElementById("mocks");
|
|
624
825
|
mocks.innerHTML = htmlRemoveFallback;
|
|
625
826
|
|
|
626
827
|
waitForCondition(() => {
|
|
627
|
-
const tabs = document.getElementById(
|
|
828
|
+
const tabs = document.getElementById("remove-fallback-tabs");
|
|
628
829
|
return (
|
|
629
830
|
tabs instanceof Tabs &&
|
|
630
|
-
tabs.getActiveTab() ===
|
|
631
|
-
tabs.shadowRoot.querySelectorAll(
|
|
831
|
+
tabs.getActiveTab() === "second" &&
|
|
832
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length === 3
|
|
632
833
|
);
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
834
|
+
})
|
|
835
|
+
.then(() => {
|
|
836
|
+
const tabs = document.getElementById("remove-fallback-tabs");
|
|
837
|
+
setTimeout(() => {
|
|
838
|
+
document.getElementById("remove-second-panel").remove();
|
|
839
|
+
|
|
840
|
+
waitForCondition(() => tabs.getActiveTab() === "first")
|
|
841
|
+
.then(() => {
|
|
842
|
+
try {
|
|
843
|
+
expect(
|
|
844
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]")
|
|
845
|
+
.length,
|
|
846
|
+
).to.equal(2);
|
|
847
|
+
done();
|
|
848
|
+
} catch (e) {
|
|
849
|
+
done(e);
|
|
850
|
+
}
|
|
851
|
+
})
|
|
852
|
+
.catch(done);
|
|
853
|
+
});
|
|
854
|
+
})
|
|
855
|
+
.catch(done);
|
|
648
856
|
});
|
|
649
857
|
|
|
650
|
-
it(
|
|
651
|
-
let mocks = document.getElementById(
|
|
858
|
+
it("should keep disabled tabs visible but not activatable", function (done) {
|
|
859
|
+
let mocks = document.getElementById("mocks");
|
|
652
860
|
mocks.innerHTML = htmlDisabled;
|
|
653
861
|
|
|
654
862
|
waitForCondition(() => {
|
|
655
|
-
const tabs = document.getElementById(
|
|
656
|
-
const disabledPanel = document.getElementById(
|
|
863
|
+
const tabs = document.getElementById("disabled-tabs");
|
|
864
|
+
const disabledPanel = document.getElementById("disabled-panel");
|
|
657
865
|
const disabledButton = tabs?.shadowRoot?.querySelector(
|
|
658
|
-
`button[part=button][data-monster-tab-reference="${disabledPanel?.getAttribute(
|
|
866
|
+
`button[part=button][data-monster-tab-reference="${disabledPanel?.getAttribute("id")}"]`,
|
|
659
867
|
);
|
|
660
868
|
return (
|
|
661
869
|
tabs instanceof Tabs &&
|
|
662
870
|
disabledButton instanceof HTMLButtonElement &&
|
|
663
|
-
tabs.getActiveTab() ===
|
|
871
|
+
tabs.getActiveTab() === "enabled"
|
|
664
872
|
);
|
|
665
|
-
})
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
873
|
+
})
|
|
874
|
+
.then(() => {
|
|
875
|
+
try {
|
|
876
|
+
const tabs = document.getElementById("disabled-tabs");
|
|
877
|
+
const disabledPanel = document.getElementById("disabled-panel");
|
|
878
|
+
const disabledButton = tabs.shadowRoot.querySelector(
|
|
879
|
+
`button[part=button][data-monster-tab-reference="${disabledPanel.getAttribute("id")}"]`,
|
|
880
|
+
);
|
|
672
881
|
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
882
|
+
expect(disabledButton.disabled).to.equal(true);
|
|
883
|
+
expect(disabledButton.getAttribute("title")).to.equal(
|
|
884
|
+
"Only available for employees",
|
|
885
|
+
);
|
|
886
|
+
tabs.activeTab("disabled");
|
|
887
|
+
expect(tabs.getActiveTab()).to.equal("enabled");
|
|
888
|
+
done();
|
|
889
|
+
} catch (e) {
|
|
890
|
+
done(e);
|
|
891
|
+
}
|
|
892
|
+
})
|
|
893
|
+
.catch(done);
|
|
682
894
|
});
|
|
683
895
|
|
|
684
|
-
it(
|
|
685
|
-
let mocks = document.getElementById(
|
|
896
|
+
it("should expose refresh, sync, hide and show APIs", function (done) {
|
|
897
|
+
let mocks = document.getElementById("mocks");
|
|
686
898
|
mocks.innerHTML = htmlAvailability;
|
|
687
899
|
|
|
688
900
|
waitForCondition(() => {
|
|
689
|
-
const tabs = document.getElementById(
|
|
690
|
-
return
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
tabs
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
901
|
+
const tabs = document.getElementById("availability-tabs");
|
|
902
|
+
return (
|
|
903
|
+
tabs instanceof Tabs &&
|
|
904
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length === 2
|
|
905
|
+
);
|
|
906
|
+
})
|
|
907
|
+
.then(() => {
|
|
908
|
+
const tabs = document.getElementById("availability-tabs");
|
|
909
|
+
|
|
910
|
+
tabs.showTab("vacation");
|
|
911
|
+
return tabs
|
|
912
|
+
.refreshTabs()
|
|
913
|
+
.then((result) => {
|
|
914
|
+
expect(result).to.equal(tabs);
|
|
915
|
+
expect(tabs.getTabs().length).to.equal(3);
|
|
916
|
+
|
|
917
|
+
tabs.hideTab("vacation");
|
|
918
|
+
return tabs.syncTabs();
|
|
919
|
+
})
|
|
920
|
+
.then((result) => {
|
|
921
|
+
try {
|
|
922
|
+
expect(result).to.equal(tabs);
|
|
923
|
+
expect(tabs.getTabs().length).to.equal(2);
|
|
924
|
+
expect(
|
|
925
|
+
document
|
|
926
|
+
.getElementById("vacation-panel")
|
|
927
|
+
.getAttribute("data-monster-tab-available"),
|
|
928
|
+
).to.equal("false");
|
|
929
|
+
done();
|
|
930
|
+
} catch (e) {
|
|
931
|
+
done(e);
|
|
932
|
+
}
|
|
933
|
+
});
|
|
934
|
+
})
|
|
935
|
+
.catch(done);
|
|
712
936
|
});
|
|
713
937
|
|
|
714
|
-
it(
|
|
715
|
-
let mocks = document.getElementById(
|
|
938
|
+
it("should not derive labels from content when disabled by option", function (done) {
|
|
939
|
+
let mocks = document.getElementById("mocks");
|
|
716
940
|
mocks.innerHTML = htmlNoDerivedLabel;
|
|
717
941
|
|
|
718
942
|
waitForCondition(() => {
|
|
719
|
-
const tabs = document.getElementById(
|
|
720
|
-
return
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
943
|
+
const tabs = document.getElementById("label-tabs");
|
|
944
|
+
return (
|
|
945
|
+
tabs instanceof Tabs &&
|
|
946
|
+
tabs.shadowRoot.querySelector("button[part=button]") !== null
|
|
947
|
+
);
|
|
948
|
+
})
|
|
949
|
+
.then(() => {
|
|
950
|
+
try {
|
|
951
|
+
const tabs = document.getElementById("label-tabs");
|
|
952
|
+
const button = tabs.shadowRoot.querySelector("button[part=button]");
|
|
953
|
+
expect(button.textContent.trim()).to.equal("New Tab");
|
|
954
|
+
expect(
|
|
955
|
+
document
|
|
956
|
+
.getElementById("content-panel")
|
|
957
|
+
.hasAttribute("data-monster-button-label"),
|
|
958
|
+
).to.equal(false);
|
|
959
|
+
done();
|
|
960
|
+
} catch (e) {
|
|
961
|
+
done(e);
|
|
962
|
+
}
|
|
963
|
+
})
|
|
964
|
+
.catch(done);
|
|
732
965
|
});
|
|
733
966
|
|
|
734
|
-
it(
|
|
735
|
-
let mocks = document.getElementById(
|
|
967
|
+
it("should render SVG tab icons as theme-aware masks by default", function (done) {
|
|
968
|
+
let mocks = document.getElementById("mocks");
|
|
736
969
|
mocks.innerHTML = htmlIconModes;
|
|
737
970
|
|
|
738
971
|
waitForCondition(() => {
|
|
739
|
-
const tabs = document.getElementById(
|
|
740
|
-
return
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
expect(autoSvgIcon.getAttribute('style')).to.contain('--monster-tab-icon-mask-image');
|
|
753
|
-
|
|
754
|
-
const imageSvgIcon = getIcon('image-svg-panel');
|
|
755
|
-
expect(imageSvgIcon.tagName).to.equal('IMG');
|
|
756
|
-
expect(imageSvgIcon.getAttribute('data-monster-icon-mode')).to.equal('image');
|
|
757
|
-
expect(imageSvgIcon.getAttribute('src')).to.equal(svgTabIcon);
|
|
972
|
+
const tabs = document.getElementById("icon-mode-tabs");
|
|
973
|
+
return (
|
|
974
|
+
tabs instanceof Tabs &&
|
|
975
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length === 4
|
|
976
|
+
);
|
|
977
|
+
})
|
|
978
|
+
.then(() => {
|
|
979
|
+
try {
|
|
980
|
+
const tabs = document.getElementById("icon-mode-tabs");
|
|
981
|
+
const getIcon = (reference) =>
|
|
982
|
+
tabs.shadowRoot.querySelector(
|
|
983
|
+
`button[part=button][data-monster-tab-reference="${reference}"] [part=icon]`,
|
|
984
|
+
);
|
|
758
985
|
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
986
|
+
const autoSvgIcon = getIcon("auto-svg-panel");
|
|
987
|
+
expect(autoSvgIcon.tagName).to.equal("SPAN");
|
|
988
|
+
expect(autoSvgIcon.getAttribute("data-monster-icon-mode")).to.equal(
|
|
989
|
+
"mask",
|
|
990
|
+
);
|
|
991
|
+
expect(autoSvgIcon.getAttribute("aria-hidden")).to.equal("true");
|
|
992
|
+
expect(autoSvgIcon.getAttribute("style")).to.contain(
|
|
993
|
+
"--monster-tab-icon-mask-image",
|
|
994
|
+
);
|
|
763
995
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
996
|
+
const imageSvgIcon = getIcon("image-svg-panel");
|
|
997
|
+
expect(imageSvgIcon.tagName).to.equal("IMG");
|
|
998
|
+
expect(
|
|
999
|
+
imageSvgIcon.getAttribute("data-monster-icon-mode"),
|
|
1000
|
+
).to.equal("image");
|
|
1001
|
+
expect(imageSvgIcon.getAttribute("src")).to.equal(svgTabIcon);
|
|
1002
|
+
|
|
1003
|
+
const autoRasterIcon = getIcon("auto-raster-panel");
|
|
1004
|
+
expect(autoRasterIcon.tagName).to.equal("IMG");
|
|
1005
|
+
expect(
|
|
1006
|
+
autoRasterIcon.getAttribute("data-monster-icon-mode"),
|
|
1007
|
+
).to.equal("image");
|
|
1008
|
+
expect(autoRasterIcon.getAttribute("src")).to.equal(pngTabIcon);
|
|
1009
|
+
|
|
1010
|
+
const maskRasterIcon = getIcon("mask-raster-panel");
|
|
1011
|
+
expect(maskRasterIcon.tagName).to.equal("SPAN");
|
|
1012
|
+
expect(
|
|
1013
|
+
maskRasterIcon.getAttribute("data-monster-icon-mode"),
|
|
1014
|
+
).to.equal("mask");
|
|
1015
|
+
expect(maskRasterIcon.getAttribute("style")).to.contain(
|
|
1016
|
+
"--monster-tab-icon-mask-image",
|
|
1017
|
+
);
|
|
768
1018
|
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1019
|
+
done();
|
|
1020
|
+
} catch (e) {
|
|
1021
|
+
done(e);
|
|
1022
|
+
}
|
|
1023
|
+
})
|
|
1024
|
+
.catch(done);
|
|
774
1025
|
});
|
|
775
1026
|
|
|
776
|
-
it(
|
|
777
|
-
let mocks = document.getElementById(
|
|
1027
|
+
it("should update tab icon mode when the icon mode attribute changes", function (done) {
|
|
1028
|
+
let mocks = document.getElementById("mocks");
|
|
778
1029
|
mocks.innerHTML = htmlIconModes;
|
|
779
1030
|
|
|
780
1031
|
waitForCondition(() => {
|
|
781
|
-
const tabs = document.getElementById(
|
|
782
|
-
return
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
return
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
1032
|
+
const tabs = document.getElementById("icon-mode-tabs");
|
|
1033
|
+
return (
|
|
1034
|
+
tabs instanceof Tabs &&
|
|
1035
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length === 4
|
|
1036
|
+
);
|
|
1037
|
+
})
|
|
1038
|
+
.then(() => {
|
|
1039
|
+
const tabs = document.getElementById("icon-mode-tabs");
|
|
1040
|
+
const panel = document.getElementById("image-svg-panel");
|
|
1041
|
+
panel.setAttribute("data-monster-button-icon-mode", "mask");
|
|
1042
|
+
|
|
1043
|
+
return waitForCondition(() => {
|
|
1044
|
+
const icon = tabs.shadowRoot.querySelector(
|
|
1045
|
+
'button[part=button][data-monster-tab-reference="image-svg-panel"] [part=icon]',
|
|
1046
|
+
);
|
|
1047
|
+
return (
|
|
1048
|
+
icon?.tagName === "SPAN" &&
|
|
1049
|
+
icon.getAttribute("data-monster-icon-mode") === "mask"
|
|
1050
|
+
);
|
|
1051
|
+
});
|
|
1052
|
+
})
|
|
1053
|
+
.then(() => {
|
|
1054
|
+
done();
|
|
1055
|
+
})
|
|
1056
|
+
.catch(done);
|
|
797
1057
|
});
|
|
798
1058
|
|
|
799
|
-
it(
|
|
800
|
-
let mocks = document.getElementById(
|
|
1059
|
+
it("should render vertical tab SVG icons as theme-aware masks by default", function (done) {
|
|
1060
|
+
let mocks = document.getElementById("mocks");
|
|
801
1061
|
mocks.innerHTML = htmlVerticalIconModes;
|
|
802
1062
|
|
|
803
1063
|
waitForCondition(() => {
|
|
804
|
-
const tabs = document.getElementById(
|
|
805
|
-
return
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
1064
|
+
const tabs = document.getElementById("vertical-icon-mode-tabs");
|
|
1065
|
+
return (
|
|
1066
|
+
tabs instanceof VerticalTabs &&
|
|
1067
|
+
tabs.shadowRoot.querySelector("button[part=button] [part=icon]") !==
|
|
1068
|
+
null
|
|
1069
|
+
);
|
|
1070
|
+
})
|
|
1071
|
+
.then(() => {
|
|
1072
|
+
try {
|
|
1073
|
+
const tabs = document.getElementById("vertical-icon-mode-tabs");
|
|
1074
|
+
const icon = tabs.shadowRoot.querySelector(
|
|
1075
|
+
'button[part=button][data-monster-tab-reference="vertical-auto-svg-panel"] [part=icon]',
|
|
1076
|
+
);
|
|
1077
|
+
expect(icon.tagName).to.equal("SPAN");
|
|
1078
|
+
expect(icon.getAttribute("data-monster-icon-mode")).to.equal(
|
|
1079
|
+
"mask",
|
|
1080
|
+
);
|
|
1081
|
+
expect(icon.getAttribute("style")).to.contain(
|
|
1082
|
+
"--monster-tab-icon-mask-image",
|
|
1083
|
+
);
|
|
1084
|
+
done();
|
|
1085
|
+
} catch (e) {
|
|
1086
|
+
done(e);
|
|
1087
|
+
}
|
|
1088
|
+
})
|
|
1089
|
+
.catch(done);
|
|
820
1090
|
});
|
|
821
1091
|
|
|
822
|
-
it(
|
|
823
|
-
let mocks = document.getElementById(
|
|
1092
|
+
it("should include stable names and metadata in tab events and buttons", function (done) {
|
|
1093
|
+
let mocks = document.getElementById("mocks");
|
|
824
1094
|
mocks.innerHTML = htmlMetadata;
|
|
825
1095
|
|
|
826
1096
|
waitForCondition(() => {
|
|
827
|
-
const tabs = document.getElementById(
|
|
828
|
-
return
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1097
|
+
const tabs = document.getElementById("metadata-tabs");
|
|
1098
|
+
return (
|
|
1099
|
+
tabs instanceof Tabs &&
|
|
1100
|
+
tabs.shadowRoot.querySelector("button[part=button]") !== null
|
|
1101
|
+
);
|
|
1102
|
+
})
|
|
1103
|
+
.then(() => {
|
|
1104
|
+
try {
|
|
1105
|
+
const tabs = document.getElementById("metadata-tabs");
|
|
1106
|
+
const button = tabs.shadowRoot.querySelector("button[part=button]");
|
|
1107
|
+
let changedEvent = null;
|
|
1108
|
+
tabs.addEventListener("monster-tab-changed", (event) => {
|
|
1109
|
+
changedEvent = event;
|
|
1110
|
+
});
|
|
837
1111
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
1112
|
+
expect(button.getAttribute("data-monster-tab-name")).to.equal(
|
|
1113
|
+
"metadata",
|
|
1114
|
+
);
|
|
1115
|
+
expect(button.getAttribute("data-monster-tab-kind")).to.equal(
|
|
1116
|
+
"profile",
|
|
1117
|
+
);
|
|
1118
|
+
expect(button.getAttribute("data-monster-tab-priority")).to.equal(
|
|
1119
|
+
"10",
|
|
1120
|
+
);
|
|
1121
|
+
expect(button.getAttribute("data-monster-tab-group")).to.equal(
|
|
1122
|
+
"main",
|
|
1123
|
+
);
|
|
842
1124
|
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
1125
|
+
tabs.activeTab("metadata");
|
|
1126
|
+
expect(changedEvent).to.not.equal(null);
|
|
1127
|
+
expect(changedEvent.detail.name).to.equal("metadata");
|
|
1128
|
+
expect(changedEvent.detail.tab).to.equal("metadata");
|
|
1129
|
+
expect(changedEvent.detail.metadata.kind).to.equal("profile");
|
|
1130
|
+
done();
|
|
1131
|
+
} catch (e) {
|
|
1132
|
+
done(e);
|
|
1133
|
+
}
|
|
1134
|
+
})
|
|
1135
|
+
.catch(done);
|
|
853
1136
|
});
|
|
854
1137
|
|
|
855
|
-
|
|
1138
|
+
it("should reorder tabs by drag and drop without persisting by default", function (done) {
|
|
1139
|
+
let mocks = document.getElementById("mocks");
|
|
1140
|
+
mocks.innerHTML = `
|
|
1141
|
+
<monster-host id="tab-host"></monster-host>
|
|
1142
|
+
`;
|
|
1143
|
+
const host = document.getElementById("tab-host");
|
|
1144
|
+
let savedConfig = null;
|
|
1145
|
+
host.hasConfig = () => Promise.resolve(false);
|
|
1146
|
+
host.getConfig = () => Promise.resolve([]);
|
|
1147
|
+
host.setConfig = (key, value) => {
|
|
1148
|
+
savedConfig = { key, value };
|
|
1149
|
+
return Promise.resolve(value);
|
|
1150
|
+
};
|
|
1151
|
+
host.innerHTML = `
|
|
1152
|
+
<monster-tabs id="reorder-tabs">
|
|
1153
|
+
<div id="first-tab" data-monster-name="first" data-monster-button-label="First">First</div>
|
|
1154
|
+
<div id="second-tab" data-monster-name="second" data-monster-button-label="Second">Second</div>
|
|
1155
|
+
<div id="third-tab" data-monster-name="third" data-monster-button-label="Third">Third</div>
|
|
1156
|
+
</monster-tabs>
|
|
1157
|
+
`;
|
|
856
1158
|
|
|
1159
|
+
waitForCondition(() => {
|
|
1160
|
+
const tabs = document.getElementById("reorder-tabs");
|
|
1161
|
+
return (
|
|
1162
|
+
tabs instanceof Tabs &&
|
|
1163
|
+
tabs.shadowRoot.querySelectorAll(
|
|
1164
|
+
'button[part=button][draggable="true"]',
|
|
1165
|
+
).length === 3
|
|
1166
|
+
);
|
|
1167
|
+
})
|
|
1168
|
+
.then(() => {
|
|
1169
|
+
const tabs = document.getElementById("reorder-tabs");
|
|
1170
|
+
let orderEvent = null;
|
|
1171
|
+
tabs.addEventListener("monster-tab-order-changed", (event) => {
|
|
1172
|
+
orderEvent = event;
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
dragTab(tabs, "first-tab", "third-tab");
|
|
857
1176
|
|
|
1177
|
+
return waitForCondition(() => {
|
|
1178
|
+
return (
|
|
1179
|
+
Array.from(tabs.children)
|
|
1180
|
+
.map((node) => node.id)
|
|
1181
|
+
.join(",") === "second-tab,third-tab,first-tab" &&
|
|
1182
|
+
orderEvent !== null
|
|
1183
|
+
);
|
|
1184
|
+
}).then(() => {
|
|
1185
|
+
try {
|
|
1186
|
+
expect(savedConfig).to.equal(null);
|
|
1187
|
+
expect(orderEvent.detail.order).to.deep.equal([
|
|
1188
|
+
"second",
|
|
1189
|
+
"third",
|
|
1190
|
+
"first",
|
|
1191
|
+
]);
|
|
1192
|
+
expect(tabs.getOption("features.order.persist")).to.equal(false);
|
|
1193
|
+
done();
|
|
1194
|
+
} catch (e) {
|
|
1195
|
+
done(e);
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
1198
|
+
})
|
|
1199
|
+
.catch(done);
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
it("should persist and restore tab order when configured", function (done) {
|
|
1203
|
+
let mocks = document.getElementById("mocks");
|
|
1204
|
+
const saved = new Map();
|
|
1205
|
+
mocks.innerHTML = `
|
|
1206
|
+
<monster-host id="persistent-tab-host"></monster-host>
|
|
1207
|
+
`;
|
|
1208
|
+
const host = document.getElementById("persistent-tab-host");
|
|
1209
|
+
host.hasConfig = (key) => Promise.resolve(saved.has(key));
|
|
1210
|
+
host.getConfig = (key) => Promise.resolve(saved.get(key));
|
|
1211
|
+
host.setConfig = (key, value) => {
|
|
1212
|
+
saved.set(key, value);
|
|
1213
|
+
return Promise.resolve(value);
|
|
1214
|
+
};
|
|
1215
|
+
host.innerHTML = `
|
|
1216
|
+
<monster-tabs id="persistent-tabs" data-monster-options='{"features":{"order":{"persist":true}},"config":{"instanceKey":"workspace.tabs"}}'>
|
|
1217
|
+
<div id="persist-first" data-monster-name="first" data-monster-button-label="First">First</div>
|
|
1218
|
+
<div id="persist-second" data-monster-name="second" data-monster-button-label="Second">Second</div>
|
|
1219
|
+
<div id="persist-third" data-monster-name="third" data-monster-button-label="Third">Third</div>
|
|
1220
|
+
</monster-tabs>
|
|
1221
|
+
`;
|
|
1222
|
+
|
|
1223
|
+
waitForCondition(() => {
|
|
1224
|
+
const tabs = document.getElementById("persistent-tabs");
|
|
1225
|
+
return (
|
|
1226
|
+
tabs instanceof Tabs &&
|
|
1227
|
+
tabs.shadowRoot.querySelectorAll("button[part=button]").length === 3
|
|
1228
|
+
);
|
|
1229
|
+
})
|
|
1230
|
+
.then(() => {
|
|
1231
|
+
const tabs = document.getElementById("persistent-tabs");
|
|
1232
|
+
dragTab(tabs, "persist-first", "persist-third");
|
|
1233
|
+
|
|
1234
|
+
return waitForCondition(() => saved.size === 1)
|
|
1235
|
+
.then(() => {
|
|
1236
|
+
const [key, value] = Array.from(saved.entries())[0];
|
|
1237
|
+
expect(key).to.equal("tab_order_tabs_workspace_tabs");
|
|
1238
|
+
expect(value).to.deep.equal(["second", "third", "first"]);
|
|
1239
|
+
|
|
1240
|
+
host.innerHTML = `
|
|
1241
|
+
<monster-tabs id="persistent-tabs-reloaded" data-monster-options='{"features":{"order":{"persist":true}},"config":{"instanceKey":"workspace.tabs"}}'>
|
|
1242
|
+
<div id="reload-first" data-monster-name="first" data-monster-button-label="First">First</div>
|
|
1243
|
+
<div id="reload-second" data-monster-name="second" data-monster-button-label="Second">Second</div>
|
|
1244
|
+
<div id="reload-third" data-monster-name="third" data-monster-button-label="Third">Third</div>
|
|
1245
|
+
</monster-tabs>
|
|
1246
|
+
`;
|
|
1247
|
+
|
|
1248
|
+
return waitForCondition(() => {
|
|
1249
|
+
const reloaded = document.getElementById(
|
|
1250
|
+
"persistent-tabs-reloaded",
|
|
1251
|
+
);
|
|
1252
|
+
return (
|
|
1253
|
+
reloaded instanceof Tabs &&
|
|
1254
|
+
Array.from(reloaded.children)
|
|
1255
|
+
.map((node) => node.id)
|
|
1256
|
+
.join(",") === "reload-second,reload-third,reload-first"
|
|
1257
|
+
);
|
|
1258
|
+
});
|
|
1259
|
+
})
|
|
1260
|
+
.then(() => {
|
|
1261
|
+
done();
|
|
1262
|
+
});
|
|
1263
|
+
})
|
|
1264
|
+
.catch(done);
|
|
1265
|
+
});
|
|
1266
|
+
});
|
|
858
1267
|
});
|