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 2025/02/07 17:38:16 UTC

(superset) branch master updated: refactor(Dropdown): Migrate Dropdown to Ant Design 5 (#31972)

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 bcc61bd933 refactor(Dropdown): Migrate Dropdown to Ant Design 5 (#31972)
bcc61bd933 is described below

commit bcc61bd9339543f43712b6966a5221bc37f1e802
Author: Mehmet Salih Yavuz <sa...@proton.me>
AuthorDate: Fri Feb 7 20:38:04 2025 +0300

    refactor(Dropdown): Migrate Dropdown to Ant Design 5 (#31972)
---
 .../cypress/e2e/dashboard/_skip.controls.test.ts   |  10 +-
 .../cypress/e2e/dashboard/drillby.test.ts          |   5 +-
 .../cypress/e2e/dashboard/drilltodetail.test.ts    |   5 +-
 .../e2e/dashboard/horizontalFilterBar.test.ts      |   8 +-
 .../cypress-base/cypress/e2e/explore/chart.test.js |  22 +-
 .../explore/visualizations/download_chart.test.js  |   6 +-
 .../cypress-base/cypress/e2e/sqllab/tabs.test.ts   |   4 +-
 .../cypress-base/cypress/support/directories.ts    |   8 +-
 superset-frontend/src/GlobalStyles.tsx             |   9 -
 .../SqlLab/components/QueryLimitSelect/index.tsx   |  53 +---
 .../components/SaveDatasetActionButton/index.tsx   |  30 +-
 .../src/SqlLab/components/SqlEditor/index.tsx      |  10 +-
 .../SqlLab/components/SqlEditorTabHeader/index.tsx |   6 +-
 .../src/SqlLab/components/TablePreview/index.tsx   |  15 +-
 superset-frontend/src/components/Button/index.tsx  |   2 +-
 .../Chart/ChartContextMenu/ChartContextMenu.tsx    |  42 ++-
 .../Chart/DrillBy/DrillByMenuItems.test.tsx        |   1 -
 .../components/Chart/DrillBy/DrillByMenuItems.tsx  |  35 +--
 .../Chart/DrillDetail/DrillDetailMenuItems.tsx     |  27 +-
 .../components/Chart/MenuItemWithTruncation.tsx    |   9 +-
 .../src/components/Dropdown/Dropdown.stories.tsx   |   6 +-
 .../src/components/Dropdown/index.tsx              |  27 +-
 .../src/components/DropdownButton/index.tsx        |  97 ++----
 .../DropdownSelectableIcon.stories.tsx             |  56 ----
 .../DropdownSelectableIcon.test.tsx                |  98 ------
 .../components/DropdownSelectableIcon/index.tsx    | 177 -----------
 .../ListViewCard/ListViewCard.stories.tsx          |  10 +-
 superset-frontend/src/components/Menu/index.tsx    |  27 +-
 .../src/components/PageHeaderWithActions/index.tsx |  10 +-
 .../src/components/PopoverDropdown/index.tsx       |  10 +-
 .../Table/cell-renderers/ActionCell/index.tsx      |   4 +-
 superset-frontend/src/components/Tooltip/index.tsx |  18 +-
 superset-frontend/src/components/index.ts          |   2 -
 .../src/dashboard/components/CssEditor/index.tsx   |   6 +-
 .../DashboardBuilder/DashboardBuilder.test.tsx     |   3 -
 .../HeaderActionsDropdown.test.tsx                 | 260 ----------------
 .../src/dashboard/components/Header/index.jsx      | 180 ++++++-----
 .../src/dashboard/components/Header/types.ts       |   5 +-
 .../index.tsx => useHeaderActionsDropdownMenu.tsx} | 339 ++++++++++-----------
 .../components/RefreshIntervalModal.test.tsx       |  18 +-
 .../SliceHeaderControls.test.tsx                   |  31 +-
 .../components/SliceHeaderControls/index.tsx       |  38 +--
 .../DownloadMenuItems/DownloadAsImage.test.tsx     |   6 +-
 .../menu/DownloadMenuItems/DownloadAsImage.tsx     |  12 +-
 .../menu/DownloadMenuItems/DownloadAsPdf.test.tsx  |   6 +-
 .../menu/DownloadMenuItems/DownloadAsPdf.tsx       |  12 +-
 .../DownloadMenuItems/DownloadMenuItems.test.tsx   |  12 +-
 .../components/menu/DownloadMenuItems/index.tsx    |  51 ++--
 .../menu/ShareMenuItems/ShareMenuItems.test.tsx    |   1 +
 .../components/menu/ShareMenuItems/index.tsx       |  16 +-
 .../FilterBarSettings/FilterBarSettings.test.tsx   |  50 ++-
 .../FilterBar/FilterBarSettings/index.tsx          |  76 +++--
 .../FilterConfigurationLink.test.tsx               |  15 +-
 .../FilterBar/FilterConfigurationLink/index.tsx    |  65 +---
 .../useFilterConfigModal.tsx                       |  82 +++++
 .../nativeFilters/FilterBar/Header/index.tsx       |   3 -
 .../nativeFilters/FilterCard/NameRow.tsx           |  15 +-
 .../src/dashboard/hooks/useDownloadScreenshot.ts   | 184 +++++++++++
 superset-frontend/src/dashboard/styles.ts          |   6 +-
 .../components/ExploreChartHeader/index.jsx        |  51 +++-
 .../components/ExportToCSVDropdown/index.tsx       |  10 +-
 .../DatasourceControl/DatasourceControl.test.jsx   |   6 +
 .../controls/DatasourceControl/index.jsx           |  14 +-
 .../useExploreAdditionalActionsMenu/index.jsx      |  52 ++--
 .../src/features/charts/ChartCard.tsx              |   6 +-
 .../src/features/dashboards/DashboardCard.tsx      |   6 +-
 superset-frontend/src/features/home/RightMenu.tsx  |   2 +
 .../src/features/home/SavedQueries.tsx             |   6 +-
 .../HeaderReportDropdown/index.test.tsx            |  71 +++--
 .../ReportModal/HeaderReportDropdown/index.tsx     | 132 ++------
 superset-frontend/src/features/tags/TagCard.tsx    |   6 +-
 71 files changed, 1132 insertions(+), 1571 deletions(-)

diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/_skip.controls.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/_skip.controls.test.ts
index 4a65d68cf5..0e5c733131 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/_skip.controls.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/_skip.controls.test.ts
@@ -47,12 +47,12 @@ describe.skip('Dashboard top-level controls', () => {
       // Solution: pause the network before clicking, assert, then unpause network.
       cy.get('[data-test="refresh-chart-menu-item"]').should(
         'have.class',
-        'ant-dropdown-menu-item-disabled',
+        'antd5-dropdown-menu-item-disabled',
       );
       waitForChartLoad(mapSpec);
       cy.get('[data-test="refresh-chart-menu-item"]').should(
         'not.have.class',
-        'ant-dropdown-menu-item-disabled',
+        'antd5-dropdown-menu-item-disabled',
       );
     });
   });
