Skip to content

Commit 862b004

Browse files
sadpandajoeclaude
andcommitted
fix(dashboard): restore top-level tab drop target height for dashboards with content
PR #37891 moved the DashboardHeader out of the root Droppable, leaving it with zero height when no top-level tabs exist. This made it impossible to drag a Tabs component onto dashboards that already have content. Add min-height to .empty-droptarget in StyledHeader, matching the pattern used by DashboardGrid for its drop targets. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8ce2343 commit 862b004

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.test.tsx

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,48 @@ test('should render ParentSize wrapper with height 100% for tabs', async () => {
487487
expect(tabPanels.length).toBeGreaterThan(0);
488488
});
489489

490+
test('should apply min-height to the top-level tab drop target so tabs can be dropped on dashboards with content', () => {
491+
(useStoredSidebarWidth as jest.Mock).mockImplementation(() => [
492+
100,
493+
jest.fn(),
494+
]);
495+
(fetchFaveStar as jest.Mock).mockReturnValue({ type: 'mock-action' });
496+
(setActiveTab as jest.Mock).mockReturnValue({ type: 'mock-action' });
497+
498+
const { container } = render(<DashboardBuilder />, {
499+
useRedux: true,
500+
store: storeWithState({
501+
...mockState,
502+
dashboardLayout: undoableDashboardLayout,
503+
dashboardState: { ...mockState.dashboardState, editMode: true },
504+
}),
505+
useDnd: true,
506+
useTheme: true,
507+
});
508+
509+
const headerWrapper = container.querySelector(
510+
'[data-test="dashboard-header-wrapper"]',
511+
);
512+
expect(headerWrapper).toBeInTheDocument();
513+
514+
// Verify the StyledHeader CSS includes min-height for the empty drop target.
515+
// Without this, the drop target has zero height and users cannot drag tabs
516+
// onto dashboards that already have content (the Droppable renders an empty
517+
// div and needs explicit min-height to be a valid react-dnd hover target).
518+
const allRules = Array.from(document.styleSheets).flatMap(sheet => {
519+
try {
520+
return Array.from(sheet.cssRules).map(rule => rule.cssText);
521+
} catch {
522+
return [];
523+
}
524+
});
525+
const emptyDroptargetRule = allRules.find(
526+
rule =>
527+
rule.includes('.empty-droptarget') && rule.includes('min-height'),
528+
);
529+
expect(emptyDroptargetRule).toBeDefined();
530+
});
531+
490532
test('should maintain layout when switching between tabs', async () => {
491533
(useStoredSidebarWidth as jest.Mock).mockImplementation(() => [
492534
100,

superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ const StyledHeader = styled.div<{ filterBarWidth: number }>`
9999
z-index: 99;
100100
max-width: calc(100vw - ${filterBarWidth}px);
101101
102+
.empty-droptarget {
103+
min-height: ${theme.sizeUnit * 4}px;
104+
}
105+
102106
.empty-droptarget:before {
103107
position: absolute;
104108
content: '';

0 commit comments

Comments
 (0)