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

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


The following commit(s) were added to refs/heads/6.0 by this push:
     new d94c92db01 fix(dashboard): normalize spacings and background colors 
(#35001)
d94c92db01 is described below

commit d94c92db016f96cd8c6ab02fea53c75af4203c54
Author: Gabriel Torres Ruiz <[email protected]>
AuthorDate: Fri Sep 5 23:13:42 2025 -0300

    fix(dashboard): normalize spacings and background colors (#35001)
    
    (cherry picked from commit 0fce5ecfa5295f9978f873f2930ca972b868cfe5)
---
 .../src/components/Tabs/Tabs.test.tsx              | 306 +++++++++++++++++++
 .../superset-ui-core/src/components/Tabs/Tabs.tsx  |   6 +-
 .../src/dashboard/actions/dashboardState.js        |   5 +
 .../BuilderComponentPane.test.tsx                  |   9 +-
 .../components/BuilderComponentPane/index.tsx      | 152 +++++-----
 .../DashboardBuilder/DashboardBuilder.tsx          |  16 +-
 .../DashboardBuilder/DashboardContainer.tsx        |   8 +-
 .../DashboardBuilder/DashboardWrapper.tsx          |   6 +-
 .../dashboard/components/DashboardBuilder/state.ts |  29 +-
 .../gridComponents/{ => Chart}/Chart.jsx           |  20 +-
 .../gridComponents/{ => Chart}/Chart.test.jsx      |   2 +-
 .../Chart/index.js}                                |  19 +-
 .../{ => ChartHolder}/ChartHolder.test.tsx         |   8 +-
 .../{ => ChartHolder}/ChartHolder.tsx              |   0
 .../ChartHolder/index.ts}                          |  19 +-
 .../gridComponents/{ => Column}/Column.jsx         |   0
 .../gridComponents/{ => Column}/Column.test.jsx    |   2 +-
 .../Column/index.js}                               |  19 +-
 .../gridComponents/{ => Divider}/Divider.jsx       |   8 +-
 .../gridComponents/{ => Divider}/Divider.test.jsx  |   2 +-
 .../Divider/index.js}                              |  19 +-
 .../DynamicComponent/DynamicComponent.test.tsx     | 329 +++++++++++++++++++++
 .../{ => DynamicComponent}/DynamicComponent.tsx    |  34 +--
 .../DynamicComponent/index.ts}                     |  19 +-
 .../gridComponents/{ => Header}/Header.jsx         |   0
 .../gridComponents/{ => Header}/Header.test.jsx    |   2 +-
 .../Header/index.js}                               |  19 +-
 .../gridComponents/{ => Markdown}/Markdown.jsx     |   0
 .../{ => Markdown}/Markdown.test.jsx               |   2 +-
 .../Markdown/index.js}                             |  19 +-
 .../components/gridComponents/{ => Row}/Row.jsx    |   2 +-
 .../gridComponents/{ => Row}/Row.test.jsx          |   2 +-
 .../Row/index.js}                                  |  19 +-
 .../components/gridComponents/Tab.test.jsx         | 141 ---------
 .../components/gridComponents/{ => Tab}/Tab.jsx    |   0
 .../gridComponents/{ => Tab}/Tab.test.tsx          |   2 +-
 .../Tab/index.js}                                  |  20 +-
 .../components/gridComponents/Tabs.test.jsx        | 203 -------------
 .../components/gridComponents/{ => Tabs}/Tabs.jsx  | 159 +++++-----
 .../gridComponents/{ => Tabs}/Tabs.test.tsx        |  33 ++-
 .../Tabs/index.js}                                 |  19 +-
 .../TabsRenderer/TabsRenderer.test.tsx             | 201 +++++++++++++
 .../gridComponents/TabsRenderer/TabsRenderer.tsx   | 121 ++++++++
 .../TabsRenderer/index.ts}                         |  20 +-
 .../dashboard/components/gridComponents/index.js   |  10 -
 .../src/dashboard/reducers/dashboardState.js       |   7 +
 .../src/dashboard/reducers/dashboardState.test.js  |  17 ++
 .../src/dashboard/reducers/dashboardState.test.ts  | 140 ++++++---
 superset-frontend/src/dashboard/types.ts           |   1 +
 49 files changed, 1383 insertions(+), 813 deletions(-)

diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.test.tsx 
b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.test.tsx
new file mode 100644
index 0000000000..e01556f336
--- /dev/null
+++ 
b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.test.tsx
@@ -0,0 +1,306 @@
+/**
+ * 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 { fireEvent, render } from '@superset-ui/core/spec';
+import Tabs, { EditableTabs, LineEditableTabs } from './Tabs';
+
+describe('Tabs', () => {
+  const defaultItems = [
+    {
+      key: '1',
+      label: 'Tab 1',
+      children: <div data-testid="tab1-content">Tab 1 content</div>,
+    },
+    {
+      key: '2',
+      label: 'Tab 2',
+      children: <div data-testid="tab2-content">Tab 2 content</div>,
+    },
+    {
+      key: '3',
+      label: 'Tab 3',
+      children: <div data-testid="tab3-content">Tab 3 content</div>,
+    },
+  ];
+
+  describe('Basic Tabs', () => {
+    it('should render tabs with default props', () => {
+      const { getByText, container } = render(<Tabs items={defaultItems} />);
+
+      expect(getByText('Tab 1')).toBeInTheDocument();
+      expect(getByText('Tab 2')).toBeInTheDocument();
+      expect(getByText('Tab 3')).toBeInTheDocument();
+
+      const activeTabContent = container.querySelector(
+        '.ant-tabs-tabpane-active',
+      );
+
+      expect(activeTabContent).toBeDefined();
+      expect(
+        activeTabContent?.querySelector('[data-testid="tab1-content"]'),
+      ).toBeDefined();
+    });
+
+    it('should render tabs component structure', () => {
+      const { container } = render(<Tabs items={defaultItems} />);
+      const tabsElement = container.querySelector('.ant-tabs');
+      const tabsNav = container.querySelector('.ant-tabs-nav');
+      const tabsContent = container.querySelector('.ant-tabs-content-holder');
+
+      expect(tabsElement).toBeDefined();
+      expect(tabsNav).toBeDefined();
+      expect(tabsContent).toBeDefined();
+    });
+
+    it('should apply default tabBarStyle with padding', () => {
+      const { container } = render(<Tabs items={defaultItems} />);
+      const tabsNav = container.querySelector('.ant-tabs-nav') as HTMLElement;
+
+      // Check that tabBarStyle is applied (default padding is added)
+      expect(tabsNav?.style?.paddingLeft).toBeDefined();
+    });
+
+    it('should merge custom tabBarStyle with defaults', () => {
+      const customStyle = { paddingRight: '20px', backgroundColor: 'red' };
+      const { container } = render(
+        <Tabs items={defaultItems} tabBarStyle={customStyle} />,
+      );
+      const tabsNav = container.querySelector('.ant-tabs-nav') as HTMLElement;
+
+      expect(tabsNav?.style?.paddingLeft).toBeDefined();
+      expect(tabsNav?.style?.paddingRight).toBe('20px');
+      expect(tabsNav?.style?.backgroundColor).toBe('red');
+    });
+
+    it('should handle allowOverflow prop', () => {
+      const { container: allowContainer } = render(
+        <Tabs items={defaultItems} allowOverflow />,
+      );
+      const { container: disallowContainer } = render(
+        <Tabs items={defaultItems} allowOverflow={false} />,
+      );
+
+      expect(allowContainer.querySelector('.ant-tabs')).toBeDefined();
+      expect(disallowContainer.querySelector('.ant-tabs')).toBeDefined();
+    });
+
+    it('should disable animation by default', () => {
+      const { container } = render(<Tabs items={defaultItems} />);
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement?.className).not.toContain('ant-tabs-animated');
+    });
+
+    it('should handle tab change events', () => {
+      const onChangeMock = jest.fn();
+      const { getByText } = render(
+        <Tabs items={defaultItems} onChange={onChangeMock} />,
+      );
+
+      fireEvent.click(getByText('Tab 2'));
+
+      expect(onChangeMock).toHaveBeenCalledWith('2');
+    });
+
+    it('should pass through additional props to Antd Tabs', () => {
+      const onTabClickMock = jest.fn();
+      const { getByText } = render(
+        <Tabs
+          items={defaultItems}
+          onTabClick={onTabClickMock}
+          size="large"
+          centered
+        />,
+      );
+
+      fireEvent.click(getByText('Tab 2'));
+
+      expect(onTabClickMock).toHaveBeenCalled();
+    });
+  });
+
+  describe('EditableTabs', () => {
+    it('should render with editable features', () => {
+      const { container } = render(<EditableTabs items={defaultItems} />);
+
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement?.className).toContain('ant-tabs-card');
+      expect(tabsElement?.className).toContain('ant-tabs-editable-card');
+    });
+
+    it('should handle onEdit callback for add/remove actions', () => {
+      const onEditMock = jest.fn();
+      const itemsWithRemove = defaultItems.map(item => ({
+        ...item,
+        closable: true,
+      }));
+
+      const { container } = render(
+        <EditableTabs items={itemsWithRemove} onEdit={onEditMock} />,
+      );
+
+      const removeButton = container.querySelector('.ant-tabs-tab-remove');
+      expect(removeButton).toBeDefined();
+
+      fireEvent.click(removeButton!);
+      expect(onEditMock).toHaveBeenCalledWith(expect.any(String), 'remove');
+    });
+
+    it('should have default props set correctly', () => {
+      expect(EditableTabs.defaultProps?.type).toBe('editable-card');
+      expect(EditableTabs.defaultProps?.animated).toEqual({
+        inkBar: true,
+        tabPane: false,
+      });
+    });
+  });
+
+  describe('LineEditableTabs', () => {
+    it('should render as line-style editable tabs', () => {
+      const { container } = render(<LineEditableTabs items={defaultItems} />);
+
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement?.className).toContain('ant-tabs-card');
+      expect(tabsElement?.className).toContain('ant-tabs-editable-card');
+    });
+
+    it('should render with line-specific styling', () => {
+      const { container } = render(<LineEditableTabs items={defaultItems} />);
+
+      const inkBar = container.querySelector('.ant-tabs-ink-bar');
+      expect(inkBar).toBeDefined();
+    });
+  });
+
+  describe('TabPane Legacy Support', () => {
+    it('should support TabPane component access', () => {
+      expect(Tabs.TabPane).toBeDefined();
+      expect(EditableTabs.TabPane).toBeDefined();
+      expect(LineEditableTabs.TabPane).toBeDefined();
+    });
+
+    it('should render using legacy TabPane syntax', () => {
+      const { getByText, container } = render(
+        <Tabs>
+          <Tabs.TabPane tab="Legacy Tab 1" key="1">
+            <div data-testid="legacy-content-1">Legacy content 1</div>
+          </Tabs.TabPane>
+          <Tabs.TabPane tab="Legacy Tab 2" key="2">
+            <div data-testid="legacy-content-2">Legacy content 2</div>
+          </Tabs.TabPane>
+        </Tabs>,
+      );
+
+      expect(getByText('Legacy Tab 1')).toBeInTheDocument();
+      expect(getByText('Legacy Tab 2')).toBeInTheDocument();
+
+      const activeTabContent = container.querySelector(
+        '.ant-tabs-tabpane-active [data-testid="legacy-content-1"]',
+      );
+
+      expect(activeTabContent).toBeDefined();
+      expect(activeTabContent?.textContent).toBe('Legacy content 1');
+    });
+  });
+
+  describe('Edge Cases', () => {
+    it('should handle empty items array', () => {
+      const { container } = render(<Tabs items={[]} />);
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement).toBeDefined();
+    });
+
+    it('should handle undefined items', () => {
+      const { container } = render(<Tabs />);
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement).toBeDefined();
+    });
+
+    it('should handle tabs with no content', () => {
+      const itemsWithoutContent = [
+        { key: '1', label: 'Tab 1' },
+        { key: '2', label: 'Tab 2' },
+      ];
+
+      const { getByText } = render(<Tabs items={itemsWithoutContent} />);
+
+      expect(getByText('Tab 1')).toBeInTheDocument();
+      expect(getByText('Tab 2')).toBeInTheDocument();
+    });
+
+    it('should handle allowOverflow default value', () => {
+      const { container } = render(<Tabs items={defaultItems} />);
+      expect(container.querySelector('.ant-tabs')).toBeDefined();
+    });
+  });
+
+  describe('Accessibility', () => {
+    it('should render with proper ARIA roles', () => {
+      const { container } = render(<Tabs items={defaultItems} />);
+
+      const tablist = container.querySelector('[role="tablist"]');
+      const tabs = container.querySelectorAll('[role="tab"]');
+
+      expect(tablist).toBeDefined();
+      expect(tabs.length).toBe(3);
+    });
+
+    it('should support keyboard navigation', () => {
+      const { container, getByText } = render(<Tabs items={defaultItems} />);
+
+      const firstTab = container.querySelector('[role="tab"]');
+      const secondTab = getByText('Tab 2');
+
+      if (firstTab) {
+        fireEvent.keyDown(firstTab, { key: 'ArrowRight', code: 'ArrowRight' });
+      }
+
+      fireEvent.click(secondTab);
+
+      expect(secondTab).toBeInTheDocument();
+    });
+  });
+
+  describe('Styling Integration', () => {
+    it('should accept and apply custom CSS classes', () => {
+      const { container } = render(
+        <Tabs items={defaultItems} className="custom-tabs-class" />,
+      );
+
+      const tabsElement = container.querySelector('.ant-tabs');
+
+      expect(tabsElement?.className).toContain('custom-tabs-class');
+    });
+
+    it('should accept and apply custom styles', () => {
+      const customStyle = { minHeight: '200px' };
+      const { container } = render(
+        <Tabs items={defaultItems} style={customStyle} />,
+      );
+
+      const tabsElement = container.querySelector('.ant-tabs') as HTMLElement;
+
+      expect(tabsElement?.style?.minHeight).toBe('200px');
+    });
+  });
+});
diff --git 
a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx 
b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
index 5ea5c0e509..d834cd05f2 100644
--- a/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
+++ b/superset-frontend/packages/superset-ui-core/src/components/Tabs/Tabs.tsx
@@ -29,14 +29,18 @@ export interface TabsProps extends AntdTabsProps {
 const StyledTabs = ({
   animated = false,
   allowOverflow = true,
+  tabBarStyle,
   ...props
 }: TabsProps) => {
   const theme = useTheme();
+  const defaultTabBarStyle = { paddingLeft: theme.sizeUnit * 4 };
+  const mergedStyle = { ...defaultTabBarStyle, ...tabBarStyle };
+
   return (
     <AntdTabs
       animated={animated}
       {...props}
-      tabBarStyle={{ paddingLeft: theme.sizeUnit * 4 }}
+      tabBarStyle={mergedStyle}
       css={theme => css`
         overflow: ${allowOverflow ? 'visible' : 'hidden'};
 
diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js 
b/superset-frontend/src/dashboard/actions/dashboardState.js
index 65bc7edc2a..ebd0a6b65b 100644
--- a/superset-frontend/src/dashboard/actions/dashboardState.js
+++ b/superset-frontend/src/dashboard/actions/dashboardState.js
@@ -79,6 +79,11 @@ import {
   getDynamicLabelsColors,
 } from '../../utils/colorScheme';
 
+export const TOGGLE_NATIVE_FILTERS_BAR = 'TOGGLE_NATIVE_FILTERS_BAR';
+export function toggleNativeFiltersBar(isOpen) {
+  return { type: TOGGLE_NATIVE_FILTERS_BAR, isOpen };
+}
+
 export const SET_UNSAVED_CHANGES = 'SET_UNSAVED_CHANGES';
 export function setUnsavedChanges(hasUnsavedChanges) {
   return { type: SET_UNSAVED_CHANGES, payload: { hasUnsavedChanges } };
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 
b/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
index 24a6a78d3f..fbfebe9a60 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
@@ -25,7 +25,14 @@ jest.mock('src/dashboard/containers/SliceAdder', () => () => 
(
 ));
 
 test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
+  render(<BuilderComponentPane topOffset={115} />, {
+    useRedux: true,
+    initialState: {
+      dashboardState: {
+        nativeFiltersBarOpen: false,
+      },
+    },
+  });
   const tabs = screen.getAllByRole('tab');
   expect(tabs).toHaveLength(2);
   expect(tabs[0]).toHaveTextContent('Charts');
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx 
b/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
index e8ff251d81..068fe3a9b8 100644
--- a/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
+++ b/superset-frontend/src/dashboard/components/BuilderComponentPane/index.tsx
@@ -19,9 +19,11 @@
 /* eslint-env browser */
 import { rgba } from 'emotion-rgba';
 import Tabs from '@superset-ui/core/components/Tabs';
