Skip to content
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
2b01c81
refactor: implement code splitting for pages and optimize manual chun…
chirag-madlani Mar 31, 2026
e134644
handle chunk loading error gracefully
chirag-madlani Apr 1, 2026
3c6a6ad
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 1, 2026
d9f1e8a
address comments
chirag-madlani Apr 1, 2026
a889d04
fix lint comment
chirag-madlani Apr 1, 2026
3a6bfd2
address copilot comments
chirag-madlani Apr 1, 2026
515db48
fix chunk and initial load time
chirag-madlani Apr 1, 2026
9b0ee89
fix theme issues
chirag-madlani Apr 2, 2026
df8ba10
fix packaging
chirag-madlani Apr 3, 2026
bfa39d1
update imports
chirag-madlani Apr 5, 2026
cd6ac88
improve imports
chirag-madlani Apr 6, 2026
bdbfcaa
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 7, 2026
fcb911a
run checkstyle
chirag-madlani Apr 7, 2026
6874953
fix build
chirag-madlani Apr 7, 2026
f384934
fix css imports
chirag-madlani Apr 7, 2026
0b78f06
update
chirag-madlani Apr 7, 2026
73904cb
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 8, 2026
0ec3718
improve imports
chirag-madlani Apr 8, 2026
69c53c5
lazy load local options
chirag-madlani Apr 8, 2026
2a60599
refactor entityUtils
chirag-madlani Apr 9, 2026
2de630f
update
chirag-madlani Apr 11, 2026
1414cec
update
chirag-madlani Apr 11, 2026
203e3e0
fix failures
chirag-madlani Apr 13, 2026
52333ac
apply checkstyle
chirag-madlani Apr 13, 2026
a12427a
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 13, 2026
f35d8e6
fix build issue
chirag-madlani Apr 13, 2026
269a59d
address comments
chirag-madlani Apr 13, 2026
af65a6a
fix import error
chirag-madlani Apr 13, 2026
84daf1e
fix import error
chirag-madlani Apr 13, 2026
7fce04e
fix tests
chirag-madlani Apr 13, 2026
b089ef6
fix tests
chirag-madlani Apr 14, 2026
9ee338e
move local to classBase approach
chirag-madlani Apr 14, 2026
c276740
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 14, 2026
8d15f9e
fix tests and update local usage
chirag-madlani Apr 14, 2026
3aab0f5
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 15, 2026
61adb1a
fix import error
chirag-madlani Apr 15, 2026
2cd98fb
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 15, 2026
1ee853a
fix unit tests
chirag-madlani Apr 15, 2026
368ab82
fix entityUtils imports
chirag-madlani Apr 15, 2026
0b8c957
fix test and checkstyle
chirag-madlani Apr 15, 2026
9a18131
fix failing playwright
chirag-madlani Apr 15, 2026
11b2b24
fix plugin issue
chirag-madlani Apr 15, 2026
7dfc9c1
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 15, 2026
1518df3
address comments
chirag-madlani Apr 15, 2026
c685ef9
address comments
chirag-madlani Apr 15, 2026
2747ce8
fix unit tests
chirag-madlani Apr 16, 2026
99e2576
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 16, 2026
b11ef67
address comment
chirag-madlani Apr 16, 2026
f05a560
fix localization issue with playwright
chirag-madlani Apr 16, 2026
6a551ff
fix login.spec
chirag-madlani Apr 16, 2026
61d790e
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 17, 2026
d66719b
fix login spec failure
chirag-madlani Apr 17, 2026
1d4be7f
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 17, 2026
92cfc32
fix login spec
chirag-madlani Apr 17, 2026
eff7401
address comments
chirag-madlani Apr 17, 2026
a6f63a6
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 17, 2026
9707eba
final fix to icons
chirag-madlani Apr 17, 2026
b697ab8
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 17, 2026
15ee0ad
Merge branch 'main' into enahncement-loading-time
chirag-madlani Apr 18, 2026
bc47919
fix failing playwrights
chirag-madlani Apr 18, 2026
179c8bd
fix checkstyle
chirag-madlani Apr 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions openmetadata-ui/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@
<!--arguments>build</arguments-->
<environmentVariables>
<NODE_OPTIONS>--max-old-space-size=${node.heap.size}</NODE_OPTIONS>
<APP_VERSION>${project.version}</APP_VERSION>
</environmentVariables>
Comment on lines 152 to 156
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The PR description says this “Fixes #3457” (replying on deleted entity threads), but the changes here are build/routing performance tweaks (APP_VERSION env var for cache busting) and don’t appear related to that backend/UI behavior. Please update the PR title/description to reference the correct issue, or link the relevant issue for these frontend build optimizations.

