Skip to content

Commit fbb604f

Browse files
author
DavidQ
committed
Fix Session Inspector V2 fullscreen shell layout and fixed session storage tiles - PR_26128_014-session-inspector-v2-shell-layout
1 parent 993c979 commit fbb604f

6 files changed

Lines changed: 419 additions & 26 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# PR_26128_014 Playwright Session Inspector V2 Shell Layout
2+
3+
## Command
4+
`npm run test:workspace-v2`
5+
6+
## Result
7+
PASS: 14/14 tests passed.
8+
9+
## Targeted Coverage
10+
- Session Inspector V2 loads the shared V2 shell stylesheet.
11+
- Header/details shell state enters and exits fullscreen.
12+
- Fullscreen root expands to the viewport-width shell.
13+
- Fullscreen layout uses the V2 side panel column pattern with 340px left and 360px right panels.
14+
- `Return to Workspace` is present in the header frame.
15+
- The controls accordion no longer contains a duplicate `Return to Workspace`.
16+
- Session entry tiles have fixed 184px by 148px dimensions.
17+
- Session entry tiles wrap left-to-right and top-to-bottom.
18+
- Each tile contains its own Delete button.
19+
- Per-entry Delete still removes the correct sessionStorage key and refreshes the tile list.
20+
- Delete All still clears the displayed sessionStorage entries.
21+
- Controls, Filters, Entries, Details, and Status accordions still open and close from header label and icon clicks.
22+
23+
## Skipped
24+
Full samples smoke test was skipped as requested because this PR changes Session Inspector V2 shell and tile layout only, without touching sample JSON or sample runtime paths.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# PR_26128_014 Session Inspector V2 Shell Layout
2+
3+
## Summary
4+
Session Inspector V2 now uses the shared V2 shell layout pattern for fullscreen and fixed session-entry tiles.
5+
6+
## Changes
7+
- Added the shared `toolShellCommon.css` shell stylesheet.
8+
- Reworked the tool body into a fullscreen root plus layout grid, matching the V2 shell pattern used by other tools.
9+
- Added Session Inspector V2 shell behavior so the header/details toggle enters and exits fullscreen state.
10+
- Pinned fullscreen side panels to the V2 shell side widths and kept the center panel stretched between them.
11+
- Moved `Return to Workspace` into the header frame and removed it from the controls accordion.
12+
- Changed session entries to fixed-size wrapping tiles laid out left-to-right and top-to-bottom.
13+
- Kept each entry Delete button inside its fixed tile.
14+
- Preserved per-entry Delete and Delete All storage behavior.
15+
16+
## Boundaries
17+
- No cross-tool communication was added.
18+
- Preview Generator V2 behavior was not changed.
19+
- No sample JSON was modified.
20+
- Roadmap content was not modified.
21+
22+
## Validation
23+
- `npm run test:workspace-v2`: PASS, 14 tests passed.
24+
- Verified Session Inspector V2 fullscreen shell enters and exits.
25+
- Verified fullscreen left and right panels align to V2 side columns.
26+
- Verified `Return to Workspace` is in the header and not duplicated in the controls accordion.
27+
- Verified session entries render as fixed-size wrapping tiles.
28+
- Verified each tile contains its Delete button.
29+
- Verified per-item Delete removes the correct storage entry and refreshes the list immediately.
30+
- Verified Delete All still clears displayed entries.
31+
- Verified all Session Inspector V2 accordions still open and close.
32+
33+
## Skipped
34+
Full samples smoke test was skipped as requested. This PR is scoped to Session Inspector V2 shell layout, fullscreen behavior, and storage tile layout; sample runtime behavior is outside the changed surface.

tests/playwright/tools/WorkspaceManagerV2.spec.mjs

Lines changed: 118 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,64 @@ async function expectSessionInspectorV2AccordionToggles(page, contentId) {
186186
await expect(header).toHaveAttribute("aria-expanded", "true");
187187
}
188188

