Fix windowed Select/SelectEditor menu constrained to control width#4452
Conversation
Virtualized (windowed) menus can't auto-size to their content the way standard react-select menus do: react-window absolutely-positions its rows at width:100%, so option text never widens the menu and it collapses to the control/cell width. Restore content-based auto-sizing by measuring the widest option label up front (canvas measureText, reusing the same technique as the grid's autosize) and applying an explicit pixel width to the menu in windowed mode. An explicit menuWidth and app-provided rsOptions still take precedence; minWidth:'100%' floors the menu at the control width. SelectEditor's width:'auto' menu workaround now applies only in non-windowed mode - it would otherwise override the measured width and re-collapse the menu. Fixes #4325, #4057
haynesjm42
left a comment
There was a problem hiding this comment.
I think this might need to account for a custom optionRenderer - at least to not apply the calculated width in that case. Looks like the calculation is specifically accounting for the default option renderer.
Is there an approach more like the column autosizing where we find the longest label and then render it (with formatOptionLabel) and measure that rendered content width? Or do we just want to leave it up to the developer if they are providing a custom optionRenderer to also handle the width?
Address review feedback re: custom optionRenderer support. - Size windowed menus by rendering each option's actual menu markup (via the configured optionRenderer, if any), canvas-estimating width, then measuring the widest sample's displayed width in a hidden probe - mirroring the grid's ColumnWidthCalculator. Custom optionRenderers now size correctly instead of being measured by their label string. - Extract the logic to impl/CalcWindowedMenuWidth.ts to keep Select.ts focused. - SelectEditor: pass an empty rsOptions object (rather than undefined) in windowed mode.
|
Good catch, thanks @haynesjm42. Reworked the sizing to use the same render-and-measure approach as the grid's column autosize (
On your "render the longest and measure it" point - that's exactly what it does now. I considered ranking candidates cheaply by label text to bound the work, but per your instinct it's no cheaper-per-item than the grid already accepts (the grid renders every value too), and a Select only sizes one menu once per options-change vs. the grid autosizing many columns over thousands of records - so it now renders all options to rank, like the grid. Pulled the logic out into |
…t-menu-width # Conflicts: # CHANGELOG.md
Demo changes that exercise the hoist-react fix for windowed dropdown menus being constrained to the control/cell width. Paired with xh/hoist-react#4452 (fixes xh/hoist-react#4325). ## Changes - **Forms > Select** - "Large list (windowed)": varied `LARGE_OPTIONS` label lengths so the virtualized menu visibly auto-sizes to its widest content instead of looking identical to a content-width menu. - **Grids > Inline Editing** - the `category` editor now uses `enableWindowed` with a large option list in a narrow (~80px) cell. This is the exact #4325 scenario. Real category values (`US`, `EU`, `BRIC`, ...) are preserved at the front of the list so the seed data and the `category === 'US'` amount-validation rule still apply. Before the hoist-react fix these menus were clamped to the control/cell width and truncated long options; now they grow to fit their content while still virtualizing. ## Testing Verified both demos in the browser against the hoist-react branch (`--env inlineHoist`): the windowed menus auto-size to fit long labels, scroll (virtualized), and the inline-editing validation still behaves.
Summary
Windowed (
enableWindowed)SelectandSelectEditordropdown menus were constrained to the width of the control/cell, truncating option text, whereas standard (non-windowed) menus auto-size to fit their content.Root cause: virtualized menus can't auto-size via CSS.
react-windowabsolutely-positions its rows atwidth: 100%, so option text never widens the menu and it collapses to the control width (thewidth: auto/minWidth: 100%menu style resolves to the control width). This is inherent to virtualization, not specific toreact-windowed-select- the v5 upgrade in #4382 did not resolve it.Fix: restore content-based auto-sizing by measuring the widest option label up front (canvas
measureText, the same technique used by the grid's column autosize) and applying an explicit pixel width to the menu in windowed mode. No customMenuListneeded -react-windowed-selectkeeps handling the virtualization.Changes
Select.ts- computewindowedMenuWidthfrom measured option labels (when options change) and apply it as the menu width in windowed mode. An explicitmenuWidthprop and app-providedrsOptions.stylesstill take precedence;minWidth: '100%'floors the menu at the control width. Falls back to control width when labels aren't measurable strings (e.g. fully customoptionRenderercontent).SelectEditor.ts- itswidth: 'auto'menu workaround now applies only in non-windowed mode; otherwise it would override the measured width and re-collapse the menu.Testing
Verified in Toolbox (running against this branch via
--env inlineHoist): on the windowed Select demo with a narrow control, the menu now grows to fit long option labels while still virtualizing (scrollbar present).tsc --noEmitandeslintclean.Fixes #4325
Related: #4057