Skip to content

Fixes 35468: Standardize UI pagination: 15/25/50 for lists, 12/24/48 for card grids - #35288

Open
sweta1308 wants to merge 33 commits into
mainfrom
ui-standardize-pagination-page-sizes
Open

sweta1308 wants to merge 33 commits into
mainfrom
ui-standardize-pagination-page-sizes

Conversation

@sweta1308

@sweta1308 sweta1308 commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Describe your changes:

Fixes #35468

Companion Collate PR: open-metadata/openmetadata-collate#7487 (aligns Collate's Pipeline Observability and AI Automations pagination with this scale).

Paged views had drifted to their own sizes (10, 9, 20, 5, and grids on 15/25/50 or lists on 12/24/48). This PR makes every paged view in the UI use one of two scales:

  • Lists / tables: default 15, picker 15 / 25 / 50
  • Card grids: default 12, picker 12 / 24 / 48 (rows of 2, 3 or 4 cards fill evenly)

Pages with a table/card toggle take the scale of the current view and go back to page 1 when the view changes. The size picked is remembered per scale (globalPageSize for lists, new globalGridPageSize for grids), so switching views comes back to the last size picked in each. Every paged view starts from the saved size (no page sets its own fixed starting size, including views without a picker such as Metrics, KPIs and Domains); a size in the URL still wins. Settings → Services and App Live Indexing show their pager whenever there is data, even a single page, and hide it when there is none. The rule is recorded in ADR:2026-10-13-ui-page-sizes-lists-15-25-50-grids-12-24-48.

Page Before After
Connections — list view 12/24/48 15/25/50
Connections — grid view 12/24/48 12/24/48 (unchanged)
App Live Indexing (Search Indexing app) 10/15/25/50 15/25/50
Reindex failures drawer 20 15
Glossary update conflict table 5 15, with 15/25/50 picker
Context Center memories 10 15
Service version history server default (10), display hard-coded 10 user's page size (default 15), sent to the API
Personas 12, picker 15/25/50 12/24/48
Settings → Applications 15/25/50 12/24/48
Applications marketplace (Add Apps) 15/25/50 12/24/48
Profile → Applications / Marketplace grids (from main) 15/25/50 12/24/48 (useCursorPagedList takes an initial page size)
Settings → Services (card/table toggle) 15/25/50 in both views table 15/25/50, cards 12/24/48
Settings → Governance → Workflow Definitions 15/25/50 (default 25) 12/24/48
Profile → Personas 15/25/50 12/24/48
Profile → Learning Resources (card/table toggle) 15/25/50 in both views table 15/25/50, cards 12/24/48
Domains / Sub-domains / Data Products 9 in both views table 15, cards 12
Metrics 20 in both views table 15, cards 12
Learning Resources 15/25/50 in both views table 15/25/50, cards 12/24/48
Core PaginationCardWithControls, TableV2 client paging, atoms listing hooks (defaults) 10 15

Also fixes pre-existing type errors in files this PR touches:

  • AppLiveIndexing: removed props TableV2 does not accept (currentPage, pageSize, paginationVisible); "hide pager on a single page" now uses hideOnSinglePage, so it actually takes effect.
  • useDataFetching: default transform typed to the hook's entity type.
  • Domains.spec.ts: extra fillDomainForm argument and possibly-undefined FQNs passed to encodeURIComponent.

Out of scope (not paged views): typeahead/suggestion searches, tree-select batches, count-only queries, bulk fetches, "show more" previews.

Type of change:

  • Improvement

High-level design:

  • Constants (src/constants/constants.ts): LIST_PAGE_SIZE_OPTIONS (15/25/50), GRID_PAGE_SIZE_OPTIONS (12/24/48), GRID_PAGE_SIZE_BASE. Removed TABLE_CARD_PAGE_SIZE (9) and an unused DEFAULT_PAGE_SIZE (10). The Connections page's own option constants now point at these.
  • Helpers (utils/PaginationUtils.ts): getViewPageSizeOptions, getViewPageSize, resolveViewPageSize pick the scale from a PageViewMode. A size picked in one view falls back to the other scale's first step (the scales share no value).
  • Grid pages pass GRID_PAGE_SIZE_OPTIONS to usePaging, which already reconciles a size from the shared pageSize URL param / globalPageSize preference to the page's own options and only persists list-scale sizes globally. No change to usePaging was needed.
  • Table/card pages compute the view before the listing hook and pass getViewPageSize(view) as its page size; the view toggle also resets to page 1.
  • Service version page merges limit: pageSize into the cursor it sends, so the request and the displayed page size agree.
  • Alternatives considered: one scale for everything (grids then leave half-empty rows); per-page sizes (what drifted in the first place).
  • Compatibility: UI-only; no API, schema or migration changes. A pageSize/size URL param from the old scale is reconciled to the page's scale on load.

Tests:

