Skip to content

Added pagination support for document listing page and added playwright test for all the cases - #29551

Merged
Rohit0301 merged 28 commits into
mainfrom
document-page-pagination
Jul 4, 2026
Merged

Added pagination support for document listing page and added playwright test for all the cases#29551
Rohit0301 merged 28 commits into
mainfrom
document-page-pagination

Conversation

@Rohit0301

@Rohit0301 Rohit0301 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor
Screenshot 2026-07-01 at 1 56 24 PM

Describe your changes:

Fixes #

I worked on ... because ...

Type of change:

  • Bug fix
  • Improvement
  • New feature
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation

High-level design:

N/A — small change.

Tests:

Use cases covered

Unit tests

Backend integration tests

Ingestion integration tests

Playwright (UI) tests

Manual testing performed

UI screen recording / screenshots:

Not applicable.

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: I updated the migration scripts or explained why it is not needed.
  • For UI changes: I attached a screen recording and/or screenshots above.
  • I have added tests (unit / integration / Playwright as applicable) and listed them above.

Summary by Gitar

  • API and Backend:
    • Added folder-based filtering to listContextFiles via folderId parameter in ListFilter and ContextFileResource.
    • Updated FolderRepository and FolderResource to include childrenCount field for folder entities.
  • UI Pagination:
    • Implemented infinite scroll pagination in ContextCenterDocumentsPage using usePaging and onScrollEnd.
    • Added isLoadingMore state to DocumentsView to show skeleton rows during incremental data fetches.
  • Testing:
    • Migrated document-related tests to a new Playwright suite ContextCenterDocumentPage.spec.ts covering full CRUD, bulk operations, and pagination.
    • Updated DocumentFolderView.test.tsx to align with the removal of local file-list prop dependencies.

This will update automatically on new commits.

Greptile Summary

This PR adds server-side cursor pagination to the Context Center documents listing page (infinite scroll with after cursors) and a matching folder-level file pagination in the sidebar tree (first-page-on-expand, "View more"/"Show less"). It also introduces a new, dedicated Playwright suite (ContextCenterDocumentPage.spec.ts) that covers full CRUD, bulk operations, search, and pagination flows, while removing those tests from ContextCenter.spec.ts.

  • Backend: ListFilter.getFolderCondition() generates a parameterised subquery so the server can scope files to a folder; FolderRepository.setListFields resolves childrenCount with a single batched GROUP BY query instead of N+1 individual counts.
  • Frontend: ContextCenterDocumentsPage manages pagination state via fetchGenerationRef (stale-response guard on folder switches) and isLoadingMoreRef (synchronous duplicate-fetch guard on rapid scroll); DocumentFolderView adds lazy-loading and expand/collapse of per-folder file lists with their own cursor chain.
  • Tests: New Playwright file covers 13 scenarios; unit tests for FolderRepository batch query path and ListFilter SQL-injection safety are added on the backend.

Confidence Score: 4/5

The backend pagination and folder-filter logic are solid, but two open issues from prior review rounds are still present in this revision and have not been addressed.

The backend changes (batch count queries, parameterised folder subquery, generation-guard on stale responses, synchronous ref guard against duplicate scroll fetches) are all well-implemented and tested. What keeps this from being fully merge-ready are two unfixed issues carried forward from previous rounds: (1) a Playwright test opens a new browser tab via browser.newPage() rather than page.context().newPage(), meaning it runs in a fresh unauthenticated context that will redirect to the login page in CI; and (2) switching folders does not clear selectedIds, so documents selected in one folder context silently remain selected and can be bulk-deleted after a folder switch without any visual indication.

ContextCenterDocumentPage.spec.ts (unauthenticated new-tab in the copy-link test) and ContextCenterDocumentsPage.tsx (selection state not cleared on folder switch).

Important Files Changed

