@jskit-ai/shell-web 0.1.149 → 0.1.150
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/CHANGELOG.md +9 -0
- package/fixtures/adaptive-shell/index.html +12 -0
- package/fixtures/adaptive-shell/src/App.vue +27 -0
- package/fixtures/adaptive-shell/src/ScreenPage.vue +14 -0
- package/fixtures/adaptive-shell/src/main.js +197 -0
- package/fixtures/adaptive-shell/vite.config.mjs +17 -0
- package/package.descriptor.mjs +2 -2
- package/package.json +2 -2
- package/src/client/components/ShellLayout.vue +242 -15
- package/src/client/components/ShellMenuLinkItem.vue +18 -12
- package/src/client/components/ShellNavigationTooltip.vue +37 -0
- package/src/client/components/ShellSurfaceAwareMenuLinkItem.vue +18 -12
- package/src/client/support/drawerWidth.js +74 -0
- package/src/client/support/navigationLinkKeyboard.js +10 -0
- package/src/test/adaptiveShellSmoke.js +179 -16
- package/templates/src/components/ShellLayout.vue +1 -0
- package/test/adaptiveShell.browser.test.js +290 -0
- package/test/drawerWidth.test.js +48 -0
- package/test/linkItemScaffoldContract.test.js +18 -2
- package/test/navigationLinkKeyboard.test.js +36 -0
- package/test/playwrightContract.test.js +15 -0
- package/test/settingsPlacementContract.test.js +13 -2
|
@@ -5,6 +5,8 @@ import {
|
|
|
5
5
|
useWebPlacementContext
|
|
6
6
|
} from "../placement/index.js";
|
|
7
7
|
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
8
|
+
import { activateShellNavigationLinkOnSpace } from "../support/navigationLinkKeyboard.js";
|
|
9
|
+
import ShellNavigationTooltip from "./ShellNavigationTooltip.vue";
|
|
8
10
|
|
|
9
11
|
const props = defineProps({
|
|
10
12
|
label: {
|
|
@@ -59,18 +61,22 @@ const resolvedIcon = computed(() =>
|
|
|
59
61
|
</script>
|
|
60
62
|
|
|
61
63
|
<template>
|
|
62
|
-
<v-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
64
|
+
<ShellNavigationTooltip v-if="resolvedTarget.href" :label="props.label">
|
|
65
|
+
<template #default="{ activatorProps }">
|
|
66
|
+
<v-list-item
|
|
67
|
+
v-bind="activatorProps"
|
|
68
|
+
class="shell-menu-link-item"
|
|
69
|
+
:title="props.label"
|
|
70
|
+
:to="resolvedTarget.sameOrigin ? resolvedTarget.href : undefined"
|
|
71
|
+
:href="resolvedTarget.sameOrigin ? undefined : resolvedTarget.href"
|
|
72
|
+
:prepend-icon="resolvedIcon || undefined"
|
|
73
|
+
:disabled="props.disabled"
|
|
74
|
+
:exact="props.exact"
|
|
75
|
+
:aria-label="props.label"
|
|
76
|
+
@keydown.space="activateShellNavigationLinkOnSpace"
|
|
77
|
+
/>
|
|
78
|
+
</template>
|
|
79
|
+
</ShellNavigationTooltip>
|
|
74
80
|
</template>
|
|
75
81
|
|
|
76
82
|
<style scoped>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import { useTheme } from "vuetify";
|
|
3
|
+
|
|
4
|
+
const props = defineProps({
|
|
5
|
+
label: {
|
|
6
|
+
type: String,
|
|
7
|
+
default: ""
|
|
8
|
+
}
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
const theme = useTheme();
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<template>
|
|
15
|
+
<v-tooltip
|
|
16
|
+
:text="props.label"
|
|
17
|
+
:theme="theme.name.value"
|
|
18
|
+
location="end"
|
|
19
|
+
content-class="shell-navigation-tooltip"
|
|
20
|
+
:open-on-focus="true"
|
|
21
|
+
:open-on-hover="true"
|
|
22
|
+
>
|
|
23
|
+
<template #activator="{ props: activatorProps }">
|
|
24
|
+
<slot :activator-props="activatorProps" />
|
|
25
|
+
</template>
|
|
26
|
+
</v-tooltip>
|
|
27
|
+
</template>
|
|
28
|
+
|
|
29
|
+
<style>
|
|
30
|
+
.shell-navigation-tooltip {
|
|
31
|
+
background-color:
|
|
32
|
+
rgb(var(--v-theme-inverse-surface, var(--v-theme-on-surface))) !important;
|
|
33
|
+
color:
|
|
34
|
+
rgb(var(--v-theme-inverse-on-surface, var(--v-theme-surface))) !important;
|
|
35
|
+
opacity: 1 !important;
|
|
36
|
+
}
|
|
37
|
+
</style>
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
} from "../placement/index.js";
|
|
10
10
|
import { resolveMenuLinkIcon } from "../lib/menuIcons.js";
|
|
11
11
|
import { resolveMenuLinkTarget } from "../support/menuLinkTarget.js";
|
|
12
|
+
import { activateShellNavigationLinkOnSpace } from "../support/navigationLinkKeyboard.js";
|
|
13
|
+
import ShellNavigationTooltip from "./ShellNavigationTooltip.vue";
|
|
12
14
|
|
|
13
15
|
const props = defineProps({
|
|
14
16
|
label: {
|
|
@@ -104,18 +106,22 @@ const resolvedIcon = computed(() =>
|
|
|
104
106
|
</script>
|
|
105
107
|
|
|
106
108
|
<template>
|
|
107
|
-
<v-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
109
|
+
<ShellNavigationTooltip v-if="resolvedTarget.href" :label="props.label">
|
|
110
|
+
<template #default="{ activatorProps }">
|
|
111
|
+
<v-list-item
|
|
112
|
+
v-bind="activatorProps"
|
|
113
|
+
class="shell-menu-link-item"
|
|
114
|
+
:title="props.label"
|
|
115
|
+
:to="resolvedTarget.sameOrigin ? resolvedTarget.href : undefined"
|
|
116
|
+
:href="resolvedTarget.sameOrigin ? undefined : resolvedTarget.href"
|
|
117
|
+
:prepend-icon="resolvedIcon || undefined"
|
|
118
|
+
:disabled="props.disabled"
|
|
119
|
+
:exact="props.exact"
|
|
120
|
+
:aria-label="props.label"
|
|
121
|
+
@keydown.space="activateShellNavigationLinkOnSpace"
|
|
122
|
+
/>
|
|
123
|
+
</template>
|
|
124
|
+
</ShellNavigationTooltip>
|
|
119
125
|
</template>
|
|
120
126
|
|
|
121
127
|
<style scoped>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
const DEFAULT_SHELL_DRAWER_WIDTH = 248;
|
|
2
|
+
const DEFAULT_SHELL_RAIL_WIDTH = 80;
|
|
3
|
+
const MINIMUM_SHELL_DRAWER_WIDTH = 120;
|
|
4
|
+
const MAXIMUM_SHELL_DRAWER_WIDTH = 360;
|
|
5
|
+
const MINIMUM_SHELL_RAIL_WIDTH = 48;
|
|
6
|
+
const MAXIMUM_SHELL_RAIL_WIDTH = 160;
|
|
7
|
+
const SHELL_DRAWER_LABEL_END_GAP = 10;
|
|
8
|
+
|
|
9
|
+
function clampNumber(value, minimum, maximum) {
|
|
10
|
+
return Math.min(maximum, Math.max(minimum, value));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function normalizeShellDrawerWidth(value, fallback = DEFAULT_SHELL_DRAWER_WIDTH) {
|
|
14
|
+
const parsed = Number(value);
|
|
15
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
16
|
+
return fallback;
|
|
17
|
+
}
|
|
18
|
+
return clampNumber(
|
|
19
|
+
parsed,
|
|
20
|
+
MINIMUM_SHELL_DRAWER_WIDTH,
|
|
21
|
+
MAXIMUM_SHELL_DRAWER_WIDTH
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function normalizeShellRailWidth(value, fallback = DEFAULT_SHELL_RAIL_WIDTH) {
|
|
26
|
+
const parsed = Number(value);
|
|
27
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
28
|
+
return fallback;
|
|
29
|
+
}
|
|
30
|
+
return clampNumber(
|
|
31
|
+
Math.round(parsed),
|
|
32
|
+
MINIMUM_SHELL_RAIL_WIDTH,
|
|
33
|
+
MAXIMUM_SHELL_RAIL_WIDTH
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function resolveContentAwareDrawerWidth(
|
|
38
|
+
measurements = [],
|
|
39
|
+
{
|
|
40
|
+
endGap = SHELL_DRAWER_LABEL_END_GAP,
|
|
41
|
+
minimum = MINIMUM_SHELL_DRAWER_WIDTH,
|
|
42
|
+
maximum = MAXIMUM_SHELL_DRAWER_WIDTH,
|
|
43
|
+
fallback = DEFAULT_SHELL_DRAWER_WIDTH
|
|
44
|
+
} = {}
|
|
45
|
+
) {
|
|
46
|
+
let requiredWidth = 0;
|
|
47
|
+
for (const measurement of measurements) {
|
|
48
|
+
const logicalStart = Number(measurement?.logicalStart);
|
|
49
|
+
const textWidth = Number(measurement?.textWidth);
|
|
50
|
+
if (!Number.isFinite(logicalStart) || logicalStart < 0 || !Number.isFinite(textWidth) || textWidth <= 0) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
requiredWidth = Math.max(requiredWidth, logicalStart + textWidth + Number(endGap || 0));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (requiredWidth <= 0) {
|
|
57
|
+
return normalizeShellDrawerWidth(fallback);
|
|
58
|
+
}
|
|
59
|
+
// Layout boxes expose integer client widths even when the rendered glyph
|
|
60
|
+
// range is fractional. Round outward so the final glyph is never ellipsized
|
|
61
|
+
// at the exact end-gap boundary.
|
|
62
|
+
return clampNumber(Math.ceil(requiredWidth), minimum, maximum);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export {
|
|
66
|
+
DEFAULT_SHELL_DRAWER_WIDTH,
|
|
67
|
+
DEFAULT_SHELL_RAIL_WIDTH,
|
|
68
|
+
MAXIMUM_SHELL_DRAWER_WIDTH,
|
|
69
|
+
MINIMUM_SHELL_DRAWER_WIDTH,
|
|
70
|
+
SHELL_DRAWER_LABEL_END_GAP,
|
|
71
|
+
normalizeShellDrawerWidth,
|
|
72
|
+
normalizeShellRailWidth,
|
|
73
|
+
resolveContentAwareDrawerWidth
|
|
74
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
function activateShellNavigationLinkOnSpace(event) {
|
|
2
|
+
if (event?.key !== " " || typeof event.currentTarget?.click !== "function") {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
event.preventDefault();
|
|
6
|
+
event.stopPropagation();
|
|
7
|
+
event.currentTarget.click();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { activateShellNavigationLinkOnSpace };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const DEFAULT_SMOKE_PATH = String(process.env.JSKIT_PLAYWRIGHT_SMOKE_PATH || "/home");
|
|
2
2
|
const DEFAULT_VIEWPORTS = Object.freeze([
|
|
3
3
|
Object.freeze({ name: "compact", width: 390, height: 844 }),
|
|
4
|
-
Object.freeze({ name: "medium", width:
|
|
4
|
+
Object.freeze({ name: "medium", width: 1024, height: 1024 }),
|
|
5
5
|
Object.freeze({ name: "expanded", width: 1280, height: 900 })
|
|
6
6
|
]);
|
|
7
7
|
|
|
@@ -68,6 +68,152 @@ async function isElementVisibleInViewport(page, testId) {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
async function expectElementVisibility(page, expect, testId, visible) {
|
|
72
|
+
await expect.poll(
|
|
73
|
+
() => isElementVisibleInViewport(page, testId),
|
|
74
|
+
{ message: `${testId} did not reach its expected visibility.` }
|
|
75
|
+
).toBe(visible);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function readShellLayoutClass(drawer) {
|
|
79
|
+
const layoutClass = String(await drawer.getAttribute("data-layout") || "");
|
|
80
|
+
if (!["compact", "medium", "expanded"].includes(layoutClass)) {
|
|
81
|
+
throw new Error(`Shell drawer exposed an invalid data-layout value: ${layoutClass || "(empty)"}.`);
|
|
82
|
+
}
|
|
83
|
+
return layoutClass;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function readConfiguredWidth(drawer, attribute) {
|
|
87
|
+
const width = Number(await drawer.getAttribute(attribute));
|
|
88
|
+
if (!Number.isFinite(width) || width <= 0) {
|
|
89
|
+
throw new Error(`Shell drawer exposed an invalid ${attribute} value.`);
|
|
90
|
+
}
|
|
91
|
+
return width;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function expectDrawerWidth(drawer, expect, expectedWidth) {
|
|
95
|
+
await expect.poll(async () => {
|
|
96
|
+
const box = await drawer.boundingBox();
|
|
97
|
+
return box ? Math.abs(box.width - expectedWidth) : Number.POSITIVE_INFINITY;
|
|
98
|
+
}, {
|
|
99
|
+
message: `Shell drawer did not settle at ${expectedWidth}px.`
|
|
100
|
+
}).toBeLessThanOrEqual(1);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async function expectContentAwareDrawerFit(drawer, expect) {
|
|
104
|
+
if (await drawer.getAttribute("data-drawer-width-mode") !== "content") {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const fit = await drawer.evaluate((element) => {
|
|
109
|
+
const drawerRect = element.getBoundingClientRect();
|
|
110
|
+
const drawerStyle = window.getComputedStyle(element);
|
|
111
|
+
const rightToLeft = drawerStyle.direction === "rtl";
|
|
112
|
+
const endBorderWidth = Number.parseFloat(
|
|
113
|
+
rightToLeft ? drawerStyle.borderLeftWidth : drawerStyle.borderRightWidth
|
|
114
|
+
) || 0;
|
|
115
|
+
const innerEnd = rightToLeft
|
|
116
|
+
? drawerRect.left + endBorderWidth
|
|
117
|
+
: drawerRect.right - endBorderWidth;
|
|
118
|
+
const labels = Array.from(element.querySelectorAll(".v-list-item-title"))
|
|
119
|
+
.filter((label) => {
|
|
120
|
+
const style = window.getComputedStyle(label);
|
|
121
|
+
return style.display !== "none" && style.visibility !== "hidden";
|
|
122
|
+
})
|
|
123
|
+
.map((label) => {
|
|
124
|
+
const range = document.createRange();
|
|
125
|
+
range.selectNodeContents(label);
|
|
126
|
+
const textRect = range.getBoundingClientRect();
|
|
127
|
+
range.detach?.();
|
|
128
|
+
return {
|
|
129
|
+
clipped: (
|
|
130
|
+
label.scrollWidth > label.clientWidth ||
|
|
131
|
+
label.scrollHeight > label.clientHeight ||
|
|
132
|
+
(rightToLeft
|
|
133
|
+
? textRect.left < innerEnd - 1
|
|
134
|
+
: textRect.right > innerEnd + 1)
|
|
135
|
+
),
|
|
136
|
+
logicalEnd: rightToLeft ? textRect.left : textRect.right,
|
|
137
|
+
width: textRect.width
|
|
138
|
+
};
|
|
139
|
+
})
|
|
140
|
+
.filter((label) => label.width > 0);
|
|
141
|
+
|
|
142
|
+
if (labels.length === 0) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const furthestLabel = labels.reduce((furthest, label) => {
|
|
146
|
+
if (!furthest) {
|
|
147
|
+
return label;
|
|
148
|
+
}
|
|
149
|
+
return rightToLeft
|
|
150
|
+
? label.logicalEnd < furthest.logicalEnd ? label : furthest
|
|
151
|
+
: label.logicalEnd > furthest.logicalEnd ? label : furthest;
|
|
152
|
+
}, null);
|
|
153
|
+
return {
|
|
154
|
+
clipped: labels.some((label) => label.clipped),
|
|
155
|
+
endGap: rightToLeft
|
|
156
|
+
? furthestLabel.logicalEnd - innerEnd
|
|
157
|
+
: innerEnd - furthestLabel.logicalEnd
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(fit).not.toBeNull();
|
|
162
|
+
expect(fit.clipped).toBe(false);
|
|
163
|
+
expect(fit.endGap).toBeGreaterThanOrEqual(9);
|
|
164
|
+
expect(fit.endGap).toBeLessThanOrEqual(11);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async function expectCentredRailNavigation(page, drawer, expect) {
|
|
168
|
+
const links = drawer.locator("a.shell-menu-link-item[href]");
|
|
169
|
+
await expect(links.first()).toBeVisible();
|
|
170
|
+
const firstLink = links.first();
|
|
171
|
+
const firstIcon = firstLink.locator(".v-icon").first();
|
|
172
|
+
const geometry = await Promise.all([drawer.boundingBox(), firstLink.boundingBox(), firstIcon.boundingBox()]);
|
|
173
|
+
const [drawerBox, linkBox, iconBox] = geometry;
|
|
174
|
+
|
|
175
|
+
expect(drawerBox).not.toBeNull();
|
|
176
|
+
expect(linkBox).not.toBeNull();
|
|
177
|
+
expect(iconBox).not.toBeNull();
|
|
178
|
+
expect(linkBox.width).toBeGreaterThanOrEqual(48);
|
|
179
|
+
expect(linkBox.height).toBeGreaterThanOrEqual(48);
|
|
180
|
+
expect(Math.abs(
|
|
181
|
+
iconBox.x + iconBox.width / 2 - (drawerBox.x + drawerBox.width / 2)
|
|
182
|
+
)).toBeLessThanOrEqual(1);
|
|
183
|
+
|
|
184
|
+
const currentUrl = new URL(page.url());
|
|
185
|
+
const linkCount = await links.count();
|
|
186
|
+
let navigationLink = null;
|
|
187
|
+
let targetUrl = null;
|
|
188
|
+
for (let index = 0; index < linkCount; index += 1) {
|
|
189
|
+
const candidate = links.nth(index);
|
|
190
|
+
const href = await candidate.getAttribute("href");
|
|
191
|
+
if (!href) {
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
const resolved = new URL(href, currentUrl);
|
|
195
|
+
if (resolved.pathname !== currentUrl.pathname || resolved.search !== currentUrl.search) {
|
|
196
|
+
navigationLink = candidate;
|
|
197
|
+
targetUrl = resolved;
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
expect(navigationLink).not.toBeNull();
|
|
203
|
+
await navigationLink.locator(".v-icon").first().click();
|
|
204
|
+
await expect.poll(() => {
|
|
205
|
+
const actual = new URL(page.url());
|
|
206
|
+
return `${actual.pathname}${actual.search}`;
|
|
207
|
+
}).toBe(`${targetUrl.pathname}${targetUrl.search}`);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function openCompactDrawer(page, expect) {
|
|
211
|
+
const drawer = page.getByTestId("jskit-shell-drawer");
|
|
212
|
+
await page.getByTestId("jskit-shell-nav-toggle").click();
|
|
213
|
+
await expect(drawer).toHaveAttribute("data-presentation", "modal");
|
|
214
|
+
await expectElementVisibility(page, expect, "jskit-shell-drawer", true);
|
|
215
|
+
}
|
|
216
|
+
|
|
71
217
|
async function runAdaptiveShellSmokeCase({
|
|
72
218
|
page,
|
|
73
219
|
expect,
|
|
@@ -83,8 +229,11 @@ async function runAdaptiveShellSmokeCase({
|
|
|
83
229
|
await expect(page.locator("body")).toBeVisible();
|
|
84
230
|
await expectGeneratedScreenContract(page, expect);
|
|
85
231
|
await expectNoHorizontalOverflow(page, expect);
|
|
232
|
+
const drawer = page.getByTestId("jskit-shell-drawer");
|
|
233
|
+
const toggle = page.getByTestId("jskit-shell-nav-toggle");
|
|
234
|
+
const layoutClass = await readShellLayoutClass(drawer);
|
|
86
235
|
|
|
87
|
-
if (
|
|
236
|
+
if (layoutClass === "compact") {
|
|
88
237
|
let bootstrapRequests = 0;
|
|
89
238
|
await page.route("**/api/bootstrap**", async (route) => {
|
|
90
239
|
bootstrapRequests += 1;
|
|
@@ -93,12 +242,20 @@ async function runAdaptiveShellSmokeCase({
|
|
|
93
242
|
const bootstrapRequestsBeforePull = bootstrapRequests;
|
|
94
243
|
|
|
95
244
|
await expect(page.getByTestId("jskit-shell-bottom-nav")).toBeVisible();
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
await
|
|
99
|
-
await
|
|
245
|
+
await expectElementVisibility(page, expect, "jskit-shell-drawer", false);
|
|
246
|
+
|
|
247
|
+
await openCompactDrawer(page, expect);
|
|
248
|
+
await toggle.click();
|
|
249
|
+
await expectElementVisibility(page, expect, "jskit-shell-drawer", false);
|
|
250
|
+
|
|
251
|
+
await openCompactDrawer(page, expect);
|
|
252
|
+
await page.locator(".v-navigation-drawer__scrim").click();
|
|
253
|
+
await expectElementVisibility(page, expect, "jskit-shell-drawer", false);
|
|
254
|
+
|
|
255
|
+
await openCompactDrawer(page, expect);
|
|
100
256
|
await page.keyboard.press("Escape");
|
|
101
|
-
|
|
257
|
+
await expectElementVisibility(page, expect, "jskit-shell-drawer", false);
|
|
258
|
+
await expect(toggle).toBeFocused();
|
|
102
259
|
|
|
103
260
|
const navButtonHeights = await page.getByTestId("jskit-shell-bottom-nav").locator(".v-btn").evaluateAll((buttons) =>
|
|
104
261
|
buttons.map((button) => button.getBoundingClientRect().height)
|
|
@@ -111,15 +268,21 @@ async function runAdaptiveShellSmokeCase({
|
|
|
111
268
|
await pullToRefresh(page, expect);
|
|
112
269
|
await expect.poll(() => bootstrapRequests).toBeGreaterThan(bootstrapRequestsBeforePull);
|
|
113
270
|
} else {
|
|
114
|
-
await expect(
|
|
115
|
-
await
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
);
|
|
121
|
-
|
|
122
|
-
expect(
|
|
271
|
+
await expect(drawer).toBeVisible();
|
|
272
|
+
if (await drawer.getAttribute("data-presentation") === "rail") {
|
|
273
|
+
await toggle.click();
|
|
274
|
+
}
|
|
275
|
+
await expect(drawer).toHaveAttribute("data-presentation", "drawer");
|
|
276
|
+
await expectDrawerWidth(drawer, expect, await readConfiguredWidth(drawer, "data-drawer-width"));
|
|
277
|
+
await expectContentAwareDrawerFit(drawer, expect);
|
|
278
|
+
await page.keyboard.press("Escape");
|
|
279
|
+
await expect(drawer).toHaveAttribute("data-presentation", "drawer");
|
|
280
|
+
|
|
281
|
+
await toggle.click();
|
|
282
|
+
await expect(drawer).toHaveAttribute("data-presentation", "rail");
|
|
283
|
+
await expectDrawerWidth(drawer, expect, await readConfiguredWidth(drawer, "data-rail-width"));
|
|
284
|
+
await expectCentredRailNavigation(page, drawer, expect);
|
|
285
|
+
await expectNoHorizontalOverflow(page, expect);
|
|
123
286
|
}
|
|
124
287
|
}
|
|
125
288
|
|