diff --git a/src/Web/autopilot-monitor-web/components/members/AddMemberForm.tsx b/src/Web/autopilot-monitor-web/components/members/AddMemberForm.tsx index a080c578..7e194e5e 100644 --- a/src/Web/autopilot-monitor-web/components/members/AddMemberForm.tsx +++ b/src/Web/autopilot-monitor-web/components/members/AddMemberForm.tsx @@ -15,8 +15,10 @@ interface AddMemberFormProps { /** Read-only caller: the form stays visible but cannot submit. */ disabled?: boolean; /** - * The input always takes a line of its own. For narrow hosts such as the GA tenant editor modal, - * where the viewport breakpoint says "wide" but the type switch, role and button leave the input no room. + * The input always takes a line of its own (the GA tenant editor modal). Without it the input keeps a + * minimum width and wraps to the next line when the row has no room; the layout follows the space the + * host gives it, never a viewport breakpoint, because the same viewport can mean a wide page or a narrow + * column next to the sidebar. */ stacked?: boolean; } @@ -73,7 +75,7 @@ export function AddMemberForm({ placeholder={addingApplication ? "Application (client) ID, e.g. 00000000-0000-0000-0000-000000000000" : "user@tenant.com"} aria-label={addingApplication ? "Application (client) ID" : "User email (UPN)"} autoComplete="off" - className={`${stacked ? "w-full" : "w-full sm:w-auto sm:flex-1"} min-w-0 px-4 py-2 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors`} + className={`${stacked ? "basis-full" : "grow basis-72"} min-w-0 px-4 py-2 border border-gray-300 rounded-lg text-gray-900 placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-purple-500 focus:border-purple-500 transition-colors`} onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); @@ -81,40 +83,42 @@ export function AddMemberForm({ } }} /> - - +
+ + +
); diff --git a/src/Web/autopilot-monitor-web/components/members/__tests__/AddMemberForm.test.ts b/src/Web/autopilot-monitor-web/components/members/__tests__/AddMemberForm.test.ts index 4b10e574..ea653ebe 100644 --- a/src/Web/autopilot-monitor-web/components/members/__tests__/AddMemberForm.test.ts +++ b/src/Web/autopilot-monitor-web/components/members/__tests__/AddMemberForm.test.ts @@ -5,8 +5,8 @@ import { AddMemberForm } from "../AddMemberForm"; const noop = () => {}; -function inputClass(stacked?: boolean): string { - const html = renderToStaticMarkup( +function render(stacked?: boolean): string { + return renderToStaticMarkup( createElement(AddMemberForm, { value: "", onValueChange: noop, @@ -19,19 +19,29 @@ function inputClass(stacked?: boolean): string { stacked, }), ); +} + +function inputClasses(html: string): string[] { const input = html.match(/]*name="new-member"[^>]*>/)?.[0] ?? ""; - return input.match(/class="([^"]*)"/)?.[1] ?? ""; + return (input.match(/class="([^"]*)"/)?.[1] ?? "").split(" "); } describe("AddMemberForm", () => { - it("shares a row with the type switch, role and button from the sm breakpoint by default", () => { - expect(inputClass().split(" ")).toEqual(expect.arrayContaining(["w-full", "sm:w-auto", "sm:flex-1"])); + it("keeps a minimum width for the input and wraps instead of squeezing it, whatever the viewport", () => { + const classes = inputClasses(render()); + expect(classes).toEqual(expect.arrayContaining(["grow", "basis-72", "min-w-0"])); + // No viewport breakpoint decides the layout: a wide window can still host a narrow column. + expect(classes.filter((c) => /^(sm|md|lg|xl):/.test(c))).toEqual([]); }); - it("gives the input a line of its own when stacked (narrow hosts like the GA tenant editor)", () => { - const classes = inputClass(true).split(" "); - expect(classes).toContain("w-full"); - expect(classes).not.toContain("sm:w-auto"); - expect(classes).not.toContain("sm:flex-1"); + it("gives the input a line of its own when stacked (the GA tenant editor modal)", () => { + const classes = inputClasses(render(true)); + expect(classes).toContain("basis-full"); + expect(classes).not.toContain("basis-72"); + }); + + it("keeps role and Add together, so they wrap as one unit", () => { + const html = render(); + expect(html).toMatch(/