@getflip/swirl-components 0.122.2 → 0.123.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,5 +1,6 @@
1
1
  import { h, Host, } from "@stencil/core";
2
2
  import Sortable from "sortablejs";
3
+ import { v4 as uuid } from "uuid";
3
4
  export class SwirlResourceList {
4
5
  constructor() {
5
6
  this.focusedIndex = 0;
@@ -57,6 +58,15 @@ export class SwirlResourceList {
57
58
  event.preventDefault();
58
59
  this.stopDrag(this.dragging);
59
60
  }
61
+ else if (Boolean(this.controllingElement)) {
62
+ const item = this.items[this.focusedIndex];
63
+ if (!Boolean(item) || !item.isConnected) {
64
+ return;
65
+ }
66
+ event.stopPropagation();
67
+ event.preventDefault();
68
+ item.click();
69
+ }
60
70
  }
61
71
  else if (event.code === "Home") {
62
72
  event.preventDefault();
@@ -71,12 +81,15 @@ export class SwirlResourceList {
71
81
  this.assistiveTextItemGrabbed = "Item grabbed. Use arrow keys to move item up or down. Use spacebar to save position.";
72
82
  this.assistiveTextItemMoving = "Current position:";
73
83
  this.assistiveTextItemMoved = "Item moved. New position:";
84
+ this.controllingElement = undefined;
74
85
  this.label = undefined;
75
86
  this.assistiveText = undefined;
87
+ this.listId = uuid();
76
88
  }
77
89
  componentDidLoad() {
78
90
  this.observeSlotChanges();
79
91
  this.collectItems();
92
+ this.setupControllingElement();
80
93
  this.setItemAllowDragState();
81
94
  this.setupDragDrop();
82
95
  }
@@ -86,6 +99,7 @@ export class SwirlResourceList {
86
99
  disconnectedCallback() {
87
100
  this.sortable?.destroy();
88
101
  this.observer?.disconnect();
102
+ this.controllingElement?.removeEventListener("keydown", this.onKeyDown);
89
103
  }
90
104
  observeSlotChanges() {
91
105
  this.observer = new MutationObserver(() => {
@@ -106,6 +120,18 @@ export class SwirlResourceList {
106
120
  this.items = Array.from(this.el.querySelectorAll("swirl-resource-list-item, swirl-resource-list-file-item")).filter((el) => el.isConnected);
107
121
  this.removeItemsFromTabOrder();
108
122
  this.enableItemFocus(this.items[this.focusedIndex]);
123
+ if (Boolean(this.controllingElement)) {
124
+ this.items[0]?.setAttribute("aria-selected", "true");
125
+ }
126
+ }
127
+ setupControllingElement() {
128
+ if (!Boolean(this.controllingElement)) {
129
+ return;
130
+ }
131
+ this.controllingElement.setAttribute("aria-controls", this.listId);
132
+ this.controllingElement.setAttribute("role", "combobox");
133
+ this.controllingElement.setAttribute("aria-autocomplete", "list");
134
+ this.controllingElement.addEventListener("keydown", this.onKeyDown);
109
135
  }
110
136
  getItemIndex(item) {
111
137
  return this.items.map((i) => i).findIndex((i) => i === item);
@@ -114,6 +140,9 @@ export class SwirlResourceList {
114
140
  this.items.forEach((item) => {
115
141
  const focusableEl = item?.querySelector(".resource-list-item__content, .resource-list-file-item");
116
142
  const dragHandle = item?.querySelector(".resource-list-item__drag-handle");
143
+ if (Boolean(this.controllingElement)) {
144
+ item.setAttribute("aria-selected", "false");
145
+ }
117
146
  focusableEl?.setAttribute("tabIndex", "-1");
118
147
  dragHandle?.setAttribute("tabIndex", "-1");
119
148
  });
@@ -153,7 +182,7 @@ export class SwirlResourceList {
153
182
  });
154
183
  }
155
184
  enableItemFocus(item, focus) {
156
- if (!Boolean(item)) {
185
+ if (!Boolean(item) || Boolean(this.controllingElement)) {
157
186
  return;
158
187
  }
159
188
  const interactiveElement = item.querySelector(".resource-list-item__content, .resource-list-file-item");
@@ -165,7 +194,7 @@ export class SwirlResourceList {
165
194
  return;
166
195
  }
167
196
  interactiveElement.setAttribute("tabIndex", "0");
168
- if (focus) {
197
+ if (focus && !Boolean(this.controllingElement)) {
169
198
  interactiveElement.focus();
170
199
  }
171
200
  }
@@ -175,7 +204,11 @@ export class SwirlResourceList {
175
204
  if (!Boolean(item) || !item.isConnected) {
176
205
  return;
177
206
  }
178
- this.enableItemFocus(item, true);
207
+ this.enableItemFocus(item, !Boolean(this.controllingElement));
208
+ if (Boolean(this.controllingElement)) {
209
+ item.setAttribute("aria-selected", "true");
210
+ item.scrollIntoView({ block: "nearest" });
211
+ }
179
212
  this.focusedIndex = index;
180
213
  }
181
214
  moveDraggedItemDown() {
@@ -199,7 +232,7 @@ export class SwirlResourceList {
199
232
  this.assistiveText = `${this.assistiveTextItemMoving} ${this.getItemIndex(this.dragging) + 1}`;
200
233
  }
201
234
  render() {
202
- return (h(Host, { onKeyDown: this.onKeyDown }, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("swirl-stack", { "aria-label": this.label, class: "resource-list", ref: (el) => (this.gridEl = el), role: "grid" }, h("slot", null))));
235
+ return (h(Host, { onKeyDown: this.onKeyDown }, h("swirl-visually-hidden", { role: "alert" }, this.assistiveText), h("swirl-stack", { "aria-label": this.label, class: "resource-list", id: this.listId, ref: (el) => (this.gridEl = el), role: "grid" }, h("slot", null))));
203
236
  }
204
237
  static get is() { return "swirl-resource-list"; }
205
238
  static get encapsulation() { return "scoped"; }
@@ -286,6 +319,25 @@ export class SwirlResourceList {
286
319
  "reflect": false,
287
320
  "defaultValue": "\"Item moved. New position:\""
288
321
  },
322
+ "controllingElement": {
323
+ "type": "unknown",
324
+ "mutable": false,
325
+ "complexType": {
326
+ "original": "HTMLElement",
327
+ "resolved": "HTMLElement",
328
+ "references": {
329
+ "HTMLElement": {
330
+ "location": "global"
331
+ }
332
+ }
333
+ },
334
+ "required": false,
335
+ "optional": true,
336
+ "docs": {
337
+ "tags": [],
338
+ "text": ""
339
+ }
340
+ },
289
341
  "label": {
290
342
  "type": "string",
291
343
  "mutable": false,
@@ -307,7 +359,8 @@ export class SwirlResourceList {
307
359
  }
308
360
  static get states() {
309
361
  return {
310
- "assistiveText": {}
362
+ "assistiveText": {},
363
+ "listId": {}
311
364
  };
312
365
  }
313
366
  static get events() {
@@ -1,7 +1,7 @@
1
+ import { MockElement } from "@stencil/core/mock-doc";
1
2
  import { newSpecPage } from "@stencil/core/testing";
2
3
  import { SwirlResourceListItem } from "../swirl-resource-list-item/swirl-resource-list-item";
3
4
  import { SwirlResourceList } from "./swirl-resource-list";
4
- import { MockElement } from "@stencil/core/mock-doc";
5
5
  global.MutationObserver = class {
6
6
  constructor() { }
7
7
  disconnect() { }
@@ -43,10 +43,11 @@ describe("swirl-resource-list", () => {
43
43
  components: [SwirlResourceList],
44
44
  html: template,
45
45
  });
46
+ const id = page.root.querySelector(".resource-list")?.id;
46
47
  expect(page.root).toEqualHtml(`
47
48
  <swirl-resource-list label="Label">
48
49
  <swirl-visually-hidden role="alert"></swirl-visually-hidden>
49
- <swirl-stack aria-label="Label" class="resource-list" role="grid">
50
+ <swirl-stack aria-label="Label" class="resource-list" id="${id}" role="grid">
50
51
  <swirl-resource-list-item description="With a description" label="This is a resource item" media="<swirl-avatar label=&quot;John Doe&quot; src=&quot;https://picsum.photos/id/433/144/144&quot;></swirl-avatar>"></swirl-resource-list-item>
51
52
  <swirl-resource-list-item description="With a description" label="This is a resource item" media="<swirl-avatar label=&quot;John Doe&quot; src=&quot;https://picsum.photos/id/103/144/144&quot;></swirl-avatar>"></swirl-resource-list-item>
52
53
  </swirl-stack>
@@ -11,6 +11,7 @@ export default {
11
11
  title: "Components/SwirlResourceList",
12
12
  };
13
13
  const Template = (args) => {
14
+ const container = document.createElement("div");
14
15
  const element = generateStoryElement("swirl-resource-list", args);
15
16
  element.innerHTML = `
16
17
  <swirl-resource-list-item
@@ -33,9 +34,14 @@ const Template = (args) => {
33
34
  <swirl-avatar label="John Doe" src="https://picsum.photos/id/1027/144/144" slot="media"></swirl-avatar>
34
35
  </swirl-resource-list-item>
35
36
  `;
36
- return element;
37
+ const input = document.createElement("input");
38
+ container.appendChild(element);
39
+ container.appendChild(input);
40
+ element.controllingElement = input;
41
+ return container;
37
42
  };
38
43
  export const SwirlResourceList = Template.bind({});
39
44
  SwirlResourceList.args = {
40
45
  label: "A resource list",
46
+ focusSelectedItems: false,
41
47
  };
@@ -8,6 +8,17 @@
8
8
  box-sizing: border-box;
9
9
  }
10
10
 
11
+ :host[aria-selected="true"] .resource-list-item__content {
12
+ background-color: var(--swirl-resource-list-item-background-hovered);
13
+ }
14
+
15
+ :host[aria-selected="true"] .resource-list-item__content .resource-list-item__media,
16
+ :host[aria-selected="true"] .resource-list-item__content .resource-list-item__meta {
17
+ --swirl-avatar-background-color: var(--s-surface-raised-hovered);
18
+ --swirl-avatar-group-border-color: var(--s-background-hovered);
19
+ --swirl-badge-border-color: var(--s-background-hovered);
20
+ }
21
+
11
22
  .resource-list-item {
12
23
  position: relative;
13
24
  width: 100%;