Skip to content

feat: content-aware column widths, width estimation tests, and screenshot comparison infra#606

Merged
paddymul merged 27 commits intomainfrom
feat/compact-number-597
Feb 28, 2026
Merged

feat: content-aware column widths, width estimation tests, and screenshot comparison infra#606
paddymul merged 27 commits intomainfrom
feat/compact-number-597

Conversation

@paddymul
Copy link
Collaborator

@paddymul paddymul commented Feb 27, 2026

Summary

  • Content-aware per-column minWidth: Python's estimate_min_width_px() computes pixel width from displayer type, data range, header name, and histogram presence — replaces the blanket defaultColDef.minWidth: 80 that wasted space on narrow columns
  • Screenshot comparison infrastructure: CI captures before/after screenshots at baseline vs current branch, with download script and self-contained HTML comparison viewer
  • Comprehensive test coverage: 23 unit tests for width estimation, 22 Storybook stories covering width contention, large numbers, pinned rows, Python-computed minWidth, and histogram + narrow columns
  • Design doc: docs/column-width-design.md documents all width levers (per-column ag_grid_specs, grid-level autoSizeStrategy, displayer choice, theme params) and four valid sizing approaches (readable, compact, balanced, data-priority)

Key changes

Python (buckaroo/customizations/styling.py)

  • _formatted_char_count() estimates character width per displayer type (float, integer, compact_number, string, datetime)
  • estimate_min_width_px() computes pixel width from max(data width, header width), with 100px histogram floor
  • DefaultMainStyling.style_column() sets ag_grid_specs.minWidth per column

JS (DFViewerInfinite.tsx, gridUtils.ts)

  • Removed blanket minWidth: 80 from defaultColDef
  • Per-column ag_grid_specs flows through ...f.ag_grid_specs spread in baseColToColDef()
  • extra_grid_config.autoSizeStrategy override supported

Stories (22 total across 6 sections)

  • A: Width contention matrix (9 stories — few/many cols × short/long headers × short/long data)
  • B: Large numbers / compact_number (4 stories)
  • C: Pinned row / index alignment (2 stories)
  • D: Mixed scenarios (2 stories)
  • E: Python-computed ag_grid_specs.minWidth (3 stories — narrow ints, long headers, mixed types)
  • F: Histogram + narrow columns (2 stories — with/without histogram minimum)

CI (.github/workflows/checks.yml)

  • StylingScreenshots job captures before screenshots at baseline b7956f8, after at current branch
  • Uploads as separate artifacts for download and comparison

Test plan

  • 23 Python unit tests for estimate_min_width_px() and _formatted_char_count() pass
  • 81 JS tests pass
  • 546 Python tests pass
  • Storybook Playwright screenshots capture all 22 stories
  • Histogram pinned rows render correctly in F20/F21 stories
  • CI green (all jobs except infra flake on uv install)

🤖 Generated with Claude Code

- 16 Storybook stories reproducing #587, #595-#597, #599-#602
- Playwright spec capturing screenshots with SCREENSHOT_DIR env var
- CI job StylingScreenshots: captures after (current) + before (b7956f8)
  via git worktree, uploads as two named artifacts
- download_styling_screenshots.sh: fetch artifacts via gh CLI
- gen_screenshot_compare.py: self-contained before/after HTML viewer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions
Copy link

github-actions bot commented Feb 27, 2026

📦 TestPyPI package published

pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.12.12.dev22511604837

or with uv:

uv pip install --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo==0.12.12.dev22511604837

MCP server for Claude Code

claude mcp add buckaroo-table -- uvx --from "buckaroo[mcp]==0.12.12.dev22511604837" --index-strategy unsafe-best-match --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ buckaroo-table

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 921c5a3991

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