Filename Overview
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/CollectionDAO.java Adds two new DAO methods: countNonDeletedChildFiles (single-folder count) and countNonDeletedChildFilesBatch (multi-folder count via GROUP BY); both correctly join entity_relationship with context_file and filter soft-deleted rows.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/FolderRepository.java Adds childrenCount field support using the new batch DAO method in setListFields and single-row lookup in setFields; batch path issues a single SQL query for all folders in a page, avoiding the N+1 pattern.
openmetadata-service/src/main/java/org/openmetadata/service/jdbi3/ListFilter.java Adds getFolderCondition() which generates a parameterised subquery on entity_relationship scoped to the provided folderId; no deleted-column predicate on the join table (correct), and SQL injection is prevented by binding the value via :folderIdParam.
openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/ContextFileResource.java Adds optional folderId query parameter wired into ListFilter; parameter is validated (non-blank check) before being stored, cleanly composing with existing filter logic.
openmetadata-service/src/main/java/org/openmetadata/service/resources/drive/FolderResource.java Adds childrenCount to the list of allowed fields and registers it with VIEW_BASIC permission — straightforward extension of the existing field pattern.
openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/EntityRepositoryBulkFieldsTest.java Adds four targeted unit tests for the childrenCount batch query path: batching, correct relation constants, empty-entity guard, and field-not-requested clearance.
openmetadata-service/src/test/java/org/openmetadata/service/jdbi3/ListFilterTest.java Adds four tests for getFolderCondition() covering absent/blank ID, parameterized subquery shape, and an explicit SQL-injection guard.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenter.spec.ts Removes all document-page tests (CRUD, bulk ops, search) that have been migrated to the new ContextCenterDocumentPage.spec.ts file, along with their corresponding helpers and cleanup sets.
openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocumentPage.spec.ts New comprehensive Playwright suite covering pagination, search, CRUD, bulk operations, and preview. One test opens a new tab via browser.newPage() (line 818) which creates an unauthenticated browser context and will fail in CI where auth is enforced.
openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentFolderView.component.tsx Adds folder-level file pagination: lazy-loads first page on expand, supports View more (next page) and Show less (in-memory collapse), and exposes refetchFolderFiles via imperative ref.
openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx Adds isLoadingMore prop and renders skeleton rows during incremental fetches; scroll handler fires onScrollEnd when within 100px of the list bottom.
openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx Core pagination implementation using fetchGenerationRef (stale-response guard), isLoadingMoreRef (synchronous duplicate-fetch guard), and usePaging cursor management. selectedIds is not cleared on folder switch (previously flagged open issue).
openmetadata-ui/src/main/resources/ui/src/rest/assetAPI.ts Adds folderId to listContextFiles params, forwarding it to the backend as a query parameter; no logic changes to other API functions.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant U as User (Browser)
    participant DV as DocumentsView
    participant DFV as DocumentFolderView
    participant Page as ContextCenterDocumentsPage
    participant API as REST API

    Note over U,API: Initial Load
    Page->>API: "GET /drive/files?limit=15"
    API-->>Page: "{ data, paging.after, paging.total }"
    Page->>DV: "data=page1, totalFileCount=N"

    Note over U,API: Infinite Scroll (main list)
    U->>DV: scroll near bottom
    DV->>Page: onScrollEnd()
    Page->>Page: "isLoadingMoreRef.current=true"
    Page->>API: "GET /drive/files?after=cursor&limit=15"
    API-->>Page: "{ data: page2, paging.after }"
    Page->>DV: "data=[...page1, ...page2]"

    Note over U,API: Folder Filter (server-side)
    U->>DFV: click folder row
    DFV->>Page: onSelectFolder(folderId)
    Page->>Page: fetchGenerationRef.current++
    Page->>API: "GET /drive/files?folderId=X&limit=15"
    API-->>Page: "{ data: folderPage1 }"
    Page->>DV: "data=folderPage1"

    Note over U,API: Folder Sidebar Expand
    U->>DFV: expand folder node
    DFV->>API: "GET /drive/files?folderId=X&limit=10"
    API-->>DFV: "{ data: first10, paging.after }"
    DFV->>U: show first 10 + View more button

    Note over U,API: View More in Sidebar
    U->>DFV: click View more
    DFV->>API: "GET /drive/files?folderId=X&after=cursor&limit=10"
    API-->>DFV: "{ data: next10, paging.after: null }"
    DFV->>U: show all 20 + Show Less button
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant U as User (Browser)
    participant DV as DocumentsView
    participant DFV as DocumentFolderView
    participant Page as ContextCenterDocumentsPage
    participant API as REST API

    Note over U,API: Initial Load
    Page->>API: "GET /drive/files?limit=15"
    API-->>Page: "{ data, paging.after, paging.total }"
    Page->>DV: "data=page1, totalFileCount=N"

    Note over U,API: Infinite Scroll (main list)
    U->>DV: scroll near bottom
    DV->>Page: onScrollEnd()
    Page->>Page: "isLoadingMoreRef.current=true"
    Page->>API: "GET /drive/files?after=cursor&limit=15"
    API-->>Page: "{ data: page2, paging.after }"
    Page->>DV: "data=[...page1, ...page2]"

    Note over U,API: Folder Filter (server-side)
    U->>DFV: click folder row
    DFV->>Page: onSelectFolder(folderId)
    Page->>Page: fetchGenerationRef.current++
    Page->>API: "GET /drive/files?folderId=X&limit=15"
    API-->>Page: "{ data: folderPage1 }"
    Page->>DV: "data=folderPage1"

    Note over U,API: Folder Sidebar Expand
    U->>DFV: expand folder node
    DFV->>API: "GET /drive/files?folderId=X&limit=10"
    API-->>DFV: "{ data: first10, paging.after }"
    DFV->>U: show first 10 + View more button

    Note over U,API: View More in Sidebar
    U->>DFV: click View more
    DFV->>API: "GET /drive/files?folderId=X&after=cursor&limit=10"
    API-->>DFV: "{ data: next10, paging.after: null }"
    DFV->>U: show all 20 + Show Less button
