@schukai/monster 4.145.3 → 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.
@@ -1,15 +1,14 @@
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";
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 = "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";
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('condition timed out'));
199
+ reject(new Error("condition timed out"));
200
200
  return;
201
201
  }
202
202
 
@@ -207,7 +207,7 @@ function waitForCondition(check, {timeout = 4000, interval = 10} = {}) {
207
207
  });
208
208
  }
209
209
 
210
- function createRect({width, height = 40, x = 0, y = 0}) {
210
+ function createRect({ width, height = 40, x = 0, y = 0 }) {
211
211
  return {
212
212
  width,
213
213
  height,
@@ -220,24 +220,27 @@ function createRect({width, height = 40, x = 0, y = 0}) {
220
220
  };
221
221
  }
222
222
 
223
- function installOverflowTabGeometryMock({navWidth = 480, switchWidth = 44} = {}) {
223
+ function installOverflowTabGeometryMock({
224
+ navWidth = 480,
225
+ switchWidth = 44,
226
+ } = {}) {
224
227
  const originalGetBoundingClientRect =
225
228
  global.HTMLElement.prototype.getBoundingClientRect;
226
229
 
227
230
  global.HTMLElement.prototype.getBoundingClientRect = function () {
228
- const role = this.getAttribute?.('data-monster-role');
229
- const label = this.textContent?.trim() || '';
231
+ const role = this.getAttribute?.("data-monster-role");
232
+ const label = this.textContent?.trim() || "";
230
233
 
231
- if (role === 'nav') {
232
- return createRect({width: navWidth});
234
+ if (role === "nav") {
235
+ return createRect({ width: navWidth });
233
236
  }
234
237
 
235
- if (role === 'button') {
236
- return createRect({width: Math.max(70, label.length * 11)});
238
+ if (role === "button") {
239
+ return createRect({ width: Math.max(70, label.length * 11) });
237
240
  }
238
241
 
239
- if (role === 'switch') {
240
- return createRect({width: switchWidth});
242
+ if (role === "switch") {
243
+ return createRect({ width: switchWidth });
241
244
  }
242
245
 
243
246
  return originalGetBoundingClientRect.call(this);
@@ -249,39 +252,84 @@ function installOverflowTabGeometryMock({navWidth = 480, switchWidth = 44} = {})
249
252
  };
250
253
  }
251
254
 
252
- describe('Tabs', function () {
253
-
254
- before(function (done) {
255
- initJSDOM().then(() => {
256
-
257
- import("element-internals-polyfill").catch(e => done(e));
258
-
259
- let promises = []
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
+ }
260
276
 
261
- if (!global['crypto']) {
262
- promises.push(import("@peculiar/webcrypto").then((m) => {
263
- const Crypto = m['Crypto'];
264
- global['crypto'] = new Crypto();
265
- }));
266
- }
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
+ }
267
293
 
268
- promises.push(import("../../../../source/components/layout/tabs.mjs").then((m) => {
269
- Tabs = m['Tabs'];
270
- }))
271
- promises.push(import("../../../../source/components/layout/vertical-tabs.mjs").then((m) => {
272
- VerticalTabs = m['VerticalTabs'];
273
- }))
294
+ describe("Tabs", function () {
295
+ before(function (done) {
296
+ initJSDOM().then(() => {
297
+ import("element-internals-polyfill").catch((e) => done(e));
274
298
 
275
- Promise.all(promises).then(()=>{
276
- done();
277
- }).catch(e => done(e))
299
+ let promises = [];
278
300
 
279
- });
280
- })
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
+ }
281
309
 
282
- describe('document.createElement()', function () {
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
+ });
283
330
 
284
- afterEach(() => {
331
+ describe("document.createElement()", function () {
332
+ afterEach(() => {
285
333
  if (restoreBoundingClientRect instanceof Function) {
286
334
  restoreBoundingClientRect();
287
335
  restoreBoundingClientRect = null;
@@ -290,136 +338,147 @@ describe('Tabs', function () {
290
338
  restoreResizeObserver();
291
339
  restoreResizeObserver = null;
292
340
  }
293
- let mocks = document.getElementById('mocks');
294
- mocks.innerHTML = "";
295
- })
296
-
297
- it('should have buttons and tabs', function (done) {
298
-
299
- let mocks = document.getElementById('mocks');
300
- mocks.innerHTML = html1;
301
-
302
- setTimeout(() => {
303
- try {
304
- const tabs = document.getElementById('mytabs')
305
- expect(tabs).is.instanceof(Tabs);
306
-
307
- setTimeout(() => {
308
- let nav = tabs.shadowRoot.querySelector('nav');
309
- const buttons = tabs.shadowRoot.querySelectorAll('button[part=button]');
310
- expect(buttons[0]).is.instanceof(HTMLButtonElement);
311
- expect(nav.hasChildNodes()).to.be.true;
312
- expect(buttons.length).to.be.equal(4);
313
- done();
314
- }, 100)
315
-
316
- } catch (e) {
317
- return done(e);
318
- }
319
-
320
- }, 0)
341
+ let mocks = document.getElementById("mocks");
342
+ mocks.innerHTML = "";
343
+ });
321
344
 
345
+ it("should have buttons and tabs", function (done) {
346
+ let mocks = document.getElementById("mocks");
347
+ mocks.innerHTML = html1;
322
348
 
323
- });
349
+ setTimeout(() => {
350
+ try {
351
+ const tabs = document.getElementById("mytabs");
352
+ expect(tabs).is.instanceof(Tabs);
324
353
 
325
- it('should shorten label from content when no explicit label is provided', function (done) {
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
+ });
326
369
 
327
- let mocks = document.getElementById('mocks');
328
- mocks.innerHTML = html1;
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;
329
373
 
330
374
  setTimeout(() => {
331
375
  try {
332
- const tabs = document.getElementById('mytabs');
376
+ const tabs = document.getElementById("mytabs");
333
377
  expect(tabs).is.instanceof(Tabs);
334
378
 
335
379
  setTimeout(() => {
336
380
  const thirdTab = tabs.children[2];
337
381
  expect(thirdTab).to.not.equal(undefined);
338
- const reference = thirdTab.getAttribute('id');
382
+ const reference = thirdTab.getAttribute("id");
339
383
  expect(reference).to.not.equal(null);
340
384
  const button = tabs.shadowRoot.querySelector(
341
385
  `button[part=button][data-monster-tab-reference="${reference}"]`,
342
386
  );
343
387
  expect(button).to.not.equal(null);
344
- const labelSpan = button.querySelector('span[data-monster-replace]');
388
+ const labelSpan = button.querySelector(
389
+ "span[data-monster-replace]",
390
+ );
345
391
  expect(labelSpan).to.not.equal(null);
346
- expect(labelSpan.textContent.trim()).to.equal('Das ist tab…');
392
+ expect(labelSpan.textContent.trim()).to.equal("Das ist tab…");
347
393
  done();
348
394
  }, 100);
349
395
  } catch (e) {
350
396
  return done(e);
351
- }
352
- }, 0);
353
- });
354
-
355
- it('should open the first tab by default when no tab is predefined as active', function (done) {
397
+ }
398
+ }, 0);
399
+ });
356
400
 
357
- let mocks = document.getElementById('mocks');
358
- mocks.innerHTML = html1;
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;
359
404
 
360
405
  setTimeout(() => {
361
406
  try {
362
- const tabs = document.getElementById('mytabs');
407
+ const tabs = document.getElementById("mytabs");
363
408
  expect(tabs).is.instanceof(Tabs);
364
409
 
365
410
  setTimeout(() => {
366
- expect(tabs.children[0].classList.contains('active')).to.equal(true);
367
- expect(tabs.children[1].classList.contains('active')).to.equal(false);
368
- expect(tabs.getActiveTab()).to.equal(tabs.children[0].getAttribute('id'));
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
+ );
369
420
  done();
370
421
  }, 100);
371
422
  } catch (e) {
372
423
  return done(e);
373
- }
374
- }, 0);
375
- });
376
-
377
- it('should keep a predefined active tab instead of opening the first tab', function (done) {
424
+ }
425
+ }, 0);
426
+ });
378
427
 
379
- let mocks = document.getElementById('mocks');
380
- mocks.innerHTML = htmlActiveSecond;
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;
381
431
 
382
432
  setTimeout(() => {
383
433
  try {
384
- const tabs = document.getElementById('mytabs');
434
+ const tabs = document.getElementById("mytabs");
385
435
  expect(tabs).is.instanceof(Tabs);
386
436
 
387
437
  setTimeout(() => {
388
- expect(tabs.children[0].classList.contains('active')).to.equal(false);
389
- expect(tabs.children[1].classList.contains('active')).to.equal(true);
390
- expect(tabs.getActiveTab()).to.equal(tabs.children[1].getAttribute('id'));
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
+ );
391
447
  done();
392
448
  }, 100);
393
449
  } catch (e) {
394
450
  return done(e);
395
- }
396
- }, 0);
397
- });
398
-
399
- it('should configure flip and shift middleware for the overflow popper', function (done) {
451
+ }
452
+ }, 0);
453
+ });
400
454
 
401
- let mocks = document.getElementById('mocks');
402
- mocks.innerHTML = html1;
455
+ it("should configure flip and shift middleware for the overflow popper", function (done) {
456
+ let mocks = document.getElementById("mocks");
457
+ mocks.innerHTML = html1;
403
458
 
404
459
  setTimeout(() => {
405
460
  try {
406
- const tabs = document.getElementById('mytabs');
461
+ const tabs = document.getElementById("mytabs");
407
462
  expect(tabs).is.instanceof(Tabs);
408
463
 
409
- const modifiers = tabs.getOption('popper.modifiers');
410
- expect(modifiers.some((entry) => entry?.name === 'flip')).to.equal(true);
411
- expect(modifiers.some((entry) => entry?.name === 'shift')).to.equal(true);
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
+ );
412
471
  done();
413
472
  } catch (e) {
414
473
  return done(e);
415
- }
416
- }, 0);
417
- });
474
+ }
475
+ }, 0);
476
+ });
418
477
 
