From e7e542bd0519cb6ade61b7e37b7c41f72b924dc3 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 18 Dec 2023 15:40:25 +0100 Subject: [PATCH 01/21] docs: draft extension overrides arch Signed-off-by: Camila Belo --- .../architecture/05-extension-overrides.md | 105 +++++++++++++++--- 1 file changed, 87 insertions(+), 18 deletions(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index e722145552..20476c24b0 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -10,38 +10,107 @@ description: Frontend extension overrides ## Introduction - +- When you just want to configure a plugin, such as changing the attachment point, a path or any other setting. Be sure to read the extension documentation before overriding an extension to make sure you cannot configure it to work the way you want; +- It is already possible to enable an extension that is already provided by the plugin. Imagine we have a Search plugin that provides a default search result item extension for rendering search results. Consider also that we have a Catalog plugin with a disabled Catalog search result item extension. To change the way Catalog search result items are rendered, you don't need to override the default result item extension. The only thing you need to do is enable the Catalog search result extension via the config file. For other types of search results, the default Search result item will continue to be used. -## Creating a Extension Override +## Creating an Extension Override - +For this example, we are creating overrides for the light and dark theme extensions and exporting the overrides from the plugin index file. Now we are able to use the overrides in a Backstage app: + +```tsx +// packages/app/src/App.tsx +import { createApp } from '@backstage/frontend-app-api'; +import apertureOverrides from ‘@backstage/plugin-aperture-overrides’ + +const app = createApp({ + features: [ apertureOverrides ], +}); + +export default app.createRoot(). +``` + +If the plugin you want to change is internal to your company or you just want to replace one of the application's core extensions, you can decide to create replacements directly in the application for local replacements. See an example below: + +```tsx +// packages/app/src/themes.ts +import { createExtensionOverrides } from from '@backstage/frontend-plugin-api'; + +// Creating the light theme extension; +export const apertureLightTheme = createThemeApi({ … }); + +// Creating the light theme extension; +const apertureDarkTheme = createThemeApi({ … }); + +// Exporting your custom extensions +export default [apertureLightTheme, apertureDarkTheme]; + +// packages/app/src/App.tsx +import { createApp } from '@backstage/frontend-app-api'; +import themes from ‘./themes’ + +const app = createApp({ + features: [ + createExtensionOverrides({ + extensions: [ + ...themes, + ], + }), + ], +}); + +export default app.createRoot(); +``` + +Note that it can still be a good idea to split your overrides out into separate packages in large projects. But it's up to you to decide how to group the extensions into extension overrides. ## Overriding Existing Extensions - +We recommend that plugin developes share the extension ids in their plugin documentations, but usually you can infer the id by following the (Naming pattern)[./08-naming-patterns] standars. From f6909fee983aaf2961b63086e724a545b1bba734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 22 Dec 2023 14:34:08 +0100 Subject: [PATCH 02/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Signed-off-by: Fredrik Adelöw --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 20476c24b0..2bceab52d9 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -42,7 +42,7 @@ const apertureDarkTheme = createThemeApi({ … }); // Creating an extension overrides preset export createExtensionOverrides({ - Extensions: [apertureLightTheme, apertureDarkTheme] + extensions: [apertureLightTheme, apertureDarkTheme] }); // plugins/aperture-overrides/src/index.ts From 27aa7dc95780976723e18b101db889fd19e32fad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 22 Dec 2023 14:34:15 +0100 Subject: [PATCH 03/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Signed-off-by: Fredrik Adelöw --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 2bceab52d9..cfb590d217 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -37,7 +37,7 @@ import { // Creating a light theme extension; const apertureLightTheme = createThemeApi({ … }); -// Creating a light theme extension; +// Creating a dark theme extension; const apertureDarkTheme = createThemeApi({ … }); // Creating an extension overrides preset From 2827fc2ba91fda463b90fcfac65c3e8022166ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 22 Dec 2023 14:34:39 +0100 Subject: [PATCH 04/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Signed-off-by: Fredrik Adelöw --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index cfb590d217..facae81e21 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -101,7 +101,7 @@ Note that it can still be a good idea to split your overrides out into separate To override an existing extension (which is already provided by a plugin), you need to provide an extension that has the same ID as the existing extension. That is, all kind, namespaces, and name must match the extension you want to replace. This means that you typically need to provide an explicit namespace when overriding extensions from a plugin. -Imagine that we have a plugin with search id and the plugin provides a search page extension that you want to fully override with your own custom component. To do so, you have to create your page extension and inform to the extension factory that the extension namespace is search to make sure you are overriding the search page of the plugin with id 'search' and not creating a anonymous page that will not override thesearch plugin one. +Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well. ```tsx // packages/app/src/search.ts From 564f731e964e960d2e1bd4970d687e88345113b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 22 Dec 2023 14:34:47 +0100 Subject: [PATCH 05/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Patrik Oldsberg Signed-off-by: Fredrik Adelöw --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index facae81e21..4ee859709b 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -113,4 +113,4 @@ const customSearchPage = createPageExtension({ }); ``` -We recommend that plugin developes share the extension ids in their plugin documentations, but usually you can infer the id by following the (Naming pattern)[./08-naming-patterns] standars. +We recommend that plugin developes share the extension IDs in their plugin documentations, but usually you can infer the id by following the (Naming pattern)[./08-naming-patterns] standars. From ebc63612ace1318e5a51d44f187a9191245cf93e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 22 Dec 2023 15:15:13 +0100 Subject: [PATCH 06/21] address some of the comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .github/vale/Vocab/Backstage/accept.txt | 4 +++- .../architecture/05-extension-overrides.md | 15 +++------------ 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index d557434d78..dc0b74b98c 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -312,6 +312,8 @@ rebase rebasing Recharts Redash +renderer +renderers replicasets repo Repo @@ -463,4 +465,4 @@ zod Zolotusky zoomable zsh -scrollable \ No newline at end of file +scrollable diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 4ee859709b..6317783347 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -10,18 +10,9 @@ description: Frontend extension overrides ## Introduction -An extension override is another building block in the Frontend System that allows you to programmatically override app or plugin extensions. Extension overrides are recommended in the following cases: +An extension override is a building block of the Frontend System that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with your a custom one, to override how the catalog page renders itself, and much more. -- Override the app skeleton layout; -- Override a default api implementation; -- Override a default app component; -- Override a plugin's default page; -- Etc. - -It is also important to clarify when an override isn't needed. Creating an extension override is not recommended when: - -- When you just want to configure a plugin, such as changing the attachment point, a path or any other setting. Be sure to read the extension documentation before overriding an extension to make sure you cannot configure it to work the way you want; -- It is already possible to enable an extension that is already provided by the plugin. Imagine we have a Search plugin that provides a default search result item extension for rendering search results. Consider also that we have a Catalog plugin with a disabled Catalog search result item extension. To change the way Catalog search result items are rendered, you don't need to override the default result item extension. The only thing you need to do is enable the Catalog search result extension via the config file. For other types of search results, the default Search result item will continue to be used. +Note that in general, most features should have a good level of customizability built into themselves, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has app-config settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should be taken advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation if available for guidance. ## Creating an Extension Override @@ -113,4 +104,4 @@ const customSearchPage = createPageExtension({ }); ``` -We recommend that plugin developes share the extension IDs in their plugin documentations, but usually you can infer the id by following the (Naming pattern)[./08-naming-patterns] standars. +We recommend that plugin developes share the extension IDs in their plugin documentation, but usually you can infer the ID by following the (naming patterns)[./08-naming-patterns] documentation. From 445a8dc1d31d783fab5dc505d63f92a84a42f6d1 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Fri, 12 Jan 2024 15:05:05 +0100 Subject: [PATCH 07/21] refactor: apply review sugggestions Signed-off-by: Camila Belo --- .../architecture/05-extension-overrides.md | 89 +++++++++---------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 6317783347..17daef6b6c 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -14,38 +14,63 @@ An extension override is a building block of the Frontend System that allows you Note that in general, most features should have a good level of customizability built into themselves, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has app-config settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should be taken advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation if available for guidance. -## Creating an Extension Override +## Overriding App Extensions -The following steps should be followed to override extensions: +If you want to override an app extension, you will need to create a new extension to replace the existing one and add it to the list of overridden features. The steps are: + +1. Create your extension overrides + +In this example below, we are going to create custom extensions for the app light and dark themes: ```tsx -// plugins/aperture-overrides/src/overrides.ts +// packages/app/src/themes.ts import { - createThemeApi, + createThemeExtension, createExtensionOverrides } from '@backstage/frontend-plugin-api'; +import { apertureThemes } from './themes'; +import { ApertureLightIcon, ApertureDarkIcon } from './icons'; // Creating a light theme extension; -const apertureLightTheme = createThemeApi({ … }); +const apertureLightTheme = createThemeExtension({ + namespace: 'app', + name: 'light', + title: 'Aperture Light Theme', + variant: 'light', + icon: , + Provider: ({ children }) => ( + + ), +}); // Creating a dark theme extension; -const apertureDarkTheme = createThemeApi({ … }); +const apertureDarkTheme = createThemeExtension({ + namespace: 'app', + name: 'dark', + title: 'Aperture Dark Theme', + variant: 'dark', + icon: , + Provider: ({ children }) => ( + + ), +}); // Creating an extension overrides preset export createExtensionOverrides({ extensions: [apertureLightTheme, apertureDarkTheme] }); - -// plugins/aperture-overrides/src/index.ts -export { default } from './overrides'; ``` -For this example, we are creating overrides for the light and dark theme extensions and exporting the overrides from the plugin index file. Now we are able to use the overrides in a Backstage app: +We exported the overrides for the light and dark app theme extensions from a separate file in the code snippet above. To override the default app's light and dark theme extensions, we must ensure that the extension namespace and name match the ones in the default app themes extension definitions. + +2. Use the overrides in your Backstage App + +Now we are able to use the overrides in a Backstage app: ```tsx // packages/app/src/App.tsx import { createApp } from '@backstage/frontend-app-api'; -import apertureOverrides from ‘@backstage/plugin-aperture-overrides’ +import apertureOverrides from '@backstage/plugin-aperture-overrides' const app = createApp({ features: [ apertureOverrides ], @@ -54,43 +79,13 @@ const app = createApp({ export default app.createRoot(). ``` -If the plugin you want to change is internal to your company or you just want to replace one of the application's core extensions, you can decide to create replacements directly in the application for local replacements. See an example below: - -```tsx -// packages/app/src/themes.ts -import { createExtensionOverrides } from from '@backstage/frontend-plugin-api'; - -// Creating the light theme extension; -export const apertureLightTheme = createThemeApi({ … }); - -// Creating the light theme extension; -const apertureDarkTheme = createThemeApi({ … }); - -// Exporting your custom extensions -export default [apertureLightTheme, apertureDarkTheme]; - -// packages/app/src/App.tsx -import { createApp } from '@backstage/frontend-app-api'; -import themes from ‘./themes’ - -const app = createApp({ - features: [ - createExtensionOverrides({ - extensions: [ - ...themes, - ], - }), - ], -}); - -export default app.createRoot(); -``` +If the plugin you want to change is internal to your company or you just want to replace one of the application's core extensions, you can decide to store the overrides code directly in the app package or extract them to a separate package. Note that it can still be a good idea to split your overrides out into separate packages in large projects. But it's up to you to decide how to group the extensions into extension overrides. -## Overriding Existing Extensions +## Overriding Plugin Extensions -To override an existing extension (which is already provided by a plugin), you need to provide an extension that has the same ID as the existing extension. That is, all kind, namespaces, and name must match the extension you want to replace. This means that you typically need to provide an explicit namespace when overriding extensions from a plugin. +To override an extension that is provided by a plugin, you need to provide an new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit namespace when overriding extensions from a plugin. Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well. @@ -98,9 +93,9 @@ Imagine you have a plugin with the ID `'search'`, and the plugin provides a page // packages/app/src/search.ts const customSearchPage = createPageExtension({ namespace: 'search', - // Omitting name since it is the root plugin page - defaultPath: 'search' - loader: async () =>
My custom search page + // Omitting name since it is the index plugin page + defaultPath: 'search', + loader: () => Promise.resolve(
My custom search page
), }); ``` From deeb5f70fda95b10fc412d82d4aa78b94fd48426 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 15 Jan 2024 22:41:57 +0100 Subject: [PATCH 08/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 17daef6b6c..53f5ad8b1d 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -12,7 +12,7 @@ description: Frontend extension overrides An extension override is a building block of the Frontend System that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with your a custom one, to override how the catalog page renders itself, and much more. -Note that in general, most features should have a good level of customizability built into themselves, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has app-config settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should be taken advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation if available for guidance. +Note that in general, most features should have a good level of customizability built into themselves, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has app-config settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation if available for guidance. ## Overriding App Extensions From 147d7a4067a5b112dcbb7a208fc77c0baa3e16ad Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 15 Jan 2024 22:42:32 +0100 Subject: [PATCH 09/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 53f5ad8b1d..03ead23537 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -22,8 +22,7 @@ If you want to override an app extension, you will need to create a new extensio In this example below, we are going to create custom extensions for the app light and dark themes: -```tsx -// packages/app/src/themes.ts +```tsx title="packages/app/src/themes.ts" import { createThemeExtension, createExtensionOverrides From 7066b00773acf715a51e7694205e4ec8c95f9289 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 15 Jan 2024 22:42:38 +0100 Subject: [PATCH 10/21] Update docs/frontend-system/architecture/05-extension-overrides.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 03ead23537..5dff6a3ccd 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -93,7 +93,7 @@ Imagine you have a plugin with the ID `'search'`, and the plugin provides a page const customSearchPage = createPageExtension({ namespace: 'search', // Omitting name since it is the index plugin page - defaultPath: 'search', + defaultPath: '/search', loader: () => Promise.resolve(
My custom search page
), }); ``` From d45d5bc879ab128e11d2c5cf081cb989ff709fdd Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Mon, 15 Jan 2024 23:50:34 +0100 Subject: [PATCH 11/21] docs(frontend-system): add orphan extension overrides section Signed-off-by: Camila Belo --- .../architecture/05-extension-overrides.md | 91 +++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 5dff6a3ccd..572018606e 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -16,13 +16,13 @@ Note that in general, most features should have a good level of customizability ## Overriding App Extensions -If you want to override an app extension, you will need to create a new extension to replace the existing one and add it to the list of overridden features. The steps are: +In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. -1. Create your extension overrides +### Example -In this example below, we are going to create custom extensions for the app light and dark themes: +In the example below, we create a file that exports custom extensions for the app's `light` and `dark` themes: -```tsx title="packages/app/src/themes.ts" +```tsx title="packages/app/src/themes.ts" {10-11,22-23} showLineNumbers import { createThemeExtension, createExtensionOverrides @@ -30,7 +30,7 @@ import { import { apertureThemes } from './themes'; import { ApertureLightIcon, ApertureDarkIcon } from './icons'; -// Creating a light theme extension; +// Creating a light theme extension const apertureLightTheme = createThemeExtension({ namespace: 'app', name: 'light', @@ -42,7 +42,7 @@ const apertureLightTheme = createThemeExtension({ ), }); -// Creating a dark theme extension; +// Creating a dark theme extension const apertureDarkTheme = createThemeExtension({ namespace: 'app', name: 'dark', @@ -60,16 +60,13 @@ export createExtensionOverrides({ }); ``` -We exported the overrides for the light and dark app theme extensions from a separate file in the code snippet above. To override the default app's light and dark theme extensions, we must ensure that the extension namespace and name match the ones in the default app themes extension definitions. - -2. Use the overrides in your Backstage App +Lines `10` and `22` should be highlighted because they declare `namespace` as `'app'`, so the system knows we are overriding app extensions. Additionally, to specifically override the `light` and `dark` theme extensions, we set the `name` option to `light` and `dark` respectively on lines `11` and `23`. Therefore, to override app theme extensions, we ensure that the extension `namespace` and `name` match those of the default app theme extension definitions. Now we are able to use the overrides in a Backstage app: -```tsx -// packages/app/src/App.tsx +```tsx title="packages/app/src/App.tsx" {5} showLineNumbers import { createApp } from '@backstage/frontend-app-api'; -import apertureOverrides from '@backstage/plugin-aperture-overrides' +import apertureOverrides from './themes'; const app = createApp({ features: [ apertureOverrides ], @@ -84,18 +81,78 @@ Note that it can still be a good idea to split your overrides out into separate ## Overriding Plugin Extensions -To override an extension that is provided by a plugin, you need to provide an new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit namespace when overriding extensions from a plugin. +To override an extension that is provided by a plugin, you need to provide an new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin. + +:::info +We recommend that plugin developers share the extension IDs in their plugin documentation, but usually you can infer the ID by following the [naming patterns](./08-naming-patterns.md) documentation. +::: + +### Example Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well. -```tsx -// packages/app/src/search.ts +```tsx title="packages/app/src/search.ts" showLineNumbers +import { createPageExtension } from '@backstage/frontend-plugin-api'; + +// Creating a custom search page extension const customSearchPage = createPageExtension({ namespace: 'search', // Omitting name since it is the index plugin page defaultPath: '/search', - loader: () => Promise.resolve(
My custom search page
), + loader: () => import('./SearchPage').then(m => m.), +}); + +export createExtensionOverrides({ + extensions: [customSearchPage] }); ``` -We recommend that plugin developes share the extension IDs in their plugin documentation, but usually you can infer the ID by following the (naming patterns)[./08-naming-patterns] documentation. +Don't forget to configure your overrides in the `createApp` function: + +```tsx title="packages/app/src/App.tsx" {5} showLineNumbers +import { createApp } from '@backstage/frontend-app-api'; +import searchOverrides from './search'; + +const app = createApp({ + features: [searchOverrides], +}); + +export default app.createRoot(); +``` + +Now let's talk about the last override case, orphan extensions. + +## Creating Orphan Extensions + +Sometimes you just need to quickly create a new extension and not overwrite an app extension or plugin. You can also use overrides to create extensions, but remember that if you want to make this extension available for installation by other users, we recommend providing it via a plugin in a separate package. + +### Example + +Imagine you want to create a page that is currently only used by your application, like an Institutional page for example, you can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override: + +```tsx title="packages/app/src/App.ts" showLineNumbers +import { createApp } from '@backstage/frontend-app-api'; +import { + createPageExtension, + createExtensionOverrides, +} from '@backstage/frontend-plugin-api'; + +const app = createApp({ + features: [ + createExtensionOverrides({ + extensions: [ + createPageExtension({ + name: 'institutional', + defaultPath: '/institutional', + loader: () => + import('./institutional').then(m => ), + }), + ], + }), + ], +}); + +export default app.createRoot(); +``` + +Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is orphaned (not an application or plugin extension) and this is all about orphaned extensions! From 6d1d2967b313728f9d92939242edeb9870877d2a Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:26:30 +0100 Subject: [PATCH 12/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 572018606e..5a40bd079c 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -12,7 +12,7 @@ description: Frontend extension overrides An extension override is a building block of the Frontend System that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with your a custom one, to override how the catalog page renders itself, and much more. -Note that in general, most features should have a good level of customizability built into themselves, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has app-config settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation if available for guidance. +In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has `app-config` settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation for guidance. ## Overriding App Extensions From e0e6e853b2953f8146b5abdd6843879bb90e78d0 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:27:03 +0100 Subject: [PATCH 13/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 5a40bd079c..52c76a4673 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -128,7 +128,7 @@ Sometimes you just need to quickly create a new extension and not overwrite an a ### Example -Imagine you want to create a page that is currently only used by your application, like an Institutional page for example, you can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override: +Imagine you want to create a page that is currently only used by your application, like an Institutional page, for example. You can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override: ```tsx title="packages/app/src/App.ts" showLineNumbers import { createApp } from '@backstage/frontend-app-api'; From e761ab547116b26853c51ff1aee122dd37ae8d78 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:27:19 +0100 Subject: [PATCH 14/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 52c76a4673..618efefc56 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -155,4 +155,4 @@ const app = createApp({ export default app.createRoot(); ``` -Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is orphaned (not an application or plugin extension) and this is all about orphaned extensions! +Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is orphaned and not an application or plugin extension! From bf48a5bb8f7d25dd38df5dab734cf781395bc0b4 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:27:39 +0100 Subject: [PATCH 15/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 618efefc56..996f60f77d 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -81,7 +81,7 @@ Note that it can still be a good idea to split your overrides out into separate ## Overriding Plugin Extensions -To override an extension that is provided by a plugin, you need to provide an new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin. +To override an extension that is provided by a plugin, you need to provide a new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin. :::info We recommend that plugin developers share the extension IDs in their plugin documentation, but usually you can infer the ID by following the [naming patterns](./08-naming-patterns.md) documentation. From b144eba4a536e259f667433fe40a0855caf2ee19 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:28:52 +0100 Subject: [PATCH 16/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 996f60f77d..d51ebcea93 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -14,7 +14,7 @@ An extension override is a building block of the Frontend System that allows you In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has `app-config` settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation for guidance. -## Overriding App Extensions +## Override App Extensions In order to override an app extension, you must create a new extension and add it to the list of overridden features. The steps are: create your extension overrides and use them in Backstage. From 012b38c8812745bd07c5896bb0af4905bdd6fb3f Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 12:29:01 +0100 Subject: [PATCH 17/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index d51ebcea93..ffe8ff11c4 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -10,7 +10,7 @@ description: Frontend extension overrides ## Introduction -An extension override is a building block of the Frontend System that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with your a custom one, to override how the catalog page renders itself, and much more. +An extension override is a building block of the frontend system that allows you to programmatically override app or plugin extensions anywhere in your application. Since the entire application is built mostly out of extensions from the bottom up, this is a powerful feature. You can use it for example to provide your own app root layout, to replace the implementation of a Utility API with a custom one, to override how the catalog page renders itself, and much more. In general, most features should have a good level of customization built into them, so that users do not have to leverage extension overrides to achieve common goals. A well written feature often has `app-config` settings, or uses extension inputs for extensibility where applicable. An example of this is the search plugin, which allows you to provide result renderers as inputs rather than replacing the result page wholesale just to tweak how results are shown. Adopters should take advantage of those when possible, and only use extension overrides when it's necessary to entirely replace the extension. Check the respective extension documentation for guidance. From 1c0c187d6ac9fe6eef4ebbc70fcf1562ea647a79 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 13:01:33 +0100 Subject: [PATCH 18/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Philipp Hugenroth Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index ffe8ff11c4..6921315d5a 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -79,7 +79,7 @@ If the plugin you want to change is internal to your company or you just want to Note that it can still be a good idea to split your overrides out into separate packages in large projects. But it's up to you to decide how to group the extensions into extension overrides. -## Overriding Plugin Extensions +## Override Plugin Extensions To override an extension that is provided by a plugin, you need to provide a new extension that has the same ID as the existing extension. That is, all kind, namespace, and name options must match the extension you want to replace. This means that you typically need to provide an explicit `namespace` when overriding extensions from a plugin. From 17b2750329136dccf61c066823eeb4c3e69fb307 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Tue, 16 Jan 2024 13:12:09 +0100 Subject: [PATCH 19/21] docs(frontend-system): update orphan extensions section title Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 6921315d5a..3791ee43b1 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -122,7 +122,7 @@ export default app.createRoot(); Now let's talk about the last override case, orphan extensions. -## Creating Orphan Extensions +## Create Orphan Extensions Sometimes you just need to quickly create a new extension and not overwrite an app extension or plugin. You can also use overrides to create extensions, but remember that if you want to make this extension available for installation by other users, we recommend providing it via a plugin in a separate package. From fbe52d23cff83f2f639f89baa8508cd1baee0bdc Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 17 Jan 2024 15:22:41 +0100 Subject: [PATCH 20/21] Update docs/frontend-system/architecture/05-extension-overrides.md Co-authored-by: Patrik Oldsberg Signed-off-by: Camila Belo --- docs/frontend-system/architecture/05-extension-overrides.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index 3791ee43b1..bb177cd703 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -122,7 +122,7 @@ export default app.createRoot(); Now let's talk about the last override case, orphan extensions. -## Create Orphan Extensions +## Create Standalone Extensions Sometimes you just need to quickly create a new extension and not overwrite an app extension or plugin. You can also use overrides to create extensions, but remember that if you want to make this extension available for installation by other users, we recommend providing it via a plugin in a separate package. From 51d351cd6d1611d7be686214c3d6d2aa3b4fede0 Mon Sep 17 00:00:00 2001 From: Camila Belo Date: Wed, 17 Jan 2024 16:08:28 +0100 Subject: [PATCH 21/21] refactor(frontend-system/overrides): apply docs suggestions Signed-off-by: Camila Belo --- .../architecture/05-extension-overrides.md | 27 ++++++++++++------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/docs/frontend-system/architecture/05-extension-overrides.md b/docs/frontend-system/architecture/05-extension-overrides.md index bb177cd703..6a1037341e 100644 --- a/docs/frontend-system/architecture/05-extension-overrides.md +++ b/docs/frontend-system/architecture/05-extension-overrides.md @@ -22,7 +22,7 @@ In order to override an app extension, you must create a new extension and add i In the example below, we create a file that exports custom extensions for the app's `light` and `dark` themes: -```tsx title="packages/app/src/themes.ts" {10-11,22-23} showLineNumbers +```tsx title="packages/app/src/themes.ts" import { createThemeExtension, createExtensionOverrides @@ -32,8 +32,10 @@ import { ApertureLightIcon, ApertureDarkIcon } from './icons'; // Creating a light theme extension const apertureLightTheme = createThemeExtension({ + // highlight-start namespace: 'app', name: 'light', + // highlight-end title: 'Aperture Light Theme', variant: 'light', icon: , @@ -44,8 +46,10 @@ const apertureLightTheme = createThemeExtension({ // Creating a dark theme extension const apertureDarkTheme = createThemeExtension({ + // highlight-start namespace: 'app', name: 'dark', + // highlight-end title: 'Aperture Dark Theme', variant: 'dark', icon: , @@ -60,16 +64,17 @@ export createExtensionOverrides({ }); ``` -Lines `10` and `22` should be highlighted because they declare `namespace` as `'app'`, so the system knows we are overriding app extensions. Additionally, to specifically override the `light` and `dark` theme extensions, we set the `name` option to `light` and `dark` respectively on lines `11` and `23`. Therefore, to override app theme extensions, we ensure that the extension `namespace` and `name` match those of the default app theme extension definitions. +Note that we declare `namespace` as `'app'` while creating the themes, so the system knows we are overriding app extensions. Additionally, to specifically override the `light` and `dark` theme extensions, we set the `name` option to `light` and `dark`. Therefore, to override app theme extensions, we ensure that the extension `namespace` and `name` match those of the default app theme extension definitions. Now we are able to use the overrides in a Backstage app: -```tsx title="packages/app/src/App.tsx" {5} showLineNumbers +```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; -import apertureOverrides from './themes'; +import themeOverrides from './themes'; const app = createApp({ - features: [ apertureOverrides ], + // highlight-next-line + features: [ themeOverrides ], }); export default app.createRoot(). @@ -91,11 +96,12 @@ We recommend that plugin developers share the extension IDs in their plugin docu Imagine you have a plugin with the ID `'search'`, and the plugin provides a page extension that you want to fully override with your own custom component. To do so, you need to create your page extension with an explicit `namespace` option that matches that of the plugin that you want to override, in this case `'search'`. If the existing extension also has an explicit `name` you'd need to set the `name` of your override extension to the same value as well. -```tsx title="packages/app/src/search.ts" showLineNumbers +```tsx title="packages/app/src/search.ts" import { createPageExtension } from '@backstage/frontend-plugin-api'; // Creating a custom search page extension const customSearchPage = createPageExtension({ + // highlight-next-line namespace: 'search', // Omitting name since it is the index plugin page defaultPath: '/search', @@ -109,11 +115,12 @@ export createExtensionOverrides({ Don't forget to configure your overrides in the `createApp` function: -```tsx title="packages/app/src/App.tsx" {5} showLineNumbers +```tsx title="packages/app/src/App.tsx" import { createApp } from '@backstage/frontend-app-api'; import searchOverrides from './search'; const app = createApp({ + // highlight-next-line features: [searchOverrides], }); @@ -130,7 +137,7 @@ Sometimes you just need to quickly create a new extension and not overwrite an a Imagine you want to create a page that is currently only used by your application, like an Institutional page, for example. You can use overrides to extend the Backstage app to render it. To do so, simply create a page extension and pass it to the app as an override: -```tsx title="packages/app/src/App.ts" showLineNumbers +```tsx title="packages/app/src/App.ts" import { createApp } from '@backstage/frontend-app-api'; import { createPageExtension, @@ -141,12 +148,14 @@ const app = createApp({ features: [ createExtensionOverrides({ extensions: [ + // highlight-start createPageExtension({ name: 'institutional', defaultPath: '/institutional', loader: () => import('./institutional').then(m => ), }), + // highlight-end ], }), ], @@ -155,4 +164,4 @@ const app = createApp({ export default app.createRoot(); ``` -Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is orphaned and not an application or plugin extension! +Note that we are omitting `namespace` when creating the page extension. When we omit `namespace`, we are telling the system the new extension is standalone and not an application or plugin extension!