diff --git a/openspec/changes/fix-combobox-flex-collapse/.openspec.yaml b/openspec/changes/fix-combobox-flex-collapse/.openspec.yaml new file mode 100644 index 0000000000..93831bd262 --- /dev/null +++ b/openspec/changes/fix-combobox-flex-collapse/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-05-13 diff --git a/openspec/changes/fix-combobox-flex-collapse/proposal.md b/openspec/changes/fix-combobox-flex-collapse/proposal.md new file mode 100644 index 0000000000..075b551da1 --- /dev/null +++ b/openspec/changes/fix-combobox-flex-collapse/proposal.md @@ -0,0 +1,40 @@ +## Why + +Combobox shrinks to 1px when placed in a flex container with `align-items: center` (row). The widget becomes unusable — invisible to the user — without any CSS override from the app developer. + +## What changes + +`packages/pluggableWidgets/combobox-web/src/ui/Combobox.scss` — `.widget-combobox` rule (line 21). + +Replace `min-width: min-content` with `min-width: 15ch`. + +`min-content` was the initial fix attempt but does not work — it asks children for their minimum width, and children suppress their own minimums (see Root Cause). An explicit `ch`-based value breaks the dependency on children entirely. + +## Root cause + +The actual DOM chain in the bug scenario: + +``` +.row-center (user's CSS: display:flex, flex-flow:row, align-items:center) + └─ .form-group.no-columns (Mendix-injected: flex-direction:column, no explicit width) + └─ .widget-combobox (flex-grow:1 — grows height in column container, not width) + └─ .widget-combobox-input-container (flex-grow:1, no min-width) + └─ .widget-combobox-selected-items (min-width:0 — intentional for tag wrapping) + └─ .widget-combobox-input (max-width:0 unfocused, width:1px multiselect inactive) +``` + +The user created `.row-center`. Mendix injected `.form-group.no-columns` as an intermediate wrapper. That wrapper has no explicit width and becomes a column-direction flex container. Inside it, `flex-grow:1` on `.widget-combobox` distributes height, not width. The widget's width is sized by content, and `min-width: min-content` resolves to ~0 because `.widget-combobox-selected-items` has `min-width: 0` (needed for multiselect tag wrapping). + +`min-width: 15ch` is an explicit value — it does not ask children, so the children's suppressed minimums are irrelevant. + +## Why `ch` not `px` + +`ch` scales with font-size and font-family, respecting user accessibility settings and Atlas theme font changes. "15 characters wide" is a semantically honest statement about a text input widget. Override via normal CSS specificity (`.widget-combobox { min-width: 0 }`). + +## Impact + +Must not break: + +- Single-select combobox layout in all container types +- Multiselect combobox layout (active and inactive states) +- Atlas UI demo site rendering (already unaffected per ticket) diff --git a/openspec/changes/fix-combobox-flex-collapse/tests.md b/openspec/changes/fix-combobox-flex-collapse/tests.md new file mode 100644 index 0000000000..a1a1e9bef8 --- /dev/null +++ b/openspec/changes/fix-combobox-flex-collapse/tests.md @@ -0,0 +1,29 @@ +## Tests + +- [ ] **Combobox root element has min-width: 15ch in stylesheet** + - **Type:** unit + - **Given:** Combobox.scss source + - **When:** `.widget-combobox` rule is inspected + - **Then:** `min-width` is `15ch` + - **Status:** needs update — `src/__tests__/ComboboxStyles.spec.ts` currently asserts `min-content` + +- [ ] **Single-select combobox renders with visible width in flex container with align-items center** + - **Type:** e2e + - **Given:** Single-select Combobox on page `/p/combobox-flex-layout` (`.mx-name-comboBoxFlexSingle`) + - **When:** Page loads with no interaction + - **Then:** Combobox bounding rect width is greater than 10px + - **Status:** pending — needs test page in Studio Pro + +- [ ] **Multiselect combobox renders with visible width in flex container with align-items center** + - **Type:** e2e + - **Given:** Multiselect Combobox on page `/p/combobox-flex-layout` (`.mx-name-comboBoxFlexMulti`) + - **When:** Page loads with no interaction + - **Then:** Combobox bounding rect width is greater than 10px + - **Status:** pending — needs test page in Studio Pro + +- [x] **Multiselect inactive input still collapses to 1px (intentional behavior preserved)** + - **Type:** unit + - **Given:** Combobox.scss source + - **When:** multiselect inactive rule inspected + - **Then:** `.widget-combobox-input` has `width: 1px` + - **Status:** done — `src/__tests__/ComboboxStyles.spec.ts` diff --git a/packages/pluggableWidgets/combobox-web/e2e/Combobox.spec.js b/packages/pluggableWidgets/combobox-web/e2e/Combobox.spec.js index f9e2729342..64fab6dbf5 100644 --- a/packages/pluggableWidgets/combobox-web/e2e/Combobox.spec.js +++ b/packages/pluggableWidgets/combobox-web/e2e/Combobox.spec.js @@ -174,6 +174,28 @@ test.describe("combobox-web", () => { }); }); +// WC-3409: flex align-items center collapse fix +test.describe("flex container layout (WC-3409)", () => { + test.beforeEach(async ({ page }) => { + await page.goto("/p/combobox-flex-layout"); + await waitForMendixApp(page); + }); + + test("single-select combobox has visible width in flex align-items:center container", async ({ page }) => { + const comboBox = page.locator(".mx-name-comboBoxFlexSingle"); + await expect(comboBox).toBeVisible({ timeout: 10000 }); + const box = await comboBox.boundingBox(); + expect(box.width).toBeGreaterThan(10); + }); + + test("multiselect combobox has visible width in flex align-items:center container", async ({ page }) => { + const comboBox = page.locator(".mx-name-comboBoxFlexMulti"); + await expect(comboBox).toBeVisible({ timeout: 10000 }); + const box = await comboBox.boundingBox(); + expect(box.width).toBeGreaterThan(10); + }); +}); + function getOptions(combobox) { return combobox.locator(`[role=listbox] [role=option]`); } diff --git a/packages/pluggableWidgets/combobox-web/src/__tests__/ComboboxStyles.spec.ts b/packages/pluggableWidgets/combobox-web/src/__tests__/ComboboxStyles.spec.ts new file mode 100644 index 0000000000..479034a3b5 --- /dev/null +++ b/packages/pluggableWidgets/combobox-web/src/__tests__/ComboboxStyles.spec.ts @@ -0,0 +1,18 @@ +import { readFileSync } from "fs"; +import { join } from "path"; + +const scss = readFileSync(join(__dirname, "../ui/Combobox.scss"), "utf-8"); + +describe("Combobox SCSS — flex collapse fix (WC-3409)", () => { + test(".widget-combobox has min-width: 15ch", () => { + // explicit ch value prevents collapse when Mendix injects .form-group.no-columns + // (column flex container with no width), causing min-content to resolve to ~0 + expect(scss).toMatch(/\.widget-combobox\s*\{[^}]*min-width:\s*15ch/s); + }); + + test("inactive multiselect input still has width: 1px (cursor-hiding preserved)", () => { + expect(scss).toMatch( + /widget-combobox-multiselect[\s\S]*?not\(\.widget-combobox-input-container-active\)[\s\S]*?widget-combobox-input[\s\S]*?width:\s*1px/ + ); + }); +}); diff --git a/packages/pluggableWidgets/combobox-web/src/ui/Combobox.scss b/packages/pluggableWidgets/combobox-web/src/ui/Combobox.scss index 60297b500e..a679d5c90a 100644 --- a/packages/pluggableWidgets/combobox-web/src/ui/Combobox.scss +++ b/packages/pluggableWidgets/combobox-web/src/ui/Combobox.scss @@ -18,7 +18,7 @@ $cb-skeleton-light: rgba(194, 194, 194, 0.2); $cb-skeleton-dark: #d2d2d2; .widget-combobox { - min-width: 0; + min-width: 15ch; // prevent collapse in flex containers with no explicit width flex-grow: 1; position: relative; transition: color 150ms ease 0s;