Skip to content

feat(design-system): add infiniteScroll to DsTable to handle scroll pagination [AR-62554]#465

Merged
iromanchuk-dn merged 7 commits into
drivenets:mainfrom
iromanchuk-dn:AR-62554-ds-table-infinite-scrolling-encapsulate
May 13, 2026
Merged

feat(design-system): add infiniteScroll to DsTable to handle scroll pagination [AR-62554]#465
iromanchuk-dn merged 7 commits into
drivenets:mainfrom
iromanchuk-dn:AR-62554-ds-table-infinite-scrolling-encapsulate

Conversation

@iromanchuk-dn
Copy link
Copy Markdown
Collaborator

@iromanchuk-dn iromanchuk-dn commented May 11, 2026

Problem

Consumers of the virtualized DsTable had to hand-roll infinite scroll on every page:

  • Listen to onScroll, compute bottomOffset, compare to a threshold, gate on isFetching/hasMore.
  • Get scroll position from a useEffect on mount and after each fetch to handle the "initial data is shorter than the viewport, so no scrollbar ever appears" footgun. It is not possible today and a related bug was reported.
  • Re-implement this ~30-line snippet for every list — error-prone, easy to forget the auto-fill check, and leaks scroll-math into product code that should only care about fetching.

Solution

Encapsulate viewport/scroll detection inside DsTable. Consumers keep ownership of fetching, pagination state, and retry; the Table owns when to ask for more.

New prop

<DsTable
  virtualized
  data={rows}
  columns={columns}
  infiniteScroll={{
    hasMore,          // required
    isLoadingMore,    // default: false
    onLoadMore,       // required; sync or () => Promise<unknown>
    thresholdPx,      // default: 500
    autoFill,         // default: true
  }}
/>
  • Presence of infiniteScroll = feature on. No enabled flag.
  • hasMore: false is the runtime off-switch (end of data, error, pause).
  • autoFill: true keeps requesting pages until the viewport is actually scrollable — fixes the "short first page" footgun by default.
  • onLoadMore accepts (() => void) | (() => Promise<unknown>), so React Query's fetchNextPage can be passed directly without a wrapper.
  • Existing onScroll prop continues to work independently for analytics / scroll indicators.

Implementation

  • New useInfiniteScroll hook (use-infinite-scroll.ts) owns threshold detection, the auto-fill loop, and an in-flight latch.
  • Reentrancy guard: latch is set the moment onLoadMore fires; cleared when isLoadingMore transitions to true. Closes the 1–2 frame gap before the consumer's loading flag propagates without forcing Promise semantics on the API.
  • Wired into the virtualizer's existing onChange for scroll-driven triggers and a useEffect on rows.length for mount / data-append / auto-fill. The isNearBottom check is gated on actual scrollability so it doesn't false-fire when content is shorter than the viewport.

Migration & coverage

  • Existing VirtualizedSelectable and VirtualizedExpandable stories migrated off the manual fetchMoreOnBottomReached + getScrollPosition plumbing.
  • New InfiniteScroll (5-row initial page) stories anchor the API.
  • New ds-table-infinite-scroll.browser.test.tsx covers: threshold trigger, hasMore: false guard, isLoadingMore: true guard, auto-fill on mount (both true and false), and the latch regression.

Test plan

  • pnpm eslint packages/design-system/src/components/ds-table/
  • pnpm --filter @drivenets/design-system typecheck
  • pnpm --filter @drivenets/design-system test src/components/ds-table/__tests__/ --run
  • Verify all four virtualized stories in Storybook (existing two + two new) trigger paging on scroll and auto-fill on short initial pages.

…it handle scroll and auto-fill pagination [AR-62554]
@netlify
Copy link
Copy Markdown

netlify Bot commented May 11, 2026

Deploy Preview for drivenets-design-system ready!

Name Link
🔨 Latest commit 8bc7c4d
🔍 Latest deploy log https://app.netlify.com/projects/drivenets-design-system/deploys/6a045678262be700081c580c
😎 Deploy Preview https://deploy-preview-465--drivenets-design-system.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@iromanchuk-dn iromanchuk-dn changed the title feat(design-system): add infiniteScroll prop to DsTable, letting it handle scroll and auto-fill pagination [AR-62554] feat(design-system): add infiniteScroll to DsTableto handle scroll and auto-fill pagination [AR-62554] May 11, 2026
@iromanchuk-dn iromanchuk-dn changed the title feat(design-system): add infiniteScroll to DsTableto handle scroll and auto-fill pagination [AR-62554] feat(design-system): add infiniteScroll to DsTableto handle scroll pagination [AR-62554] May 11, 2026
@iromanchuk-dn iromanchuk-dn changed the title feat(design-system): add infiniteScroll to DsTableto handle scroll pagination [AR-62554] feat(design-system): add infiniteScroll to DsTable to handle scroll pagination [AR-62554] May 11, 2026
mmurawski-dn
mmurawski-dn previously approved these changes May 12, 2026
expect(onLoadMore).not.toHaveBeenCalled();
});

it('latch: rapid scroll events before isLoadingMore propagates fire onLoadMore at most once', async () => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Tests cover “rapid scroll before loading flag propagates”, but there’s no test for the valid API case where onLoadMore is sync (or async without isLoadingMore) and should still allow repeated pagination cycles.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I removed latch behavior, it's overcomplicates things.

mmurawski-dn
mmurawski-dn previously approved these changes May 13, 2026
@iromanchuk-dn iromanchuk-dn merged commit c7579a1 into drivenets:main May 13, 2026
24 checks passed
@iromanchuk-dn iromanchuk-dn deleted the AR-62554-ds-table-infinite-scrolling-encapsulate branch May 13, 2026 12:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants