Skip to content

feat(ui): lazy-load tab-level and heavy components with withSuspenseFallback#28830

Open
shah-harshit wants to merge 10 commits into
mainfrom
feat/lazy-load-tabs
Open

feat(ui): lazy-load tab-level and heavy components with withSuspenseFallback#28830
shah-harshit wants to merge 10 commits into
mainfrom
feat/lazy-load-tabs

Conversation

@shah-harshit

@shah-harshit shah-harshit commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Converts 105 files from eager component imports to React.lazy() + withSuspenseFallback() for heavy/deferred components
  • Targets: SchemaEditor, ActivityFeedTab, ContractTab, CustomPropertyTable, TaskTabNew, EntitySummaryPanel, ModalWithMarkdownEditor, BlockEditor, CodeEditor, FeedEditor, WorkflowHistory, TagSelectForm, TestCaseFormV1, and ~20 more
  • Lazy constants placed after all traditional imports per convention
  • Eager component imports replaced with import type for props interfaces where applicable
  • Zero TypeScript errors; all checkstyle passes

Scope

Tab registry utils (ChartDetails, Dashboard, DashboardDataModel, Topic, Container, MlModel, Pipeline, StoredProcedures, SearchIndex, Directory, File, Spreadsheet, Worksheet, APICollection, APIEndpoints, DataProduct, DomainUtils, GlossaryUtils, MetricUtils, DataAssetSummaryPanel, EntitySummaryPanel, GlossaryTermUtils, GlossaryTermUtil, DataMarketplace, CSV): lazy-wrap heavy tab components

Component files (~80 files): ActivityFeed components, DataContract tabs, DataQuality forms, Database profiler modals, Entity drawers/panels, common editors, explore/knowledge panels, settings pages, form widgets

Test plan

  • Verify tabs still render correctly (SchemaEditor, ActivityFeedTab, ContractTab load on demand)
  • No blank screen / loading spinner regressions on entity detail pages
  • CI passes

Part of open-metadata/openmetadata-collate#4230

🤖 Generated with Claude Code


Summary by Gitar

  • Build system:
    • Added injectCriticalPreloads Vite plugin to preload Inter fonts and hero SVGs in production.
    • Updated manualChunks and assetFileNames in vite.config.ts with explicit type annotations.
  • Core utility:
    • Refactored withSuspenseFallback to support forwardRef and explicit generics, ensuring compatibility with complex component trees.
  • UI/Performance:
    • Optimized MyDataPage to use a skeleton-first rendering pattern, preventing unnecessary API round-trips that previously blocked LCP.
  • Testing infrastructure:
    • Added a withSuspenseFallback mock to jest.config.js to ensure consistent component behavior across the unit test suite.

This will update automatically on new commits.

…allback

Convert 105 component/util files from eager imports to React.lazy() +
withSuspenseFallback() for heavy components: SchemaEditor, ActivityFeedTab,
ContractTab, CustomPropertyTable, TaskTabNew, EntitySummaryPanel,
ModalWithMarkdownEditor, BlockEditor, CodeEditor, and others.

Lazy constants are placed after all traditional imports. Removed eager
component imports are replaced with import type for props interfaces where
applicable. Zero TypeScript errors introduced.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@shah-harshit shah-harshit requested a review from a team as a code owner June 8, 2026 16:40
@shah-harshit shah-harshit added UI UI specific issues safe to test Add this label to run secure Github workflows on PRs labels Jun 8, 2026
@shah-harshit shah-harshit self-assigned this Jun 8, 2026
@github-actions

github-actions Bot commented Jun 8, 2026

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.

@shah-harshit shah-harshit added the skip-pr-checks Bypass PR metadata validation check label Jun 8, 2026
shah-harshit and others added 2 commits June 8, 2026 22:25
Keep lazy-wrapped constants (CustomPropertyTable, ActivityFeedTab, ContractTab,
ContainerChildren, SampleDataTableComponent, OwnerLabel) while accepting main's
changes: ContainerDetail function bodies moved to ContainerDetailPureUtils,
ColumnSearchResult re-exported from DataAssetSummaryPanelPureUtils, getUsagePercentile
removed from DataAssetSummaryPanelUtils.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Jest test Coverage

UI tests summary

Lines Statements Branches Functions
Coverage: 62%
62.51% (66842/106920) 44.04% (37076/84179) 46.12% (11526/24987)

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

🔴 Playwright Results — 5 failure(s), 21 flaky