Use cases covered

  • Each listed page opens at its scale's default and its picker offers only that scale.
  • Switching Connections, Domains, Sub-domains, Data Products, Metrics or Learning Resources between table and card view changes the scale and returns to page 1.
  • A size from the other scale (shared URL param / preference) falls back to the page's first step.
  • Service version history requests and displays the same page size.

Unit tests

  • Added tests for the new helpers: src/utils/PaginationUtils.test.ts — 11/11 pass; coverage on PaginationUtils.ts: 100% lines/branches/functions.
  • Updated: ConnectionsListView.test.tsx, useConnectionsData.test.ts (now import the shared constants; new test: switching grid ↔ list returns to page 1 — fails without the fix), MetricListPage.test.tsx (expects 15 instead of 20).
  • yarn jest --findRelatedTests <changed src files>: 1,329 suites, 16,494 passed, 2 failed — KnowledgePageSummary.test.tsx (5 s timeout under load) and KnowledgeGraphDetails.test.tsx; both pass when run alone (KnowledgePageSummary 3/3 reruns).
  • usePaging.integration.test.tsx: a page that declares no sizes ignores a grid pageSize=48 in the URL and an off-scale saved preference; the hook never returns the old scale's size in the render where the options change (all fail without the fix).
  • Services.test.tsx: card view uses 12/24/48 and returns to page 1 (fails without the fix). ListView.test.tsx and AlertRecentEventsTab.desync.real-hook.test.tsx updated (the latter stubbed an off-scale preference of 5).
  • usePaging.integration.test.tsx: grid sizes save to the grid preference and each scale is remembered. WorkflowsPage.test.tsx: workflow cards page at 12 with 12/24/48 (fails without the change). PersonasLanding.test.tsx updated to grid sizes.
  • Core components: pagination.test.tsx (vitest) passes.
  • tsc --noEmit: no errors in changed files. ESLint: no errors. yarn ui-checkstyle:changed: pass.

Backend integration tests

  • Not applicable (no backend API changes).

NOT NEEDED — UI-only change.

Ingestion integration tests

  • Not applicable (no ingestion changes).

Playwright (UI) tests

  • Files updated (waits now use the new sizes):
    • Pages/Domains.spec.ts, Pages/SubDomainPagination.spec.ts — domain listing size=9 → 15
    • Pages/AppStopRunModal.spec.ts, Pages/DataInsightSettings.spec.ts, Pages/DataInsightReportApplication.spec.ts — apps?limit=15 → 12
    • Pages/SearchIndexApplication.spec.ts — apps/marketplace?limit=15 → 12

Ran locally against the Vite dev server (PLAYWRIGHT_TEST_BASE_URL=http://localhost:3000) and the OSS backend (PLAYWRIGHT_IS_OSS=true), --project=chromium --workers=2, the six specs above:

Manual testing performed

  1. Local OSS stack (docker/development/docker-compose.yml) on :8585, UI via yarn start on :3000, logged in as admin.
  2. Pages to spot-check: Domains, Metrics, Settings → Applications (+ Add Apps), Connections — confirm default size and picker steps per the table above, and that toggling table/card changes the scale and returns to page 1.

UI screen recording:

Screen-Recording.57.mp4

Checklist:

  • I have read the CONTRIBUTING document.
  • My PR title is Fixes <issue-number>: <short explanation>
  • My PR is linked to a GitHub issue via Fixes #<issue-number> above.
  • I have commented on my code, particularly in hard-to-understand areas.
  • For JSON Schema changes: not applicable (no schema changes).
  • For a UI feature or feature task: not applicable (standardization, explained above).
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.
  • Decision check: adds docs/decisions/2026-10-13-ui-page-sizes-lists-15-25-50-grids-12-24-48.md.
  • I have added tests around the new logic.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 4/5

Preserve valid pages restored through browser history before merging.

Findings

  1. P1 Back opens the wrong page ▶

Summary

Standardizes paged lists to 15/25/50 and card grids to 12/24/48.

  • Paged views use separate size scales for lists and card grids.

Reviews (15) · Last reviewed commit: "Reset stale pages on a saved-size change..." · Reviewed by Greptile

Paged lists and tables default to 15 with a 15/25/50 picker; paged card
grids default to 12 with 12/24/48. Pages with a table/card toggle take
the scale of the current view and return to page 1 when it changes.

- Add LIST/GRID page-size option constants and view helpers in
  PaginationUtils.
- Core PaginationCardWithControls, TableV2 client paging and the atoms
  listing hooks default to 15 instead of 10.
- Lists: Connections list view, App Live Indexing, Reindex failures,
  glossary update conflict table, Context Center memories, service
  version history (now sends its page size to the API).
- Grids: Personas, installed Applications, Applications marketplace.
- Table/card: Domains, Sub-domains, Data Products, Metrics, Learning
  Resources.
- Fix pre-existing type errors in the touched files (AppLiveIndexing
  unsupported TableV2 props, useDataFetching default transform,
  Domains.spec.ts).
- Update Playwright waits to the new sizes and record the rule in
  ADR:2026-10-13-ui-page-sizes-lists-15-25-50-grids-12-24-48.
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR checks passed

The linked issue has a description and all required Shipping project fields set. Thanks!

@github-actions github-actions Bot added safe to test Add this label to run secure Github workflows on PRs UI UI specific issues labels Oct 13, 2026
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 78%
78.5% (129318/164735) 65.26% (82281/126066) 66.58% (27459/41239)

sweta1308 and others added 5 commits October 13, 2026 11:00
…tyle

- Connections grid (12/24/48) and list (15/25/50) now page at different
  sizes, so switching layout resets to page 1 instead of keeping a page
  number that can fall past the last row. Add a regression test.
- ServiceVersionPage: add pageSize to the pagingHandler and tabs
  dependency lists so a size change reaches the table.
- Apply organize-imports ordering flagged by UI checkstyle.
Resolve conflicts in AppLiveIndexing and ReindexFailures by keeping
main's rework (SlideoutMenu drawer with entity-type filter, appData
prop, refetch on page-size change) and applying the 15/25/50 sizes.

