@gjsify/adwaita-web 0.36.0 → 0.37.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/dist/adwaita-web.css +433 -33
- package/dist/adwaita-web.css.map +1 -1
- package/lib/types/adw-shortcut-label.spec.d.ts +2 -0
- package/lib/types/breakpoints.d.ts +10 -12
- package/lib/types/elements/adw-about-dialog.d.ts +19 -36
- package/lib/types/elements/adw-action-row.d.ts +9 -12
- package/lib/types/elements/adw-alert-dialog.d.ts +0 -12
- package/lib/types/elements/adw-avatar.d.ts +1 -0
- package/lib/types/elements/adw-bottom-sheet.d.ts +7 -12
- package/lib/types/elements/adw-button-content.d.ts +3 -4
- package/lib/types/elements/adw-button-row.d.ts +1 -1
- package/lib/types/elements/adw-carousel.d.ts +28 -49
- package/lib/types/elements/adw-checks.d.ts +2 -8
- package/lib/types/elements/adw-combo-row.d.ts +6 -6
- package/lib/types/elements/adw-data-grid.d.ts +5 -10
- package/lib/types/elements/adw-drop-down.d.ts +3 -11
- package/lib/types/elements/adw-entry-row.d.ts +20 -23
- package/lib/types/elements/adw-icon.d.ts +2 -4
- package/lib/types/elements/adw-inline-view-switcher.d.ts +18 -31
- package/lib/types/elements/adw-navigation-split-view.d.ts +7 -12
- package/lib/types/elements/adw-navigation-view.d.ts +2 -10
- package/lib/types/elements/adw-overlay-split-view.d.ts +28 -52
- package/lib/types/elements/adw-password-entry-row.d.ts +4 -5
- package/lib/types/elements/adw-popover.d.ts +2 -7
- package/lib/types/elements/adw-preferences-dialog.d.ts +21 -34
- package/lib/types/elements/adw-preferences-group.d.ts +12 -14
- package/lib/types/elements/adw-progress-bar.d.ts +3 -4
- package/lib/types/elements/adw-shortcut-label.d.ts +20 -0
- package/lib/types/elements/adw-sidebar.d.ts +5 -10
- package/lib/types/elements/adw-spinner.d.ts +3 -5
- package/lib/types/elements/adw-split-button.d.ts +6 -16
- package/lib/types/elements/adw-switch-row.d.ts +3 -3
- package/lib/types/elements/adw-tab-view.d.ts +16 -26
- package/lib/types/elements/adw-toast-overlay.d.ts +8 -13
- package/lib/types/elements/adw-view-stack.d.ts +7 -17
- package/lib/types/elements/adw-view-switcher-bar.d.ts +4 -8
- package/lib/types/elements/adw-view-switcher.d.ts +18 -38
- package/lib/types/elements/adw-wrap-box.d.ts +7 -17
- package/lib/types/elements/view-switcher-dom.d.ts +10 -13
- package/lib/types/index.d.ts +1 -0
- package/lib/types/source-view/hex.d.ts +7 -13
- package/lib/types/style-classes.spec.d.ts +11 -0
- package/lib/types/styles.generated.d.ts +1 -1
- package/package.json +6 -6
- package/scss/_about_dialog.scss +3 -2
- package/scss/_action_row.scss +33 -0
- package/scss/_avatar.scss +12 -0
- package/scss/_button.scss +44 -22
- package/scss/_card.scss +11 -8
- package/scss/_checks.scss +2 -4
- package/scss/_dialog.scss +3 -2
- package/scss/_headerbar.scss +23 -0
- package/scss/_popover.scss +8 -4
- package/scss/_preferences.scss +7 -7
- package/scss/_progress_bar.scss +9 -6
- package/scss/_reset.scss +1 -1
- package/scss/_shortcut_label.scss +80 -0
- package/scss/_sidebar.scss +57 -0
- package/scss/_status_page.scss +20 -0
- package/scss/_style_classes.scss +407 -0
- package/scss/_switch.scss +4 -3
- package/scss/_variables.scss +25 -0
- package/scss/adwaita-skin.scss +12 -0
- package/src/adw-about-dialog.spec.ts +34 -49
- package/src/adw-action-rows.spec.ts +25 -33
- package/src/adw-avatar.spec.ts +51 -20
- package/src/adw-banner.spec.ts +5 -8
- package/src/adw-button-content.spec.ts +9 -12
- package/src/adw-button-row.spec.ts +2 -4
- package/src/adw-carousel.spec.ts +40 -53
- package/src/adw-checks.spec.ts +36 -47
- package/src/adw-drop-down.spec.ts +25 -32
- package/src/adw-header-bar.spec.ts +5 -7
- package/src/adw-icon.spec.ts +23 -32
- package/src/adw-progress-bar.spec.ts +10 -12
- package/src/adw-row-state.spec.ts +9 -10
- package/src/adw-shortcut-label.spec.ts +112 -0
- package/src/adw-sidebar.spec.ts +17 -24
- package/src/adw-switch.spec.ts +11 -17
- package/src/adw-tab-view.spec.ts +7 -10
- package/src/adw-toast-overlay.spec.ts +7 -11
- package/src/adw-view-switcher-bar.spec.ts +10 -14
- package/src/adw-wrap-box.spec.ts +25 -36
- package/src/bottom-sheet.spec.ts +30 -36
- package/src/breakpoints.ts +15 -18
- package/src/chrome.spec.ts +13 -21
- package/src/elements/adw-about-dialog.ts +71 -114
- package/src/elements/adw-action-row.ts +22 -37
- package/src/elements/adw-alert-dialog.ts +25 -50
- package/src/elements/adw-avatar.ts +40 -15
- package/src/elements/adw-banner.ts +14 -18
- package/src/elements/adw-bottom-sheet.ts +31 -52
- package/src/elements/adw-button-content.ts +30 -37
- package/src/elements/adw-button-row.ts +14 -24
- package/src/elements/adw-carousel.ts +87 -148
- package/src/elements/adw-checks.ts +43 -56
- package/src/elements/adw-clamp.ts +8 -9
- package/src/elements/adw-combo-row.ts +17 -25
- package/src/elements/adw-data-grid.ts +26 -38
- package/src/elements/adw-dialog.ts +25 -56
- package/src/elements/adw-drop-down.ts +31 -51
- package/src/elements/adw-entry-row.ts +39 -46
- package/src/elements/adw-expander-row.ts +30 -53
- package/src/elements/adw-icon.ts +30 -53
- package/src/elements/adw-inline-view-switcher.ts +38 -67
- package/src/elements/adw-menu-button.ts +28 -58
- package/src/elements/adw-navigation-split-view.ts +35 -47
- package/src/elements/adw-navigation-view.ts +12 -28
- package/src/elements/adw-overlay-split-view.ts +100 -175
- package/src/elements/adw-password-entry-row.ts +18 -28
- package/src/elements/adw-popover.ts +28 -44
- package/src/elements/adw-preferences-dialog.ts +41 -66
- package/src/elements/adw-preferences-group.ts +33 -46
- package/src/elements/adw-progress-bar.ts +27 -47
- package/src/elements/adw-shortcut-label.ts +138 -0
- package/src/elements/adw-sidebar.ts +31 -39
- package/src/elements/adw-spin-row.ts +7 -14
- package/src/elements/adw-spinner.ts +31 -43
- package/src/elements/adw-split-button.ts +45 -75
- package/src/elements/adw-switch-row.ts +29 -46
- package/src/elements/adw-switch.ts +28 -47
- package/src/elements/adw-tab-view.ts +62 -103
- package/src/elements/adw-toast-overlay.ts +28 -35
- package/src/elements/adw-toggle-group.ts +9 -16
- package/src/elements/adw-view-stack.ts +32 -51
- package/src/elements/adw-view-switcher-bar.ts +26 -50
- package/src/elements/adw-view-switcher.ts +60 -110
- package/src/elements/adw-window-title.ts +4 -4
- package/src/elements/adw-wrap-box.ts +33 -59
- package/src/elements/view-switcher-dom.ts +15 -22
- package/src/entry-rows.spec.ts +18 -24
- package/src/index.ts +13 -15
- package/src/navigation-view.spec.ts +7 -10
- package/src/preferences-search.ts +11 -15
- package/src/preferences.spec.ts +13 -15
- package/src/source-view/adw-source-view.ts +6 -19
- package/src/source-view/hex.ts +7 -13
- package/src/split-button.spec.ts +32 -44
- package/src/split-views.spec.ts +83 -149
- package/src/style-classes.spec.ts +181 -0
- package/src/style-isolation.spec.ts +3 -6
- package/src/styles.generated.ts +1 -1
- package/src/tab-view.spec.ts +26 -35
- package/src/test.browser.mts +4 -0
- package/src/view-stack.spec.ts +23 -34
- package/src/view-switcher.spec.ts +56 -84
package/src/split-views.spec.ts
CHANGED
|
@@ -1,15 +1,8 @@
|
|
|
1
|
-
// The two split views against the shared conformance vectors
|
|
1
|
+
// The two split views against the shared conformance vectors: the attributes reach
|
|
2
|
+
// `OverlaySplitViewState`, and what the state derives reaches the DOM.
|
|
2
3
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// that the attributes reach the state, and that what the state derives actually
|
|
6
|
-
// reaches the DOM.
|
|
7
|
-
//
|
|
8
|
-
// The rule it pins down first is the one the element got wrong for its whole
|
|
9
|
-
// life: `Adw.OverlaySplitView:show-sidebar` defaults to TRUE
|
|
10
|
-
// (adw-overlay-split-view.c:974-976), and an HTML boolean attribute is
|
|
11
|
-
// presence-only, which can only express a FALSE default. Bare markup showed no
|
|
12
|
-
// sidebar where GTK shows one.
|
|
4
|
+
// `Adw.OverlaySplitView:show-sidebar` defaults to TRUE, but an HTML boolean attribute
|
|
5
|
+
// is presence-only and can only express a FALSE default — hence `show-sidebar="false"`.
|
|
13
6
|
import { describe, expect, it } from '@gjsify/unit';
|
|
14
7
|
|
|
15
8
|
import type { OverlaySplitViewState } from '@gjsify/adwaita-core';
|
|
@@ -37,7 +30,6 @@ function settle(): Promise<void> {
|
|
|
37
30
|
});
|
|
38
31
|
}
|
|
39
32
|
|
|
40
|
-
/** Mount a split view from markup and return it. */
|
|
41
33
|
function mount(attrs = ''): { view: AdwOverlaySplitView; host: HTMLElement } {
|
|
42
34
|
const host = document.createElement('div');
|
|
43
35
|
document.body.appendChild(host);
|
|
@@ -58,18 +50,13 @@ interface DomSnapshot {
|
|
|
58
50
|
}
|
|
59
51
|
|
|
60
52
|
/**
|
|
61
|
-
* Read the whole derived state back OUT of the DOM
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `observedAttributes` unnoticed: both of those still agreed, because the vectors
|
|
65
|
-
* only ever set the attribute in markup, where `connectedCallback` reads it
|
|
66
|
-
* directly. Every field the element publishes is checked here instead.
|
|
53
|
+
* Read the whole derived state back OUT of the DOM — EVERY field the element publishes,
|
|
54
|
+
* because `showSidebar`/`collapsed` agreed while `pin-sidebar` had fallen out of
|
|
55
|
+
* `observedAttributes`.
|
|
67
56
|
*
|
|
68
|
-
* `showProgress`
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* derived from it (`collapsed && showProgress > 0`), so a progress bug still
|
|
72
|
-
* shows up here.
|
|
57
|
+
* `showProgress` has no DOM surface (the reveal is painted from the
|
|
58
|
+
* `collapsed`/`show-sidebar` classes, not a numeric progress), but `shieldVisible` is
|
|
59
|
+
* derived from it, so a progress bug still shows here.
|
|
73
60
|
*/
|
|
74
61
|
function domSnapshot(view: AdwOverlaySplitView): DomSnapshot {
|
|
75
62
|
const backdrop = view.querySelector('.adw-osv-backdrop') as HTMLElement;
|
|
@@ -136,8 +123,7 @@ export const AdwSplitViewsTest = async () => {
|
|
|
136
123
|
await describe('adw-overlay-split-view collapse (shared conformance vectors)', async () => {
|
|
137
124
|
for (const vector of OVERLAY_COLLAPSE_VECTORS) {
|
|
138
125
|
const { initial, before, setCollapsed, after, rule } = vector;
|
|
139
|
-
// Only the
|
|
140
|
-
// starting `collapsed`/`showSidebar` all have attributes.
|
|
126
|
+
// Only the vector fields a DOM element can express as attributes.
|
|
141
127
|
const attrs = [
|
|
142
128
|
initial.collapsed ? 'collapsed' : '',
|
|
143
129
|
initial.showSidebar === false ? 'show-sidebar="false"' : '',
|
|
@@ -161,11 +147,9 @@ export const AdwSplitViewsTest = async () => {
|
|
|
161
147
|
}
|
|
162
148
|
|
|
163
149
|
await it('honours pin-sidebar set AFTER construction, not only in markup', () => {
|
|
164
|
-
// The
|
|
165
|
-
// `
|
|
166
|
-
//
|
|
167
|
-
// `_readAttribute` branch was dead, so pinning a live element silently
|
|
168
|
-
// did nothing and the next collapse still hid the sidebar.
|
|
150
|
+
// The vector loop only puts `pin-sidebar` in markup, which
|
|
151
|
+
// `connectedCallback` reads directly: with the attribute missing from
|
|
152
|
+
// `observedAttributes`, pinning a live element was a silent no-op.
|
|
169
153
|
const { view, host } = mount();
|
|
170
154
|
view.setAttribute('pin-sidebar', '');
|
|
171
155
|
|
|
@@ -200,16 +184,14 @@ export const AdwSplitViewsTest = async () => {
|
|
|
200
184
|
await it('hides the shield unless collapsed AND revealed', () => {
|
|
201
185
|
const { view, host } = mount();
|
|
202
186
|
const backdrop = view.querySelector('.adw-osv-backdrop') as HTMLElement;
|
|
203
|
-
// Expanded: no shield, whatever the sidebar is doing.
|
|
204
187
|
expect(backdrop.hidden).toBe(true);
|
|
205
188
|
host.remove();
|
|
206
189
|
});
|
|
207
190
|
|
|
208
191
|
await it('marks the off-screen pane aria-hidden once collapsing hides it', () => {
|
|
209
|
-
// Collapsing is a TRANSITION
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
// the same as building the GTK widget with both properties set.
|
|
192
|
+
// Collapsing is a TRANSITION and auto-hides an unpinned sidebar, so content
|
|
193
|
+
// becomes the reachable pane; constructing collapsed does NOT, matching a GTK
|
|
194
|
+
// widget built with both properties set.
|
|
213
195
|
const { view, host } = mount();
|
|
214
196
|
expect((view.querySelector('.adw-osv-sidebar') as HTMLElement).getAttribute('aria-hidden')).toBe('false');
|
|
215
197
|
|
|
@@ -234,10 +216,9 @@ export const AdwSplitViewsTest = async () => {
|
|
|
234
216
|
});
|
|
235
217
|
await describe('adw-overlay-split-view sidebar width (Adw.OverlaySplitView properties)', async () => {
|
|
236
218
|
await it('defaults to the WIDGET values, not to an app preference', () => {
|
|
237
|
-
// 180 / 280 / 0.25
|
|
238
|
-
// 280 / 400 / 0.30
|
|
239
|
-
//
|
|
240
|
-
// GTK and browser sides showed different sidebars.
|
|
219
|
+
// 180 / 280 / 0.25 per `adw-overlay-split-view.c`. The showcase values
|
|
220
|
+
// (280 / 400 / 0.30, set explicitly in their .blp) had leaked in as the
|
|
221
|
+
// defaults, so storybook's GTK and browser sides diverged.
|
|
241
222
|
const { view, host } = mount();
|
|
242
223
|
expect(view.minSidebarWidth).toBe(180);
|
|
243
224
|
expect(view.maxSidebarWidth).toBe(280);
|
|
@@ -248,17 +229,16 @@ export const AdwSplitViewsTest = async () => {
|
|
|
248
229
|
await it('writes the normalised bounds, not the raw attributes', () => {
|
|
249
230
|
const { view, host } = mount('min-sidebar-width="300" max-sidebar-width="200"');
|
|
250
231
|
const sidebar = view.querySelector('.adw-osv-sidebar') as HTMLElement;
|
|
251
|
-
// libadwaita never lets max fall below min, so both land at 300
|
|
252
|
-
//
|
|
253
|
-
// over max-width), which would render a different widget.
|
|
232
|
+
// libadwaita never lets max fall below min, so both land at 300 — CSS alone
|
|
233
|
+
// resolves it the other way (min-width beats max-width).
|
|
254
234
|
expect(sidebar.style.minWidth).toBe('300px');
|
|
255
235
|
expect(sidebar.style.maxWidth).toBe('300px');
|
|
256
236
|
host.remove();
|
|
257
237
|
});
|
|
258
238
|
|
|
259
239
|
for (const vector of SIDEBAR_BOUNDS_VECTORS) {
|
|
260
|
-
// Only
|
|
261
|
-
//
|
|
240
|
+
// Only rows expressible in CSS pixels: the element has no dpi or length-unit
|
|
241
|
+
// attribute, so `sp`-at-another-dpi rows stay in core.
|
|
262
242
|
if (vector.spec.sidebarWidthUnit !== undefined || vector.spec.dpi !== undefined) continue;
|
|
263
243
|
if (vector.sidebarChildMin !== 0 || !vector.ceil) continue;
|
|
264
244
|
|
|
@@ -293,8 +273,6 @@ export const AdwSplitViewsTest = async () => {
|
|
|
293
273
|
};
|
|
294
274
|
|
|
295
275
|
await it('bounds the sidebar even with no attributes', () => {
|
|
296
|
-
// An absent attribute used to mean "no bound at all"; the widget
|
|
297
|
-
// always has 180 / 280.
|
|
298
276
|
const { view, host } = mountNav();
|
|
299
277
|
const sidebar = view.querySelector('.adw-nsv-sidebar') as HTMLElement;
|
|
300
278
|
expect(sidebar.style.minWidth).toBe('180px');
|
|
@@ -351,7 +329,7 @@ export const AdwSplitViewsTest = async () => {
|
|
|
351
329
|
offsets.add(sidebar.style.marginLeft || sidebar.style.left || '');
|
|
352
330
|
}
|
|
353
331
|
// Two end states would give at most two distinct offsets; the five
|
|
354
|
-
// OVERLAY_SWIPE_* tables need every value
|
|
332
|
+
// OVERLAY_SWIPE_* tables need every value between them to mean something.
|
|
355
333
|
expect(offsets.size).toBeGreaterThan(2);
|
|
356
334
|
host.remove();
|
|
357
335
|
});
|
|
@@ -372,10 +350,9 @@ export const AdwSplitViewsTest = async () => {
|
|
|
372
350
|
}
|
|
373
351
|
|
|
374
352
|
await it('grabs only where the swipe area is — the ADW_SWIPE_BORDER strip when closed', async () => {
|
|
375
|
-
//
|
|
376
|
-
//
|
|
377
|
-
//
|
|
378
|
-
// `resolveSwipeArea` can show.
|
|
353
|
+
// Closed, start edge, LTR: the grabbable strip is `ADW_SWIPE_BORDER` wide and
|
|
354
|
+
// nothing outside it starts a gesture — the second half is the DOM renderer's,
|
|
355
|
+
// which no unit test of `resolveSwipeArea` can show.
|
|
379
356
|
const closedAtStart = OVERLAY_SWIPE_AREA_VECTORS.find(
|
|
380
357
|
(vector) =>
|
|
381
358
|
vector.isDrag &&
|
|
@@ -411,12 +388,10 @@ export const AdwSplitViewsTest = async () => {
|
|
|
411
388
|
}),
|
|
412
389
|
);
|
|
413
390
|
|
|
414
|
-
// Just OUTSIDE the strip, so nothing begins.
|
|
415
391
|
down(strip + 4);
|
|
416
392
|
move(strip + 120);
|
|
417
393
|
expect(state.swipeActive).toBe(false);
|
|
418
394
|
|
|
419
|
-
// Just inside it: the gesture takes over and the progress follows.
|
|
420
395
|
down(Math.max(strip - 4, 0));
|
|
421
396
|
move(strip + 120);
|
|
422
397
|
expect(state.swipeActive).toBe(true);
|
|
@@ -429,7 +404,6 @@ export const AdwSplitViewsTest = async () => {
|
|
|
429
404
|
const { view, host } = mountSizedView(800, 'collapsed');
|
|
430
405
|
const state = stateOf(view);
|
|
431
406
|
await settle();
|
|
432
|
-
// Seed `show-sidebar` without animating, then take the gesture.
|
|
433
407
|
state.setShowSidebar(vector.showSidebar, { animate: false });
|
|
434
408
|
state.beginSwipe();
|
|
435
409
|
const plan = state.endSwipe(vector.to);
|
|
@@ -472,15 +446,12 @@ export const AdwSplitViewsTest = async () => {
|
|
|
472
446
|
});
|
|
473
447
|
|
|
474
448
|
await it('Escape closes a collapsed revealed sidebar', async () => {
|
|
475
|
-
// `escape_shortcut_cb
|
|
476
|
-
// from both ports, which made `OverlaySplitViewState.escape()` dead
|
|
477
|
-
// code.
|
|
449
|
+
// `escape_shortcut_cb`.
|
|
478
450
|
const { view, host } = mountSizedView(800, 'collapsed');
|
|
479
451
|
await settle();
|
|
480
|
-
// Parse-time attributes are CONSTRUCTION options, not sequential
|
|
481
|
-
//
|
|
482
|
-
//
|
|
483
|
-
// the setter path. So the sidebar is already revealed here.
|
|
452
|
+
// Parse-time attributes are CONSTRUCTION options, not sequential setters, so
|
|
453
|
+
// markup `collapsed` never fires the auto-hide (the setter path is
|
|
454
|
+
// OVERLAY_COLLAPSE_VECTORS) and the sidebar is revealed here.
|
|
484
455
|
expect(view.showSidebar).toBe(true);
|
|
485
456
|
|
|
486
457
|
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true });
|
|
@@ -498,20 +469,18 @@ export const AdwSplitViewsTest = async () => {
|
|
|
498
469
|
|
|
499
470
|
const event = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true, cancelable: true });
|
|
500
471
|
view.dispatchEvent(event);
|
|
501
|
-
//
|
|
502
|
-
//
|
|
472
|
+
// Escape never closes a DOCKED sidebar, however revealed: the gate is
|
|
473
|
+
// `!collapsed`, checked before the progress.
|
|
503
474
|
expect(event.defaultPrevented).toBe(false);
|
|
504
475
|
expect(view.showSidebar).toBe(true);
|
|
505
476
|
host.remove();
|
|
506
477
|
});
|
|
507
478
|
|
|
508
479
|
await it('is gated on the PROGRESS, so it still consumes mid-close', async () => {
|
|
509
|
-
//
|
|
510
|
-
//
|
|
511
|
-
//
|
|
512
|
-
//
|
|
513
|
-
// key propagate for ~250ms after the first press and close a dialog
|
|
514
|
-
// behind the view.
|
|
480
|
+
// `escape_shortcut_cb` gates on `show_progress`, not `show_sidebar`, so a
|
|
481
|
+
// second Escape mid-close is consumed too. Reading the gate as "is the
|
|
482
|
+
// sidebar shown" would let the key propagate for ~250ms after the first
|
|
483
|
+
// press and close a dialog behind.
|
|
515
484
|
const { view, host } = mountSizedView(800, 'collapsed');
|
|
516
485
|
await settle();
|
|
517
486
|
expect(view.showSidebar).toBe(true);
|
|
@@ -529,8 +498,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
529
498
|
});
|
|
530
499
|
|
|
531
500
|
await it('slides off the edge the sidebar is ON, in both positions', async () => {
|
|
532
|
-
// `marginRight: -offsetWidth` regardless of
|
|
533
|
-
//
|
|
501
|
+
// Regression: `marginRight: -offsetWidth` regardless of
|
|
502
|
+
// `sidebarPosition` slid an `end` sidebar the wrong way.
|
|
534
503
|
const start = mountSizedView(800, 'show-sidebar="false"');
|
|
535
504
|
await settle();
|
|
536
505
|
const startBar = start.view.querySelector('.adw-osv-sidebar') as HTMLElement;
|
|
@@ -571,9 +540,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
571
540
|
});
|
|
572
541
|
|
|
573
542
|
await it('keeps the content pane from being squeezed to nothing', async () => {
|
|
574
|
-
// `measure_uncollapsed`
|
|
575
|
-
//
|
|
576
|
-
// everything.
|
|
543
|
+
// `measure_uncollapsed` as CSS spells it: the pane claims its own min-content,
|
|
544
|
+
// where `min-width: 0` let the sidebar take everything.
|
|
577
545
|
const { view, host } = mountSizedView(800);
|
|
578
546
|
await settle();
|
|
579
547
|
const content = view.querySelector('.adw-osv-content') as HTMLElement;
|
|
@@ -582,11 +550,6 @@ export const AdwSplitViewsTest = async () => {
|
|
|
582
550
|
});
|
|
583
551
|
|
|
584
552
|
for (const vector of OVERLAY_SPLIT_VIEW_LAYOUT_VECTORS) {
|
|
585
|
-
// Its exact analogue NAVIGATION_SPLIT_VIEW_LAYOUT_VECTORS was driven
|
|
586
|
-
// from the NativeScript suite while this one was driven by nobody —
|
|
587
|
-
// two sibling tables in the same file, with no reason for the
|
|
588
|
-
// difference. The element writes the pane rect from
|
|
589
|
-
// `layoutOverlaySplitView` now, so the rects are readable.
|
|
590
553
|
const label = `${vector.collapsed ? 'collapsed' : 'docked'} ${vector.sidebarPosition}/${vector.direction} @${vector.showProgress}`;
|
|
591
554
|
await it(`${label} — ${vector.rule}`, async () => {
|
|
592
555
|
const attrs =
|
|
@@ -599,9 +562,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
599
562
|
const state = stateOf(view);
|
|
600
563
|
state.setShowProgress(vector.showProgress);
|
|
601
564
|
|
|
602
|
-
//
|
|
603
|
-
//
|
|
604
|
-
// real layout decides, so the shared half is what is compared.
|
|
565
|
+
// Shield and paint gate only: the pixel rects depend on the measured
|
|
566
|
+
// sidebar width, which a real layout decides.
|
|
605
567
|
const backdrop = view.querySelector('.adw-osv-backdrop') as HTMLElement;
|
|
606
568
|
expect(backdrop.hidden).toBe(!vector.layout.shieldVisible);
|
|
607
569
|
const sidebar = view.querySelector('.adw-osv-sidebar') as HTMLElement;
|
|
@@ -611,16 +573,14 @@ export const AdwSplitViewsTest = async () => {
|
|
|
611
573
|
}
|
|
612
574
|
|
|
613
575
|
await it('names the cancel rows the core owns, so this suite is not read as covering them', () => {
|
|
614
|
-
// `swipeCancelProgress`'s half-away-from-zero rounding
|
|
615
|
-
//
|
|
616
|
-
// OUTCOME, not the rule. The core suite drives every row.
|
|
576
|
+
// `swipeCancelProgress`'s half-away-from-zero rounding has no DOM surface: a
|
|
577
|
+
// pointercancel shows the rounded OUTCOME, not the rule.
|
|
617
578
|
expect(OVERLAY_SWIPE_CANCEL_VECTORS.length).toBeGreaterThan(0);
|
|
618
579
|
expect(OVERLAY_SWIPE_START_VECTORS.length).toBeGreaterThan(0);
|
|
619
580
|
});
|
|
620
581
|
});
|
|
621
582
|
|
|
622
583
|
await describe('<adw-navigation-split-view> stack, tags and navigation.* actions', async () => {
|
|
623
|
-
/** Mount a navigation split view with the given panes and attributes. */
|
|
624
584
|
function mountNav(attrs = '', panes = { sidebar: true, content: true, sidebarTag: '', contentTag: '' }) {
|
|
625
585
|
const host = document.createElement('div');
|
|
626
586
|
host.style.width = '800px';
|
|
@@ -650,9 +610,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
650
610
|
};
|
|
651
611
|
|
|
652
612
|
for (const vector of NAVIGATION_STACK_VECTORS) {
|
|
653
|
-
// Only the STATIC rows: the animated branch reaches the same stack by
|
|
654
|
-
//
|
|
655
|
-
// tell them apart. The core suite drives the direction.
|
|
613
|
+
// Only the STATIC rows: the animated branch reaches the same stack by a push
|
|
614
|
+
// or a pop, which a renderer with no transition cannot tell apart.
|
|
656
615
|
if (vector.changingPage) continue;
|
|
657
616
|
const label =
|
|
658
617
|
`${vector.hasSidebar ? 'sidebar' : '-'}/${vector.hasContent ? 'content' : '-'} ` +
|
|
@@ -671,9 +630,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
671
630
|
await settle();
|
|
672
631
|
const top = vector.plan.stack[vector.plan.stack.length - 1] ?? null;
|
|
673
632
|
const panes = visiblePane(view);
|
|
674
|
-
// A LONE child stays visible whatever `show-content` says
|
|
675
|
-
//
|
|
676
|
-
// collapsed sidebar-only view used to render blank.
|
|
633
|
+
// A LONE child stays visible whatever `show-content` says; two CSS classes
|
|
634
|
+
// cannot express that, and a collapsed sidebar-only view renders blank.
|
|
677
635
|
if (vector.hasSidebar) expect(panes.sidebar).toBe(top === 'sidebar');
|
|
678
636
|
if (vector.hasContent) expect(panes.content).toBe(top === 'content');
|
|
679
637
|
host.remove();
|
|
@@ -685,10 +643,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
685
643
|
vector.action === 'push'
|
|
686
644
|
? `push "${vector.tag}" (sidebar=${vector.sidebarTag ?? 'none'}, content=${vector.contentTag ?? 'none'})`
|
|
687
645
|
: `pop (sidebar=${vector.hasSidebar}, content=${vector.hasContent})`;
|
|
688
|
-
//
|
|
689
|
-
//
|
|
690
|
-
// second assignment is refused (:1195-1201), so the element can never
|
|
691
|
-
// hold both. Asserted below instead of driven here.
|
|
646
|
+
// Two panes with the SAME tag is a state the duplicate-tag guard makes
|
|
647
|
+
// unreachable — the second assignment is refused. Asserted below instead.
|
|
692
648
|
const collides =
|
|
693
649
|
vector.action === 'push' && vector.sidebarTag != null && vector.sidebarTag === vector.contentTag;
|
|
694
650
|
if (collides) continue;
|
|
@@ -701,10 +657,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
701
657
|
sidebarTag: vector.sidebarTag ?? '',
|
|
702
658
|
contentTag: vector.contentTag ?? '',
|
|
703
659
|
});
|
|
704
|
-
// `delegate` is the ROUTING's answer;
|
|
705
|
-
//
|
|
706
|
-
// table describes — the unclaimed one becomes a critical, which
|
|
707
|
-
// is the state's own step and has its own test below.
|
|
660
|
+
// `delegate` is the ROUTING's answer; the table describes an ancestor that
|
|
661
|
+
// CLAIMS the tag. The unclaimed case is a critical, tested below.
|
|
708
662
|
host.addEventListener('navigation-push', (event) => {
|
|
709
663
|
(event as CustomEvent<{ handled: boolean }>).detail.handled = true;
|
|
710
664
|
});
|
|
@@ -716,8 +670,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
716
670
|
expect(result.kind).toBe(vector.result.kind);
|
|
717
671
|
if (result.kind === 'set-show-content' && vector.result.kind === 'set-show-content') {
|
|
718
672
|
expect(view.showContent).toBe(vector.result.showContent);
|
|
719
|
-
// The DOM has to agree, or a consumer reading the attribute
|
|
720
|
-
//
|
|
673
|
+
// The DOM has to agree, or a consumer reading the attribute back after
|
|
674
|
+
// a push sees the pre-push answer.
|
|
721
675
|
expect(view.hasAttribute('show-content')).toBe(vector.result.showContent);
|
|
722
676
|
}
|
|
723
677
|
host.remove();
|
|
@@ -728,16 +682,15 @@ export const AdwSplitViewsTest = async () => {
|
|
|
728
682
|
const collidingRows = NAVIGATION_ACTION_VECTORS.filter(
|
|
729
683
|
(v) => v.action === 'push' && v.sidebarTag != null && v.sidebarTag === v.contentTag,
|
|
730
684
|
);
|
|
731
|
-
// Exactly the shared-tag pushes
|
|
732
|
-
//
|
|
733
|
-
// collision the setters refuse to create.
|
|
685
|
+
// Exactly the shared-tag pushes: the C's routing tests the content tag
|
|
686
|
+
// first, which only matters in a collision the setters refuse to create.
|
|
734
687
|
expect(collidingRows.length).toBeGreaterThan(0);
|
|
735
688
|
for (const row of collidingRows) expect(row.contentTag).toBe(row.sidebarTag);
|
|
736
689
|
});
|
|
737
690
|
|
|
738
691
|
await it('an UNCLAIMED push becomes a critical, not a silent no-op', async () => {
|
|
739
|
-
// `navigation_push_cb`
|
|
740
|
-
//
|
|
692
|
+
// `navigation_push_cb` — the parent gets first refusal, and only then is it
|
|
693
|
+
// an error. A pop walks off the end silently instead.
|
|
741
694
|
const { view, host } = mountNav('collapsed', {
|
|
742
695
|
sidebar: true,
|
|
743
696
|
content: true,
|
|
@@ -774,9 +727,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
774
727
|
});
|
|
775
728
|
|
|
776
729
|
await it('REFUSES a pane whose tag the other one already carries', async () => {
|
|
777
|
-
// `adw_navigation_split_view_set_content`
|
|
778
|
-
//
|
|
779
|
-
// duplicate-tag guard at all.
|
|
730
|
+
// `adw_navigation_split_view_set_content` — the pane keeps what it had; the
|
|
731
|
+
// assignment does not happen.
|
|
780
732
|
const { view, host } = mountNav('', {
|
|
781
733
|
sidebar: true,
|
|
782
734
|
content: true,
|
|
@@ -803,19 +755,12 @@ export const AdwSplitViewsTest = async () => {
|
|
|
803
755
|
});
|
|
804
756
|
});
|
|
805
757
|
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
//
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
// wrong edge — the state was right the whole time, only the geometry never
|
|
813
|
-
// reached the DOM.
|
|
814
|
-
//
|
|
815
|
-
// These are placement assertions on purpose. Every other test here reads the
|
|
816
|
-
// state or a class, and every one of them PASSED while the widget was
|
|
817
|
-
// visibly wrong: nothing in the suite looked at where the pane actually
|
|
818
|
-
// ended up. That is the gap, not the individual bug.
|
|
758
|
+
// The docs-site slideshow builds each slide inside `display: none` and moves it in,
|
|
759
|
+
// so the pane had no measurement when it first rendered and no live observer
|
|
760
|
+
// afterwards, and a hidden sidebar was painted over the content at the wrong edge.
|
|
761
|
+
// These assert PLACEMENT on purpose: every state-and-class test here PASSED while
|
|
762
|
+
// the widget was visibly wrong, because none of them looked at where the pane
|
|
763
|
+
// ended up.
|
|
819
764
|
await describe('AdwOverlaySplitView — geometry survives being unmeasured and moved', async () => {
|
|
820
765
|
/** Does the pane cover the view's own box? */
|
|
821
766
|
const covers = (view: AdwOverlaySplitView): boolean => {
|
|
@@ -837,7 +782,6 @@ export const AdwSplitViewsTest = async () => {
|
|
|
837
782
|
const view = slide.querySelector('adw-overlay-split-view') as AdwOverlaySplitView;
|
|
838
783
|
await settle();
|
|
839
784
|
|
|
840
|
-
// The slide becomes the visible one.
|
|
841
785
|
slide.style.display = '';
|
|
842
786
|
await settle();
|
|
843
787
|
expect(view.showSidebar).toBe(false);
|
|
@@ -880,10 +824,9 @@ export const AdwSplitViewsTest = async () => {
|
|
|
880
824
|
const pane = view.querySelector('.adw-osv-sidebar') as HTMLElement;
|
|
881
825
|
expect(pane.style.left).toBe('900px');
|
|
882
826
|
|
|
883
|
-
// Unmeasurable again: the inline offset must be CLEARED
|
|
884
|
-
//
|
|
885
|
-
//
|
|
886
|
-
// to and pin the pane wherever it last happened to be.
|
|
827
|
+
// Unmeasurable again: the inline offset must be CLEARED — an absolutely
|
|
828
|
+
// positioned box with `left` + `width` ignores `right`, so a leftover
|
|
829
|
+
// value outranks the resting rule and pins the pane where it last was.
|
|
887
830
|
host.style.display = 'none';
|
|
888
831
|
await settle();
|
|
889
832
|
expect(pane.style.left).toBe('');
|
|
@@ -891,20 +834,12 @@ export const AdwSplitViewsTest = async () => {
|
|
|
891
834
|
});
|
|
892
835
|
});
|
|
893
836
|
|
|
894
|
-
// The pane STYLE CLASSES
|
|
895
|
-
//
|
|
896
|
-
//
|
|
897
|
-
//
|
|
898
|
-
//
|
|
899
|
-
//
|
|
900
|
-
// the colour follows from that — so a collapsed overlay sidebar, which
|
|
901
|
-
// libadwaita puts on the window background, was painted as though it were
|
|
902
|
-
// still docked beside the content.
|
|
903
|
-
//
|
|
904
|
-
// Asserting the class names and not only the colours is deliberate. The
|
|
905
|
-
// names are the app-facing contract; a rendering that happened to look right
|
|
906
|
-
// through some other selector would still have broken every app stylesheet
|
|
907
|
-
// written against the documented one.
|
|
837
|
+
// The pane STYLE CLASSES libadwaita documents as each split view's CSS node tree. A
|
|
838
|
+
// class says which STATE a pane is in and the colour follows: hard-coding
|
|
839
|
+
// `--sidebar-bg-color` onto a private pane class painted a collapsed overlay
|
|
840
|
+
// sidebar as though it were still docked. The class NAMES are asserted, not only
|
|
841
|
+
// the colours, because the names are the app-facing contract — looking right
|
|
842
|
+
// through some other selector still breaks every app stylesheet.
|
|
908
843
|
await describe('split-view panes carry libadwaita pane style classes', async () => {
|
|
909
844
|
const bg = (el: Element) => getComputedStyle(el).backgroundColor;
|
|
910
845
|
const token = (name: string) => {
|
|
@@ -941,13 +876,13 @@ export const AdwSplitViewsTest = async () => {
|
|
|
941
876
|
await settle();
|
|
942
877
|
expect(sidebar.classList.contains('overlay-pane')).toBe(true);
|
|
943
878
|
expect(sidebar.classList.contains('sidebar-pane')).toBe(false);
|
|
944
|
-
// Collapsed, libadwaita removes `.content-pane` outright — the
|
|
945
|
-
//
|
|
879
|
+
// Collapsed, libadwaita removes `.content-pane` outright — the content
|
|
880
|
+
// is no longer one pane of two, it is the whole view.
|
|
946
881
|
expect(content.classList.contains('content-pane')).toBe(false);
|
|
947
882
|
expect(bg(sidebar)).toBe(token('--window-bg-color'));
|
|
948
883
|
|
|
949
|
-
//
|
|
950
|
-
//
|
|
884
|
+
// BOTH directions: a one-way toggle would leave a re-expanded sidebar
|
|
885
|
+
// window-coloured.
|
|
951
886
|
view.removeAttribute('collapsed');
|
|
952
887
|
await settle();
|
|
953
888
|
expect(sidebar.classList.contains('sidebar-pane')).toBe(true);
|
|
@@ -970,9 +905,8 @@ export const AdwSplitViewsTest = async () => {
|
|
|
970
905
|
expect(content.classList.contains('content-pane')).toBe(true);
|
|
971
906
|
expect(bg(sidebar)).toBe(token('--sidebar-bg-color'));
|
|
972
907
|
|
|
973
|
-
// Collapsed, libadwaita builds no pane bins
|
|
974
|
-
//
|
|
975
|
-
// page and must stop being sidebar-coloured.
|
|
908
|
+
// Collapsed, libadwaita builds no pane bins: both pages move into one
|
|
909
|
+
// navigation view, so the sidebar is an ordinary full-width page.
|
|
976
910
|
view.setAttribute('collapsed', '');
|
|
977
911
|
await settle();
|
|
978
912
|
expect(sidebar.classList.contains('sidebar-pane')).toBe(false);
|