✅ 4240 passed · ❌ 5 failed · 🟡 21 flaky · ⏭️ 105 skipped

Shard Passed Failed Flaky Skipped
🔴 Shard 1 296 3 0 6
🟡 Shard 2 801 0 5 9
🟡 Shard 3 801 0 3 8
🔴 Shard 4 837 1 6 15
🟡 Shard 5 720 0 1 47
🔴 Shard 6 785 1 6 20

Genuine Failures (failed on all attempts)

Features/DescriptionSuggestion.spec.ts › should decline a requested api endpoint request schema field description (shard 1)
�[31mTest timeout of 180000ms exceeded.�[39m
Features/TagsSuggestion.spec.ts › should decline requested tags for an api endpoint request schema field (shard 1)
�[31mTest timeout of 180000ms exceeded.�[39m
Pages/Roles.spec.ts › Roles page should work properly (shard 1)
Error: �[2mexpect(�[22m�[31mlocator�[39m�[2m).not.�[22mtoBeVisible�[2m(�[22m�[2m)�[22m failed

Locator: locator('[data-testid="loader"]')
Expected: not visible
Error: strict mode violation: locator('[data-testid="loader"]') resolved to 2 elements:
    1) <div class="loader" data-testid="loader"></div> aka getByTestId('markdown-parser').getByTestId('loader')
    2) <div class="loader" data-testid="loader"></div> aka getByTestId('loader').nth(1)

Call log:
�[2m  - Expect "not toBeVisible" with timeout 15000ms�[22m
�[2m  - waiting for locator('[data-testid="loader"]')�[22m
�[2m    2 × locator resolved to <div class="loader" data-testid="loader"></div>�[22m
�[2m      - unexpected value "visible"�[22m

Pages/CustomProperties.spec.ts › Should display custom properties for dashboard in right panel (shard 4)
Error: locator.waitFor: Error: strict mode violation: locator('[data-testid="loader"]') resolved to 2 elements:
    1) <div class="loader" data-testid="loader"></div> aka getByTestId('cp-8c2b462c-sqlQuery').getByTestId('loader')
    2) <div class="loader" data-testid="loader"></div> aka getByTestId('cp-sqlQuery-1991108c.!@#%`()_-=+{}[]|;\',.?').getByTestId('loader')

Call log:
�[2m  - waiting for locator('[data-testid="loader"]') to be detached�[22m

Pages/Tag.spec.ts › Verify Owner Add Delete (shard 6)

🟡 21 flaky test(s) (passed on retry)
  • Features/DataQuality/TestCaseImportExportE2eFlow.spec.ts › Admin: Complete export-import-validate flow (shard 2, 1 retry)
  • Features/DataQuality/TestCaseResultPermissions.spec.ts › User with only VIEW cannot PATCH results (shard 2, 1 retry)
  • Features/DataQuality/TestLibrary.spec.ts › should require supported data types only when OpenMetadata platform is selected (shard 2, 1 retry)
  • Features/Glossary/GlossaryWorkflow.spec.ts › should display correct status badge color and icon (shard 2, 1 retry)
  • Features/Glossary/LargeGlossaryPerformance.spec.ts › should expand and collapse all terms (shard 2, 1 retry)
  • Features/SampleDataTableOperations.spec.ts › should show empty state for table without sample data (shard 3, 1 retry)
  • Features/Workflows/WorkflowOssRestrictions.spec.ts › workflow-description-input is enabled in OSS (shard 3, 1 retry)
  • Flow/ConditionalPermissions.spec.ts › User with owner permission can only view owned Database Schema (shard 3, 1 retry)
  • Pages/ClassificationConditionalRendering.spec.ts › Should render all classification detail sections after loading (shard 4, 1 retry)
  • Pages/ClassificationConditionalRendering.spec.ts › Should render classification correctly after page reload (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Table (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Date Time (shard 4, 1 retry)
  • Pages/CustomProperties.spec.ts › Should search custom properties for storedProcedure in right panel (shard 4, 1 retry)
  • Pages/Entity.spec.ts › User as Owner with unsorted list (shard 4, 1 retry)
  • Pages/ExplorePageRightPanel_KnowledgeCenter.spec.ts › Should remove user owner for knowledgeCenter (shard 5, 1 retry)
  • Pages/Lineage/LineageFilters.spec.ts › Verify lineage schema filter selection (shard 6, 1 retry)
  • Pages/Tag.spec.ts › Verify Tag UI (shard 6, 2 retries)
  • Pages/Tags.spec.ts › Classification Page (shard 6, 1 retry)
  • Pages/Tags.spec.ts › Verify Owner Add Delete (shard 6, 1 retry)
  • Pages/Tags.spec.ts › Disabled tag should not allow adding assets from Assets tab (shard 6, 1 retry)
  • VersionPages/ClassificationVersionPage.spec.ts › Classification version page (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

Comment thread openmetadata-ui/src/main/resources/ui/vite.config.ts Outdated
@sonarqubecloud

sonarqubecloud Bot commented Jun 9, 2026

Copy link
Copy Markdown

@gitar-bot

gitar-bot Bot commented Jun 10, 2026

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

Implements lazy-loading across 105 UI components using withSuspenseFallback and adds critical asset preloading to Vite. Ref forwarding issues in FeedEditor were successfully resolved during the review process.

✅ 3 resolved
Bug: Lazy-wrapping FeedEditor breaks ref forwarding in feed editors

📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx:31-37 📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditor.tsx:99-107 📄 openmetadata-ui/src/main/resources/ui/src/components/ActivityFeed/ActivityFeedEditor/ActivityFeedEditorNew.tsx:105
withSuspenseFallback (components/AppRouter/withSuspenseFallback.tsx) is a plain function component — it does NOT use React.forwardRef. When a component converted to lazy() + withSuspenseFallback() is rendered with a ref prop, the ref is silently dropped (React logs "Function components cannot be given refs") and ref.current stays undefined.

This PR wraps FeedEditor with the HOC and still passes a ref to it:

  • ActivityFeedEditor.tsx:99-107 renders <FeedEditor ... ref={editorRef as LegacyRef<EditorContentRef>} />
  • ActivityFeedEditorNew.tsx:105 does the same.

Because the ref no longer reaches the underlying FeedEditor, editorRef.current is always undefined. As a result useImperativeHandle (ActivityFeedEditor.tsx:89-93) falls back to () => '' / noop, and onSaveHandler (lines 73-84) reads empty content. The activity-feed comment/edit editor will fail to return or clear content — effectively breaking posting/editing feed messages, a core feature reached on every entity page.

The same risk applies to any other component this PR lazy-wrapped that is consumed with a ref (e.g. other editors). Fix by making the HOC forward refs, or keep ref-receiving components eagerly imported.

Quality: Test mock of withSuspenseFallback does not forward refs

📄 openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/withSuspenseFallback.mock.tsx:21-33 📄 openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx:17-31
The production withSuspenseFallback was just changed to use forwardRef and pass ref={ref} to the wrapped component (to fix ref forwarding for components like FeedEditor/ActivityFeedEditor). The new Jest mock (withSuspenseFallback.mock.tsx), wired in via jest.config.js moduleNameMapper, instead returns a plain function component (DefaultFallback) that does NOT use forwardRef and never threads ref through to the inner <Component>.

Consequences in tests:

  • Any component under test that passes a ref through a withSuspenseFallback-wrapped child (e.g. ActivityFeedEditor forwarding editorRef to the lazy FeedEditor) will silently lose the ref under the mock, so the very ref-forwarding behavior this PR fixes is not exercised by the test suite.
  • Worse, passing a non-null ref to the mock's plain function component triggers React's "Function components cannot be given refs" warning and yields a null ref, which can cause ref-dependent tests to fail or behave differently than production.

Suggestion: mirror production by making the mock a forwardRef that passes ref to the inner component, keeping the Suspense fallback={null} behavior.

Quality: Comment says enforce:'post' but plugin uses transformIndexHtml order

📄 openmetadata-ui/src/main/resources/ui/vite.config.ts:28 📄 openmetadata-ui/src/main/resources/ui/vite.config.ts:59-60 📄 openmetadata-ui/src/main/resources/ui/vite.config.ts:34-48
The doc comment for injectCriticalPreloads states "Running with enforce: 'post' ensures this transform runs after the existing html-transform plugin", but the returned plugin object never sets a top-level enforce: 'post'. The actual mechanism used is transformIndexHtml: { order: 'post' }, which is the correct way to order one transformIndexHtml hook after another. The functional behavior is fine, but the comment references a different (unused) API and may mislead future maintainers. Update the comment to say transformIndexHtml's order: 'post' instead of enforce: 'post'.

Options

Display: compact → Showing less information.

Comment with these commands to change:

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 skip-pr-checks Bypass PR metadata validation check UI UI specific issues

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant