fix(Select): Prevent closing the select when clicking on a tag (#35487)

This commit is contained in:
Mehmet Salih Yavuz
2025-10-04 08:11:14 +03:00
committed by GitHub
parent 821173f6c4
commit d39c55e941
2 changed files with 62 additions and 3 deletions

View File

@@ -779,6 +779,38 @@ test('Renders only an overflow tag if dropdown is open in oneLine mode', async (
expect(withinSelector.getByText('+ 2 ...')).toBeVisible();
});
// Test for checking the issue described in: https://github.com/apache/superset/issues/35132
test('Maintains stable maxTagCount to prevent click target disappearing in oneLine mode', async () => {
render(
<Select
{...defaultProps}
value={[OPTIONS[0], OPTIONS[1], OPTIONS[2]]}
mode="multiple"
oneLine
/>,
);
const withinSelector = within(getElementByClassName('.ant-select-selector'));
expect(withinSelector.getByText(OPTIONS[0].label)).toBeVisible();
expect(withinSelector.getByText('+ 2 ...')).toBeVisible();
await userEvent.click(getSelect());
expect(withinSelector.getByText(OPTIONS[0].label)).toBeVisible();
await waitFor(() => {
expect(
withinSelector.queryByText(OPTIONS[0].label),
).not.toBeInTheDocument();
expect(withinSelector.getByText('+ 3 ...')).toBeVisible();
});
// Close dropdown
await type('{esc}');
expect(await withinSelector.findByText(OPTIONS[0].label)).toBeVisible();
expect(withinSelector.getByText('+ 2 ...')).toBeVisible();
});
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

@@ -24,6 +24,7 @@ import {
useMemo,
useState,
useCallback,
useRef,
ClipboardEvent,
Ref,
ReactElement,
@@ -143,6 +144,32 @@ const Select = forwardRef(
: 1
: (propsMaxTagCount ?? MAX_TAG_COUNT);
// Prevent maxTagCount change during click events to avoid click target disappearing
const [stableMaxTagCount, setStableMaxTagCount] = useState(maxTagCount);
const isOpeningRef = useRef(false);
useEffect(() => {
if (oneLine) {
if (isDropdownVisible && !isOpeningRef.current) {
// Mark that we're in the opening process
isOpeningRef.current = true;
// Use requestAnimationFrame to ensure DOM has settled after the click
requestAnimationFrame(() => {
setStableMaxTagCount(0);
isOpeningRef.current = false;
});
return;
}
if (!isDropdownVisible) {
// When closing, immediately show the first tag
setStableMaxTagCount(1);
isOpeningRef.current = false;
}
return;
}
setStableMaxTagCount(maxTagCount);
}, [maxTagCount, isDropdownVisible, oneLine]);
const mappedMode = isSingleMode ? undefined : 'multiple';
const sortSelectedFirst = useCallback(
@@ -599,16 +626,16 @@ const Select = forwardRef(
const omittedCount = useMemo(() => {
const num_selected = ensureIsArray(selectValue).length;
const num_shown = maxTagCount as number;
const num_shown = stableMaxTagCount as number;
return num_selected - num_shown - (selectAllMode ? 1 : 0);
}, [maxTagCount, selectAllMode, selectValue]);
}, [stableMaxTagCount, selectAllMode, selectValue]);
const customMaxTagPlaceholder = () =>
`+ ${omittedCount > 0 ? omittedCount : 1} ...`;
// We can't remove the + tag so when Select All
// is the only item omitted, we subtract one from maxTagCount
let actualMaxTagCount = maxTagCount;
let actualMaxTagCount = stableMaxTagCount;
if (
actualMaxTagCount !== 'responsive' &&
omittedCount === 0 &&