Incubator-superset: State is undefined in dashboardFilters.js

Created on 30 Sep 2020  路  9Comments  路  Source: apache/incubator-superset

Emitting filter from a plugin leads to a TypeError: Cannot read property 'columns' of undefined exception incubator-superset/superset-frontend/src/dashboard/reducers/dashboardFilters.js line 110.

The same issue seems to happen with incubator-superset 0.37.2 table plugin when you enable the emit filter functionality.

Expected results

No exception and filter being emitted.

Actual results

Exception: TypeError: Cannot read property 'columns' of undefined exception incubator-superset/superset-frontend/src/dashboard/reducers/dashboardFilters.js line 110

Screenshots

image

image

How to reproduce the bug

  1. Pull incubator-superset 39cce712afdc00af73daa2243cbae08a14b7dc0e
  2. Go to data > datasets
  3. Select birth_names
  4. Select customize > emit filter evens
  5. Select Data > GROUP BY and add fields gender and name
  6. Run query
  7. Save as tblNames and add to new dashboard dbNames
  8. Go to dashboard
  9. Open browser console
  10. Click any of the cells in the table
  11. Observe the following error
Uncaught TypeError: Cannot read property 'columns' of undefined
    at Object.CHANGE_FILTER (dashboardFilters.js?12c8:105)
    at dashboardFiltersReducer (dashboardFilters.js?12c8:169)
    at combination (combineReducers.js?89a8:120)
    at dispatch (createStore.js?61ed:165)
    at eval (loggerMiddleware.js?7f0c:68)
    at eval (index.js?b083:11)
    at dispatch (applyMiddleware.js?05f2:35)
    at eval (dashboardFilters.js?1bd4:50)
    at eval (index.js?b083:8)
    at Object.eval [as changeFilter] (bindActionCreators.js?f901:3)
CHANGE_FILTER @ dashboardFilters.js?12c8:105
dashboardFiltersReducer @ dashboardFilters.js?12c8:169
combination @ combineReducers.js?89a8:120
dispatch @ createStore.js?61ed:165
eval @ loggerMiddleware.js?7f0c:68
eval @ index.js?b083:11
dispatch @ applyMiddleware.js?05f2:35
eval @ dashboardFilters.js?1bd4:50
eval @ index.js?b083:8
eval @ bindActionCreators.js?f901:3
changeFilter @ Chart.jsx?484f:192
changeFilter @ react-hot-loader.development.js?c2cb:714
handleAddFilter @ ChartRenderer.jsx?b661:103
handleAddFilter @ react-hot-loader.development.js?c2cb:714
toggleFilter @ TableChart.js?8bc0:178
Cell.cellProps.onClick @ TableChart.js?8bc0:222
callCallback @ react-dom.development.js?7f13:189
invokeGuardedCallbackDev @ react-dom.development.js?7f13:238
invokeGuardedCallback @ react-dom.development.js?7f13:293
invokeGuardedCallbackAndCatchFirstError @ react-dom.development.js?7f13:307
executeDispatch @ react-dom.development.js?7f13:390
executeDispatchesInOrder @ react-dom.development.js?7f13:412
executeDispatchesAndRelease @ react-dom.development.js?7f13:3279
executeDispatchesAndReleaseTopLevel @ react-dom.development.js?7f13:3288
forEachAccumulated @ react-dom.development.js?7f13:3260
runEventsInBatch @ react-dom.development.js?7f13:3305
runExtractedPluginEventsInBatch @ react-dom.development.js?7f13:3515
handleTopLevel @ react-dom.development.js?7f13:3559
batchedEventUpdates$1 @ react-dom.development.js?7f13:21903
batchedEventUpdates @ react-dom.development.js?7f13:1061
dispatchEventForLegacyPluginEventSystem @ react-dom.development.js?7f13:3569
attemptToDispatchEvent @ react-dom.development.js?7f13:4268
dispatchEvent @ react-dom.development.js?7f13:4190
unstable_runWithPriority @ scheduler.development.js?b589:653
runWithPriority$1 @ react-dom.development.js?7f13:11062
discreteUpdates$1 @ react-dom.development.js?7f13:21919
discreteUpdates @ react-dom.development.js?7f13:1072
dispatchDiscreteEvent @ react-dom.development.js?7f13:4169
Show 8 more frames

Environment

  • superset version: 39cce712afdc00af73daa2243cbae08a14b7dc0e
  • also replicable with superset version: 0.37.2
  • python version: Python 2.7.17
  • node.js version: v12.18.4
  • npm version: 6.14.6

Checklist

Make sure these boxes are checked before submitting your issue - thank you!

  • [ ] I have checked the superset logs for python stacktraces and included it here as text if there are any.
  • [x] I have reproduced the issue with at least the latest released version of superset.
  • [x] I have checked the issue tracker for the same issue and I haven't found one similar.

Additional context

I may be able to fix this myself if you just give me proper context so that I know where to look.

#bug inactive

Most helpful comment

@villebro kiitos ;)

@suddjian Actually, I already found out that the FilterBoxItemControl emits filter_configs array when added to controlPanel so I tried the following:

in incubator-superset/superset-frontend/src/dashboard/reducers/dashboardState.js

index d9ddc59cd..a0c5bb7d6 100644
--- a/superset-frontend/src/dashboard/reducers/getInitialState.js
+++ b/superset-frontend/src/dashboard/reducers/getInitialState.js
@@ -169,7 +169,7 @@ export default function getInitialState(bootstrapData) {
     }

     // build DashboardFilters for interactive filter features