@@ -65,7 +65,7 @@ describe.skip('Dashboard top-level controls', () => {
       cy.get('[aria-label="more-horiz"]').click();
       cy.get('[data-test="refresh-dashboard-menu-item"]').should(
         'not.have.class',
-        'ant-dropdown-menu-item-disabled',
+        'antd5-dropdown-menu-item-disabled',
       );
 
       cy.get('[data-test="refresh-dashboard-menu-item"]').click({
@@ -73,7 +73,7 @@ describe.skip('Dashboard top-level controls', () => {
       });
       cy.get('[data-test="refresh-dashboard-menu-item"]').should(
         'have.class',
-        'ant-dropdown-menu-item-disabled',
+        'antd5-dropdown-menu-item-disabled',
       );
 
       // wait all charts force refreshed.
@@ -94,7 +94,7 @@ describe.skip('Dashboard top-level controls', () => {
     cy.get('[aria-label="more-horiz"]').click();
     cy.get('[data-test="refresh-dashboard-menu-item"]').and(
       'not.have.class',
-      'ant-dropdown-menu-item-disabled',
+      'antd5-dropdown-menu-item-disabled',
     );
   });
 });
diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts
index e471d1da8c..2586fbd438 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/drillby.test.ts
@@ -54,15 +54,14 @@ const drillBy = (targetDrillByColumn: string, isLegacy = false) => {
     interceptV1ChartData();
   }
 
-  cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
-    .first()
+  cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
     .should('be.visible')
     .find("[role='menu'] [role='menuitem']")
     .contains(/^Drill by$/)
     .trigger('mouseover', { force: true });
 
   cy.get(
-    '.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
+    '.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-by-submenu"]',
   )
     .should('be.visible')
     .find('[role="menuitem"]')
diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
index 4ebd64dd6e..d2eb482a9f 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/drilltodetail.test.ts
@@ -61,15 +61,14 @@ function drillToDetail(targetMenuItem: string) {
 const drillToDetailBy = (targetDrill: string) => {
   interceptSamples();
 
-  cy.get('.ant-dropdown:not(.ant-dropdown-hidden)')
-    .first()
+  cy.get('.antd5-dropdown:not(.antd5-dropdown-hidden)')
     .should('be.visible')
     .find("[role='menu'] [role='menuitem']")
     .contains(/^Drill to detail by$/)
     .trigger('mouseover', { force: true });
 
   cy.get(
-    '.ant-dropdown-menu-submenu:not(.ant-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
+    '.antd5-dropdown-menu-submenu:not(.antd5-dropdown-menu-submenu-hidden) [data-test="drill-to-detail-by-submenu"]',
   )
     .should('be.visible')
     .find('[role="menuitem"]')
diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/horizontalFilterBar.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/horizontalFilterBar.test.ts
index bcacae8a36..ec20224fce 100644
--- a/superset-frontend/cypress-base/cypress/e2e/dashboard/horizontalFilterBar.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/horizontalFilterBar.test.ts
@@ -57,16 +57,16 @@ function setFilterBarOrientation(orientation: 'vertical' | 'horizontal') {
     .trigger('mouseover');
 
   if (orientation === 'vertical') {
-    cy.get('.antd5-menu-item-selected')
+    cy.get('.antd5-dropdown-menu-item-selected')
       .contains('Horizontal (Top)')
       .should('exist');
-    cy.get('.antd5-menu-item').contains('Vertical (Left)').click();
+    cy.get('.antd5-dropdown-menu-item').contains('Vertical (Left)').click();
     cy.getBySel('dashboard-filters-panel').should('exist');
   } else {
-    cy.get('.antd5-menu-item-selected')
+    cy.get('.antd5-dropdown-menu-item-selected')
       .contains('Vertical (Left)')
       .should('exist');
-    cy.get('.antd5-menu-item').contains('Horizontal (Top)').click();
+    cy.get('.antd5-dropdown-menu-item').contains('Horizontal (Top)').click();
     cy.getBySel('loading-indicator').should('exist');
     cy.getBySel('filter-bar').should('exist');
     cy.getBySel('dashboard-filters-panel').should('not.exist');
diff --git a/superset-frontend/cypress-base/cypress/e2e/explore/chart.test.js b/superset-frontend/cypress-base/cypress/e2e/explore/chart.test.js
index c7b81a3e12..abe5a0c51d 100644
--- a/superset-frontend/cypress-base/cypress/e2e/explore/chart.test.js
+++ b/superset-frontend/cypress-base/cypress/e2e/explore/chart.test.js
@@ -31,35 +31,35 @@ const SAMPLE_DASHBOARDS_INDEXES = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
 function openDashboardsAddedTo() {
   cy.getBySel('actions-trigger').click();
-  cy.get('.ant-dropdown-menu-submenu-title')
+  cy.get('.antd5-dropdown-menu-submenu-title')
     .contains('On dashboards')
     .trigger('mouseover', { force: true });
 }
 
 function closeDashboardsAddedTo() {
-  cy.get('.ant-dropdown-menu-submenu-title')
+  cy.get('.antd5-dropdown-menu-submenu-title')
     .contains('On dashboards')
     .trigger('mouseout', { force: true });
   cy.getBySel('actions-trigger').click();
 }
 
 function verifyDashboardsSubmenuItem(dashboardName) {
-  cy.get('.ant-dropdown-menu-submenu-popup').contains(dashboardName);
+  cy.get('.antd5-dropdown-menu-submenu-popup').contains(dashboardName);
   closeDashboardsAddedTo();
 }
 
 function verifyDashboardSearch() {
   openDashboardsAddedTo();
-  cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
-  cy.get('.ant-dropdown-menu-submenu-popup')
+  cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
+  cy.get('.antd5-dropdown-menu-submenu-popup')
     .find('input[placeholder="Search"]')
     .type('1');
-  cy.get('.ant-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
-  cy.get('.ant-dropdown-menu-submenu-popup')
+  cy.get('.antd5-dropdown-menu-submenu-popup').contains('1 - Sample dashboard');
+  cy.get('.antd5-dropdown-menu-submenu-popup')
     .find('input[placeholder="Search"]')
     .type('Blahblah');
-  cy.get('.ant-dropdown-menu-submenu-popup').contains('No results found');
-  cy.get('.ant-dropdown-menu-submenu-popup')
+  cy.get('.antd5-dropdown-menu-submenu-popup').contains('No results found');
+  cy.get('.antd5-dropdown-menu-submenu-popup')
     .find('[aria-label="close-circle"]')
     .click();
   closeDashboardsAddedTo();
@@ -68,8 +68,8 @@ function verifyDashboardSearch() {
 function verifyDashboardLink() {
   interceptDashboardGet();
   openDashboardsAddedTo();
-  cy.get('.ant-dropdown-menu-submenu-popup').trigger('mouseover');
-  cy.get('.ant-dropdown-menu-submenu-popup a')
+  cy.get('.antd5-dropdown-menu-submenu-popup').trigger('mouseover');
+  cy.get('.antd5-dropdown-menu-submenu-popup a')
     .first()
     .invoke('removeAttr', 'target')
     .click();
diff --git a/superset-frontend/cypress-base/cypress/e2e/explore/visualizations/download_chart.test.js b/superset-frontend/cypress-base/cypress/e2e/explore/visualizations/download_chart.test.js
index ce7e522774..76653dada9 100644
--- a/superset-frontend/cypress-base/cypress/e2e/explore/visualizations/download_chart.test.js
+++ b/superset-frontend/cypress-base/cypress/e2e/explore/visualizations/download_chart.test.js
@@ -36,10 +36,10 @@ describe('Download Chart > Bar chart', () => {
     };
 
     cy.visitChartByParams(formData);
-    cy.get('.header-with-actions .ant-dropdown-trigger').click();
-    cy.get(':nth-child(3) > .ant-dropdown-menu-submenu-title').click();
+    cy.get('.header-with-actions .antd5-dropdown-trigger').click();
+    cy.get(':nth-child(3) > .antd5-dropdown-menu-submenu-title').click();
     cy.get(
-      '.ant-dropdown-menu-submenu > .ant-dropdown-menu li:nth-child(3)',
+      '.antd5-dropdown-menu-submenu > .antd5-dropdown-menu li:nth-child(3)',
     ).click();
     cy.verifyDownload('.jpg', {
       contains: true,
diff --git a/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts b/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
index 0deeabde8d..ef97eb1c19 100644
--- a/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
+++ b/superset-frontend/cypress-base/cypress/e2e/sqllab/tabs.test.ts
@@ -80,9 +80,9 @@ describe('SqlLab query tabs', () => {
       // configure some editor settings
       cy.get(editorInput).type('some random query string', { force: true });
       cy.get(queryLimitSelector).parent().click({ force: true });
-      cy.get('.ant-dropdown-menu')
+      cy.get('.antd5-dropdown-menu')
         .last()
-        .find('.ant-dropdown-menu-item')
+        .find('.antd5-dropdown-menu-item')
         .first()
         .click({ force: true });
 
diff --git a/superset-frontend/cypress-base/cypress/support/directories.ts b/superset-frontend/cypress-base/cypress/support/directories.ts
index 3445cf5f26..c2754e4543 100644
--- a/superset-frontend/cypress-base/cypress/support/directories.ts
+++ b/superset-frontend/cypress-base/cypress/support/directories.ts
@@ -158,10 +158,10 @@ export const sqlLabView = {
     runButton: '.css-d3dxop',
   },
   rowsLimit: {
-    dropdown: '.ant-dropdown-menu',
-    limitButton: '.ant-dropdown-menu-item',
+    dropdown: '.antd5-dropdown-menu',
+    limitButton: '.antd5-dropdown-menu-item',
     limitButtonText: '.css-151uxnz',
-    limitTextWithValue: '[class="ant-dropdown-trigger"]',
+    limitTextWithValue: '[class="antd5-dropdown-trigger"]',
   },
   renderedTableHeader: '.ReactVirtualized__Table__headerRow',
   renderedTableRow: '.ReactVirtualized__Table__row',
@@ -633,7 +633,7 @@ export const dashboardView = {
     refreshChart: dataTestLocator('refresh-chart-menu-item'),
   },
   threeDotsMenuIcon:
-    '.header-with-actions .right-button-panel .ant-dropdown-trigger',
+    '.header-with-actions .right-button-panel .antd5-dropdown-trigger',
   threeDotsMenuDropdown: dataTestLocator('header-actions-menu'),
   refreshDashboard: dataTestLocator('refresh-dashboard-menu-item'),
   saveAsMenuOption: dataTestLocator('save-as-menu-item'),
diff --git a/superset-frontend/src/GlobalStyles.tsx b/superset-frontend/src/GlobalStyles.tsx
index 09a2792dde..d213faac8d 100644
--- a/superset-frontend/src/GlobalStyles.tsx
+++ b/superset-frontend/src/GlobalStyles.tsx
@@ -43,8 +43,6 @@ export const GlobalStyles = () => (
       // TODO: Remove z-indexes when Ant Design is fully upgraded to v5
       // Prefer vanilla Ant Design z-indexes that should work out of the box
       .ant-popover,
-      .antd5-dropdown,
-      .ant-dropdown,
       .ant-select-dropdown,
       .antd5-modal-wrap,
       .antd5-modal-mask,
@@ -105,13 +103,6 @@ export const GlobalStyles = () => (
           margin-right: 0;
         }
       }
-      .ant-dropdown-menu-sub .antd5-menu.antd5-menu-vertical {
-        box-shadow: none;
-      }
-      .ant-dropdown-menu-submenu-title,
-      .ant-dropdown-menu-item {
-        line-height: 1.5em !important;
-      }
     `}
   />
 );
diff --git a/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx b/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
index df35351e5c..ce9cb2b06d 100644
--- a/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
+++ b/superset-frontend/src/SqlLab/components/QueryLimitSelect/index.tsx
@@ -17,12 +17,13 @@
  * under the License.
  */
 import { useDispatch } from 'react-redux';
-import { styled, useTheme, t } from '@superset-ui/core';
-import { AntdDropdown } from 'src/components';
+import { useTheme, t } from '@superset-ui/core';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import Icons from 'src/components/Icons';
 import { queryEditorSetQueryLimit } from 'src/SqlLab/actions/sqlLab';
 import useQueryEditor from 'src/SqlLab/hooks/useQueryEditor';
+import Button from 'src/components/Button';
 
 export interface QueryLimitSelectProps {
   queryEditorId: string;
@@ -34,28 +35,6 @@ export function convertToNumWithSpaces(num: number) {
   return num.toString().replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1 ');
 }
 
-const LimitSelectStyled = styled.span`
-  ${({ theme }) => `
-    .ant-dropdown-trigger {
-      align-items: center;
-      color: ${theme.colors.grayscale.dark2};
-      display: flex;
-      font-size: 12px;
-      margin-right: ${theme.gridUnit * 2}px;
-      text-decoration: none;
-      border: 0;
-      background: transparent;
-      span {
-        display: inline-block;
-        margin-right: ${theme.gridUnit * 2}px;
-        &:last-of-type: {
-          margin-right: ${theme.gridUnit * 4}px;
-        }
-      }
-    }
-  `}
-`;
-
 function renderQueryLimit(
   maxRow: number,
   setQueryLimit: (limit: number) => void,
@@ -94,20 +73,18 @@ const QueryLimitSelect = ({
     dispatch(queryEditorSetQueryLimit(queryEditor, updatedQueryLimit));
 
   return (
-    <LimitSelectStyled>
-      <AntdDropdown
-        overlay={renderQueryLimit(maxRow, setQueryLimit)}
-        trigger={['click']}
-      >
-        <button type="button" onClick={e => e.preventDefault()}>
-          <span>{t('LIMIT')}:</span>
-          <span className="limitDropdown">
-            {convertToNumWithSpaces(queryLimit)}
-          </span>
-          <Icons.TriangleDown iconColor={theme.colors.grayscale.base} />
-        </button>
-      </AntdDropdown>
-    </LimitSelectStyled>
+    <Dropdown
+      dropdownRender={() => renderQueryLimit(maxRow, setQueryLimit)}
+      trigger={['click']}
+    >
+      <Button size="small" showMarginRight={false} type="link">
+        <span>{t('LIMIT')}:</span>
+        <span className="limitDropdown">
+          {convertToNumWithSpaces(queryLimit)}
+        </span>
+        <Icons.TriangleDown iconColor={theme.colors.grayscale.base} />
+      </Button>
+    </Dropdown>
   );
 };
 
diff --git a/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx
index 2acd7665bf..6cb17bc2b4 100644
--- a/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SaveDatasetActionButton/index.tsx
@@ -16,12 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { FC } from 'react';
-import { t, useTheme, styled } from '@superset-ui/core';
+import { t, useTheme } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 import { DropdownButton } from 'src/components/DropdownButton';
 import Button from 'src/components/Button';
-import { DropdownButtonProps } from 'antd/lib/dropdown';
 
 interface SaveDatasetActionButtonProps {
   setShowSave: (arg0: boolean) => void;
@@ -34,34 +32,14 @@ const SaveDatasetActionButton = ({
 }: SaveDatasetActionButtonProps) => {
   const theme = useTheme();
 
-  const StyledDropdownButton = styled(
-    DropdownButton as FC<DropdownButtonProps>,
-  )`
-    &.ant-dropdown-button button.ant-btn.ant-btn-default {
-      font-weight: ${theme.gridUnit * 150};
-      background-color: ${theme.colors.primary.light4};
-      color: ${theme.colors.primary.dark1};
-      &:nth-of-type(2) {
-        &:before,
-        &:hover:before {
-          border-left: 2px solid ${theme.colors.primary.dark2};
-        }
-      }
-    }
-    span[name='caret-down'] {
-      margin-left: ${theme.gridUnit * 1}px;
-      color: ${theme.colors.primary.dark2};
-    }
-  `;
-
   return !overlayMenu ? (
     <Button onClick={() => setShowSave(true)} buttonStyle="primary">
       {t('Save')}
     </Button>
   ) : (
-    <StyledDropdownButton
+    <DropdownButton
       onClick={() => setShowSave(true)}
-      overlay={overlayMenu}
+      dropdownRender={() => overlayMenu}
       icon={
         <Icons.CaretDown
           iconColor={theme.colors.grayscale.light5}
@@ -71,7 +49,7 @@ const SaveDatasetActionButton = ({
       trigger={['click']}
     >
       {t('Save')}
-    </StyledDropdownButton>
+    </DropdownButton>
   );
 };
 
diff --git a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
index 527c22752d..957de4fbc7 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditor/index.tsx
@@ -56,7 +56,8 @@ import Mousetrap from 'mousetrap';
 import Button from 'src/components/Button';
 import Timer from 'src/components/Timer';
 import ResizableSidebar from 'src/components/ResizableSidebar';
-import { AntdDropdown, Skeleton } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
+import { Skeleton } from 'src/components';
 import { Switch } from 'src/components/Switch';
 import { Input } from 'src/components/Input';
 import { Menu } from 'src/components/Menu';
@@ -868,9 +869,12 @@ const SqlEditor: FC<Props> = ({
               <span>
                 <ShareSqlLabQuery queryEditorId={queryEditor.id} />
               </span>
-              <AntdDropdown overlay={renderDropdown()} trigger={['click']}>
+              <Dropdown
+                dropdownRender={() => renderDropdown()}
+                trigger={['click']}
+              >
                 <Icons.MoreHoriz iconColor={theme.colors.grayscale.base} />
-              </AntdDropdown>
+              </Dropdown>
             </div>
           </>
         )}
diff --git a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
index d8f4c8e4ed..76c8b74b19 100644
--- a/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
+++ b/superset-frontend/src/SqlLab/components/SqlEditorTabHeader/index.tsx
@@ -20,7 +20,7 @@ import { useMemo, FC } from 'react';
 
 import { bindActionCreators } from 'redux';
 import { useSelector, useDispatch, shallowEqual } from 'react-redux';
-import { Dropdown } from 'src/components/Dropdown';
+import { MenuDotsDropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import { styled, t, QueryState } from '@superset-ui/core';
 import {
@@ -88,10 +88,10 @@ const SqlEditorTabHeader: FC<Props> = ({ queryEditor }) => {
 
   return (
     <TabTitleWrapper>
-      <Dropdown
+      <MenuDotsDropdown
         trigger={['click']}
         overlay={
-          <Menu style={{ width: 176 }}>
+          <Menu>
             <Menu.Item
               className="close-btn"
               key="1"
diff --git a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
index 7f17b8e327..31714607cc 100644
--- a/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
+++ b/superset-frontend/src/SqlLab/components/TablePreview/index.tsx
@@ -30,11 +30,8 @@ import {
 import AutoSizer from 'react-virtualized-auto-sizer';
 import Icons from 'src/components/Icons';
 import type { SqlLabRootState } from 'src/SqlLab/types';
-import {
-  Skeleton,
-  AntdBreadcrumb as Breadcrumb,
-  AntdDropdown,
-} from 'src/components';
+import { Skeleton, AntdBreadcrumb as Breadcrumb } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import FilterableTable from 'src/components/FilterableTable';
 import Tabs from 'src/components/Tabs';
 import {
@@ -308,8 +305,8 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
       <Title>
         <Icons.Table iconSize="l" />
         {tableName}
-        <AntdDropdown
-          overlay={
+        <Dropdown
+          dropdownRender={() => (
             <Menu
               onClick={({ key }) => {
                 if (key === 'refresh-table') {
@@ -324,7 +321,7 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
               }}
               items={dropdownMenu}
             />
-          }
+          )}
           trigger={['click']}
         >
           <Icons.DownSquareOutlined
@@ -332,7 +329,7 @@ const TablePreview: FC<Props> = ({ dbId, catalog, schema, tableName }) => {
             style={{ marginTop: 2, marginLeft: 4 }}
             aria-label={t('Table actions')}
           />
-        </AntdDropdown>
+        </Dropdown>
       </Title>
       {isMetadataRefreshing ? (
         <Skeleton active />
diff --git a/superset-frontend/src/components/Button/index.tsx b/superset-frontend/src/components/Button/index.tsx
index c38fbc15ca..aa447e13bc 100644
--- a/superset-frontend/src/components/Button/index.tsx
+++ b/superset-frontend/src/components/Button/index.tsx
@@ -67,7 +67,7 @@ const decideType = (buttonStyle: ButtonStyle) => {
     success: 'primary',
     secondary: 'default',
     default: 'default',
-    tertiary: 'dashed',
+    tertiary: 'default',
     dashed: 'dashed',
     link: 'link',
   };
diff --git a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx
index d619dae489..efea8cc23f 100644
--- a/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx
+++ b/superset-frontend/src/components/Chart/ChartContextMenu/ChartContextMenu.tsx
@@ -30,6 +30,7 @@ import { useDispatch, useSelector } from 'react-redux';
 import {
   Behavior,
   BinaryQueryObjectFilterClause,
+  Column,
   ContextMenuFilters,
   ensureIsArray,
   FeatureFlag,
@@ -42,8 +43,11 @@ import {
 import { RootState } from 'src/dashboard/types';
 import { Menu } from 'src/components/Menu';
 import { usePermissions } from 'src/hooks/usePermissions';
-import { AntdDropdown as Dropdown } from 'src/components/index';
+import { Dropdown } from 'src/components/Dropdown';
 import { updateDataMask } from 'src/dataMask/actions';
+import DrillByModal from 'src/components/Chart/DrillBy/DrillByModal';
+import { useVerboseMap } from 'src/hooks/apiResources/datasets';
+import { Dataset } from 'src/components/Chart/types';
 import { DrillDetailMenuItems } from '../DrillDetail';
 import { getMenuAdjustedY } from '../utils';
 import { MenuItemTooltip } from '../DisabledMenuItemTooltip';
@@ -114,8 +118,22 @@ const ChartContextMenu = (
   }>({ clientX: 0, clientY: 0 });
 
   const [drillModalIsOpen, setDrillModalIsOpen] = useState(false);
+  const [drillByColumn, setDrillByColumn] = useState<Column>();
+  const [showDrillByModal, setShowDrillByModal] = useState(false);
+  const [dataset, setDataset] = useState<Dataset>();
+  const verboseMap = useVerboseMap(dataset);
 
-  const menuItems = [];
+  const handleDrillBy = useCallback((column: Column, dataset: Dataset) => {
+    setDrillByColumn(column);
+    setDataset(dataset); // Save dataset when drilling
+    setShowDrillByModal(true);
+  }, []);
+
+  const handleCloseDrillByModal = useCallback(() => {
+    setShowDrillByModal(false);
+  }, []);
+
+  const menuItems: React.JSX.Element[] = [];
 
   const showDrillToDetail =
     isFeatureEnabled(FeatureFlag.DrillToDetail) &&
@@ -249,9 +267,9 @@ const ChartContextMenu = (
         formData={formData}
         contextMenuY={clientY}
         submenuIndex={submenuIndex}
-        canDownload={canDownload}
         open={openKeys.includes('drill-by-submenu')}
         key="drill-by-submenu"
+        onDrillBy={handleDrillBy}
         {...(additionalConfig?.drillBy || {})}
       />,
     );
@@ -286,7 +304,7 @@ const ChartContextMenu = (
   return ReactDOM.createPortal(
     <>
       <Dropdown
-        overlay={
+        dropdownRender={() => (
           <Menu
             className="chart-context-menu"
             data-test="chart-context-menu"
@@ -302,15 +320,15 @@ const ChartContextMenu = (
               <Menu.Item disabled>{t('No actions')}</Menu.Item>
             )}
           </Menu>
-        }
+        )}
         trigger={['click']}
-        onVisibleChange={value => {
+        onOpenChange={value => {
           setVisible(value);
           if (!value) {
             setOpenKeys([]);
           }
         }}
-        visible={visible}
+        open={visible}
       >
         <span
           id={`hidden-span-${id}`}
@@ -335,6 +353,16 @@ const ChartContextMenu = (
           }}
         />
       )}
+      {showDrillByModal && drillByColumn && dataset && filters?.drillBy && (
+        <DrillByModal
+          column={drillByColumn}
+          drillByConfig={filters?.drillBy}
+          formData={formData}
+          onHideModal={handleCloseDrillByModal}
+          dataset={{ ...dataset!, verbose_map: verboseMap }}
+          canDownload={canDownload}
+        />
+      )}
     </>,
     document.body,
   );
diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.test.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.test.tsx
index c874da6854..c494ce52aa 100644
--- a/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.test.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.test.tsx
@@ -74,7 +74,6 @@ const renderMenu = ({
       <DrillByMenuItems
         formData={formData ?? defaultFormData}
         drillByConfig={drillByConfig}
-        canDownload
         open
         {...rest}
       />
diff --git a/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.tsx b/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.tsx
index 47666db780..b054ff02d6 100644
--- a/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.tsx
+++ b/superset-frontend/src/components/Chart/DrillBy/DrillByMenuItems.tsx
@@ -53,10 +53,8 @@ import {
   cachedSupersetGet,
   supersetGetCache,
 } from 'src/utils/cachedSupersetGet';
-import { useVerboseMap } from 'src/hooks/apiResources/datasets';
 import { InputRef } from 'antd-v5';
 import { MenuItemTooltip } from '../DisabledMenuItemTooltip';
-import DrillByModal from './DrillByModal';
 import { getSubmenuYOffset } from '../utils';
 import { MenuItemWithTruncation } from '../MenuItemWithTruncation';
 import { Dataset } from '../types';
@@ -74,8 +72,8 @@ export interface DrillByMenuItemsProps {
   onClick?: (event: MouseEvent) => void;
   openNewModal?: boolean;
   excludedColumns?: Column[];
-  canDownload: boolean;
   open: boolean;
+  onDrillBy?: (column: Column, dataset: Dataset) => void;
 }
 
 const loadDrillByOptions = getExtensionsRegistry().get('load.drillby.options');
@@ -106,8 +104,8 @@ export const DrillByMenuItems = ({
   onClick = () => {},
   excludedColumns,
   openNewModal = true,
-  canDownload,
   open,
+  onDrillBy,
   ...rest
 }: DrillByMenuItemsProps) => {
   const theme = useTheme();
@@ -117,25 +115,20 @@ export const DrillByMenuItems = ({
   const [debouncedSearchInput, setDebouncedSearchInput] = useState('');
   const [dataset, setDataset] = useState<Dataset>();
   const [columns, setColumns] = useState<Column[]>([]);
-  const [showModal, setShowModal] = useState(false);
-  const [currentColumn, setCurrentColumn] = useState();
   const ref = useRef<InputRef>(null);
   const showSearch =
     loadDrillByOptions || columns.length > SHOW_COLUMNS_SEARCH_THRESHOLD;
+
   const handleSelection = useCallback(
     (event, column) => {
       onClick(event);
       onSelection(column, drillByConfig);
-      setCurrentColumn(column);
-      if (openNewModal) {
-        setShowModal(true);
+      if (openNewModal && onDrillBy && dataset) {
+        onDrillBy(column, dataset);
       }
     },
-    [drillByConfig, onClick, onSelection, openNewModal],
+    [drillByConfig, onClick, onSelection, openNewModal, onDrillBy, dataset],
   );
-  const closeModal = useCallback(() => {
-    setShowModal(false);
-  }, []);
 
   useEffect(() => {
     if (open) {
@@ -156,7 +149,6 @@ export const DrillByMenuItems = ({
         ?.behaviors.find(behavior => behavior === Behavior.DrillBy),
     [formData.viz_type],
   );
-  const verboseMap = useVerboseMap(dataset);
 
   useEffect(() => {
     async function loadOptions() {
@@ -275,11 +267,11 @@ export const DrillByMenuItems = ({
     const column = columns[index];
     return (
       <MenuItemWithTruncation
-        key={`drill-by-item-${column.column_name}`}
+        menuKey={`drill-by-item-${column.column_name}`}
         tooltipText={column.verbose_name || column.column_name}
-        {...rest}
         onClick={e => handleSelection(e, column)}
         style={style}
+        {...rest}
       >
         {column.verbose_name || column.column_name}
       </MenuItemWithTruncation>
@@ -289,6 +281,7 @@ export const DrillByMenuItems = ({
   return (
     <>
       <Menu.SubMenu
+        key="drill-by-submenu"
         title={t('Drill by')}
         popupClassName="chart-context-submenu"
         popupOffset={[0, submenuYOffset]}
@@ -349,16 +342,6 @@ export const DrillByMenuItems = ({
           )}
         </div>
       </Menu.SubMenu>
-      {showModal && (
-        <DrillByModal
-          column={currentColumn}
-          drillByConfig={drillByConfig}
-          formData={formData}
-          onHideModal={closeModal}
-          dataset={{ ...dataset!, verbose_map: verboseMap }}
-          canDownload={canDownload}
-        />
-      )}
     </>
   );
 };
diff --git a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
index e20bc0290f..aae82d8309 100644
--- a/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
+++ b/superset-frontend/src/components/Chart/DrillDetail/DrillDetailMenuItems.tsx
@@ -60,8 +60,15 @@ const DISABLED_REASONS = {
   ),
 };
 
-const DisabledMenuItem = ({ children, ...props }: { children: ReactNode }) => (
-  <Menu.Item disabled {...props}>
+const DisabledMenuItem = ({
+  children,
+  menuKey,
+  ...rest
+}: {
+  children: ReactNode;
+  menuKey: string;
+}) => (
+  <Menu.Item disabled key={menuKey} {...rest}>
     <div
       css={css`
         white-space: normal;
@@ -183,39 +190,34 @@ const DrillDetailMenuItems = ({
   }
 
   const drillToDetailMenuItem = drillDisabled ? (
-    <DisabledMenuItem {...props} key="drill-to-detail-disabled">
+    <DisabledMenuItem menuKey="drill-to-detail-disabled" {...props}>
       {DRILL_TO_DETAIL}
       <MenuItemTooltip title={drillDisabled} />
     </DisabledMenuItem>
   ) : (
-    <Menu.Item
-      {...props}
-      key="drill-to-detail"
-      onClick={openModal.bind(null, [])}
-    >
+    <Menu.Item key="drill-to-detail" onClick={openModal.bind(null, [])}>
       {DRILL_TO_DETAIL}
     </Menu.Item>
   );
 
   const drillToDetailByMenuItem = drillByDisabled ? (
-    <DisabledMenuItem {...props} key="drill-to-detail-by-disabled">
+    <DisabledMenuItem menuKey="drill-to-detail-by-disabled" {...props}>
       {DRILL_TO_DETAIL_BY}
       <MenuItemTooltip title={drillByDisabled} />
     </DisabledMenuItem>
   ) : (
     <Menu.SubMenu
-      {...props}
       popupOffset={[0, submenuYOffset]}
       popupClassName="chart-context-submenu"
       title={DRILL_TO_DETAIL_BY}
       key={key}
+      {...props}
     >
       <div data-test="drill-to-detail-by-submenu">
         {filters.map((filter, i) => (
           <MenuItemWithTruncation
-            {...props}
             tooltipText={`${DRILL_TO_DETAIL_BY} ${filter.formattedVal}`}
-            key={`drill-detail-filter-${i}`}
+            menuKey={`drill-detail-filter-${i}`}
             onClick={openModal.bind(null, [filter])}
           >
             {`${DRILL_TO_DETAIL_BY} `}
@@ -224,7 +226,6 @@ const DrillDetailMenuItems = ({
         ))}
         {filters.length > 1 && (
           <Menu.Item
-            {...props}
             key="drill-detail-filter-all"
             onClick={openModal.bind(null, filters)}
           >
diff --git a/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx b/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx
index 9e8802f128..cf77427cc7 100644
--- a/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx
+++ b/superset-frontend/src/components/Chart/MenuItemWithTruncation.tsx
@@ -28,12 +28,15 @@ export type MenuItemWithTruncationProps = {
   children: ReactNode;
   onClick?: MenuItemProps['onClick'];
   style?: CSSProperties;
+  menuKey?: string;
 };
 
 export const MenuItemWithTruncation = ({
   tooltipText,
   children,
-  ...props
+  onClick,
+  style,
+  menuKey,
 }: MenuItemWithTruncationProps) => {
   const [itemRef, itemIsTruncated] = useCSSTextTruncation<HTMLDivElement>();
 
@@ -43,7 +46,9 @@ export const MenuItemWithTruncation = ({
         display: flex;
         line-height: 1.5em;
       `}
-      {...props}
+      eventKey={menuKey}
+      onClick={onClick}
+      style={style}
     >
       <Tooltip title={itemIsTruncated ? tooltipText : null}>
         <div
diff --git a/superset-frontend/src/components/Dropdown/Dropdown.stories.tsx b/superset-frontend/src/components/Dropdown/Dropdown.stories.tsx
index 43d281505a..e30a69a38a 100644
--- a/superset-frontend/src/components/Dropdown/Dropdown.stories.tsx
+++ b/superset-frontend/src/components/Dropdown/Dropdown.stories.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import { Menu } from 'src/components/Menu';
-import { Dropdown, DropdownProps } from '.';
+import { MenuDotsDropdown, MenuDotsDropdownProps } from '.';
 
 export default {
   title: 'Dropdown',
@@ -50,8 +50,8 @@ const customOverlay = (
 export const InteractiveDropdown = ({
   overlayType,
   ...rest
-}: DropdownProps & { overlayType: string }) => (
-  <Dropdown
+}: MenuDotsDropdownProps & { overlayType: string }) => (
+  <MenuDotsDropdown
     {...rest}
     overlay={overlayType === 'custom' ? customOverlay : menu}
   />
diff --git a/superset-frontend/src/components/Dropdown/index.tsx b/superset-frontend/src/components/Dropdown/index.tsx
index ef81bd42cc..1fdca2e75d 100644
--- a/superset-frontend/src/components/Dropdown/index.tsx
+++ b/superset-frontend/src/components/Dropdown/index.tsx
@@ -24,13 +24,10 @@ import {
   cloneElement,
 } from 'react';
 
-import { AntdDropdown } from 'src/components';
-// TODO: @geido - Remove these after dropdown is fully migrated to Antd v5
 import {
-  Dropdown as Antd5Dropdown,
-  DropDownProps as Antd5DropdownProps,
+  Dropdown as AntdDropdown,
+  DropdownProps as AntdDropdownProps,
 } from 'antd-v5';
-import { DropDownProps } from 'antd/lib/dropdown';
 import { styled } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
 
@@ -83,7 +80,8 @@ export enum IconOrientation {
   Vertical = 'vertical',
   Horizontal = 'horizontal',
 }
-export interface DropdownProps extends DropDownProps {
+
+export interface MenuDotsDropdownProps extends AntdDropdownProps {
   overlay: ReactElement;
   iconOrientation?: IconOrientation;
 }
@@ -100,19 +98,19 @@ const RenderIcon = (
   return component;
 };
 
-export const Dropdown = ({
+export const MenuDotsDropdown = ({
   overlay,
   iconOrientation = IconOrientation.Vertical,
   ...rest
-}: DropdownProps) => (
-  <AntdDropdown overlay={overlay} {...rest}>
+}: MenuDotsDropdownProps) => (
+  <AntdDropdown dropdownRender={() => overlay} {...rest}>
     <MenuDotsWrapper data-test="dropdown-trigger">
       {RenderIcon(iconOrientation)}
     </MenuDotsWrapper>
   </AntdDropdown>
 );
 
-export interface NoAnimationDropdownProps extends Antd5DropdownProps {
+export interface NoAnimationDropdownProps extends AntdDropdownProps {
   children: ReactNode;
   onBlur?: (e: FocusEvent<HTMLDivElement>) => void;
   onKeyDown?: (e: KeyboardEvent<HTMLDivElement>) => void;
@@ -126,8 +124,13 @@ export const NoAnimationDropdown = (props: NoAnimationDropdownProps) => {
   });
 
   return (
-    <Antd5Dropdown overlayStyle={props.overlayStyle} {...rest}>
+    <AntdDropdown autoFocus overlayStyle={props.overlayStyle} {...rest}>
       {childrenWithProps}
-    </Antd5Dropdown>
+    </AntdDropdown>
   );
 };
+
+export type DropdownProps = AntdDropdownProps;
+export const Dropdown = (props: DropdownProps) => (
+  <AntdDropdown autoFocus {...props} />
+);
diff --git a/superset-frontend/src/components/DropdownButton/index.tsx b/superset-frontend/src/components/DropdownButton/index.tsx
index 32a7739e3c..84ffd5fe28 100644
--- a/superset-frontend/src/components/DropdownButton/index.tsx
+++ b/superset-frontend/src/components/DropdownButton/index.tsx
@@ -16,90 +16,39 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { ReactNode, ReactElement } from 'react';
+import { type ComponentProps } from 'react';
 
-import { AntdDropdown, AntdTooltip } from 'src/components';
-import { styled } from '@superset-ui/core';
+import { Dropdown } from 'antd-v5';
+import { Tooltip, TooltipPlacement } from 'src/components/Tooltip';
 import { kebabCase } from 'lodash';
 
-const StyledDropdownButton = styled.div`
-  .ant-btn-group {
-    button.ant-btn {
-      background-color: ${({ theme }) => theme.colors.primary.dark1};
-      border-color: transparent;
-      color: ${({ theme }) => theme.colors.grayscale.light5};
-      font-size: 12px;
-      line-height: 13px;
-      outline: none;
-      &:first-of-type {
-        border-radius: ${({ theme }) =>
-          `${theme.gridUnit}px 0 0 ${theme.gridUnit}px`};
-        margin: 0;
-      }
-
-      &:disabled {
-        background-color: ${({ theme }) => theme.colors.grayscale.light2};
-        color: ${({ theme }) => theme.colors.grayscale.base};
-      }
-      &:nth-of-type(2) {
-        margin: 0;
-        border-radius: ${({ theme }) =>
-          `0 ${theme.gridUnit}px ${theme.gridUnit}px 0`};
-        width: ${({ theme }) => theme.gridUnit * 9}px;
-        &:before,
-        &:hover:before {
-          border-left: 1px solid ${({ theme }) => theme.colors.grayscale.light5};
-          content: '';
-          display: block;
-          height: ${({ theme }) => theme.gridUnit * 8}px;
-          margin: 0;
-          position: absolute;
-          width: ${({ theme }) => theme.gridUnit * 0.25}px;
-        }
-
-        &:disabled:before {
-          border-left: 1px solid ${({ theme }) => theme.colors.grayscale.base};
-        }
-      }
-    }
-  }
-`;
-
-export interface DropdownButtonProps {
-  overlay: ReactElement;
+export type DropdownButtonProps = ComponentProps<typeof Dropdown.Button> & {
   tooltip?: string;
-  placement?: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
-  buttonsRender?: ((buttons: ReactNode[]) => ReactNode[]) | undefined;
-}
+  tooltipPlacement?: TooltipPlacement;
+};
 
 export const DropdownButton = ({
-  overlay,
+  dropdownRender,
   tooltip,
-  placement,
+  tooltipPlacement,
+  children,
   ...rest
 }: DropdownButtonProps) => {
-  const buildButton = (
-    props: {
-      buttonsRender?: DropdownButtonProps['buttonsRender'];
-    } = {},
-  ) => (
-    <StyledDropdownButton>
-      <AntdDropdown.Button overlay={overlay} {...rest} {...props} />
-    </StyledDropdownButton>
+  const button = (
+    <Dropdown.Button dropdownRender={dropdownRender} {...rest}>
+      {children}
+    </Dropdown.Button>
   );
   if (tooltip) {
-    return buildButton({
-      buttonsRender: ([leftButton, rightButton]) => [
-        <AntdTooltip
-          placement={placement}
-          id={`${kebabCase(tooltip)}-tooltip`}
-          title={tooltip}
-        >
-          {leftButton}
-        </AntdTooltip>,
-        rightButton,
-      ],
-    });
+    return (
+      <Tooltip
+        placement={tooltipPlacement}
+        id={`${kebabCase(tooltip)}-tooltip`}
+        title={tooltip}
+      >
+        {button}
+      </Tooltip>
+    );
   }
-  return buildButton();
+  return button;
 };
diff --git a/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.stories.tsx b/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.stories.tsx
deleted file mode 100644
index cc12a8ba5b..0000000000
--- a/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.stories.tsx
+++ /dev/null
@@ -1,56 +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 Icons from 'src/components/Icons';
-import DropdownSelectableIcon, { DropDownSelectableProps } from '.';
-
-export default {
-  title: 'DropdownSelectableIcon',
-  component: DropdownSelectableIcon,
-};
-
-export const Component = (props: DropDownSelectableProps) => (
-  <DropdownSelectableIcon
-    {...props}
-    icon={<Icons.Gear name="gear" iconColor="#000000" />}
-  />
-);
-
-Component.args = {
-  info: 'Info go here',
-  selectedKeys: ['vertical'],
-  menuItems: [
-    {
-      key: 'vertical',
-      label: 'Vertical',
-    },
-    {
-      key: 'horizontal',
-      label: 'Horizontal',
-    },
-  ],
-};
-
-Component.argTypes = {
-  onSelect: {
-    action: 'onSelect',
-    table: {
-      disable: true,
-    },
-  },
-};
diff --git a/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.test.tsx b/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.test.tsx
deleted file mode 100644
index cfff77d5ed..0000000000
--- a/superset-frontend/src/components/DropdownSelectableIcon/DropdownSelectableIcon.test.tsx
+++ /dev/null
@@ -1,98 +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 { render, screen, waitFor } from 'spec/helpers/testing-library';
-import Icons from 'src/components/Icons';
-import userEvent from '@testing-library/user-event';
-import DropdownSelectableIcon, { DropDownSelectableProps } from '.';
-
-const mockedProps = {
-  menuItems: [
-    {
-      key: 'vertical',
-      label: 'vertical',
-    },
-    {
-      key: 'horizontal',
-      label: 'horizontal',
-    },
-  ],
-  selectedKeys: [],
-  icon: <Icons.Gear name="gear" />,
-};
-
-const asyncRender = (props: DropDownSelectableProps) =>
-  waitFor(() => render(<DropdownSelectableIcon {...props} />));
-
-const openMenu = () => {
-  userEvent.click(screen.getByRole('img', { name: 'gear' }));
-};
-
-test('should render', async () => {
-  const { container } = await asyncRender(mockedProps);
-  expect(container).toBeInTheDocument();
-});
-
-test('should render the icon', async () => {
-  await asyncRender(mockedProps);
-  expect(screen.getByRole('img', { name: 'gear' })).toBeInTheDocument();
-});
-
-test('should not render the info', async () => {
-  await asyncRender(mockedProps);
-  openMenu();
-  expect(
-    screen.queryByTestId('dropdown-selectable-info'),
-  ).not.toBeInTheDocument();
-});
-
-test('should render the info', async () => {
-  const infoProps = {
-    ...mockedProps,
-    info: 'Test',
-  };
-  await asyncRender(infoProps);
-  openMenu();
-  expect(screen.getByTestId('dropdown-selectable-info')).toBeInTheDocument();
-  expect(screen.getByText('Test')).toBeInTheDocument();
-});
-
-test('should render the menu items', async () => {
-  await asyncRender(mockedProps);
-  openMenu();
-  expect(screen.getAllByRole('menuitem')).toHaveLength(2);
-  expect(screen.getByText('vertical')).toBeInTheDocument();
-  expect(screen.getByText('horizontal')).toBeInTheDocument();
-});
-
-test('should not render any selected menu item', async () => {
-  await asyncRender(mockedProps);
-  openMenu();
-  expect(screen.getAllByRole('menuitem')).toHaveLength(2);
-  expect(screen.queryByRole('img', { name: 'check' })).not.toBeInTheDocument();
-});
-
-test('should render the selected menu items', async () => {
-  const selectedProps = {
-    ...mockedProps,
-    selectedKeys: ['vertical'],
-  };
-  await asyncRender(selectedProps);
-  openMenu();
-  expect(screen.getByRole('img', { name: 'check' })).toBeInTheDocument();
-});
diff --git a/superset-frontend/src/components/DropdownSelectableIcon/index.tsx b/superset-frontend/src/components/DropdownSelectableIcon/index.tsx
deleted file mode 100644
index 8d791929d3..0000000000
--- a/superset-frontend/src/components/DropdownSelectableIcon/index.tsx
+++ /dev/null
@@ -1,177 +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 { addAlpha, styled, useTheme } from '@superset-ui/core';
-import { FC, RefObject, useMemo, ReactNode, useState } from 'react';
-import Icons from 'src/components/Icons';
-import { DropdownButton } from 'src/components/DropdownButton';
-import { DropdownButtonProps } from 'antd/lib/dropdown';
-import { Menu, MenuProps } from 'src/components/Menu';
-
-const { SubMenu } = Menu;
-
-type SubMenuItemProps = { key: string; label: string | ReactNode };
-
-export interface DropDownSelectableProps extends Pick<MenuProps, 'onSelect'> {
-  ref?: RefObject<HTMLDivElement>;
-  icon: ReactNode;
-  info?: string;
-  menuItems: {
-    key: string;
-    label: string | ReactNode;
-    children?: SubMenuItemProps[];
-    divider?: boolean;
-  }[];
-  selectedKeys?: string[];
-}
-
-const StyledDropdownButton = styled(DropdownButton as FC<DropdownButtonProps>)`
-  button.ant-btn:first-of-type {
-    display: none;
-  }
-  > button.ant-btn:nth-of-type(2) {
-    display: inline-flex;
-    background-color: transparent !important;
-    height: unset;
-    padding: 0;
-    border: none;
-    width: auto !important;
-
-    .anticon {
-      line-height: 0;
-    }
-    &:after {
-      box-shadow: none !important;
-    }
-  }
-`;
-
-const StyledMenu = styled(Menu)`
-  ${({ theme }) => `
-    box-shadow:
-        0 3px 6px -4px ${addAlpha(theme.colors.grayscale.dark2, 0.12)},
-        0 6px 16px 0
-      ${addAlpha(theme.colors.grayscale.dark2, 0.08)},
-        0 9px 28px 8px
-      ${addAlpha(theme.colors.grayscale.dark2, 0.05)};
-    .info {
-      font-size: ${theme.typography.sizes.s}px;
-      color: ${theme.colors.grayscale.base};
-      padding: ${theme.gridUnit}px ${theme.gridUnit * 3}px ${
-        theme.gridUnit
-      }px ${theme.gridUnit * 3}px;
-    }
-    .ant-dropdown-menu-item-selected {
-      color: ${theme.colors.grayscale.dark1};
-      background-color: ${theme.colors.primary.light5};
-    }
-  `}
-`;
-
-const StyleMenuItem = styled(Menu.Item)<{ divider?: boolean }>`
-  display: flex;
-  justify-content: space-between;
-  > span {
-    width: 100%;
-  }
-  border-bottom: ${({ divider, theme }) =>
-    divider ? `1px solid ${theme.colors.grayscale.light3};` : 'none;'};
-`;
-
-const StyleSubmenuItem = styled.div`
-  display: flex;
-  justify-content: space-between;
-  width: 100%;
-  > div {
-    flex-grow: 1;
-  }
-`;
-
-export default (props: DropDownSelectableProps) => {
-  const theme = useTheme();
-  const [visible, setVisible] = useState(false);
-  const { icon, info, menuItems, selectedKeys, onSelect } = props;
-
-  const handleVisibleChange = setVisible;
-
-  const handleMenuSelect: MenuProps['onSelect'] = info => {
-    if (onSelect) {
-      onSelect(info);
-    }
-    setVisible(false);
-  };
-  const menuItem = useMemo(
-    () => (label: string | ReactNode, key: string, divider?: boolean) => (
-      <StyleMenuItem key={key} divider={divider}>
-        <StyleSubmenuItem>
-          {label}
-          {selectedKeys?.includes(key) && (
-            <Icons.Check
-              iconColor={theme.colors.primary.base}
-              className="tick-menu-item"
-              iconSize="xl"
-            />
-          )}
-        </StyleSubmenuItem>
-      </StyleMenuItem>
-    ),
-    [selectedKeys, theme.colors.primary.base],
-  );
-
-  const overlayMenu = useMemo(
-    () => (
-      <>
-        {info && (
-          <div className="info" data-test="dropdown-selectable-info">
-            {info}
-          </div>
-        )}
-        <StyledMenu
-          selectedKeys={selectedKeys}
-          onSelect={handleMenuSelect}
-          selectable
-        >
-          {menuItems.map(m =>
-            m.children?.length ? (
-              <SubMenu
-                title={m.label}
-                key={m.key}
-                data-test="dropdown-selectable-icon-submenu"
-              >
-                {m.children.map(s => menuItem(s.label, s.key))}
-              </SubMenu>
-            ) : (
-              menuItem(m.label, m.key, m.divider)
-            ),
-          )}
-        </StyledMenu>
-      </>
-    ),
-    [selectedKeys, onSelect, info, menuItems, menuItem, handleMenuSelect],
-  );
-
-  return (
-    <StyledDropdownButton
-      overlay={overlayMenu}
-      trigger={['click']}
-      icon={icon}
-      visible={visible}
-      onVisibleChange={handleVisibleChange}
-    />
-  );
-};
diff --git a/superset-frontend/src/components/ListViewCard/ListViewCard.stories.tsx b/superset-frontend/src/components/ListViewCard/ListViewCard.stories.tsx
index 38fc7cbe94..61d2c70a31 100644
--- a/superset-frontend/src/components/ListViewCard/ListViewCard.stories.tsx
+++ b/superset-frontend/src/components/ListViewCard/ListViewCard.stories.tsx
@@ -17,7 +17,7 @@
  * under the License.
  */
 import { action } from '@storybook/addon-actions';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import Icons from 'src/components/Icons';
 import FaveStar from 'src/components/FaveStar';
@@ -70,8 +70,8 @@ export const SupersetListViewCard = ({
           saveFaveStar={action('saveFaveStar')}
           isStarred={isStarred}
         />
-        <AntdDropdown
-          overlay={
+        <Dropdown
+          dropdownRender={() => (
             <Menu>
               <Menu.Item role="button" tabIndex={0} onClick={action('Delete')}>
                 <Icons.Trash /> Delete
@@ -80,10 +80,10 @@ export const SupersetListViewCard = ({
                 <Icons.EditAlt /> Edit
               </Menu.Item>
             </Menu>
-          }
+          )}
         >
           <Icons.MoreHoriz />
-        </AntdDropdown>
+        </Dropdown>
       </ListViewCard.Actions>
     }
   />
diff --git a/superset-frontend/src/components/Menu/index.tsx b/superset-frontend/src/components/Menu/index.tsx
index ff2c1c0b6e..5d0f6e0dcc 100644
--- a/superset-frontend/src/components/Menu/index.tsx
+++ b/superset-frontend/src/components/Menu/index.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { addAlpha, styled } from '@superset-ui/core';
+import { styled } from '@superset-ui/core';
 import { ReactElement } from 'react';
 import { Menu as AntdMenu } from 'antd-v5';
 import { MenuProps as AntdMenuProps } from 'antd-v5/es/menu';
@@ -73,23 +73,11 @@ const StyledMenuItem = styled(AntdMenu.Item)`
   }
 `;
 
-// TODO: @geido - Move this to theme after fully migrating dropdown to Antd5
 const StyledMenu = styled(AntdMenu)`
-  ${({ theme }) => `
-    &.antd5-menu-horizontal {
-      background-color: inherit;
-      border-bottom: 1px solid transparent;
-    }
-    &.antd5-menu-vertical,
-    &.ant-dropdown-menu {
-      box-shadow:
-        0 3px 6px -4px ${addAlpha(theme.colors.grayscale.dark2, 0.12)},
-        0 6px 16px 0
-      ${addAlpha(theme.colors.grayscale.dark2, 0.08)},
-        0 9px 28px 8px
-      ${addAlpha(theme.colors.grayscale.dark2, 0.05)};
-    }
-    `}
+  &.antd5-menu-horizontal {
+    background-color: inherit;
+    border-bottom: 1px solid transparent;
+  }
 `;
 
 const StyledNav = styled(AntdMenu)`
@@ -145,11 +133,6 @@ const StyledSubMenu = styled(AntdMenu.SubMenu)`
       transition: all ${({ theme }) => theme.transitionTiming}s;
     }
   }
-
-  .ant-dropdown-menu-submenu-arrow:before,
-  .ant-dropdown-menu-submenu-arrow:after {
-    content: none !important;
-  }
 `;
 
 export type MenuMode = AntdMenuProps['mode'];
diff --git a/superset-frontend/src/components/PageHeaderWithActions/index.tsx b/superset-frontend/src/components/PageHeaderWithActions/index.tsx
index b84e67b31f..68c33d78c7 100644
--- a/superset-frontend/src/components/PageHeaderWithActions/index.tsx
+++ b/superset-frontend/src/components/PageHeaderWithActions/index.tsx
@@ -18,7 +18,7 @@
  */
 import { ReactNode, ReactElement } from 'react';
 import { css, SupersetTheme, t, useTheme } from '@superset-ui/core';
-import { AntdDropdown, AntdDropdownProps } from 'src/components';
+import { Dropdown, DropdownProps } from 'src/components/Dropdown';
 import { TooltipPlacement } from 'src/components/Tooltip';
 import {
   DynamicEditableTitle,
@@ -116,7 +116,7 @@ export type PageHeaderWithActionsProps = {
   titlePanelAdditionalItems: ReactNode;
   rightPanelAdditionalItems: ReactNode;
   additionalActionsMenu: ReactElement;
-  menuDropdownProps: Omit<AntdDropdownProps, 'overlay'>;
+  menuDropdownProps: Omit<DropdownProps, 'overlay'>;
   tooltipProps?: {
     text?: string;
     placement?: TooltipPlacement;
@@ -155,9 +155,9 @@ export const PageHeaderWithActions = ({
         {rightPanelAdditionalItems}
         <div css={additionalActionsContainerStyles}>
           {showMenuDropdown && (
-            <AntdDropdown
+            <Dropdown
               trigger={['click']}
-              overlay={additionalActionsMenu}
+              dropdownRender={() => additionalActionsMenu}
               {...menuDropdownProps}
             >
               <Button
@@ -173,7 +173,7 @@ export const PageHeaderWithActions = ({
                   iconSize="l"
                 />
               </Button>
-            </AntdDropdown>
+            </Dropdown>
           )}
         </div>
       </div>
diff --git a/superset-frontend/src/components/PopoverDropdown/index.tsx b/superset-frontend/src/components/PopoverDropdown/index.tsx
index 9ad507d5f7..41812ea13f 100644
--- a/superset-frontend/src/components/PopoverDropdown/index.tsx
+++ b/superset-frontend/src/components/PopoverDropdown/index.tsx
@@ -19,7 +19,7 @@
 import { Key } from 'react';
 import cx from 'classnames';
 import { styled, useTheme } from '@superset-ui/core';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import Icons from 'src/components/Icons';
 
@@ -89,10 +89,10 @@ const PopoverDropdown = (props: PopoverDropdownProps) => {
   const theme = useTheme();
   const selected = options.find(opt => opt.value === value);
   return (
-    <AntdDropdown
+    <Dropdown
       trigger={['click']}
       overlayStyle={{ zIndex: theme.zIndex.max }}
-      overlay={
+      dropdownRender={() => (
         <Menu onClick={({ key }: HandleSelectProps) => onChange(key)}>
           {options.map(option => (
             <MenuItem
@@ -106,7 +106,7 @@ const PopoverDropdown = (props: PopoverDropdownProps) => {
             </MenuItem>
           ))}
         </Menu>
-      }
+      )}
     >
       <div role="button" css={{ display: 'flex', alignItems: 'center' }}>
         {selected && renderButton(selected)}
@@ -115,7 +115,7 @@ const PopoverDropdown = (props: PopoverDropdownProps) => {
           css={{ marginTop: theme.gridUnit * 0.5 }}
         />
       </div>
-    </AntdDropdown>
+    </Dropdown>
   );
 };
 
diff --git a/superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx b/superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx
index 1c3127504f..5b404ccbdd 100644
--- a/superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx
+++ b/superset-frontend/src/components/Table/cell-renderers/ActionCell/index.tsx
@@ -18,7 +18,7 @@
  */
 import { useState, useEffect } from 'react';
 import { styled } from '@superset-ui/core';
-import { Dropdown, IconOrientation } from 'src/components/Dropdown';
+import { MenuDotsDropdown, IconOrientation } from 'src/components/Dropdown';
 import { Menu, MenuProps } from 'src/components/Menu';
 
 /**
@@ -126,7 +126,7 @@ export function ActionCell(props: ActionCellProps) {
     setVisible(flag);
   };
   return (
-    <Dropdown
+    <MenuDotsDropdown
       iconOrientation={IconOrientation.Horizontal}
       onVisibleChange={handleVisibleChange}
       trigger={['click']}
diff --git a/superset-frontend/src/components/Tooltip/index.tsx b/superset-frontend/src/components/Tooltip/index.tsx
index 252aac3402..615ad802e7 100644
--- a/superset-frontend/src/components/Tooltip/index.tsx
+++ b/superset-frontend/src/components/Tooltip/index.tsx
@@ -23,14 +23,12 @@ import { TooltipProps, TooltipPlacement } from 'antd-v5/lib/tooltip';
 export { TooltipProps, TooltipPlacement };
 
 export const Tooltip = ({ overlayStyle, ...props }: TooltipProps) => (
-  <>
-    <AntdTooltip
-      styles={{
-        body: { overflow: 'hidden', textOverflow: 'ellipsis' },
-        root: overlayStyle ?? {},
-      }}
-      color={`${supersetTheme.colors.grayscale.dark2}e6`}
-      {...props}
-    />
-  </>
+  <AntdTooltip
+    styles={{
+      body: { overflow: 'hidden', textOverflow: 'ellipsis' },
+      root: overlayStyle ?? {},
+    }}
+    color={`${supersetTheme.colors.grayscale.dark2}e6`}
+    {...props}
+  />
 );
diff --git a/superset-frontend/src/components/index.ts b/superset-frontend/src/components/index.ts
index cb949ef5c3..9d9ad65ebe 100644
--- a/superset-frontend/src/components/index.ts
+++ b/superset-frontend/src/components/index.ts
@@ -56,7 +56,6 @@ export {
   Card as AntdCard,
   Checkbox as AntdCheckbox,
   Collapse as AntdCollapse,
-  Dropdown as AntdDropdown,
   Form as AntdForm,
   Input as AntdInput,
   Select as AntdSelect,
@@ -67,5 +66,4 @@ export {
 
 // Exported types
 export type { FormInstance } from 'antd/lib/form';
-export type { DropDownProps as AntdDropdownProps } from 'antd/lib/dropdown';
 export type { RadioChangeEvent } from 'antd/lib/radio';
diff --git a/superset-frontend/src/dashboard/components/CssEditor/index.tsx b/superset-frontend/src/dashboard/components/CssEditor/index.tsx
index e2748c8b0b..2b9b990704 100644
--- a/superset-frontend/src/dashboard/components/CssEditor/index.tsx
+++ b/superset-frontend/src/dashboard/components/CssEditor/index.tsx
@@ -17,8 +17,8 @@
  * under the License.
  */
 import { Key, ReactNode, PureComponent } from 'react';
+import { Dropdown } from 'src/components/Dropdown';
 import rison from 'rison';
-import { AntdDropdown } from 'src/components';
 import { Menu } from 'src/components/Menu';
 import Button from 'src/components/Button';
 import { t, styled, SupersetClient } from '@superset-ui/core';
@@ -115,9 +115,9 @@ class CssEditor extends PureComponent<CssEditorProps, CssEditorState> {
         </Menu>
       );
       return (
-        <AntdDropdown overlay={menu} placement="bottomRight">
+        <Dropdown dropdownRender={() => menu} placement="bottomRight">
           <Button>{t('Load a CSS template')}</Button>
-        </AntdDropdown>
+        </Dropdown>
       );
     }
     return null;
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx
index 1a0b73165a..877d9203a0 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx
@@ -53,9 +53,6 @@ jest.mock('src/components/Select/Select', () => () => (
 jest.mock('src/components/Select/AsyncSelect', () => () => (
   <div data-test="mock-async-select" />
 ));
-jest.mock('src/dashboard/components/Header/HeaderActionsDropdown', () => () => (
-  <div data-test="mock-header-actions-dropdown" />
-));
 jest.mock('src/components/PageHeaderWithActions', () => ({
   PageHeaderWithActions: () => (
     <div data-test="mock-page-header-with-actions" />
diff --git a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx b/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx
deleted file mode 100644
index 090f12a2bd..0000000000
--- a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/HeaderActionsDropdown.test.tsx
+++ /dev/null
@@ -1,260 +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 { shallow } from 'enzyme';
-import sinon from 'sinon';
-import { render, screen } from 'spec/helpers/testing-library';
-import userEvent from '@testing-library/user-event';
-import fetchMock from 'fetch-mock';
-import { HeaderDropdownProps } from 'src/dashboard/components/Header/types';
-import injectCustomCss from 'src/dashboard/util/injectCustomCss';
-import { HeaderActionsDropdown } from '.';
-
-const createProps = (): HeaderDropdownProps => ({
-  addSuccessToast: jest.fn(),
-  addDangerToast: jest.fn(),
-  customCss: '.ant-menu {margin-left: 100px;}',
-  dashboardId: 1,
-  dashboardInfo: {
-    id: 1,
-    dash_edit_perm: true,
-    dash_save_perm: true,
-    userId: '1',
-    metadata: {},
-    common: {
-      conf: {
-        DASHBOARD_AUTO_REFRESH_INTERVALS: [
-          [0, "Don't refresh"],
-          [10, '10 seconds'],
-        ],
-      },
-    },
-  },
-  dashboardTitle: 'Title',
-  editMode: false,
-  expandedSlices: {},
-  forceRefreshAllCharts: jest.fn(),
-  hasUnsavedChanges: false,
-  isLoading: false,
-  layout: {},
-  onChange: jest.fn(),
-  onSave: jest.fn(),
-  refreshFrequency: 200,
-  setRefreshFrequency: jest.fn(),
-  shouldPersistRefreshFrequency: false,
-  showPropertiesModal: jest.fn(),
-  startPeriodicRender: jest.fn(),
-  updateCss: jest.fn(),
-  userCanEdit: false,
-  userCanSave: false,
-  userCanShare: false,
-  userCanCurate: false,
-  lastModifiedTime: 0,
-  isDropdownVisible: true,
-  setIsDropdownVisible: jest.fn(),
-  directPathToChild: [],
-  manageEmbedded: jest.fn(),
-  dataMask: {},
-  logEvent: jest.fn(),
-  refreshLimit: 0,
-  refreshWarning: '',
-});
-
-const editModeOnProps = {
-  ...createProps(),
-  editMode: true,
-};
-
-const editModeOnWithFilterScopesProps = {
-  ...editModeOnProps,
-  dashboardInfo: {
-    ...editModeOnProps.dashboardInfo,
-    metadata: {
-      filter_scopes: {
-        '1': { scopes: ['ROOT_ID'], immune: [] },
-      },
-    },
-  },
-};
-
-const guestUserProps = {
-  ...createProps(),
-  dashboardInfo: {
-    ...createProps().dashboardInfo,
-    userId: undefined,
-  },
-};
-
-function setup(props: HeaderDropdownProps) {
-  return render(
-    <div className="dashboard-header">
-      <HeaderActionsDropdown {...props} />
-    </div>,
-    { useRedux: true },
-  );
-}
-
-fetchMock.get('glob:*/csstemplateasyncmodelview/api/read', {});
-
-test('should render', () => {
-  const mockedProps = createProps();
-  const { container } = setup(mockedProps);
-  expect(container).toBeInTheDocument();
-});
-
-test('should render the Download dropdown button when not in edit mode', () => {
-  const mockedProps = createProps();
-  setup(mockedProps);
-  expect(
-    screen.getByRole('menuitem', { name: 'Download' }),
-  ).toBeInTheDocument();
-});
-
-test('should render the menu items', async () => {
-  const mockedProps = createProps();
-  setup(mockedProps);
-  expect(screen.getAllByRole('menuitem')).toHaveLength(4);
-  expect(screen.getByText('Refresh dashboard')).toBeInTheDocument();
-  expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
-  expect(screen.getByText('Enter fullscreen')).toBeInTheDocument();
-  expect(screen.getByText('Download')).toBeInTheDocument();
-});
-
-test('should render the menu items in edit mode', async () => {
-  setup(editModeOnProps);
-  expect(screen.getAllByRole('menuitem')).toHaveLength(4);
-  expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
-  expect(screen.getByText('Edit properties')).toBeInTheDocument();
-  expect(screen.getByText('Edit CSS')).toBeInTheDocument();
-  expect(screen.getByText('Download')).toBeInTheDocument();
-});
-
-test('should render the menu items in Embedded mode', async () => {
-  setup(guestUserProps);
-  expect(screen.getAllByRole('menuitem')).toHaveLength(3);
-  expect(screen.getByText('Refresh dashboard')).toBeInTheDocument();
-  expect(screen.getByText('Download')).toBeInTheDocument();
-  expect(screen.getByText('Set auto-refresh interval')).toBeInTheDocument();
-});
-
-test('should not render filter mapping in edit mode if explicit filter scopes undefined', async () => {
-  setup(editModeOnProps);
-  expect(screen.queryByText('Set filter mapping')).not.toBeInTheDocument();
-});
-
-test('should render filter mapping in edit mode if explicit filter scopes defined', async () => {
-  setup(editModeOnWithFilterScopesProps);
-  expect(screen.getByText('Set filter mapping')).toBeInTheDocument();
-});
-
-test('should show the share actions', async () => {
-  const mockedProps = createProps();
-  const canShareProps = {
-    ...mockedProps,
-    userCanShare: true,
-  };
-  setup(canShareProps);
-
-  expect(screen.getByText('Share')).toBeInTheDocument();
-});
-
-test('should render the "Save as" menu item when user can save', async () => {
-  const mockedProps = createProps();
-  const canSaveProps = {
-    ...mockedProps,
-    userCanSave: true,
-  };
-  setup(canSaveProps);
-  expect(screen.getByText('Save as')).toBeInTheDocument();
-});
-
-test('should NOT render the "Save as" menu item when user cannot save', async () => {
-  const mockedProps = createProps();
-  setup(mockedProps);
-  expect(screen.queryByText('Save as')).not.toBeInTheDocument();
-});
-
-test('should render the "Refresh dashboard" menu item as disabled when loading', async () => {
-  const mockedProps = createProps();
-  const loadingProps = {
-    ...mockedProps,
-    isLoading: true,
-  };
-  setup(loadingProps);
-  expect(screen.getByText('Refresh dashboard').parentElement).toHaveClass(
-    'ant-menu-item-disabled',
-  );
-});
-
-test('should NOT render the "Refresh dashboard" menu item as disabled', async () => {
-  const mockedProps = createProps();
-  setup(mockedProps);
-  expect(screen.getByText('Refresh dashboard')).not.toHaveClass(
-    'ant-menu-item-disabled',
-  );
-});
-
-test('should render with custom css', () => {
-  const mockedProps = createProps();
-  const { customCss } = mockedProps;
-  setup(mockedProps);
-  injectCustomCss(customCss);
-  expect(screen.getByTestId('header-actions-menu')).toHaveStyle(
-    'margin-left: 100px',
-  );
-});
-
-test('should refresh the charts', async () => {
-  const mockedProps = createProps();
-  setup(mockedProps);
-  userEvent.click(screen.getByText('Refresh dashboard'));
-  expect(mockedProps.forceRefreshAllCharts).toHaveBeenCalledTimes(1);
-  expect(mockedProps.addSuccessToast).toHaveBeenCalledTimes(1);
-});
-
-test('should show the properties modal', async () => {
-  setup(editModeOnProps);
-  userEvent.click(screen.getByText('Edit properties'));
-  expect(editModeOnProps.showPropertiesModal).toHaveBeenCalledTimes(1);
-});
-
-describe('UNSAFE_componentWillReceiveProps', () => {
-  let wrapper: any;
-
-  const mockedProps = createProps();
-  const props = { ...mockedProps, customCss: '' };
-  beforeEach(() => {
-    wrapper = shallow(<HeaderActionsDropdown {...props} />);
-    wrapper.setState({ css: props.customCss });
-    sinon.spy(wrapper.instance(), 'setState');
-  });
-
-  afterEach(() => {
-    wrapper.instance().setState.restore();
-  });
-
-  it('css should update state and inject custom css', () => {
-    wrapper.instance().UNSAFE_componentWillReceiveProps({
-      ...props,
-      customCss: mockedProps.customCss,
-    });
-    expect(wrapper.instance().setState.calledOnce).toBe(true);
-    const stateKeys = Object.keys(wrapper.instance().setState.lastCall.args[0]);
-    expect(stateKeys).toContain('css');
-  });
-});
diff --git a/superset-frontend/src/dashboard/components/Header/index.jsx b/superset-frontend/src/dashboard/components/Header/index.jsx
index 5ce86cc52f..1324d785cd 100644
--- a/superset-frontend/src/dashboard/components/Header/index.jsx
+++ b/superset-frontend/src/dashboard/components/Header/index.jsx
@@ -40,7 +40,6 @@ import { Button } from 'src/components/';
 import { findPermission } from 'src/utils/findPermission';
 import { Tooltip } from 'src/components/Tooltip';
 import { safeStringify } from 'src/utils/safeStringify';
-import ConnectedHeaderActionsDropdown from 'src/dashboard/components/Header/HeaderActionsDropdown';
 import PublishedStatus from 'src/dashboard/components/PublishedStatus';
 import UndoRedoKeyListeners from 'src/dashboard/components/UndoRedoKeyListeners';
 import PropertiesModal from 'src/dashboard/components/PropertiesModal';
@@ -53,6 +52,9 @@ import {
 import setPeriodicRunner, {
   stopPeriodicRender,
 } from 'src/dashboard/util/setPeriodicRunner';
+import ReportModal from 'src/features/reports/ReportModal';
+import DeleteModal from 'src/components/DeleteModal';
+import { deleteActiveReport } from 'src/features/reports/ReportModal/actions';
 import { PageHeaderWithActions } from 'src/components/PageHeaderWithActions';
 import DashboardEmbedModal from '../EmbeddedModal';
 import OverwriteConfirm from '../OverwriteConfirm';
@@ -88,6 +90,7 @@ import { dashboardInfoChanged } from '../../actions/dashboardInfo';
 import isDashboardLoading from '../../util/isDashboardLoading';
 import { useChartIds } from '../../util/charts/useChartIds';
 import { useDashboardMetadataBar } from './useDashboardMetadataBar';
+import { useHeaderActionsMenu } from './useHeaderActionsDropdownMenu';
 
 const extensionsRegistry = getExtensionsRegistry();
 
@@ -160,8 +163,9 @@ const Header = () => {
   const [emphasizeUndo, setEmphasizeUndo] = useState(false);
   const [emphasizeRedo, setEmphasizeRedo] = useState(false);
   const [showingPropertiesModal, setShowingPropertiesModal] = useState(false);
-  const [isDropdownVisible, setIsDropdownVisible] = useState(false);
   const [showingEmbedModal, setShowingEmbedModal] = useState(false);
+  const [showingReportModal, setShowingReportModal] = useState(false);
+  const [currentReportDeleting, setCurrentReportDeleting] = useState(null);
   const dashboardInfo = useSelector(state => state.dashboardInfo);
   const layout = useSelector(state => state.dashboardLayout.present);
   const undoLength = useSelector(state => state.dashboardLayout.past.length);
@@ -348,10 +352,6 @@ const Header = () => {
     [boundActionCreators, dashboardTitle],
   );
 
-  const setDropdownVisible = useCallback(visible => {
-    setIsDropdownVisible(visible);
-  }, []);
-
   const handleCtrlY = useCallback(() => {
     boundActionCreators.onRedo();
     setEmphasizeRedo(true);
@@ -475,6 +475,14 @@ const Header = () => {
     setShowingEmbedModal(false);
   }, []);
 
+  const showReportModal = useCallback(() => {
+    setShowingReportModal(true);
+  }, []);
+
+  const hideReportModal = useCallback(() => {
+    setShowingReportModal(false);
+  }, []);
+
   const metadataBar = useDashboardMetadataBar(dashboardInfo);
 
   const userCanEdit =
@@ -689,92 +697,47 @@ const Header = () => {
     ],
   );
 
-  const menuDropdownProps = useMemo(
-    () => ({
-      getPopupContainer: triggerNode =>
-        triggerNode.closest('.header-with-actions'),
-      visible: isDropdownVisible,
-      onVisibleChange: setDropdownVisible,
-    }),
-    [isDropdownVisible, setDropdownVisible],
-  );
-
-  const additionalActionsMenu = useMemo(
-    () => (
-      <ConnectedHeaderActionsDropdown
-        addSuccessToast={boundActionCreators.addSuccessToast}
-        addDangerToast={boundActionCreators.addDangerToast}
-        dashboardId={dashboardInfo.id}
-        dashboardTitle={dashboardTitle}
-        dashboardInfo={dashboardInfo}
-        dataMask={dataMask}
-        layout={layout}
-        expandedSlices={expandedSlices}
-        customCss={customCss}
-        colorNamespace={colorNamespace}
-        colorScheme={colorScheme}
-        onSave={boundActionCreators.onSave}
-        onChange={boundActionCreators.onChange}
-        forceRefreshAllCharts={forceRefresh}
-        startPeriodicRender={startPeriodicRender}
-        refreshFrequency={refreshFrequency}
-        shouldPersistRefreshFrequency={shouldPersistRefreshFrequency}
-        setRefreshFrequency={boundActionCreators.setRefreshFrequency}
-        updateCss={boundActionCreators.updateCss}
-        editMode={editMode}
-        hasUnsavedChanges={hasUnsavedChanges}
-        userCanEdit={userCanEdit}
-        userCanShare={userCanShare}
-        userCanSave={userCanSaveAs}
-        userCanCurate={userCanCurate}
-        isLoading={isLoading}
-        showPropertiesModal={showPropertiesModal}
-        manageEmbedded={showEmbedModal}
-        refreshLimit={refreshLimit}
-        refreshWarning={refreshWarning}
-        lastModifiedTime={actualLastModifiedTime}
-        isDropdownVisible={isDropdownVisible}
-        setIsDropdownVisible={setDropdownVisible}
-        logEvent={boundActionCreators.logEvent}
-      />
-    ),
-    [
-      actualLastModifiedTime,
-      boundActionCreators.addDangerToast,
-      boundActionCreators.addSuccessToast,
-      boundActionCreators.logEvent,
-      boundActionCreators.onChange,
-      boundActionCreators.onSave,
-      boundActionCreators.setRefreshFrequency,
-      boundActionCreators.updateCss,
-      colorNamespace,
-      colorScheme,
-      customCss,
-      dashboardInfo,
-      dashboardTitle,
-      dataMask,
-      editMode,
-      expandedSlices,
-      forceRefresh,
-      hasUnsavedChanges,
-      isDropdownVisible,
-      isLoading,
-      layout,
-      refreshFrequency,
-      refreshLimit,
-      refreshWarning,
-      setDropdownVisible,
-      shouldPersistRefreshFrequency,
-      showEmbedModal,
-      showPropertiesModal,
-      startPeriodicRender,
-      userCanCurate,
-      userCanEdit,
-      userCanSaveAs,
-      userCanShare,
-    ],
-  );
+  const handleReportDelete = async report => {
+    await dispatch(deleteActiveReport(report));
+    setCurrentReportDeleting(null);
+  };
 
+  const [menu, isDropdownVisible, setIsDropdownVisible] = useHeaderActionsMenu({
+    addSuccessToast: boundActionCreators.addSuccessToast,
+    addDangerToast: boundActionCreators.addDangerToast,
+    dashboardInfo,
+    dashboardId: dashboardInfo.id,
+    dashboardTitle,
+    dataMask,
+    layout,
+    expandedSlices,
+    customCss,
+    colorNamespace,
+    colorScheme,
+    onSave: boundActionCreators.onSave,
+    onChange: boundActionCreators.onChange,
+    forceRefreshAllCharts: forceRefresh,
+    startPeriodicRender,
+    refreshFrequency,
+    shouldPersistRefreshFrequency,
+    setRefreshFrequency: boundActionCreators.setRefreshFrequency,
+    updateCss: boundActionCreators.updateCss,
+    editMode,
+    hasUnsavedChanges,
+    userCanEdit,
+    userCanShare,
+    userCanSave: userCanSaveAs,
+    userCanCurate,
+    isLoading,
+    showReportModal,
+    showPropertiesModal,
+    setCurrentReportDeleting,
+    manageEmbedded: showEmbedModal,
+    refreshLimit,
+    refreshWarning,
+    lastModifiedTime: actualLastModifiedTime,
+    logEvent: boundActionCreators.logEvent,
+  });
   return (
     <div
       css={headerContainerStyle}
@@ -788,8 +751,11 @@ const Header = () => {
         faveStarProps={faveStarProps}
         titlePanelAdditionalItems={titlePanelAdditionalItems}
         rightPanelAdditionalItems={rightPanelAdditionalItems}
-        menuDropdownProps={menuDropdownProps}
-        additionalActionsMenu={additionalActionsMenu}
+        menuDropdownProps={{
+          open: isDropdownVisible,
+          onOpenChange: setIsDropdownVisible,
+        }}
+        additionalActionsMenu={menu}
         showFaveStar={user?.userId && dashboardInfo?.id}
         showTitlePanelItems
       />
@@ -806,6 +772,32 @@ const Header = () => {
         />
       )}
 
+      <ReportModal
+        userId={user.userId}
+        show={showingReportModal}
+        onHide={hideReportModal}
+        userEmail={user.email}
+        dashboardId={dashboardInfo.id}
+        creationMethod="dashboards"
+      />
+
+      {currentReportDeleting && (
+        <DeleteModal
+          description={t(
+            'This action will permanently delete %s.',
+            currentReportDeleting?.name,
+          )}
+          onConfirm={() => {
+            if (currentReportDeleting) {
+              handleReportDelete(currentReportDeleting);
+            }
+          }}
+          onHide={() => setCurrentReportDeleting(null)}
+          open
+          title={t('Delete Report?')}
+        />
+      )}
+
       <OverwriteConfirm />
 
       {userCanCurate && (
@@ -817,7 +809,7 @@ const Header = () => {
       )}
       <Global
         styles={css`
-          .ant-menu-vertical {
+          .antd5-menu-vertical {
             border-right: none;
           }
         `}
diff --git a/superset-frontend/src/dashboard/components/Header/types.ts b/superset-frontend/src/dashboard/components/Header/types.ts
index caf087404f..d69a33e87b 100644
--- a/superset-frontend/src/dashboard/components/Header/types.ts
+++ b/superset-frontend/src/dashboard/components/Header/types.ts
@@ -19,6 +19,7 @@
 
 import { Layout } from 'src/dashboard/types';
 import { ChartState } from 'src/explore/types';
+import { AlertObject } from 'src/features/alerts/types';
 
 interface DashboardInfo {
   id: number;
@@ -60,11 +61,11 @@ export interface HeaderDropdownProps {
   dataMask: any;
   lastModifiedTime: number;
   logEvent: () => void;
-  setIsDropdownVisible: (visible: boolean) => void;
-  isDropdownVisible: boolean;
   refreshLimit: number;
   refreshWarning: string;
   directPathToChild: string[];
+  showReportModal: () => void;
+  setCurrentReportDeleting: (alert: AlertObject | null) => void;
 }
 
 export interface HeaderProps {
diff --git a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.tsx b/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx
similarity index 53%
rename from superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.tsx
rename to superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx
index 6bc712f6b8..420e91e552 100644
--- a/superset-frontend/src/dashboard/components/Header/HeaderActionsDropdown/index.tsx
+++ b/superset-frontend/src/dashboard/components/Header/useHeaderActionsDropdownMenu.tsx
@@ -16,11 +16,11 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { PureComponent } from 'react';
-import { isEmpty } from 'lodash';
-import { connect } from 'react-redux';
-import { t } from '@superset-ui/core';
+import { useState, useEffect, useCallback, useMemo } from 'react';
+import { useSelector } from 'react-redux';
 import { Menu } from 'src/components/Menu';
+import { t } from '@superset-ui/core';
+import { isEmpty } from 'lodash';
 import { URL_PARAMS } from 'src/constants';
 import ShareMenuItems from 'src/dashboard/components/menu/ShareMenuItems';
 import DownloadMenuItems from 'src/dashboard/components/menu/DownloadMenuItems';
@@ -37,158 +37,156 @@ import { getUrlParam } from 'src/utils/urlUtils';
 import { MenuKeys, RootState } from 'src/dashboard/types';
 import { HeaderDropdownProps } from 'src/dashboard/components/Header/types';
 
-const mapStateToProps = (state: RootState) => ({
-  directPathToChild: state.dashboardState.directPathToChild,
-});
-
-interface HeaderActionsDropdownState {
-  css: string;
-  showReportSubMenu: boolean | null;
-}
-
-export class HeaderActionsDropdown extends PureComponent<
-  HeaderDropdownProps,
-  HeaderActionsDropdownState
-> {
-  static defaultProps = {
-    colorNamespace: undefined,
-    colorScheme: undefined,
-    refreshLimit: 0,
-    refreshWarning: null,
-  };
-
-  constructor(props: HeaderDropdownProps) {
-    super(props);
-    this.state = {
-      css: props.customCss || '',
-      showReportSubMenu: null,
-    };
-  }
-
-  UNSAFE_componentWillReceiveProps(nextProps: HeaderDropdownProps) {
-    if (this.props.customCss !== nextProps.customCss) {
-      this.setState({ css: nextProps.customCss }, () => {
-        injectCustomCss(nextProps.customCss);
-      });
+export const useHeaderActionsMenu = ({
+  customCss,
+  dashboardId,
+  dashboardInfo,
+  refreshFrequency,
+  shouldPersistRefreshFrequency,
+  editMode,
+  colorNamespace,
+  colorScheme,
+  layout,
+  expandedSlices,
+  onSave,
+  userCanEdit,
+  userCanShare,
+  userCanSave,
+  userCanCurate,
+  isLoading,
+  refreshLimit,
+  refreshWarning,
+  lastModifiedTime,
+  addSuccessToast,
+  addDangerToast,
+  forceRefreshAllCharts,
+  showPropertiesModal,
+  showReportModal,
+  manageEmbedded,
+  onChange,
+  updateCss,
+  startPeriodicRender,
+  setRefreshFrequency,
+  dashboardTitle,
+  logEvent,
+  setCurrentReportDeleting,
+}: HeaderDropdownProps) => {
+  const [css, setCss] = useState(customCss || '');
+  const [showReportSubMenu, setShowReportSubMenu] = useState<boolean | null>(
+    null,
+  );
+  const [isDropdownVisible, setIsDropdownVisible] = useState(false);
+  const directPathToChild = useSelector(
+    (state: RootState) => state.dashboardState.directPathToChild,
+  );
+  useEffect(() => {
+    if (customCss !== css) {
+      setCss(customCss || '');
+      injectCustomCss(customCss);
     }
-  }
+  }, [css, customCss]);
 
-  setShowReportSubMenu = (show: boolean) => {
-    this.setState({ showReportSubMenu: show });
-  };
+  const handleMenuClick = useCallback(
+    ({ key }: { key: string }) => {
+      switch (key) {
+        case MenuKeys.RefreshDashboard:
+          forceRefreshAllCharts();
+          addSuccessToast(t('Refreshing charts'));
+          break;
+        case MenuKeys.EditProperties:
+          showPropertiesModal();
+          break;
+        case MenuKeys.ToggleFullscreen: {
+          const url = getDashboardUrl({
+            pathname: window.location.pathname,
+            filters: getActiveFilters(),
+            hash: window.location.hash,
+            standalone: getUrlParam(URL_PARAMS.standalone),
+          });
+          window.location.replace(url);
+          break;
+        }
+        case MenuKeys.ManageEmbedded:
+          manageEmbedded();
+          break;
+        default:
+          break;
+      }
+      setIsDropdownVisible(false);
+    },
+    [
+      forceRefreshAllCharts,
+      addSuccessToast,
+      showPropertiesModal,
+      manageEmbedded,
+    ],
+  );
 
-  changeCss = (css: string) => {
-    this.props.onChange();
-    this.props.updateCss(css);
-  };
+  const changeCss = useCallback(
+    (newCss: string) => {
+      onChange();
+      updateCss(newCss);
+    },
+    [onChange, updateCss],
+  );
 
-  changeRefreshInterval = (refreshInterval: number, isPersistent: boolean) => {
-    this.props.setRefreshFrequency(refreshInterval, isPersistent);
-    this.props.startPeriodicRender(refreshInterval * 1000);
-  };
+  const changeRefreshInterval = useCallback(
+    (refreshInterval: number, isPersistent: boolean) => {
+      setRefreshFrequency(refreshInterval, isPersistent);
+      startPeriodicRender(refreshInterval * 1000);
+    },
+    [setRefreshFrequency, startPeriodicRender],
+  );
 
-  handleMenuClick = ({ key }: Record<string, any>) => {
-    switch (key) {
-      case MenuKeys.RefreshDashboard:
-        this.props.forceRefreshAllCharts();
-        this.props.addSuccessToast(t('Refreshing charts'));
-        break;
-      case MenuKeys.EditProperties:
-        this.props.showPropertiesModal();
-        break;
-      case MenuKeys.ToggleFullscreen: {
-        const url = getDashboardUrl({
-          pathname: window.location.pathname,
-          filters: getActiveFilters(),
-          hash: window.location.hash,
-          standalone: getUrlParam(URL_PARAMS.standalone),
-        });
-        window.location.replace(url);
-        break;
-      }
-      case MenuKeys.ManageEmbedded: {
-        this.props.manageEmbedded();
-        break;
-      }
-      default:
-        break;
-    }
-  };
+  const emailSubject = useMemo(
+    () => `${t('Superset dashboard')} ${dashboardTitle}`,
+    [dashboardTitle],
+  );
 
-  render() {
-    const {
-      dashboardTitle,
-      dashboardId,
-      dashboardInfo,
-      refreshFrequency,
-      shouldPersistRefreshFrequency,
-      editMode,
-      customCss,
-      colorNamespace,
-      colorScheme,
-      layout,
-      expandedSlices,
-      onSave,
-      userCanEdit,
-      userCanShare,
-      userCanSave,
-      userCanCurate,
-      isLoading,
-      refreshLimit,
-      refreshWarning,
-      lastModifiedTime,
-      addSuccessToast,
-      addDangerToast,
-      setIsDropdownVisible,
-      isDropdownVisible,
-      directPathToChild,
-      ...rest
-    } = this.props;
+  const url = useMemo(
+    () =>
+      getDashboardUrl({
+        pathname: window.location.pathname,
+        filters: getActiveFilters(),
+        hash: window.location.hash,
+      }),
+    [],
+  );
 
-    const emailTitle = t('Superset dashboard');
-    const emailSubject = `${emailTitle} ${dashboardTitle}`;
-    const emailBody = t('Check out this dashboard: ');
+  const dashboardComponentId = useMemo(
+    () => [...(directPathToChild || [])].pop(),
+    [directPathToChild],
+  );
 
+  const menu = useMemo(() => {
     const isEmbedded = !dashboardInfo?.userId;
-
-    const url = getDashboardUrl({
-      pathname: window.location.pathname,
-      filters: getActiveFilters(),
-      hash: window.location.hash,
-    });
-
     const refreshIntervalOptions =
       dashboardInfo.common?.conf?.DASHBOARD_AUTO_REFRESH_INTERVALS;
 
-    const dashboardComponentId = [...(directPathToChild || [])].pop();
-
     return (
-      <Menu selectable={false} data-test="header-actions-menu" {...rest}>
+      <Menu
+        selectable={false}
+        data-test="header-actions-menu"
+        onClick={handleMenuClick}
+      >
         {!editMode && (
           <Menu.Item
             key={MenuKeys.RefreshDashboard}
             data-test="refresh-dashboard-menu-item"
             disabled={isLoading}
-            onClick={this.handleMenuClick}
           >
             {t('Refresh dashboard')}
           </Menu.Item>
         )}
         {!editMode && !isEmbedded && (
-          <Menu.Item
-            key={MenuKeys.ToggleFullscreen}
-            onClick={this.handleMenuClick}
-          >
+          <Menu.Item key={MenuKeys.ToggleFullscreen}>
             {getUrlParam(URL_PARAMS.standalone)
               ? t('Exit fullscreen')
               : t('Enter fullscreen')}
           </Menu.Item>
         )}
         {editMode && (
-          <Menu.Item
-            key={MenuKeys.EditProperties}
-            onClick={this.handleMenuClick}
-          >
+          <Menu.Item key={MenuKeys.EditProperties}>
             {t('Edit properties')}
           </Menu.Item>
         )}
@@ -196,8 +194,8 @@ export class HeaderActionsDropdown extends PureComponent<
           <Menu.Item key={MenuKeys.EditCss}>
             <CssEditor
               triggerNode={<div>{t('Edit CSS')}</div>}
-              initialCss={this.state.css}
-              onChange={this.changeCss}
+              initialCss={css}
+              onChange={changeCss}
               addDangerToast={addDangerToast}
             />
           </Menu.Item>
@@ -228,29 +226,26 @@ export class HeaderActionsDropdown extends PureComponent<
             />
           </Menu.Item>
         )}
-        <Menu.SubMenu
-          key={MenuKeys.Download}
+        <DownloadMenuItems
+          submenuKey={MenuKeys.Download}
           disabled={isLoading}
           title={t('Download')}
-        >
-          <DownloadMenuItems
-            pdfMenuItemTitle={t('Export to PDF')}
-            imageMenuItemTitle={t('Download as Image')}
-            dashboardTitle={dashboardTitle}
-            dashboardId={dashboardId}
-            logEvent={this.props.logEvent}
-          />
-        </Menu.SubMenu>
+          pdfMenuItemTitle={t('Export to PDF')}
+          imageMenuItemTitle={t('Download as Image')}
+          dashboardTitle={dashboardTitle}
+          dashboardId={dashboardId}
+          logEvent={logEvent}
+        />
         {userCanShare && (
           <ShareMenuItems
-            key={MenuKeys.Share}
+            disabled={isLoading}
             data-test="share-dashboard-menu-item"
             title={t('Share')}
             url={url}
             copyMenuItemTitle={t('Copy permalink to clipboard')}
             emailMenuItemTitle={t('Share permalink by email')}
             emailSubject={emailSubject}
-            emailBody={emailBody}
+            emailBody={t('Check out this dashboard: ')}
             addSuccessToast={addSuccessToast}
             addDangerToast={addDangerToast}
             dashboardId={dashboardId}
@@ -258,39 +253,33 @@ export class HeaderActionsDropdown extends PureComponent<
           />
         )}
         {!editMode && userCanCurate && (
-          <Menu.Item
-            key={MenuKeys.ManageEmbedded}
-            onClick={this.handleMenuClick}
-          >
+          <Menu.Item key={MenuKeys.ManageEmbedded}>
             {t('Embed dashboard')}
           </Menu.Item>
         )}
         <Menu.Divider />
         {!editMode ? (
-          this.state.showReportSubMenu ? (
+          showReportSubMenu ? (
             <>
-              <Menu.SubMenu title={t('Manage email report')}>
-                <HeaderReportDropdown
-                  dashboardId={dashboardInfo.id}
-                  setShowReportSubMenu={this.setShowReportSubMenu}
-                  showReportSubMenu={this.state.showReportSubMenu}
-                  setIsDropdownVisible={setIsDropdownVisible}
-                  isDropdownVisible={isDropdownVisible}
-                  useTextMenu
-                />
-              </Menu.SubMenu>
-              <Menu.Divider />
-            </>
-          ) : (
-            <Menu>
               <HeaderReportDropdown
+                submenuTitle={t('Manage email report')}
                 dashboardId={dashboardInfo.id}
-                setShowReportSubMenu={this.setShowReportSubMenu}
-                setIsDropdownVisible={setIsDropdownVisible}
-                isDropdownVisible={isDropdownVisible}
+                setShowReportSubMenu={setShowReportSubMenu}
+                showReportModal={showReportModal}
+                showReportSubMenu={showReportSubMenu}
+                setCurrentReportDeleting={setCurrentReportDeleting}
                 useTextMenu
               />
-            </Menu>
+              <Menu.Divider />
+            </>
+          ) : (
+            <HeaderReportDropdown
+              dashboardId={dashboardInfo.id}
+              setShowReportSubMenu={setShowReportSubMenu}
+              showReportModal={showReportModal}
+              setCurrentReportDeleting={setCurrentReportDeleting}
+              useTextMenu
+            />
           )
         ) : null}
         {editMode && !isEmpty(dashboardInfo?.metadata?.filter_scopes) && (
@@ -302,14 +291,13 @@ export class HeaderActionsDropdown extends PureComponent<
             />
           </Menu.Item>
         )}
-
         <Menu.Item key={MenuKeys.AutorefreshModal}>
           <RefreshIntervalModal
             addSuccessToast={addSuccessToast}
             refreshFrequency={refreshFrequency}
             refreshLimit={refreshLimit}
             refreshWarning={refreshWarning}
-            onChange={this.changeRefreshInterval}
+            onChange={changeRefreshInterval}
             editMode={editMode}
             refreshIntervalOptions={refreshIntervalOptions}
             triggerNode={<div>{t('Set auto-refresh interval')}</div>}
@@ -317,7 +305,18 @@ export class HeaderActionsDropdown extends PureComponent<
         </Menu.Item>
       </Menu>
     );
-  }
-}
+  }, [
+    css,
+    showReportSubMenu,
+    isDropdownVisible,
+    directPathToChild,
+    handleMenuClick,
+    changeCss,
+    changeRefreshInterval,
+    emailSubject,
+    url,
+    dashboardComponentId,
+  ]);
 
-export default connect(mapStateToProps)(HeaderActionsDropdown);
+  return [menu, isDropdownVisible, setIsDropdownVisible];
+};
diff --git a/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx b/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
index a683dfa2b2..3a9878f4fc 100644
--- a/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
+++ b/superset-frontend/src/dashboard/components/RefreshIntervalModal.test.tsx
@@ -22,16 +22,16 @@ import userEvent from '@testing-library/user-event';
 import fetchMock from 'fetch-mock';
 
 import RefreshIntervalModal from 'src/dashboard/components/RefreshIntervalModal';
-import { HeaderActionsDropdown } from 'src/dashboard/components/Header/HeaderActionsDropdown';
 import { Provider } from 'react-redux';
 import configureStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
+import { useHeaderActionsMenu } from './Header/useHeaderActionsDropdownMenu';
 
 const createProps = () => ({
   addSuccessToast: jest.fn(),
   addDangerToast: jest.fn(),
   customCss:
-    '.header-with-actions .right-button-panel .ant-dropdown-trigger{margin-left: 100px;}',
+    '.header-with-actions .right-button-panel .antd5-dropdown-trigger{margin-left: 100px;}',
   dashboardId: 1,
   dashboardInfo: {
     id: 1,
@@ -85,12 +85,22 @@ const editModeOnProps = {
 };
 
 const mockStore = configureStore([thunk]);
-const store = mockStore({});
+const store = mockStore({
+  dashboardState: {
+    dashboardInfo: createProps().dashboardInfo,
+  },
+});
+
+const HeaderActionsMenu = (props: any) => {
+  const [menu] = useHeaderActionsMenu(props);
+
+  return <>{menu}</>;
+};
 
 const setup = (overrides?: any) => (
   <Provider store={store}>
     <div className="dashboard-header">
-      <HeaderActionsDropdown {...editModeOnProps} {...overrides} />
+      <HeaderActionsMenu {...editModeOnProps} {...overrides} />
     </div>
   </Provider>
 );
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
index e1f20b5505..7c813809c0 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/SliceHeaderControls.test.tsx
@@ -112,11 +112,13 @@ const renderWrapper = (
   });
 };
 
+const openMenu = () => {
+  userEvent.click(screen.getByRole('button', { name: 'More Options' }));
+};
+
 test('Should render', () => {
   renderWrapper();
-  expect(
-    screen.getByRole('button', { name: 'More Options' }),
-  ).toBeInTheDocument();
+  openMenu();
   expect(screen.getByTestId(`slice_${SLICE_ID}-menu`)).toBeInTheDocument();
 });
 
@@ -143,6 +145,7 @@ test('Should render default props', () => {
   delete props.isExpanded;
 
   renderWrapper(props);
+  openMenu();
   expect(screen.getByText('Enter fullscreen')).toBeInTheDocument();
   expect(screen.getByText('Force refresh')).toBeInTheDocument();
   expect(screen.getByText('Show chart description')).toBeInTheDocument();
@@ -159,6 +162,7 @@ test('Should render default props', () => {
 test('Should "export to CSV"', async () => {
   const props = createProps();
   renderWrapper(props);
+  openMenu();
   expect(props.exportCSV).toHaveBeenCalledTimes(0);
   userEvent.hover(screen.getByText('Download'));
   userEvent.click(await screen.findByText('Export to .CSV'));
@@ -169,6 +173,7 @@ test('Should "export to CSV"', async () => {
 test('Should "export to Excel"', async () => {
   const props = createProps();
   renderWrapper(props);
+  openMenu();
   expect(props.exportXLSX).toHaveBeenCalledTimes(0);
   userEvent.hover(screen.getByText('Download'));
   userEvent.click(await screen.findByText('Export to Excel'));
@@ -182,6 +187,7 @@ test('Export full CSV is under featureflag', async () => {
   };
   const props = createProps(VizType.Table);
   renderWrapper(props);
+  openMenu();
   userEvent.hover(screen.getByText('Download'));
   expect(await screen.findByText('Export to .CSV')).toBeInTheDocument();
   expect(screen.queryByText('Export to full .CSV')).not.toBeInTheDocument();
@@ -193,6 +199,7 @@ test('Should "export full CSV"', async () => {
   };
   const props = createProps(VizType.Table);
   renderWrapper(props);
+  openMenu();
   expect(props.exportFullCSV).toHaveBeenCalledTimes(0);
   userEvent.hover(screen.getByText('Download'));
   userEvent.click(await screen.findByText('Export to full .CSV'));
@@ -205,6 +212,7 @@ test('Should not show export full CSV if report is not table', async () => {
     [FeatureFlag.AllowFullCsvExport]: true,
   };
   renderWrapper();
+  openMenu();
   userEvent.hover(screen.getByText('Download'));
   expect(await screen.findByText('Export to .CSV')).toBeInTheDocument();
   expect(screen.queryByText('Export to full .CSV')).not.toBeInTheDocument();
@@ -216,6 +224,7 @@ test('Export full Excel is under featureflag', async () => {
   };
   const props = createProps(VizType.Table);
   renderWrapper(props);
+  openMenu();
   userEvent.hover(screen.getByText('Download'));
   expect(await screen.findByText('Export to Excel')).toBeInTheDocument();
   expect(screen.queryByText('Export to full Excel')).not.toBeInTheDocument();
@@ -227,6 +236,7 @@ test('Should "export full Excel"', async () => {
   };
   const props = createProps(VizType.Table);
   renderWrapper(props);
+  openMenu();
   expect(props.exportFullXLSX).toHaveBeenCalledTimes(0);
   userEvent.hover(screen.getByText('Download'));
   userEvent.click(await screen.findByText('Export to full Excel'));
@@ -239,6 +249,7 @@ test('Should not show export full Excel if report is not table', async () => {
     [FeatureFlag.AllowFullCsvExport]: true,
   };
   renderWrapper();
+  openMenu();
   userEvent.hover(screen.getByText('Download'));
   expect(await screen.findByText('Export to Excel')).toBeInTheDocument();
   expect(screen.queryByText('Export to full Excel')).not.toBeInTheDocument();
@@ -247,6 +258,7 @@ test('Should not show export full Excel if report is not table', async () => {
 test('Should "Show chart description"', () => {
   const props = createProps();
   renderWrapper(props);
+  openMenu();
   expect(props.toggleExpandSlice).toHaveBeenCalledTimes(0);
   userEvent.click(screen.getByText('Show chart description'));
   expect(props.toggleExpandSlice).toHaveBeenCalledTimes(1);
@@ -256,6 +268,7 @@ test('Should "Show chart description"', () => {
 test('Should "Force refresh"', () => {
   const props = createProps();
   renderWrapper(props);
+  openMenu();
   expect(props.forceRefresh).toHaveBeenCalledTimes(0);
   userEvent.click(screen.getByText('Force refresh'));
   expect(props.forceRefresh).toHaveBeenCalledTimes(1);
@@ -266,6 +279,7 @@ test('Should "Force refresh"', () => {
 test('Should "Enter fullscreen"', () => {
   const props = createProps();
   renderWrapper(props);
+  openMenu();
 
   expect(props.handleToggleFullSize).toHaveBeenCalledTimes(0);
   userEvent.click(screen.getByText('Enter fullscreen'));
@@ -278,6 +292,7 @@ test('Drill to detail modal is under featureflag', () => {
   };
   const props = createProps();
   renderWrapper(props);
+  openMenu();
   expect(screen.queryByText('Drill to detail')).not.toBeInTheDocument();
 });
 
@@ -293,6 +308,7 @@ test('Should show "Drill to detail" with `can_explore` & `can_samples` perms', (
       ['can_explore', 'Superset'],
     ],
   });
+  openMenu();
   expect(screen.getByText('Drill to detail')).toBeInTheDocument();
 });
 
@@ -311,6 +327,7 @@ test('Should show "Drill to detail" with `can_drill` & `can_samples` perms', ()
       ['can_drill', 'Dashboard'],
     ],
   });
+  openMenu();
   expect(screen.getByText('Drill to detail')).toBeInTheDocument();
 });
 
@@ -329,6 +346,7 @@ test('Should show "Drill to detail" with both `canexplore` + `can_drill` & `can_
       ['can_drill', 'Dashboard'],
     ],
   });
+  openMenu();
   expect(screen.getByText('Drill to detail')).toBeInTheDocument();
 });
 
@@ -344,6 +362,7 @@ test('Should not show "Drill to detail" with neither of required perms', () => {
   renderWrapper(props, {
     Admin: [['invalid_permission', 'Dashboard']],
   });
+  openMenu();
   expect(screen.queryByText('Drill to detail')).not.toBeInTheDocument();
 });
 
@@ -359,6 +378,7 @@ test('Should not show "Drill to detail" only `can_dril` perm', () => {
   renderWrapper(props, {
     Admin: [['can_drill', 'Dashboard']],
   });
+  openMenu();
   expect(screen.queryByText('Drill to detail')).not.toBeInTheDocument();
 });
 
@@ -371,6 +391,7 @@ test('Should show "View query"', () => {
   renderWrapper(props, {
     Admin: [['can_view_query', 'Dashboard']],
   });
+  openMenu();
   expect(screen.getByText('View query')).toBeInTheDocument();
 });
 
@@ -383,6 +404,7 @@ test('Should not show "View query"', () => {
   renderWrapper(props, {
     Admin: [['invalid_permission', 'Dashboard']],
   });
+  openMenu();
   expect(screen.queryByText('View query')).not.toBeInTheDocument();
 });
 
@@ -395,6 +417,7 @@ test('Should show "View as table"', () => {
   renderWrapper(props, {
     Admin: [['can_view_chart_as_table', 'Dashboard']],
   });
+  openMenu();
   expect(screen.getByText('View as table')).toBeInTheDocument();
 });
 
@@ -407,6 +430,7 @@ test('Should not show "View as table"', () => {
   renderWrapper(props, {
     Admin: [['invalid_permission', 'Dashboard']],
   });
+  openMenu();
   expect(screen.queryByText('View as table')).not.toBeInTheDocument();
 });
 
@@ -423,5 +447,6 @@ test('Should not show the "Edit chart" button', () => {
       ['can_view_chart_as_table', 'Dashboard'],
     ],
   });
+  openMenu();
   expect(screen.queryByText('Edit chart')).not.toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
index 12479d02f6..1c31b67529 100644
--- a/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
+++ b/superset-frontend/src/dashboard/components/SliceHeaderControls/index.tsx
@@ -55,6 +55,7 @@ import { LOG_ACTIONS_CHART_DOWNLOAD_AS_IMAGE } from 'src/logger/LogUtils';
 import { MenuKeys, RootState } from 'src/dashboard/types';
 import DrillDetailModal from 'src/components/Chart/DrillDetail/DrillDetailModal';
 import { usePermissions } from 'src/hooks/usePermissions';
+import Button from 'src/components/Button';
 import { useCrossFiltersScopingModal } from '../nativeFilters/FilterBar/CrossFilters/ScopingModal/useCrossFiltersScopingModal';
 import { ViewResultsModalTrigger } from './ViewResultsModalTrigger';
 
@@ -158,9 +159,8 @@ const SliceHeaderControls = (
   props: SliceHeaderControlsPropsWithRouter | SliceHeaderControlsProps,
 ) => {
   const [drillModalIsOpen, setDrillModalIsOpen] = useState(false);
-  const [selectedKeys, setSelectedKeys] = useState<string[]>([]);
   // setting openKeys undefined falls back to uncontrolled behaviour
-  const [openKeys, setOpenKeys] = useState<string[] | undefined>(undefined);
+  const [isDropdownVisible, setIsDropdownVisible] = useState(false);
   const [openScopingModal, scopingModal] = useCrossFiltersScopingModal(
     props.slice.slice_id,
   );
@@ -241,7 +241,7 @@ const SliceHeaderControls = (
         // menu closes with a delay, we need to hide it manually,
         // so that we don't capture it on the screenshot
         const menu = document.querySelector(
-          '.ant-dropdown:not(.ant-dropdown-hidden)',
+          '.antd5-dropdown:not(.antd5-dropdown-hidden)',
         ) as HTMLElement;
         if (menu) {
           menu.style.visibility = 'hidden';
@@ -284,6 +284,7 @@ const SliceHeaderControls = (
       default:
         break;
     }
+    setIsDropdownVisible(false);
   };
 
   const {
@@ -334,24 +335,12 @@ const SliceHeaderControls = (
     animationDuration: '0s',
   };
 
-  // controlled/uncontrolled behaviour for submenus
-  const openKeysProps: Record<string, string[]> = {};
-  if (openKeys) {
-    openKeysProps.openKeys = openKeys;
-  }
-
   const menu = (
     <Menu
       onClick={handleMenuClick}
-      selectable={false}
       data-test={`slice_${slice.slice_id}-menu`}
-      selectedKeys={selectedKeys}
-      onSelect={({ selectedKeys: keys }) => setSelectedKeys(keys)}
-      openKeys={openKeys}
       id={`slice_${slice.slice_id}-menu`}
-      // submenus must be rendered for handleDropdownNavigation
-      forceSubMenuRender
-      {...openKeysProps}
+      selectable={false}
     >
       <Menu.Item
         key={MenuKeys.ForceRefresh}
@@ -458,9 +447,7 @@ const SliceHeaderControls = (
           emailBody={t('Check out this chart: ')}
           addSuccessToast={addSuccessToast}
           addDangerToast={addDangerToast}
-          setOpenKeys={setOpenKeys}
           title={t('Share')}
-          key={MenuKeys.Share}
         />
       )}
 
@@ -532,22 +519,17 @@ const SliceHeaderControls = (
         overlayStyle={dropdownOverlayStyle}
         trigger={['click']}
         placement="bottomRight"
-        autoFocus
-        forceRender
+        open={isDropdownVisible}
+        onOpenChange={visible => setIsDropdownVisible(visible)}
       >
-        <span
-          css={() => css`
-            display: flex;
-            align-items: center;
-          `}
+        <Button
+          type="link"
           id={`slice_${slice.slice_id}-controls`}
-          role="button"
           aria-label="More Options"
           aria-haspopup="true"
-          tabIndex={0}
         >
           <VerticalDotsTrigger />
-        </span>
+        </Button>
       </NoAnimationDropdown>
       <DrillDetailModal
         formData={props.formData}
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.test.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.test.tsx
index 8401ece73c..3f4b232bca 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.test.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.test.tsx
@@ -60,7 +60,7 @@ test('Should call download image on click', async () => {
     expect(mockAddDangerToast).toHaveBeenCalledTimes(0);
   });
 
-  userEvent.click(screen.getByRole('button', { name: 'Download as Image' }));
+  userEvent.click(screen.getByRole('menuitem', { name: 'Download as Image' }));
 
   await waitFor(() => {
     expect(downloadAsImage).toHaveBeenCalledTimes(1);
@@ -68,8 +68,8 @@ test('Should call download image on click', async () => {
   });
 });
 
-test('Component is rendered with role="button"', async () => {
+test('Component is rendered with role="menuitem"', async () => {
   renderComponent();
-  const button = screen.getByRole('button', { name: 'Download as Image' });
+  const button = screen.getByRole('menuitem', { name: 'Download as Image' });
   expect(button).toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.tsx
index 505a9b8184..5490d7b805 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsImage.tsx
@@ -27,7 +27,6 @@ export default function DownloadAsImage({
   text,
   logEvent,
   dashboardTitle,
-  ...rest
 }: {
   text: string;
   dashboardTitle: string;
@@ -46,10 +45,13 @@ export default function DownloadAsImage({
   };
 
   return (
-    <Menu.Item key="download-image" {...rest}>
-      <div onClick={onDownloadImage} role="button" tabIndex={0}>
-        {text}
-      </div>
+    <Menu.Item
+      key="download-image"
+      onClick={e => {
+        onDownloadImage(e.domEvent);
+      }}
+    >
+      {text}
     </Menu.Item>
   );
 }
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.test.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.test.tsx
index 56916f4b64..ec6652ca41 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.test.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.test.tsx
@@ -58,7 +58,7 @@ test('Should call download pdf on click', async () => {
     expect(mockAddDangerToast).toHaveBeenCalledTimes(0);
   });
 
-  userEvent.click(screen.getByRole('button', { name: 'Export as PDF' }));
+  userEvent.click(screen.getByRole('menuitem', { name: 'Export as PDF' }));
 
   await waitFor(() => {
     expect(downloadAsPdf).toHaveBeenCalledTimes(1);
@@ -66,8 +66,8 @@ test('Should call download pdf on click', async () => {
   });
 });
 
-test('Component is rendered with role="button"', async () => {
+test('Component is rendered with role="menuitem"', async () => {
   renderComponent();
-  const button = screen.getByRole('button', { name: 'Export as PDF' });
+  const button = screen.getByRole('menuitem', { name: 'Export as PDF' });
   expect(button).toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.tsx
index a07a2e232c..47309b842a 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadAsPdf.tsx
@@ -27,7 +27,6 @@ export default function DownloadAsPdf({
   text,
   logEvent,
   dashboardTitle,
-  ...rest
 }: {
   text: string;
   dashboardTitle: string;
@@ -46,10 +45,13 @@ export default function DownloadAsPdf({
   };
 
   return (
-    <Menu.Item key="download-pdf" {...rest}>
-      <div onClick={onDownloadPdf} role="button" tabIndex={0}>
-        {text}
-      </div>
+    <Menu.Item
+      key="download-pdf"
+      onClick={e => {
+        onDownloadPdf(e.domEvent);
+      }}
+    >
+      {text}
     </Menu.Item>
   );
 }
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadMenuItems.test.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadMenuItems.test.tsx
index 0f3049d847..2bd3a2cd9d 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadMenuItems.test.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/DownloadMenuItems.test.tsx
@@ -26,11 +26,13 @@ const createProps = () => ({
   dashboardTitle: 'Test Dashboard',
   logEvent: jest.fn(),
   dashboardId: 123,
+  title: 'Download',
+  submenuKey: 'download',
 });
 
 const renderComponent = () => {
   render(
-    <Menu>
+    <Menu forceSubMenuRender>
       <DownloadMenuItems {...createProps()} />
     </Menu>,
     {
@@ -41,10 +43,6 @@ const renderComponent = () => {
 
 test('Should render menu items', () => {
   renderComponent();
-  expect(
-    screen.getByRole('menuitem', { name: 'Export to PDF' }),
-  ).toBeInTheDocument();
-  expect(
-    screen.getByRole('menuitem', { name: 'Download as Image' }),
-  ).toBeInTheDocument();
+  expect(screen.getByText('Export to PDF')).toBeInTheDocument();
+  expect(screen.getByText('Download as Image')).toBeInTheDocument();
 });
diff --git a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/index.tsx b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/index.tsx
index d9ffaaaedf..cdb73d5e17 100644
--- a/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/index.tsx
+++ b/superset-frontend/src/dashboard/components/menu/DownloadMenuItems/index.tsx
@@ -17,17 +17,23 @@
  * under the License.
  */
 import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
-import DownloadScreenshot from './DownloadScreenshot';
+import { Menu } from 'src/components/Menu';
+import { useDownloadScreenshot } from 'src/dashboard/hooks/useDownloadScreenshot';
+import { ComponentProps } from 'react';
 import { DownloadScreenshotFormat } from './types';
 import DownloadAsPdf from './DownloadAsPdf';
 import DownloadAsImage from './DownloadAsImage';
 
-export interface DownloadMenuItemProps {
+export interface DownloadMenuItemProps
+  extends ComponentProps<typeof Menu.SubMenu> {
   pdfMenuItemTitle: string;
   imageMenuItemTitle: string;
   dashboardTitle: string;
   logEvent?: Function;
   dashboardId: number;
+  title: string;
+  disabled?: boolean;
+  submenuKey: string;
 }
 
 const DownloadMenuItems = (props: DownloadMenuItemProps) => {
@@ -37,44 +43,45 @@ const DownloadMenuItems = (props: DownloadMenuItemProps) => {
     logEvent,
     dashboardId,
     dashboardTitle,
+    submenuKey,
+    disabled,
+    title,
     ...rest
   } = props;
   const isWebDriverScreenshotEnabled =
     isFeatureEnabled(FeatureFlag.EnableDashboardScreenshotEndpoints) &&
     isFeatureEnabled(FeatureFlag.EnableDashboardDownloadWebDriverScreenshot);
 
+  const downloadScreenshot = useDownloadScreenshot(dashboardId, logEvent);
+
   return isWebDriverScreenshotEnabled ? (
-    <>
-      <DownloadScreenshot
-        text={pdfMenuItemTitle}
-        dashboardId={dashboardId}
-        logEvent={logEvent}
-        format={DownloadScreenshotFormat.PDF}
-        {...rest}
-      />
-      <DownloadScreenshot
-        text={imageMenuItemTitle}
-        dashboardId={dashboardId}
-        logEvent={logEvent}
-        format={DownloadScreenshotFormat.PNG}
-        {...rest}
-      />
-    </>
+    <Menu.SubMenu key={submenuKey} title={title} disabled={disabled} {...rest}>
+      <Menu.Item
+        key={DownloadScreenshotFormat.PDF}
+        onClick={() => downloadScreenshot(DownloadScreenshotFormat.PDF)}
+      >
+        {pdfMenuItemTitle}
+      </Menu.Item>
+      <Menu.Item
+        key={DownloadScreenshotFormat.PNG}
+        onClick={() => downloadScreenshot(DownloadScreenshotFormat.PNG)}
+      >
+        {imageMenuItemTitle}
+      </Menu.Item>
+    </Menu.SubMenu>
   ) : (
-    <>
+    <Menu.SubMenu key={submenuKey} title={title} disabled={disabled} {...rest}>
       <DownloadAsPdf
         text={pdfMenuItemTitle}
         dashboardTitle={dashboardTitle}
         logEvent={logEvent}
-        {...rest}
       />
       <DownloadAsImage
         text={imageMenuItemTitle}
         dashboardTitle={dashboardTitle}
         logEvent={logEvent}
-        {...rest}
       />
-    </>
+    </Menu.SubMenu>
   );
 };
 
diff --git a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx
index 16bbcff9ec..f290a1a4b3 100644
--- a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx
+++ b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/ShareMenuItems.test.tsx
@@ -37,6 +37,7 @@ const createProps = () => ({
   emailBody: 'Check out this dashboard: ',
   dashboardId: DASHBOARD_ID,
   title: 'Test Dashboard',
+  submenuKey: 'share',
 });
 
 const { location } = window;
diff --git a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
index 6c5468da24..4fb191a067 100644
--- a/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
+++ b/superset-frontend/src/dashboard/components/menu/ShareMenuItems/index.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { RefObject } from 'react';
+import { ComponentProps, RefObject } from 'react';
 import copyTextToClipboard from 'src/utils/copy';
 import { t, logging } from '@superset-ui/core';
 import { Menu } from 'src/components/Menu';
@@ -24,7 +24,7 @@ import { getDashboardPermalink } from 'src/utils/urlUtils';
 import { MenuKeys, RootState } from 'src/dashboard/types';
 import { shallowEqual, useSelector } from 'react-redux';
 
-interface ShareMenuItemProps {
+interface ShareMenuItemProps extends ComponentProps<typeof Menu.SubMenu> {
   url?: string;
   copyMenuItemTitle: string;
   emailMenuItemTitle: string;
@@ -38,8 +38,8 @@ interface ShareMenuItemProps {
   shareByEmailMenuItemRef?: RefObject<any>;
   selectedKeys?: string[];
   setOpenKeys?: Function;
-  key?: string;
   title: string;
+  disabled?: boolean;
 }
 
 const ShareMenuItems = (props: ShareMenuItemProps) => {
@@ -52,8 +52,9 @@ const ShareMenuItems = (props: ShareMenuItemProps) => {
     addSuccessToast,
     dashboardId,
     dashboardComponentId,
-    key,
     title,
+    disabled,
+    ...rest
   } = props;
   const { dataMask, activeTabs } = useSelector(
     (state: RootState) => ({
@@ -96,7 +97,12 @@ const ShareMenuItems = (props: ShareMenuItemProps) => {
   }
 
   return (
-    <Menu.SubMenu title={title} key={key}>
+    <Menu.SubMenu
+      title={title}
+      key={MenuKeys.Share}
+      disabled={disabled}
+      {...rest}
+    >
       <Menu.Item key={MenuKeys.CopyLink} onClick={() => onCopyLink()}>
         {copyMenuItemTitle}
       </Menu.Item>
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx
index b073fe655e..45a34b110f 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/FilterBarSettings.test.tsx
@@ -121,13 +121,13 @@ test('Can enable/disable cross-filtering', async () => {
   });
   await setup();
   userEvent.click(screen.getByLabelText('gear'));
-  const checkbox = screen.getByRole('checkbox');
-  expect(checkbox).toBeChecked();
+  const initialCheckbox = screen.getByRole('checkbox');
+  expect(initialCheckbox).toBeChecked();
 
-  userEvent.click(checkbox);
+  userEvent.click(initialCheckbox);
 
   userEvent.click(screen.getByLabelText('gear'));
-  expect(checkbox).not.toBeChecked();
+  expect(screen.getByRole('checkbox')).not.toBeChecked();
 });
 
 test('Popover opens with "Vertical" selected', async () => {
@@ -178,19 +178,21 @@ test('On selection change, send request and update checked value', async () => {
   userEvent.click(screen.getByLabelText('gear'));
   userEvent.hover(screen.getByText('Orientation of filter bar'));
 
-  expect(await screen.findByText('Vertical (Left)')).toBeInTheDocument();
-  expect(screen.getByText('Horizontal (Top)')).toBeInTheDocument();
+  const verticalItem = await screen.findByText('Vertical (Left)');
   expect(
-    within(screen.getAllByRole('menuitem')[4]).getByLabelText('check'),
+    within(verticalItem.closest('li')!).getByLabelText('check'),
   ).toBeInTheDocument();
 
   userEvent.click(screen.getByText('Horizontal (Top)'));
 
-  // 1st check - checkmark appears immediately after click
+  userEvent.click(screen.getByLabelText('gear'));
+  userEvent.hover(screen.getByText('Orientation of filter bar'));
+
+  const horizontalItem = await screen.findByText('Horizontal (Top)');
   expect(
-    await within(screen.getAllByRole('menuitem')[5]).findByLabelText('check'),
+    within(horizontalItem.closest('li')!).getByLabelText('check'),
   ).toBeInTheDocument();
-  // successful query
+
   await waitFor(() =>
     expect(fetchMock.lastCall()?.[1]?.body).toEqual(
       JSON.stringify({
@@ -201,23 +203,18 @@ test('On selection change, send request and update checked value', async () => {
       }),
     ),
   );
+
   await waitFor(() => {
-    const menuitems = screen.getAllByRole('menuitem');
-    expect(menuitems.length).toBeGreaterThanOrEqual(6);
+    userEvent.click(screen.getByLabelText('gear'));
+    userEvent.hover(screen.getByText('Orientation of filter bar'));
+    const updatedHorizontalItem = screen.getByText('Horizontal (Top)');
+    expect(
+      within(updatedHorizontalItem.closest('li')!).getByLabelText('check'),
+    ).toBeInTheDocument();
+    expect(
+      within(verticalItem.closest('li')!).queryByLabelText('check'),
+    ).not.toBeInTheDocument();
   });
-
-  userEvent.click(screen.getByLabelText('gear'));
-  userEvent.hover(screen.getByText('Orientation of filter bar'));
-  expect(await screen.findByText('Vertical (Left)')).toBeInTheDocument();
-  expect(screen.getByText('Horizontal (Top)')).toBeInTheDocument();
-
-  // 2nd check - checkmark stays after successful query
-  expect(
-    await within(screen.getAllByRole('menuitem')[5]).findByLabelText('check'),
-  ).toBeInTheDocument();
-  expect(
-    within(screen.getAllByRole('menuitem')[4]).queryByLabelText('check'),
-  ).not.toBeInTheDocument();
 });
 
 test('On failed request, restore previous selection', async () => {
@@ -254,9 +251,8 @@ test('On failed request, restore previous selection', async () => {
   userEvent.click(screen.getByLabelText('gear'));
   userEvent.hover(screen.getByText('Orientation of filter bar'));
 
-  expect(await screen.findByText('Vertical (Left)')).toBeInTheDocument();
-
   await waitFor(() => {
+    expect(screen.getByText('Vertical (Left)')).toBeInTheDocument();
     const menuitems = screen.getAllByRole('menuitem');
     expect(menuitems.length).toBeGreaterThanOrEqual(6);
   });
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx
index 404d737999..7b617abf18 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBarSettings/index.tsx
@@ -33,12 +33,13 @@ import {
   saveCrossFiltersSetting,
 } from 'src/dashboard/actions/dashboardInfo';
 import Icons from 'src/components/Icons';
-import DropdownSelectableIcon, {
-  DropDownSelectableProps,
-} from 'src/components/DropdownSelectableIcon';
 import Checkbox from 'src/components/Checkbox';
+import { Dropdown } from 'src/components/Dropdown';
+import { Button } from 'src/components';
+import { Space } from 'src/components/Space';
 import { clearDataMaskState } from 'src/dataMask/actions';
 import { useFilters } from 'src/dashboard/components/nativeFilters/FilterBar/state';
+import { useFilterConfigModal } from 'src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/useFilterConfigModal';
 import { useCrossFiltersScopingModal } from '../CrossFilters/ScopingModal/useCrossFiltersScopingModal';
 import FilterConfigurationLink from '../FilterConfigurationLink';
 
@@ -100,6 +101,12 @@ const FilterBarSettings = () => {
 
   const [openScopingModal, scopingModal] = useCrossFiltersScopingModal();
 
+  const { openFilterConfigModal, FilterConfigModalComponent } =
+    useFilterConfigModal({
+      createNewOnOpen: filterValues.length === 0,
+      dashboardId,
+    });
+
   const updateCrossFiltersSetting = useCallback(
     async isEnabled => {
       if (!isEnabled) {
@@ -133,7 +140,7 @@ const FilterBarSettings = () => {
     [dispatch, filterBarOrientation],
   );
 
-  const handleSelect = useCallback(
+  const handleClick = useCallback(
     (
       selection: Parameters<
         Required<Pick<MenuProps, 'onSelect'>>['onSelect']
@@ -146,9 +153,16 @@ const FilterBarSettings = () => {
         toggleFilterBarOrientation(selectedKey);
       } else if (selectedKey === CROSS_FILTERS_SCOPING_MENU_KEY) {
         openScopingModal();
+      } else if (selectedKey === ADD_EDIT_FILTERS_MENU_KEY) {
+        openFilterConfigModal();
       }
     },
-    [openScopingModal, toggleCrossFiltering, toggleFilterBarOrientation],
+    [
+      openScopingModal,
+      toggleCrossFiltering,
+      toggleFilterBarOrientation,
+      openFilterConfigModal,
+    ],
   );
 
   const crossFiltersMenuItem = useMemo(
@@ -168,21 +182,20 @@ const FilterBarSettings = () => {
   );
 
   const menuItems = useMemo(() => {
-    const items: DropDownSelectableProps['menuItems'] = [];
+    const items: MenuProps['items'] = [];
 
     if (canEdit) {
       items.push({
         key: ADD_EDIT_FILTERS_MENU_KEY,
         label: (
-          <FilterConfigurationLink
-            dashboardId={dashboardId}
-            createNewOnOpen={filterValues.length === 0}
-          >
+          <FilterConfigurationLink>
             {t('Add or edit filters')}
           </FilterConfigurationLink>
         ),
-        divider: canSetHorizontalFilterBar,
       });
+      if (canSetHorizontalFilterBar) {
+        items.push({ type: 'divider' });
+      }
     }
     if (canEdit) {
       items.push({
@@ -192,8 +205,10 @@ const FilterBarSettings = () => {
       items.push({
         key: CROSS_FILTERS_SCOPING_MENU_KEY,
         label: t('Cross-filtering scoping'),
-        divider: canSetHorizontalFilterBar,
       });
+      if (canSetHorizontalFilterBar) {
+        items.push({ type: 'divider' });
+      }
     }
     if (canSetHorizontalFilterBar) {
       items.push({
@@ -202,17 +217,31 @@ const FilterBarSettings = () => {
         children: [
           {
             key: FilterBarOrientation.Vertical,
-            label: t('Vertical (Left)'),
+            label: (
+              <Space>
+                {t('Vertical (Left)')}
+                {selectedFilterBarOrientation ===
+                  FilterBarOrientation.Vertical && <Icons.Check />}
+              </Space>
+            ),
           },
           {
             key: FilterBarOrientation.Horizontal,
-            label: t('Horizontal (Top)'),
+            label: (
+              <Space>
+                {t('Horizontal (Top)')}
+                {selectedFilterBarOrientation ===
+                  FilterBarOrientation.Horizontal && <Icons.Check />}
+              </Space>
+            ),
           },
         ],
+        ...{ 'data-test': 'dropdown-selectable-icon-submenu' },
       });
     }
     return items;
   }, [
+    selectedFilterBarOrientation,
     canEdit,
     canSetHorizontalFilterBar,
     crossFiltersMenuItem,
@@ -226,19 +255,24 @@ const FilterBarSettings = () => {
 
   return (
     <>
-      <DropdownSelectableIcon
-        onSelect={handleSelect}
-        icon={
+      <Dropdown
+        menu={{
+          onClick: handleClick,
+          items: menuItems,
+          selectedKeys: [selectedFilterBarOrientation],
+        }}
+        trigger={['click']}
+      >
+        <Button type="link">
           <Icons.Gear
             name="gear"
             iconColor={theme.colors.grayscale.base}
             data-test="filterbar-orientation-icon"
           />
-        }
-        menuItems={menuItems}
-        selectedKeys={[selectedFilterBarOrientation]}
-      />
+        </Button>
+      </Dropdown>
       {scopingModal}
+      {FilterConfigModalComponent}
     </>
   );
 };
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx
index 0b5a18835d..7ce156a7c4 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/FilterConfigurationLink.test.tsx
@@ -38,11 +38,16 @@ test('should render the config link text', () => {
 });
 
 test('should render the modal on click', () => {
-  render(<FilterConfigurationLink>Config link</FilterConfigurationLink>, {
-    useRedux: true,
-  });
+  const showModal = jest.fn();
+  render(
+    <FilterConfigurationLink onClick={showModal}>
+      Config link
+    </FilterConfigurationLink>,
+    {
+      useRedux: true,
+    },
+  );
   const configLink = screen.getByText('Config link');
-  expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
   userEvent.click(configLink);
-  expect(screen.getByRole('dialog')).toBeInTheDocument();
+  expect(showModal).toHaveBeenCalled();
 });
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
index 6b4fea62a4..367072fbab 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/index.tsx
@@ -16,70 +16,27 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { ReactNode, FC, useCallback, useState, memo } from 'react';
+import { ReactNode, FC, memo } from 'react';
 
-import { useDispatch } from 'react-redux';
-import { setFilterConfiguration } from 'src/dashboard/actions/nativeFilters';
-import FiltersConfigModal from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal';
 import { getFilterBarTestId } from '../utils';
-import { SaveFilterChangesType } from '../../FiltersConfigModal/types';
 
 export interface FCBProps {
-  createNewOnOpen?: boolean;
-  dashboardId?: number;
-  initialFilterId?: string;
   onClick?: () => void;
   children?: ReactNode;
 }
 
 export const FilterConfigurationLink: FC<FCBProps> = ({
-  createNewOnOpen,
-  dashboardId,
-  initialFilterId,
   onClick,
   children,
-}) => {
-  const dispatch = useDispatch();
-  const [isOpen, setOpen] = useState(false);
-  const close = useCallback(() => {
-    setOpen(false);
-  }, [setOpen]);
-
-  const submit = useCallback(
-    async (filterChanges: SaveFilterChangesType) => {
-      dispatch(await setFilterConfiguration(filterChanges));
-      close();
-    },
-    [dispatch, close],
-  );
-
-  const handleClick = useCallback(() => {
-    setOpen(true);
-    if (onClick) {
-      onClick();
-    }
-  }, [setOpen, onClick]);
-
-  return (
-    <>
-      <div
-        {...getFilterBarTestId('create-filter')}
-        onClick={handleClick}
-        role="button"
-        tabIndex={0}
-      >
-        {children}
-      </div>
-      <FiltersConfigModal
-        isOpen={isOpen}
-        onSave={submit}
-        onCancel={close}
-        initialFilterId={initialFilterId}
-        createNewOnOpen={createNewOnOpen}
-        key={`filters-for-${dashboardId}`}
-      />
-    </>
-  );
-};
+}) => (
+  <div
+    {...getFilterBarTestId('create-filter')}
+    onClick={onClick}
+    role="button"
+    tabIndex={0}
+  >
+    {children}
+  </div>
+);
 
 export default memo(FilterConfigurationLink);
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/useFilterConfigModal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/useFilterConfigModal.tsx
new file mode 100644
index 0000000000..2a08f7800f
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/useFilterConfigModal.tsx
@@ -0,0 +1,82 @@
+/**
+ * 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 { useCallback, useState } from 'react';
+import { useDispatch } from 'react-redux';
+import { setFilterConfiguration } from 'src/dashboard/actions/nativeFilters';
+import { SaveFilterChangesType } from 'src/dashboard/components/nativeFilters/FiltersConfigModal/types';
+import FiltersConfigModal from 'src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigModal';
+
+interface UseFilterConfigModalProps {
+  createNewOnOpen?: boolean;
+  dashboardId: number;
+  initialFilterId?: string;
+}
+
+interface UseFilterConfigModalReturn {
+  isFilterConfigModalOpen: boolean;
+  openFilterConfigModal: () => void;
+  closeFilterConfigModal: () => void;
+  handleFilterSave: (filterChanges: SaveFilterChangesType) => Promise<void>;
+  FilterConfigModalComponent: JSX.Element | null;
+}
+
+export const useFilterConfigModal = ({
+  createNewOnOpen = false,
+  dashboardId,
+  initialFilterId,
+}: UseFilterConfigModalProps): UseFilterConfigModalReturn => {
+  const dispatch = useDispatch();
+  const [isFilterConfigModalOpen, setIsFilterConfigModalOpen] = useState(false);
+
+  const openFilterConfigModal = useCallback(() => {
+    setIsFilterConfigModalOpen(true);
+  }, []);
+
+  const closeFilterConfigModal = useCallback(() => {
+    setIsFilterConfigModalOpen(false);
+  }, []);
+
+  const handleFilterSave = useCallback(
+    async (filterChanges: SaveFilterChangesType) => {
+      dispatch(await setFilterConfiguration(filterChanges));
+      closeFilterConfigModal();
+    },
+    [dispatch, closeFilterConfigModal],
+  );
+
+  const FilterConfigModalComponent = isFilterConfigModalOpen ? (
+    <FiltersConfigModal
+      isOpen={isFilterConfigModalOpen}
+      onSave={handleFilterSave}
+      onCancel={closeFilterConfigModal}
+      key={`filters-for-${dashboardId}`}
+      createNewOnOpen={createNewOnOpen}
+      initialFilterId={initialFilterId}
+    />
+  ) : null;
+
+  return {
+    isFilterConfigModalOpen,
+    openFilterConfigModal,
+    closeFilterConfigModal,
+    handleFilterSave,
+    FilterConfigModalComponent,
+  };
+};
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx
index b82b96767f..0d6887f0c5 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Header/index.tsx
@@ -58,9 +58,6 @@ const Wrapper = styled.div`
     padding: ${theme.gridUnit * 3}px ${theme.gridUnit * 2}px ${
       theme.gridUnit
     }px;
-    .ant-dropdown-trigger span {
-      padding-right: ${theme.gridUnit * 2}px;
-    }
   `}
 `;
 
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/NameRow.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/NameRow.tsx
index 01679b1926..9346a72ab5 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/NameRow.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterCard/NameRow.tsx
@@ -19,6 +19,7 @@
 import { useSelector } from 'react-redux';
 import { css, SupersetTheme, useTheme, useTruncation } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
+import { useFilterConfigModal } from 'src/dashboard/components/nativeFilters/FilterBar/FilterConfigurationLink/useFilterConfigModal';
 import { RootState } from 'src/dashboard/types';
 import { Row, FilterName, InternalRow } from './Styles';
 import { FilterCardRowProps } from './types';
@@ -39,6 +40,12 @@ export const NameRow = ({
     ({ dashboardInfo }) => dashboardInfo.dash_edit_perm,
   );
 
+  const { FilterConfigModalComponent, openFilterConfigModal } =
+    useFilterConfigModal({
+      dashboardId,
+      initialFilterId: filter.id,
+    });
+
   return (
     <Row
       css={(theme: SupersetTheme) => css`
@@ -58,9 +65,10 @@ export const NameRow = ({
       </InternalRow>
       {canEdit && (
         <FilterConfigurationLink
-          dashboardId={dashboardId}
-          onClick={hidePopover}
-          initialFilterId={filter.id}
+          onClick={() => {
+            openFilterConfigModal();
+            hidePopover();
+          }}
         >
           <Icons.Edit
             iconSize="l"
@@ -71,6 +79,7 @@ export const NameRow = ({
           />
         </FilterConfigurationLink>
       )}
+      {FilterConfigModalComponent}
     </Row>
   );
 };
diff --git a/superset-frontend/src/dashboard/hooks/useDownloadScreenshot.ts b/superset-frontend/src/dashboard/hooks/useDownloadScreenshot.ts
new file mode 100644
index 0000000000..bc8ef7d1ff
--- /dev/null
+++ b/superset-frontend/src/dashboard/hooks/useDownloadScreenshot.ts
@@ -0,0 +1,184 @@
+/**
+ * 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 { useCallback, useEffect, useRef } from 'react';
+import { useSelector } from 'react-redux';
+import { useToasts } from 'src/components/MessageToasts/withToasts';
+import { last } from 'lodash';
+import {
+  logging,
+  t,
+  SupersetClient,
+  SupersetApiError,
+} from '@superset-ui/core';
+import {
+  LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_IMAGE,
+  LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_PDF,
+} from 'src/logger/LogUtils';
+import { RootState } from 'src/dashboard/types';
+import { getDashboardUrlParams } from 'src/utils/urlUtils';
+import { DownloadScreenshotFormat } from '../components/menu/DownloadMenuItems/types';
+
+const RETRY_INTERVAL = 3000;
+const MAX_RETRIES = 30;
+
+export const useDownloadScreenshot = (
+  dashboardId: number,
+  logEvent?: Function,
+) => {
+  const activeTabs = useSelector(
+    (state: RootState) => state.dashboardState.activeTabs || undefined,
+  );
+  const anchor = useSelector(
+    (state: RootState) =>
+      last(state.dashboardState.directPathToChild) || undefined,
+  );
+  const dataMask = useSelector(
+    (state: RootState) => state.dataMask || undefined,
+  );
+
+  const { addDangerToast, addSuccessToast, addInfoToast } = useToasts();
+
+  const currentIntervalIds = useRef<NodeJS.Timeout[]>([]);
+
+  const stopIntervals = useCallback(
+    (message?: 'success' | 'failure') => {
+      currentIntervalIds.current.forEach(clearInterval);
+
+      if (message === 'failure') {
+        addDangerToast(
+          t('The screenshot could not be downloaded. Please, try again later.'),
+        );
+      }
+      if (message === 'success') {
+        addSuccessToast(t('The screenshot has been downloaded.'));
+      }
+    },
+    [addDangerToast, addSuccessToast],
+  );
+
+  const downloadScreenshot = useCallback(
+    (format: DownloadScreenshotFormat) => {
+      let retries = 0;
+
+      const toastIntervalId = setInterval(
+        () =>
+          addInfoToast(
+            t(
+              'The screenshot is being generated. Please, do not leave the page.',
+            ),
+            { noDuplicate: true },
+          ),
+        RETRY_INTERVAL,
+      );
+
+      currentIntervalIds.current = [
+        ...(currentIntervalIds.current || []),
+        toastIntervalId,
+      ];
+
+      const checkImageReady = (cacheKey: string) =>
+        SupersetClient.get({
+          endpoint: `/api/v1/dashboard/${dashboardId}/screenshot/${cacheKey}/?download_format=${format}`,
+          headers: { Accept: 'application/pdf, image/png' },
+          parseMethod: 'raw',
+        })
+          .then((response: Response) => response.blob())
+          .then(blob => {
+            const url = window.URL.createObjectURL(blob);
+            const a = document.createElement('a');
+            a.href = url;
+            a.download = `screenshot.${format}`;
+            document.body.appendChild(a);
+            a.click();
+            document.body.removeChild(a);
+            window.URL.revokeObjectURL(url);
+            stopIntervals('success');
+          })
+          .catch(err => {
+            if ((err as SupersetApiError).status === 404) {
+              throw new Error('Image not ready');
+            }
+          });
+
+      const fetchImageWithRetry = (cacheKey: string) => {
+        if (retries >= MAX_RETRIES) {
+          stopIntervals('failure');
+          logging.error('Max retries reached');
+          return;
+        }
+        checkImageReady(cacheKey).catch(() => {
+          retries += 1;
+        });
+      };
+
+      SupersetClient.post({
+        endpoint: `/api/v1/dashboard/${dashboardId}/cache_dashboard_screenshot/`,
+        jsonPayload: {
+          anchor,
+          activeTabs,
+          dataMask,
+          urlParams: getDashboardUrlParams(['edit']),
+        },
+      })
+        .then(({ json }) => {
+          const cacheKey = json?.cache_key;
+          if (!cacheKey) {
+            throw new Error('No image URL in response');
+          }
+          const retryIntervalId = setInterval(() => {
+            fetchImageWithRetry(cacheKey);
+          }, RETRY_INTERVAL);
+          currentIntervalIds.current.push(retryIntervalId);
+          fetchImageWithRetry(cacheKey);
+        })
+        .catch(error => {
+          logging.error(error);
+          stopIntervals('failure');
+        })
+        .finally(() => {
+          logEvent?.(
+            format === DownloadScreenshotFormat.PNG
+              ? LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_IMAGE
+              : LOG_ACTIONS_DASHBOARD_DOWNLOAD_AS_PDF,
+          );
+        });
+    },
+    [
+      dashboardId,
+      anchor,
+      activeTabs,
+      dataMask,
+      addInfoToast,
+      stopIntervals,
+      logEvent,
+    ],
+  );
+
+  useEffect(
+    () => () => {
+      if (currentIntervalIds.current.length > 0) {
+        stopIntervals();
+      }
+      currentIntervalIds.current = [];
+    },
+    [stopIntervals],
+  );
+
+  return downloadScreenshot;
+};
diff --git a/superset-frontend/src/dashboard/styles.ts b/superset-frontend/src/dashboard/styles.ts
index 0aae5aa4ff..f3f0d753bf 100644
--- a/superset-frontend/src/dashboard/styles.ts
+++ b/superset-frontend/src/dashboard/styles.ts
@@ -103,10 +103,10 @@ export const filterCardPopoverStyle = (theme: SupersetTheme) => css`
 `;
 
 export const chartContextMenuStyles = (theme: SupersetTheme) => css`
-  .ant-dropdown-menu.chart-context-menu {
+  .antd5-dropdown-menu.chart-context-menu {
     min-width: ${theme.gridUnit * 43}px;
   }
-  .ant-dropdown-menu-submenu.chart-context-submenu {
+  .antd5-dropdown-menu-submenu.chart-context-submenu {
     max-width: ${theme.gridUnit * 60}px;
     min-width: ${theme.gridUnit * 40}px;
   }
@@ -117,7 +117,7 @@ export const focusStyle = (theme: SupersetTheme) => css`
   .ant-tabs-tabpane,
   .ant-tabs-tab-btn,
   .superset-button,
-  .superset-button.ant-dropdown-trigger,
+  .superset-button.antd5-dropdown-trigger,
   .header-controls span {
     &:focus-visible {
       box-shadow: 0 0 0 2px ${theme.colors.primary.dark1};
diff --git a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
index 97bd8808eb..69718f55fb 100644
--- a/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
+++ b/superset-frontend/src/explore/components/ExploreChartHeader/index.jsx
@@ -31,6 +31,9 @@ import { sliceUpdated } from 'src/explore/actions/exploreActions';
 import { PageHeaderWithActions } from 'src/components/PageHeaderWithActions';
 import { setSaveChartModalVisibility } from 'src/explore/actions/saveModalActions';
 import { applyColors, resetColors } from 'src/utils/colorScheme';
+import ReportModal from 'src/features/reports/ReportModal';
+import DeleteModal from 'src/components/DeleteModal';
+import { deleteActiveReport } from 'src/features/reports/ReportModal/actions';
 import { useExploreAdditionalActionsMenu } from '../useExploreAdditionalActionsMenu';
 import { useExploreMetadataBar } from './useExploreMetadataBar';
 
@@ -86,6 +89,8 @@ export const ExploreChartHeader = ({
   const dispatch = useDispatch();
   const { latestQueryFormData, sliceFormData } = chart;
   const [isPropertiesModalOpen, setIsPropertiesModalOpen] = useState(false);
+  const [isReportModalOpen, setIsReportModalOpen] = useState(false);
+  const [currentReportDeleting, setCurrentReportDeleting] = useState(null);
   const updateCategoricalNamespace = async () => {
     const { dashboards } = metadata || {};
     const dashboard =
@@ -128,6 +133,14 @@ export const ExploreChartHeader = ({
     setIsPropertiesModalOpen(false);
   };
 
+  const showReportModal = () => {
+    setIsReportModalOpen(true);
+  };
+
+  const closeReportModal = () => {
+    setIsReportModalOpen(false);
+  };
+
   const showModal = useCallback(() => {
     dispatch(setSaveChartModalVisibility(true));
   }, [dispatch]);
@@ -139,6 +152,11 @@ export const ExploreChartHeader = ({
     [dispatch],
   );
 
+  const handleReportDelete = async report => {
+    await dispatch(deleteActiveReport(report));
+    setCurrentReportDeleting(null);
+  };
+
   const history = useHistory();
   const { redirectSQLLab } = actions;
 
@@ -158,6 +176,8 @@ export const ExploreChartHeader = ({
       openPropertiesModal,
       ownState,
       metadata?.dashboards,
+      showReportModal,
+      setCurrentReportDeleting,
     );
 
   const metadataBar = useExploreMetadataBar(metadata, slice);
@@ -229,8 +249,8 @@ export const ExploreChartHeader = ({
         }
         additionalActionsMenu={menu}
         menuDropdownProps={{
-          visible: isDropdownVisible,
-          onVisibleChange: setIsDropdownVisible,
+          open: isDropdownVisible,
+          onOpenChange: setIsDropdownVisible,
         }}
       />
       {isPropertiesModalOpen && (
@@ -241,6 +261,33 @@ export const ExploreChartHeader = ({
           slice={slice}
         />
       )}
+
+      <ReportModal
+        userId={user.userId}
+        show={isReportModalOpen}
+        onHide={closeReportModal}
+        userEmail={user.email}
+        dashboardId={dashboardId}
+        chart={chart}
+        creationMethod="charts"
+      />
+
+      {currentReportDeleting && (
+        <DeleteModal
+          description={t(
+            'This action will permanently delete %s.',
+            currentReportDeleting?.name,
+          )}
+          onConfirm={() => {
+            if (currentReportDeleting) {
+              handleReportDelete(currentReportDeleting);
+            }
+          }}
+          onHide={() => setCurrentReportDeleting(null)}
+          open
+          title={t('Delete Report?')}
+        />
+      )}
     </>
   );
 };
diff --git a/superset-frontend/src/explore/components/ExportToCSVDropdown/index.tsx b/superset-frontend/src/explore/components/ExportToCSVDropdown/index.tsx
index 2490ee7544..cc7fd23d30 100644
--- a/superset-frontend/src/explore/components/ExportToCSVDropdown/index.tsx
+++ b/superset-frontend/src/explore/components/ExportToCSVDropdown/index.tsx
@@ -20,7 +20,7 @@ import { ReactChild, useCallback, Key } from 'react';
 
 import { t, styled } from '@superset-ui/core';
 import Icons from 'src/components/Icons';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 
 enum MenuKeys {
@@ -67,9 +67,9 @@ export const ExportToCSVDropdown = ({
   );
 
   return (
-    <AntdDropdown
+    <Dropdown
       trigger={['click']}
-      overlay={
+      dropdownRender={() => (
         <Menu onClick={handleMenuClick} selectable={false}>
           <Menu.Item key={MenuKeys.ExportOriginal}>
             <MenuItemContent>
@@ -84,9 +84,9 @@ export const ExportToCSVDropdown = ({
             </MenuItemContent>
           </Menu.Item>
         </Menu>
-      }
+      )}
     >
       {children}
-    </AntdDropdown>
+    </Dropdown>
   );
 };
diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.jsx
index 130e96443a..92694dc1ae 100644
--- a/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.jsx
+++ b/superset-frontend/src/explore/components/controls/DatasourceControl/DatasourceControl.test.jsx
@@ -107,6 +107,12 @@ describe('DatasourceControl', () => {
       expect(screen.queryAllByRole('menuitem')).toHaveLength(3);
     });
 
+    // Close the menu
+    userEvent.click(document.body);
+    await waitFor(() => {
+      expect(screen.queryAllByRole('menuitem')).toHaveLength(0);
+    });
+
     rerender(<DatasourceControl {...{ ...props, isEditable: false }} />, {
       useRedux: true,
       useRouter: true,
diff --git a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
index 4e5063b1e4..3070cdc7d1 100644
--- a/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
+++ b/superset-frontend/src/explore/components/controls/DatasourceControl/index.jsx
@@ -29,7 +29,7 @@ import {
 } from '@superset-ui/core';
 import { getTemporalColumns } from '@superset-ui/chart-controls';
 import { getUrlParam } from 'src/utils/urlUtils';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import { Tooltip } from 'src/components/Tooltip';
 import Icons from 'src/components/Icons';
@@ -82,12 +82,8 @@ const Styles = styled.div`
   .error-alert {
     margin: ${({ theme }) => 2 * theme.gridUnit}px;
   }
-  .ant-dropdown-trigger {
+  .antd5-dropdown-trigger {
     margin-left: ${({ theme }) => 2 * theme.gridUnit}px;
-    box-shadow: none;
-    &:active {
-      box-shadow: none;
-    }
   }
   .btn-group .open .dropdown-toggle {
     box-shadow: none;
@@ -410,8 +406,8 @@ class DatasourceControl extends PureComponent {
           {extra?.warning_markdown && (
             <WarningIconWithTooltip warningMarkdown={extra.warning_markdown} />
           )}
-          <AntdDropdown
-            overlay={
+          <Dropdown
+            dropdownRender={() =>
               datasource.type === DatasourceType.Query
                 ? queryDatasourceMenu
                 : defaultDatasourceMenu
@@ -423,7 +419,7 @@ class DatasourceControl extends PureComponent {
               className="datasource-modal-trigger"
               data-test="datasource-menu-trigger"
             />
-          </AntdDropdown>
+          </Dropdown>
         </div>
         {/* missing dataset */}
         {isMissingDatasource && isMissingParams && (
diff --git a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx
index 21cf69cec4..6f7b64bc2e 100644
--- a/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx
+++ b/superset-frontend/src/explore/components/useExploreAdditionalActionsMenu/index.jsx
@@ -108,13 +108,6 @@ export const MenuTrigger = styled(Button)`
   `}
 `;
 
-const iconReset = css`
-  .ant-dropdown-menu-item > & > .anticon:first-child {
-    margin-right: 0;
-    vertical-align: 0;
-  }
-`;
-
 export const useExploreAdditionalActionsMenu = (
   latestQueryFormData,
   canDownloadCSV,
@@ -123,6 +116,8 @@ export const useExploreAdditionalActionsMenu = (
   onOpenPropertiesModal,
   ownState,
   dashboards,
+  showReportModal,
+  setCurrentReportDeleting,
   ...rest
 ) => {
   const theme = useTheme();
@@ -330,14 +325,14 @@ export const useExploreAdditionalActionsMenu = (
             <>
               <Menu.Item
                 key={MENU_KEYS.EXPORT_TO_CSV}
-                icon={<Icons.FileOutlined css={iconReset} />}
+                icon={<Icons.FileOutlined />}
                 disabled={!canDownloadCSV}
               >
                 {t('Export to original .CSV')}
               </Menu.Item>
               <Menu.Item
                 key={MENU_KEYS.EXPORT_TO_CSV_PIVOTED}
-                icon={<Icons.FileOutlined css={iconReset} />}
+                icon={<Icons.FileOutlined />}
                 disabled={!canDownloadCSV}
               >
                 {t('Export to pivoted .CSV')}
@@ -346,7 +341,7 @@ export const useExploreAdditionalActionsMenu = (
           ) : (
             <Menu.Item
               key={MENU_KEYS.EXPORT_TO_CSV}
-              icon={<Icons.FileOutlined css={iconReset} />}
+              icon={<Icons.FileOutlined />}
               disabled={!canDownloadCSV}
             >
               {t('Export to .CSV')}
@@ -354,20 +349,20 @@ export const useExploreAdditionalActionsMenu = (
           )}
           <Menu.Item
             key={MENU_KEYS.EXPORT_TO_JSON}
-            icon={<Icons.FileOutlined css={iconReset} />}
+            icon={<Icons.FileOutlined />}
             disabled={!canDownloadCSV}
           >
             {t('Export to .JSON')}
           </Menu.Item>
           <Menu.Item
             key={MENU_KEYS.DOWNLOAD_AS_IMAGE}
-            icon={<Icons.FileImageOutlined css={iconReset} />}
+            icon={<Icons.FileImageOutlined />}
           >
             {t('Download as image')}
           </Menu.Item>
           <Menu.Item
             key={MENU_KEYS.EXPORT_TO_XLSX}
-            icon={<Icons.FileOutlined css={iconReset} />}
+            icon={<Icons.FileOutlined />}
             disabled={!canDownloadCSV}
           >
             {t('Export to Excel')}
@@ -403,28 +398,25 @@ export const useExploreAdditionalActionsMenu = (
         <Menu.Divider />
         {showReportSubMenu ? (
           <>
-            <Menu.SubMenu title={t('Manage email report')}>
-              <HeaderReportDropDown
-                chart={chart}
-                setShowReportSubMenu={setShowReportSubMenu}
-                showReportSubMenu={showReportSubMenu}
-                setIsDropdownVisible={setIsDropdownVisible}
-                isDropdownVisible={isDropdownVisible}
-                useTextMenu
-              />
-            </Menu.SubMenu>
-            <Menu.Divider />
-          </>
-        ) : (
-          <Menu>
             <HeaderReportDropDown
+              submenuTitle={t('Manage email report')}
               chart={chart}
               setShowReportSubMenu={setShowReportSubMenu}
-              setIsDropdownVisible={setIsDropdownVisible}
-              isDropdownVisible={isDropdownVisible}
+              showReportSubMenu={showReportSubMenu}
+              showReportModal={showReportModal}
+              setCurrentReportDeleting={setCurrentReportDeleting}
               useTextMenu
             />
-          </Menu>
+            <Menu.Divider />
+          </>
+        ) : (
+          <HeaderReportDropDown
+            chart={chart}
+            setShowReportSubMenu={setShowReportSubMenu}
+            showReportModal={showReportModal}
+            setCurrentReportDeleting={setCurrentReportDeleting}
+            useTextMenu
+          />
         )}
         <Menu.Item key={MENU_KEYS.VIEW_QUERY}>
           <ModalTrigger
diff --git a/superset-frontend/src/features/charts/ChartCard.tsx b/superset-frontend/src/features/charts/ChartCard.tsx
index 9dd18d1a89..76554c00b8 100644
--- a/superset-frontend/src/features/charts/ChartCard.tsx
+++ b/superset-frontend/src/features/charts/ChartCard.tsx
@@ -24,7 +24,7 @@ import Chart from 'src/types/Chart';
 
 import ListViewCard from 'src/components/ListViewCard';
 import Label from 'src/components/Label';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import FaveStar from 'src/components/FaveStar';
 import FacePile from 'src/components/FacePile';
@@ -172,9 +172,9 @@ export default function ChartCard({
                 isStarred={favoriteStatus}
               />
             )}
-            <AntdDropdown overlay={menu}>
+            <Dropdown dropdownRender={() => menu}>
               <Icons.MoreVert iconColor={theme.colors.grayscale.base} />
-            </AntdDropdown>
+            </Dropdown>
           </ListViewCard.Actions>
         }
       />
diff --git a/superset-frontend/src/features/dashboards/DashboardCard.tsx b/superset-frontend/src/features/dashboards/DashboardCard.tsx
index 2b09233d96..dff8b638b9 100644
--- a/superset-frontend/src/features/dashboards/DashboardCard.tsx
+++ b/superset-frontend/src/features/dashboards/DashboardCard.tsx
@@ -26,7 +26,7 @@ import {
   SupersetClient,
 } from '@superset-ui/core';
 import { CardStyles } from 'src/views/CRUD/utils';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import ListViewCard from 'src/components/ListViewCard';
 import Icons from 'src/components/Icons';
@@ -179,9 +179,9 @@ function DashboardCard({
                 isStarred={favoriteStatus}
               />
             )}
-            <AntdDropdown overlay={menu}>
+            <Dropdown dropdownRender={() => menu}>
               <Icons.MoreVert iconColor={theme.colors.grayscale.base} />
-            </AntdDropdown>
+            </Dropdown>
           </ListViewCard.Actions>
         }
       />
diff --git a/superset-frontend/src/features/home/RightMenu.tsx b/superset-frontend/src/features/home/RightMenu.tsx
index 5dd8c0f883..a9518fdcbd 100644
--- a/superset-frontend/src/features/home/RightMenu.tsx
+++ b/superset-frontend/src/features/home/RightMenu.tsx
@@ -409,6 +409,7 @@ const RightMenu = ({
         {RightMenuExtension && <RightMenuExtension />}
         {!navbarRight.user_is_anonymous && showActionDropdown && (
           <StyledSubMenu
+            key="sub1"
             data-test="new-dropdown"
             title={
               <StyledI data-test="new-dropdown-icon" className="fa fa-plus" />
@@ -474,6 +475,7 @@ const RightMenu = ({
           </StyledSubMenu>
         )}
         <StyledSubMenu
+          key="sub3_settings"
           title={t('Settings')}
           icon={<Icons.TriangleDown iconSize="xl" />}
         >
diff --git a/superset-frontend/src/features/home/SavedQueries.tsx b/superset-frontend/src/features/home/SavedQueries.tsx
index fdc8cb8648..12683f581f 100644
--- a/superset-frontend/src/features/home/SavedQueries.tsx
+++ b/superset-frontend/src/features/home/SavedQueries.tsx
@@ -25,7 +25,7 @@ import github from 'react-syntax-highlighter/dist/cjs/styles/hljs/github';
 import { LoadingCards } from 'src/pages/Home';
 import { TableTab } from 'src/views/CRUD/types';
 import withToasts from 'src/components/MessageToasts/withToasts';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import { copyQueryLink, useListViewResource } from 'src/views/CRUD/hooks';
 import ListViewCard from 'src/components/ListViewCard';
@@ -315,11 +315,11 @@ const SavedQueries = ({
                         e.preventDefault();
                       }}
                     >
-                      <AntdDropdown overlay={renderMenu(q)}>
+                      <Dropdown dropdownRender={() => renderMenu(q)}>
                         <Icons.MoreVert
                           iconColor={theme.colors.grayscale.base}
                         />
-                      </AntdDropdown>
+                      </Dropdown>
                     </ListViewCard.Actions>
                   </QueryData>
                 }
diff --git a/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.test.tsx b/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.test.tsx
index 11fb215c35..51fa418689 100644
--- a/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.test.tsx
+++ b/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.test.tsx
@@ -19,14 +19,15 @@
 import userEvent from '@testing-library/user-event';
 import { render, screen, act } from 'spec/helpers/testing-library';
 import { FeatureFlag, isFeatureEnabled } from '@superset-ui/core';
+import { Menu } from 'src/components/Menu';
 import HeaderReportDropdown, { HeaderReportProps } from '.';
 
 const createProps = () => ({
   dashboardId: 1,
   useTextMenu: false,
-  isDropdownVisible: true,
-  setIsDropdownVisible: jest.fn,
   setShowReportSubMenu: jest.fn,
+  showReportModal: jest.fn,
+  setCurrentReportDeleting: jest.fn,
 });
 
 const stateWithOnlyUser = {
@@ -117,9 +118,9 @@ const stateWithUserAndReport = {
 
 function setup(props: HeaderReportProps, initialState = {}) {
   render(
-    <div>
+    <Menu>
       <HeaderReportDropdown {...props} />
-    </div>,
+    </Menu>,
     { useRedux: true, initialState },
   );
 }
@@ -147,95 +148,90 @@ describe('Header Report Dropdown', () => {
     act(() => {
       setup(mockedProps, stateWithUserAndReport);
     });
-    expect(screen.getByRole('button')).toBeInTheDocument();
+    expect(screen.getByRole('menuitem')).toBeInTheDocument();
   });
 
-  it('renders the dropdown correctly', () => {
+  it('renders the dropdown correctly', async () => {
     const mockedProps = createProps();
     act(() => {
       setup(mockedProps, stateWithUserAndReport);
     });
-    const emailReportModalButton = screen.getByRole('button');
-    userEvent.click(emailReportModalButton);
-    expect(screen.getByText('Email reports active')).toBeInTheDocument();
+    const emailReportModalButton = screen.getByRole('menuitem');
+    userEvent.hover(emailReportModalButton);
+    expect(await screen.findByText('Email reports active')).toBeInTheDocument();
     expect(screen.getByText('Edit email report')).toBeInTheDocument();
     expect(screen.getByText('Delete email report')).toBeInTheDocument();
   });
 
   it('opens an edit modal', async () => {
     const mockedProps = createProps();
+    mockedProps.showReportModal = jest.fn();
     act(() => {
       setup(mockedProps, stateWithUserAndReport);
     });
-    const emailReportModalButton = screen.getByRole('button');
+    const emailReportModalButton = screen.getByRole('menuitem');
     userEvent.click(emailReportModalButton);
-    const editModal = screen.getByText('Edit email report');
+    const editModal = await screen.findByText('Edit email report');
     userEvent.click(editModal);
-    const textBoxes = await screen.findAllByText('Edit email report');
-    expect(textBoxes).toHaveLength(2);
+    expect(mockedProps.showReportModal).toHaveBeenCalled();
   });
 
-  it('opens a delete modal', () => {
+  it('opens a delete modal', async () => {
     const mockedProps = createProps();
+    mockedProps.setCurrentReportDeleting = jest.fn();
     act(() => {
       setup(mockedProps, stateWithUserAndReport);
     });
-    const emailReportModalButton = screen.getByRole('button');
+    const emailReportModalButton = screen.getByRole('menuitem');
     userEvent.click(emailReportModalButton);
-    const deleteModal = screen.getByText('Delete email report');
+    const deleteModal = await screen.findByText('Delete email report');
     userEvent.click(deleteModal);
-    expect(screen.getByText('Delete Report?')).toBeInTheDocument();
+    expect(mockedProps.setCurrentReportDeleting).toHaveBeenCalled();
   });
 
-  it('renders a new report modal if there is no report', () => {
-    const mockedProps = createProps();
-    act(() => {
-      setup(mockedProps, stateWithOnlyUser);
-    });
-    const emailReportModalButton = screen.getByRole('button');
-    userEvent.click(emailReportModalButton);
-    expect(screen.getByText('Schedule a new email report')).toBeInTheDocument();
-  });
-
-  it('renders Manage Email Reports Menu if textMenu is set to true and there is a report', () => {
+  it('renders Manage Email Reports Menu if textMenu is set to true and there is a report', async () => {
     let mockedProps = createProps();
     mockedProps = {
       ...mockedProps,
       useTextMenu: true,
-      isDropdownVisible: true,
     };
     act(() => {
       setup(mockedProps, stateWithUserAndReport);
     });
-    expect(screen.getByText('Email reports active')).toBeInTheDocument();
+    userEvent.click(screen.getByRole('menuitem'));
+    expect(await screen.findByText('Email reports active')).toBeInTheDocument();
     expect(screen.getByText('Edit email report')).toBeInTheDocument();
     expect(screen.getByText('Delete email report')).toBeInTheDocument();
   });
 
-  it('renders Schedule Email Reports if textMenu is set to true and there is a report', () => {
+  it('renders Schedule Email Reports if textMenu is set to true and there is a report', async () => {
     let mockedProps = createProps();
     mockedProps = {
       ...mockedProps,
       useTextMenu: true,
-      isDropdownVisible: true,
     };
     act(() => {
       setup(mockedProps, stateWithOnlyUser);
     });
-    expect(screen.getByText('Set up an email report')).toBeInTheDocument();
+    userEvent.click(screen.getByRole('menuitem'));
+    expect(
+      await screen.findByText('Set up an email report'),
+    ).toBeInTheDocument();
   });
 
-  it('renders Schedule Email Reports as long as user has permission through any role', () => {
+  it('renders Schedule Email Reports as long as user has permission through any role', async () => {
     let mockedProps = createProps();
     mockedProps = {
       ...mockedProps,
       useTextMenu: true,
-      isDropdownVisible: true,
     };
     act(() => {
       setup(mockedProps, stateWithNonAdminUser);
     });
-    expect(screen.getByText('Set up an email report')).toBeInTheDocument();
+    userEvent.click(screen.getByRole('menuitem'));
+    expect(
+      await screen.findByText('Set up an email report'),
+    ).toBeInTheDocument();
   });
 
   it('do not render Schedule Email Reports if user no permission', () => {
@@ -243,11 +239,12 @@ describe('Header Report Dropdown', () => {
     mockedProps = {
       ...mockedProps,
       useTextMenu: true,
-      isDropdownVisible: true,
     };
     act(() => {
       setup(mockedProps, stateWithNonMenuAccessOnManage);
     });
+
+    userEvent.click(screen.getByRole('menu'));
     expect(
       screen.queryByText('Set up an email report'),
     ).not.toBeInTheDocument();
diff --git a/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.tsx b/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.tsx
index 66a8195f1f..9f05232809 100644
--- a/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.tsx
+++ b/superset-frontend/src/features/reports/ReportModal/HeaderReportDropdown/index.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { useState, useEffect } from 'react';
+import { ReactNode, useEffect } from 'react';
 import { useSelector, useDispatch } from 'react-redux';
 import { isEmpty } from 'lodash';
 import {
@@ -24,7 +24,6 @@ import {
   SupersetTheme,
   css,
   styled,
-  useTheme,
   FeatureFlag,
   isFeatureEnabled,
   getExtensionsRegistry,
@@ -36,15 +35,11 @@ import { AlertObject } from 'src/features/alerts/types';
 import { Menu } from 'src/components/Menu';
 import Checkbox from 'src/components/Checkbox';
 import { noOp } from 'src/utils/common';
-import { NoAnimationDropdown } from 'src/components/Dropdown';
-import DeleteModal from 'src/components/DeleteModal';
-import ReportModal from 'src/features/reports/ReportModal';
 import { ChartState } from 'src/explore/types';
 import { UserWithPermissionsAndRoles } from 'src/types/bootstrapTypes';
 import {
   fetchUISpecificReport,
   toggleActive,
-  deleteActiveReport,
 } from 'src/features/reports/ReportModal/actions';
 import { reportSelector } from 'src/views/CRUD/hooks';
 import { MenuItemWithCheckboxContainer } from 'src/explore/components/useExploreAdditionalActionsMenu/index';
@@ -99,9 +94,10 @@ export interface HeaderReportProps {
   chart?: ChartState;
   useTextMenu?: boolean;
   setShowReportSubMenu?: (show: boolean) => void;
-  setIsDropdownVisible?: (visible: boolean) => void;
-  isDropdownVisible?: boolean;
   showReportSubMenu?: boolean;
+  submenuTitle?: string;
+  showReportModal: () => void;
+  setCurrentReportDeleting: (report: AlertObject | null) => void;
 }
 
 // Same instance to be used in useEffects
@@ -112,9 +108,9 @@ export default function HeaderReportDropDown({
   chart,
   useTextMenu = false,
   setShowReportSubMenu,
-  setIsDropdownVisible,
-  isDropdownVisible,
-  ...rest
+  submenuTitle,
+  showReportModal,
+  setCurrentReportDeleting,
 }: HeaderReportProps) {
   const dispatch = useDispatch();
   const report = useSelector<any, AlertObject>(state => {
@@ -156,22 +152,13 @@ export default function HeaderReportDropDown({
     return permissions.some(permission => permission.length > 0);
   };
 
-  const [currentReportDeleting, setCurrentReportDeleting] =
-    useState<AlertObject | null>(null);
-  const theme = useTheme();
   const prevDashboard = usePrevious(dashboardId);
-  const [showModal, setShowModal] = useState<boolean>(false);
   const toggleActiveKey = async (data: AlertObject, checked: boolean) => {
     if (data?.id) {
       dispatch(toggleActive(data, checked));
     }
   };
 
-  const handleReportDelete = async (report: AlertObject) => {
-    await dispatch(deleteActiveReport(report));
-    setCurrentReportDeleting(null);
-  };
-
   const shouldFetch =
     canAddReports() &&
     !!((dashboardId && prevDashboard !== dashboardId) || chart?.id);
@@ -191,12 +178,6 @@ export default function HeaderReportDropDown({
 
   const showReportSubMenu = report && setShowReportSubMenu && canAddReports();
 
-  // @z-index-below-dashboard-header (100) - 1 = 99
-  const dropdownOverlayStyle = {
-    zIndex: 99,
-    animationDuration: '0s',
-  };
-
   useEffect(() => {
     if (showReportSubMenu) {
       setShowReportSubMenu(true);
@@ -206,22 +187,16 @@ export default function HeaderReportDropDown({
   }, [report]);
 
   const handleShowMenu = () => {
-    if (setIsDropdownVisible) {
-      setIsDropdownVisible(false);
-      setShowModal(true);
-    }
+    showReportModal();
   };
 
   const handleDeleteMenuClick = () => {
-    if (setIsDropdownVisible) {
-      setIsDropdownVisible(false);
-      setCurrentReportDeleting(report);
-    }
+    setCurrentReportDeleting(report);
   };
 
   const textMenu = () =>
     isEmpty(report) ? (
-      <Menu selectable={false} {...rest} css={onMenuHover}>
+      <Menu.SubMenu title={submenuTitle} css={onMenuHover}>
         <Menu.Item onClick={handleShowMenu}>
           {DropdownItemExtension ? (
             <StyledDropdownItemWithIcon>
@@ -233,9 +208,14 @@ export default function HeaderReportDropDown({
           )}
         </Menu.Item>
         <Menu.Divider />
-      </Menu>
+      </Menu.SubMenu>
     ) : (
-      <Menu selectable={false} css={{ border: 'none' }}>
+      <Menu.SubMenu
+        title={submenuTitle}
+        css={css`
+          border: none;
+        `}
+      >
         <Menu.Item
           css={onMenuItemHover}
           onClick={() => toggleActiveKey(report, !isReportActive)}
@@ -251,10 +231,15 @@ export default function HeaderReportDropDown({
         <Menu.Item css={onMenuItemHover} onClick={handleDeleteMenuClick}>
           {t('Delete email report')}
         </Menu.Item>
-      </Menu>
+      </Menu.SubMenu>
     );
-  const menu = () => (
-    <Menu selectable={false} css={{ width: '200px' }}>
+  const menu = (title: ReactNode) => (
+    <Menu.SubMenu
+      title={title}
+      css={css`
+        width: 200px;
+      `}
+    >
       <Menu.Item>
         {t('Email reports active')}
         <Switch
@@ -262,10 +247,12 @@ export default function HeaderReportDropDown({
           checked={isReportActive}
           onClick={(checked: boolean) => toggleActiveKey(report, checked)}
           size="small"
-          css={{ marginLeft: theme.gridUnit * 2 }}
+          css={theme => css`
+            margin-left: ${theme.gridUnit * 2}px;
+          `}
         />
       </Menu.Item>
-      <Menu.Item onClick={() => setShowModal(true)}>
+      <Menu.Item onClick={() => showReportModal()}>
         {t('Edit email report')}
       </Menu.Item>
       <Menu.Item
@@ -274,7 +261,7 @@ export default function HeaderReportDropDown({
       >
         {t('Delete email report')}
       </Menu.Item>
-    </Menu>
+    </Menu.SubMenu>
   );
 
   const iconMenu = () =>
@@ -284,65 +271,12 @@ export default function HeaderReportDropDown({
         title={t('Schedule email report')}
         tabIndex={0}
         className="action-button action-schedule-report"
-        onClick={() => setShowModal(true)}
+        onClick={() => showReportModal()}
       >
         <Icons.Calendar />
       </span>
     ) : (
-      <>
-        <NoAnimationDropdown
-          overlay={menu()}
-          overlayStyle={dropdownOverlayStyle}
-          trigger={['click']}
-          getPopupContainer={(triggerNode: any) =>
-            triggerNode.closest('.action-button')
-          }
-        >
-          <span
-            role="button"
-            className="action-button action-schedule-report"
-            tabIndex={0}
-          >
-            <Icons.Calendar />
-          </span>
-        </NoAnimationDropdown>
-      </>
+      menu(<Icons.Calendar />)
     );
-
-  return (
-    <>
-      {canAddReports() && (
-        <>
-          <ReportModal
-            userId={user.userId}
-            show={showModal}
-            onHide={() => setShowModal(false)}
-            userEmail={user.email}
-            dashboardId={dashboardId}
-            chart={chart}
-            creationMethod={
-              dashboardId ? CreationMethod.Dashboards : CreationMethod.Charts
-            }
-          />
-          {isDropdownVisible ? (useTextMenu ? textMenu() : iconMenu()) : null}
-          {currentReportDeleting && (
-            <DeleteModal
-              description={t(
-                'This action will permanently delete %s.',
-                currentReportDeleting?.name,
-              )}
-              onConfirm={() => {
-                if (currentReportDeleting) {
-                  handleReportDelete(currentReportDeleting);
-                }
-              }}
-              onHide={() => setCurrentReportDeleting(null)}
-              open
-              title={t('Delete Report?')}
-            />
-          )}
-        </>
-      )}
-    </>
-  );
+  return <>{canAddReports() && (useTextMenu ? textMenu() : iconMenu())}</>;
 }
diff --git a/superset-frontend/src/features/tags/TagCard.tsx b/superset-frontend/src/features/tags/TagCard.tsx
index 6b68868695..092ba90f12 100644
--- a/superset-frontend/src/features/tags/TagCard.tsx
+++ b/superset-frontend/src/features/tags/TagCard.tsx
@@ -19,7 +19,7 @@
 import { Link } from 'react-router-dom';
 import { isFeatureEnabled, FeatureFlag, t, useTheme } from '@superset-ui/core';
 import { CardStyles } from 'src/views/CRUD/utils';
-import { AntdDropdown } from 'src/components';
+import { Dropdown } from 'src/components/Dropdown';
 import { Menu } from 'src/components/Menu';
 import ConfirmStatusChange from 'src/components/ConfirmStatusChange';
 import ListViewCard from 'src/components/ListViewCard';
@@ -108,9 +108,9 @@ function TagCard({
               e.preventDefault();
             }}
           >
-            <AntdDropdown overlay={menu}>
+            <Dropdown dropdownRender={() => menu}>
               <Icons.MoreVert iconColor={theme.colors.grayscale.base} />
-            </AntdDropdown>
+            </Dropdown>
           </ListViewCard.Actions>
         }
       />