Move the new profile Applications and Marketplace card grids from
15/25/50 to 12/24/48: useCursorPagedList takes an initial page size.
Add the now-required pageSize prop to the ServiceVersionMainTabContent
test.
- MetricListPage: card and table views page at different sizes, and
  useMetricHierarchy clears expansion when the size changes. Include the
  page size in the default-expansion scope so groups re-expand after a
  view switch (the desktop visual-regression test found "Gross Margin"
  hidden inside a collapsed group).
- ContextCenter.constants: re-export PAGE_SIZE_BASE with export...from
  (S7763).
- LearningResourcesPage: name the setter after its state value (S6754).
- TableV2: `customPaginationProps?: never` instead of the redundant
  `?: undefined` (S4782).
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 922 → 923
- 1 added, 0 removed, 1 changed spec-list

New source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/DataQuality/AddDataQualityTest/components/TestCaseFormDrawer.tsx

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/src/components/Explore/EntitySummaryPanel/EntitySummaryPanel.component.tsx

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

…Paging

- Services: ListView's card/table view is now owned by Services, which
  pages cards at 12/24/48 and the table at 15/25/50 and returns to page
  1 (clearing the cursor) when the view changes.
- usePaging: a caller that declares no options is on the list scale, so
  a grid size left in the shared pageSize param or an off-scale size an
  older build stored in the preference never reaches a 15/25/50 picker.
- usePaging: hand out the resolved size in the render where the options
  change. The stored size caught up a render late, so switching to cards
  fetched 25 under a picker reading 12.
- Record the usePaging default in the page-size ADR.
Restore the behavior before this PR: the pager always shows. The
hideOnSinglePage option added here replaced an ignored prop and hid it.
- Save the size picked on each scale: lists in globalPageSize, card
  grids in the new globalGridPageSize (useSavedPageSize). usePaging,
  useCursorPagedList and the pages with their own picker read and save
  through it, so switching a page between table and cards comes back to
  the size last picked in each view.
- Page on the grid scale: Workflow Definitions, profile Personas and
  profile Learning Resources (card view), which still offered 15/25/50.
- Settings Services: always show the pager, and on a view switch drop
  the page, cursor and page size the other view left in the URL.
- Remove resolveViewPageSize, now unused; update the page-size ADR.
Resolve AlertRecentEventsTab.desync.real-hook.test.tsx: keep main's
pagination-sync fixture (mockSetPreference, URL-restored page fetches its
own offset) at a list-scale preference of 25, so 100 events are 4 pages
and page 3 starts at offset 50.

Also hoist the empty useTableFilters argument in Services to a module
constant so its parsed filters stay memoized.
@sweta1308 sweta1308 changed the title Standardize UI pagination: 15/25/50 for lists, 12/24/48 for card grids Fixes 35468: Standardize UI pagination: 15/25/50 for lists, 12/24/48 for card grids Oct 14, 2026
Settings Services and App Live Indexing show their pager whenever there
is data, even a single page, and hide it when there is none, so an empty
list shows only its empty state.
sweta1308 and others added 4 commits October 15, 2026 14:26
No page sets a fixed starting size any more; each starts from the size
last picked on its scale (useSavedPageSize), and a size in the URL or
settings hash still wins.

- Local-state pickers now read and save it: Audit Logs, profile Audit
  Logs, Roles, Policies, Notification Alerts and Recent Events, Bots,
  Governance glossaries, Search preview, Glossary Term Relations,
  Column Bulk Operations, Incident Groups (useIncidentPaging).
- usePaging callers drop their fixed defaults: schema, column profiler,
  data model, glossary terms and lineage tables (50), table queries,
  data product ports, test definitions, Context Center archive, AI
  alert events (15), data contract schema (25).
- Views with no picker follow it too: Domains, Data Products,
  Sub-domains and Metrics (per view scale), KPIs, Reindex failures,
  Context Center memories.
