@gjsify/adwaita-web 0.55.0 → 0.56.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/dist/adwaita-web.css +129 -27
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-status-page.spec.d.ts +1 -0
- package/lib/types/attributes.d.ts +19 -0
- package/lib/types/elements/adw-action-row.d.ts +4 -0
- package/lib/types/elements/adw-combo-row.d.ts +4 -0
- package/lib/types/elements/adw-entry-row.d.ts +6 -0
- package/lib/types/elements/adw-expander-row.d.ts +2 -0
- package/lib/types/elements/adw-header-bar.d.ts +6 -0
- package/lib/types/elements/adw-overlay-split-view.d.ts +13 -0
- package/lib/types/elements/adw-sidebar.d.ts +3 -2
- package/lib/types/elements/adw-spin-row.d.ts +6 -0
- package/lib/types/elements/adw-switch-row.d.ts +4 -0
- package/lib/types/elements/adw-toggle-group.d.ts +17 -0
- package/lib/types/elements/gtk-scrolled-window.d.ts +10 -2
- package/lib/types/elements/gtk-window.d.ts +8 -2
- package/lib/types/index.d.ts +1 -1
- package/lib/types/row-line-clamp.d.ts +20 -0
- package/lib/types/row-tooltip.d.ts +8 -0
- package/lib/types/row-tooltip.spec.d.ts +1 -0
- package/lib/types/shared-tree-builder.d.ts +49 -19
- package/lib/types/shared-tree-mount.spec.d.ts +1 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/lib/types/widget-size.spec.d.ts +1 -0
- package/package.json +8 -8
- package/scss/_action_row.scss +39 -15
- package/scss/_button_row.scss +5 -0
- package/scss/_expander_row.scss +36 -3
- package/scss/_headerbar.scss +2 -1
- package/scss/_preferences_row.scss +9 -0
- package/scss/_row.scss +16 -0
- package/scss/_status_page.scss +1 -1
- package/scss/_toggle_group.scss +28 -0
- package/scss/_widget.scss +14 -3
- package/scss/_window.scss +13 -1
- package/src/adw-action-rows.spec.ts +255 -0
- package/src/adw-header-bar.spec.ts +18 -0
- package/src/adw-status-page.spec.ts +124 -0
- package/src/attributes.ts +52 -0
- package/src/capabilities.mjs +3 -0
- package/src/construct-vectors.spec.ts +1 -1
- package/src/elements/adw-action-row.ts +31 -2
- package/src/elements/adw-alert-dialog.ts +4 -1
- package/src/elements/adw-button-row.ts +7 -1
- package/src/elements/adw-combo-row.ts +26 -1
- package/src/elements/adw-entry-row.ts +22 -12
- package/src/elements/adw-expander-row.ts +34 -1
- package/src/elements/adw-header-bar.ts +18 -4
- package/src/elements/adw-overlay-split-view.ts +29 -6
- package/src/elements/adw-preferences-row.ts +5 -2
- package/src/elements/adw-sidebar.ts +33 -5
- package/src/elements/adw-spin-row.ts +37 -2
- package/src/elements/adw-status-page.ts +10 -2
- package/src/elements/adw-switch-row.ts +25 -1
- package/src/elements/adw-toggle-group.ts +119 -10
- package/src/elements/adw-window.ts +9 -4
- package/src/elements/gtk-scrolled-window.ts +18 -7
- package/src/elements/gtk-window.ts +9 -3
- package/src/gtk-window.spec.ts +12 -0
- package/src/index.ts +1 -1
- package/src/keyboard-operable.spec.ts +47 -11
- package/src/row-line-clamp.ts +74 -0
- package/src/row-tooltip.spec.ts +313 -0
- package/src/row-tooltip.ts +52 -0
- package/src/shared-tree-builder.ts +88 -35
- package/src/shared-tree-mount.spec.ts +130 -0
- package/src/shared-trees.spec.ts +15 -0
- package/src/styles.generated.ts +1 -1
- package/src/test.browser.mts +8 -0
- package/src/widget-size.spec.ts +117 -0
|
@@ -36,13 +36,21 @@
|
|
|
36
36
|
|
|
37
37
|
import { createBreakpointDriver, parseBreakpointCondition, type BreakpointSize } from '@gjsify/adwaita-core';
|
|
38
38
|
import { assertTreeConstructs, type SharedTreeNode } from '@gjsify/adwaita-core/conformance';
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
GTK_WIDGET_EXPAND,
|
|
41
|
+
attributeOf,
|
|
42
|
+
hostTagOf,
|
|
43
|
+
isValueBasedBooleanAttr,
|
|
44
|
+
propertyOf,
|
|
45
|
+
widgetLengthStyle,
|
|
46
|
+
} from '@gjsify/adwaita-core/tags';
|
|
40
47
|
|
|
41
48
|
import { observeAdaptiveSize } from './breakpoints.js';
|
|
42
49
|
import { capabilities } from './capabilities.mjs';
|
|
43
50
|
import { dispatchedSignalsOf } from './signals.js';
|
|
44
51
|
import { slottedChildrenOf } from './slotted-children.js';
|
|
45
52
|
import { templateTagFor } from './template-classes.js';
|
|
53
|
+
import { writeBooleanAttribute } from './attributes.js';
|
|
46
54
|
|
|
47
55
|
/** One authored placement, kept so {@link mountSharedTree} can hold the renderer to it. */
|
|
48
56
|
interface PlacedChild {
|
|
@@ -104,21 +112,22 @@ function isWritable(el: object, member: string): boolean {
|
|
|
104
112
|
* A `SharedTreeNode`, realised as a DETACHED element tree: a tag, its authored properties as
|
|
105
113
|
* attributes, its style classes as classes, its extensions (ADR 0072) as the markup the element
|
|
106
114
|
* reads, its placement as `slot=`, its children, in that order — recursive and total,
|
|
107
|
-
* no tag list, no per-block case.
|
|
108
|
-
* (`toggleAttribute`), which is what every element in the corpus reads
|
|
109
|
-
* (`hasAttribute('revealed')`, `hasAttribute('expanded')`); spelling `"true"` would set a
|
|
110
|
-
* present attribute for `false` as well.
|
|
115
|
+
* no tag list, no per-block case.
|
|
111
116
|
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
117
|
+
* BOOLEAN ATTRIBUTES are written per (widget, property), not per attribute name. The same
|
|
118
|
+
* attribute name (e.g. `revealed`) can be VALUE-BASED for one widget (`gtk-action-bar`,
|
|
119
|
+
* default TRUE) and PRESENCE-BASED for another (`adw-banner`, default FALSE). The
|
|
120
|
+
* per-widget map in `@gjsify/adwaita-core/tags` ({@link isValueBasedBooleanAttr}) records this.
|
|
121
|
+
*
|
|
122
|
+
* - VALUE-BASED: `attr="false"` means false, absent means true (the GTK default).
|
|
123
|
+
* The builder writes `attr="true"|"false"` directly via {@link writeBooleanAttribute}.
|
|
124
|
+
* - PRESENCE-BASED (style classes like `flat`, `round`, `compact`): `toggleAttribute`
|
|
125
|
+
* writes the bare attribute for `true`, removes it for `false`.
|
|
126
|
+
*
|
|
127
|
+
* EXCEPT an authored `false` on a NON-value-based, WRITABLE property: the property is set
|
|
128
|
+
* directly (bypassing the attribute) because the element's setter will reflect it correctly.
|
|
129
|
+
* A getter-only property of the same name (e.g. `AdwSplitButton.active`) falls back to the
|
|
130
|
+
* presence rule since assignment would throw.
|
|
122
131
|
*
|
|
123
132
|
* THE SLOT IS WRITTEN AS THE ATTRIBUTE THIS RENDERER ALREADY ROUTES ON, not translated:
|
|
124
133
|
* `src/slotted-children.ts` reads `slot=` off every light-DOM child and keeps the routing
|
|
@@ -258,16 +267,47 @@ function bindProperty(bind: PendingBind, record: BuildRecord): void {
|
|
|
258
267
|
from.addEventListener(event, follow);
|
|
259
268
|
}
|
|
260
269
|
|
|
261
|
-
/**
|
|
262
|
-
|
|
270
|
+
/**
|
|
271
|
+
* One authored property, written as the element reads it; a breakpoint setter takes the same door.
|
|
272
|
+
*
|
|
273
|
+
* BOOLEAN ATTRIBUTES are written per (widget, property), not per attribute name. The same
|
|
274
|
+
* attribute name (e.g. `revealed`) can be VALUE-BASED for one widget (`gtk-action-bar`,
|
|
275
|
+
* default TRUE) and PRESENCE-BASED for another (`adw-banner`, default FALSE). The
|
|
276
|
+
* per-widget map in `@gjsify/adwaita-core/tags` ({@link isValueBasedBooleanAttr}) records this.
|
|
277
|
+
*
|
|
278
|
+
* - VALUE-BASED: `attr="false"` means false, absent means true (the GTK default).
|
|
279
|
+
* The builder writes `attr="true"|"false"` directly via {@link writeBooleanAttribute}.
|
|
280
|
+
* - PRESENCE-BASED (style classes like `flat`, `round`, `compact`): `toggleAttribute`
|
|
281
|
+
* writes the bare attribute for `true`, removes it for `false`.
|
|
282
|
+
*
|
|
283
|
+
* EXCEPT an authored `false` on a NON-value-based, WRITABLE property: the property is set
|
|
284
|
+
* directly (bypassing the attribute) because the element's setter will reflect it correctly.
|
|
285
|
+
* A getter-only property of the same name (e.g. `AdwSplitButton.active`) falls back to the
|
|
286
|
+
* presence rule since assignment would throw. `hexpand` / `vexpand` keep an authored `false`
|
|
287
|
+
* as the attribute `"false"` and a later `true` rewrites it — `GTK_WIDGET_EXPAND` says why.
|
|
288
|
+
*/
|
|
289
|
+
export function writeProp(el: HTMLElement, prop: string, value: string | number | boolean): void {
|
|
290
|
+
const attr = attributeOf(prop);
|
|
263
291
|
const member = propertyOf(prop);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
else el.
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
292
|
+
const expand = GTK_WIDGET_EXPAND.find((name) => name === attr);
|
|
293
|
+
if (expand !== undefined && typeof value === 'boolean') el.setAttribute(expand, value ? '' : 'false');
|
|
294
|
+
else if (typeof value === 'boolean' && isValueBasedBooleanAttr(el.localName, attr)) {
|
|
295
|
+
writeBooleanAttribute(el, attr, value);
|
|
296
|
+
} else if (value === false && isWritable(el, member)) {
|
|
297
|
+
// Non-value-based, writable property: set the property directly (bypasses attribute).
|
|
298
|
+
(el as unknown as Record<string, unknown>)[member] = false;
|
|
299
|
+
} else if (typeof value === 'boolean') {
|
|
300
|
+
// Presence-based (style classes) or true for non-value-based: use toggleAttribute.
|
|
301
|
+
el.toggleAttribute(attr, value);
|
|
302
|
+
} else {
|
|
303
|
+
el.setAttribute(attr, String(value));
|
|
304
|
+
}
|
|
305
|
+
// A margin or a size request is also inline style (`GTK_WIDGET_MARGIN_CSS` says why); the
|
|
306
|
+
// attribute stays, since it is what the tree authored and what a reader of the DOM looks for.
|
|
307
|
+
const length = widgetLengthStyle(attr, value);
|
|
308
|
+
if (length === undefined) return;
|
|
309
|
+
if (length[1] === null) el.style.removeProperty(length[0]);
|
|
310
|
+
else el.style.setProperty(length[0], length[1]);
|
|
271
311
|
}
|
|
272
312
|
|
|
273
313
|
function buildNode(node: SharedTreeNode, record: BuildRecord): HTMLElement {
|
|
@@ -452,24 +492,37 @@ function refuseUnknownSlots(placed: readonly PlacedChild[]): void {
|
|
|
452
492
|
export interface MountedSharedTree {
|
|
453
493
|
/** The authored root — connected, so every custom element under it has upgraded and run. */
|
|
454
494
|
root: HTMLElement;
|
|
455
|
-
/** Disconnects and discards the mount point. */
|
|
495
|
+
/** Disconnects the tree, and discards the mount point when this call made one. */
|
|
456
496
|
unmount: () => void;
|
|
457
497
|
}
|
|
458
498
|
|
|
499
|
+
/** What {@link mountSharedTree} takes beside the tree. */
|
|
500
|
+
export interface MountOptions {
|
|
501
|
+
/** The object a handler NAME is looked up on (ADR 0093 § 3). */
|
|
502
|
+
scope?: Readonly<Record<string, unknown>>;
|
|
503
|
+
/** The size source a `breakpoints` node is driven from. */
|
|
504
|
+
observeSize?: SizeSource;
|
|
505
|
+
/**
|
|
506
|
+
* Where the root is appended — a showcase's own container, which then lays the window out.
|
|
507
|
+
* Without it the tree gets a fresh host `<div>` in `document.body`, discarded on `unmount`.
|
|
508
|
+
*/
|
|
509
|
+
into?: HTMLElement;
|
|
510
|
+
}
|
|
511
|
+
|
|
459
512
|
/**
|
|
460
513
|
* {@link buildSharedTree}, attached under a fresh host `<div>` in `document.body` so the tree
|
|
461
514
|
* — and every custom element in it — is REAL rather than merely constructed. This is the
|
|
462
515
|
* instantiation half a caller reading the corpus's elements normally wants; a bare
|
|
463
516
|
* `buildSharedTree` is for a caller that already has somewhere of its own to attach it.
|
|
464
517
|
*/
|
|
465
|
-
export function mountSharedTree(
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
): MountedSharedTree {
|
|
469
|
-
const host = document.createElement('div');
|
|
518
|
+
export function mountSharedTree(node: SharedTreeNode, options: MountOptions = {}): MountedSharedTree {
|
|
519
|
+
const { into } = options;
|
|
520
|
+
const host = into ?? document.createElement('div');
|
|
470
521
|
const record = newRecord(options.scope, options.observeSize);
|
|
471
|
-
|
|
472
|
-
|
|
522
|
+
const root = buildSharedTree(node, record);
|
|
523
|
+
host.append(root);
|
|
524
|
+
if (into === undefined) document.body.append(host);
|
|
525
|
+
const discard = () => (into === undefined ? host : root).remove();
|
|
473
526
|
// After the append, because that is what upgrades the elements and runs the binds the
|
|
474
527
|
// refusal reads; before the return, because a caller handed a tree back has no way left
|
|
475
528
|
// to tell a placement that was honoured from one that was dropped.
|
|
@@ -478,14 +531,14 @@ export function mountSharedTree(
|
|
|
478
531
|
refuseUnheldExtensions(record.extended);
|
|
479
532
|
} catch (error) {
|
|
480
533
|
for (const dispose of record.disposers) dispose();
|
|
481
|
-
|
|
534
|
+
discard();
|
|
482
535
|
throw error;
|
|
483
536
|
}
|
|
484
537
|
return {
|
|
485
|
-
root
|
|
538
|
+
root,
|
|
486
539
|
unmount: () => {
|
|
487
540
|
for (const dispose of record.disposers) dispose();
|
|
488
|
-
|
|
541
|
+
discard();
|
|
489
542
|
},
|
|
490
543
|
};
|
|
491
544
|
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
// A showcase window mounted from its tree (ADR 0093): the shape every showcase `.blp` writes — a
|
|
2
|
+
// toggle button and a split view that bind to each other, two breakpoints that collapse the split
|
|
3
|
+
// view, and a window sized by `default-width`. What is held here is the exact cycle the showcases
|
|
4
|
+
// rely on, and `into`, the container a showcase mounts its window in.
|
|
5
|
+
import { describe, expect, it } from '@gjsify/unit';
|
|
6
|
+
import type { SharedTreeNode } from '@gjsify/adwaita-core/conformance';
|
|
7
|
+
|
|
8
|
+
import { mountSharedTree } from './shared-tree-builder.js';
|
|
9
|
+
|
|
10
|
+
/** Wait for a ResizeObserver delivery (it runs after layout, before paint). */
|
|
11
|
+
function settle(): Promise<void> {
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
requestAnimationFrame(() => requestAnimationFrame(() => resolve()));
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** The showcase window, sized by `default-width` and adapting at 800sp. */
|
|
18
|
+
const tree = (width: number): SharedTreeNode => ({
|
|
19
|
+
tag: 'AdwApplicationWindow',
|
|
20
|
+
props: { 'default-width': width, 'default-height': 400 },
|
|
21
|
+
breakpoints: [
|
|
22
|
+
{
|
|
23
|
+
condition: 'max-width: 799sp',
|
|
24
|
+
setters: [
|
|
25
|
+
{ object: 'splitView', property: 'collapsed', value: true },
|
|
26
|
+
{ object: 'splitView', property: 'show-sidebar', value: false },
|
|
27
|
+
],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
condition: 'min-width: 800sp',
|
|
31
|
+
setters: [
|
|
32
|
+
{ object: 'splitView', property: 'collapsed', value: false },
|
|
33
|
+
{ object: 'splitView', property: 'show-sidebar', value: true },
|
|
34
|
+
],
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
children: [
|
|
38
|
+
{
|
|
39
|
+
tag: 'GtkBox',
|
|
40
|
+
slot: 'content',
|
|
41
|
+
children: [
|
|
42
|
+
{
|
|
43
|
+
tag: 'GtkToggleButton',
|
|
44
|
+
id: 'sidebarToggleButton',
|
|
45
|
+
bindings: { active: { source: 'splitView', property: 'show-sidebar' } },
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
tag: 'AdwOverlaySplitView',
|
|
49
|
+
id: 'splitView',
|
|
50
|
+
bindings: { 'show-sidebar': { source: 'sidebarToggleButton', property: 'active' } },
|
|
51
|
+
children: [
|
|
52
|
+
{ tag: 'GtkBox', slot: 'sidebar' },
|
|
53
|
+
{ tag: 'GtkBox', slot: 'content' },
|
|
54
|
+
],
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
type SplitView = HTMLElement & { showSidebar: boolean; collapsed: boolean };
|
|
62
|
+
type Toggle = HTMLElement & { active: boolean };
|
|
63
|
+
|
|
64
|
+
export const AdwSharedTreeMountTest = async () => {
|
|
65
|
+
await describe('adwaita-web: a showcase window mounted from its tree', async () => {
|
|
66
|
+
await it('<adw-overlay-split-view> raises notify::show-sidebar once per real change', () => {
|
|
67
|
+
const view = document.createElement('adw-overlay-split-view') as SplitView;
|
|
68
|
+
document.body.append(view);
|
|
69
|
+
const seen: boolean[] = [];
|
|
70
|
+
view.addEventListener('notify::show-sidebar', (event) => {
|
|
71
|
+
seen.push((event as CustomEvent<{ showSidebar: boolean }>).detail.showSidebar);
|
|
72
|
+
});
|
|
73
|
+
view.showSidebar = true;
|
|
74
|
+
view.showSidebar = false;
|
|
75
|
+
view.showSidebar = false;
|
|
76
|
+
view.showSidebar = true;
|
|
77
|
+
view.remove();
|
|
78
|
+
expect(seen).toStrictEqual([false, true]);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
await it('a wide window keeps the sidebar and the toggle follows the split view', async () => {
|
|
82
|
+
const { root, unmount } = mountSharedTree(tree(1100));
|
|
83
|
+
await settle();
|
|
84
|
+
const split = root.querySelector('#splitView') as SplitView;
|
|
85
|
+
expect(root.getBoundingClientRect().width).toBe(1100);
|
|
86
|
+
expect(split.collapsed).toBe(false);
|
|
87
|
+
expect(split.showSidebar).toBe(true);
|
|
88
|
+
expect((root.querySelector('#sidebarToggleButton') as Toggle).active).toBe(true);
|
|
89
|
+
unmount();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await it('a narrow window collapses the split view and the toggle follows it', async () => {
|
|
93
|
+
const { root, unmount } = mountSharedTree(tree(500));
|
|
94
|
+
await settle();
|
|
95
|
+
const split = root.querySelector('#splitView') as SplitView;
|
|
96
|
+
expect(split.collapsed).toBe(true);
|
|
97
|
+
expect(split.showSidebar).toBe(false);
|
|
98
|
+
expect((root.querySelector('#sidebarToggleButton') as Toggle).active).toBe(false);
|
|
99
|
+
unmount();
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
await it('the toggle drives the split view back, and the cycle settles', async () => {
|
|
103
|
+
const { root, unmount } = mountSharedTree(tree(1100));
|
|
104
|
+
await settle();
|
|
105
|
+
const toggle = root.querySelector('#sidebarToggleButton') as Toggle;
|
|
106
|
+
const split = root.querySelector('#splitView') as SplitView;
|
|
107
|
+
toggle.active = false;
|
|
108
|
+
expect(split.showSidebar).toBe(false);
|
|
109
|
+
split.showSidebar = true;
|
|
110
|
+
expect(toggle.active).toBe(true);
|
|
111
|
+
toggle.active = false;
|
|
112
|
+
toggle.active = true;
|
|
113
|
+
expect(split.showSidebar).toBe(true);
|
|
114
|
+
expect(split.getAttribute('show-sidebar')).toBe('');
|
|
115
|
+
unmount();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
await it('a tree mounted into a container lives in it, and unmount takes only the tree', () => {
|
|
119
|
+
const container = document.createElement('div');
|
|
120
|
+
document.body.append(container);
|
|
121
|
+
const { root, unmount } = mountSharedTree(tree(1100), { into: container });
|
|
122
|
+
expect(root.parentElement).toBe(container);
|
|
123
|
+
expect(container.querySelector('adw-application-window')).toBe(root);
|
|
124
|
+
unmount();
|
|
125
|
+
expect(container.childElementCount).toBe(0);
|
|
126
|
+
expect(container.isConnected).toBe(true);
|
|
127
|
+
container.remove();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
};
|
package/src/shared-trees.spec.ts
CHANGED
|
@@ -295,6 +295,21 @@ export const AdwSharedTreesTest = async () => {
|
|
|
295
295
|
});
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
await it('Adw.Banner with revealed: false stays closed — attribute must be absent, not "false"', () => {
|
|
299
|
+
const tree = blueprintTree(`Adw.Banner {
|
|
300
|
+
title: "Test";
|
|
301
|
+
revealed: false;
|
|
302
|
+
}`);
|
|
303
|
+
mounted(tree, (root) => {
|
|
304
|
+
const banner = root as HTMLElement;
|
|
305
|
+
// The banner reads 'revealed' by PRESENCE (hasAttribute), so the attribute must be absent.
|
|
306
|
+
// Currently the builder writes revealed="false", which makes hasAttribute return true.
|
|
307
|
+
expect(banner.hasAttribute('revealed')).toBe(false);
|
|
308
|
+
expect(banner.classList.contains('revealed')).toBe(false);
|
|
309
|
+
expect(getComputedStyle(banner).display).toBe('none');
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
|
|
298
313
|
await it('an authored false on a getter-only property keeps the presence rule instead of throwing', () => {
|
|
299
314
|
// `<adw-split-button>` exposes `active` as a read-only getter; assigning through it
|
|
300
315
|
// threw a bare TypeError out of the builder before the writability check.
|