You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by kg...@apache.org on 2023/03/10 19:58:33 UTC

[superset] branch master updated: feat(dashboard): Refactor FiltersBadge (#23286)

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

kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new c2b282ac71 feat(dashboard): Refactor FiltersBadge (#23286)
c2b282ac71 is described below

commit c2b282ac71eb84efe82ef550d9559d409153313e
Author: Kamil Gabryjelski <ka...@gmail.com>
AuthorDate: Fri Mar 10 20:58:24 2023 +0100

    feat(dashboard): Refactor FiltersBadge (#23286)
---
 .../integration/dashboard/nativeFilters.test.ts    |   7 +-
 .../DetailsPanel/DetailsPanel.test.tsx             |  80 +-------
 .../components/FiltersBadge/DetailsPanel/index.tsx | 211 +++++----------------
 .../FiltersBadge/FilterIndicator/index.tsx         |  49 ++---
 .../components/FiltersBadge/FiltersBadge.test.tsx  |  63 ------
 .../dashboard/components/FiltersBadge/Styles.tsx   | 106 ++++-------
 .../dashboard/components/FiltersBadge/index.tsx    |  47 +----
 .../src/dashboard/components/SliceHeader/index.tsx |  30 +--
 .../FilterBar/CrossFilters/Vertical.tsx            |  10 +-
 .../FilterBar/CrossFilters/selectors.ts            |  46 +++--
 .../FilterBar/FilterControls/FilterControls.tsx    |  10 +-
 .../nativeFilters/FilterBar/Horizontal.tsx         |   9 +-
 .../components/nativeFilters/selectors.ts          |  93 +++++----
 13 files changed, 237 insertions(+), 524 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
index 7e1d3d5371..1e119ae775 100644
--- a/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
+++ b/superset-frontend/cypress-base/cypress/integration/dashboard/nativeFilters.test.ts
@@ -378,10 +378,13 @@ describe('Horizontal FilterBar', () => {
       { name: 'test_12', column: 'year', datasetId: 2 },
     ]);
     setFilterBarOrientation('horizontal');
+    openMoreFilters();
+    applyNativeFilterValueWithIndex(8, testItems.filterDefaultValue);
+    cy.get(nativeFilters.applyFilter).click({ force: true });
     cy.getBySel('slice-header').within(() => {
-      cy.get('.filter-counts').click();
+      cy.get('.filter-counts').trigger('mouseover');
     });
-    cy.get('.filterStatusPopover').contains('test_8').click();
+    cy.get('.filterStatusPopover').contains('test_9').click();
     cy.getBySel('dropdown-content').should('be.visible');
     cy.get('.ant-select-focused').should('be.visible');
   });
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/DetailsPanel.test.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/DetailsPanel.test.tsx
index 3c3e25ee56..ef4b49d218 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/DetailsPanel.test.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/DetailsPanel.test.tsx
@@ -86,7 +86,7 @@ const createProps = () => ({
   onHighlightFilterSource: jest.fn(),
 });
 
-test('Should render "appliedCrossFilterIndicators"', () => {
+test('Should render "appliedCrossFilterIndicators"', async () => {
   const props = createProps();
   props.appliedIndicators = [];
   props.incompatibleIndicators = [];
@@ -99,8 +99,10 @@ test('Should render "appliedCrossFilterIndicators"', () => {
     { useRedux: true },
   );
 
-  userEvent.click(screen.getByTestId('details-panel-content'));
-  expect(screen.getByText('Applied Cross Filters (1)')).toBeInTheDocument();
+  userEvent.hover(screen.getByTestId('details-panel-content'));
+  expect(
+    await screen.findByText('Applied cross-filters (1)'),
+  ).toBeInTheDocument();
   expect(
     screen.getByRole('button', { name: 'Clinical Stage' }),
   ).toBeInTheDocument();
@@ -118,7 +120,7 @@ test('Should render "appliedCrossFilterIndicators"', () => {
   ]);
 });
 
-test('Should render "appliedIndicators"', () => {
+test('Should render "appliedIndicators"', async () => {
   const props = createProps();
   props.appliedCrossFilterIndicators = [];
   props.incompatibleIndicators = [];
@@ -131,8 +133,8 @@ test('Should render "appliedIndicators"', () => {
     { useRedux: true },
   );
 
-  userEvent.click(screen.getByTestId('details-panel-content'));
-  expect(screen.getByText('Applied Filters (1)')).toBeInTheDocument();
+  userEvent.hover(screen.getByTestId('details-panel-content'));
+  expect(await screen.findByText('Applied filters (1)')).toBeInTheDocument();
   expect(screen.getByRole('button', { name: 'Country' })).toBeInTheDocument();
 
   expect(props.onHighlightFilterSource).toBeCalledTimes(0);
@@ -148,72 +150,6 @@ test('Should render "appliedIndicators"', () => {
   ]);
 });
 
-test('Should render "incompatibleIndicators"', () => {
-  const props = createProps();
-  props.appliedCrossFilterIndicators = [];
-  props.appliedIndicators = [];
-  props.unsetIndicators = [];
-
-  render(
-    <DetailsPanel {...props}>
-      <div data-test="details-panel-content">Content</div>
-    </DetailsPanel>,
-    { useRedux: true },
-  );
-
-  userEvent.click(screen.getByTestId('details-panel-content'));
-  expect(screen.getByText('Incompatible Filters (1)')).toBeInTheDocument();
-  expect(
-    screen.getByRole('button', { name: 'Vaccine Approach Copy' }),
-  ).toBeInTheDocument();
-
-  expect(props.onHighlightFilterSource).toBeCalledTimes(0);
-  userEvent.click(
-    screen.getByRole('button', { name: 'Vaccine Approach Copy' }),
-  );
-  expect(props.onHighlightFilterSource).toBeCalledTimes(1);
-  expect(props.onHighlightFilterSource).toBeCalledWith([
-    'ROOT_ID',
-    'TABS-wUKya7eQ0Zz',
-    'TAB-BCIJF4NvgQq',
-    'ROW-xSeNAspgww',
-    'CHART-eirDduqb1Aa',
-    'LABEL-product_category_copy',
-  ]);
-});
-
-test('Should render "unsetIndicators"', () => {
-  const props = createProps();
-  props.appliedCrossFilterIndicators = [];
-  props.appliedIndicators = [];
-  props.incompatibleIndicators = [];
-
-  render(
-    <DetailsPanel {...props}>
-      <div data-test="details-panel-content">Content</div>
-    </DetailsPanel>,
-    { useRedux: true },
-  );
-
-  userEvent.click(screen.getByTestId('details-panel-content'));
-  expect(screen.getByText('Unset Filters (1)')).toBeInTheDocument();
-  expect(
-    screen.getByRole('button', { name: 'Vaccine Approach' }),
-  ).toBeInTheDocument();
-
-  expect(props.onHighlightFilterSource).toBeCalledTimes(0);
-  userEvent.click(screen.getByRole('button', { name: 'Vaccine Approach' }));
-  expect(props.onHighlightFilterSource).toBeCalledTimes(1);
-  expect(props.onHighlightFilterSource).toBeCalledWith([
-    'ROOT_ID',
-    'TABS-wUKya7eQ0Z',
-    'TAB-BCIJF4NvgQ',
-    'ROW-xSeNAspgw',
-    'CHART-eirDduqb1A',
-    'LABEL-product_category',
-  ]);
-});
-
 test('Should render empty', () => {
   const props = createProps();
   props.appliedCrossFilterIndicators = [];
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/index.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/index.tsx
index 022dab7968..53d20cd1f8 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/index.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/DetailsPanel/index.tsx
@@ -19,31 +19,21 @@
 import React, { useEffect, useState } from 'react';
 import { useSelector } from 'react-redux';
 import { Global, css } from '@emotion/react';
-import { t, useTheme } from '@superset-ui/core';
+import { t } from '@superset-ui/core';
 import Popover from 'src/components/Popover';
-import Collapse from 'src/components/Collapse';
-import Icons from 'src/components/Icons';
 import {
-  Indent,
-  Panel,
-  Reset,
-  Title,
+  FiltersContainer,
+  FiltersDetailsContainer,
+  Separator,
+  SectionName,
 } from 'src/dashboard/components/FiltersBadge/Styles';
 import { Indicator } from 'src/dashboard/components/nativeFilters/selectors';
 import FilterIndicator from 'src/dashboard/components/FiltersBadge/FilterIndicator';
 import { RootState } from 'src/dashboard/types';
 
-const iconReset = css`
-  span {
-    line-height: 0;
-  }
-`;
-
 export interface DetailsPanelProps {
   appliedCrossFilterIndicators: Indicator[];
   appliedIndicators: Indicator[];
-  incompatibleIndicators: Indicator[];
-  unsetIndicators: Indicator[];
   onHighlightFilterSource: (path: string[]) => void;
   children: JSX.Element;
 }
@@ -51,13 +41,10 @@ export interface DetailsPanelProps {
 const DetailsPanelPopover = ({
   appliedCrossFilterIndicators = [],
   appliedIndicators = [],
-  incompatibleIndicators = [],
-  unsetIndicators = [],
   onHighlightFilterSource,
   children,
 }: DetailsPanelProps) => {
   const [visible, setVisible] = useState(false);
-  const theme = useTheme();
   const activeTabs = useSelector<RootState>(
     state => state.dashboardState?.activeTabs,
   );
@@ -76,57 +63,22 @@ const DetailsPanelPopover = ({
     setVisible(false);
   }, [activeTabs]);
 
-  const getDefaultActivePanel = () => {
-    const result = [];
-    if (appliedCrossFilterIndicators.length) {
-      result.push('appliedCrossFilters');
-    }
-    if (appliedIndicators.length) {
-      result.push('applied');
-    }
-    if (incompatibleIndicators.length) {
-      result.push('incompatible');
-    }
-    if (result.length) {
-      return result;
-    }
-    return ['unset'];
-  };
-
-  const [activePanels, setActivePanels] = useState<string[]>(() => [
-    ...getDefaultActivePanel(),
-  ]);
-
   function handlePopoverStatus(isOpen: boolean) {
     setVisible(isOpen);
-    // every time the popover opens, make sure the most relevant panel is active
-    if (isOpen) {
-      setActivePanels(getDefaultActivePanel());
-    }
-  }
-
-  function handleActivePanelChange(panels: string | string[]) {
-    // need to convert to an array so that handlePopoverStatus will work
-    if (typeof panels === 'string') {
-      setActivePanels([panels]);
-    } else {
-      setActivePanels(panels);
-    }
   }
 
   const indicatorKey = (indicator: Indicator): string =>
     `${indicator.column} - ${indicator.name}`;
 
   const content = (
-    <Panel>
+    <FiltersDetailsContainer>
       <Global
-        styles={css`
+        styles={theme => css`
           .filterStatusPopover {
             .ant-popover-inner {
               background-color: ${theme.colors.grayscale.dark2}cc;
               .ant-popover-inner-content {
-                padding-top: 0;
-                padding-bottom: 0;
+                padding: ${theme.gridUnit * 2}px;
               }
             }
             &.ant-popover-placement-bottom,
@@ -168,110 +120,47 @@ const DetailsPanelPopover = ({
           }
         `}
       />
-      <Reset>
-        <Collapse
-          ghost
-          light
-          activeKey={activePanels}
-          onChange={handleActivePanelChange}
-        >
-          {appliedCrossFilterIndicators.length ? (
-            <Collapse.Panel
-              key="appliedCrossFilters"
-              header={
-                <Title bold color={theme.colors.primary.light1}>
-                  <Icons.CursorTarget
-                    css={{ fill: theme.colors.primary.light1 }}
-                    iconSize="xl"
-                  />
-                  {t(
-                    'Applied Cross Filters (%d)',
-                    appliedCrossFilterIndicators.length,
-                  )}
-                </Title>
-              }
-            >
-              <Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
-                {appliedCrossFilterIndicators.map(indicator => (
-                  <FilterIndicator
-                    key={indicatorKey(indicator)}
-                    indicator={indicator}
-                    onClick={onHighlightFilterSource}
-                  />
-                ))}
-              </Indent>
-            </Collapse.Panel>
-          ) : null}
-          {appliedIndicators.length ? (
-            <Collapse.Panel
-              key="applied"
-              header={
-                <Title bold color={theme.colors.success.base}>
-                  <Icons.CheckCircleFilled css={iconReset} iconSize="m" />{' '}
-                  {t('Applied Filters (%d)', appliedIndicators.length)}
-                </Title>
-              }
-            >
-              <Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
-                {appliedIndicators.map(indicator => (
-                  <FilterIndicator
-                    key={indicatorKey(indicator)}
-                    indicator={indicator}
-                    onClick={onHighlightFilterSource}
-                  />
-                ))}
-              </Indent>
-            </Collapse.Panel>
-          ) : null}
-          {incompatibleIndicators.length ? (
-            <Collapse.Panel
-              key="incompatible"
-              header={
-                <Title bold color={theme.colors.alert.base}>
-                  <Icons.ExclamationCircleFilled css={iconReset} iconSize="m" />{' '}
-                  {t(
-                    'Incompatible Filters (%d)',
-                    incompatibleIndicators.length,
-                  )}
-                </Title>
-              }
-            >
-              <Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
-                {incompatibleIndicators.map(indicator => (
-                  <FilterIndicator
-                    key={indicatorKey(indicator)}
-                    indicator={indicator}
-                    onClick={onHighlightFilterSource}
-                  />
-                ))}
-              </Indent>
-            </Collapse.Panel>
-          ) : null}
-          {unsetIndicators.length ? (
-            <Collapse.Panel
-              key="unset"
-              header={
-                <Title bold color={theme.colors.grayscale.light1}>
-                  <Icons.MinusCircleFilled css={iconReset} iconSize="m" />{' '}
-                  {t('Unset Filters (%d)', unsetIndicators.length)}
-                </Title>
-              }
-              disabled={!unsetIndicators.length}
-            >
-              <Indent css={{ paddingBottom: theme.gridUnit * 3 }}>
-                {unsetIndicators.map(indicator => (
-                  <FilterIndicator
-                    key={indicatorKey(indicator)}
-                    indicator={indicator}
-                    onClick={onHighlightFilterSource}
-                  />
-                ))}
-              </Indent>
-            </Collapse.Panel>
-          ) : null}
-        </Collapse>
-      </Reset>
-    </Panel>
+      <div>
+        {appliedCrossFilterIndicators.length ? (
+          <div>
+            <SectionName>
+              {t(
+                'Applied cross-filters (%d)',
+                appliedCrossFilterIndicators.length,
+              )}
+            </SectionName>
+            <FiltersContainer>
+              {appliedCrossFilterIndicators.map(indicator => (
+                <FilterIndicator
+                  key={indicatorKey(indicator)}
+                  indicator={indicator}
+                  onClick={onHighlightFilterSource}
+                />
+              ))}
+            </FiltersContainer>
+          </div>
+        ) : null}
+        {appliedCrossFilterIndicators.length && appliedIndicators.length ? (
+          <Separator />
+        ) : null}
+        {appliedIndicators.length ? (
+          <div>
+            <SectionName>
+              {t('Applied filters (%d)', appliedIndicators.length)}
+            </SectionName>
+            <FiltersContainer>
+              {appliedIndicators.map(indicator => (
+                <FilterIndicator
+                  key={indicatorKey(indicator)}
+                  indicator={indicator}
+                  onClick={onHighlightFilterSource}
+                />
+              ))}
+            </FiltersContainer>
+          </div>
+        ) : null}
+      </div>
+    </FiltersDetailsContainer>
   );
 
   return (
@@ -281,7 +170,7 @@ const DetailsPanelPopover = ({
       visible={visible}
       onVisibleChange={handlePopoverStatus}
       placement="bottomRight"
-      trigger="click"
+      trigger="hover"
     >
       {children}
     </Popover>
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/FilterIndicator/index.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/FilterIndicator/index.tsx
index 2954474e64..ed09e16790 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/FilterIndicator/index.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/FilterIndicator/index.tsx
@@ -22,47 +22,48 @@ import { css } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 import { getFilterValueForDisplay } from 'src/dashboard/components/nativeFilters/FilterBar/FilterSets/utils';
 import {
-  FilterIndicatorText,
   FilterValue,
-  Item,
-  ItemIcon,
-  Title,
+  FilterItem,
+  FilterName,
 } from 'src/dashboard/components/FiltersBadge/Styles';
 import { Indicator } from 'src/dashboard/components/nativeFilters/selectors';
 
 export interface IndicatorProps {
   indicator: Indicator;
   onClick?: (path: string[]) => void;
-  text?: string;
 }
 
 const FilterIndicator: FC<IndicatorProps> = ({
   indicator: { column, name, value, path = [] },
-  onClick = () => {},
-  text,
+  onClick,
 }) => {
   const resultValue = getFilterValueForDisplay(value);
   return (
-    <>
-      <Item onClick={() => onClick([...path, `LABEL-${column}`])}>
-        <Title bold>
-          <ItemIcon>
-            <Icons.SearchOutlined
-              iconSize="m"
-              css={css`
-                span {
-                  vertical-align: 0;
-                }
-              `}
-            />
-          </ItemIcon>
+    <FilterItem
+      onClick={
+        onClick ? () => onClick([...path, `LABEL-${column}`]) : undefined
+      }
+    >
+      {onClick && (
+        <i>
+          <Icons.SearchOutlined
+            iconSize="m"
+            css={css`
+              span {
+                vertical-align: 0;
+              }
+            `}
+          />
+        </i>
+      )}
+      <div>
+        <FilterName>
           {name}
           {resultValue ? ': ' : ''}
-        </Title>
+        </FilterName>
         <FilterValue>{resultValue}</FilterValue>
-      </Item>
-      {text && <FilterIndicatorText>{text}</FilterIndicatorText>}
-    </>
+      </div>
+    </FilterItem>
   );
 };
 
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
index 2a1f7bcfb0..c9e6c9e1fc 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/FiltersBadge.test.tsx
@@ -36,7 +36,6 @@ import {
 import { sliceId } from 'spec/fixtures/mockChartQueries';
 import { dashboardFilters } from 'spec/fixtures/mockDashboardFilters';
 import { dashboardWithFilter } from 'spec/fixtures/mockDashboardLayout';
-import Icons from 'src/components/Icons';
 import { FeatureFlag } from 'src/featureFlags';
 
 const defaultStore = getMockStoreWithFilters();
@@ -111,36 +110,6 @@ describe('FiltersBadge', () => {
       );
       expect(wrapper.find('WarningFilled')).not.toExist();
     });
-
-    it("shows a warning when there's a rejected filter", () => {
-      const store = getMockStoreWithFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [],
-            rejected_filters: [
-              { column: 'region', reason: 'not_in_datasource' },
-            ],
-          },
-        ],
-        dashboardFilters,
-      });
-      store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
-      const wrapper = setup(store);
-      expect(wrapper.find('DetailsPanelPopover')).toExist();
-      expect(wrapper.find('[data-test="applied-filter-count"]')).toHaveText(
-        '0',
-      );
-      expect(
-        wrapper.find('[data-test="incompatible-filter-count"]'),
-      ).toHaveText('1');
-      // to look at the shape of the wrapper use:
-      expect(wrapper.find(Icons.AlertSolid)).toExist();
-    });
   });
 
   describe('for native filters', () => {
@@ -189,37 +158,5 @@ describe('FiltersBadge', () => {
       );
       expect(wrapper.find('WarningFilled')).not.toExist();
     });
-
-    it("shows a warning when there's a rejected filter", () => {
-      // @ts-ignore
-      global.featureFlags = {
-        [FeatureFlag.DASHBOARD_NATIVE_FILTERS]: true,
-      };
-      const store = getMockStoreWithNativeFilters();
-      // start with basic dashboard state, dispatch an event to simulate query completion
-      store.dispatch({
-        type: CHART_UPDATE_SUCCEEDED,
-        key: sliceId,
-        queriesResponse: [
-          {
-            status: 'success',
-            applied_filters: [],
-            rejected_filters: [
-              { column: 'region', reason: 'not_in_datasource' },
-            ],
-          },
-        ],
-      });
-      store.dispatch({ type: CHART_RENDERING_SUCCEEDED, key: sliceId });
-      const wrapper = setup(store);
-      expect(wrapper.find('DetailsPanelPopover')).toExist();
-      expect(wrapper.find('[data-test="applied-filter-count"]')).toHaveText(
-        '0',
-      );
-      expect(
-        wrapper.find('[data-test="incompatible-filter-count"]'),
-      ).toHaveText('1');
-      expect(wrapper.find(Icons.AlertSolid)).toExist();
-    });
   });
 });
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
index c80e0ec0cb..0018a007ba 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/Styles.tsx
@@ -20,7 +20,7 @@ import { css, styled } from '@superset-ui/core';
 
 export const Pill = styled.div`
   ${({ theme }) => css`
-    display: inline-block;
+    display: flex;
     color: ${theme.colors.grayscale.light5};
     background: ${theme.colors.grayscale.base};
     border-radius: 1em;
@@ -36,7 +36,6 @@ export const Pill = styled.div`
 
     svg {
       position: relative;
-      top: -2px;
       color: ${theme.colors.grayscale.light5};
       width: 1em;
       height: 1em;
@@ -55,64 +54,27 @@ export const Pill = styled.div`
         background: ${theme.colors.primary.dark1};
       }
     }
-
-    &.has-incompatible-filters {
-      color: ${theme.colors.grayscale.dark2};
-      background: ${theme.colors.alert.base};
-      &:hover {
-        background: ${theme.colors.alert.dark1};
-      }
-      svg {
-        color: ${theme.colors.grayscale.dark2};
-      }
-    }
-
-    &.filters-inactive {
-      color: ${theme.colors.grayscale.light5};
-      background: ${theme.colors.grayscale.light1};
-      padding: ${theme.gridUnit}px;
-      text-align: center;
-      height: 22px;
-      width: 22px;
-
-      &:hover {
-        background: ${theme.colors.grayscale.base};
-      }
-    }
   `}
 `;
 
-export interface TitleProps {
-  bold?: boolean;
-  color?: string;
-}
-
-export const Title = styled.span<TitleProps>`
-  position: relative;
-  margin-right: ${({ theme }) => theme.gridUnit}px;
-  font-weight: ${({ bold, theme }) => {
-    if (bold) return theme.typography.weights.bold;
-    return 'auto';
-  }};
-  color: ${({ color, theme }) => color || theme.colors.grayscale.light5};
-  display: flex;
-  align-items: center;
-  & > * {
-    margin-right: ${({ theme }) => theme.gridUnit}px;
-  }
+export const SectionName = styled.span`
+  ${({ theme }) => css`
+    font-weight: ${theme.typography.weights.bold};
+  `}
 `;
-
-export const ItemIcon = styled.i`
-  position: absolute;
-  top: 50%;
-  transform: translateY(-50%);
-  left: -${({ theme }) => theme.gridUnit * 5}px;
+export const FilterName = styled.span`
+  ${({ theme }) => css`
+    padding-right: ${theme.gridUnit}px;
+    font-style: italic;
+    & > * {
+      margin-right: ${theme.gridUnit}px;
+    }
+  `}
 `;
 
-export const Item = styled.button`
+export const FilterItem = styled.button`
   cursor: pointer;
   display: flex;
-  flex-wrap: wrap;
   text-align: left;
   padding: 0;
   border: none;
@@ -134,34 +96,36 @@ export const Item = styled.button`
   }
 `;
 
-export const Reset = styled.div`
-  margin: 0 -${({ theme }) => theme.gridUnit * 4}px;
+export const FiltersContainer = styled.div`
+  ${({ theme }) => css`
+    margin-top: ${theme.gridUnit}px;
+    &:not(:last-child) {
+      padding-bottom: ${theme.gridUnit * 3}px;
+    }
+  `}
 `;
 
-export const Indent = styled.div`
-  padding-left: ${({ theme }) => theme.gridUnit * 6}px;
-  margin: -${({ theme }) => theme.gridUnit * 3}px 0;
-`;
+export const FiltersDetailsContainer = styled.div`
+  ${({ theme }) => css`
+    min-width: 200px;
+    max-width: 300px;
+    overflow-x: hidden;
 
-export const Panel = styled.div`
-  min-width: 200px;
-  max-width: 300px;
-  overflow-x: hidden;
+    color: ${theme.colors.grayscale.light5};
+  `}
 `;
 
-export const FilterValue = styled.div`
+export const FilterValue = styled.span`
   max-width: 100%;
   flex-grow: 1;
   overflow: auto;
-  color: ${({ theme }) => theme.colors.grayscale.light5};
 `;
 
-export const FilterIndicatorText = styled.div`
-  ${({ theme }) => `
-  padding-top: ${theme.gridUnit * 3}px;
-  max-width: 100%;
-  flex-grow: 1;
-  overflow: auto;
-  color: ${theme.colors.grayscale.light5};
+export const Separator = styled.div`
+  ${({ theme }) => css`
+    width: 100%;
+    height: 1px;
+    background-color: ${theme.colors.grayscale.light1};
+    margin: ${theme.gridUnit * 4}px 0;
   `}
 `;
diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx
index fb0718bf54..655a97c7d8 100644
--- a/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx
+++ b/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx
@@ -211,66 +211,27 @@ export const FiltersBadge = ({ chartId }: FiltersBadgeProps) => {
       ),
     [indicators],
   );
-  const unsetIndicators = useMemo(
-    () =>
-      indicators.filter(
-        indicator => indicator.status === IndicatorStatus.Unset,
-      ),
-    [indicators],
-  );
-  const incompatibleIndicators = useMemo(
-    () =>
-      indicators.filter(
-        indicator => indicator.status === IndicatorStatus.Incompatible,
-      ),
-    [indicators],
-  );
 
-  if (
-    !appliedCrossFilterIndicators.length &&
-    !appliedIndicators.length &&
-    !incompatibleIndicators.length &&
-    !unsetIndicators.length
-  ) {
+  if (!appliedCrossFilterIndicators.length && !appliedIndicators.length) {
     return null;
   }
 
-  const isInactive =
-    !appliedCrossFilterIndicators.length &&
-    !appliedIndicators.length &&
-    !incompatibleIndicators.length;
-
   return (
     <DetailsPanelPopover
       appliedCrossFilterIndicators={appliedCrossFilterIndicators}
       appliedIndicators={appliedIndicators}
-      unsetIndicators={unsetIndicators}
-      incompatibleIndicators={incompatibleIndicators}
       onHighlightFilterSource={onHighlightFilterSource}
     >
       <Pill
         className={cx(
           'filter-counts',
-          !!incompatibleIndicators.length && 'has-incompatible-filters',
           !!appliedCrossFilterIndicators.length && 'has-cross-filters',
-          isInactive && 'filters-inactive',
         )}
       >
         <Icons.Filter iconSize="m" />
-        {!isInactive && (
-          <span data-test="applied-filter-count">
-            {appliedIndicators.length + appliedCrossFilterIndicators.length}
-          </span>
-        )}
-        {incompatibleIndicators.length ? (
-          <>
-            {' '}
-            <Icons.AlertSolid />
-            <span data-test="incompatible-filter-count">
-              {incompatibleIndicators.length}
-            </span>
-          </>
-        ) : null}
+        <span data-test="applied-filter-count">
+          {appliedIndicators.length + appliedCrossFilterIndicators.length}
+        </span>
       </Pill>
     </DetailsPanelPopover>
   );
diff --git a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx
index a928933b54..41b6a7dbe7 100644
--- a/superset-frontend/src/dashboard/components/SliceHeader/index.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeader/index.tsx
@@ -21,11 +21,10 @@ import React, {
   ReactNode,
   useContext,
   useEffect,
-  useMemo,
   useRef,
   useState,
 } from 'react';
-import { css, styled, t } from '@superset-ui/core';
+import { css, styled, SupersetTheme, t } from '@superset-ui/core';
 import { useUiConfig } from 'src/components/UiConfigContext';
 import { Tooltip } from 'src/components/Tooltip';
 import { useDispatch, useSelector } from 'react-redux';
@@ -36,10 +35,10 @@ import SliceHeaderControls, {
 import FiltersBadge from 'src/dashboard/components/FiltersBadge';
 import Icons from 'src/components/Icons';
 import { RootState } from 'src/dashboard/types';
-import FilterIndicator from 'src/dashboard/components/FiltersBadge/FilterIndicator';
 import { getSliceHeaderTooltip } from 'src/dashboard/util/getSliceHeaderTooltip';
 import { DashboardPageIdContext } from 'src/dashboard/containers/DashboardPage';
 import { clearDataMask } from 'src/dataMask/actions';
+import { getFilterValueForDisplay } from '../nativeFilters/FilterBar/FilterSets/utils';
 
 type SliceHeaderProps = SliceHeaderControlsProps & {
   innerRef?: string;
@@ -173,14 +172,6 @@ const SliceHeader: FC<SliceHeaderProps> = ({
     ({ dashboardInfo }) => dashboardInfo.crossFiltersEnabled,
   );
 
-  const indicator = useMemo(
-    () => ({
-      value: crossFilterValue,
-      name: t('Emitted values'),
-    }),
-    [crossFilterValue],
-  );
-
   const canExplore = !editMode && supersetCanExplore;
 
   useEffect(() => {
@@ -251,10 +242,19 @@ const SliceHeader: FC<SliceHeaderProps> = ({
               <Tooltip
                 placement="top"
                 title={
-                  <FilterIndicator
-                    indicator={indicator}
-                    text={t('Click to clear emitted filters')}
-                  />
+                  <div>
+                    <span>{t('Emitted values: ')}</span>
+                    <span>{getFilterValueForDisplay(crossFilterValue)}</span>
+                    <div
+                      css={(theme: SupersetTheme) =>
+                        css`
+                          margin-top: ${theme.gridUnit * 2}px;
+                        `
+                      }
+                    >
+                      {t('Click to clear emitted filters')}
+                    </div>
+                  </div>
                 }
               >
                 <CrossFilterIcon
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/Vertical.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/Vertical.tsx
index 93fb649d68..907e73c39e 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/Vertical.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/Vertical.tsx
@@ -18,9 +18,9 @@
  */
 
 import React from 'react';
-import { DataMaskStateWithId } from '@superset-ui/core';
+import { DataMaskStateWithId, JsonObject } from '@superset-ui/core';
 import { useSelector } from 'react-redux';
-import { DashboardInfo, DashboardLayout, RootState } from 'src/dashboard/types';
+import { DashboardLayout, RootState } from 'src/dashboard/types';
 import crossFiltersSelector from './selectors';
 import VerticalCollapse from './VerticalCollapse';
 
@@ -28,15 +28,15 @@ const CrossFiltersVertical = () => {
   const dataMask = useSelector<RootState, DataMaskStateWithId>(
     state => state.dataMask,
   );
-  const dashboardInfo = useSelector<RootState, DashboardInfo>(
-    state => state.dashboardInfo,
+  const chartConfiguration = useSelector<RootState, JsonObject>(
+    state => state.dashboardInfo.metadata?.chart_configuration,
   );
   const dashboardLayout = useSelector<RootState, DashboardLayout>(
     state => state.dashboardLayout.present,
   );
   const selectedCrossFilters = crossFiltersSelector({
     dataMask,
-    dashboardInfo,
+    chartConfiguration,
     dashboardLayout,
   });
 
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/selectors.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/selectors.ts
index c0f45af5b3..bf19ecabf9 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/selectors.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/CrossFilters/selectors.ts
@@ -17,37 +17,35 @@
  * under the License.
  */
 
-import { DataMaskStateWithId } from '@superset-ui/core';
-import { DashboardInfo, DashboardLayout } from 'src/dashboard/types';
-import { CrossFilterIndicator, selectChartCrossFilters } from '../../selectors';
+import { DataMaskStateWithId, isDefined, JsonObject } from '@superset-ui/core';
+import { DashboardLayout } from 'src/dashboard/types';
+import { CrossFilterIndicator, getCrossFilterIndicator } from '../../selectors';
 
 export const crossFiltersSelector = (props: {
   dataMask: DataMaskStateWithId;
-  dashboardInfo: DashboardInfo;
+  chartConfiguration: JsonObject;
   dashboardLayout: DashboardLayout;
 }): CrossFilterIndicator[] => {
-  const { dataMask, dashboardInfo, dashboardLayout } = props;
-  const chartConfiguration = dashboardInfo.metadata?.chart_configuration;
+  const { dataMask, chartConfiguration, dashboardLayout } = props;
   const chartsIds = Object.keys(chartConfiguration);
-  const shouldFilterEmitters = true;
 
-  let selectedCrossFilters: CrossFilterIndicator[] = [];
-
-  for (let i = 0; i < chartsIds.length; i += 1) {
-    const chartId = Number(chartsIds[i]);
-    const crossFilters = selectChartCrossFilters(
-      dataMask,
-      chartId,
-      dashboardLayout,
-      chartConfiguration,
-      shouldFilterEmitters,
-    );
-    selectedCrossFilters = [
-      ...selectedCrossFilters,
-      ...(crossFilters as CrossFilterIndicator[]),
-    ];
-  }
-  return selectedCrossFilters;
+  return chartsIds
+    .map(chartId => {
+      const id = Number(chartId);
+      const filterIndicator = getCrossFilterIndicator(
+        id,
+        dataMask[id],
+        dashboardLayout,
+      );
+      if (
+        isDefined(filterIndicator.column) &&
+        isDefined(filterIndicator.value)
+      ) {
+        return { ...filterIndicator, emitterId: id };
+      }
+      return null;
+    })
+    .filter(Boolean) as CrossFilterIndicator[];
 };
 
 export default crossFiltersSelector;
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx
index 589e7609e8..b44591f4b1 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx
@@ -35,6 +35,7 @@ import {
   isFeatureEnabled,
   FeatureFlag,
   isNativeFilterWithDataMask,
+  JsonObject,
 } from '@superset-ui/core';
 import {
   createHtmlPortalNode,
@@ -47,7 +48,6 @@ import {
   useSelectFiltersInScope,
 } from 'src/dashboard/components/nativeFilters/state';
 import {
-  DashboardInfo,
   DashboardLayout,
   FilterBarOrientation,
   RootState,
@@ -87,8 +87,8 @@ const FilterControls: FC<FilterControlsProps> = ({
   const dataMask = useSelector<RootState, DataMaskStateWithId>(
     state => state.dataMask,
   );
-  const dashboardInfo = useSelector<RootState, DashboardInfo>(
-    state => state.dashboardInfo,
+  const chartConfiguration = useSelector<RootState, JsonObject>(
+    state => state.dashboardInfo.metadata?.chart_configuration,
   );
   const dashboardLayout = useSelector<RootState, DashboardLayout>(
     state => state.dashboardLayout.present,
@@ -101,11 +101,11 @@ const FilterControls: FC<FilterControlsProps> = ({
       isCrossFiltersEnabled
         ? crossFiltersSelector({
             dataMask,
-            dashboardInfo,
+            chartConfiguration,
             dashboardLayout,
           })
         : [],
-    [dashboardInfo, dashboardLayout, dataMask, isCrossFiltersEnabled],
+    [chartConfiguration, dashboardLayout, dataMask, isCrossFiltersEnabled],
   );
   const { filterControlFactory, filtersWithValues } = useFilterControlFactory(
     dataMaskSelected,
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx
index badff64295..24b2caa033 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx
@@ -22,12 +22,13 @@ import {
   DataMaskStateWithId,
   FeatureFlag,
   isFeatureEnabled,
+  JsonObject,
   styled,
   t,
 } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 import Loading from 'src/components/Loading';
-import { DashboardInfo, DashboardLayout, RootState } from 'src/dashboard/types';
+import { DashboardLayout, RootState } from 'src/dashboard/types';
 import { useSelector } from 'react-redux';
 import FilterControls from './FilterControls/FilterControls';
 import { getFilterBarTestId } from './utils';
@@ -107,8 +108,8 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({
   const dataMask = useSelector<RootState, DataMaskStateWithId>(
     state => state.dataMask,
   );
-  const dashboardInfo = useSelector<RootState, DashboardInfo>(
-    state => state.dashboardInfo,
+  const chartConfiguration = useSelector<RootState, JsonObject>(
+    state => state.dashboardInfo.metadata?.chart_configuration,
   );
   const dashboardLayout = useSelector<RootState, DashboardLayout>(
     state => state.dashboardLayout.present,
@@ -119,7 +120,7 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({
   const selectedCrossFilters = isCrossFiltersEnabled
     ? crossFiltersSelector({
         dataMask,
-        dashboardInfo,
+        chartConfiguration,
         dashboardLayout,
       })
     : [];
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts b/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
index 27845727aa..5d4e980a40 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/selectors.ts
@@ -17,6 +17,7 @@
  * under the License.
  */
 import {
+  DataMask,
   DataMaskStateWithId,
   DataMaskType,
   ensureIsArray,
@@ -32,7 +33,7 @@ import {
 import { TIME_FILTER_MAP } from 'src/explore/constants';
 import { getChartIdsInFilterBoxScope } from 'src/dashboard/util/activeDashboardFilters';
 import { ChartConfiguration } from 'src/dashboard/reducers/types';
-import { Layout } from 'src/dashboard/types';
+import { DashboardLayout, Layout } from 'src/dashboard/types';
 import { areObjectsEqual } from 'src/reduxUtils';
 
 export enum IndicatorStatus {
@@ -61,7 +62,7 @@ type Filter = {
   datasourceId: string;
 };
 
-const extractLabel = (filter?: FilterState): string | null => {
+export const extractLabel = (filter?: FilterState): string | null => {
   if (filter?.label && !filter?.label?.includes(undefined)) {
     return filter.label;
   }
@@ -162,6 +163,36 @@ export type Indicator = {
 
 export type CrossFilterIndicator = Indicator & { emitterId: number };
 
+export const getCrossFilterIndicator = (
+  chartId: number,
+  dataMask: DataMask,
+  dashboardLayout: DashboardLayout,
+) => {
+  const filterState = dataMask?.filterState;
+  const filters = dataMask?.extraFormData?.filters;
+  const label = extractLabel(filterState);
+  const filtersState = filterState?.filters;
+  const column =
+    filters?.[0]?.col || (filtersState && Object.keys(filtersState)[0]);
+
+  const dashboardLayoutItem = Object.values(dashboardLayout).find(
+    layoutItem => layoutItem?.meta?.chartId === chartId,
+  );
+  const filterObject: Indicator = {
+    column,
+    name:
+      dashboardLayoutItem?.meta?.sliceNameOverride ||
+      dashboardLayoutItem?.meta?.sliceName ||
+      '',
+    path: [
+      ...(dashboardLayoutItem?.parents ?? []),
+      dashboardLayoutItem?.id || '',
+    ],
+    value: label,
+  };
+  return filterObject;
+};
+
 const cachedIndicatorsForChart = {};
 const cachedDashboardFilterDataForChart = {};
 // inspects redux state to find what the filter indicators should be shown for a given chart
@@ -233,17 +264,18 @@ const getStatus = ({
 }): IndicatorStatus => {
   // a filter is only considered unset if it's value is null
   const hasValue = label !== null;
-  if (type === DataMaskType.CrossFilters && hasValue) {
-    return IndicatorStatus.CrossFilterApplied;
-  }
+  const APPLIED_STATUS =
+    type === DataMaskType.CrossFilters
+      ? IndicatorStatus.CrossFilterApplied
+      : IndicatorStatus.Applied;
   if (!column && hasValue) {
     // Filter without datasource
-    return IndicatorStatus.Applied;
+    return APPLIED_STATUS;
   }
   if (column && rejectedColumns?.has(column))
     return IndicatorStatus.Incompatible;
   if (column && appliedColumns?.has(column) && hasValue) {
-    return IndicatorStatus.Applied;
+    return APPLIED_STATUS;
   }
   return IndicatorStatus.Unset;
 };
@@ -254,11 +286,12 @@ export const selectChartCrossFilters = (
   chartId: number,
   dashboardLayout: Layout,
   chartConfiguration: ChartConfiguration = defaultChartConfig,
+  appliedColumns: Set<string>,
+  rejectedColumns: Set<string>,
   filterEmitter = false,
 ): Indicator[] | CrossFilterIndicator[] => {
   let crossFilterIndicators: any = [];
   if (isFeatureEnabled(FeatureFlag.DASHBOARD_CROSS_FILTERS)) {
-    const dashboardLayoutValues = Object.values(dashboardLayout);
     crossFilterIndicators = Object.values(chartConfiguration)
       .filter(chartConfig => {
         const inScope =
@@ -272,34 +305,22 @@ export const selectChartCrossFilters = (
         return false;
       })
       .map(chartConfig => {
-        const filterState = dataMask[chartConfig.id]?.filterState;
-        const extraFormData = dataMask[chartConfig.id]?.extraFormData;
-        const label = extractLabel(filterState);
-        const filtersState = filterState?.filters;
-        const column =
-          extraFormData?.filters?.[0]?.col ||
-          (filtersState && Object.keys(filtersState)[0]);
-
-        const dashboardLayoutItem = dashboardLayoutValues.find(
-          layoutItem => layoutItem?.meta?.chartId === chartConfig.id,
+        const filterIndicator = getCrossFilterIndicator(
+          chartConfig.id,
+          dataMask[chartConfig.id],
+          dashboardLayout,
         );
-        const filterObject: Indicator = {
-          column,
-          name: dashboardLayoutItem?.meta?.sliceName as string,
-          path: [
-            ...(dashboardLayoutItem?.parents ?? []),
-            dashboardLayoutItem?.id || '',
-          ],
-          status: getStatus({
-            label,
-            type: DataMaskType.CrossFilters,
-          }),
-          value: label,
-        };
-        if (filterEmitter) {
-          (filterObject as CrossFilterIndicator).emitterId = chartId;
-        }
-        return filterObject;
+        const filterStatus = getStatus({
+          label: filterIndicator.value,
+          column: filterIndicator.column
+            ? getColumnLabel(filterIndicator.column)
+            : undefined,
+          type: DataMaskType.CrossFilters,
+          appliedColumns,
+          rejectedColumns,
+        });
+
+        return { ...filterIndicator, status: filterStatus };
       })
       .filter(filter => filter.status === IndicatorStatus.CrossFilterApplied);
   }
@@ -369,6 +390,8 @@ export const selectNativeIndicatorsForChart = (
       chartId,
       dashboardLayout,
       chartConfiguration,
+      appliedColumns,
+      rejectedColumns,
     );
   }
   const indicators = crossFilterIndicators.concat(nativeFilterIndicators);