diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/GlobalPageSize.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/GlobalPageSize.spec.ts index 7f568ad03737..6c44205bbae4 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/GlobalPageSize.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/GlobalPageSize.spec.ts @@ -28,8 +28,18 @@ test.describe('Table & Data Model columns table pagination', () => { await waitForAllLoadersToDisappear(page); // Change page size to 25 - await page.getByTestId('page-size-selection-dropdown').click(); - await page.getByRole('menuitem', { name: '25 / Page' }).click(); + const tablePageSizeDropdown = page.getByTestId( + 'page-size-selection-dropdown' + ); + await tablePageSizeDropdown.scrollIntoViewIfNeeded(); + await expect(tablePageSizeDropdown).toBeVisible(); + await tablePageSizeDropdown.hover(); + + const tablePageSizeOption = page + .locator('.ant-dropdown:not(.ant-dropdown-hidden)') + .getByRole('menuitem', { name: '25 / Page' }); + await expect(tablePageSizeOption).toBeVisible(); + await tablePageSizeOption.click(); await waitForAllLoadersToDisappear(page); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LandingPageWidgets/DomainDataProductsWidgets.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LandingPageWidgets/DomainDataProductsWidgets.spec.ts index a1cf6d76b7fb..5e92f5492f8d 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LandingPageWidgets/DomainDataProductsWidgets.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Features/LandingPageWidgets/DomainDataProductsWidgets.spec.ts @@ -342,59 +342,34 @@ test.describe.serial('Domain and Data Product Asset Counts', () => { await waitForAllLoadersToDisappear(page); await sidebarClick(page, SidebarItem.DATA_PRODUCT); await selectDataProduct(page, dataProduct.data); + await waitForAllLoadersToDisappear(page); + const dataProductAssetsResponse = page.waitForResponse( + (response) => + response.url().includes('/api/v1/dataProducts/name/') && + response.url().includes('fields=domains%2Cassets') && + response.request().method() === 'GET' + ); await page.getByTestId('assets').click(); + await dataProductAssetsResponse; - await page - .getByTestId('loader') - .waitFor({ - state: 'detached', - timeout: 10000, - }) - .catch(() => { - /* ignore if loader not found */ - }); - - let hasAssets = true; - while (hasAssets) { - const checkboxes = page.locator( - '[data-testid^="table-data-card_"] input[type="checkbox"]' - ); - const count = await checkboxes.count(); - - if (count === 0) { - hasAssets = false; - break; - } - - const selectAll = page.getByRole('checkbox', { name: 'Select All' }); - if (await selectAll.isVisible()) { - await selectAll.check(); - } else { - for (let i = 0; i < count; i++) { - await checkboxes.nth(i).check(); - } - } - - const previousCount = count; - const removeRes = page.waitForResponse('**/assets/remove'); - await page.getByTestId('delete-all-button').click(); - await removeRes; - - await expect - .poll( - async () => - page - .locator( - '[data-testid^="table-data-card_"] input[type="checkbox"]' - ) - .count(), - { timeout: 10_000 } - ) - .toBeLessThan(previousCount); + // The card list paints after the assets response resolves, and count() + // does not auto-wait. Wait for a card before selecting every attached asset. + await waitForAllLoadersToDisappear(page); + const assetCard = page.locator('[data-testid^="table-data-card_"]'); + await assetCard.first().waitFor({ state: 'visible' }); + + const attachedCount = await assetCard.count(); + for (let i = 0; i < attachedCount; i++) { + await assetCard.nth(i).locator('input[type="checkbox"]').check(); } + const removeRes = page.waitForResponse('**/assets/remove'); + await page.getByTestId('delete-all-button').click(); + await removeRes; + await page.reload(); + await waitForAllLoadersToDisappear(page); await checkAssetsCount(page, 0); await redirectToHomePage(page); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts index 6cb8c799a4c3..80f1f76cce21 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/Glossary.spec.ts @@ -1258,11 +1258,11 @@ test.describe('Glossary tests', () => { await selectActiveGlossary(page, glossary1.data.displayName); await selectActiveGlossaryTerm(page, glossaryTerm1.data.displayName); - const viewerContainerText = await page.textContent( - '[data-testid="viewer-container"]' - ); - - expect(viewerContainerText).toContain('Updated description'); + // The description renders after the term page finishes loading, so assert + // on the locator rather than reading textContent once. + await expect( + page.locator('[data-testid="viewer-container"]') + ).toContainText('Updated description'); } finally { await glossaryTerm1.delete(apiContext); await glossary1.delete(apiContext); diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/odcsImportExport.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/odcsImportExport.ts index c30ab52bd12c..973eeccb0cea 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/odcsImportExport.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/odcsImportExport.ts @@ -10,7 +10,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Page, Response } from '@playwright/test'; +import { expect, Page, Response } from '@playwright/test'; import { TableClass } from '../support/entity/TableClass'; import { toastNotification } from './common'; import { waitForAllLoadersToDisappear } from './entity'; @@ -25,17 +25,22 @@ export const openODCSImportDropdown = async (page: Page) => { const addButton = page.getByTestId('add-contract-button'); const manageButton = page.getByTestId('manage-contract-actions'); - const addButtonVisible = await addButton.isVisible().catch(() => false); - const manageButtonVisible = await manageButton.isVisible().catch(() => false); + // Contract actions can render after the page loader disappears, so wait for + // either valid entry point instead of making a one-shot visibility decision. + await expect(addButton.or(manageButton)).toBeVisible({ timeout: 15000 }); - if (addButtonVisible) { + if (await addButton.isVisible()) { await addButton.click(); await page.getByTestId('add-contract-menu').waitFor({ state: 'visible', timeout: 10000, }); - } else if (manageButtonVisible) { + } else { await manageButton.click(); + await page.locator('.contract-action-dropdown').waitFor({ + state: 'visible', + timeout: 10000, + }); } }; diff --git a/openmetadata-ui/src/main/resources/ui/playwright/utils/permission.ts b/openmetadata-ui/src/main/resources/ui/playwright/utils/permission.ts index 6ed253c35e75..097fc1b21ecf 100644 --- a/openmetadata-ui/src/main/resources/ui/playwright/utils/permission.ts +++ b/openmetadata-ui/src/main/resources/ui/playwright/utils/permission.ts @@ -137,9 +137,12 @@ export const validateViewPermissions = async ( await expect(page.locator('[data-testid="add-domain"]')).not.toBeVisible(); if (permission?.editDisplayName) { - expect( - await page.locator('[data-testid="edit-displayName-button"]').count() - ).toBeGreaterThan(0); + const editDisplayNameButton = page.locator( + '[data-testid="edit-displayName-button"]' + ); + await expect(editDisplayNameButton.first()).toBeVisible({ + timeout: 30_000, + }); } else { await expect( page.locator('[data-testid="edit-displayName-button"]') diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx index 8fc8921459a3..a9f0cb69b9c1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AppRouter.tsx @@ -19,13 +19,13 @@ import { APP_ROUTER_ROUTES } from '../../constants/router.constants'; import { useApplicationStore } from '../../hooks/useApplicationStore'; import applicationRoutesClass from '../../utils/ApplicationRoutesClassBase'; import Loader from '../common/Loader/Loader'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const AuthenticatedApp = withSuspenseFallback( +const AuthenticatedApp = withPageSuspenseFallback( lazy(() => import('./AuthenticatedApp')) ); -const AuthenticatedRoutes = withSuspenseFallback( +const AuthenticatedRoutes = withPageSuspenseFallback( lazy(() => import('./AuthenticatedRoutes').then((m) => ({ default: m.AuthenticatedRoutes, @@ -34,11 +34,11 @@ const AuthenticatedRoutes = withSuspenseFallback( ); // Lazy-load infrequently-visited unauthenticated pages -const AccessNotAllowedPage = withSuspenseFallback( +const AccessNotAllowedPage = withPageSuspenseFallback( lazy(() => import('../../pages/AccessNotAllowedPage/AccessNotAllowedPage')) ); -const LogoutPage = withSuspenseFallback( +const LogoutPage = withPageSuspenseFallback( lazy(() => import('../../pages/LogoutPage/LogoutPage').then((m) => ({ default: m.LogoutPage, @@ -46,15 +46,15 @@ const LogoutPage = withSuspenseFallback( ) ); -const PageNotFound = withSuspenseFallback( +const PageNotFound = withPageSuspenseFallback( lazy(() => import('../../pages/PageNotFound/PageNotFound')) ); -const SamlCallback = withSuspenseFallback( +const SamlCallback = withPageSuspenseFallback( lazy(() => import('../../pages/SamlCallback')) ); -const SignUpPage = withSuspenseFallback( +const SignUpPage = withPageSuspenseFallback( lazy(() => import('../../pages/SignUp/SignUpPage')) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx index 1063967aeb34..12386ee40996 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedAppRouter.tsx @@ -25,16 +25,16 @@ import { checkPermission, userPermissions } from '../../utils/PermissionsUtils'; import { useApplicationsProvider } from '../Settings/Applications/ApplicationsProvider/ApplicationsProvider'; import { RoutePosition } from '../Settings/Applications/plugins/AppPlugin'; import AdminProtectedRoute from './AdminProtectedRoute'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; // Previously statically imported — lazify so they stay out of the main chunk -const AddCustomMetricPage = withSuspenseFallback( +const AddCustomMetricPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/AddCustomMetricPage/AddCustomMetricPage') ) ); -const CustomizablePage = withSuspenseFallback( +const CustomizablePage = withPageSuspenseFallback( React.lazy(() => import('../../pages/CustomizablePage/CustomizablePage').then((m) => ({ default: m.CustomizablePage, @@ -42,74 +42,74 @@ const CustomizablePage = withSuspenseFallback( ) ); -const DataQualityPage = withSuspenseFallback( +const DataQualityPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/DataQuality/DataQualityPage')) ); -const ForbiddenPage = withSuspenseFallback( +const ForbiddenPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/ForbiddenPage/ForbiddenPage')) ); -const PlatformLineage = withSuspenseFallback( +const PlatformLineage = withPageSuspenseFallback( React.lazy(() => import('../../pages/PlatformLineage/PlatformLineage')) ); -const TagPage = withSuspenseFallback( +const TagPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/TagPage/TagPage')) ); -const DomainRouter = withSuspenseFallback( +const DomainRouter = withPageSuspenseFallback( React.lazy(() => import('./DomainRouter')) ); -const DataProductListPage = withSuspenseFallback( +const DataProductListPage = withPageSuspenseFallback( React.lazy(() => import('../DataProduct/DataProductListPage')) ); -const SettingsRouter = withSuspenseFallback( +const SettingsRouter = withPageSuspenseFallback( React.lazy(() => import('./SettingsRouter')) ); -const EntityRouter = withSuspenseFallback( +const EntityRouter = withPageSuspenseFallback( React.lazy(() => import('./EntityRouter')) ); -const ClassificationRouter = withSuspenseFallback( +const ClassificationRouter = withPageSuspenseFallback( React.lazy(() => import('./ClassificationRouter')) ); -const GlossaryRouter = withSuspenseFallback( +const GlossaryRouter = withPageSuspenseFallback( React.lazy(() => import('./GlossaryRouter/GlossaryRouter')) ); -const GlossaryTermRouter = withSuspenseFallback( +const GlossaryTermRouter = withPageSuspenseFallback( React.lazy(() => import('./GlossaryTermRouter/GlossaryTermRouter')) ); -const MyDataPage = withSuspenseFallback( +const MyDataPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/MyDataPage/MyDataPage.component')) ); -const TestSuiteIngestionPage = withSuspenseFallback( +const TestSuiteIngestionPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/TestSuiteIngestionPage/TestSuiteIngestionPage') ) ); -const TestSuiteDetailsPage = withSuspenseFallback( +const TestSuiteDetailsPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/TestSuiteDetailsPage/TestSuiteDetailsPage.component') ) ); -const AddCustomProperty = withSuspenseFallback( +const AddCustomProperty = withPageSuspenseFallback( React.lazy( () => import('../Settings/CustomProperty/AddCustomProperty/AddCustomProperty') ) ); -const MarketPlacePage = withSuspenseFallback( +const MarketPlacePage = withPageSuspenseFallback( React.lazy(() => import('../../pages/MarketPlacePage/MarketPlacePage')) ); -const DataMarketplacePage = withSuspenseFallback( +const DataMarketplacePage = withPageSuspenseFallback( React.lazy( () => import( @@ -118,42 +118,42 @@ const DataMarketplacePage = withSuspenseFallback( ) ); -const BotDetailsPage = withSuspenseFallback( +const BotDetailsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/BotDetailsPage/BotDetailsPage')) ); -const ServicePage = withSuspenseFallback( +const ServicePage = withPageSuspenseFallback( React.lazy(() => import('../../pages/ServiceDetailsPage/ServiceDetailsPage')) ); -const SwaggerPage = withSuspenseFallback( +const SwaggerPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/SwaggerPage')) ); -const TourPageComponent = withSuspenseFallback( +const TourPageComponent = withPageSuspenseFallback( React.lazy(() => import('../../pages/TourPage/TourPage.component')) ); -const UserPage = withSuspenseFallback( +const UserPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/UserPage/UserPage.component')) ); -const DomainVersionPage = withSuspenseFallback( +const DomainVersionPage = withPageSuspenseFallback( React.lazy( () => import('../../components/Domain/DomainVersion/DomainVersion.component') ) ); -const AddIngestionPage = withSuspenseFallback( +const AddIngestionPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/AddIngestionPage/AddIngestionPage.component') ) ); -const AddServicePage = withSuspenseFallback( +const AddServicePage = withPageSuspenseFallback( React.lazy( () => import('../../pages/AddServicePage/AddServicePage.component') ) ); -const MarketPlaceAppDetails = withSuspenseFallback( +const MarketPlaceAppDetails = withPageSuspenseFallback( React.lazy( () => import( @@ -162,11 +162,11 @@ const MarketPlaceAppDetails = withSuspenseFallback( ) ); -const AppInstallPage = withSuspenseFallback( +const AppInstallPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/AppInstall/AppInstall.component')) ); -const EditConnectionFormPage = withSuspenseFallback( +const EditConnectionFormPage = withPageSuspenseFallback( React.lazy( () => import( @@ -175,31 +175,31 @@ const EditConnectionFormPage = withSuspenseFallback( ) ); -const CreateUserPage = withSuspenseFallback( +const CreateUserPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/CreateUserPage/CreateUserPage.component') ) ); -const EditIngestionPage = withSuspenseFallback( +const EditIngestionPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/EditIngestionPage/EditIngestionPage.component') ) ); -const ServiceVersionPage = withSuspenseFallback( +const ServiceVersionPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/ServiceVersionPage/ServiceVersionPage')) ); -const ExplorePageV1 = withSuspenseFallback( +const ExplorePageV1 = withPageSuspenseFallback( React.lazy(() => import('../../pages/ExplorePage/ExplorePageV1.component')) ); -const OntologyExplorerPage = withSuspenseFallback( +const OntologyExplorerPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/OntologyExplorerPage/OntologyExplorerPage') ) ); -const RequestDescriptionPage = withSuspenseFallback( +const RequestDescriptionPage = withPageSuspenseFallback( React.lazy( () => import( @@ -208,13 +208,13 @@ const RequestDescriptionPage = withSuspenseFallback( ) ); -const RequestTagsPage = withSuspenseFallback( +const RequestTagsPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/TasksPage/RequestTagPage/RequestTagPage') ) ); -const UpdateDescriptionPage = withSuspenseFallback( +const UpdateDescriptionPage = withPageSuspenseFallback( React.lazy( () => import( @@ -223,44 +223,44 @@ const UpdateDescriptionPage = withSuspenseFallback( ) ); -const UpdateTagsPage = withSuspenseFallback( +const UpdateTagsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/TasksPage/UpdateTagPage/UpdateTagPage')) ); -const LogsViewerPage = withSuspenseFallback( +const LogsViewerPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/LogsViewerPage/LogsViewerPage')) ); -const DataInsightPage = withSuspenseFallback( +const DataInsightPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/DataInsightPage/DataInsightPage.component') ) ); -const AddKPIPage = withSuspenseFallback( +const AddKPIPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/KPIPage/AddKPIPage')) ); -const EditKPIPage = withSuspenseFallback( +const EditKPIPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/KPIPage/EditKPIPage')) ); -const QueryPage = withSuspenseFallback( +const QueryPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/QueryPage/QueryPage.component')) ); -const AddQueryPage = withSuspenseFallback( +const AddQueryPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/AddQueryPage/AddQueryPage.component')) ); -const IncidentManagerPage = withSuspenseFallback( +const IncidentManagerPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/IncidentManager/IncidentManagerPage')) ); -const TestLibraryPage = withSuspenseFallback( +const TestLibraryPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/TestLibrary/TestLibraryPage')) ); -const IncidentManagerDetailPage = withSuspenseFallback( +const IncidentManagerDetailPage = withPageSuspenseFallback( React.lazy( () => import( @@ -269,41 +269,41 @@ const IncidentManagerDetailPage = withSuspenseFallback( ) ); -const TestCaseVersionPage = withSuspenseFallback( +const TestCaseVersionPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/TestCaseVersionPage/TestCaseVersionPage') ) ); -const ObservabilityAlertsPage = withSuspenseFallback( +const ObservabilityAlertsPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/ObservabilityAlertsPage/ObservabilityAlertsPage') ) ); -const AlertDetailsPage = withSuspenseFallback( +const AlertDetailsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/AlertDetailsPage/AlertDetailsPage')) ); -const AddObservabilityPage = withSuspenseFallback( +const AddObservabilityPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/AddObservabilityPage/AddObservabilityPage') ) ); -const MetricListPage = withSuspenseFallback( +const MetricListPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/MetricsPage/MetricListPage/MetricListPage') ) ); -const AddMetricPage = withSuspenseFallback( +const AddMetricPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/MetricsPage/AddMetricPage/AddMetricPage') ) ); -const ColumnBulkOperationsPage = withSuspenseFallback( +const ColumnBulkOperationsPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/ColumnBulkOperations/ColumnBulkOperations.component') diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedRoutes.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedRoutes.tsx index 9bb196dc01f9..e6727104c74b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedRoutes.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/AuthenticatedRoutes.tsx @@ -20,13 +20,13 @@ import { useApplicationStore } from '../../hooks/useApplicationStore'; import AppContainer from '../AppContainer/AppContainer'; import { useApplicationsProvider } from '../Settings/Applications/ApplicationsProvider/ApplicationsProvider'; import { RoutePosition } from '../Settings/Applications/plugins/AppPlugin'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const PageNotFound = withSuspenseFallback( +const PageNotFound = withPageSuspenseFallback( lazy(() => import('../../pages/PageNotFound/PageNotFound')) ); -const LogoutPage = withSuspenseFallback( +const LogoutPage = withPageSuspenseFallback( lazy(() => import('../../pages/LogoutPage/LogoutPage').then((module) => ({ default: module.LogoutPage, @@ -34,15 +34,15 @@ const LogoutPage = withSuspenseFallback( ) ); -const AccessNotAllowedPage = withSuspenseFallback( +const AccessNotAllowedPage = withPageSuspenseFallback( lazy(() => import('../../pages/AccessNotAllowedPage/AccessNotAllowedPage')) ); -const SignUpPage = withSuspenseFallback( +const SignUpPage = withPageSuspenseFallback( lazy(() => import('../../pages/SignUp/SignUpPage')) ); -const SamlCallback = withSuspenseFallback( +const SamlCallback = withPageSuspenseFallback( lazy(() => import('../../pages/SamlCallback')) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityImportRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityImportRouter.tsx index db0f485ee09d..7bc505522ae2 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityImportRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityImportRouter.tsx @@ -19,9 +19,9 @@ import { ResourceEntity } from '../../context/PermissionProvider/PermissionProvi import { useFqn } from '../../hooks/useFqn'; import { DEFAULT_ENTITY_PERMISSION } from '../../utils/PermissionsUtils'; import { useRequiredParams } from '../../utils/useRequiredParams'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const BulkEntityImportPage = withSuspenseFallback( +const BulkEntityImportPage = withPageSuspenseFallback( React.lazy( () => import( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityRouter.tsx index 16ef1a8ce6b3..d1fb8a423819 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/EntityRouter.tsx @@ -20,9 +20,9 @@ import { EntityType } from '../../enums/entity.enum'; import entityUtilClassBase from '../../utils/EntityUtilClassBase'; import { useRequiredParams } from '../../utils/useRequiredParams'; import EntityImportRouter from './EntityImportRouter'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const EntityVersionPage = withSuspenseFallback( +const EntityVersionPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/EntityVersionPage/EntityVersionPage.component') ) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx index 8e90371eaafa..71c901123b74 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/SettingsRouter.tsx @@ -30,19 +30,19 @@ import { getTeamsWithFqnPath, } from '../../utils/RouterUtils'; import AdminProtectedRoute from './AdminProtectedRoute'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const AddNotificationPage = withSuspenseFallback( +const AddNotificationPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/AddNotificationPage/AddNotificationPage') ) ); -const AlertDetailsPage = withSuspenseFallback( +const AlertDetailsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/AlertDetailsPage/AlertDetailsPage')) ); -const AppearanceConfigSettingsPage = withSuspenseFallback( +const AppearanceConfigSettingsPage = withPageSuspenseFallback( React.lazy( () => import( @@ -51,32 +51,32 @@ const AppearanceConfigSettingsPage = withSuspenseFallback( ) ); -const ApplicationPage = withSuspenseFallback( +const ApplicationPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/Application/ApplicationPage')) ); -const AuditLogsPage = withSuspenseFallback( +const AuditLogsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/AuditLogsPage/AuditLogsPage')) ); -const BotsPageV1 = withSuspenseFallback( +const BotsPageV1 = withPageSuspenseFallback( React.lazy(() => import('../../pages/BotsPageV1/BotsPageV1.component')) ); -const ColumnBulkOperations = withSuspenseFallback( +const ColumnBulkOperations = withPageSuspenseFallback( React.lazy( () => import('../../pages/ColumnBulkOperations/ColumnBulkOperations.component') ) ); -const DataAssetRulesPage = withSuspenseFallback( +const DataAssetRulesPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/Configuration/DataAssetRules/DataAssetRulesPage') ) ); -const EditLoginConfiguration = withSuspenseFallback( +const EditLoginConfiguration = withPageSuspenseFallback( React.lazy( () => import( @@ -85,7 +85,7 @@ const EditLoginConfiguration = withSuspenseFallback( ) ); -const EditUrlConfigurationPage = withSuspenseFallback( +const EditUrlConfigurationPage = withPageSuspenseFallback( React.lazy( () => import( @@ -94,7 +94,7 @@ const EditUrlConfigurationPage = withSuspenseFallback( ) ); -const LoginConfigurationPage = withSuspenseFallback( +const LoginConfigurationPage = withPageSuspenseFallback( React.lazy( () => import( @@ -103,27 +103,27 @@ const LoginConfigurationPage = withSuspenseFallback( ) ); -const UrlConfigurationPage = withSuspenseFallback( +const UrlConfigurationPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/Configuration/UrlConfiguration/UrlConfigurationPage') ) ); -const CustomPropertiesPageV1 = withSuspenseFallback( +const CustomPropertiesPageV1 = withPageSuspenseFallback( React.lazy( () => import('../../pages/CustomPropertiesPageV1/CustomPropertiesPageV1') ) ); -const EditEmailConfigPage = withSuspenseFallback( +const EditEmailConfigPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/EditEmailConfigPage/EditEmailConfigPage.component') ) ); -const EmailConfigSettingsPage = withSuspenseFallback( +const EmailConfigSettingsPage = withPageSuspenseFallback( React.lazy( () => import( @@ -132,7 +132,7 @@ const EmailConfigSettingsPage = withSuspenseFallback( ) ); -const GlobalSettingCategoryPage = withSuspenseFallback( +const GlobalSettingCategoryPage = withPageSuspenseFallback( React.lazy( () => import( @@ -141,11 +141,11 @@ const GlobalSettingCategoryPage = withSuspenseFallback( ) ); -const GlobalSettingPage = withSuspenseFallback( +const GlobalSettingPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/GlobalSettingPage/GlobalSettingPage')) ); -const GlossaryTermRelationSettingsPage = withSuspenseFallback( +const GlossaryTermRelationSettingsPage = withPageSuspenseFallback( React.lazy( () => import( @@ -154,7 +154,7 @@ const GlossaryTermRelationSettingsPage = withSuspenseFallback( ) ); -const LearningResourcesPage = withSuspenseFallback( +const LearningResourcesPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/LearningResourcesPage/LearningResourcesPage').then( (m) => ({ default: m.LearningResourcesPage }) @@ -162,25 +162,25 @@ const LearningResourcesPage = withSuspenseFallback( ) ); -const LineageConfigPage = withSuspenseFallback( +const LineageConfigPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/LineageConfigPage/LineageConfigPage')) ); -const NotificationListPage = withSuspenseFallback( +const NotificationListPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/NotificationListPage/NotificationListPage') ) ); -const OmHealthPage = withSuspenseFallback( +const OmHealthPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/OmHealth/OmHealthPage')) ); -const OnlineUsersPage = withSuspenseFallback( +const OnlineUsersPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/OnlineUsersPage/OnlineUsersPage')) ); -const PersonaDetailsPage = withSuspenseFallback( +const PersonaDetailsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/Persona/PersonaDetailsPage/PersonaDetailsPage').then( (m) => ({ default: m.PersonaDetailsPage }) @@ -188,7 +188,7 @@ const PersonaDetailsPage = withSuspenseFallback( ) ); -const PersonaPage = withSuspenseFallback( +const PersonaPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/Persona/PersonaListPage/PersonaPage').then((m) => ({ default: m.PersonaPage, @@ -196,97 +196,97 @@ const PersonaPage = withSuspenseFallback( ) ); -const AddPolicyPage = withSuspenseFallback( +const AddPolicyPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/PoliciesPage/AddPolicyPage/AddPolicyPage') ) ); -const AddRulePage = withSuspenseFallback( +const AddRulePage = withPageSuspenseFallback( React.lazy( () => import('../../pages/PoliciesPage/PoliciesDetailPage/AddRulePage') ) ); -const EditRulePage = withSuspenseFallback( +const EditRulePage = withPageSuspenseFallback( React.lazy( () => import('../../pages/PoliciesPage/PoliciesDetailPage/EditRulePage') ) ); -const PoliciesDetailPage = withSuspenseFallback( +const PoliciesDetailPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/PoliciesPage/PoliciesDetailPage/PoliciesDetailPage') ) ); -const PoliciesListPage = withSuspenseFallback( +const PoliciesListPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/PoliciesPage/PoliciesListPage/PoliciesListPage') ) ); -const ProfilerConfigurationPage = withSuspenseFallback( +const ProfilerConfigurationPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/ProfilerConfigurationPage/ProfilerConfigurationPage') ) ); -const AddRolePage = withSuspenseFallback( +const AddRolePage = withPageSuspenseFallback( React.lazy(() => import('../../pages/RolesPage/AddRolePage/AddRolePage')) ); -const RolesDetailPage = withSuspenseFallback( +const RolesDetailPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/RolesPage/RolesDetailPage/RolesDetailPage') ) ); -const RolesListPage = withSuspenseFallback( +const RolesListPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/RolesPage/RolesListPage/RolesListPage')) ); -const SearchSettingsPage = withSuspenseFallback( +const SearchSettingsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/SearchSettingsPage/SearchSettingsPage')) ); -const ServicesPage = withSuspenseFallback( +const ServicesPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/ServicesPage/ServicesPage')) ); -const IntakeFormsPage = withSuspenseFallback( +const IntakeFormsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/IntakeForms/IntakeFormsPage')) ); -const ImportTeamsPage = withSuspenseFallback( +const ImportTeamsPage = withPageSuspenseFallback( React.lazy( () => import('../../pages/TeamsPage/ImportTeamsPage/ImportTeamsPage') ) ); -const TeamsPage = withSuspenseFallback( +const TeamsPage = withPageSuspenseFallback( React.lazy(() => import('../../pages/TeamsPage/TeamsPage')) ); -const UserListPageV1 = withSuspenseFallback( +const UserListPageV1 = withPageSuspenseFallback( React.lazy(() => import('../../pages/UserListPage/UserListPageV1')) ); -const EntitySearchSettings = withSuspenseFallback( +const EntitySearchSettings = withPageSuspenseFallback( React.lazy( () => import('../SearchSettings/EntitySeachSettings/EntitySearchSettings') ) ); -const AppDetails = withSuspenseFallback( +const AppDetails = withPageSuspenseFallback( React.lazy( () => import('../Settings/Applications/AppDetails/AppDetails.component') ) ); -const AdminPermissionDebugger = withSuspenseFallback( +const AdminPermissionDebugger = withPageSuspenseFallback( React.lazy( () => import( @@ -295,7 +295,7 @@ const AdminPermissionDebugger = withSuspenseFallback( ) ); -const SettingsSso = withSuspenseFallback( +const SettingsSso = withPageSuspenseFallback( React.lazy(() => import('../SettingsSso/SettingsSso')) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/UnAuthenticatedAppRouter.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/UnAuthenticatedAppRouter.tsx index 1dc50b4ff6d1..9a82a524e315 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/UnAuthenticatedAppRouter.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/UnAuthenticatedAppRouter.tsx @@ -19,39 +19,39 @@ import { AuthProvider } from '../../generated/configuration/authenticationConfig import { useApplicationStore } from '../../hooks/useApplicationStore'; import useCustomLocation from '../../hooks/useCustomLocation/useCustomLocation'; import applicationRoutesClass from '../../utils/ApplicationRoutesClassBase'; -import withSuspenseFallback from './withSuspenseFallback'; +import { withPageSuspenseFallback } from './withSuspenseFallback'; -const SigninPage = withSuspenseFallback( +const SigninPage = withPageSuspenseFallback( lazy(() => import('../../pages/LoginPage/SignInPage')) ); -const ForgotPassword = withSuspenseFallback( +const ForgotPassword = withPageSuspenseFallback( lazy(() => import('../../pages/ForgotPassword/ForgotPassword.component')) ); -const ResetPassword = withSuspenseFallback( +const ResetPassword = withPageSuspenseFallback( lazy(() => import('../../pages/ResetPassword/ResetPassword.component')) ); -const BasicSignupPage = withSuspenseFallback( +const BasicSignupPage = withPageSuspenseFallback( lazy(() => import('../../pages/SignUp/BasicSignup.component')) ); -const PageNotFound = withSuspenseFallback( +const PageNotFound = withPageSuspenseFallback( lazy(() => import('../../pages/PageNotFound/PageNotFound')) ); -const AccountActivationConfirmation = withSuspenseFallback( +const AccountActivationConfirmation = withPageSuspenseFallback( lazy( () => import('../../pages/SignUp/account-activation-confirmation.component') ) ); -const Auth0Callback = withSuspenseFallback( +const Auth0Callback = withPageSuspenseFallback( lazy(() => import('../Auth/AppCallbacks/Auth0Callback/Auth0Callback')) ); -const LoginCallback = withSuspenseFallback( +const LoginCallback = withPageSuspenseFallback( lazy(() => import('@okta/okta-react').then((m) => ({ default: m.LoginCallback })) ) diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.test.tsx new file mode 100644 index 000000000000..ee04b6940d5c --- /dev/null +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.test.tsx @@ -0,0 +1,69 @@ +/* + * Copyright 2026 Collate. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { render, screen } from '@testing-library/react'; +import { lazy } from 'react'; +import { + withPageSuspenseFallback, + withSuspenseFallback, +} from './withSuspenseFallback'; + +describe('withSuspenseFallback', () => { + const getLazyComponent = () => + lazy( + () => + new Promise<{ default: () => JSX.Element }>((resolve) => { + setTimeout(() => { + resolve({ + default: () =>
Loaded component
, + }); + }, 0); + }) + ); + + it('does not render a default loading indicator while the chunk loads', () => { + const WrappedComponent = withSuspenseFallback(getLazyComponent()); + + render(); + + expect(screen.queryByTestId('loader')).not.toBeInTheDocument(); + }); + + it('does not render a loading indicator when the caller opts into a silent fallback', () => { + const WrappedComponent = withSuspenseFallback(getLazyComponent(), null); + + render(); + + expect(screen.queryByTestId('loader')).not.toBeInTheDocument(); + }); + + it('renders an explicit fallback when the caller provides one', async () => { + const WrappedComponent = withSuspenseFallback( + getLazyComponent(), +
Loading active route
+ ); + + render(); + + expect(screen.getByText('Loading active route')).toBeInTheDocument(); + expect(await screen.findByText('Loaded component')).toBeInTheDocument(); + }); + + it('renders the page loading indicator for route-level chunks', () => { + const WrappedComponent = withPageSuspenseFallback(getLazyComponent()); + + render(); + + expect(screen.getByTestId('loader')).toBeInTheDocument(); + }); +}); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx index 691eeaa37f04..4f021b5ac50b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/AppRouter/withSuspenseFallback.tsx @@ -11,24 +11,29 @@ * limitations under the License. */ -import { ComponentType, forwardRef, Suspense } from 'react'; +import { ComponentType, forwardRef, ReactNode, Suspense } from 'react'; import Loader from '../common/Loader/Loader'; +export const TAB_CONTENT_FALLBACK = ; + export function withSuspenseFallback( - Component: ComponentType + Component: ComponentType, + // Keep embedded/background lazy chunks silent unless a caller opts into visible progress. + fallback: ReactNode = null ) { return forwardRef(function DefaultFallback(props, ref) { return ( - - - - }> + ); }); } +export function withPageSuspenseFallback( + Component: ComponentType +) { + return withSuspenseFallback(Component, ); +} + export default withSuspenseFallback; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/LazyAuthProviderWrappers.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/LazyAuthProviderWrappers.tsx index 137a4b58e5b2..0249e9fb27df 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/LazyAuthProviderWrappers.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Auth/AuthProviders/LazyAuthProviderWrappers.tsx @@ -14,27 +14,28 @@ import type { CacheLocation } from '@auth0/auth0-react'; import type { IPublicClientApplication } from '@azure/msal-browser'; import { lazy, ReactNode } from 'react'; -import withSuspenseFallback from '../../AppRouter/withSuspenseFallback'; +import { withPageSuspenseFallback } from '../../AppRouter/withSuspenseFallback'; -const Auth0ProviderComponent = withSuspenseFallback( +const Auth0ProviderComponent = withPageSuspenseFallback( lazy(() => import('@auth0/auth0-react').then((m) => ({ default: m.Auth0Provider })) ) ); -const MsalProviderComponent = withSuspenseFallback( +const MsalProviderComponent = withPageSuspenseFallback( lazy(() => import('@azure/msal-react').then((m) => ({ default: m.MsalProvider })) ) ); -const OktaAuthProviderComponent = withSuspenseFallback( +const OktaAuthProviderComponent = withPageSuspenseFallback( lazy(() => import('./OktaAuthProvider').then((m) => ({ default: m.OktaAuthProvider })) ) ); -const BasicAuthProviderComponent = withSuspenseFallback( +// Auth providers wrap the app shell, so their cold chunks need a visible page fallback. +const BasicAuthProviderComponent = withPageSuspenseFallback( lazy(() => import('./BasicAuthProvider')) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/CommonWidgets/CommonWidgets.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/CommonWidgets/CommonWidgets.tsx index b3e6d58ec4a9..54f843493ff7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/CommonWidgets/CommonWidgets.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DataAssets/CommonWidgets/CommonWidgets.tsx @@ -51,7 +51,9 @@ import { import { getPrioritizedViewPermission } from '../../../utils/PermissionsUtils'; import { getTagsWithoutTier, getTierTags } from '../../../utils/TablePureUtils'; import { createTagObject } from '../../../utils/TagsPureUtils'; -import withSuspenseFallback from '../../AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../../AppRouter/withSuspenseFallback'; import CertificationWidget from '../../common/CertificationWidget/CertificationWidget'; import { CustomPropertyTable } from '../../common/CustomPropertyTable/CustomPropertyTable'; import DescriptionV1 from '../../common/EntityDescription/DescriptionV1'; @@ -65,12 +67,14 @@ import { DisplayType } from '../../Tag/TagsViewer/TagsViewer.interface'; import { DomainLabelV2 } from '../DomainLabelV2/DomainLabelV2'; import { OwnerLabelV2 } from '../OwnerLabelV2/OwnerLabelV2'; import { ReviewerLabelV2 } from '../ReviewerLabelV2/ReviewerLabelV2'; + const GlossaryUpdateConfirmationModal = withSuspenseFallback( lazy(() => import( '../../Glossary/GlossaryUpdateConfirmationModal/GlossaryUpdateConfirmationModal' ).then((m) => ({ default: m.GlossaryUpdateConfirmationModal })) - ) + ), + TAB_CONTENT_FALLBACK ); interface GenericEntity diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryDetails.test.tsx index a562568147a6..531a544a11df 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/Directory/DirectoryDetails.test.tsx @@ -79,9 +79,15 @@ jest.mock('../../common/CustomPropertyTable/CustomPropertyTable', () => ({ )), })); -jest.mock('../../common/Loader/Loader', () => - jest.fn(() =>
Loading...
) -); +jest.mock('../../common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loading...
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../Customization/GenericProvider/GenericProvider', () => ({ GenericProvider: jest.fn(({ children }) => ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileDetails.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileDetails.test.tsx index 6cdb07db1e12..5123b1e2b911 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileDetails.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/DriveService/File/FileDetails.test.tsx @@ -77,9 +77,15 @@ jest.mock('../../common/CustomPropertyTable/CustomPropertyTable', () => ({ )), })); -jest.mock('../../common/Loader/Loader', () => - jest.fn(() =>
Loading...
) -); +jest.mock('../../common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loading...
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../Customization/GenericProvider/GenericProvider', () => ({ GenericProvider: jest.fn(({ children }) => ( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx index 19bfcaf79c09..d29a43e261ba 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Entity/EntityHeader/EntityHeader.component.tsx @@ -12,16 +12,12 @@ */ import classNames from 'classnames'; -import { lazy, ReactNode } from 'react'; +import { ReactNode } from 'react'; import { EntityType } from '../../../enums/entity.enum'; import { getEntityLinkFromType } from '../../../utils/EntityLinkUtils'; -import withSuspenseFallback from '../../AppRouter/withSuspenseFallback'; import TitleBreadcrumb from '../../common/TitleBreadcrumb/TitleBreadcrumb.component'; import { TitleBreadcrumbProps } from '../../common/TitleBreadcrumb/TitleBreadcrumb.interface'; - -const EntityHeaderTitle = withSuspenseFallback( - lazy(() => import('../EntityHeaderTitle/EntityHeaderTitle.component')) -); +import EntityHeaderTitle from '../EntityHeaderTitle/EntityHeaderTitle.component'; interface Props { breadcrumb: TitleBreadcrumbProps['titleLinks']; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/DataProductsWidget/DataProductsWidget.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/DataProductsWidget/DataProductsWidget.component.tsx index f21fabe23039..c6e7ae953785 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/DataProductsWidget/DataProductsWidget.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/MyData/Widgets/DataProductsWidget/DataProductsWidget.component.tsx @@ -218,7 +218,7 @@ const DataProductsWidget = ({ ), - [dataProducts, isFullSize] + [assetsCounts, dataProducts, handleDataProductClick, isFullSize] ); const showWidgetFooterMoreButton = useMemo( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/Execution/Execution.component.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/Execution/Execution.component.tsx index 7177da707110..c563ee07bee5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/Execution/Execution.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Pipeline/Execution/Execution.component.tsx @@ -57,7 +57,7 @@ const ExecutionsTab = ({ pipelineFQN, tasks }: ExecutionProps) => { const [endTime, setEndTime] = useState(getCurrentMillis()); const [isClickedCalendar, setIsClickedCalendar] = useState(false); const [status, setStatus] = useState(MenuOptions.all); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const fetchPipelineStatus = async (startRange: number, endRange: number) => { try { diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx index d080890f79af..fcbf608b7e14 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Settings/Applications/ApplicationsProvider/ApplicationsProvider.tsx @@ -101,7 +101,7 @@ export const ApplicationsProvider = ({ children }: { children: ReactNode }) => { return ( - {loading ? : children} + {loading ? : children} ); }; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicSchema/TopicSchema.tsx b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicSchema/TopicSchema.tsx index 7407a38f1a53..e29569ba9982 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicSchema/TopicSchema.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/Topic/TopicSchema/TopicSchema.tsx @@ -59,7 +59,9 @@ import { } from '../../../utils/TablePureUtils'; import { getAllTags } from '../../../utils/TableTags/TableTags.utils'; import { getTableExpandableConfig } from '../../../utils/TableUtils'; -import withSuspenseFallback from '../../AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../../AppRouter/withSuspenseFallback'; import CopyLinkButton from '../../common/CopyLinkButton/CopyLinkButton'; import { EntityAttachmentProvider } from '../../common/EntityDescription/EntityAttachmentProvider/EntityAttachmentProvider'; import ErrorPlaceHolder from '../../common/ErrorWithPlaceholder/ErrorPlaceHolder'; @@ -76,7 +78,8 @@ import { } from './TopicSchema.interface'; const SchemaEditor = withSuspenseFallback( - lazy(() => import('../../Database/SchemaEditor/SchemaEditor')) + lazy(() => import('../../Database/SchemaEditor/SchemaEditor')), + TAB_CONTENT_FALLBACK ); const ModalWithMarkdownEditor = withSuspenseFallback( diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/Loader/Loader.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/Loader/Loader.tsx index 778c8372a205..d2e5d2652b38 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/Loader/Loader.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/Loader/Loader.tsx @@ -83,4 +83,12 @@ const Loader: FunctionComponent = ({ ); }; +// Use for blocking page-level loads only, so route and initial entity loaders +// stay in the same position instead of jumping between bare spinner layouts. +export const PageLoader = () => ( +
+ +
+); + export default Loader; diff --git a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/TagChip/TagChip.test.tsx b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/TagChip/TagChip.test.tsx index bc96ca165fd4..434eb2fb386b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/TagChip/TagChip.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/components/common/atoms/TagChip/TagChip.test.tsx @@ -13,6 +13,9 @@ import { render, screen } from '@testing-library/react'; import TagChip from './TagChip'; +// The global test setup stubs MUI styling; this suite needs real sx-generated CSS. +jest.unmock('@mui/styled-engine'); + jest.mock('react-i18next', () => ({ useTranslation: () => ({ t: (key: string) => key }), })); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.test.tsx index 9f90476ebef0..486b2158498c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.test.tsx @@ -91,9 +91,11 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => jest.fn().mockImplementation(() =>
ErrorPlaceHolder
) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() =>
Loader
), + PageLoader: jest.fn().mockImplementation(() =>
Loader
), +})); jest.mock('../../components/AppRouter/withActivityFeed', () => ({ withActivityFeed: jest.fn().mockImplementation((Component) => Component), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx index 1fca09c30d12..db1ab75fe4df 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/APICollectionPage/APICollectionPage.tsx @@ -27,7 +27,7 @@ import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../components/common/IconButtons/EditIconButton'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { GenericProvider } from '../../components/Customization/GenericProvider/GenericProvider'; import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; @@ -499,7 +499,7 @@ const APICollectionPage: FunctionComponent = () => { [tabs[0], tab] ); if (isPermissionsLoading || isLoading) { - return ; + return ; } if (!viewAPICollectionPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/APIEndpointPage/APIEndpointPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/APIEndpointPage/APIEndpointPage.tsx index 5d8ba2c4b139..b518204b1b2b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/APIEndpointPage/APIEndpointPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/APIEndpointPage/APIEndpointPage.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import APIEndpointDetails from '../../components/APIEndpoint/APIEndpointDetails/APIEndpointDetails'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import { ROUTES } from '../../constants/constants'; @@ -264,7 +264,7 @@ const APIEndpointPage = () => { }, [apiEndpointPermissions, apiEndpointFqn]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/AddServicePage/AddServicePage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/AddServicePage/AddServicePage.component.tsx index 3c77154f0fb2..0188bc9b6967 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/AddServicePage/AddServicePage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/AddServicePage/AddServicePage.component.tsx @@ -15,7 +15,7 @@ import { Space, Typography } from 'antd'; import { AxiosError } from 'axios'; import { isEmpty } from 'lodash'; import { LoadingState } from 'Models'; -import { useEffect, useMemo, useState } from 'react'; +import { Suspense, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ResizablePanels from '../../components/common/ResizablePanels/ResizablePanels'; @@ -335,11 +335,13 @@ const AddServicePage = () => { pageTitle={t('label.add-entity', { entity: t('label.service') })} secondPanel={{ children: ( - + + + ), className: 'service-doc-panel content-resizable-panel-container', minWidth: 400, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ChartDetailsPage/ChartDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ChartDetailsPage/ChartDetailsPage.component.tsx index 6282e3f28908..9d944f2a1763 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ChartDetailsPage/ChartDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ChartDetailsPage/ChartDetailsPage.component.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ChartDetails from '../../components/Chart/ChartDetails/ChartDetails.component'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import { ROUTES } from '../../constants/constants'; @@ -253,7 +253,7 @@ const ChartDetailsPage = () => { }, [chartFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.test.tsx index 81cef1ac08f1..83096a554523 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ContainerPage/ContainerPage.test.tsx @@ -172,9 +172,15 @@ jest.mock('../../context/LineageProvider/LineageProvider', () => jest.fn().mockReturnValue(<>LineageProvider) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockReturnValue(
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/PageLayoutV1/PageLayoutV1', () => jest.fn().mockImplementation(({ children }) => <>{children}) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx index c693cf587497..e4125966f297 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DashboardDetailsPage/DashboardDetailsPage.component.tsx @@ -18,7 +18,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import DashboardDetails from '../../components/Dashboard/DashboardDetails/DashboardDetails.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; @@ -277,7 +277,7 @@ const DashboardDetailsPage = () => { }, [dashboardFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx index 7717361ac90f..0ab5c8ec02d9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.component.tsx @@ -18,7 +18,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import DataModelDetails from '../../components/Dashboard/DataModel/DataModels/DataModelDetails.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; @@ -292,7 +292,7 @@ const DataModelsPage = () => { // Rendering if (isLoading) { - return ; + return ; } if (hasError) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.test.tsx index 4e0036a4ee35..e8fa0ca24661 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DataModelPage/DataModelPage.test.tsx @@ -96,9 +96,15 @@ jest.mock( ) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../context/PermissionProvider/PermissionProvider', () => ({ usePermissionProvider: jest.fn().mockImplementation(() => ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx index ff78ab2763a6..c3719dbcdbd9 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseDetailsPage/DatabaseDetailsPage.tsx @@ -28,7 +28,7 @@ import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../components/common/IconButtons/EditIconButton'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { GenericProvider } from '../../components/Customization/GenericProvider/GenericProvider'; import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; @@ -583,7 +583,7 @@ const DatabaseDetails: FunctionComponent = () => { ); if (isLoading || isDatabaseDetailsLoading || loading) { - return ; + return ; } if (!hasViewBasicPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx index 8b0699cf7cdd..d59b2a352fd5 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.component.tsx @@ -27,7 +27,7 @@ import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../components/common/IconButtons/EditIconButton'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { GenericProvider } from '../../components/Customization/GenericProvider/GenericProvider'; import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; @@ -642,7 +642,7 @@ const DatabaseSchemaPage: FunctionComponent = () => { }, [isFollowing, unFollowSchema, followSchema]); if (isPermissionsLoading) { - return ; + return ; } if (!viewDatabaseSchemaPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx index c608cb4dfbfa..a545d8aa3f42 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DatabaseSchemaPage/DatabaseSchemaPage.test.tsx @@ -157,9 +157,15 @@ jest.mock('../../utils/ToastUtils', () => ({ .mockImplementation(({ children }) =>
{children}
), })); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
testLoader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => jest.fn().mockImplementation(() =>

ErrorPlaceHolder

) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.test.tsx index 3fe4f332bf69..bb1fc97de578 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.test.tsx @@ -212,9 +212,15 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => ) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/AppRouter/withActivityFeed', () => ({ withActivityFeed: jest.fn().mockImplementation((Component) => Component), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.tsx index 26da40aafe64..258d26fc6dbf 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/DirectoryDetailsPage/DirectoryDetailsPage.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import DirectoryDetails from '../../components/DriveService/Directory/DirectoryDetails'; @@ -283,7 +283,7 @@ const DirectoryDetailsPage = () => { }, [directoryPermissions, directoryFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/EditConnectionFormPage/EditConnectionFormPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/EditConnectionFormPage/EditConnectionFormPage.component.tsx index 0651738845c2..25fd6362898e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/EditConnectionFormPage/EditConnectionFormPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/EditConnectionFormPage/EditConnectionFormPage.component.tsx @@ -16,7 +16,7 @@ import { AxiosError } from 'axios'; import { compare } from 'fast-json-patch'; import { isEmpty, isUndefined, startCase } from 'lodash'; import { LoadingState, ServicesUpdateRequest, ServiceTypes } from 'Models'; -import { useEffect, useMemo, useState } from 'react'; +import { Suspense, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; @@ -293,11 +293,13 @@ function EditConnectionFormPage() { pageTitle={t('label.edit-entity', { entity: t('label.connection') })} secondPanel={{ children: ( - + + + ), className: 'service-doc-panel content-resizable-panel-container', minWidth: 400, diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.test.tsx index fd74dc71dc52..ad9b512a18fc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.test.tsx @@ -204,9 +204,15 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => ) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/AppRouter/withActivityFeed', () => ({ withActivityFeed: jest.fn().mockImplementation((Component) => Component), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.tsx index dffaee366875..a3e094f19139 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/FileDetailsPage/FileDetailsPage.tsx @@ -18,7 +18,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import FileDetails from '../../components/DriveService/File/FileDetails'; @@ -260,7 +260,7 @@ function FileDetailsPage() { }, [filePermissions, fileFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx index 5a8e50a702a8..cbc7faced7c3 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.test.tsx @@ -129,9 +129,15 @@ jest.mock('../../../components/PageLayoutV1/PageLayoutV1', () =>
{children}
)) ); -jest.mock('../../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock( '../../../components/DataQuality/IncidentManager/IncidentManagerPageHeader/IncidentManagerPageHeader.component', () => jest.fn().mockImplementation(() =>
IncidentManagerPageHeader
) diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx index be0e8d6488fd..28dc9a8ddc1f 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/IncidentManager/IncidentManagerDetailPage/IncidentManagerDetailPage.tsx @@ -28,7 +28,7 @@ import { BetaBadge } from '../../../components/common/Badge/Badge.component'; import ManageButton from '../../../components/common/EntityPageInfos/ManageButton/ManageButton'; import ErrorPlaceHolder from '../../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../../components/common/IconButtons/EditIconButton'; -import Loader from '../../../components/common/Loader/Loader'; +import { PageLoader } from '../../../components/common/Loader/Loader'; import { ManageButtonItemLabel } from '../../../components/common/ManageButtonContentItem/ManageButtonContentItem.component'; import TitleBreadcrumb from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.component'; import { TitleBreadcrumbProps } from '../../../components/common/TitleBreadcrumb/TitleBreadcrumb.interface'; @@ -412,7 +412,7 @@ const IncidentManagerDetailPage = ({ }, [t, hasEditPermission, isVersionPage, testCase?.entityLink]); if (isLoading || isPermissionLoading) { - return ; + return ; } if (!hasViewPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricDetailsPage/MetricDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricDetailsPage/MetricDetailsPage.tsx index 783eb9ce5f61..72017f95cf1c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricDetailsPage/MetricDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/MetricsPage/MetricDetailsPage/MetricDetailsPage.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../../components/common/Loader/Loader'; +import { PageLoader } from '../../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../../components/Database/TableQueries/TableQueries.interface'; import MetricDetails from '../../../components/Metric/MetricDetails/MetricDetails'; @@ -268,7 +268,7 @@ const MetricDetailsPage = () => { }, [metricPermissions, metricFqn]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/MlModelPage/MlModelPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/MlModelPage/MlModelPage.component.tsx index 945df303a0d2..a478895e053c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/MlModelPage/MlModelPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/MlModelPage/MlModelPage.component.tsx @@ -18,7 +18,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import MlModelDetailComponent from '../../components/MlModel/MlModelDetail/MlModelDetail.component'; @@ -296,7 +296,7 @@ const MlModelPage = () => { }, [mlModelFqn]); if (isDetailLoading) { - return ; + return ; } if (isNil(mlModelDetail) || isEmpty(mlModelDetail)) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx index fc9406dde8be..d452e16a0eea 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/PipelineDetails/PipelineDetailsPage.component.tsx @@ -18,7 +18,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import PipelineDetails from '../../components/Pipeline/PipelineDetails/PipelineDetails.component'; @@ -342,7 +342,7 @@ const PipelineDetailsPage = () => { }, [decodedPipelineFQN]); if (isLoading) { - return ; + return ; } if (isError) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.test.tsx index 5ae487ba0a6b..6f488d3bc921 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.test.tsx @@ -115,9 +115,13 @@ jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockImplementation(() => ({ pathname: 'mockPath' })), })); -jest.mock('../../components/common/Loader/Loader', () => { - return jest.fn().mockImplementation(() => <>testLoader); -}); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => <>testLoader), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('./SearchIndexFieldsTab/SearchIndexFieldsTab', () => { return jest.fn().mockImplementation(() =>

testSearchIndexFieldsTab

); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx index d6e3d4b56db8..2fe4a425483c 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexDetailsPage.tsx @@ -22,7 +22,7 @@ import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../components/common/IconButtons/EditIconButton'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { GenericProvider } from '../../components/Customization/GenericProvider/GenericProvider'; import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; @@ -568,7 +568,7 @@ function SearchIndexDetailsPage() { [tabs[0], activeTab] ); if (isLoading || loading) { - return ; + return ; } if (!viewPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexFieldsTab/SearchIndexFieldsTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexFieldsTab/SearchIndexFieldsTab.tsx index 25a2c2016439..1c8f0bc43a72 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexFieldsTab/SearchIndexFieldsTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SearchIndexDetailsPage/SearchIndexFieldsTab/SearchIndexFieldsTab.tsx @@ -12,7 +12,9 @@ */ import { lazy, useCallback, useMemo } from 'react'; -import withSuspenseFallback from '../../../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../../../components/AppRouter/withSuspenseFallback'; import { useGenericContext } from '../../../components/Customization/GenericProvider/GenericContext'; import { SearchIndex, @@ -22,7 +24,8 @@ import { useFqn } from '../../../hooks/useFqn'; import { getAllRowKeysByKeyName } from '../../../utils/TablePureUtils'; const SearchIndexFieldsTable = withSuspenseFallback( - lazy(() => import('../SearchIndexFieldsTable/SearchIndexFieldsTable')) + lazy(() => import('../SearchIndexFieldsTable/SearchIndexFieldsTable')), + TAB_CONTENT_FALLBACK ); function SearchIndexFieldsTab() { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.test.tsx index 37d74985dcab..2ab6c111a054 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.test.tsx @@ -439,9 +439,15 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => )) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); // Additional missing component mocks jest.mock( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx index c6e484223f73..269f0fad48af 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/ServiceDetailsPage/ServiceDetailsPage.tsx @@ -33,7 +33,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import AirflowMessageBanner from '../../components/common/AirflowMessageBanner/AirflowMessageBanner'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { PagingHandlerParams } from '../../components/common/NextPrevious/NextPrevious.interface'; import TabsLabel from '../../components/common/TabsLabel/TabsLabel.component'; import TestConnection from '../../components/common/TestConnection/TestConnection'; @@ -1944,7 +1944,7 @@ const ServiceDetailsPage: FunctionComponent = () => { }, []); if (isLoading) { - return ; + return ; } if (!(servicePermission.ViewAll || servicePermission.ViewBasic)) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.test.tsx index 81428722667a..ed257db04abc 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.test.tsx @@ -211,9 +211,15 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => ) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/AppRouter/withActivityFeed', () => ({ withActivityFeed: jest.fn().mockImplementation((Component) => Component), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.tsx index cee4b9711d93..7b5045a43c16 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/SpreadsheetDetailsPage/SpreadsheetDetailsPage.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import SpreadsheetDetails from '../../components/DriveService/Spreadsheet/SpreadsheetDetails'; @@ -287,7 +287,7 @@ const SpreadsheetDetailsPage = () => { }, [spreadsheetPermissions, spreadsheetFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.test.tsx index 59a96a08e214..a0bb15189975 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.test.tsx @@ -135,9 +135,13 @@ jest.mock('react-router-dom', () => ({ useLocation: jest.fn().mockImplementation(() => ({ pathname: 'mockPath' })), })); -jest.mock('../../components/common/Loader/Loader', () => { - return jest.fn().mockImplementation(() => <>testLoader); -}); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => <>testLoader), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../hoc/LimitWrapper', () => { return jest.fn().mockImplementation(({ children }) =>

{children}

); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx index b606f04b9079..fae4946f7bf0 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedurePage.tsx @@ -19,7 +19,7 @@ import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; import { AlignRightIconButton } from '../../components/common/IconButtons/EditIconButton'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { GenericProvider } from '../../components/Customization/GenericProvider/GenericProvider'; import { DataAssetsHeader } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.component'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; @@ -562,7 +562,7 @@ const StoredProcedurePage = () => { }, [decodedStoredProcedureFQN, storedProcedurePermissions]); if (isLoading || loading) { - return ; + return ; } if (!viewBasicPermission) { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx index f1c21d59c1b4..f12f8e3cf46e 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/StoredProcedure/StoredProcedureTab.tsx @@ -57,7 +57,7 @@ const StoredProcedureTab = () => { } = usePaging(); const [storedProcedure, setStoredProcedure] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const [isLoading, setIsLoading] = useState(true); const { fqn: decodedDatabaseSchemaFQN } = useFqn(); useEffect(() => { diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx index d0c75308c9b7..8e1963e73ce7 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TableDetailsPageV1/TableDetailsPageV1.test.tsx @@ -300,9 +300,13 @@ jest.mock('../../context/TourProvider/TourProvider', () => ({ })), })); -jest.mock('../../components/common/Loader/Loader', () => { - return jest.fn().mockImplementation(() => <>testLoader); -}); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest.fn().mockImplementation(() => <>testLoader), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.useFakeTimers(); diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx index 12af41ed90e9..4b3631701206 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/TopicDetails/TopicDetailsPage.component.tsx @@ -18,7 +18,7 @@ import { FunctionComponent, useCallback, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import TopicDetails from '../../components/Topic/TopicDetails/TopicDetails.component'; @@ -262,7 +262,7 @@ const TopicDetailsPage: FunctionComponent = () => { }, [topicPermissions, topicFQN]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.test.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.test.tsx index 9ef016d8c8a6..e1724223f0ee 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.test.tsx @@ -215,9 +215,15 @@ jest.mock('../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder', () => ) ); -jest.mock('../../components/common/Loader/Loader', () => - jest.fn().mockImplementation(() =>
Loader
) -); +jest.mock('../../components/common/Loader/Loader', () => ({ + __esModule: true, + default: jest + .fn() + .mockImplementation(() =>
Loader
), + PageLoader: jest + .fn() + .mockImplementation(() =>
Loader
), +})); jest.mock('../../components/AppRouter/withActivityFeed', () => ({ withActivityFeed: jest.fn().mockImplementation((Component) => Component), diff --git a/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.tsx b/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.tsx index a80493ba8a5d..bbfccd284940 100644 --- a/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/pages/WorksheetDetailsPage/WorksheetDetailsPage.tsx @@ -19,7 +19,7 @@ import { useTranslation } from 'react-i18next'; import { useNavigate } from 'react-router-dom'; import { withActivityFeed } from '../../components/AppRouter/withActivityFeed'; import ErrorPlaceHolder from '../../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../../components/common/Loader/Loader'; +import { PageLoader } from '../../components/common/Loader/Loader'; import { DataAssetWithDomains } from '../../components/DataAssets/DataAssetsHeader/DataAssetsHeader.interface'; import { QueryVote } from '../../components/Database/TableQueries/TableQueries.interface'; import WorksheetDetails from '../../components/DriveService/Worksheet/WorksheetDetails'; @@ -324,7 +324,7 @@ const WorksheetDetailsPage = () => { }, [decodedWorksheetFQN, resolvedEntityFqn]); if (isLoading) { - return ; + return ; } if (isError) { return ( diff --git a/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/withSuspenseFallback.mock.tsx b/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/withSuspenseFallback.mock.tsx index d3c5cf1832e9..f509454accb6 100644 --- a/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/withSuspenseFallback.mock.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/test/unit/mocks/withSuspenseFallback.mock.tsx @@ -30,4 +30,8 @@ export function withSuspenseFallback( }); } +// Route-level chunks use a visible fallback in production, but unit tests keep +// the mock silent to avoid unrelated loader assertions across router tests. +export const withPageSuspenseFallback = withSuspenseFallback; + export default withSuspenseFallback; diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts index 39db2517477e..7823f79a64e1 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.test.ts @@ -18,6 +18,10 @@ import applicationRoutesClassBase, { ApplicationRoutesClassBase, } from './ApplicationRoutesClassBase'; +jest.mock('../components/AppRouter/UnAuthenticatedAppRouter', () => ({ + UnAuthenticatedAppRouter: jest.fn(), +})); + jest.mock('../components/AppRouter/AuthenticatedAppRouter', () => ({ __esModule: true, default: function AuthenticatedAppRouter() { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts index cf8250ab69d6..86a54705c607 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/ApplicationRoutesClassBase.ts @@ -13,10 +13,10 @@ import { FC, lazy } from 'react'; import { UnAuthenticatedAppRouter } from '../components/AppRouter/UnAuthenticatedAppRouter'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import { withPageSuspenseFallback } from '../components/AppRouter/withSuspenseFallback'; import { UNPROTECTED_ROUTES } from '../constants/router.constants'; -const AuthenticatedAppRouter = withSuspenseFallback( +const AuthenticatedAppRouter = withPageSuspenseFallback( lazy(() => import('../components/AppRouter/AuthenticatedAppRouter')) ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.test.ts b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.test.ts index 4f1b5cabb1c7..da3d18d88993 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.test.ts +++ b/openmetadata-ui/src/main/resources/ui/src/utils/AuthProvider.util.test.ts @@ -19,7 +19,6 @@ import { import { AuthProvider } from '../generated/settings/settings'; import { getAuthConfig, - getCandidateUserManagerConfig, getUserManagerConfig, } from './AuthProvider.util'; @@ -127,13 +126,3 @@ describe('getAuthConfig — every OIDC provider respects the server-provided res expect(config.responseType).toBe('code'); }); }); - -describe('getCandidateUserManagerConfig — SSO test-login popup respects responseType', () => { - it('should use the configured response_type instead of a hardcoded "id_token"', () => { - const config = getCandidateUserManagerConfig( - withScope({ responseType: ResponseType.Code }) - ); - - expect(config.response_type).toBe('code'); - }); -}); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.test.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.test.tsx index 3f3fa721365e..1417e6407787 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.test.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.test.tsx @@ -10,6 +10,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { render, screen } from '@testing-library/react'; +import { lazy } from 'react'; import { mockWidget } from '../mocks/AddWidgetTabContent.mock'; import { mockCurrentAddWidget } from '../mocks/CustomizablePage.mock'; import { @@ -22,8 +24,95 @@ import { getUniqueFilteredLayout, getWidgetWidthLabelFromKey, } from './CustomizableLandingPagePureUtils'; +import { getWidgetFromKey } from './CustomizableLandingPageUtils'; +import customizeMyDataPageClassBase from './CustomizeMyDataPageClassBase'; + +jest.mock( + '../components/MyData/Widgets/Common/WidgetWrapper/WidgetWrapper', + () => ({ + __esModule: true, + default: jest.fn().mockImplementation(({ children, loading }) => ( +
+ {children} +
+ )), + }) +); describe('CustomizableLandingPageUtils', () => { + describe('getWidgetFromKey', () => { + // Keep the lazy component pending so each assertion observes the Suspense fallback. + const PendingWidget = lazy(() => new Promise(() => undefined)); + + beforeEach(() => { + jest + .spyOn(customizeMyDataPageClassBase, 'getWidgetsFromKey') + .mockReturnValue(PendingWidget); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it('should render normal widget chunks without a per-widget loader', () => { + const { container } = render( + getWidgetFromKey({ + widgetConfig: { + h: 3, + i: 'KnowledgePanel.ActivityFeed', + static: false, + w: 1, + x: 0, + y: 0, + }, + }) + ); + + expect(screen.queryByTestId('widget-wrapper')).not.toBeInTheDocument(); + expect(container).toBeEmptyDOMElement(); + }); + + it('should preserve widget slots while edit-mode chunks load', () => { + render( + getWidgetFromKey({ + isEditView: true, + widgetConfig: { + h: 3, + i: 'KnowledgePanel.ActivityFeed', + static: false, + w: 1, + x: 0, + y: 0, + }, + }) + ); + + expect(screen.getByTestId('widget-wrapper')).toHaveAttribute( + 'data-loading', + 'true' + ); + }); + + it('should render empty placeholders immediately', () => { + render( + getWidgetFromKey({ + handleOpenAddWidgetModal: jest.fn(), + handlePlaceholderWidgetKey: jest.fn(), + widgetConfig: { + h: 3, + i: 'ExtraWidget.EmptyWidgetPlaceholder', + static: false, + w: 1, + x: 0, + y: 0, + }, + }) + ); + + expect(screen.getByTestId('add-widget-button')).toBeInTheDocument(); + }); + }); + describe('getNewWidgetPlacement', () => { it('should place widget in same row if space available', () => { const currentLayout = [ diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx index d0d43add46a5..d5c56f06b861 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/CustomizableLandingPageUtils.tsx @@ -60,9 +60,14 @@ export const getWidgetFromKey = ({ } const Widget = customizeMyDataPageClassBase.getWidgetsFromKey(widgetConfig.i); + // Normal My Data keeps lazy widget chunks silent; edit mode preserves grid slots + // because disappearing cells are more disruptive while arranging widgets. + const fallback = isEditView ? ( + {null} + ) : null; return ( - {null}}> + ({ default: module.InputOutputPortsTab, })) - ) + ), + TAB_CONTENT_FALLBACK ); const ResizablePanels = withSuspenseFallback( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaTabsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaTabsUtils.tsx index 08a95ce9ce67..44ed78be3c48 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaTabsUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DatabaseSchemaTabsUtils.tsx @@ -13,7 +13,9 @@ import { get } from 'lodash'; import { lazy } from 'react'; import { ActivityFeedLayoutType } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import type { CustomPropertyProps, ExtentionEntitiesKeys, @@ -56,11 +58,13 @@ const ContractTab = withSuspenseFallback( ); const SchemaTablesTab = withSuspenseFallback( - lazy(() => import('../pages/DatabaseSchemaPage/SchemaTablesTab')) + lazy(() => import('../pages/DatabaseSchemaPage/SchemaTablesTab')), + TAB_CONTENT_FALLBACK ); const StoredProcedureTab = withSuspenseFallback( - lazy(() => import('../pages/StoredProcedure/StoredProcedureTab')) + lazy(() => import('../pages/StoredProcedure/StoredProcedureTab')), + TAB_CONTENT_FALLBACK ); export const getDataBaseSchemaPageBaseTabs = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/DirectoryDetailsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/DirectoryDetailsUtils.tsx index 80196b165f8e..590cad07504d 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/DirectoryDetailsUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/DirectoryDetailsUtils.tsx @@ -13,7 +13,9 @@ import { get } from 'lodash'; import { lazy } from 'react'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import TabsLabel from '../components/common/TabsLabel/TabsLabel.component'; import { GenericTab } from '../components/Customization/GenericTab/GenericTab'; import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidgets'; @@ -37,7 +39,8 @@ const DirectoryChildrenTable = withSuspenseFallback( import( '../components/DriveService/Directory/DirectoryChildrenTable/DirectoryChildrenTable' ) - ) + ), + TAB_CONTENT_FALLBACK ); export interface DirectoryDetailPageTabProps { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDetailComponentUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDetailComponentUtils.tsx index 04418d7de24c..80095688f8ca 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/EntityDetailComponentUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/EntityDetailComponentUtils.tsx @@ -12,6 +12,7 @@ */ import { ComponentType, FC, lazy, LazyExoticComponent, Suspense } from 'react'; +import Loader from '../components/common/Loader/Loader'; import { EntityType } from '../enums/entity.enum'; const lazyComponentMap: Partial< @@ -90,7 +91,7 @@ export function getEntityDetailComponent(entityType: string): FC | null { } const WrappedComponent: FC = (props) => ( - + }> ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.tsx index 91f2d7d270f5..2cd4a4e15a41 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/FileDetailsUtils.tsx @@ -13,7 +13,9 @@ import { get, isEmpty } from 'lodash'; import { lazy } from 'react'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import TabsLabel from '../components/common/TabsLabel/TabsLabel.component'; import { GenericTab } from '../components/Customization/GenericTab/GenericTab'; import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidgets'; @@ -37,7 +39,8 @@ const FileColumnsTable = withSuspenseFallback( import( '../components/DriveService/File/FileColumnsTable/FileColumnsTable' ) - ) + ), + TAB_CONTENT_FALLBACK ); export interface FileDetailPageTabProps { diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/LazyTagComponents.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/LazyTagComponents.tsx index 08bc13c24728..968e3aadad69 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/LazyTagComponents.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/LazyTagComponents.tsx @@ -12,6 +12,7 @@ */ import { lazy, Suspense } from 'react'; +import { TAB_CONTENT_FALLBACK } from '../components/AppRouter/withSuspenseFallback'; import { DomainLabelProps } from '../components/common/DomainLabel/DomainLabel.interface'; import { OwnerLabelV2Props } from '../components/DataAssets/OwnerLabelV2/OwnerLabelV2'; import { EntityType } from '../enums/entity.enum'; @@ -42,7 +43,7 @@ interface LazyCommonWidgetsProps { } export const LazyCommonWidgets = (props: LazyCommonWidgetsProps) => ( - + ); diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.tsx index 12e4ad76d392..17622137cc55 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/PipelineDetailsUtils.tsx @@ -17,7 +17,9 @@ import { ReactComponent as IconFailBadge } from '../assets/svg/fail-badge.svg'; import { ReactComponent as IconSkippedBadge } from '../assets/svg/skipped-badge.svg'; import { ReactComponent as IconSuccessBadge } from '../assets/svg/success-badge.svg'; import { ActivityFeedLayoutType } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import type { CustomPropertyProps, ExtentionEntitiesKeys, @@ -75,7 +77,8 @@ const EntityLineageTab = withSuspenseFallback( ); const ExecutionsTab = withSuspenseFallback( - lazy(() => import('../components/Pipeline/Execution/Execution.component')) + lazy(() => import('../components/Pipeline/Execution/Execution.component')), + TAB_CONTENT_FALLBACK ); const PipelineTaskTab = withSuspenseFallback( diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx index d9cad5bdffe5..25ad9920a06b 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/SearchIndexUtils.tsx @@ -14,13 +14,14 @@ import { get, uniqueId } from 'lodash'; import { lazy, Suspense } from 'react'; import { ActivityFeedLayoutType } from '../components/ActivityFeed/ActivityFeedTab/ActivityFeedTab.interface'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import type { CustomPropertyProps, ExtentionEntitiesKeys, } from '../components/common/CustomPropertyTable/CustomPropertyTable.interface'; import ErrorPlaceHolder from '../components/common/ErrorWithPlaceholder/ErrorPlaceHolder'; -import Loader from '../components/common/Loader/Loader'; import TabsLabel from '../components/common/TabsLabel/TabsLabel.component'; import { GenericTab } from '../components/Customization/GenericTab/GenericTab'; import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidgets'; @@ -63,7 +64,8 @@ const ContractTab = withSuspenseFallback( ) ); const QueryViewer = withSuspenseFallback( - lazy(() => import('../components/common/QueryViewer/QueryViewer.component')) + lazy(() => import('../components/common/QueryViewer/QueryViewer.component')), + TAB_CONTENT_FALLBACK ); const SampleDataWithMessages = withSuspenseFallback( lazy( @@ -71,7 +73,8 @@ const SampleDataWithMessages = withSuspenseFallback( import( '../components/Database/SampleDataWithMessages/SampleDataWithMessages' ) - ) + ), + TAB_CONTENT_FALLBACK ); const SearchIndexFieldsTab = withSuspenseFallback( lazy( @@ -79,7 +82,8 @@ const SearchIndexFieldsTab = withSuspenseFallback( import( '../pages/SearchIndexDetailsPage/SearchIndexFieldsTab/SearchIndexFieldsTab' ) - ) + ), + TAB_CONTENT_FALLBACK ); const EntityLineageTab = lazy(() => import('../components/Lineage/EntityLineageTab/EntityLineageTab').then( @@ -186,7 +190,7 @@ export const getSearchIndexDetailsTabs = ({ ), key: EntityTabs.LINEAGE, children: ( - }> + ({ default: module.ActivityFeedTab })) - ) + ), + TAB_CONTENT_FALLBACK ); const ErrorPlaceHolder = withSuspenseFallback( lazy( () => import('../components/common/ErrorWithPlaceholder/ErrorPlaceHolder') - ) + ), + TAB_CONTENT_FALLBACK ); const GenericTab = withSuspenseFallback( @@ -56,7 +59,8 @@ const GenericTab = withSuspenseFallback( import('../components/Customization/GenericTab/GenericTab').then( (module) => ({ default: module.GenericTab }) ) - ) + ), + TAB_CONTENT_FALLBACK ); const CommonWidgets = withSuspenseFallback( @@ -64,7 +68,8 @@ const CommonWidgets = withSuspenseFallback( import('../components/DataAssets/CommonWidgets/CommonWidgets').then( (module) => ({ default: module.CommonWidgets }) ) - ) + ), + TAB_CONTENT_FALLBACK ); const CustomPropertyTable = withSuspenseFallback( @@ -72,24 +77,30 @@ const CustomPropertyTable = withSuspenseFallback( import('../components/common/CustomPropertyTable/CustomPropertyTable').then( (module) => ({ default: module.CustomPropertyTable }) ) - ) + ), + TAB_CONTENT_FALLBACK ) as ( props: CustomPropertyProps ) => JSX.Element; const SchemaTable = withSuspenseFallback( - lazy(() => import('../components/Database/SchemaTable/SchemaTable.component')) + lazy( + () => import('../components/Database/SchemaTable/SchemaTable.component') + ), + TAB_CONTENT_FALLBACK ); const SampleDataTableComponent = withSuspenseFallback( lazy( () => import('../components/Database/SampleDataTable/SampleDataTable.component') - ) + ), + TAB_CONTENT_FALLBACK ); const TableQueries = withSuspenseFallback( - lazy(() => import('../components/Database/TableQueries/TableQueries')) + lazy(() => import('../components/Database/TableQueries/TableQueries')), + TAB_CONTENT_FALLBACK ); const ContractTab = withSuspenseFallback( @@ -97,7 +108,8 @@ const ContractTab = withSuspenseFallback( import('../components/DataContract/ContractTab/ContractTab').then( (module) => ({ default: module.ContractTab }) ) - ) + ), + TAB_CONTENT_FALLBACK ); const DataObservabilityTab = withSuspenseFallback( @@ -106,7 +118,8 @@ const DataObservabilityTab = withSuspenseFallback( import( '../components/Database/Profiler/DataObservability/DataObservabilityTab' ) - ) + ), + TAB_CONTENT_FALLBACK ); const EntityLineageTab = withSuspenseFallback( @@ -114,22 +127,26 @@ const EntityLineageTab = withSuspenseFallback( import('../components/Lineage/EntityLineageTab/EntityLineageTab').then( (module) => ({ default: module.EntityLineageTab }) ) - ) + ), + TAB_CONTENT_FALLBACK ); const TableConstraints = withSuspenseFallback( lazy( () => import('../pages/TableDetailsPageV1/TableConstraints/TableConstraints') - ) + ), + TAB_CONTENT_FALLBACK ); const KnowledgeGraph = withSuspenseFallback( - lazy(() => import('../components/KnowledgeGraph/KnowledgeGraph')) + lazy(() => import('../components/KnowledgeGraph/KnowledgeGraph')), + TAB_CONTENT_FALLBACK ); const QueryViewer = withSuspenseFallback( - lazy(() => import('../components/common/QueryViewer/QueryViewer.component')) + lazy(() => import('../components/common/QueryViewer/QueryViewer.component')), + TAB_CONTENT_FALLBACK ); const FrequentlyJoinedTables = withSuspenseFallback( @@ -137,7 +154,8 @@ const FrequentlyJoinedTables = withSuspenseFallback( import( '../pages/TableDetailsPageV1/FrequentlyJoinedTables/FrequentlyJoinedTables.component' ).then((module) => ({ default: module.FrequentlyJoinedTables })) - ) + ), + TAB_CONTENT_FALLBACK ); const PartitionedKeys = withSuspenseFallback( @@ -145,7 +163,8 @@ const PartitionedKeys = withSuspenseFallback( import( '../pages/TableDetailsPageV1/PartitionedKeys/PartitionedKeys.component' ).then((module) => ({ default: module.PartitionedKeys })) - ) + ), + TAB_CONTENT_FALLBACK ); export const getTableDetailPageBaseTabs = ({ @@ -295,7 +314,7 @@ export const getTableDetailPageBaseTabs = ({ ), key: EntityTabs.LINEAGE, children: ( - }> + }> + ({ default: module.GenericTab }) ) - ) + ), + TAB_CONTENT_FALLBACK ); const CommonWidgets = withSuspenseFallback( @@ -59,7 +62,8 @@ const ContractTab = withSuspenseFallback( ) ); const TopicSchemaFields = withSuspenseFallback( - lazy(() => import('../components/Topic/TopicSchema/TopicSchema')) + lazy(() => import('../components/Topic/TopicSchema/TopicSchema')), + TAB_CONTENT_FALLBACK ); export const getTopicDetailsPageTabs = ({ diff --git a/openmetadata-ui/src/main/resources/ui/src/utils/WorksheetDetailsUtils.tsx b/openmetadata-ui/src/main/resources/ui/src/utils/WorksheetDetailsUtils.tsx index 12f35efb7f37..fb3b121c65ef 100644 --- a/openmetadata-ui/src/main/resources/ui/src/utils/WorksheetDetailsUtils.tsx +++ b/openmetadata-ui/src/main/resources/ui/src/utils/WorksheetDetailsUtils.tsx @@ -13,7 +13,9 @@ import { get } from 'lodash'; import { lazy } from 'react'; -import withSuspenseFallback from '../components/AppRouter/withSuspenseFallback'; +import withSuspenseFallback, { + TAB_CONTENT_FALLBACK, +} from '../components/AppRouter/withSuspenseFallback'; import TabsLabel from '../components/common/TabsLabel/TabsLabel.component'; import { GenericTab } from '../components/Customization/GenericTab/GenericTab'; import { CommonWidgets } from '../components/DataAssets/CommonWidgets/CommonWidgets'; @@ -35,7 +37,8 @@ const WorksheetColumnsTable = withSuspenseFallback( import( '../components/DriveService/Worksheet/WorksheetColumnsTable/WorksheetColumnsTable' ) - ) + ), + TAB_CONTENT_FALLBACK ); export interface WorksheetDetailPageTabProps {