Fix dropdown top item being clipped on auto-scroll (WEB-1194)
Version: 0.8.0 ยท Type: ๐ Bug Fix
Problemโ
When a Select (the Radix Select branch: composed usage and non-searchable declarative options) opens and the selected option sits far down the list, the dropdown auto-scrolls to reveal it โ and the first still-visible item at the top gets clipped, pressed flush against the panel's top edge. It was reproduced on the display-language setting: a Chinese user whose value is "ไธญๆ๏ผ็ฎไฝ๏ผ" (index 8) opens the list, it scrolls down, and "Espaรฑol" at the top is cut off.
The root cause is where the vertical padding lived. Radix SelectPrimitive.Viewport ships an inline overflow: hidden auto plus flex: 1, which makes the viewport itself the scroll container (its flex: 1 overrides the h-(--radix-select-trigger-height) class and grows it to the content's max-h-[268px]). The dropdown's top / bottom py-(--Spacing_8) was applied to that viewport โ i.e. inside the scroll region. So when the browser scrolled the selected item into view, the 8px top padding scrolled away with the content and the top-most visible item lost its breathing room, landing clipped against the edge.
This surfaced after the earlier refactor that removed SelectScrollUpButton / SelectScrollDownButton and switched the content to a native overflow-y-auto: the scrolling paradigm changed, but the padding was left inside the (now natively scrolling) viewport.
Changed Filesโ
packages/design/src/components/Select/styles.tspackages/design/src/components/Select/Select.tsxpackages/design/src/components/Select/__tests__/Select.test.tsx
Changesโ
- Slim
SELECT_VIEWPORT_TOKEN_CLASSdown frommergeClass('pd-select__viewport', 'py-(--Spacing_8)', 'overflow-y-auto')to just the semantic marker'pd-select__viewport': drop the in-scrollpy-(--Spacing_8)(moved to the content frame) and the redundantoverflow-y-auto(Radix's inlineoverflow: hidden autoalready owns scrolling). - Add
py-(--Spacing_8)to theSelectContentcontent className. Radix appliesdisplay:flex; flex-direction:columnto the popper content element, so this padding lands on the non-scrolling flex frame and stays fixed. - Bump
SELECT_DROPDOWN_MAX_HEIGHTfrommax-h-[268px]tomax-h-[270px]. The content carries a 1px border and Tailwind's preflight setsbox-sizing: border-box, somax-heightincludes the border. At 268px the viewport (flex: 1) was only 268 โ 2(border) โ 16(padding) = 250px โ less than 7 ร 36 = 252px โ so a later selected option still left a ~2px clip on the top item when scrolled. Counting the 2px border makes the viewport 252px, exactly 7 ร 36px, so scrolling snaps to item boundaries and no item is clipped. - Leave the shared
SELECT_CONTENT_TOKEN_CLASSuntouched โ it is reused by the searchable Popover (SELECT_POPOVER_CONTENT_CLASS) andMultiSelect(MULTI_SELECT_CONTENT_CLASS), which handle their own padding; scoping the change toSelectContentavoids double padding there. - Add regression tests asserting the vertical padding lives on the listbox (content frame) not the viewport, and that the listbox is
max-h-[270px].
The visual of a non-scrolled panel is unchanged (still an 8px gap between the panel border and the first item); only the scroll-clipping is fixed. The searchable / MultiSelect branches are unaffected.