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 000000000000..e01556f33696
--- /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:
-
-);
+ );
+};
export default BuilderComponentPane;
diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx
index 19215399dd1c..880d5c7fbe80 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 784d6020cd14..253c17dc4224 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(
@@ -297,6 +296,7 @@ const DashboardContainer: FC = ({ 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 4d5190e79dfc..f73fe36e9aeb 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 = ({ children }) => {
+const DashboardWrapper: FC> = ({ children }) => {
const editMode = useSelector(
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 a338e21a9427..c7b7a56fdf42 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(
() => getUrlParam(URL_PARAMS.showFilters) ?? true,
);
const canEdit = useSelector(
({ dashboardInfo }) => dashboardInfo.dash_edit_perm,
);
+ const dashboardFiltersOpen = useSelector(
+ 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 ec12d98d7bea..d81a6ed0f253 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 c5f743a52c51..a00b9e88c086 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/gridComponents/Chart/index.js b/superset-frontend/src/dashboard/components/gridComponents/Chart/index.js
new file mode 100644
index 000000000000..7cda5527b4aa
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Chart/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Chart from './Chart';
+
+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 072a087c8f93..aac33a802cb9 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/gridComponents/ChartHolder/index.ts b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/index.ts
new file mode 100644
index 000000000000..f5a0b92ff193
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/ChartHolder/index.ts
@@ -0,0 +1,19 @@
+/**
+ * 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.
+ */
+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 b70f865dd303..e67be2c20d5d 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/gridComponents/Column/index.js b/superset-frontend/src/dashboard/components/gridComponents/Column/index.js
new file mode 100644
index 000000000000..9b22df6f6ebe
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Column/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Column from './Column';
+
+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 466b77d0b78e..3247cf794d69 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 85a30d115d17..1cf54aab0d7b 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/gridComponents/Divider/index.js b/superset-frontend/src/dashboard/components/gridComponents/Divider/index.js
new file mode 100644
index 000000000000..8a7a0c9781ac
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Divider/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Divider from './Divider';
+
+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 000000000000..d3d2ade7ca91
--- /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 = () => (
+
),
+);
+
+jest.mock('src/dashboard/components/DeleteComponentButton', () =>
+ jest.fn(({ onDelete }) => (
+
+ )),
+);
+
+jest.mock('src/dashboard/components/menu/BackgroundStyleDropdown', () =>
+ jest.fn(({ onChange, value }) => (
+
+ )),
+);
+
+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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ 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();
+
+ // 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();
+
+ // 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();
+
+ // 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();
+
+ // 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(, {
+ 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();
+
+ // 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();
+
+ 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 66db0fd898d0..eb06833fba0d 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) => void;
+ parentId: string;
+ id: string;
};
-const DynamicComponent: FC = ({
+const DynamicComponent: FC = ({
component,
parentComponent,
index,
diff --git a/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts
new file mode 100644
index 000000000000..482eedb7f059
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/DynamicComponent/index.ts
@@ -0,0 +1,19 @@
+/**
+ * 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.
+ */
+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 48c969bb9b53..1f204406a27c 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/gridComponents/Header/index.js b/superset-frontend/src/dashboard/components/gridComponents/Header/index.js
new file mode 100644
index 000000000000..87090f257488
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Header/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Header from './Header';
+
+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 0d968b1b7a7f..c10d10b7762c 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/gridComponents/Markdown/index.js b/superset-frontend/src/dashboard/components/gridComponents/Markdown/index.js
new file mode 100644
index 000000000000..af0149d0681e
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Markdown/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Markdown from './Markdown';
+
+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 61cd5ff69e51..917cdfc9654a 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 e15d116f06df..2f8499a6fe6e 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/gridComponents/Row/index.js b/superset-frontend/src/dashboard/components/gridComponents/Row/index.js
new file mode 100644
index 000000000000..2b78be10dc73
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Row/index.js
@@ -0,0 +1,21 @@
+/**
+ * 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 Row from './Row';
+
+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 74c5f4813aee..000000000000
--- 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(
-
-
-
-
- ,
- );
- }
-
- 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 05ceb7e0cc42..3ff5f2879783 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(() => ),
diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tab/index.js b/superset-frontend/src/dashboard/components/gridComponents/Tab/index.js
new file mode 100644
index 000000000000..f1c4c41d48d6
--- /dev/null
+++ b/superset-frontend/src/dashboard/components/gridComponents/Tab/index.js
@@ -0,0 +1,22 @@
+/**
+ * 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 Tab from './Tab';
+
+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 d3c606fc2b8b..000000000000
--- 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 }) => (
-