mirror of
https://github.com/apache/superset.git
synced 2026-05-13 11:55:16 +00:00
Compare commits
2 Commits
fix/extens
...
fix-asyncs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83fe647a2b | ||
|
|
c81ba84a46 |
@@ -897,6 +897,86 @@ test('fires onChange when pasting a selection', async () => {
|
||||
await waitFor(() => expect(onChange).toHaveBeenCalledTimes(1));
|
||||
});
|
||||
|
||||
test('replaces cached options with search results instead of merging', async () => {
|
||||
const page0Data = Array.from({ length: 10 }, (_, i) => ({
|
||||
label: `Option ${i}`,
|
||||
value: i,
|
||||
}));
|
||||
const searchData = [{ label: 'Search Match', value: 100 }];
|
||||
const loadOptions = jest.fn(async (search: string) => {
|
||||
if (search === '') {
|
||||
return { data: page0Data, totalCount: 100 };
|
||||
}
|
||||
return { data: searchData, totalCount: 1 };
|
||||
});
|
||||
|
||||
render(<AsyncSelect {...defaultProps} options={loadOptions} />);
|
||||
await open();
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(1));
|
||||
|
||||
let options = await findAllSelectOptions();
|
||||
expect(options).toHaveLength(10);
|
||||
|
||||
await type('search');
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(2));
|
||||
|
||||
options = await findAllSelectOptions();
|
||||
expect(options).toHaveLength(1);
|
||||
expect(options[0]).toHaveTextContent('Search Match');
|
||||
});
|
||||
|
||||
test('shows all options when filterOption is false', async () => {
|
||||
const loadOptions = jest.fn(async () => ({
|
||||
data: OPTIONS.slice(0, 10),
|
||||
totalCount: 20,
|
||||
}));
|
||||
|
||||
render(
|
||||
<AsyncSelect
|
||||
{...defaultProps}
|
||||
options={loadOptions}
|
||||
filterOption={false}
|
||||
/>,
|
||||
);
|
||||
await open();
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(1));
|
||||
|
||||
await type('zzz_no_match');
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(2));
|
||||
|
||||
const options = await findAllSelectOptions();
|
||||
expect(options.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
test('restores base options when search is cleared', async () => {
|
||||
const page0Data = Array.from({ length: 10 }, (_, i) => ({
|
||||
label: `Option ${i}`,
|
||||
value: i,
|
||||
}));
|
||||
const searchData = [{ label: 'Search Match', value: 100 }];
|
||||
const loadOptions = jest.fn(async (search: string) => {
|
||||
if (search === '') {
|
||||
return { data: page0Data, totalCount: 100 };
|
||||
}
|
||||
return { data: searchData, totalCount: 1 };
|
||||
});
|
||||
|
||||
render(<AsyncSelect {...defaultProps} options={loadOptions} />);
|
||||
await open();
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(1));
|
||||
|
||||
await type('search');
|
||||
await waitFor(() => expect(loadOptions).toHaveBeenCalledTimes(2));
|
||||
let options = await findAllSelectOptions();
|
||||
expect(options).toHaveLength(1);
|
||||
|
||||
await type('{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}');
|
||||
await waitFor(async () => {
|
||||
options = await findAllSelectOptions();
|
||||
expect(options).toHaveLength(10);
|
||||
});
|
||||
});
|
||||
|
||||
test('does not duplicate options when using numeric values', async () => {
|
||||
render(
|
||||
<AsyncSelect
|
||||
|
||||
@@ -160,6 +160,8 @@ const AsyncSelect = forwardRef(
|
||||
const [allValuesLoaded, setAllValuesLoaded] = useState(false);
|
||||
const selectValueRef = useRef(selectValue);
|
||||
const fetchedQueries = useRef(new Map<string, number>());
|
||||
const initialOptionsRef = useRef<SelectOptionsType>(EMPTY_OPTIONS);
|
||||
const wasSearchingRef = useRef(false);
|
||||
const mappedMode = isSingleMode ? undefined : 'multiple';
|
||||
const allowFetch = !fetchOnlyOnSearch || inputValue;
|
||||
const [maxTagCount, setMaxTagCount] = useState(
|
||||
@@ -335,13 +337,22 @@ const AsyncSelect = forwardRef(
|
||||
const fetchOptions = options as SelectOptionsPagePromise;
|
||||
fetchOptions(search, page, pageSize)
|
||||
.then(({ data, totalCount }: SelectOptionsTypePage) => {
|
||||
const mergedData = mergeData(data);
|
||||
let resultData: SelectOptionsType;
|
||||
if (search && page === 0) {
|
||||
resultData = data.slice().sort(sortComparatorForNoSearch);
|
||||
setSelectOptions(resultData);
|
||||
} else {
|
||||
resultData = mergeData(data);
|
||||
if (!search) {
|
||||
initialOptionsRef.current = resultData;
|
||||
}
|
||||
}
|
||||
fetchedQueries.current.set(key, totalCount);
|
||||
setTotalCount(totalCount);
|
||||
if (
|
||||
!fetchOnlyOnSearch &&
|
||||
search === '' &&
|
||||
mergedData.length >= totalCount
|
||||
resultData.length >= totalCount
|
||||
) {
|
||||
setAllValuesLoaded(true);
|
||||
}
|
||||
@@ -358,6 +369,7 @@ const AsyncSelect = forwardRef(
|
||||
internalOnError,
|
||||
options,
|
||||
pageSize,
|
||||
sortComparatorForNoSearch,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -518,12 +530,26 @@ const AsyncSelect = forwardRef(
|
||||
if (loadingEnabled && allowFetch) {
|
||||
// trigger fetch every time inputValue changes
|
||||
if (inputValue) {
|
||||
wasSearchingRef.current = true;
|
||||
debouncedFetchPage(inputValue, 0);
|
||||
} else {
|
||||
if (wasSearchingRef.current && initialOptionsRef.current.length > 0) {
|
||||
setSelectOptions(
|
||||
[...initialOptionsRef.current].sort(sortComparatorForNoSearch),
|
||||
);
|
||||
}
|
||||
wasSearchingRef.current = false;
|
||||
fetchPage('', 0);
|
||||
}
|
||||
}
|
||||
}, [loadingEnabled, fetchPage, allowFetch, inputValue, debouncedFetchPage]);
|
||||
}, [
|
||||
loadingEnabled,
|
||||
fetchPage,
|
||||
allowFetch,
|
||||
inputValue,
|
||||
debouncedFetchPage,
|
||||
sortComparatorForNoSearch,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading !== undefined && loading !== isLoading) {
|
||||
|
||||
@@ -211,6 +211,10 @@ export const handleFilterOptionHelper = (
|
||||
return filterOption(search, option);
|
||||
}
|
||||
|
||||
if (filterOption === false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filterOption) {
|
||||
const searchValue = search.trim().toLowerCase();
|
||||
if (optionFilterProps?.length) {
|
||||
|
||||
Reference in New Issue
Block a user