*/
const STORIES = [
// Section A – width/contention (#595, #596, #599, #600)
{ id: 'buckaroo-dfviewer-stylingissues--fewcols-shorthdr-shortdata', name: 'A1_FewCols_ShortHdr_ShortData', issues: '#599' },

Choose a reason for hiding this comment

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

P1 Badge Use valid Storybook IDs for styling screenshot captures

The id values in this spec don't match Storybook's generated slugs for these exports (e.g. FewCols_ShortHdr_ShortData resolves to ...--few-cols-short-hdr-short-data, not ...--fewcols-shorthdr-shortdata), so the Playwright run navigates to missing-story pages instead of the target stories. Because the wait condition accepts #storybook-root, these tests can still pass and produce screenshots of error/fallback content, which makes the before/after artifacts misleading rather than validating the styling regressions.

Useful? React with 👍 / 👎.

- Fix Storybook story IDs: export names go through startCase() before
  slugifying, so FewCols_ShortHdr_ShortData → few-cols-short-hdr-short-data
- Remove extraneous f-string prefixes in gen_screenshot_compare.py (ruff F541)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
paddymul and others added 2 commits February 27, 2026 13:40
The reuseExistingServer:true in playwright config caused the "before"
screenshots to be captured against the still-running "after" storybook,
making all screenshots identical.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Left sidebar (20%) with story list and keyboard navigation (j/k/arrows).
Detail view with stacked before/after images, zoom slider (50-500%),
and pan X/Y controls that apply to both images simultaneously.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
paddymul and others added 2 commits February 27, 2026 13:49
Remove the broken worktree-based before/after approach. Each branch
just captures its own screenshots and uploads one artifact. Compare
by running CI on two separate branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GitHub PR events checkout a merge of PR+base, so the baseline branch
was getting main's fixes merged in. Use head.sha to get the actual
branch code. Also use --no-frozen-lockfile for older branches.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The AG-Grid is inside a Shadow DOM wrapper, so page.locator can't
find .ag-body-viewport. Use page.evaluate to access shadowRoot.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…croll

C13/C14/D15 had short headers that fit in 800px with no scrollbar.
Use long headers + scroll to scrollWidth to expose #587 alignment bug.
Also simplified scroll code — Playwright locators pierce shadow DOM.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fitCellContents was shrinking all columns to fit 800px viewport
(scrollWidth == clientWidth). Narrower container forces overflow
so the scroll-right in the spec actually works.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erflow

fitCellContents shrinks all columns to fit container regardless of
defaultMinWidth. Explicit minWidth on ColDef forces horizontal scroll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
AG-Grid fitCellContents prevents programmatic scroll (scrollWidth==clientWidth).
Click a cell then press End to let AG-Grid handle navigation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
15 cols with long headers (revenue_total, etc.) and 4-digit year values.
Without defaultMinWidth, fitCellContents crushes columns to data width
(~35px), truncating headers. With the fix, columns get min 80px.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
fitCellContents never crushes columns, so use ag_grid_specs maxWidth
to cap columns at 50px. 7-digit data should show "..." truncation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
paddymul and others added 2 commits February 27, 2026 17:25
- Set minWidth:80 on defaultColDef so AG-Grid enforces a minimum column
  width regardless of auto-size strategy
- Remove broken defaultMinWidth on fitCellContents (that property only
  exists on fitGridWidth strategy and was silently ignored)
- Update A9 story to demonstrate the fix: width:40 + suppressAutoSize
  creates narrow columns; minWidth:80 overrides to show readable values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Navigate directly to a story via URL hash (e.g. compare.html#A9_ManyCols_LongHdr_YearData).
Also update A9 tag from no-diff to diff.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…trategy override

- A9 now uses fitGridWidth strategy (15 cols in 800px → ~53px each)
- extra_grid_config.autoSizeStrategy takes precedence over default
- minWidth:80 on defaultColDef prevents columns from crushing → readable values

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
25 cols in 800px → ~32px each without minWidth → "2,..." on every cell.
With minWidth:80 → 10 readable columns with horizontal scroll.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update StylingScreenshots CI job to:
- Start Storybook (was missing, causing Playwright failures)
- Checkout baseline b7956f8, copy stories, capture "before" screenshots
- Checkout current branch, capture "after" screenshots
- Upload as two separate artifacts (styling-screenshots-before/after)

The download script and compare viewer already expect this artifact layout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
pnpm install --no-frozen-lockfile at baseline modifies pnpm-lock.yaml,
causing git checkout to fail. Use -f flag and git clean to discard
changes from the baseline install.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace blanket defaultColDef.minWidth:80 with per-column widths
computed from displayer type, data range, and header name length.

Python side (styling.py):
- estimate_min_width_px() computes pixel width from formatted char count
- Accounts for commas in integers, decimal+fraction in floats, header length
- Histogram pinned rows request 100px minimum
- Set via ag_grid_specs.minWidth per column

JS side:
- Remove hardcoded minWidth:80 from defaultColDef
- Per-column ag_grid_specs.minWidth already flows through via gridUtils

Narrow columns (1-2 digit data, short headers) stay tight instead of
being forced to 80px. Wide columns get appropriate minimums.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…istogram stories

- 23 unit tests for estimate_min_width_px() and _formatted_char_count()
  covering all displayer types, edge cases (NaN, None, negative), and
  histogram minimum width enforcement
- 3 new stories (Section E) exercising Python-computed ag_grid_specs.minWidth
  with realistic values (38px narrow ints, 134px long headers, mixed types)
- 2 new stories (Section F) showing histogram + narrow columns: one with
  100px histogram minimum, one without (to show the difference)
- Updated Playwright spec and compare tool for new stories
- Added docs/column-width-design.md documenting all width levers and approaches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The histogram renderer expects [{name, population}] objects, not raw
number arrays. Fixes blank histogram rows in F20/F21 stories.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@paddymul paddymul changed the title feat: add StylingIssues stories and before/after screenshot CI feat: content-aware column widths, width estimation tests, and screenshot comparison infra Feb 28, 2026
@paddymul paddymul added this pull request to the merge queue Feb 28, 2026
Merged via the queue into main with commit 7b6a05c Feb 28, 2026
25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant