@getflip/swirl-components 0.78.0 → 0.79.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.
Files changed (87) hide show
  1. package/README.md +1 -1
  2. package/components.json +138 -20
  3. package/dist/cjs/file-manager.cjs.entry.js +2 -1
  4. package/dist/cjs/index-506fe4ea.js +2 -2
  5. package/dist/cjs/loader.cjs.js +1 -1
  6. package/dist/cjs/swirl-app-layout_7.cjs.entry.js +7 -4
  7. package/dist/cjs/swirl-button.cjs.entry.js +3 -1
  8. package/dist/cjs/swirl-components.cjs.js +1 -1
  9. package/dist/cjs/swirl-date-input.cjs.entry.js +1 -1
  10. package/dist/cjs/swirl-lightbox.cjs.entry.js +1 -1
  11. package/dist/cjs/swirl-pdf-reader.cjs.entry.js +2 -2
  12. package/dist/cjs/{swirl-popover.cjs.entry.js → swirl-popover_2.cjs.entry.js} +104 -18
  13. package/dist/cjs/swirl-select.cjs.entry.js +2 -2
  14. package/dist/collection/collection-manifest.json +1 -0
  15. package/dist/collection/components/swirl-button/swirl-button.js +37 -1
  16. package/dist/collection/components/swirl-date-input/swirl-date-input.js +1 -1
  17. package/dist/collection/components/swirl-date-input/swirl-date-input.spec.js +6 -4
  18. package/dist/collection/components/swirl-lightbox/swirl-lightbox.js +1 -1
  19. package/dist/collection/components/swirl-lightbox/swirl-lightbox.spec.js +6 -4
  20. package/dist/collection/components/swirl-menu/swirl-menu.spec.js +38 -14
  21. package/dist/collection/components/swirl-menu/swirl-menu.stories.js +35 -33
  22. package/dist/collection/components/swirl-pdf-reader/swirl-pdf-reader.js +2 -2
  23. package/dist/collection/components/swirl-pdf-reader/swirl-pdf-reader.spec.js +4 -2
  24. package/dist/collection/components/swirl-popover/swirl-popover.js +35 -39
  25. package/dist/collection/components/swirl-popover/swirl-popover.spec.js +20 -11
  26. package/dist/collection/components/swirl-popover/swirl-popover.stories.js +8 -25
  27. package/dist/collection/components/swirl-popover-trigger/swirl-popover-trigger.css +3 -0
  28. package/dist/collection/components/swirl-popover-trigger/swirl-popover-trigger.js +132 -0
  29. package/dist/collection/components/swirl-popover-trigger/swirl-popover-trigger.spec.js +26 -0
  30. package/dist/collection/components/swirl-popover-trigger/swirl-popover-trigger.stories.js +48 -0
  31. package/dist/collection/components/swirl-resource-list-item/swirl-resource-list-item.css +7 -0
  32. package/dist/collection/components/swirl-resource-list-item/swirl-resource-list-item.js +7 -3
  33. package/dist/collection/components/swirl-resource-list-item/swirl-resource-list-item.spec.js +1 -0
  34. package/dist/collection/components/swirl-resource-list-item/swirl-resource-list-item.stories.js +29 -29
  35. package/dist/collection/components/swirl-select/swirl-select.js +2 -2
  36. package/dist/collection/components/swirl-select/swirl-select.spec.js +4 -2
  37. package/dist/collection/prototypes/file-manager/file-manager.js +4 -2
  38. package/dist/components/file-manager.js +54 -46
  39. package/dist/components/swirl-button2.js +5 -1
  40. package/dist/components/swirl-date-input.js +13 -7
  41. package/dist/components/swirl-lightbox.js +21 -15
  42. package/dist/components/swirl-pdf-reader.js +48 -42
  43. package/dist/components/swirl-popover-trigger.d.ts +11 -0
  44. package/dist/components/swirl-popover-trigger.js +6 -0
  45. package/dist/components/swirl-popover-trigger2.js +99 -0
  46. package/dist/components/swirl-popover2.js +25 -19
  47. package/dist/components/swirl-resource-list-item2.js +19 -10
  48. package/dist/components/swirl-select.js +18 -12
  49. package/dist/esm/file-manager.entry.js +2 -1
  50. package/dist/esm/index-99d0060d.js +2 -2
  51. package/dist/esm/loader.js +1 -1
  52. package/dist/esm/swirl-app-layout_7.entry.js +7 -4
  53. package/dist/esm/swirl-button.entry.js +3 -1
  54. package/dist/esm/swirl-components.js +1 -1
  55. package/dist/esm/swirl-date-input.entry.js +1 -1
  56. package/dist/esm/swirl-lightbox.entry.js +1 -1
  57. package/dist/esm/swirl-pdf-reader.entry.js +2 -2
  58. package/dist/esm/{swirl-popover.entry.js → swirl-popover_2.entry.js} +104 -19
  59. package/dist/esm/swirl-select.entry.js +2 -2
  60. package/dist/swirl-components/p-0fce283f.entry.js +1 -0
  61. package/dist/swirl-components/p-2278aeae.entry.js +1 -0
  62. package/dist/swirl-components/p-251884ec.entry.js +1 -0
  63. package/dist/swirl-components/{p-a16a6381.entry.js → p-30ae7d5c.entry.js} +1 -1
  64. package/dist/swirl-components/p-3abcd491.entry.js +1 -0
  65. package/dist/swirl-components/p-794d25e2.entry.js +1 -0
  66. package/dist/swirl-components/p-ed921838.entry.js +1 -0
  67. package/dist/swirl-components/p-f9054b79.entry.js +1 -0
  68. package/dist/swirl-components/swirl-components.esm.js +1 -1
  69. package/dist/types/components/swirl-button/swirl-button.d.ts +2 -0
  70. package/dist/types/components/swirl-popover/swirl-popover.d.ts +3 -5
  71. package/dist/types/components/swirl-popover/swirl-popover.stories.d.ts +0 -3
  72. package/dist/types/components/swirl-popover-trigger/swirl-popover-trigger.d.ts +13 -0
  73. package/dist/types/components/swirl-popover-trigger/swirl-popover-trigger.stories.d.ts +22 -0
  74. package/dist/types/components/swirl-resource-list-item/swirl-resource-list-item.d.ts +1 -0
  75. package/dist/types/components/swirl-resource-list-item/swirl-resource-list-item.stories.d.ts +8 -3
  76. package/dist/types/components.d.ts +24 -5
  77. package/dist/types/prototypes/file-manager/file-manager.d.ts +1 -0
  78. package/dist/typings.d.ts +1 -0
  79. package/package.json +2 -2
  80. package/vscode-data.json +24 -3
  81. package/dist/swirl-components/p-8c507544.entry.js +0 -1
  82. package/dist/swirl-components/p-acd95936.entry.js +0 -1
  83. package/dist/swirl-components/p-b105d565.entry.js +0 -1
  84. package/dist/swirl-components/p-b28fadcf.entry.js +0 -1
  85. package/dist/swirl-components/p-d98edc75.entry.js +0 -1
  86. package/dist/swirl-components/p-ef50db1d.entry.js +0 -1
  87. package/dist/swirl-components/p-f990566c.entry.js +0 -1
