Skip to content

Commit c4d4613

Browse files
authored
feat(frontend): Create theme provider (#936)
Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> fix UI loading bug Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> rename ThemeProvider, remove conditional rendering for upstream Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> remove unused imports Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> delete ThemeContext.ts and move logic to ThemeContext.tsx Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> reapply conditionals Signed-off-by: Jenny <32821331+jenny-s51@users.noreply.github.com> revert to isMUITheme
1 parent aa6d15e commit c4d4613

21 files changed

Lines changed: 133 additions & 80 deletions

clients/ui/frontend/src/app/App.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
} from '@patternfly/react-core';
1515
import ToastNotifications from '~/shared/components/ToastNotifications';
1616
import { useSettings } from '~/shared/hooks/useSettings';
17-
import { isMUITheme, Theme, AUTH_HEADER, MOCK_AUTH, isStandalone } from '~/shared/utilities/const';
17+
import { AUTH_HEADER, MOCK_AUTH, isStandalone } from '~/shared/utilities/const';
1818
import { logout } from '~/shared/utilities/appUtils';
1919
import { NamespaceSelectorContext } from '~/shared/context/NamespaceSelectorContext';
2020
import NavSidebar from './NavSidebar';
@@ -36,15 +36,6 @@ const App: React.FC = () => {
3636

3737
const username = userSettings?.userId;
3838

39-
React.useEffect(() => {
40-
// Apply the theme based on the value of STYLE_THEME
41-
if (isMUITheme()) {
42-
document.documentElement.classList.add(Theme.MUI);
43-
} else {
44-
document.documentElement.classList.remove(Theme.MUI);
45-
}
46-
}, []);
47-
4839
React.useEffect(() => {
4940
if (MOCK_AUTH && username) {
5041
localStorage.setItem(AUTH_HEADER, username);

clients/ui/frontend/src/app/NavBar.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import {
2121
import { SimpleSelect } from '@patternfly/react-templates';
2222
import { BarsIcon } from '@patternfly/react-icons';
2323
import { NamespaceSelectorContext } from '~/shared/context/NamespaceSelectorContext';
24-
import { isMUITheme } from '~/shared/utilities/const';
2524
import logoDarkTheme from '~/images/logo-dark-theme.svg';
25+
import { useThemeContext } from './ThemeContext';
2626

2727
interface NavBarProps {
2828
username?: string;
@@ -32,6 +32,7 @@ interface NavBarProps {
3232
const NavBar: React.FC<NavBarProps> = ({ username, onLogout }) => {
3333
const { namespaces, preferredNamespace, updatePreferredNamespace } =
3434
React.useContext(NamespaceSelectorContext);
35+
const { isMUITheme } = useThemeContext();
3536

3637
const [userMenuOpen, setUserMenuOpen] = React.useState(false);
3738

@@ -60,7 +61,7 @@ const NavBar: React.FC<NavBarProps> = ({ username, onLogout }) => {
6061
<BarsIcon />
6162
</PageToggleButton>
6263
</MastheadToggle>
63-
{!isMUITheme() ? (
64+
{!isMUITheme ? (
6465
<MastheadBrand>
6566
<MastheadLogo component="a">
6667
<Brand src={logoDarkTheme} alt="Kubeflow" heights={{ default: '36px' }} />

clients/ui/frontend/src/app/NavSidebar.tsx

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
PageSidebar,
1010
PageSidebarBody,
1111
} from '@patternfly/react-core';
12-
import { isMUITheme, LOGO_LIGHT } from '~/shared/utilities/const';
12+
import { LOGO_LIGHT } from '~/shared/utilities/const';
1313
import { useNavData, isNavDataGroup, NavDataHref, NavDataGroup } from './AppRoutes';
1414

1515
const NavHref: React.FC<{ item: NavDataHref }> = ({ item }) => (
@@ -48,17 +48,13 @@ const NavSidebar: React.FC = () => {
4848
<PageSidebarBody>
4949
<Nav id="nav-primary-simple">
5050
<NavList id="nav-list-simple">
51-
{isMUITheme() ? (
52-
<NavItem>
53-
<Brand
54-
className="kubeflow_brand"
55-
src={`${window.location.origin}/images/${LOGO_LIGHT}`}
56-
alt="Kubeflow Logo"
57-
/>
58-
</NavItem>
59-
) : (
60-
''
61-
)}
51+
<NavItem>
52+
<Brand
53+
className="kubeflow_brand"
54+
src={`${window.location.origin}/images/${LOGO_LIGHT}`}
55+
alt="Kubeflow Logo"
56+
/>
57+
</NavItem>
6258
{navData.map((item) =>
6359
isNavDataGroup(item) ? (
6460
<NavGroup key={item.label} item={item} />
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import * as React from 'react';
2+
import { createTheme } from '@mui/material';
3+
import { ThemeProvider as MUIThemeProvider } from '@mui/material/styles';
4+
import { isMUITheme, Theme } from '~/shared/utilities/const';
5+
6+
type ThemeProviderProps = {
7+
children: React.ReactNode;
8+
};
9+
10+
type ThemeContextProps = {
11+
isMUITheme: boolean;
12+
};
13+
14+
export const ThemeContext = React.createContext({
15+
isMUITheme: false,
16+
});
17+
18+
export const useThemeContext = (): ThemeContextProps => React.useContext(ThemeContext);
19+
20+
const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => {
21+
const themeValue = React.useMemo(() => ({ isMUITheme: isMUITheme() }), []);
22+
const createMUITheme = React.useMemo(() => createTheme({ cssVariables: true }), []);
23+
24+
React.useEffect(() => {
25+
// Apply the theme based on the value of STYLE_THEME
26+
if (isMUITheme()) {
27+
document.documentElement.classList.add(Theme.MUI);
28+
} else {
29+
document.documentElement.classList.remove(Theme.MUI);
30+
}
31+
}, []);
32+
33+
return (
34+
<ThemeContext.Provider value={themeValue}>
35+
{isMUITheme() ? (
36+
<MUIThemeProvider theme={createMUITheme}>{children}</MUIThemeProvider>
37+
) : (
38+
children
39+
)}
40+
</ThemeContext.Provider>
41+
);
42+
};
43+
44+
export default ThemeProvider;

clients/ui/frontend/src/app/pages/modelRegistry/ModelRegistryCoreLoader.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import { ProjectObjectType, typedEmptyImage } from '~/shared/components/design/u
77
import { ModelRegistryContextProvider } from '~/app/context/ModelRegistryContext';
88
import TitleWithIcon from '~/shared/components/design/TitleWithIcon';
99
import WhosMyAdministrator from '~/shared/components/WhosMyAdministrator';
10-
import { isMUITheme } from '~/shared/utilities/const';
1110
import KubeflowDocs from '~/shared/components/KubeflowDocs';
11+
import { useThemeContext } from '~/app/ThemeContext';
1212
import EmptyModelRegistryState from './screens/components/EmptyModelRegistryState';
1313
import InvalidModelRegistry from './screens/InvalidModelRegistry';
1414
import ModelRegistrySelectorNavigator from './screens/ModelRegistrySelectorNavigator';
@@ -36,6 +36,7 @@ const ModelRegistryCoreLoader: React.FC<ModelRegistryCoreLoaderProps> = ({
3636
preferredModelRegistry,
3737
updatePreferredModelRegistry,
3838
} = React.useContext(ModelRegistrySelectorContext);
39+
const { isMUITheme } = useThemeContext();
3940

4041
const modelRegistryFromRoute = modelRegistries.find((mr) => mr.name === modelRegistry);
4142

@@ -65,16 +66,16 @@ const ModelRegistryCoreLoader: React.FC<ModelRegistryCoreLoaderProps> = ({
6566
emptyStatePage: (
6667
<EmptyModelRegistryState
6768
testid="empty-model-registries-state"
68-
title={isMUITheme() ? 'Deploy a model registry' : 'Request access to model registries'}
69+
title={isMUITheme ? 'Deploy a model registry' : 'Request access to model registries'}
6970
description={
70-
isMUITheme()
71+
isMUITheme
7172
? 'To deploy a new model registry, follow the instructions in the docs below.'
7273
: 'To request a new model registry, or to request permission to access an existing model registry, contact your administrator.'
7374
}
7475
headerIcon={() => (
7576
<img src={typedEmptyImage(ProjectObjectType.registeredModels)} alt="" />
7677
)}
77-
customAction={isMUITheme() ? <KubeflowDocs /> : <WhosMyAdministrator />}
78+
customAction={isMUITheme ? <KubeflowDocs /> : <WhosMyAdministrator />}
7879
/>
7980
),
8081
headerContent: null,
@@ -103,14 +104,14 @@ const ModelRegistryCoreLoader: React.FC<ModelRegistryCoreLoaderProps> = ({
103104
return (
104105
<ApplicationsPage
105106
title={
106-
!isMUITheme() ? (
107+
!isMUITheme ? (
107108
<TitleWithIcon title="Model Registry" objectType={ProjectObjectType.registeredModels} />
108109
) : (
109110
'Model Registry'
110111
)
111112
}
112113
description={
113-
!isMUITheme() ? (
114+
!isMUITheme ? (
114115
'Select a model registry to view and manage your registered models. Model registries provide a structured and organized way to store, share, version, deploy, and track models.'
115116
) : (
116117
<Divider />

clients/ui/frontend/src/app/pages/modelRegistry/screens/ModelPropertiesTableRow.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { CheckIcon, ExternalLinkAltIcon, TimesIcon } from '@patternfly/react-ico
1515
import { KeyValuePair } from '~/shared/types';
1616
import { EitherNotBoth } from '~/shared/typeHelpers';
1717
import FormFieldset from '~/app/pages/modelRegistry/screens/components/FormFieldset';
18-
import { isMUITheme } from '~/shared/utilities/const';
18+
import { useThemeContext } from '~/app/ThemeContext';
1919
import { isValidHttpUrl } from './utils';
2020

2121
type ModelPropertiesTableRowProps = {
@@ -47,6 +47,8 @@ const ModelPropertiesTableRow: React.FC<ModelPropertiesTableRowProps> = ({
4747
saveEditedProperty,
4848
}) => {
4949
const { key, value } = keyValuePair;
50+
const { isMUITheme } = useThemeContext();
51+
5052
const [unsavedKey, setUnsavedKey] = React.useState(key);
5153
const [unsavedValue, setUnsavedValue] = React.useState(value);
5254

@@ -128,7 +130,7 @@ const ModelPropertiesTableRow: React.FC<ModelPropertiesTableRowProps> = ({
128130
<Td dataLabel="Key" width={45} modifier="breakWord">
129131
{isEditing ? (
130132
<>
131-
{isMUITheme() ? (
133+
{isMUITheme ? (
132134
<FormFieldset className="tr-fieldset-wrapper" component={propertyKeyInput} />
133135
) : (
134136
propertyKeyInput
@@ -148,7 +150,7 @@ const ModelPropertiesTableRow: React.FC<ModelPropertiesTableRowProps> = ({
148150
</Td>
149151
<Td dataLabel="Value" width={45} modifier="breakWord">
150152
{isEditing ? (
151-
isMUITheme() ? (
153+
isMUITheme ? (
152154
<FormFieldset className="tr-fieldset-wrapper" component={propertyValueInput} />
153155
) : (
154156
propertyValueInput

clients/ui/frontend/src/app/pages/modelRegistry/screens/ModelRegistry.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import { Divider } from '@patternfly/react-core';
33
import ApplicationsPage from '~/shared/components/ApplicationsPage';
44
import useRegisteredModels from '~/app/hooks/useRegisteredModels';
55
import useModelVersions from '~/app/hooks/useModelVersions';
6-
import { isMUITheme } from '~/shared/utilities/const';
76
import TitleWithIcon from '~/shared/components/design/TitleWithIcon';
87
import { ProjectObjectType } from '~/shared/components/design/utils';
8+
import { useThemeContext } from '~/app/ThemeContext';
99
import ModelRegistrySelectorNavigator from './ModelRegistrySelectorNavigator';
1010
import RegisteredModelListView from './RegisteredModels/RegisteredModelListView';
1111
import { modelRegistryUrl } from './routeUtils';
@@ -25,6 +25,7 @@ const ModelRegistry: React.FC<ModelRegistryProps> = ({ ...pageProps }) => {
2525
const [registeredModels, modelsLoaded, modelsLoadError, refreshModels] = useRegisteredModels();
2626
const [modelVersions, versionsLoaded, versionsLoadError, refreshVersions] = useModelVersions();
2727

28+
const { isMUITheme } = useThemeContext();
2829
const loaded = modelsLoaded && versionsLoaded;
2930
const loadError = modelsLoadError || versionsLoadError;
3031

@@ -37,14 +38,14 @@ const ModelRegistry: React.FC<ModelRegistryProps> = ({ ...pageProps }) => {
3738
<ApplicationsPage
3839
{...pageProps}
3940
title={
40-
!isMUITheme() ? (
41+
!isMUITheme ? (
4142
<TitleWithIcon title="Model Registry" objectType={ProjectObjectType.registeredModels} />
4243
) : (
4344
'Model Registry'
4445
)
4546
}
4647
description={
47-
!isMUITheme() ? (
48+
!isMUITheme ? (
4849
'Select a model registry to view and manage your registered models. Model registries provide a structured and organized way to store, share, version, deploy, and track models.'
4950
) : (
5051
<Divider />

clients/ui/frontend/src/app/pages/modelRegistry/screens/ModelRegistrySelector.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ import { ModelRegistrySelectorContext } from '~/app/context/ModelRegistrySelecto
2121
import { ModelRegistry } from '~/app/types';
2222
import SimpleSelect, { SimpleSelectOption } from '~/shared/components/SimpleSelect';
2323
import WhosMyAdministrator from '~/shared/components/WhosMyAdministrator';
24-
import { isMUITheme } from '~/shared/utilities/const';
2524
import KubeflowDocs from '~/shared/components/KubeflowDocs';
25+
import { useThemeContext } from '~/app/ThemeContext';
2626

2727
const MODEL_REGISTRY_FAVORITE_STORAGE_KEY = 'kubeflow.dashboard.model.registry.favorite';
2828

@@ -42,6 +42,7 @@ const ModelRegistrySelector: React.FC<ModelRegistrySelectorProps> = ({
4242
const { modelRegistries, updatePreferredModelRegistry } = React.useContext(
4343
ModelRegistrySelectorContext,
4444
);
45+
const { isMUITheme } = useThemeContext();
4546

4647
const selection = modelRegistries.find((mr) => mr.name === modelRegistry);
4748
const [favorites, setFavorites] = useBrowserStorage<string[]>(
@@ -175,7 +176,7 @@ const ModelRegistrySelector: React.FC<ModelRegistrySelectorProps> = ({
175176
</FlexItem>
176177
)}
177178
<FlexItem align={{ default: 'alignRight' }}>
178-
{isMUITheme() ? (
179+
{isMUITheme ? (
179180
<KubeflowDocs
180181
buttonLabel="Need another registry?"
181182
linkTestId="model-registry-help-button"

clients/ui/frontend/src/app/pages/modelRegistry/screens/ModelVersionsArchive/ModelVersionsArchiveListView.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { asEnumMember } from '~/shared/utilities/utils';
1616
import { filterModelVersions } from '~/app/pages/modelRegistry/screens/utils';
1717
import EmptyModelRegistryState from '~/app/pages/modelRegistry/screens/components/EmptyModelRegistryState';
1818
import FormFieldset from '~/app/pages/modelRegistry/screens/components/FormFieldset';
19-
import { isMUITheme } from '~/shared/utilities/const';
19+
import { useThemeContext } from '~/app/ThemeContext';
2020
import ModelVersionsArchiveTable from './ModelVersionsArchiveTable';
2121

2222
type ModelVersionsArchiveListViewProps = {
@@ -33,6 +33,8 @@ const ModelVersionsArchiveListView: React.FC<ModelVersionsArchiveListViewProps>
3333

3434
const searchTypes = [SearchType.KEYWORD, SearchType.AUTHOR];
3535

36+
const { isMUITheme } = useThemeContext();
37+
3638
const filteredModelVersions = filterModelVersions(unfilteredmodelVersions, search, searchType);
3739

3840
if (unfilteredmodelVersions.length === 0) {
@@ -77,7 +79,7 @@ const ModelVersionsArchiveListView: React.FC<ModelVersionsArchiveListViewProps>
7779
/>
7880
</ToolbarFilter>
7981
<ToolbarItem>
80-
{isMUITheme() ? (
82+
{isMUITheme ? (
8183
<FormFieldset
8284
className="toolbar-fieldset-wrapper"
8385
component={

clients/ui/frontend/src/app/pages/modelRegistry/screens/RegisterModel/PrefilledModelRegistryField.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,22 @@
11
import React from 'react';
22
import { FormGroup, TextInput } from '@patternfly/react-core';
33
import FormFieldset from '~/app/pages/modelRegistry/screens/components/FormFieldset';
4-
import { isMUITheme } from '~/shared/utilities/const';
4+
import { useThemeContext } from '~/app/ThemeContext';
55

66
type PrefilledModelRegistryFieldProps = {
77
mrName?: string;
88
};
99

1010
const PrefilledModelRegistryField: React.FC<PrefilledModelRegistryFieldProps> = ({ mrName }) => {
11+
const { isMUITheme } = useThemeContext();
12+
1113
const mrNameInput = (
1214
<TextInput isDisabled isRequired type="text" id="mr-name" name="mr-name" value={mrName} />
1315
);
1416

1517
return (
1618
<FormGroup className="form-group-disabled" label="Model registry" isRequired fieldId="mr-name">
17-
{isMUITheme() ? <FormFieldset component={mrNameInput} field="Model Registry" /> : mrNameInput}
19+
{isMUITheme ? <FormFieldset component={mrNameInput} field="Model Registry" /> : mrNameInput}
1820
</FormGroup>
1921
);
2022
};

0 commit comments

Comments
 (0)