- Remove getViewPageSize, INCIDENT_GROUPS_PAGE_SIZE, ARCHIVE_PAGE_SIZE
  and MEMORIES_PER_PAGE, now unused; record the rule in the ADR.
- Settings Services and App Live Indexing hide the pager only on an
  empty first page. A later page that comes back empty (a drained
  retry queue, a stale link) keeps it so the user can page back.
- Sonar: name state setters as value/setter pairs (usePaging,
  useIncidentPaging); NonNullable for TableV2's optional pagination;
  core Select value/onChange instead of the deprecated
  selectedKey/onSelectionChange (SchemaTable); drop the redundant
  click handler and presentation role around TableQueries' date picker,
  which already opens itself; mark fire-and-forget promises with void
  (Context Center memories).
Comment thread openmetadata-ui/src/main/resources/ui/src/pages/DataInsightPage/KPIList.tsx Outdated
- KPIs: show the pager from the server cursors (the fetched page never
  exceeds the page size, so the old row-count check hid it and the
  remaining KPIs were unreachable), and refetch from page 1 when the
  saved size changes.
- Domains, Data Products, Sub-domains: useListingData takes the view's
  page-size options and ignores a URL size the view does not offer, so
  a table's size=25 no longer fetches 25 cards.
- useSavedPageSize.resolvePageSize: a URL or hash size wins only if the
  picker offers it. Used by Roles, Policies, Notification Alerts and
  Recent Events, profile Audit Logs, Bots and Personas.
- Connections: queryClient.query(...).catch(noop) instead of the
  deprecated prefetchQuery.
- useIncidentPaging drops its test-only defaultPageSize; memories marks
  its remaining fire-and-forget calls with void.
- Tests: useSavedPageSize, KPI pager, off-scale URL size in
  useListingData, Audit Logs opening at the saved size.
sweta1308 and others added 7 commits October 16, 2026 15:11
Grid and list views page on different scales, so a size carried in the
URL applies only to the view it was opened in. A view switch now drops
the page and page size from the URL and the new view (and any later
switch back) starts at the saved size for its scale.

- useUrlState.resetPaging clears page and size in one update; Domains,
  Data Products and Sub-domains call it on a view switch.
- Connections drops currentPage, pageSize and the cursor params when the
  grid/list view changes.
- Record the rule in the page-size ADR; tests for resetPaging and the
  Connections switch.
The refetch-on-page-size effect added a react-hooks/exhaustive-deps
warning; keying fetchKpiList on the page size and running the effect on
it removes that warning and the older one about fetchKpiList changing on
every render.
A settings-hash link can carry a size the picker no longer offers
(pageSize=10 from before the scales). Replacing only the size kept the
link's page and cursor, which were counted at the old size, so an older
Recent Events link asked for an offset past the data and showed an empty
page with no pager.

- useHashPagingParams(pageSizeOptions): when the hash size is off scale,
  report page 1 with no cursor and rewrite the hash to match.
- Roles, Policies, Notification Alerts, profile Audit Logs (list scale)
  and Personas (grid scale) pass their options; Bots and Recent Events
  read their starting page, size and cursor through it.
- Tests on the real hook: an on-scale link keeps its page and size, an
  off-scale one opens on page 1 with the hash cleared.
Restore main's code where the PR only fixed pre-existing type errors or
Sonar findings on lines pagination does not touch.
A saved size other than the page's default kept the pager visible so the
size could be changed back, even when the list had no records.
…tion-page-sizes

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/src/components/observability/IncidentManager/IncidentGroups/IncidentGroupDrawer.tsx
#	openmetadata-ui/src/main/resources/ui/src/components/observability/IncidentManager/IncidentGroups/IncidentGroups.constants.ts
#	openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterMemoriesPage/ContextCenterMemoriesPage.tsx
@github-actions

Copy link
Copy Markdown
Contributor

🔄 Playwright impact map auto-refreshed

This PR touched specs or UI source that changed the source→spec routing map. I regenerated .github/playwright/impact-map.generated.json and pushed the diff to this branch.

- source entries: 936 → 937
- 1 added, 0 removed, 11 changed spec-list

New source→spec entries:
  openmetadata-ui/src/main/resources/ui/src/components/Database/Profiler/TableProfiler/QualityTab/QualityTab.component.tsx

Entries whose spec list changed:
  openmetadata-ui/src/main/resources/ui/playwright/e2e/fixtures/pages.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/TableClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/support/entity/service/DatabaseServiceClass.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/admin.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/apiResponse.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/common.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/entity.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/scopedLocators.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/testCases.ts
  openmetadata-ui/src/main/resources/ui/playwright/utils/waitHelpers.ts
  … and 1 more

What is this file? It is the auto-generated half of Playwright's PR planner. It routes "if source X changes, run specs Y" by walking spec imports and cross-referencing getByTestId strings. Hand-authored routing in impact-map.json always wins on conflict.

What if I want to regenerate locally instead? Run this before pushing your next change to skip the bot commit:

