This is an automated email from the ASF dual-hosted git repository.

graceguo pushed a commit to branch gg-ShowFilterBoxAsFilterComponent
in repository https://gitbox.apache.org/repos/asf/superset.git

commit 71d0d7f48ccadabdb369bf3406c8326078dc7abc
Author: Grace <[email protected]>
AuthorDate: Sun Apr 25 22:28:29 2021 -0700

    dispaly filter_box as native filters
---
 superset-frontend/src/dashboard/actions/hydrate.js | 306 +++++++++++++++++----
 .../dashboard/components/gridComponents/Chart.jsx  |   5 +-
 .../components/nativeFilters/FilterBar/index.tsx   |   1 +
 superset-frontend/src/explore/constants.ts         |   1 +
 superset-frontend/src/filters/utils.ts             |  10 +-
 5 files changed, 266 insertions(+), 57 deletions(-)

diff --git a/superset-frontend/src/dashboard/actions/hydrate.js 
b/superset-frontend/src/dashboard/actions/hydrate.js
index 0206d91..d4b9872 100644
--- a/superset-frontend/src/dashboard/actions/hydrate.js
+++ b/superset-frontend/src/dashboard/actions/hydrate.js
@@ -17,44 +17,34 @@
  * under the License.
  */
 /* eslint-disable camelcase */
-import { isString, keyBy } from 'lodash';
+import {isString, keyBy, isEmpty} from 'lodash';
 import shortid from 'shortid';
-import {
-  Behavior,
-  CategoricalColorNamespace,
-  getChartMetadataRegistry,
-} from '@superset-ui/core';
+import {Behavior, CategoricalColorNamespace, getChartMetadataRegistry,} from 
'@superset-ui/core';
 import querystring from 'query-string';
 
-import { chart } from 'src/chart/chartReducer';
-import { initSliceEntities } from 'src/dashboard/reducers/sliceEntities';
-import { getInitialState as getInitialNativeFilterState } from 
'src/dashboard/reducers/nativeFilters';
-import { getParam } from 'src/modules/utils';
-import { applyDefaultFormData } from 'src/explore/store';
-import { buildActiveFilters } from 'src/dashboard/util/activeDashboardFilters';
+import {chart} from 'src/chart/chartReducer';
+import {initSliceEntities} from 'src/dashboard/reducers/sliceEntities';
+import {getInitialState as getInitialNativeFilterState} from 
'src/dashboard/reducers/nativeFilters';
+import {getParam} from 'src/modules/utils';
+import {applyDefaultFormData} from 'src/explore/store';
+import {buildActiveFilters} from 'src/dashboard/util/activeDashboardFilters';
 import findPermission from 'src/dashboard/util/findPermission';
-import {
-  DASHBOARD_FILTER_SCOPE_GLOBAL,
-  dashboardFilter,
-} from 'src/dashboard/reducers/dashboardFilters';
+import {DASHBOARD_FILTER_SCOPE_GLOBAL, dashboardFilter,} from 
'src/dashboard/reducers/dashboardFilters';
 import {
   DASHBOARD_HEADER_ID,
-  GRID_DEFAULT_CHART_WIDTH,
-  GRID_COLUMN_COUNT,
   DASHBOARD_ROOT_ID,
+  GRID_COLUMN_COUNT,
+  GRID_DEFAULT_CHART_WIDTH,
 } from 'src/dashboard/util/constants';
-import {
-  DASHBOARD_HEADER_TYPE,
-  CHART_TYPE,
-  ROW_TYPE,
-} from 'src/dashboard/util/componentTypes';
+import {CHART_TYPE, DASHBOARD_HEADER_TYPE, ROW_TYPE,} from 
'src/dashboard/util/componentTypes';
 import findFirstParentContainerId from 
'src/dashboard/util/findFirstParentContainer';
 import getEmptyLayout from 'src/dashboard/util/getEmptyLayout';
 import getFilterConfigsFromFormdata from 
'src/dashboard/util/getFilterConfigsFromFormdata';
 import getLocationHash from 'src/dashboard/util/getLocationHash';
 import newComponentFactory from 'src/dashboard/util/newComponentFactory';
-import { TIME_RANGE } from 'src/visualizations/FilterBox/FilterBox';
-import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
+import {TIME_RANGE} from 'src/visualizations/FilterBox/FilterBox';
+import {FeatureFlag, isFeatureEnabled} from '../../featureFlags';
+import {FILTER_CONFIG_ATTRIBUTES, TIME_FILTER_LABELS, TIME_FILTER_MAP} from 
"../../explore/constants";
 
 const reservedQueryParams = new Set(['standalone', 'edit']);
 