@@ -48,7 +48,9 @@ describe("swirl-pdf-reader", () => {
48
48
  </span>
49
49
  </span>
50
50
  <span class="pdf-reader__header-right">
51
- <swirl-button class="pdf-reader__menu-button" hidelabel="" icon="<swirl-icon-more-vertikal></swirl-icon-more-vertikal>" id="menu-trigger" label="Open file menu"></swirl-button>
51
+ <swirl-popover-trigger>
52
+ <swirl-button class="pdf-reader__menu-button" hidelabel="" icon="<swirl-icon-more-vertikal></swirl-icon-more-vertikal>" label="Open file menu"></swirl-button>
53
+ </swirl-popover-trigger>
52
54
  </span>
53
55
  <span class="pdf-reader__floating-tools">
54
56
  <button aria-label="Toggle side by side view" class="pdf-reader__floating-tool-button" type="button">
@@ -75,7 +77,7 @@ describe("swirl-pdf-reader", () => {
75
77
  </div>
76
78
  </div>
77
79
  </div>
78
- <swirl-popover animation="scale-in-y" disablescrolllock="" label="File menu" placement="bottom-end" popoverid="menu" trigger="menu-trigger">
80
+ <swirl-popover animation="scale-in-y" disablescrolllock="" id="menu" label="File menu" placement="bottom-end">
79
81
  <swirl-stack>
80
82
  <div class="pdf-reader__meta">
81
83
  <div class="pdf-reader__file-icon">
@@ -55,7 +55,6 @@ export class SwirlPopover {
55
55
  this.maxHeight = "22rem";
56
56
  this.offset = 8;
57
57
  this.placement = "bottom-start";
58
- this.popoverId = undefined;
59
58
  this.trigger = undefined;
60
59
  this.triggerContainer = undefined;
61
60
  this.useContainerWidth = undefined;
@@ -65,13 +64,16 @@ export class SwirlPopover {
65
64
  }
66
65
  componentDidLoad() {
67
66
  this.connectTrigger();
68
- this.updateFocusableChildren();
69
67
  this.updateTriggerAttributes();
68
+ if (Boolean(this.trigger)) {
69
+ console.warn('[Swirl] The "trigger" prop of swirl-popover is deprecated and will be removed with the next major release. Please use the swirl-popover-trigger component instead. https://swirl-storybook.flip-app.dev/?path=/docs/components-swirlpopovertrigger--docs');
70
+ }
70
71
  }
71
72
  disconnectedCallback() {
72
73
  this.unlockBodyScroll();
73
74
  }
74
75
  onWindowFocusIn(event) {
76
+ var _a;
75
77
  if (!this.active) {
76
78
  return;
77
79
  }
@@ -82,7 +84,7 @@ export class SwirlPopover {
82
84
  const popoverLostFocus = !this.el.contains(target) &&
83
85
  !this.el.contains(activeElement) &&
84
86
  target !== this.triggerEl &&
85
- !this.triggerEl.contains(target) &&
87
+ !((_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.contains(target)) &&
86
88
  (!isSafari ||
87
89
  (isSafari &&
88
90
  !this.el.contains(relatedTarget || target) &&
@@ -102,7 +104,7 @@ export class SwirlPopover {
102
104
  .some((el) => Boolean(el) && el instanceof Node
103
105
  ? this.el.contains(el)
104
106
  : false);
105
- const clickedTrigger = event.target === this.triggerEl;
107
+ const clickedTrigger = target === this.triggerEl;
106
108
  if (!clickedChild && !clickedShadowChild && !clickedTrigger) {
107
109
  this.close();
108
110
  }
@@ -133,19 +135,20 @@ export class SwirlPopover {
133
135
  * Open the popover.
134
136
  * @returns
135
137
  */
136
- async open() {
137
- if (this.active) {
138
+ async open(triggerEl) {
139
+ this.triggerEl = triggerEl || this.triggerEl;
140
+ if (this.active || !Boolean(this.triggerEl)) {
138
141
  return;
139
142
  }
140
143
  this.adjustWidth();
141
144
  this.active = true;
142
- this.updateFocusableChildren();
143
145
  this.updateTriggerAttributes();
146
+ const focusableChildren = this.getFocusableChildren();
144
147
  requestAnimationFrame(async () => {
145
148
  await this.reposition();
146
149
  this.popoverOpen.emit({ position: this.position });
147
- if (this.focusableChildren.length > 0) {
148
- this.focusableChildren[0].focus();
150
+ if (focusableChildren.length > 0) {
151
+ focusableChildren[0].focus();
149
152
  }
150
153
  else {
151
154
  this.contentContainer.focus();
@@ -153,12 +156,16 @@ export class SwirlPopover {
153
156
  if (this.disableAutoUpdate) {
154
157
  this.disableAutoUpdate();
155
158
  }
156
- this.disableAutoUpdate = autoUpdate(this.triggerEl, this.contentContainer, this.reposition);
159
+ this.disableAutoUpdate = autoUpdate(this.triggerEl, this.contentContainer, () => this.reposition());
157
160
  this.scrollContainer.scrollTop = 0;
158
161
  this.lockBodyScroll();
159
162
  });
160
163
  }
161
164
  connectTrigger() {
165
+ if (!Boolean(this.trigger)) {
166
+ this.triggerEl = undefined;
167
+ return;
168
+ }
162
169
  this.triggerEl =
163
170
  typeof this.trigger === "string"
164
171
  ? querySelectorAllDeep(this.triggerContainer || document.body, `#${this.trigger}`)[0]
@@ -171,10 +178,10 @@ export class SwirlPopover {
171
178
  });
172
179
  }
173
180
  getNativeTriggerElement() {
174
- var _a, _b, _c;
175
- return this.triggerEl.tagName.startsWith("SWIRL-")
176
- ? (((_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.children[0]) ||
177
- ((_c = (_b = this.triggerEl) === null || _b === void 0 ? void 0 : _b.shadowRoot) === null || _c === void 0 ? void 0 : _c.children[0]) ||
181
+ var _a, _b, _c, _d;
182
+ return ((_a = this.triggerEl) === null || _a === void 0 ? void 0 : _a.tagName.startsWith("SWIRL-"))
183
+ ? (((_b = this.triggerEl) === null || _b === void 0 ? void 0 : _b.children[0]) ||
184
+ ((_d = (_c = this.triggerEl) === null || _c === void 0 ? void 0 : _c.shadowRoot) === null || _d === void 0 ? void 0 : _d.children[0]) ||
178
185
  this.triggerEl)
179
186
  : this.triggerEl;
180
187
  }
@@ -183,12 +190,12 @@ export class SwirlPopover {
183
190
  return;
184
191
  }
185
192
  const nativeTriggerEl = this.getNativeTriggerElement();
186
- nativeTriggerEl.setAttribute("aria-controls", this.popoverId);
193
+ nativeTriggerEl.setAttribute("aria-controls", this.el.id);
187
194
  nativeTriggerEl.setAttribute("aria-expanded", String(this.active));
188
195
  nativeTriggerEl.setAttribute("aria-haspopup", "dialog");
189
196
  }
190
- updateFocusableChildren() {
191
- this.focusableChildren = querySelectorAllDeep(this.el, '[role="menuitem"], [role="listbox"]');
197
+ getFocusableChildren() {
198
+ return querySelectorAllDeep(this.el, '[role="menuitem"], [role="listbox"]');
192
199
  }
193
200
  adjustWidth() {
194
201
  let useContainerWidth = this.useContainerWidth;
@@ -235,7 +242,7 @@ export class SwirlPopover {
235
242
  "popover--fullscreen-bottom-sheet": this.fullscreenBottomSheet,
236
243
  "popover--inactive": !this.active,
237
244
  });
238
- return (h(Host, { id: this.popoverId }, h("div", { class: className, onKeyDown: this.onKeydown }, h("div", { "aria-hidden": !this.active ? "true" : "false", "aria-label": this.label, class: "popover__content", part: "popover__content", role: "dialog", ref: (el) => (this.contentContainer = el), style: {
245
+ return (h(Host, null, h("div", { class: className, onKeyDown: this.onKeydown }, h("div", { "aria-hidden": !this.active ? "true" : "false", "aria-label": this.label, class: "popover__content", part: "popover__content", role: "dialog", ref: (el) => (this.contentContainer = el), style: {
239
246
  top: Boolean(this.position) ? `${(_b = this.position) === null || _b === void 0 ? void 0 : _b.y}px` : "",
240
247
  left: Boolean(this.position) ? `${(_c = this.position) === null || _c === void 0 ? void 0 : _c.x}px` : "",
241
248
  }, tabindex: "-1" }, h("span", { class: "popover__handle" }), h("div", { class: "popover__scroll-container", ref: (el) => (this.scrollContainer = el), style: {
@@ -409,23 +416,6 @@ export class SwirlPopover {
409
416
  "reflect": false,
410
417
  "defaultValue": "\"bottom-start\""
411
418
  },
412
- "popoverId": {
413
- "type": "string",
414
- "mutable": false,
415
- "complexType": {
416
- "original": "string",
417
- "resolved": "string",
418
- "references": {}
419
- },
420
- "required": true,
421
- "optional": false,
422
- "docs": {
423
- "tags": [],
424
- "text": ""
425
- },
426
- "attribute": "popover-id",
427
- "reflect": false
428
- },
429
419
  "trigger": {
430
420
  "type": "string",
431
421
  "mutable": false,
@@ -438,8 +428,8 @@ export class SwirlPopover {
438
428
  }
439
429
  }
440
430
  },
441
- "required": true,
442
- "optional": false,
431
+ "required": false,
432
+ "optional": true,
443
433
  "docs": {
444
434
  "tags": [],
445
435
  "text": ""
@@ -553,11 +543,17 @@ export class SwirlPopover {
553
543
  },
554
544
  "open": {
555
545
  "complexType": {
556
- "signature": "() => Promise<void>",
557
- "parameters": [],
546
+ "signature": "(triggerEl?: HTMLElement) => Promise<void>",
547
+ "parameters": [{
548
+ "tags": [],
549
+ "text": ""
550
+ }],
558
551
  "references": {
559
552
  "Promise": {
560
553
  "location": "global"
554
+ },
555
+ "HTMLElement": {
556
+ "location": "global"
561
557
  }
562
558
  },
563
559
  "return": "Promise<void>"
@@ -1,10 +1,13 @@
1
1
  import { newSpecPage } from "@stencil/core/testing";
2
2
  import { SwirlPopover } from "./swirl-popover";
3
+ import { SwirlPopoverTrigger } from "../swirl-popover-trigger/swirl-popover-trigger";
3
4
  describe("swirl-popover", () => {
4
5
  const template = `
5
6
  <div>
6
- <button id="trigger">Trigger popover</button>
7
- <swirl-popover label="Popover" popover-id="popover" trigger="trigger">
7
+ <swirl-popover-trigger popover="popover">
8
+ <button id="trigger">Trigger popover</button>
9
+ </swirl-popover-trigger>
10
+ <swirl-popover label="Popover" id="popover">
8
11
  <div>Content</div>
9
12
  </swirl-popover>
10
13
  </div>
@@ -19,13 +22,15 @@ describe("swirl-popover", () => {
19
22
  });
20
23
  it("renders the trigger and content", async () => {
21
24
  const page = await newSpecPage({
22
- components: [SwirlPopover],
25
+ components: [SwirlPopover, SwirlPopoverTrigger],
23
26
  html: template,
24
27
  });
25
28
  expect(page.body).toEqualHtml(`
26
29
  <div>
27
- <button aria-controls="popover" aria-expanded="false" aria-haspopup="dialog" id="trigger">Trigger popover</button>
28
- <swirl-popover id="popover" label="Popover" popover-id="popover" trigger="trigger">
30
+ <swirl-popover-trigger popover="popover">
31
+ <button aria-controls="popover" aria-expanded="false" aria-haspopup="dialog" id="trigger">Trigger popover</button>
32
+ </swirl-popover-trigger>
33
+ <swirl-popover id="popover" label="Popover">
29
34
  <mock:shadow-root>
30
35
  <div class="popover popover--animation-scale-in-xy popover--inactive popover--placement-undefined">
31
36
  <div aria-hidden="true" aria-label="Popover" class="popover__content" part="popover__content" role="dialog" tabindex="-1">
@@ -45,18 +50,20 @@ describe("swirl-popover", () => {
45
50
  });
46
51
  it("opens on click and closes on blur/esc", async () => {
47
52
  const page = await newSpecPage({
48
- components: [SwirlPopover],
53
+ components: [SwirlPopover, SwirlPopoverTrigger],
49
54
  html: template,
50
55
  });
51
56
  function isOpen() {
52
- return !page.root.shadowRoot
53
- .querySelector(".popover")
57
+ return !page.doc
58
+ .querySelector("swirl-popover")
59
+ .shadowRoot.querySelector(".popover")
54
60
  .classList.contains("popover--inactive");
55
61
  }
56
62
  expect(isOpen()).toBeFalsy();
57
63
  // click trigger
58
- const trigger = page.body.querySelector("#trigger");
64
+ const trigger = page.root;
59
65
  trigger.click();
66
+ await new Promise((resolve) => setTimeout(resolve, 150));
60
67
  await page.waitForChanges();
61
68
  expect(isOpen()).toBeTruthy();
62
69
  // blur popover
@@ -66,11 +73,13 @@ describe("swirl-popover", () => {
66
73
  expect(isOpen()).toBeFalsy();
67
74
  // re-open popover
68
75
  trigger.click();
76
+ await new Promise((resolve) => setTimeout(resolve, 150));
69
77
  await page.waitForChanges();
70
78
  expect(isOpen()).toBeTruthy();
71
79
  // close via escape key
72
- page.root.shadowRoot
73
- .querySelector(".popover")
80
+ page.doc
81
+ .querySelector("swirl-popover")
82
+ .shadowRoot.querySelector(".popover")
74
83
  .dispatchEvent(new KeyboardEvent("keydown", { code: "Escape" }));
75
84
  await new Promise((resolve) => setTimeout(resolve, 150));
76
85
  await page.waitForChanges();
@@ -13,7 +13,7 @@ export default {
13
13
  },
14
14
  },
15
15
  trigger: {
16
- description: "ID of the trigger element or the trigger DOM element.",
16
+ description: "**Deprecated! Please use the swirl-popover-trigger component instead.** ID of the trigger element or the trigger DOM element.",
17
17
  control: {
18
18
  type: "text",
19
19
  },
@@ -31,34 +31,18 @@ export default {
31
31
  parameters: {
32
32
  docs: {
33
33
  page: Docs,
34
- source: {
35
- code: `<swirl-button id="trigger" label="Trigger"></swirl-button>
36
-
37
- <swirl-popover label="Popover" popover-id="popover" trigger="trigger">
38
- <swirl-action-list>
39
- <swirl-action-list-section label="Section 1">
40
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 1"></swirl-action-list-item>
41
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 2"></swirl-action-list-item>
42
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 3"></swirl-action-list-item>
43
- </swirl-action-list-section>
44
- <swirl-action-list-section label="Section 2">
45
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 1"></swirl-action-list-item>
46
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 2"></swirl-action-list-item>
47
- <swirl-action-list-item icon="<swirl-icon-mention></swirl-icon-mention>" label="Action item 3"></swirl-action-list-item>
48
- </swirl-action-list-section>
49
- </swirl-action-list>
50
- </swirl-popover>`,
51
- },
52
34
  },
53
35
  },
54
36
  title: "Components/SwirlPopover",
55
37
  };
56
38
  const Template = (args) => {
57
39
  const container = document.createElement("div");
58
- const trigger = document.createElement("swirl-button");
40
+ const trigger = document.createElement("swirl-popover-trigger");
59
41
  const element = generateStoryElement("swirl-popover", args);
60
- trigger.id = "trigger";
61
- trigger.label = "Trigger popover";
42
+ trigger.setAttribute("popover", "popover");
43
+ trigger.innerHTML = `
44
+ <swirl-button label="Trigger popover"></swirl-button>
45
+ `;
62
46
  element.innerHTML = `
63
47
  <swirl-action-list>
64
48
  <swirl-action-list-section label="Section 1">
@@ -73,7 +57,7 @@ const Template = (args) => {
73
57
  </swirl-action-list-section>
74
58
  </swirl-action-list>
75
59
  `;
76
- container.append(trigger, element);
60
+ container.append("\n ", trigger, "\n ", element);
77
61
  element.addEventListener("click", (event) => {
78
62
  const target = event.target;
79
63
  if ((target === null || target === void 0 ? void 0 : target.tagName) === "SWIRL-ACTION-LIST-ITEM") {
@@ -84,7 +68,6 @@ const Template = (args) => {
84
68
  };
85
69
  export const SwirlPopover = Template.bind({});
86
70
  SwirlPopover.args = {
71
+ id: "popover",
87
72
  label: "Popover",
88
- popoverId: "popover",
89
- trigger: "trigger",
90
73
  };
@@ -0,0 +1,3 @@
1
+ :host {
2
+ display: contents;
3
+ }
@@ -0,0 +1,132 @@
1
+ import { h, Host } from "@stencil/core";
2
+ export class SwirlPopoverTrigger {
3
+ constructor() {
4
+ this.popoverOpen = false;
5
+ this.onClick = (event) => {
6
+ event.stopPropagation();
7
+ const popoverEl = this.getPopoverEl();
8
+ if (!Boolean(popoverEl)) {
9
+ return;
10
+ }
11
+ if (this.popoverOpen) {
12
+ popoverEl.close();
13
+ return;
14
+ }
15
+ const triggerEl = this.getTriggerEl();
16
+ if (!Boolean(triggerEl)) {
17
+ return;
18
+ }
19
+ popoverEl.open(triggerEl);
20
+ popoverEl.addEventListener("popoverOpen", () => {
21
+ this.popoverOpen = true;
22
+ this.updateTriggerElAriaAttributes(true);
23
+ }, { once: true });
24
+ popoverEl.addEventListener("popoverClose", () => {
25
+ this.popoverOpen = false;
26
+ this.updateTriggerElAriaAttributes(false);
27
+ }, { once: true });
28
+ };
29
+ this.updateTriggerElAriaAttributes = (open) => {
30
+ var _a;
31
+ if (!this.setAriaAttributes) {
32
+ return;
33
+ }
34
+ const triggerEl = this.getTriggerEl();
35
+ const popoverId = typeof this.popover === "string" ? this.popover : (_a = this.popover) === null || _a === void 0 ? void 0 : _a.id;
36
+ if (triggerEl.tagName.startsWith("SWIRL-")) {
37
+ triggerEl.setAttribute("swirl-aria-controls", popoverId);
38
+ triggerEl.setAttribute("swirl-aria-expanded", String(open || "false"));
39
+ triggerEl.setAttribute("swirl-aria-haspopup", "dialog");
40
+ }
41
+ else {
42
+ triggerEl.setAttribute("aria-controls", popoverId);
43
+ triggerEl.setAttribute("aria-expanded", String(open || "false"));
44
+ triggerEl.setAttribute("aria-haspopup", "dialog");
45
+ }
46
+ };
47
+ this.popover = undefined;
48
+ this.setAriaAttributes = true;
49
+ }
50
+ componentDidLoad() {
51
+ this.updateTriggerElAriaAttributes();
52
+ }
53
+ watchPopover() {
54
+ this.updateTriggerElAriaAttributes();
55
+ }
56
+ getPopoverEl() {
57
+ return typeof this.popover === "string"
58
+ ? document.querySelector(`#${this.popover}`)
59
+ : this.popover;
60
+ }
61
+ getTriggerEl() {
62
+ if (this.el.children.length !== 1) {
63
+ console.warn('[Swirl] The "swirl-popover-trigger" component expects exactly one child element.');
64
+ }
65
+ return this.el.children[0];
66
+ }
67
+ render() {
68
+ return (h(Host, { onClick: this.onClick }, h("slot", null)));
69
+ }
70
+ static get is() { return "swirl-popover-trigger"; }
71
+ static get encapsulation() { return "scoped"; }
72
+ static get originalStyleUrls() {
73
+ return {
74
+ "$": ["swirl-popover-trigger.css"]
75
+ };
76
+ }
77
+ static get styleUrls() {
78
+ return {
79
+ "$": ["swirl-popover-trigger.css"]
80
+ };
81
+ }
82
+ static get properties() {
83
+ return {
84
+ "popover": {
85
+ "type": "string",
86
+ "mutable": false,
87
+ "complexType": {
88
+ "original": "string | HTMLSwirlPopoverElement",
89
+ "resolved": "HTMLSwirlPopoverElement | string",
90
+ "references": {
91
+ "HTMLSwirlPopoverElement": {
92
+ "location": "global"
93
+ }
94
+ }
95
+ },
96
+ "required": true,
97
+ "optional": false,
98
+ "docs": {
99
+ "tags": [],
100
+ "text": ""
101
+ },
102
+ "attribute": "popover",
103
+ "reflect": false
104
+ },
105
+ "setAriaAttributes": {
106
+ "type": "boolean",
107
+ "mutable": false,
108
+ "complexType": {
109
+ "original": "boolean",
110
+ "resolved": "boolean",
111
+ "references": {}
112
+ },
113
+ "required": false,
114
+ "optional": true,
115
+ "docs": {
116
+ "tags": [],
117
+ "text": ""
118
+ },
119
+ "attribute": "set-aria-attributes",
120
+ "reflect": false,
121
+ "defaultValue": "true"
122
+ }
123
+ };
124
+ }
125
+ static get elementRef() { return "el"; }
126
+ static get watchers() {
127
+ return [{
128
+ "propName": "popover",
129
+ "methodName": "watchPopover"
130
+ }];
131
+ }
132
+ }
@@ -0,0 +1,26 @@
1
+ import { newSpecPage } from "@stencil/core/testing";
2
+ import { SwirlPopoverTrigger } from "./swirl-popover-trigger";
3
+ import { SwirlButton } from "../swirl-button/swirl-button";
4
+ describe("swirl-popover-trigger", () => {
5
+ it("renders its trigger element and adds aria attributes", async () => {
6
+ const page = await newSpecPage({
7
+ components: [SwirlPopoverTrigger, SwirlButton],
8
+ html: `
9
+ <swirl-popover-trigger popover="popover">
10
+ <swirl-button label="trigger"></swirl-button>
11
+ </swirl-popover-trigger>
12
+ `,
13
+ });
14
+ expect(page.root).toEqualHtml(`
15
+ <swirl-popover-trigger popover="popover">
16
+ <swirl-button label="trigger" swirl-aria-controls="popover" swirl-aria-expanded="false" swirl-aria-haspopup="dialog">
17
+ <button aria-controls="popover" aria-expanded="false" aria-haspopup="dialog" class="button button--icon-position-start button--intent-default button--size-m button--variant-ghost" type="button">
18
+ <span class="button__label">
19
+ trigger
20
+ </span>
21
+ </button>
22
+ </swirl-button>
23
+ </swirl-popover-trigger>
24
+ `);
25
+ });
26
+ });
@@ -0,0 +1,48 @@
1
+ import { generateStoryElement } from "../../utils";
2
+ import Docs from "./swirl-popover-trigger.mdx";
3
+ export default {
4
+ argTypes: {
5
+ popover: {
6
+ description: "ID of a popover element or a reference to a DOM element of type HTMLSwirlPopoverElement.",
7
+ control: {
8
+ type: "text",
9
+ },
10
+ },
11
+ },
12
+ component: "swirl-popover-trigger",
13
+ tags: ["autodocs"],
14
+ parameters: {
15
+ docs: {
16
+ page: Docs,
17
+ },
18
+ },
19
+ title: "Components/SwirlPopoverTrigger",
20
+ };
21
+ const Template = (args) => {
22
+ const container = document.createElement("swirl-stack");
23
+ const element = generateStoryElement("swirl-popover-trigger", args);
24
+ const secondTrigger = generateStoryElement("swirl-popover-trigger", args);
25
+ const popover = document.createElement("swirl-popover");
26
+ container.orientation = "horizontal";
27
+ container.spacing = "16";
28
+ popover.label = "Popover";
29
+ popover.id = "popover";
30
+ popover.placement = "bottom-start";
31
+ popover.innerHTML = `
32
+ <swirl-box padding="12">
33
+ <swirl-text size="sm">Popover</swirl-text>
34
+ </swirl-box>
35
+ `;
36
+ element.innerHTML = `
37
+ <swirl-button label="Trigger element" variant="flat"></swirl-button>
38
+ `;
39
+ secondTrigger.innerHTML = `
40
+ <swirl-button label="Second trigger, same popover" variant="flat"></swirl-button>
41
+ `;
42
+ container.append("\n ", element, "\n ", secondTrigger, "\n ", popover, "\n");
43
+ return container;
44
+ };
45
+ export const SwirlPopoverTrigger = Template.bind({});
46
+ SwirlPopoverTrigger.args = {
47
+ popover: "popover",
48
+ };
@@ -223,6 +223,13 @@
223
223
  transform: translateY(-50%);
224
224
  }
225
225
 
226
+ .resource-list-item__control {
227
+ position: absolute;
228
+ top: 50%;
229
+ right: var(--s-space-16);
230
+ transform: translateY(-50%);
231
+ }
232
+
226
233
  .resource-list-item__checkbox {
227
234
  position: absolute;
228
235
  top: 50%;
@@ -2,6 +2,7 @@ import { h, Host, } from "@stencil/core";
2
2
  import classnames from "classnames";
3
3
  import { getDesktopMediaQuery } from "../../utils";
4
4
  /**
5
+ * @slot control - Used to add a menu button to the item
5
6
  * @slot media - Media displayed inside the item (e.g. swirl-avatar)
6
7
  */
7
8
  export class SwirlResourceListItem {
@@ -56,6 +57,9 @@ export class SwirlResourceListItem {
56
57
  this.forceIconProps(this.desktopMediaQuery.matches);
57
58
  this.updateIconSize(this.desktopMediaQuery.matches);
58
59
  this.desktopMediaQuery.onchange = this.desktopMediaQueryHandler;
60
+ if (Boolean(this.menuTriggerId)) {
61
+ console.warn('[Swirl] The "menu-trigger-id" prop of swirl-resource-list-item is deprecated and will be removed with the next major release. Please use the "control" slot to add a menu button instead. https://swirl-storybook.flip-app.dev/?path=/docs/components-swirlresourcelistitem--docs');
62
+ }
59
63
  }
60
64
  disconnectedCallback() {
61
65
  var _a, _b;
@@ -83,9 +87,9 @@ export class SwirlResourceListItem {
83
87
  ? "a"
84
88
  : "button";
85
89
  const disabled = this.disabled && !Boolean(this.href);
86
- const hasMenu = Boolean(this.menuTriggerId);
90
+ const hasMenu = Boolean(this.menuTriggerId) || this.el.querySelector("[slot='control']");
87
91
  const href = this.interactive && Boolean(this.href) ? this.href : undefined;
88
- const showMenu = hasMenu && !Boolean(this.meta) && !this.selectable;
92
+ const showMenu = Boolean(this.menuTriggerId) && !Boolean(this.meta) && !this.selectable;
89
93
  const showMeta = Boolean(this.meta) && !this.selectable;
90
94
  const ariaChecked = this.selectable ? String(this.checked) : undefined;
91
95
  const role = this.interactive && this.selectable ? "checkbox" : undefined;
@@ -99,7 +103,7 @@ export class SwirlResourceListItem {
99
103
  "resource-list-item--interactive": this.interactive || this.selectable,
100
104
  "resource-list-item--selectable": this.selectable,
101
105
  });
102
- return (h(Host, { role: "row" }, h("div", { class: className, role: "gridcell" }, h(Tag, { "aria-checked": ariaChecked, "aria-disabled": disabled ? "true" : undefined, "aria-labelledby": "label", class: "resource-list-item__content", href: href, disabled: disabled, onClick: this.onClick, part: "resource-list-item__content", role: role, tabIndex: 0 }, this.hasMedia && (h("span", { class: "resource-list-item__media" }, h("slot", { name: "media" }))), h("span", { class: "resource-list-item__label-container" }, h("span", { class: "resource-list-item__label", id: "label", innerHTML: this.label }), this.description && (h("span", { class: "resource-list-item__description" }, this.description)))), this.selectable && (h("span", { "aria-hidden": "true", class: "resource-list-item__checkbox" }, h("span", { class: "resource-list-item__checkbox-icon" }, this.checked && (h("swirl-icon-check-strong", null))))), showMeta && (h("span", { class: "resource-list-item__meta" }, this.meta)), showMenu && (h("swirl-button", { "aria-disabled": disabled ? "true" : undefined, class: "resource-list-item__menu-trigger", disabled: disabled, hideLabel: true, icon: "<swirl-icon-more-horizontal></swirl-icon-more-horizontal>", id: this.menuTriggerId, intent: "primary", label: this.menuTriggerLabel, onClick: this.onMenuTriggerClick }))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "resource-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
106
+ return (h(Host, { role: "row" }, h("div", { class: className, role: "gridcell" }, h(Tag, { "aria-checked": ariaChecked, "aria-disabled": disabled ? "true" : undefined, "aria-labelledby": "label", class: "resource-list-item__content", href: href, disabled: disabled, onClick: this.onClick, part: "resource-list-item__content", role: role, tabIndex: 0 }, this.hasMedia && (h("span", { class: "resource-list-item__media" }, h("slot", { name: "media" }))), h("span", { class: "resource-list-item__label-container" }, h("span", { class: "resource-list-item__label", id: "label", innerHTML: this.label }), this.description && (h("span", { class: "resource-list-item__description" }, this.description)))), this.selectable && (h("span", { "aria-hidden": "true", class: "resource-list-item__checkbox" }, h("span", { class: "resource-list-item__checkbox-icon" }, this.checked && (h("swirl-icon-check-strong", null))))), showMeta && (h("span", { class: "resource-list-item__meta" }, this.meta)), h("span", { class: "resource-list-item__control" }, h("slot", { name: "control" })), showMenu && (h("swirl-popover-trigger", { popover: this.menuTriggerId }, h("swirl-button", { "aria-disabled": disabled ? "true" : undefined, class: "resource-list-item__menu-trigger", disabled: disabled, hideLabel: true, icon: "<swirl-icon-more-horizontal></swirl-icon-more-horizontal>", intent: "primary", label: this.menuTriggerLabel, onClick: this.onMenuTriggerClick })))), this.allowDrag && (h("button", { "aria-describedby": this.dragHandleDescription, "aria-label": `${this.dragHandleLabel} "${this.label}"`, class: "resource-list-item__drag-handle", onKeyDown: this.onDragHandleKeyDown, type: "button" }, h("swirl-icon-drag-handle", { size: this.iconSize })))));
103
107
  }
104
108
  static get is() { return "swirl-resource-list-item"; }
105
109
  static get encapsulation() { return "scoped"; }
@@ -29,6 +29,7 @@ describe("swirl-resource-list-item", () => {
29
29
  </span>
30
30
  </span>
31
31
  </button>
32
+ <span class="resource-list-item__control"></span>
32
33
  </div>
33
34
  </swirl-resource-list-item>
34
35
  `);