python3 .github/scripts/generate_playwright_impact_map.py
git add .github/playwright/impact-map.generated.json
git commit --amend --no-edit  # or a separate commit

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ UI Checkstyle passed — lint findings in changed files

🔍 ESLint findings in this PR's files — 0 error(s), 98 warning(s)

Errors block the build. Warnings do not yet — they are rules whose backlog is still
being worked down, listed so this PR does not add to it. See docs/ui-code-quality-gate.md.

0 error(s), 98 warning(s) across 28 changed file(s).

Count Rule
89 react-hooks/exhaustive-deps
3 openmetadata-imports/review-sequential-api-calls
2 openmetadata-ui-patterns/no-non-adaptive-palette
2 openmetadata-imports/no-api-calls-in-iteration
1 openmetadata-imports/no-lower-layer-page-imports
1 openmetadata-imports/no-cross-page-imports
All findings
Location Rule Message
🟡 src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx:167:5 react-hooks/exhaustive-deps React Hook useCallback has an unnecessary dependency: 'paging'. Either exclude it or remove the dependency array.
🟡 src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx:223:9 react-hooks/exhaustive-deps The 'updateColumnDetails' function makes the dependencies of useCallback Hook (at line 252) change on every render. To fix this, wrap the definition of 'updateC
🟡 src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx:223:9 react-hooks/exhaustive-deps The 'updateColumnDetails' function makes the dependencies of useCallback Hook (at line 269) change on every render. To fix this, wrap the definition of 'updateC
🟡 src/components/Dashboard/DataModel/DataModels/ModelTab/ModelTab.component.tsx:453:5 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:155:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 'entityType'. Either include it or remove the dependency array.
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:196:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'handlePagingChange'. Either include it or remove the dependency array.
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:238:5 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'oldRemovedColumns' and 'selectedSchema'. Either include them or remove the dependency array. If 'setAllColumns
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:266:6 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'oldRemovedColumns' and 'selectedSchema'. Either include them or remove the dependency array. If 'setAllColumns
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:268:9 react-hooks/exhaustive-deps The 'fetchApiEndPointColumns' function makes the dependencies of useCallback Hook (at line 324) change on every render. Move it inside the useCallback callback.
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:490:5 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/DataContract/ContractSchemaFormTab/ContractScehmaFormTab.tsx:530:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchColumnsBasedOnEntity'. Either include it or remove the dependency array.
🟡 src/components/DataProduct/DataProductListPage.tsx:421:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'dataProductColumns', 'dataProductListing.handleSelect', and 'dataProductListing.handleSelectAll'. Either include t
🟡 src/components/DataProducts/InputOutputPortsTab/PortsListView/PortsListView.component.tsx:176:8 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchPorts'. Either include it or remove the dependency array.
🟡 src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx:256:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'handlePagingChange'. Either include it or remove the dependency array.
🟡 src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx:263:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'handlePageChange'. Either include it or remove the dependency array.
🟡 src/components/Database/Profiler/TableProfiler/ColumnProfileTable/ColumnProfileTable.tsx:270:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchTableColumnWithProfiler'. Either include it or remove the dependency array.
🟡 src/components/Database/SchemaTable/SchemaTable.component.tsx:373:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchTestCaseSummary'. Either include it or remove the dependency array.
🟡 src/components/Database/SchemaTable/SchemaTable.component.tsx:475:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'tableFqn'. Either include it or remove the dependency array.
🟡 src/components/Database/SchemaTable/SchemaTable.component.tsx:753:5 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'handleEditDisplayNameClick' and 't'. Either include them or remove the dependency array.
🟡 src/components/Database/SchemaTable/SchemaTable.component.tsx:879:5 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'handleColumnClick' and 't'. Either include them or remove the dependency array.
🟡 src/components/Database/SchemaTable/SchemaTable.component.tsx:971:5 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Database/TableQueries/TableQueries.tsx:177:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'fetchResourcePermission'. Either include it or remove the dependency array.
🟡 src/components/Database/TableQueries/TableQueries.tsx:202:24 openmetadata-imports/review-sequential-api-calls Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
🟡 src/components/Database/TableQueries/TableQueries.tsx:472:6 react-hooks/exhaustive-deps React Hook useEffect has missing dependencies: 'fetchFilteredQueries', 'isTableDeleted', and 'searchParams.queryFrom'. Either include them or remove the depende
🟡 src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx:137:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'subdomainListing'. Either include it or remove the dependency array.
🟡 src/components/Domain/SubDomainsTable/SubDomainsTable.component.tsx:226:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'subDomainColumns', 'subdomainListing.handleClearAll', 'subdomainListing.handleSelect', 'subdomainListing.handleSel
🟡 src/components/DomainListing/DomainListPage.tsx:309:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'domainColumns', 'domainListing.handleSelect', and 'domainListing.handleSelectAll'. Either include them or remove t
🟡 src/components/DomainListing/hooks/useDomainListingData.tsx:20:1 openmetadata-imports/no-lower-layer-page-imports Pages are route-level composition modules. Move the shared implementation/type to a lower layer instead of importing a page from here.
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:655:34 react-hooks/exhaustive-deps React Hook useCallback received a function whose dependencies are unknown. Pass an inline function instead.
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:659:9 react-hooks/exhaustive-deps The 'fetchChildTerms' function makes the dependencies of useCallback Hook (at line 1214) change on every render. To fix this, wrap the definition of 'fetchChild
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:743:22 openmetadata-imports/review-sequential-api-calls Review these sequential API requests. If they are independent, start them together with Promise.all/Promise.allSettled; keep sequencing only when data-dependent
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:758:9 react-hooks/exhaustive-deps The 'fetchAllTerms' function makes the dependencies of useCallback Hook (at line 1500) change on every render. To fix this, wrap the definition of 'fetchAllTerm
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:831:9 react-hooks/exhaustive-deps The 'fetchExpadedTree' function makes the dependencies of useCallback Hook (at line 1500) change on every render. To fix this, wrap the definition of 'fetchExpa
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:981:6 react-hooks/exhaustive-deps React Hook useEffect has missing dependencies: 'fetchAllTerms', 'handlePagingChange', and 'setGlossaryChildTerms'. Either include them or remove the dependency
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:993:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'setGlossaryChildTerms'. Either include it or remove the dependency array.
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1160:5 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'applyGlossaryTermApprovalOutcome', 'notifyPendingApprovalTasks', and 't'. Either include them or remove the de
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1419:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'currentUser', 'onAddGlossaryTerm', 'onEditGlossaryTerm', and 't'. Either include them or remove the dependency arr
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1610:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'handleEditGlossary', 'handleSearchChange', 'handleStatusSelectionDropdownCancel', 'handleStatusSelectionDropdownSa
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1686:5 react-hooks/exhaustive-deps React Hook useMemo has unnecessary dependencies: 'glossaryChildTerms', 'glossaryTerms', and 'setGlossaryChildTerms'. Either exclude them or remove the dependenc
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1759:6 react-hooks/exhaustive-deps React Hook useMemo has an unnecessary dependency: 'movedGlossaryTerm'. Either exclude it or remove the dependency array.
🟡 src/components/Glossary/GlossaryTermTab/GlossaryTermTab.component.tsx:1972:6 react-hooks/exhaustive-deps React Hook useEffect has missing dependencies: 'activeGlossary', 'fetchAllTerms', 'handlePagingChange', and 'previousGlossaryFQN'. Either include them or remove
🟡 src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx:242:6 react-hooks/exhaustive-deps React Hook useMemo has missing dependencies: 'glossaryTerm', 'handleUpdateConfirmation', 'onCancel', 'progress', 't', and 'tagError'. Either include them or rem
🟡 src/components/Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal.tsx:255:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 't'. Either include it or remove the dependency array.
🟡 src/components/Settings/Services/Services.tsx:243:5 react-hooks/exhaustive-deps React Hook useCallback has missing dependencies: 'handlePagingChange' and 't'. Either include them or remove the dependency array.
🟡 src/components/Settings/Services/Services.tsx:266:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'handlePageChange'. Either include it or remove the dependency array.
🟡 src/components/Settings/Services/Services.tsx:475:5 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 'getServiceLogoElement'. Either include it or remove the dependency array.
🟡 src/components/Settings/Services/Services.tsx:552:5 react-hooks/exhaustive-deps React Hook useCallback has a missing dependency: 'handlePageChange'. Either include it or remove the dependency array.
🟡 src/components/Settings/Services/Services.tsx:571:6 react-hooks/exhaustive-deps React Hook useEffect has a missing dependency: 'getServiceDetails'. Either include it or remove the dependency array.
🟡 src/components/TestLibrary/TestDefinitionList/useTestDefinitionListPage.ts:141:5 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 'handlePageChangeCallback'. Either include it or remove the dependency array.
🟡 src/components/common/Table/TableV2.tsx:1073:6 react-hooks/exhaustive-deps React Hook useMemo has a missing dependency: 'scroll'. Either include it or remove the dependency array.