@@ -77,6 +67,51 @@ const extractUrlParams = queryParams =>
     return { ...acc, [key]: value };
   }, {});
 
+const getPreselectedValuesFromDashboard = (
+  preselectedFilters,
+) => {
+  return (filterKey, column) => {
+    if (preselectedFilters[filterKey] &&
+      preselectedFilters[filterKey].hasOwnProperty(column)) {
+      // overwrite default values by dashboard default_filters
+      return preselectedFilters[filterKey][column];
+    }
+  }
+}
+
+const getFilterBoxDefaultValues = (config) => {
+  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 buildValuesInArray = (value1, value2) => {
+  if (!isEmpty(value1)) {
+    return [value1];
+  } else if (isEmpty(value2)) {
+    return [value2];
+  } else{
+    return [];
+  }
+}
+
+const FILTER_BOX_TRANSITION_MODE = {
+  test: 'TEST',
+  snooze: 'SNOOZE',
+  confirmed: 'CONFIRMED',
+}
+
 export const HYDRATE_DASHBOARD = 'HYDRATE_DASHBOARD';
 
 export const hydrateDashboard = (dashboardData, chartData, datasourcesData) => 
(
@@ -143,6 +178,8 @@ export const hydrateDashboard = (dashboardData, chartData, 
datasourcesData) => (
   let newSlicesContainerWidth = 0;
 
   const filterScopes = metadata?.filter_scopes || {};
+  const filterConfig = metadata?.native_filter_configuration || [];
+  const isFilterBoxConverted = !!(metadata?.native_filter_configuration);
 
   const chartQueries = {};
   const dashboardFilters = {};
@@ -210,7 +247,9 @@ export const hydrateDashboard = (dashboardData, chartData, 
datasourcesData) => (
     }
 
     // build DashboardFilters for interactive filter features
-    if (slice.form_data.viz_type === 'filter_box') {
+    if (!isFilterBoxConverted &&
+      slice.form_data.viz_type === 'filter_box'
+    ) {
       const configs = getFilterConfigsFromFormdata(slice.form_data);
       let { columns } = configs;
       const { labels } = configs;
@@ -243,22 +282,195 @@ export const hydrateDashboard = (dashboardData, 
chartData, datasourcesData) => (
         };
       }, {});
 
-      const componentId = chartIdToLayoutId[key];
-      const directPathToFilter = (layout[componentId].parents || []).slice();
-      directPathToFilter.push(componentId);
-      dashboardFilters[key] = {
-        ...dashboardFilter,
-        chartId: key,
-        componentId,
-        datasourceId: slice.form_data.datasource,
-        filterName: slice.slice_name,
-        directPathToFilter,
-        columns,
-        labels,
-        scopes: scopesByChartId,
-        isInstantFilter: !!slice.form_data.instant_filtering,
-        isDateFilter: Object.keys(columns).includes(TIME_RANGE),
-      };
+      const {
+        date_filter = false,
+        datasource = '',
+        druid_time_origin,
+        filter_configs = [],
+        instant_filtering = false,
+        granularity,
+        granularity_sqla,
+        show_druid_time_granularity = false,
+        show_druid_time_origin = false,
+        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} = scopesByChartId[TIME_FILTER_MAP['time_range']] 
|| DASHBOARD_FILTER_SCOPE_GLOBAL;
+        const dashboardDefaultValues = getDashboardDefaultValues(key, 
TIME_FILTER_MAP['time_range']);
+        const timeRangeFilter = {
+          id: `NATIVE_FILTER-${shortid.generate()}`,
+          controlValues: {},
+          name: TIME_FILTER_LABELS['time_range'],
+          filterType: "filter_time",
+          targets: [{}],
+          defaultValue: dashboardDefaultValues || time_range,
+          cascadeParentIds: [],
+          scope: {
+            "rootPath": scope,
+            "excluded": immune,
+          },
+          isInstant: instant_filtering,
+        }
+        filterConfig.push(timeRangeFilter);
+
+        if (show_sqla_time_granularity) {
+          const {scope, immune} = scopesByChartId['time_grain_sqla'] || 
DASHBOARD_FILTER_SCOPE_GLOBAL;
+          const dashboardDefaultValues = getDashboardDefaultValues(key, 
TIME_FILTER_MAP['time_grain_sqla']);
+          const timeGrainFilter = {
+            id: `NATIVE_FILTER-${shortid.generate()}`,
+            controlValues: {},
+            name: TIME_FILTER_LABELS['time_grain_sqla'],
+            filterType: 'filter_timegrain',
+            targets: [
+              {
+                datasetId: parseInt(datasource.split('__')[0], 10),
+              }
+            ],
+            defaultValue: buildValuesInArray(dashboardDefaultValues, 
time_grain_sqla),
+            cascadeParentIds: [],
+            scope: {
+              "rootPath": scope,
+              "excluded": immune,
+            },
+            isInstant: instant_filtering,
+          }
+          filterConfig.push(timeGrainFilter);
+        }
+
+        if (show_sqla_time_column) {
+          const {scope, immune} = scopesByChartId['granularity_sqla'] || 
DASHBOARD_FILTER_SCOPE_GLOBAL;
+          const dashboardDefaultValues = getDashboardDefaultValues(key, 
TIME_FILTER_MAP['granularity_sqla']);
+          const timeColumnFilter = {
+            id: `NATIVE_FILTER-${shortid.generate()}`,
+            controlValues: {},
+            name: TIME_FILTER_LABELS['granularity_sqla'],
+            filterType: 'filter_timecolumn',
+            targets: [
+              {
+                datasetId: parseInt(datasource.split('__')[0], 10),
+              }
+            ],
+            defaultValue: buildValuesInArray(dashboardDefaultValues, 
granularity_sqla),
+            cascadeParentIds: [],
+            scope: {
+              "rootPath": scope,
+              "excluded": immune,
+            },
+            isInstant: instant_filtering,
+          }
+          filterConfig.push(timeColumnFilter);
+        }
+
+        if (show_druid_time_granularity) {
+          const {scope, immune} = scopesByChartId['granularity'] || 
DASHBOARD_FILTER_SCOPE_GLOBAL;
+          const dashboardDefaultValues = getDashboardDefaultValues(key, 
TIME_FILTER_MAP['granularity']);
+          const druidGranularityFilter = {
+            id: `NATIVE_FILTER-${shortid.generate()}`,
+            controlValues: {},
+            name: TIME_FILTER_LABELS['granularity'],
+            filterType: 'filter_timegrain',
+            targets: [
+              {
+                datasetId: parseInt(datasource.split('__')[0], 10),
+              }
+            ],
+            defaultValue: buildValuesInArray(dashboardDefaultValues, 
granularity),
+            cascadeParentIds: [],
+            scope: {
+              "rootPath": scope,
+              "excluded": immune,
+            },
+            isInstant: instant_filtering,
+          }
+          filterConfig.push(druidGranularityFilter);
+        }
+
+        // TODO: test
+        if (show_druid_time_origin) {
+          const {scope, immune} = scopesByChartId['druid_time_origin'] || 
DASHBOARD_FILTER_SCOPE_GLOBAL;
+          const dashboardDefaultValues = getDashboardDefaultValues(key, 
TIME_FILTER_MAP['druid_time_origin']);
+          const druidOriginFilter = {
+            id: `NATIVE_FILTER-${shortid.generate()}`,
+            controlValues: {},
+            name: TIME_FILTER_LABELS['druid_time_origin'],
+            filterType: 'filter_timegrain',
+            targets: [
+              {
+                datasetId: parseInt(datasource.split('__')[0], 10),
+              }
+            ],
+            defaultValue: buildValuesInArray(dashboardDefaultValues, 
druid_time_origin),
+            cascadeParentIds: [],
+            scope: {
+              "rootPath": scope,
+              "excluded": immune,
+            },
+            isInstant: instant_filtering,
+          }
+          filterConfig.push(druidOriginFilter);
+        }
+      }
+      filter_configs.forEach(config => {
+        const {scope, immune} = scopesByChartId[config.column] || 
DASHBOARD_FILTER_SCOPE_GLOBAL;
+        const defaultValues = getDashboardDefaultValues(key, config.column);
+        const entry = {
+          id: `NATIVE_FILTER-${config.key}`,
+          controlValues: {
+            // TODO: test
+            enableEmptyFilter: !config[FILTER_CONFIG_ATTRIBUTES.CLEARABLE],
+            defaultToFirstItem: false, // n/a
+            inverseSelection: false, // n/a
+            multiSelect: config[FILTER_CONFIG_ATTRIBUTES.MULTIPLE],
+            sortAscending: config[FILTER_CONFIG_ATTRIBUTES.SORT_ASCENDING],
+          },
+          name: config.label || config.column,
+          filterType: "filter_select",
+          targets: [
+            {
+              datasetId: parseInt(datasource.split('__')[0], 10),
+              column: {
+                name: config.column,
+              },
+            }
+          ],
+          // TODO: test
+          defaultValue: defaultValues || getFilterBoxDefaultValues(config),
+          cascadeParentIds:[],
+          scope:{
+            "rootPath": scope,
+            "excluded": immune,
+          },
+          isInstant: instant_filtering,
+        }
+        filterConfig.push(entry);
+      });
+
+      metadata = metadata || {};
+      metadata.native_filter_configuration = filterConfig;
+      // done
+
+      // const componentId = chartIdToLayoutId[key];
+      // const directPathToFilter = (layout[componentId].parents || 
[]).slice();
+      // directPathToFilter.push(componentId);
+      // dashboardFilters[key] = {
+      //   ...dashboardFilter,
+      //   chartId: key,
+      //   componentId,
+      //   datasourceId: slice.form_data.datasource,
+      //   filterName: slice.slice_name,
+      //   directPathToFilter,
+      //   columns,
+      //   labels,
+      //   scopes: scopesByChartId,
+      //   isInstantFilter: !!slice.form_data.instant_filtering,
+      //   isDateFilter: Object.keys(columns).includes(TIME_RANGE),
+      // };
     }
 
     // sync layout names with current slice names in case a slice was edited
