Skip to content

fix: viewer affordance for text/blob/json and overflow-detected cells (#216) - #221

Merged
EVWorth merged 1 commit into
mainfrom
fix/216-truncated-cell
Jul 19, 2026
Merged

fix: viewer affordance for text/blob/json and overflow-detected cells (#216)#221
EVWorth merged 1 commit into
mainfrom
fix/216-truncated-cell

Conversation

@EVWorth

@EVWorth EVWorth commented Jul 19, 2026

Copy link
Copy Markdown
Owner

What

Fixes three compounding bugs in TruncatedCell:

  1. Stale slice. Cell text was sliced to 47 chars + "..." in JS at render time, so dragging a column wider never revealed more of the value.
  2. No affordance for short structured values. TEXT/BLOB/JSON/MEDIUMTEXT/LONGTEXT columns got no viewer hint, even though the viewer (formatting, search, character count) is useful regardless of length.
  3. Heuristic ≠ reality. isLongValue = length > 50 was used to decide truncation visibility — didn't match actual visual overflow.

Why

The first is the literal #216 report. The second and third are downstream consequences of the same heuristic that the bug exposed.

Changes

File Change
src/components/grid/TruncatedCell.tsx Render full formatValue(value) via CSS truncate. Add ResizeObserver-driven isOverflowing state + rAF debounce. Render a Maximize2 icon button when showForTextType (text/blob/json type AND ≥ 20 chars) OR isOverflowing. Add optional dataType prop. Icon click opens viewer and stops propagation. Double-click kept as fallback.
src/components/grid/ResultsGrid.tsx Pass col.data_type as the new dataType prop.
src/components/grid/__tests__/TruncatedCell.test.tsx Rewrite as 19 cases: NULL handling, primitive types, full-text render, no-icon for short non-text, icon appearance for each text/blob/json variant, ResizeObserver-driven show/hide, icon click → viewer with stopPropagation, double-click fallback on/off. Adds a FakeResizeObserver and a synchronous requestAnimationFrame mock since jsdom doesn't ship either.

Reveal rules

Icon is shown when either:

  • data type matches /text|blob|json|mediumtext|longtext/i and the formatted value is ≥ 20 chars and the value is non-null, OR
  • ResizeObserver reports scrollWidth > clientWidth (actual visible overflow).

The icon disappears the moment the column widens enough to fit the value — no more stale heuristic.

Verification

Gate Result
npx vitest run src/components/grid/__tests__/TruncatedCell.test.tsx 19/19 pass
npx vitest run (full suite) 77 files / 1453 tests pass
npm run type-check 0 errors
npx eslint (touched files) 0 errors (1 pre-existing warning in ResultsGrid unrelated)
npx dprint check (whole repo) clean

Implementation notes

  • ResizeObserver uses a flag-based rAF debounce rather than handle tracking. With a synchronous test mock, handle-tracking broke dedup because the mock returned the same id each call.
  • Icon button stops propagation so single clicks on the icon don't trip the grid's row selection handler.
  • Double-click on the text element is still wired to the viewer when the icon is shown — preserves muscle memory for existing users.
  • truncate alone gives text-overflow: ellipsis overflow: hidden white-space: nowrap. The wrapping flex row uses min-w-0 so the inner truncate actually measures against the available column width.

…#216)

TruncatedCell had three problems compounding:

1. Sliced text at 47 chars + '...' in JS at render time, so dragging
   a column wider never revealed more of the value.
2. Offered no viewer affordance for short text/blob/json values, even
   though those types are inherently multi-line / structured and
   benefit from the formatted viewer regardless of length.
3. Used a character-length heuristic (>50) to decide whether the cell
   was 'truncated', which didn't reflect actual visible overflow.

Fix:

- Render the full value via CSS truncation (text-overflow: ellipsis).
  Cell reflows as the column resizes. Closes #216.

- Show a viewer affordance (Maximize2 icon button) on every cell where
  it is useful. Icon appears when EITHER:
    (a) dataType matches /text|blob|json|mediumtext|longtext/i and
        value length >= 20, OR
    (b) actual visual overflow is detected by ResizeObserver.
  Icon disappears the moment the column widens enough (ResizeObserver
  re-checks after each resize).

- Icon click opens the viewer and stops propagation so row selection
  is unaffected. Double-click on the cell is kept as a fallback
  affordance for users without an icon shown (e.g. non-truncated
  non-text values).

- ResizeObserver + rAF debounce so a flurry of resize events
  collapse to a single scrollWidth/clientWidth check per frame.

- TruncatedCell now takes an optional dataType prop. ResultsGrid
  passes col.data_type from the result set.

Tests:
- 19 cases covering NULL, primitive types, full-text render, no-icon
  for short non-text, icon for each text/blob/json variant, overflow
  via ResizeObserver, hide-on-widen, icon click triggers viewer,
  stopPropagation, double-click fallback on/off.
@EVWorth
EVWorth force-pushed the fix/216-truncated-cell branch from df54f5a to 51194c5 Compare July 19, 2026 18:42
@EVWorth EVWorth changed the title fix: widening a column does not reveal truncated value (#216) fix: viewer affordance for text/blob/json and overflow-detected cells (#216) Jul 19, 2026
@EVWorth
EVWorth merged commit 829f2dd into main Jul 19, 2026
11 checks passed
@EVWorth
EVWorth deleted the fix/216-truncated-cell branch July 19, 2026 19:09
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