… and 48 more. Run make ui-checkstyle-changed locally for the full list.


Fix locally (fast - only checks files changed in this branch):

make ui-checkstyle-changed

The glossary terms list read the saved page size but offered no way to
change it, unlike the classification tags pager.
- An off-scale size in a listing URL now also opens page 1 and is cleared
  from the URL, so an old ?page=3&size=9 link no longer lands past the
  last result with no pager to page back.
- Replace the Services view-switch test that asserted on mocked hooks with
  one that runs the real paging hooks, ListView and router and checks the
  request limit, URL and displayed page.
- Move NO_URL_FILTERS to Services.constants.ts; it needs a stable reference
  so useTableFilters keeps its parsed filters memoized.
Keep-alive keeps hidden listings mounted on the shared URL, so a hidden
card view rejecting size=25 cleared the visible table page's paging.
- SchemaTable: use the core Select's value/onChange instead of the
  deprecated selectedKey/onSelectionChange.
- TableQueries: drop the role=presentation click wrapper around the date
  picker; the picker opens itself through onOpenChange.
- TableV2: type the no-custom-pagination case as never rather than an
  optional undefined.
Its type already includes undefined, which Sonar flags as redundant.
…tion-page-sizes

# Conflicts:
#	openmetadata-ui/src/main/resources/ui/src/components/Database/TableQueries/TableQueries.tsx
#	openmetadata-ui/src/main/resources/ui/src/pages/AuditLogsPage/AuditLogsPage.tsx
Comment thread openmetadata-ui/src/main/resources/ui/src/hooks/paging/usePaging.ts Outdated
sweta1308 and others added 2 commits October 17, 2026 11:05
- usePaging: an off-scale size in the URL now also opens page 1 without
  its cursor and is cleared from the URL, only from the visible route, so
  an old ?currentPage=3&pageSize=10 link no longer requests past the end.
