@gjsify/adwaita-web 0.64.13 → 0.64.14

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 (36) hide show
  1. package/lib/types/application.d.ts +3 -3
  2. package/lib/types/elements/adw-application-window.d.ts +4 -4
  3. package/lib/types/elements/adw-split-button.d.ts +19 -28
  4. package/lib/types/elements/adw-tab-overview.d.ts +4 -4
  5. package/lib/types/elements/gtk-menu-button.d.ts +30 -42
  6. package/lib/types/elements/gtk-popover-bin.d.ts +18 -11
  7. package/lib/types/elements/gtk-popover-menu-bar.d.ts +15 -8
  8. package/lib/types/elements/gtk-popover-menu.d.ts +13 -15
  9. package/lib/types/elements/menu-slot.d.ts +49 -0
  10. package/lib/types/elements/popover-menu.d.ts +11 -7
  11. package/lib/types/index.d.ts +2 -1
  12. package/lib/types/namespace/gio.d.ts +1 -1
  13. package/package.json +11 -11
  14. package/src/adw-application-window.spec.ts +5 -2
  15. package/src/application.ts +4 -4
  16. package/src/elements/adw-application-window.ts +17 -21
  17. package/src/elements/adw-split-button.ts +35 -59
  18. package/src/elements/adw-tab-overview.ts +18 -28
  19. package/src/elements/gtk-menu-button.ts +60 -82
  20. package/src/elements/gtk-popover-bin.ts +32 -38
  21. package/src/elements/gtk-popover-menu-bar.ts +41 -46
  22. package/src/elements/gtk-popover-menu.ts +35 -54
  23. package/src/elements/menu-slot.ts +139 -0
  24. package/src/elements/popover-menu.ts +26 -16
  25. package/src/gio-menu.spec.ts +173 -34
  26. package/src/gtk-image.spec.ts +3 -2
  27. package/src/gtk-menu-button.spec.ts +19 -16
  28. package/src/gtk-popover-bin.spec.ts +6 -5
  29. package/src/gtk-popover-menu-bar.spec.ts +11 -6
  30. package/src/gtk-popover-menu.spec.ts +22 -22
  31. package/src/index.ts +10 -2
  32. package/src/keyboard-operable.spec.ts +6 -5
  33. package/src/menu-actions.spec.ts +6 -1
  34. package/src/namespace/gio.ts +19 -2
  35. package/src/shared-tree-builder.ts +6 -1
  36. package/src/split-button.spec.ts +25 -8
@@ -9,11 +9,11 @@
9
9
 
10
10
  import { describe, expect, it } from '@gjsify/unit';
11
11
  import { MENU_LABEL_TEXT_VECTORS, MENU_REFUSAL_VECTORS } from '@gjsify/adwaita-core/conformance';
12
- import { flattenMenu } from '@gjsify/adwaita-core';
13
- import type { AdwMenuInput, AdwMenuModel } from '@gjsify/adwaita-core';
12
+ import { buildMenu, flattenMenu } from '@gjsify/adwaita-core';
13
+ import type { AdwMenuModel, AdwMenuTree } from '@gjsify/adwaita-core';
14
14
 
15
15
  interface PopoverMenuElement extends HTMLElement {
16
- menuModel: AdwMenuInput | AdwMenuModel;
16
+ menuModel: AdwMenuModel | string | null;
17
17
  actions: Record<string, { enabled?: boolean; state?: string }> | null;
18
18
  visibleSubmenu: string;
19
19
  // The three below are `GtkPopover`'s own — the reason this spec can say "inherits the
@@ -76,7 +76,7 @@ export const GtkPopoverMenuTest = async () => {
76
76
  await describe('<gtk-popover-menu> the page stack', async () => {
77
77
  await it('renders the model and reports the root page as `main`', () => {
78
78
  const el = mount();
79
- el.menuModel = NESTED;
79
+ el.menuModel = buildMenu(NESTED);
80
80
  expect(labels(el)).toStrictEqual(['New', 'More']);
81
81
  expect(el.visibleSubmenu).toBe('main');
82
82
  unmountAll();
@@ -84,7 +84,7 @@ export const GtkPopoverMenuTest = async () => {
84
84
 
85
85
  await it('opens a submenu by name and swaps the page', () => {
86
86
  const el = mount();
87
- el.menuModel = NESTED;
87
+ el.menuModel = buildMenu(NESTED);
88
88
  el.openSubmenu('More');
89
89
  // A submenu's page is the back row plus its own items — the submenu's contents
90
90
  // are never inlined into the parent.
@@ -95,7 +95,7 @@ export const GtkPopoverMenuTest = async () => {
95
95
 
96
96
  await it('`main` goes back, and so does closeSubmenus', () => {
97
97
  const el = mount();
98
- el.menuModel = NESTED;
98
+ el.menuModel = buildMenu(NESTED);
99
99
  el.openSubmenu('More');
100
100
  el.openSubmenu('main');
101
101
  expect(labels(el)).toStrictEqual(['New', 'More']);
@@ -109,7 +109,7 @@ export const GtkPopoverMenuTest = async () => {
109
109
 
110
110
  await it('a name no submenu carries leaves the open page alone', () => {
111
111
  const el = mount();
112
- el.menuModel = NESTED;
112
+ el.menuModel = buildMenu(NESTED);
113
113
  el.openSubmenu('More');
114
114
  // `gtk_stack_set_visible_child_name` on a child that is not there is a no-op in
115
115
  // GTK, so the current page must survive rather than blank the surface.
@@ -121,7 +121,7 @@ export const GtkPopoverMenuTest = async () => {
121
121
 
122
122
  await it('`visible-submenu` as an ATTRIBUTE navigates, and notifies once per page', () => {
123
123
  const el = mount();
124
- el.menuModel = NESTED;
124
+ el.menuModel = buildMenu(NESTED);
125
125
  const seen: string[] = [];
126
126
  el.addEventListener('notify::visible-submenu', (event) => {
127
127
  seen.push((event as CustomEvent<{ visibleSubmenu: string }>).detail.visibleSubmenu);
@@ -137,9 +137,9 @@ export const GtkPopoverMenuTest = async () => {
137
137
 
138
138
  await it('a CHANGED model drops the open page', () => {
139
139
  const el = mount();
140
- el.menuModel = NESTED;
140
+ el.menuModel = buildMenu(NESTED);
141
141
  el.openSubmenu('More');
142
- el.menuModel = [{ label: 'Only' }];
142
+ el.menuModel = buildMenu([{ label: 'Only' }]);
143
143
  // The submenu the page named is gone; staying on it would leave a back row and
144
144
  // no items behind it.
145
145
  expect(el.visibleSubmenu).toBe('main');
@@ -149,16 +149,16 @@ export const GtkPopoverMenuTest = async () => {
149
149
 
150
150
  await it('an UNCHANGED model keeps the open page', () => {
151
151
  const el = mount();
152
- el.menuModel = NESTED;
152
+ el.menuModel = buildMenu(NESTED);
153
153
  el.openSubmenu('More');
154
- el.menuModel = JSON.parse(JSON.stringify(NESTED)) as AdwMenuInput;
154
+ el.menuModel = buildMenu(JSON.parse(JSON.stringify(NESTED)));
155
155
  expect(el.visibleSubmenu).toBe('More');
156
156
  unmountAll();
157
157
  });
158
158
 
159
159
  await it('closing the popover returns to the root page', async () => {
160
160
  const el = mount();
161
- el.menuModel = NESTED;
161
+ el.menuModel = buildMenu(NESTED);
162
162
  el.openSubmenu('More');
163
163
  el.open = true;
164
164
  el.open = false;
@@ -175,7 +175,7 @@ export const GtkPopoverMenuTest = async () => {
175
175
  const el = document.createElement('gtk-popover-menu') as PopoverMenuElement;
176
176
  anchor.appendChild(el);
177
177
  document.body.appendChild(anchor);
178
- el.menuModel = NESTED;
178
+ el.menuModel = buildMenu(NESTED);
179
179
  el.openSubmenu('More');
180
180
  el.anchor = anchor;
181
181
  el.open = true;
@@ -197,7 +197,7 @@ export const GtkPopoverMenuTest = async () => {
197
197
 
198
198
  await it('a disabled row activates nothing', () => {
199
199
  const el = mount();
200
- el.menuModel = [{ label: 'Quit', action: 'app.quit' }];
200
+ el.menuModel = buildMenu([{ label: 'Quit', action: 'app.quit' }]);
201
201
  el.actions = { 'app.quit': { enabled: false } };
202
202
  let fired = false;
203
203
  el.addEventListener('menu-item-activated', () => {
@@ -212,11 +212,11 @@ export const GtkPopoverMenuTest = async () => {
212
212
  await describe('<gtk-popover-menu> the menu attribute', async () => {
213
213
  await it('reads the JSON and draws the same rows `flattenMenu` names', () => {
214
214
  const el = mount('[{"label":"New"},{"section":[{"label":"Cut"}],"label":"Edit"}]');
215
- const model: AdwMenuModel = [
215
+ const tree: AdwMenuTree = [
216
216
  { kind: 'item', label: 'New' },
217
217
  { kind: 'section', label: 'Edit', items: [{ kind: 'item', label: 'Cut' }] },
218
218
  ];
219
- expect(labels(el)).toStrictEqual(flattenMenu(model).map((row) => row.node.label));
219
+ expect(labels(el)).toStrictEqual(flattenMenu(tree).map((row) => row.node.label));
220
220
  unmountAll();
221
221
  });
222
222
 
@@ -243,15 +243,15 @@ export const GtkPopoverMenuTest = async () => {
243
243
  });
244
244
 
245
245
  await it('the PROPERTY path refuses a `custom` item loudly', () => {
246
- for (const { model, paths, rule } of MENU_REFUSAL_VECTORS) {
246
+ for (const { tree, paths, rule } of MENU_REFUSAL_VECTORS) {
247
247
  const el = mount();
248
248
  if (paths.length > 0) {
249
249
  expect(() => {
250
- el.menuModel = model;
250
+ el.menuModel = buildMenu(tree);
251
251
  }).toThrow('adwaita-web');
252
252
  } else {
253
- el.menuModel = model;
254
- expect(labels(el)).toStrictEqual(flattenMenu(model).map((row) => row.node.label));
253
+ el.menuModel = buildMenu(tree);
254
+ expect(labels(el)).toStrictEqual(flattenMenu(tree).map((row) => row.node.label));
255
255
  }
256
256
  expect(rule.length > 0).toBe(true);
257
257
  el.remove();
@@ -263,7 +263,7 @@ export const GtkPopoverMenuTest = async () => {
263
263
  for (const { label, text, rule } of MENU_LABEL_TEXT_VECTORS) {
264
264
  await it(`item, submenu and back row: ${rule}`, () => {
265
265
  const el = mount();
266
- el.menuModel = [{ label }, { label, submenu: [{ label }] }];
266
+ el.menuModel = buildMenu([{ label }, { label, submenu: [{ label }] }]);
267
267
  expect(labels(el)).toStrictEqual([text, text]);
268
268
  el.openSubmenu(label);
269
269
  expect(labels(el)).toStrictEqual([text, text]);
package/src/index.ts CHANGED
@@ -114,8 +114,9 @@ export * as Adw from './namespace/adw.js';
114
114
  export * as Gtk from './namespace/gtk.js';
115
115
 
116
116
  // The THIRD namespace, and the one that defines no element: the GObject types an author
117
- // CONSTRUCTS while building a tree. `Gio.Menu` is what makes `menuModel: menu` the same
118
- // program here as on GJS — `./namespace/gio.ts` says what does not go in it.
117
+ // CONSTRUCTS while building a tree. `Gio.Menu` is a real `Gio.MenuModel` (ADR 0128), which
118
+ // is what makes `menuModel: menu` the same program here as on GJS — `./namespace/gio.ts`
119
+ // says what does not go in it.
119
120
  export * as Gio from './namespace/gio.js';
120
121
 
121
122
  // Gdk's clipboard (`Display`, `Clipboard`, `ContentProvider`): `./namespace/gdk.ts` says what is absent.
@@ -189,6 +190,12 @@ export type { AdwViewStackPageInfo } from './elements/adw-view-stack.js';
189
190
  // The portable menu model (ADR 0042) is `@gjsify/adwaita-core`'s, re-exported so a
190
191
  // consumer of THIS package reaches the menu types without a second dependency — one
191
192
  // definition, two doors, never two declarations.
193
+ //
194
+ // `buildMenu` is a VALUE and not only a type, because since ADR 0128 a `menuModel`
195
+ // property takes a real `Gio.MenuModel` and an authored array is refused: without the
196
+ // builder an app depending on this package alone could not write a menu in code at all.
197
+ // The `menu-model` ATTRIBUTE still takes the JSON string, so markup needs none of this.
198
+ export { buildMenu, menuTree, watchMenuModel } from '@gjsify/adwaita-core';
192
199
  export type {
193
200
  AdwMenuActions,
194
201
  AdwMenuEntryInput,
@@ -199,6 +206,7 @@ export type {
199
206
  AdwMenuPath,
200
207
  AdwMenuSection,
201
208
  AdwMenuSubmenu,
209
+ AdwMenuTree,
202
210
  } from '@gjsify/adwaita-core';
203
211
  export type { GtkDropDownOption } from './elements/gtk-drop-down.js';
204
212
  export type { AdwDialogPresentationMode } from './elements/adw-dialog.js';
@@ -19,6 +19,7 @@
19
19
  // A real Tab press escaping an untrapped dialog is measured one layer up, by
20
20
  // `tests/browser/specs/adwaita-keyboard.spec.ts`.
21
21
  import { describe, expect, it } from '@gjsify/unit';
22
+ import { buildMenu } from '@gjsify/adwaita-core';
22
23
 
23
24
  import type { AdwAlertDialog } from './elements/adw-alert-dialog.js';
24
25
  import { AdwToggle, AdwToggleGroup } from './elements/adw-toggle-group.js';
@@ -737,11 +738,11 @@ export const AdwKeyboardOperableTest = async () => {
737
738
  };
738
739
  document.body.appendChild(el);
739
740
  el.actions = { 'app.export': { enabled: false }, 'app.new': {} };
740
- el.menuModel = [
741
+ el.menuModel = buildMenu([
741
742
  { label: 'New', action: 'app.new' },
742
743
  { section: [{ label: 'Export', action: 'app.export' }, { label: 'Print' }] },
743
744
  { label: 'More', submenu: [{ label: 'Rename' }, { label: 'Duplicate' }] },
744
- ];
745
+ ]);
745
746
  (el.querySelector('.adw-menu-button-button') as HTMLElement).click();
746
747
  return {
747
748
  el,
@@ -828,14 +829,14 @@ export const AdwKeyboardOperableTest = async () => {
828
829
  };
829
830
  document.body.appendChild(el);
830
831
  el.actions = { 'app.off': { enabled: false } };
831
- el.menuModel = [
832
+ el.menuModel = buildMenu([
832
833
  { label: 'Dim', action: 'app.off' },
833
834
  { label: 'Live' },
834
835
  {
835
836
  label: 'More',
836
837
  submenu: [{ label: 'SubDim', action: 'app.off' }, { label: 'SubLive' }],
837
838
  },
838
- ];
839
+ ]);
839
840
  (el.querySelector('.adw-menu-button-button') as HTMLElement).click();
840
841
  const rows = () => [...el.querySelectorAll<HTMLButtonElement>('.adw-popover-item')];
841
842
  const label = () =>
@@ -867,7 +868,7 @@ export const AdwKeyboardOperableTest = async () => {
867
868
  };
868
869
  document.body.appendChild(el);
869
870
  el.actions = { 'app.off': { enabled: false } };
870
- el.menuModel = [{ label: 'More', submenu: [{ label: 'Only', action: 'app.off' }] }];
871
+ el.menuModel = buildMenu([{ label: 'More', submenu: [{ label: 'Only', action: 'app.off' }] }]);
871
872
  (el.querySelector('.adw-menu-button-button') as HTMLElement).click();
872
873
  const opener = el.querySelector('.adw-popover-item') as HTMLButtonElement;
873
874
  opener.focus();
@@ -7,6 +7,7 @@ import {
7
7
  registryMenuActionScene,
8
8
  type SharedTreeNode,
9
9
  } from '@gjsify/adwaita-core/conformance';
10
+ import { menuTree } from '@gjsify/adwaita-core';
10
11
  import { describe, expect, it } from '@gjsify/unit';
11
12
 
12
13
  import { parentOfElement } from './application.js';
@@ -39,7 +40,11 @@ export const MenuActionsTest = async () => {
39
40
  return;
40
41
  }
41
42
  const button = buildSharedTree(node) as unknown as GtkMenuButton;
42
- expect(button.menuModel.map((entry) => entry.kind).join(',')).toBe(result.kinds.join(','));
43
+ expect(
44
+ menuTree(button.menuModel!)
45
+ .map((entry) => entry.kind)
46
+ .join(','),
47
+ ).toBe(result.kinds.join(','));
43
48
  });
44
49
  }
45
50
  });
@@ -1,4 +1,4 @@
1
- // The GIO half of `@gjsify/adwaita-web`'s vocabulary — `Gio.Menu`, `Gio.MenuItem`.
1
+ // The GIO half of `@gjsify/adwaita-web`'s vocabulary — `Gio.MenuModel` and its subclasses.
2
2
  // `export * as Gio from './namespace/gio.js'` in `src/index.ts` makes it the namespace,
3
3
  // the same module shape `./adw.ts` and `./gtk.ts` take and for the same reason (one list
4
4
  // carrying the value AND the type meaning — see the header of `./adw.ts`).
@@ -28,7 +28,24 @@ import { createAppInfo } from '@gjsify/adwaita-core';
28
28
 
29
29
  import { webPlatformHost } from '../platform-host.js';
30
30
 
31
- export { GioMenu as Menu, GioMenuItem as MenuItem } from '@gjsify/adwaita-core';
31
+ // ADR 0128: `Gio.MenuModel` is an abstract CLASS, so the whole menu hierarchy is here and
32
+ // `menu instanceof Gio.MenuModel` answers the same on both runtimes. The two iterators come
33
+ // with it because `iterate_item_attributes` / `iterate_item_links` return them by name.
34
+ export {
35
+ GioMenu as Menu,
36
+ GioMenuItem as MenuItem,
37
+ GioMenuModel as MenuModel,
38
+ GioMenuAttributeIter as MenuAttributeIter,
39
+ GioMenuLinkIter as MenuLinkIter,
40
+ MENU_ATTRIBUTE_ACTION,
41
+ MENU_ATTRIBUTE_ACTION_NAMESPACE,
42
+ MENU_ATTRIBUTE_ICON,
43
+ MENU_ATTRIBUTE_LABEL,
44
+ MENU_ATTRIBUTE_TARGET,
45
+ MENU_EXPORTER_MAX_SECTION_SIZE,
46
+ MENU_LINK_SECTION,
47
+ MENU_LINK_SUBMENU,
48
+ } from '@gjsify/adwaita-core';
32
49
  // ADR 0122 § 2: the interface a `Gtk.StringList` implements and an `Implements: [Gio.ListModel]`
33
50
  // names. It is a token, not a class — the whole of the subset's interface support.
34
51
  export { ListModel } from '@gjsify/adwaita-core';
@@ -35,6 +35,7 @@
35
35
  // this small, most of which (the `SharedTreeNode` type) is erased at build anyway.
36
36
 
37
37
  import {
38
+ buildMenu,
38
39
  createBreakpointDriver,
39
40
  menuAssignments,
40
41
  parseBreakpointCondition,
@@ -512,8 +513,12 @@ function writeLook(el: HTMLElement, node: SharedTreeNode, menus?: SharedTreeNode
512
513
  for (const [prop, value] of Object.entries(node.props ?? {})) {
513
514
  if (prop !== 'menu-model') writeProp(el, prop, value);
514
515
  }
516
+ // `buildMenu`, because since ADR 0128 `menuModel` is a real `Gio.MenuModel` and a tree
517
+ // written at it is refused the way GJS refuses one. `menuAssignments` stays a tree walker
518
+ // — it resolves a `menu-model: id` against the file's `menus` — so the model is built
519
+ // here, at the one place the resolved tree becomes a widget's property.
515
520
  for (const [prop, model] of menuAssignments(node, menus)) {
516
- (el as unknown as Record<string, unknown>)[propertyOf(prop)] = model;
521
+ (el as unknown as Record<string, unknown>)[propertyOf(prop)] = buildMenu(model);
517
522
  }
518
523
  // `styleClasses` is `GtkWidget:css-classes`, and this renderer's door for it is the
519
524
  // `class` attribute — what `.title-1`, `.dimmed` and `.card` select on. Unread, a
@@ -17,7 +17,7 @@ import {
17
17
  SPLIT_BUTTON_STYLE_CLASS_VECTORS,
18
18
  SPLIT_BUTTON_TOOLTIP_VECTORS,
19
19
  } from '@gjsify/adwaita-core/conformance';
20
- import { ADW_MENU_SURFACE_WEB, flattenMenu, menuRefusals } from '@gjsify/adwaita-core';
20
+ import { ADW_MENU_SURFACE_WEB, buildMenu, flattenMenu, menuRefusals, menuTree } from '@gjsify/adwaita-core';
21
21
  import type { SplitButtonProperty } from '@gjsify/adwaita-core';
22
22
  import type { AdwSplitButton } from './elements/adw-split-button.js';
23
23
  import { fallbackMask, maskOf } from './icon-masks.spec.js';
@@ -238,24 +238,24 @@ export const AdwSplitButtonTest = async () => {
238
238
  });
239
239
 
240
240
  await describe('adw-split-button menu', async () => {
241
- for (const { input, model, rule } of MENU_NORMALIZE_VECTORS) {
241
+ for (const { input, tree, rule } of MENU_NORMALIZE_VECTORS) {
242
242
  await it(`normalises an authored menu — ${rule}`, () => {
243
243
  const { el, host } = mount({ label: 'Save' });
244
244
  // What THIS surface refuses is core's answer, not a list restated here:
245
245
  // a `custom` item names an application widget the browser cannot host,
246
246
  // and the setter is where that is said out loud.
247
- if (menuRefusals(model, ADW_MENU_SURFACE_WEB).length > 0) {
247
+ if (menuRefusals(tree, ADW_MENU_SURFACE_WEB).length > 0) {
248
248
  expect(() => {
249
- el.menuModel = input;
249
+ el.menuModel = buildMenu(input);
250
250
  }).toThrow('adwaita-web');
251
251
  host.remove();
252
252
  return;
253
253
  }
254
- el.menuModel = input;
255
- expect([...el.menuModel]).toStrictEqual([...model]);
254
+ el.menuModel = buildMenu(input);
255
+ expect(menuTree(el.menuModel!)).toStrictEqual([...tree]);
256
256
  // Every node the model draws is a row: a section is inlined, a submenu
257
257
  // is one row of its own.
258
- expect(el.querySelectorAll('.adw-split-button-menu-item').length).toBe(flattenMenu(model).length);
258
+ expect(el.querySelectorAll('.adw-split-button-menu-item').length).toBe(flattenMenu(tree).length);
259
259
  host.remove();
260
260
  });
261
261
  }
@@ -312,11 +312,28 @@ export const AdwSplitButtonTest = async () => {
312
312
  const { el, host } = mount({ 'menu-model': '[{"label":"Print"}]' });
313
313
  dropdownHalf(el).click();
314
314
  expect(el.active).toBe(true);
315
- el.setAttribute('menu-model', '[]');
315
+ // REMOVING the attribute is how a menu is lost since ADR 0128 — `null` is the
316
+ // only spelling of "no menu", and the row below is the other half of that.
317
+ el.removeAttribute('menu-model');
316
318
  expect(el.active).toBe(false);
317
319
  expect(el.dropdownEnabled).toBe(false);
318
320
  host.remove();
319
321
  });
322
+
323
+ await it('an EMPTY menu is still a menu, so the dropdown stays live', () => {
324
+ // Measured on `Adw.SplitButton`: an empty non-null `Gio.Menu` at `menu-model`
325
+ // still builds a `GtkPopoverMenu` and `set_menu_model(null)` is what returns
326
+ // the popover to null (`MENU_MODEL_DOOR_VECTORS`). `[]` therefore edits the
327
+ // menu this element OWNS rather than taking it away, so the open popup follows
328
+ // the edit instead of being closed by it.
329
+ const { el, host } = mount({ 'menu-model': '[{"label":"Print"}]' });
330
+ dropdownHalf(el).click();
331
+ el.setAttribute('menu-model', '[]');
332
+ expect(el.dropdownEnabled).toBe(true);
333
+ expect(el.active).toBe(true);
334
+ expect(el.querySelectorAll('.adw-split-button-menu-item').length).toBe(0);
335
+ host.remove();
336
+ });
320
337
  });
321
338
 
322
339
  await describe('adw-split-button signals + state classes', async () => {