@@ -269,10 +481,10 @@ export const hydrateDashboard = (dashboardData, 
chartData, datasourcesData) => (
       layout[layoutId].meta.sliceName = slice.slice_name;
     }
   });
-  buildActiveFilters({
-    dashboardFilters,
-    components: layout,
-  });
+  // buildActiveFilters({
+  //   dashboardFilters,
+  //   components: layout,
+  // });
 
   // store the header as a layout component so we can undo/redo changes
   layout[DASHBOARD_HEADER_ID] = {
@@ -298,7 +510,7 @@ export const hydrateDashboard = (dashboardData, chartData, 
datasourcesData) => (
   }
 
   const nativeFilters = getInitialNativeFilterState({
-    filterConfig: metadata?.native_filter_configuration || [],
+    filterConfig,
     filterSetsConfig: metadata?.filter_sets_configuration || [],
   });
 
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
index bc1f78f..831dfd3 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
@@ -93,6 +93,8 @@ const ChartOverlay = styled.div`
   top: 0;
   left: 0;
   z-index: 5;
+  opacity: 0.25;
+  background-color: #000;
 `;
 
 export default class Chart extends React.Component {
@@ -279,6 +281,7 @@ export default class Chart extends React.Component {
 
     const { queriesResponse, chartUpdateEndTime, chartStatus } = chart;
     const isLoading = chartStatus === 'loading';
+    const isDeactivated = slice.viz_type === 'filter_box'
     // eslint-disable-next-line camelcase
     const isCached = queriesResponse?.map(({ is_cached }) => is_cached) || [];
     const cachedDttm =
@@ -350,7 +353,7 @@ export default class Chart extends React.Component {
             isOverflowable && 'dashboard-chart--overflowable',
           )}
         >
-          {isLoading && (
+          {(isLoading || isDeactivated) && (
             <ChartOverlay
               style={{
                 width,
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
index 4771f03..d5557d5 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx
@@ -212,6 +212,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
   const isApplyDisabled =
     !isInitialized || areObjectsEqual(dataMaskSelected, lastAppliedFilterData);
 
+  console.log('is filter change:', isFilterSetChanged)
   return (
     <BarWrapper {...getFilterBarTestId()} className={cx({ open: filtersOpen 
})}>
       <CollapsedBar
diff --git a/superset-frontend/src/explore/constants.ts 
b/superset-frontend/src/explore/constants.ts
index 06fa62c..bd0de27 100644
--- a/superset-frontend/src/explore/constants.ts
+++ b/superset-frontend/src/explore/constants.ts
@@ -80,6 +80,7 @@ export const TIME_FILTER_LABELS = {
 };
 
 export const FILTER_CONFIG_ATTRIBUTES = {
+  SORT_ASCENDING: 'asc',
   DEFAULT_VALUE: 'defaultValue',
   MULTIPLE: 'multiple',
   SEARCH_ALL_OPTIONS: 'searchAllOptions',
diff --git a/superset-frontend/src/filters/utils.ts 
b/superset-frontend/src/filters/utils.ts
index 6ca88dd..1df31db 100644
--- a/superset-frontend/src/filters/utils.ts
+++ b/superset-frontend/src/filters/utils.ts
@@ -33,15 +33,7 @@ export const getSelectExtraFormData = (
   inverseSelection = false,
 ): ExtraFormData => {
   const extra: ExtraFormData = {};
-  if (emptyFilter) {
-    extra.adhoc_filters = [
-      {
-        expressionType: 'SQL',
-        clause: 'WHERE',
-        sqlExpression: '1 = 0',
-      },
-    ];
-  } else {
+  if (!emptyFilter) {
     extra.filters =
       value === undefined || value === null || value.length === 0
         ? []

Reply via email to