From 8fee25c97bcfe22395bd44ba1bf0d11705b6dd2c Mon Sep 17 00:00:00 2001 From: Gautam0507 <110854761+Gautam0507@users.noreply.github.com> Date: Sat, 29 Aug 2026 01:32:50 +0530 Subject: [PATCH] fix(ui): let SettingsFieldRow labels truncate on long content (#3196) * test: reproduce long passkey label overlay (issue #3181) * fix(ui): let SettingsFieldRow labels truncate on long content --------- Co-authored-by: Repro Agent --- .../sections/openchamber/PasskeySettings.tsx | 2 +- .../sections/shared/SettingsSection.tsx | 4 +- .../sections/shared/fieldrow.repro.test.tsx | 71 +++++++++++++++++++ 3 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 packages/ui/src/components/sections/shared/fieldrow.repro.test.tsx diff --git a/packages/ui/src/components/sections/openchamber/PasskeySettings.tsx b/packages/ui/src/components/sections/openchamber/PasskeySettings.tsx index 5dcbc15c..450410d6 100644 --- a/packages/ui/src/components/sections/openchamber/PasskeySettings.tsx +++ b/packages/ui/src/components/sections/openchamber/PasskeySettings.tsx @@ -219,7 +219,7 @@ export const PasskeySettings: React.FC = () => { {passkeys.map((passkey) => ( {passkey.label}} + label={{passkey.label}} alignEnd={false} controlClassName="justify-between sm:flex-1" > diff --git a/packages/ui/src/components/sections/shared/SettingsSection.tsx b/packages/ui/src/components/sections/shared/SettingsSection.tsx index 7672b8a2..5cc520f9 100644 --- a/packages/ui/src/components/sections/shared/SettingsSection.tsx +++ b/packages/ui/src/components/sections/shared/SettingsSection.tsx @@ -310,8 +310,8 @@ export const SettingsFieldRow: React.FC = ({ )} >
-
-
{label}
+
+
{label}
{info != null ? {info} : null}
{description != null ? ( diff --git a/packages/ui/src/components/sections/shared/fieldrow.repro.test.tsx b/packages/ui/src/components/sections/shared/fieldrow.repro.test.tsx new file mode 100644 index 00000000..a13820db --- /dev/null +++ b/packages/ui/src/components/sections/shared/fieldrow.repro.test.tsx @@ -0,0 +1,71 @@ +import { afterEach, describe, expect, test } from 'bun:test'; +import { Window } from 'happy-dom'; +import React, { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; +import { SettingsFieldRow } from '@/components/sections/shared/SettingsSection'; + +// Browser-generated passkeys (e.g. Edge on Windows) use the full user-agent +// string as the device label, so it is very long. +const longLabel = + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0'; + +let windowInstance: Window; +let host: HTMLDivElement; +let root: Root; + +afterEach(() => { + root?.unmount(); +}); + +describe('PasskeySettings long device-name label (issue #3181)', () => { + test('label truncation requires min-w-0 on every flex ancestor', async () => { + windowInstance = new Window({ width: 1000, height: 800 }); + Object.assign(globalThis, { + window: windowInstance, + document: windowInstance.document, + HTMLElement: windowInstance.HTMLElement, + Element: windowInstance.Element, + Node: windowInstance.Node, + IS_REACT_ACT_ENVIRONMENT: true, + }); + + host = document.createElement('div'); + document.body.appendChild(host); + root = createRoot(host); + + await act(async () => { + root.render( + // Mirrors the PasskeySettings row (label = passkey.label with `truncate`). + {longLabel}} + alignEnd={false} + controlClassName="justify-between sm:flex-1" + > + Added Aug 27, 2026 + + , + ); + }); + + // For `truncate` (overflow:hidden + text-overflow:ellipsis + white-space:nowrap) + // to constrain a flex child, every flex ancestor between the row and the + // truncated element must carry min-w-0. Without it, the flex item's default + // min-width:auto stops it shrinking, and the long text overflows the 224px + // label column, overlaying the date and delete button in the control column. + const row = host.firstElementChild as HTMLElement; + const labelColumn = row?.firstElementChild as HTMLElement; + const innerFlex = labelColumn?.firstElementChild as HTMLElement; + const labelDiv = innerFlex?.firstElementChild as HTMLElement; + const labelSpan = labelDiv?.firstElementChild as HTMLElement; + + const flexAncestors = [labelColumn, innerFlex, labelDiv].filter(Boolean); + const missingMinW0 = flexAncestors.filter( + (el) => !String(el.className).split(/\s+/).includes('min-w-0'), + ); + + expect(labelSpan?.className.split(/\s+/)).toContain('truncate'); + // Repro: two intermediate flex containers lack min-w-0, so the ellipsis is + // inert and the long label overflows its column. + expect(missingMinW0.length).toBe(0); + }); +});