mirror of
https://github.com/apache/superset.git
synced 2024-09-06 13:57:40 -04:00
refactor: preparation for time section migration (#21766)
This commit is contained in:
parent
bd3166b603
commit
8f61e3c5d9
@ -37,4 +37,4 @@ export { legacySortBy } from './shared-controls/legacySortBy';
|
|||||||
export * from './shared-controls/emitFilterControl';
|
export * from './shared-controls/emitFilterControl';
|
||||||
export * from './shared-controls/components';
|
export * from './shared-controls/components';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
export { xAxisMixin, temporalColumnMixin } from './shared-controls/mixins';
|
export * from './shared-controls/mixins';
|
||||||
|
@ -16,12 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import {
|
import { ContributionType, hasGenericChartAxes, t } from '@superset-ui/core';
|
||||||
ContributionType,
|
|
||||||
FeatureFlag,
|
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
|
||||||
} from '@superset-ui/core';
|
|
||||||
import { ControlPanelSectionConfig } from '../types';
|
import { ControlPanelSectionConfig } from '../types';
|
||||||
import { emitFilterControl } from '../shared-controls/emitFilterControl';
|
import { emitFilterControl } from '../shared-controls/emitFilterControl';
|
||||||
|
|
||||||
@ -29,12 +24,8 @@ export const echartsTimeSeriesQuery: ControlPanelSectionConfig = {
|
|||||||
label: t('Query'),
|
label: t('Query'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
[isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? 'x_axis' : null],
|
[hasGenericChartAxes ? 'x_axis' : null],
|
||||||
[
|
[hasGenericChartAxes ? 'time_grain_sqla' : null],
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
|
||||||
? 'time_grain_sqla'
|
|
||||||
: null,
|
|
||||||
],
|
|
||||||
['metrics'],
|
['metrics'],
|
||||||
['groupby'],
|
['groupby'],
|
||||||
[
|
[
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
|
import { hasGenericChartAxes, t } from '@superset-ui/core';
|
||||||
import { ControlPanelSectionConfig } from '../types';
|
import { ControlPanelSectionConfig } from '../types';
|
||||||
|
|
||||||
// A few standard controls sections that are used internally.
|
// A few standard controls sections that are used internally.
|
||||||
@ -42,11 +42,7 @@ export const genericTime: ControlPanelSectionConfig = {
|
|||||||
...baseTimeSection,
|
...baseTimeSection,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
['granularity_sqla'],
|
['granularity_sqla'],
|
||||||
[
|
[hasGenericChartAxes ? null : 'time_grain_sqla'],
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
|
||||||
? null
|
|
||||||
: 'time_grain_sqla',
|
|
||||||
],
|
|
||||||
['time_range'],
|
['time_range'],
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@ -30,6 +30,7 @@ import {
|
|||||||
SharedControlConfig,
|
SharedControlConfig,
|
||||||
Dataset,
|
Dataset,
|
||||||
Metric,
|
Metric,
|
||||||
|
isDataset,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants';
|
import { DATASET_TIME_COLUMN_OPTION, TIME_FILTER_LABELS } from '../constants';
|
||||||
import {
|
import {
|
||||||
@ -138,8 +139,8 @@ export const dndAdhocFilterControl: SharedControlConfig<
|
|||||||
default: [],
|
default: [],
|
||||||
description: '',
|
description: '',
|
||||||
mapStateToProps: ({ datasource, form_data }) => ({
|
mapStateToProps: ({ datasource, form_data }) => ({
|
||||||
columns: datasource?.columns[0]?.hasOwnProperty('filterable')
|
columns: isDataset(datasource)
|
||||||
? (datasource as Dataset)?.columns.filter(c => c.filterable)
|
? datasource.columns.filter(c => c.filterable)
|
||||||
: datasource?.columns || [],
|
: datasource?.columns || [],
|
||||||
savedMetrics: defineSavedMetrics(datasource),
|
savedMetrics: defineSavedMetrics(datasource),
|
||||||
// current active adhoc metrics
|
// current active adhoc metrics
|
||||||
|
@ -35,11 +35,9 @@
|
|||||||
*/
|
*/
|
||||||
import { isEmpty } from 'lodash';
|
import { isEmpty } from 'lodash';
|
||||||
import {
|
import {
|
||||||
FeatureFlag,
|
|
||||||
t,
|
t,
|
||||||
getCategoricalSchemeRegistry,
|
getCategoricalSchemeRegistry,
|
||||||
getSequentialSchemeRegistry,
|
getSequentialSchemeRegistry,
|
||||||
isFeatureEnabled,
|
|
||||||
SequentialScheme,
|
SequentialScheme,
|
||||||
legacyValidateInteger,
|
legacyValidateInteger,
|
||||||
ComparisionType,
|
ComparisionType,
|
||||||
@ -47,6 +45,8 @@ import {
|
|||||||
isPhysicalColumn,
|
isPhysicalColumn,
|
||||||
ensureIsArray,
|
ensureIsArray,
|
||||||
isDefined,
|
isDefined,
|
||||||
|
hasGenericChartAxes,
|
||||||
|
NO_TIME_RANGE,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -205,7 +205,7 @@ const time_grain_sqla: SharedControlConfig<'SelectControl'> = {
|
|||||||
choices: (datasource as Dataset)?.time_grain_sqla || [],
|
choices: (datasource as Dataset)?.time_grain_sqla || [],
|
||||||
}),
|
}),
|
||||||
visibility: ({ controls }) => {
|
visibility: ({ controls }) => {
|
||||||
if (!isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)) {
|
if (!hasGenericChartAxes) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +227,7 @@ const time_range: SharedControlConfig<'DateFilterControl'> = {
|
|||||||
type: 'DateFilterControl',
|
type: 'DateFilterControl',
|
||||||
freeForm: true,
|
freeForm: true,
|
||||||
label: TIME_FILTER_LABELS.time_range,
|
label: TIME_FILTER_LABELS.time_range,
|
||||||
default: t('No filter'), // this value is translated, but the backend wouldn't understand a translated value?
|
default: NO_TIME_RANGE, // this value is an empty filter constant so shouldn't translate it.
|
||||||
description: t(
|
description: t(
|
||||||
'The time range for the visualization. All relative times, e.g. "Last month", ' +
|
'The time range for the visualization. All relative times, e.g. "Last month", ' +
|
||||||
'"Last 7 days", "now", etc. are evaluated on the server using the server\'s ' +
|
'"Last 7 days", "now", etc. are evaluated on the server using the server\'s ' +
|
||||||
@ -236,9 +236,6 @@ const time_range: SharedControlConfig<'DateFilterControl'> = {
|
|||||||
"using the engine's local timezone. Note one can explicitly set the timezone " +
|
"using the engine's local timezone. Note one can explicitly set the timezone " +
|
||||||
'per the ISO 8601 format if specifying either the start and/or end time.',
|
'per the ISO 8601 format if specifying either the start and/or end time.',
|
||||||
),
|
),
|
||||||
mapStateToProps: ({ datasource }) => ({
|
|
||||||
datasource,
|
|
||||||
}),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const row_limit: SharedControlConfig<'SelectControl'> = {
|
const row_limit: SharedControlConfig<'SelectControl'> = {
|
||||||
|
@ -17,8 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
QueryFormData,
|
QueryFormData,
|
||||||
t,
|
t,
|
||||||
validateNonEmpty,
|
validateNonEmpty,
|
||||||
@ -41,7 +40,7 @@ export const xAxisMixin = {
|
|||||||
validators: [validateNonEmpty],
|
validators: [validateNonEmpty],
|
||||||
initialValue: (control: ControlState, state: ControlPanelState | null) => {
|
initialValue: (control: ControlState, state: ControlPanelState | null) => {
|
||||||
if (
|
if (
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) &&
|
hasGenericChartAxes &&
|
||||||
state?.form_data?.granularity_sqla &&
|
state?.form_data?.granularity_sqla &&
|
||||||
!state.form_data?.x_axis &&
|
!state.form_data?.x_axis &&
|
||||||
!control?.value
|
!control?.value
|
||||||
|
@ -23,7 +23,7 @@ export const TestDataset: Dataset = {
|
|||||||
column_format: {},
|
column_format: {},
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'num',
|
column_name: 'num',
|
||||||
@ -41,7 +41,7 @@ export const TestDataset: Dataset = {
|
|||||||
warning_markdown: null,
|
warning_markdown: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'gender',
|
column_name: 'gender',
|
||||||
@ -59,7 +59,7 @@ export const TestDataset: Dataset = {
|
|||||||
warning_markdown: null,
|
warning_markdown: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'state',
|
column_name: 'state',
|
||||||
@ -77,7 +77,7 @@ export const TestDataset: Dataset = {
|
|||||||
warning_markdown: null,
|
warning_markdown: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'ds',
|
column_name: 'ds',
|
||||||
@ -95,7 +95,7 @@ export const TestDataset: Dataset = {
|
|||||||
warning_markdown: null,
|
warning_markdown: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'name',
|
column_name: 'name',
|
||||||
|
@ -24,7 +24,7 @@ test('get temporal columns from a Dataset', () => {
|
|||||||
expect(getTemporalColumns(TestDataset)).toEqual({
|
expect(getTemporalColumns(TestDataset)).toEqual({
|
||||||
temporalColumns: [
|
temporalColumns: [
|
||||||
{
|
{
|
||||||
advanced_data_type: null,
|
advanced_data_type: undefined,
|
||||||
certification_details: null,
|
certification_details: null,
|
||||||
certified_by: null,
|
certified_by: null,
|
||||||
column_name: 'ds',
|
column_name: 'ds',
|
||||||
|
@ -1,11 +1,3 @@
|
|||||||
import {
|
|
||||||
ExtraFormDataAppend,
|
|
||||||
ExtraFormDataOverrideExtras,
|
|
||||||
ExtraFormDataOverrideRegular,
|
|
||||||
ExtraFormDataOverride,
|
|
||||||
QueryObject,
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
@ -24,7 +16,17 @@ import {
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import {
|
||||||
|
ExtraFormDataAppend,
|
||||||
|
ExtraFormDataOverrideExtras,
|
||||||
|
ExtraFormDataOverrideRegular,
|
||||||
|
ExtraFormDataOverride,
|
||||||
|
QueryObject,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
export const DTTM_ALIAS = '__timestamp';
|
export const DTTM_ALIAS = '__timestamp';
|
||||||
|
export const DEFAULT_TIME_RANGE = 'No filter'; // TODO: make this configurable per Superset installation
|
||||||
|
export const NO_TIME_RANGE = 'No filter';
|
||||||
|
|
||||||
export const EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS: (keyof ExtraFormDataOverrideExtras)[] =
|
export const EXTRA_FORM_DATA_OVERRIDE_EXTRA_KEYS: (keyof ExtraFormDataOverrideExtras)[] =
|
||||||
['relative_start', 'relative_end', 'time_grain_sqla'];
|
['relative_start', 'relative_end', 'time_grain_sqla'];
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
import {
|
import {
|
||||||
DTTM_ALIAS,
|
DTTM_ALIAS,
|
||||||
|
FeatureFlag,
|
||||||
|
isFeatureEnabled,
|
||||||
getColumnLabel,
|
getColumnLabel,
|
||||||
isQueryFormColumn,
|
isQueryFormColumn,
|
||||||
QueryFormData,
|
QueryFormData,
|
||||||
@ -26,6 +28,10 @@ import {
|
|||||||
export const isXAxisSet = (formData: QueryFormData) =>
|
export const isXAxisSet = (formData: QueryFormData) =>
|
||||||
isQueryFormColumn(formData.x_axis);
|
isQueryFormColumn(formData.x_axis);
|
||||||
|
|
||||||
|
export const hasGenericChartAxes = isFeatureEnabled(
|
||||||
|
FeatureFlag.GENERIC_CHART_AXES,
|
||||||
|
);
|
||||||
|
|
||||||
export const getXAxis = (formData: QueryFormData): string | undefined => {
|
export const getXAxis = (formData: QueryFormData): string | undefined => {
|
||||||
// The formData should be "raw form_data" -- the snake_case version of formData rather than camelCase.
|
// The formData should be "raw form_data" -- the snake_case version of formData rather than camelCase.
|
||||||
if (!(formData.granularity_sqla || formData.x_axis)) {
|
if (!(formData.granularity_sqla || formData.x_axis)) {
|
||||||
|
@ -29,7 +29,7 @@ export { default as getMetricLabel } from './getMetricLabel';
|
|||||||
export { default as DatasourceKey } from './DatasourceKey';
|
export { default as DatasourceKey } from './DatasourceKey';
|
||||||
export { default as normalizeOrderBy } from './normalizeOrderBy';
|
export { default as normalizeOrderBy } from './normalizeOrderBy';
|
||||||
export { normalizeTimeColumn } from './normalizeTimeColumn';
|
export { normalizeTimeColumn } from './normalizeTimeColumn';
|
||||||
export { getXAxis, isXAxisSet } from './getXAxis';
|
export { getXAxis, isXAxisSet, hasGenericChartAxes } from './getXAxis';
|
||||||
|
|
||||||
export * from './types/AnnotationLayer';
|
export * from './types/AnnotationLayer';
|
||||||
export * from './types/QueryFormData';
|
export * from './types/QueryFormData';
|
||||||
|
@ -52,6 +52,12 @@ export interface Column {
|
|||||||
expression?: string | null;
|
expression?: string | null;
|
||||||
database_expression?: string | null;
|
database_expression?: string | null;
|
||||||
python_date_format?: string | null;
|
python_date_format?: string | null;
|
||||||
|
|
||||||
|
// used for advanced_data_type
|
||||||
|
optionName?: string;
|
||||||
|
filterBy?: string;
|
||||||
|
value?: string;
|
||||||
|
advanced_data_type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPhysicalColumn(column?: any): column is PhysicalColumn {
|
export function isPhysicalColumn(column?: any): column is PhysicalColumn {
|
||||||
|
@ -16,12 +16,7 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import {
|
import { hasGenericChartAxes, smartDateFormatter, t } from '@superset-ui/core';
|
||||||
FeatureFlag,
|
|
||||||
isFeatureEnabled,
|
|
||||||
smartDateFormatter,
|
|
||||||
t,
|
|
||||||
} from '@superset-ui/core';
|
|
||||||
import {
|
import {
|
||||||
ControlPanelConfig,
|
ControlPanelConfig,
|
||||||
D3_FORMAT_DOCS,
|
D3_FORMAT_DOCS,
|
||||||
@ -41,12 +36,8 @@ const config: ControlPanelConfig = {
|
|||||||
label: t('Query'),
|
label: t('Query'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
controlSetRows: [
|
controlSetRows: [
|
||||||
[isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) ? 'x_axis' : null],
|
[hasGenericChartAxes ? 'x_axis' : null],
|
||||||
[
|
[hasGenericChartAxes ? 'time_grain_sqla' : null],
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
|
||||||
? 'time_grain_sqla'
|
|
||||||
: null,
|
|
||||||
],
|
|
||||||
['metric'],
|
['metric'],
|
||||||
['adhoc_filters'],
|
['adhoc_filters'],
|
||||||
],
|
],
|
||||||
|
@ -17,12 +17,7 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import { ensureIsArray, hasGenericChartAxes, t } from '@superset-ui/core';
|
||||||
ensureIsArray,
|
|
||||||
FeatureFlag,
|
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
|
||||||
} from '@superset-ui/core';
|
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
import {
|
import {
|
||||||
ControlPanelConfig,
|
ControlPanelConfig,
|
||||||
@ -292,7 +287,7 @@ function createAdvancedAnalyticsSection(
|
|||||||
const config: ControlPanelConfig = {
|
const config: ControlPanelConfig = {
|
||||||
controlPanelSections: [
|
controlPanelSections: [
|
||||||
sections.genericTime,
|
sections.genericTime,
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
hasGenericChartAxes
|
||||||
? {
|
? {
|
||||||
label: t('Shared query fields'),
|
label: t('Shared query fields'),
|
||||||
expanded: true,
|
expanded: true,
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import buildQuery from './buildQuery';
|
import buildQuery from './buildQuery';
|
||||||
@ -57,7 +56,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Visualize two different series using the same x-axis. Note that both series can be visualized with a different chart type (e.g. 1 using bars and 1 using a line).',
|
'Visualize two different series using the same x-axis. Note that both series can be visualized with a different chart type (e.g. 1 using bars and 1 using a line).',
|
||||||
)
|
)
|
||||||
@ -70,9 +69,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes ? t('Mixed Chart') : t('Mixed Time-Series'),
|
||||||
? t('Mixed Chart')
|
|
||||||
: t('Mixed Time-Series'),
|
|
||||||
thumbnail,
|
thumbnail,
|
||||||
tags: [
|
tags: [
|
||||||
t('Advanced-Analytics'),
|
t('Advanced-Analytics'),
|
||||||
|
@ -22,8 +22,7 @@ import {
|
|||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
AnnotationType,
|
AnnotationType,
|
||||||
Behavior,
|
Behavior,
|
||||||
isFeatureEnabled,
|
hasGenericChartAxes,
|
||||||
FeatureFlag,
|
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import buildQuery from '../buildQuery';
|
import buildQuery from '../buildQuery';
|
||||||
import controlPanel from './controlPanel';
|
import controlPanel from './controlPanel';
|
||||||
@ -54,7 +53,7 @@ export default class EchartsAreaChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Area charts are similar to line charts in that they represent variables with the same scale, but area charts stack the metrics on top of each other.',
|
'Area charts are similar to line charts in that they represent variables with the same scale, but area charts stack the metrics on top of each other.',
|
||||||
)
|
)
|
||||||
@ -68,7 +67,7 @@ export default class EchartsAreaChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Area Chart v2')
|
? t('Area Chart v2')
|
||||||
: t('Time-series Area Chart'),
|
: t('Time-series Area Chart'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
@ -60,7 +59,7 @@ export default class EchartsTimeseriesBarChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t('Bar Charts are used to show metrics as a series of bars.')
|
? t('Bar Charts are used to show metrics as a series of bars.')
|
||||||
: t(
|
: t(
|
||||||
'Time-series Bar Charts are used to show the changes in a metric over time as a series of bars.',
|
'Time-series Bar Charts are used to show the changes in a metric over time as a series of bars.',
|
||||||
@ -76,7 +75,7 @@ export default class EchartsTimeseriesBarChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Bar Chart v2')
|
? t('Bar Chart v2')
|
||||||
: t('Time-series Bar Chart v2'),
|
: t('Time-series Bar Chart v2'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
@ -59,7 +58,7 @@ export default class EchartsTimeseriesLineChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Line chart is used to visualize measurements taken over a given category. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
|
'Line chart is used to visualize measurements taken over a given category. Line chart is a type of chart which displays information as a series of data points connected by straight line segments. It is a basic type of chart common in many fields.',
|
||||||
)
|
)
|
||||||
@ -73,7 +72,7 @@ export default class EchartsTimeseriesLineChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Line Chart v2')
|
? t('Line Chart v2')
|
||||||
: t('Time-series Line Chart'),
|
: t('Time-series Line Chart'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
@ -58,7 +57,7 @@ export default class EchartsTimeseriesScatterChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Scatter Plot has the horizontal axis in linear units, and the points are connected in order. It shows a statistical relationship between two variables.',
|
'Scatter Plot has the horizontal axis in linear units, and the points are connected in order. It shows a statistical relationship between two variables.',
|
||||||
)
|
)
|
||||||
@ -72,7 +71,7 @@ export default class EchartsTimeseriesScatterChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Scatter Plot')
|
? t('Scatter Plot')
|
||||||
: t('Time-series Scatter Plot'),
|
: t('Time-series Scatter Plot'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
@ -58,7 +57,7 @@ export default class EchartsTimeseriesSmoothLineChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Smooth-line is a variation of the line chart. Without angles and hard edges, Smooth-line sometimes looks smarter and more professional.',
|
'Smooth-line is a variation of the line chart. Without angles and hard edges, Smooth-line sometimes looks smarter and more professional.',
|
||||||
)
|
)
|
||||||
@ -72,7 +71,7 @@ export default class EchartsTimeseriesSmoothLineChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Smooth Line')
|
? t('Smooth Line')
|
||||||
: t('Time-series Smooth Line'),
|
: t('Time-series Smooth Line'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import {
|
import {
|
||||||
@ -49,7 +48,7 @@ export default class EchartsTimeseriesStepChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Stepped-line graph (also called step chart) is a variation of line chart but with the line forming a series of steps between data points. A step chart can be useful when you want to show the changes that occur at irregular intervals.',
|
'Stepped-line graph (also called step chart) is a variation of line chart but with the line forming a series of steps between data points. A step chart can be useful when you want to show the changes that occur at irregular intervals.',
|
||||||
)
|
)
|
||||||
@ -63,7 +62,7 @@ export default class EchartsTimeseriesStepChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes
|
||||||
? t('Stepped Line')
|
? t('Stepped Line')
|
||||||
: t('Time-series Stepped Line'),
|
: t('Time-series Stepped Line'),
|
||||||
tags: [
|
tags: [
|
||||||
|
@ -21,8 +21,7 @@ import {
|
|||||||
Behavior,
|
Behavior,
|
||||||
ChartMetadata,
|
ChartMetadata,
|
||||||
ChartPlugin,
|
ChartPlugin,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import buildQuery from './buildQuery';
|
import buildQuery from './buildQuery';
|
||||||
@ -48,7 +47,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
|
|||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART],
|
||||||
category: t('Evolution'),
|
category: t('Evolution'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
description: hasGenericChartAxes
|
||||||
? t(
|
? t(
|
||||||
'Swiss army knife for visualizing data. Choose between step, line, scatter, and bar charts. This viz type has many customization options as well.',
|
'Swiss army knife for visualizing data. Choose between step, line, scatter, and bar charts. This viz type has many customization options as well.',
|
||||||
)
|
)
|
||||||
@ -62,9 +61,7 @@ export default class EchartsTimeseriesChartPlugin extends ChartPlugin<
|
|||||||
AnnotationType.Interval,
|
AnnotationType.Interval,
|
||||||
AnnotationType.Timeseries,
|
AnnotationType.Timeseries,
|
||||||
],
|
],
|
||||||
name: isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
name: hasGenericChartAxes ? t('Generic Chart') : t('Time-series Chart'),
|
||||||
? t('Generic Chart')
|
|
||||||
: t('Time-series Chart'),
|
|
||||||
tags: [
|
tags: [
|
||||||
t('Advanced-Analytics'),
|
t('Advanced-Analytics'),
|
||||||
t('Aesthetic'),
|
t('Aesthetic'),
|
||||||
|
@ -22,8 +22,7 @@ import {
|
|||||||
AdhocColumn,
|
AdhocColumn,
|
||||||
buildQueryContext,
|
buildQueryContext,
|
||||||
ensureIsArray,
|
ensureIsArray,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isFeatureEnabled,
|
|
||||||
isPhysicalColumn,
|
isPhysicalColumn,
|
||||||
QueryFormColumn,
|
QueryFormColumn,
|
||||||
QueryFormOrderBy,
|
QueryFormOrderBy,
|
||||||
@ -42,7 +41,7 @@ export default function buildQuery(formData: PivotTableQueryFormData) {
|
|||||||
if (
|
if (
|
||||||
isPhysicalColumn(col) &&
|
isPhysicalColumn(col) &&
|
||||||
formData.time_grain_sqla &&
|
formData.time_grain_sqla &&
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) &&
|
hasGenericChartAxes &&
|
||||||
formData?.datetime_columns_lookup?.[col]
|
formData?.datetime_columns_lookup?.[col]
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
@ -66,7 +65,7 @@ export default function buildQuery(formData: PivotTableQueryFormData) {
|
|||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
...(isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
...(hasGenericChartAxes
|
||||||
? omit(baseQueryObject, ['extras.time_grain_sqla'])
|
? omit(baseQueryObject, ['extras.time_grain_sqla'])
|
||||||
: baseQueryObject),
|
: baseQueryObject),
|
||||||
orderby: orderBy,
|
orderby: orderBy,
|
||||||
|
@ -19,9 +19,8 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {
|
import {
|
||||||
ensureIsArray,
|
ensureIsArray,
|
||||||
FeatureFlag,
|
hasGenericChartAxes,
|
||||||
isAdhocColumn,
|
isAdhocColumn,
|
||||||
isFeatureEnabled,
|
|
||||||
isPhysicalColumn,
|
isPhysicalColumn,
|
||||||
QueryFormMetric,
|
QueryFormMetric,
|
||||||
smartDateFormatter,
|
smartDateFormatter,
|
||||||
@ -68,7 +67,7 @@ const config: ControlPanelConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
hasGenericChartAxes
|
||||||
? {
|
? {
|
||||||
name: 'time_grain_sqla',
|
name: 'time_grain_sqla',
|
||||||
config: {
|
config: {
|
||||||
@ -98,9 +97,7 @@ const config: ControlPanelConfig = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES)
|
hasGenericChartAxes ? 'datetime_columns_lookup' : null,
|
||||||
? 'datetime_columns_lookup'
|
|
||||||
: null,
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -20,9 +20,8 @@ import {
|
|||||||
AdhocColumn,
|
AdhocColumn,
|
||||||
buildQueryContext,
|
buildQueryContext,
|
||||||
ensureIsArray,
|
ensureIsArray,
|
||||||
FeatureFlag,
|
|
||||||
getMetricLabel,
|
getMetricLabel,
|
||||||
isFeatureEnabled,
|
hasGenericChartAxes,
|
||||||
isPhysicalColumn,
|
isPhysicalColumn,
|
||||||
QueryMode,
|
QueryMode,
|
||||||
QueryObject,
|
QueryObject,
|
||||||
@ -104,7 +103,7 @@ const buildQuery: BuildQuery<TableChartFormData> = (
|
|||||||
if (
|
if (
|
||||||
isPhysicalColumn(col) &&
|
isPhysicalColumn(col) &&
|
||||||
formData.time_grain_sqla &&
|
formData.time_grain_sqla &&
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) &&
|
hasGenericChartAxes &&
|
||||||
formData?.datetime_columns_lookup?.[col]
|
formData?.datetime_columns_lookup?.[col]
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
|
@ -23,6 +23,7 @@ import {
|
|||||||
ensureIsArray,
|
ensureIsArray,
|
||||||
FeatureFlag,
|
FeatureFlag,
|
||||||
GenericDataType,
|
GenericDataType,
|
||||||
|
hasGenericChartAxes,
|
||||||
isAdhocColumn,
|
isAdhocColumn,
|
||||||
isFeatureEnabled,
|
isFeatureEnabled,
|
||||||
isPhysicalColumn,
|
isPhysicalColumn,
|
||||||
@ -189,7 +190,7 @@ const config: ControlPanelConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) && isAggMode
|
hasGenericChartAxes && isAggMode
|
||||||
? {
|
? {
|
||||||
name: 'time_grain_sqla',
|
name: 'time_grain_sqla',
|
||||||
config: {
|
config: {
|
||||||
@ -217,9 +218,7 @@ const config: ControlPanelConfig = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
isFeatureEnabled(FeatureFlag.GENERIC_CHART_AXES) && isAggMode
|
hasGenericChartAxes && isAggMode ? 'datetime_columns_lookup' : null,
|
||||||
? 'datetime_columns_lookup'
|
|
||||||
: null,
|
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
|
@ -25,8 +25,9 @@ import {
|
|||||||
FilterState,
|
FilterState,
|
||||||
isFeatureEnabled,
|
isFeatureEnabled,
|
||||||
NativeFilterType,
|
NativeFilterType,
|
||||||
|
NO_TIME_RANGE,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { NO_TIME_RANGE, TIME_FILTER_MAP } from 'src/explore/constants';
|
import { TIME_FILTER_MAP } from 'src/explore/constants';
|
||||||
import { getChartIdsInFilterBoxScope } from 'src/dashboard/util/activeDashboardFilters';
|
import { getChartIdsInFilterBoxScope } from 'src/dashboard/util/activeDashboardFilters';
|
||||||
import { ChartConfiguration } from 'src/dashboard/reducers/types';
|
import { ChartConfiguration } from 'src/dashboard/reducers/types';
|
||||||
import { Layout } from 'src/dashboard/types';
|
import { Layout } from 'src/dashboard/types';
|
||||||
|
@ -26,7 +26,7 @@ import { testWithId } from 'src/utils/testUtils';
|
|||||||
import { FeatureFlag } from 'src/featureFlags';
|
import { FeatureFlag } from 'src/featureFlags';
|
||||||
import { Preset } from '@superset-ui/core';
|
import { Preset } from '@superset-ui/core';
|
||||||
import { TimeFilterPlugin, SelectFilterPlugin } from 'src/filters/components';
|
import { TimeFilterPlugin, SelectFilterPlugin } from 'src/filters/components';
|
||||||
import { DATE_FILTER_CONTROL_TEST_ID } from 'src/explore/components/controls/DateFilterControl/DateFilterLabel';
|
import { DATE_FILTER_CONTROL_TEST_ID } from 'src/explore/components/controls/DateFilterControl';
|
||||||
import fetchMock from 'fetch-mock';
|
import fetchMock from 'fetch-mock';
|
||||||
import { waitFor } from '@testing-library/react';
|
import { waitFor } from '@testing-library/react';
|
||||||
import FilterBar, { FILTER_BAR_TEST_ID } from '.';
|
import FilterBar, { FILTER_BAR_TEST_ID } from '.';
|
||||||
|
@ -17,17 +17,14 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import rison from 'rison';
|
|
||||||
import { css, SupersetClient, styled, t, useTheme } from '@superset-ui/core';
|
|
||||||
import {
|
import {
|
||||||
buildTimeRangeString,
|
css,
|
||||||
formatTimeRange,
|
styled,
|
||||||
COMMON_RANGE_VALUES_SET,
|
t,
|
||||||
CALENDAR_RANGE_VALUES_SET,
|
useTheme,
|
||||||
FRAME_OPTIONS,
|
DEFAULT_TIME_RANGE,
|
||||||
customTimeRangeDecode,
|
NO_TIME_RANGE,
|
||||||
} from 'src/explore/components/controls/DateFilterControl/utils';
|
} from '@superset-ui/core';
|
||||||
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
|
||||||
import Button from 'src/components/Button';
|
import Button from 'src/components/Button';
|
||||||
import ControlHeader from 'src/explore/components/ControlHeader';
|
import ControlHeader from 'src/explore/components/ControlHeader';
|
||||||
import Label, { Type } from 'src/components/Label';
|
import Label, { Type } from 'src/components/Label';
|
||||||
@ -35,14 +32,18 @@ import { Divider } from 'src/components';
|
|||||||
import Icons from 'src/components/Icons';
|
import Icons from 'src/components/Icons';
|
||||||
import Select from 'src/components/Select/Select';
|
import Select from 'src/components/Select/Select';
|
||||||
import { Tooltip } from 'src/components/Tooltip';
|
import { Tooltip } from 'src/components/Tooltip';
|
||||||
import { DEFAULT_TIME_RANGE } from 'src/explore/constants';
|
|
||||||
import { useDebouncedEffect } from 'src/explore/exploreUtils';
|
import { useDebouncedEffect } from 'src/explore/exploreUtils';
|
||||||
import { SLOW_DEBOUNCE } from 'src/constants';
|
import { SLOW_DEBOUNCE } from 'src/constants';
|
||||||
import { testWithId } from 'src/utils/testUtils';
|
|
||||||
import { noOp } from 'src/utils/common';
|
import { noOp } from 'src/utils/common';
|
||||||
import { FrameType } from './types';
|
|
||||||
import ControlPopover from '../ControlPopover/ControlPopover';
|
import ControlPopover from '../ControlPopover/ControlPopover';
|
||||||
|
|
||||||
|
import { DateFilterControlProps, FrameType } from './types';
|
||||||
|
import {
|
||||||
|
fetchTimeRange,
|
||||||
|
FRAME_OPTIONS,
|
||||||
|
getDateFilterControlTestId,
|
||||||
|
guessFrame,
|
||||||
|
} from './utils';
|
||||||
import {
|
import {
|
||||||
CommonFrame,
|
CommonFrame,
|
||||||
CalendarFrame,
|
CalendarFrame,
|
||||||
@ -50,42 +51,6 @@ import {
|
|||||||
AdvancedFrame,
|
AdvancedFrame,
|
||||||
} from './components';
|
} from './components';
|
||||||
|
|
||||||
const guessFrame = (timeRange: string): FrameType => {
|
|
||||||
if (COMMON_RANGE_VALUES_SET.has(timeRange)) {
|
|
||||||
return 'Common';
|
|
||||||
}
|
|
||||||
if (CALENDAR_RANGE_VALUES_SET.has(timeRange)) {
|
|
||||||
return 'Calendar';
|
|
||||||
}
|
|
||||||
if (timeRange === 'No filter') {
|
|
||||||
return 'No filter';
|
|
||||||
}
|
|
||||||
if (customTimeRangeDecode(timeRange).matchedFlag) {
|
|
||||||
return 'Custom';
|
|
||||||
}
|
|
||||||
return 'Advanced';
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchTimeRange = async (timeRange: string) => {
|
|
||||||
const query = rison.encode_uri(timeRange);
|
|
||||||
const endpoint = `/api/v1/time_range/?q=${query}`;
|
|
||||||
try {
|
|
||||||
const response = await SupersetClient.get({ endpoint });
|
|
||||||
const timeRangeString = buildTimeRangeString(
|
|
||||||
response?.json?.result?.since || '',
|
|
||||||
response?.json?.result?.until || '',
|
|
||||||
);
|
|
||||||
return {
|
|
||||||
value: formatTimeRange(timeRangeString),
|
|
||||||
};
|
|
||||||
} catch (response) {
|
|
||||||
const clientError = await getClientErrorObject(response);
|
|
||||||
return {
|
|
||||||
error: clientError.message || clientError.error,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const StyledPopover = styled(ControlPopover)``;
|
const StyledPopover = styled(ControlPopover)``;
|
||||||
const StyledRangeType = styled(Select)`
|
const StyledRangeType = styled(Select)`
|
||||||
width: 272px;
|
width: 272px;
|
||||||
@ -161,20 +126,6 @@ const IconWrapper = styled.span`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
interface DateFilterControlProps {
|
|
||||||
name: string;
|
|
||||||
onChange: (timeRange: string) => void;
|
|
||||||
value?: string;
|
|
||||||
type?: Type;
|
|
||||||
onOpenPopover?: () => void;
|
|
||||||
onClosePopover?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DATE_FILTER_CONTROL_TEST_ID = 'date-filter-control';
|
|
||||||
export const getDateFilterControlTestId = testWithId(
|
|
||||||
DATE_FILTER_CONTROL_TEST_ID,
|
|
||||||
);
|
|
||||||
|
|
||||||
export default function DateFilterLabel(props: DateFilterControlProps) {
|
export default function DateFilterLabel(props: DateFilterControlProps) {
|
||||||
const {
|
const {
|
||||||
value = DEFAULT_TIME_RANGE,
|
value = DEFAULT_TIME_RANGE,
|
||||||
@ -195,6 +146,12 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||||||
const [tooltipTitle, setTooltipTitle] = useState<string>(value);
|
const [tooltipTitle, setTooltipTitle] = useState<string>(value);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (value === NO_TIME_RANGE) {
|
||||||
|
setActualTimeRange(NO_TIME_RANGE);
|
||||||
|
setTooltipTitle(NO_TIME_RANGE);
|
||||||
|
setValidTimeRange(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
fetchTimeRange(value).then(({ value: actualRange, error }) => {
|
fetchTimeRange(value).then(({ value: actualRange, error }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
setEvalResponse(error || '');
|
setEvalResponse(error || '');
|
||||||
@ -235,6 +192,12 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||||||
|
|
||||||
useDebouncedEffect(
|
useDebouncedEffect(
|
||||||
() => {
|
() => {
|
||||||
|
if (timeRangeValue === NO_TIME_RANGE) {
|
||||||
|
setEvalResponse(NO_TIME_RANGE);
|
||||||
|
setLastFetchedTimeRange(NO_TIME_RANGE);
|
||||||
|
setValidTimeRange(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (lastFetchedTimeRange !== timeRangeValue) {
|
if (lastFetchedTimeRange !== timeRangeValue) {
|
||||||
fetchTimeRange(timeRangeValue).then(({ value: actualRange, error }) => {
|
fetchTimeRange(timeRangeValue).then(({ value: actualRange, error }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
@ -279,11 +242,11 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function onChangeFrame(value: string) {
|
function onChangeFrame(value: FrameType) {
|
||||||
if (value === 'No filter') {
|
if (value === NO_TIME_RANGE) {
|
||||||
setTimeRangeValue('No filter');
|
setTimeRangeValue(NO_TIME_RANGE);
|
||||||
}
|
}
|
||||||
setFrame(value as FrameType);
|
setFrame(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
@ -354,10 +317,6 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
);
|
);
|
||||||
|
|
||||||
const overlayStyle = {
|
|
||||||
width: '600px',
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ControlHeader {...props} />
|
<ControlHeader {...props} />
|
||||||
@ -369,7 +328,7 @@ export default function DateFilterLabel(props: DateFilterControlProps) {
|
|||||||
defaultVisible={show}
|
defaultVisible={show}
|
||||||
visible={show}
|
visible={show}
|
||||||
onVisibleChange={togglePopover}
|
onVisibleChange={togglePopover}
|
||||||
overlayStyle={overlayStyle}
|
overlayStyle={{ width: '600px' }}
|
||||||
>
|
>
|
||||||
<Tooltip placement="top" title={tooltipTitle}>
|
<Tooltip placement="top" title={tooltipTitle}>
|
||||||
<Label className="pointer" data-test="time-range-trigger">
|
<Label className="pointer" data-test="time-range-trigger">
|
||||||
|
@ -24,8 +24,7 @@ import { InfoTooltipWithTrigger } from '@superset-ui/chart-controls';
|
|||||||
import { FrameComponentProps } from 'src/explore/components/controls/DateFilterControl/types';
|
import { FrameComponentProps } from 'src/explore/components/controls/DateFilterControl/types';
|
||||||
import DateFunctionTooltip from './DateFunctionTooltip';
|
import DateFunctionTooltip from './DateFunctionTooltip';
|
||||||
|
|
||||||
export function AdvancedFrame(props: FrameComponentProps) {
|
function getAdvancedRange(value: string): string {
|
||||||
function getAdvancedRange(value: string): string {
|
|
||||||
if (value.includes(SEPARATOR)) {
|
if (value.includes(SEPARATOR)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
@ -36,8 +35,9 @@ export function AdvancedFrame(props: FrameComponentProps) {
|
|||||||
return ['', value].join(SEPARATOR);
|
return ['', value].join(SEPARATOR);
|
||||||
}
|
}
|
||||||
return SEPARATOR;
|
return SEPARATOR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function AdvancedFrame(props: FrameComponentProps) {
|
||||||
const advancedRange = getAdvancedRange(props.value || '');
|
const advancedRange = getAdvancedRange(props.value || '');
|
||||||
const [since, until] = advancedRange.split(SEPARATOR);
|
const [since, until] = advancedRange.split(SEPARATOR);
|
||||||
if (advancedRange !== props.value) {
|
if (advancedRange !== props.value) {
|
||||||
|
@ -17,3 +17,8 @@
|
|||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
export { default } from './DateFilterLabel';
|
export { default } from './DateFilterLabel';
|
||||||
|
export {
|
||||||
|
DATE_FILTER_CONTROL_TEST_ID,
|
||||||
|
fetchTimeRange,
|
||||||
|
guessFrame,
|
||||||
|
} from './utils';
|
||||||
|
@ -16,6 +16,8 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import { Type } from 'src/components/Label';
|
||||||
|
|
||||||
export type SelectOptionType = {
|
export type SelectOptionType = {
|
||||||
value: string;
|
value: string;
|
||||||
label: string;
|
label: string;
|
||||||
@ -89,3 +91,12 @@ export type FrameComponentProps = {
|
|||||||
onChange: (timeRange: string) => void;
|
onChange: (timeRange: string) => void;
|
||||||
value: string;
|
value: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface DateFilterControlProps {
|
||||||
|
name: string;
|
||||||
|
onChange: (timeRange: string) => void;
|
||||||
|
value?: string;
|
||||||
|
type?: Type;
|
||||||
|
onOpenPopover?: () => void;
|
||||||
|
onClosePopover?: () => void;
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
CommonRangeType,
|
CommonRangeType,
|
||||||
CalendarRangeType,
|
CalendarRangeType,
|
||||||
} from 'src/explore/components/controls/DateFilterControl/types';
|
} from 'src/explore/components/controls/DateFilterControl/types';
|
||||||
|
import { testWithId } from 'src/utils/testUtils';
|
||||||
|
|
||||||
export const FRAME_OPTIONS: SelectOptionType[] = [
|
export const FRAME_OPTIONS: SelectOptionType[] = [
|
||||||
{ value: 'Common', label: t('Last') },
|
{ value: 'Common', label: t('Last') },
|
||||||
@ -131,3 +132,8 @@ export const LOCALE_MAPPING = {
|
|||||||
sl: 'sl_SI',
|
sl: 'sl_SI',
|
||||||
nl: 'nl_NL',
|
nl: 'nl_NL',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const DATE_FILTER_CONTROL_TEST_ID = 'date-filter-control';
|
||||||
|
export const getDateFilterControlTestId = testWithId(
|
||||||
|
DATE_FILTER_CONTROL_TEST_ID,
|
||||||
|
);
|
||||||
|
@ -16,6 +16,16 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import rison from 'rison';
|
||||||
|
import { SupersetClient, NO_TIME_RANGE } from '@superset-ui/core';
|
||||||
|
import { getClientErrorObject } from 'src/utils/getClientErrorObject';
|
||||||
|
import {
|
||||||
|
COMMON_RANGE_VALUES_SET,
|
||||||
|
CALENDAR_RANGE_VALUES_SET,
|
||||||
|
customTimeRangeDecode,
|
||||||
|
} from '.';
|
||||||
|
import { FrameType } from '../types';
|
||||||
|
|
||||||
export const SEPARATOR = ' : ';
|
export const SEPARATOR = ' : ';
|
||||||
|
|
||||||
export const buildTimeRangeString = (since: string, until: string): string =>
|
export const buildTimeRangeString = (since: string, until: string): string =>
|
||||||
@ -24,11 +34,53 @@ export const buildTimeRangeString = (since: string, until: string): string =>
|
|||||||
const formatDateEndpoint = (dttm: string, isStart?: boolean): string =>
|
const formatDateEndpoint = (dttm: string, isStart?: boolean): string =>
|
||||||
dttm.replace('T00:00:00', '') || (isStart ? '-∞' : '∞');
|
dttm.replace('T00:00:00', '') || (isStart ? '-∞' : '∞');
|
||||||
|
|
||||||
export const formatTimeRange = (timeRange: string) => {
|
export const formatTimeRange = (
|
||||||
|
timeRange: string,
|
||||||
|
columnPlaceholder = 'col',
|
||||||
|
) => {
|
||||||
const splitDateRange = timeRange.split(SEPARATOR);
|
const splitDateRange = timeRange.split(SEPARATOR);
|
||||||
if (splitDateRange.length === 1) return timeRange;
|
if (splitDateRange.length === 1) return timeRange;
|
||||||
return `${formatDateEndpoint(
|
return `${formatDateEndpoint(
|
||||||
splitDateRange[0],
|
splitDateRange[0],
|
||||||
true,
|
true,
|
||||||
)} ≤ col < ${formatDateEndpoint(splitDateRange[1])}`;
|
)} ≤ ${columnPlaceholder} < ${formatDateEndpoint(splitDateRange[1])}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const guessFrame = (timeRange: string): FrameType => {
|
||||||
|
if (COMMON_RANGE_VALUES_SET.has(timeRange)) {
|
||||||
|
return 'Common';
|
||||||
|
}
|
||||||
|
if (CALENDAR_RANGE_VALUES_SET.has(timeRange)) {
|
||||||
|
return 'Calendar';
|
||||||
|
}
|
||||||
|
if (timeRange === NO_TIME_RANGE) {
|
||||||
|
return 'No filter';
|
||||||
|
}
|
||||||
|
if (customTimeRangeDecode(timeRange).matchedFlag) {
|
||||||
|
return 'Custom';
|
||||||
|
}
|
||||||
|
return 'Advanced';
|
||||||
|
};
|
||||||
|
|
||||||
|
export const fetchTimeRange = async (
|
||||||
|
timeRange: string,
|
||||||
|
columnPlaceholder = 'col',
|
||||||
|
) => {
|
||||||
|
const query = rison.encode_uri(timeRange);
|
||||||
|
const endpoint = `/api/v1/time_range/?q=${query}`;
|
||||||
|
try {
|
||||||
|
const response = await SupersetClient.get({ endpoint });
|
||||||
|
const timeRangeString = buildTimeRangeString(
|
||||||
|
response?.json?.result?.since || '',
|
||||||
|
response?.json?.result?.until || '',
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
value: formatTimeRange(timeRangeString, columnPlaceholder),
|
||||||
|
};
|
||||||
|
} catch (response) {
|
||||||
|
const clientError = await getClientErrorObject(response);
|
||||||
|
return {
|
||||||
|
error: clientError.message || clientError.error || response.statusText,
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
/**
|
||||||
|
* 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 { 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 OptionWrapper from './OptionWrapper';
|
||||||
|
|
||||||
|
export interface DndAdhocFilterOptionProps {
|
||||||
|
adhocFilter: AdhocFilter;
|
||||||
|
onFilterEdit: (changedFilter: AdhocFilter) => void;
|
||||||
|
onClickClose: (index: number) => void;
|
||||||
|
onShiftOptions: (dragIndex: number, hoverIndex: number) => void;
|
||||||
|
options: OptionSortType[];
|
||||||
|
datasource: Record<string, any>;
|
||||||
|
partitionColumn?: string;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function DndAdhocFilterOption({
|
||||||
|
adhocFilter,
|
||||||
|
options,
|
||||||
|
datasource,
|
||||||
|
onFilterEdit,
|
||||||
|
onShiftOptions,
|
||||||
|
onClickClose,
|
||||||
|
partitionColumn,
|
||||||
|
index,
|
||||||
|
}: DndAdhocFilterOptionProps) {
|
||||||
|
return (
|
||||||
|
<AdhocFilterPopoverTrigger
|
||||||
|
key={index}
|
||||||
|
adhocFilter={adhocFilter}
|
||||||
|
options={options}
|
||||||
|
datasource={datasource}
|
||||||
|
onFilterEdit={onFilterEdit}
|
||||||
|
partitionColumn={partitionColumn}
|
||||||
|
>
|
||||||
|
<OptionWrapper
|
||||||
|
key={index}
|
||||||
|
index={index}
|
||||||
|
label={adhocFilter.getDefaultLabel()}
|
||||||
|
tooltipTitle={adhocFilter.getTooltipTitle()}
|
||||||
|
clickClose={onClickClose}
|
||||||
|
onShiftOptions={onShiftOptions}
|
||||||
|
type={DndItemType.FilterOption}
|
||||||
|
withCaret
|
||||||
|
isExtra={adhocFilter.isExtra}
|
||||||
|
/>
|
||||||
|
</AdhocFilterPopoverTrigger>
|
||||||
|
);
|
||||||
|
}
|
@ -35,7 +35,6 @@ import {
|
|||||||
import { Datasource, OptionSortType } from 'src/explore/types';
|
import { Datasource, OptionSortType } from 'src/explore/types';
|
||||||
import { OptionValueType } from 'src/explore/components/controls/DndColumnSelectControl/types';
|
import { OptionValueType } from 'src/explore/components/controls/DndColumnSelectControl/types';
|
||||||
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
|
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
|
||||||
import OptionWrapper from 'src/explore/components/controls/DndColumnSelectControl/OptionWrapper';
|
|
||||||
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
|
import DndSelectLabel from 'src/explore/components/controls/DndColumnSelectControl/DndSelectLabel';
|
||||||
import AdhocFilter, {
|
import AdhocFilter, {
|
||||||
CLAUSES,
|
CLAUSES,
|
||||||
@ -50,6 +49,7 @@ import {
|
|||||||
import { DndItemType } from 'src/explore/components/DndItemType';
|
import { DndItemType } from 'src/explore/components/DndItemType';
|
||||||
import { ControlComponentProps } from 'src/explore/components/Control';
|
import { ControlComponentProps } from 'src/explore/components/Control';
|
||||||
import AdhocFilterControl from '../FilterControl/AdhocFilterControl';
|
import AdhocFilterControl from '../FilterControl/AdhocFilterControl';
|
||||||
|
import DndAdhocFilterOption from './DndAdhocFilterOption';
|
||||||
|
|
||||||
const EMPTY_OBJECT = {};
|
const EMPTY_OBJECT = {};
|
||||||
const DND_ACCEPTED_TYPES = [
|
const DND_ACCEPTED_TYPES = [
|
||||||
@ -296,32 +296,18 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||||||
|
|
||||||
const valuesRenderer = useCallback(
|
const valuesRenderer = useCallback(
|
||||||
() =>
|
() =>
|
||||||
values.map((adhocFilter: AdhocFilter, index: number) => {
|
values.map((adhocFilter: AdhocFilter, index: number) => (
|
||||||
const label = adhocFilter.getDefaultLabel();
|
<DndAdhocFilterOption
|
||||||
const tooltipTitle = adhocFilter.getTooltipTitle();
|
index={index}
|
||||||
return (
|
|
||||||
<AdhocFilterPopoverTrigger
|
|
||||||
key={index}
|
|
||||||
adhocFilter={adhocFilter}
|
adhocFilter={adhocFilter}
|
||||||
options={options}
|
options={options}
|
||||||
datasource={datasource}
|
datasource={datasource}
|
||||||
onFilterEdit={onFilterEdit}
|
onFilterEdit={onFilterEdit}
|
||||||
partitionColumn={partitionColumn}
|
partitionColumn={partitionColumn}
|
||||||
>
|
onClickClose={onClickClose}
|
||||||
<OptionWrapper
|
|
||||||
key={index}
|
|
||||||
index={index}
|
|
||||||
label={label}
|
|
||||||
tooltipTitle={tooltipTitle}
|
|
||||||
clickClose={onClickClose}
|
|
||||||
onShiftOptions={onShiftOptions}
|
onShiftOptions={onShiftOptions}
|
||||||
type={DndItemType.FilterOption}
|
|
||||||
withCaret
|
|
||||||
isExtra={adhocFilter.isExtra}
|
|
||||||
/>
|
/>
|
||||||
</AdhocFilterPopoverTrigger>
|
)),
|
||||||
);
|
|
||||||
}),
|
|
||||||
[
|
[
|
||||||
onClickClose,
|
onClickClose,
|
||||||
onFilterEdit,
|
onFilterEdit,
|
||||||
@ -401,9 +387,7 @@ const DndFilterSelect = (props: DndFilterSelectProps) => {
|
|||||||
visible={newFilterPopoverVisible}
|
visible={newFilterPopoverVisible}
|
||||||
togglePopover={togglePopover}
|
togglePopover={togglePopover}
|
||||||
closePopover={closePopover}
|
closePopover={closePopover}
|
||||||
>
|
/>
|
||||||
<div />
|
|
||||||
</AdhocFilterPopoverTrigger>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -39,6 +39,7 @@ import { Tooltip } from 'src/components/Tooltip';
|
|||||||
import { Input } from 'src/components/Input';
|
import { Input } from 'src/components/Input';
|
||||||
import { optionLabel } from 'src/utils/common';
|
import { optionLabel } from 'src/utils/common';
|
||||||
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
|
||||||
|
import { ColumnMeta } from '@superset-ui/chart-controls';
|
||||||
import useAdvancedDataTypes from './useAdvancedDataTypes';
|
import useAdvancedDataTypes from './useAdvancedDataTypes';
|
||||||
|
|
||||||
const StyledInput = styled(Input)`
|
const StyledInput = styled(Input)`
|
||||||
@ -61,20 +62,9 @@ const SelectWithLabel = styled(Select)<{ labelText: string }>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export interface SimpleColumnType {
|
|
||||||
id: number;
|
|
||||||
column_name: string;
|
|
||||||
expression?: string;
|
|
||||||
type: string;
|
|
||||||
optionName?: string;
|
|
||||||
filterBy?: string;
|
|
||||||
value?: string;
|
|
||||||
advanced_data_type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SimpleExpressionType {
|
export interface SimpleExpressionType {
|
||||||
expressionType: keyof typeof EXPRESSION_TYPES;
|
expressionType: keyof typeof EXPRESSION_TYPES;
|
||||||
column: SimpleColumnType;
|
column: ColumnMeta;
|
||||||
aggregate: keyof typeof AGGREGATES;
|
aggregate: keyof typeof AGGREGATES;
|
||||||
label: string;
|
label: string;
|
||||||
}
|
}
|
||||||
@ -89,7 +79,7 @@ export interface MetricColumnType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ColumnType =
|
export type ColumnType =
|
||||||
| SimpleColumnType
|
| ColumnMeta
|
||||||
| SimpleExpressionType
|
| SimpleExpressionType
|
||||||
| SQLExpressionType
|
| SQLExpressionType
|
||||||
| MetricColumnType;
|
| MetricColumnType;
|
||||||
@ -100,7 +90,7 @@ export interface Props {
|
|||||||
options: ColumnType[];
|
options: ColumnType[];
|
||||||
datasource: {
|
datasource: {
|
||||||
id: string;
|
id: string;
|
||||||
columns: SimpleColumnType[];
|
columns: ColumnMeta[];
|
||||||
type: string;
|
type: string;
|
||||||
filter_select: boolean;
|
filter_select: boolean;
|
||||||
};
|
};
|
||||||
@ -410,8 +400,8 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
|
|||||||
}
|
}
|
||||||
}, [props.adhocFilter.comparator]);
|
}, [props.adhocFilter.comparator]);
|
||||||
|
|
||||||
return (
|
// another name for columns, just for following previous naming.
|
||||||
<>
|
const subjectComponent = (
|
||||||
<Select
|
<Select
|
||||||
css={(theme: SupersetTheme) => ({
|
css={(theme: SupersetTheme) => ({
|
||||||
marginTop: theme.gridUnit * 4,
|
marginTop: theme.gridUnit * 4,
|
||||||
@ -435,6 +425,10 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
|
|||||||
}))}
|
}))}
|
||||||
{...subjectSelectProps}
|
{...subjectSelectProps}
|
||||||
/>
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const operatorsAndOperandComponent = (
|
||||||
|
<>
|
||||||
<Select
|
<Select
|
||||||
css={(theme: SupersetTheme) => ({ marginBottom: theme.gridUnit * 4 })}
|
css={(theme: SupersetTheme) => ({ marginBottom: theme.gridUnit * 4 })}
|
||||||
options={(props.operators ?? OPERATORS_OPTIONS)
|
options={(props.operators ?? OPERATORS_OPTIONS)
|
||||||
@ -484,6 +478,12 @@ const AdhocFilterEditPopoverSimpleTabContent: React.FC<Props> = props => {
|
|||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{subjectComponent}
|
||||||
|
{operatorsAndOperandComponent}
|
||||||
|
</>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default AdhocFilterEditPopoverSimpleTabContent;
|
export default AdhocFilterEditPopoverSimpleTabContent;
|
||||||
|
@ -25,7 +25,7 @@ import AdhocFilter, {
|
|||||||
EXPRESSION_TYPES,
|
EXPRESSION_TYPES,
|
||||||
CLAUSES,
|
CLAUSES,
|
||||||
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
|
} from 'src/explore/components/controls/FilterControl/AdhocFilter';
|
||||||
import AdhocFilterOption from '.';
|
import AdhocFilterOption, { AdhocFilterOptionProps } from '.';
|
||||||
|
|
||||||
const simpleAdhocFilter = new AdhocFilter({
|
const simpleAdhocFilter = new AdhocFilter({
|
||||||
expressionType: EXPRESSION_TYPES.SIMPLE,
|
expressionType: EXPRESSION_TYPES.SIMPLE,
|
||||||
@ -44,18 +44,18 @@ const options = [
|
|||||||
const mockedProps = {
|
const mockedProps = {
|
||||||
adhocFilter: simpleAdhocFilter,
|
adhocFilter: simpleAdhocFilter,
|
||||||
onFilterEdit: jest.fn(),
|
onFilterEdit: jest.fn(),
|
||||||
|
onRemoveFilter: jest.fn(),
|
||||||
options,
|
options,
|
||||||
|
sections: [],
|
||||||
|
operators: [],
|
||||||
|
datasource: {},
|
||||||
|
partitionColumn: '',
|
||||||
|
onMoveLabel: jest.fn(),
|
||||||
|
onDropLabel: jest.fn(),
|
||||||
|
index: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
const setup = (props: {
|
const setup = (props: AdhocFilterOptionProps) => (
|
||||||
adhocFilter: typeof simpleAdhocFilter;
|
|
||||||
onFilterEdit: () => void;
|
|
||||||
options: {
|
|
||||||
type: string;
|
|
||||||
column_name: string;
|
|
||||||
id: number;
|
|
||||||
}[];
|
|
||||||
}) => (
|
|
||||||
<DndProvider backend={HTML5Backend}>
|
<DndProvider backend={HTML5Backend}>
|
||||||
<AdhocFilterOption {...props} />
|
<AdhocFilterOption {...props} />
|
||||||
</DndProvider>
|
</DndProvider>
|
||||||
|
@ -1,86 +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 PropTypes from 'prop-types';
|
|
||||||
import adhocMetricType from 'src/explore/components/controls/MetricControl/adhocMetricType';
|
|
||||||
import { OptionControlLabel } from 'src/explore/components/controls/OptionControls';
|
|
||||||
import { DndItemType } from 'src/explore/components/DndItemType';
|
|
||||||
import columnType from 'src/explore/components/controls/FilterControl/columnType';
|
|
||||||
import AdhocFilterPopoverTrigger from 'src/explore/components/controls/FilterControl/AdhocFilterPopoverTrigger';
|
|
||||||
import AdhocFilter from 'src/explore/components/controls/FilterControl/AdhocFilter';
|
|
||||||
|
|
||||||
const propTypes = {
|
|
||||||
adhocFilter: PropTypes.instanceOf(AdhocFilter).isRequired,
|
|
||||||
onFilterEdit: PropTypes.func.isRequired,
|
|
||||||
onRemoveFilter: PropTypes.func,
|
|
||||||
options: PropTypes.arrayOf(
|
|
||||||
PropTypes.oneOfType([
|
|
||||||
columnType,
|
|
||||||
PropTypes.shape({ saved_metric_name: PropTypes.string.isRequired }),
|
|
||||||
adhocMetricType,
|
|
||||||
]),
|
|
||||||
).isRequired,
|
|
||||||
sections: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
operators: PropTypes.arrayOf(PropTypes.string),
|
|
||||||
datasource: PropTypes.object,
|
|
||||||
partitionColumn: PropTypes.string,
|
|
||||||
onMoveLabel: PropTypes.func,
|
|
||||||
onDropLabel: PropTypes.func,
|
|
||||||
index: PropTypes.number,
|
|
||||||
};
|
|
||||||
|
|
||||||
const AdhocFilterOption = ({
|
|
||||||
adhocFilter,
|
|
||||||
options,
|
|
||||||
datasource,
|
|
||||||
onFilterEdit,
|
|
||||||
onRemoveFilter,
|
|
||||||
partitionColumn,
|
|
||||||
onMoveLabel,
|
|
||||||
onDropLabel,
|
|
||||||
index,
|
|
||||||
sections,
|
|
||||||
operators,
|
|
||||||
}) => (
|
|
||||||
<AdhocFilterPopoverTrigger
|
|
||||||
sections={sections}
|
|
||||||
operators={operators}
|
|
||||||
adhocFilter={adhocFilter}
|
|
||||||
options={options}
|
|
||||||
datasource={datasource}
|
|
||||||
onFilterEdit={onFilterEdit}
|
|
||||||
partitionColumn={partitionColumn}
|
|
||||||
>
|
|
||||||
<OptionControlLabel
|
|
||||||
label={adhocFilter.getDefaultLabel()}
|
|
||||||
tooltipTitle={adhocFilter.getTooltipTitle()}
|
|
||||||
onRemove={onRemoveFilter}
|
|
||||||
onMoveLabel={onMoveLabel}
|
|
||||||
onDropLabel={onDropLabel}
|
|
||||||
index={index}
|
|
||||||
type={DndItemType.FilterOption}
|
|
||||||
withCaret
|
|
||||||
isExtra={adhocFilter.isExtra}
|
|
||||||
/>
|
|
||||||
</AdhocFilterPopoverTrigger>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default AdhocFilterOption;
|
|
||||||
|
|
||||||
AdhocFilterOption.propTypes = propTypes;
|
|
@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* 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 { OptionControlLabel } from 'src/explore/components/controls/OptionControls';
|
||||||
|
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 { Operators } from 'src/explore/constants';
|
||||||
|
|
||||||
|
export interface AdhocFilterOptionProps {
|
||||||
|
adhocFilter: AdhocFilter;
|
||||||
|
onFilterEdit: () => void;
|
||||||
|
onRemoveFilter: () => void;
|
||||||
|
options: OptionSortType[];
|
||||||
|
sections: string[];
|
||||||
|
operators: Operators[];
|
||||||
|
datasource: Record<string, any>;
|
||||||
|
partitionColumn: string;
|
||||||
|
onMoveLabel: () => void;
|
||||||
|
onDropLabel: () => void;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function AdhocFilterOption({
|
||||||
|
adhocFilter,
|
||||||
|
options,
|
||||||
|
datasource,
|
||||||
|
onFilterEdit,
|
||||||
|
onRemoveFilter,
|
||||||
|
partitionColumn,
|
||||||
|
onMoveLabel,
|
||||||
|
onDropLabel,
|
||||||
|
index,
|
||||||
|
sections,
|
||||||
|
operators,
|
||||||
|
}: AdhocFilterOptionProps) {
|
||||||
|
return (
|
||||||
|
<AdhocFilterPopoverTrigger
|
||||||
|
sections={sections}
|
||||||
|
operators={operators}
|
||||||
|
adhocFilter={adhocFilter}
|
||||||
|
options={options}
|
||||||
|
datasource={datasource}
|
||||||
|
onFilterEdit={onFilterEdit}
|
||||||
|
partitionColumn={partitionColumn}
|
||||||
|
>
|
||||||
|
<OptionControlLabel
|
||||||
|
label={adhocFilter.getDefaultLabel()}
|
||||||
|
tooltipTitle={adhocFilter.getTooltipTitle()}
|
||||||
|
onRemove={onRemoveFilter}
|
||||||
|
onMoveLabel={onMoveLabel}
|
||||||
|
onDropLabel={onDropLabel}
|
||||||
|
index={index}
|
||||||
|
type={DndItemType.FilterOption}
|
||||||
|
withCaret
|
||||||
|
isExtra={adhocFilter.isExtra}
|
||||||
|
/>
|
||||||
|
</AdhocFilterPopoverTrigger>
|
||||||
|
);
|
||||||
|
}
|
@ -142,10 +142,6 @@ export const TIME_FILTER_MAP = {
|
|||||||
granularity: '__granularity',
|
granularity: '__granularity',
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: make this configurable per Superset installation
|
|
||||||
export const DEFAULT_TIME_RANGE = 'No filter';
|
|
||||||
export const NO_TIME_RANGE = 'No filter';
|
|
||||||
|
|
||||||
export enum FILTER_BOX_MIGRATION_STATES {
|
export enum FILTER_BOX_MIGRATION_STATES {
|
||||||
CONVERTED = 'CONVERTED',
|
CONVERTED = 'CONVERTED',
|
||||||
NOOP = 'NOOP',
|
NOOP = 'NOOP',
|
||||||
|
@ -29,8 +29,8 @@ import {
|
|||||||
AdhocFilter,
|
AdhocFilter,
|
||||||
isFreeFormAdhocFilter,
|
isFreeFormAdhocFilter,
|
||||||
isSimpleAdhocFilter,
|
isSimpleAdhocFilter,
|
||||||
|
NO_TIME_RANGE,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { NO_TIME_RANGE } from '../constants';
|
|
||||||
|
|
||||||
const simpleFilterToAdhoc = (
|
const simpleFilterToAdhoc = (
|
||||||
filterClause: QueryObjectFilterClause,
|
filterClause: QueryObjectFilterClause,
|
||||||
|
@ -16,10 +16,9 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { styled } from '@superset-ui/core';
|
import { styled, NO_TIME_RANGE } from '@superset-ui/core';
|
||||||
import React, { useCallback, useEffect } from 'react';
|
import React, { useCallback, useEffect } from 'react';
|
||||||
import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
|
import DateFilterControl from 'src/explore/components/controls/DateFilterControl';
|
||||||
import { NO_TIME_RANGE } from 'src/explore/constants';
|
|
||||||
import { PluginFilterTimeProps } from './types';
|
import { PluginFilterTimeProps } from './types';
|
||||||
import { FilterPluginStyle } from '../common';
|
import { FilterPluginStyle } from '../common';
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user