diff --git a/.changeset/slick-books-sleep.md b/.changeset/slick-books-sleep.md new file mode 100644 index 0000000000..b1deab67ba --- /dev/null +++ b/.changeset/slick-books-sleep.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-app-api': patch +'@backstage/theme': patch +--- + +Added support for the `data-theme-name` attribute to dynamically update based on the active theme. Previously, this attribute was statically set to `backstage` and did not reflect theme changes. diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 46f1dc04c4..e7f9455392 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -48,6 +48,7 @@ "dependencies": { "@backstage/config": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/theme": "workspace:^", "@backstage/types": "workspace:^", "@backstage/version-bridge": "workspace:^", "@types/prop-types": "^15.7.3", diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index 719736eabb..93c76eeba0 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -16,6 +16,7 @@ import { useMemo, useEffect, useState, PropsWithChildren } from 'react'; import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api'; +import { AppThemeIdContext } from '@backstage/theme'; import useObservable from 'react-use/esm/useObservable'; // This tries to find the most accurate match, but also falls back to less @@ -88,5 +89,9 @@ export function AppThemeProvider({ children }: PropsWithChildren<{}>) { throw new Error('App has no themes'); } - return ; + return ( + + + + ); } diff --git a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx index e80d058140..037c7e7d8c 100644 --- a/packages/frontend-defaults/src/createPublicSignInApp.test.tsx +++ b/packages/frontend-defaults/src/createPublicSignInApp.test.tsx @@ -95,8 +95,8 @@ describe('createPublicSignInApp', () => { expect(baseElement).toMatchInlineSnapshot(`
{ + afterEach(() => { + document.body.removeAttribute('data-theme-name'); + document.body.removeAttribute('data-theme-mode'); + document.body.removeAttribute('data-unified-theme-stack'); + }); + it('provides a themes for v4 and v5 directly', () => { function MyV4Component() { const theme = useV4Theme(); @@ -81,4 +90,19 @@ describe('UnifiedThemeProvider', () => { 'rgb(158, 158, 158)', ); }); + + it('applies theme attributes based on the provided theme id', async () => { + render( + + + theme + + , + ); + + await waitFor(() => + expect(document.body.getAttribute('data-theme-name')).toBe('aperture'), + ); + expect(document.body.getAttribute('data-theme-mode')).toBe('light'); + }); }); diff --git a/packages/theme/src/unified/UnifiedThemeProvider.tsx b/packages/theme/src/unified/UnifiedThemeProvider.tsx index d0886cd146..74131ede20 100644 --- a/packages/theme/src/unified/UnifiedThemeProvider.tsx +++ b/packages/theme/src/unified/UnifiedThemeProvider.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { ReactNode } from 'react'; +import { ReactNode, createContext, useContext } from 'react'; import { ThemeProvider, StylesProvider, @@ -29,6 +29,8 @@ import { import { UnifiedTheme } from './types'; import { unstable_ClassNameGenerator as ClassNameGenerator } from '@mui/material/className'; +export const AppThemeIdContext = createContext(undefined); + /** * Props for {@link UnifiedThemeProvider}. * @@ -71,10 +73,10 @@ export function UnifiedThemeProvider( const v4Theme = theme.getTheme('v4') as Mui4Theme; const v5Theme = theme.getTheme('v5') as Mui5Theme; - useApplyThemeAttributes( - v4Theme ? v4Theme.palette.type : v5Theme?.palette.mode, - 'backstage', - ); + const themeMode = v4Theme ? v4Theme.palette.type : v5Theme?.palette.mode; + const themeId = useContext(AppThemeIdContext) ?? 'backstage'; + + useApplyThemeAttributes(themeMode, themeId); let result = children as JSX.Element; diff --git a/packages/theme/src/unified/index.ts b/packages/theme/src/unified/index.ts index 747662e195..6bb5ba46de 100644 --- a/packages/theme/src/unified/index.ts +++ b/packages/theme/src/unified/index.ts @@ -18,6 +18,9 @@ export { transformV5ComponentThemesToV4 } from './overrides'; export { createUnifiedTheme, createUnifiedThemeFromV4 } from './UnifiedTheme'; export type { UnifiedThemeOptions } from './UnifiedTheme'; export { themes } from './themes'; -export { UnifiedThemeProvider } from './UnifiedThemeProvider'; +export { + UnifiedThemeProvider, + AppThemeIdContext, +} from './UnifiedThemeProvider'; export type { UnifiedThemeProviderProps } from './UnifiedThemeProvider'; export type { UnifiedTheme, SupportedThemes, SupportedVersions } from './types'; diff --git a/plugins/app/src/alpha/appModulePublicSignIn.test.tsx b/plugins/app/src/alpha/appModulePublicSignIn.test.tsx index 787b8949e5..0c27a5f2e1 100644 --- a/plugins/app/src/alpha/appModulePublicSignIn.test.tsx +++ b/plugins/app/src/alpha/appModulePublicSignIn.test.tsx @@ -98,8 +98,8 @@ describe('appModulePublicSignIn', () => { expect(baseElement).toMatchInlineSnapshot(`