Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,25 @@ test('removes duplicated values', async () => {
});
});

test('trims whitespace from pasted comma-separated values', async () => {
render(<AsyncSelect {...defaultProps} mode="multiple" allowNewOptions />);
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a, b, c , d',
},
});
fireEvent(input, paste);
await waitFor(async () => {
const values = await findAllSelectValues();
expect(values.length).toBe(4);
expect(values[0]).toHaveTextContent('a');
expect(values[1]).toHaveTextContent('b');
expect(values[2]).toHaveTextContent('c');
expect(values[3]).toHaveTextContent('d');
});
});

test('renders a custom label', async () => {
const loadOptions = jest.fn(async () => ({
data: [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -694,7 +694,14 @@ const AsyncSelect = forwardRef(
}
} else {
const token = tokenSeparators.find(token => pastedText.includes(token));
const array = token ? uniq(pastedText.split(token)) : [pastedText];
const array = token
? uniq(
pastedText
.split(token)
.map(s => s.trim())
.filter(Boolean),
)
: [pastedText.trim()].filter(Boolean);
const values = (
await Promise.all(array.map(item => getPastedTextValue(item)))
).filter(item => item !== undefined) as AntdLabeledValue[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,23 @@ test('removes duplicated values', async () => {
expect(values[3]).toHaveTextContent('d');
});

test('trims whitespace from pasted comma-separated values', async () => {
render(<Select {...defaultProps} mode="multiple" allowNewOptions />);
const input = getElementByClassName('.ant-select-selection-search-input');
const paste = createEvent.paste(input, {
clipboardData: {
getData: () => 'a, b, c , d',
},
});
fireEvent(input, paste);
const values = await findAllSelectValues();
expect(values.length).toBe(4);
expect(values[0]).toHaveTextContent('a');
expect(values[1]).toHaveTextContent('b');
expect(values[2]).toHaveTextContent('c');
expect(values[3]).toHaveTextContent('d');
});

test('renders a custom label', async () => {
const options = [
{ value: 'John', label: <h1>John</h1> },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,18 @@ describe('SelectFilterPlugin', () => {
expect(options[1]).toHaveTextContent('alpha');
expect(options[2]).toHaveTextContent('beta');
});

test('shows create option for multi-select creatable filter when typing', async () => {
getWrapper({ creatable: true, multiSelect: true });
userEvent.type(screen.getByRole('combobox'), 'brand-new');
expect(await screen.findByTitle('brand-new')).toBeInTheDocument();
});

test('does not show create option when searchAllOptions is true', () => {
getWrapper({ creatable: true, searchAllOptions: true });
userEvent.type(screen.getByRole('combobox'), 'brand-new');
expect(screen.queryByTitle('brand-new')).not.toBeInTheDocument();
});
});

test('Select boolean FALSE value in single-select mode', async () => {
Expand Down Expand Up @@ -1659,6 +1671,49 @@ test('renders standard Select dropdown when operatorType is Exact', () => {
expect(screen.getAllByRole('combobox').length).toBeGreaterThan(0);
});

test('renders created filterState values not in dataset as selectable chips', async () => {
const props = buildSelectFilterProps({
formData: { creatable: true },
filterState: { value: ['custom-created-value'] },
setDataMask: jest.fn(),
});
render(<SelectFilterPlugin {...props} />, {
useRedux: true,
initialState: {
nativeFilters: { filters: { 'test-filter': { name: 'Test Filter' } } },
dataMask: {
'test-filter': {
extraFormData: {},
filterState: { value: ['custom-created-value'] },
},
},
},
});
expect(await screen.findByTitle('custom-created-value')).toBeInTheDocument();
});

test('does not duplicate chip when filterState value is already in the dataset', async () => {
const props = buildSelectFilterProps({
formData: { creatable: true },
filterState: { value: ['boy'] },
setDataMask: jest.fn(),
});
render(<SelectFilterPlugin {...props} />, {
useRedux: true,
initialState: {
nativeFilters: { filters: { 'test-filter': { name: 'Test Filter' } } },
dataMask: {
'test-filter': {
extraFormData: {},
filterState: { value: ['boy'] },
},
},
},
});
await screen.findByTitle('boy');
expect(screen.queryAllByTitle('boy')).toHaveLength(1);
});

test('renders dashboard select dropdown popup under document body', async () => {
jest.useFakeTimers({ advanceTimers: true });
render(<SelectFilterPlugin {...buildSelectFilterProps()} />, {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,23 +315,35 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {

const uniqueOptions = useMemo(() => {
const allOptions = new Set(data.map(el => el[col]));
return [...allOptions].map((value: string) => ({
const baseOptions = [...allOptions].map((value: string) => ({
label: labelFormatter(value, datatype),
value,
isNewOption: false,
}));
}, [data, datatype, col, labelFormatter]);
if (creatable !== false && filterState.value) {
ensureIsArray(filterState.value)
.filter(v => v != null && !hasOption(v, baseOptions, true))
.forEach(v => {
baseOptions.push({ label: String(v), value: v, isNewOption: true });
});
}
return baseOptions;
}, [data, datatype, col, labelFormatter, creatable, filterState.value]);

const options = useMemo(() => {
if (search && !multiSelect && !hasOption(search, uniqueOptions, true)) {
uniqueOptions.unshift({
label: search,
value: search,
isNewOption: true,
});
if (
search &&
!searchAllOptions &&
creatable !== false &&
!hasOption(search, uniqueOptions, true)
) {
return [
{ label: search, value: search, isNewOption: true },
...uniqueOptions,
];
}
return uniqueOptions;
}, [multiSelect, search, uniqueOptions]);
}, [search, uniqueOptions, creatable, searchAllOptions]);

const sortComparator = useCallback(
(a: LabeledValue, b: LabeledValue) => {
Expand Down
Loading