-    if (slice.form_data.viz_type === 'filter_box') {
+    if (slice.form_data.filter_configs && slice.form_data.filter_configs.length > 0) {
       const configs = getFilterConfigsFromFormdata(slice.form_data);
       let { columns } = configs;
       const { labels } = configs;

and in incubator-superset/superset-frontend/src/dashboard/actions/dashboardState.js

index 593ca4d78..da793c938 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -284,7 +284,7 @@ export function addSliceToDashboard(id, component) {
     ]).then(() => {
       dispatch(addSlice(selectedSlice));

-      if (selectedSlice && selectedSlice.viz_type === 'filter_box') {
+      if (selectedSlice && form_data.filter_configs && form_data.filter_configs.length > 0) {
         dispatch(addFilter(id, component, selectedSlice.form_data));
       }
     });
@@ -294,7 +294,7 @@ export function addSliceToDashboard(id, component) {
 export function removeSliceFromDashboard(id) {
   return (dispatch, getState) => {
     const sliceEntity = getState().sliceEntities.slices[id];
-    if (sliceEntity && sliceEntity.viz_type === 'filter_box') {
+    if (sliceEntity && sliceEntity.form_data.filter_configs && sliceEntity.form_data.filter_configs.length > 0) {
       dispatch(removeFilter(id));
     }

And for my surprise, I didn't break the FilterBox plugin and my plugin works :D

In any case @villebro, I will drop by the Superset Slack workspace a little bit later as I have this to prepare for our visualization meeting and see what they think of it.

All 9 comments

Issue-Label Bot is automatically applying the label #bug to this issue, with a confidence of 0.97. Please mark this comment with :thumbsup: or :thumbsdown: to give our bot feedback!

Links: app homepage, dashboard and code for this bot.

Thanks for the ticket @codepic . @ktmud @graceguo-supercat , do I remember correctly that emitting filter events from viz plugins is currently not supported?

My guess is the issue boils down to the chartIds for the visualizations emitting filter events not being present in the dashboardFilters in the state:

image

Any idea how I get it there? Do I need to add the FilterBoxItemControl into the controlPanel.ts in my plugin or what?

@amitNielsen you've worked on similar stuff lately, does this sound familiar?

@villebro I managed to get the filtering working :+1:

image

Below are the relevant changes I needed to do in superset-frontend

image

As it seems, the check form_data.viz_type === 'filter_box' is hard coded so I added my viz_type to the check and my plugin started emitting filter events correctly.

There's a catch. If I add filter_box into the dashboard, the functionality breaks and the table only listens to the filters set in the filter_box plugin.

However this is not an issue as I can still use multiple custom plugins which emit filters and they seem to be working as expected:

image

Since hard coding viz_type check is not optimal, I would suggest we would add a flag to the form_data when ever a plugin developer adds the FilterBoxItemControl into their control panel. Then we can check in dashboardState.js and getInitialState.js that if this flag is set to true.

This way plugin developers could start implementing their own filters just by adding FilterBoxItemControl into their controlPanel.ts and using the hooks provided by superset.

Table plugin would also benefit from this as it currently emits any clicked fields as filters but by adding the FilterBoxItemControl into the control panel, user could gain more granular control over the filtering. E.g. which fields to filter by, can you select multiple, etc.

As it currently stands, having custom filtering functionality is impossible with Superset without modifying superset-frontend yourself and the table plugin's Emit Filter Events -functionality goes to show that.

Erinomaista ty枚t盲! 馃榾 (great to see a fellow Finn here! 馃嚝馃嚠) Ping @suddjian, this is probably very relevant for the native filters work you're doing right now. @codepic please drop by the Superset Slack workspace, would be great to discuss this theme in more detail with you and see how to collaborate on this!

@villebro kiitos ;)

@suddjian Actually, I already found out that the FilterBoxItemControl emits filter_configs array when added to controlPanel so I tried the following:

in incubator-superset/superset-frontend/src/dashboard/reducers/dashboardState.js

index d9ddc59cd..a0c5bb7d6 100644
--- a/superset-frontend/src/dashboard/reducers/getInitialState.js
+++ b/superset-frontend/src/dashboard/reducers/getInitialState.js
@@ -169,7 +169,7 @@ export default function getInitialState(bootstrapData) {
     }

     // build DashboardFilters for interactive filter features
-    if (slice.form_data.viz_type === 'filter_box') {
+    if (slice.form_data.filter_configs && slice.form_data.filter_configs.length > 0) {
       const configs = getFilterConfigsFromFormdata(slice.form_data);
       let { columns } = configs;
       const { labels } = configs;

and in incubator-superset/superset-frontend/src/dashboard/actions/dashboardState.js

index 593ca4d78..da793c938 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -284,7 +284,7 @@ export function addSliceToDashboard(id, component) {
     ]).then(() => {
       dispatch(addSlice(selectedSlice));

-      if (selectedSlice && selectedSlice.viz_type === 'filter_box') {
+      if (selectedSlice && form_data.filter_configs && form_data.filter_configs.length > 0) {
         dispatch(addFilter(id, component, selectedSlice.form_data));
       }
     });
@@ -294,7 +294,7 @@ export function addSliceToDashboard(id, component) {
 export function removeSliceFromDashboard(id) {
   return (dispatch, getState) => {
     const sliceEntity = getState().sliceEntities.slices[id];
-    if (sliceEntity && sliceEntity.viz_type === 'filter_box') {
+    if (sliceEntity && sliceEntity.form_data.filter_configs && sliceEntity.form_data.filter_configs.length > 0) {
       dispatch(removeFilter(id));
     }

And for my surprise, I didn't break the FilterBox plugin and my plugin works :D

In any case @villebro, I will drop by the Superset Slack workspace a little bit later as I have this to prepare for our visualization meeting and see what they think of it.

Related to #9593

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. For admin, please label this issue .pinned to prevent stale bot from closing the issue.

Was this page helpful?
0 / 5 - 0 ratings