feat: add automatic skeleton rows to Table - #4772
Conversation
5ba3e9e to
6978122
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #4772 +/- ##
==========================================
+ Coverage 97.61% 97.63% +0.01%
==========================================
Files 952 957 +5
Lines 30816 31097 +281
Branches 11318 11435 +117
==========================================
+ Hits 30081 30361 +280
- Misses 688 689 +1
Partials 47 47 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Adds an “auto” mode for Table skeleton rows so loading states can fill the available vertical viewport with placeholder rows, including a post-render overflow correction and coverage across unit + browser integration scenarios.
Changes:
- Introduces
useAutoSkeletonRowsand wires it into Table rendering soskeleton={{ totalRows: 'auto' }}computes skeleton row count from viewport constraints (optionally capped viamaxAutoRows). - Extends the public skeleton configuration type and updates feature-metrics reporting for auto mode.
- Adds unit + browser integration tests plus dev/demo pages to validate behavior with nested scroll containers, sticky header/footer, wrapLines, mixed data+skeleton rows, and AppLayout page chrome.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/table/use-auto-skeleton-rows.ts | New hook to measure available viewport space and compute/correct skeleton row count. |
| src/table/internal.tsx | Integrates auto skeleton row calculation into Table rendering and refs. |
| src/table/interfaces.tsx | Updates SkeletonConfig to support 'auto' + maxAutoRows and documents the new options. |
| src/table/index.tsx | Updates component metadata reporting to distinguish fixed vs automatic skeleton configuration. |
| src/table/tests/table-feature-metrics.test.tsx | Adds unit coverage for metrics emitted by automatic vs fixed skeleton config. |
| src/table/tests/skeleton.test.tsx | Adds unit coverage for auto sizing, overflow correction, caps, and ordering with data rows. |
| src/table/integ/skeleton.test.ts | Adds browser integration coverage for nested scroll viewports, features, mixed rows, and AppLayout chrome. |
| src/internal/utils/scrollable-containers.ts | Adds getScrollableParents utility used for viewport detection. |
| src/tests/snapshot-tests/snapshots/documenter.test.ts.snap | Updates generated docs snapshot for the new union type + docs text. |
| pages/table/skeleton-rows.page.tsx | Adds a dev-page toggle to switch between fixed and auto skeleton rows. |
| pages/table/auto-skeleton-rows.page.tsx | New dev page exercising auto skeleton rows across layout permutations. |
| pages/app-layout/auto-skeleton-table.page.tsx | New dev page validating auto skeleton rows inside AppLayout with external header/footer. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| * Returns the element's ancestors whose computed `overflow-y` is `auto` or `scroll`, | ||
| * ordered nearest-first. These are the ancestors capable of producing a vertical scrollbar. | ||
| */ | ||
| export function getScrollableParents(element: HTMLElement): HTMLElement[] { |
There was a problem hiding this comment.
Do we have a test for this?
There was a problem hiding this comment.
implicitly, but now added an explicit test too
| export interface SkeletonConfig { | ||
| totalRows: number; | ||
| } | ||
| export type SkeletonConfig = { totalRows: number; maxAutoRows?: never } | { totalRows: 'auto'; maxAutoRows?: number }; |
There was a problem hiding this comment.
can we do union types that are named in the namespace for these instead of inline one, it'll make it more readable I think? Just like we do for ColumnDisplayProperties = ColumnDisplay | GroupDisplay; Maybe something like AutoConfig and FixedConfig or AutoSkeletonConfig,
There was a problem hiding this comment.
good point, updated
|
|
||
| import { getScrollableParents } from '../internal/utils/scrollable-containers'; | ||
|
|
||
| const AUTO_SKELETON_VIEWPORT_BUFFER = 16; |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
There was a problem hiding this comment.
In my testing it needed a certain amount of buffer away from the bottom to prevent often over-shooting with measurements because things shift around the page a little during loading. So no specific reason for this number rather than another close number, but some buffer is needed
…on; test getScrollableParents
| * - `totalRows` ('auto') - The number of skeleton rows is calculated from the available viewport height. | ||
| * - `maxAutoRows` (number) - Limits the number of rows rendered when `totalRows` is set to `'auto'`. | ||
| * - `minAutoRows` (number) - Sets the minimum number of rows rendered when `totalRows` is set to `'auto'`. |
| const minRowsValue = Math.max(1, minRows ?? 1); | ||
| const [rows, setRows] = useState(minRowsValue); | ||
| const rowHeightRef = useRef<number>(); | ||
|
|
||
| const updateRows = useCallback(() => { | ||
| if (!enabled) { | ||
| return; | ||
| } | ||
|
|
||
| const tableRoot = tableRootRef.current; | ||
| const tableWrapper = tableWrapperRef.current; | ||
| const skeletonRows = tableBodyRef.current?.querySelectorAll<HTMLElement>(SKELETON_ROW_SELECTOR); | ||
| const firstSkeletonRowRect = skeletonRows?.[0]?.getBoundingClientRect(); | ||
| const lastSkeletonRowRect = skeletonRows?.[skeletonRows.length - 1]?.getBoundingClientRect(); | ||
| const rowHeight = lastSkeletonRowRect?.height ?? rowHeightRef.current; | ||
|
|
||
| if (!tableRoot || !tableWrapper || !firstSkeletonRowRect || !lastSkeletonRowRect || !rowHeight) { | ||
| return; | ||
| } | ||
|
|
||
| rowHeightRef.current = rowHeight; | ||
| const nextRows = calculateAutoSkeletonRows({ | ||
| maxRows, | ||
| minRows: minRowsValue, | ||
| rowHeight, |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 13 out of 13 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/table/use-auto-skeleton-rows.ts:57
getOverflowHeightpicks the first ancestor withoverflow-y: auto|scroll, even if it isn't actually overflowing. In layouts where an ancestor hasoverflow-y: autobut expands with content, this returns 0 and prevents the overflow-correction step from removing rows even when the document (or another ancestor) is overflowing.
function getOverflowHeight(element: HTMLElement) {
const scrollContainer = getScrollableParents(element)[0];
if (scrollContainer) {
return Math.max(0, scrollContainer.scrollHeight - scrollContainer.clientHeight);
}
Adds automatic skeleton-row sizing to Table via
skeleton={{ totalRows: 'auto' }}: during loading the table fills its available viewport with placeholder rows, and a post-render correction removes rows if the rendered page would overflow (never below one row).Changes
use-auto-skeleton-rows.ts, wired throughTableinternals and interfaces.wrapLineson/off with long headers, and mixed data + skeleton rows (data rows always precede skeleton rows).#/light/app-layout/auto-skeleton-table,...-nowrap) with an external#hheader and#ffooter, matching a real console layout.Testing
npm run build,npm run lint, and the table unit suites (17/17) pass.