Description
Design and ship the page-transition state for paginated lists: when the user moves to another page of the same list (same filters and sorting, different cursor), the current rows should stay on screen in a dimmed, non-interactive state until the next page arrives, then be replaced in one commit. Today every page change renders a skeleton pass instead.
This is the behaviour change that was split out of #3697. The rest of that refactor (URL as the single source of truth, one request per action, cached pages shown without a skeleton) lands without it; skeletons remain the treatment for page changes until the design here is agreed.
Details
- The plumbing is already in place but inert: the paginated query hook exposes an
isTransitioning flag that is always false, and the shared list wrapper (DataList) accepts an isTransitioning prop that dims its body with the disabled-control opacity, disables pointer events and sets aria-busy. Every migrated list already passes the flag through.
- Flipping the behaviour means the hook's
placeholderData keeps the previous page's data when the new query key differs from the previous one only by page / cursor, and falls back to the caller's stub otherwise (first load, filter or sort change). isInitialLoading stays true only in the stub case; pagination.isLoading stays true in both so the pagination buttons disable.
- Row keys already drop their index suffix for real data, so the previous rows keep their identity during the transition and the new rows replace them in a single commit.
Expected Behavior
- Design specifies the dimmed treatment: opacity value or token, whether a fade transition is used and its duration.
- Product confirms that skeletons stay for first load and filter / sort changes only.
- During a page transition the pagination buttons are disabled while the sticky table header and sorting controls remain usable.
- The socket-driven "new items" notice on page 1 keeps prepending as it does today.
- Unit spec covers: page change keeps previous rows with
isTransitioning true; filter change and first load render the stub with isInitialLoading true; "Prev" onto a cached page sets neither flag.
- Playwright screenshot baseline for the transition state of
DataList.
Description
Design and ship the page-transition state for paginated lists: when the user moves to another page of the same list (same filters and sorting, different cursor), the current rows should stay on screen in a dimmed, non-interactive state until the next page arrives, then be replaced in one commit. Today every page change renders a skeleton pass instead.
This is the behaviour change that was split out of #3697. The rest of that refactor (URL as the single source of truth, one request per action, cached pages shown without a skeleton) lands without it; skeletons remain the treatment for page changes until the design here is agreed.
Details
isTransitioningflag that is alwaysfalse, and the shared list wrapper (DataList) accepts anisTransitioningprop that dims its body with the disabled-control opacity, disables pointer events and setsaria-busy. Every migrated list already passes the flag through.placeholderDatakeeps the previous page's data when the new query key differs from the previous one only by page / cursor, and falls back to the caller's stub otherwise (first load, filter or sort change).isInitialLoadingstays true only in the stub case;pagination.isLoadingstays true in both so the pagination buttons disable.Expected Behavior
isTransitioningtrue; filter change and first load render the stub withisInitialLoadingtrue; "Prev" onto a cached page sets neither flag.DataList.