- Memories: the page is tied to the size it was picked at, so a change to
  the saved page size starts it on page 1 before the next request.
- Learning resources and Metrics tie their page to the size it was picked
  at (usePageForSize), and the atoms listings (Domains, Data Products,
  Sub-domains) treat the URL page as 1 once the size changes, so a size
  picked elsewhere while they stay mounted in app mode no longer requests
  past the last page.
- Add usePageForSize tests and a listing test for the size change.
- Record the old-link page reset and the visible-route guard in the ADR.
- Remove the comments this PR added; the reasoning lives in the ADR.
Comment on lines +24 to +27
const setPage = useCallback(
(page: number) => setPageAtSize({ page, size: pageSize }),
[pageSize]
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Bug: Stale setPage on Metrics leaves paging stuck on page 1 after a size change

usePageForSize returns a setPage that writes size: pageSize from its closure, so its identity changes whenever the size changes. In MetricListPage, setCurrentPage is a useCallback(..., []) and keeps the setPage from the first render. Say the page opens in table view (15) and the user switches to cards (12). Clicking Next then calls the old setPage, which stores { page: 2, size: 15 }. Since 15 !== 12, the hook reads the page as INITIAL_PAGING_VALUE. Previous/Next on Metrics stay on page 1 until the page remounts, and the same happens after any other change to the saved size. The new usePageForSize.test.ts misses this because it always calls the latest result.current[1]. The cleanest fix is to give the hook a stable setter that reads the current size from a ref; adding setPage to the setCurrentPage deps would also work.

Make the setter stable by reading the current size from a ref.:

const sizeRef = useRef(pageSize);
sizeRef.current = pageSize;

const setPage = useCallback(
  (page: number) => setPageAtSize({ page, size: sizeRef.current }),
  []
);
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Oct 17, 2026

Copy link
Copy Markdown
Code Review ⚠️ Changes requested 2 closed / 3 findings

🟡 Medium risk · Standardizing pagination across UI lists and grids changes URL, saved-size, and request behavior; stale paging state could hide results or show empty pages.

Standardizes paged views to 15/25/50 for lists and 12/24/48 for card grids, with saved sizes and view-toggle page resets. Connections view toggle and empty-later-page pager issues are resolved.

  • Open: usePageForSize returns a setPage whose identity changes with the size, but MetricListPage keeps a stale copy in setCurrentPage (useCallback(..., [])), so Previous/Next stay on page 1 after a size change until remount. Fix with a stable setter that reads the current size from a ref, or add setPage to the callback deps.
⚠️ Bug: Stale setPage on Metrics leaves paging stuck on page 1 after a size change

📄 openmetadata-ui/src/main/resources/ui/src/hooks/paging/usePageForSize.ts:24-27 📄 openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx:270 📄 openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx:515-518

usePageForSize returns a setPage that writes size: pageSize from its closure, so its identity changes whenever the size changes. In MetricListPage, setCurrentPage is a useCallback(..., []) and keeps the setPage from the first render. Say the page opens in table view (15) and the user switches to cards (12). Clicking Next then calls the old setPage, which stores { page: 2, size: 15 }. Since 15 !== 12, the hook reads the page as INITIAL_PAGING_VALUE. Previous/Next on Metrics stay on page 1 until the page remounts, and the same happens after any other change to the saved size. The new usePageForSize.test.ts misses this because it always calls the latest result.current[1]. The cleanest fix is to give the hook a stable setter that reads the current size from a ref; adding setPage to the setCurrentPage deps would also work.

Make the setter stable by reading the current size from a ref.
const sizeRef = useRef(pageSize);
sizeRef.current = pageSize;

const setPage = useCallback(
  (page: number) => setPageAtSize({ page, size: sizeRef.current }),
  []
);
✅ 2 closed
✅ Bug: Connections view toggle keeps the current page now that the scales differ

📄 openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/useConnectionsData.ts:172-183 📄 openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsListView.tsx:170-171 📄 openmetadata-ui/src/main/resources/ui/src/components/integration/ConnectionsPage/ConnectionsPage.constants.ts:107-110
Until this PR, Connections used 12/24/48 for both views. The removed comment in ConnectionsPage.constants.ts gave the reason: "so switching layout keeps the page you were on rather than resizing it". The list view now uses 15/25/50. When viewMode changes, useConnectionsData gets new pageSizeOptions, and usePaging's sync effect updates pageSize. Nothing resets currentPage, though. setViewMode in useConnectionsViewMode only writes viewMode, and the reset-to-page-1 queryKey in useConnectionsData does not include the page size or the view.

Example: with 40 connections, grid page 4 shows items 37–40. Switching to list keeps page 4 at 15 per page, which starts past the last row. In client mode the page renders empty next to a non-zero count. The PR description and the ADR both say the toggle goes back to page 1 on Connections, but no code does that here.

Fix: call handlePageChange(1) when pageSizeOptions (or viewMode) changes, the same way the Domains/Metrics/Learning pages reset.

✅ Edge Case: Pager hidden on an empty later page leaves user stranded

📄 openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/AppLiveIndexing/AppLiveIndexing.component.tsx:188-202 📄 openmetadata-ui/src/main/resources/ui/src/components/Settings/Services/Services.tsx:381-384
Both views now hide the pager whenever the current page's data is empty, but they don't check whether they are on page 1. The live-indexing retry queue drains as records get processed. If a user goes to page 3 after the queue has shrunk, getLiveIndexingQueue returns [] with total > 0. The pager disappears and the only thing left is the 'no retry queue records' placeholder, with no way back to earlier pages. Settings → Services behaves the same way when the currentPage/cursor in the URL points past the data, for example after services were deleted or when a stale link is shared. The fix is to hide the pager only when nothing exists at all: base the condition on paging.total (or on currentPage > 1) rather than on the current page's rows.

🤖 Prompt for agents
Code Review: Standardizes paged views to 15/25/50 for lists and 12/24/48 for card grids, with saved sizes and view-toggle page resets. Connections view toggle and empty-later-page pager issues are resolved.
  
  - Open: `usePageForSize` returns a `setPage` whose identity changes with the size, but `MetricListPage` keeps a stale copy in `setCurrentPage` (`useCallback(..., [])`), so Previous/Next stay on page 1 after a size change until remount. Fix with a stable setter that reads the current size from a ref, or add `setPage` to the callback deps.

1. ⚠️ Bug: Stale setPage on Metrics leaves paging stuck on page 1 after a size change
   Files: openmetadata-ui/src/main/resources/ui/src/hooks/paging/usePageForSize.ts:24-27, openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx:270, openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricListPage/MetricListPage.tsx:515-518

   `usePageForSize` returns a `setPage` that writes `size: pageSize` from its closure, so its identity changes whenever the size changes. In `MetricListPage`, `setCurrentPage` is a `useCallback(..., [])` and keeps the `setPage` from the first render. Say the page opens in table view (15) and the user switches to cards (12). Clicking Next then calls the old `setPage`, which stores `{ page: 2, size: 15 }`. Since 15 !== 12, the hook reads the page as `INITIAL_PAGING_VALUE`. Previous/Next on Metrics stay on page 1 until the page remounts, and the same happens after any other change to the saved size. The new `usePageForSize.test.ts` misses this because it always calls the latest `result.current[1]`. The cleanest fix is to give the hook a stable setter that reads the current size from a ref; adding `setPage` to the `setCurrentPage` deps would also work.

   Fix (Make the setter stable by reading the current size from a ref.):
   const sizeRef = useRef(pageSize);
   sizeRef.current = pageSize;
   
   const setPage = useCallback(
     (page: number) => setPageAtSize({ page, size: sizeRef.current }),
     []
   );

Verification

Rules No rules evaluated

Functional validation Not enabled

Set up

Auto-approval Not enabled

Set up

For more verification details, comment gitar display:verbose.

Options

Display: compact → Showing compact verification results.

Comment with these commands to change the behavior for this request:

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source

Comment on lines +114 to +121
useEffect(() => {
if (!isRouteVisible || !isPageFromOtherSize) {
return;
}
if (urlState.currentPage > 1) {
setCurrentPage(1);
} else {
setPagedAtSize(effectivePageSize);

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.

P1 Back opens the wrong page

In useListingData, browser Back to a valid URL with a different size now loses its page. Visit Domains at ?page=2&size=25, change to size 50, then go Back. pagedAtSize is still 50, so the hook treats the restored size 25 as a preference change, fetches page 1, and removes page=2 from the URL.

Preserve the page when navigation supplies a valid size. Reset it only when the saved size changes without a URL override.

@sonarqubecloud

Copy link
Copy Markdown

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test Add this label to run secure Github workflows on PRs UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Standardize UI pagination: 15/25/50 for lists, 12/24/48 for card grids

1 participant