diff --git a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts index 5080f531f1..54b41090f3 100644 --- a/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts +++ b/superset-frontend/spec/javascripts/dashboard/util/getFormDataWithExtraFilters_spec.ts @@ -27,19 +27,24 @@ import { sliceId as chartId } from '../../../fixtures/mockChartQueries'; describe('getFormDataWithExtraFilters', () => { const filterId = 'native-filter-1'; - const mockArgs: GetFormDataWithExtraFiltersArguments = { - chart: { - id: chartId, - formData: { - filters: [ - { - col: 'country_name', - op: 'IN', - val: ['United States'], - }, - ], - }, + const mockChart = { + id: chartId, + formData: { + viz_type: 'filter_select', + filters: [ + { + col: 'country_name', + op: 'IN', + val: ['United States'], + }, + ], }, + }; + const mockArgs: GetFormDataWithExtraFiltersArguments = { + charts: { + [chartId]: mockChart, + }, + chart: mockChart, filters: { region: ['Spain'], color: ['pink', 'purple'], diff --git a/superset-frontend/src/chart/ChartContainer.jsx b/superset-frontend/src/chart/ChartContainer.jsx index f509281f3d..ecc4a2d8a8 100644 --- a/superset-frontend/src/chart/ChartContainer.jsx +++ b/superset-frontend/src/chart/ChartContainer.jsx @@ -22,12 +22,14 @@ import { bindActionCreators } from 'redux'; import * as actions from './chartAction'; import { logEvent } from '../logger/actions'; import Chart from './Chart'; +import { setExtraFormData } from '../dashboard/actions/nativeFilters'; function mapDispatchToProps(dispatch) { return { actions: bindActionCreators( { ...actions, + setExtraFormData, logEvent, }, dispatch, diff --git a/superset-frontend/src/chart/ChartRenderer.jsx b/superset-frontend/src/chart/ChartRenderer.jsx index 0c21a3a5c0..eafe4b40c7 100644 --- a/superset-frontend/src/chart/ChartRenderer.jsx +++ b/superset-frontend/src/chart/ChartRenderer.jsx @@ -42,6 +42,7 @@ const propTypes = { refreshOverlayVisible: PropTypes.bool, // dashboard callbacks addFilter: PropTypes.func, + setExtraFormData: PropTypes.func, onFilterMenuOpen: PropTypes.func, onFilterMenuClose: PropTypes.func, }; @@ -73,6 +74,8 @@ class ChartRenderer extends React.Component { setControlValue: this.handleSetControlValue, onFilterMenuOpen: this.props.onFilterMenuOpen, onFilterMenuClose: this.props.onFilterMenuClose, + setExtraFormData: extraFormData => + this.props.actions?.setExtraFormData(this.props.chartId, extraFormData), }; } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts index b0815b6322..d4417b2f03 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/utils.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/utils.ts @@ -16,8 +16,16 @@ * specific language governing permissions and limitations * under the License. */ -import { ExtraFormData, QueryFormData, QueryObject } from '@superset-ui/core'; +import { + ExtraFormData, + QueryFormData, + getChartMetadataRegistry, + QueryObject, + Behavior, +} from '@superset-ui/core'; +import { Charts } from 'src/dashboard/types'; import { RefObject } from 'react'; +import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { Filter } from './types'; import { NativeFiltersState } from '../../reducers/types'; @@ -93,8 +101,16 @@ export function mergeExtraFormData( }; } +export function isCrossFilter(vizType: string) { + // @ts-ignore need export from superset-ui `ItemWithValue` + return getChartMetadataRegistry().items[vizType]?.value.behaviors?.includes( + Behavior.CROSS_FILTER, + ); +} + export function getExtraFormData( nativeFilters: NativeFiltersState, + charts: Charts, ): ExtraFormData { let extraFormData: ExtraFormData = {}; Object.keys(nativeFilters.filters).forEach(key => { @@ -102,5 +118,14 @@ export function getExtraFormData( const { extraFormData: newExtra = {} } = filterState; extraFormData = mergeExtraFormData(extraFormData, newExtra); }); + if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) { + Object.entries(charts).forEach(([key, chart]) => { + if (isCrossFilter(chart?.formData?.viz_type)) { + const filterState = nativeFilters.filtersState[key] || {}; + const { extraFormData: newExtra = {} } = filterState; + extraFormData = mergeExtraFormData(extraFormData, newExtra); + } + }); + } return extraFormData; } diff --git a/superset-frontend/src/dashboard/containers/Chart.jsx b/superset-frontend/src/dashboard/containers/Chart.jsx index 9c48a89488..7f74657380 100644 --- a/superset-frontend/src/dashboard/containers/Chart.jsx +++ b/superset-frontend/src/dashboard/containers/Chart.jsx @@ -60,6 +60,7 @@ function mapStateToProps( const formData = getFormDataWithExtraFilters({ layout: dashboardLayout.present, chart, + charts: chartQueries, filters: getAppliedFilterValues(id), colorScheme, colorNamespace, diff --git a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts index 47aada1e1d..967e05b295 100644 --- a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts +++ b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts @@ -21,7 +21,7 @@ import { CategoricalColorNamespace, DataRecordFilters, } from '@superset-ui/core'; -import { ChartQueryPayload, LayoutItem } from 'src/dashboard/types'; +import { ChartQueryPayload, Charts, LayoutItem } from 'src/dashboard/types'; import { getExtraFormData } from 'src/dashboard/components/nativeFilters/utils'; import getEffectiveExtraFilters from './getEffectiveExtraFilters'; import { getActiveNativeFilters } from '../activeDashboardNativeFilters'; @@ -34,6 +34,7 @@ const cachedFormdataByChart = {}; export interface GetFormDataWithExtraFiltersArguments { chart: ChartQueryPayload; + charts: Charts; filters: DataRecordFilters; layout: { [key: string]: LayoutItem }; colorScheme?: string; @@ -47,6 +48,7 @@ export interface GetFormDataWithExtraFiltersArguments { // filters param only contains those applicable to this chart. export default function getFormDataWithExtraFilters({ chart, + charts, filters, colorScheme, colorNamespace, @@ -78,7 +80,7 @@ export default function getFormDataWithExtraFilters({ ); if (isAffectedChart) { extraData = { - extra_form_data: getExtraFormData(nativeFilters), + extra_form_data: getExtraFormData(nativeFilters, charts), }; } diff --git a/superset-frontend/src/explore/components/controls/VizTypeControl.jsx b/superset-frontend/src/explore/components/controls/VizTypeControl.jsx index af30ce86a6..085fdd8da6 100644 --- a/superset-frontend/src/explore/components/controls/VizTypeControl.jsx +++ b/superset-frontend/src/explore/components/controls/VizTypeControl.jsx @@ -24,9 +24,9 @@ import { useDynamicPluginContext } from 'src/components/DynamicPlugins'; import { Tooltip } from 'src/common/components/Tooltip'; import Modal from 'src/common/components/Modal'; import Label from 'src/components/Label'; - import ControlHeader from '../ControlHeader'; import './VizTypeControl.less'; +import { FeatureFlag, isFeatureEnabled } from '../../../featureFlags'; const propTypes = { description: PropTypes.string, @@ -168,7 +168,11 @@ const VizTypeControl = props => { const filteredTypes = DEFAULT_ORDER.filter(type => registry.has(type)) .filter(type => { const behaviors = registry.get(type)?.behaviors || []; - return behaviors.includes(Behavior.CROSS_FILTER) || !behaviors.length; + return ( + (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) && + behaviors.includes(Behavior.CROSS_FILTER)) || + !behaviors.length + ); }) .map(type => ({ key: type, @@ -179,7 +183,11 @@ const VizTypeControl = props => { .entries() .filter(entry => { const behaviors = entry.value?.behaviors || []; - return behaviors.includes(Behavior.CROSS_FILTER) || !behaviors.length; + return ( + (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS) && + behaviors.includes(Behavior.CROSS_FILTER)) || + !behaviors.length + ); }) .filter(({ key }) => !typesWithDefaultOrder.has(key)), ) diff --git a/superset-frontend/src/featureFlags.ts b/superset-frontend/src/featureFlags.ts index 52e157c629..c8b8acc20c 100644 --- a/superset-frontend/src/featureFlags.ts +++ b/superset-frontend/src/featureFlags.ts @@ -35,6 +35,7 @@ export enum FeatureFlag { DISPLAY_MARKDOWN_HTML = 'DISPLAY_MARKDOWN_HTML', ESCAPE_MARKDOWN_HTML = 'ESCAPE_MARKDOWN_HTML', DASHBOARD_NATIVE_FILTERS = 'DASHBOARD_NATIVE_FILTERS', + DASHBOARD_CROSS_FILTERS = 'DASHBOARD_CROSS_FILTERS', VERSIONED_EXPORT = 'VERSIONED_EXPORT', GLOBAL_ASYNC_QUERIES = 'GLOBAL_ASYNC_QUERIES', ENABLE_TEMPLATE_PROCESSING = 'ENABLE_TEMPLATE_PROCESSING', diff --git a/superset/config.py b/superset/config.py index 028a23789b..2686f4544d 100644 --- a/superset/config.py +++ b/superset/config.py @@ -328,6 +328,7 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = { # When True, this escapes HTML (rather than rendering it) in Markdown components "ESCAPE_MARKDOWN_HTML": False, "DASHBOARD_NATIVE_FILTERS": False, + "DASHBOARD_CROSS_FILTERS": False, "GLOBAL_ASYNC_QUERIES": False, "VERSIONED_EXPORT": False, # Note that: RowLevelSecurityFilter is only given by default to the Admin role