diff --git a/.changeset/clean-jokes-think.md b/.changeset/clean-jokes-think.md
index 4a2c6a2c13..a4fc65e6fc 100644
--- a/.changeset/clean-jokes-think.md
+++ b/.changeset/clean-jokes-think.md
@@ -1,5 +1,18 @@
---
-'@backstage/plugin-techdocs': minor
+'@backstage/plugin-techdocs': patch
---
-Rework `TechDocsHome` to use `EntityListProvider` with support for starring docs and filtering on owned, starred, owner, and tags.
+Expose a new composable `TechDocsIndexPage` and a `DefaultTechDocsHome` with support for starring docs and filtering on owned, starred, owner, and tags.
+
+You can migrate to the new UI view by making the following changes in your `App.tsx`:
+
+```diff
+- } />
++ }>
++
++
++ }
++ />
+```
diff --git a/.changeset/plenty-carpets-jog.md b/.changeset/plenty-carpets-jog.md
new file mode 100644
index 0000000000..f4586072a7
--- /dev/null
+++ b/.changeset/plenty-carpets-jog.md
@@ -0,0 +1,18 @@
+---
+'@backstage/create-app': patch
+---
+
+Use new composable `TechDocsIndexPage` and `DefaultTechDocsHome`
+
+Make the following changes to your `App.tsx` to migrate existing apps:
+
+```diff
+- } />
++ }>
++
++
++ }
++ />
+```
diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx
index 10fb743304..1132368a91 100644
--- a/packages/app/src/App.tsx
+++ b/packages/app/src/App.tsx
@@ -52,7 +52,11 @@ import {
} from '@backstage/plugin-scaffolder';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
-import { TechdocsPage } from '@backstage/plugin-techdocs';
+import {
+ DefaultTechDocsHome,
+ TechDocsIndexPage,
+ TechDocsReaderPage,
+} from '@backstage/plugin-techdocs';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import AlarmIcon from '@material-ui/icons/Alarm';
import React from 'react';
@@ -116,7 +120,13 @@ const routes = (
{entityPage}
} />
- } />
+ }>
+
+
+ }
+ />
}>
diff --git a/packages/create-app/templates/default-app/packages/app/src/App.tsx b/packages/create-app/templates/default-app/packages/app/src/App.tsx
index 9b77cf6a95..57491c9222 100644
--- a/packages/create-app/templates/default-app/packages/app/src/App.tsx
+++ b/packages/create-app/templates/default-app/packages/app/src/App.tsx
@@ -13,7 +13,11 @@ import {
import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
-import { TechdocsPage } from '@backstage/plugin-techdocs';
+import {
+ DefaultTechDocsHome,
+ TechDocsIndexPage,
+ TechDocsReaderPage,
+} from '@backstage/plugin-techdocs';
import { UserSettingsPage } from '@backstage/plugin-user-settings';
import { apis } from './apis';
import { entityPage } from './components/catalog/EntityPage';
@@ -50,7 +54,13 @@ const routes = (
>
{entityPage}
- } />
+ }>
+
+
+ }
+ />
} />
} />
{
icon: () => JSX.Element;
@@ -50,9 +48,7 @@ function createOwnerColumn(): TableColumn;
function createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
-): ({
- entity,
-}: DocsTableRow) => {
+): ({ entity }: DocsTableRow) => {
cellStyle: {
paddingLeft: string;
};
@@ -66,6 +62,19 @@ function createStarEntityAction(
// @public (undocumented)
function createTypeColumn(): TableColumn;
+// Warning: (ae-missing-release-tag) "DefaultTechDocsHome" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const DefaultTechDocsHome: ({
+ initialFilter,
+ columns,
+ actions,
+}: {
+ initialFilter?: UserListFilterKind | undefined;
+ columns?: TableColumn[] | undefined;
+ actions?: TableProps['actions'];
+}) => JSX.Element;
+
// Warning: (ae-missing-release-tag) "DocsCardGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -235,39 +244,22 @@ export const TechDocsCustomHome: ({
tabsConfig: TabsConfig;
}) => JSX.Element;
-// Warning: (ae-missing-release-tag) "TechDocsHome" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+// Warning: (ae-missing-release-tag) "TechDocsIndexPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
-export const TechDocsHome: ({
- initialFilter,
- columns,
- actions,
-}: {
- initialFilter?: UserListFilterKind | undefined;
- columns?: TableColumn[] | undefined;
- actions?:
- | (
- | Action
- | {
- action: (rowData: DocsTableRow) => Action;
- position: string;
- }
- | ((rowData: DocsTableRow) => Action)
- )[]
- | undefined;
-}) => JSX.Element;
-
-// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
-// Warning: (ae-missing-release-tag) "TechDocsHomeLayout" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
-//
-// @public (undocumented)
-export const TechDocsHomeLayout: ({ children }: Props) => JSX.Element;
+export const TechDocsIndexPage: () => JSX.Element;
// Warning: (ae-missing-release-tag) "TechdocsPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TechdocsPage: () => JSX.Element;
+// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
+// Warning: (ae-missing-release-tag) "TechDocsPageWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
+//
+// @public (undocumented)
+export const TechDocsPageWrapper: ({ children }: Props) => JSX.Element;
+
// Warning: (ae-missing-release-tag) "TechDocsPicker" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json
index 9e15dd7aff..bb9f8a7c95 100644
--- a/plugins/techdocs/package.json
+++ b/plugins/techdocs/package.json
@@ -45,7 +45,7 @@
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@material-ui/styles": "^4.10.0",
- "@types/react": "^16.9",
+ "@types/react": "*",
"dompurify": "^2.2.9",
"event-source-polyfill": "^1.0.25",
"lodash": "^4.17.21",
@@ -70,7 +70,6 @@
"@types/dompurify": "^2.2.2",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32",
- "@types/react": "*",
"canvas": "^2.6.1",
"cross-fetch": "^3.0.6",
"msw": "^0.29.0"
diff --git a/plugins/techdocs/src/Router.tsx b/plugins/techdocs/src/Router.tsx
index 6218f7520d..3fb109e790 100644
--- a/plugins/techdocs/src/Router.tsx
+++ b/plugins/techdocs/src/Router.tsx
@@ -23,8 +23,8 @@ import {
rootDocsRouteRef,
rootCatalogDocsRouteRef,
} from './routes';
-import { TechDocsHome } from './home/components/TechDocsHome';
-import { TechDocsPage } from './reader/components/TechDocsPage';
+import { TechDocsIndexPage } from './home/components/TechDocsIndexPage';
+import { TechDocsPage as TechDocsReaderPage } from './reader/components/TechDocsPage';
import { EntityPageDocs } from './EntityPageDocs';
import { MissingAnnotationEmptyState } from '@backstage/core-components';
@@ -33,8 +33,11 @@ const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
export const Router = () => {
return (
- } />
- } />
+ } />
+ }
+ />
);
};
diff --git a/plugins/techdocs/src/home/components/TechDocsHome.test.tsx b/plugins/techdocs/src/home/components/DefaultTechDocsHome.test.tsx
similarity index 95%
rename from plugins/techdocs/src/home/components/TechDocsHome.test.tsx
rename to plugins/techdocs/src/home/components/DefaultTechDocsHome.test.tsx
index 9289884f1c..0b1d6e8db2 100644
--- a/plugins/techdocs/src/home/components/TechDocsHome.test.tsx
+++ b/plugins/techdocs/src/home/components/DefaultTechDocsHome.test.tsx
@@ -18,7 +18,7 @@ import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
import { MockStorageApi, renderInTestApp } from '@backstage/test-utils';
import { screen } from '@testing-library/react';
import React from 'react';
-import { TechDocsHome } from './TechDocsHome';
+import { DefaultTechDocsHome } from './DefaultTechDocsHome';
import {
ApiProvider,
@@ -71,7 +71,7 @@ describe('TechDocs Home', () => {
it('should render a TechDocs home page', async () => {
await renderInTestApp(
-
+
,
);
diff --git a/plugins/techdocs/src/home/components/TechDocsHome.tsx b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
similarity index 93%
rename from plugins/techdocs/src/home/components/TechDocsHome.tsx
rename to plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
index 0aa1b87282..ff4bf63724 100644
--- a/plugins/techdocs/src/home/components/TechDocsHome.tsx
+++ b/plugins/techdocs/src/home/components/DefaultTechDocsHome.tsx
@@ -35,11 +35,11 @@ import {
UserListPicker,
} from '@backstage/plugin-catalog-react';
import { EntityListDocsTable } from './EntityListDocsTable';
-import { TechDocsHomeLayout } from './TechDocsHomeLayout';
+import { TechDocsPageWrapper } from './TechDocsPageWrapper';
import { TechDocsPicker } from './TechDocsPicker';
import { DocsTableRow } from './types';
-export const TechDocsHome = ({
+export const DefaultTechDocsHome = ({
initialFilter = 'all',
columns,
actions,
@@ -49,7 +49,7 @@ export const TechDocsHome = ({
actions?: TableProps['actions'];
}) => {
return (
-
+
@@ -70,6 +70,6 @@ export const TechDocsHome = ({
-
+
);
};
diff --git a/plugins/techdocs/src/home/components/LegacyTechDocsHome.test.tsx b/plugins/techdocs/src/home/components/LegacyTechDocsHome.test.tsx
new file mode 100644
index 0000000000..c9a6871906
--- /dev/null
+++ b/plugins/techdocs/src/home/components/LegacyTechDocsHome.test.tsx
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { CatalogApi, catalogApiRef } from '@backstage/plugin-catalog-react';
+import { renderInTestApp } from '@backstage/test-utils';
+import { screen } from '@testing-library/react';
+import React from 'react';
+import { LegacyTechDocsHome } from './LegacyTechDocsHome';
+
+import {
+ ApiProvider,
+ ApiRegistry,
+ ConfigReader,
+} from '@backstage/core-app-api';
+import { ConfigApi, configApiRef } from '@backstage/core-plugin-api';
+
+jest.mock('@backstage/plugin-catalog-react', () => {
+ const actual = jest.requireActual('@backstage/plugin-catalog-react');
+ return {
+ ...actual,
+ useOwnUser: () => 'test-user',
+ };
+});
+
+const mockCatalogApi = {
+ getEntityByName: jest.fn(),
+ getEntities: async () => ({
+ items: [
+ {
+ apiVersion: 'version',
+ kind: 'User',
+ metadata: {
+ name: 'owned',
+ namespace: 'default',
+ },
+ },
+ ],
+ }),
+} as Partial;
+
+describe('Legacy TechDocs Home', () => {
+ const configApi: ConfigApi = new ConfigReader({
+ organization: {
+ name: 'My Company',
+ },
+ });
+
+ const apiRegistry = ApiRegistry.from([
+ [catalogApiRef, mockCatalogApi],
+ [configApiRef, configApi],
+ ]);
+
+ it('should render a TechDocs home page', async () => {
+ await renderInTestApp(
+
+
+ ,
+ );
+
+ // Header
+ expect(await screen.findByText('Documentation')).toBeInTheDocument();
+ expect(
+ await screen.findByText(/Documentation available in My Company/i),
+ ).toBeInTheDocument();
+
+ // Explore Content
+ expect(await screen.findByTestId('docs-explore')).toBeDefined();
+ });
+});
diff --git a/plugins/techdocs/src/home/components/LegacyTechDocsHome.tsx b/plugins/techdocs/src/home/components/LegacyTechDocsHome.tsx
new file mode 100644
index 0000000000..34ea416026
--- /dev/null
+++ b/plugins/techdocs/src/home/components/LegacyTechDocsHome.tsx
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';
+
+export const LegacyTechDocsHome = () => {
+ const tabsConfig = [
+ {
+ label: 'Overview',
+ panels: [
+ {
+ title: 'Overview',
+ description:
+ 'Explore your internal technical ecosystem through documentation.',
+ panelType: 'DocsCardGrid' as PanelType,
+ filterPredicate: () => true,
+ },
+ // uncomment this if you would like to have a secondary panel with owned documents
+ // {
+ // title: 'Owned',
+ // description: 'Explore your owned internal documentation.',
+ // panelType: 'DocsCardGrid' as PanelType,
+ // filterPredicate: 'ownedByUser',
+ // },
+ ],
+ },
+ {
+ label: 'Owned Documents',
+ panels: [
+ {
+ title: 'Owned documents',
+ description: 'Access your documentation.',
+ panelType: 'DocsTable' as PanelType,
+ // ownedByUser filters out entities owned by signed in user
+ filterPredicate: 'ownedByUser',
+ },
+ ],
+ },
+ ];
+ return ;
+};
diff --git a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
index 7f93f6a306..9975db6435 100644
--- a/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
+++ b/plugins/techdocs/src/home/components/TechDocsCustomHome.tsx
@@ -28,7 +28,7 @@ import {
import { Entity } from '@backstage/catalog-model';
import { DocsTable } from './DocsTable';
import { DocsCardGrid } from './DocsCardGrid';
-import { TechDocsHomeLayout } from './TechDocsHomeLayout';
+import { TechDocsPageWrapper } from './TechDocsPageWrapper';
import {
CodeSnippet,
@@ -149,17 +149,17 @@ export const TechDocsCustomHome = ({
if (loading) {
return (
-
+
-
+
);
}
if (error) {
return (
-
+
-
+
);
}
return (
-
+
setSelectedTab(index)}
@@ -192,6 +192,6 @@ export const TechDocsCustomHome = ({
/>
))}
-
+
);
};
diff --git a/plugins/techdocs/src/home/components/TechDocsIndexPage.test.tsx b/plugins/techdocs/src/home/components/TechDocsIndexPage.test.tsx
new file mode 100644
index 0000000000..fbeca7cfd4
--- /dev/null
+++ b/plugins/techdocs/src/home/components/TechDocsIndexPage.test.tsx
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { renderInTestApp } from '@backstage/test-utils';
+import { useOutlet } from 'react-router';
+import { TechDocsIndexPage } from './TechDocsIndexPage';
+
+jest.mock('react-router', () => ({
+ ...jest.requireActual('react-router'),
+ useOutlet: jest.fn().mockReturnValue('Route Children'),
+}));
+
+jest.mock('./LegacyTechDocsHome', () => ({
+ LegacyTechDocsHome: jest.fn().mockReturnValue('LegacyTechDocsHomeMock'),
+}));
+
+describe('TechDocsIndexPage', () => {
+ it('renders provided router element', async () => {
+ const { getByText } = await renderInTestApp();
+
+ expect(getByText('Route Children')).toBeInTheDocument();
+ });
+
+ it('renders legacy TechDocs home when no router children are provided', async () => {
+ (useOutlet as jest.Mock).mockReturnValueOnce(null);
+ const { getByText } = await renderInTestApp();
+
+ expect(getByText('LegacyTechDocsHomeMock')).toBeInTheDocument();
+ });
+});
diff --git a/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx
new file mode 100644
index 0000000000..ff694d3570
--- /dev/null
+++ b/plugins/techdocs/src/home/components/TechDocsIndexPage.tsx
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2021 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import React from 'react';
+import { useOutlet } from 'react-router';
+import { LegacyTechDocsHome } from './LegacyTechDocsHome';
+
+export const TechDocsIndexPage = () => {
+ const outlet = useOutlet();
+
+ return outlet || ;
+};
diff --git a/plugins/techdocs/src/home/components/TechDocsHomeLayout.tsx b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx
similarity index 94%
rename from plugins/techdocs/src/home/components/TechDocsHomeLayout.tsx
rename to plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx
index e6717fbecc..453cfc22cc 100644
--- a/plugins/techdocs/src/home/components/TechDocsHomeLayout.tsx
+++ b/plugins/techdocs/src/home/components/TechDocsPageWrapper.tsx
@@ -23,7 +23,7 @@ type Props = {
children?: React.ReactNode;
};
-export const TechDocsHomeLayout = ({ children }: Props) => {
+export const TechDocsPageWrapper = ({ children }: Props) => {
const configApi = useApi(configApiRef);
const generatedSubtitle = `Documentation available in ${
configApi.getOptionalString('organization.name') ?? 'Backstage'
diff --git a/plugins/techdocs/src/home/components/index.ts b/plugins/techdocs/src/home/components/index.ts
index a56b80f9a1..53dd0fd909 100644
--- a/plugins/techdocs/src/home/components/index.ts
+++ b/plugins/techdocs/src/home/components/index.ts
@@ -15,7 +15,8 @@
*/
export { EntityListDocsTable } from './EntityListDocsTable';
-export { TechDocsHomeLayout } from './TechDocsHomeLayout';
+export { DefaultTechDocsHome } from './DefaultTechDocsHome';
+export { TechDocsPageWrapper } from './TechDocsPageWrapper';
export { TechDocsPicker } from './TechDocsPicker';
export type { PanelType } from './TechDocsCustomHome';
export type { DocsTableRow } from './types';
diff --git a/plugins/techdocs/src/index.ts b/plugins/techdocs/src/index.ts
index 7c3bf607b4..4ebf29180c 100644
--- a/plugins/techdocs/src/index.ts
+++ b/plugins/techdocs/src/index.ts
@@ -21,7 +21,8 @@ export { TechDocsClient, TechDocsStorageClient } from './client';
export type { DocsTableRow, PanelType } from './home/components';
export {
EntityListDocsTable,
- TechDocsHomeLayout,
+ DefaultTechDocsHome,
+ TechDocsPageWrapper,
TechDocsPicker,
} from './home/components';
export * from './components/DocsResultListItem';
@@ -30,7 +31,7 @@ export {
DocsTable,
EntityTechdocsContent,
TechDocsCustomHome,
- TechDocsHome,
+ TechDocsIndexPage,
TechdocsPage,
techdocsPlugin as plugin,
techdocsPlugin,
diff --git a/plugins/techdocs/src/plugin.ts b/plugins/techdocs/src/plugin.ts
index 2e031176e4..18b386f88c 100644
--- a/plugins/techdocs/src/plugin.ts
+++ b/plugins/techdocs/src/plugin.ts
@@ -113,10 +113,12 @@ export const TechDocsCustomHome = techdocsPlugin.provide(
}),
);
-export const TechDocsHome = techdocsPlugin.provide(
+export const TechDocsIndexPage = techdocsPlugin.provide(
createRoutableExtension({
component: () =>
- import('./home/components/TechDocsHome').then(m => m.TechDocsHome),
+ import('./home/components/TechDocsIndexPage').then(
+ m => m.TechDocsIndexPage,
+ ),
mountPoint: rootRouteRef,
}),
);