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

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

commit b1140a75e73e82c4d596fb8219fdec5478f5688e
Author: Grace <[email protected]>
AuthorDate: Mon Oct 21 17:48:36 2019 -0700

    convert ids to scope object
---
 .../util/getFilterScopeFromNodesTree_spec.js       | 216 +++++++++++++++++++++
 .../dashboard/util/getFilterScopeFromNodesTree.js  | 146 ++++++++++++++
 2 files changed, 362 insertions(+)

diff --git 
a/superset/assets/spec/javascripts/dashboard/util/getFilterScopeFromNodesTree_spec.js
 
b/superset/assets/spec/javascripts/dashboard/util/getFilterScopeFromNodesTree_spec.js
new file mode 100644
index 0000000..067e193
--- /dev/null
+++ 
b/superset/assets/spec/javascripts/dashboard/util/getFilterScopeFromNodesTree_spec.js
@@ -0,0 +1,216 @@
+/**
+ * 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 getFilterScopeFromNodesTree from 
'../../../../src/dashboard/util/getFilterScopeFromNodesTree';
+
+describe('getFilterScopeFromNodesTree', () => {
+  it('should return empty scope', () => {
+    const nodes = [];
+    expect(
+      getFilterScopeFromNodesTree({
+        filterKey: '107_region',
+        nodes,
+        checkedChartIds: [],
+      }),
+    ).toEqual({});
+  });
+
+  it('should return scope for simple grid', () => {
+    const nodes = [
+      {
+        label: 'All dashboard',
+        type: 'ROOT',
+        value: 'ROOT_ID',
+        children: [
+          {
+            value: 104,
+            label: 'Life Expectancy VS Rural %',
+            type: 'CHART',
+          },
+          { value: 105, label: 'Rural Breakdown', type: 'CHART' },
+          {
+            value: 106,
+            label: "World's Pop Growth",
+            type: 'CHART',
+          },
+          {
+            label: 'Time Filter',
+            showCheckbox: false,
+            type: 'CHART',
+            value: 108,
+          },
+        ],
+      },
+    ];
+    const checkedChartIds = [104, 106];
+    expect(
+      getFilterScopeFromNodesTree({
+        filterKey: '108___time_range',
+        nodes,
+        checkedChartIds,
+      }),
+    ).toEqual({
+      scope: ['ROOT_ID'],
+      immune: [105],
+    });
+  });
+
+  describe('should return scope for tabbed dashboard', () => {
+    const nodes = [
+      {
+        label: 'All dashboard',
+        type: 'ROOT',
+        value: 'ROOT_ID',
+        children: [
+          {
+            label: 'Tab 1',
+            type: 'TAB',
+            value: 'TAB-Rb5aaqKWgG',
+            children: [
+              {
+                label: 'Geo Filters',
+                showCheckbox: false,
+                type: 'CHART',
+                value: 107,
+              },
+              {
+                label: "World's Pop Growth",
+                showCheckbox: true,
+                type: 'CHART',
+                value: 106,
+              },
+            ],
+          },
+          {
+            label: 'Tab 2',
+            type: 'TAB',
+            value: 'TAB-w5Fp904Rs',
+            children: [
+              {
+                label: 'Time Filter',
+                showCheckbox: true,
+                type: 'CHART',
+                value: 108,
+              },
+              {
+                label: 'Life Expectancy VS Rural %',
+                showCheckbox: true,
+                type: 'CHART',
+                value: 104,
+              },
+              {
+                label: 'Row Tab 1',
+                type: 'TAB',
+                value: 'TAB-E4mJaZ-uQM',
+                children: [
+                  {
+                    value: 105,
+                    label: 'Rural Breakdown',
+                    type: 'CHART',
+                    showCheckbox: true,
+                  },
+                  {
+                    value: 103,
+                    label: '% Rural',
+                    type: 'CHART',
+                    showCheckbox: true,
+                  },
+                ],
+              },
+              {
+                value: 'TAB-rLYu-Cryu',
+                label: 'New Tab',
+                type: 'TAB',
+                children: [
+                  {
+                    value: 102,
+                    label: 'Most Populated Countries',
+                    type: 'CHART',
+                    showCheckbox: true,
+                  },
+                  {
+                    value: 101,
+                    label: "World's Population",
+                    type: 'CHART',
+                    showCheckbox: true,
+                  },
+                ],
+              },
+            ],
+          },
+        ],
+      },
+    ];
+
+    it('root level tab scope', () => {
+      const checkedChartIds = [106];
+      expect(
+        getFilterScopeFromNodesTree({
+          filterKey: '107_region',
+          nodes,
+          checkedChartIds,
+        }),
+      ).toEqual({
+        scope: ['TAB-Rb5aaqKWgG'],
+        immune: [],
+      });
+    });
+
+    it('global scope', () => {
+      const checkedChartIds = [106, 104, 101, 102, 103, 105];
+      expect(
+        getFilterScopeFromNodesTree({
+          filterKey: '107_country_name',
+          nodes,
+          checkedChartIds,
+        }),
+      ).toEqual({
+        scope: ['ROOT_ID'],
+        immune: [108],
+      });
+    });
+
+    it('row level tab scope', () => {
+      const checkedChartIds = [103, 105];
+      expect(
+        getFilterScopeFromNodesTree({
+          filterKey: '108___time_range',
+          nodes,
+          checkedChartIds,
+        }),
+      ).toEqual({
+        scope: ['TAB-E4mJaZ-uQM'],
+        immune: [],
+      });
+    });
+
+    it('mixed row level and root level scope', () => {
+      const checkedChartIds = [103, 105, 106];
+      expect(
+        getFilterScopeFromNodesTree({
+          filterKey: '107_region',
+          nodes,
+          checkedChartIds,
+        }),
+      ).toEqual({
+        scope: ['TAB-Rb5aaqKWgG', 'TAB-E4mJaZ-uQM'],
+        immune: [],
+      });
+    });
+  });
+});
diff --git a/superset/assets/src/dashboard/util/getFilterScopeFromNodesTree.js 
b/superset/assets/src/dashboard/util/getFilterScopeFromNodesTree.js
new file mode 100644
index 0000000..d45e22b
--- /dev/null
+++ b/superset/assets/src/dashboard/util/getFilterScopeFromNodesTree.js
@@ -0,0 +1,146 @@
+/**
+ * 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 { isEmpty } from 'lodash';
+import { CHART_TYPE, TAB_TYPE } from './componentTypes';
+import { getDashboardFilterByKey } from './getDashboardFilterKey';
+
+// filterKey: '107_region',
+// nodes: [
+//  { value, label, children:
+//    [
+//      { value1, label1, children1 },
+//      { value2, label2, children2 },
+//    ]
+//  },
+// ],
+// checkedIds: [101]
+//
+// output {
+//   { scope: [tab1, tab2], immune: [chart1, chart2] }
+// }
+export default function getFilterScopeFromNodesTree({
+  filterKey,
+  nodes = [],
+  checkedChartIds = [],
+}) {
+  function getTabChildrenScope({
+    tabScopes,
+    parentNodeValue,
+    forceAggregate = false,
+  }) {
+    // if all sub-tabs are in scope, or forceAggregate =  true
+    // aggregate scope to parentNodeValue
+    if (
+      forceAggregate ||
+      Object.entries(tabScopes).every(
+        ([key, { scope }]) => scope.length && key === scope[0],
+      )
+    ) {
+      return {
+        scope: [parentNodeValue],
+        immune: [].concat(
+          ...Object.values(tabScopes).map(({ immune }) => immune),
+        ),
+      };
+    }
+
+    const componentsInScope = Object.values(tabScopes).filter(
+      ({ scope }) => scope && scope.length,
+    );
+    const scopeValue = [].concat(
+      ...componentsInScope.map(({ scope }) => scope),
+    );
+    const immuneValue = [].concat(
+      ...componentsInScope.map(({ immune }) => immune),
+    );
+
+    return {
+      scope: scopeValue,
+      immune: immuneValue,
+    };
+  }
+
+  function traverse({ currentNode, filterId }) {
+    if (!currentNode) {
+      return {};
+    }
+
+    const { value: currentValue, children } = currentNode;
+    const chartChildren = children.filter(({ type }) => type === CHART_TYPE);
+    const tabChildren = children.filter(({ type }) => type === TAB_TYPE);
+
+    const chartsImmune = chartChildren
+      .filter(
+        ({ value }) => filterId !== value && !checkedChartIds.includes(value),
+      )
+      .map(({ value }) => value);
+    const tabScopes = tabChildren.reduce((map, child) => {
+      const { value: tabValue } = child;
+      return {
+        ...map,
+        [tabValue]: traverse({
+          currentNode: child,
+          filterId,
+        }),
+      };
+    }, {});
+
+    // if any chart type child is in scope,
+    // no matter has tab children or not, current node should be scope
+    if (
+      !isEmpty(chartChildren) &&
+      chartChildren.some(({ value }) => checkedChartIds.includes(value))
+    ) {
+      if (isEmpty(tabChildren)) {
+        return { scope: [currentValue], immune: chartsImmune };
+      }
+
+      const { scope, immune } = getTabChildrenScope({
+        tabScopes,
+        parentNodeValue: currentValue,
+        forceAggregate: true,
+      });
+      return {
+        scope,
+        immune: chartsImmune.concat(immune),
+      };
+    }
+
+    // has tab children but only some sub-tab in scope
+    if (!isEmpty(tabChildren)) {
+      return getTabChildrenScope({ tabScopes, parentNodeValue: currentValue });
+    }
+
+    // no tab children and no chart children in scope
+    return {
+      scope: [],
+      immune: chartsImmune,
+    };
+  }
+
+  const [chartId] = getDashboardFilterByKey(filterKey);
+  if (nodes && nodes.length) {
+    return traverse({
+      currentNode: nodes[0],
+      filterId: chartId,
+    });
+  }
+
+  return {};
+}

Reply via email to