fix(popup): Dropdown popup width doesn't match input width when tags collapse in oneLine mode (#39136)

This commit is contained in:
Alexandru Soare
2026-04-14 11:09:26 +03:00
committed by GitHub
parent 5138aa2c11
commit c2a35e2eea
2 changed files with 75 additions and 2 deletions

View File

@@ -814,6 +814,62 @@ test('Maintains stable maxTagCount to prevent click target disappearing in oneLi
expect(withinSelector.getByText('+ 2 ...')).toBeVisible();
});
test('dropdown width matches input width after tags collapse in oneLine mode', async () => {
render(
<div style={{ width: '300px' }}>
<Select
{...defaultProps}
value={[OPTIONS[0], OPTIONS[1], OPTIONS[2]]}
mode="multiple"
oneLine
/>
</div>,
);
await open();
// Wait for RAF to complete and tags to collapse
await waitFor(() => {
const withinSelector = within(
getElementByClassName('.ant-select-selector'),
);
expect(
withinSelector.queryByText(OPTIONS[0].label),
).not.toBeInTheDocument();
expect(withinSelector.getByText('+ 3 ...')).toBeVisible();
});
const selectElement = document.querySelector('.ant-select') as HTMLElement;
expect(selectElement).toBeInTheDocument();
// Mock the select element's width since JSDOM doesn't perform real layout
jest.spyOn(selectElement, 'getBoundingClientRect').mockReturnValue({
width: 300,
height: 32,
top: 0,
left: 0,
right: 300,
bottom: 32,
x: 0,
y: 0,
toJSON: () => ({}),
} as DOMRect);
// Close and reopen to trigger width measurement with mocked value
await type('{esc}');
await open();
const dropdown = document.querySelector(
'.ant-select-dropdown',
) as HTMLElement;
expect(dropdown).toBeInTheDocument();
// Verify the dropdown has inline width matching the mocked select width
await waitFor(() => {
expect(parseInt(dropdown.style.width, 10)).toBe(300);
});
});
test('does not render "Select all" when there are 0 or 1 options', async () => {
const { rerender } = render(
<Select {...defaultProps} options={[]} mode="multiple" allowNewOptions />,

View File

@@ -149,6 +149,8 @@ const Select = forwardRef(
// Prevent maxTagCount change during click events to avoid click target disappearing
const [stableMaxTagCount, setStableMaxTagCount] = useState(maxTagCount);
const isOpeningRef = useRef(false);
const selectContainerRef = useRef<HTMLDivElement>(null);
const [dropdownWidth, setDropdownWidth] = useState<number | true>(true);
useEffect(() => {
if (oneLine) {
@@ -159,12 +161,23 @@ const Select = forwardRef(
requestAnimationFrame(() => {
setStableMaxTagCount(0);
isOpeningRef.current = false;
// Measure collapsed width and update dropdown width
const selectElement =
selectContainerRef.current?.querySelector('.ant-select');
if (selectElement) {
const { width } = selectElement.getBoundingClientRect();
if (width > 0) {
setDropdownWidth(width);
}
}
});
return;
}
if (!isDropdownVisible) {
// When closing, immediately show the first tag
setStableMaxTagCount(1);
setDropdownWidth(true); // Reset to default when closing
isOpeningRef.current = false;
}
return;
@@ -717,7 +730,11 @@ const Select = forwardRef(
};
return (
<StyledContainer className={className} headerPosition={headerPosition}>
<StyledContainer
ref={selectContainerRef}
className={className}
headerPosition={headerPosition}
>
{header && (
<StyledHeader headerPosition={headerPosition}>{header}</StyledHeader>
)}
@@ -777,7 +794,7 @@ const Select = forwardRef(
options={visibleOptions}
optionRender={option => <Space>{option.label || option.value}</Space>}
oneLine={oneLine}
popupMatchSelectWidth
popupMatchSelectWidth={oneLine ? dropdownWidth : true}
css={props.css}
dropdownAlign={DROPDOWN_ALIGN_BOTTOM}
{...props}