Loading

Comments Outside Diff (4)

  1. openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx, line 211-238 (link)

    P1 Deep-link broken for documents beyond the first page

    The ?document=X effect compares against allDocuments, which only contains the first page of results. If the linked document is on page 2, 3, etc., the find will return undefined, the effect shows an error toast, and silently removes the document param — losing the deep link entirely. Previously all documents were loaded at once, so this always worked. The guard if (match) { ... } else { showErrorToast(...) } now fires incorrectly whenever a user shares a link to any document that isn't in the initial page load.

  2. openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx, line 124-130 (link)

    P1 Client-side folder filter is incompatible with server-side cursor pagination

    documents is computed by filtering the locally-accumulated allDocuments array. Before this PR, listContextFiles was called with limit: 100 and would typically fetch all (or most) documents in one shot, making client-side folder filtering work. After this PR the limit defaults to pageSize (15 via PAGE_SIZE_BASE), so clicking a folder in the sidebar only shows documents from already-loaded pages. Any documents in that folder on later pages are silently invisible until the user has scrolled far enough to load them all.

  3. openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx, line 570-582 (link)

    P2 count shows loaded documents, not the real total

    With pagination, data.length is the number of rows loaded so far (e.g. 15 on the first page), not the actual total stored server-side. The parent already tracks totalFileCount from response.paging.total. Consider threading that value through a new totalCount prop on DocumentsViewProps and passing it to ListHeader so the file count is always accurate.

  4. openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocumentPage.spec.ts, line 1420-1435 (link)

    P1 New tab opened with browser.newPage() lacks authentication cookies

    browser.newPage() opens a fresh browser context that does not share the storage state set by test.use({ storageState: 'playwright/.auth/admin.json' }). In any environment where authentication is enforced, newTab.goto(clipboardText) will redirect to the login page and context-center-documents-page will never become visible, causing the test to time out.

    Use page.context().newPage() to create the tab inside the same authenticated context as the rest of the test.

  5. openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/ContextCenterDocumentPage.spec.ts, line 1647-1648 (link)

    P1 browser.newPage() opens unauthenticated context

    browser.newPage() creates a page in a brand-new browser context that does not inherit the storage state set by test.use({ storageState: 'playwright/.auth/admin.json' }). In any CI or remote environment where authentication is enforced, newTab.goto(clipboardText) will land on the login page, context-center-documents-page will never become visible, and the test will time out. Use page.context().newPage() so the new tab shares the same authenticated browser context as the rest of the test.

  6. openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx, line 383-394 (link)

    P1 Stale bulk selection after folder switch

    When the user selects documents and then switches folders (or deselects the current folder), selectedIds is never cleared. Because fetchDocuments now replaces allDocuments with the server-side page for the new folder, the previously selected IDs disappear from view but stay in selectedIds. The bulk action bar then shows a non-zero count of "invisible" documents. Clicking the bulk-delete button at that point fires bulkDeleteDriveFiles(Array.from(selectedIds), false) and permanently archives those documents — even though none of them were visible in the current folder context.

    A setSelectedIds(new Set()) call should be issued alongside setSelectedFolderId (or inside fetchDocuments on every non-after reset) to ensure the selection is scoped to the currently-visible list.

