diff --git a/ui/src/components/EntityRow.test.tsx b/ui/src/components/EntityRow.test.tsx
index 90e7666f1e7..fc7d9c6dac7 100644
--- a/ui/src/components/EntityRow.test.tsx
+++ b/ui/src/components/EntityRow.test.tsx
@@ -46,4 +46,37 @@ describe("EntityRow", () => {
const markup = renderToStaticMarkup();
expect(markup).toContain("min-w-0 flex-1");
});
+
+ it("gives the title a min-width floor and lets meta shrink under titlePriority (PAP-12988)", () => {
+ const markup = renderToStaticMarkup(
+ chips}
+ />,
+ );
+
+ // The name keeps a usable floor instead of collapsing to zero...
+ expect(markup).toContain("min-w-[6rem]");
+ // ...and the meta cluster is the item that yields (shrinks), not the title.
+ expect(markup).toContain("min-w-0 shrink");
+ expect(markup).not.toContain('class="flex items-center gap-2 shrink-0"');
+ });
+
+ it("stacks a secondaryRow on its own line beneath the main row (PAP-12988)", () => {
+ const markup = renderToStaticMarkup(
+ chips-inline}
+ secondaryRow={chips-stacked}
+ />,
+ );
+
+ // The secondary content renders, and the shell switches from a single flex
+ // row to a stacked block layout so the cluster gets its own full-width line.
+ expect(markup).toContain("secondary-cell");
+ expect(markup).toContain("chips-stacked");
+ expect(markup).not.toMatch(/^
}
>
);
+ // With a secondaryRow, wrap the main line in its own flex row and stack the
+ // secondary content beneath it (indented to align under the title, past the
+ // leading capsule). Without it, `content` is rendered directly (unchanged).
+ const body = secondaryRow ? (
+ <>
+
+ {/* Name floor + `truncate` keeps the primary identifier readable; the
+ cluster wraps to a second line under pressure instead of starving
+ the name at narrow widths (PAP-12988). */}
+
{node.name}
{roleLabels[node.role] ?? node.role}
diff --git a/ui/storybook/stories/built-in-agents.stories.tsx b/ui/storybook/stories/built-in-agents.stories.tsx
index b623bde2c14..04a52ce5d27 100644
--- a/ui/storybook/stories/built-in-agents.stories.tsx
+++ b/ui/storybook/stories/built-in-agents.stories.tsx
@@ -66,6 +66,42 @@ const notProvisionedState: BuiltInAgentState = {
pauseReason: null,
};
+/**
+ * Mirrors Agents.tsx renderAgentRow: the built-in cluster sits inline in `meta`
+ * at xl and drops to a full-width `secondaryRow` beneath the name below xl, with
+ * `titlePriority` giving the name a floor so it never collapses (PAP-12988).
+ */
+function RosterRow({
+ name,
+ lifecycle,
+ status,
+}: {
+ name: string;
+ lifecycle?: "needs_setup" | "pending_approval";
+ status: string;
+}) {
+ const cluster = (
+ <>
+
+ {lifecycle && }
+ {lifecycle === "needs_setup" && (
+
+ )}
+ >
+ );
+ return (
+ {cluster}
}
+ meta={
{cluster}
}
+ trailing={}
+ />
+ );
+}
+
function SectionLabel({ children }: { children: React.ReactNode }) {
return (
@@ -89,40 +125,15 @@ export const SurfaceGallery: Story = {
Board 1 — Roster rows
-
-
-
-
-
- }
- trailing={}
- />
-
-
-
- }
- trailing={}
- />
-
-
-
-
- }
- trailing={}
- />
+
+
+
+
+ Resize below the xl breakpoint to see the badge/action
+ cluster drop to a second line so the agent name never collapses
+ (PAP-12988).
+