Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,10 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
if (filteredOptions.some((item) => item[mergedFieldNames.value] === mergedSearchValue)) {
return filteredOptions;
}
// Skip creating temp tag option if it matches a disabled option value
if (valueOptions.get(mergedSearchValue)?.disabled) {
return filteredOptions;
}
// Fill search value as option
return [createTagOption(mergedSearchValue), ...filteredOptions];
}, [
Expand All @@ -468,6 +472,7 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
filteredOptions,
mergedSearchValue,
mergedFieldNames,
valueOptions,
]);
const sorter = (inputOptions: DefaultOptionType[]) => {
const sortedOptions = [...inputOptions].sort((a, b) =>
Expand Down Expand Up @@ -628,6 +633,12 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
const formatted = (searchText || '').trim();
// prevent empty tags from appearing when you click the Enter button
if (formatted) {
// Skip disabled options in tags mode
if (valueOptions.get(formatted)?.disabled) {
setSearchValue('');
return;
}

const newRawValues = Array.from(new Set<RawValueType>([...rawValues, formatted]));
triggerChange(newRawValues);
triggerSelect(formatted, true);
Expand Down Expand Up @@ -658,6 +669,11 @@ const Select = React.forwardRef<BaseSelectRef, SelectProps<any, DefaultOptionTyp
.filter((val) => val !== undefined);
}

// Filter out disabled option values in tags mode
if (mode === 'tags') {
patchValues = patchValues.filter((val) => !valueOptions.get(val)?.disabled);
}

const newRawValues = Array.from(new Set<RawValueType>([...rawValues, ...patchValues]));
triggerChange(newRawValues);
newRawValues.forEach((newRawValue) => {
Expand Down
70 changes: 70 additions & 0 deletions tests/Tags.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,54 @@ describe('Select.Tags', () => {
expect(onChange).toHaveBeenCalledWith(['foo'], [{}]);
});

// https://github.com/ant-design/ant-design/issues/30878
it('should not create tag when input matches disabled option on blur', () => {
const onChange = jest.fn();
const { container } = render(
<Select
mode="tags"
onChange={onChange}
options={[
{ value: 'apple', label: 'Apple' },
{ value: 'orange', label: 'Orange', disabled: true },
]}
/>,
);

fireEvent.change(container.querySelector('input'), { target: { value: 'orange' } });
fireEvent.blur(container.querySelector('input'));

jest.runAllTimers();
expect(findSelection(container).textContent).toBe('');
expect(onChange).not.toHaveBeenCalled();
});
Comment thread
EmilyyyLiu marked this conversation as resolved.

// https://github.com/ant-design/ant-design/issues/30878
it('should not create tag when input matches disabled option even with custom filterOption', () => {
const onChange = jest.fn();
const { container } = render(
<Select
mode="tags"
onChange={onChange}
open
options={[
{ value: 'apple', label: 'Apple' },
{ value: 'orange', label: 'Orange', disabled: true },
]}
// Custom filterOption that hides the disabled option from dropdown
filterOption={() => false}
/>,
);

fireEvent.change(container.querySelector('input'), { target: { value: 'orange' } });
fireEvent.keyDown(container.querySelector('input'), { key: 'Enter' });

jest.runAllTimers();
// Should not create tag or show temp option in dropdown
expect(findSelection(container).textContent).toBe('');
expect(onChange).not.toHaveBeenCalled();
});

it('tokenize input', () => {
const handleChange = jest.fn();
const handleSelect = jest.fn();
Expand All @@ -82,6 +130,28 @@ describe('Select.Tags', () => {
expectOpen(container, false);
});

// https://github.com/ant-design/ant-design/issues/30878
it('should not create tag when token matches disabled option with tokenSeparators', () => {
const handleChange = jest.fn();
const { container } = render(
<Select
mode="tags"
tokenSeparators={[',']}
onChange={handleChange}
options={[
{ value: 'apple', label: 'Apple' },
{ value: 'orange', label: 'Orange', disabled: true },
]}
/>,
);

fireEvent.change(container.querySelector('input'), { target: { value: 'apple,orange' } });

// Only 'apple' should be created, 'orange' is disabled and should be skipped
expect(handleChange).toHaveBeenCalledWith(['apple'], expect.anything());
expect(container.querySelector('input').value).toBe('');
});

it('tokenSeparators function overrides string token split with quote-aware splitting', () => {
const handleChange = jest.fn();
const tokenSeparators = jest.fn((input: string) => {
Expand Down
Loading