mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
feat(native-filters): enable filter indicator and make datasource optional (#13148)
* refactor: continue decouple native filters * refactor(native-filters): decouple native filter parms from config modal * lint: fix TS errors * lint: fix TS errors * refactor: update filter badge * refactor: rename ant filters * test: fix tests * test: fix tests * refactor: remove 18 from dataset
This commit is contained in:
parent
5ab613d89e
commit
5aa38ef929
@ -19,7 +19,16 @@
|
|||||||
export default {
|
export default {
|
||||||
id: 1234,
|
id: 1234,
|
||||||
slug: 'dashboardSlug',
|
slug: 'dashboardSlug',
|
||||||
metadata: {},
|
metadata: {
|
||||||
|
filter_configuration: [
|
||||||
|
{
|
||||||
|
id: 'DefaultsID',
|
||||||
|
filterType: 'filter_select',
|
||||||
|
targets: [{}],
|
||||||
|
cascadeParentIds: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
userId: 'mock_user_id',
|
userId: 'mock_user_id',
|
||||||
dash_edit_perm: true,
|
dash_edit_perm: true,
|
||||||
dash_save_perm: true,
|
dash_save_perm: true,
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { FilterType } from 'src/dashboard/components/nativeFilters/types';
|
|
||||||
import { NativeFiltersState } from 'src/dashboard/reducers/types';
|
import { NativeFiltersState } from 'src/dashboard/reducers/types';
|
||||||
|
|
||||||
export const nativeFilters: NativeFiltersState = {
|
export const nativeFilters: NativeFiltersState = {
|
||||||
@ -24,7 +23,7 @@ export const nativeFilters: NativeFiltersState = {
|
|||||||
'NATIVE_FILTER-e7Q8zKixx': {
|
'NATIVE_FILTER-e7Q8zKixx': {
|
||||||
id: 'NATIVE_FILTER-e7Q8zKixx',
|
id: 'NATIVE_FILTER-e7Q8zKixx',
|
||||||
name: 'region',
|
name: 'region',
|
||||||
filterType: FilterType.filter_select,
|
filterType: 'filter_select',
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
datasetId: 2,
|
datasetId: 2,
|
||||||
@ -49,7 +48,7 @@ export const nativeFilters: NativeFiltersState = {
|
|||||||
'NATIVE_FILTER-x9QPw0so1': {
|
'NATIVE_FILTER-x9QPw0so1': {
|
||||||
id: 'NATIVE_FILTER-x9QPw0so1',
|
id: 'NATIVE_FILTER-x9QPw0so1',
|
||||||
name: 'country_code',
|
name: 'country_code',
|
||||||
filterType: FilterType.filter_select,
|
filterType: 'filter_select',
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
datasetId: 2,
|
datasetId: 2,
|
||||||
@ -75,6 +74,9 @@ export const nativeFilters: NativeFiltersState = {
|
|||||||
filtersState: {
|
filtersState: {
|
||||||
'NATIVE_FILTER-e7Q8zKixx': {
|
'NATIVE_FILTER-e7Q8zKixx': {
|
||||||
id: 'NATIVE_FILTER-e7Q8zKixx',
|
id: 'NATIVE_FILTER-e7Q8zKixx',
|
||||||
|
currentState: {
|
||||||
|
value: ['East Asia & Pacific'],
|
||||||
|
},
|
||||||
extraFormData: {
|
extraFormData: {
|
||||||
append_form_data: {
|
append_form_data: {
|
||||||
filters: [
|
filters: [
|
||||||
@ -128,6 +130,9 @@ export const singleNativeFiltersState = {
|
|||||||
[NATIVE_FILTER_ID]: {
|
[NATIVE_FILTER_ID]: {
|
||||||
id: NATIVE_FILTER_ID,
|
id: NATIVE_FILTER_ID,
|
||||||
extraFormData,
|
extraFormData,
|
||||||
|
currentState: {
|
||||||
|
value: ['No, not an ethnic minority'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -40,10 +40,25 @@ Object.defineProperty(window, 'matchMedia', {
|
|||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
jest.mock('@superset-ui/core', () => ({
|
||||||
|
// @ts-ignore
|
||||||
|
...jest.requireActual('@superset-ui/core'),
|
||||||
|
getChartMetadataRegistry: () => ({
|
||||||
|
items: {
|
||||||
|
filter_select: {
|
||||||
|
value: {
|
||||||
|
datasourceCount: 1,
|
||||||
|
behaviors: ['NATIVE_FILTER'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('FiltersConfigModal', () => {
|
describe('FiltersConfigModal', () => {
|
||||||
const mockedProps = {
|
const mockedProps = {
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
initialFilterId: 'DefaultFilterId',
|
initialFilterId: 'DefaultsID',
|
||||||
createNewOnOpen: true,
|
createNewOnOpen: true,
|
||||||
onCancel: jest.fn(),
|
onCancel: jest.fn(),
|
||||||
save: jest.fn(),
|
save: jest.fn(),
|
||||||
|
@ -18,10 +18,10 @@
|
|||||||
*/
|
*/
|
||||||
export const nativeFiltersInfo = {
|
export const nativeFiltersInfo = {
|
||||||
filters: {
|
filters: {
|
||||||
DefaultID1: {
|
DefaultsID: {
|
||||||
id: 'DefaultID1',
|
id: 'DefaultsID',
|
||||||
name: 'test',
|
name: 'test',
|
||||||
type: 'text',
|
type: 'filter_select',
|
||||||
targets: [
|
targets: [
|
||||||
{
|
{
|
||||||
datasetId: 0,
|
datasetId: 0,
|
||||||
|
@ -18,10 +18,9 @@
|
|||||||
*/
|
*/
|
||||||
import { TIME_FILTER_MAP } from 'src/explore/constants';
|
import { TIME_FILTER_MAP } from 'src/explore/constants';
|
||||||
import { getChartIdsInFilterScope } from 'src/dashboard/util/activeDashboardFilters';
|
import { getChartIdsInFilterScope } from 'src/dashboard/util/activeDashboardFilters';
|
||||||
import {
|
import { NativeFiltersState } from 'src/dashboard/reducers/types';
|
||||||
NativeFiltersState,
|
import { getTreeCheckedItems } from '../nativeFilters/FilterConfigModal/utils';
|
||||||
NativeFilterState,
|
import { Layout } from '../../types';
|
||||||
} from 'src/dashboard/reducers/types';
|
|
||||||
|
|
||||||
export enum IndicatorStatus {
|
export enum IndicatorStatus {
|
||||||
Unset = 'UNSET',
|
Unset = 'UNSET',
|
||||||
@ -130,7 +129,7 @@ const getRejectedColumns = (chart: any): Set<string> =>
|
|||||||
);
|
);
|
||||||
|
|
||||||
export type Indicator = {
|
export type Indicator = {
|
||||||
column: string;
|
column?: string;
|
||||||
name: string;
|
name: string;
|
||||||
value: string[];
|
value: string[];
|
||||||
status: IndicatorStatus;
|
status: IndicatorStatus;
|
||||||
@ -172,50 +171,52 @@ export const selectIndicatorsForChart = (
|
|||||||
return indicators;
|
return indicators;
|
||||||
};
|
};
|
||||||
|
|
||||||
const selectNativeIndicatorValue = (
|
|
||||||
filterState: NativeFilterState,
|
|
||||||
): string[] => {
|
|
||||||
const filters = filterState?.extraFormData?.append_form_data?.filters;
|
|
||||||
if (filters?.length) {
|
|
||||||
const filter = filters[0];
|
|
||||||
if ('val' in filter) {
|
|
||||||
const val = filter.val as string | string[];
|
|
||||||
if (Array.isArray(val)) {
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
return [val];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
};
|
|
||||||
|
|
||||||
export const selectNativeIndicatorsForChart = (
|
export const selectNativeIndicatorsForChart = (
|
||||||
nativeFilters: NativeFiltersState,
|
nativeFilters: NativeFiltersState,
|
||||||
chartId: number,
|
chartId: number,
|
||||||
charts: any,
|
charts: any,
|
||||||
|
dashboardLayout: Layout,
|
||||||
): Indicator[] => {
|
): Indicator[] => {
|
||||||
const chart = charts[chartId];
|
const chart = charts[chartId];
|
||||||
|
|
||||||
const appliedColumns = getAppliedColumns(chart);
|
const appliedColumns = getAppliedColumns(chart);
|
||||||
const rejectedColumns = getRejectedColumns(chart);
|
const rejectedColumns = getRejectedColumns(chart);
|
||||||
|
|
||||||
const getStatus = (column: string, value: string[]): IndicatorStatus => {
|
const getStatus = (
|
||||||
if (rejectedColumns.has(column)) return IndicatorStatus.Incompatible;
|
value: string[],
|
||||||
if (appliedColumns.has(column) && value.length > 0) {
|
isAffectedByScope: boolean,
|
||||||
|
column?: string,
|
||||||
|
): IndicatorStatus => {
|
||||||
|
if (!column && isAffectedByScope) {
|
||||||
|
// Filter without datasource
|
||||||
|
return IndicatorStatus.Applied;
|
||||||
|
}
|
||||||
|
if (column && rejectedColumns.has(column))
|
||||||
|
return IndicatorStatus.Incompatible;
|
||||||
|
if (column && appliedColumns.has(column) && value.length > 0) {
|
||||||
return IndicatorStatus.Applied;
|
return IndicatorStatus.Applied;
|
||||||
}
|
}
|
||||||
return IndicatorStatus.Unset;
|
return IndicatorStatus.Unset;
|
||||||
};
|
};
|
||||||
|
|
||||||
const indicators = Object.values(nativeFilters.filters).map(nativeFilter => {
|
const indicators = Object.values(nativeFilters.filters).map(nativeFilter => {
|
||||||
const column = nativeFilter.targets[0].column.name;
|
const isAffectedByScope = getTreeCheckedItems(
|
||||||
|
nativeFilter.scope,
|
||||||
|
dashboardLayout,
|
||||||
|
).some(
|
||||||
|
layoutItem => dashboardLayout[layoutItem]?.meta?.chartId === chartId,
|
||||||
|
);
|
||||||
|
const column = nativeFilter.targets[0]?.column?.name;
|
||||||
const filterState = nativeFilters.filtersState[nativeFilter.id];
|
const filterState = nativeFilters.filtersState[nativeFilter.id];
|
||||||
const value = selectNativeIndicatorValue(filterState);
|
let value = filterState?.currentState?.value ?? [];
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
value = [value];
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
column,
|
column,
|
||||||
name: nativeFilter.name,
|
name: nativeFilter.name,
|
||||||
path: [nativeFilter.id],
|
path: [nativeFilter.id],
|
||||||
status: getStatus(column, value),
|
status: getStatus(value, isAffectedByScope, column),
|
||||||
value,
|
value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -51,15 +51,19 @@ const FilterValue: React.FC<FilterProps> = ({
|
|||||||
const { id, targets, filterType } = filter;
|
const { id, targets, filterType } = filter;
|
||||||
const cascadingFilters = useCascadingFilters(id);
|
const cascadingFilters = useCascadingFilters(id);
|
||||||
const filterState = useFilterState(id);
|
const filterState = useFilterState(id);
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
|
||||||
const [state, setState] = useState([]);
|
const [state, setState] = useState([]);
|
||||||
const [error, setError] = useState<boolean>(false);
|
const [error, setError] = useState<boolean>(false);
|
||||||
const [formData, setFormData] = useState<Partial<QueryFormData>>({});
|
const [formData, setFormData] = useState<Partial<QueryFormData>>({});
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const [target] = targets;
|
const [target] = targets;
|
||||||
const { datasetId = 18, column } = target;
|
const {
|
||||||
|
datasetId,
|
||||||
|
column = {},
|
||||||
|
}: Partial<{ datasetId: number; column: { name?: string } }> = target;
|
||||||
const { name: groupby } = column;
|
const { name: groupby } = column;
|
||||||
const currentValue = filterState.currentState?.value;
|
const currentValue = filterState.currentState?.value;
|
||||||
|
const hasDataSource = !!(datasetId && groupby);
|
||||||
|
const [loading, setLoading] = useState<boolean>(hasDataSource);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newFormData = getFormData({
|
const newFormData = getFormData({
|
||||||
datasetId,
|
datasetId,
|
||||||
@ -71,6 +75,9 @@ const FilterValue: React.FC<FilterProps> = ({
|
|||||||
});
|
});
|
||||||
if (!areObjectsEqual(formData || {}, newFormData)) {
|
if (!areObjectsEqual(formData || {}, newFormData)) {
|
||||||
setFormData(newFormData);
|
setFormData(newFormData);
|
||||||
|
if (!hasDataSource) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
getChartDataRequest({
|
getChartDataRequest({
|
||||||
formData: newFormData,
|
formData: newFormData,
|
||||||
force: false,
|
force: false,
|
||||||
@ -131,7 +138,8 @@ const FilterValue: React.FC<FilterProps> = ({
|
|||||||
height={20}
|
height={20}
|
||||||
width={220}
|
width={220}
|
||||||
formData={formData}
|
formData={formData}
|
||||||
queriesData={state}
|
// For charts that don't have datasource we need workaround for empty placeholder
|
||||||
|
queriesData={hasDataSource ? state : [{ data: [null] }]}
|
||||||
chartType={filterType}
|
chartType={filterType}
|
||||||
// @ts-ignore (update superset-ui)
|
// @ts-ignore (update superset-ui)
|
||||||
hooks={{ setExtraFormData }}
|
hooks={{ setExtraFormData }}
|
||||||
|
@ -21,6 +21,8 @@ import {
|
|||||||
SuperChart,
|
SuperChart,
|
||||||
t,
|
t,
|
||||||
getChartControlPanelRegistry,
|
getChartControlPanelRegistry,
|
||||||
|
getChartMetadataRegistry,
|
||||||
|
Behavior,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { FormInstance } from 'antd/lib/form';
|
import { FormInstance } from 'antd/lib/form';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
@ -39,15 +41,10 @@ import { CustomControlItem } from '@superset-ui/chart-controls';
|
|||||||
import { ColumnSelect } from './ColumnSelect';
|
import { ColumnSelect } from './ColumnSelect';
|
||||||
import { NativeFiltersForm } from './types';
|
import { NativeFiltersForm } from './types';
|
||||||
import FilterScope from './FilterScope';
|
import FilterScope from './FilterScope';
|
||||||
import {
|
import { getControlItems, setFilterFieldValues, useForceUpdate } from './utils';
|
||||||
FilterTypeNames,
|
|
||||||
getControlItems,
|
|
||||||
setFilterFieldValues,
|
|
||||||
useForceUpdate,
|
|
||||||
} from './utils';
|
|
||||||
import { useBackendFormUpdate } from './state';
|
import { useBackendFormUpdate } from './state';
|
||||||
import { getFormData } from '../utils';
|
import { getFormData } from '../utils';
|
||||||
import { Filter, FilterType } from '../types';
|
import { Filter } from '../types';
|
||||||
|
|
||||||
type DatasetSelectValue = {
|
type DatasetSelectValue = {
|
||||||
value: number;
|
value: number;
|
||||||
@ -121,9 +118,25 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
|
|||||||
const controlItems = getControlItems(
|
const controlItems = getControlItems(
|
||||||
controlPanelRegistry.get(formFilter?.filterType),
|
controlPanelRegistry.get(formFilter?.filterType),
|
||||||
);
|
);
|
||||||
useBackendFormUpdate(form, filterId, filterToEdit);
|
|
||||||
|
|
||||||
const initDatasetId = filterToEdit?.targets[0].datasetId;
|
const nativeFilterItems = getChartMetadataRegistry().items;
|
||||||
|
const nativeFilterVizTypes = Object.entries(nativeFilterItems)
|
||||||
|
// @ts-ignore
|
||||||
|
.filter(([, { value }]) =>
|
||||||
|
value.behaviors?.includes(Behavior.NATIVE_FILTER),
|
||||||
|
)
|
||||||
|
.map(([key]) => key);
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
|
const hasDatasource = !!nativeFilterItems[formFilter?.filterType]?.value
|
||||||
|
?.datasourceCount;
|
||||||
|
|
||||||
|
const hasFilledDatasource =
|
||||||
|
(formFilter?.dataset && formFilter?.column) || !hasDatasource;
|
||||||
|
|
||||||
|
useBackendFormUpdate(form, filterId, filterToEdit, hasDatasource);
|
||||||
|
|
||||||
|
const initDatasetId = filterToEdit?.targets[0]?.datasetId;
|
||||||
const initColumn = filterToEdit?.targets[0]?.column?.name;
|
const initColumn = filterToEdit?.targets[0]?.column?.name;
|
||||||
const newFormData = getFormData({
|
const newFormData = getFormData({
|
||||||
datasetId: formFilter?.dataset?.value,
|
datasetId: formFilter?.dataset?.value,
|
||||||
@ -179,69 +192,76 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
|
|||||||
<Input />
|
<Input />
|
||||||
</StyledFormItem>
|
</StyledFormItem>
|
||||||
<StyledFormItem
|
<StyledFormItem
|
||||||
name={['filters', filterId, 'dataset']}
|
name={['filters', filterId, 'filterType']}
|
||||||
initialValue={{ value: initDatasetId }}
|
rules={[{ required: !removed, message: t('Name is required') }]}
|
||||||
label={<StyledLabel>{t('Datasource')}</StyledLabel>}
|
initialValue={filterToEdit?.filterType || 'filter_select'}
|
||||||
rules={[{ required: !removed, message: t('Datasource is required') }]}
|
label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
|
||||||
data-test="datasource-input"
|
|
||||||
>
|
>
|
||||||
<SupersetResourceSelect
|
<Select
|
||||||
initialId={initDatasetId}
|
options={nativeFilterVizTypes.map(filterType => ({
|
||||||
resource="dataset"
|
value: filterType,
|
||||||
searchColumn="table_name"
|
// @ts-ignore
|
||||||
transformItem={datasetToSelectOption}
|
label: nativeFilterItems[filterType]?.value.name,
|
||||||
isMulti={false}
|
}))}
|
||||||
onError={onDatasetSelectError}
|
onChange={({ value }: { value: string }) => {
|
||||||
onChange={e => {
|
setFilterFieldValues(form, filterId, {
|
||||||
// We need reset column when dataset changed
|
filterType: value,
|
||||||
const datasetId = formFilter?.dataset?.value;
|
defaultValue: null,
|
||||||
if (datasetId && e?.value !== datasetId) {
|
});
|
||||||
setFilterFieldValues(form, filterId, {
|
|
||||||
column: null,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
forceUpdate();
|
forceUpdate();
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</StyledFormItem>
|
</StyledFormItem>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
<StyledFormItem
|
{hasDatasource && (
|
||||||
// don't show the column select unless we have a dataset
|
<>
|
||||||
// style={{ display: datasetId == null ? undefined : 'none' }}
|
<StyledFormItem
|
||||||
name={['filters', filterId, 'column']}
|
name={['filters', filterId, 'dataset']}
|
||||||
initialValue={initColumn}
|
initialValue={{ value: initDatasetId }}
|
||||||
label={<StyledLabel>{t('Field')}</StyledLabel>}
|
label={<StyledLabel>{t('Datasource')}</StyledLabel>}
|
||||||
rules={[{ required: !removed, message: t('Field is required') }]}
|
rules={[
|
||||||
data-test="field-input"
|
{ required: !removed, message: t('Datasource is required') },
|
||||||
>
|
]}
|
||||||
<ColumnSelect
|
data-test="datasource-input"
|
||||||
form={form}
|
>
|
||||||
filterId={filterId}
|
<SupersetResourceSelect
|
||||||
datasetId={formFilter?.dataset?.value}
|
initialId={initDatasetId}
|
||||||
onChange={forceUpdate}
|
resource="dataset"
|
||||||
/>
|
searchColumn="table_name"
|
||||||
</StyledFormItem>
|
transformItem={datasetToSelectOption}
|
||||||
<StyledFormItem
|
isMulti={false}
|
||||||
name={['filters', filterId, 'filterType']}
|
onError={onDatasetSelectError}
|
||||||
rules={[{ required: !removed, message: t('Name is required') }]}
|
onChange={e => {
|
||||||
initialValue={filterToEdit?.filterType || FilterType.filter_select}
|
// We need reset column when dataset changed
|
||||||
label={<StyledLabel>{t('Filter Type')}</StyledLabel>}
|
const datasetId = formFilter?.dataset?.value;
|
||||||
>
|
if (datasetId && e?.value !== datasetId) {
|
||||||
<Select
|
setFilterFieldValues(form, filterId, {
|
||||||
options={Object.values(FilterType).map(filterType => ({
|
column: null,
|
||||||
value: filterType,
|
});
|
||||||
label: FilterTypeNames[filterType],
|
}
|
||||||
}))}
|
forceUpdate();
|
||||||
onChange={({ value }: { value: FilterType }) => {
|
}}
|
||||||
setFilterFieldValues(form, filterId, {
|
/>
|
||||||
filterType: value,
|
</StyledFormItem>
|
||||||
defaultValue: null,
|
<StyledFormItem
|
||||||
});
|
// don't show the column select unless we have a dataset
|
||||||
forceUpdate();
|
// style={{ display: datasetId == null ? undefined : 'none' }}
|
||||||
}}
|
name={['filters', filterId, 'column']}
|
||||||
/>
|
initialValue={initColumn}
|
||||||
</StyledFormItem>
|
label={<StyledLabel>{t('Field')}</StyledLabel>}
|
||||||
{formFilter?.dataset && formFilter?.column && (
|
rules={[{ required: !removed, message: t('Field is required') }]}
|
||||||
|
data-test="field-input"
|
||||||
|
>
|
||||||
|
<ColumnSelect
|
||||||
|
form={form}
|
||||||
|
filterId={filterId}
|
||||||
|
datasetId={formFilter?.dataset?.value}
|
||||||
|
onChange={forceUpdate}
|
||||||
|
/>
|
||||||
|
</StyledFormItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{hasFilledDatasource && (
|
||||||
<CleanFormItem
|
<CleanFormItem
|
||||||
name={['filters', filterId, 'defaultValueFormData']}
|
name={['filters', filterId, 'defaultValueFormData']}
|
||||||
hidden
|
hidden
|
||||||
@ -259,25 +279,29 @@ export const FilterConfigForm: React.FC<FilterConfigFormProps> = ({
|
|||||||
data-test="default-input"
|
data-test="default-input"
|
||||||
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
label={<StyledLabel>{t('Default Value')}</StyledLabel>}
|
||||||
>
|
>
|
||||||
{formFilter?.dataset &&
|
{((hasFilledDatasource && formFilter?.defaultValueQueriesData) ||
|
||||||
formFilter?.column &&
|
!hasDatasource) && (
|
||||||
formFilter?.defaultValueQueriesData && (
|
<SuperChart
|
||||||
<SuperChart
|
height={25}
|
||||||
height={25}
|
width={250}
|
||||||
width={250}
|
formData={newFormData}
|
||||||
formData={newFormData}
|
// For charts that don't have datasource we need workaround for empty placeholder
|
||||||
queriesData={formFilter?.defaultValueQueriesData}
|
queriesData={
|
||||||
chartType={formFilter?.filterType}
|
hasDatasource
|
||||||
hooks={{
|
? formFilter?.defaultValueQueriesData
|
||||||
setExtraFormData: ({ currentState }) => {
|
: [{ data: [null] }]
|
||||||
setFilterFieldValues(form, filterId, {
|
}
|
||||||
defaultValue: currentState?.value,
|
chartType={formFilter?.filterType}
|
||||||
});
|
hooks={{
|
||||||
forceUpdate();
|
setExtraFormData: ({ currentState }) => {
|
||||||
},
|
setFilterFieldValues(form, filterId, {
|
||||||
}}
|
defaultValue: currentState?.value,
|
||||||
/>
|
});
|
||||||
)}
|
forceUpdate();
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</StyledFormItem>
|
</StyledFormItem>
|
||||||
<StyledFormItem
|
<StyledFormItem
|
||||||
name={['filters', filterId, 'parentFilter']}
|
name={['filters', filterId, 'parentFilter']}
|
||||||
|
@ -400,20 +400,22 @@ export function FilterConfigModal({
|
|||||||
const formInputs = values.filters[id];
|
const formInputs = values.filters[id];
|
||||||
// if user didn't open a filter, return the original config
|
// if user didn't open a filter, return the original config
|
||||||
if (!formInputs) return filterConfigMap[id];
|
if (!formInputs) return filterConfigMap[id];
|
||||||
|
let target = {};
|
||||||
|
if (formInputs.dataset && formInputs.column) {
|
||||||
|
target = {
|
||||||
|
datasetId: formInputs.dataset.value,
|
||||||
|
column: {
|
||||||
|
name: formInputs.column,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
controlValues: formInputs.controlValues,
|
controlValues: formInputs.controlValues,
|
||||||
name: formInputs.name,
|
name: formInputs.name,
|
||||||
filterType: formInputs.filterType,
|
filterType: formInputs.filterType,
|
||||||
// for now there will only ever be one target
|
// for now there will only ever be one target
|
||||||
targets: [
|
targets: [target],
|
||||||
{
|
|
||||||
datasetId: formInputs.dataset.value,
|
|
||||||
column: {
|
|
||||||
name: formInputs.column,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
defaultValue: formInputs.defaultValue || null,
|
defaultValue: formInputs.defaultValue || null,
|
||||||
cascadeParentIds: formInputs.parentFilter
|
cascadeParentIds: formInputs.parentFilter
|
||||||
? [formInputs.parentFilter.value]
|
? [formInputs.parentFilter.value]
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
/**
|
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
|
||||||
* or more contributor license agreements. See the NOTICE file
|
|
||||||
* distributed with this work for additional information
|
|
||||||
* regarding copyright ownership. The ASF licenses this file
|
|
||||||
* to you under the Apache License, Version 2.0 (the
|
|
||||||
* "License"); you may not use this file except in compliance
|
|
||||||
* with the License. You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing,
|
|
||||||
* software distributed under the License is distributed on an
|
|
||||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
||||||
* KIND, either express or implied. See the License for the
|
|
||||||
* specific language governing permissions and limitations
|
|
||||||
* under the License.
|
|
||||||
*/
|
|
||||||
import React from 'react';
|
|
||||||
import { styled } from '@superset-ui/core';
|
|
||||||
import { Button } from 'src/common/components';
|
|
||||||
import Icon from 'src/components/Icon';
|
|
||||||
import { useFilterConfiguration } from '../state';
|
|
||||||
|
|
||||||
interface Args {
|
|
||||||
filter: any;
|
|
||||||
index: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FiltersListProps {
|
|
||||||
setEditFilter: (arg0: Args) => void;
|
|
||||||
setDataset: (arg0: any) => void;
|
|
||||||
}
|
|
||||||
const FiltersStyle = styled.div`
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
`;
|
|
||||||
|
|
||||||
const FiltersList = ({ setEditFilter, setDataset }: FiltersListProps) => {
|
|
||||||
const filterConfigs = useFilterConfiguration();
|
|
||||||
<>
|
|
||||||
{filterConfigs.map((filter, i: number) => (
|
|
||||||
<FiltersStyle>
|
|
||||||
<Button
|
|
||||||
type="link"
|
|
||||||
key={filter.name}
|
|
||||||
onClick={() => {
|
|
||||||
setEditFilter({ filter, index: i });
|
|
||||||
setDataset(filter.targets[0].datasetId);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{filter.name}
|
|
||||||
</Button>
|
|
||||||
<span
|
|
||||||
role="button"
|
|
||||||
title="Edit dashboard"
|
|
||||||
tabIndex={0}
|
|
||||||
className="action-button"
|
|
||||||
>
|
|
||||||
<Icon name="trash" />
|
|
||||||
</span>
|
|
||||||
</FiltersStyle>
|
|
||||||
))}
|
|
||||||
</>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export default FiltersList;
|
|
@ -79,11 +79,16 @@ export const useBackendFormUpdate = (
|
|||||||
form: FormInstance<NativeFiltersForm>,
|
form: FormInstance<NativeFiltersForm>,
|
||||||
filterId: string,
|
filterId: string,
|
||||||
filterToEdit?: Filter,
|
filterToEdit?: Filter,
|
||||||
|
hasDatasource?: boolean,
|
||||||
) => {
|
) => {
|
||||||
const forceUpdate = useForceUpdate();
|
const forceUpdate = useForceUpdate();
|
||||||
const formFilter = (form.getFieldValue('filters') || {})[filterId];
|
const formFilter = (form.getFieldValue('filters') || {})[filterId];
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let resolvedDefaultValue: any = null;
|
let resolvedDefaultValue: any = null;
|
||||||
|
if (!hasDatasource) {
|
||||||
|
forceUpdate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
// No need to check data set change because it cascading update column
|
// No need to check data set change because it cascading update column
|
||||||
// So check that column exists is enough
|
// So check that column exists is enough
|
||||||
if (!formFilter?.column) {
|
if (!formFilter?.column) {
|
||||||
@ -107,7 +112,7 @@ export const useBackendFormUpdate = (
|
|||||||
if (
|
if (
|
||||||
filterToEdit?.filterType === formFilter?.filterType &&
|
filterToEdit?.filterType === formFilter?.filterType &&
|
||||||
filterToEdit?.targets[0].datasetId === formFilter?.dataset?.value &&
|
filterToEdit?.targets[0].datasetId === formFilter?.dataset?.value &&
|
||||||
formFilter?.column === filterToEdit?.targets[0]?.column?.name
|
formFilter?.column === filterToEdit?.targets[0].column?.name
|
||||||
) {
|
) {
|
||||||
resolvedDefaultValue = filterToEdit?.defaultValue;
|
resolvedDefaultValue = filterToEdit?.defaultValue;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { QueryObjectFilterClause } from '@superset-ui/core';
|
import { QueryObjectFilterClause } from '@superset-ui/core';
|
||||||
import { Column, FilterType, Scope } from '../types';
|
import { Column, Scope } from '../types';
|
||||||
|
|
||||||
export enum Scoping {
|
export enum Scoping {
|
||||||
all,
|
all,
|
||||||
@ -30,7 +30,7 @@ export type AntCallback = (value1?: any, value2?: any) => void;
|
|||||||
export interface NativeFiltersFormItem {
|
export interface NativeFiltersFormItem {
|
||||||
scope: Scope;
|
scope: Scope;
|
||||||
name: string;
|
name: string;
|
||||||
filterType: FilterType;
|
filterType: string;
|
||||||
dataset: {
|
dataset: {
|
||||||
value: number;
|
value: number;
|
||||||
label: string;
|
label: string;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { t } from '@superset-ui/core';
|
|
||||||
import { flatMapDeep } from 'lodash';
|
import { flatMapDeep } from 'lodash';
|
||||||
import { Charts, Layout, LayoutItem } from 'src/dashboard/types';
|
import { Charts, Layout, LayoutItem } from 'src/dashboard/types';
|
||||||
import {
|
import {
|
||||||
@ -29,7 +29,7 @@ import React from 'react';
|
|||||||
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
|
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
|
||||||
import { CustomControlItem } from '@superset-ui/chart-controls';
|
import { CustomControlItem } from '@superset-ui/chart-controls';
|
||||||
import { TreeItem } from './types';
|
import { TreeItem } from './types';
|
||||||
import { FilterType, Scope } from '../types';
|
import { Scope } from '../types';
|
||||||
|
|
||||||
export const useForceUpdate = () => {
|
export const useForceUpdate = () => {
|
||||||
const [, updateState] = React.useState({});
|
const [, updateState] = React.useState({});
|
||||||
@ -155,12 +155,6 @@ export const findFilterScope = (
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FilterTypeNames = {
|
|
||||||
[FilterType.filter_select]: t('Select'),
|
|
||||||
[FilterType.filter_range]: t('Range'),
|
|
||||||
[FilterType.filter_time]: t('Time'),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const setFilterFieldValues = (
|
export const setFilterFieldValues = (
|
||||||
form: FormInstance,
|
form: FormInstance,
|
||||||
filterId: string,
|
filterId: string,
|
||||||
|
@ -27,12 +27,6 @@ export interface Scope {
|
|||||||
excluded: number[];
|
excluded: number[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FilterType {
|
|
||||||
filter_select = 'filter_select',
|
|
||||||
filter_range = 'filter_range',
|
|
||||||
filter_time = 'filter_time',
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The target of a filter is the datasource/column being filtered */
|
/** The target of a filter is the datasource/column being filtered */
|
||||||
export interface Target {
|
export interface Target {
|
||||||
datasetId: number;
|
datasetId: number;
|
||||||
@ -51,10 +45,10 @@ export interface Filter {
|
|||||||
id: string; // randomly generated at filter creation
|
id: string; // randomly generated at filter creation
|
||||||
name: string;
|
name: string;
|
||||||
scope: Scope;
|
scope: Scope;
|
||||||
filterType: FilterType;
|
filterType: string;
|
||||||
// for now there will only ever be one target
|
// for now there will only ever be one target
|
||||||
// when multiple targets are supported, change this to Target[]
|
// when multiple targets are supported, change this to Target[]
|
||||||
targets: [Target];
|
targets: [Partial<Target>];
|
||||||
controlValues: {
|
controlValues: {
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
@ -41,26 +41,34 @@ export const getFormData = ({
|
|||||||
datasetId?: number;
|
datasetId?: number;
|
||||||
inputRef?: RefObject<HTMLInputElement>;
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
cascadingFilters?: object;
|
cascadingFilters?: object;
|
||||||
groupby: string;
|
groupby?: string;
|
||||||
}): Partial<QueryFormData> => ({
|
}): Partial<QueryFormData> => {
|
||||||
adhoc_filters: [],
|
let otherProps: { datasource?: string; groupby?: string[] } = {};
|
||||||
datasource: `${datasetId}__table`,
|
if (datasetId && groupby) {
|
||||||
extra_filters: [],
|
otherProps = {
|
||||||
extra_form_data: cascadingFilters,
|
datasource: `${datasetId}__table`,
|
||||||
granularity_sqla: 'ds',
|
groupby: [groupby],
|
||||||
groupby: [groupby],
|
};
|
||||||
metrics: ['count'],
|
}
|
||||||
row_limit: 10000,
|
return {
|
||||||
showSearch: true,
|
adhoc_filters: [],
|
||||||
currentValue,
|
extra_filters: [],
|
||||||
defaultValue,
|
extra_form_data: cascadingFilters,
|
||||||
time_range: 'No filter',
|
granularity_sqla: 'ds',
|
||||||
time_range_endpoints: ['inclusive', 'exclusive'],
|
metrics: ['count'],
|
||||||
url_params: {},
|
row_limit: 10000,
|
||||||
viz_type: 'filter_select',
|
showSearch: true,
|
||||||
inputRef,
|
currentValue,
|
||||||
...controlValues,
|
defaultValue,
|
||||||
});
|
time_range: 'No filter',
|
||||||
|
time_range_endpoints: ['inclusive', 'exclusive'],
|
||||||
|
url_params: {},
|
||||||
|
viz_type: 'filter_select',
|
||||||
|
inputRef,
|
||||||
|
...controlValues,
|
||||||
|
...otherProps,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export function mergeExtraFormData(
|
export function mergeExtraFormData(
|
||||||
originalExtra: ExtraFormData,
|
originalExtra: ExtraFormData,
|
||||||
|
@ -52,7 +52,13 @@ const sortByStatus = (indicators: Indicator[]): Indicator[] => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mapStateToProps = (
|
const mapStateToProps = (
|
||||||
{ datasources, dashboardFilters, nativeFilters, charts }: any,
|
{
|
||||||
|
datasources,
|
||||||
|
dashboardFilters,
|
||||||
|
nativeFilters,
|
||||||
|
charts,
|
||||||
|
dashboardLayout: { present },
|
||||||
|
}: any,
|
||||||
{ chartId }: FiltersBadgeProps,
|
{ chartId }: FiltersBadgeProps,
|
||||||
) => {
|
) => {
|
||||||
const dashboardIndicators = selectIndicatorsForChart(
|
const dashboardIndicators = selectIndicatorsForChart(
|
||||||
@ -66,6 +72,7 @@ const mapStateToProps = (
|
|||||||
nativeFilters,
|
nativeFilters,
|
||||||
chartId,
|
chartId,
|
||||||
charts,
|
charts,
|
||||||
|
present,
|
||||||
);
|
);
|
||||||
|
|
||||||
const indicators = uniqWith(
|
const indicators = uniqWith(
|
||||||
|
@ -19,16 +19,16 @@
|
|||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Slider } from 'src/common/components';
|
import { Slider } from 'src/common/components';
|
||||||
import { AntdPluginFilterRangeProps } from './types';
|
import { PluginFilterRangeProps } from './types';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
import { getRangeExtraFormData } from '../../utils';
|
import { getRangeExtraFormData } from '../../utils';
|
||||||
|
|
||||||
const Styles = styled.div<AntdPluginFilterStylesProps>`
|
const Styles = styled.div<PluginFilterStylesProps>`
|
||||||
height: ${({ height }) => height};
|
height: ${({ height }) => height};
|
||||||
width: ${({ width }) => width};
|
width: ${({ width }) => width};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function AntdRangeFilter(props: AntdPluginFilterRangeProps) {
|
export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
|
||||||
const { data, formData, height, width, setExtraFormData, inputRef } = props;
|
const { data, formData, height, width, setExtraFormData, inputRef } = props;
|
||||||
const [row] = data;
|
const [row] = data;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
@ -22,10 +22,10 @@ import controlPanel from './controlPanel';
|
|||||||
import transformProps from './transformProps';
|
import transformProps from './transformProps';
|
||||||
import thumbnail from './images/thumbnail.png';
|
import thumbnail from './images/thumbnail.png';
|
||||||
|
|
||||||
export default class AntdRangeFilterPlugin extends ChartPlugin {
|
export default class RangeFilterPlugin extends ChartPlugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
const metadata = new ChartMetadata({
|
const metadata = new ChartMetadata({
|
||||||
name: t('Range filter plugin'),
|
name: t('Range filter'),
|
||||||
description: 'Range filter plugin using AntD',
|
description: 'Range filter plugin using AntD',
|
||||||
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
||||||
thumbnail,
|
thumbnail,
|
||||||
@ -34,7 +34,7 @@ export default class AntdRangeFilterPlugin extends ChartPlugin {
|
|||||||
super({
|
super({
|
||||||
buildQuery,
|
buildQuery,
|
||||||
controlPanel,
|
controlPanel,
|
||||||
loadChart: () => import('./AntdRangeFilter'),
|
loadChart: () => import('./RangeFilterPlugin'),
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
});
|
});
|
||||||
|
@ -22,18 +22,18 @@ import {
|
|||||||
SetExtraFormDataHook,
|
SetExtraFormDataHook,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
|
|
||||||
interface AntdPluginFilterSelectCustomizeProps {
|
interface PluginFilterSelectCustomizeProps {
|
||||||
max?: number;
|
max?: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PluginFilterRangeQueryFormData = QueryFormData &
|
export type PluginFilterRangeQueryFormData = QueryFormData &
|
||||||
AntdPluginFilterStylesProps &
|
PluginFilterStylesProps &
|
||||||
AntdPluginFilterSelectCustomizeProps;
|
PluginFilterSelectCustomizeProps;
|
||||||
|
|
||||||
export type AntdPluginFilterRangeProps = AntdPluginFilterStylesProps & {
|
export type PluginFilterRangeProps = PluginFilterStylesProps & {
|
||||||
data: DataRecord[];
|
data: DataRecord[];
|
||||||
formData: PluginFilterRangeQueryFormData;
|
formData: PluginFilterRangeQueryFormData;
|
||||||
setExtraFormData: SetExtraFormDataHook;
|
setExtraFormData: SetExtraFormDataHook;
|
||||||
|
@ -21,10 +21,10 @@ import { action } from '@storybook/addon-actions';
|
|||||||
import { boolean, withKnobs } from '@storybook/addon-knobs';
|
import { boolean, withKnobs } from '@storybook/addon-knobs';
|
||||||
import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
|
import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
|
||||||
import { mockQueryDataForCountries } from 'spec/fixtures/mockNativeFilters';
|
import { mockQueryDataForCountries } from 'spec/fixtures/mockNativeFilters';
|
||||||
import AntdSelectFilterPlugin from './index';
|
import SelectFilterPlugin from './index';
|
||||||
import transformProps from './transformProps';
|
import transformProps from './transformProps';
|
||||||
|
|
||||||
new AntdSelectFilterPlugin().configure({ key: 'filter_select' }).register();
|
new SelectFilterPlugin().configure({ key: 'filter_select' }).register();
|
||||||
|
|
||||||
getChartTransformPropsRegistry().registerValue('filter_select', transformProps);
|
getChartTransformPropsRegistry().registerValue('filter_select', transformProps);
|
||||||
|
|
@ -19,20 +19,18 @@
|
|||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Select } from 'src/common/components';
|
import { Select } from 'src/common/components';
|
||||||
import { AntdPluginFilterSelectProps } from './types';
|
import { PluginFilterSelectProps } from './types';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
import { getSelectExtraFormData } from '../../utils';
|
import { getSelectExtraFormData } from '../../utils';
|
||||||
|
|
||||||
const Styles = styled.div<AntdPluginFilterStylesProps>`
|
const Styles = styled.div<PluginFilterStylesProps>`
|
||||||
height: ${({ height }) => height};
|
height: ${({ height }) => height};
|
||||||
width: ${({ width }) => width};
|
width: ${({ width }) => width};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const { Option } = Select;
|
const { Option } = Select;
|
||||||
|
|
||||||
export default function AntdPluginFilterSelect(
|
export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
||||||
props: AntdPluginFilterSelectProps,
|
|
||||||
) {
|
|
||||||
const { data, formData, height, width, setExtraFormData } = props;
|
const { data, formData, height, width, setExtraFormData } = props;
|
||||||
const {
|
const {
|
||||||
defaultValue,
|
defaultValue,
|
@ -22,10 +22,10 @@ import controlPanel from './controlPanel';
|
|||||||
import transformProps from './transformProps';
|
import transformProps from './transformProps';
|
||||||
import thumbnail from './images/thumbnail.png';
|
import thumbnail from './images/thumbnail.png';
|
||||||
|
|
||||||
export default class AntdFilterSelectPlugin extends ChartPlugin {
|
export default class FilterSelectPlugin extends ChartPlugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
const metadata = new ChartMetadata({
|
const metadata = new ChartMetadata({
|
||||||
name: t('Select filter plugin'),
|
name: t('Select filter'),
|
||||||
description: 'Select filter plugin using AntD',
|
description: 'Select filter plugin using AntD',
|
||||||
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
||||||
thumbnail,
|
thumbnail,
|
||||||
@ -34,7 +34,7 @@ export default class AntdFilterSelectPlugin extends ChartPlugin {
|
|||||||
super({
|
super({
|
||||||
buildQuery,
|
buildQuery,
|
||||||
controlPanel,
|
controlPanel,
|
||||||
loadChart: () => import('./AntdSelectFilter'),
|
loadChart: () => import('./SelectFilterPlugin'),
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
});
|
});
|
||||||
|
@ -22,9 +22,9 @@ import {
|
|||||||
SetExtraFormDataHook,
|
SetExtraFormDataHook,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { RefObject } from 'react';
|
import { RefObject } from 'react';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
|
|
||||||
interface AntdPluginFilterSelectCustomizeProps {
|
interface PluginFilterSelectCustomizeProps {
|
||||||
defaultValue?: (string | number)[] | null;
|
defaultValue?: (string | number)[] | null;
|
||||||
currentValue?: (string | number)[] | null;
|
currentValue?: (string | number)[] | null;
|
||||||
enableEmptyFilter: boolean;
|
enableEmptyFilter: boolean;
|
||||||
@ -34,17 +34,17 @@ interface AntdPluginFilterSelectCustomizeProps {
|
|||||||
inputRef?: RefObject<HTMLInputElement>;
|
inputRef?: RefObject<HTMLInputElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AntdPluginFilterSelectQueryFormData = QueryFormData &
|
export type PluginFilterSelectQueryFormData = QueryFormData &
|
||||||
AntdPluginFilterStylesProps &
|
PluginFilterStylesProps &
|
||||||
AntdPluginFilterSelectCustomizeProps;
|
PluginFilterSelectCustomizeProps;
|
||||||
|
|
||||||
export type AntdPluginFilterSelectProps = AntdPluginFilterStylesProps & {
|
export type PluginFilterSelectProps = PluginFilterStylesProps & {
|
||||||
data: DataRecord[];
|
data: DataRecord[];
|
||||||
setExtraFormData: SetExtraFormDataHook;
|
setExtraFormData: SetExtraFormDataHook;
|
||||||
formData: AntdPluginFilterSelectQueryFormData;
|
formData: PluginFilterSelectQueryFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_FORM_DATA: AntdPluginFilterSelectCustomizeProps = {
|
export const DEFAULT_FORM_DATA: PluginFilterSelectCustomizeProps = {
|
||||||
defaultValue: null,
|
defaultValue: null,
|
||||||
currentValue: null,
|
currentValue: null,
|
||||||
enableEmptyFilter: false,
|
enableEmptyFilter: false,
|
||||||
|
@ -19,18 +19,18 @@
|
|||||||
import { styled } from '@superset-ui/core';
|
import { styled } from '@superset-ui/core';
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import DateFilterControl from 'src/explore/components/controls/DateFilterControl/DateFilterControl';
|
import DateFilterControl from 'src/explore/components/controls/DateFilterControl/DateFilterControl';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
import { AntdPluginFilterTimeProps } from './types';
|
import { PluginFilterTimeProps } from './types';
|
||||||
|
|
||||||
const DEFAULT_VALUE = 'Last week';
|
const DEFAULT_VALUE = 'Last week';
|
||||||
|
|
||||||
const Styles = styled.div<AntdPluginFilterStylesProps>`
|
const Styles = styled.div<PluginFilterStylesProps>`
|
||||||
height: ${({ height }) => height}px;
|
height: ${({ height }) => height}px;
|
||||||
width: ${({ width }) => width}px;
|
width: ${({ width }) => width}px;
|
||||||
overflow-x: scroll;
|
overflow-x: scroll;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default function AntdTimeFilter(props: AntdPluginFilterTimeProps) {
|
export default function TimeFilterPlugin(props: PluginFilterTimeProps) {
|
||||||
const { formData, setExtraFormData, width } = props;
|
const { formData, setExtraFormData, width } = props;
|
||||||
const { defaultValue, currentValue } = formData;
|
const { defaultValue, currentValue } = formData;
|
||||||
|
|
@ -24,15 +24,16 @@ import thumbnail from './images/thumbnail.png';
|
|||||||
export default class TimeFilterPlugin extends ChartPlugin {
|
export default class TimeFilterPlugin extends ChartPlugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
const metadata = new ChartMetadata({
|
const metadata = new ChartMetadata({
|
||||||
name: t('Time range filter plugin'),
|
name: t('Time filter'),
|
||||||
description: 'Custom time filter plugin',
|
description: 'Custom time filter plugin',
|
||||||
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
behaviors: [Behavior.CROSS_FILTER, Behavior.NATIVE_FILTER],
|
||||||
thumbnail,
|
thumbnail,
|
||||||
|
datasourceCount: 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
super({
|
super({
|
||||||
controlPanel,
|
controlPanel,
|
||||||
loadChart: () => import('./AntdTimeFilter'),
|
loadChart: () => import('./TimeFilterPlugin'),
|
||||||
metadata,
|
metadata,
|
||||||
transformProps,
|
transformProps,
|
||||||
});
|
});
|
||||||
|
@ -21,21 +21,21 @@ import {
|
|||||||
DataRecord,
|
DataRecord,
|
||||||
SetExtraFormDataHook,
|
SetExtraFormDataHook,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { AntdPluginFilterStylesProps } from '../types';
|
import { PluginFilterStylesProps } from '../types';
|
||||||
|
|
||||||
interface PluginFilterTimeCustomizeProps {
|
interface PluginFilterTimeCustomizeProps {
|
||||||
defaultValue?: string | null;
|
defaultValue?: string | null;
|
||||||
currentValue?: string | null;
|
currentValue?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AntdPluginFilterSelectQueryFormData = QueryFormData &
|
export type PluginFilterSelectQueryFormData = QueryFormData &
|
||||||
AntdPluginFilterStylesProps &
|
PluginFilterStylesProps &
|
||||||
PluginFilterTimeCustomizeProps;
|
PluginFilterTimeCustomizeProps;
|
||||||
|
|
||||||
export type AntdPluginFilterTimeProps = AntdPluginFilterStylesProps & {
|
export type PluginFilterTimeProps = PluginFilterStylesProps & {
|
||||||
data: DataRecord[];
|
data: DataRecord[];
|
||||||
setExtraFormData: SetExtraFormDataHook;
|
setExtraFormData: SetExtraFormDataHook;
|
||||||
formData: AntdPluginFilterSelectQueryFormData;
|
formData: PluginFilterSelectQueryFormData;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const DEFAULT_FORM_DATA: PluginFilterTimeCustomizeProps = {
|
export const DEFAULT_FORM_DATA: PluginFilterTimeCustomizeProps = {
|
||||||
|
@ -16,6 +16,6 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
export { default as AntdSelectFilterPlugin } from './Select';
|
export { default as SelectFilterPlugin } from './Select';
|
||||||
export { default as AntdRangeFilterPlugin } from './Range';
|
export { default as RangeFilterPlugin } from './Range';
|
||||||
export { default as TimeFilterPlugin } from './Time';
|
export { default as TimeFilterPlugin } from './Time';
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
export interface AntdPluginFilterStylesProps {
|
export interface PluginFilterStylesProps {
|
||||||
height: number;
|
height: number;
|
||||||
width: number;
|
width: number;
|
||||||
}
|
}
|
||||||
|
@ -60,8 +60,8 @@ import {
|
|||||||
EchartsTimeseriesChartPlugin,
|
EchartsTimeseriesChartPlugin,
|
||||||
} from '@superset-ui/plugin-chart-echarts';
|
} from '@superset-ui/plugin-chart-echarts';
|
||||||
import {
|
import {
|
||||||
AntdSelectFilterPlugin,
|
SelectFilterPlugin,
|
||||||
AntdRangeFilterPlugin,
|
RangeFilterPlugin,
|
||||||
TimeFilterPlugin,
|
TimeFilterPlugin,
|
||||||
} from 'src/filters/components/';
|
} from 'src/filters/components/';
|
||||||
import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin';
|
import FilterBoxChartPlugin from '../FilterBox/FilterBoxChartPlugin';
|
||||||
@ -112,8 +112,8 @@ export default class MainPreset extends Preset {
|
|||||||
new EchartsTimeseriesChartPlugin().configure({
|
new EchartsTimeseriesChartPlugin().configure({
|
||||||
key: 'echarts_timeseries',
|
key: 'echarts_timeseries',
|
||||||
}),
|
}),
|
||||||
new AntdSelectFilterPlugin().configure({ key: 'filter_select' }),
|
new SelectFilterPlugin().configure({ key: 'filter_select' }),
|
||||||
new AntdRangeFilterPlugin().configure({ key: 'filter_range' }),
|
new RangeFilterPlugin().configure({ key: 'filter_range' }),
|
||||||
new TimeFilterPlugin().configure({ key: 'filter_time' }),
|
new TimeFilterPlugin().configure({ key: 'filter_time' }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user