catalog,catalog-react,techdocs: extract experimental entity page implementation from app-next
Co-authored-by: Camila Belo <camilaibs@gmail.com> Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
committed by
Camila Belo
parent
c82fc7e672
commit
0bf6ebda88
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Initial entity page implementation for new frontend system at `/alpha`, with an overview page enabled by default and the about card available as an optional card.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
---
|
||||
|
||||
Added entity page content for the new plugin exported via `/alpha`.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Added new APIs at the `/alpha` subpath for creating entity page cards and content for the new frontend system.
|
||||
@@ -4,12 +4,17 @@ app:
|
||||
routes:
|
||||
bindings:
|
||||
plugin.pages.externalRoutes.pageX: plugin.pages.routes.pageX
|
||||
# waiting for https://github.com/backstage/backstage/pull/20605
|
||||
# catalog.externalRoutes.viewTechDoc: techdocs.routes.docRoot
|
||||
plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot
|
||||
|
||||
extensions:
|
||||
- apis.plugin.graphiql.browse.gitlab: true
|
||||
|
||||
# Entity page cards
|
||||
- 'entity.cards.about'
|
||||
|
||||
# Entity page content
|
||||
- 'entity.content.techdocs'
|
||||
|
||||
# scmAuthExtension: >-
|
||||
# createScmAuthExtension({
|
||||
# id: 'apis.scmAuth.addons.ghe',
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
import React from 'react';
|
||||
import { createApp } from '@backstage/frontend-app-api';
|
||||
import { pagesPlugin } from './examples/pagesPlugin';
|
||||
import { entityPagePlugins } from './examples/entityPages';
|
||||
import graphiqlPlugin from '@backstage/plugin-graphiql/alpha';
|
||||
import techRadarPlugin from '@backstage/plugin-tech-radar/alpha';
|
||||
import userSettingsPlugin from '@backstage/plugin-user-settings/alpha';
|
||||
@@ -30,11 +29,8 @@ import {
|
||||
createExtension,
|
||||
createApiExtension,
|
||||
createExtensionOverrides,
|
||||
createPageExtension,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { entityRouteRef } from '@backstage/plugin-catalog-react';
|
||||
import techdocsPlugin from '@backstage/plugin-techdocs/alpha';
|
||||
import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha';
|
||||
import { homePage } from './HomePage';
|
||||
import { collectLegacyRoutes } from '@backstage/core-compat-api';
|
||||
import { FlatRoutes } from '@backstage/core-app-api';
|
||||
@@ -76,13 +72,6 @@ TODO:
|
||||
|
||||
/* app.tsx */
|
||||
|
||||
const entityPageExtension = createPageExtension({
|
||||
id: 'catalog:entity',
|
||||
defaultPath: '/catalog/:namespace/:kind/:name',
|
||||
routeRef: convertLegacyRouteRef(entityRouteRef),
|
||||
loader: async () => <div>Just a temporary mocked entity page</div>,
|
||||
});
|
||||
|
||||
const homePageExtension = createExtension({
|
||||
id: 'myhomepage',
|
||||
attachTo: { id: 'home', input: 'props' },
|
||||
@@ -121,15 +110,9 @@ const app = createApp({
|
||||
techdocsPlugin,
|
||||
userSettingsPlugin,
|
||||
homePlugin,
|
||||
...entityPagePlugins,
|
||||
...collectedLegacyPlugins,
|
||||
createExtensionOverrides({
|
||||
extensions: [
|
||||
entityPageExtension,
|
||||
homePageExtension,
|
||||
scmAuthExtension,
|
||||
scmIntegrationApi,
|
||||
],
|
||||
extensions: [homePageExtension, scmAuthExtension, scmIntegrationApi],
|
||||
}),
|
||||
],
|
||||
/* Handled through config instead */
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useEffect } from 'react';
|
||||
import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha';
|
||||
import {
|
||||
AnyExtensionInputMap,
|
||||
Extension,
|
||||
ExtensionBoundary,
|
||||
ExtensionInputValues,
|
||||
PortableSchema,
|
||||
RouteRef,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionDataRef,
|
||||
createExtensionInput,
|
||||
createPageExtension,
|
||||
createPlugin,
|
||||
createSchemaFromZod,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
AsyncEntityProvider,
|
||||
EntityLoadingStatus,
|
||||
catalogApiRef,
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { Expand } from '../../../frontend-plugin-api/src/types';
|
||||
import { EntityAboutCard, EntityLayout } from '@backstage/plugin-catalog';
|
||||
import {
|
||||
useApi,
|
||||
errorApiRef,
|
||||
useRouteRefParams,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { useNavigate } from 'react-router';
|
||||
import useAsyncRetry from 'react-use/lib/useAsyncRetry';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
||||
export const useEntityFromUrl = (): EntityLoadingStatus => {
|
||||
const { kind, namespace, name } = useRouteRefParams(entityRouteRef);
|
||||
const navigate = useNavigate();
|
||||
const errorApi = useApi(errorApiRef);
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const {
|
||||
value: entity,
|
||||
error,
|
||||
loading,
|
||||
retry: refresh,
|
||||
} = useAsyncRetry(
|
||||
() => catalogApi.getEntityByRef({ kind, namespace, name }),
|
||||
[catalogApi, kind, namespace, name],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!name) {
|
||||
errorApi.post(new Error('No name provided!'));
|
||||
navigate('/');
|
||||
}
|
||||
}, [errorApi, navigate, error, loading, entity, name]);
|
||||
|
||||
return { entity, loading, error, refresh };
|
||||
};
|
||||
|
||||
export const titleExtensionDataRef = createExtensionDataRef<string>(
|
||||
'plugin.catalog.entity.content.title',
|
||||
);
|
||||
|
||||
const CatalogEntityPage = createPageExtension({
|
||||
id: 'plugin.catalog.page.entity',
|
||||
defaultPath: '/catalog/:namespace/:kind/:name',
|
||||
routeRef: convertLegacyRouteRef(entityRouteRef),
|
||||
inputs: {
|
||||
contents: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
path: coreExtensionData.routePath,
|
||||
routeRef: coreExtensionData.routeRef.optional(),
|
||||
title: titleExtensionDataRef,
|
||||
}),
|
||||
},
|
||||
loader: async ({ inputs }) => {
|
||||
const Component = () => {
|
||||
return (
|
||||
<AsyncEntityProvider {...useEntityFromUrl()}>
|
||||
<EntityLayout>
|
||||
{inputs.contents.map(content => (
|
||||
<EntityLayout.Route
|
||||
key={content.path}
|
||||
path={content.path}
|
||||
title={content.title}
|
||||
>
|
||||
{content.element}
|
||||
</EntityLayout.Route>
|
||||
))}
|
||||
</EntityLayout>
|
||||
</AsyncEntityProvider>
|
||||
);
|
||||
};
|
||||
return <Component />;
|
||||
},
|
||||
});
|
||||
|
||||
export function createEntityCardExtension<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(options: {
|
||||
id: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): Extension<TConfig> {
|
||||
return createExtension({
|
||||
id: `entity.content.${options.id}`,
|
||||
attachTo: options.attachTo ?? {
|
||||
id: 'entity.content.overview',
|
||||
input: 'cards',
|
||||
},
|
||||
disabled: options.disabled ?? true,
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
inputs: options.inputs,
|
||||
configSchema: options.configSchema,
|
||||
factory({ bind, config, inputs, source }) {
|
||||
const LazyComponent = React.lazy(() =>
|
||||
options
|
||||
.loader({ config, inputs })
|
||||
.then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
bind({
|
||||
element: (
|
||||
<ExtensionBoundary source={source}>
|
||||
<React.Suspense fallback="...">
|
||||
<LazyComponent />
|
||||
</React.Suspense>
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function createEntityContentExtension<
|
||||
TConfig extends { path: string; title: string },
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(
|
||||
options: (
|
||||
| {
|
||||
defaultPath: string;
|
||||
defaultTitle: string;
|
||||
}
|
||||
| {
|
||||
configSchema: PortableSchema<TConfig>;
|
||||
}
|
||||
) & {
|
||||
id: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
routeRef?: RouteRef;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): Extension<TConfig> {
|
||||
const configSchema =
|
||||
'configSchema' in options
|
||||
? options.configSchema
|
||||
: (createSchemaFromZod(z =>
|
||||
z.object({
|
||||
path: z.string().default(options.defaultPath),
|
||||
title: z.string().default(options.defaultTitle),
|
||||
}),
|
||||
) as PortableSchema<TConfig>);
|
||||
|
||||
return createExtension({
|
||||
id: `entity.content.${options.id}`,
|
||||
attachTo: options.attachTo ?? {
|
||||
id: 'plugin.catalog.page.entity',
|
||||
input: 'contents',
|
||||
},
|
||||
disabled: options.disabled ?? true,
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
path: coreExtensionData.routePath,
|
||||
routeRef: coreExtensionData.routeRef.optional(),
|
||||
title: titleExtensionDataRef,
|
||||
},
|
||||
inputs: options.inputs,
|
||||
configSchema,
|
||||
factory({ bind, config, inputs, source }) {
|
||||
const LazyComponent = React.lazy(() =>
|
||||
options
|
||||
.loader({ config, inputs })
|
||||
.then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
bind({
|
||||
path: config.path,
|
||||
element: (
|
||||
<ExtensionBoundary source={source}>
|
||||
<React.Suspense fallback="...">
|
||||
<LazyComponent />
|
||||
</React.Suspense>
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
routeRef: options.routeRef,
|
||||
title: config.title,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const entityAboutCardExtension = createEntityCardExtension({
|
||||
id: 'about',
|
||||
disabled: false,
|
||||
loader: async () => <EntityAboutCard variant="gridItem" />,
|
||||
// entityFilter: isDerp,
|
||||
});
|
||||
|
||||
const overviewContentExtension = createEntityContentExtension({
|
||||
id: 'overview',
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
disabled: false,
|
||||
inputs: {
|
||||
cards: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
}),
|
||||
},
|
||||
loader: async ({ inputs }) => (
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
{inputs.cards.map(card => (
|
||||
<Grid item md={6} xs={12}>
|
||||
{card.element}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
),
|
||||
});
|
||||
|
||||
const bonusTechdocsPlugin = createPlugin({
|
||||
id: 'techdocs-entity',
|
||||
extensions: [
|
||||
createEntityContentExtension({
|
||||
id: 'techdocs',
|
||||
defaultPath: 'docs',
|
||||
defaultTitle: 'TechDocs',
|
||||
disabled: false,
|
||||
loader: () =>
|
||||
import('@backstage/plugin-techdocs').then(m => (
|
||||
<m.EmbeddedDocsRouter />
|
||||
)),
|
||||
// entityFilter: isPullRequestsAvailable,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const entityPagePlugins = [
|
||||
createPlugin({
|
||||
id: 'entity-pages',
|
||||
extensions: [
|
||||
CatalogEntityPage,
|
||||
overviewContentExtension,
|
||||
entityAboutCardExtension,
|
||||
],
|
||||
}),
|
||||
bonusTechdocsPlugin,
|
||||
// deploymentsPlugin,
|
||||
];
|
||||
@@ -3,8 +3,73 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api';
|
||||
import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Extension } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionInputValues } from '@backstage/frontend-plugin-api';
|
||||
import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createEntityCardExtension<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(options: {
|
||||
id: string;
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): Extension<TConfig>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createEntityContentExtension<
|
||||
TConfig extends {
|
||||
path: string;
|
||||
title: string;
|
||||
},
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(
|
||||
options: (
|
||||
| {
|
||||
defaultPath: string;
|
||||
defaultTitle: string;
|
||||
}
|
||||
| {
|
||||
configSchema: PortableSchema<TConfig>;
|
||||
}
|
||||
) & {
|
||||
id: string;
|
||||
attachTo?: {
|
||||
id: string;
|
||||
input: string;
|
||||
};
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
routeRef?: RouteRef;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): Extension<TConfig>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const entityContentTitleExtensionDataRef: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
{}
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export function isOwnerOf(owner: Entity, entity: Entity): boolean;
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha.ts",
|
||||
"./alpha": "./src/alpha.tsx",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha.ts"
|
||||
"src/alpha.tsx"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
@@ -51,6 +51,7 @@
|
||||
"@backstage/core-components": "workspace:^",
|
||||
"@backstage/core-plugin-api": "workspace:^",
|
||||
"@backstage/errors": "workspace:^",
|
||||
"@backstage/frontend-plugin-api": "workspace:^",
|
||||
"@backstage/integration": "workspace:^",
|
||||
"@backstage/plugin-catalog-common": "workspace:^",
|
||||
"@backstage/plugin-permission-common": "workspace:^",
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2023 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import {
|
||||
AnyExtensionInputMap,
|
||||
Extension,
|
||||
ExtensionBoundary,
|
||||
ExtensionInputValues,
|
||||
PortableSchema,
|
||||
RouteRef,
|
||||
coreExtensionData,
|
||||
createExtension,
|
||||
createExtensionDataRef,
|
||||
createSchemaFromZod,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
// eslint-disable-next-line @backstage/no-relative-monorepo-imports
|
||||
import { Expand } from '../../../packages/frontend-plugin-api/src/types';
|
||||
|
||||
export { isOwnerOf } from './utils';
|
||||
export { useEntityPermission } from './hooks/useEntityPermission';
|
||||
|
||||
/** @alpha */
|
||||
export const entityContentTitleExtensionDataRef =
|
||||
createExtensionDataRef<string>('plugin.catalog.entity.content.title');
|
||||
|
||||
/** @alpha */
|
||||
export function createEntityCardExtension<
|
||||
TConfig,
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(options: {
|
||||
id: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
configSchema?: PortableSchema<TConfig>;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
}): Extension<TConfig> {
|
||||
const id = `entity.cards.${options.id}`;
|
||||
|
||||
return createExtension({
|
||||
id,
|
||||
attachTo: options.attachTo ?? {
|
||||
id: 'entity.content.overview',
|
||||
input: 'cards',
|
||||
},
|
||||
disabled: options.disabled ?? true,
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
},
|
||||
inputs: options.inputs,
|
||||
configSchema: options.configSchema,
|
||||
factory({ bind, config, inputs, source }) {
|
||||
const ExtensionComponent = React.lazy(() =>
|
||||
options
|
||||
.loader({ config, inputs })
|
||||
.then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
bind({
|
||||
element: (
|
||||
<ExtensionBoundary id={id} source={source}>
|
||||
<ExtensionComponent />
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** @alpha */
|
||||
export function createEntityContentExtension<
|
||||
TConfig extends { path: string; title: string },
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
>(
|
||||
options: (
|
||||
| {
|
||||
defaultPath: string;
|
||||
defaultTitle: string;
|
||||
}
|
||||
| {
|
||||
configSchema: PortableSchema<TConfig>;
|
||||
}
|
||||
) & {
|
||||
id: string;
|
||||
attachTo?: { id: string; input: string };
|
||||
disabled?: boolean;
|
||||
inputs?: TInputs;
|
||||
routeRef?: RouteRef;
|
||||
loader: (options: {
|
||||
config: TConfig;
|
||||
inputs: Expand<ExtensionInputValues<TInputs>>;
|
||||
}) => Promise<JSX.Element>;
|
||||
},
|
||||
): Extension<TConfig> {
|
||||
const id = `entity.content.${options.id}`;
|
||||
|
||||
const configSchema =
|
||||
'configSchema' in options
|
||||
? options.configSchema
|
||||
: (createSchemaFromZod(z =>
|
||||
z.object({
|
||||
path: z.string().default(options.defaultPath),
|
||||
title: z.string().default(options.defaultTitle),
|
||||
}),
|
||||
) as PortableSchema<TConfig>);
|
||||
|
||||
return createExtension({
|
||||
id,
|
||||
attachTo: options.attachTo ?? {
|
||||
id: 'plugin.catalog.page.entity',
|
||||
input: 'contents',
|
||||
},
|
||||
disabled: options.disabled ?? true,
|
||||
output: {
|
||||
element: coreExtensionData.reactElement,
|
||||
path: coreExtensionData.routePath,
|
||||
routeRef: coreExtensionData.routeRef.optional(),
|
||||
title: entityContentTitleExtensionDataRef,
|
||||
},
|
||||
inputs: options.inputs,
|
||||
configSchema,
|
||||
factory({ bind, config, inputs, source }) {
|
||||
const LazyComponent = React.lazy(() =>
|
||||
options
|
||||
.loader({ config, inputs })
|
||||
.then(element => ({ default: () => element })),
|
||||
);
|
||||
|
||||
bind({
|
||||
path: config.path,
|
||||
element: (
|
||||
<ExtensionBoundary id={id} source={source}>
|
||||
<LazyComponent />
|
||||
</ExtensionBoundary>
|
||||
),
|
||||
routeRef: options.routeRef,
|
||||
title: config.title,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -12,14 +12,6 @@ import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { PortableSchema } from '@backstage/frontend-plugin-api';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const CatalogApi: Extension<{}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const CatalogSearchResultListItemExtension: Extension<{
|
||||
noTrack?: boolean | undefined;
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export function createCatalogFilterExtension<
|
||||
TInputs extends AnyExtensionInputMap,
|
||||
@@ -62,8 +54,5 @@ const _default: BackstagePlugin<
|
||||
>;
|
||||
export default _default;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const StarredEntitiesApi: Extension<{}>;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
},
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./alpha": "./src/alpha/index.ts",
|
||||
"./alpha": "./src/alpha.ts",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"typesVersions": {
|
||||
"*": {
|
||||
"alpha": [
|
||||
"src/alpha/index.ts"
|
||||
"src/alpha.ts"
|
||||
],
|
||||
"package.json": [
|
||||
"package.json"
|
||||
|
||||
@@ -14,5 +14,5 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export { isOwnerOf } from './utils';
|
||||
export { useEntityPermission } from './hooks/useEntityPermission';
|
||||
export * from './alpha/index';
|
||||
export { default } from './alpha/index';
|
||||
@@ -38,6 +38,11 @@ import {
|
||||
entityRouteRef,
|
||||
starredEntitiesApiRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
createEntityContentExtension,
|
||||
createEntityCardExtension,
|
||||
entityContentTitleExtensionDataRef,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha';
|
||||
import { DefaultStarredEntitiesApi } from '../apis';
|
||||
import {
|
||||
@@ -48,6 +53,7 @@ import {
|
||||
} from '../routes';
|
||||
import { builtInFilterExtensions } from './builtInFilterExtensions';
|
||||
import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
|
||||
/** @alpha */
|
||||
export const CatalogApi = createApiExtension({
|
||||
@@ -102,11 +108,30 @@ const CatalogEntityPage = createPageExtension({
|
||||
id: 'plugin.catalog.page.entity',
|
||||
defaultPath: '/catalog/:namespace/:kind/:name',
|
||||
routeRef: convertLegacyRouteRef(entityRouteRef),
|
||||
loader: async () => {
|
||||
inputs: {
|
||||
contents: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
path: coreExtensionData.routePath,
|
||||
routeRef: coreExtensionData.routeRef.optional(),
|
||||
title: entityContentTitleExtensionDataRef,
|
||||
}),
|
||||
},
|
||||
loader: async ({ inputs }) => {
|
||||
const { EntityLayout } = await import('../components/EntityLayout');
|
||||
const Component = () => {
|
||||
return (
|
||||
<AsyncEntityProvider {...useEntityFromUrl()}>
|
||||
<div>🚧 Work In Progress</div>
|
||||
<EntityLayout>
|
||||
{inputs.contents.map(content => (
|
||||
<EntityLayout.Route
|
||||
key={content.path}
|
||||
path={content.path}
|
||||
title={content.title}
|
||||
>
|
||||
{content.element}
|
||||
</EntityLayout.Route>
|
||||
))}
|
||||
</EntityLayout>
|
||||
</AsyncEntityProvider>
|
||||
);
|
||||
};
|
||||
@@ -114,6 +139,35 @@ const CatalogEntityPage = createPageExtension({
|
||||
},
|
||||
});
|
||||
|
||||
const EntityAboutCard = createEntityCardExtension({
|
||||
id: 'about',
|
||||
loader: async () =>
|
||||
import('../components/AboutCard').then(m => (
|
||||
<m.AboutCard variant="gridItem" />
|
||||
)),
|
||||
});
|
||||
|
||||
const OverviewEntityContent = createEntityContentExtension({
|
||||
id: 'overview',
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
disabled: false,
|
||||
inputs: {
|
||||
cards: createExtensionInput({
|
||||
element: coreExtensionData.reactElement,
|
||||
}),
|
||||
},
|
||||
loader: async ({ inputs }) => (
|
||||
<Grid container spacing={3} alignItems="stretch">
|
||||
{inputs.cards.map(card => (
|
||||
<Grid item md={6} xs={12}>
|
||||
{card.element}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
),
|
||||
});
|
||||
|
||||
const CatalogNavItem = createNavItemExtension({
|
||||
id: 'catalog.nav.index',
|
||||
routeRef: convertLegacyRouteRef(rootRouteRef),
|
||||
@@ -140,6 +194,8 @@ export default createPlugin({
|
||||
CatalogIndexPage,
|
||||
CatalogEntityPage,
|
||||
CatalogNavItem,
|
||||
OverviewEntityContent,
|
||||
EntityAboutCard,
|
||||
...builtInFilterExtensions,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
rootDocsRouteRef,
|
||||
rootRouteRef,
|
||||
} from './routes';
|
||||
import { createEntityContentExtension } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
/** @alpha */
|
||||
const techDocsStorage = createApiExtension({
|
||||
@@ -141,6 +142,18 @@ const TechDocsReaderPage = createPageExtension({
|
||||
)),
|
||||
});
|
||||
|
||||
/**
|
||||
* Component responsible for rendering techdocs on entity pages
|
||||
*
|
||||
* @alpha
|
||||
*/
|
||||
const TechDocsEntityContent = createEntityContentExtension({
|
||||
id: 'techdocs',
|
||||
defaultPath: 'docs',
|
||||
defaultTitle: 'TechDocs',
|
||||
loader: () => import('./Router').then(m => <m.EmbeddedDocsRouter />),
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
const TechDocsNavItem = createNavItemExtension({
|
||||
id: 'plugin.techdocs.nav.index',
|
||||
@@ -158,6 +171,7 @@ export default createPlugin({
|
||||
TechDocsNavItem,
|
||||
TechDocsIndexPage,
|
||||
TechDocsReaderPage,
|
||||
TechDocsEntityContent,
|
||||
TechDocsSearchResultListItemExtension,
|
||||
],
|
||||
routes: {
|
||||
|
||||
@@ -5837,6 +5837,7 @@ __metadata:
|
||||
"@backstage/core-components": "workspace:^"
|
||||
"@backstage/core-plugin-api": "workspace:^"
|
||||
"@backstage/errors": "workspace:^"
|
||||
"@backstage/frontend-plugin-api": "workspace:^"
|
||||
"@backstage/integration": "workspace:^"
|
||||
"@backstage/plugin-catalog-common": "workspace:^"
|
||||
"@backstage/plugin-permission-common": "workspace:^"
|
||||
|
||||
Reference in New Issue
Block a user