chore: Adds lazy loading to the Select component (#15799)

This commit is contained in:
Michael S. Molina 2021-07-22 15:17:31 -03:00 committed by GitHub
parent 04c0680f6e
commit e660de6936
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 195 additions and 98 deletions

View File

@ -144,6 +144,11 @@ InteractiveSelect.argTypes = {
disable: true, disable: true,
}, },
}, },
fetchOnlyOnSearch: {
table: {
disable: true,
},
},
}; };
InteractiveSelect.story = { InteractiveSelect.story = {
@ -296,10 +301,12 @@ const USERS = [
export const AsyncSelect = ({ export const AsyncSelect = ({
withError, withError,
withInitialValue,
responseTime, responseTime,
...rest ...rest
}: SelectProps & { }: SelectProps & {
withError: boolean; withError: boolean;
withInitialValue: boolean;
responseTime: number; responseTime: number;
}) => { }) => {
const [requests, setRequests] = useState<ReactNode[]>([]); const [requests, setRequests] = useState<ReactNode[]>([]);
@ -375,6 +382,11 @@ export const AsyncSelect = ({
<Select <Select
{...rest} {...rest}
options={withError ? fetchUserListError : fetchUserListPage} options={withError ? fetchUserListError : fetchUserListPage}
value={
withInitialValue
? { label: 'Valentina', value: 'Valentina' }
: undefined
}
/> />
</div> </div>
<div <div
@ -398,9 +410,11 @@ export const AsyncSelect = ({
}; };
AsyncSelect.args = { AsyncSelect.args = {
withError: false,
pageSize: 10,
allowNewOptions: false, allowNewOptions: false,
fetchOnlyOnSearch: false,
pageSize: 10,
withError: false,
withInitialValue: false,
}; };
AsyncSelect.argTypes = { AsyncSelect.argTypes = {
@ -431,6 +445,7 @@ AsyncSelect.argTypes = {
type: 'range', type: 'range',
min: 0.5, min: 0.5,
max: 5, max: 5,
step: 0.5,
}, },
}, },
}; };

View File

@ -28,16 +28,17 @@ import React, {
useCallback, useCallback,
} from 'react'; } from 'react';
import { styled, t } from '@superset-ui/core'; import { styled, t } from '@superset-ui/core';
import { Select as AntdSelect } from 'antd'; import AntdSelect, {
import Icons from 'src/components/Icons';
import {
SelectProps as AntdSelectProps, SelectProps as AntdSelectProps,
SelectValue as AntdSelectValue, SelectValue as AntdSelectValue,
LabeledValue as AntdLabeledValue, LabeledValue as AntdLabeledValue,
} from 'antd/lib/select'; } from 'antd/lib/select';
import { DownOutlined, SearchOutlined } from '@ant-design/icons';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { Spin } from 'antd';
import Icons from 'src/components/Icons';
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
import { hasOption } from './utils'; import { hasOption } from './utils';
type AntdSelectAllProps = AntdSelectProps<AntdSelectValue>; type AntdSelectAllProps = AntdSelectProps<AntdSelectValue>;
@ -47,7 +48,6 @@ type PickedSelectProps = Pick<
| 'allowClear' | 'allowClear'
| 'autoFocus' | 'autoFocus'
| 'value' | 'value'
| 'defaultValue'
| 'disabled' | 'disabled'
| 'filterOption' | 'filterOption'
| 'notFoundContent' | 'notFoundContent'
@ -79,6 +79,7 @@ export interface SelectProps extends PickedSelectProps {
options: OptionsType | OptionsPagePromise; options: OptionsType | OptionsPagePromise;
pageSize?: number; pageSize?: number;
invertSelection?: boolean; invertSelection?: boolean;
fetchOnlyOnSearch?: boolean;
} }
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -122,6 +123,10 @@ const StyledError = styled.div`
`} `}
`; `;
const StyledSpin = styled(Spin)`
margin-top: ${({ theme }) => -theme.gridUnit}px;
`;
const MAX_TAG_COUNT = 4; const MAX_TAG_COUNT = 4;
const TOKEN_SEPARATORS = [',', '\n', '\t', ';']; const TOKEN_SEPARATORS = [',', '\n', '\t', ';'];
const DEBOUNCE_TIMEOUT = 500; const DEBOUNCE_TIMEOUT = 500;
@ -137,15 +142,16 @@ const Error = ({ error }: { error: string }) => (
const Select = ({ const Select = ({
allowNewOptions = false, allowNewOptions = false,
ariaLabel, ariaLabel,
fetchOnlyOnSearch,
filterOption = true, filterOption = true,
header = null, header = null,
invertSelection = false,
mode = 'single', mode = 'single',
name, name,
options,
pageSize = DEFAULT_PAGE_SIZE, pageSize = DEFAULT_PAGE_SIZE,
placeholder = t('Select ...'), placeholder = t('Select ...'),
options,
showSearch, showSearch,
invertSelection = false,
value, value,
...props ...props
}: SelectProps) => { }: SelectProps) => {
@ -164,7 +170,8 @@ const Select = ({
const [isDropdownVisible, setIsDropdownVisible] = useState(false); const [isDropdownVisible, setIsDropdownVisible] = useState(false);
const [page, setPage] = useState(0); const [page, setPage] = useState(0);
const [totalCount, setTotalCount] = useState(0); const [totalCount, setTotalCount] = useState(0);
const fetchedQueries = useRef(new Set<string>()); const [loadingEnabled, setLoadingEnabled] = useState(false);
const fetchedQueries = useRef(new Map<string, number>());
const mappedMode = isSingleMode const mappedMode = isSingleMode
? undefined ? undefined
: allowNewOptions : allowNewOptions
@ -177,6 +184,26 @@ const Select = ({
); );
}, [options]); }, [options]);
useEffect(() => {
if (isAsync && value) {
const array: AntdLabeledValue[] = Array.isArray(value)
? (value as AntdLabeledValue[])
: [value as AntdLabeledValue];
const options: AntdLabeledValue[] = [];
array.forEach(element => {
const found = selectOptions.find(
option => option.value === element.value,
);
if (!found) {
options.push(element);
}
});
if (options.length > 0) {
setSelectOptions([...selectOptions, ...options]);
}
}
}, [isAsync, selectOptions, value]);
useEffect(() => { useEffect(() => {
setSelectValue(value); setSelectValue(value);
}, [value]); }, [value]);
@ -185,24 +212,56 @@ const Select = ({
(selectedValue: AntdSelectValue | undefined) => { (selectedValue: AntdSelectValue | undefined) => {
// bringing selected options to the top of the list // bringing selected options to the top of the list
if (selectedValue) { if (selectedValue) {
const currentValue = selectedValue as string[] | string; const topOptions: OptionsType = [];
const topOptions = selectOptions.filter(opt => const otherOptions: OptionsType = [];
Array.isArray(currentValue)
? currentValue.includes(opt.value) selectOptions.forEach(opt => {
: currentValue === opt.value, let found = false;
); if (Array.isArray(selectedValue)) {
const otherOptions = selectOptions.filter( if (isAsync) {
opt => !topOptions.find(tOpt => tOpt.value === opt.value), found =
); (selectedValue as AntdLabeledValue[]).find(
element => element.value === opt.value,
) !== undefined;
} else {
found = selectedValue.includes(opt.value);
}
} else {
found = isAsync
? (selectedValue as AntdLabeledValue).value === opt.value
: selectedValue === opt.value;
}
if (found) {
topOptions.push(opt);
} else {
otherOptions.push(opt);
}
});
// fallback for custom options in tags mode as they // fallback for custom options in tags mode as they
// do not appear in the selectOptions state // do not appear in the selectOptions state
if (!isSingleMode && Array.isArray(currentValue)) { if (!isSingleMode && Array.isArray(selectedValue)) {
// eslint-disable-next-line no-restricted-syntax selectedValue.forEach((val: string | number | AntdLabeledValue) => {
for (const val of currentValue) { if (
if (!topOptions.find(tOpt => tOpt.value === val)) { !topOptions.find(
topOptions.push({ label: val, value: val }); tOpt =>
tOpt.value ===
(isAsync ? (val as AntdLabeledValue)?.value : val),
)
) {
if (isAsync) {
const labelValue = val as AntdLabeledValue;
topOptions.push({
label: labelValue.label,
value: labelValue.value,
});
} else {
const value = val as string | number;
topOptions.push({ label: String(value), value });
}
} }
} });
} }
const sortedOptions = [...topOptions, ...otherOptions]; const sortedOptions = [...topOptions, ...otherOptions];
@ -211,7 +270,7 @@ const Select = ({
} }
} }
}, },
[isSingleMode, selectOptions], [isAsync, isSingleMode, selectOptions],
); );
const handleOnSelect = ( const handleOnSelect = (
@ -220,7 +279,11 @@ const Select = ({
if (isSingleMode) { if (isSingleMode) {
setSelectValue(selectedValue); setSelectValue(selectedValue);
} else { } else {
const currentSelected = Array.isArray(selectValue) ? selectValue : []; const currentSelected = selectValue
? Array.isArray(selectValue)
? selectValue
: [selectValue]
: [];
if ( if (
typeof selectedValue === 'number' || typeof selectedValue === 'number' ||
typeof selectedValue === 'string' typeof selectedValue === 'string'
@ -271,7 +334,9 @@ const Select = ({
const handlePaginatedFetch = useMemo( const handlePaginatedFetch = useMemo(
() => (value: string, page: number, pageSize: number) => { () => (value: string, page: number, pageSize: number) => {
const key = `${value};${page};${pageSize}`; const key = `${value};${page};${pageSize}`;
if (fetchedQueries.current.has(key)) { const cachedCount = fetchedQueries.current.get(key);
if (cachedCount) {
setTotalCount(cachedCount);
return; return;
} }
setLoading(true); setLoading(true);
@ -279,7 +344,7 @@ const Select = ({
fetchOptions(value, page, pageSize) fetchOptions(value, page, pageSize)
.then(({ data, totalCount }: OptionsTypePage) => { .then(({ data, totalCount }: OptionsTypePage) => {
handleData(data); handleData(data);
fetchedQueries.current.add(key); fetchedQueries.current.set(key, totalCount);
setTotalCount(totalCount); setTotalCount(totalCount);
}) })
.catch(onError) .catch(onError)
@ -351,6 +416,11 @@ const Select = ({
const handleOnDropdownVisibleChange = (isDropdownVisible: boolean) => { const handleOnDropdownVisibleChange = (isDropdownVisible: boolean) => {
setIsDropdownVisible(isDropdownVisible); setIsDropdownVisible(isDropdownVisible);
if (isAsync && !loadingEnabled) {
setLoadingEnabled(true);
}
// multiple or tags mode keep the dropdown visible while selecting options // multiple or tags mode keep the dropdown visible while selecting options
// this waits for the dropdown to be closed before sorting the top options // this waits for the dropdown to be closed before sorting the top options
if (!isSingleMode && !isDropdownVisible) { if (!isSingleMode && !isDropdownVisible) {
@ -359,13 +429,20 @@ const Select = ({
}; };
useEffect(() => { useEffect(() => {
const foundOption = hasOption(searchedValue, selectOptions); const allowFetch = !fetchOnlyOnSearch || searchedValue;
if (isAsync && !foundOption) { if (isAsync && loadingEnabled && allowFetch) {
const page = 0; const page = 0;
handlePaginatedFetch(searchedValue, page, pageSize); handlePaginatedFetch(searchedValue, page, pageSize);
setPage(page); setPage(page);
} }
}, [isAsync, searchedValue, selectOptions, pageSize, handlePaginatedFetch]); }, [
isAsync,
searchedValue,
pageSize,
handlePaginatedFetch,
loadingEnabled,
fetchOnlyOnSearch,
]);
useEffect(() => { useEffect(() => {
if (isSingleMode) { if (isSingleMode) {
@ -382,6 +459,16 @@ const Select = ({
return error ? <Error error={error} /> : originNode; return error ? <Error error={error} /> : originNode;
}; };
const SuffixIcon = () => {
if (isLoading) {
return <StyledSpin size="small" />;
}
if (shouldShowSearch && isDropdownVisible) {
return <SearchOutlined />;
}
return <DownOutlined />;
};
return ( return (
<StyledContainer> <StyledContainer>
{header} {header}
@ -391,7 +478,7 @@ const Select = ({
dropdownRender={dropdownRender} dropdownRender={dropdownRender}
filterOption={handleFilterOption} filterOption={handleFilterOption}
getPopupContainer={triggerNode => triggerNode.parentNode} getPopupContainer={triggerNode => triggerNode.parentNode}
loading={isLoading} labelInValue={isAsync}
maxTagCount={MAX_TAG_COUNT} maxTagCount={MAX_TAG_COUNT}
mode={mappedMode} mode={mappedMode}
onDeselect={handleOnDeselect} onDeselect={handleOnDeselect}
@ -406,6 +493,7 @@ const Select = ({
showArrow showArrow
tokenSeparators={TOKEN_SEPARATORS} tokenSeparators={TOKEN_SEPARATORS}
value={selectValue} value={selectValue}
suffixIcon={<SuffixIcon />}
menuItemSelectedIcon={ menuItemSelectedIcon={
invertSelection ? ( invertSelection ? (
<StyledStopOutlined iconSize="m" /> <StyledStopOutlined iconSize="m" />

View File

@ -36,18 +36,11 @@ const cachedSupersetGet = cacheWrapper(
); );
interface DatasetSelectProps { interface DatasetSelectProps {
datasetDetails: Record<string, any> | undefined; onChange: (value: { label: string; value: number }) => void;
datasetId: number; value?: { label: string; value: number };
onChange: (value: number) => void;
value?: { value: number | undefined };
} }
const DatasetSelect = ({ const DatasetSelect = ({ onChange, value }: DatasetSelectProps) => {
datasetDetails,
datasetId,
onChange,
value,
}: DatasetSelectProps) => {
const getErrorMessage = useCallback( const getErrorMessage = useCallback(
({ error, message }: ClientErrorObject) => { ({ error, message }: ClientErrorObject) => {
let errorText = message || error || t('An error has occurred'); let errorText = message || error || t('An error has occurred');
@ -84,15 +77,6 @@ const DatasetSelect = ({
.sort((a: { label: string }, b: { label: string }) => .sort((a: { label: string }, b: { label: string }) =>
a.label.localeCompare(b.label), a.label.localeCompare(b.label),
); );
if (!search) {
const found = data.find(element => element.value === datasetId);
if (!found && datasetDetails?.table_name) {
data.push({
label: datasetDetails.table_name,
value: datasetId,
});
}
}
return { return {
data, data,
totalCount: response.json.count, totalCount: response.json.count,
@ -107,7 +91,7 @@ const DatasetSelect = ({
return ( return (
<Select <Select
ariaLabel={t('Dataset')} ariaLabel={t('Dataset')}
value={value?.value} value={value}
options={loadDatasetOptions} options={loadDatasetOptions}
onChange={onChange} onChange={onChange}
/> />

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations * specific language governing permissions and limitations
* under the License. * under the License.
*/ */
import React, { FC, useEffect, useState } from 'react'; import React, { FC } from 'react';
import { import {
Behavior, Behavior,
SetDataMaskHook, SetDataMaskHook,
@ -48,17 +48,9 @@ const DefaultValue: FC<DefaultValueProps> = ({
formData, formData,
enableNoResults, enableNoResults,
}) => { }) => {
const [loading, setLoading] = useState(hasDataset);
const formFilter = (form.getFieldValue('filters') || {})[filterId]; const formFilter = (form.getFieldValue('filters') || {})[filterId];
const queriesData = formFilter?.defaultValueQueriesData; const queriesData = formFilter?.defaultValueQueriesData;
const loading = hasDataset && queriesData === null;
useEffect(() => {
if (!hasDataset || queriesData !== null) {
setLoading(false);
} else {
setLoading(true);
}
}, [hasDataset, queriesData]);
const value = formFilter.defaultDataMask?.filterState.value; const value = formFilter.defaultDataMask?.filterState.value;
const isMissingRequiredValue = const isMissingRequiredValue =
hasDefaultValue && (value === null || value === undefined); hasDefaultValue && (value === null || value === undefined);

View File

@ -355,8 +355,14 @@ const FiltersConfigForm = (
const hasDataset = !!nativeFilterItems[formFilter?.filterType]?.value const hasDataset = !!nativeFilterItems[formFilter?.filterType]?.value
?.datasourceCount; ?.datasourceCount;
const datasetId =
formFilter?.dataset?.value ??
filterToEdit?.targets[0]?.datasetId ??
mostUsedDataset(loadedDatasets, charts);
const { controlItems = {}, mainControlItems = {} } = formFilter const { controlItems = {}, mainControlItems = {} } = formFilter
? getControlItemsMap({ ? getControlItemsMap({
datasetId,
disabled: false, disabled: false,
forceUpdate, forceUpdate,
form, form,
@ -372,10 +378,9 @@ const FiltersConfigForm = (
const nativeFilterItem = nativeFilterItems[formFilter?.filterType] ?? {}; const nativeFilterItem = nativeFilterItems[formFilter?.filterType] ?? {};
// @ts-ignore // @ts-ignore
const enableNoResults = !!nativeFilterItem.value?.enableNoResults; const enableNoResults = !!nativeFilterItem.value?.enableNoResults;
const datasetId = formFilter?.dataset?.value;
useEffect(() => { useEffect(() => {
if (datasetId && hasColumn) { if (datasetId) {
cachedSupersetGet({ cachedSupersetGet({
endpoint: `/api/v1/dataset/${datasetId}`, endpoint: `/api/v1/dataset/${datasetId}`,
}) })
@ -391,7 +396,7 @@ const FiltersConfigForm = (
addDangerToast(response.message); addDangerToast(response.message);
}); });
} }
}, [datasetId, hasColumn]); }, [datasetId]);
useImperativeHandle(ref, () => ({ useImperativeHandle(ref, () => ({
changeTab(tab: 'configuration' | 'scoping') { changeTab(tab: 'configuration' | 'scoping') {
@ -491,10 +496,6 @@ const FiltersConfigForm = (
[filterId, forceUpdate, form, formFilter, hasDataset], [filterId, forceUpdate, form, formFilter, hasDataset],
); );
const initialDatasetId =
filterToEdit?.targets[0]?.datasetId ??
mostUsedDataset(loadedDatasets, charts);
const newFormData = getFormData({ const newFormData = getFormData({
datasetId, datasetId,
groupby: hasColumn ? formFilter?.column : undefined, groupby: hasColumn ? formFilter?.column : undefined,
@ -508,6 +509,8 @@ const FiltersConfigForm = (
setHasDefaultValue, setHasDefaultValue,
] = useDefaultValue(formFilter, filterToEdit); ] = useDefaultValue(formFilter, filterToEdit);
const showDataset = !datasetId || datasetDetails;
useEffect(() => { useEffect(() => {
if (hasDataset && hasFilledDataset && hasDefaultValue && isDataDirty) { if (hasDataset && hasFilledDataset && hasDefaultValue && isDataDirty) {
refreshHandler(); refreshHandler();
@ -519,6 +522,7 @@ const FiltersConfigForm = (
formFilter, formFilter,
isDataDirty, isDataDirty,
refreshHandler, refreshHandler,
showDataset,
]); ]);
const updateFormValues = useCallback( const updateFormValues = useCallback(
@ -713,24 +717,29 @@ const FiltersConfigForm = (
</StyledContainer> </StyledContainer>
{hasDataset && ( {hasDataset && (
<StyledRowContainer> <StyledRowContainer>
<StyledFormItem {showDataset ? (
name={['filters', filterId, 'dataset']} <StyledFormItem
initialValue={{ value: initialDatasetId }} name={['filters', filterId, 'dataset']}
label={<StyledLabel>{t('Dataset')}</StyledLabel>} label={<StyledLabel>{t('Dataset')}</StyledLabel>}
rules={[ initialValue={
{ required: !removed, message: t('Dataset is required') }, datasetDetails
]} ? {
{...getFiltersConfigModalTestId('datasource-input')} label: datasetDetails.table_name,
> value: datasetDetails.id,
{!datasetId || !hasColumn || datasetDetails ? ( }
: undefined
}
rules={[
{ required: !removed, message: t('Dataset is required') },
]}
{...getFiltersConfigModalTestId('datasource-input')}
>
<DatasetSelect <DatasetSelect
datasetDetails={datasetDetails} onChange={(value: { label: string; value: number }) => {
datasetId={initialDatasetId}
onChange={(value: number) => {
// We need to reset the column when the dataset has changed // We need to reset the column when the dataset has changed
if (value !== datasetId) { if (value.value !== datasetId) {
setNativeFilterFieldValues(form, filterId, { setNativeFilterFieldValues(form, filterId, {
dataset: { value }, dataset: value,
defaultDataMask: null, defaultDataMask: null,
column: null, column: null,
}); });
@ -738,10 +747,12 @@ const FiltersConfigForm = (
forceUpdate(); forceUpdate();
}} }}
/> />
) : ( </StyledFormItem>
) : (
<StyledFormItem label={<StyledLabel>{t('Dataset')}</StyledLabel>}>
<Loading position="inline-centered" /> <Loading position="inline-centered" />
)} </StyledFormItem>
</StyledFormItem> )}
{hasDataset && {hasDataset &&
Object.keys(mainControlItems).map( Object.keys(mainControlItems).map(
key => mainControlItems[key].element, key => mainControlItems[key].element,
@ -784,11 +795,8 @@ const FiltersConfigForm = (
required={hasDefaultValue} required={hasDefaultValue}
rules={[ rules={[
{ {
validator: (rule, value) => { validator: () => {
const hasValue = if (formFilter?.defaultDataMask?.filterState?.value) {
value?.filterState?.value !== null &&
value?.filterState?.value !== undefined;
if (hasValue) {
return Promise.resolve(); return Promise.resolve();
} }
return Promise.reject( return Promise.reject(

View File

@ -63,6 +63,7 @@ const filterMock: Filter = {
}; };
const createProps: () => ControlItemsProps = () => ({ const createProps: () => ControlItemsProps = () => ({
datasetId: 1,
disabled: false, disabled: false,
forceUpdate: jest.fn(), forceUpdate: jest.fn(),
form: formMock, form: formMock,

View File

@ -41,6 +41,7 @@ import { Filter } from '../../types';
import { ColumnSelect } from './ColumnSelect'; import { ColumnSelect } from './ColumnSelect';
export interface ControlItemsProps { export interface ControlItemsProps {
datasetId: number;
disabled: boolean; disabled: boolean;
forceUpdate: Function; forceUpdate: Function;
form: FormInstance<NativeFiltersForm>; form: FormInstance<NativeFiltersForm>;
@ -56,6 +57,7 @@ const CleanFormItem = styled(FormItem)`
`; `;
export default function getControlItemsMap({ export default function getControlItemsMap({
datasetId,
disabled, disabled,
forceUpdate, forceUpdate,
form, form,
@ -87,7 +89,6 @@ export default function getControlItemsMap({
filterToEdit?.controlValues?.[mainControlItem.name] ?? filterToEdit?.controlValues?.[mainControlItem.name] ??
mainControlItem?.config?.default; mainControlItem?.config?.default;
const initColumn = filterToEdit?.targets[0]?.column?.name; const initColumn = filterToEdit?.targets[0]?.column?.name;
const datasetId = formFilter?.dataset?.value;
const element = ( const element = (
<> <>

View File

@ -28,7 +28,7 @@ import {
TimeGrainFilterPlugin, TimeGrainFilterPlugin,
} from 'src/filters/components'; } from 'src/filters/components';
import { render, screen, waitFor } from 'spec/helpers/testing-library'; import { render, screen, waitFor } from 'spec/helpers/testing-library';
import mockDatasource, { datasourceId } from 'spec/fixtures/mockDatasource'; import mockDatasource, { id, datasourceId } from 'spec/fixtures/mockDatasource';
import chartQueries from 'spec/fixtures/mockChartQueries'; import chartQueries from 'spec/fixtures/mockChartQueries';
import { import {
FiltersConfigModal, FiltersConfigModal,
@ -71,9 +71,9 @@ const noTemporalColumnsState = () => {
}; };
}; };
fetchMock.get('glob:*/api/v1/dataset/1', { const datasetResult = (id: number) => ({
description_columns: {}, description_columns: {},
id: 1, id,
label_columns: { label_columns: {
columns: 'Columns', columns: 'Columns',
table_name: 'Table Name', table_name: 'Table Name',
@ -87,11 +87,14 @@ fetchMock.get('glob:*/api/v1/dataset/1', {
}, },
], ],
table_name: 'birth_names', table_name: 'birth_names',
id: 1, id,
}, },
show_columns: ['id', 'table_name'], show_columns: ['id', 'table_name'],
}); });
fetchMock.get('glob:*/api/v1/dataset/1', datasetResult(1));
fetchMock.get(`glob:*/api/v1/dataset/${id}`, datasetResult(id));
fetchMock.post('glob:*/api/v1/chart/data', { fetchMock.post('glob:*/api/v1/chart/data', {
result: [ result: [
{ {
@ -320,6 +323,11 @@ test('validates the pre-filter value', async () => {
test("doesn't render time range pre-filter if there are no temporal columns in datasource", async () => { test("doesn't render time range pre-filter if there are no temporal columns in datasource", async () => {
defaultRender(noTemporalColumnsState()); defaultRender(noTemporalColumnsState());
userEvent.click(screen.getByText(DATASET_REGEX));
await waitFor(() => {
expect(screen.queryByLabelText('Loading')).not.toBeInTheDocument();
userEvent.click(screen.getByText('birth_names'));
});
userEvent.click(screen.getByText(ADVANCED_REGEX)); userEvent.click(screen.getByText(ADVANCED_REGEX));
userEvent.click(getCheckbox(PRE_FILTER_REGEX)); userEvent.click(getCheckbox(PRE_FILTER_REGEX));
await waitFor(() => await waitFor(() =>