fix: guard workbench wiring layout

This commit is contained in:
Code Queue Review
2026-05-23 14:05:18 +00:00
parent 63274cd2ef
commit 02ae48835b
3 changed files with 83 additions and 6 deletions
+65 -5
View File
@@ -1078,9 +1078,21 @@ export async function runDevCloudWorkbenchLayoutSmoke(args = {}) {
{
id: "layout-issue-287-future-hardware-status-tabs",
status: allViewportCoverage(viewportResults, "issue287HardwareTabsReady") ? "pass" : "skip",
summary: "#287 hardware status tab set is not required for this #273 smoke until the wider live hardware workspace is implemented; current visible right-side containers are still checked for overflow.",
summary: "#287 hardware status tabs are current layout contract: desktop and 390x844 mobile viewports must expose center-hit tabs without horizontal overflow.",
observations: summarizeCoverageGap(viewportResults, "issue287")
},
{
id: "layout-hardware-tabs-center-hit",
status: allViewportCoverage(viewportResults, "hardwareTabsCenterHit") ? "pass" : "blocked",
summary: "Hardware status tabs are center-hit reachable in desktop, narrow desktop, and 390x844 mobile layout checks.",
observations: summarizeCoverageGap(viewportResults, "hardwareTabs")
},
{
id: "layout-wiring-readable-two-column",
status: allViewportCoverage(viewportResults, "wiringReadableTwoColumn") ? "pass" : "blocked",
summary: "Patch-panel wiring stays a two-column long table with DO1/DI1 text readable, non-overlapping, and horizontally contained.",
observations: summarizeCoverageGap(viewportResults, "wiring")
},
{
id: "layout-issue-288-future-single-table-gate",
status: allViewportCoverage(viewportResults, "issue288SingleTableReady") ? "pass" : "skip",
@@ -5678,6 +5690,25 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
const wiringRows = [...document.querySelectorAll("#wiring-body tr")].map((row) =>
[...row.querySelectorAll("td")].map((cell) => cell.textContent?.replace(/\s+/gu, " ").trim() ?? "")
);
const wiringCellGeometry = [...document.querySelectorAll(".wiring-long-table th, .wiring-long-table td, #wiring-summary")].map((element) => {
const box = element.getBoundingClientRect();
const style = getComputedStyle(element);
return {
label: element.id ? `#${element.id}` : element.tagName.toLowerCase(),
text: element.textContent?.replace(/\s+/gu, " ").trim() ?? "",
box: { left: box.left, top: box.top, right: box.right, bottom: box.bottom, width: box.width, height: box.height },
scrollWidth: element.scrollWidth,
clientWidth: element.clientWidth,
overflowX: style.overflowX,
wordBreak: style.wordBreak,
overflowWrap: style.overflowWrap
};
});
const wiringCellBoxes = wiringCellGeometry.map((item) => item.box).filter(Boolean);
const wiringTextContained = wiringCellGeometry.every((item) => item.scrollWidth <= item.clientWidth + 2);
const wiringTextNonOverlapping = wiringCellBoxes.every((box, index) =>
wiringCellBoxes.every((other, otherIndex) => index === otherIndex || !boxesOverlap(box, other, -1))
);
const wiringLegacyHeaderHits = wiringHeaders.filter((header) => wiringLegacyHeaders.includes(header));
const wiringHorizontalScroll = {
panelClientWidth: wiringPanel?.clientWidth ?? 0,
@@ -5730,6 +5761,17 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
wiringHorizontalScroll
});
}
if (!wiringTextContained || !wiringTextNonOverlapping) {
failures.push({
failureType: wiringTextContained ? "overlap" : "overflow",
selector: "#panel-wiring .wiring-long-table",
viewport,
summary: "#276 wiring panel text is clipped or overlaps in the two-column table/summary.",
wiringCellGeometry,
wiringTextContained,
wiringTextNonOverlapping
});
}
const controlTab = document.querySelector("#tab-control");
if (controlTab?.getAttribute("aria-selected") !== "true") {
controlTab?.click();
@@ -5884,6 +5926,9 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
const issue287HardwareTabsReady = ["总览", "Gateway-SIMU", "BOX-SIMU-1", "BOX-SIMU-2", "Patch Panel"].every((label) =>
Boolean([...document.querySelectorAll(".hardware-status button, .hardware-status [role='tab'], .hardware-status .side-tab")].find((item) => item.textContent?.includes(label)))
);
const hardwareTabsCenterHit = keyTargets
.filter((target) => target.selector?.startsWith("[data-hardware-tab="))
.every((target) => target.ok);
if (!issue287HardwareTabsReady) {
skip.push({
failureType: "skip",
@@ -5910,7 +5955,10 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
const passWithWiring =
pass &&
wiringLongTableOk &&
wiringNoHorizontalScroll;
wiringNoHorizontalScroll &&
wiringTextContained &&
wiringTextNonOverlapping &&
hardwareTabsCenterHit;
return {
mode,
viewport,
@@ -5940,14 +5988,19 @@ async function inspectLayoutState(page, { mode, viewport, compareTo }) {
blockedDrawerTargets,
sidePanelScroll,
sidePanelOverflowUsable,
hardwareTabsCenterHit,
wiring: {
longTableOk: wiringLongTableOk,
noHorizontalScroll: wiringNoHorizontalScroll,
textContained: wiringTextContained,
textNonOverlapping: wiringTextNonOverlapping,
headers: wiringHeaders,
legacyHeaderHits: wiringLegacyHeaderHits,
rows: wiringRows,
horizontalScroll: wiringHorizontalScroll
horizontalScroll: wiringHorizontalScroll,
cellGeometry: wiringCellGeometry
},
wiringReadableTwoColumn: wiringLongTableOk && wiringNoHorizontalScroll && wiringTextContained && wiringTextNonOverlapping,
semanticOverlapChecks,
overflowChecks,
defaultCollapsedMobile,
@@ -6218,7 +6271,12 @@ function allViewportCoverage(viewportResults, fieldName) {
}
function summarizeCoverageGap(viewportResults, issueKey) {
const fieldName = issueKey === "issue288" ? "issue288SingleTableReady" : "issue287HardwareTabsReady";
const fieldName = {
issue288: "issue288SingleTableReady",
issue287: "issue287HardwareTabsReady",
hardwareTabs: "hardwareTabsCenterHit",
wiring: "wiringReadableTwoColumn"
}[issueKey] ?? "issue287HardwareTabsReady";
return {
status: allViewportCoverage(viewportResults, fieldName) ? "pass" : "skip",
coverage: Object.fromEntries(
@@ -6234,7 +6292,9 @@ function summarizeCoverageGap(viewportResults, issueKey) {
...(result.gate?.currentRoute?.skip ?? [])
].filter((item) => issueKey === "issue288"
? item.summary?.includes("#288")
: item.summary?.includes("#287"))
: issueKey === "issue287"
? item.summary?.includes("#287")
: false)
}
])
)
+10
View File
@@ -144,6 +144,16 @@ assert.equal(
"pass",
"#287 hardware status tabs must remain covered by layout smoke"
);
assert.equal(
layoutSmoke.checks.find((check) => check.id === "layout-hardware-tabs-center-hit")?.status,
"pass",
"hardware status tabs must be center-hit reachable on desktop and 390x844 mobile"
);
assert.equal(
layoutSmoke.checks.find((check) => check.id === "layout-wiring-readable-two-column")?.status,
"pass",
"#276 wiring table must stay readable, non-overlapping, and horizontally contained"
);
assert.equal(
layoutSmoke.checks.find((check) => check.id === "layout-issue-288-future-single-table-gate")?.status,
"skip",
+8 -1
View File
@@ -1032,6 +1032,11 @@ h3 {
grid-template-rows: auto auto minmax(0, 1fr);
}
#panel-wiring {
grid-template-rows: auto auto auto;
align-content: start;
}
.m3-control-form {
min-width: 0;
display: grid;
@@ -1188,8 +1193,10 @@ table {
.wiring-table-wrap {
width: 100%;
min-width: 0;
min-height: 128px;
max-height: min(220px, 42dvh);
overflow-x: hidden;
overflow-y: visible;
overflow-y: auto;
}
.wiring-long-table {