419
- it('should move overflowing tabs into the popper without losing them', function (done) {
478
+ it("should move overflowing tabs into the popper without losing them", function (done) {
420
479
  this.timeout(5000);
421
480
 
422
- let mocks = document.getElementById('mocks');
481
+ let mocks = document.getElementById("mocks");
423
482
  const OriginalResizeObserver = window.ResizeObserver;
424
483
  const originalGlobalResizeObserver = global.ResizeObserver;
425
484
  class TrackingResizeObserver extends ResizeObserverMock {
@@ -442,15 +501,15 @@ describe('Tabs', function () {
442
501
  };
443
502
  restoreBoundingClientRect = installOverflowTabGeometryMock();
444
503
 
445
- mocks.innerHTML = htmlOverflow;
504
+ mocks.innerHTML = htmlOverflow;
446
505
 
447
506
  waitForCondition(() => {
448
- const tabs = document.getElementById('overflow-tabs');
507
+ const tabs = document.getElementById("overflow-tabs");
449
508
  const switchButton = tabs?.shadowRoot?.querySelector(
450
509
  '[data-monster-role="switch"]',
451
510
  );
452
- const standardButtons = tabs?.getOption?.('buttons.standard') || [];
453
- const popperButtons = tabs?.getOption?.('buttons.popper') || [];
511
+ const standardButtons = tabs?.getOption?.("buttons.standard") || [];
512
+ const popperButtons = tabs?.getOption?.("buttons.popper") || [];
454
513
 
455
514
  return (
456
515
  tabs instanceof Tabs &&
@@ -458,108 +517,116 @@ describe('Tabs', function () {
458
517
  standardButtons.length > 0 &&
459
518
  popperButtons.length > 0 &&
460
519
  standardButtons.length + popperButtons.length === 11 &&
461
- switchButton.classList.contains('hidden') === false
520
+ switchButton.classList.contains("hidden") === false
462
521
  );
463
- }).then(() => {
464
- let tabs;
465
- let originalSetOption;
466
- try {
467
- tabs = document.getElementById('overflow-tabs');
468
- expect(tabs).is.instanceof(Tabs);
469
- const switchButton = tabs.shadowRoot.querySelector(
470
- '[data-monster-role="switch"]',
471
- );
472
- let standardButtons = tabs.getOption('buttons.standard');
473
- let popperButtons = tabs.getOption('buttons.popper');
474
-
475
- expect(switchButton).to.not.equal(null);
476
- expect(standardButtons.length).to.be.greaterThan(0);
477
- expect(popperButtons.length).to.be.greaterThan(0);
478
- expect(standardButtons.length + popperButtons.length).to.equal(11);
479
- expect(switchButton.classList.contains('hidden')).to.equal(false);
480
-
481
- const resizeObserver = TrackingResizeObserver.instances.find(
482
- (observer) => observer.elements.includes(tabs.shadowRoot.querySelector('[data-monster-role="nav"]')),
483
- );
484
- expect(resizeObserver).to.not.equal(undefined);
485
-
486
- originalSetOption = tabs.setOption.bind(tabs);
487
- let resetAllTabsDuringResize = false;
488
- tabs.setOption = function (path, value) {
489
- if (
490
- path === 'buttons' &&
491
- value?.standard?.length === 11 &&
492
- value?.popper?.length === 0
493
- ) {
494
- resetAllTabsDuringResize = true;
495
- }
496
- return originalSetOption(path, value);
497
- };
498
-
499
- resizeObserver.triggerResize([]);
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");
500
534
 
501
- return waitForCondition(() => {
502
- return TrackingResizeObserver.callbackCount > 0;
503
- }).then(() => {
504
- return new Promise((resolve) => setTimeout(resolve, 260));
505
- }).then(() => {
506
- return new Promise((resolve) => requestAnimationFrame(resolve));
507
- }).then(() => {
508
- tabs.setOption = originalSetOption;
509
- expect(resetAllTabsDuringResize).to.equal(false);
510
- standardButtons = tabs.getOption('buttons.standard');
511
- popperButtons = tabs.getOption('buttons.popper');
535
+ expect(switchButton).to.not.equal(null);
512
536
  expect(standardButtons.length).to.be.greaterThan(0);
513
537
  expect(popperButtons.length).to.be.greaterThan(0);
514
538
  expect(standardButtons.length + popperButtons.length).to.equal(11);
515
- expect(
516
- new Set(
517
- standardButtons
518
- .concat(popperButtons)
519
- .map((button) => button.reference),
520
- ).size,
521
- ).to.equal(11);
522
- }).then(() => {
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([]);
563
+
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
+ }
523
602
  restoreBoundingClientRect();
524
603
  restoreBoundingClientRect = null;
525
604
  restoreResizeObserver();
526
605
  restoreResizeObserver = null;
527
- done();
528
- });
529
- } catch (e) {
530
- if (tabs && originalSetOption) {
531
- tabs.setOption = originalSetOption;
606
+ return done(e);
532
607
  }
533
- restoreBoundingClientRect();
534
- restoreBoundingClientRect = null;
535
- restoreResizeObserver();
536
- restoreResizeObserver = null;
537
- return done(e);
538
- }
539
- }).catch((e) => {
540
- const tabs = document.getElementById('overflow-tabs');
541
- if (
542
- tabs &&
543
- Object.prototype.hasOwnProperty.call(tabs, 'setOption')
544
- ) {
545
- delete tabs.setOption;
546
- }
547
- if (restoreBoundingClientRect instanceof Function) {
548
- restoreBoundingClientRect();
549
- restoreBoundingClientRect = null;
550
- }
551
- if (restoreResizeObserver instanceof Function) {
552
- restoreResizeObserver();
553
- restoreResizeObserver = null;
554
- }
555
- done(e);
556
- });
557
- });
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
+ });
558
625
 
559
- it('should preserve the overflow split and recalculate widths while rebuilding tab buttons', async function () {
626
+ it("should preserve the overflow split and recalculate widths while rebuilding tab buttons", async function () {
560
627
  this.timeout(5000);
561
628
 
562
- const mocks = document.getElementById('mocks');
629
+ const mocks = document.getElementById("mocks");
563
630
  restoreBoundingClientRect = installOverflowTabGeometryMock();
564
631
 
565
632
  let tabs;
@@ -569,9 +636,9 @@ describe('Tabs', function () {
569
636
  mocks.innerHTML = htmlOverflow;
570
637
 
571
638
  await waitForCondition(() => {
572
- tabs = document.getElementById('overflow-tabs');
573
- const standardButtons = tabs?.getOption?.('buttons.standard') || [];
574
- const popperButtons = tabs?.getOption?.('buttons.popper') || [];
639
+ tabs = document.getElementById("overflow-tabs");
640
+ const standardButtons = tabs?.getOption?.("buttons.standard") || [];
641
+ const popperButtons = tabs?.getOption?.("buttons.popper") || [];
575
642
 
576
643
  return (
577
644
  tabs instanceof Tabs &&
@@ -581,8 +648,10 @@ describe('Tabs', function () {
581
648
  );
582
649
  });
583
650
 
584
- const initialPopperCount = tabs.getOption('buttons.popper').length;
585
- const standardReference = tabs.getOption('buttons.standard.0.reference');
651
+ const initialPopperCount = tabs.getOption("buttons.popper").length;
652
+ const standardReference = tabs.getOption(
653
+ "buttons.standard.0.reference",
654
+ );
586
655
  const panel = document.getElementById(standardReference);
587
656
  expect(panel).to.not.equal(null);
588
657
 
@@ -590,7 +659,7 @@ describe('Tabs', function () {
590
659
  let resetAllTabsDuringRebuild = false;
591
660
  tabs.setOption = function (path, value) {
592
661
  if (
593
- path === 'buttons' &&
662
+ path === "buttons" &&
594
663
  value?.standard?.length === 11 &&
595
664
  value?.popper?.length === 0
596
665
  ) {
@@ -600,32 +669,32 @@ describe('Tabs', function () {
600
669
  };
601
670
 
602
671
  panel.setAttribute(
603
- 'data-monster-button-label',
604
- 'Aktualisierte sehr lange Tab Beschriftung mit viel mehr Platzbedarf',
672
+ "data-monster-button-label",
673
+ "Aktualisierte sehr lange Tab Beschriftung mit viel mehr Platzbedarf",
605
674
  );
606
675
 
607
676
  await waitForCondition(() => {
608
677
  return tabs
609
- .getOption('buttons.standard', [])
610
- .concat(tabs.getOption('buttons.popper', []))
678
+ .getOption("buttons.standard", [])
679
+ .concat(tabs.getOption("buttons.popper", []))
611
680
  .some((button) => {
612
681
  return (
613
682
  button.reference === standardReference &&
614
- button.label.includes('Aktualisierte')
683
+ button.label.includes("Aktualisierte")
615
684
  );
616
685
  });
617
686
  });
618
687
  await waitForCondition(() => {
619
- return tabs.getOption('buttons.popper').length > initialPopperCount;
688
+ return tabs.getOption("buttons.popper").length > initialPopperCount;
620
689
  });
621
690
 
622
691
  tabs.setOption = originalSetOption;
623
692
  expect(resetAllTabsDuringRebuild).to.equal(false);
624
- expect(tabs.getOption('buttons.popper').length).to.be.greaterThan(0);
693
+ expect(tabs.getOption("buttons.popper").length).to.be.greaterThan(0);
625
694
  expect(
626
695
  tabs
627
- .getOption('buttons.standard')
628
- .concat(tabs.getOption('buttons.popper')).length,
696
+ .getOption("buttons.standard")
697
+ .concat(tabs.getOption("buttons.popper")).length,
629
698
  ).to.equal(11);
630
699
  } finally {
631
700
  if (tabs && originalSetOption) {
@@ -636,334 +705,563 @@ describe('Tabs', function () {
636
705
  }
637
706
  });
638
707
 
639
- it('should ignore unavailable panels when creating tabs and buttons', function (done) {
640
- let mocks = document.getElementById('mocks');
708
+ it("should ignore unavailable panels when creating tabs and buttons", function (done) {
709
+ let mocks = document.getElementById("mocks");
641
710
  mocks.innerHTML = htmlAvailability;
642
711
 
643
712
  waitForCondition(() => {
644
- const tabs = document.getElementById('availability-tabs');
713
+ const tabs = document.getElementById("availability-tabs");
645
714
  return (
646
715
  tabs instanceof Tabs &&
647
- tabs.shadowRoot.querySelectorAll('button[part=button]').length === 2 &&
648
- tabs.getActiveTab() === 'overview'
716
+ tabs.shadowRoot.querySelectorAll("button[part=button]").length ===
717
+ 2 &&
718
+ tabs.getActiveTab() === "overview"
649
719
  );
650
- }).then(() => {
651
- try {
652
- const tabs = document.getElementById('availability-tabs');
653
- const vacation = document.getElementById('vacation-panel');
654
- const buttons = tabs.shadowRoot.querySelectorAll('button[part=button]');
655
-
656
- expect(tabs.getTabs().length).to.equal(2);
657
- expect(tabs.getTabs().includes(vacation)).to.equal(false);
658
- expect(tabs.shadowRoot.querySelector(
659
- `button[part=button][data-monster-tab-reference="${vacation.getAttribute('id')}"]`,
660
- )).to.equal(null);
661
- expect(buttons[0].textContent.trim()).to.equal('Overview');
662
- expect(buttons[1].textContent.trim()).to.equal('Details');
663
- expect(tabs.getActiveTab()).to.equal('overview');
664
- done();
665
- } catch (e) {
666
- done(e);
667
- }
668
- }).catch(done);
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);
669
745
  });
670
746
 
671
- it('should ignore unavailable tabs in activeTab and rebuild when they become available', function (done) {
672
- let mocks = document.getElementById('mocks');
747
+ it("should ignore unavailable tabs in activeTab and rebuild when they become available", function (done) {
748
+ let mocks = document.getElementById("mocks");
673
749
  mocks.innerHTML = htmlAvailability;
674
750
 
675
751
  waitForCondition(() => {
676
- const tabs = document.getElementById('availability-tabs');
677
- return tabs instanceof Tabs && tabs.getActiveTab() === 'overview';
678
- }).then(() => {
679
- const tabs = document.getElementById('availability-tabs');
680
- const vacation = document.getElementById('vacation-panel');
681
- let availabilityEvent = null;
682
- tabs.addEventListener('monster-tab-availability-changed', (event) => {
683
- availabilityEvent = event;
684
- });
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
+ });
685
762
 
686
- tabs.activeTab('vacation');
687
- expect(tabs.getActiveTab()).to.equal('overview');
763
+ tabs.activeTab("vacation");
764
+ expect(tabs.getActiveTab()).to.equal("overview");
688
765
 
689
- vacation.setAttribute('data-monster-tab-available', 'true');
766
+ vacation.setAttribute("data-monster-tab-available", "true");
690
767
 
691
- return waitForCondition(() => {
692
- return tabs.shadowRoot.querySelectorAll('button[part=button]').length === 3;
693
- }).then(() => {
694
- try {
695
- expect(availabilityEvent).to.not.equal(null);
696
- expect(availabilityEvent.detail.name).to.equal('vacation');
697
- expect(availabilityEvent.detail.available).to.equal(true);
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);
698
778
 
699
- tabs.activeTab('vacation');
700
- expect(tabs.getActiveTab()).to.equal('vacation');
701
- done();
702
- } catch (e) {
703
- done(e);
704
- }
705
- }, 100);
706
- }).catch(done);
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);
707
788
  });
708
789
 
709
- it('should fallback when the active tab becomes unavailable', function (done) {
710
- let mocks = document.getElementById('mocks');
790
+ it("should fallback when the active tab becomes unavailable", function (done) {
791
+ let mocks = document.getElementById("mocks");
711
792
  mocks.innerHTML = htmlActiveUnavailable;
712
793
 
713
794
  waitForCondition(() => {
714
- const tabs = document.getElementById('fallback-tabs');
715
- return tabs instanceof Tabs && tabs.getActiveTab() === 'second';
716
- }).then(() => {
717
- const tabs = document.getElementById('fallback-tabs');
718
- const activePanel = document.getElementById('second-panel');
719
- activePanel.setAttribute('data-monster-tab-available', 'false');
720
-
721
- return waitForCondition(() => tabs.getActiveTab() === 'first').then(() => {
722
- try {
723
- expect(activePanel.classList.contains('active')).to.equal(false);
724
- expect(tabs.shadowRoot.querySelectorAll('button[part=button]').length).to.equal(2);
725
- done();
726
- } catch (e) {
727
- done(e);
728
- }
729
- });
730
- }).catch(done);
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);
731
821
  });
732
822
 
733
- it('should fallback when the active tab is removed directly', function (done) {
734
- let mocks = document.getElementById('mocks');
823
+ it("should fallback when the active tab is removed directly", function (done) {
824
+ let mocks = document.getElementById("mocks");
735
825
  mocks.innerHTML = htmlRemoveFallback;
736
826
 
737
827
  waitForCondition(() => {
738
- const tabs = document.getElementById('remove-fallback-tabs');
828
+ const tabs = document.getElementById("remove-fallback-tabs");
739
829
  return (
740
830
  tabs instanceof Tabs &&
741
- tabs.getActiveTab() === 'second' &&
742
- tabs.shadowRoot.querySelectorAll('button[part=button]').length === 3
831
+ tabs.getActiveTab() === "second" &&
832
+ tabs.shadowRoot.querySelectorAll("button[part=button]").length === 3
743
833
  );
744
- }).then(() => {
745
- const tabs = document.getElementById('remove-fallback-tabs');
746
- setTimeout(() => {
747
- document.getElementById('remove-second-panel').remove();
748
-
749
- waitForCondition(() => tabs.getActiveTab() === 'first').then(() => {
750
- try {
751
- expect(tabs.shadowRoot.querySelectorAll('button[part=button]').length).to.equal(2);
752
- done();
753
- } catch (e) {
754
- done(e);
755
- }
756
- }).catch(done);
757
- });
758
- }).catch(done);
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);
759
856
  });
760
857
 
761
- it('should keep disabled tabs visible but not activatable', function (done) {
762
- let mocks = document.getElementById('mocks');
858
+ it("should keep disabled tabs visible but not activatable", function (done) {
859
+ let mocks = document.getElementById("mocks");
763
860
  mocks.innerHTML = htmlDisabled;
764
861
 
765
862
  waitForCondition(() => {
766
- const tabs = document.getElementById('disabled-tabs');
767
- const disabledPanel = document.getElementById('disabled-panel');
863
+ const tabs = document.getElementById("disabled-tabs");
864
+ const disabledPanel = document.getElementById("disabled-panel");
768
865
  const disabledButton = tabs?.shadowRoot?.querySelector(
769
- `button[part=button][data-monster-tab-reference="${disabledPanel?.getAttribute('id')}"]`,
866
+ `button[part=button][data-monster-tab-reference="${disabledPanel?.getAttribute("id")}"]`,
770
867
  );
771
868
  return (
772
869
  tabs instanceof Tabs &&
773
870
  disabledButton instanceof HTMLButtonElement &&
774
- tabs.getActiveTab() === 'enabled'
871
+ tabs.getActiveTab() === "enabled"
775
872
  );
776
- }).then(() => {
777
- try {
778
- const tabs = document.getElementById('disabled-tabs');
779
- const disabledPanel = document.getElementById('disabled-panel');
780
- const disabledButton = tabs.shadowRoot.querySelector(
781
- `button[part=button][data-monster-tab-reference="${disabledPanel.getAttribute('id')}"]`,
782
- );
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
+ );
783
881
 
784
- expect(disabledButton.disabled).to.equal(true);
785
- expect(disabledButton.getAttribute('title')).to.equal('Only available for employees');
786
- tabs.activeTab('disabled');
787
- expect(tabs.getActiveTab()).to.equal('enabled');
788
- done();
789
- } catch (e) {
790
- done(e);
791
- }
792
- }).catch(done);
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);
793
894
  });
794
895
 
795
- it('should expose refresh, sync, hide and show APIs', function (done) {
796
- let mocks = document.getElementById('mocks');
896
+ it("should expose refresh, sync, hide and show APIs", function (done) {
897
+ let mocks = document.getElementById("mocks");
797
898
  mocks.innerHTML = htmlAvailability;
798
899
 
799
900
  waitForCondition(() => {
800
- const tabs = document.getElementById('availability-tabs');
801
- return tabs instanceof Tabs && tabs.shadowRoot.querySelectorAll('button[part=button]').length === 2;
802
- }).then(() => {
803
- const tabs = document.getElementById('availability-tabs');
804
-
805
- tabs.showTab('vacation');
806
- return tabs.refreshTabs().then((result) => {
807
- expect(result).to.equal(tabs);
808
- expect(tabs.getTabs().length).to.equal(3);
809
-
810
- tabs.hideTab('vacation');
811
- return tabs.syncTabs();
812
- }).then((result) => {
813
- try {
814
- expect(result).to.equal(tabs);
815
- expect(tabs.getTabs().length).to.equal(2);
816
- expect(document.getElementById('vacation-panel').getAttribute('data-monster-tab-available')).to.equal('false');
817
- done();
818
- } catch (e) {
819
- done(e);
820
- }
821
- });
822
- }).catch(done);
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);
823
936
  });
824
937
 
825
- it('should not derive labels from content when disabled by option', function (done) {
826
- let mocks = document.getElementById('mocks');
938
+ it("should not derive labels from content when disabled by option", function (done) {
939
+ let mocks = document.getElementById("mocks");
827
940
  mocks.innerHTML = htmlNoDerivedLabel;
828
941
 
829
942
  waitForCondition(() => {
830
- const tabs = document.getElementById('label-tabs');
831
- return tabs instanceof Tabs && tabs.shadowRoot.querySelector('button[part=button]') !== null;
832
- }).then(() => {
833
- try {
834
- const tabs = document.getElementById('label-tabs');
835
- const button = tabs.shadowRoot.querySelector('button[part=button]');
836
- expect(button.textContent.trim()).to.equal('New Tab');
837
- expect(document.getElementById('content-panel').hasAttribute('data-monster-button-label')).to.equal(false);
838
- done();
839
- } catch (e) {
840
- done(e);
841
- }
842
- }).catch(done);
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);
843
965
  });
844
966
 
845
- it('should render SVG tab icons as theme-aware masks by default', function (done) {
846
- let mocks = document.getElementById('mocks');
967
+ it("should render SVG tab icons as theme-aware masks by default", function (done) {
968
+ let mocks = document.getElementById("mocks");
847
969
  mocks.innerHTML = htmlIconModes;
848
970
 
849
971
  waitForCondition(() => {
850
- const tabs = document.getElementById('icon-mode-tabs');
851
- return tabs instanceof Tabs && tabs.shadowRoot.querySelectorAll('button[part=button]').length === 4;
852
- }).then(() => {
853
- try {
854
- const tabs = document.getElementById('icon-mode-tabs');
855
- const getIcon = (reference) => tabs.shadowRoot.querySelector(
856
- `button[part=button][data-monster-tab-reference="${reference}"] [part=icon]`,
857
- );
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
+ );
858
985
 
859
- const autoSvgIcon = getIcon('auto-svg-panel');
860
- expect(autoSvgIcon.tagName).to.equal('SPAN');
861
- expect(autoSvgIcon.getAttribute('data-monster-icon-mode')).to.equal('mask');
862
- expect(autoSvgIcon.getAttribute('aria-hidden')).to.equal('true');
863
- expect(autoSvgIcon.getAttribute('style')).to.contain('--monster-tab-icon-mask-image');
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
+ );
864
995
 
865
- const imageSvgIcon = getIcon('image-svg-panel');
866
- expect(imageSvgIcon.tagName).to.equal('IMG');
867
- expect(imageSvgIcon.getAttribute('data-monster-icon-mode')).to.equal('image');
868
- expect(imageSvgIcon.getAttribute('src')).to.equal(svgTabIcon);
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);
869
1002
 
870
- const autoRasterIcon = getIcon('auto-raster-panel');
871
- expect(autoRasterIcon.tagName).to.equal('IMG');
872
- expect(autoRasterIcon.getAttribute('data-monster-icon-mode')).to.equal('image');
873
- expect(autoRasterIcon.getAttribute('src')).to.equal(pngTabIcon);
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);
874
1009
 
875
- const maskRasterIcon = getIcon('mask-raster-panel');
876
- expect(maskRasterIcon.tagName).to.equal('SPAN');
877
- expect(maskRasterIcon.getAttribute('data-monster-icon-mode')).to.equal('mask');
878
- expect(maskRasterIcon.getAttribute('style')).to.contain('--monster-tab-icon-mask-image');
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
+ );
879
1018
 
880
- done();
881
- } catch (e) {
882
- done(e);
883
- }
884
- }).catch(done);
1019
+ done();
1020
+ } catch (e) {
1021
+ done(e);
1022
+ }
1023
+ })
1024
+ .catch(done);
885
1025
  });
886
1026
 
887
- it('should update tab icon mode when the icon mode attribute changes', function (done) {
888
- let mocks = document.getElementById('mocks');
1027
+ it("should update tab icon mode when the icon mode attribute changes", function (done) {
1028
+ let mocks = document.getElementById("mocks");
889
1029
  mocks.innerHTML = htmlIconModes;
890
1030
 
891
1031
  waitForCondition(() => {
892
- const tabs = document.getElementById('icon-mode-tabs');
893
- return tabs instanceof Tabs && tabs.shadowRoot.querySelectorAll('button[part=button]').length === 4;
894
- }).then(() => {
895
- const tabs = document.getElementById('icon-mode-tabs');
896
- const panel = document.getElementById('image-svg-panel');
897
- panel.setAttribute('data-monster-button-icon-mode', 'mask');
898
-
899
- return waitForCondition(() => {
900
- const icon = tabs.shadowRoot.querySelector(
901
- 'button[part=button][data-monster-tab-reference="image-svg-panel"] [part=icon]',
902
- );
903
- return icon?.tagName === 'SPAN' && icon.getAttribute('data-monster-icon-mode') === 'mask';
904
- });
905
- }).then(() => {
906
- done();
907
- }).catch(done);
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);
908
1057
  });
909
1058
 
910
- it('should render vertical tab SVG icons as theme-aware masks by default', function (done) {
911
- let mocks = document.getElementById('mocks');
1059
+ it("should render vertical tab SVG icons as theme-aware masks by default", function (done) {
1060
+ let mocks = document.getElementById("mocks");
912
1061
  mocks.innerHTML = htmlVerticalIconModes;
913
1062
 
914
1063
  waitForCondition(() => {
915
- const tabs = document.getElementById('vertical-icon-mode-tabs');
916
- return tabs instanceof VerticalTabs && tabs.shadowRoot.querySelector('button[part=button] [part=icon]') !== null;
917
- }).then(() => {
918
- try {
919
- const tabs = document.getElementById('vertical-icon-mode-tabs');
920
- const icon = tabs.shadowRoot.querySelector(
921
- 'button[part=button][data-monster-tab-reference="vertical-auto-svg-panel"] [part=icon]',
922
- );
923
- expect(icon.tagName).to.equal('SPAN');
924
- expect(icon.getAttribute('data-monster-icon-mode')).to.equal('mask');
925
- expect(icon.getAttribute('style')).to.contain('--monster-tab-icon-mask-image');
926
- done();
927
- } catch (e) {
928
- done(e);
929
- }
930
- }).catch(done);
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);
931
1090
  });
932
1091
 
933
- it('should include stable names and metadata in tab events and buttons', function (done) {
934
- let mocks = document.getElementById('mocks');
1092
+ it("should include stable names and metadata in tab events and buttons", function (done) {
1093
+ let mocks = document.getElementById("mocks");
935
1094
  mocks.innerHTML = htmlMetadata;
936
1095
 
937
1096
  waitForCondition(() => {
938
- const tabs = document.getElementById('metadata-tabs');
939
- return tabs instanceof Tabs && tabs.shadowRoot.querySelector('button[part=button]') !== null;
940
- }).then(() => {
941
- try {
942
- const tabs = document.getElementById('metadata-tabs');
943
- const button = tabs.shadowRoot.querySelector('button[part=button]');
944
- let changedEvent = null;
945
- tabs.addEventListener('monster-tab-changed', (event) => {
946
- changedEvent = event;
947
- });
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
+ });
948
1111
 
949
- expect(button.getAttribute('data-monster-tab-name')).to.equal('metadata');
950
- expect(button.getAttribute('data-monster-tab-kind')).to.equal('profile');
951
- expect(button.getAttribute('data-monster-tab-priority')).to.equal('10');
952
- expect(button.getAttribute('data-monster-tab-group')).to.equal('main');
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
+ );
953
1124
 
954
- tabs.activeTab('metadata');
955
- expect(changedEvent).to.not.equal(null);
956
- expect(changedEvent.detail.name).to.equal('metadata');
957
- expect(changedEvent.detail.tab).to.equal('metadata');
958
- expect(changedEvent.detail.metadata.kind).to.equal('profile');
959
- done();
960
- } catch (e) {
961
- done(e);
962
- }
963
- }).catch(done);
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);
964
1136
  });
965
1137
 
966
- });
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
+ `;
967
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
+ });
968
1174
 
1175
+ dragTab(tabs, "first-tab", "third-tab");
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
+ });
969
1267
  });