-import { t, css, SupersetTheme } from '@superset-ui/core';
+import { t, css, SupersetTheme, useTheme } from '@superset-ui/core';
+import { useSelector } from 'react-redux';
 import SliceAdder from 'src/dashboard/containers/SliceAdder';
 import dashboardComponents from 
'src/visualizations/presets/dashboardComponents';
+import { useMemo } from 'react';
 import NewColumn from '../gridComponents/new/NewColumn';
 import NewDivider from '../gridComponents/new/NewDivider';
 import NewHeader from '../gridComponents/new/NewHeader';
@@ -37,81 +39,97 @@ const TABS_KEYS = {
   LAYOUT_ELEMENTS: 'LAYOUT_ELEMENTS',
 };
 
-const BuilderComponentPane = ({ topOffset = 0 }) => (
-  <div
-    data-test="dashboard-builder-sidepane"
-    css={css`
-      position: sticky;
-      right: 0;
-      top: ${topOffset}px;
-      height: calc(100vh - ${topOffset}px);
-      width: ${BUILDER_PANE_WIDTH}px;
-    `}
-  >
+const BuilderComponentPane = ({ topOffset = 0 }) => {
+  const theme = useTheme();
+  const nativeFiltersBarOpen = useSelector(
+    (state: any) => state.dashboardState.nativeFiltersBarOpen ?? false,
+  );
+
+  const tabBarStyle = useMemo(
+    () => ({
+      paddingLeft: nativeFiltersBarOpen ? 0 : theme.sizeUnit * 4,
+    }),
+    [nativeFiltersBarOpen, theme.sizeUnit],
+  );
+
+  return (
     <div
-      css={(theme: SupersetTheme) => css`
-        position: absolute;
-        height: 100%;
+      data-test="dashboard-builder-sidepane"
+      css={css`
+        position: sticky;
+        right: 0;
+        top: ${topOffset}px;
+        height: calc(100vh - ${topOffset}px);
         width: ${BUILDER_PANE_WIDTH}px;
-        box-shadow: -4px 0 4px 0 ${rgba(theme.colorBorder, 0.1)};
-        background-color: ${theme.colorBgBase};
       `}
     >
-      <Tabs
-        data-test="dashboard-builder-component-pane-tabs-navigation"
-        id="tabs"
+      <div
         css={(theme: SupersetTheme) => css`
-          line-height: inherit;
-          margin-top: ${theme.sizeUnit * 2}px;
+          position: absolute;
           height: 100%;
-
-          & .ant-tabs-content-holder {
+          width: ${BUILDER_PANE_WIDTH}px;
+          box-shadow: -4px 0 4px 0 ${rgba(theme.colorBorder, 0.1)};
+          background-color: ${theme.colorBgBase};
+        `}
+      >
+        <Tabs
+          data-test="dashboard-builder-component-pane-tabs-navigation"
+          id="tabs"
+          tabBarStyle={tabBarStyle}
+          css={(theme: SupersetTheme) => css`
+            line-height: inherit;
+            margin-top: ${theme.sizeUnit * 2}px;
             height: 100%;
-            & .ant-tabs-content {
+
+            & .ant-tabs-content-holder {
               height: 100%;
+              & .ant-tabs-content {
+                height: 100%;
+              }
             }
-          }
-        `}
-        items={[
-          {
-            key: TABS_KEYS.CHARTS,
-            label: t('Charts'),
-            children: (
-              <div
-                css={css`
-                  height: calc(100vh - ${topOffset * 2}px);
-                `}
-              >
-                <SliceAdder />
-              </div>
-            ),
-          },
-          {
-            key: TABS_KEYS.LAYOUT_ELEMENTS,
-            label: t('Layout elements'),
-            children: (
-              <>
-                <NewTabs />
-                <NewRow />
-                <NewColumn />
-                <NewHeader />
-                <NewMarkdown />
-                <NewDivider />
-                {dashboardComponents
-                  .getAll()
-                  .map(({ key: componentKey, metadata }) => (
-                    <NewDynamicComponent
-                      metadata={metadata}
-                      componentKey={componentKey}
-                    />
-                  ))}
-              </>
-            ),
-          },
-        ]}
-      />
+          `}
+          items={[
+            {
+              key: TABS_KEYS.CHARTS,
+              label: t('Charts'),
+              children: (
+                <div
+                  css={css`
+                    height: calc(100vh - ${topOffset * 2}px);
+                  `}
+                >
+                  <SliceAdder />
+                </div>
+              ),
+            },
+            {
+              key: TABS_KEYS.LAYOUT_ELEMENTS,
+              label: t('Layout elements'),
+              children: (
+                <>
+                  <NewTabs />
+                  <NewRow />
+                  <NewColumn />
+                  <NewHeader />
+                  <NewMarkdown />
+                  <NewDivider />
+                  {dashboardComponents
+                    .getAll()
+                    .map(({ key: componentKey, metadata }) => (
+                      <NewDynamicComponent
+                        key={componentKey}
+                        metadata={metadata}
+                        componentKey={componentKey}
+                      />
+                    ))}
+                </>
+              ),
+            },
+          ]}
+        />
+      </div>
     </div>
-  </div>
-);
+  );
+};
 
 export default BuilderComponentPane;
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index 19215399dd..880d5c7fbe 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
@@ -80,6 +80,7 @@ import DashboardWrapper from './DashboardWrapper';
 
 // @z-index-above-dashboard-charts + 1 = 11
 const FiltersPanel = styled.div<{ width: number; hidden: boolean }>`
+  background-color: ${({ theme }) => theme.colorBgContainer};
   grid-column: 1;
   grid-row: 1 / span 2;
   z-index: 11;
@@ -275,6 +276,7 @@ const StyledDashboardContent = styled.div<{
   marginLeft: number;
 }>`
   ${({ theme, editMode, marginLeft }) => css`
+    background-color: ${theme.colorBgLayout};
     display: flex;
     flex-direction: row;
     flex-wrap: nowrap;
@@ -291,9 +293,7 @@ const StyledDashboardContent = styled.div<{
       width: 0;
       flex: 1;
       position: relative;
-      margin-top: ${theme.sizeUnit * 4}px;
-      margin-right: ${theme.sizeUnit * 8}px;
-      margin-bottom: ${theme.sizeUnit * 4}px;
+      margin: ${theme.sizeUnit * 4}px;
       margin-left: ${marginLeft}px;
 
       ${editMode &&
@@ -557,13 +557,9 @@ const DashboardBuilder = () => {
     ],
   );
 
-  const dashboardContentMarginLeft =
-    !dashboardFiltersOpen &&
-    !editMode &&
-    nativeFiltersEnabled &&
-    filterBarOrientation !== FilterBarOrientation.Horizontal
-      ? 0
-      : theme.sizeUnit * 8;
+  const dashboardContentMarginLeft = !editMode
+    ? theme.sizeUnit * 4
+    : theme.sizeUnit * 8;
 
   const renderChild = useCallback(
     adjustedWidth => {
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
index 784d6020cd..253c17dc42 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardContainer.tsx
@@ -70,13 +70,12 @@ type DashboardContainerProps = {
   topLevelTabs?: LayoutItem;
 };
 
-export const renderedChartIdsSelector = createSelector(
-  [(state: RootState) => state.charts],
-  charts =>
+export const renderedChartIdsSelector: (state: RootState) => number[] =
+  createSelector([(state: RootState) => state.charts], charts =>
     Object.values(charts)
       .filter(chart => chart.chartStatus === 'rendered')
       .map(chart => chart.id),
-);
+  );
 
 const useRenderedChartIds = () => {
   const renderedChartIds = useSelector<RootState, number[]>(
@@ -297,6 +296,7 @@ const DashboardContainer: FC<DashboardContainerProps> = ({ 
topLevelTabs }) => {
           allowOverflow
           onFocus={handleFocus}
           items={tabItems}
+          tabBarStyle={{ paddingLeft: 0 }}
         />
       );
     },
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
index 4d5190e79d..f73fe36e9a 100644
--- 
a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
+++ 
b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardWrapper.tsx
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { FC, useEffect, useState } from 'react';
+import { FC, PropsWithChildren, useEffect, useState } from 'react';
 
 import { css, styled } from '@superset-ui/core';
 import { Constants } from '@superset-ui/core/components';
@@ -113,9 +113,7 @@ const StyledDiv = styled.div`
   `}
 `;
 
-type Props = {};
-
-const DashboardWrapper: FC<Props> = ({ children }) => {
+const DashboardWrapper: FC<PropsWithChildren<{}>> = ({ children }) => {
   const editMode = useSelector<RootState, boolean>(
     state => state.dashboardState.editMode,
   );
diff --git 
a/superset-frontend/src/dashboard/components/DashboardBuilder/state.ts 
b/superset-frontend/src/dashboard/components/DashboardBuilder/state.ts
index a338e21a94..c7b7a56fdf 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder/state.ts
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder/state.ts
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import { useSelector } from 'react-redux';
+import { useSelector, useDispatch } from 'react-redux';
 import { useCallback, useEffect, useMemo, useState } from 'react';
 import { URL_PARAMS } from 'src/constants';
 import { getUrlParam } from 'src/utils/urlUtils';
@@ -26,23 +26,26 @@ import {
   useFilters,
   useNativeFiltersDataMask,
 } from '../nativeFilters/FilterBar/state';
+import { toggleNativeFiltersBar } from '../../actions/dashboardState';
 
-// eslint-disable-next-line import/prefer-default-export
 export const useNativeFilters = () => {
+  const dispatch = useDispatch();
+
   const [isInitialized, setIsInitialized] = useState(false);
+
   const showNativeFilters = useSelector<RootState, boolean>(
     () => getUrlParam(URL_PARAMS.showFilters) ?? true,
   );
   const canEdit = useSelector<RootState, boolean>(
     ({ dashboardInfo }) => dashboardInfo.dash_edit_perm,
   );
+  const dashboardFiltersOpen = useSelector<RootState, boolean>(
+    state => state.dashboardState.nativeFiltersBarOpen ?? false,
+  );
 
   const filters = useFilters();
   const filterValues = useMemo(() => Object.values(filters), [filters]);
   const expandFilters = getUrlParam(URL_PARAMS.expandFilters);
-  const [dashboardFiltersOpen, setDashboardFiltersOpen] = useState(
-    expandFilters ?? !!filterValues.length,
-  );
 
   const nativeFiltersEnabled =
     showNativeFilters && (canEdit || (!canEdit && filterValues.length !== 0));
@@ -66,9 +69,13 @@ export const useNativeFilters = () => {
     !nativeFiltersEnabled ||
     missingInitialFilters.length === 0;
 
-  const toggleDashboardFiltersOpen = useCallback((visible?: boolean) => {
-    setDashboardFiltersOpen(prevState => visible ?? !prevState);
-  }, []);
+  const toggleDashboardFiltersOpen = useCallback(
+    (visible?: boolean) => {
+      const newState = visible ?? !dashboardFiltersOpen;
+      dispatch(toggleNativeFiltersBar(newState));
+    },
+    [dispatch, dashboardFiltersOpen],
+  );
 
   useEffect(() => {
     if (
@@ -77,11 +84,11 @@ export const useNativeFilters = () => {
       expandFilters === false ||
       (filterValues.length === 0 && nativeFiltersEnabled)
     ) {
-      toggleDashboardFiltersOpen(false);
+      dispatch(toggleNativeFiltersBar(false));
     } else {
-      toggleDashboardFiltersOpen(true);
+      dispatch(toggleNativeFiltersBar(true));
     }
-  }, [filterValues.length]);
+  }, [dispatch, filterValues.length, expandFilters, nativeFiltersEnabled]);
 
   useEffect(() => {
     if (showDashboard) {
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.jsx
similarity index 96%
rename from superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.jsx
index ec12d98d7b..d81a6ed0f2 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Chart.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.jsx
@@ -39,26 +39,26 @@ import { URL_PARAMS } from 'src/constants';
 import { enforceSharedLabelsColorsArray } from 'src/utils/colorScheme';
 import exportPivotExcel from 'src/utils/downloadAsPivotExcel';
 
-import SliceHeader from '../SliceHeader';
-import MissingChart from '../MissingChart';
+import SliceHeader from '../../SliceHeader';
+import MissingChart from '../../MissingChart';
 import {
   addDangerToast,
   addSuccessToast,
-} from '../../../components/MessageToasts/actions';
+} from '../../../../components/MessageToasts/actions';
 import {
   setFocusedFilterField,
   toggleExpandSlice,
   unsetFocusedFilterField,
-} from '../../actions/dashboardState';
-import { changeFilter } from '../../actions/dashboardFilters';
-import { refreshChart } from '../../../components/Chart/chartAction';
-import { logEvent } from '../../../logger/actions';
+} from '../../../actions/dashboardState';
+import { changeFilter } from '../../../actions/dashboardFilters';
+import { refreshChart } from '../../../../components/Chart/chartAction';
+import { logEvent } from '../../../../logger/actions';
 import {
   getActiveFilters,
   getAppliedFilterValues,
-} from '../../util/activeDashboardFilters';
-import getFormDataWithExtraFilters from 
'../../util/charts/getFormDataWithExtraFilters';
-import { PLACEHOLDER_DATASOURCE } from '../../constants';
+} from '../../../util/activeDashboardFilters';
+import getFormDataWithExtraFilters from 
'../../../util/charts/getFormDataWithExtraFilters';
+import { PLACEHOLDER_DATASOURCE } from '../../../constants';
 
 const propTypes = {
   id: PropTypes.number.isRequired,
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.test.jsx
similarity index 99%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.test.jsx
index c5f743a52c..a00b9e88c0 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Chart.test.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Chart/Chart.test.jsx
@@ -20,13 +20,13 @@ import { fireEvent, render } from 
'spec/helpers/testing-library';
 import { FeatureFlag, VizType } from '@superset-ui/core';
 import * as redux from 'redux';
 
-import Chart from 'src/dashboard/components/gridComponents/Chart';
 import * as exploreUtils from 'src/explore/exploreUtils';
 import { sliceEntitiesForChart as sliceEntities } from 
'spec/fixtures/mockSliceEntities';
 import mockDatasource from 'spec/fixtures/mockDatasource';
 import chartQueries, {
   sliceId as queryId,
 } from 'spec/fixtures/mockChartQueries';
+import Chart from './Chart';
 
 const props = {
   id: queryId,
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Chart/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to superset-frontend/src/dashboard/components/gridComponents/Chart/index.js
index 24a6a78d3f..7cda5527b4 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Chart from './Chart';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Chart;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/ChartHolder.test.tsx
similarity index 98%
rename from 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder/ChartHolder.test.tsx
index 072a087c8f..aac33a802c 100644
--- 
a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/ChartHolder.test.tsx
@@ -35,9 +35,13 @@ import { nativeFiltersInfo } from 
'src/dashboard/fixtures/mockNativeFilters';
 import newComponentFactory from 'src/dashboard/util/newComponentFactory';
 import { initialState } from 'src/SqlLab/fixtures';
 import { SET_DIRECT_PATH } from 'src/dashboard/actions/dashboardState';
-import { CHART_TYPE, COLUMN_TYPE, ROW_TYPE } from '../../util/componentTypes';
+import {
+  CHART_TYPE,
+  COLUMN_TYPE,
+  ROW_TYPE,
+} from '../../../util/componentTypes';
 import ChartHolder, { CHART_MARGIN } from './ChartHolder';
-import { GRID_BASE_UNIT, GRID_GUTTER_SIZE } from '../../util/constants';
+import { GRID_BASE_UNIT, GRID_GUTTER_SIZE } from '../../../util/constants';
 
 const DEFAULT_HEADER_HEIGHT = 22;
 
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx 
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/ChartHolder.tsx
similarity index 100%
rename from 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder.tsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder/ChartHolder.tsx
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/index.ts
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/ChartHolder/index.ts
index 24a6a78d3f..f5a0b92ff1 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/index.ts
@@ -16,21 +16,4 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export { default } from './ChartHolder';
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Column.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Column/Column.jsx
similarity index 100%
rename from superset-frontend/src/dashboard/components/gridComponents/Column.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Column/Column.jsx
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Column/Column.test.jsx
similarity index 99%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Column/Column.test.jsx
index b70f865dd3..e67be2c20d 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Column.test.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Column/Column.test.jsx
@@ -19,12 +19,12 @@
 import { fireEvent, render } from 'spec/helpers/testing-library';
 
 import BackgroundStyleDropdown from 
'src/dashboard/components/menu/BackgroundStyleDropdown';
-import Column from 'src/dashboard/components/gridComponents/Column';
 import IconButton from 'src/dashboard/components/IconButton';
 
 import { getMockStore } from 'spec/fixtures/mockStore';
 import { dashboardLayout as mockLayout } from 
'spec/fixtures/mockDashboardLayout';
 import { initialState } from 'src/SqlLab/fixtures';
+import Column from './Column';
 
 jest.mock('src/dashboard/components/dnd/DragDroppable', () => ({
   Draggable: ({ children }) => (
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Column/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/Column/index.js
index 24a6a78d3f..9b22df6f6e 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Column/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Column from './Column';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Column;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.jsx
similarity index 93%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Divider.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.jsx
index 466b77d0b7..3247cf794d 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Divider.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.jsx
@@ -20,10 +20,10 @@ import { PureComponent } from 'react';
 import PropTypes from 'prop-types';
 import { css, styled } from '@superset-ui/core';
 
-import { Draggable } from '../dnd/DragDroppable';
-import HoverMenu from '../menu/HoverMenu';
-import DeleteComponentButton from '../DeleteComponentButton';
-import { componentShape } from '../../util/propShapes';
+import { Draggable } from '../../dnd/DragDroppable';
+import HoverMenu from '../../menu/HoverMenu';
+import DeleteComponentButton from '../../DeleteComponentButton';
+import { componentShape } from '../../../util/propShapes';
 
 const propTypes = {
   id: PropTypes.string.isRequired,
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Divider.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.test.jsx
similarity index 97%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Divider.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.test.jsx
index 85a30d115d..1cf54aab0d 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Divider.test.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Divider/Divider.test.jsx
@@ -18,13 +18,13 @@
  */
 import sinon from 'sinon';
 
-import Divider from 'src/dashboard/components/gridComponents/Divider';
 import newComponentFactory from 'src/dashboard/util/newComponentFactory';
 import {
   DIVIDER_TYPE,
   DASHBOARD_GRID_TYPE,
 } from 'src/dashboard/util/componentTypes';
 import { screen, render, userEvent } from 'spec/helpers/testing-library';
+import Divider from './Divider';
 
 describe('Divider', () => {
   const props = {
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Divider/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/Divider/index.js
index 24a6a78d3f..8a7a0c9781 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Divider/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Divider from './Divider';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Divider;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.test.tsx
new file mode 100644
index 0000000000..d3d2ade7ca
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.test.tsx
@@ -0,0 +1,329 @@
+/**
+ * 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, fireEvent } from 'spec/helpers/testing-library';
+import { COLUMN_TYPE, ROW_TYPE } from 'src/dashboard/util/componentTypes';
+import { BACKGROUND_TRANSPARENT } from 'src/dashboard/util/constants';
+import DynamicComponent from './DynamicComponent';
+
+// Mock the dashboard components registry
+const mockComponent = () => (
+  <div data-test="mock-dynamic-component">Test Component</div>
+);
+jest.mock('src/visualizations/presets/dashboardComponents', () => ({
+  get: jest.fn(() => ({ Component: mockComponent })),
+}));
+
+// Mock other dependencies
+jest.mock('src/dashboard/components/dnd/DragDroppable', () => ({
+  Draggable: jest.fn(({ children, editMode }) => {
+    const mockElement = { tagName: 'DIV', dataset: {} };
+    const mockDragSourceRef = { current: mockElement };
+    return (
+      <div data-test="mock-draggable">
+        {children({ dragSourceRef: editMode ? mockDragSourceRef : null })}
+      </div>
+    );
+  }),
+}));
+
+jest.mock('src/dashboard/components/menu/WithPopoverMenu', () =>
+  jest.fn(({ children, menuItems, editMode }) => (
+    <div data-test="mock-popover-menu">
+      {editMode &&
+        menuItems &&
+        menuItems.map((item: React.ReactNode, index: number) => (
+          <div key={index} data-test="menu-item">
+            {item}
+          </div>
+        ))}
+      {children}
+    </div>
+  )),
+);
+
+jest.mock('src/dashboard/components/resizable/ResizableContainer', () =>
+  jest.fn(({ children }) => (
+    <div data-test="mock-resizable-container">{children}</div>
+  )),
+);
+
+jest.mock('src/dashboard/components/menu/HoverMenu', () =>
+  jest.fn(({ children }) => <div data-test="mock-hover-menu">{children}</div>),
+);
+
+jest.mock('src/dashboard/components/DeleteComponentButton', () =>
+  jest.fn(({ onDelete }) => (
+    <button type="button" data-test="mock-delete-button" onClick={onDelete}>
+      Delete
+    </button>
+  )),
+);
+
+jest.mock('src/dashboard/components/menu/BackgroundStyleDropdown', () =>
+  jest.fn(({ onChange, value }) => (
+    <select
+      data-test="mock-background-dropdown"
+      value={value}
+      onChange={e => onChange(e.target.value)}
+    >
+      <option value="BACKGROUND_TRANSPARENT">Transparent</option>
+      <option value="BACKGROUND_WHITE">White</option>
+    </select>
+  )),
+);
+
+const createProps = (overrides = {}) => ({
+  component: {
+    id: 'DYNAMIC_COMPONENT_1',
+    meta: {
+      componentKey: 'test-component',
+      width: 6,
+      height: 4,
+      background: BACKGROUND_TRANSPARENT,
+    },
+    componentKey: 'test-component',
+  },
+  parentComponent: {
+    id: 'ROW_1',
+    type: ROW_TYPE,
+    meta: {
+      width: 12,
+    },
+  },
+  index: 0,
+  depth: 1,
+  handleComponentDrop: jest.fn(),
+  editMode: false,
+  columnWidth: 100,
+  availableColumnCount: 12,
+  onResizeStart: jest.fn(),
+  onResizeStop: jest.fn(),
+  onResize: jest.fn(),
+  deleteComponent: jest.fn(),
+  updateComponents: jest.fn(),
+  parentId: 'ROW_1',
+  id: 'DYNAMIC_COMPONENT_1',
+  ...overrides,
+});
+
+const renderWithRedux = (component: React.ReactElement) =>
+  render(component, {
+    useRedux: true,
+    initialState: {
+      nativeFilters: { filters: {} },
+      dataMask: {},
+    },
+  });
+
+describe('DynamicComponent', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  test('should render the component with basic structure', () => {
+    const props = createProps();
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    expect(screen.getByTestId('mock-draggable')).toBeInTheDocument();
+    expect(screen.getByTestId('mock-popover-menu')).toBeInTheDocument();
+    expect(screen.getByTestId('mock-resizable-container')).toBeInTheDocument();
+    expect(
+      screen.getByTestId('dashboard-component-chart-holder'),
+    ).toBeInTheDocument();
+    expect(screen.getByTestId('mock-dynamic-component')).toBeInTheDocument();
+  });
+
+  test('should render with proper CSS classes and data attributes', () => {
+    const props = createProps();
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    const componentElement = screen.getByTestId('dashboard-test-component');
+    expect(componentElement).toHaveClass('dashboard-component');
+    expect(componentElement).toHaveClass('dashboard-test-component');
+    expect(componentElement).toHaveAttribute('id', 'DYNAMIC_COMPONENT_1');
+  });
+
+  test('should render HoverMenu and DeleteComponentButton in edit mode', () => 
{
+    const props = createProps({ editMode: true });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    expect(screen.getByTestId('mock-hover-menu')).toBeInTheDocument();
+    expect(screen.getByTestId('mock-delete-button')).toBeInTheDocument();
+  });
+
+  test('should not render HoverMenu and DeleteComponentButton when not in edit 
mode', () => {
+    const props = createProps({ editMode: false });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    expect(screen.queryByTestId('mock-hover-menu')).not.toBeInTheDocument();
+    expect(screen.queryByTestId('mock-delete-button')).not.toBeInTheDocument();
+  });
+
+  test('should call deleteComponent when delete button is clicked', () => {
+    const props = createProps({ editMode: true });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    fireEvent.click(screen.getByTestId('mock-delete-button'));
+    expect(props.deleteComponent).toHaveBeenCalledWith(
+      'DYNAMIC_COMPONENT_1',
+      'ROW_1',
+    );
+  });
+
+  test('should call updateComponents when background is changed', () => {
+    const props = createProps({ editMode: true });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    const backgroundDropdown = screen.getByTestId('mock-background-dropdown');
+    fireEvent.change(backgroundDropdown, {
+      target: { value: 'BACKGROUND_WHITE' },
+    });
+
+    expect(props.updateComponents).toHaveBeenCalledWith({
+      DYNAMIC_COMPONENT_1: {
+        ...props.component,
+        meta: {
+          ...props.component.meta,
+          background: 'BACKGROUND_WHITE',
+        },
+      },
+    });
+  });
+
+  test('should calculate width multiple from component meta when parent is not 
COLUMN_TYPE', () => {
+    const props = createProps({
+      component: {
+        ...createProps().component,
+        meta: { ...createProps().component.meta, width: 8 },
+      },
+      parentComponent: {
+        ...createProps().parentComponent,
+        type: ROW_TYPE,
+      },
+    });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    // Component should render successfully with width from 
component.meta.width
+    expect(screen.getByTestId('mock-resizable-container')).toBeInTheDocument();
+  });
+
+  test('should calculate width multiple from parent meta when parent is 
COLUMN_TYPE', () => {
+    const props = createProps({
+      parentComponent: {
+        id: 'COLUMN_1',
+        type: COLUMN_TYPE,
+        meta: {
+          width: 6,
+        },
+      },
+    });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    // Component should render successfully with width from 
parentComponent.meta.width
+    expect(screen.getByTestId('mock-resizable-container')).toBeInTheDocument();
+  });
+
+  test('should use default width when no width is specified', () => {
+    const props = createProps({
+      component: {
+        ...createProps().component,
+        meta: {
+          ...createProps().component.meta,
+          width: undefined,
+        },
+      },
+      parentComponent: {
+        ...createProps().parentComponent,
+        type: ROW_TYPE,
+        meta: {},
+      },
+    });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    // Component should render successfully with default width 
(GRID_MIN_COLUMN_COUNT)
+    expect(screen.getByTestId('mock-resizable-container')).toBeInTheDocument();
+  });
+
+  test('should render background style correctly', () => {
+    const props = createProps({
+      editMode: true, // Need edit mode for menu items to render
+      component: {
+        ...createProps().component,
+        meta: {
+          ...createProps().component.meta,
+          background: 'BACKGROUND_WHITE',
+        },
+      },
+    });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    // Background dropdown should have the correct value
+    const backgroundDropdown = screen.getByTestId('mock-background-dropdown');
+    expect(backgroundDropdown).toHaveValue('BACKGROUND_WHITE');
+  });
+
+  test('should pass dashboard data from Redux store to dynamic component', () 
=> {
+    const props = createProps();
+    const initialState = {
+      nativeFilters: { filters: { filter1: {} } },
+      dataMask: { mask1: {} },
+    };
+
+    render(<DynamicComponent {...props} />, {
+      useRedux: true,
+      initialState,
+    });
+
+    // Component should render - either the mock component or loading state
+    const container = screen.getByTestId('dashboard-component-chart-holder');
+    expect(container).toBeInTheDocument();
+    // Check that either the component loaded or is loading
+    expect(
+      screen.queryByTestId('mock-dynamic-component') ||
+        screen.queryByText('Loading...'),
+    ).toBeTruthy();
+  });
+
+  test('should handle resize callbacks', () => {
+    const props = createProps();
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    // Resize callbacks should be passed to ResizableContainer
+    expect(screen.getByTestId('mock-resizable-container')).toBeInTheDocument();
+  });
+
+  test('should render with proper data-test attribute based on componentKey', 
() => {
+    const props = createProps({
+      component: {
+        ...createProps().component,
+        meta: {
+          ...createProps().component.meta,
+          componentKey: 'custom-component',
+        },
+        componentKey: 'custom-component',
+      },
+    });
+    renderWithRedux(<DynamicComponent {...props} />);
+
+    expect(
+      screen.getByTestId('dashboard-custom-component'),
+    ).toBeInTheDocument();
+  });
+});
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.tsx
similarity index 84%
rename from 
superset-frontend/src/dashboard/components/gridComponents/DynamicComponent.tsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.tsx
index 66db0fd898..eb06833fba 100644
--- 
a/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/DynamicComponent.tsx
@@ -22,40 +22,40 @@ import backgroundStyleOptions from 
'src/dashboard/util/backgroundStyleOptions';
 import cx from 'classnames';
 import { shallowEqual, useSelector } from 'react-redux';
 import { ResizeCallback, ResizeStartCallback } from 're-resizable';
-import { Draggable } from '../dnd/DragDroppable';
-import { COLUMN_TYPE, ROW_TYPE } from '../../util/componentTypes';
-import WithPopoverMenu from '../menu/WithPopoverMenu';
-import ResizableContainer from '../resizable/ResizableContainer';
+import { Draggable } from '../../dnd/DragDroppable';
+import { COLUMN_TYPE, ROW_TYPE } from '../../../util/componentTypes';
+import WithPopoverMenu from '../../menu/WithPopoverMenu';
+import ResizableContainer from '../../resizable/ResizableContainer';
 import {
   BACKGROUND_TRANSPARENT,
   GRID_BASE_UNIT,
   GRID_MIN_COLUMN_COUNT,
-} from '../../util/constants';
-import HoverMenu from '../menu/HoverMenu';
-import DeleteComponentButton from '../DeleteComponentButton';
-import BackgroundStyleDropdown from '../menu/BackgroundStyleDropdown';
-import dashboardComponents from 
'../../../visualizations/presets/dashboardComponents';
-import { RootState } from '../../types';
+} from '../../../util/constants';
+import HoverMenu from '../../menu/HoverMenu';
+import DeleteComponentButton from '../../DeleteComponentButton';
+import BackgroundStyleDropdown from '../../menu/BackgroundStyleDropdown';
+import dashboardComponents from 
'../../../../visualizations/presets/dashboardComponents';
+import { RootState } from '../../../types';
 
-type FilterSummaryType = {
+type DynamicComponentProps = {
   component: JsonObject;
   parentComponent: JsonObject;
   index: number;
   depth: number;
-  handleComponentDrop: (...args: any[]) => any;
+  handleComponentDrop: (dropResult: unknown) => void;
   editMode: boolean;
   columnWidth: number;
   availableColumnCount: number;
   onResizeStart: ResizeStartCallback;
   onResizeStop: ResizeCallback;
   onResize: ResizeCallback;
-  deleteComponent: Function;
-  updateComponents: Function;
-  parentId: number;
-  id: number;
+  deleteComponent: (id: string, parentId: string) => void;
+  updateComponents: (updates: Record<string, JsonObject>) => void;
+  parentId: string;
+  id: string;
 };
 
-const DynamicComponent: FC<FilterSummaryType> = ({
+const DynamicComponent: FC<DynamicComponentProps> = ({
   component,
   parentComponent,
   index,
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts
index 24a6a78d3f..482eedb7f0 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts
@@ -16,21 +16,4 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export { default } from './DynamicComponent';
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Header.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Header/Header.jsx
similarity index 100%
rename from superset-frontend/src/dashboard/components/gridComponents/Header.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Header/Header.jsx
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Header.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.jsx
similarity index 98%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Header.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.jsx
index 48c969bb9b..1f204406a2 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Header.test.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Header/Header.test.jsx
@@ -22,7 +22,6 @@ import { HTML5Backend } from 'react-dnd-html5-backend';
 import sinon from 'sinon';
 
 import { render, screen, fireEvent } from 'spec/helpers/testing-library';
-import Header from 'src/dashboard/components/gridComponents/Header';
 import newComponentFactory from 'src/dashboard/util/newComponentFactory';
 import {
   HEADER_TYPE,
@@ -30,6 +29,7 @@ import {
 } from 'src/dashboard/util/componentTypes';
 
 import { mockStoreWithTabs } from 'spec/fixtures/mockStore';
+import Header from './Header';
 
 describe('Header', () => {
   const props = {
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Header/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/Header/index.js
index 24a6a78d3f..87090f2574 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Header/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Header from './Header';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Header;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx
similarity index 100%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Markdown.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.jsx
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Markdown.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.test.jsx
similarity index 99%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Markdown.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.test.jsx
index 0d968b1b7a..c10d10b776 100644
--- 
a/superset-frontend/src/dashboard/components/gridComponents/Markdown.test.jsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Markdown/Markdown.test.jsx
@@ -18,9 +18,9 @@
  */
 import { Provider } from 'react-redux';
 import { act, render, screen, fireEvent } from 'spec/helpers/testing-library';
-import MarkdownConnected from 
'src/dashboard/components/gridComponents/Markdown';
 import { mockStore } from 'spec/fixtures/mockStore';
 import { dashboardLayout as mockLayout } from 
'spec/fixtures/mockDashboardLayout';
+import MarkdownConnected from './Markdown';
 
 describe('Markdown', () => {
   const props = {
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Markdown/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/Markdown/index.js
index 24a6a78d3f..af0149d068 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Markdown/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Markdown from './Markdown';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Markdown;
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Row/Row.jsx
similarity index 99%
rename from superset-frontend/src/dashboard/components/gridComponents/Row.jsx
rename to superset-frontend/src/dashboard/components/gridComponents/Row/Row.jsx
index 61cd5ff69e..917cdfc965 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Row.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Row/Row.jsx
@@ -53,7 +53,7 @@ import { BACKGROUND_TRANSPARENT } from 
'src/dashboard/util/constants';
 import { isEmbedded } from 'src/dashboard/util/isEmbedded';
 import { EMPTY_CONTAINER_Z_INDEX } from 'src/dashboard/constants';
 import { isCurrentUserBot } from 'src/utils/isBot';
-import { useDebouncedEffect } from '../../../explore/exploreUtils';
+import { useDebouncedEffect } from '../../../../explore/exploreUtils';
 
 const propTypes = {
   id: PropTypes.string.isRequired,
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Row/Row.test.jsx
similarity index 99%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Row/Row.test.jsx
index e15d116f06..2f8499a6fe 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Row.test.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Row/Row.test.jsx
@@ -20,12 +20,12 @@ import { fireEvent, render } from 
'spec/helpers/testing-library';
 
 import BackgroundStyleDropdown from 
'src/dashboard/components/menu/BackgroundStyleDropdown';
 import IconButton from 'src/dashboard/components/IconButton';
-import Row from 'src/dashboard/components/gridComponents/Row';
 import { DASHBOARD_GRID_ID } from 'src/dashboard/util/constants';
 
 import { getMockStore } from 'spec/fixtures/mockStore';
 import { dashboardLayout as mockLayout } from 
'spec/fixtures/mockDashboardLayout';
 import { initialState } from 'src/SqlLab/fixtures';
+import Row from './Row';
 
 jest.mock('@superset-ui/core', () => ({
   ...jest.requireActual('@superset-ui/core'),
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Row/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to superset-frontend/src/dashboard/components/gridComponents/Row/index.js
index 24a6a78d3f..2b78be10dc 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Row/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Row from './Row';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Row;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tab.test.jsx
deleted file mode 100644
index 74c5f4813a..0000000000
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.jsx
+++ /dev/null
@@ -1,141 +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, fireEvent } from 'spec/helpers/testing-library';
-
-import { Provider } from 'react-redux';
-import { DndProvider } from 'react-dnd';
-import { HTML5Backend } from 'react-dnd-html5-backend';
-import Tab, { RENDER_TAB } from 'src/dashboard/components/gridComponents/Tab';
-import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
-import { getMockStore } from 'spec/fixtures/mockStore';
-
-// TODO: rewrite to RTL
-describe('Tabs', () => {
-  const props = {
-    id: 'TAB_ID',
-    parentId: 'TABS_ID',
-    component: dashboardLayoutWithTabs.present.TAB_ID,
-    parentComponent: dashboardLayoutWithTabs.present.TABS_ID,
-    index: 0,
-    depth: 1,
-    editMode: false,
-    renderType: RENDER_TAB,
-    filters: {},
-    dashboardId: 123,
-    setDirectPathToChild: jest.fn(),
-    onDropOnTab() {},
-    onDeleteTab() {},
-    availableColumnCount: 12,
-    columnWidth: 50,
-    onResizeStart() {},
-    onResize() {},
-    onResizeStop() {},
-    createComponent() {},
-    handleComponentDrop() {},
-    onChangeTab() {},
-    deleteComponent() {},
-    updateComponents() {},
-    dropToChild: false,
-    maxChildrenHeight: 100,
-    shouldDropToChild: () => false, // Add this prop
-  };
-
-  function setup(overrideProps = {}) {
-    return render(
-      <Provider
-        store={getMockStore({
-          dashboardLayout: dashboardLayoutWithTabs,
-        })}
-      >
-        <DndProvider backend={HTML5Backend}>
-          <Tab {...props} {...overrideProps} />
-        </DndProvider>
-      </Provider>,
-    );
-  }
-
-  describe('renderType=RENDER_TAB', () => {
-    it('should render a DragDroppable', () => {
-      setup();
-      expect(screen.getByTestId('dragdroppable-object')).toBeInTheDocument();
-    });
-
-    it('should render an EditableTitle with meta.text', () => {
-      setup();
-      const titleElement = screen.getByTestId('editable-title');
-      expect(titleElement).toBeInTheDocument();
-      expect(titleElement).toHaveTextContent(
-        props.component.meta.defaultText || '',
-      );
-    });
-
-    it('should call updateComponents when EditableTitle changes', async () => {
-      const updateComponents = jest.fn();
-      setup({
-        editMode: true,
-        updateComponents,
-        component: {
-          ...dashboardLayoutWithTabs.present.TAB_ID,
-          meta: {
-            text: 'Original Title',
-            defaultText: 'Original Title', // Add defaultText to match 
component
-          },
-        },
-        isFocused: true,
-      });
-
-      const titleElement = screen.getByTestId('editable-title');
-      fireEvent.click(titleElement);
-
-      const titleInput = await screen.findByTestId(
-        'textarea-editable-title-input',
-      );
-      fireEvent.change(titleInput, { target: { value: 'New title' } });
-      fireEvent.blur(titleInput);
-
-      expect(updateComponents).toHaveBeenCalledWith({
-        TAB_ID: {
-          ...dashboardLayoutWithTabs.present.TAB_ID,
-          meta: {
-            ...dashboardLayoutWithTabs.present.TAB_ID.meta,
-            text: 'New title',
-            defaultText: 'Original Title', // Keep the original defaultText
-          },
-        },
-      });
-    });
-  });
-
-  describe('renderType=RENDER_TAB_CONTENT', () => {
-    it('should render DashboardComponents', () => {
-      setup({
-        renderType: 'RENDER_TAB_CONTENT',
-        component: {
-          ...dashboardLayoutWithTabs.present.TAB_ID,
-          children: ['ROW_ID'],
-        },
-      });
-
-      expect(
-        screen.getByTestId('dashboard-component-chart-holder'),
-      ).toBeInTheDocument();
-    });
-  });
-});
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx
similarity index 100%
rename from superset-frontend/src/dashboard/components/gridComponents/Tab.jsx
rename to superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.jsx
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx
similarity index 99%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx
index 05ceb7e0cc..3ff5f28797 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tab.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/Tab.test.tsx
@@ -29,7 +29,7 @@ import { EditableTitle } from '@superset-ui/core/components';
 import { setEditMode } from 'src/dashboard/actions/dashboardState';
 
 import Tab from './Tab';
-import Markdown from './Markdown';
+import Markdown from '../Markdown';
 
 jest.mock('src/dashboard/containers/DashboardComponent', () =>
   jest.fn(() => <div data-test="DashboardComponent" />),
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Tab/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to superset-frontend/src/dashboard/components/gridComponents/Tab/index.js
index 24a6a78d3f..f1c4c41d48 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/index.js
@@ -16,21 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Tab from './Tab';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Tab;
+export { RENDER_TAB, RENDER_TAB_CONTENT } from './Tab';
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx
deleted file mode 100644
index d3c606fc2b..0000000000
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.jsx
+++ /dev/null
@@ -1,203 +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 { fireEvent, render } from 'spec/helpers/testing-library';
-import fetchMock from 'fetch-mock';
-import Tabs from 'src/dashboard/components/gridComponents/Tabs';
-import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
-import emptyDashboardLayout from 'src/dashboard/fixtures/emptyDashboardLayout';
-import { dashboardLayoutWithTabs } from 'spec/fixtures/mockDashboardLayout';
-import { nativeFilters } from 'spec/fixtures/mockNativeFilters';
-import { initialState } from 'src/SqlLab/fixtures';
-
-jest.mock('src/dashboard/components/dnd/DragDroppable', () => ({
-  Draggable: ({ children }) => (
-    <div data-test="mock-draggable">{children({})}</div>
-  ),
-  Droppable: ({ children }) => (
-    <div data-test="mock-droppable">{children({})}</div>
-  ),
-}));
-jest.mock('src/dashboard/containers/DashboardComponent', () => ({ id }) => (
-  <div data-test="mock-dashboard-component">{id}</div>
-));
-
-jest.mock(
-  'src/dashboard/components/DeleteComponentButton',
-  () =>
-    ({ onDelete }) => (
-      <button
-        type="button"
-        data-test="mock-delete-component-button"
-        onClick={onDelete}
-      >
-        Delete
-      </button>
-    ),
-);
-
-fetchMock.post('glob:*/r/shortener/', {});
-
-const props = {
-  id: 'TABS_ID',
-  parentId: DASHBOARD_ROOT_ID,
-  component: dashboardLayoutWithTabs.present.TABS_ID,
-  parentComponent: dashboardLayoutWithTabs.present[DASHBOARD_ROOT_ID],
-  index: 0,
-  depth: 1,
-  renderTabContent: true,
-  editMode: false,
-  availableColumnCount: 12,
-  columnWidth: 50,
-  dashboardId: 1,
-  onResizeStart() {},
-  onResize() {},
-  onResizeStop() {},
-  createComponent() {},
-  handleComponentDrop() {},
-  onChangeTab() {},
-  deleteComponent() {},
-  updateComponents() {},
-  logEvent() {},
-  dashboardLayout: emptyDashboardLayout,
-  nativeFilters: nativeFilters.filters,
-};
-
-function setup(overrideProps, overrideState = {}) {
-  return render(<Tabs {...props} {...overrideProps} />, {
-    useDnd: true,
-    useRouter: true,
-    useRedux: true,
-    initialState: {
-      ...initialState,
-      dashboardLayout: dashboardLayoutWithTabs,
-      dashboardFilters: {},
-      ...overrideState,
-    },
-  });
-}
-
-test('should render a Draggable', () => {
-  // test just Tabs with no children Draggable
-  const { getByTestId } = setup({
-    component: { ...props.component, children: [] },
-  });
-  expect(getByTestId('mock-draggable')).toBeInTheDocument();
-});
-
-test('should render non-editable tabs', () => {
-  const { getAllByRole, container } = setup();
-  expect(getAllByRole('tab')[0]).toBeInTheDocument();
-  expect(container.querySelector('.ant-tabs-nav-add')).not.toBeInTheDocument();
-});
-
-test('should render a tab pane for each child', () => {
-  const { getAllByRole } = setup();
-  expect(getAllByRole('tab')).toHaveLength(props.component.children.length);
-});
-
-test('should render editable tabs in editMode', () => {
-  const { getAllByRole, container } = setup({ editMode: true });
-  expect(getAllByRole('tab')[0]).toBeInTheDocument();
-  expect(container.querySelector('.ant-tabs-nav-add')).toBeInTheDocument();
-});
-
-test('should render a DashboardComponent for each child', () => {
-  // note: this does not test Tab content
-  const { getAllByTestId } = setup({ renderTabContent: false });
-  expect(getAllByTestId('mock-dashboard-component')).toHaveLength(
-    props.component.children.length,
-  );
-});
-
-test('should call createComponent if the (+) tab is clicked', () => {
-  const createComponent = jest.fn();
-  const { getAllByRole } = setup({ editMode: true, createComponent });
-  const addButtons = getAllByRole('button', { name: 'Add tab' });
-  fireEvent.click(addButtons[0]);
-  expect(createComponent).toHaveBeenCalledTimes(1);
-});
-
-test('should call onChangeTab when a tab is clicked', () => {
-  const onChangeTab = jest.fn();
-  const { getByRole } = setup({ editMode: true, onChangeTab });
-  const newTab = getByRole('tab', { selected: false });
-  fireEvent.click(newTab);
-  expect(onChangeTab).toHaveBeenCalledTimes(1);
-});
-
-test('should not call onChangeTab when anchor link is clicked', () => {
-  const onChangeTab = jest.fn();
-  const { getByRole } = setup({ editMode: true, onChangeTab });
-  const currentTab = getByRole('tab', { selected: true });
-  fireEvent.click(currentTab);
-
-  expect(onChangeTab).toHaveBeenCalledTimes(0);
-});
-
-test('should render a HoverMenu in editMode', () => {
-  const { container } = setup({ editMode: true });
-  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
-});
-
-test('should render a DeleteComponentButton in editMode', () => {
-  const { getByTestId } = setup({ editMode: true });
-  expect(getByTestId('mock-delete-component-button')).toBeInTheDocument();
-});
-
-test('should call deleteComponent when deleted', () => {
-  const deleteComponent = jest.fn();
-  const { getByTestId } = setup({ editMode: true, deleteComponent });
-  fireEvent.click(getByTestId('mock-delete-component-button'));
-  expect(deleteComponent).toHaveBeenCalledTimes(1);
-});
-
-test('should direct display direct-link tab', () => {
-  // display child in directPathToChild list
-  const directPathToChild =
-    dashboardLayoutWithTabs.present.ROW_ID2.parents.slice();
-  const { getByRole } = setup({}, { dashboardState: { directPathToChild } });
-  expect(getByRole('tab', { selected: true })).toHaveTextContent('TAB_ID2');
-});
-
-test('should render Modal when clicked remove tab button', () => {
-  const deleteComponent = jest.fn();
-  const { container, getByText, queryByText } = setup({
-    editMode: true,
-    deleteComponent,
-  });
-
-  // Initially no modal should be visible
-  expect(queryByText('Delete dashboard tab?')).not.toBeInTheDocument();
-
-  // Click the remove tab button
-  fireEvent.click(container.querySelector('.ant-tabs-tab-remove'));
-
-  // Modal should now be visible
-  expect(getByText('Delete dashboard tab?')).toBeInTheDocument();
-  expect(deleteComponent).toHaveBeenCalledTimes(0);
-});
-
-test('should set new tab key if dashboardId was changed', () => {
-  const { getByRole } = setup({
-    ...props,
-    dashboardId: 2,
-    component: dashboardLayoutWithTabs.present.TAB_ID,
-  });
-  expect(getByRole('tab', { selected: true })).toHaveTextContent('ROW_ID');
-});
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
similarity index 84%
rename from superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
index 68abbfcee6..c59b38609c 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.jsx
@@ -18,25 +18,22 @@
  */
 import { useCallback, useEffect, useMemo, useState, memo } from 'react';
 import PropTypes from 'prop-types';
-import { styled, t, usePrevious, css } from '@superset-ui/core';
+import { t, usePrevious, useTheme, styled } from '@superset-ui/core';
 import { useSelector } from 'react-redux';
-import { LineEditableTabs } from '@superset-ui/core/components/Tabs';
 import { Icons } from '@superset-ui/core/components/Icons';
 import { LOG_ACTIONS_SELECT_DASHBOARD_TAB } from 'src/logger/LogUtils';
 import { Modal } from '@superset-ui/core/components';
 import { DROP_LEFT, DROP_RIGHT } from 'src/dashboard/util/getDropPosition';
-import { Draggable } from '../dnd/DragDroppable';
-import DragHandle from '../dnd/DragHandle';
-import DashboardComponent from '../../containers/DashboardComponent';
-import DeleteComponentButton from '../DeleteComponentButton';
-import HoverMenu from '../menu/HoverMenu';
-import findTabIndexByComponentId from '../../util/findTabIndexByComponentId';
-import getDirectPathToTabIndex from '../../util/getDirectPathToTabIndex';
-import getLeafComponentIdFromPath from '../../util/getLeafComponentIdFromPath';
-import { componentShape } from '../../util/propShapes';
-import { NEW_TAB_ID } from '../../util/constants';
-import { RENDER_TAB, RENDER_TAB_CONTENT } from './Tab';
-import { TABS_TYPE, TAB_TYPE } from '../../util/componentTypes';
+import { Draggable } from '../../dnd/DragDroppable';
+import DashboardComponent from '../../../containers/DashboardComponent';
+import findTabIndexByComponentId from 
'../../../util/findTabIndexByComponentId';
+import getDirectPathToTabIndex from '../../../util/getDirectPathToTabIndex';
+import getLeafComponentIdFromPath from 
'../../../util/getLeafComponentIdFromPath';
+import { componentShape } from '../../../util/propShapes';
+import { NEW_TAB_ID } from '../../../util/constants';
+import { RENDER_TAB, RENDER_TAB_CONTENT } from '../Tab';
+import { TABS_TYPE, TAB_TYPE } from '../../../util/componentTypes';
+import TabsRenderer from '../TabsRenderer';
 
 const propTypes = {
   id: PropTypes.string.isRequired,
@@ -76,34 +73,6 @@ const defaultProps = {
   onResizeStop() {},
 };
 
-const StyledTabsContainer = styled.div`
-  ${({ theme }) => css`
-    width: 100%;
-    background-color: ${theme.colorBgBase};
-
-    .dashboard-component-tabs-content {
-      min-height: ${theme.sizeUnit * 12}px;
-      margin-top: ${theme.sizeUnit / 4}px;
-      position: relative;
-    }
-
-    .ant-tabs {
-      overflow: visible;
-
-      .ant-tabs-nav-wrap {
-        min-height: ${theme.sizeUnit * 12.5}px;
-      }
-
-      .ant-tabs-content-holder {
-        overflow: visible;
-      }
-    }
-
-    div .ant-tabs-tab-btn {
-      text-transform: none;
-    }
-  `}
-`;
 const DropIndicator = styled.div`
   border: 2px solid ${({ theme }) => theme.colorPrimary};
   width: 5px;
@@ -124,11 +93,16 @@ const CloseIconWithDropIndicator = props => (
 );
 
 const Tabs = props => {
+  const theme = useTheme();
+
   const nativeFilters = useSelector(state => state.nativeFilters);
   const activeTabs = useSelector(state => state.dashboardState.activeTabs);
   const directPathToChild = useSelector(
     state => state.dashboardState.directPathToChild,
   );
+  const nativeFiltersBarOpen = useSelector(
+    state => state.dashboardState.nativeFiltersBarOpen ?? false,
+  );
 
   const { tabIndex: initTabIndex, activeKey: initActiveKey } = useMemo(() => {
     let tabIndex = Math.max(
@@ -378,6 +352,13 @@ const Tabs = props => {
 
   const { children: tabIds } = tabsComponent;
 
+  const tabBarPaddingLeft =
+    renderTabContent === false
+      ? nativeFiltersBarOpen
+        ? 0
+        : theme.sizeUnit * 4
+      : 0;
+
   const showDropIndicators = useCallback(
     currentDropTabIndex =>
       currentDropTabIndex === dragOverTabIndex && {
@@ -392,16 +373,21 @@ const Tabs = props => {
     [draggingTabId],
   );
 
-  let tabsToHighlight;
-  const highlightedFilterId =
-    nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId;
-  if (highlightedFilterId) {
-    tabsToHighlight = nativeFilters.filters[highlightedFilterId]?.tabsInScope;
-  }
-
-  const renderChild = useCallback(
-    ({ dragSourceRef: tabsDragSourceRef }) => {
-      const tabItems = tabIds.map((tabId, tabIndex) => ({
+  // Extract tab highlighting logic into a hook
+  const useTabHighlighting = useCallback(() => {
+    const highlightedFilterId =
+      nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId;
+    return highlightedFilterId
+      ? nativeFilters.filters[highlightedFilterId]?.tabsInScope
+      : undefined;
+  }, [nativeFilters]);
+
+  const tabsToHighlight = useTabHighlighting();
+
+  // Extract tab items creation logic into a memoized value (not a hook inside 
hook)
+  const tabItems = useMemo(
+    () =>
+      tabIds.map((tabId, tabIndex) => ({
         key: tabId,
         label: removeDraggedTab(tabId) ? (
           <></>
@@ -456,51 +442,20 @@ const Tabs = props => {
             }
           />
         ),
-      }));
-
-      return (
-        <StyledTabsContainer
-          className="dashboard-component dashboard-component-tabs"
-          data-test="dashboard-component-tabs"
-        >
-          {editMode && renderHoverMenu && (
-            <HoverMenu innerRef={tabsDragSourceRef} position="left">
-              <DragHandle position="left" />
-              <DeleteComponentButton onDelete={handleDeleteComponent} />
-            </HoverMenu>
-          )}
-
-          <LineEditableTabs
-            id={tabsComponent.id}
-            activeKey={activeKey}
-            onChange={key => {
-              handleClickTab(tabIds.indexOf(key));
-            }}
-            onEdit={handleEdit}
-            data-test="nav-list"
-            type={editMode ? 'editable-card' : 'card'}
-            items={tabItems} // Pass the dynamically generated items array
-          />
-        </StyledTabsContainer>
-      );
-    },
+      })),
     [
-      editMode,
-      renderHoverMenu,
-      handleDeleteComponent,
-      tabsComponent.id,
-      activeKey,
-      handleEdit,
       tabIds,
-      handleClickTab,
       removeDraggedTab,
       showDropIndicators,
+      tabsComponent.id,
       depth,
       availableColumnCount,
       columnWidth,
       handleDropOnTab,
       handleGetDropPosition,
       handleDragggingTab,
+      handleClickTab,
+      activeKey,
       tabsToHighlight,
       renderTabContent,
       onResizeStart,
@@ -511,6 +466,36 @@ const Tabs = props => {
     ],
   );
 
+  const renderChild = useCallback(
+    ({ dragSourceRef: tabsDragSourceRef }) => (
+      <TabsRenderer
+        tabItems={tabItems}
+        editMode={editMode}
+        renderHoverMenu={renderHoverMenu}
+        tabsDragSourceRef={tabsDragSourceRef}
+        handleDeleteComponent={handleDeleteComponent}
+        tabsComponent={tabsComponent}
+        activeKey={activeKey}
+        tabIds={tabIds}
+        handleClickTab={handleClickTab}
+        handleEdit={handleEdit}
+        tabBarPaddingLeft={tabBarPaddingLeft}
+      />
+    ),
+    [
+      tabItems,
+      editMode,
+      renderHoverMenu,
+      handleDeleteComponent,
+      tabsComponent,
+      activeKey,
+      tabIds,
+      handleClickTab,
+      handleEdit,
+      tabBarPaddingLeft,
+    ],
+  );
+
   return (
     <>
       <Draggable
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx 
b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.test.tsx
similarity index 87%
rename from 
superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx
rename to 
superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.test.tsx
index 9fdf935d2a..573fa268f6 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/Tabs/Tabs.test.tsx
@@ -59,9 +59,10 @@ jest.mock('src/dashboard/util/getLeafComponentIdFromPath', 
() => jest.fn());
 
 jest.mock('src/dashboard/components/dnd/DragDroppable', () => ({
   Draggable: jest.fn(props => {
+    const mockElement = { tagName: 'DIV', dataset: {} };
     const childProps = props.editMode
       ? {
-          dragSourceRef: props.dragSourceRef,
+          dragSourceRef: { current: mockElement },
           dropIndicatorProps: props.dropIndicatorProps,
         }
       : {};
@@ -135,6 +136,36 @@ test('Should render editMode:true', () => {
   expect(DeleteComponentButton).toHaveBeenCalledTimes(1);
 });
 
+test('Should render HoverMenu in editMode', () => {
+  const props = createProps();
+  const { container } = render(<Tabs {...props} />, {
+    useRedux: true,
+    useDnd: true,
+  });
+  // HoverMenu is rendered inside TabsRenderer when editMode is true
+  expect(container.querySelector('.hover-menu')).toBeInTheDocument();
+});
+
+test('Should not render HoverMenu when not in editMode', () => {
+  const props = createProps();
+  props.editMode = false;
+  const { container } = render(<Tabs {...props} />, {
+    useRedux: true,
+    useDnd: true,
+  });
+  expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
+});
+
+test('Should not render HoverMenu when renderHoverMenu is false', () => {
+  const props = createProps();
+  props.renderHoverMenu = false;
+  const { container } = render(<Tabs {...props} />, {
+    useRedux: true,
+    useDnd: true,
+  });
+  expect(container.querySelector('.hover-menu')).not.toBeInTheDocument();
+});
+
 test('Should render editMode:false', () => {
   const props = createProps();
   props.editMode = false;
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 b/superset-frontend/src/dashboard/components/gridComponents/Tabs/index.js
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to superset-frontend/src/dashboard/components/gridComponents/Tabs/index.js
index 24a6a78d3f..30383821d3 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs/index.js
@@ -16,21 +16,6 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import Tabs from './Tabs';
 
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export default Tabs;
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.test.tsx
new file mode 100644
index 0000000000..bbba9f01eb
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.test.tsx
@@ -0,0 +1,201 @@
+/**
+ * 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 { fireEvent, render, screen } from 'spec/helpers/testing-library';
+import TabsRenderer, { TabItem, TabsRendererProps } from './TabsRenderer';
+
+const mockTabItems: TabItem[] = [
+  {
+    key: 'tab-1',
+    label: <div>Tab 1</div>,
+    closeIcon: <div>×</div>,
+    children: <div>Tab 1 Content</div>,
+  },
+  {
+    key: 'tab-2',
+    label: <div>Tab 2</div>,
+    closeIcon: <div>×</div>,
+    children: <div>Tab 2 Content</div>,
+  },
+];
+
+const mockProps: TabsRendererProps = {
+  tabItems: mockTabItems,
+  editMode: false,
+  renderHoverMenu: true,
+  tabsDragSourceRef: undefined,
+  handleDeleteComponent: jest.fn(),
+  tabsComponent: { id: 'test-tabs-id' },
+  activeKey: 'tab-1',
+  tabIds: ['tab-1', 'tab-2'],
+  handleClickTab: jest.fn(),
+  handleEdit: jest.fn(),
+  tabBarPaddingLeft: 16,
+};
+
+describe('TabsRenderer', () => {
+  beforeEach(() => {
+    jest.clearAllMocks();
+  });
+
+  test('renders tabs container with correct test attributes', () => {
+    render(<TabsRenderer {...mockProps} />);
+
+    const tabsContainer = screen.getByTestId('dashboard-component-tabs');
+
+    expect(tabsContainer).toBeInTheDocument();
+    expect(tabsContainer).toHaveClass('dashboard-component-tabs');
+  });
+
+  test('renders LineEditableTabs with correct props', () => {
+    render(<TabsRenderer {...mockProps} />);
+
+    const editableTabs = screen.getByTestId('nav-list');
+    expect(editableTabs).toBeInTheDocument();
+  });
+
+  test('applies correct tab bar padding', () => {
+    const { rerender } = render(<TabsRenderer {...mockProps} />);
+
+    let editableTabs = screen.getByTestId('nav-list');
+    expect(editableTabs).toBeInTheDocument();
+
+    rerender(<TabsRenderer {...mockProps} tabBarPaddingLeft={0} />);
+    editableTabs = screen.getByTestId('nav-list');
+
+    expect(editableTabs).toBeInTheDocument();
+  });
+
+  test('calls handleClickTab when tab is clicked', () => {
+    const handleClickTabMock = jest.fn();
+    const propsWithTab2Active = {
+      ...mockProps,
+      activeKey: 'tab-2',
+      handleClickTab: handleClickTabMock,
+    };
+    render(<TabsRenderer {...propsWithTab2Active} />);
+
+    const tabElement = screen.getByText('Tab 1').closest('[role="tab"]');
+    expect(tabElement).not.toBeNull();
+
+    fireEvent.click(tabElement!);
+
+    expect(handleClickTabMock).toHaveBeenCalledWith(0);
+    expect(handleClickTabMock).toHaveBeenCalledTimes(1);
+  });
+
+  test('shows hover menu in edit mode', () => {
+    const mockRef = { current: null };
+    const editModeProps: TabsRendererProps = {
+      ...mockProps,
+      editMode: true,
+      renderHoverMenu: true,
+      tabsDragSourceRef: mockRef,
+    };
+
+    render(<TabsRenderer {...editModeProps} />);
+
+    const hoverMenu = document.querySelector('.hover-menu');
+
+    expect(hoverMenu).toBeInTheDocument();
+  });
+
+  test('hides hover menu when not in edit mode', () => {
+    const viewModeProps: TabsRendererProps = {
+      ...mockProps,
+      editMode: false,
+      renderHoverMenu: true,
+    };
+
+    render(<TabsRenderer {...viewModeProps} />);
+
+    const hoverMenu = document.querySelector('.hover-menu');
+
+    expect(hoverMenu).not.toBeInTheDocument();
+  });
+
+  test('hides hover menu when renderHoverMenu is false', () => {
+    const mockRef = { current: null };
+    const noHoverMenuProps: TabsRendererProps = {
+      ...mockProps,
+      editMode: true,
+      renderHoverMenu: false,
+      tabsDragSourceRef: mockRef,
+    };
+
+    render(<TabsRenderer {...noHoverMenuProps} />);
+
+    const hoverMenu = document.querySelector('.hover-menu');
+
+    expect(hoverMenu).not.toBeInTheDocument();
+  });
+
+  test('renders with correct tab type based on edit mode', () => {
+    const { rerender } = render(
+      <TabsRenderer {...mockProps} editMode={false} />,
+    );
+
+    let editableTabs = screen.getByTestId('nav-list');
+    expect(editableTabs).toBeInTheDocument();
+
+    rerender(<TabsRenderer {...mockProps} editMode />);
+
+    editableTabs = screen.getByTestId('nav-list');
+
+    expect(editableTabs).toBeInTheDocument();
+  });
+
+  test('handles default props correctly', () => {
+    const minimalProps: TabsRendererProps = {
+      tabItems: mockProps.tabItems,
+      editMode: false,
+      handleDeleteComponent: mockProps.handleDeleteComponent,
+      tabsComponent: mockProps.tabsComponent,
+      activeKey: mockProps.activeKey,
+      tabIds: mockProps.tabIds,
+      handleClickTab: mockProps.handleClickTab,
+      handleEdit: mockProps.handleEdit,
+    };
+
+    render(<TabsRenderer {...minimalProps} />);
+
+    const tabsContainer = screen.getByTestId('dashboard-component-tabs');
+
+    expect(tabsContainer).toBeInTheDocument();
+  });
+
+  test('calls onEdit when edit action is triggered', () => {
+    const handleEditMock = jest.fn();
+    const editableProps = {
+      ...mockProps,
+      editMode: true,
+      handleEdit: handleEditMock,
+    };
+
+    render(<TabsRenderer {...editableProps} />);
+
+    expect(screen.getByTestId('nav-list')).toBeInTheDocument();
+  });
+
+  test('renders tab content correctly', () => {
+    render(<TabsRenderer {...mockProps} />);
+
+    expect(screen.getByText('Tab 1 Content')).toBeInTheDocument();
+    expect(screen.queryByText('Tab 2 Content')).not.toBeInTheDocument(); // 
Not active
+  });
+});
diff --git 
a/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
new file mode 100644
index 0000000000..52e34638f7
--- /dev/null
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/TabsRenderer.tsx
@@ -0,0 +1,121 @@
+/**
+ * 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 { memo, ReactElement, RefObject } from 'react';
+import { styled } from '@superset-ui/core';
+import {
+  LineEditableTabs,
+  TabsProps as AntdTabsProps,
+} from '@superset-ui/core/components/Tabs';
+import HoverMenu from '../../menu/HoverMenu';
+import DragHandle from '../../dnd/DragHandle';
+import DeleteComponentButton from '../../DeleteComponentButton';
+
+const StyledTabsContainer = styled.div`
+  width: 100%;
+  background-color: ${({ theme }) => theme.colorBgContainer};
+
+  & .dashboard-component-tabs-content {
+    height: 100%;
+  }
+
+  & > .hover-menu:hover {
+    opacity: 1;
+  }
+
+  &.dragdroppable-row .dashboard-component-tabs-content {
+    height: calc(100% - 47px);
+  }
+`;
+
+export interface TabItem {
+  key: string;
+  label: ReactElement;
+  closeIcon: ReactElement;
+  children: ReactElement;
+}
+
+export interface TabsComponent {
+  id: string;
+}
+
+export interface TabsRendererProps {
+  tabItems: TabItem[];
+  editMode: boolean;
+  renderHoverMenu?: boolean;
+  tabsDragSourceRef?: RefObject<HTMLDivElement>;
+  handleDeleteComponent: () => void;
+  tabsComponent: TabsComponent;
+  activeKey: string;
+  tabIds: string[];
+  handleClickTab: (index: number) => void;
+  handleEdit: AntdTabsProps['onEdit'];
+  tabBarPaddingLeft?: number;
+}
+
+/**
+ * TabsRenderer component handles the rendering of dashboard tabs
+ * Extracted from the main Tabs component for better separation of concerns
+ */
+const TabsRenderer = memo<TabsRendererProps>(
+  ({
+    tabItems,
+    editMode,
+    renderHoverMenu = true,
+    tabsDragSourceRef,
+    handleDeleteComponent,
+    tabsComponent,
+    activeKey,
+    tabIds,
+    handleClickTab,
+    handleEdit,
+    tabBarPaddingLeft = 0,
+  }) => (
+    <StyledTabsContainer
+      className="dashboard-component dashboard-component-tabs"
+      data-test="dashboard-component-tabs"
+    >
+      {editMode && renderHoverMenu && tabsDragSourceRef && (
+        <HoverMenu innerRef={tabsDragSourceRef} position="left">
+          <DragHandle position="left" />
+          <DeleteComponentButton onDelete={handleDeleteComponent} />
+        </HoverMenu>
+      )}
+
+      <LineEditableTabs
+        id={tabsComponent.id}
+        activeKey={activeKey}
+        onChange={key => {
+          if (typeof key === 'string') {
+            const tabIndex = tabIds.indexOf(key);
+            if (tabIndex !== -1) handleClickTab(tabIndex);
+          }
+        }}
+        onEdit={handleEdit}
+        data-test="nav-list"
+        type={editMode ? 'editable-card' : 'card'}
+        items={tabItems}
+        tabBarStyle={{ paddingLeft: tabBarPaddingLeft }}
+      />
+    </StyledTabsContainer>
+  ),
+);
+
+TabsRenderer.displayName = 'TabsRenderer';
+
+export default TabsRenderer;
diff --git 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/index.ts
similarity index 57%
copy from 
superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
copy to 
superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/index.ts
index 24a6a78d3f..320e8ebb02 100644
--- 
a/superset-frontend/src/dashboard/components/BuilderComponentPane/BuilderComponentPane.test.tsx
+++ 
b/superset-frontend/src/dashboard/components/gridComponents/TabsRenderer/index.ts
@@ -16,21 +16,5 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-
-import { render, screen } from 'spec/helpers/testing-library';
-import BuilderComponentPane from '.';
-
-jest.mock('src/dashboard/containers/SliceAdder', () => () => (
-  <div data-test="mock-slice-adder" />
-));
-
-test('BuilderComponentPane has correct tabs in correct order', () => {
-  render(<BuilderComponentPane topOffset={115} />);
-  const tabs = screen.getAllByRole('tab');
-  expect(tabs).toHaveLength(2);
-  expect(tabs[0]).toHaveTextContent('Charts');
-  expect(tabs[1]).toHaveTextContent('Layout elements');
-  expect(screen.getByRole('tab', { selected: true })).toHaveTextContent(
-    'Charts',
-  );
-});
+export { default } from './TabsRenderer';
+export type { TabsRendererProps, TabItem, TabsComponent } from 
'./TabsRenderer';
diff --git a/superset-frontend/src/dashboard/components/gridComponents/index.js 
b/superset-frontend/src/dashboard/components/gridComponents/index.js
index 38f3558864..8d3078c9eb 100644
--- a/superset-frontend/src/dashboard/components/gridComponents/index.js
+++ b/superset-frontend/src/dashboard/components/gridComponents/index.js
@@ -38,16 +38,6 @@ import Tab from './Tab';
 import Tabs from './Tabs';
 import DynamicComponent from './DynamicComponent';
 
-export { default as ChartHolder } from './ChartHolder';
-export { default as Markdown } from './Markdown';
-export { default as Column } from './Column';
-export { default as Divider } from './Divider';
-export { default as Header } from './Header';
-export { default as Row } from './Row';
-export { default as Tab } from './Tab';
-export { default as Tabs } from './Tabs';
-export { default as DynamicComponent } from './DynamicComponent';
-
 export const componentLookup = {
   [CHART_TYPE]: ChartHolder,
   [MARKDOWN_TYPE]: Markdown,
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js 
b/superset-frontend/src/dashboard/reducers/dashboardState.js
index 3c7c65c601..7897584b23 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.js
@@ -50,6 +50,7 @@ import {
   SET_DASHBOARD_LABELS_COLORMAP_SYNCED,
   SET_DASHBOARD_SHARED_LABELS_COLORS_SYNCABLE,
   SET_DASHBOARD_SHARED_LABELS_COLORS_SYNCED,
+  TOGGLE_NATIVE_FILTERS_BAR,
 } from '../actions/dashboardState';
 import { HYDRATE_DASHBOARD } from '../actions/hydrate';
 
@@ -271,6 +272,12 @@ export default function dashboardStateReducer(state = {}, 
action) {
         datasetsStatus: action.status,
       };
     },
+    [TOGGLE_NATIVE_FILTERS_BAR]() {
+      return {
+        ...state,
+        nativeFiltersBarOpen: action.isOpen,
+      };
+    },
   };
 
   if (action.type in actionHandlers) {
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.test.js 
b/superset-frontend/src/dashboard/reducers/dashboardState.test.js
index 39798ecf13..803e159657 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.test.js
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.test.js
@@ -27,6 +27,7 @@ import {
   SET_UNSAVED_CHANGES,
   TOGGLE_EXPAND_SLICE,
   TOGGLE_FAVE_STAR,
+  TOGGLE_NATIVE_FILTERS_BAR,
   UNSET_FOCUSED_FILTER_FIELD,
 } from 'src/dashboard/actions/dashboardState';
 
@@ -197,4 +198,20 @@ describe('dashboardState reducer', () => {
       column: 'column_2',
     });
   });
+
+  it('should toggle native filters bar', () => {
+    expect(
+      dashboardStateReducer(
+        { nativeFiltersBarOpen: false },
+        { type: TOGGLE_NATIVE_FILTERS_BAR, isOpen: true },
+      ),
+    ).toEqual({ nativeFiltersBarOpen: true });
+
+    expect(
+      dashboardStateReducer(
+        { nativeFiltersBarOpen: true },
+        { type: TOGGLE_NATIVE_FILTERS_BAR, isOpen: false },
+      ),
+    ).toEqual({ nativeFiltersBarOpen: false });
+  });
 });
diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.test.ts 
b/superset-frontend/src/dashboard/reducers/dashboardState.test.ts
index 5e77b41022..1594c1b2a6 100644
--- a/superset-frontend/src/dashboard/reducers/dashboardState.test.ts
+++ b/superset-frontend/src/dashboard/reducers/dashboardState.test.ts
@@ -20,10 +20,39 @@ import configureMockStore from 'redux-mock-store';
 import thunk from 'redux-thunk';
 import dashboardStateReducer from './dashboardState';
 import { setActiveTab, setActiveTabs } from '../actions/dashboardState';
+import { DashboardState } from '../types';
+
+// Type the reducer function properly since it's imported from JS
+type DashboardStateReducer = (
+  state: Partial<DashboardState> | undefined,
+  action: any,
+) => Partial<DashboardState>;
+const typedDashboardStateReducer =
+  dashboardStateReducer as DashboardStateReducer;
 
 const middlewares = [thunk];
 const mockStore = configureMockStore(middlewares);
 
+// Helper function to create mock dashboard state with proper types
+const createMockDashboardState = (
+  overrides: Partial<DashboardState> = {},
+): DashboardState => ({
+  editMode: false,
+  isPublished: false,
+  directPathToChild: [],
+  activeTabs: [],
+  fullSizeChartId: null,
+  isRefreshing: false,
+  isFiltersRefreshing: false,
+  hasUnsavedChanges: false,
+  dashboardIsSaving: false,
+  colorScheme: '',
+  sliceIds: [],
+  directPathLastUpdated: 0,
+  nativeFiltersBarOpen: false,
+  ...overrides,
+});
+
 describe('DashboardState reducer', () => {
   describe('SET_ACTIVE_TAB', () => {
     it('switches a single tab', () => {
@@ -34,16 +63,28 @@ describe('DashboardState reducer', () => {
       const request = setActiveTab('tab1');
       const thunkAction = request(store.dispatch, store.getState);
 
-      expect(dashboardStateReducer({ activeTabs: [] }, thunkAction)).toEqual({
-        activeTabs: ['tab1'],
-        inactiveTabs: [],
-      });
+      expect(
+        typedDashboardStateReducer(
+          createMockDashboardState({ activeTabs: [] }),
+          thunkAction,
+        ),
+      ).toEqual(
+        expect.objectContaining({
+          activeTabs: ['tab1'],
+          inactiveTabs: [],
+        }),
+      );
 
       const request2 = setActiveTab('tab2', 'tab1');
       const thunkAction2 = request2(store.dispatch, store.getState);
       expect(
-        dashboardStateReducer({ activeTabs: ['tab1'] }, thunkAction2),
-      ).toEqual({ activeTabs: ['tab2'], inactiveTabs: [] });
+        typedDashboardStateReducer(
+          createMockDashboardState({ activeTabs: ['tab1'] }),
+          thunkAction2,
+        ),
+      ).toEqual(
+        expect.objectContaining({ activeTabs: ['tab2'], inactiveTabs: [] }),
+      );
     });
 
     it('switches a multi-depth tab', () => {
@@ -63,75 +104,90 @@ describe('DashboardState reducer', () => {
       });
       let request = setActiveTab('TAB-B', 'TAB-A');
       let thunkAction = request(store.dispatch, store.getState);
-      let result = dashboardStateReducer(
-        { activeTabs: ['TAB-1', 'TAB-A', 'TAB-__a'] },
+      let result = typedDashboardStateReducer(
+        createMockDashboardState({ activeTabs: ['TAB-1', 'TAB-A', 'TAB-__a'] 
}),
         thunkAction,
       );
-      expect(result).toEqual({
-        activeTabs: expect.arrayContaining(['TAB-1', 'TAB-B']),
-        inactiveTabs: ['TAB-__a'],
-      });
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: expect.arrayContaining(['TAB-1', 'TAB-B']),
+          inactiveTabs: ['TAB-__a'],
+        }),
+      );
       request = setActiveTab('TAB-2', 'TAB-1');
       thunkAction = request(store.dispatch, () => ({
         ...(store.getState() ?? {}),
         dashboardState: result,
       }));
-      result = dashboardStateReducer(result, thunkAction);
-      expect(result).toEqual({
-        activeTabs: ['TAB-2'],
-        inactiveTabs: expect.arrayContaining(['TAB-B', 'TAB-__a']),
-      });
+      result = typedDashboardStateReducer(result, thunkAction);
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: ['TAB-2'],
+          inactiveTabs: expect.arrayContaining(['TAB-B', 'TAB-__a']),
+        }),
+      );
       request = setActiveTab('TAB-1', 'TAB-2');
       thunkAction = request(store.dispatch, () => ({
         ...(store.getState() ?? {}),
         dashboardState: result,
       }));
-      result = dashboardStateReducer(result, thunkAction);
-      expect(result).toEqual({
-        activeTabs: expect.arrayContaining(['TAB-1', 'TAB-B']),
-        inactiveTabs: ['TAB-__a'],
-      });
+      result = typedDashboardStateReducer(result, thunkAction);
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: expect.arrayContaining(['TAB-1', 'TAB-B']),
+          inactiveTabs: ['TAB-__a'],
+        }),
+      );
       request = setActiveTab('TAB-A', 'TAB-B');
       thunkAction = request(store.dispatch, () => ({
         ...(store.getState() ?? {}),
         dashboardState: result,
       }));
-      result = dashboardStateReducer(result, thunkAction);
-      expect(result).toEqual({
-        activeTabs: expect.arrayContaining(['TAB-1', 'TAB-A', 'TAB-__a']),
-        inactiveTabs: [],
-      });
+      result = typedDashboardStateReducer(result, thunkAction);
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: expect.arrayContaining(['TAB-1', 'TAB-A', 'TAB-__a']),
+          inactiveTabs: [],
+        }),
+      );
       request = setActiveTab('TAB-2', 'TAB-1');
       thunkAction = request(store.dispatch, () => ({
         ...(store.getState() ?? {}),
         dashboardState: result,
       }));
-      result = dashboardStateReducer(result, thunkAction);
-      expect(result).toEqual({
-        activeTabs: expect.arrayContaining(['TAB-2']),
-        inactiveTabs: ['TAB-A', 'TAB-__a'],
-      });
+      result = typedDashboardStateReducer(result, thunkAction);
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: expect.arrayContaining(['TAB-2']),
+          inactiveTabs: ['TAB-A', 'TAB-__a'],
+        }),
+      );
       request = setActiveTab('TAB-1', 'TAB-2');
       thunkAction = request(store.dispatch, () => ({
         ...(store.getState() ?? {}),
         dashboardState: result,
       }));
-      result = dashboardStateReducer(result, thunkAction);
-      expect(result).toEqual({
-        activeTabs: expect.arrayContaining(['TAB-1', 'TAB-A', 'TAB-__a']),
-        inactiveTabs: [],
-      });
+      result = typedDashboardStateReducer(result, thunkAction);
+      expect(result).toEqual(
+        expect.objectContaining({
+          activeTabs: expect.arrayContaining(['TAB-1', 'TAB-A', 'TAB-__a']),
+          inactiveTabs: [],
+        }),
+      );
     });
   });
   it('SET_ACTIVE_TABS', () => {
     expect(
-      dashboardStateReducer({ activeTabs: [] }, setActiveTabs(['tab1'])),
-    ).toEqual({ activeTabs: ['tab1'] });
+      typedDashboardStateReducer(
+        createMockDashboardState({ activeTabs: [] }),
+        setActiveTabs(['tab1']),
+      ),
+    ).toEqual(expect.objectContaining({ activeTabs: ['tab1'] }));
     expect(
-      dashboardStateReducer(
-        { activeTabs: ['tab1', 'tab2'] },
+      typedDashboardStateReducer(
+        createMockDashboardState({ activeTabs: ['tab1', 'tab2'] }),
         setActiveTabs(['tab3', 'tab4']),
       ),
-    ).toEqual({ activeTabs: ['tab3', 'tab4'] });
+    ).toEqual(expect.objectContaining({ activeTabs: ['tab3', 'tab4'] }));
   });
 });
diff --git a/superset-frontend/src/dashboard/types.ts 
b/superset-frontend/src/dashboard/types.ts
index c7bf2c097a..15b9095932 100644
--- a/superset-frontend/src/dashboard/types.ts
+++ b/superset-frontend/src/dashboard/types.ts
@@ -107,6 +107,7 @@ export type DashboardState = {
   colorScheme: string;
   sliceIds: number[];
   directPathLastUpdated: number;
+  nativeFiltersBarOpen?: boolean;
   css?: string;
   focusedFilterField?: {
     chartId: number;

Reply via email to