Skip to content

fix(core): align Drill to detail table headers with scrollable body - #43472

Open
anishtilekar wants to merge 5 commits into
apache:masterfrom
anishtilekar:fix/drill-to-detail-header-alignment
Open

fix(core): align Drill to detail table headers with scrollable body#43472
anishtilekar wants to merge 5 commits into
apache:masterfrom
anishtilekar:fix/drill-to-detail-header-alignment

Conversation

@anishtilekar

@anishtilekar anishtilekar commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

SUMMARY

In the Drill to detail modal, VirtualTable (used by DrillDetailPane) renders its header as a plain antd <thead> inside a <table>, separate from the react-window Grid that renders the body rows. Column widths for both are derived from the same measured container width.

When the current page's rows are tall enough to need vertical scrolling, the Grid's own scrollbar eats into the body's visible column width, but the header isn't shrunk to match, so header cells drift out of alignment with their data columns (and can appear clipped/truncated). This is the same class of bug already fixed for the non-virtualized table's sticky header in useSticky.tsx (#36190, #36891, #42573), but VirtualTable.tsx uses a completely different header/body split and wasn't covered by those fixes.

This PR reserves the scrollbar's width when sizing columns whenever the current page's row count would overflow the visible height, so the header and the Grid's columns stay in sync - mirroring the approach used in useSticky.tsx.

BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF

N/A - the bug only reproduces with a live scrollable dataset; see reproduction steps in the linked issue.

TESTING INSTRUCTIONS

  1. Open a dashboard or Explore view with a chart that supports Drill to detail.
  2. Right-click a data point (or use the chart menu) -> Drill to detail.
  3. With enough columns/rows that the modal table needs to scroll vertically, confirm the column headers line up with their data cells.
  4. npx jest packages/superset-ui-core/src/components/Table/VirtualTable.test.tsx packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.test.ts from superset-frontend/.

ADDITIONAL INFORMATION

VirtualTable renders its header as a plain antd <thead> separate from
the react-window Grid that renders the body. When the Grid's vertical
scrollbar appears, it eats into the body's visible column width
without shrinking the header, so header and data columns drift out of
alignment - most noticeable in the Drill to detail modal, which always
paginates and often needs to scroll.

Reserve the scrollbar's width when computing column widths whenever
the current page's rows would overflow the visible height, mirroring
the equivalent fix already applied to the non-virtualized table's
sticky header in useSticky.tsx.

Fixes apache#43383
@dosubot dosubot Bot added change:frontend Requires changing the frontend dashboard:drill-to-detail labels Aug 24, 2026
@netlify

netlify Bot commented Aug 24, 2026

Copy link
Copy Markdown

Deploy Preview for superset-docs-preview ready!

Name Link
🔨 Latest commit a6e2f15
🔍 Latest deploy log https://app.netlify.com/projects/superset-docs-preview/deploys/6a9022b8f3de6a00087714c5
😎 Deploy Preview https://deploy-preview-43472--superset-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #e49b9f

Actionable Suggestions - 1
  • superset-frontend/packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.ts - 1
    • Duplicate measureScrollBarSize logic · Line 1-59
Additional Suggestions - 1
  • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.test.tsx - 1
    • Mock cleanup placement · Line 172-172
      The `resizeSpy.mockRestore()` call at line 172 is correctly placed to clean up the spy. However, if more tests are added that mock useResizeDetector, consider moving the cleanup to a beforeEach/afterEach block to ensure isolation.
Review Details
  • Files reviewed - 4 · Commit Range: 014cc0f..014cc0f
    • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.test.tsx
    • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx
    • superset-frontend/packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.test.ts
    • superset-frontend/packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

Comment on lines +1 to +59
/**
* 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.
*/

let cached: number | undefined;

const css = (x: TemplateStringsArray) => x.join('\n');

function measureScrollBarSize() {
const inner = document.createElement('div');
const outer = document.createElement('div');
inner.style.cssText = css`
width: auto;
height: 100%;
overflow: scroll;
`;
outer.style.cssText = css`
position: absolute;
visibility: hidden;
overflow: hidden;
width: 100px;
height: 50px;
`;
outer.append(inner);
document.body.append(outer);
const size = outer.clientWidth - inner.clientWidth;
outer.remove();
return size;
}

