You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2026/08/31 13:02:42 UTC

(juneau) branch master updated: Add third BarSlot host: dialog ModalDef.barSlot anchored at the dialog title, with dynamic sidecar id-minting and enhance-on-insert (reusing the row-detail host's existing functions); adds ANCHOR_DIALOG_TITLE and an examples demonstration. 37 new tests, all green.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 4b95d4f7fd Add third BarSlot host: dialog ModalDef.barSlot anchored at the dialog title, with dynamic sidecar id-minting and enhance-on-insert (reusing the row-detail host's existing functions); adds ANCHOR_DIALOG_TITLE and an examples demonstration. 37 new tests, all green.
4b95d4f7fd is described below

commit 4b95d4f7fd1cbb3ced5895fe94bd86eafecd8396
Author: James Bognar <ja...@apache.org>
AuthorDate: Mon Aug 31 09:02:35 2026 -0400

    Add third BarSlot host: dialog ModalDef.barSlot anchored at the dialog title, with dynamic sidecar id-minting and enhance-on-insert (reusing the row-detail host's existing functions); adds ANCHOR_DIALOG_TITLE and an examples demonstration. 37 new tests, all green.
---
 .../juneau/examples/views/ExampleViewsRest.java    |  25 +
 .../examples/views/ExampleViewsEndToEnd_Test.java  |  21 +
 .../juneau/rest/server/views/BarSlotTable.java     |  37 +-
 .../org/apache/juneau/views/juneau-views.css       |  10 +
 .../org/apache/juneau/views/juneau-views.js        | 132 ++++++
 .../views/BarSlotTable_DialogAnchor_Test.java      |  82 ++++
 .../server/views/ViewsJs_Dialog_BarSlot_Test.java  | 303 ++++++++++++
 .../src/test/js/dialog-bar-slot.cjs                | 511 +++++++++++++++++++++
 .../juneau/rest/server/widgets/ModalDef.java       |  40 +-
 .../rest/server/widgets/ModalDef_BarSlot_Test.java | 132 ++++++
 10 files changed, 1283 insertions(+), 10 deletions(-)

diff --git a/juneau-examples/juneau-examples-views/src/main/java/org/apache/juneau/examples/views/ExampleViewsRest.java b/juneau-examples/juneau-examples-views/src/main/java/org/apache/juneau/examples/views/ExampleViewsRest.java
index d6fb96b9a2..a4db4af1df 100644
--- a/juneau-examples/juneau-examples-views/src/main/java/org/apache/juneau/examples/views/ExampleViewsRest.java
+++ b/juneau-examples/juneau-examples-views/src/main/java/org/apache/juneau/examples/views/ExampleViewsRest.java
@@ -738,6 +738,13 @@ public class ExampleViewsRest extends BasicRestServlet {
 	 * The serving-path {@link ModalDef#checked() checked()} hook stamps the contract version and fail-closed validates
 	 * the modal/form &mdash; a malformed form fails here at serve time, not silently on the wire.
 	 *
+	 * <p>
+	 * This is also the toolkit's example of the <b>third</b> named {@link BarSlot} host, {@link ModalDef#barSlot}:
+	 * a static {@link BarText} for severity context beside a {@link BarBadge} whose count
+	 * ({@link #countOtherOpen(Alert)}) is live, painted client-side from this same JSON by
+	 * {@code insertDialogBarSlot} the moment the dialog opens &mdash; unlike {@link PageDef#barSlot}/
+	 * {@link RowDetailDef#barSlot}, there is no server-rendered pass to ride into.
+	 *
 	 * @param id The alert id.
 	 * @return The validated, version-stamped modal definition.
 	 */
@@ -756,10 +763,28 @@ public class ExampleViewsRest extends BasicRestServlet {
 				.field(FormDef.Input.of(COL_SEVERITY, "Re-assign severity", "select")
 					.option(SEVERITY_CRITICAL, "Critical").option("warning", "Warning").option(SEVERITY_INFO, "Info").value(a.severity))
 				.field(FormDef.Input.of("escalate", "Escalate instead…", INPUT_TYPE_ACTION).action(ActionRef.of(ACTION_ESC))))
+			.barSlot(BarSlot.create("ack-form-bar").widgets(
+				BarText.of("severity-note", "Severity: " + a.severity),
+				BarBadge.of("other-open").label("other open at this severity").badge(Badge.count(countOtherOpen(a))
+					.tone(SEVERITY_CRITICAL.equals(a.severity) ? Tone.DANGER : Tone.WARN))))
 			.idempotencyKey(key.value())
 			.checked();
 	}
 
+	/**
+	 * Counts other {@link #STATUS_OPEN} alerts sharing {@code a}'s severity, for the {@link #ackForm(String)}
+	 * dialog's live {@link BarBadge} count.
+	 *
+	 * @param a The alert being acknowledged (excluded from its own count).
+	 * @return The number of other open alerts at the same severity.
+	 */
+	private static int countOtherOpen(Alert a) {
+		var n = 0;
+		for (var o : ALERTS)
+			if (o != a && STATUS_OPEN.equals(o.status) && a.severity.equals(o.severity)) n++;
+		return n;
+	}
+
 	/**
 	 * [POST /data/alerts/{id}/ack] &mdash; acknowledge an open alert.
 	 *
diff --git a/juneau-examples/juneau-examples-views/src/test/java/org/apache/juneau/examples/views/ExampleViewsEndToEnd_Test.java b/juneau-examples/juneau-examples-views/src/test/java/org/apache/juneau/examples/views/ExampleViewsEndToEnd_Test.java
index 7d3411879f..47a52948a1 100644
--- a/juneau-examples/juneau-examples-views/src/test/java/org/apache/juneau/examples/views/ExampleViewsEndToEnd_Test.java
+++ b/juneau-examples/juneau-examples-views/src/test/java/org/apache/juneau/examples/views/ExampleViewsEndToEnd_Test.java
@@ -311,4 +311,25 @@ class ExampleViewsEndToEnd_Test extends TestBase {
 		assertTrue(body.contains("\"actionId\":\"esc\""), body);
 		assertTrue(body.contains("\"idempotencyKey\""), body);
 	}
+
+	@Test
+	void d07_ackForm_barSlotHost_ridesTheWireAsAnAdditiveField() throws Exception {
+		// The example's ackForm() dialog also declares ModalDef.barSlot - the THIRD named BarSlot host.  Unlike
+		// PageDef/RowDetailDef (Java-only, server-rendered), a dialog is a fetched JSON payload, so the bean must
+		// actually be present on the wire for juneau-views.js to paint it client-side.  BarSlot carries its OWN
+		// independently-frozen contract version (never bumped alongside ModalDef's), so this is a distinct stamp
+		// from the modal/form one already asserted in d06.
+		var res = getJson("/data/alerts/ALRT-2/ack-form");
+		assertEquals(200, res.statusCode(), res.body());
+		var body = res.body();
+		assertTrue(body.contains("\"barSlot\":{"), body);
+		assertTrue(body.contains("\"contractVersion\":\"" + BarSlot.CONTRACT_VERSION + "\""), body);
+		assertTrue(body.contains("\"id\":\"ack-form-bar\""), body);
+		// ALRT-2 (severities cycle critical/warning/info) is "info" severity, so the widget mirrors that AND the
+		// non-critical (WARN, not DANGER) tone - proving the tone/text are actually DERIVED from the alert, not
+		// a copy-pasted literal.
+		assertTrue(body.contains("\"text\":\"Severity: info\""), body);
+		assertTrue(body.contains("\"label\":\"other open at this severity\""), body);
+		assertTrue(body.contains("\"tone\":\"WARN\""), body);
+	}
 }
diff --git a/juneau-rest/juneau-rest-server-views/src/main/java/org/apache/juneau/rest/server/views/BarSlotTable.java b/juneau-rest/juneau-rest-server-views/src/main/java/org/apache/juneau/rest/server/views/BarSlotTable.java
index 637b456ddb..d3b0d14af7 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/java/org/apache/juneau/rest/server/views/BarSlotTable.java
+++ b/juneau-rest/juneau-rest-server-views/src/main/java/org/apache/juneau/rest/server/views/BarSlotTable.java
@@ -31,14 +31,21 @@ import org.apache.juneau.rest.server.widgets.*;
  * {@code data-juneau-bar-slot} region {@link PageTable} emits as a <b>trailing sibling of {@code .jc-subtab-bar}</b>
  * (concept #9), plus a data-only sidecar for its dynamic counts.
  *
- * <h5 class='section'>Two named hosts, one emitter:</h5>
+ * <h5 class='section'>Three named hosts, one emitter:</h5>
  * <p>
  * {@link PageDef#barSlot} is the page host: {@link #of(BarSlot)} + {@link #sidecar(BarSlot)}, emitted once per page
  * with a document-unique sidecar {@code id}.  {@link RowDetailDef#barSlot} is the row-detail host:
- * {@link #detailRegion(BarSlot, String)} + {@link #detailSidecar(BarSlot)}, emitted into the row-expand
- * {@code <template>} and therefore cloned per expanded row &mdash; so its sidecar ships {@code id}-less and the
- * runtime mints a row-qualified identity after cloning.  Sharing the bean and the emitter is the point; the hosts and
- * their placements stay distinct.
+ * {@link #detailRegion(BarSlot, String)} with {@link #ANCHOR_RIBBON} or {@link #ANCHOR_SECTION_TITLE} +
+ * {@link #detailSidecar(BarSlot)}, emitted into the row-expand {@code <template>} and therefore cloned per expanded
+ * row &mdash; so its sidecar ships {@code id}-less and the runtime mints a row-qualified identity after cloning. The
+ * <i>third</i> host is a dialog's {@code ModalDef.barSlot} (in {@code juneau-rest-server-widgets}): unlike the other
+ * two, that field travels the wire (the modal itself is a fetched payload with no server-rendered pass to ride into),
+ * so this emitter's {@link #detailRegion(BarSlot, String)} with {@link #ANCHOR_DIALOG_TITLE} +
+ * {@link #detailSidecar(BarSlot)} shape is instead mirrored client-side by the {@code juneau-views.js} runtime,
+ * which paints the region from the fetched JSON and reuses the SAME {@code id}-less clone-time id minting the
+ * row-detail host uses (there is no per-dialog {@code <template>} to clone from server-rendered markup, but two
+ * stacked dialogs need the same per-instance identity a cloned row does).  Sharing the bean and this shape is the
+ * point; the hosts and their placements stay distinct.
  *
  * <p>
  * The bar beans live in {@code juneau-rest-server-widgets}; this emitter is the only place that turns them into markup.
@@ -80,6 +87,16 @@ public class BarSlotTable {
 	 */
 	public static final String ANCHOR_SECTION_TITLE = "section-title";
 
+	/**
+	 * Anchor for a dialog's {@code ModalDef.barSlot}: the region is placed as the immediate next sibling of the
+	 * dialog's {@code h2.juneau-view-dialog-title}.  Unlike {@link #ANCHOR_RIBBON} / {@link #ANCHOR_SECTION_TITLE},
+	 * no server-rendered markup ever carries this anchor value &mdash; the dialog is a fetched JSON payload, so the
+	 * {@code juneau-views.js} runtime paints the region (and stamps this same anchor attribute onto it) client-side,
+	 * mirroring {@link #detailRegion(BarSlot, String)}'s shape.  The dialog owns that placement itself, exactly as
+	 * the row-detail host owns its own ribbon-trailing relocation; the shared strip builder never learns of it.
+	 */
+	public static final String ANCHOR_DIALOG_TITLE = "dialog-title";
+
 	/** Class added to a detail-hosted region, so CSS and the runtime address it by name rather than by position. */
 	public static final String DETAIL_SLOT_CLASS = "juneau-view-detail-bar-slot";
 
@@ -114,16 +131,18 @@ public class BarSlotTable {
 	}
 
 	/**
-	 * Builds the {@code data-juneau-bar-slot} region for a bar slot hosted on the <b>row-detail ribbon</b>.
+	 * Builds the {@code data-juneau-bar-slot} region for a bar slot hosted on the <b>row-detail ribbon</b> (or, for
+	 * the {@link #ANCHOR_DIALOG_TITLE} shape a dialog's client-side painting mirrors, a dialog title).
 	 *
 	 * <p>
 	 * Same region as {@link #of(BarSlot)}, plus {@link #DETAIL_SLOT_CLASS} and the declared
 	 * {@link #BAR_SLOT_ANCHOR_ATTR}.  The marker still carries the <b>author</b> {@link BarSlot#id}; the runtime
-	 * rewrites it to a row-qualified suffix once the enclosing {@code <template>} has been cloned, since one document
-	 * can hold one such region per expanded row.
+	 * rewrites it to an instance-qualified suffix once the enclosing {@code <template>} has been cloned (row-detail)
+	 * or the dialog has been built (dialog), since one document can hold one such region per expanded row / open
+	 * dialog instance.
 	 *
 	 * @param bar The built bar slot.  Must not be <jk>null</jk>.
-	 * @param anchor {@link #ANCHOR_RIBBON} or {@link #ANCHOR_SECTION_TITLE}.
+	 * @param anchor {@link #ANCHOR_RIBBON}, {@link #ANCHOR_SECTION_TITLE}, or {@link #ANCHOR_DIALOG_TITLE}.
 	 * @return A new {@link Div} carrying the detail-hosted region.
 	 * @throws IllegalArgumentException If {@code bar} is <jk>null</jk> or fails {@link BarSlot#validate()}.
 	 */
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.css b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.css
index c3204923f3..97e6b656c3 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.css
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.css
@@ -1479,6 +1479,16 @@ table.dataTable thead .sorting_desc_disabled:after {
 	font-size: 1.1em;
 }
 
+/*
+ * THIRD bar-slot host: a bar slot anchored to a dialog's title (ModalDef.barSlot), painted client-side from JSON
+ * (juneau-views.js buildDialogBarSlotRegion) rather than relocated like the row-detail host - so only the block
+ * bottom-margin needs tuning here to match the dialog's own vertical rhythm; .jc-bar-slot already supplies the
+ * shared flex/wrap/gap shape.  NEUTRAL - shape only.
+ */
+.juneau-view-dialog-bar-slot {
+	margin: 0 0 0.75em;
+}
+
 .juneau-view-dialog-fields {
 	margin: 0 0 var(--jc-space-4);
 }
diff --git a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
index 0ac43b63ff..37033adcf8 100644
--- a/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
+++ b/juneau-rest/juneau-rest-server-views/src/main/resources/org/apache/juneau/views/juneau-views.js
@@ -3423,6 +3423,128 @@
 			.catch(function () { renderRowActionRefusal(tr, action, "request-failed"); });
 	}
 
+	// THIRD bar-slot host: a dialog's ModalDef.barSlot (BarSlotTable constants of the same names on the server).
+	// Unlike the row-detail host, this one travels the wire as JSON - the modal itself IS the fetched payload, with
+	// no server-rendered pass to ride into - so the dialog caller paints the region + its id-less dynamic-count
+	// sidecar from that JSON, mirroring BarSlotTable.detailRegion(bar, ANCHOR_DIALOG_TITLE) +
+	// BarSlotTable.detailSidecar(bar)'s exact shape, rather than relocating server-painted markup.  Identity minting,
+	// enhance-on-insert, and collapse teardown are NOT reimplemented here: this caller reuses
+	// mintDetailBarSlotIdentity / enhanceChromeInPanel / teardownDetailBarSlot verbatim (see showActionDialog below),
+	// exactly as spec'd - the shared strip builder never learns ANY bar-slot host exists (FINISHED-J0445u/J0445w).
+	const DIALOG_BAR_SLOT_CLASS = "juneau-view-dialog-bar-slot";
+	const DIALOG_BAR_ANCHOR = "dialog-title";
+	const BAR_SLOT_ANCHOR_ATTR = "data-juneau-bar-slot-anchor";
+	const BAR_WIDGET_MARKER = "data-juneau-bar-widget";
+	const JUNEAU_BAR_SLOT_CONTRACT_VERSION = "1";
+	const BAR_BADGE_NS = "bar";
+	const BAR_BADGE_ATTR = "data-juneau-badge";
+	const BAR_BADGE_TONE_ATTR = "data-juneau-badge-tone";
+	const BAR_BADGE_MAX_ATTR = "data-juneau-badge-max";
+
+	/** Clamps a fresh count above `max` to "<max>+", exactly as the server's AppHeaderTable.clampCount does.  Pure. */
+	function clampDialogBarCount(count, max) {
+		if (typeof count !== "number") return "";
+		if (typeof max === "number" && count > max) return max + "+";
+		return String(count);
+	}
+
+	/** True for a BarText-shaped widget: duck-typed on `text` presence, since a BarBadge JSON never carries one. */
+	function isDialogBarTextWidget(w) {
+		return !! w && typeof w.text === "string";
+	}
+
+	/** Paints one BarBadge's count/tone overlay from JSON, mirroring BarSlotTable's private emitBadge(). */
+	function buildDialogBarBadgeEl(id, badge) {
+		const span = document.createElement("span");
+		span.className = "jc-badge";
+		span.setAttribute(BAR_BADGE_ATTR, BAR_BADGE_NS + ":" + id);
+		if (badge.tone != null) span.setAttribute(BAR_BADGE_TONE_ATTR, String(badge.tone).toLowerCase());
+		if (badge.dot) {
+			span.className = "jc-badge jc-badge-dot";
+		} else if (typeof badge.count === "number") {
+			const max = typeof badge.max === "number" ? badge.max : null;
+			if (max != null) span.setAttribute(BAR_BADGE_MAX_ATTR, String(max));
+			span.textContent = clampDialogBarCount(badge.count, max);
+		}
+		if (typeof badge.label === "string" && badge.label !== "") span.setAttribute("aria-label", badge.label);
+		return span;
+	}
+
+	/** Paints one BarWidget (BarText or BarBadge) from JSON, mirroring BarSlotTable's private emitWidget(). */
+	function buildDialogBarWidgetEl(w) {
+		if (! w || typeof w.id !== "string" || w.id === "") return null;
+		const span = document.createElement("span");
+		span.setAttribute(BAR_WIDGET_MARKER, w.id);
+		if (isDialogBarTextWidget(w)) {
+			span.className = "jc-bar-text";
+			span.textContent = w.text;
+			return span;
+		}
+		span.className = "jc-bar-badge";
+		if (typeof w.label === "string" && w.label !== "") {
+			const label = document.createElement("span");
+			label.className = "jc-bar-label";
+			label.textContent = w.label;
+			span.appendChild(label);
+		}
+		if (w.badge) span.appendChild(buildDialogBarBadgeEl(w.id, w.badge));
+		return span;
+	}
+
+	/**
+	 * Builds a dialog's bar-slot region + id-less sidecar from the JSON `bar` (a {@code ModalDef.barSlot}), mirroring
+	 * {@code BarSlotTable.detailRegion(bar, BarSlotTable.ANCHOR_DIALOG_TITLE)} +
+	 * {@code BarSlotTable.detailSidecar(bar)}'s exact shape - {@code createElement}/{@code textContent} only, NEVER
+	 * {@code innerHTML} (BLK-1/MED-9: this is fetched, attacker-influenceable data, the same reasoning
+	 * {@code buildDialogOverlay} states for the confirmation fields below).  Returns <jk>null</jk> for a
+	 * missing/malformed/empty bar - a peripheral, additive feature failing closed must never block the confirm/cancel
+	 * dialog itself from opening.
+	 */
+	function buildDialogBarSlotRegion(bar) {
+		if (! bar || typeof bar.id !== "string" || bar.id === "") return null;
+		if (bar.contractVersion !== JUNEAU_BAR_SLOT_CONTRACT_VERSION) return null;
+		if (! Array.isArray(bar.widgets) || ! bar.widgets.length) return null;
+		const region = document.createElement("div");
+		region.className = "jc-bar-slot " + DIALOG_BAR_SLOT_CLASS;
+		region.setAttribute(DETAIL_BAR_MARKER, bar.id);
+		region.setAttribute(BAR_SLOT_ANCHOR_ATTR, DIALOG_BAR_ANCHOR);
+		const badges = {};
+		let any = false;
+		bar.widgets.forEach(function (w) {
+			const el = buildDialogBarWidgetEl(w);
+			if (! el) return;
+			region.appendChild(el);
+			any = true;
+			if (! isDialogBarTextWidget(w) && w.badge && typeof w.badge.count === "number")
+				badges[BAR_BADGE_NS + ":" + w.id] = w.badge.count;
+		});
+		if (! any) return null;
+		const sidecar = document.createElement("script");
+		sidecar.setAttribute("type", "application/json");
+		sidecar.setAttribute(DETAIL_BAR_META, bar.id);
+		sidecar.textContent = JSON.stringify({ contractVersion: bar.contractVersion, badges: badges });
+		return { region: region, sidecar: sidecar };
+	}
+
+	/**
+	 * Inserts a dialog's bar slot as the immediate next sibling of `titleEl` (the {@code ANCHOR_DIALOG_TITLE}
+	 * placement - the dialog OWNS this call site, exactly as the row-detail caller owns its own ribbon-trailing
+	 * relocation; the shared strip builder never learns a dialog bar slot exists), appends its sidecar, and mints its
+	 * clone-time identity via the SAME {@code mintDetailBarSlotIdentity} the row-detail host uses - reused verbatim
+	 * rather than reimplemented.  Two stacked dialogs need the same per-instance identity a cloned detail row does,
+	 * and `dialogSeq` already namespaces other per-dialog state (the form field ids), so it namespaces this too.
+	 *
+	 * @return true when a region was painted, inserted, and minted.
+	 */
+	function insertDialogBarSlot(dialog, titleEl, bar, seq) {
+		const built = buildDialogBarSlotRegion(bar);
+		if (! built) return false;
+		dialog.insertBefore(built.region, titleEl.nextSibling);
+		dialog.appendChild(built.sidecar);
+		mintDetailBarSlotIdentity(dialog, "dialog", seq);
+		return true;
+	}
+
 	/**
 	 * Builds a `present=dialog` overlay DOM node (a backdrop + a `role=dialog` box) from a ModalDef, painting the
 	 * title and each typed confirmation field with `textContent` ONLY - never `innerHTML`, never raw markup, never a
@@ -3444,6 +3566,8 @@
 		title.textContent = (modal?.title) || (action?.confirm || action?.label || action?.id) || "Confirm";
 		dialog.appendChild(title);
 
+		insertDialogBarSlot(dialog, title, modal?.barSlot, seq);
+
 		if (modal?.fields?.length) {
 			const dl = document.createElement("dl");
 			dl.className = "juneau-view-dialog-fields";
@@ -3915,6 +4039,7 @@
 		pushLayer(ui.backdrop, {
 			kind: "dialog", portal: true, trapFocus: true, lightDismiss: false, detachOnPop: true,
 			onDismiss: function () {
+				teardownDetailBarSlot(ui.dialog);   // reused verbatim from the row-detail host - see insertDialogBarSlot
 				if (ctx?._dialogStack) {
 					const i = ctx._dialogStack.indexOf(ui.backdrop);
 					if (i >= 0) ctx._dialogStack.splice(i, 1);
@@ -3922,6 +4047,10 @@
 				}
 			}
 		});
+		// Enhance-on-insert for a dialog-hosted bar slot: MUST run after pushLayer has portalled the dialog into the
+		// document (initAll() scans document-wide), reusing the SAME chrome entry + shared wired marker the
+		// row-detail host's enhanceChromeInPanel already uses - mirroring FINISHED-J0445u.
+		enhanceChromeInPanel(ui.dialog);
 		return ui;
 	}
 
@@ -5021,6 +5150,9 @@
 		renderActionOutcome: renderActionOutcome,
 		openActionDialog: openActionDialog,
 		buildDialogOverlay: buildDialogOverlay,
+		// Dialog-hosted bar slot (the THIRD named host) - exposed for manual verification and the node harness.
+		buildDialogBarSlotRegion: buildDialogBarSlotRegion,
+		insertDialogBarSlot: insertDialogBarSlot,
 		appendDialogForm: appendDialogForm,
 		collectDialogFormFields: collectDialogFormFields,
 		isTypedFormInputType: isTypedFormInputType,
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/BarSlotTable_DialogAnchor_Test.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/BarSlotTable_DialogAnchor_Test.java
new file mode 100644
index 0000000000..e99c5bfde7
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/BarSlotTable_DialogAnchor_Test.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.views;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.apache.juneau.rest.server.widgets.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Pins {@link BarSlotTable#ANCHOR_DIALOG_TITLE} &mdash; the anchor constant for the <b>third</b> named
+ * {@link BarSlot} host, a dialog's {@code ModalDef.barSlot}.
+ *
+ * <p>
+ * Unlike the row-detail host, no server-side code path ever calls {@link BarSlotTable#detailRegion} with this anchor
+ * in production: a dialog is a fetched JSON payload with no server-rendered pass to ride into, so the
+ * {@code juneau-views.js} runtime paints the region client-side instead (pinned separately by the node harness in
+ * {@code ViewsJs_Dialog_BarSlot_Test}).  This class instead proves the SHAPE the runtime must mirror: the existing,
+ * anchor-agnostic {@link BarSlotTable#detailRegion(BarSlot, String)} emitter already produces the correct markup for
+ * this third anchor value with no code change, exactly as it does for {@link BarSlotTable#ANCHOR_RIBBON} and
+ * {@link BarSlotTable#ANCHOR_SECTION_TITLE} &mdash; the "wire the dialog host through {@code of}/{@code
+ * detailRegion}" shape the spec calls for.
+ */
+class BarSlotTable_DialogAnchor_Test extends TestBase {
+
+	private static BarSlot bar() {
+		return BarSlot.create("dialog-ctx").widgets(BarBadge.of("open").label("Open").badge(Badge.count(3)));
+	}
+
+	@Test void a01_anchorConstant_isDialogTitle() {
+		assertEquals("dialog-title", BarSlotTable.ANCHOR_DIALOG_TITLE);
+	}
+
+	@Test void a02_anchorConstant_isDistinctFromTheOtherTwo() {
+		assertNotEquals(BarSlotTable.ANCHOR_DIALOG_TITLE, BarSlotTable.ANCHOR_RIBBON);
+		assertNotEquals(BarSlotTable.ANCHOR_DIALOG_TITLE, BarSlotTable.ANCHOR_SECTION_TITLE);
+	}
+
+	@Test void a03_detailRegion_emitsTheDialogAnchorAttribute() {
+		var h = Html.of(BarSlotTable.detailRegion(bar(), BarSlotTable.ANCHOR_DIALOG_TITLE));
+		assertTrue(h.contains(BarSlotTable.BAR_SLOT_ANCHOR_ATTR + "=\"" + BarSlotTable.ANCHOR_DIALOG_TITLE + "\""), h);
+	}
+
+	@Test void a04_detailRegion_carriesTheAuthorMarkerAndDetailClass() {
+		var h = Html.of(BarSlotTable.detailRegion(bar(), BarSlotTable.ANCHOR_DIALOG_TITLE));
+		assertTrue(h.contains(BarSlotTable.BAR_SLOT_MARKER + "=\"dialog-ctx\""), h);
+		assertTrue(h.contains(BarSlotTable.DETAIL_SLOT_CLASS), h);
+	}
+
+	@Test void a05_detailRegion_paintsTheWidget() {
+		var h = Html.of(BarSlotTable.detailRegion(bar(), BarSlotTable.ANCHOR_DIALOG_TITLE));
+		assertTrue(h.contains("jc-bar-slot"), h);
+		assertTrue(h.contains("jc-bar-badge"), h);
+		assertTrue(h.contains(">Open<"), h);
+	}
+
+	@Test void a06_detailSidecar_isIdLess_andFoundByAttribute_sameAsTheOtherTwoAnchors() {
+		// The dialog runtime mints its own clone-time id from this id-less shape (mintDetailBarSlotIdentity), just
+		// like the row-detail host - so the sidecar's SERVER shape must be identical regardless of anchor.
+		var h = Html.of(BarSlotTable.detailSidecar(bar()));
+		assertTrue(h.contains(BarSlotTable.BAR_META_ATTR + "=\"dialog-ctx\""), h);
+		assertFalse(h.contains("id=\"" + BarSlotTable.SIDECAR_ID_PREFIX), h);
+		assertTrue(h.contains("\"contractVersion\":\"" + BarSlot.CONTRACT_VERSION + "\""), h);
+		assertTrue(h.contains(BarSlotTable.BADGE_NS + ":open"), h);
+	}
+}
diff --git a/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_Dialog_BarSlot_Test.java b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_Dialog_BarSlot_Test.java
new file mode 100644
index 0000000000..4e4e41b24f
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-views/src/test/java/org/apache/juneau/rest/server/views/ViewsJs_Dialog_BarSlot_Test.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.views;
+
+import static java.nio.charset.StandardCharsets.*;
+import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assumptions.*;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.*;
+import java.util.concurrent.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Always-on coverage for the dialog-hosted {@link org.apache.juneau.rest.server.widgets.BarSlot} runtime &mdash; the
+ * <b>third</b> named host, {@code ModalDef.barSlot}: painting the region + widgets + sidecar from the fetched JSON
+ * (there is no server-rendered pass to relocate, unlike the row-detail host), the {@code dialog-title} anchor, and
+ * the REUSE (not reimplementation) of the row-detail host's clone-time id minting, enhance-on-insert, and collapse
+ * teardown.
+ *
+ * <p>
+ * Source-shape always runs; the behavioral {@code dialog-bar-slot.cjs} harness runs when {@code node} is on
+ * {@code PATH} (skipped otherwise &mdash; no {@code -Pjs-tests} required).  That harness loads
+ * {@code juneau-views.js} <b>and</b> {@code juneau-chrome.js} into one sandbox, exactly as
+ * {@code ViewsJs_RowDetail_BarSlot_Test} does for the second host.
+ */
+class ViewsJs_Dialog_BarSlot_Test extends TestBase {
+
+	private static String resource(String path) throws IOException {
+		try (var in = ViewsMixin.class.getResourceAsStream(path)) {
+			assertNotNull(in, () -> "missing classpath resource: " + path);
+			return new String(in.readAllBytes(), UTF_8);
+		}
+	}
+
+	private static String viewsJs() throws IOException {
+		return resource(ViewsMixin.VIEWS_JS_RESOURCE);
+	}
+
+	private static String chromeJs() throws IOException {
+		return resource(ViewsMixin.CHROME_JS_RESOURCE);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Source shape
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_helpersExportedOnNsInit() throws Exception {
+		var body = viewsJs();
+		for (var name : new String[]{
+			"buildDialogBarSlotRegion: buildDialogBarSlotRegion",
+			"insertDialogBarSlot: insertDialogBarSlot"
+		})
+			assertTrue(body.contains(name), () -> "missing export '" + name + "'");
+	}
+
+	@Test void a02_dialogOwnsItsOwnPlacement_notTheSharedStripBuilder() throws Exception {
+		var body = viewsJs();
+		// buildDialogOverlay calls the dialog's own insert helper; the generic strip builders must never learn a
+		// dialog bar slot exists (FINISHED-J0445u/J0445w locks this the same way for the row-detail host).
+		assertTrue(body.contains("insertDialogBarSlot(dialog, title, modal?.barSlot, seq)"), body);
+		var buildRibbonAt = body.indexOf("function buildRibbonStrip(");
+		var buildDetailAt = body.indexOf("function buildDetailStrip(");
+		assertTrue(buildRibbonAt >= 0 && buildDetailAt >= 0, body);
+		var ribbonBody = body.substring(buildRibbonAt, body.indexOf("\n\t}", buildRibbonAt));
+		var detailStripBody = body.substring(buildDetailAt, body.indexOf("\n\t}", buildDetailAt));
+		assertFalse(ribbonBody.contains("Dialog"), "buildRibbonStrip must not learn about dialogs");
+		assertFalse(detailStripBody.contains("insertDialogBarSlot"), "buildDetailStrip must not learn about dialogs");
+	}
+
+	@Test void a03_identityMintingIsReusedVerbatim_notReimplemented() throws Exception {
+		var body = viewsJs();
+		// The dialog caller calls the SAME mintDetailBarSlotIdentity/teardownDetailBarSlot/enhanceChromeInPanel the
+		// row-detail host uses - no dialog-specific mint/teardown/enhance function exists.
+		assertTrue(body.contains("mintDetailBarSlotIdentity(dialog, \"dialog\", seq)"), body);
+		assertTrue(body.contains("teardownDetailBarSlot(ui.dialog)"), body);
+		assertTrue(body.contains("enhanceChromeInPanel(ui.dialog)"), body);
+		assertFalse(body.contains("function mintDialogBarSlotIdentity("), body);
+		assertFalse(body.contains("function teardownDialogBarSlot("), body);
+	}
+
+	@Test void a04_regionPaintedWithCreateElementAndTextContentOnly_neverInnerHtml() throws Exception {
+		var body = viewsJs();
+		var at = body.indexOf("function buildDialogBarSlotRegion(");
+		var widgetAt = body.indexOf("function buildDialogBarWidgetEl(");
+		var badgeAt = body.indexOf("function buildDialogBarBadgeEl(");
+		assertTrue(at >= 0 && widgetAt >= 0 && badgeAt >= 0, body);
+		var end = body.indexOf("function buildDialogOverlay(");
+		assertTrue(end > at, body);
+		var slice = body.substring(Math.min(at, Math.min(widgetAt, badgeAt)), end);
+		assertFalse(slice.contains(".innerHTML"), "dialog bar-slot painting must never use innerHTML:\n" + slice);
+	}
+
+	@Test void a05_enhanceOnInsertGoesThroughTheExportedChromeEntry() throws Exception {
+		var body = viewsJs();
+		assertTrue(body.contains("window.JuneauChrome"), body);
+		assertTrue(body.contains("initAll()"), body);
+	}
+
+	@Test void a06_chromeSharesOneWiredMarkerForSafeActions() throws Exception {
+		var body = chromeJs();
+		assertTrue(body.contains("SAFE_WIRED_ATTR = \"data-juneau-safe-wired\""), body);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Behavioral harness
+	//------------------------------------------------------------------------------------------------------------------
+
+	private static Map<?,?> report;
+
+	@BeforeAll
+	static void probeIfNodeAvailable() throws Exception {
+		if (!nodeAvailable())
+			return;
+		var harness = locateHarness();
+		if (harness == null)
+			return;
+		var viewsFile = Files.createTempFile("juneau-views-", ".js");
+		var chromeFile = Files.createTempFile("juneau-chrome-", ".js");
+		try {
+			Files.writeString(viewsFile, viewsJs(), UTF_8);
+			Files.writeString(chromeFile, chromeJs(), UTF_8);
+			report = Json.to(runNode(harness, viewsFile, chromeFile), Map.class);
+		} finally {
+			Files.deleteIfExists(viewsFile);
+			Files.deleteIfExists(chromeFile);
+		}
+	}
+
+	private static boolean nodeAvailable() {
+		try {
+			var p = new ProcessBuilder("node", "--version").redirectErrorStream(true).start();
+			if (!p.waitFor(5, TimeUnit.SECONDS)) {
+				p.destroyForcibly();
+				return false;
+			}
+			return p.exitValue() == 0;
+		} catch (Exception e) {
+			return false;
+		}
+	}
+
+	private static Path locateHarness() {
+		var basedir = System.getProperty("basedir");
+		if (basedir != null) {
+			var p = Path.of(basedir, "src/test/js/dialog-bar-slot.cjs");
+			if (Files.isRegularFile(p)) return p.toAbsolutePath().normalize();
+		}
+		for (var rel : List.of(
+			"src/test/js/dialog-bar-slot.cjs",
+			"juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs"
+		)) {
+			var p = Path.of(rel);
+			if (Files.isRegularFile(p)) return p.toAbsolutePath().normalize();
+		}
+		return null;
+	}
+
+	private static String runNode(Path harness, Path viewsJs, Path chromeJs) throws Exception {
+		var stdout = Files.createTempFile("dialog-bar-slot-stdout-", ".json");
+		var stderr = Files.createTempFile("dialog-bar-slot-stderr-", ".txt");
+		try {
+			var cmd = List.of("node", harness.toString(), viewsJs.toString(), chromeJs.toString());
+			var p = new ProcessBuilder(cmd).redirectOutput(stdout.toFile()).redirectError(stderr.toFile()).start();
+			if (!p.waitFor(30, TimeUnit.SECONDS)) {
+				p.destroyForcibly();
+				fail("dialog-bar-slot.cjs did not finish within 30s; stderr:\n" + quietRead(stderr));
+			}
+			if (p.exitValue() != 0)
+				fail("dialog-bar-slot.cjs exited " + p.exitValue() + "; stderr:\n" + quietRead(stderr)
+					+ "\nstdout:\n" + quietRead(stdout));
+			return Files.readString(stdout, UTF_8);
+		} finally {
+			Files.deleteIfExists(stdout);
+			Files.deleteIfExists(stderr);
+		}
+	}
+
+	private static String quietRead(Path p) {
+		try { return Files.readString(p, UTF_8); }
+		catch (IOException e) { return "(unreadable: " + e.getMessage() + ")"; }
+	}
+
+	private static Map<?,?> report() {
+		assumeTrue(report != null, "node not available or dialog-bar-slot.cjs not found — behavioral layer skipped");
+		return report;
+	}
+
+	@Test void b01_bothRuntimesLoadedAndHelpersPresent() {
+		var r = report();
+		assertEquals(true, r.get("hasViews"), r::toString);
+		assertEquals(true, r.get("hasChrome"), r::toString);
+		assertEquals(true, r.get("hasBuildRegion"), r::toString);
+		assertEquals(true, r.get("hasInsert"), r::toString);
+		assertEquals(true, r.get("hasMint"), r::toString);
+		assertEquals(true, r.get("hasTeardown"), r::toString);
+		assertEquals(true, r.get("hasEnhance"), r::toString);
+	}
+
+	@Test void b02_regionPaintedFromJson_immediatelyFollowingTheTitle() {
+		var r = report();
+		assertEquals(true, r.get("paint_inserted"), r::toString);
+		assertEquals(1.0, ((Number)r.get("paint_regionCount")).doubleValue(), r::toString);
+		assertEquals(true, r.get("paint_regionFollowsTitle"), r::toString);
+		assertEquals(true, r.get("paint_hasBarSlotClass"), r::toString);
+		assertEquals(true, r.get("paint_hasDialogSlotClass"), r::toString);
+	}
+
+	@Test void b03_regionCarriesTheDialogTitleAnchor() {
+		var r = report();
+		assertEquals(true, r.get("paint_anchorIsDialogTitle"), r::toString);
+	}
+
+	@Test void b04_widgetsPaintedWithTextContentOnly() {
+		var r = report();
+		assertEquals(true, r.get("paint_textWidgetPainted"), r::toString);
+		assertEquals("state", r.get("paint_textWidgetMarker"), r::toString);
+		assertEquals(true, r.get("paint_badgeLabelPainted"), r::toString);
+		assertEquals(true, r.get("paint_badgeCountPainted"), r::toString);
+		assertEquals(true, r.get("paint_badgeNamespaced"), r::toString);
+	}
+
+	@Test void b05_sidecarIsIdLess_andCarriesTheBarContractAndInitialCounts() {
+		var r = report();
+		assertEquals(true, r.get("paint_sidecarPresent"), r::toString);
+		assertEquals(true, r.get("paint_sidecarJsonOk"), r::toString);
+	}
+
+	@Test void b06_cloneTimeIdMinting_reusedForTwoStackedDialogs() {
+		var r = report();
+		assertEquals("dialog:1", r.get("mint_markerA"), r::toString);
+		assertEquals("dialog:2", r.get("mint_markerB"), r::toString);
+		assertEquals(true, r.get("mint_markerA_isDialogSeq1"), r::toString);
+		assertEquals(true, r.get("mint_markerB_isDialogSeq2"), r::toString);
+		assertEquals(true, r.get("mint_distinct"), () -> "two stacked dialogs must never share an id: " + r);
+		assertEquals("juneau-bar:dialog:1", r.get("mint_sidecarIdA"), r::toString);
+		assertEquals("juneau-bar:dialog:2", r.get("mint_sidecarIdB"), r::toString);
+		assertEquals(true, r.get("mint_sidecarIdA_isPrefixPlusMarker"), r::toString);
+		assertEquals(true, r.get("mint_authorIdUnresolvable"),
+			() -> "getElementById('juneau-bar:' + authorId) must not resolve after minting: " + r);
+	}
+
+	@Test void b07_eachMintedIdRoundTripsThroughReadSidecar() {
+		var r = report();
+		assertEquals(true, r.get("mint_roundTripA"), r::toString);
+		assertEquals(true, r.get("mint_roundTripB"), r::toString);
+	}
+
+	@Test void b08_enhanceOnInsert_appliesCountsAndIsIdempotent_noDoubleBoundSafeClick() {
+		var r = report();
+		assertEquals(true, r.get("enh_seamCalled"), r::toString);
+		assertEquals("3", r.get("enh_countBeforeA"), r::toString);
+		assertEquals("3", r.get("enh_countAfterA"), r::toString);
+		assertEquals("1", r.get("enh_wiredMarker"), () -> "the shared wired marker must be stamped: " + r);
+		assertEquals(1.0, ((Number)r.get("enh_safeListenersAfterFirst")).doubleValue(), r::toString);
+		assertEquals(1.0, ((Number)r.get("enh_safeListenersAfterSecond")).doubleValue(),
+			() -> "a second enhance-on-insert must not double-bind the SAFE action: " + r);
+		assertEquals("3", r.get("enh_countStillA"), r::toString);
+		assertEquals(1.0, ((Number)r.get("enh_safeFires")).doubleValue(), r::toString);
+	}
+
+	@Test void b09_dialogWithNoBarSlot_enhanceIsACleanNoOp() {
+		var r = report();
+		assertEquals(false, r.get("enh_seamSkippedWithoutSlot"), r::toString);
+	}
+
+	@Test void b10_collapseTeardown_reusedVerbatim_leavesTheOtherStackedDialogAlone() {
+		var r = report();
+		assertEquals(true, r.get("tear_sidecarIdRemoved"), r::toString);
+		assertEquals(true, r.get("tear_aUnresolvable"), r::toString);
+		assertEquals(true, r.get("tear_bStillResolvable"), r::toString);
+		assertEquals(true, r.get("tear_bStillRoundTrips"), r::toString);
+		assertEquals(true, r.get("tear_noThrowOnNull"), r::toString);
+	}
+
+	@Test void b11_failClosedNoOp_missingMalformedOrEmptyBarSlotNeverBlocksTheDialog() {
+		var r = report();
+		assertEquals(false, r.get("noop_undeclared"), r::toString);
+		assertEquals(0.0, ((Number)r.get("noop_undeclaredRegionCount")).doubleValue(), r::toString);
+		assertEquals(false, r.get("noop_emptyWidgets"), r::toString);
+		assertEquals(false, r.get("noop_badContractVersion"),
+			() -> "a bar-slot contract mismatch must be refused, not painted: " + r);
+		assertEquals(false, r.get("noop_missingId"), r::toString);
+	}
+}
diff --git a/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs
new file mode 100644
index 0000000000..5db064d820
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-views/src/test/js/dialog-bar-slot.cjs
@@ -0,0 +1,511 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * dialog-bar-slot.cjs - always-on Node harness for the THIRD named bar-slot host: a BarSlot anchored to a dialog's
+ * title (ModalDef.barSlot).  Loads juneau-views.js AND juneau-chrome.js into ONE sandbox sharing one fake document,
+ * the same shape detail-bar-slot.cjs uses for the row-detail host, because this slice reuses that host's identity
+ * minting / enhance-on-insert / teardown machinery verbatim rather than reimplementing it.
+ *
+ *   Usage:  node dialog-bar-slot.cjs <juneau-views.js> <juneau-chrome.js>
+ *
+ * Covers: painting the region + widgets + sidecar from JSON (client-side, since a dialog has no server-rendered pass
+ * to ride into); the dialog-title anchor attribute; clone-time id minting reused from the row-detail host for two
+ * stacked dialogs; enhance-on-insert via JuneauChrome.init.initAll() sharing the wired marker with wireSafeActions;
+ * collapse teardown; and the fail-closed no-op for a missing/malformed/empty bar slot.
+ *
+ * Prints ONE JSON object to stdout; every assertion lives in the Java test.
+ */
+'use strict';
+
+const fs = require('node:fs');
+const path = require('node:path');
+const vm = require('node:vm');
+
+const viewsJsPath = process.argv[2];
+const chromeJsPath = process.argv[3];
+if (!viewsJsPath || !chromeJsPath) {
+	console.error('usage: node dialog-bar-slot.cjs <juneau-views.js> <juneau-chrome.js>');
+	process.exit(2);
+}
+
+// ------------------------------------------------------------------------------------------------------------------
+// Fake DOM.  Verbatim copy of detail-bar-slot.cjs's self-contained shim (deliberately not shared via a shim module -
+// see that file's own header note) - rich enough for createElement/insertBefore/appendChild/querySelectorAll/
+// getElementById/dataset/addEventListener-dispatchEvent, which is everything the paths under test touch.
+// ------------------------------------------------------------------------------------------------------------------
+
+function matchesClassToken(node, tok) {
+	const raw = ' ' + (node.className || node.getAttribute('class') || '') + ' ';
+	return raw.indexOf(' ' + tok + ' ') >= 0;
+}
+
+function matchesAttrToken(node, name, value) {
+	const v = node.getAttribute(name);
+	if (v == null) return false;
+	return value === undefined || v === value;
+}
+
+function elMatches(node, sel) {
+	if (!node || node.nodeType !== 1) return false;
+	if (sel.indexOf(',') >= 0)
+		return sel.split(',').some(function (part) { return elMatches(node, part.trim()); });
+	let rest = sel.trim();
+	const tm = /^[a-zA-Z][\w-]*/.exec(rest);
+	if (tm) {
+		if (node.tagName !== tm[0].toUpperCase()) return false;
+		rest = rest.slice(tm[0].length);
+		if (!rest) return true;
+	}
+	while (rest.length) {
+		const cm = /^\.([\w-]+)/.exec(rest);
+		if (cm) {
+			if (!matchesClassToken(node, cm[1])) return false;
+			rest = rest.slice(cm[0].length);
+			continue;
+		}
+		const am = /^\[([\w:-]+)(?:=["']?([^\]"']*)["']?)?\]/.exec(rest);
+		if (am) {
+			if (!matchesAttrToken(node, am[1], am[2])) return false;
+			rest = rest.slice(am[0].length);
+			continue;
+		}
+		return false;
+	}
+	return true;
+}
+
+function elWalk(node, sel, acc) {
+	for (const c of node.childNodes) {
+		if (c.nodeType === 1) {
+			if (elMatches(c, sel)) acc.push(c);
+			elWalk(c, sel, acc);
+		}
+	}
+	return acc;
+}
+
+function closestFrom(node, sel) {
+	let n = node;
+	while (n && n.nodeType === 1) {
+		if (elMatches(n, sel)) return n;
+		n = n.parentNode;
+	}
+	return null;
+}
+
+function dispatchFrom(node, ev) {
+	let n = node;
+	while (n) {
+		(n._listeners && n._listeners[ev.type] || []).slice().forEach(function (fn) { fn(ev); });
+		n = ev.bubbles ? n.parentNode : null;
+	}
+}
+
+function datasetKeyToAttr(key) {
+	return 'data-' + key.replace(/[A-Z]/g, function (m) { return '-' + m.toLowerCase(); });
+}
+
+function attrToDatasetKey(attr) {
+	return attr.slice(5).replace(/-([a-z])/g, function (_, c) { return c.toUpperCase(); });
+}
+
+function makeDataset(node) {
+	return new Proxy({}, {
+		get(_, key) {
+			if (typeof key !== 'string') return undefined;
+			const v = node.getAttribute(datasetKeyToAttr(key));
+			return v == null ? undefined : v;
+		},
+		set(_, key, val) {
+			node.setAttribute(datasetKeyToAttr(key), val);
+			return true;
+		},
+		deleteProperty(_, key) {
+			node.removeAttribute(datasetKeyToAttr(key));
+			return true;
+		},
+		has(_, key) {
+			return typeof key === 'string' && node.getAttribute(datasetKeyToAttr(key)) != null;
+		},
+		ownKeys() {
+			return Object.keys(node.attrs)
+				.filter(function (k) { return k.indexOf('data-') === 0; })
+				.map(attrToDatasetKey);
+		},
+		getOwnPropertyDescriptor(_, key) {
+			const v = node.getAttribute(datasetKeyToAttr(key));
+			return v == null ? undefined : { value: v, enumerable: true, configurable: true };
+		}
+	});
+}
+
+const byId = {};
+
+function el(tag) {
+	const node = {
+		nodeType: 1,
+		tagName: String(tag).toUpperCase(),
+		childNodes: [],
+		attrs: {},
+		parentNode: null,
+		className: '',
+		hidden: false,
+		_listeners: {},
+		_text: '',
+		get firstChild() { return this.childNodes[0] || null; },
+		get nextSibling() {
+			if (!this.parentNode) return null;
+			const kids = this.parentNode.childNodes;
+			const i = kids.indexOf(this);
+			return i >= 0 && i + 1 < kids.length ? kids[i + 1] : null;
+		},
+		get dataset() { return makeDataset(this); },
+		getAttribute: function (k) {
+			if (k === 'class' && this.className) return this.className;
+			return Object.hasOwn(this.attrs, k) ? this.attrs[k] : null;
+		},
+		setAttribute: function (k, v) {
+			this.attrs[k] = v == null ? '' : String(v);
+			if (k === 'class') this.className = this.attrs[k];
+			if (k === 'id') byId[this.attrs[k]] = this;
+		},
+		removeAttribute: function (k) {
+			if (k === 'id' && this.attrs.id != null) delete byId[this.attrs.id];
+			delete this.attrs[k];
+			if (k === 'class') this.className = '';
+		},
+		remove: function () {
+			if (this.parentNode) {
+				const kids = this.parentNode.childNodes;
+				const i = kids.indexOf(this);
+				if (i >= 0) kids.splice(i, 1);
+				this.parentNode = null;
+			}
+		},
+		_detach: function () {
+			this.remove();
+		},
+		appendChild: function (c) {
+			if (c._detach) c._detach();
+			this.childNodes.push(c);
+			c.parentNode = this;
+			return c;
+		},
+		insertBefore: function (c, ref) {
+			if (c._detach) c._detach();
+			const i = ref ? this.childNodes.indexOf(ref) : -1;
+			if (i < 0) this.childNodes.push(c); else this.childNodes.splice(i, 0, c);
+			c.parentNode = this;
+			return c;
+		},
+		removeChild: function (c) {
+			c.remove();
+			return c;
+		},
+		querySelectorAll: function (sel) { return elWalk(this, sel, []); },
+		querySelector: function (sel) { const r = elWalk(this, sel, []); return r.length ? r[0] : null; },
+		closest: function (sel) { return closestFrom(this, sel); },
+		contains: function (other) {
+			if (other === this) return true;
+			for (const c of this.childNodes) {
+				if (c === other) return true;
+				if (c.nodeType === 1 && c.contains?.(other)) return true;
+			}
+			return false;
+		},
+		addEventListener: function (type, fn) {
+			this._listeners[type] = this._listeners[type] || [];
+			this._listeners[type].push(fn);
+		},
+		listenerCount: function (type) { return (this._listeners[type] || []).length; },
+		dispatchEvent: function (ev) {
+			dispatchFrom(this, ev);
+			return true;
+		},
+		focus: function () { document.activeElement = this; },
+		getBoundingClientRect: function () { return { left: 0, top: 0, right: 0, bottom: 0, width: 0, height: 0 }; },
+		set textContent(v) { this.childNodes.length = 0; this._text = v == null ? '' : String(v); },
+		get textContent() {
+			if (this.childNodes.length === 0) return this._text || '';
+			return this.childNodes.map(function (c) { return c.textContent; }).join('');
+		}
+	};
+	return node;
+}
+
+const body = el('body');
+
+const document = {
+	readyState: 'loading',
+	activeElement: null,
+	body: body,
+	addEventListener: function () {},
+	createElement: function (tag) { return el(tag); },
+	getElementById: function (id) { return Object.hasOwn(byId, id) ? byId[id] : null; },
+	querySelectorAll: function (sel) { return body.querySelectorAll(sel); },
+	querySelector: function (sel) { return body.querySelector(sel); }
+};
+
+function CustomEvent(type, init) {
+	this.type = type;
+	this.detail = init && init.detail;
+	this.bubbles = !!(init && init.bubbles);
+	this.defaultPrevented = false;
+	this.preventDefault = function () { this.defaultPrevented = true; };
+}
+
+const window = {
+	document: document,
+	console: console,
+	jQuery: undefined,
+	innerWidth: 1024,
+	innerHeight: 768,
+	CustomEvent: CustomEvent,
+	addEventListener: function () {},
+	matchMedia: function () { return { matches: false, addEventListener: function () {} }; },
+	getComputedStyle: function () { return { getPropertyValue: function () { return ''; } }; }
+};
+
+const sandbox = {
+	window: window,
+	document: document,
+	console: console,
+	CustomEvent: CustomEvent,
+	setTimeout: function (fn) { if (typeof fn === 'function') { fn(); } return 0; },
+	clearTimeout: function () {},
+	setInterval: function () { return 0; },
+	clearInterval: function () {},
+	Promise: Promise
+};
+
+// NOSONAR javascript:S1523 -- this harness's entire purpose is to load the production runtime under test (a
+// repo-local file path from argv, not attacker-controlled input) into an isolated VM sandbox; that IS the test.
+vm.runInNewContext(fs.readFileSync(path.resolve(viewsJsPath), 'utf8'), sandbox, { filename: 'juneau-views.js' });
+// NOSONAR javascript:S1523 -- same rationale: loading the production juneau-chrome.js under test into the sandbox.
+vm.runInNewContext(fs.readFileSync(path.resolve(chromeJsPath), 'utf8'), sandbox, { filename: 'juneau-chrome.js' });
+
+const VNS = window.JuneauViews;
+const V = VNS?.init;
+const CNS = window.JuneauChrome;
+const C = CNS?.init;
+
+const out = {
+	hasViews: !!(typeof V?.buildDialogOverlay === 'function'),
+	hasChrome: !!(typeof C?.initAll === 'function'),
+	hasBuildRegion: !!(typeof V?.buildDialogBarSlotRegion === 'function'),
+	hasInsert: !!(typeof V?.insertDialogBarSlot === 'function'),
+	hasMint: !!(typeof V?.mintDetailBarSlotIdentity === 'function'),
+	hasTeardown: !!(typeof V?.teardownDetailBarSlot === 'function'),
+	hasEnhance: !!(typeof V?.enhanceChromeInPanel === 'function')
+};
+if (!(out.hasViews && out.hasChrome && out.hasBuildRegion && out.hasInsert && out.hasMint && out.hasTeardown
+		&& out.hasEnhance)) {
+	process.stdout.write(JSON.stringify(out));
+	process.exit(0);
+}
+
+const BAR_MARKER = 'data-juneau-bar-slot';
+const BAR_ANCHOR = 'data-juneau-bar-slot-anchor';
+const BAR_META = 'data-juneau-bar-meta';
+const BAR_WIDGET_MARKER = 'data-juneau-bar-widget';
+const SIDECAR_PREFIX = 'juneau-bar:';
+const AUTHOR_ID = 'dialog-ctx';
+
+// ------------------------------------------------------------------------------------------------------------------
+// Fixture builders: a hand-painted equivalent of what buildDialogOverlay assembles (backdrop > dialog > title).
+// ------------------------------------------------------------------------------------------------------------------
+
+function barSlotJson(id, count) {
+	return {
+		contractVersion: '1',
+		id: id || AUTHOR_ID,
+		widgets: [
+			{ id: 'state', text: 'Region us-east' },
+			{ id: 'open', label: 'Open', badge: { count: count == null ? 3 : count } }
+		]
+	};
+}
+
+function dialogFixture() {
+	const backdrop = el('div');
+	backdrop.setAttribute('class', 'juneau-view-dialog-backdrop');
+	const dialog = el('div');
+	dialog.setAttribute('class', 'juneau-view-dialog');
+	dialog.setAttribute('role', 'dialog');
+	const title = el('h2');
+	title.setAttribute('class', 'juneau-view-dialog-title');
+	title.textContent = 'Really delete?';
+	dialog.appendChild(title);
+	backdrop.appendChild(dialog);
+	return { backdrop: backdrop, dialog: dialog, title: title };
+}
+
+function regionsIn(root) {
+	return root.querySelectorAll('[' + BAR_MARKER + ']');
+}
+
+// ------------------------------------------------------------------------------------------------------------------
+// 1. Paint from JSON: widgets, anchor attribute, placement as the title's immediate next sibling, id-less sidecar.
+// ------------------------------------------------------------------------------------------------------------------
+
+(function () {
+	const f = dialogFixture();
+	const inserted = V.insertDialogBarSlot(f.dialog, f.title, barSlotJson(), 1);
+	out.paint_inserted = inserted;
+	out.paint_regionCount = regionsIn(f.dialog).length;
+
+	const region = regionsIn(f.dialog)[0];
+	out.paint_regionFollowsTitle = f.title.nextSibling === region;
+	out.paint_anchorIsDialogTitle = region.getAttribute(BAR_ANCHOR) === 'dialog-title';
+	out.paint_hasBarSlotClass = matchesClassToken(region, 'jc-bar-slot');
+	out.paint_hasDialogSlotClass = matchesClassToken(region, 'juneau-view-dialog-bar-slot');
+
+	const textWidget = region.querySelector('.jc-bar-text');
+	out.paint_textWidgetPainted = !!textWidget && textWidget.textContent === 'Region us-east';
+	out.paint_textWidgetMarker = textWidget?.getAttribute(BAR_WIDGET_MARKER);
+
+	const badgeWidget = region.querySelector('.jc-bar-badge');
+	out.paint_badgeLabelPainted = badgeWidget?.querySelector('.jc-bar-label')?.textContent === 'Open';
+	const badge = badgeWidget?.querySelector('[data-juneau-badge]');
+	out.paint_badgeCountPainted = badge?.textContent === '3';
+	out.paint_badgeNamespaced = badge?.getAttribute('data-juneau-badge') === 'bar:open';
+
+	const sidecar = f.dialog.querySelector('[' + BAR_META + ']');
+	out.paint_sidecarIsIdLess_beforeMint = false;   // insertDialogBarSlot mints immediately; checked via mint_ below
+	out.paint_sidecarPresent = !!sidecar;
+	out.paint_sidecarJsonOk = (function () {
+		try {
+			const j = JSON.parse(sidecar.textContent);
+			return j.contractVersion === '1' && j.badges['bar:open'] === 3;
+		} catch (e) { return false; }
+	})();
+})();
+
+// ------------------------------------------------------------------------------------------------------------------
+// 2. Clone-time id minting is REUSED verbatim from the row-detail host, namespaced by dialogSeq for two stacked
+//    dialogs - each must resolve its OWN sidecar, never the other's.
+// ------------------------------------------------------------------------------------------------------------------
+
+const fA = dialogFixture();
+const fB = dialogFixture();
+
+(function () {
+	body.appendChild(fA.backdrop);
+	body.appendChild(fB.backdrop);
+	V.insertDialogBarSlot(fA.dialog, fA.title, barSlotJson('dialog-ctx', 3), 1);
+	V.insertDialogBarSlot(fB.dialog, fB.title, barSlotJson('dialog-ctx', 7), 2);
+
+	const regionA = regionsIn(fA.dialog)[0];
+	const regionB = regionsIn(fB.dialog)[0];
+	out.mint_markerA = regionA.getAttribute(BAR_MARKER);
+	out.mint_markerB = regionB.getAttribute(BAR_MARKER);
+	out.mint_markerA_isDialogSeq1 = out.mint_markerA === 'dialog:1';
+	out.mint_markerB_isDialogSeq2 = out.mint_markerB === 'dialog:2';
+	out.mint_distinct = out.mint_markerA !== out.mint_markerB;
+
+	const sidecarA = fA.dialog.querySelector('[' + BAR_META + ']');
+	const sidecarB = fB.dialog.querySelector('[' + BAR_META + ']');
+	out.mint_sidecarIdA = sidecarA.getAttribute('id');
+	out.mint_sidecarIdB = sidecarB.getAttribute('id');
+	out.mint_sidecarIdA_isPrefixPlusMarker = out.mint_sidecarIdA === SIDECAR_PREFIX + out.mint_markerA;
+	out.mint_authorIdUnresolvable = document.getElementById(SIDECAR_PREFIX + AUTHOR_ID) === null;
+
+	const rtA = C.readSidecar(SIDECAR_PREFIX, out.mint_markerA);
+	const rtB = C.readSidecar(SIDECAR_PREFIX, out.mint_markerB);
+	out.mint_roundTripA = !!(rtA?.badges?.['bar:open'] === 3);
+	out.mint_roundTripB = !!(rtB?.badges?.['bar:open'] === 7);
+})();
+
+// ------------------------------------------------------------------------------------------------------------------
+// 3. Enhance-on-insert: initAll() (reused verbatim via enhanceChromeInPanel) applies the sidecar counts and shares
+//    the SAFE_WIRED_ATTR marker with wireSafeActions - a re-scan must not double-bind an existing SAFE action.
+// ------------------------------------------------------------------------------------------------------------------
+
+let safeAction = null;
+let safeFires = 0;
+
+(function () {
+	const header = el('div');
+	header.setAttribute('class', 'jc-app-header');
+	header.dataset.juneauAppHeader = 'main';
+	safeAction = el('button');
+	safeAction.dataset.juneauHeaderAction = 'ping';
+	safeAction.dataset.juneauBehavior = 'safe';
+	safeAction.dataset.juneauSafe = 'ping';
+	header.appendChild(safeAction);
+	body.appendChild(header);
+	header.addEventListener(C.SAFE_EVENT, function () { safeFires++; });
+
+	const badgeA = fA.dialog.querySelector('[data-juneau-badge]');
+	out.enh_countBeforeA = badgeA.textContent;   // painted at insert-time from the JSON count itself
+	out.enh_seamCalled = V.enhanceChromeInPanel(fA.dialog);
+	out.enh_countAfterA = badgeA.textContent;
+	out.enh_safeListenersAfterFirst = safeAction.listenerCount('click');
+	out.enh_wiredMarker = safeAction.dataset.juneauSafeWired;
+
+	// Idempotent: a second enhance-on-insert call must not re-bind the already-wired SAFE action.
+	V.enhanceChromeInPanel(fA.dialog);
+	out.enh_safeListenersAfterSecond = safeAction.listenerCount('click');
+	out.enh_countStillA = badgeA.textContent;
+
+	safeAction.dispatchEvent(new CustomEvent('click', { bubbles: true }));
+	out.enh_safeFires = safeFires;
+
+	// A dialog with NO bar slot must not drag chrome in at all (a confirm-only dialog stays a clean no-op).
+	const fPlain = dialogFixture();
+	out.enh_seamSkippedWithoutSlot = V.enhanceChromeInPanel(fPlain.dialog);
+})();
+
+// ------------------------------------------------------------------------------------------------------------------
+// 4. Collapse teardown: reused verbatim from the row-detail host - drops the minted sidecar id without touching the
+//    OTHER stacked dialog's own sidecar.
+// ------------------------------------------------------------------------------------------------------------------
+
+(function () {
+	V.teardownDetailBarSlot(fA.dialog);
+	out.tear_sidecarIdRemoved = fA.dialog.querySelector('[' + BAR_META + ']').getAttribute('id') === null;
+	out.tear_aUnresolvable = document.getElementById(SIDECAR_PREFIX + 'dialog:1') === null;
+	out.tear_bStillResolvable = document.getElementById(SIDECAR_PREFIX + 'dialog:2') !== null;
+	out.tear_bStillRoundTrips = !!C.readSidecar(SIDECAR_PREFIX, 'dialog:2');
+	out.tear_noThrowOnNull = (function () { V.teardownDetailBarSlot(null); return true; })();
+})();
+
+// ------------------------------------------------------------------------------------------------------------------
+// 5. Fail-closed no-op: a missing/malformed/empty bar slot never blocks the confirm/cancel dialog from opening, and
+//    a contract-version mismatch is refused rather than painted.
+// ------------------------------------------------------------------------------------------------------------------
+
+(function () {
+	const fNone = dialogFixture();
+	out.noop_undeclared = V.insertDialogBarSlot(fNone.dialog, fNone.title, undefined, 3);
+	out.noop_undeclaredRegionCount = regionsIn(fNone.dialog).length;
+
+	const fEmpty = dialogFixture();
+	out.noop_emptyWidgets = V.insertDialogBarSlot(fEmpty.dialog, fEmpty.title,
+		{ contractVersion: '1', id: 'x', widgets: [] }, 4);
+
+	const fBadContract = dialogFixture();
+	out.noop_badContractVersion = V.insertDialogBarSlot(fBadContract.dialog, fBadContract.title,
+		{ contractVersion: '99', id: 'x', widgets: [{ id: 'a', text: 'A' }] }, 5);
+
+	const fNoId = dialogFixture();
+	out.noop_missingId = V.insertDialogBarSlot(fNoId.dialog, fNoId.title,
+		{ contractVersion: '1', widgets: [{ id: 'a', text: 'A' }] }, 6);
+})();
+
+process.stdout.write(JSON.stringify(out, null, 2) + '\n');
diff --git a/juneau-rest/juneau-rest-server-widgets/src/main/java/org/apache/juneau/rest/server/widgets/ModalDef.java b/juneau-rest/juneau-rest-server-widgets/src/main/java/org/apache/juneau/rest/server/widgets/ModalDef.java
index b770db03a3..0897042abb 100644
--- a/juneau-rest/juneau-rest-server-widgets/src/main/java/org/apache/juneau/rest/server/widgets/ModalDef.java
+++ b/juneau-rest/juneau-rest-server-widgets/src/main/java/org/apache/juneau/rest/server/widgets/ModalDef.java
@@ -72,9 +72,23 @@ import org.apache.juneau.commons.bean.*;
  * 	<li class='jc'>{@link Widget}
  * </ul>
  *
+ * <h5 class='section'>The third named bar-slot host</h5>
+ * <p>
+ * {@link #barSlot} is the <i>third</i> named {@link BarSlot} attachment &mdash; alongside the rich-view module's page
+ * and row-detail hosts (that module never depends on this one, so they are not linked here).  Unlike those two
+ * &mdash; Java-only fields a server-side emitter paints directly, omitted from their wire &mdash; this modal itself
+ * <b>is</b> the wire payload the modal-open confirmation fetch returns, with no separate server-rendered pass the
+ * dialog could ride into.  So this field <b>is</b> carried on the wire (additive-only: {@code null} when unset, and
+ * its presence does not bump {@link #CONTRACT_VERSION}), and the {@code juneau-views.js} runtime paints the region
+ * and its dynamic-count sidecar client-side from the JSON, the same way it already paints typed confirmation
+ * {@link #fields} and {@link #form} controls &mdash; {@code textContent} only, never {@code innerHTML}.  The dialog
+ * anchors its own region immediately after the title (the rich-view module's {@code
+ * BarSlotTable.ANCHOR_DIALOG_TITLE}), owning that placement itself rather than leaning on the shared strip builder,
+ * exactly as the row-detail host owns its own ribbon-trailing relocation.
+ *
  * @since 10.0.0
  */
-@BeanType(properties="contractVersion,title,fields,form,idempotencyKey")
+@BeanType(properties="contractVersion,title,fields,form,idempotencyKey,barSlot")
 @SuppressWarnings("java:S1845") // Fluent-builder setters intentionally mirror field names (Juneau DSL convention).
 public class ModalDef implements Widget {
 
@@ -147,6 +161,13 @@ public class ModalDef implements Widget {
 	 */
 	public String idempotencyKey;
 
+	/**
+	 * Optional additive bar slot anchored to this dialog's title &mdash; the <i>third</i> named {@link BarSlot}
+	 * attachment (see the class Javadoc for how this host differs from the other two).  Omitted from the wire when
+	 * unset; painted by the runtime immediately after {@link #title}, ahead of {@link #fields} and {@link #form}.
+	 */
+	public BarSlot barSlot;
+
 	/**
 	 * Starts a new {@link ModalDef} with the specified title / confirmation prompt.
 	 *
@@ -198,6 +219,21 @@ public class ModalDef implements Widget {
 		return this;
 	}
 
+	/**
+	 * Declares the additive bar slot anchored to this dialog's title.
+	 *
+	 * <p>
+	 * See {@link #barSlot} &mdash; a third named host for the same {@link BarSlot} bean, not a re-use of the
+	 * rich-view module's page or row-detail hosts.
+	 *
+	 * @param value The bar slot.  Can be <jk>null</jk> (no dialog bar slot).
+	 * @return This object.
+	 */
+	public ModalDef barSlot(BarSlot value) {
+		barSlot = value;
+		return this;
+	}
+
 	/**
 	 * Fail-closed bean validation.
 	 *
@@ -222,6 +258,8 @@ public class ModalDef implements Widget {
 			}
 		if (form != null)
 			form.validate();
+		if (barSlot != null)
+			barSlot.validate();
 	}
 
 	/**
diff --git a/juneau-rest/juneau-rest-server-widgets/src/test/java/org/apache/juneau/rest/server/widgets/ModalDef_BarSlot_Test.java b/juneau-rest/juneau-rest-server-widgets/src/test/java/org/apache/juneau/rest/server/widgets/ModalDef_BarSlot_Test.java
new file mode 100644
index 0000000000..fbebea8dc8
--- /dev/null
+++ b/juneau-rest/juneau-rest-server-widgets/src/test/java/org/apache/juneau/rest/server/widgets/ModalDef_BarSlot_Test.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.juneau.rest.server.widgets;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.*;
+
+import org.apache.juneau.*;
+import org.apache.juneau.marshall.marshaller.*;
+import org.junit.jupiter.api.*;
+
+/**
+ * Bean contract for the <b>third</b> named {@link BarSlot} attachment, {@link ModalDef#barSlot}.
+ *
+ * <p>
+ * The type is shared with the rich-view module's page and row-detail hosts while the placement is not: this host is
+ * a dialog title.  Unlike those two Java-only fields, this one travels the wire (the modal itself is the fetched
+ * payload with no separate server-rendered pass to ride into), so this class pins the wire shape (additive, omitted
+ * when unset, no {@link ModalDef#CONTRACT_VERSION} bump) in addition to the field/fluent-setter/validate-cascade
+ * shape {@code RowDetailDef_BarSlot_Test} pins for the second host.
+ */
+class ModalDef_BarSlot_Test extends TestBase {
+
+	private static BarSlot bar(String id) {
+		return BarSlot.create(id).widgets(BarBadge.of("open").label("Open").badge(Badge.count(3)));
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Field + fluent setter + validate cascade
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test void a01_fluentSetter_storesSlot() {
+		var m = ModalDef.create("Really delete?").barSlot(bar("dialog-bar"));
+		assertNotNull(m.barSlot);
+		assertEquals("dialog-bar", m.barSlot.id);
+	}
+
+	@Test void a02_nullSlot_isLegal() {
+		assertDoesNotThrow(() -> ModalDef.create("Really delete?").validate());
+	}
+
+	@Test void a03_validSlot_passes() {
+		assertDoesNotThrow(() -> ModalDef.create("Really delete?").barSlot(bar("dialog-bar")).validate());
+	}
+
+	@Test void a04_cascadesIntoBarSlotValidate_blankId() {
+		var m = ModalDef.create("Really delete?").barSlot(BarSlot.create(" ").widgets(BarText.of("x", "X")));
+		var e = assertThrows(IllegalArgumentException.class, m::validate);
+		assertTrue(e.getMessage().contains("BarSlot"), e::getMessage);
+	}
+
+	@Test void a05_cascadesIntoBarSlotValidate_noWidgets() {
+		var m = ModalDef.create("Really delete?").barSlot(BarSlot.create("dialog-bar"));
+		var e = assertThrows(IllegalArgumentException.class, m::validate);
+		assertTrue(e.getMessage().contains("BarSlot"), e::getMessage);
+	}
+
+	@Test void a06_cascadesIntoBarSlotValidate_badRefreshUrl() {
+		var m = ModalDef.create("Really delete?").barSlot(bar("dialog-bar").refreshUrl("http://evil.example/counts"));
+		var e = assertThrows(IllegalArgumentException.class, m::validate);
+		assertTrue(e.getMessage().contains("BarSlot"), e::getMessage);
+	}
+
+	@Test void a07_checkedCascadesToBarSlotValidateToo() {
+		// The serving-path hook validates the whole modal, including a malformed dialog bar slot.
+		var m = ModalDef.create("Really delete?").barSlot(BarSlot.create("dialog-bar"));
+		assertThrows(IllegalArgumentException.class, m::checked);
+	}
+
+	//------------------------------------------------------------------------------------------------------------------
+	// Wire shape: additive, omitted when unset, no CONTRACT_VERSION bump
+	//------------------------------------------------------------------------------------------------------------------
+
+	@Test void b01_barSlotIsAdditiveOnTheWire() {
+		var json = Json.of(ModalDef.create("Really delete?").barSlot(bar("dialog-bar")));
+		var expected = Json.to("""
+			{"title":"Really delete?",
+			 "barSlot":{"contractVersion":"1","id":"dialog-bar",
+			            "widgets":[{"id":"open","label":"Open","badge":{"count":3}}]}}
+			""", Map.class);
+		assertEquals(expected, Json.to(json, Map.class), json);
+	}
+
+	@Test void b02_confirmOnlyModal_withNoBarSlot_omitsIt() {
+		var json = Json.of(ModalDef.create("Really delete?"));
+		assertFalse(json.contains("barSlot"), json);
+	}
+
+	@Test void b03_barSlotRidesAlongsideAFormBearingModal() {
+		var modal = ModalDef.create("Acknowledge this incident?")
+			.field("Incident", "INC-42")
+			.form(FormDef.ofTemplate("servlet:/incidents/ack-form.ftl"))
+			.barSlot(bar("dialog-bar"))
+			.checked();
+		var json = Json.of(modal);
+		assertTrue(json.contains("\"barSlot\""), json);
+		assertTrue(json.contains("\"contractVersion\":\"" + ModalDef.CONTRACT_VERSION + "\""), json);
+	}
+
+	@Test void b04_topLevelKeyOrder_barSlotIsLast() {
+		var modal = ModalDef.create("t").field("A", "1").form(FormDef.ofTemplate("u")).idempotencyKey("k")
+			.barSlot(bar("dialog-bar")).checked();
+		Map<?,?> actual = Json.to(Json.of(modal), Map.class);
+		assertEquals(List.of("contractVersion", "title", "fields", "form", "idempotencyKey", "barSlot"),
+			new ArrayList<>(actual.keySet()));
+	}
+
+	@Test void c01_modalContractVersionUnchanged() {
+		// Adding a third wire-carried, additive-only field does not bump the modal's own contract version - the
+		// version guards the fail-loud form handshake, not this orthogonal, additive attachment.
+		assertEquals("2", ModalDef.CONTRACT_VERSION);
+	}
+
+	@Test void c02_barSlotContractVersionUnchanged() {
+		assertEquals("1", BarSlot.CONTRACT_VERSION);
+	}
+}