Remove NavItemBlueprint in favor of page-based nav discovery
Drop the deprecated NavItemBlueprint from the public API and migrate core plugins to set title and icon on PageBlueprint instead. AppNav keeps backward compatibility for legacy nav-item extensions via an internal core.nav-item.target data ref. Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
---
|
||||
'@backstage/frontend-plugin-api': minor
|
||||
'@backstage/plugin-app': patch
|
||||
'@backstage/plugin-catalog': patch
|
||||
'@backstage/plugin-search': patch
|
||||
'@backstage/plugin-home': patch
|
||||
'@backstage/plugin-api-docs': patch
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
'@backstage/plugin-techdocs': patch
|
||||
'@backstage/plugin-user-settings': patch
|
||||
'@backstage/plugin-devtools': patch
|
||||
'@backstage/plugin-catalog-unprocessed-entities': patch
|
||||
'@backstage/plugin-app-visualizer': patch
|
||||
'@backstage/frontend-test-utils': patch
|
||||
---
|
||||
|
||||
Removed the deprecated `NavItemBlueprint`. Navigation items are now discovered from page extensions via their `title` and `icon` params. The app nav extension still accepts legacy `nav-item` extensions for backward compatibility.
|
||||
@@ -63,13 +63,6 @@ app:
|
||||
# - apis.plugin.graphiql.browse.gitlab: true
|
||||
# - graphiql-endpoint:graphiql/gitlab: true
|
||||
|
||||
- nav-item:search: false
|
||||
- nav-item:user-settings: false
|
||||
- nav-item:catalog
|
||||
- nav-item:api-docs
|
||||
- nav-item:scaffolder
|
||||
- nav-item:app-visualizer
|
||||
|
||||
# Opt in to the experimental BUI scaffolder form theme
|
||||
- sub-page:scaffolder/templates:
|
||||
config:
|
||||
|
||||
@@ -60,7 +60,7 @@ Even with feature discovery enabled, you can disable specific extensions via con
|
||||
app:
|
||||
extensions:
|
||||
- page:techdocs: false
|
||||
- nav-item:search: false
|
||||
- page:search: false
|
||||
```
|
||||
|
||||
### How Discovery Works with Manual Imports
|
||||
|
||||
@@ -882,8 +882,7 @@ import { CheckboxGroup, Checkbox } from '@backstage/ui';
|
||||
|
||||
Some Backstage APIs still require MUI-compatible icon types:
|
||||
|
||||
- **NavItemBlueprint** (`@backstage/frontend-plugin-api`): The `icon` prop expects MUI `IconComponent` type. Remix icons
|
||||
are not type-compatible.
|
||||
- **PageBlueprint** (`@backstage/frontend-plugin-api`): The `icon` param on page extensions expects an `IconElement`. MUI icon components can still be used via `<Icon fontSize="inherit" />`.
|
||||
- **Timeline** (`@material-ui/lab`): No BUI equivalent exists.
|
||||
|
||||
For these cases, keep using MUI components.
|
||||
|
||||
@@ -52,16 +52,15 @@ _Example disabling the search page extension_
|
||||
app:
|
||||
extensions:
|
||||
- page:search: false # ✨
|
||||
- nav-item:search: false # ✨
|
||||
```
|
||||
|
||||
_Example setting the search sidebar item title_
|
||||
_Example setting the search page title (used in the sidebar)_
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
app:
|
||||
extensions:
|
||||
- nav-item:search: # ✨
|
||||
- page:search: # ✨
|
||||
config:
|
||||
title: 'Search Page'
|
||||
```
|
||||
|
||||
@@ -129,26 +129,20 @@ A plugin might not always behave exactly the way you want. It could be that you
|
||||
|
||||
```tsx
|
||||
import plugin from '@backstage/plugin-catalog';
|
||||
import { PageBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import CustomCatalogIcon from '@material-ui/icons/Category';
|
||||
|
||||
export default plugin.withOverrides({
|
||||
// These overrides are merged with the original extensions
|
||||
extensions: [
|
||||
// Override the catalog nav item to use a custom icon
|
||||
plugin.getExtension('nav-item:catalog').override({
|
||||
factory: origFactory => [
|
||||
NavItemBlueprint.dataRefs.target({
|
||||
...origFactory().get(NavItemBlueprint.dataRefs.target),
|
||||
icon: CustomCatalogIcon,
|
||||
// Override the catalog index page with a custom icon and implementation
|
||||
plugin.getExtension('page:catalog').override({
|
||||
factory: origFactory =>
|
||||
origFactory({
|
||||
icon: <CustomCatalogIcon fontSize="inherit" />,
|
||||
loader: () =>
|
||||
import('./CustomCatalogIndexPage').then(m => <m.Page />),
|
||||
}),
|
||||
],
|
||||
}),
|
||||
// Override the catalog index page with a completely custom implementation
|
||||
PageBlueprint.make({
|
||||
params: {
|
||||
path: '/catalog',
|
||||
routeRef: plugin.routes.catalogIndex,
|
||||
loader: () => import('./CustomCatalogIndexPage').then(m => <m.Page />),
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -164,7 +164,7 @@ Extension responsible for rendering the logo and items in the app's sidebar.
|
||||
| Name | Description | Type | Optional | Default | Extension creator |
|
||||
| ------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------- |
|
||||
| content | Overrides the default content of the navbar. | [NavContentBlueprint.dataRefs.component](https://backstage.io/api/stable/variables/_backstage_plugin-app-react.NavContentBlueprint.html) | true | - | [NavContentBlueprint](https://backstage.io/api/stable/variables/_backstage_plugin-app-react.NavContentBlueprint.html) |
|
||||
| items | Nav items target objects. | [createNavItemExtension.targetDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension.targetdataref) | true | - | [createNavItemExtension](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension) |
|
||||
| items | Legacy nav item target objects. | `core.nav-item.target` | true | - | Legacy extensions only; nav items are auto-discovered from page extensions. |
|
||||
|
||||
### App routes
|
||||
|
||||
|
||||
@@ -686,7 +686,7 @@ createApp({
|
||||
|
||||
#### App Root Sidebar
|
||||
|
||||
New apps feature a built-in sidebar extension which is created by using the `NavContentBlueprint` in `src/modules/nav/Sidebar.tsx`. The default implementation of the sidebar in this blueprint will render some items explicitly in different groups, and then render the rest of the items. Nav items are auto-discovered from page extensions registered under `app/routes` (no explicit `NavItemBlueprint` required), with metadata from page config, nav item extensions, or plugin defaults.
|
||||
New apps feature a built-in sidebar extension which is created by using the `NavContentBlueprint` in `src/modules/nav/Sidebar.tsx`. The default implementation of the sidebar in this blueprint will render some items explicitly in different groups, and then render the rest of the items. Nav items are auto-discovered from page extensions registered under `app/routes`, with metadata from page config or plugin defaults.
|
||||
|
||||
In order to migrate your existing sidebar, you will want to create an override for the `app/nav` extension. You can do this by copying the standard of having a `src/modules/nav/` folder, which can contain an extension which you can install into the `app` in the form of a `module`.
|
||||
|
||||
@@ -740,14 +740,7 @@ The deprecated `items` prop (a flat list compatible with `<SidebarItem {...item}
|
||||
|
||||
You might also notice that when you're rendering additional fixed icons for plugins (e.g. Search in a dedicated group) these might become duplicated, since that page is also included in `nav.rest()`. To exclude an item from the remaining list, call `nav.take('page:search')` before calling `nav.rest()` — you can discard the return value. Items that have been taken will not appear in `rest()`.
|
||||
|
||||
You can also use the old `NavItemBlueprint`-based nav item extensions to disable items from the nav bar, these can be disabled in config without affecting the page itself:
|
||||
|
||||
```yaml title="in app-config.yaml"
|
||||
app:
|
||||
extensions:
|
||||
- nav-item:search: false
|
||||
- nav-item:catalog: false
|
||||
```
|
||||
To hide a page from the sidebar without disabling the page itself, use `nav.take('page:...')` in your custom sidebar implementation before calling `nav.rest()`, or disable the page extension in config with `page:<plugin-id>: false`.
|
||||
|
||||
#### App Root Routes
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ The plugin ID should be a lowercase dash-separated string, while the plugin inst
|
||||
|
||||
The plugin that we created above is empty, and doesn't provide any actual functionality. To add functionality to a plugin you need to create and provide it with one or more [extensions](../architecture/20-extensions.md). Let's continue by adding a standalone page to our plugin, as well as a navigation item that allows users to navigate to the page.
|
||||
|
||||
To create a new extension you typically use pre-defined [extension blueprints](../architecture/23-extension-blueprints.md), provided either by the framework itself or by other plugins. In this case we'll use `PageBlueprint` and `NavItemBlueprint`, both from `@backstage/frontend-plugin-api`. We will also need to [create a route reference](../architecture/36-routes.md#creating-a-route-reference) to use as a reference for our page, allowing us to dynamically create URLs that link to our page.
|
||||
To create a new extension you typically use pre-defined [extension blueprints](../architecture/23-extension-blueprints.md), provided either by the framework itself or by other plugins. In this case we'll use `PageBlueprint` from `@backstage/frontend-plugin-api`. We will also need to [create a route reference](../architecture/36-routes.md#creating-a-route-reference) to use as a reference for our page, allowing us to dynamically create URLs that link to our page.
|
||||
|
||||
```tsx title="in src/routes.ts"
|
||||
import { createRouteRef } from '@backstage/frontend-plugin-api';
|
||||
@@ -62,8 +62,8 @@ export const rootRouteRef = createRouteRef();
|
||||
import {
|
||||
createFrontendPlugin,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import ExampleIcon from '@material-ui/icons/Extension';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
// Note that these extensions aren't exported, only the plugin itself is.
|
||||
@@ -75,6 +75,10 @@ const examplePage = PageBlueprint.make({
|
||||
// This is the default path of this page, but integrators are free to override it
|
||||
path: '/example',
|
||||
|
||||
// The title and icon are used to populate the app sidebar automatically
|
||||
title: 'Example',
|
||||
icon: <ExampleIcon fontSize="inherit" />,
|
||||
|
||||
// Page extensions are always dynamically loaded using React.lazy().
|
||||
// All of the functionality of this page is implemented in the
|
||||
// ExamplePage component, which is a regular React component.
|
||||
@@ -84,19 +88,10 @@ const examplePage = PageBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
// This nav item is provided to the app.nav extension, and will by default be rendered as a sidebar item
|
||||
const exampleNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Example',
|
||||
icon: ExampleIcon, // Custom SvgIcon, or one from the Material UI icon library
|
||||
},
|
||||
});
|
||||
|
||||
// The same plugin as above, now with the extensions added
|
||||
export const examplePlugin = createFrontendPlugin({
|
||||
pluginId: 'example',
|
||||
extensions: [examplePage, exampleNavItem],
|
||||
extensions: [examplePage],
|
||||
// We can also make routes available to other plugins.
|
||||
// highlight-start
|
||||
routes: {
|
||||
|
||||
@@ -15,10 +15,6 @@ These are the [extension blueprints](../architecture/23-extension-blueprints.md)
|
||||
|
||||
An API extension is used to add or override [Utility API factories](../utility-apis/01-index.md) in the app. They are commonly used by plugins for both internal and shared APIs. There are also many built-in Api extensions provided by the framework that you are able to override.
|
||||
|
||||
### NavItem (deprecated) - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.index.NavItemBlueprint.html)
|
||||
|
||||
The `NavItemBlueprint` is deprecated. The app now auto-discovers navigation items from page extensions, so explicit nav item extensions are no longer needed. To migrate, ensure your plugin and/or page extensions have a `title` and `icon` set — these are used to populate the sidebar automatically.
|
||||
|
||||
### Page - [Reference](https://backstage.io/api/stable/variables/_backstage_frontend-plugin-api.index.PageBlueprint.html)
|
||||
|
||||
Page extensions provide content for a particular route in the app. By default pages are attached to the app routes extensions, which renders the root routes. Pages automatically inherit the plugin's `title` and `icon` as defaults, which can be overridden per-page via `PageBlueprint` params.
|
||||
|
||||
@@ -6,12 +6,6 @@ app:
|
||||
packages: all
|
||||
|
||||
extensions:
|
||||
# Disable the nav items that we're manually rendering in packages/app/src/modules/nav/Sidebar.tsx
|
||||
- nav-item:search: false
|
||||
- nav-item:user-settings: false
|
||||
- nav-item:catalog: false
|
||||
- nav-item:scaffolder: false
|
||||
|
||||
# Configure the catalog index page to be the root page, this is normally mounted on /catalog
|
||||
- page:catalog:
|
||||
config:
|
||||
|
||||
@@ -1821,43 +1821,6 @@ export const microsoftAuthApiRef: ApiRef_2<
|
||||
readonly $$type: '@backstage/ApiRef';
|
||||
};
|
||||
|
||||
// @public @deprecated
|
||||
export const NavItemBlueprint: ExtensionBlueprint_2<{
|
||||
kind: 'nav-item';
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
output: ExtensionDataRef_2<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
dataRefs: {
|
||||
target: ConfigurableExtensionDataRef_2<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @public (undocumented)
|
||||
export const NotFoundErrorPage: {
|
||||
(props: NotFoundErrorPageProps): JSX.Element | null;
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 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 { createExtensionTester } from '@backstage/frontend-test-utils';
|
||||
import { createRouteRef } from '../routing';
|
||||
import { NavItemBlueprint } from './NavItemBlueprint';
|
||||
|
||||
describe('NavItemBlueprint', () => {
|
||||
const mockRouteRef = createRouteRef();
|
||||
const MockIcon = () => null;
|
||||
|
||||
it('should return an extension with sensible defaults', () => {
|
||||
const extension = NavItemBlueprint.make({
|
||||
params: {
|
||||
icon: MockIcon,
|
||||
routeRef: mockRouteRef,
|
||||
title: 'TEST',
|
||||
},
|
||||
});
|
||||
|
||||
expect(extension).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDefinition",
|
||||
"T": undefined,
|
||||
"attachTo": {
|
||||
"id": "app/nav",
|
||||
"input": "items",
|
||||
},
|
||||
"configSchema": {
|
||||
"parse": [Function],
|
||||
"schema": [Function],
|
||||
},
|
||||
"disabled": false,
|
||||
"factory": [Function],
|
||||
"if": undefined,
|
||||
"inputs": {},
|
||||
"kind": "nav-item",
|
||||
"name": undefined,
|
||||
"output": [
|
||||
[Function],
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
"version": "v2",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should return the correct extension data', () => {
|
||||
const extension = NavItemBlueprint.make({
|
||||
params: {
|
||||
icon: MockIcon,
|
||||
routeRef: mockRouteRef,
|
||||
title: 'TEST',
|
||||
},
|
||||
});
|
||||
|
||||
const tester = createExtensionTester(extension);
|
||||
|
||||
expect(tester.get(NavItemBlueprint.dataRefs.target)).toEqual({
|
||||
title: 'TEST',
|
||||
icon: MockIcon,
|
||||
routeRef: mockRouteRef,
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow overriding of the title using config', () => {
|
||||
const extension = NavItemBlueprint.make({
|
||||
params: {
|
||||
icon: MockIcon,
|
||||
routeRef: mockRouteRef,
|
||||
title: 'TEST',
|
||||
},
|
||||
});
|
||||
|
||||
const tester = createExtensionTester(extension, {
|
||||
config: { title: 'OVERRIDDEN' },
|
||||
});
|
||||
|
||||
expect(tester.get(NavItemBlueprint.dataRefs.target)).toEqual({
|
||||
title: 'OVERRIDDEN',
|
||||
icon: MockIcon,
|
||||
routeRef: mockRouteRef,
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,66 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 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 { z } from 'zod/v4';
|
||||
import { IconComponent } from '../icons/types';
|
||||
import { RouteRef } from '../routing';
|
||||
import { createExtensionBlueprint, createExtensionDataRef } from '../wiring';
|
||||
|
||||
// TODO(Rugvip): Should this be broken apart into separate refs? title/icon/routeRef
|
||||
const targetDataRef = createExtensionDataRef<{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
}>().with({ id: 'core.nav-item.target' });
|
||||
|
||||
/**
|
||||
* Creates extensions that make up the items of the nav bar.
|
||||
*
|
||||
* @public
|
||||
* @deprecated Nav items are now automatically inferred from `PageBlueprint`
|
||||
* extensions based on their `title` and `icon` params. You can remove your
|
||||
* `NavItemBlueprint` usage and instead pass `title` and `icon` directly to
|
||||
* the `PageBlueprint`.
|
||||
*/
|
||||
export const NavItemBlueprint = createExtensionBlueprint({
|
||||
kind: 'nav-item',
|
||||
attachTo: { id: 'app/nav', input: 'items' },
|
||||
output: [targetDataRef],
|
||||
dataRefs: {
|
||||
target: targetDataRef,
|
||||
},
|
||||
factory: (
|
||||
{
|
||||
icon,
|
||||
routeRef,
|
||||
title,
|
||||
}: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
{ config },
|
||||
) => [
|
||||
targetDataRef({
|
||||
title: config.title ?? title,
|
||||
icon,
|
||||
routeRef,
|
||||
}),
|
||||
],
|
||||
configSchema: {
|
||||
title: z.string().optional(),
|
||||
},
|
||||
});
|
||||
@@ -20,7 +20,6 @@ export {
|
||||
} from './AnalyticsImplementationBlueprint';
|
||||
export { ApiBlueprint } from './ApiBlueprint';
|
||||
export { AppRootElementBlueprint } from './AppRootElementBlueprint';
|
||||
export { NavItemBlueprint } from './NavItemBlueprint';
|
||||
export { PageBlueprint } from './PageBlueprint';
|
||||
export { SubPageBlueprint } from './SubPageBlueprint';
|
||||
export { PluginHeaderActionBlueprint } from './PluginHeaderActionBlueprint';
|
||||
|
||||
@@ -22,12 +22,12 @@ import { ConfigReader } from '@backstage/config';
|
||||
import { JsonObject } from '@backstage/types';
|
||||
import {
|
||||
createExtension,
|
||||
createExtensionDataRef,
|
||||
ExtensionDefinition,
|
||||
coreExtensionData,
|
||||
RouteRef,
|
||||
useRouteRef,
|
||||
IconComponent,
|
||||
NavItemBlueprint,
|
||||
createFrontendPlugin,
|
||||
FrontendFeature,
|
||||
createFrontendModule,
|
||||
@@ -49,6 +49,13 @@ const DEFAULT_MOCK_CONFIG = {
|
||||
backend: { baseUrl: 'http://localhost:7007' },
|
||||
};
|
||||
|
||||
// Must match the data ref in @backstage/plugin-app/src/extensions/legacyNavItem.ts
|
||||
const legacyNavItemTargetDataRef = createExtensionDataRef<{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
}>().with({ id: 'core.nav-item.target' });
|
||||
|
||||
/**
|
||||
* Options to customize the behavior of the test app.
|
||||
* @public
|
||||
@@ -143,7 +150,7 @@ const appPluginOverride = appPlugin.withOverrides({
|
||||
{inputs.items.map(
|
||||
(item: (typeof inputs.items)[number], index: number) => {
|
||||
const { icon, title, routeRef } = item.get(
|
||||
NavItemBlueprint.dataRefs.target,
|
||||
legacyNavItemTargetDataRef,
|
||||
);
|
||||
|
||||
return (
|
||||
|
||||
@@ -32,7 +32,6 @@ To link that a component provides or consumes an API, see the [`providesApis`](h
|
||||
- [Packages](#packages)
|
||||
- [Routes](#routes)
|
||||
- [Extensions](#extensions)
|
||||
- [Apis Nav Item](#apis-nav-item)
|
||||
- [Apis Explorer Page](#apis-explore-page)
|
||||
- [Apis Entity Cards](#apis-entities-cards)
|
||||
- [Has Apis Entity Card](#has-apis-entity-card)
|
||||
@@ -135,94 +134,6 @@ Route binding is also possible through code. For more information, see [this](ht
|
||||
|
||||
### Extensions
|
||||
|
||||
#### Apis Nav Item
|
||||
|
||||
This [nav item](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension) extension adds a link to the Apis Explorer page in the main app sidebar.
|
||||
|
||||
| Kind | Namespace | Name | Id |
|
||||
| ---------- | ---------- | ---- | ------------------- |
|
||||
| `nav-item` | `api-docs` | - | `nav-item:api-docs` |
|
||||
|
||||
##### Disable
|
||||
|
||||
This extension is enabled by default when you install the `api-docs` plugin, but you can disable it and prevent it from showing up in the sidebar by setting the following configuration:
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
app:
|
||||
extensions:
|
||||
# this is the extension id and it follows the naming pattern bellow:
|
||||
# <extension-kind>/<plugin-namespace>:<extension-name>
|
||||
# example disabling the apis docs nav item extension
|
||||
- nav-item:api-docs: false
|
||||
# or
|
||||
# - nav-item:api-docs:
|
||||
# disabled: true
|
||||
```
|
||||
|
||||
To enable the extension again, simple remove the previous `nav-item:api-docs: false` configuration or do:
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
app:
|
||||
extensions:
|
||||
# this is the extension id and it follows the naming pattern bellow:
|
||||
# <extension-kind>/<plugin-namespace>:<extension-name>
|
||||
- nav-item:api-docs
|
||||
# or
|
||||
# - nav-item:api-docs: true
|
||||
# or
|
||||
# - nav-item:api-docs:
|
||||
# disabled: false
|
||||
```
|
||||
|
||||
##### Config
|
||||
|
||||
The apis nav item can be customized under the `app.extensions.nav-item:api-docs.config` key in `app-config.yaml`. Configurations include:
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
# example configuring the apis docs nav item extension
|
||||
app:
|
||||
extensions:
|
||||
# this is the extension id and it follows the naming pattern bellow:
|
||||
# <extension-kind>/<plugin-namespace>:<extension-name>
|
||||
- nav-item:api-docs:
|
||||
config:
|
||||
# The nav item title text, defaults to "APIs"
|
||||
title: 'Apis Explorer'
|
||||
# The nav item path text, defaults to "/api-docs"
|
||||
path: '/apis-explorer'
|
||||
```
|
||||
|
||||
##### Override
|
||||
|
||||
The apis nav item icon can only be changed by overriding the extension, as the icon cannot be changed via the `app-config.yaml` file.
|
||||
|
||||
Here is an example overriding the nav item extension with a custom icon component:
|
||||
|
||||
```tsx
|
||||
import {
|
||||
createFrontendModule,
|
||||
createNavItemExtension,
|
||||
} from '@backstage/backstage-plugin-api';
|
||||
import { MyCustomApiDocsIcon } from './components';
|
||||
|
||||
export default createFrontendModule({
|
||||
pluginId: 'api-docs',
|
||||
extensions: [
|
||||
createNavItemExtension({
|
||||
// It's your choice whether to use the original extension's title or a different one
|
||||
title: 'APIs',
|
||||
// Setting a custom icon component
|
||||
icon: MyCustomApiDocsIcon,
|
||||
}),
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
For more information about where to place extension overrides, see the official [documentation](https://backstage.io/docs/frontend-system/architecture/extension-overrides).
|
||||
|
||||
#### Apis Explore Page
|
||||
|
||||
This `api-docs` plugin installs an "Apis Explore" page extension that helps you visualize apis registered in the Backstage software catalog.
|
||||
@@ -235,9 +146,6 @@ This `api-docs` plugin installs an "Apis Explore" page extension that helps you
|
||||
|
||||
The explore page extension is enable by default when you install the `api-docs` plugin, for disabling it, set the configuration below:
|
||||
|
||||
> [!CAUTION]
|
||||
> The `api-docs` plugin also install a sidebar item that points to this page, remember to disable the nav item as well otherwise it will point to a not found page.
|
||||
|
||||
```yaml
|
||||
# app-config.yaml
|
||||
# example disabling the apis docs explorer page
|
||||
|
||||
@@ -15,7 +15,6 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { ExternalRouteRef } from '@backstage/core-plugin-api';
|
||||
import { FilterPredicate } from '@backstage/filter-predicates';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { JSXElementConstructor } from 'react';
|
||||
@@ -472,31 +471,6 @@ const _default: OverridableFrontendPlugin<
|
||||
filter?: string | FilterPredicate | ((entity: Entity) => boolean);
|
||||
};
|
||||
}>;
|
||||
'nav-item:api-docs': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:api-docs': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
initiallySelectedFilter: 'all' | 'owned' | 'starred' | undefined;
|
||||
|
||||
@@ -18,7 +18,6 @@ import Grid from '@material-ui/core/Grid';
|
||||
|
||||
import {
|
||||
ApiBlueprint,
|
||||
NavItemBlueprint,
|
||||
PageBlueprint,
|
||||
createFrontendPlugin,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
@@ -39,14 +38,6 @@ import {
|
||||
EntityContentBlueprint,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
const apiDocsNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
title: 'APIs',
|
||||
routeRef: rootRoute,
|
||||
icon: () => <AppIcon fontSize="inherit" id="kind:api" />,
|
||||
},
|
||||
});
|
||||
|
||||
const apiDocsConfigApi = ApiBlueprint.make({
|
||||
name: 'config',
|
||||
params: defineParams =>
|
||||
@@ -76,6 +67,8 @@ const apiDocsExplorerPage = PageBlueprint.makeWithOverrides({
|
||||
return originalFactory({
|
||||
path: '/api-docs',
|
||||
routeRef: rootRoute,
|
||||
title: 'APIs',
|
||||
icon: <AppIcon fontSize="inherit" id="kind:api" />,
|
||||
loader: () =>
|
||||
import('./components/ApiExplorerPage/DefaultApiExplorerPage').then(
|
||||
m => (
|
||||
@@ -222,7 +215,6 @@ export default createFrontendPlugin({
|
||||
registerApi: registerComponentRouteRef,
|
||||
},
|
||||
extensions: [
|
||||
apiDocsNavItem,
|
||||
apiDocsConfigApi,
|
||||
apiDocsExplorerPage,
|
||||
apiDocsHasApisEntityCard,
|
||||
|
||||
@@ -4,7 +4,6 @@ A plugin to help explore the structure of your Backstage app.
|
||||
|
||||
This plugin provides the following extensions:
|
||||
|
||||
| ID | Type | Description | Default Config |
|
||||
| ------------------------- | --------- | ------------------------------------ | ------------------------- |
|
||||
| `page:app-visualizer` | `Page` | The app visualizer page | `{ path: '/visualizer' }` |
|
||||
| `nav-item:app-visualizer` | `NavItem` | Nav item for the app visualizer page | |
|
||||
| ID | Type | Description | Default Config |
|
||||
| --------------------- | ------ | ----------------------- | ------------------------- |
|
||||
| `page:app-visualizer` | `Page` | The app visualizer page | `{ path: '/visualizer' }` |
|
||||
|
||||
@@ -7,7 +7,6 @@ import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
@@ -19,31 +18,6 @@ const visualizerPlugin: OverridableFrontendPlugin<
|
||||
{},
|
||||
{},
|
||||
{
|
||||
'nav-item:app-visualizer': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:app-visualizer': OverridableExtensionDefinition<{
|
||||
kind: 'page';
|
||||
name: undefined;
|
||||
|
||||
@@ -19,11 +19,15 @@ import {
|
||||
ExtensionDataRef,
|
||||
coreExtensionData,
|
||||
ApiBlueprint,
|
||||
NavItemBlueprint,
|
||||
useApi,
|
||||
routeResolutionApiRef,
|
||||
appTreeApiRef,
|
||||
createExtensionDataRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
const legacyNavItemTargetDataRef = createExtensionDataRef<unknown>().with({
|
||||
id: 'core.nav-item.target',
|
||||
});
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -86,7 +90,7 @@ const getOutputColor = createOutputColorGenerator(
|
||||
[coreExtensionData.routePath.id]: '#ffeb3b',
|
||||
[coreExtensionData.routeRef.id]: '#9c27b0',
|
||||
[ApiBlueprint.dataRefs.factory.id]: '#2196f3',
|
||||
[NavItemBlueprint.dataRefs.target.id]: '#ff9800',
|
||||
[legacyNavItemTargetDataRef.id]: '#ff9800',
|
||||
},
|
||||
|
||||
['#90caf9', '#ffcc80', '#a5d6a7', '#ef9a9a', '#fff59d', '#ce93d8', '#e6ee9c'],
|
||||
@@ -335,7 +339,7 @@ const legendMap = {
|
||||
'Utility API': ApiBlueprint.dataRefs.factory,
|
||||
'Route Path': coreExtensionData.routePath,
|
||||
'Route Ref': coreExtensionData.routeRef,
|
||||
'Nav Target': NavItemBlueprint.dataRefs.target,
|
||||
'Nav Target': legacyNavItemTargetDataRef,
|
||||
};
|
||||
|
||||
function Legend() {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import {
|
||||
createFrontendPlugin,
|
||||
createRouteRef,
|
||||
NavItemBlueprint,
|
||||
PageBlueprint,
|
||||
PluginHeaderActionBlueprint,
|
||||
SubPageBlueprint,
|
||||
@@ -31,6 +30,7 @@ const appVisualizerPage = PageBlueprint.make({
|
||||
path: '/visualizer',
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Visualizer',
|
||||
icon: <RiEyeLine />,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -82,14 +82,6 @@ const copyTreeAsJson = PluginHeaderActionBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
export const appVisualizerNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
title: 'Visualizer',
|
||||
icon: () => <RiEyeLine />,
|
||||
routeRef: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
/** @public */
|
||||
export const visualizerPlugin = createFrontendPlugin({
|
||||
pluginId: 'app-visualizer',
|
||||
@@ -101,7 +93,6 @@ export const visualizerPlugin = createFrontendPlugin({
|
||||
appVisualizerTreePage,
|
||||
appVisualizerDetailedPage,
|
||||
appVisualizerTextPage,
|
||||
appVisualizerNavItem,
|
||||
copyTreeAsJson,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -18,38 +18,46 @@ import { screen, waitFor, within } from '@testing-library/react';
|
||||
import { renderTestApp } from '@backstage/frontend-test-utils';
|
||||
import {
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
createExtension,
|
||||
createRouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { legacyNavItemTargetDataRef } from './legacyNavItem';
|
||||
|
||||
const DEFAULT_CONFIG = {
|
||||
app: { baseUrl: 'http://localhost:3000' },
|
||||
backend: { baseUrl: 'http://localhost:7007' },
|
||||
};
|
||||
|
||||
const mockRouteRef = createRouteRef();
|
||||
|
||||
const mockPage = PageBlueprint.make({
|
||||
name: 'my-plugin',
|
||||
params: {
|
||||
title: 'My Plugin',
|
||||
icon: <span>icon</span>,
|
||||
path: '/my-plugin',
|
||||
routeRef: createRouteRef(),
|
||||
routeRef: mockRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
const mockNavItem = NavItemBlueprint.make({
|
||||
const mockLegacyNavItem = createExtension({
|
||||
kind: 'nav-item',
|
||||
name: 'my-plugin',
|
||||
params: {
|
||||
title: 'My Plugin',
|
||||
icon: () => <span>icon</span>,
|
||||
routeRef: createRouteRef(),
|
||||
},
|
||||
attachTo: { id: 'app/nav', input: 'items' },
|
||||
output: [legacyNavItemTargetDataRef],
|
||||
factory: () => [
|
||||
legacyNavItemTargetDataRef({
|
||||
title: 'Legacy Nav Title',
|
||||
icon: () => <span>legacy icon</span>,
|
||||
routeRef: mockRouteRef,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
describe('AppNav', () => {
|
||||
it('should show a nav item for a page with an enabled nav-item extension', async () => {
|
||||
it('should show a nav item for a page with title and icon', async () => {
|
||||
renderTestApp({
|
||||
extensions: [mockPage, mockNavItem],
|
||||
extensions: [mockPage],
|
||||
config: DEFAULT_CONFIG,
|
||||
});
|
||||
|
||||
@@ -60,40 +68,24 @@ describe('AppNav', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should hide a nav item when its nav-item extension is disabled via config', async () => {
|
||||
renderTestApp({
|
||||
extensions: [mockPage, mockNavItem],
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
app: {
|
||||
...DEFAULT_CONFIG.app,
|
||||
extensions: [{ 'nav-item:test/my-plugin': false }],
|
||||
},
|
||||
it('should merge legacy nav item metadata when page has no explicit title', async () => {
|
||||
const pageWithoutTitle = PageBlueprint.make({
|
||||
name: 'legacy-plugin',
|
||||
params: {
|
||||
path: '/legacy-plugin',
|
||||
routeRef: mockRouteRef,
|
||||
icon: <span>page icon</span>,
|
||||
},
|
||||
});
|
||||
|
||||
renderTestApp({
|
||||
extensions: [pageWithoutTitle, mockLegacyNavItem],
|
||||
config: DEFAULT_CONFIG,
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
within(screen.getByRole('navigation')).queryByText('My Plugin'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('should still show a nav item for a page without a nav-item extension', async () => {
|
||||
renderTestApp({
|
||||
extensions: [mockPage],
|
||||
config: {
|
||||
...DEFAULT_CONFIG,
|
||||
app: {
|
||||
...DEFAULT_CONFIG.app,
|
||||
extensions: [{ 'nav-item:test/my-plugin': false }],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
within(screen.getByRole('navigation')).getByText('My Plugin'),
|
||||
within(screen.getByRole('navigation')).getByText('Legacy Nav Title'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
createExtension,
|
||||
coreExtensionData,
|
||||
createExtensionInput,
|
||||
NavItemBlueprint,
|
||||
routeResolutionApiRef,
|
||||
appTreeApiRef,
|
||||
IconComponent,
|
||||
@@ -27,6 +26,7 @@ import {
|
||||
RouteResolutionApi,
|
||||
useApi,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { legacyNavItemTargetDataRef } from './legacyNavItem';
|
||||
import {
|
||||
NavContentBlueprint,
|
||||
NavContentComponent,
|
||||
@@ -248,7 +248,7 @@ export const AppNav = createExtension({
|
||||
name: 'nav',
|
||||
attachTo: { id: 'app/layout', input: 'nav' },
|
||||
inputs: {
|
||||
items: createExtensionInput([NavItemBlueprint.dataRefs.target]),
|
||||
items: createExtensionInput([legacyNavItemTargetDataRef]),
|
||||
content: createExtensionInput([NavContentBlueprint.dataRefs.component], {
|
||||
singleton: true,
|
||||
optional: true,
|
||||
@@ -264,7 +264,7 @@ export const AppNav = createExtension({
|
||||
yield coreExtensionData.reactElement(
|
||||
<NavContentRenderer
|
||||
legacyNavItems={inputs.items.map(item =>
|
||||
item.get(NavItemBlueprint.dataRefs.target),
|
||||
item.get(legacyNavItemTargetDataRef),
|
||||
)}
|
||||
Content={Content}
|
||||
/>,
|
||||
|
||||
+17
-13
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
* Copyright 2026 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.
|
||||
@@ -14,16 +14,20 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import HomeIcon from '@material-ui/icons/Home';
|
||||
import { NavItemBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { rootRouteRef } from '../routes';
|
||||
import {
|
||||
createExtensionDataRef,
|
||||
IconComponent,
|
||||
RouteRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
export const catalogNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Catalog',
|
||||
icon: HomeIcon,
|
||||
},
|
||||
});
|
||||
|
||||
export default [catalogNavItem];
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Data ref for legacy nav-item extensions. Kept for backward compatibility with
|
||||
* extensions created by older versions of the framework.
|
||||
*/
|
||||
export const legacyNavItemTargetDataRef = createExtensionDataRef<{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
}>().with({ id: 'core.nav-item.target' });
|
||||
@@ -10,7 +10,6 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
@@ -40,31 +39,6 @@ const _default: OverridableFrontendPlugin<
|
||||
params: ApiFactory<TApi, TImpl, TDeps>,
|
||||
) => ExtensionBlueprintParams<AnyApiFactory>;
|
||||
}>;
|
||||
'nav-item:catalog-unprocessed-entities': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:catalog-unprocessed-entities': OverridableExtensionDefinition<{
|
||||
kind: 'page';
|
||||
name: undefined;
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
fetchApiRef,
|
||||
ApiBlueprint,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
SubPageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -52,6 +51,8 @@ export const catalogUnprocessedEntitiesPage = PageBlueprint.make({
|
||||
params: {
|
||||
path: '/catalog-unprocessed-entities',
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Unprocessed Entities',
|
||||
icon: <QueueIcon fontSize="inherit" />,
|
||||
loader: () =>
|
||||
import('../components/UnprocessedEntities').then(m => (
|
||||
<m.NfsUnprocessedEntities />
|
||||
@@ -59,15 +60,6 @@ export const catalogUnprocessedEntitiesPage = PageBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export const catalogUnprocessedEntitiesNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
title: 'Unprocessed Entities',
|
||||
routeRef: rootRouteRef,
|
||||
icon: QueueIcon,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* DevTools content for catalog unprocessed entities.
|
||||
*
|
||||
@@ -99,7 +91,6 @@ export default createFrontendPlugin({
|
||||
extensions: [
|
||||
catalogUnprocessedEntitiesApi,
|
||||
catalogUnprocessedEntitiesPage,
|
||||
catalogUnprocessedEntitiesNavItem,
|
||||
unprocessedEntitiesDevToolsContent,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -21,7 +21,6 @@ import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { ExternalRouteRef } from '@backstage/core-plugin-api';
|
||||
import { FilterPredicate } from '@backstage/filter-predicates';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { IconLinkVerticalProps } from '@backstage/core-components';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
@@ -1027,31 +1026,6 @@ const _default: OverridableFrontendPlugin<
|
||||
filter?: FilterPredicate | ((entity: Entity) => boolean);
|
||||
};
|
||||
}>;
|
||||
'nav-item:catalog': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:catalog': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
pagination:
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
import apis from './apis';
|
||||
import pages from './pages';
|
||||
import filters from './filters';
|
||||
import navItems from './navItems';
|
||||
import entityCards from './entityCards';
|
||||
import entityContents from './entityContents';
|
||||
import entityIconLinks from './entityIconLinks';
|
||||
@@ -58,7 +57,6 @@ export default createFrontendPlugin({
|
||||
...apis,
|
||||
...pages,
|
||||
...filters,
|
||||
...navItems,
|
||||
...entityCards,
|
||||
...entityContents,
|
||||
...entityIconLinks,
|
||||
|
||||
@@ -10,7 +10,6 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
@@ -40,31 +39,6 @@ const _default: OverridableFrontendPlugin<
|
||||
params: ApiFactory<TApi, TImpl, TDeps>,
|
||||
) => ExtensionBlueprintParams<AnyApiFactory>;
|
||||
}>;
|
||||
'nav-item:devtools': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:devtools': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
path: string | undefined;
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
fetchApiRef,
|
||||
ApiBlueprint,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
SubPageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -72,6 +71,7 @@ export const devToolsPage = PageBlueprint.makeWithOverrides({
|
||||
path: '/devtools',
|
||||
routeRef: rootRouteRef,
|
||||
title: 'DevTools',
|
||||
icon: <BuildIcon fontSize="inherit" />,
|
||||
},
|
||||
{
|
||||
inputs: {
|
||||
@@ -133,15 +133,6 @@ export const devToolsScheduledTasksPage = SubPageBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export const devToolsNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
title: 'DevTools',
|
||||
routeRef: rootRouteRef,
|
||||
icon: BuildIcon,
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export default createFrontendPlugin({
|
||||
pluginId: 'devtools',
|
||||
@@ -157,6 +148,5 @@ export default createFrontendPlugin({
|
||||
devToolsInfoPage,
|
||||
devToolsConfigPage,
|
||||
devToolsScheduledTasksPage,
|
||||
devToolsNavItem,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { HomePageLayoutProps } from '@backstage/plugin-home-react/alpha';
|
||||
import { HomePageWidgetBlueprintParams } from '@backstage/plugin-home-react/alpha';
|
||||
import { HomePageWidgetData } from '@backstage/plugin-home-react/alpha';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
@@ -81,31 +80,6 @@ const _default: OverridableFrontendPlugin<
|
||||
inputs: {};
|
||||
params: HomePageWidgetBlueprintParams;
|
||||
}>;
|
||||
'nav-item:home': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:home': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
path: string | undefined;
|
||||
|
||||
@@ -38,10 +38,6 @@ describe('Home Plugin Alpha', () => {
|
||||
it('should export core home page extension', () => {
|
||||
expect(homePlugin.getExtension('page:home')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should export navigation item extension', () => {
|
||||
expect(homePlugin.getExtension('nav-item:home')).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Optional Extensions (Disabled by Default)', () => {
|
||||
@@ -99,7 +95,6 @@ describe('Home Plugin Alpha', () => {
|
||||
it('should include all extensions in the correct order', () => {
|
||||
// Core extensions (always enabled)
|
||||
expect(homePlugin.getExtension('page:home')).toBeDefined();
|
||||
expect(homePlugin.getExtension('nav-item:home')).toBeDefined();
|
||||
|
||||
// Optional extensions (disabled by default)
|
||||
expect(homePlugin.getExtension('api:home/visits')).toBeDefined();
|
||||
|
||||
@@ -28,7 +28,6 @@ import { lazy as reactLazy } from 'react';
|
||||
import {
|
||||
createExtensionInput,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
createFrontendPlugin,
|
||||
createRouteRef,
|
||||
AppRootElementBlueprint,
|
||||
@@ -65,6 +64,8 @@ const homePage = PageBlueprint.makeWithOverrides({
|
||||
path: '/home',
|
||||
noHeader: true,
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Home',
|
||||
icon: <HomeIcon fontSize="inherit" />,
|
||||
loader: async () => {
|
||||
const LazyDefaultLayout = reactLazy(() =>
|
||||
import('./alpha/DefaultHomePageLayout').then(m => ({
|
||||
@@ -122,14 +123,6 @@ const visitsApi = ApiBlueprint.make({
|
||||
}),
|
||||
});
|
||||
|
||||
const homeNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
title: 'Home',
|
||||
routeRef: rootRouteRef,
|
||||
icon: HomeIcon,
|
||||
},
|
||||
});
|
||||
|
||||
const homePageToolkitWidget = HomePageWidgetBlueprint.make({
|
||||
name: 'toolkit',
|
||||
params: {
|
||||
@@ -213,7 +206,6 @@ export default createFrontendPlugin({
|
||||
info: { packageJson: () => import('../package.json') },
|
||||
extensions: [
|
||||
homePage,
|
||||
homeNavItem,
|
||||
visitsApi,
|
||||
visitListenerAppRootElement,
|
||||
homePageToolkitWidget,
|
||||
|
||||
+27
-57
@@ -305,67 +305,37 @@ For more information about where to place extension overrides, see the official
|
||||
|
||||
### My Groups Sidebar Item
|
||||
|
||||
As the [NavItem](https://backstage.io/docs/reference/frontend-plugin-api.createnavitemextension) extension type does not support conditional rendering, this plugin does not provide navigation items, so to use the `MyGroupsSidebarItem` component, we recommend overriding the [App/Nav](https://backstage.io/docs/frontend-system/building-apps/built-in-extensions#app-nav) extension and adding the item statically.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> As you can see in the example below, we are using the same attachment point, inputs and outputs as the default App/Nav extension to avoid side effects on the NavItem and NavLogo extensions.
|
||||
This plugin does not provide a page extension for the groups sidebar item, since it requires conditional rendering based on the logged-in user. To use the `MyGroupsSidebarItem` component, add it to your custom sidebar implementation using the `NavContentBlueprint` in `packages/app/src/modules/nav/Sidebar.tsx`:
|
||||
|
||||
```tsx
|
||||
// ...
|
||||
import { MyGroupsSidebarItem } from '@backstage/plugin-org';
|
||||
import GroupIcon from '@material-ui/icons/People';
|
||||
import { NavContentBlueprint } from '@backstage/plugin-app-react';
|
||||
|
||||
export default createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
createExtension({
|
||||
// Name is necessary so the system knows that this extension will override the default app nav extension
|
||||
name: 'nav',
|
||||
// Keeping the same attachment point as in the default App/Nav extension
|
||||
attachTo: { id: 'app/layout', input: 'nav' },
|
||||
// Keeping the same inputs as in the default App/Nav extension
|
||||
inputs: {
|
||||
items: createExtensionInput({
|
||||
target: createNavItemExtension.targetDataRef,
|
||||
}),
|
||||
logos: createExtensionInput(
|
||||
{
|
||||
elements: createNavLogoExtension.logoElementsDataRef,
|
||||
},
|
||||
{
|
||||
singleton: true,
|
||||
optional: true,
|
||||
},
|
||||
),
|
||||
},
|
||||
// Keeping the same output as in the default App/Nav extension
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
factory({ inputs }) {
|
||||
return {
|
||||
element: (
|
||||
<Sidebar>
|
||||
{/* Code borrowed from the default extension implementation to render the logos and items inputs */}
|
||||
<SidebarLogo {...inputs.logos?.output.elements} />
|
||||
<SidebarDivider />
|
||||
{inputs.items.map((item, index) => (
|
||||
<SidebarNavItem {...item.output.target} key={index} />
|
||||
))}
|
||||
{/* Here is where we actually modifies the default implementation by adding a static item to render a group of squad pages */}
|
||||
<SidebarGroup label="Menu" icon={<MenuIcon />}>
|
||||
{/* The MyGroupsSidebarItem provides quick access to the group(s) the logged in user is a member of directly in the sidebar. */}
|
||||
<MyGroupsSidebarItem
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
/>
|
||||
</SidebarGroup>
|
||||
</Sidebar>
|
||||
),
|
||||
};
|
||||
},
|
||||
}),
|
||||
],
|
||||
export const SidebarContent = NavContentBlueprint.make({
|
||||
params: {
|
||||
component: ({ navItems }) => {
|
||||
const nav = navItems.withComponent(item => (
|
||||
<SidebarItem icon={() => item.icon} to={item.href} text={item.title} />
|
||||
));
|
||||
|
||||
return (
|
||||
<Sidebar>
|
||||
<SidebarLogo />
|
||||
<SidebarDivider />
|
||||
<SidebarGroup label="Menu" icon={<MenuIcon />}>
|
||||
{nav.rest()}
|
||||
<MyGroupsSidebarItem
|
||||
singularTitle="My Squad"
|
||||
pluralTitle="My Squads"
|
||||
icon={GroupIcon}
|
||||
/>
|
||||
</SidebarGroup>
|
||||
</Sidebar>
|
||||
);
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
For more details on customizing the sidebar, see the [app migration guide](https://backstage.io/docs/frontend-system/building-apps/migrating#app-root-sidebar).
|
||||
|
||||
@@ -20,7 +20,6 @@ import { FormField } from '@backstage/plugin-scaffolder-react/alpha';
|
||||
import { formFieldsApiRef } from '@backstage/plugin-scaffolder-react/alpha';
|
||||
import type { FormProps as FormProps_2 } from '@rjsf/core';
|
||||
import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { IconLinkVerticalProps } from '@backstage/core-components';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
@@ -173,31 +172,6 @@ const _default: OverridableFrontendPlugin<
|
||||
filter?: FilterPredicate | ((entity: Entity) => boolean);
|
||||
};
|
||||
}>;
|
||||
'nav-item:scaffolder': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:scaffolder': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
path: string | undefined;
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
discoveryApiRef,
|
||||
fetchApiRef,
|
||||
identityApiRef,
|
||||
NavItemBlueprint,
|
||||
PageBlueprint,
|
||||
SubPageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
@@ -52,7 +51,8 @@ export const scaffolderPage = PageBlueprint.makeWithOverrides({
|
||||
return originalFactory({
|
||||
routeRef: rootRouteRef,
|
||||
path: '/create',
|
||||
title: 'Create',
|
||||
title: 'Create...',
|
||||
icon: <CreateComponentIcon fontSize="inherit" />,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -154,14 +154,6 @@ export const scaffolderTemplatingExtensionsSubPage = SubPageBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
export const scaffolderNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Create...',
|
||||
icon: CreateComponentIcon,
|
||||
},
|
||||
});
|
||||
|
||||
export const repoUrlPickerFormField = FormFieldBlueprint.make({
|
||||
name: 'repo-url-picker',
|
||||
params: {
|
||||
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
repoOwnerPickerFormField,
|
||||
repoUrlPickerFormField,
|
||||
scaffolderApi,
|
||||
scaffolderNavItem,
|
||||
scaffolderPage,
|
||||
scaffolderTemplatesSubPage,
|
||||
scaffolderTasksSubPage,
|
||||
@@ -89,7 +88,6 @@ export default createFrontendPlugin({
|
||||
scaffolderActionsSubPage,
|
||||
scaffolderEditorSubPage,
|
||||
scaffolderTemplatingExtensionsSubPage,
|
||||
scaffolderNavItem,
|
||||
scaffolderEntityIconLink,
|
||||
formDecoratorsApi,
|
||||
formFieldsApi,
|
||||
|
||||
@@ -10,7 +10,6 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
@@ -44,31 +43,6 @@ const _default: OverridableFrontendPlugin<
|
||||
params: ApiFactory<TApi, TImpl, TDeps>,
|
||||
) => ExtensionBlueprintParams<AnyApiFactory>;
|
||||
}>;
|
||||
'nav-item:search': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:search': OverridableExtensionDefinition<{
|
||||
config: {
|
||||
noTrack: boolean;
|
||||
@@ -214,33 +188,6 @@ export const searchApi: OverridableExtensionDefinition<{
|
||||
) => ExtensionBlueprintParams<AnyApiFactory>;
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchNavItem: OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const searchPage: OverridableExtensionDefinition<{
|
||||
config: {
|
||||
|
||||
@@ -37,7 +37,6 @@ import {
|
||||
ApiBlueprint,
|
||||
createExtensionInput,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
configApiRef,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
|
||||
@@ -110,6 +109,8 @@ export const searchPage = PageBlueprint.makeWithOverrides({
|
||||
return originalFactory({
|
||||
path: '/search',
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Search',
|
||||
icon: <SearchIcon fontSize="inherit" />,
|
||||
loader: async () => {
|
||||
const getResultItemComponent = (result: SearchResult) => {
|
||||
const value = inputs.items.find(item =>
|
||||
@@ -258,22 +259,13 @@ export const searchPage = PageBlueprint.makeWithOverrides({
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export const searchNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Search',
|
||||
icon: SearchIcon,
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export default createFrontendPlugin({
|
||||
pluginId: 'search',
|
||||
title: 'Search',
|
||||
icon: <SearchIcon fontSize="inherit" />,
|
||||
info: { packageJson: () => import('../package.json') },
|
||||
extensions: [searchApi, searchPage, searchNavItem],
|
||||
extensions: [searchApi, searchPage],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
|
||||
@@ -13,7 +13,6 @@ import { ExtensionBlueprintParams } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { FilterPredicate } from '@backstage/filter-predicates';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { IconLinkVerticalProps } from '@backstage/core-components';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
@@ -259,31 +258,6 @@ const _default: OverridableFrontendPlugin<
|
||||
filter?: FilterPredicate | ((entity: Entity) => boolean);
|
||||
};
|
||||
}>;
|
||||
'nav-item:techdocs': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef_2<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:techdocs': OverridableExtensionDefinition<{
|
||||
kind: 'page';
|
||||
name: undefined;
|
||||
|
||||
@@ -21,7 +21,6 @@ import {
|
||||
createFrontendPlugin,
|
||||
ApiBlueprint,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
PluginHeaderActionBlueprint,
|
||||
createExtensionInput,
|
||||
coreExtensionData,
|
||||
@@ -139,6 +138,8 @@ const techDocsPage = PageBlueprint.make({
|
||||
params: {
|
||||
path: '/docs',
|
||||
routeRef: rootRouteRef,
|
||||
title: 'Docs',
|
||||
icon: <RiArticleLine />,
|
||||
loader: () =>
|
||||
import('./components/TechDocsIndexPageContent').then(m => (
|
||||
<m.TechDocsIndexPageContent />
|
||||
@@ -265,15 +266,6 @@ const techDocsEntityContentEmptyState = createExtension({
|
||||
factory: () => [],
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
const techDocsNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
icon: () => <RiArticleLine />,
|
||||
title: 'Docs',
|
||||
routeRef: rootRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
const techDocsSupportAction = PluginHeaderActionBlueprint.make({
|
||||
params: defineParams =>
|
||||
defineParams({
|
||||
@@ -293,7 +285,6 @@ export default createFrontendPlugin({
|
||||
techDocsClientApi,
|
||||
techDocsStorageApi,
|
||||
TechDocsAddonsApiExtension,
|
||||
techDocsNavItem,
|
||||
techDocsSupportAction,
|
||||
techDocsPage,
|
||||
techDocsReaderPage,
|
||||
|
||||
@@ -7,47 +7,21 @@ import { AnyRouteRefParams } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInput } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/frontend-plugin-api';
|
||||
import { IconElement } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverridableExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { OverridableFrontendPlugin } from '@backstage/frontend-plugin-api';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { RouteRef as RouteRef_2 } from '@backstage/core-plugin-api';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { RouteRef as RouteRef_2 } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
const _default: OverridableFrontendPlugin<
|
||||
{
|
||||
root: RouteRef_2<undefined>;
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{},
|
||||
{
|
||||
'nav-item:user-settings': OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
'page:user-settings': OverridableExtensionDefinition<{
|
||||
kind: 'page';
|
||||
name: undefined;
|
||||
@@ -62,7 +36,7 @@ const _default: OverridableFrontendPlugin<
|
||||
output:
|
||||
| ExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
RouteRef_2<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
@@ -88,7 +62,7 @@ const _default: OverridableFrontendPlugin<
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
RouteRef_2<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
@@ -120,7 +94,7 @@ const _default: OverridableFrontendPlugin<
|
||||
title?: string;
|
||||
icon?: IconElement;
|
||||
loader?: () => Promise<JSX_2.Element>;
|
||||
routeRef?: RouteRef;
|
||||
routeRef?: RouteRef_2;
|
||||
noHeader?: boolean;
|
||||
};
|
||||
}>;
|
||||
@@ -136,7 +110,7 @@ const _default: OverridableFrontendPlugin<
|
||||
output:
|
||||
| ExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
RouteRef_2<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
@@ -168,7 +142,7 @@ const _default: OverridableFrontendPlugin<
|
||||
title: string;
|
||||
icon?: IconElement;
|
||||
loader: () => Promise<JSX.Element>;
|
||||
routeRef?: RouteRef;
|
||||
routeRef?: RouteRef_2;
|
||||
};
|
||||
}>;
|
||||
'sub-page:user-settings/feature-flags': OverridableExtensionDefinition<{
|
||||
@@ -185,7 +159,7 @@ const _default: OverridableFrontendPlugin<
|
||||
output:
|
||||
| ExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
RouteRef_2<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
@@ -206,7 +180,7 @@ const _default: OverridableFrontendPlugin<
|
||||
title: string;
|
||||
icon?: IconElement;
|
||||
loader: () => Promise<JSX.Element>;
|
||||
routeRef?: RouteRef;
|
||||
routeRef?: RouteRef_2;
|
||||
};
|
||||
}>;
|
||||
'sub-page:user-settings/general': OverridableExtensionDefinition<{
|
||||
@@ -223,7 +197,7 @@ const _default: OverridableFrontendPlugin<
|
||||
output:
|
||||
| ExtensionDataRef<string, 'core.routing.path', {}>
|
||||
| ExtensionDataRef<
|
||||
RouteRef<AnyRouteRefParams>,
|
||||
RouteRef_2<AnyRouteRefParams>,
|
||||
'core.routing.ref',
|
||||
{
|
||||
optional: true;
|
||||
@@ -244,40 +218,13 @@ const _default: OverridableFrontendPlugin<
|
||||
title: string;
|
||||
icon?: IconElement;
|
||||
loader: () => Promise<JSX.Element>;
|
||||
routeRef?: RouteRef;
|
||||
routeRef?: RouteRef_2;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
>;
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const settingsNavItem: OverridableExtensionDefinition<{
|
||||
kind: 'nav-item';
|
||||
name: undefined;
|
||||
config: {
|
||||
title: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
title?: string | undefined;
|
||||
};
|
||||
output: ExtensionDataRef<
|
||||
{
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
},
|
||||
'core.nav-item.target',
|
||||
{}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
title: string;
|
||||
icon: IconComponent;
|
||||
routeRef: RouteRef<undefined>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @alpha @deprecated (undocumented)
|
||||
export const userSettingsTranslationRef: TranslationRef<
|
||||
'user-settings',
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
createExtensionInput,
|
||||
createFrontendPlugin,
|
||||
PageBlueprint,
|
||||
NavItemBlueprint,
|
||||
SubPageBlueprint,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { Content } from '@backstage/core-components';
|
||||
@@ -38,6 +37,7 @@ const userSettingsPage = PageBlueprint.make({
|
||||
path: '/settings',
|
||||
routeRef: settingsRouteRef,
|
||||
title: 'Settings',
|
||||
icon: <SettingsIcon fontSize="inherit" />,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -95,15 +95,6 @@ const featureFlagsSettingsPage = SubPageBlueprint.make({
|
||||
},
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export const settingsNavItem = NavItemBlueprint.make({
|
||||
params: {
|
||||
routeRef: settingsRouteRef,
|
||||
title: 'Settings',
|
||||
icon: SettingsIcon,
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
*/
|
||||
@@ -117,7 +108,6 @@ export default createFrontendPlugin({
|
||||
generalSettingsPage,
|
||||
authProvidersSettingsPage,
|
||||
featureFlagsSettingsPage,
|
||||
settingsNavItem,
|
||||
],
|
||||
routes: {
|
||||
root: settingsRouteRef,
|
||||
|
||||
Reference in New Issue
Block a user