mirror of
https://github.com/apache/superset.git
synced 2026-09-01 13:01:33 +00:00
feat(native-filters): add refresh button to default value picker (#14375)
* feat(native-filters): add refresh button to default value picker * skip flaky test
This commit is contained in:
+112
-28
@@ -17,13 +17,14 @@
|
||||
* under the License.
|
||||
*/
|
||||
import {
|
||||
styled,
|
||||
t,
|
||||
getChartMetadataRegistry,
|
||||
Behavior,
|
||||
AdhocFilter,
|
||||
Behavior,
|
||||
ChartDataResponseResult,
|
||||
getChartMetadataRegistry,
|
||||
JsonResponse,
|
||||
styled,
|
||||
SupersetApiError,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { ColumnMeta, DatasourceMeta } from '@superset-ui/chart-controls';
|
||||
import { FormInstance } from 'antd/lib/form';
|
||||
@@ -38,6 +39,10 @@ import AdhocFilterControl from 'src/explore/components/controls/FilterControl/Ad
|
||||
import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
|
||||
import { addDangerToast } from 'src/messageToasts/actions';
|
||||
import { ClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||
import Button from 'src/components/Button';
|
||||
import { getChartDataRequest } from 'src/chart/chartAction';
|
||||
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
||||
import { waitForAsyncData } from 'src/middleware/asyncEvent';
|
||||
import { ColumnSelect } from './ColumnSelect';
|
||||
import { NativeFiltersForm } from '../types';
|
||||
import {
|
||||
@@ -111,8 +116,22 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
|
||||
parentFilters,
|
||||
}) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const formFilter = (form.getFieldValue('filters') || {})[filterId];
|
||||
const [datasetDetails, setDatasetDetails] = useState<Record<string, any>>();
|
||||
|
||||
// make sure the formFilter is populated
|
||||
if (!form.getFieldValue('filters')) {
|
||||
setNativeFilterFieldValues(form, filterId, filterToEdit || {});
|
||||
forceUpdate();
|
||||
}
|
||||
const formFilter = form.getFieldValue('filters')[filterId];
|
||||
|
||||
useEffect(() => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: filterToEdit?.defaultValue,
|
||||
});
|
||||
forceUpdate();
|
||||
}, [form, forceUpdate, filterId, filterToEdit?.defaultValue]);
|
||||
|
||||
const nativeFilterItems = getChartMetadataRegistry().items;
|
||||
const nativeFilterVizTypes = Object.entries(nativeFilterItems)
|
||||
// @ts-ignore
|
||||
@@ -158,7 +177,56 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
|
||||
formFilter?.filterType,
|
||||
);
|
||||
|
||||
useBackendFormUpdate(form, filterId, filterToEdit, hasDataset, hasColumn);
|
||||
const isDataDirty = formFilter?.isDataDirty ?? true;
|
||||
|
||||
useBackendFormUpdate(form, filterId);
|
||||
|
||||
const refreshHandler = () => {
|
||||
if (!hasDataset || !formFilter?.dataset?.value) {
|
||||
forceUpdate();
|
||||
return;
|
||||
}
|
||||
const formData = getFormData({
|
||||
datasetId: formFilter?.dataset?.value,
|
||||
groupby: formFilter?.column,
|
||||
defaultValue: formFilter?.defaultValue,
|
||||
...formFilter,
|
||||
});
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: null,
|
||||
isDataDirty: false,
|
||||
});
|
||||
forceUpdate();
|
||||
getChartDataRequest({
|
||||
formData,
|
||||
force: false,
|
||||
requestParams: { dashboardId: 0 },
|
||||
}).then(response => {
|
||||
if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) {
|
||||
// deal with getChartDataRequest transforming the response data
|
||||
const result = 'result' in response ? response.result[0] : response;
|
||||
waitForAsyncData(result)
|
||||
.then((asyncResult: ChartDataResponseResult[]) => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: asyncResult,
|
||||
});
|
||||
forceUpdate();
|
||||
})
|
||||
.catch((error: ClientErrorObject) => {
|
||||
// TODO: show error once this logic is moved into new NativeFilter
|
||||
// component
|
||||
console.error(
|
||||
error.message || error.error || t('Check configuration'),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: response.result,
|
||||
});
|
||||
forceUpdate();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const defaultDatasetSelectOptions = Object.values(loadedDatasets).map(
|
||||
datasetToSelectOption,
|
||||
@@ -254,6 +322,7 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
|
||||
// We need reset column when dataset changed
|
||||
if (datasetId && e?.value !== datasetId) {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: null,
|
||||
column: null,
|
||||
});
|
||||
}
|
||||
@@ -275,7 +344,13 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
|
||||
form={form}
|
||||
filterId={filterId}
|
||||
datasetId={datasetId}
|
||||
onChange={forceUpdate}
|
||||
onChange={e => {
|
||||
// We need reset default value when when column changed
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: null,
|
||||
});
|
||||
forceUpdate();
|
||||
}}
|
||||
/>
|
||||
</StyledFormItem>
|
||||
)}
|
||||
@@ -347,27 +422,36 @@ export const FiltersConfigForm: React.FC<FiltersConfigFormProps> = ({
|
||||
isClearable
|
||||
/>
|
||||
</StyledFormItem>
|
||||
<StyledFormItem
|
||||
name={['filters', filterId, 'defaultValue']}
|
||||
initialValue={filterToEdit?.defaultValue}
|
||||
data-test="default-input"
|
||||
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
||||
>
|
||||
{(hasFilledDataset || !hasDataset) && (
|
||||
<DefaultValue
|
||||
setDataMask={({ filterState }) => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: filterState?.value,
|
||||
});
|
||||
forceUpdate();
|
||||
}}
|
||||
filterId={filterId}
|
||||
hasDataset={hasDataset}
|
||||
form={form}
|
||||
formData={newFormData}
|
||||
/>
|
||||
)}
|
||||
</StyledFormItem>
|
||||
<StyledContainer>
|
||||
<StyledFormItem className="bottom" label={<StyledLabel />}>
|
||||
{hasDataset && hasFilledDataset && (
|
||||
<Button onClick={refreshHandler}>
|
||||
{isDataDirty ? t('Populate') : t('Refresh')}
|
||||
</Button>
|
||||
)}
|
||||
</StyledFormItem>
|
||||
<StyledFormItem
|
||||
name={['filters', filterId, 'defaultValue']}
|
||||
initialValue={filterToEdit?.defaultValue}
|
||||
data-test="default-input"
|
||||
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
||||
>
|
||||
{!isDataDirty && (hasFilledDataset || !hasDataset) && (
|
||||
<DefaultValue
|
||||
setDataMask={({ filterState }) => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: filterState?.value,
|
||||
});
|
||||
forceUpdate();
|
||||
}}
|
||||
filterId={filterId}
|
||||
hasDataset={hasDataset}
|
||||
form={form}
|
||||
formData={newFormData}
|
||||
/>
|
||||
)}
|
||||
</StyledFormItem>
|
||||
</StyledContainer>
|
||||
<StyledCheckboxFormItem
|
||||
name={['filters', filterId, 'isInstant']}
|
||||
initialValue={filterToEdit?.isInstant || false}
|
||||
|
||||
+10
-74
@@ -18,102 +18,38 @@
|
||||
*/
|
||||
import { useEffect } from 'react';
|
||||
import { FormInstance } from 'antd/lib/form';
|
||||
import { getChartDataRequest } from 'src/chart/chartAction';
|
||||
import { ChartDataResponseResult, t } from '@superset-ui/core';
|
||||
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
||||
import { waitForAsyncData } from 'src/middleware/asyncEvent';
|
||||
import { ClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||
import { NativeFiltersForm } from '../types';
|
||||
import { setNativeFilterFieldValues, useForceUpdate } from './utils';
|
||||
import { Filter } from '../../types';
|
||||
import { getFormData } from '../../utils';
|
||||
|
||||
// When some fields in form changed we need re-fetch data for Filter defaultValue
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const useBackendFormUpdate = (
|
||||
form: FormInstance<NativeFiltersForm>,
|
||||
filterId: string,
|
||||
filterToEdit?: Filter,
|
||||
hasDatasource?: boolean,
|
||||
hasColumn?: boolean,
|
||||
) => {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const formFilter = (form.getFieldValue('filters') || {})[filterId];
|
||||
useEffect(() => {
|
||||
let resolvedDefaultValue: any = null;
|
||||
if (!hasDatasource) {
|
||||
forceUpdate();
|
||||
return;
|
||||
}
|
||||
// No need to check data set change because it cascading update column
|
||||
// So check that column exists is enough
|
||||
if (hasColumn && !formFilter?.column) {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: [],
|
||||
defaultValue: resolvedDefaultValue,
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!formFilter?.dataset?.value) {
|
||||
// no need to make chart data request if no dataset is defined
|
||||
return;
|
||||
}
|
||||
const formData = getFormData({
|
||||
datasetId: formFilter?.dataset?.value,
|
||||
groupby: formFilter?.column,
|
||||
defaultValue: formFilter?.defaultValue,
|
||||
...formFilter,
|
||||
});
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
isDataDirty: true,
|
||||
defaultValueQueriesData: null,
|
||||
defaultValue: resolvedDefaultValue,
|
||||
});
|
||||
forceUpdate();
|
||||
getChartDataRequest({
|
||||
formData,
|
||||
force: false,
|
||||
requestParams: { dashboardId: 0 },
|
||||
}).then(response => {
|
||||
if (
|
||||
filterToEdit?.filterType === formFilter?.filterType &&
|
||||
filterToEdit?.targets[0].datasetId === formFilter?.dataset?.value &&
|
||||
(!hasColumn ||
|
||||
formFilter?.column === filterToEdit?.targets[0].column?.name)
|
||||
) {
|
||||
resolvedDefaultValue = filterToEdit?.defaultValue;
|
||||
}
|
||||
if (isFeatureEnabled(FeatureFlag.GLOBAL_ASYNC_QUERIES)) {
|
||||
// deal with getChartDataRequest transforming the response data
|
||||
const result = 'result' in response ? response.result[0] : response;
|
||||
waitForAsyncData(result)
|
||||
.then((asyncResult: ChartDataResponseResult[]) => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: asyncResult,
|
||||
defaultValue: resolvedDefaultValue,
|
||||
});
|
||||
forceUpdate();
|
||||
})
|
||||
.catch((error: ClientErrorObject) => {
|
||||
// TODO: show error once this logic is moved into new NativeFilter
|
||||
// component
|
||||
console.error(
|
||||
error.message || error.error || t('Check configuration'),
|
||||
);
|
||||
});
|
||||
} else {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValueQueriesData: response.result,
|
||||
defaultValue: resolvedDefaultValue,
|
||||
});
|
||||
forceUpdate();
|
||||
}
|
||||
});
|
||||
}, [
|
||||
form,
|
||||
formFilter?.filterType,
|
||||
formFilter?.column,
|
||||
formFilter?.dataset?.value,
|
||||
JSON.stringify(formFilter?.adhoc_filters),
|
||||
formFilter?.time_range,
|
||||
forceUpdate,
|
||||
filterId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
setNativeFilterFieldValues(form, filterId, {
|
||||
defaultValue: formFilter?.defaultValue,
|
||||
});
|
||||
forceUpdate();
|
||||
}, [form, formFilter?.defaultValue, forceUpdate, filterId]);
|
||||
};
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ export const setNativeFilterFieldValues = (
|
||||
filterId: string,
|
||||
values: object,
|
||||
) => {
|
||||
const formFilters = form.getFieldValue('filters');
|
||||
const formFilters = form.getFieldValue('filters') || {};
|
||||
form.setFieldsValue({
|
||||
filters: {
|
||||
...formFilters,
|
||||
|
||||
Reference in New Issue
Block a user