Copilot uses AI. Check for mistakes.
</configuration>
</execution>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { clickOutside, redirectToHomePage } from '../../utils/common';
import {
followEntity,
validateFollowedEntityToWidget,
waitForAllLoadersToDisappear,
} from '../../utils/entity';

const user = new UserClass();
Expand Down Expand Up @@ -52,6 +53,7 @@ test.describe('Verify RTL Layout for landing page', () => {
.locator('.ant-dropdown:visible [data-menu-id*="-he-HE"]')
.click();
await page.waitForLoadState('domcontentloaded');
await waitForAllLoadersToDisappear(page);
});

test('Verify DataAssets widget functionality', async ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ import { JWT_EXPIRY_TIME_MAP, LOGIN_ERROR_MESSAGE } from '../../constant/login';
import { AdminClass } from '../../support/user/AdminClass';
import { UserClass } from '../../support/user/UserClass';
import { performAdminLogin } from '../../utils/admin';
import { clickOutside, redirectToHomePage } from '../../utils/common';
import {
clickOutside,
getDefaultAdminAPIContext,
redirectToHomePage,
visitOwnProfilePage,
} from '../../utils/common';
import { waitForAllLoadersToDisappear } from '../../utils/entity';
import { updateJWTTokenExpiryTime } from '../../utils/login';
import { visitUserProfilePage } from '../../utils/user';

const user = new UserClass();
const CREDENTIALS = user.data;
Expand Down Expand Up @@ -149,36 +154,56 @@ test.describe('Login flow should work properly', () => {
await page.locator('[data-testid="go-back-button"]').click();
});

test('Refresh should work', async ({ browser }) => {
const browserContext = await browser.newContext();
const { apiContext, afterAction } = await performAdminLogin(browser);
const page1 = await browserContext.newPage(),
page2 = await browserContext.newPage();
test('Refresh should work', async ({ page: page1, browser }) => {
test.slow();

const { apiContext, afterAction } = await getDefaultAdminAPIContext(
browser
);
const context = page1.context();
const page2 = await context.newPage();

const testUser = new UserClass();
await testUser.create(apiContext);
await testUser.setAdminRole(apiContext);

await test.step('Login and wait for refresh call is made', async () => {
// User login

await testUser.login(page1);
await redirectToHomePage(page1);
await waitForAllLoadersToDisappear(page1);
await redirectToHomePage(page2);
await waitForAllLoadersToDisappear(page2);
await page2.reload();

// eslint-disable-next-line playwright/no-wait-for-timeout -- wait for token refresh timer to fire
await page1.waitForTimeout(3 * 60 * 1000);

await redirectToHomePage(page1);
await page1.bringToFront();
await visitOwnProfilePage(page1);
await waitForAllLoadersToDisappear(page1);
await expect(page1.getByTestId('user-display-name')).toHaveText(
testUser.responseData.displayName ?? testUser.responseData.name
);

await visitUserProfilePage(page1, testUser.responseData.name);
await redirectToHomePage(page2);
await visitUserProfilePage(page2, testUser.responseData.name);
await page2.bringToFront();
await page2.evaluate(() => {
document.dispatchEvent(
new Event('visibilitychange', { bubbles: true })
);
});

await visitOwnProfilePage(page2);
await waitForAllLoadersToDisappear(page2);
await expect(page2.getByTestId('user-display-name')).toHaveText(
testUser.responseData.displayName ?? testUser.responseData.name
);

await page1.close();
await page2.close();
});

await browserContext.close();
await afterAction();
});

Expand Down
72 changes: 50 additions & 22 deletions openmetadata-ui/src/main/resources/ui/src/App.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,58 @@
*/

import { render } from '@testing-library/react';
import React from 'react';
import App from './App';
import { AuthProvider } from './components/Auth/AuthProviders/AuthProvider';
import AppRouter from './components/AppRouter/AppRouter';

jest.mock('./components/AppRouter/AppRouter', () => {
return jest.fn().mockReturnValue(<p>AppRouter</p>);
});
const mockAuthProvider = jest.fn();

jest.mock('./components/Auth/AuthProviders/AuthProvider', () => {
return {
AuthProvider: jest
.fn()
.mockImplementation(({ children }) => <>{children}</>),
AuthContext: {
Provider: jest.fn().mockImplementation(({ children }) => <>{children}</>),
},
};
});
jest.mock('./components/AppRouter/AppRouter', () => ({
__esModule: true,
default: function AppRouter() {
return React.createElement(
'div',
{ 'data-testid': 'app-router' },
'AppRouter'
);
},
}));

jest.mock('./components/Auth/AuthProviders/AuthProvider', () => ({
AuthProvider: function AuthProvider({
children,
childComponentType,
}: {
children: React.ReactNode;
childComponentType: React.ComponentType;
}) {
mockAuthProvider({ childComponentType });

return React.createElement(
'div',
{ 'data-testid': 'auth-provider' },
children
);
},
}));

describe('App', () => {
beforeEach(() => {
jest.clearAllMocks();
});

it('should render AuthProvider wrapping AppRouter', () => {
const { getByTestId } = render(React.createElement(App));

expect(getByTestId('auth-provider')).toBeInTheDocument();
expect(getByTestId('app-router')).toBeInTheDocument();
});

it('should pass AppRouter as childComponentType to AuthProvider', () => {
render(React.createElement(App));

it('renders learn react link', () => {
const { getAllByTestId } = render(
<AuthProvider childComponentType={App}>
<App />
</AuthProvider>
);
const linkElement = getAllByTestId(/content-wrapper/i);
linkElement.map((elm) => expect(elm).toBeInTheDocument());
expect(mockAuthProvider).toHaveBeenCalledWith({
childComponentType: AppRouter,
});
});
});
164 changes: 4 additions & 160 deletions openmetadata-ui/src/main/resources/ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,171 +11,15 @@
* limitations under the License.
*/

import { isEmpty } from 'lodash';
import { FC, ReactNode, useEffect, useMemo } from 'react';
import { RouterProvider } from 'react-aria-components';
import { HelmetProvider } from 'react-helmet-async';
import { I18nextProvider } from 'react-i18next';
import { BrowserRouter, useNavigate } from 'react-router-dom';
import { useShallow } from 'zustand/react/shallow';
import { FC } from 'react';
import AppRouter from './components/AppRouter/AppRouter';
import { AuthProvider } from './components/Auth/AuthProviders/AuthProvider';
import ErrorBoundary from './components/common/ErrorBoundary/ErrorBoundary';
import { EntityExportModalProvider } from './components/Entity/EntityExportModalProvider/EntityExportModalProvider.component';
import ApplicationsProvider from './components/Settings/Applications/ApplicationsProvider/ApplicationsProvider';
import WebAnalyticsProvider from './components/WebAnalytics/WebAnalyticsProvider';
import AirflowStatusProvider from './context/AirflowStatusProvider/AirflowStatusProvider';
import AntDConfigProvider from './context/AntDConfigProvider/AntDConfigProvider';
import AsyncDeleteProvider from './context/AsyncDeleteProvider/AsyncDeleteProvider';
import PermissionProvider from './context/PermissionProvider/PermissionProvider';
import TourProvider from './context/TourProvider/TourProvider';
import WebSocketProvider from './context/WebSocketProvider/WebSocketProvider';
import { useApplicationStore } from './hooks/useApplicationStore';
import {
getCustomUiThemePreference,
getSystemConfig,
} from './rest/settingConfigAPI';
import { getBasePath } from './utils/HistoryUtils';

import GlobalStyles from '@mui/material/GlobalStyles';
import { ThemeProvider } from '@mui/material/styles';
import {
createMuiTheme,
SnackbarContent,
} from '@openmetadata/ui-core-components';
import { SnackbarProvider } from 'notistack';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DEFAULT_THEME } from './constants/Appearance.constants';
import RuleEnforcementProvider from './context/RuleEnforcementProvider/RuleEnforcementProvider';
import { ThemeProvider as UntitledUIThemeProvider } from './context/UntitledUIThemeProvider/theme-provider';
import i18n from './utils/i18next/LocalUtil';
import { getThemeConfig } from './utils/ThemeUtils';

const ReactAriaRouterBridge = ({ children }: { children: ReactNode }) => {
const navigate = useNavigate();

return <RouterProvider navigate={navigate}>{children}</RouterProvider>;
};

const App: FC = () => {
const { applicationConfig, setApplicationConfig, setRdfEnabled } =
useApplicationStore(
useShallow((state) => ({
applicationConfig: state.applicationConfig,
setApplicationConfig: state.setApplicationConfig,
setRdfEnabled: state.setRdfEnabled,
}))
);

// Create dynamic MUI theme based on user customizations
const muiTheme = useMemo(
() => createMuiTheme(applicationConfig?.customTheme, DEFAULT_THEME),
[applicationConfig?.customTheme]
);

const fetchApplicationConfig = async () => {
try {
const [themeData, systemConfig] = await Promise.all([
getCustomUiThemePreference(),
getSystemConfig(),
]);

setApplicationConfig({
...themeData,
customTheme: getThemeConfig(themeData.customTheme),
});

// Set RDF enabled state
setRdfEnabled(systemConfig.rdfEnabled || false);
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
}
};

useEffect(() => {
fetchApplicationConfig();
}, []);

useEffect(() => {
const faviconHref = isEmpty(
applicationConfig?.customLogoConfig?.customFaviconUrlPath
)
? '/favicon.png'
: applicationConfig?.customLogoConfig?.customFaviconUrlPath ??
'/favicon.png';
const link = document.querySelectorAll('link[rel~="icon"]');

if (!isEmpty(link)) {
link.forEach((item) => {
item.setAttribute('href', faviconHref);
});
}
}, [applicationConfig]);

return (
<div className="main-container">
<div className="content-wrapper" data-testid="content-wrapper">
<BrowserRouter basename={getBasePath()}>
<ReactAriaRouterBridge>
<I18nextProvider i18n={i18n}>
<HelmetProvider>
<ErrorBoundary>
<AntDConfigProvider>
<UntitledUIThemeProvider
brandColors={applicationConfig?.customTheme}>
<ThemeProvider theme={muiTheme}>
<GlobalStyles styles={{ html: { fontSize: '14px' } }} />
<SnackbarProvider
Components={{
default: SnackbarContent,
error: SnackbarContent,
success: SnackbarContent,
warning: SnackbarContent,
info: SnackbarContent,
}}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
autoHideDuration={6000}
maxSnack={3}>
<AuthProvider childComponentType={AppRouter}>
<TourProvider>
<WebAnalyticsProvider>
<PermissionProvider>
<WebSocketProvider>
<ApplicationsProvider>
<AsyncDeleteProvider>
<EntityExportModalProvider>
<AirflowStatusProvider>
<RuleEnforcementProvider>
<DndProvider
backend={HTML5Backend}>
<AppRouter />
</DndProvider>
</RuleEnforcementProvider>
</AirflowStatusProvider>
</EntityExportModalProvider>
</AsyncDeleteProvider>
</ApplicationsProvider>
</WebSocketProvider>
</PermissionProvider>
</WebAnalyticsProvider>
</TourProvider>
</AuthProvider>
</SnackbarProvider>
</ThemeProvider>
</UntitledUIThemeProvider>
</AntDConfigProvider>
</ErrorBoundary>
</HelmetProvider>
</I18nextProvider>
</ReactAriaRouterBridge>
</BrowserRouter>
</div>
</div>
<AuthProvider childComponentType={AppRouter}>
<AppRouter />
</AuthProvider>
Comment thread
chirag-madlani marked this conversation as resolved.
);
};

Expand Down
Loading
Loading