Skip to content

feat: Per-column table skeleton placeholders via renderCell - #4879

Draft
gethinwebster wants to merge 4 commits into
mainfrom
dev-v3-gethinw-table-skeleton-render-cell
Draft

feat: Per-column table skeleton placeholders via renderCell#4879
gethinwebster wants to merge 4 commits into
mainfrom
dev-v3-gethinw-table-skeleton-render-cell

Conversation

@gethinwebster

Copy link
Copy Markdown
Member

Description

Adds an optional renderCell function to the Table skeleton config so teams can provide a per-column skeleton placeholder for cells whose settled content isn't a single line of text (multi-line cells, status indicators, actions, etc.). A uniform one-line bar mismatches those shapes and causes a layout jump when data lands; renderCell lets the placeholder match the final cell shape so the load-to-settle transition stays stable.

  • API: skeleton={{ totalRows, renderCell: (column) => ReactNode }} — available on both the fixed and auto configs via a shared base. SkeletonConfig is now generic (SkeletonConfig<T>, defaulting to any), so existing TableProps.SkeletonConfig references are unaffected.
  • The function receives the column definition and is called per cell; return undefined for a column to fall back to the default single-line skeleton (so a single central function can customize only the columns that need it).
  • Compose the returned content from the public Skeleton component. It renders inside the existing aria-hidden skeleton <tr>, so it is not announced to screen readers; JSDoc guidance: do not render focusable/interactive elements.
  • Backward compatible: renderCell is optional; omitting it preserves today's single-line skeleton exactly.

Draft for design review of the API shape.

Related links, issue #, if available: n/a

How has this been tested?

  • New unit tests in src/table/__tests__/skeleton.test.tsx: custom content rendered per column, undefined -> default-skeleton fallback, and custom content staying inside aria-hidden rows. Full skeleton suite: 22/22 pass.
  • Documenter snapshot regenerated for the new prop; snapshot suite 97/97 pass.
  • New dev page pages/table/skeleton-render-cell.page.tsx compares the default single-line skeleton against a column-shaped one (two-line description, status indicator, actions button).
  • tsc --noEmit clean for the changed files.
Review checklist

Correctness

  • Changes include appropriate documentation updates. — JSDoc on skeleton and renderCell; dev page added.
  • Changes are backward-compatible if not indicated. — Yes; renderCell optional, SkeletonConfig<T = any> keeps existing refs valid.
  • Changes do not include unsupported browser features. — No new browser features.
  • Changes were manually tested for accessibility. — Custom content renders inside the existing aria-hidden row (test asserts this); JSDoc warns against focusable elements.

Security

  • If the code handles URLs. — N/A.

Testing

  • Changes are covered with new/existing unit tests? — Yes (3 new).
  • Changes are covered with new/existing integration tests? — Existing integ skeleton tests unaffected; new behavior is unit-covered.

@gethinwebster gethinwebster changed the title feat(table): per-column skeleton placeholders via renderCell feat: Per-column table skeleton placeholders via renderCell Aug 7, 2026
@gethinwebster
gethinwebster requested a review from Copilot August 7, 2026 08:39

Copilot AI 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.

Pull request overview

Adds a new renderCell callback to the Table skeleton configuration to allow per-column skeleton placeholders (e.g., multi-line cells, status indicators, action areas), while keeping the existing single-line skeleton as the default/fallback.

Changes:

  • Extends TableProps.SkeletonConfig to SkeletonConfig<T = any> and adds optional renderCell(column) => ReactNode.
  • Wires renderCell through InternalTable into SkeletonRows, rendering custom per-column skeleton content with fallback to the default skeleton.
  • Adds unit tests covering custom rendering, fallback behavior, and aria-hidden containment; updates documenter snapshot and adds a dev page example.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/table/skeleton-rows.tsx Uses renderCell to render per-column skeleton content (with fallback).
src/table/internal.tsx Passes skeleton.renderCell through to SkeletonRows in both initial and progressive skeleton rendering paths.
src/table/interfaces.tsx Introduces generic SkeletonConfig<T> and documents/defines the new renderCell API.
src/table/tests/skeleton.test.tsx Adds unit tests for renderCell behavior and accessibility placement.
src/tests/snapshot-tests/snapshots/documenter.test.ts.snap Updates generated snapshot output for the new generic skeleton type and docs.
pages/table/skeleton-render-cell.page.tsx Adds a dev page demonstrating default vs per-column skeleton placeholders.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/table/skeleton-rows.tsx
Comment thread pages/table/skeleton-render-cell.page.tsx Outdated
Address Copilot review: renderCell returns ReactNode (incl. null); use an
explicit undefined check instead of ?? so a null return renders an empty
placeholder. Document the null vs undefined distinction and add a test.
@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.64%. Comparing base (08d9c53) to head (a0cb1c8).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4879   +/-   ##
=======================================
  Coverage   97.64%   97.64%           
=======================================
  Files         957      958    +1     
  Lines       31200    31243   +43     
  Branches    11504    11534   +30     
=======================================
+ Hits        30464    30506   +42     
- Misses        729      730    +1     
  Partials        7        7           

☔ 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.

Copilot AI 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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated 1 comment.

Suppressed comments (1)

pages/table/skeleton-render-cell.page.tsx:41

  • This status column cell renderer is complex enough that keeping it on one line hurts readability and is inconsistent with the multi-line formatting used for other non-trivial cells in this file (for example the description column). Reformatting also helps avoid print-width churn in future diffs.
  {
    id: 'status',
    header: 'Status',
    cell: item => <StatusIndicator type={item.status === 'error' ? 'error' : 'success'}>{item.status}</StatusIndicator>,
  },

Comment thread src/table/interfaces.tsx
}

export interface FixedSkeletonConfig {
interface BaseSkeletonConfig<T> {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving as-is intentionally. The declaration-emit concern doesn't apply here: BaseSkeletonConfig lives in the same module/namespace as the interfaces that extend it, so there's no TS4023 "private name" issue — confirmed by build/build (which runs declaration emit) passing green. It's also not against convention: this repo already has non-exported Base* helper interfaces inside *Props namespaces (e.g. BaseUtility inside TopNavigationProps). Keeping it non-exported is deliberate — it exists only to share the single optional renderCell prop across the fixed/auto configs, and exporting it would add a public type to the API surface for no consumer benefit.

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.

2 participants