mirror of
https://github.com/apache/superset.git
synced 2026-06-01 21:59:26 +00:00
feat: support mulitple temporal filters in AdhocFilter and move the Time Section away (#21767)
This commit is contained in:
@@ -22,6 +22,7 @@ import { DndItemType } from 'src/explore/components/DndItemType';
|
||||
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
|
||||
import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
|
||||
import { OptionSortType } from 'src/explore/types';
|
||||
import { useGetTimeRangeLabel } from 'src/explore/components/controls/FilterControl/utils';
|
||||
import OptionWrapper from './OptionWrapper';
|
||||
|
||||
export interface DndAdhocFilterOptionProps {
|
||||
@@ -45,6 +46,8 @@ export default function DndAdhocFilterOption({
|
||||
partitionColumn,
|
||||
index,
|
||||
}: DndAdhocFilterOptionProps) {
|
||||
const { actualTimeRange, title } = useGetTimeRangeLabel(adhocFilter);
|
||||
|
||||
return (
|
||||
<AdhocFilterPopoverTrigger
|
||||
key={index}
|
||||
@@ -57,8 +60,8 @@ export default function DndAdhocFilterOption({
|
||||
<OptionWrapper
|
||||
key={index}
|
||||
index={index}
|
||||
label={adhocFilter.getDefaultLabel()}
|
||||
tooltipTitle={adhocFilter.getTooltipTitle()}
|
||||
label={actualTimeRange ?? adhocFilter.getDefaultLabel()}
|
||||
tooltipTitle={title ?? adhocFilter.getTooltipTitle()}
|
||||
clickClose={onClickClose}
|
||||
onShiftOptions={onShiftOptions}
|
||||
type={DndItemType.FilterOption}
|
||||
|
||||
@@ -17,7 +17,19 @@
|
||||
* under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { FeatureFlag, GenericDataType } from '@superset-ui/core';
|
||||
import thunk from 'redux-thunk';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import {
|
||||
ensureIsArray,
|
||||
FeatureFlag,
|
||||
GenericDataType,
|
||||
QueryFormData,
|
||||
} from '@superset-ui/core';
|
||||
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||
import { TimeseriesDefaultFormData } from '@superset-ui/plugin-chart-echarts';
|
||||
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import AdhocMetric from 'src/explore/components/controls/MetricControl/AdhocMetric';
|
||||
import AdhocFilter, {
|
||||
@@ -28,7 +40,6 @@ import {
|
||||
DndFilterSelectProps,
|
||||
} from 'src/explore/components/controls/DndColumnSelectControl/DndFilterSelect';
|
||||
import { PLACEHOLDER_DATASOURCE } from 'src/dashboard/constants';
|
||||
import { TimeseriesDefaultFormData } from '@superset-ui/plugin-chart-echarts';
|
||||
|
||||
const defaultProps: DndFilterSelectProps = {
|
||||
type: 'DndFilterSelect',
|
||||
@@ -56,8 +67,32 @@ afterAll(() => {
|
||||
window.featureFlags = {};
|
||||
});
|
||||
|
||||
const mockStore = configureStore([thunk]);
|
||||
const store = mockStore({});
|
||||
|
||||
function setup({
|
||||
value = undefined,
|
||||
formData = baseFormData,
|
||||
columns = [],
|
||||
}: {
|
||||
value?: AdhocFilter;
|
||||
formData?: QueryFormData;
|
||||
columns?: ColumnMeta[];
|
||||
} = {}) {
|
||||
return (
|
||||
<Provider store={store}>
|
||||
<DndFilterSelect
|
||||
{...defaultProps}
|
||||
value={ensureIsArray(value)}
|
||||
formData={formData}
|
||||
columns={columns}
|
||||
/>
|
||||
</Provider>
|
||||
);
|
||||
}
|
||||
|
||||
test('renders with default props', async () => {
|
||||
render(<DndFilterSelect {...defaultProps} />, { useDnd: true });
|
||||
render(setup(), { useDnd: true });
|
||||
expect(
|
||||
await screen.findByText('Drop columns or metrics here'),
|
||||
).toBeInTheDocument();
|
||||
@@ -68,7 +103,7 @@ test('renders with value', async () => {
|
||||
sqlExpression: 'COUNT(*)',
|
||||
expressionType: EXPRESSION_TYPES.SQL,
|
||||
});
|
||||
render(<DndFilterSelect {...defaultProps} value={[value]} />, {
|
||||
render(setup({ value }), {
|
||||
useDnd: true,
|
||||
});
|
||||
expect(await screen.findByText('COUNT(*)')).toBeInTheDocument();
|
||||
@@ -76,14 +111,13 @@ test('renders with value', async () => {
|
||||
|
||||
test('renders options with saved metric', async () => {
|
||||
render(
|
||||
<DndFilterSelect
|
||||
{...defaultProps}
|
||||
formData={{
|
||||
setup({
|
||||
formData: {
|
||||
...baseFormData,
|
||||
...TimeseriesDefaultFormData,
|
||||
metrics: ['saved_metric'],
|
||||
}}
|
||||
/>,
|
||||
},
|
||||
}),
|
||||
{
|
||||
useDnd: true,
|
||||
},
|
||||
@@ -95,17 +129,16 @@ test('renders options with saved metric', async () => {
|
||||
|
||||
test('renders options with column', async () => {
|
||||
render(
|
||||
<DndFilterSelect
|
||||
{...defaultProps}
|
||||
columns={[
|
||||
setup({
|
||||
columns: [
|
||||
{
|
||||
id: 1,
|
||||
type: 'VARCHAR',
|
||||
type_generic: GenericDataType.STRING,
|
||||
column_name: 'Column',
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
],
|
||||
}),
|
||||
{
|
||||
useDnd: true,
|
||||
},
|
||||
@@ -121,14 +154,13 @@ test('renders options with adhoc metric', async () => {
|
||||
metric_name: 'avg__num',
|
||||
});
|
||||
render(
|
||||
<DndFilterSelect
|
||||
{...defaultProps}
|
||||
formData={{
|
||||
setup({
|
||||
formData: {
|
||||
...baseFormData,
|
||||
...TimeseriesDefaultFormData,
|
||||
metrics: [adhocMetric],
|
||||
}}
|
||||
/>,
|
||||
},
|
||||
}),
|
||||
{
|
||||
useDnd: true,
|
||||
},
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
FeatureFlag,
|
||||
hasGenericChartAxes,
|
||||
isFeatureEnabled,
|
||||
logging,
|
||||
Metric,
|
||||
@@ -27,7 +28,12 @@ import {
|
||||
SupersetClient,
|
||||
t,
|
||||
} from '@superset-ui/core';
|
||||
import { ColumnMeta, withDndFallback } from '@superset-ui/chart-controls';
|
||||
import {
|
||||
ColumnMeta,
|
||||
isColumnMeta,
|
||||
isTemporalColumn,
|
||||
withDndFallback,
|
||||
} from '@superset-ui/chart-controls';
|
||||
import {
|
||||
OPERATOR_ENUM_TO_OPERATOR_TYPE,
|
||||
Operators,
|
||||
@@ -50,6 +56,7 @@ import { DndItemType } from 'src/explore/components/DndItemType';
|
||||
import { ControlComponentProps } from 'src/explore/components/Control';
|
||||
import AdhocFilterControl from '../FilterControl/AdhocFilterControl';
|
||||
import DndAdhocFilterOption from './DndAdhocFilterOption';
|
||||
import { useDefaultTimeFilter } from '../DateFilterControl/utils';
|
||||
|
||||
const EMPTY_OBJECT = {};
|
||||
const DND_ACCEPTED_TYPES = [
|
||||
@@ -324,6 +331,7 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
|
||||
togglePopover(true);
|
||||
}, [togglePopover]);
|
||||
|
||||
const defaultTimeFilter = useDefaultTimeFilter();
|
||||
const adhocFilter = useMemo(() => {
|
||||
if (isSavedMetric(droppedItem)) {
|
||||
return new AdhocFilter({
|
||||
@@ -346,6 +354,15 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
|
||||
config.operator = OPERATOR_ENUM_TO_OPERATOR_TYPE[Operators.IN].operation;
|
||||
config.operatorId = Operators.IN;
|
||||
}
|
||||
if (
|
||||
hasGenericChartAxes &&
|
||||
isColumnMeta(droppedItem) &&
|
||||
isTemporalColumn(droppedItem?.column_name, props.datasource)
|
||||
) {
|
||||
config.operator = Operators.TEMPORAL_RANGE;
|
||||
config.operatorId = Operators.TEMPORAL_RANGE;
|
||||
config.comparator = defaultTimeFilter;
|
||||
}
|
||||
return new AdhocFilter(config);
|
||||
}, [droppedItem]);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user