🐛 fix: VirtualizedTable query lifecycle + errorState/headerContent/scroll container - #666
Merged
Merged
Conversation
…strands The provider seeded initialData on every query key, forcing isLoading to stay false and the skeleton to gate on isFetching. Consumers suppressed the resulting poll flicker with notifyOnChangeProps: ['data','error'], which silently dropped the isFetching:false re-render whenever a refetch resolved to structurally-equal data, stranding the table on the skeleton. - Seed initialData only for the first cache entry and only when rows were provided, so isLoading regains meaning (skeleton on initial load and key changes, silent background refetches) - Merge consumer notifyOnChangeProps with the props the provider reads so internal re-renders can never be suppressed - Move queryFn side effects into an effect keyed on dataUpdatedAt, which changes on every successful fetch regardless of structural sharing - Gate Skeleton/Pagination/WrapperBody on tableLoading instead of tableFetching, expose tableError and headerContent through the context - Stop mutating the consumer id array when building the query key - Move root tests into tests/ and add fetch integration coverage
…izedTable
- errorState?: ReactNode | ((error: Error) => ReactNode) renders in place
of rows when the query failed and there is nothing to show; background
refetch failures keep the current rows. With React Query's default
retry it appears after retries; queryOptions={{ retry: false }} shows
it immediately
- headerContent renders a full-width band inside the thead above the
column headers (scrolls with the table, visible in every state) and
takes over the top rounded corners, including the expand-column handoff
- classNameScrollContainer opts into a horizontal scroll region wrapping
the table body and pagination bar while the filter row stays fixed to
the container width
- Stable kvt-* class hooks on every section (filter, search, header,
band, body, rows, cells, states, pagination) so consumers can restyle
any part from outside
- Stories for HeaderContent, ErrorState and HorizontalScrollWithFilters
Filter dropdown menus were hand-rolled absolute divs that any overflow container clipped (overflow-x forces overflow-y), blocking the new table scroll region. All five dropdowns now share a Radix Popover based FilterDropdown (portaled, collision-aware, Escape to close); the manual mousedown outside-click handlers are gone because they would close the portaled menus from inside. The pagination page-size dropdown is rebuilt on Radix Popover reusing the Select variants: direction 'auto' flips up only when the viewport has no room below (dropdownPaginationDirection fixes it to 'up'/'down'), and it portals automatically when classNameScrollContainer is set. The 150px spacer that reserved room below the bar is no longer needed.
Removes memoization whose result feeds inline arrows or plain DOM elements, plus trivial boolean/number computations. Callbacks exposed through the table context, column-identity memos and the search debounce stay memoized because consumers depend on their identity.
npm 11 prunes transitive deps of wasm platform packages when resolving next to an existing node_modules, which left the lock missing @emnapi entries npm ci requires. Regenerated from origin/main's lock in a clean directory; validated with npm ci --dry-run on npm 10 and 11.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Root-cause fix for tables getting stranded on the loading skeleton (reported from compute instances list, civo/dashboard/compute-micro-frontend!187 / issue #75), plus the follow-up features that remove the need for consumer-side workarounds.
🐛 Query lifecycle fix
initialDataseeded[]on every query key, soisLoadingwas always false and the skeleton gated onisFetching(flashing on every background poll). Consumers suppressed the flicker withnotifyOnChangeProps: ['data','error'], which dropped theisFetching:falsere-render whenever a refetch resolved to structurally-equal data → skeleton stranded forever.notifyOnChangePropsis merged with the props the provider reads so the table can never strand again. Side effects moved out ofqueryFninto adataUpdatedAt-keyed effect. Query-key building no longer mutates the consumeridarray.✨ New API
errorState?: ReactNode | ((error: Error) => ReactNode)— shown when the query failed and there are no rows; failures during background polls keep the current rows.headerContent+classNameHeaderContent— full-width band inside the<thead>above the column headers (Figma2926-11781); top rounded corners move to the band, including the expand-column handoff.classNameScrollContainer— opt-in horizontal scroll region wrapping table + pagination while the filter row stays fixed.dropdownPaginationDirection?: 'auto' | 'up' | 'down'— page-size dropdown flips up only when the viewport has no room below; can be fixed.kvt-*class hooks on every table section for external styling.♻️ Internals
FilterDropdown(portal, collision handling, Escape-to-close) so overflow containers never clip them. New dependency:@radix-ui/react-popover.useCallback/useMemoremoved across the component.vite-plugin-dtsentryRootpinned sodist/keeps thelib/layoutcopy.shexpects (broke after the ⬆️ chore: update dependencies to latest compatible versions #665 dependency bump).Consumer migration (next alpha release)
notifyOnChangeProps: ['data','error'],placeholderData: keepPreviousDataandrefetchOnMount: trueworkarounds fromqueryOptions.queryOptions={{ placeholderData: keepPreviousData }}.overflow-x-auto contain-inline-sizefromclassNameWrapperTabletoclassNameScrollContainerto include pagination in the scroll region.Test plan
npm run check:types,npm run lint,npm run buildclean; verified manually via Storybook and against compute-micro-frontend.