chore: revert "feat(native_filter_migration): add transition mode (#16992)" (#23144)

This commit is contained in:
John Bodley 2023-02-24 06:22:36 +13:00 committed by GitHub
parent ee1952e488
commit 6338ea5d42
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 60 additions and 933 deletions

View File

@ -56,7 +56,6 @@ These features are **finished** but currently being tested. They are usable, but
- DASHBOARD_FILTERS_EXPERIMENTAL
- DASHBOARD_NATIVE_FILTERS
- DYNAMIC_PLUGINS: [(docs)](https://superset.apache.org/docs/installation/running-on-kubernetes)
- ENABLE_FILTER_BOX_MIGRATION
- ENABLE_JAVASCRIPT_CONTROLS
- GENERIC_CHART_AXES
- GLOBAL_ASYNC_QUERIES [(docs)](https://github.com/apache/superset/blob/master/CONTRIBUTING.md#async-chart-queries)

View File

@ -44,7 +44,6 @@ export enum FeatureFlag {
ENABLE_ADVANCED_DATA_TYPES = 'ENABLE_ADVANCED_DATA_TYPES',
ENABLE_DND_WITH_CLICK_UX = 'ENABLE_DND_WITH_CLICK_UX',
ENABLE_EXPLORE_DRAG_AND_DROP = 'ENABLE_EXPLORE_DRAG_AND_DROP',
ENABLE_FILTER_BOX_MIGRATION = 'ENABLE_FILTER_BOX_MIGRATION',
ENABLE_JAVASCRIPT_CONTROLS = 'ENABLE_JAVASCRIPT_CONTROLS',
ENABLE_TEMPLATE_PROCESSING = 'ENABLE_TEMPLATE_PROCESSING',
ENABLE_TEMPLATE_REMOVE_FILTERS = 'ENABLE_TEMPLATE_REMOVE_FILTERS',

View File

@ -59,7 +59,6 @@ const propTypes = {
triggerRender: PropTypes.bool,
force: PropTypes.bool,
isFiltersInitialized: PropTypes.bool,
isDeactivatedViz: PropTypes.bool,
// state
chartAlert: PropTypes.string,
chartStatus: PropTypes.string,
@ -94,7 +93,6 @@ const defaultProps = {
triggerRender: false,
dashboardId: null,
chartStackTrace: null,
isDeactivatedViz: false,
force: false,
isInView: true,
};
@ -140,25 +138,13 @@ class Chart extends React.PureComponent {
}
componentDidMount() {
// during migration, hold chart queries before user choose review or cancel
if (
this.props.triggerQuery &&
this.props.filterboxMigrationState !== 'UNDECIDED'
) {
if (this.props.triggerQuery) {
this.runQuery();
}
}
componentDidUpdate() {
// during migration, hold chart queries before user choose review or cancel
if (
this.props.triggerQuery &&
this.props.filterboxMigrationState !== 'UNDECIDED'
) {
// if the chart is deactivated (filter_box), only load once
if (this.props.isDeactivatedViz && this.props.queriesResponse) {
return;
}
if (this.props.triggerQuery) {
this.runQuery();
}
}
@ -261,7 +247,6 @@ class Chart extends React.PureComponent {
errorMessage,
chartIsStale,
queriesResponse = [],
isDeactivatedViz = false,
width,
} = this.props;
@ -332,7 +317,7 @@ class Chart extends React.PureComponent {
<Loading />
)}
</div>
{isLoading && !isDeactivatedViz && <Loading />}
{isLoading && <Loading />}
</Styles>
</ErrorBoundary>
);

View File

@ -27,10 +27,6 @@ export const BOOL_TRUE_DISPLAY = 'True';
export const BOOL_FALSE_DISPLAY = 'False';
export const URL_PARAMS = {
migrationState: {
name: 'migration_state',
type: 'string',
},
standalone: {
name: 'standalone',
type: 'number',

View File

@ -50,11 +50,9 @@ import newComponentFactory from 'src/dashboard/util/newComponentFactory';
import { TIME_RANGE } from 'src/visualizations/FilterBox/FilterBox';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { ResourceStatus } from 'src/hooks/apiResources/apiResources';
import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
import extractUrlParams from '../util/extractUrlParams';
import getNativeFilterConfig from '../util/filterboxMigrationHelper';
import { updateColorSchema } from './dashboardInfo';
import { getChartIdsInFilterScope } from '../util/getChartIdsInFilterScope';
import updateComponentParentsList from '../util/updateComponentParentsList';
@ -63,14 +61,7 @@ import { FilterBarOrientation } from '../types';
export const HYDRATE_DASHBOARD = 'HYDRATE_DASHBOARD';
export const hydrateDashboard =
({
history,
dashboard,
charts,
filterboxMigrationState = FILTER_BOX_MIGRATION_STATES.NOOP,
dataMask,
activeTabs,
}) =>
({ history, dashboard, charts, dataMask, activeTabs }) =>
(dispatch, getState) => {
const { user, common, dashboardState } = getState();
const { metadata, position_data: positionData } = dashboard;
@ -232,25 +223,18 @@ export const hydrateDashboard =
const componentId = chartIdToLayoutId[key];
const directPathToFilter = (layout[componentId].parents || []).slice();
directPathToFilter.push(componentId);
if (
[
FILTER_BOX_MIGRATION_STATES.NOOP,
FILTER_BOX_MIGRATION_STATES.SNOOZED,
].includes(filterboxMigrationState)
) {
dashboardFilters[key] = {
...dashboardFilter,
chartId: key,
componentId,
datasourceId: slice.form_data.datasource,
filterName: slice.slice_name,
directPathToFilter,
columns,
labels,
scopes: scopesByChartId,
isDateFilter: Object.keys(columns).includes(TIME_RANGE),
};
}
dashboardFilters[key] = {
...dashboardFilter,
chartId: key,
componentId,
datasourceId: slice.form_data.datasource,
filterName: slice.slice_name,
directPathToFilter,
columns,
labels,
scopes: scopesByChartId,
isDateFilter: Object.keys(columns).includes(TIME_RANGE),
};
}
// sync layout names with current slice names in case a slice was edited
@ -319,28 +303,12 @@ export const hydrateDashboard =
directPathToChild.push(directLinkComponentId);
}
// should convert filter_box to filter component?
let filterConfig = metadata?.native_filter_configuration || [];
if (filterboxMigrationState === FILTER_BOX_MIGRATION_STATES.REVIEWING) {
filterConfig = getNativeFilterConfig(
charts,
filterScopes,
preselectFilters,
);
metadata.native_filter_configuration = filterConfig;
metadata.show_native_filters = true;
}
const nativeFilters = getInitialNativeFilterState({
filterConfig,
filterConfig: metadata?.native_filter_configuration || [],
});
metadata.show_native_filters =
dashboard?.metadata?.show_native_filters ??
(isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS) &&
[
FILTER_BOX_MIGRATION_STATES.CONVERTED,
FILTER_BOX_MIGRATION_STATES.REVIEWING,
FILTER_BOX_MIGRATION_STATES.NOOP,
].includes(filterboxMigrationState));
metadata.show_native_filters = isFeatureEnabled(
FeatureFlag.DASHBOARD_NATIVE_FILTERS,
);
if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {
// If user just added cross filter to dashboard it's not saving it scope on server,
@ -465,7 +433,6 @@ export const hydrateDashboard =
isRefreshing: false,
isFiltersRefreshing: false,
activeTabs: activeTabs || dashboardState?.activeTabs || [],
filterboxMigrationState,
datasetsStatus: ResourceStatus.LOADING,
},
dashboardLayout,

View File

@ -46,7 +46,6 @@ export function fetchAllSlicesFailed(error) {
export function fetchSlices(
userId,
excludeFilterBox,
dispatch,
filter_value,
sortColumn = 'changed_on',
@ -84,11 +83,7 @@ export function fetchSlices(
})}`,
})
.then(({ json }) => {
let { result } = json;
// disable add filter_box viz to dashboard
if (excludeFilterBox) {
result = result.filter(slice => slice.viz_type !== 'filter_box');
}
const { result } = json;
result.forEach(slice => {
let form_data = JSON.parse(slice.params);
form_data = {
@ -135,46 +130,31 @@ export function fetchSlices(
);
}
export function fetchAllSlices(userId, excludeFilterBox = false) {
export function fetchAllSlices(userId) {
return (dispatch, getState) => {
const { sliceEntities } = getState();
if (sliceEntities.lastUpdated === 0) {
dispatch(fetchAllSlicesStarted());
return fetchSlices(userId, excludeFilterBox, dispatch, undefined);
return fetchSlices(userId, dispatch, undefined);
}
return dispatch(setAllSlices(sliceEntities.slices));
};
}
export function fetchSortedSlices(
userId,
excludeFilterBox = false,
order_column,
) {
export function fetchSortedSlices(userId, order_column) {
return dispatch => {
dispatch(fetchAllSlicesStarted());
return fetchSlices(
userId,
excludeFilterBox,
dispatch,
undefined,
order_column,
);
return fetchSlices(userId, dispatch, undefined, order_column);
};
}
export function fetchFilteredSlices(
userId,
excludeFilterBox = false,
filter_value,
) {
export function fetchFilteredSlices(userId, filter_value) {
return (dispatch, getState) => {
dispatch(fetchAllSlicesStarted());
const { sliceEntities } = getState();
return fetchSlices(
userId,
excludeFilterBox,
dispatch,
filter_value,
undefined,

View File

@ -67,7 +67,7 @@ describe('slice entity actions', () => {
describe('fetchFilteredSlices', () => {
it('should dispatch an fetchAllSlicesStarted action', async () => {
const { dispatch, getState } = setup();
const thunk1 = fetchFilteredSlices('userId', false, 'filter_value');
const thunk1 = fetchFilteredSlices('userId', 'filter_value');
await thunk1(dispatch, getState);
expect(dispatch.getCall(0).args[0]).toEqual({
type: FETCH_ALL_SLICES_STARTED,
@ -82,7 +82,7 @@ describe('slice entity actions', () => {
sliceEntities: { slices: {}, lastUpdated: 1 },
});
const thunk1 = fetchAllSlices('userId', false, 'filter_value');
const thunk1 = fetchAllSlices('userId', 'filter_value');
await thunk1(dispatch, getState);
expect(spy.get.callCount).toBe(0);

View File

@ -18,11 +18,10 @@
*/
import { useSelector } from 'react-redux';
import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags';
import { useCallback, useEffect, useState, useContext } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
import { RootState } from 'src/dashboard/types';
import { MigrationContext } from 'src/dashboard/containers/DashboardPage';
import {
useFilters,
useNativeFiltersDataMask,
@ -30,7 +29,6 @@ import {
// eslint-disable-next-line import/prefer-default-export
export const useNativeFilters = () => {
const filterboxMigrationState = useContext(MigrationContext);
const [isInitialized, setIsInitialized] = useState(false);
const showNativeFilters = useSelector<RootState, boolean>(
state =>
@ -78,15 +76,13 @@ export const useNativeFilters = () => {
useEffect(() => {
if (
expandFilters === false ||
(filterValues.length === 0 &&
nativeFiltersEnabled &&
['CONVERTED', 'REVIEWING', 'NOOP'].includes(filterboxMigrationState))
(filterValues.length === 0 && nativeFiltersEnabled)
) {
toggleDashboardFiltersOpen(false);
} else {
toggleDashboardFiltersOpen(true);
}
}, [filterValues.length, filterboxMigrationState]);
}, [filterValues.length]);
useEffect(() => {
if (showDashboard) {

View File

@ -35,7 +35,6 @@ import downloadAsImage from 'src/utils/downloadAsImage';
import getDashboardUrl from 'src/dashboard/util/getDashboardUrl';
import { getActiveFilters } from 'src/dashboard/util/activeDashboardFilters';
import { getUrlParam } from 'src/utils/urlUtils';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_IMAGE } from 'src/logger/LogUtils';
const propTypes = {
@ -70,11 +69,6 @@ const propTypes = {
refreshLimit: PropTypes.number,
refreshWarning: PropTypes.string,
lastModifiedTime: PropTypes.number.isRequired,
filterboxMigrationState: PropTypes.oneOf(
Object.keys(FILTER_BOX_MIGRATION_STATES).map(
key => FILTER_BOX_MIGRATION_STATES[key],
),
),
};
const defaultProps = {
@ -82,7 +76,6 @@ const defaultProps = {
colorScheme: undefined,
refreshLimit: 0,
refreshWarning: null,
filterboxMigrationState: FILTER_BOX_MIGRATION_STATES.NOOP,
};
const MENU_KEYS = {
@ -230,7 +223,6 @@ class HeaderActionsDropdown extends React.PureComponent {
lastModifiedTime,
addSuccessToast,
addDangerToast,
filterboxMigrationState,
setIsDropdownVisible,
isDropdownVisible,
...rest
@ -378,15 +370,14 @@ class HeaderActionsDropdown extends React.PureComponent {
</Menu>
)
) : null}
{editMode &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.CONVERTED && (
<Menu.Item key={MENU_KEYS.SET_FILTER_MAPPING}>
<FilterScopeModal
className="m-r-5"
triggerNode={t('Set filter mapping')}
/>
</Menu.Item>
)}
{editMode && (
<Menu.Item key={MENU_KEYS.SET_FILTER_MAPPING}>
<FilterScopeModal
className="m-r-5"
triggerNode={t('Set filter mapping')}
/>
</Menu.Item>
)}
<Menu.Item key={MENU_KEYS.AUTOREFRESH_MODAL}>
<RefreshIntervalModal

View File

@ -53,7 +53,6 @@ import {
import setPeriodicRunner, {
stopPeriodicRender,
} from 'src/dashboard/util/setPeriodicRunner';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { PageHeaderWithActions } from 'src/components/PageHeaderWithActions';
import { DashboardEmbedModal } from '../DashboardEmbedControls';
import OverwriteConfirm from '../OverwriteConfirm';
@ -463,18 +462,13 @@ class Header extends React.PureComponent {
shouldPersistRefreshFrequency,
setRefreshFrequency,
lastModifiedTime,
filterboxMigrationState,
logEvent,
} = this.props;
const userCanEdit =
dashboardInfo.dash_edit_perm &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.REVIEWING &&
!dashboardInfo.is_managed_externally;
dashboardInfo.dash_edit_perm && !dashboardInfo.is_managed_externally;
const userCanShare = dashboardInfo.dash_share_perm;
const userCanSaveAs =
dashboardInfo.dash_save_perm &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.REVIEWING;
const userCanSaveAs = dashboardInfo.dash_save_perm;
const userCanCurate =
isFeatureEnabled(FeatureFlag.EMBEDDED_SUPERSET) &&
findPermission('can_set_embedded', 'Dashboard', user.roles);
@ -680,7 +674,6 @@ class Header extends React.PureComponent {
refreshLimit={refreshLimit}
refreshWarning={refreshWarning}
lastModifiedTime={lastModifiedTime}
filterboxMigrationState={filterboxMigrationState}
isDropdownVisible={this.state.isDropdownVisible}
setIsDropdownVisible={this.setIsDropdownVisible}
logEvent={logEvent}

View File

@ -22,13 +22,7 @@ import PropTypes from 'prop-types';
import AutoSizer from 'react-virtualized-auto-sizer';
import { FixedSizeList as List } from 'react-window';
import { createFilter } from 'react-search-input';
import {
t,
styled,
isFeatureEnabled,
FeatureFlag,
css,
} from '@superset-ui/core';
import { t, styled, css } from '@superset-ui/core';
import { Input } from 'src/components/Input';
import { Select } from 'src/components';
import Loading from 'src/components/Loading';
@ -43,7 +37,6 @@ import {
NEW_COMPONENTS_SOURCE_ID,
} from 'src/dashboard/util/constants';
import { slicePropShape } from 'src/dashboard/util/propShapes';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import _ from 'lodash';
import AddSliceCard from './AddSliceCard';
import AddSliceDragPreview from './dnd/AddSliceDragPreview';
@ -58,7 +51,6 @@ const propTypes = {
userId: PropTypes.string.isRequired,
selectedSliceIds: PropTypes.arrayOf(PropTypes.number),
editMode: PropTypes.bool,
filterboxMigrationState: FILTER_BOX_MIGRATION_STATES,
dashboardId: PropTypes.number,
};
@ -66,7 +58,6 @@ const defaultProps = {
selectedSliceIds: [],
editMode: false,
errorMessage: '',
filterboxMigrationState: FILTER_BOX_MIGRATION_STATES.NOOP,
};
const KEYS_TO_FILTERS = ['slice_name', 'viz_type', 'datasource_name'];
@ -150,12 +141,7 @@ class SliceAdder extends React.Component {
}
componentDidMount() {
const { userId, filterboxMigrationState } = this.props;
this.slicesRequest = this.props.fetchAllSlices(
userId,
isFeatureEnabled(FeatureFlag.ENABLE_FILTER_BOX_MIGRATION) &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.SNOOZED,
);
this.slicesRequest = this.props.fetchAllSlices(this.props.userId);
}
UNSAFE_componentWillReceiveProps(nextProps) {
@ -198,13 +184,8 @@ class SliceAdder extends React.Component {
handleChange = _.debounce(value => {
this.searchUpdated(value);
const { userId, filterboxMigrationState } = this.props;
this.slicesRequest = this.props.fetchFilteredSlices(
userId,
isFeatureEnabled(FeatureFlag.ENABLE_FILTER_BOX_MIGRATION) &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.SNOOZED,
value,
);
const { userId } = this.props;
this.slicesRequest = this.props.fetchFilteredSlices(userId, value);
}, 300);
searchUpdated(searchTerm) {
@ -226,13 +207,8 @@ class SliceAdder extends React.Component {
),
}));
const { userId, filterboxMigrationState } = this.props;
this.slicesRequest = this.props.fetchSortedSlices(
userId,
isFeatureEnabled(FeatureFlag.ENABLE_FILTER_BOX_MIGRATION) &&
filterboxMigrationState !== FILTER_BOX_MIGRATION_STATES.SNOOZED,
sortBy,
);
const { userId } = this.props;
this.slicesRequest = this.props.fetchSortedSlices(userId, sortBy);
}
rowRenderer({ key, index, style }) {

View File

@ -38,7 +38,6 @@ import {
LOG_ACTIONS_FORCE_REFRESH_CHART,
} from 'src/logger/LogUtils';
import { areObjectsEqual } from 'src/reduxUtils';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { postFormData } from 'src/explore/exploreUtils/formData';
import { URL_PARAMS } from 'src/constants';
@ -70,11 +69,6 @@ const propTypes = {
sliceName: PropTypes.string.isRequired,
timeout: PropTypes.number.isRequired,
maxRows: PropTypes.number.isRequired,
filterboxMigrationState: PropTypes.oneOf(
Object.keys(FILTER_BOX_MIGRATION_STATES).map(
key => FILTER_BOX_MIGRATION_STATES[key],
),
),
// all active filter fields in dashboard
filters: PropTypes.object.isRequired,
refreshChart: PropTypes.func.isRequired,
@ -129,11 +123,6 @@ const ChartOverlay = styled.div`
top: 0;
left: 0;
z-index: 5;
&.is-deactivated {
opacity: 0.5;
background-color: ${({ theme }) => theme.colors.grayscale.light1};
}
`;
const SliceContainer = styled.div`
@ -405,7 +394,6 @@ class Chart extends React.Component {
handleToggleFullSize,
isFullSize,
setControlValue,
filterboxMigrationState,
postTransformProps,
datasetsStatus,
isInView,
@ -422,12 +410,6 @@ class Chart extends React.Component {
const { queriesResponse, chartUpdateEndTime, chartStatus } = chart;
const isLoading = chartStatus === 'loading';
const isDeactivatedViz =
slice.viz_type === 'filter_box' &&
[
FILTER_BOX_MIGRATION_STATES.REVIEWING,
FILTER_BOX_MIGRATION_STATES.CONVERTED,
].includes(filterboxMigrationState);
// eslint-disable-next-line camelcase
const isCached = queriesResponse?.map(({ is_cached }) => is_cached) || [];
const cachedDttm =
@ -506,15 +488,15 @@ class Chart extends React.Component {
isOverflowable && 'dashboard-chart--overflowable',
)}
>
{(isLoading || isDeactivatedViz) && (
{isLoading && (
<ChartOverlay
className={cx(isDeactivatedViz && 'is-deactivated')}
style={{
width,
height: this.getChartHeight(),
}}
/>
)}
<ChartContainer
width={width}
height={this.getChartHeight()}
@ -538,8 +520,6 @@ class Chart extends React.Component {
triggerQuery={chart.triggerQuery}
vizType={slice.viz_type}
setControlValue={setControlValue}
isDeactivatedViz={isDeactivatedViz}
filterboxMigrationState={filterboxMigrationState}
postTransformProps={postTransformProps}
datasetsStatus={datasetsStatus}
isInView={isInView}

View File

@ -23,7 +23,6 @@ import { connect } from 'react-redux';
import { LineEditableTabs } from 'src/components/Tabs';
import { LOG_ACTIONS_SELECT_DASHBOARD_TAB } from 'src/logger/LogUtils';
import { AntdModal } from 'src/components';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import DragDroppable from '../dnd/DragDroppable';
import DragHandle from '../dnd/DragHandle';
import DashboardComponent from '../../containers/DashboardComponent';
@ -49,11 +48,6 @@ const propTypes = {
renderHoverMenu: PropTypes.bool,
directPathToChild: PropTypes.arrayOf(PropTypes.string),
activeTabs: PropTypes.arrayOf(PropTypes.string),
filterboxMigrationState: PropTypes.oneOf(
Object.keys(FILTER_BOX_MIGRATION_STATES).map(
key => FILTER_BOX_MIGRATION_STATES[key],
),
),
// actions (from DashboardComponent.jsx)
logEvent: PropTypes.func.isRequired,
@ -81,7 +75,6 @@ const defaultProps = {
columnWidth: 0,
activeTabs: [],
directPathToChild: [],
filterboxMigrationState: FILTER_BOX_MIGRATION_STATES.NOOP,
setActiveTabs() {},
onResizeStart() {},
onResize() {},
@ -136,10 +129,7 @@ export class Tabs extends React.PureComponent {
}
componentDidUpdate(prevProps, prevState) {
if (
prevState.activeKey !== this.state.activeKey ||
prevProps.filterboxMigrationState !== this.props.filterboxMigrationState
) {
if (prevState.activeKey !== this.state.activeKey) {
this.props.setActiveTabs(this.state.activeKey, prevState.activeKey);
}
}
@ -446,7 +436,6 @@ function mapStateToProps(state) {
nativeFilters: state.nativeFilters,
activeTabs: state.dashboardState.activeTabs,
directPathToChild: state.dashboardState.directPathToChild,
filterboxMigrationState: state.dashboardState.filterboxMigrationState,
};
}
export default connect(mapStateToProps)(Tabs);

View File

@ -18,7 +18,6 @@
*/
/* eslint-disable no-param-reassign */
import { useSelector } from 'react-redux';
import { filter, keyBy } from 'lodash';
import {
DataMaskState,
DataMaskStateWithId,
@ -27,10 +26,8 @@ import {
Filters,
FilterSets as FilterSetsType,
} from '@superset-ui/core';
import { useContext, useEffect, useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { ChartsState, RootState } from 'src/dashboard/types';
import { MigrationContext } from 'src/dashboard/containers/DashboardPage';
import { FILTER_BOX_MIGRATION_STATES } from 'src/explore/constants';
import { NATIVE_FILTER_PREFIX } from '../FiltersConfigModal/utils';
export const useFilterSets = () =>
@ -102,30 +99,14 @@ export const useFilterUpdates = (
export const useInitialization = () => {
const [isInitialized, setIsInitialized] = useState<boolean>(false);
const filters = useFilters();
const filterboxMigrationState = useContext(MigrationContext);
let charts = useSelector<RootState, ChartsState>(state => state.charts);
const charts = useSelector<RootState, ChartsState>(state => state.charts);
// We need to know how much charts now shown on dashboard to know how many of all charts should be loaded
let numberOfLoadingCharts = 0;
if (!isInitialized) {
// do not load filter_box in reviewing
if (filterboxMigrationState === FILTER_BOX_MIGRATION_STATES.REVIEWING) {
charts = keyBy(
filter(charts, chart => chart.form_data?.viz_type !== 'filter_box'),
'id',
);
const numberOfFilterbox = document.querySelectorAll(
'[data-test-viz-type="filter_box"]',
).length;
numberOfLoadingCharts =
document.querySelectorAll('[data-ui-anchor="chart"]').length -
numberOfFilterbox;
} else {
numberOfLoadingCharts = document.querySelectorAll(
'[data-ui-anchor="chart"]',
).length;
}
numberOfLoadingCharts = document.querySelectorAll(
'[data-ui-anchor="chart"]',
).length;
}
useEffect(() => {
if (isInitialized) {

View File

@ -100,7 +100,6 @@ function mapStateToProps(
filterState: dataMask[id]?.filterState,
maxRows: common.conf.SQL_MAX_ROW,
setControlValue,
filterboxMigrationState: dashboardState.filterboxMigrationState,
datasetsStatus,
emitCrossFilters: !!dashboardInfo.crossFiltersEnabled,
};

View File

@ -99,7 +99,6 @@ function mapStateToProps({
slug: dashboardInfo.slug,
metadata: dashboardInfo.metadata,
reports,
filterboxMigrationState: dashboardState.filterboxMigrationState,
};
}

View File

@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { FC, useEffect, useMemo, useRef, useState } from 'react';
import React, { FC, useEffect, useMemo, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import {
CategoricalColorNamespace,
@ -25,14 +25,11 @@ import {
isFeatureEnabled,
SharedLabelColorSource,
t,
useTheme,
} from '@superset-ui/core';
import pick from 'lodash/pick';
import { useDispatch, useSelector } from 'react-redux';
import { Global } from '@emotion/react';
import { useToasts } from 'src/components/MessageToasts/withToasts';
import Loading from 'src/components/Loading';
import FilterBoxMigrationModal from 'src/dashboard/components/FilterBoxMigrationModal';
import {
useDashboard,
useDashboardCharts,
@ -42,37 +39,25 @@ import { hydrateDashboard } from 'src/dashboard/actions/hydrate';
import { setDatasources } from 'src/dashboard/actions/datasources';
import injectCustomCss from 'src/dashboard/util/injectCustomCss';
import setupPlugins from 'src/setup/setupPlugins';
import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
import { addWarningToast } from 'src/components/MessageToasts/actions';
import {
getItem,
LocalStorageKeys,
setItem,
} from 'src/utils/localStorageHelpers';
import {
FILTER_BOX_MIGRATION_STATES,
FILTER_BOX_TRANSITION_SNOOZE_DURATION,
} from 'src/explore/constants';
import { URL_PARAMS } from 'src/constants';
import { getUrlParam } from 'src/utils/urlUtils';
import { canUserEditDashboard } from 'src/dashboard/util/permissionUtils';
import { getFilterSets } from 'src/dashboard/actions/nativeFilters';
import { setDatasetsStatus } from 'src/dashboard/actions/dashboardState';
import {
getFilterValue,
getPermalinkValue,
} from 'src/dashboard/components/nativeFilters/FilterBar/keyValue';
import { filterCardPopoverStyle, headerStyles } from 'src/dashboard/styles';
import { DashboardContextForExplore } from 'src/types/DashboardContextForExplore';
import shortid from 'shortid';
import { RootState } from '../types';
import { getActiveFilters } from '../util/activeDashboardFilters';
export const MigrationContext = React.createContext(
FILTER_BOX_MIGRATION_STATES.NOOP,
);
export const DashboardPageIdContext = React.createContext('');
setupPlugins();
@ -156,11 +141,7 @@ const useSyncDashboardStateWithLocalStorage = () => {
export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
const dispatch = useDispatch();
const theme = useTheme();
const history = useHistory();
const user = useSelector<any, UserWithPermissionsAndRoles>(
state => state.user,
);
const dashboardPageId = useSyncDashboardStateWithLocalStorage();
const { addDangerToast } = useToasts();
const { result: dashboard, error: dashboardApiError } =
@ -176,16 +157,7 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
const error = dashboardApiError || chartsApiError;
const readyToRender = Boolean(dashboard && charts);
const migrationStateParam = getUrlParam(
URL_PARAMS.migrationState,
) as FILTER_BOX_MIGRATION_STATES;
const isMigrationEnabled = isFeatureEnabled(
FeatureFlag.ENABLE_FILTER_BOX_MIGRATION,
);
const { dashboard_title, css, metadata, id = 0 } = dashboard || {};
const [filterboxMigrationState, setFilterboxMigrationState] = useState(
migrationStateParam || FILTER_BOX_MIGRATION_STATES.NOOP,
);
useEffect(() => {
// mark tab id as redundant when user closes browser tab - a new id will be
@ -210,67 +182,6 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
dispatch(setDatasetsStatus(status));
}, [dispatch, status]);
useEffect(() => {
// should convert filter_box to filter component?
const hasFilterBox = charts?.some(
chart => chart.form_data?.viz_type === 'filter_box',
);
const canEdit = dashboard && canUserEditDashboard(dashboard, user);
if (canEdit) {
// can user edit dashboard?
if (metadata?.native_filter_configuration) {
setFilterboxMigrationState(
isMigrationEnabled
? FILTER_BOX_MIGRATION_STATES.CONVERTED
: FILTER_BOX_MIGRATION_STATES.NOOP,
);
return;
}
// set filterbox migration state if has filter_box in the dash:
if (hasFilterBox) {
if (isMigrationEnabled) {
// has url param?
if (
migrationStateParam &&
Object.values(FILTER_BOX_MIGRATION_STATES).includes(
migrationStateParam,
)
) {
setFilterboxMigrationState(migrationStateParam);
return;
}
// has cookie?
const snoozeDash = getItem(
LocalStorageKeys.filter_box_transition_snoozed_at,
{},
);
if (
Date.now() - (snoozeDash[id] || 0) <
FILTER_BOX_TRANSITION_SNOOZE_DURATION
) {
setFilterboxMigrationState(FILTER_BOX_MIGRATION_STATES.SNOOZED);
return;
}
setFilterboxMigrationState(FILTER_BOX_MIGRATION_STATES.UNDECIDED);
} else if (isFeatureEnabled(FeatureFlag.DASHBOARD_NATIVE_FILTERS)) {
dispatch(
addWarningToast(
t(
'filter_box will be deprecated ' +
'in a future version of Superset. ' +
'Please replace filter_box by dashboard filter components.',
),
),
);
}
}
}
}, [readyToRender]);
useEffect(() => {
// eslint-disable-next-line consistent-return
async function getDataMaskApplied() {
@ -308,7 +219,6 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
dashboard,
charts,
activeTabs,
filterboxMigrationState,
dataMask,
}),
);
@ -317,7 +227,7 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
}
if (id) getDataMaskApplied();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [readyToRender, filterboxMigrationState]);
}, [readyToRender]);
useEffect(() => {
if (dashboard_title) {
@ -364,37 +274,9 @@ export const DashboardPage: FC<PageProps> = ({ idOrSlug }: PageProps) => {
if (!readyToRender) return <Loading />;
return (
<>
<Global styles={[filterCardPopoverStyle(theme), headerStyles(theme)]} />
<FilterBoxMigrationModal
show={filterboxMigrationState === FILTER_BOX_MIGRATION_STATES.UNDECIDED}
hideFooter={!isMigrationEnabled}
onHide={() => {
// cancel button: only snooze this visit
setFilterboxMigrationState(FILTER_BOX_MIGRATION_STATES.SNOOZED);
}}
onClickReview={() => {
setFilterboxMigrationState(FILTER_BOX_MIGRATION_STATES.REVIEWING);
}}
onClickSnooze={() => {
const snoozedDash = getItem(
LocalStorageKeys.filter_box_transition_snoozed_at,
{},
);
setItem(LocalStorageKeys.filter_box_transition_snoozed_at, {
...snoozedDash,
[id]: Date.now(),
});
setFilterboxMigrationState(FILTER_BOX_MIGRATION_STATES.SNOOZED);
}}
/>
<MigrationContext.Provider value={filterboxMigrationState}>
<DashboardPageIdContext.Provider value={dashboardPageId}>
<DashboardContainer />
</DashboardPageIdContext.Provider>
</MigrationContext.Provider>
</>
<DashboardPageIdContext.Provider value={dashboardPageId}>
<DashboardContainer />
</DashboardPageIdContext.Provider>
);
};

View File

@ -40,7 +40,6 @@ function mapStateToProps(
errorMessage: sliceEntities.errorMessage,
lastUpdated: sliceEntities.lastUpdated,
editMode: dashboardState.editMode,
filterboxMigrationState: dashboardState.filterboxMigrationState,
};
}

View File

@ -1,142 +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 getNativeFilterConfig from './filterboxMigrationHelper';
const regionFilter = {
cache_timeout: null,
changed_on: '2021-10-07 11:57:48.355047',
description: null,
description_markeddown: '',
form_data: {
compare_lag: '10',
compare_suffix: 'o10Y',
country_fieldtype: 'cca3',
datasource: '1__table',
date_filter: false,
entity: 'country_code',
filter_configs: [
{
asc: false,
clearable: true,
column: 'region',
key: '2s98dfu',
metric: 'sum__SP_POP_TOTL',
multiple: false,
},
{
asc: false,
clearable: true,
column: 'country_name',
key: 'li3j2lk',
metric: 'sum__SP_POP_TOTL',
multiple: true,
},
],
granularity_sqla: 'year',
groupby: [],
limit: '25',
markup_type: 'markdown',
row_limit: 50000,
show_bubbles: true,
slice_id: 32,
time_range: '2014-01-01 : 2014-01-02',
viz_type: 'filter_box',
},
modified: '<bound method AuditMixinNullable.modified of Region Filter>',
slice_name: 'Region Filter',
slice_url: '/explore/?form_data=%7B%22slice_id%22%3A%2032%7D',
slice_id: 32,
};
const chart1 = {
cache_timeout: null,
changed_on: '2021-09-07 18:05:18.896212',
description: null,
description_markeddown: '',
form_data: {
compare_lag: '10',
compare_suffix: 'over 10Y',
country_fieldtype: 'cca3',
datasource: '1__table',
entity: 'country_code',
granularity_sqla: 'year',
groupby: [],
limit: '25',
markup_type: 'markdown',
metric: 'sum__SP_POP_TOTL',
row_limit: 50000,
show_bubbles: true,
slice_id: 33,
time_range: '2000 : 2014-01-02',
viz_type: 'big_number',
},
modified: "<bound method AuditMixinNullable.modified of World's Population>",
slice_name: "World's Population",
slice_url: '/explore/?form_data=%7B%22slice_id%22%3A%2033%7D',
slice_id: 33,
};
const chartData = [regionFilter, chart1];
const preselectedFilters = {
'32': {
region: ['East Asia & Pacific'],
},
};
test('should convert filter_box config to dashboard native filter config', () => {
const filterConfig = getNativeFilterConfig(chartData, {}, {});
// convert to 2 components
expect(filterConfig.length).toEqual(2);
expect(filterConfig[0].id).toBeDefined();
expect(filterConfig[0].filterType).toBe('filter_select');
expect(filterConfig[0].name).toBe('region');
expect(filterConfig[0].targets).toEqual([
{ column: { name: 'region' }, datasetId: 1 },
]);
expect(filterConfig[0].scope).toEqual({
excluded: [],
rootPath: ['ROOT_ID'],
});
expect(filterConfig[1].id).toBeDefined();
expect(filterConfig[1].filterType).toBe('filter_select');
expect(filterConfig[1].name).toBe('country_name');
expect(filterConfig[1].targets).toEqual([
{ column: { name: 'country_name' }, datasetId: 1 },
]);
expect(filterConfig[1].scope).toEqual({
excluded: [],
rootPath: ['ROOT_ID'],
});
});
test('should convert preselected filters', () => {
const filterConfig = getNativeFilterConfig(chartData, {}, preselectedFilters);
const { defaultDataMask } = filterConfig[0];
expect(defaultDataMask.filterState).toEqual({
value: ['East Asia & Pacific'],
});
expect(defaultDataMask.extraFormData?.filters).toEqual([
{
col: 'region',
op: 'IN',
val: ['East Asia & Pacific'],
},
]);
});

View File

@ -1,423 +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 shortid from 'shortid';
import { find, isEmpty } from 'lodash';
import {
FILTER_CONFIG_ATTRIBUTES,
TIME_FILTER_LABELS,
TIME_FILTER_MAP,
} from 'src/explore/constants';
import { DASHBOARD_FILTER_SCOPE_GLOBAL } from 'src/dashboard/reducers/dashboardFilters';
import { Filter, NativeFilterType, TimeGranularity } from '@superset-ui/core';
import { getChartIdsInFilterBoxScope } from './activeDashboardFilters';
import getFilterConfigsFromFormdata from './getFilterConfigsFromFormdata';
interface FilterConfig {
asc: boolean;
clearable: boolean;
column: string;
defaultValue?: any;
key: string;
label?: string;
metric: string;
multiple: boolean;
}
interface SliceData {
slice_id: number;
form_data: {
adhoc_filters?: [];
datasource: string;
date_filter?: boolean;
filter_configs?: FilterConfig[];
granularity?: string;
granularity_sqla?: string;
time_grain_sqla?: string;
time_range?: string;
show_sqla_time_column?: boolean;
show_sqla_time_granularity?: boolean;
viz_type: string;
};
}
interface FilterScopeType {
scope: string[];
immune: number[];
}
interface FilterScopesMetadata {
[key: string]: {
[key: string]: FilterScopeType;
};
}
interface PreselectedFilterColumn {
[key: string]: boolean | string | number | string[] | number[];
}
interface PreselectedFiltersMetadata {
[key: string]: PreselectedFilterColumn;
}
interface FilterBoxToFilterComponentMap {
[key: string]: {
[key: string]: string;
};
}
interface FilterBoxDependencyMap {
[key: string]: {
[key: string]: number[];
};
}
enum FILTER_COMPONENT_FILTER_TYPES {
FILTER_TIME = 'filter_time',
FILTER_TIMEGRAIN = 'filter_timegrain',
FILTER_TIMECOLUMN = 'filter_timecolumn',
FILTER_SELECT = 'filter_select',
FILTER_RANGE = 'filter_range',
}
const getPreselectedValuesFromDashboard =
(preselectedFilters: PreselectedFiltersMetadata) =>
(filterKey: string, column: string) => {
if (preselectedFilters[filterKey]?.[column]) {
// overwrite default values by dashboard default_filters
return preselectedFilters[filterKey][column];
}
return null;
};
const getFilterBoxDefaultValues = (config: FilterConfig) => {
let defaultValues = config[FILTER_CONFIG_ATTRIBUTES.DEFAULT_VALUE];
// treat empty string as null (no default value)
if (defaultValues === '') {
defaultValues = null;
}
// defaultValue could be ; separated values,
// could be null or ''
if (defaultValues && config[FILTER_CONFIG_ATTRIBUTES.MULTIPLE]) {
defaultValues = config.defaultValue.split(';');
}
return defaultValues;
};
const setValuesInArray = (value1: any, value2: any) => {
if (!isEmpty(value1)) {
return [value1];
}
if (!isEmpty(value2)) {
return [value2];
}
return [];
};
const getFilterboxDependencies = (filterScopes: FilterScopesMetadata) => {
const filterFieldsDependencies: FilterBoxDependencyMap = {};
const filterChartIds: number[] = Object.keys(filterScopes).map(key =>
parseInt(key, 10),
);
Object.entries(filterScopes).forEach(([key, filterFields]) => {
filterFieldsDependencies[key] = {};
Object.entries(filterFields).forEach(([filterField, filterScope]) => {
filterFieldsDependencies[key][filterField] = getChartIdsInFilterBoxScope({
filterScope,
}).filter(
chartId => filterChartIds.includes(chartId) && String(chartId) !== key,
);
});
});
return filterFieldsDependencies;
};
export default function getNativeFilterConfig(
chartData: SliceData[] = [],
filterScopes: FilterScopesMetadata = {},
preselectFilters: PreselectedFiltersMetadata = {},
): Filter[] {
const filterConfig: Filter[] = [];
const filterBoxToFilterComponentMap: FilterBoxToFilterComponentMap = {};
chartData.forEach(slice => {
const key = String(slice.slice_id);
if (slice.form_data.viz_type === 'filter_box') {
filterBoxToFilterComponentMap[key] = {};
const configs = getFilterConfigsFromFormdata(slice.form_data);
let { columns } = configs;
if (preselectFilters[key]) {
Object.keys(columns).forEach(col => {
if (preselectFilters[key][col]) {
columns = {
...columns,
[col]: preselectFilters[key][col],
};
}
});
}
const scopesByChartId = Object.keys(columns).reduce((map, column) => {
const scopeSettings = {
...filterScopes[key],
};
const { scope, immune }: FilterScopeType = {
...DASHBOARD_FILTER_SCOPE_GLOBAL,
...scopeSettings[column],
};
return {
...map,
[column]: {
scope,
immune,
},
};
}, {});
const {
adhoc_filters = [],
datasource = '',
date_filter = false,
filter_configs = [],
granularity_sqla,
show_sqla_time_column = false,
show_sqla_time_granularity = false,
time_grain_sqla,
time_range,
} = slice.form_data;
const getDashboardDefaultValues =
getPreselectedValuesFromDashboard(preselectFilters);
if (date_filter) {
const { scope, immune }: FilterScopeType =
scopesByChartId[TIME_FILTER_MAP.time_range] ||
DASHBOARD_FILTER_SCOPE_GLOBAL;
const timeRangeFilter: Filter = {
id: `NATIVE_FILTER-${shortid.generate()}`,
description: 'time range filter',
controlValues: {},
name: TIME_FILTER_LABELS.time_range,
filterType: FILTER_COMPONENT_FILTER_TYPES.FILTER_TIME,
targets: [{}],
cascadeParentIds: [],
defaultDataMask: {},
type: NativeFilterType.NATIVE_FILTER,
scope: {
rootPath: scope,
excluded: immune,
},
};
filterBoxToFilterComponentMap[key][TIME_FILTER_MAP.time_range] =
timeRangeFilter.id;
const dashboardDefaultValues =
getDashboardDefaultValues(key, TIME_FILTER_MAP.time_range) ||
time_range;
if (!isEmpty(dashboardDefaultValues)) {
timeRangeFilter.defaultDataMask = {
extraFormData: { time_range: dashboardDefaultValues as string },
filterState: { value: dashboardDefaultValues },
};
}
filterConfig.push(timeRangeFilter);
if (show_sqla_time_granularity) {
const { scope, immune }: FilterScopeType =
scopesByChartId[TIME_FILTER_MAP.time_grain_sqla] ||
DASHBOARD_FILTER_SCOPE_GLOBAL;
const timeGrainFilter: Filter = {
id: `NATIVE_FILTER-${shortid.generate()}`,
controlValues: {},
description: 'time grain filter',
name: TIME_FILTER_LABELS.time_grain_sqla,
filterType: FILTER_COMPONENT_FILTER_TYPES.FILTER_TIMEGRAIN,
targets: [
{
datasetId: parseInt(datasource.split('__')[0], 10),
},
],
cascadeParentIds: [],
defaultDataMask: {},
type: NativeFilterType.NATIVE_FILTER,
scope: {
rootPath: scope,
excluded: immune,
},
};
filterBoxToFilterComponentMap[key][TIME_FILTER_MAP.time_grain_sqla] =
timeGrainFilter.id;
const dashboardDefaultValues = getDashboardDefaultValues(
key,
TIME_FILTER_MAP.time_grain_sqla,
);
if (!isEmpty(dashboardDefaultValues)) {
timeGrainFilter.defaultDataMask = {
extraFormData: {
time_grain_sqla: (dashboardDefaultValues ||
time_grain_sqla) as TimeGranularity,
},
filterState: {
value: setValuesInArray(
dashboardDefaultValues,
time_grain_sqla,
),
},
};
}
filterConfig.push(timeGrainFilter);
}
if (show_sqla_time_column) {
const { scope, immune }: FilterScopeType =
scopesByChartId[TIME_FILTER_MAP.granularity_sqla] ||
DASHBOARD_FILTER_SCOPE_GLOBAL;
const timeColumnFilter: Filter = {
id: `NATIVE_FILTER-${shortid.generate()}`,
description: 'time column filter',
controlValues: {},
name: TIME_FILTER_LABELS.granularity_sqla,
filterType: FILTER_COMPONENT_FILTER_TYPES.FILTER_TIMECOLUMN,
targets: [
{
datasetId: parseInt(datasource.split('__')[0], 10),
},
],
cascadeParentIds: [],
defaultDataMask: {},
type: NativeFilterType.NATIVE_FILTER,
scope: {
rootPath: scope,
excluded: immune,
},
};
filterBoxToFilterComponentMap[key][TIME_FILTER_MAP.granularity_sqla] =
timeColumnFilter.id;
const dashboardDefaultValues = getDashboardDefaultValues(
key,
TIME_FILTER_MAP.granularity_sqla,
);
if (!isEmpty(dashboardDefaultValues)) {
timeColumnFilter.defaultDataMask = {
extraFormData: {
granularity_sqla: (dashboardDefaultValues ||
granularity_sqla) as string,
},
filterState: {
value: setValuesInArray(
dashboardDefaultValues,
granularity_sqla,
),
},
};
}
filterConfig.push(timeColumnFilter);
}
}
filter_configs.forEach(config => {
const { scope, immune }: FilterScopeType =
scopesByChartId[config.column] || DASHBOARD_FILTER_SCOPE_GLOBAL;
const entry: Filter = {
id: `NATIVE_FILTER-${shortid.generate()}`,
description: '',
controlValues: {
enableEmptyFilter: !config[FILTER_CONFIG_ATTRIBUTES.CLEARABLE],
defaultToFirstItem: false,
inverseSelection: false,
multiSelect: config[FILTER_CONFIG_ATTRIBUTES.MULTIPLE],
sortAscending: config[FILTER_CONFIG_ATTRIBUTES.SORT_ASCENDING],
},
name: config.label || config.column,
filterType: FILTER_COMPONENT_FILTER_TYPES.FILTER_SELECT,
targets: [
{
datasetId: parseInt(datasource.split('__')[0], 10),
column: {
name: config.column,
},
},
],
cascadeParentIds: [],
defaultDataMask: {},
type: NativeFilterType.NATIVE_FILTER,
scope: {
rootPath: scope,
excluded: immune,
},
adhoc_filters,
sortMetric: config[FILTER_CONFIG_ATTRIBUTES.SORT_METRIC],
time_range,
};
filterBoxToFilterComponentMap[key][config.column] = entry.id;
const defaultValues =
getDashboardDefaultValues(key, config.column) ||
getFilterBoxDefaultValues(config);
if (!isEmpty(defaultValues)) {
entry.defaultDataMask = {
extraFormData: {
filters: [{ col: config.column, op: 'IN', val: defaultValues }],
},
filterState: { value: defaultValues },
};
}
filterConfig.push(entry);
});
}
});
const dependencies: FilterBoxDependencyMap =
getFilterboxDependencies(filterScopes);
Object.entries(dependencies).forEach(([key, filterFields]) => {
Object.entries(filterFields).forEach(([field, childrenChartIds]) => {
const parentComponentId = filterBoxToFilterComponentMap[key][field];
childrenChartIds.forEach(childrenChartId => {
const childComponentIds = Object.values(
filterBoxToFilterComponentMap[childrenChartId],
);
childComponentIds.forEach(childComponentId => {
const childComponent = find(
filterConfig,
({ id }) => id === childComponentId,
);
if (
childComponent &&
// time related filter components don't have parent
[
FILTER_COMPONENT_FILTER_TYPES.FILTER_SELECT,
FILTER_COMPONENT_FILTER_TYPES.FILTER_RANGE,
].includes(
childComponent.filterType as FILTER_COMPONENT_FILTER_TYPES,
)
) {
childComponent.cascadeParentIds =
childComponent.cascadeParentIds || [];
childComponent.cascadeParentIds.push(parentComponentId);
}
});
});
});
});
return filterConfig;
}

View File

@ -156,16 +156,6 @@ export const TIME_FILTER_MAP = {
granularity: '__granularity',
};
export enum FILTER_BOX_MIGRATION_STATES {
CONVERTED = 'CONVERTED',
NOOP = 'NOOP',
REVIEWING = 'REVIEWING',
SNOOZED = 'SNOOZED',
UNDECIDED = 'UNDECIDED',
}
export const FILTER_BOX_TRANSITION_SNOOZE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
export const POPOVER_INITIAL_HEIGHT = 240;
export const POPOVER_INITIAL_WIDTH = 320;
export const UNRESIZABLE_POPOVER_WIDTH = 296;

View File

@ -444,7 +444,6 @@ DEFAULT_FEATURE_FLAGS: Dict[str, bool] = {
"ALERT_REPORTS": False,
"DASHBOARD_RBAC": False,
"ENABLE_EXPLORE_DRAG_AND_DROP": True,
"ENABLE_FILTER_BOX_MIGRATION": False,
"ENABLE_ADVANCED_DATA_TYPES": False,
"ENABLE_DND_WITH_CLICK_UX": True,
# Enabling ALERTS_ATTACH_REPORTS, the system sends email and slack message

View File

@ -114,17 +114,9 @@ class Dashboard(BaseSupersetView):
@expose("/new/")
def new(self) -> FlaskResponse: # pylint: disable=no-self-use
"""Creates a new, blank dashboard and redirects to it in edit mode"""
metadata = {}
if is_feature_enabled("ENABLE_FILTER_BOX_MIGRATION"):
metadata = {
"native_filter_configuration": [],
"show_native_filters": True,
}
new_dashboard = DashboardModel(
dashboard_title="[ untitled dashboard ]",
owners=[g.user],
json_metadata=json.dumps(metadata, sort_keys=True),
)
db.session.add(new_dashboard)
db.session.commit()