Reviews (26): Last reviewed commit: "Merge branch 'main' into document-page-p..." | Re-trigger Greptile

@Rohit0301 Rohit0301 self-assigned this Jun 28, 2026
@Rohit0301
Rohit0301 requested a review from a team as a code owner June 28, 2026 12:42
@Rohit0301 Rohit0301 added the safe to test Add this label to run secure Github workflows on PRs label Jun 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

❌ PR checklist incomplete

This PR cannot be merged until the following are addressed on its linked issue:

  • No GitHub issue is linked. Link an issue in the Development section of the PR (or add Fixes #12345 to the description). For a same-org cross-repo issue, add Fixes open-metadata/<repo>#123 to the description.

The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically.

Maintainers can bypass this check by adding the skip-pr-checks label.

@github-actions

github-actions Bot commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

🟡 Playwright Results — all passed (28 flaky)

✅ 4492 passed · ❌ 0 failed · 🟡 28 flaky · ⏭️ 37 skipped

Shard Passed Failed Flaky Skipped
🟡 Shard 1 439 0 4 16
🟡 Shard 2 818 0 6 8
🟡 Shard 3 797 0 4 7
🟡 Shard 4 811 0 4 5
🟡 Shard 5 860 0 3 0
🟡 Shard 6 767 0 7 1
🟡 28 flaky test(s) (passed on retry)
  • Features/EntityRenameConsolidation.spec.ts › Glossary - multiple rename + update cycles should preserve terms (shard 1, 1 retry)
  • Features/Glossary/GlossaryPagination.spec.ts › should check for nested glossary term search (shard 1, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab IS visible for supported type: metric (shard 1, 1 retry)
  • Flow/SearchRBAC.spec.ts › the browse tree only shows the asset-type categories a user can access (shard 1, 1 retry)
  • Features/BulkEditEntity.spec.ts › Glossary (shard 2, 1 retry)
  • Features/BulkImport.spec.ts › Database Schema (shard 2, 2 retries)
  • Features/ContextCenterPermission.spec.ts › user with all permissions can see restore and delete actions on an archived document (shard 2, 2 retries)
  • Features/DataQuality/DataQuality.spec.ts › TestCase filters (shard 2, 1 retry)
  • Features/DataQuality/TableLevelTests.spec.ts › Table Row Inserted Count To Be Between (shard 2, 1 retry)
  • Features/DataQuality/TestCaseImportExportBasic.spec.ts › should show validation errors for invalid CSV (shard 2, 1 retry)
  • Features/KnowledgeCenterList.spec.ts › Knowledge Center List - Verify Recently Viewed widget (shard 3, 1 retry)
  • Features/SearchExport.spec.ts › Export queues a background job and downloads from the jobs tray (shard 3, 1 retry)
  • Features/Table.spec.ts › should persist page size (shard 3, 1 retry)
  • Features/Tasks/TaskNavigation.spec.ts › navigating to /table/TASK-XXXXX should show 404 (invalid URL pattern) (shard 3, 1 retry)
  • Flow/ExploreDiscovery.spec.ts › Should display domain and owner of deleted asset in suggestions when showDeleted is on (shard 4, 1 retry)
  • Flow/ServiceCreationPermissions.spec.ts › User with service creation permission can create a new database service (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Sql Query (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Should display custom properties for apiCollection in right panel (shard 4, 1 retry)
  • Pages/EntityDataConsumer.spec.ts › Tier Add, Update and Remove (shard 5, 1 retry)
  • Pages/ExploreBrowse.spec.ts › service type drill-down disables unrelated roots and query-panel Clear resets it (shard 5, 2 retries)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/ExplorePageRightPanel.spec.ts › Should allow Data Steward to edit tier for dashboardDataModel (shard 6, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Glossary Bulk Import Export (shard 6, 1 retry)
  • Pages/GlossaryImportExport.spec.ts › Import partial success - some terms pass, some fail (shard 6, 1 retry)
  • Pages/InputOutputPorts.spec.ts › Output ports section collapse/expand (shard 6, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Lineage/LineageRightPanel.spec.ts › Verify custom properties tab is NOT visible for pipelineService in platform lineage (shard 6, 1 retry)
  • Pages/UserDetails.spec.ts › Create team with domain and verify visibility of inherited domain in user profile after team removal (shard 6, 1 retry)

📦 Download artifacts

How to debug locally
# Download playwright-test-results-<shard> artifact and unzip
npx playwright show-trace path/to/trace.zip    # view trace

@Rohit0301
Rohit0301 force-pushed the document-page-pagination branch from 5e834a5 to 473e0ae Compare June 29, 2026 05:36
@Rohit0301
Rohit0301 force-pushed the harshach/drive-document-apis branch from 4e7bd0c to c5014dd Compare June 30, 2026 07:36
Base automatically changed from harshach/drive-document-apis to main June 30, 2026 15:13
@Rohit0301
Rohit0301 force-pushed the document-page-pagination branch from 473e0ae to 082685b Compare July 1, 2026 08:27
…bi3/FolderRepository.java

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Rohit0301 and others added 3 commits July 2, 2026 19:48
…ContextCenterDocumentPage.spec.ts

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
anuj-kumary
anuj-kumary previously approved these changes Jul 3, 2026
sonika-shah and others added 5 commits July 3, 2026 15:43
…lter

Add folder childrenCount cases to EntityRepositoryBulkFieldsTest: childrenCount
resolves in one batched scan per page (not N+1), absent folders default to 0,
the query uses folder->CONTAINS->contextFile, and an empty page issues no query.
Add ListFilter.getFolderCondition() cases for the parameterized CONTAINS
subquery and that folderId is bound, never inlined.
anuj-kumary
anuj-kumary previously approved these changes Jul 3, 2026
@gitar-bot

gitar-bot Bot commented Jul 4, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 9 resolved / 9 findings

Implements infinite scroll pagination for the document listing page and adds comprehensive Playwright coverage. All previously identified functional issues, including stale counts, duplicate fetches, and folder filtering inconsistencies, have been resolved.

✅ 9 resolved
Bug: totalFileCount drifts when deleting/searching (stale count)

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:140-154 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:259 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:337
totalFileCount is only set from response.paging.total in the browse (non-search) branch of fetchDocuments (line 160). During an active search the count is never recomputed, so the folder-view subtitle keeps showing the previous browse total while search results are displayed.

Additionally, the delete handlers do setTotalFileCount((prev) => prev - 1) and prev - deletedIds.size (lines 259, 337). If a delete occurs while viewing search results (where the count was never aligned to what is shown) or repeatedly, the count can drift and even go negative. Consider clamping with Math.max(0, ...) and recomputing/clearing the count when entering search mode so the displayed file count stays consistent.

Bug: Rapid scroll can double-fetch same page (duplicate rows)

📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:555-560 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:154-168
handleScroll calls onScrollEnd on every scroll event while within SCROLL_THRESHOLD, and handleLoadMore guards re-entry with !isLoadingMore. Because isLoadingMore is React state, the guard is stale within the same event tick: multiple scroll events fired before the setIsLoadingMore(true) re-render commits can each pass the guard and call fetchDocuments(paging.after) with the same after cursor. Since results are appended via setAllDocuments((prev) => [...prev, ...response.data]), this can append the same page twice, producing duplicate rows and React duplicate-key warnings. Consider guarding with a ref (e.g. isFetchingRef) set synchronously, or debouncing the scroll handler.

Quality: ListHeader count shows loaded rows, not the true total

📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:570-582
DocumentsView passes count={data.length} to ListHeader (L574). With pagination data.length is the number of rows loaded so far (15 on first page), not the real total stored server-side. The parent already tracks totalFileCount from response.paging.total. Thread that value through a totalCount prop on DocumentsViewProps and pass it to ListHeader so the file count shown above the list stays accurate as the user scrolls.

Edge Case: Folder filtering is incomplete with paginated loading

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:124-130 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:152-166 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:555-560 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:127-133 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:214-228
documents is derived by client-side filtering of allDocuments (only the pages loaded so far) against selectedFolderId (ContextCenterDocumentsPage.tsx:124-130). The paginated API call in fetchDocuments (listContextFiles({ after, limit: pageSize })) never filters by folder server-side, and handleLoadMore only fetches the next global page when the user scrolls the visible list to the bottom.

When a folder is selected, the filtered list can be very small (e.g. 1-2 matching files among 100 loaded). If that filtered list does not overflow the scroll container, the onScroll handler in DocumentsView never fires, so additional pages are never requested. As a result, files that belong to the selected folder but live on a not-yet-loaded page are never displayed and the user has no way to load them. The header file count (totalFileCount) also reflects the total across all folders, not the selected folder, so the subtitle and the visible rows disagree.

The new Playwright tests only seed a couple of files per folder, so they pass, but in a real account with more than pageSize files this produces an effectively broken folder view. Consider filtering server-side by folder (pass the folder id / FQN to listContextFiles) and tracking paging per folder, rather than filtering loaded pages client-side.

Bug: handleFileMoved not updated for null (move-to-root) target

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:273-287 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:138-140 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.interface.ts:33
This commit adds a "remove from folder" (move-to-root) path: FileActions.handleMoveToFolder now calls onFileMoved?.(file, null) and the onFileMoved prop type was widened to (file, targetFolderId: string | null). However, the parent handler handleFileMoved was NOT updated — it is still typed (file, targetFolderId: string) and unconditionally rebuilds file.folder from the argument.

When called with null, it produces a malformed folder object: folder: { id: null, name: (targetFolder?.name ?? null) = null, displayName: undefined, type: 'folder' } instead of clearing the folder association. The document row's folder badge happens to hide because getEntityName returns an empty string for the null name, but the accumulated allDocuments state now holds a folder object with id: null rather than an unset/undefined folder. This is inconsistent state that can leak into folder-tree filtering, preview panels, and any code that treats file.folder as present.

There is also a type-safety concern: assigning a (file, string) => void handler to a prop typed (file, string | null) => void is unsound under strictFunctionTypes and may fail type-checking.

Update handleFileMoved to accept string | null and clear the folder when the target is null.

...and 4 more resolved from earlier reviews

Options

Display: compact → Showing less information.

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

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqubecloud

sonarqubecloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

@sonarqubecloud

sonarqubecloud Bot commented Jul 4, 2026

Copy link
Copy Markdown

@gitar-bot

gitar-bot Bot commented Jul 4, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 9 resolved / 9 findings

Implements infinite scroll pagination for the document listing page and adds comprehensive Playwright coverage. All previously identified functional issues, including stale counts, duplicate fetches, and folder filtering inconsistencies, have been resolved.

✅ 9 resolved
Bug: totalFileCount drifts when deleting/searching (stale count)

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:140-154 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:259 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:337
totalFileCount is only set from response.paging.total in the browse (non-search) branch of fetchDocuments (line 160). During an active search the count is never recomputed, so the folder-view subtitle keeps showing the previous browse total while search results are displayed.

Additionally, the delete handlers do setTotalFileCount((prev) => prev - 1) and prev - deletedIds.size (lines 259, 337). If a delete occurs while viewing search results (where the count was never aligned to what is shown) or repeatedly, the count can drift and even go negative. Consider clamping with Math.max(0, ...) and recomputing/clearing the count when entering search mode so the displayed file count stays consistent.

Bug: Rapid scroll can double-fetch same page (duplicate rows)

📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:555-560 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:154-168
handleScroll calls onScrollEnd on every scroll event while within SCROLL_THRESHOLD, and handleLoadMore guards re-entry with !isLoadingMore. Because isLoadingMore is React state, the guard is stale within the same event tick: multiple scroll events fired before the setIsLoadingMore(true) re-render commits can each pass the guard and call fetchDocuments(paging.after) with the same after cursor. Since results are appended via setAllDocuments((prev) => [...prev, ...response.data]), this can append the same page twice, producing duplicate rows and React duplicate-key warnings. Consider guarding with a ref (e.g. isFetchingRef) set synchronously, or debouncing the scroll handler.

Quality: ListHeader count shows loaded rows, not the true total

📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:570-582
DocumentsView passes count={data.length} to ListHeader (L574). With pagination data.length is the number of rows loaded so far (15 on first page), not the real total stored server-side. The parent already tracks totalFileCount from response.paging.total. Thread that value through a totalCount prop on DocumentsViewProps and pass it to ListHeader so the file count shown above the list stays accurate as the user scrolls.

Edge Case: Folder filtering is incomplete with paginated loading

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:124-130 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:152-166 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:555-560 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:127-133 📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:214-228
documents is derived by client-side filtering of allDocuments (only the pages loaded so far) against selectedFolderId (ContextCenterDocumentsPage.tsx:124-130). The paginated API call in fetchDocuments (listContextFiles({ after, limit: pageSize })) never filters by folder server-side, and handleLoadMore only fetches the next global page when the user scrolls the visible list to the bottom.

When a folder is selected, the filtered list can be very small (e.g. 1-2 matching files among 100 loaded). If that filtered list does not overflow the scroll container, the onScroll handler in DocumentsView never fires, so additional pages are never requested. As a result, files that belong to the selected folder but live on a not-yet-loaded page are never displayed and the user has no way to load them. The header file count (totalFileCount) also reflects the total across all folders, not the selected folder, so the subtitle and the visible rows disagree.

The new Playwright tests only seed a couple of files per folder, so they pass, but in a real account with more than pageSize files this produces an effectively broken folder view. Consider filtering server-side by folder (pass the folder id / FQN to listContextFiles) and tracking paging per folder, rather than filtering loaded pages client-side.

Bug: handleFileMoved not updated for null (move-to-root) target

📄 openmetadata-ui/src/main/resources/ui/src/pages/ContextCenterPage/ContextCenterDocumentsPage/ContextCenterDocumentsPage.tsx:273-287 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.component.tsx:138-140 📄 openmetadata-ui/src/main/resources/ui/src/components/ContextCenter/DocumentsView/DocumentsView.interface.ts:33
This commit adds a "remove from folder" (move-to-root) path: FileActions.handleMoveToFolder now calls onFileMoved?.(file, null) and the onFileMoved prop type was widened to (file, targetFolderId: string | null). However, the parent handler handleFileMoved was NOT updated — it is still typed (file, targetFolderId: string) and unconditionally rebuilds file.folder from the argument.

When called with null, it produces a malformed folder object: folder: { id: null, name: (targetFolder?.name ?? null) = null, displayName: undefined, type: 'folder' } instead of clearing the folder association. The document row's folder badge happens to hide because getEntityName returns an empty string for the null name, but the accumulated allDocuments state now holds a folder object with id: null rather than an unset/undefined folder. This is inconsistent state that can leak into folder-tree filtering, preview panels, and any code that treats file.folder as present.

There is also a type-safety concern: assigning a (file, string) => void handler to a prop typed (file, string | null) => void is unsound under strictFunctionTypes and may fail type-checking.

Update handleFileMoved to accept string | null and clear the folder when the target is null.

...and 4 more resolved from earlier reviews

Options

Display: compact → Showing less information.

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

Compact
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants