Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m

## Unreleased

- fix(page-fidelity): **a popup wireframe is scored against its dialog, not the page drawn behind it** (#471). A popup wireframe draws the caller's page behind a scrim (`.popup-stage > .shell-behind + .dialog-scrim > .dialog`); `page-fidelity.js` looked for `div.main` first and landed in the shell, so a popup whose fields and bindings all matched scored 29% in the field, every miss being the caller's page head, h1 and card. With popup evidence (a `.popup-stage` or `.dialog-scrim` in the wireframe, or a page whose MDL names a popup layout) the scorer now scores the `.dialog` only (class word, so `dialog-head` does not count), or the stage minus `.shell-behind` when there is no `.dialog`. Without that evidence nothing changes, so a full-page wireframe that draws an inline `.dialog` confirm (the design system's own component) scores exactly as before, and a bare-`.dialog` popup wireframe still reads `<body>`. `test-page-fidelity-mocks.sh` gains the popup cases on a verbatim v0.23.0 describe of a real popup page and a wireframe drawn in the issue's shape, plus layout-only evidence and the full-page-plus-inline-confirm regression case: 18% and 8 FAIL on master, 100% and 78/78 with the fix. — jopterhorst
- fix(progress-report): **the build plan is shown once: the phase cards are the plan.** Expanding a card now shows its full rows (#, Kind, Step, Produces/Proves, Depends on, State, Scripts) and Claims, which only the duplicate "Full plan tables" block carried; that block is gone and views A (script folders) and B (module proof) are their own section. The `now` phase renders open with a selected style, opening another phase closes the previous one (accordion; `#phase-N` links still open their card), and a viewer's saved open/closed state survives the 30 s reload and wins over the default (#474) — field feedback from a requirements-driven project at Stage 5 — MendixMau (field feedback 2026-10-20)

## v2026.10.19
Expand Down
41 changes: 33 additions & 8 deletions project-bin/page-fidelity.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ function innerBalanced(html, openRe) {
return html.slice(open, end);
}

function contentOf(html) {
function contentOf(html, popupLayout) {
// Boundary preference, most-specific first:
// <main> ToeicBuddy shape — content-only wireframes
// div.main the topbar portal full-page shape — the wireframe mocks the WHOLE app
Expand All @@ -161,10 +161,34 @@ function contentOf(html) {
// 06/07 .mockup-frame. Measured, not assumed — every one of them
// fell through to <body> before this list existed.
// div.content same shape when the wireframe has no .main wrapper
// <body> popup/dialog wireframes with annotation siblings
// then strip annotation chrome either way.
const m = html.match(/<main[^>]*>([\s\S]*?)<\/main>/i);
let s = m ? m[1]
// <body> popup/dialog wireframes with annotation siblings — a bare `.dialog`
// popup with no stage and no scrim still lands here, as it always did
// then strip annotation chrome either way. Ahead of the whole list, only with popup
// evidence: the dialog itself (see POPUP below).
//
// POPUP, ONLY WITH POPUP EVIDENCE (#471). A popup wireframe draws the CALLER's page behind a
// scrim for context: `.popup-stage > .shell-behind + .dialog-scrim > .dialog`. The shell
// behind carries its own div.main, so the list above landed on the caller's page and scored
// the popup against it: 29% in the field for a popup whose fields and bindings all matched,
// every miss in the shell. So when the wireframe is a popup, the dialog is the page.
//
// But `.dialog` alone is not evidence: it is the toolkit's own design-system component, and
// a full-page wireframe may draw an inline confirm with it. Switching on any `.dialog` scored
// a grouped overview plus a confirm as the confirm alone (39% on master, 5% on the first cut
// of this fix; review of #482). Evidence is a `.popup-stage` or `.dialog-scrim` in the
// wireframe, or a page whose MDL names a popup layout (`Layout: …Popup…`, the same test the
// chrome set uses). Matched on the class WORD, so `dialog-head`/`dialog-foot` never count.
const cls = name => new RegExp(`<(div|section|dialog|aside)\\b[^>]*class="(?:[^"]*\\s)?${name}(?:\\s[^"]*)?"`, 'i');
const popupEvidence = !!popupLayout || cls('popup-stage').test(html) || cls('dialog-scrim').test(html);
const popup = !popupEvidence ? null
: innerBalanced(html, cls('dialog'))
|| (() => { // a stage with no .dialog: everything but the shell
const stage = innerBalanced(html, cls('popup-stage'));
return stage === null ? null : dropBalanced(stage, cls('shell-behind'));
})();
const m = popup === null ? html.match(/<main[^>]*>([\s\S]*?)<\/main>/i) : null;
let s = popup !== null ? popup
: m ? m[1]
: innerBalanced(html, /<(div)[^>]*class="[^"]*\bwf-screen\b[^"]*"/i)
|| innerBalanced(html, /<(div)[^>]*class="[^"]*\bwf-shell\b[^"]*"/i)
|| innerBalanced(html, /<(div)[^>]*class="[^"]*\bmockup-frame\b[^"]*"/i)
Expand Down Expand Up @@ -318,7 +342,7 @@ function bindRows(html) {
return rows;
}

function wfFacts(html) {
function wfFacts(html, popupLayout) {
// HTML COMMENTS ARE NOT PAGE. contentOf() picks its boundary with a regex, and a template
// whose header comment says "put the screen inside <main> … the bind table goes AFTER
// </main>" handed it the comment text as the page. Field case, 2026-10-02, two builds
Expand All @@ -327,7 +351,7 @@ function wfFacts(html) {
// neither describing the page. Strip comments before anything reads the document.
html = html.replace(/<!--[\s\S]*?-->/g, ' ');
const mock = localMockClasses(html);
let main = contentOf(html);
let main = contentOf(html, popupLayout);
const mockUsed = [];
const structural = [];
for (const c of mock) {
Expand Down Expand Up @@ -739,9 +763,10 @@ function score(wf, mdl, chromeExtra) {
return { pct: den ? Math.round(100 * num / den) : null, h, b, k, c, bd };
}

const wf = wfFacts(readWireframe());
const ownMdl = pageMdl();
if (!ownMdl.trim()) { console.error('page-fidelity: no declaration of page "' + PAGE + '" found in input'); process.exit(2); }
// Read first: a popup layout is evidence that a `.dialog` in the wireframe is the page (#471).
const wf = wfFacts(readWireframe(), /Layout:\s*[^,)\n]*Popup/i.test(ownMdl));
const snips = expandSnippets(ownMdl);
const mdl = ownMdl + snips.extra;
const lay = layoutChrome(SRCS, ownMdl);
Expand Down
19 changes: 19 additions & 0 deletions tests/wave2/fixtures/page-fidelity-popup/CAPTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# page-fidelity-popup — a popup wireframe and the popup page it draws (#471)

`product-newedit-describe-v023.mdl` is **verbatim** `mxcli describe page Sales.Product_NewEdit`
output (mxcli v0.23.0, Mendix 11.14.0, captured 2026-10-05), copied unchanged from
`tests/wave2/fixtures/context-pack/capture/describe/PAGE__Sales.Product_NewEdit.out`. It is a
real popup page: `Layout: Atlas_Core.PopupLayout`, a form grid, Cancel and Save in a footer.

`product-newedit-popup.html` is **hand-drawn**, because issue #471 quotes the shape of the field
wireframe, not the file: `.popup-stage > .shell-behind + .dialog-scrim > .dialog`. The shell
behind holds the caller's page as such wireframes draw it (a sidebar, a `div.main` with a page
head, its `<h1>` and a card), and the dialog holds the popup page's own title, fields, help
texts and buttons, taken from the describe above. The `wf-bar` and `wf-note` annotation
siblings are the toolkit's own convention.

On master the scorer chose `div.main` inside `.shell-behind` and printed 18%, every miss from
the shell (the issue measured 29% on the field popup and 13% on a throwaway one, the same way).
With the fix it scores the dialog: 100%.

Re-capture the wireframe from a real popup wireframe when one can be shared.
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
create or modify page Sales.Product_NewEdit (
Title: 'Catalog item',
Layout: Atlas_Core.PopupLayout,
Folder: 'Catalog/Pages',
Params: { $Product: Sales.Product }
) {
layoutgrid g {
row row1 {
column col1 (DesktopWidth: AutoFill) {
dataview dv (DataSource: $Product, ReadOnlyStyle: Control) {
-- Context: $currentObject (Product)
container formGrid (Class: 'form-grid') {
container fgSku (Class: 'form-group') {
textbox tbSku (Label: 'SKU *', Attribute: Sku)
dynamictext helpSku (
Content: 'Set when the item is created. It cannot change afterwards.',
Class: 'form-help'
)
}
container fgName (Class: 'form-group') {
textbox tbName (Label: 'Name *', Attribute: Name)
}
container fgDescription (Class: 'form-group span-2') {
textarea taDescription (Label: 'Description', Attribute: Description)
}
container fgCategory (Class: 'form-group') {
textbox tbCategory (Label: 'Category', Attribute: Category)
}
container fgPrice (Class: 'form-group') {
textbox tbPrice (Label: 'Unit price *', Attribute: UnitPrice)
}
container fgBrand (Class: 'form-group') {
combobox cbBrand (
Label: 'Brand *',
Attribute: Product_Brand,
DataSource: database from Sales.Brand,
CaptionAttribute: Name
)
}
container fgActive (Class: 'form-group') {
checkbox cbActive (Label: 'Active', Attribute: Active)
dynamictext helpActive (
Content: 'Inactive items are not offered on new sales orders.',
Class: 'form-help'
)
}
}
footer footer1 {
actionbutton btnCancel (Caption: 'Cancel', Action: cancel_changes close_page, Class: 'btn')
actionbutton btnSave (
Caption: 'Save',
Action: microflow Sales.ACT_Product_Save(Product: $Product),
ButtonStyle: Primary,
Class: 'btn btn-primary'
)
}
}
}
}
}
}

grant view on page Sales.Product_NewEdit to Sales.Admin, Sales.SalesOfficer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Wireframe: Sales.Product_NewEdit (popup)</title>
<link rel="stylesheet" href="../ds.css">
</head>
<body>
<div class="wf-bar">Wireframe · Sales.Product_NewEdit · popup over Product_Overview</div>
<div class="popup-stage">
<div class="shell-behind">
<div class="sidebar"><div class="brand">Catalog</div></div>
<div class="main">
<div class="page-head">
<h1>Product catalog</h1>
<p class="sub">Every item offered on sales orders.</p>
<div class="page-actions"><button class="btn btn-primary">New item</button></div>
</div>
<div class="card">
<h2>Active items</h2>
<p class="card-sub">Filtered by category and brand.</p>
</div>
</div>
</div>
<div class="dialog-scrim">
<div class="dialog" role="dialog" aria-label="Catalog item">
<div class="dialog-head"><h2>Catalog item</h2></div>
<div class="form-grid">
<div class="form-group"><label>SKU *</label><input>
<p class="form-help">Set when the item is created. It cannot change afterwards.</p></div>
<div class="form-group"><label>Name *</label><input></div>
<div class="form-group span-2"><label>Description</label><textarea></textarea></div>
<div class="form-group"><label>Category</label><input></div>
<div class="form-group"><label>Unit price *</label><input></div>
<div class="form-group"><label>Brand *</label><select></select></div>
<div class="form-group"><label>Active</label><input type="checkbox">
<p class="form-help">Inactive items are not offered on new sales orders.</p></div>
</div>
<div class="dialog-foot">
<button class="btn">Cancel</button>
<button class="btn btn-primary">Save</button>
</div>
</div>
</div>
</div>
<div class="wf-note">The overview behind the scrim is the caller's page, drawn for context only.</div>
</body>
</html>
34 changes: 34 additions & 0 deletions tests/wave2/test-page-fidelity-mocks.sh
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,40 @@ hasnt "no layout hint when no missed class looks like chrome" "$OUT19" "is not i
hasnt "no layout hint for page classes (seg-tabs, dg-*)" "$OUT6" "is not in the input"
has "the hint names the layout when region-* is missed" "$OUT16" "layout LayoutTest.App_Default is not in the input"

echo " -- a popup wireframe scores the dialog, not the caller's page behind it (#471)"
# fixtures/page-fidelity-popup/CAPTURE.md: the MDL is a verbatim mxcli v0.23.0 describe of a
# popup page; the wireframe draws it in the issue's shape, .popup-stage > .shell-behind +
# .dialog-scrim > .dialog, with the caller's overview (div.main, h1, card) behind the scrim.
PF="$TOOLKIT/tests/wave2/fixtures/page-fidelity-popup"
OUT20=$(cd "$TMP" && node "$SUT" --no-log "$PF/product-newedit-popup.html" Product_NewEdit "$PF/product-newedit-describe-v023.mdl" 2>&1)
echo "$OUT20" | sed 's/^/ | /'
hasnt "the caller's h1 behind the scrim is not scored" "$OUT20" "heading: Product catalog"
hasnt "the caller's card behind the scrim is not scored" "$OUT20" "heading: Active items"
hasnt "the caller's button is not an owed action" "$OUT20" "action: New item"
has "the dialog's fields and buttons all match" "$OUT20" "text-match 100%"
sed 's/class="dialog" /class="sheet" /' "$PF/product-newedit-popup.html" > "$TMP/stage-only.html"
OUT21=$(cd "$TMP" && node "$SUT" --no-log "$TMP/stage-only.html" Product_NewEdit "$PF/product-newedit-describe-v023.mdl" 2>&1)
hasnt "a .popup-stage with no .dialog still drops the shell" "$OUT21" "heading: Product catalog"
has "...and still scores what is in front of it" "$OUT21" "headings 1/1"
sed 's/class="dialog" /class="panel" /; s/class="popup-stage"/class="stage"/' "$PF/product-newedit-popup.html" > "$TMP/no-popup.html"
OUT22=$(cd "$TMP" && node "$SUT" --no-log "$TMP/no-popup.html" Product_NewEdit "$PF/product-newedit-describe-v023.mdl" 2>&1)
has "popup evidence but no .dialog and no stage: the old div.main boundary, as before" "$OUT22" "heading: Product catalog"
# Popup evidence from the page alone: no stage, no scrim, but the MDL names Atlas_Core.PopupLayout.
sed 's/class="popup-stage"/class="stage"/; s/class="dialog-scrim"/class="scrim"/' "$PF/product-newedit-popup.html" > "$TMP/layout-only.html"
OUT23=$(cd "$TMP" && node "$SUT" --no-log "$TMP/layout-only.html" Product_NewEdit "$PF/product-newedit-describe-v023.mdl" 2>&1)
hasnt "a popup layout in the MDL is evidence enough: the shell is not scored" "$OUT23" "heading: Product catalog"
has "...and the dialog is" "$OUT23" "text-match 100%"
echo " -- a full-page wireframe with an inline .dialog scores as before (review of #482)"
# .dialog is the design system's component; a full page may draw an open confirm with it. On
# master this wireframe scored 39% (the confirm counted as page content); the first cut of #471
# scored the confirm alone, 5%. No stage, no scrim, a full-page layout: no popup evidence.
sed 's#<div class="wf-screen">#&<div class="dialog" role="dialog"><div class="dialog-head"><h2>Delete project?</h2></div><p>This cannot be undone.</p><div class="dialog-foot"><button class="btn">Cancel</button><button class="btn btn-danger">Delete</button></div></div>#' \
"$FIX/grouped-overview.html" > "$TMP/inline-dialog.html"
grep -q 'Delete project' "$TMP/inline-dialog.html" || bad "inline-dialog setup: the sed did not insert the confirm" ""
OUT24=$(cd "$TMP" && node "$SUT" --no-log "$TMP/inline-dialog.html" Demo_Overview "$FIX/demo-overview.mdl" 2>&1)
echo "$OUT24" | sed 's/^/ | /'
has "an inline confirm does not become the boundary: the master score, unchanged" "$OUT24" "text-match 39% headings 1/2 actions 1/3 content 1/2 classes 1/5"

echo
printf 'test-page-fidelity-mocks: %d passed, %d failed\n' "$PASS" "$FAIL"
[ "$FAIL" -eq 0 ]
Loading