diff --git a/packages/core-app-api/src/app/App.tsx b/packages/core-app-api/src/app/App.tsx index 8cb3f167fb..6fadd22808 100644 --- a/packages/core-app-api/src/app/App.tsx +++ b/packages/core-app-api/src/app/App.tsx @@ -126,7 +126,7 @@ type FullAppOptions = { icons: NonNullable; plugins: BackstagePlugin[]; components: AppComponents; - themes: AppTheme[]; + themes: (Partial & Omit)[]; configLoader?: AppConfigLoader; defaultApis: Iterable; bindRoutes?: AppOptions['bindRoutes']; @@ -206,7 +206,7 @@ export class PrivateAppImpl implements BackstageApp { this.icons = options.icons; this.plugins = new Set(options.plugins); this.components = options.components; - this.themes = options.themes; + this.themes = options.themes as AppTheme[]; this.configLoader = options.configLoader; this.defaultApis = options.defaultApis; this.bindRoutes = options.bindRoutes; diff --git a/packages/core-app-api/src/app/AppThemeProvider.tsx b/packages/core-app-api/src/app/AppThemeProvider.tsx index 4e3a8487a6..aa48a2187d 100644 --- a/packages/core-app-api/src/app/AppThemeProvider.tsx +++ b/packages/core-app-api/src/app/AppThemeProvider.tsx @@ -90,6 +90,17 @@ export function AppThemeProvider({ children }: PropsWithChildren<{}>) { throw new Error('App has no themes'); } + if (appTheme.Provider) { + return ; + } + + // eslint-disable-next-line no-console + console.warn( + "DEPRECATION WARNING: A provided app theme is using the deprecated 'theme' property " + + 'and should be migrated to use a Provider instead. ' + + 'See https://backstage.io/docs/deprecations/TODO for more info.', + ); + return ( {children} diff --git a/packages/core-app-api/src/app/createApp.tsx b/packages/core-app-api/src/app/createApp.tsx index 7b93daf40f..f5989219e8 100644 --- a/packages/core-app-api/src/app/createApp.tsx +++ b/packages/core-app-api/src/app/createApp.tsx @@ -19,11 +19,8 @@ import { JsonObject } from '@backstage/types'; import { defaultAppComponents, defaultAppIcons, + defaultAppThemes, } from '@backstage/core-components'; -import { darkTheme, lightTheme } from '@backstage/theme'; -import DarkIcon from '@material-ui/icons/Brightness2'; -import LightIcon from '@material-ui/icons/WbSunny'; -import React from 'react'; import { BrowserRouter } from 'react-router-dom'; import { PrivateAppImpl } from './App'; import { AppThemeProvider } from './AppThemeProvider'; @@ -129,6 +126,15 @@ export function createApp(options?: AppOptions) { ); } + if (!options?.themes) { + // eslint-disable-next-line no-console + console.warn( + 'DEPRECATION WARNING: The createApp options will soon require themes to be provided. ' + + 'Themes can be created using defaultAppThemes from @backstage/core-components ' + + 'and then passed along like this: createApp({ theme: defaultAppThemes() })', + ); + } + const apis = options?.apis ?? []; const plugins = options?.plugins ?? []; const components = { @@ -137,22 +143,6 @@ export function createApp(options?: AppOptions) { ThemeProvider: AppThemeProvider, ...options?.components, }; - const themes = options?.themes ?? [ - { - id: 'light', - title: 'Light Theme', - variant: 'light', - theme: lightTheme, - icon: , - }, - { - id: 'dark', - title: 'Dark Theme', - variant: 'dark', - theme: darkTheme, - icon: , - }, - ]; const configLoader = options?.configLoader ?? defaultConfigLoader; return new PrivateAppImpl({ @@ -160,7 +150,7 @@ export function createApp(options?: AppOptions) { icons: { ...appIcons, ...options?.icons }, plugins: plugins as BackstagePlugin[], components, - themes, + themes: options?.themes ?? defaultAppThemes(), configLoader, defaultApis, bindRoutes: options?.bindRoutes, diff --git a/packages/core-app-api/src/app/types.ts b/packages/core-app-api/src/app/types.ts index 407018111b..78ef277c90 100644 --- a/packages/core-app-api/src/app/types.ts +++ b/packages/core-app-api/src/app/types.ts @@ -226,18 +226,26 @@ export type AppOptions = { * id: 'light', * title: 'Light Theme', * variant: 'light', - * theme: lightTheme, * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), * }, { * id: 'dark', * title: 'Dark Theme', * variant: 'dark', - * theme: darkTheme, * icon: , + * Provider: ({ children }) => ( + * + * {children} + * + * ), * }] * ``` */ - themes?: AppTheme[]; + themes?: (Partial & Omit)[]; /** * A function that loads in App configuration that will be accessible via diff --git a/packages/core-components/src/defaultAppThemes.tsx b/packages/core-components/src/defaultAppThemes.tsx new file mode 100644 index 0000000000..894716942b --- /dev/null +++ b/packages/core-components/src/defaultAppThemes.tsx @@ -0,0 +1,52 @@ +/* + * Copyright 2021 The Backstage Authors + * + * 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 React from 'react'; +import { darkTheme, lightTheme } from '@backstage/theme'; +import DarkIcon from '@material-ui/icons/Brightness2'; +import LightIcon from '@material-ui/icons/WbSunny'; +import { ThemeProvider } from '@material-ui/core/styles'; +import CssBaseline from '@material-ui/core/CssBaseline'; +import { AppTheme } from '@backstage/core-plugin-api'; + +export function defaultAppThemes(): AppTheme[] { + return [ + { + id: 'light', + title: 'Light Theme', + variant: 'light', + icon: , + theme: lightTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, + { + id: 'dark', + title: 'Dark Theme', + variant: 'dark', + icon: , + theme: darkTheme, + Provider: ({ children }) => ( + + {children} + + ), + }, + ]; +} diff --git a/packages/core-components/src/index.ts b/packages/core-components/src/index.ts index 3538cd4186..305783dec8 100644 --- a/packages/core-components/src/index.ts +++ b/packages/core-components/src/index.ts @@ -27,3 +27,4 @@ export * from './layout'; export * from './overridableComponents'; export { defaultAppComponents } from './defaultAppComponents'; export { defaultAppIcons } from './defaultAppIcons'; +export { defaultAppThemes } from './defaultAppThemes'; diff --git a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts index de523663f2..52cf202422 100644 --- a/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/AppThemeApi.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { ReactNode } from 'react'; import { ApiRef, createApiRef } from '../system'; import { BackstageTheme } from '@backstage/theme'; import { Observable } from '@backstage/types'; @@ -41,6 +42,7 @@ export type AppTheme = { /** * The specialized MaterialUI theme instance. + * @deprecated use Provider instead, see https://backstage.io/docs/deprecations/TODO */ theme: BackstageTheme; @@ -48,6 +50,8 @@ export type AppTheme = { * An Icon for the theme mode setting. */ icon?: React.ReactElement; + + Provider?(props: { children: ReactNode }): JSX.Element | null; }; /**