Finish TechDocs migration to composability API.
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
The entity `<AboutCard />` now uses an external route ref to link to TechDocs
|
||||
sites. This external route must now be bound in order for the "View TechDocs"
|
||||
link to continue working. See the [create-app changelog][cacl] for details.
|
||||
|
||||
[cacl]: https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Wire up TechDocs, which now relies on the composability API for routing.
|
||||
|
||||
First, ensure you've mounted `<TechDocsReaderPage />`. If you already updated
|
||||
to use the composable `<TechDocsIndexPage />` (see below), no action is
|
||||
necessary. Otherwise, update your `App.tsx` so that `<TechDocsReaderPage />` is
|
||||
mounted:
|
||||
|
||||
```diff
|
||||
<Route path="/docs" element={<TechdocsPage />} />
|
||||
+ <Route
|
||||
+ path="/docs/:namespace/:kind/:name/*"
|
||||
+ element={<TechDocsReaderPage />}
|
||||
+ />
|
||||
```
|
||||
|
||||
Next, ensure links from the Catalog Entity Page to its TechDocs site are bound:
|
||||
|
||||
```diff
|
||||
bindRoutes({ bind }) {
|
||||
bind(catalogPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
+ viewTechDoc: techdocsPlugin.routes.docRoot,
|
||||
});
|
||||
```
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': minor
|
||||
---
|
||||
|
||||
The TechDocs plugin has completed the migration to the Composability API. In
|
||||
order to update to this version, please ensure you've made all necessary
|
||||
changes to your `App.tsx` file as outlined in the [create-app changelog][cacl].
|
||||
|
||||
[cacl]: https://github.com/backstage/backstage/blob/master/packages/create-app/CHANGELOG.md
|
||||
@@ -55,6 +55,7 @@ import { TechRadarPage } from '@backstage/plugin-tech-radar';
|
||||
import {
|
||||
DefaultTechDocsHome,
|
||||
TechDocsIndexPage,
|
||||
techdocsPlugin,
|
||||
TechDocsReaderPage,
|
||||
} from '@backstage/plugin-techdocs';
|
||||
import { UserSettingsPage } from '@backstage/plugin-user-settings';
|
||||
@@ -93,6 +94,7 @@ const app = createApp({
|
||||
bindRoutes({ bind }) {
|
||||
bind(catalogPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
viewTechDoc: techdocsPlugin.routes.docRoot,
|
||||
});
|
||||
bind(apiDocsPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
|
||||
@@ -16,6 +16,7 @@ import { TechRadarPage } from '@backstage/plugin-tech-radar';
|
||||
import {
|
||||
DefaultTechDocsHome,
|
||||
TechDocsIndexPage,
|
||||
techdocsPlugin,
|
||||
TechDocsReaderPage,
|
||||
} from '@backstage/plugin-techdocs';
|
||||
import { UserSettingsPage } from '@backstage/plugin-user-settings';
|
||||
@@ -31,6 +32,7 @@ const app = createApp({
|
||||
bindRoutes({ bind }) {
|
||||
bind(catalogPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
viewTechDoc: techdocsPlugin.routes.docRoot,
|
||||
});
|
||||
bind(apiDocsPlugin.externalRoutes, {
|
||||
createComponent: scaffolderPlugin.routes.root,
|
||||
|
||||
@@ -128,6 +128,14 @@ const catalogPlugin: BackstagePlugin<
|
||||
},
|
||||
{
|
||||
createComponent: ExternalRouteRef<undefined, true>;
|
||||
viewTechDoc: ExternalRouteRef<
|
||||
{
|
||||
name: string;
|
||||
kind: string;
|
||||
namespace: string;
|
||||
},
|
||||
true
|
||||
>;
|
||||
}
|
||||
>;
|
||||
export { catalogPlugin };
|
||||
@@ -399,7 +407,7 @@ export const Router: ({
|
||||
// src/components/EntityLayout/EntityLayout.d.ts:43:5 - (ae-forgotten-export) The symbol "EntityLayoutProps" needs to be exported by the entry point index.d.ts
|
||||
// src/components/EntityLayout/EntityLayout.d.ts:44:5 - (ae-forgotten-export) The symbol "SubRoute" needs to be exported by the entry point index.d.ts
|
||||
// src/components/EntityPageLayout/EntityPageLayout.d.ts:17:5 - (ae-forgotten-export) The symbol "EntityPageLayoutProps" needs to be exported by the entry point index.d.ts
|
||||
// src/plugin.d.ts:17:5 - (ae-forgotten-export) The symbol "ColumnBreakpoints" needs to be exported by the entry point index.d.ts
|
||||
// src/plugin.d.ts:22:5 - (ae-forgotten-export) The symbol "ColumnBreakpoints" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
ApiRegistry,
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { viewTechDocRouteRef } from '../../routes';
|
||||
|
||||
describe('<AboutCard />', () => {
|
||||
it('renders info', async () => {
|
||||
@@ -203,4 +204,138 @@ describe('<AboutCard />', () => {
|
||||
);
|
||||
expect(getByText('View Source').closest('a')).not.toHaveAttribute('href');
|
||||
});
|
||||
|
||||
it('renders techdocs link', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'software',
|
||||
annotations: {
|
||||
'backstage.io/techdocs-ref': './',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name': viewTechDocRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
expect(getByText('View TechDocs').closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
'/docs/default/Component/software',
|
||||
);
|
||||
});
|
||||
|
||||
it('renders disabled techdocs link when no docs exist', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'software',
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
expect(getByText('View TechDocs').closest('a')).not.toHaveAttribute('href');
|
||||
});
|
||||
|
||||
it('renders disbaled techdocs link when route is not bound', async () => {
|
||||
const entity = {
|
||||
apiVersion: 'v1',
|
||||
kind: 'Component',
|
||||
metadata: {
|
||||
name: 'software',
|
||||
annotations: {
|
||||
'backstage.io/techdocs-ref': './',
|
||||
},
|
||||
},
|
||||
spec: {
|
||||
owner: 'guest',
|
||||
type: 'service',
|
||||
lifecycle: 'production',
|
||||
},
|
||||
};
|
||||
const apis = ApiRegistry.with(
|
||||
scmIntegrationsApiRef,
|
||||
ScmIntegrationsApi.fromConfig(
|
||||
new ConfigReader({
|
||||
integrations: {
|
||||
github: [
|
||||
{
|
||||
host: 'github.com',
|
||||
token: '...',
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
const { getByText } = await renderInTestApp(
|
||||
<ApiProvider apis={apis}>
|
||||
<EntityProvider entity={entity}>
|
||||
<AboutCard />
|
||||
</EntityProvider>
|
||||
</ApiProvider>,
|
||||
);
|
||||
|
||||
expect(getByText('View TechDocs').closest('a')).not.toHaveAttribute('href');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,7 +49,8 @@ import {
|
||||
IconLinkVerticalProps,
|
||||
InfoCardVariants,
|
||||
} from '@backstage/core-components';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { viewTechDocRouteRef } from '../../routes';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
gridItemCard: {
|
||||
@@ -81,6 +82,8 @@ export function AboutCard({ variant }: AboutCardProps) {
|
||||
const classes = useStyles();
|
||||
const { entity } = useEntity();
|
||||
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
|
||||
const viewTechdocLink = useRouteRef(viewTechDocRouteRef);
|
||||
|
||||
const entitySourceLocation = getEntitySourceLocation(
|
||||
entity,
|
||||
scmIntegrationsApi,
|
||||
@@ -105,11 +108,17 @@ export function AboutCard({ variant }: AboutCardProps) {
|
||||
};
|
||||
const viewInTechDocs: IconLinkVerticalProps = {
|
||||
label: 'View TechDocs',
|
||||
disabled: !entity.metadata.annotations?.['backstage.io/techdocs-ref'],
|
||||
disabled:
|
||||
!entity.metadata.annotations?.['backstage.io/techdocs-ref'] ||
|
||||
!viewTechdocLink,
|
||||
icon: <DocsIcon />,
|
||||
href: `/docs/${entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE}/${
|
||||
entity.kind
|
||||
}/${entity.metadata.name}`,
|
||||
href:
|
||||
viewTechdocLink &&
|
||||
viewTechdocLink({
|
||||
namespace: entity.metadata.namespace || ENTITY_DEFAULT_NAMESPACE,
|
||||
kind: entity.kind,
|
||||
name: entity.metadata.name,
|
||||
}),
|
||||
};
|
||||
const viewApi: IconLinkVerticalProps = {
|
||||
title: hasApis ? '' : 'No APIs available',
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import { CatalogClientWrapper } from './CatalogClientWrapper';
|
||||
import { createComponentRouteRef } from './routes';
|
||||
import { createComponentRouteRef, viewTechDocRouteRef } from './routes';
|
||||
import {
|
||||
createApiFactory,
|
||||
createComponentExtension,
|
||||
@@ -50,6 +50,7 @@ export const catalogPlugin = createPlugin({
|
||||
},
|
||||
externalRoutes: {
|
||||
createComponent: createComponentRouteRef,
|
||||
viewTechDoc: viewTechDocRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -20,3 +20,9 @@ export const createComponentRouteRef = createExternalRouteRef({
|
||||
id: 'create-component',
|
||||
optional: true,
|
||||
});
|
||||
|
||||
export const viewTechDocRouteRef = createExternalRouteRef({
|
||||
id: 'view-techdoc',
|
||||
optional: true,
|
||||
params: ['namespace', 'kind', 'name'],
|
||||
});
|
||||
|
||||
@@ -271,6 +271,11 @@ export const TechDocsPicker: () => null;
|
||||
const techdocsPlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
docRoot: RouteRef<{
|
||||
name: string;
|
||||
kind: string;
|
||||
namespace: string;
|
||||
}>;
|
||||
entityContent: RouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
@@ -374,7 +379,7 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
|
||||
//
|
||||
// src/home/components/EntityListDocsTable.d.ts:11:5 - (ae-forgotten-export) The symbol "columnFactories" needs to be exported by the entry point index.d.ts
|
||||
// src/home/components/EntityListDocsTable.d.ts:12:5 - (ae-forgotten-export) The symbol "actionFactories" needs to be exported by the entry point index.d.ts
|
||||
// src/plugin.d.ts:24:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts
|
||||
// src/plugin.d.ts:29:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
|
||||
@@ -18,11 +18,6 @@ import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
import {
|
||||
rootRouteRef,
|
||||
rootDocsRouteRef,
|
||||
rootCatalogDocsRouteRef,
|
||||
} from './routes';
|
||||
import { TechDocsIndexPage } from './home/components/TechDocsIndexPage';
|
||||
import { TechDocsPage as TechDocsReaderPage } from './reader/components/TechDocsPage';
|
||||
import { EntityPageDocs } from './EntityPageDocs';
|
||||
@@ -33,9 +28,9 @@ const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
|
||||
export const Router = () => {
|
||||
return (
|
||||
<Routes>
|
||||
<Route path={`/${rootRouteRef.path}`} element={<TechDocsIndexPage />} />
|
||||
<Route path="/" element={<TechDocsIndexPage />} />
|
||||
<Route
|
||||
path={`/${rootDocsRouteRef.path}`}
|
||||
path="/:namespace/:kind/:name/*"
|
||||
element={<TechDocsReaderPage />}
|
||||
/>
|
||||
</Routes>
|
||||
@@ -58,10 +53,7 @@ export const EmbeddedDocsRouter = (_props: Props) => {
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route
|
||||
path={`/${rootCatalogDocsRouteRef.path}`}
|
||||
element={<EntityPageDocs entity={entity} />}
|
||||
/>
|
||||
<Route path="/*" element={<EntityPageDocs entity={entity} />} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
configApiRef,
|
||||
storageApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react', () => {
|
||||
const actual = jest.requireActual('@backstage/plugin-catalog-react');
|
||||
@@ -73,6 +74,11 @@ describe('TechDocs Home', () => {
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<DefaultTechDocsHome />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Header
|
||||
|
||||
@@ -19,6 +19,7 @@ import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { DocsCardGrid } from './DocsCardGrid';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
// Hacky way to mock a specific boolean config value.
|
||||
const getOptionalBooleanMock = jest.fn().mockReturnValue(false);
|
||||
@@ -71,16 +72,21 @@ describe('Entity Docs Card Grid', () => {
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
expect(await findByText('testName')).toBeInTheDocument();
|
||||
expect(await findByText('testName2')).toBeInTheDocument();
|
||||
const [button1, button2] = await findAllByRole('button');
|
||||
expect(button1.getAttribute('href')).toContain(
|
||||
'/default/testkind/testname',
|
||||
'/docs/default/testkind/testname',
|
||||
);
|
||||
expect(button2.getAttribute('href')).toContain(
|
||||
'/default/testkind2/testname2',
|
||||
'/docs/default/testkind2/testname2',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -104,6 +110,11 @@ describe('Entity Docs Card Grid', () => {
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/techdocs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -112,7 +123,7 @@ describe('Entity Docs Card Grid', () => {
|
||||
'techdocs.legacyUseCaseSensitiveTripletPaths',
|
||||
);
|
||||
expect(button.getAttribute('href')).toContain(
|
||||
'/SomeNamespace/TestKind/testName',
|
||||
'/techdocs/SomeNamespace/TestKind/testName',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,10 +15,9 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
@@ -33,6 +32,8 @@ export const DocsCardGrid = ({
|
||||
}: {
|
||||
entities: Entity[] | undefined;
|
||||
}) => {
|
||||
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
|
||||
|
||||
// Lower-case entity triplets by default, but allow override.
|
||||
const toLowerMaybe = useApi(configApiRef).getOptionalBoolean(
|
||||
'techdocs.legacyUseCaseSensitiveTripletPaths',
|
||||
@@ -53,7 +54,7 @@ export const DocsCardGrid = ({
|
||||
<CardContent>{entity.metadata.description}</CardContent>
|
||||
<CardActions>
|
||||
<Button
|
||||
to={generatePath(rootDocsRouteRef.path, {
|
||||
to={getRouteToReaderPageFor({
|
||||
namespace: toLowerMaybe(
|
||||
entity.metadata.namespace ?? 'default',
|
||||
),
|
||||
|
||||
@@ -18,6 +18,7 @@ import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { configApiRef } from '@backstage/core-plugin-api';
|
||||
import { DocsTable } from './DocsTable';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
// Hacky way to mock a specific boolean config value.
|
||||
const getOptionalBooleanMock = jest.fn().mockReturnValue(false);
|
||||
@@ -90,16 +91,23 @@ describe('DocsTable test', () => {
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
const link1 = await findByText('testName');
|
||||
const link2 = await findByText('testName2');
|
||||
expect(link1).toBeInTheDocument();
|
||||
expect(link1.getAttribute('href')).toContain('/default/testkind/testname');
|
||||
expect(link1.getAttribute('href')).toContain(
|
||||
'/docs/default/testkind/testname',
|
||||
);
|
||||
expect(link2).toBeInTheDocument();
|
||||
expect(link2.getAttribute('href')).toContain(
|
||||
'/default/testkind2/testname2',
|
||||
'/docs/default/testkind2/testname2',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -133,6 +141,11 @@ describe('DocsTable test', () => {
|
||||
},
|
||||
]}
|
||||
/>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/techdocs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
@@ -141,12 +154,18 @@ describe('DocsTable test', () => {
|
||||
'techdocs.legacyUseCaseSensitiveTripletPaths',
|
||||
);
|
||||
expect(button.getAttribute('href')).toContain(
|
||||
'/SomeNamespace/TestKind/testName',
|
||||
'/techdocs/SomeNamespace/TestKind/testName',
|
||||
);
|
||||
});
|
||||
|
||||
it('should render empty state if no owned documents exist', async () => {
|
||||
const { findByText } = render(wrapInTestApp(<DocsTable entities={[]} />));
|
||||
const { findByText } = render(
|
||||
wrapInTestApp(<DocsTable entities={[]} />, {
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(await findByText('No documents to show')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
@@ -16,9 +16,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useCopyToClipboard } from 'react-use';
|
||||
import { generatePath } from 'react-router-dom';
|
||||
|
||||
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api';
|
||||
import { Entity, RELATION_OWNED_BY } from '@backstage/catalog-model';
|
||||
import {
|
||||
formatEntityRefTitle,
|
||||
@@ -50,6 +49,8 @@ export const DocsTable = ({
|
||||
actions?: TableProps<DocsTableRow>['actions'];
|
||||
}) => {
|
||||
const [, copyToClipboard] = useCopyToClipboard();
|
||||
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
|
||||
|
||||
// Lower-case entity triplets by default, but allow override.
|
||||
const toLowerMaybe = useApi(configApiRef).getOptionalBoolean(
|
||||
'techdocs.legacyUseCaseSensitiveTripletPaths',
|
||||
@@ -65,7 +66,7 @@ export const DocsTable = ({
|
||||
return {
|
||||
entity,
|
||||
resolved: {
|
||||
docsUrl: generatePath(rootDocsRouteRef.path, {
|
||||
docsUrl: getRouteToReaderPageFor({
|
||||
namespace: toLowerMaybe(entity.metadata.namespace ?? 'default'),
|
||||
kind: toLowerMaybe(entity.kind),
|
||||
name: toLowerMaybe(entity.metadata.name),
|
||||
|
||||
@@ -26,6 +26,7 @@ import {
|
||||
ConfigReader,
|
||||
} from '@backstage/core-app-api';
|
||||
import { ConfigApi, configApiRef } from '@backstage/core-plugin-api';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react', () => {
|
||||
const actual = jest.requireActual('@backstage/plugin-catalog-react');
|
||||
@@ -68,6 +69,11 @@ describe('Legacy TechDocs Home', () => {
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<LegacyTechDocsHome />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Header
|
||||
|
||||
@@ -20,6 +20,7 @@ import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { TechDocsCustomHome, PanelType } from './TechDocsCustomHome';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
|
||||
import { rootDocsRouteRef } from '../../routes';
|
||||
|
||||
jest.mock('@backstage/plugin-catalog-react', () => {
|
||||
const actual = jest.requireActual('@backstage/plugin-catalog-react');
|
||||
@@ -78,6 +79,11 @@ describe('TechDocsCustomHome', () => {
|
||||
<ApiProvider apis={apiRegistry}>
|
||||
<TechDocsCustomHome tabsConfig={tabsConfig} />
|
||||
</ApiProvider>,
|
||||
{
|
||||
mountedRoutes: {
|
||||
'/docs/:namespace/:kind/:name/*': rootDocsRouteRef,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Header
|
||||
|
||||
@@ -65,6 +65,7 @@ export const techdocsPlugin = createPlugin({
|
||||
],
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
docRoot: rootDocsRouteRef,
|
||||
entityContent: rootCatalogDocsRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
path: '',
|
||||
id: 'techdocs-index-page',
|
||||
title: 'TechDocs Landing Page',
|
||||
});
|
||||
|
||||
export const rootDocsRouteRef = createRouteRef({
|
||||
path: ':namespace/:kind/:name/*',
|
||||
id: 'techdocs-reader-page',
|
||||
title: 'Docs',
|
||||
params: ['namespace', 'kind', 'name'],
|
||||
});
|
||||
|
||||
export const rootCatalogDocsRouteRef = createRouteRef({
|
||||
path: '*',
|
||||
id: 'catalog-techdocs-reader-view',
|
||||
title: 'Docs',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user