Repository navigation
Attachment table: keep the search suggestions aligned with the field - #1209
Conversation
Hello jeanmarcmilletscality,My role is to assist you with the merge of this Available options
Available commands
Status report is not available. |
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
The suggestion menu took its width from a measurement of the search box, read in a ref callback and held in state. That refreshed on every render but never on a resize that causes no render -- a container query, or a side panel narrowing the content box -- so the menu kept a width the field no longer had. It could not bite while the box was pinned to one width; making the box shrinkable is what exposed it. Anchor the menu to the search container's padding box with left/right insets instead, so it follows the field with no measurement and no state. Both insets set means the used width falls out of the over-constrained equation, which already accounts for the menu's own borders -- so the `- 2` goes too, and the menu now lines up with the field's outer edge instead of sitting a pixel short on each side. Also drops two notes that stopped being true when the box became shrinkable: it is no longer a fixed 287px, and no longer this table's narrowest point.
…t with the seam The previous commit anchored the menu to `SearchBoxContainer`'s padding box, which is wider than the field, since the field hugs its own content. A `SearchAnchor` wrapper with `width: fit-content` becomes the menu's containing block instead, so `left: 0; right: 0` resolves to the field's width. Measured over one menu held open while the panel resized underneath it with no re-render, 600px down to 220px and back: the menu tracked the field to 0px at every step, against an overhang reaching 95px before. The field's seam with the open menu moves off `:focus-within` onto the menu's own open state. An outside click blurs the input on `mousedown` while the menu closes on the `mouseup` that follows, so a focus-driven seam snapped back under a menu that was still open. `&&` is required on the new rule: `Input` paints its border from `:hover` and `:focus-within`, both of which outrank a single-class `& > div`. Dropping the consumer's `onBlur` also restores downshift's own, which it had been overriding by being spread after `getInputProps()`, so tabbing out of the field now closes the menu. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
363af93 to
8b557f0
Compare
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
…ses focus Handing blur back to downshift also handed it the selection it performs there: on InputBlur it commits the highlighted row, and committing one runs onSelectedItemChange, which attaches the entity. Tabbing out of an open list with a row under the cursor attached it. A state reducer keeps the close and drops the selection, so blur does the one thing it is wanted for. Three comments come down with it. Two restated what the CSS already says, and the third narrated the event ordering behind the seam; the specificity bump on the seam rule is the one part a reader cannot derive, and it stays.
… floor The anchor the list hangs off tracked the field with `fit-content`, which follows it only while the field is still shrinking. Past its own `min-width` the field holds its width and overflows its container, while the anchor kept shrinking with the panel, so the list came out up to 48px narrower than the field it belongs to. `SearchInput` exports that floor now, and the anchor reads it rather than spelling it out a second time. The list's outline also carried a literal 1px border and 4px radius against the field's `spacing.r1` and `spacing.r4`. At a 14px root those are 0.875px and 3.5px, so on a 2x display the list drew two device pixels against the field's one, along the same frame.
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
…ching on blur The css mapper named identity-obj-proxy, which is in neither package.json nor the lockfile, so nothing that imports src/lib/index.ts could be tested -- and this table does. It now points at the stub already sitting in __mocks__.
…r token spacing.f1 is a fixed pixel used nowhere else on a border; spacing.r1 is the token every other border in the library uses, including the field's own, so the list and the field now hold the same hairline at any root size.
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
🟡 Approve with nitsGood to merge as it stands — the point(s) below are nits: worth closing, not required. Findings
This review is advisory and does not block the merge. Full reviewFindings
|
A name the browser cannot break -- an email, an id -- left the list entirely, by 33px once the field sits on its floor, and past the panel's own border with it. The typography guideline allows wrapping at a delimiter or an ellipsis carrying the full value in a tooltip; the list did neither, while the table of attached entities in this same component already draws the same names through ConstrainedText. The tooltip goes to the right rather than the default top, which over the first suggestion would cover the search field and the query just typed -- the placement Selectv2 already uses for an option's tooltip.
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
Waiting for approvalThe following approvals are needed before I can proceed with the merge:
Peer approvals must include at least 1 approval from the following list: |
|
/approve |
In the queueThe changeset has received all authorizations and has been added to the The changeset will be merged in:
There is no action required on your side. You will be notified here once IMPORTANT Please do not attempt to modify this pull request.
If you need this pull request to be removed from the queue, please contact a The following options are set: approve |
|
I have successfully merged the changeset of this pull request
Please check the status of the associated issue None. Goodbye jeanmarcmilletscality. |
TL;DR — Attachment table: the entity search's suggestion list no longer hangs past the field when the panel narrows, a long name no longer runs out of the list, and tabbing out of an open list no longer attaches whichever suggestion was under the cursor.
NarrowPanelattachment story, list open, the panel then narrowed directly in the DOM so that no re-render refreshes the measurement — the field goes from 301px to 271px. The dashed red rule is the field's right edge, drawn for the capture at the same place in both shots. The list overhangs it by 30px on the left and sits against it on the right.Context / Why
The list is anchored to the search box, and until #1201 that box could not change width, so a width measured once was always right. Making the box shrinkable is what exposed the measurement: the field now narrows with its container, and the list did not follow.
🧩 Approach
The width came from a DOM measurement held in React state. It is now two CSS insets on a wrapper that is itself exactly as wide as the field, so there is no width to keep in sync at all.
Before
After
fit-contentalone tracks the field down to the point where the field stops: past its ownmin-widththe field holds its width and overflows, while the anchor kept shrinking with thepanel, which left the list up to 48px narrower than the field under a 140px frame.
SearchInputnow exports that floor as
SEARCH_INPUT_MIN_WIDTHand the anchor reads it, so the two overflowtogether.
Two behaviours move with it.
The field's seam with the open list was drawn from
:focus-withinwhile the list used its own open state — two different events, since an outside click blurs the input onmousedownand closes the list on themouseupthat follows. Both now read the same open state, so the seam and the list go together.Blur also reaches downshift again, which closes the list — wanted — but commits the highlighted row on the way, and committing one runs
onSelectedItemChange, which attaches the entity. A state reducer keeps the close and drops the selection:A third thing becomes visible once the list is held to the field's floor. A suggestion's name was a plain
<Text>and the list isoverflow: visible, so a name the browser cannot break simply leaves it —jean.dupont@example.comoffers no break opportunity, neither at the dot nor at the at-sign. Swept over theNarrowPanelstory, it crosses the list's right edge under a 200px panel and ends 33px outside at the floor, 3px past the panel's own border. A name with hyphens does break, and its row grows from 36px to 57px instead.The typography guideline allows either: wrap, but only at a delimiter, a dot or a dash; or ellipsis, with the full value in a tooltip. The list did neither, while the attached-entities table in this same file already draws the same names through
ConstrainedText. Now the list does too:lineClampstays at its default of 1 rather than the table's 2. A list walked with the arrow keys wants a constant row height, and a two-line clamp brings backoverflow-wrap: break-word— the mid-segment break the guideline rules out. The placement is not the default either:Tooltipopens upwards, which over the first suggestion means over the search field and the query just typed, so this followsSelectv2, which already places an option's tooltip to the right for the same reason.The name now stops 8px short of the list's edge at every width, which is the row's own padding, and the full value is in the tooltip wherever it is cut.
🔍 Review focus
organisms/attachments/AttachmentTable.tsx › useCombobox— the state reducer is what stops blur attaching an entity; without it, Tab out of an open list with a row highlighted or hovered attaches that row.AttachmentTable.test.tsxpins both sides of it: take the reducer out and the blur test fails while the click test still passes.StyledSearchInput— the&&is load-bearing.Inputpaints its own border from:hoverand:focus-within, and a plain& > divcannot outrank a pseudo-class, so the class is doubled. Dropping an&leaves the open state silently unstyled.SearchAnchor, error path — there the anchor wraps theStackthat sits the field beside itsLoader, so it hugs field plus gap plus loader and the list draws wider than the field. Only reachable when the search fails while the list is already open: the input isdisabledon that branch, so it cannot be focused into the error state. Left as is — closing it means restructuring both branches of the field.ConstrainedTextdecides whether its tooltip is needed by measuring once at mount, with noResizeObserver. Narrowing the panel while the list is already open therefore cuts a name without offering its full value until the list is reopened. The behaviour is the component's own, and the attached-entities table below carries it too.SearchAnchor—position: relativemoved offSearchBoxContaineronto the new wrapper. The list is the only absolutely positioned element in the file, so nothing else changes containing block.AttachmentTable.test.tsx— abeforeAllmakes jsdom report every element as overflowing. jsdom measures everything at zero, soConstrainedTextwould always render the branch a browser takes only when the name fits, and the suite would be exercising markup no user gets. With the stub, all three tests run against the truncated shape.jest.config.js— stylesheets now map to the repo's own__mocks__/styleMock.js. The config namedidentity-obj-proxy, which is in neitherpackage.jsonnor the lockfile, so any suite importingsrc/lib/index.tsfailed to resolve before it could render anything — which is why this table had no test file to add to. One line, and not a dependency change.components/searchinput/SearchInput.component.tsx › SEARCH_INPUT_MIN_WIDTH— the field's floor becomes an export so the anchor reads it instead of repeating it. Not added to theindex.tsbarrel.🧪 How to test
NarrowPanelattachment story and click into the entity search so the list opens.frameWidthcontrol, so React does not re-render. The list should stay flush with the field.🔗 References
What changed
Two comments claimed the search box was a fixed 287px and that this was the table's narrowest point; neither has been true since #1201, and leaving them would send the next reader looking for a floor that no longer exists. One was in this file, the other in the
NarrowPanelstory.The list is drawn with the field's own tokens —
spacing.r1andspacing.r4, where its outlineand its row separators carried a literal
1pxand4px. At a 14px root those are 0.875px and 3.5px, so on a 2× displaythe list drew two device pixels against the field's one and read as a deeper blue along the same
frame.
The
searchInputIsFocusedstate goes with them, along with the list's focused-but-closed bottom border that was its only consumer — with the seam now keyed on the open state, the field keeps its own border there and already draws that edge.