Repository navigation
Conversation
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.
✅ PR checks passedThe linked issue has a description and all required Shipping project fields set. Thanks! |
…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).
🔄 Playwright impact map auto-refreshedThis PR touched specs or UI source that changed the source→spec routing map. I regenerated 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 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.
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.
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).
- 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.
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
🔄 Playwright impact map auto-refreshedThis PR touched specs or UI source that changed the source→spec routing map. I regenerated 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 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 |
|
| 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-changedThe 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
- 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.
| const setPage = useCallback( | ||
| (page: number) => setPageAtSize({ page, size: pageSize }), | ||
| [pageSize] | ||
| ); |
There was a problem hiding this comment.
⚠️ 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 👍 / 👎
Code Review
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Powered by Gitar — free for open source
| useEffect(() => { | ||
| if (!isRouteVisible || !isPageFromOtherSize) { | ||
| return; | ||
| } | ||
| if (urlState.currentPage > 1) { | ||
| setCurrentPage(1); | ||
| } else { | ||
| setPagedAtSize(effectivePageSize); |
There was a problem hiding this comment.
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.
|



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:
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 (
globalPageSizefor lists, newglobalGridPageSizefor 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 inADR:2026-10-13-ui-page-sizes-lists-15-25-50-grids-12-24-48.main)useCursorPagedListtakes an initial page size)PaginationCardWithControls,TableV2client paging, atoms listing hooks (defaults)Also fixes pre-existing type errors in files this PR touches:
AppLiveIndexing: removed propsTableV2does not accept (currentPage,pageSize,paginationVisible); "hide pager on a single page" now useshideOnSinglePage, so it actually takes effect.useDataFetching: default transform typed to the hook's entity type.Domains.spec.ts: extrafillDomainFormargument and possibly-undefined FQNs passed toencodeURIComponent.Out of scope (not paged views): typeahead/suggestion searches, tree-select batches, count-only queries, bulk fetches, "show more" previews.
Type of change:
High-level design:
src/constants/constants.ts):LIST_PAGE_SIZE_OPTIONS(15/25/50),GRID_PAGE_SIZE_OPTIONS(12/24/48),GRID_PAGE_SIZE_BASE. RemovedTABLE_CARD_PAGE_SIZE(9) and an unusedDEFAULT_PAGE_SIZE(10). The Connections page's own option constants now point at these.utils/PaginationUtils.ts):getViewPageSizeOptions,getViewPageSize,resolveViewPageSizepick the scale from aPageViewMode. A size picked in one view falls back to the other scale's first step (the scales share no value).GRID_PAGE_SIZE_OPTIONStousePaging, which already reconciles a size from the sharedpageSizeURL param /globalPageSizepreference to the page's own options and only persists list-scale sizes globally. No change tousePagingwas needed.getViewPageSize(view)as its page size; the view toggle also resets to page 1.limit: pageSizeinto the cursor it sends, so the request and the displayed page size agree.pageSize/sizeURL param from the old scale is reconciled to the page's scale on load.Tests:
Use cases covered
Unit tests
src/utils/PaginationUtils.test.ts— 11/11 pass; coverage onPaginationUtils.ts: 100% lines/branches/functions.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) andKnowledgeGraphDetails.test.tsx; both pass when run alone (KnowledgePageSummary 3/3 reruns).usePaging.integration.test.tsx: a page that declares no sizes ignores a gridpageSize=48in 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.tsxandAlertRecentEventsTab.desync.real-hook.test.tsxupdated (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.tsxupdated to grid sizes.pagination.test.tsx(vitest) passes.tsc --noEmit: no errors in changed files. ESLint: no errors.yarn ui-checkstyle:changed: pass.Backend integration tests
NOT NEEDED — UI-only change.
Ingestion integration tests
Playwright (UI) tests
Pages/Domains.spec.ts,Pages/SubDomainPagination.spec.ts— domain listingsize=9→15Pages/AppStopRunModal.spec.ts,Pages/DataInsightSettings.spec.ts,Pages/DataInsightReportApplication.spec.ts—apps?limit=15→12Pages/SearchIndexApplication.spec.ts—apps/marketplace?limit=15→12Ran 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
docker/development/docker-compose.yml) on:8585, UI viayarn starton:3000, logged in as admin.UI screen recording:
Screen-Recording.57.mp4
Checklist:
Fixes <issue-number>: <short explanation>Fixes #<issue-number>above.docs/decisions/2026-10-13-ui-page-sizes-lists-15-25-50-grids-12-24-48.md.🤖 Generated with Claude Code
Preserve valid pages restored through browser history before merging.
Findings
Summary
Standardizes paged lists to 15/25/50 and card grids to 12/24/48.
Reviews (15) · Last reviewed commit: "Reset stale pages on a saved-size change..." · Reviewed by Greptile