// Measures the browser/OS native scrollbar width. `VirtualTable`'s
// react-window `Grid` body doesn't apply any custom `::-webkit-scrollbar`
// styling, so its actual vertical scrollbar always renders at this native
// size - used to keep the (separately rendered) header in sync with how
// much horizontal space the scrollbar steals from the body's columns.
export default function getScrollBarSize(forceRefresh = false) {
if (typeof document === 'undefined') {
return 0;
}
if (cached === undefined || forceRefresh) {
cached = measureScrollBarSize();
}
return cached;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate measureScrollBarSize logic

Lines 20-58 (the entire implementation) are a semantic duplicate of plugins/plugin-chart-table/src/DataTable/utils/getScrollBarSize.ts (lines 23-70). The only substantive difference is removal of the custom-scrollbar API. This duplication creates maintenance risk — both copies must stay in sync if measureScrollBarSize changes. The existing implementation already handles the non-custom case used by VirtualTable; the caller at line 171 of VirtualTable.tsx does not need custom scrollbar behavior. A single shared implementation (with re-export if needed) eliminates divergence risk.

Code Review Run #e49b9f


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

Comment on lines +169 to +171
const rowCount = pageSize ?? dataSource?.length ?? 0;
const hasVerticalScroll = rowCount * cellSize > availableHeight;
const scrollBarSize = hasVerticalScroll ? getScrollBarSize() : 0;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: hasVerticalScroll only compares row content height with the nominal Grid height. When the columns are wider than the Grid, a horizontal scrollbar consumes part of the vertical viewport; rows that otherwise exactly fit can then trigger a vertical scrollbar as well. Because that case is not included here, the header does not reserve the vertical scrollbar width and becomes misaligned. Determine scrollbar presence using both horizontal and vertical overflow, including the scrollbar footprint. [incorrect condition logic]

Severity Level: Major ⚠️
- ⚠️ Wide virtualized tables can gain an unreserved vertical scrollbar.
- ⚠️ Header cells misalign when horizontal overflow reduces Grid height.
- ⚠️ Drill-detail columns may appear clipped or shifted.

Use CodeAnt Skill Fix in Cursor Fix in VSCode Claude

Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx
**Line:** 169:171
**Comment:**
	*Incorrect Condition Logic: `hasVerticalScroll` only compares row content height with the nominal Grid height. When the columns are wider than the Grid, a horizontal scrollbar consumes part of the vertical viewport; rows that otherwise exactly fit can then trigger a vertical scrollbar as well. Because that case is not included here, the header does not reserve the vertical scrollbar width and becomes misaligned. Determine scrollbar presence using both horizontal and vertical overflow, including the scrollbar footprint.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
Once fix is implemented, also check other comments on the same PR, and ask user if the user wants to fix the rest of the comments as well. if said yes, then fetch all the comments validate the correctness and implement a minimal fix
👍 | 👎

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed—the horizontal scrollbar reduces the Grid's client height. A page whose rows fit the outer height can then gain a vertical scrollbar with no header reservation, recreating the alignment problem. Should the detection include the horizontal scrollbar footprint?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The detection should account for the horizontal scrollbar’s height before deciding whether the vertical scrollbar is present.

Use the Grid’s actual column width to detect horizontal overflow, then reduce the effective height by the scrollbar footprint:

const scrollBarSize = getScrollBarSize();

const hasHorizontalScroll = totalWidth > tableWidth;
const effectiveHeight =
  availableHeight - (hasHorizontalScroll ? scrollBarSize : 0);

const hasVerticalScroll = rowCount * cellSize > effectiveHeight;
const columnSizingWidth = Math.max(
  tableWidth - (hasVerticalScroll ? scrollBarSize : 0),
  0,
);

Because totalWidth currently depends on columnSizingWidth, the width calculation should be structured in two passes (or otherwise determine horizontal overflow from the unadjusted column widths) to avoid a circular dependency. This covers the edge case where rows exactly fit the nominal height but a horizontal scrollbar reduces the Grid’s client height and causes vertical overflow.

@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.29630% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 78.87%. Comparing base (7f1b414) to head (014cc0f).
⚠️ Report is 3 commits behind head on master.

Files with missing lines Patch % Lines
...ore/src/components/Table/utils/getScrollBarSize.ts 94.11% 1 Missing ⚠️

❌ Your project check has failed because the head coverage (99.97%) is below the target coverage (100.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #43472   +/-   ##
=======================================
  Coverage   78.86%   78.87%           
=======================================
  Files        2876     2877    +1     
  Lines      164623   164648   +25     
  Branches    38019    38028    +9     
=======================================
+ Hits       129835   129861   +26     
+ Misses      32345    32344    -1     
  Partials     2443     2443           
Flag Coverage Δ
javascript 74.22% <96.29%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@bito-code-review

bito-code-review Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Code Review Agent Run #9f46fd

Actionable Suggestions - 0
Filtered by Review Rules

Bito filtered these suggestions based on rules created automatically for your feedback. Manage rules.

  • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx - 1
Review Details
  • Files reviewed - 1 · Commit Range: 014cc0f..dcd7483
    • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

@anishtilekar
anishtilekar force-pushed the fix/drill-to-detail-header-alignment branch from 508250f to 8717947 Compare August 25, 2026 05:45

@bito-code-review bito-code-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Agent Run #5493ea

Actionable Suggestions - 1
  • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx - 1
Review Details
  • Files reviewed - 4 · Commit Range: cc8ded7..8717947
    • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.test.tsx
    • superset-frontend/packages/superset-ui-core/src/components/Table/VirtualTable.tsx
    • superset-frontend/packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.test.ts
    • superset-frontend/packages/superset-ui-core/src/components/Table/utils/getScrollBarSize.ts
  • Files skipped - 0
  • Tools
    • Whispers (Secret Scanner) - ✔︎ Successful
    • Detect-secrets (Secret Scanner) - ✔︎ Successful
    • Eslint (Linter) - ✔︎ Successful

Bito Usage Guide

Commands

Type the following command in the pull request comment and save the comment.

  • /review - Manually triggers an incremental AI Review.

  • /review full - Manually triggers a full AI Review.

  • /pause - Pauses automatic reviews on this pull request.

  • /resume - Resumes automatic reviews.

  • /resolve - Marks all Bito-posted review comments as resolved.

  • /abort - Cancels all in-progress reviews.

Refer to the documentation for additional commands.

Configuration

This repository uses Superset You can customize the agent settings here or contact your Bito workspace admin at evan@preset.io.

Documentation & Help

AI Code Review powered by Bito Logo

const { ref } = useResizeDetector({ onResize });
const theme = useTheme();

const cellSize = size === TableSize.Middle ? MIDDLE : SMALL;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete size enum mapping

size === TableSize.Middle ? MIDDLE : SMALL silently maps both TableSize.Small and TableSize.Large to 39px, but TableSize.Large is a distinct, user-visible size that should render a taller row. If Large is intentionally collapsed into Small's height, document it; otherwise branch on TableSize.Large or adjust the default accordingly. Also note dataSource is already extracted at line 142, so moving cellSize to after the destructuring block is safe.

Code Review Run #5493ea


Should Bito avoid suggestions like this for future reviews? (Manage Rules)

  • Yes, avoid them

pagination && typeof pagination === 'object'
? pagination.pageSize
: undefined;
const rowCount = pageSize ?? dataSource?.length ?? 0;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a paginated table's final page contains fewer rows than pagination.pageSize, this reserves scrollbar width for the configured page capacity even though the mounted rows fit. The header then becomes narrower than the body and reintroduces the alignment problem on short pages. Should this use the actual mounted row count when determining vertical overflow?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Drill to detail modal VirtualTable header and body become misaligned Drill to detail: column headers misaligned with table body (headers truncated)

2 participants