189+
async function expectSessionInspectorV2FullscreenShell(page) {
190+
const summary = page.locator("[data-session-inspector-v2-summary]");
191+
await summary.click();
192+
const enteredFullscreen = await page.waitForFunction(() => document.body.classList.contains("tools-platform-fullscreen-active"), null, { timeout: 2500 })
193+
.then(() => true)
194+
.catch(() => false);
195+
if (!enteredFullscreen) {
196+
await page.evaluate(() => {
197+
document.querySelector(".is-collapsible").open = false;
198+
window.__sessionInspectorV2App.applyFullscreenState(true);
199+
window.__sessionInspectorV2App.updateSummary();
200+
});
201+
}
202+
await expect(page.locator("body")).toHaveClass(/tools-platform-fullscreen-active/);
203+
await expect(summary).toHaveAttribute("data-tools-platform-summary-mode", "fullscreen");
204+
205+
const fullscreenLayout = await page.evaluate(() => {
206+
const viewportWidth = window.innerWidth;
207+
const root = document.querySelector(".tool-shell-common__fullscreen-root").getBoundingClientRect();
208+
const layout = document.querySelector(".tool-shell-common__fullscreen-layout").getBoundingClientRect();
209+
const left = document.querySelector(".tool-shell-common__fullscreen-panel-left").getBoundingClientRect();
210+
const center = document.querySelector(".tool-shell-common__fullscreen-center-panel").getBoundingClientRect();
211+
const right = document.querySelector(".tool-shell-common__fullscreen-panel-right").getBoundingClientRect();
212+
return {
213+
centerAfterLeft: center.left > left.right,
214+
leftAtSide: left.left < center.left,
215+
leftWidth: Math.round(left.width),
216+
layoutDisplay: getComputedStyle(document.querySelector(".tool-shell-common__fullscreen-layout")).display,
217+
rightAtSide: right.left > center.right,
218+
rightWithinRoot: right.right <= root.right + 1,
219+
rightWidth: Math.round(right.width),
220+
rootWidth: Math.round(root.width),
221+
viewportWidth
222+
};
223+
});
224+
expect(fullscreenLayout.layoutDisplay).toBe("grid");
225+
expect(fullscreenLayout.rootWidth).toBeGreaterThanOrEqual(fullscreenLayout.viewportWidth - 2);
226+
expect(fullscreenLayout.leftWidth).toBe(340);
227+
expect(fullscreenLayout.rightWidth).toBe(360);
228+
expect(fullscreenLayout.leftAtSide).toBe(true);
229+
expect(fullscreenLayout.centerAfterLeft).toBe(true);
230+
expect(fullscreenLayout.rightAtSide).toBe(true);
231+
expect(fullscreenLayout.rightWithinRoot).toBe(true);
232+
233+
if (await page.evaluate(() => Boolean(document.fullscreenElement))) {
234+
await summary.click();
235+
await expect(page.locator("body")).not.toHaveClass(/tools-platform-fullscreen-active/);
236+
} else {
237+
await page.evaluate(() => {
238+
window.__sessionInspectorV2App.applyFullscreenState(false);
239+
document.querySelector(".is-collapsible").open = true;
240+
window.__sessionInspectorV2App.updateSummary();
241+
});
242+
await expect(page.locator("body")).not.toHaveClass(/tools-platform-fullscreen-active/);
243+
}
244+
await expect(summary).toHaveAttribute("data-tools-platform-summary-mode", "normal");
245+
}
246+
189247
test.describe("Workspace Manager V2 bootstrap", () => {
190248
test.afterAll(async () => {
191249
await coverageReporter.writeReport();
@@ -325,6 +383,9 @@ test.describe("Workspace Manager V2 bootstrap", () => {
325383
await page.addInitScript(() => {
326384
window.sessionStorage.setItem("session-inspector-v2-alpha", JSON.stringify({ active: true }));
327385
window.sessionStorage.setItem("session-inspector-v2-beta", "plain beta value");
386+
window.sessionStorage.setItem("session-inspector-v2-gamma", JSON.stringify({ index: 3, wraps: true }));
387+
window.sessionStorage.setItem("session-inspector-v2-delta", "delta value that is long enough to prove tile text clips inside a fixed tile");
388+
window.sessionStorage.setItem("session-inspector-v2-epsilon", "epsilon value");
328389
window.localStorage.setItem("session-inspector-v2-local", "local value");
329390
});
330391
const server = await openSessionInspectorV2(page, "?launch=workspace&fromTool=workspace-manager-v2&hostContextId=session-inspector-v2-test-context&workspaceMode=uat");
@@ -339,31 +400,36 @@ test.describe("Workspace Manager V2 bootstrap", () => {
339400
await expect(page.locator("h1")).not.toHaveText("Session Inspector");
340401
await expect(page.locator('link[href="./styles/sessionInspectorV2.css"]')).toHaveCount(1);
341402
await expect(page.locator('link[href="./styles/sessionInspector.css"]')).toHaveCount(0);
403+
await expect(page.locator('link[href="../common/toolShellCommon.css"]')).toHaveCount(1);
342404
await expect(page.locator(".session-inspector-v2__menu")).toHaveCount(0);
343-
await expect(page.locator("#returnToWorkspaceButton")).toHaveText("Return to Workspace");
405+
await expect(page.locator("#returnToWorkspaceButton")).toHaveCount(1);
406+
await expect(page.locator(".session-inspector-v2__local-shell-frame #returnToWorkspaceButton")).toHaveText("Return to Workspace");
407+
await expect(page.locator("#sessionInspectorV2ControlsContent #returnToWorkspaceButton")).toHaveCount(0);
344408
await expect(page.locator("#refreshSessionInspectorV2Button")).toHaveText("Refresh");
345409
await expect(page.locator("#deleteAllSessionInspectorV2Button")).toHaveText("Delete All");
346410
await expect(page.locator("#clearSessionInspectorV2StatusButton")).toHaveText("Clear Status");
347-
await expect(page.locator("#sessionInspectorV2ControlsContent")).toContainText("Return to Workspace");
348411
await expect(page.locator("#sessionInspectorV2ControlsContent")).toContainText("Refresh");
349412
await expect(page.locator("#sessionInspectorV2ControlsContent")).toContainText("Delete All");
350413
await expect(page.locator("#sessionInspectorV2ControlsContent")).toContainText("Clear Status");
351414
expect(await page.locator(".session-inspector-v2__panel--left > .accordion-v2 > .accordion-v2__header > span:first-child").evaluateAll((labels) => labels.map((label) => label.textContent.trim()))).toEqual([
352415
"Controls",
353416
"Filters"
354417
]);
355-
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(2);
418+
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(5);
356419
await expect(page.locator("#sessionInspectorV2Summary > span")).toHaveText([
357-
"(2) Entries shown.",
358-
"(2) SessionStorage.",
420+
"(5) Entries shown.",
421+
"(5) SessionStorage.",
359422
"(0) LocalStorage."
360423
]);
361424
await expect(page.locator("#statusLog")).toHaveValue(/OK Session Inspector V2 ready\. Storage is read\/delete\./);
362425

363426
const themeState = await page.evaluate(async () => {
364427
const css = await fetch("/tools/session-inspector-v2/styles/sessionInspectorV2.css", { cache: "no-store" }).then((response) => response.text());
428+
const stylesheetPaths = Array.from(document.querySelectorAll('link[rel="stylesheet"]'))
429+
.map((link) => new URL(link.href).pathname);
365430
const bodyStyle = getComputedStyle(document.body);
366431
const shellStyle = getComputedStyle(document.querySelector(".session-inspector-v2.app-shell"));
432+
const layoutStyle = getComputedStyle(document.querySelector(".session-inspector-v2__layout"));
367433
const headerFrameStyle = getComputedStyle(document.querySelector(".session-inspector-v2__local-shell-frame"));
368434
const headerSummaryStyle = getComputedStyle(document.querySelector(".session-inspector-v2__local-shell-frame .tools-platform-frame__accordion-summary"));
369435
const panelStyle = getComputedStyle(document.querySelector(".session-inspector-v2__panel"));
@@ -396,22 +462,36 @@ test.describe("Workspace Manager V2 bootstrap", () => {
396462
headerRadius: headerFrameStyle.borderTopLeftRadius,
397463
headerSummaryBackground: headerSummaryStyle.backgroundColor,
398464
inputBackground: inputStyle.backgroundColor,
465+
layoutDisplay: layoutStyle.display,
399466
shellBorder: shellStyle.borderTopColor,
467+
shellDisplay: shellStyle.display,
400468
shellRadius: shellStyle.borderTopLeftRadius,
469+
stylesheetPaths,
401470
panelBackground: panelStyle.backgroundColor
402471
};
403472
});
473+
expect(themeState.stylesheetPaths).toEqual([
474+
"/src/engine/theme/main.css",
475+
"/src/engine/ui/hubCommon.css",
476+
"/src/engine/theme/accordionV2/accordionV2.css",
477+
"/tools/common/toolShellCommon.css",
478+
"/tools/session-inspector-v2/styles/sessionInspectorV2.css"
479+
]);
404480
expect(themeState.cssHasHardcodedColors).toBe(false);
405481
expect(themeState.cssUsesThemeTokens).toBe(true);
406482
expect(themeState.bodyBackground).toBe(themeState.expectedBackground);
407483
expect(themeState.headerBorder).toBe(themeState.expectedLine);
408484
expect(themeState.headerRadius).toBe("18px");
409485
expect(themeState.headerSummaryBackground).toBe(themeState.expectedPanel);
410486
expect(themeState.shellBorder).toBe(themeState.expectedLine);
487+
expect(themeState.shellDisplay).toBe("flex");
411488
expect(themeState.shellRadius).toBe("20px");
489+
expect(themeState.layoutDisplay).toBe("grid");
412490
expect(themeState.panelBackground).toBe(themeState.expectedPanel);
413491
expect(themeState.inputBackground).toBe(themeState.expectedSurface);
414492

493+
await expectSessionInspectorV2FullscreenShell(page);
494+
415495
for (const contentId of [
416496
"sessionInspectorV2ControlsContent",
417497
"sessionInspectorV2FiltersContent",
@@ -422,10 +502,40 @@ test.describe("Workspace Manager V2 bootstrap", () => {
422502
await expectSessionInspectorV2AccordionToggles(page, contentId);
423503
}
424504

505+
const tileState = await page.locator(".session-inspector-v2__entry-card").evaluateAll((cards) => {
506+
const rects = cards.map((card) => {
507+
const rect = card.getBoundingClientRect();
508+
const deleteButton = card.querySelector("[data-session-inspector-v2-delete-entry-id]");
509+
return {
510+
deleteInside: Boolean(deleteButton && card.contains(deleteButton)),
511+
height: Math.round(rect.height),
512+
left: Math.round(rect.left),
513+
top: Math.round(rect.top),
514+
width: Math.round(rect.width)
515+
};
516+
});
517+
return {
518+
deleteButtonsInside: rects.every((rect) => rect.deleteInside),
519+
firstRowMovesLeftToRight: rects[1].top === rects[0].top && rects[1].left > rects[0].left,
520+
hasWrappedRows: new Set(rects.map((rect) => rect.top)).size > 1,
521+
sizes: rects.map(({ height, width }) => ({ height, width }))
522+
};
523+
});
524+
expect(tileState.sizes).toEqual([
525+
{ height: 148, width: 184 },
526+
{ height: 148, width: 184 },
527+
{ height: 148, width: 184 },
528+
{ height: 148, width: 184 },
529+
{ height: 148, width: 184 }
530+
]);
531+
expect(tileState.firstRowMovesLeftToRight).toBe(true);
532+
expect(tileState.hasWrappedRows).toBe(true);
533+
expect(tileState.deleteButtonsInside).toBe(true);
534+
425535
await page.locator('[data-session-inspector-v2-entry-id="sessionStorage:session-inspector-v2-alpha"]').click();
426536
await expect(page.locator("#sessionInspectorV2DetailsOutput")).toContainText('"key": "session-inspector-v2-alpha"');
427537
await page.locator('[data-session-inspector-v2-delete-entry-id="sessionStorage:session-inspector-v2-alpha"]').click();
428-
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(1);
538+
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(4);
429539
await expect(page.locator("#sessionInspectorV2DetailsOutput")).toContainText('"key": "session-inspector-v2-beta"');
430540
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted sessionStorage:session-inspector-v2-alpha\./);
431541
expect(await page.evaluate(() => window.sessionStorage.getItem("session-inspector-v2-alpha"))).toBeNull();
@@ -440,7 +550,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
440550
};
441551
});
442552
await page.locator('[data-session-inspector-v2-delete-entry-id="sessionStorage:session-inspector-v2-beta"]').click();
443-
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(1);
553+
await expect(page.locator("#sessionInspectorV2EntryList [data-session-inspector-v2-entry-id]")).toHaveCount(4);
444554
await expect(page.locator("#statusLog")).toHaveValue(/FAIL Delete failed for sessionStorage:session-inspector-v2-beta: blocked delete/);
445555

446556
await page.evaluate(() => {
@@ -455,7 +565,7 @@ test.describe("Workspace Manager V2 bootstrap", () => {
455565
"(0) SessionStorage.",
456566
"(0) LocalStorage."
457567
]);
458-
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted 1 shown storage entry\./);
568+
await expect(page.locator("#statusLog")).toHaveValue(/OK Deleted 4 shown storage entries\./);
459569
await page.locator("#deleteAllSessionInspectorV2Button").click();
460570
await expect(page.locator("#statusLog")).toHaveValue(/WARN Delete All skipped: no matching storage entries are shown\./);
461571
expect(await page.evaluate(() => window.localStorage.getItem("session-inspector-v2-local"))).toBe("local value");

0 commit comments

Comments
 (0)