Merge pull request #9730 from backstage/emmaindal/techdocs-api-report

This commit is contained in:
Emma Indal
2022-02-25 17:23:04 +01:00
committed by GitHub
49 changed files with 885 additions and 526 deletions
+31
View File
@@ -0,0 +1,31 @@
---
'@backstage/create-app': patch
---
Update the template to reflect the renaming of `DocsResultListItem` to `TechDocsSearchResultListItem` from `@backstage/plugin-techdocs`.
To apply this change to an existing app, make the following change to `packages/app/src/components/search/SearchPage.tsx`:
```diff
-import { DocsResultListItem } from '@backstage/plugin-techdocs';
+import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
```
```diff
case 'techdocs':
return (
- <DocsResultListItem
+ <TechDocsSearchResultListItem
key={document.location}
result={document}
/>
```
The `TechDocsIndexPage` now uses `DefaultTechDocsHome` as fall back if no children is provided as `LegacyTechDocsHome` is marked as deprecated. If you do not use a custom techdocs homepage, you can therefore update your app to the following:
```diff
- <Route path="/docs" element={<TechDocsIndexPage />}>
- <DefaultTechDocsHome />
- </Route>
+ <Route path="/docs" element={<TechDocsIndexPage />} />
```
+29
View File
@@ -0,0 +1,29 @@
---
'@backstage/plugin-techdocs': minor
---
**BREAKING:**
Table column utilities `createNameColumn`, `createOwnerColumn`, `createTypeColumn` as well as actions utilities `createCopyDocsUrlAction` and `createStarEntityAction` are no longer directly exported. Instead accessible through DocsTable and EntityListDocsTable.
Use as following:
```tsx
DocsTable.columns.createNameColumn();
DocsTable.columns.createOwnerColumn();
DocsTable.columns.createTypeColumn();
DocsTable.actions.createCopyDocsUrlAction();
DocsTable.actions.createStarEntityAction();
```
- Renamed `DocsResultListItem` to `TechDocsSearchResultListItem`, leaving the old name in place as a deprecations.
- Renamed `TechDocsPage` to `TechDocsReaderPage`, leaving the old name in place as a deprecations.
- Renamed `TechDocsPageRenderFunction` to `TechDocsPageRenderFunction`, leaving the old name in place as a deprecations.
- Renamed `TechDocsPageHeader` to `TechDocsReaderPageHeader`, leaving the old name in place as a deprecations.
- `LegacyTechDocsHome` marked as deprecated and will be deleted in next release, use `TechDocsCustomHome` instead.
- `LegacyTechDocsPage` marked as deprecated and will be deleted in next release, use `TechDocsReaderPage` instead.
+1 -4
View File
@@ -65,7 +65,6 @@ import {
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import {
DefaultTechDocsHome,
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
@@ -169,9 +168,7 @@ const routes = (
/>
}
/>
<Route path="/docs" element={<TechDocsIndexPage />}>
<DefaultTechDocsHome />
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
@@ -38,7 +38,7 @@ import {
SearchType,
useSearch,
} from '@backstage/plugin-search';
import { DocsResultListItem } from '@backstage/plugin-techdocs';
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core';
import React, { useContext } from 'react';
@@ -143,7 +143,7 @@ const SearchPage = () => {
);
case 'techdocs':
return (
<DocsResultListItem
<TechDocsSearchResultListItem
key={document.location}
result={document}
/>
@@ -16,18 +16,18 @@
import { Content } from '@backstage/core-components';
import {
TechDocsPageHeader,
TechDocsPage,
TechDocsReaderPageHeader,
TechDocsReaderPage,
Reader,
} from '@backstage/plugin-techdocs';
import React from 'react';
const DefaultTechDocsPage = () => {
return (
<TechDocsPage>
<TechDocsReaderPage>
{({ techdocsMetadataValue, entityMetadataValue, entityRef, onReady }) => (
<>
<TechDocsPageHeader
<TechDocsReaderPageHeader
techDocsMetadata={techdocsMetadataValue}
entityMetadata={entityMetadataValue}
entityRef={entityRef}
@@ -37,7 +37,7 @@ const DefaultTechDocsPage = () => {
</Content>
</>
)}
</TechDocsPage>
</TechDocsReaderPage>
);
};
@@ -15,7 +15,6 @@ import { orgPlugin } from '@backstage/plugin-org';
import { SearchPage } from '@backstage/plugin-search';
import { TechRadarPage } from '@backstage/plugin-tech-radar';
import {
DefaultTechDocsHome,
TechDocsIndexPage,
techdocsPlugin,
TechDocsReaderPage,
@@ -65,9 +64,7 @@ const routes = (
>
{entityPage}
</Route>
<Route path="/docs" element={<TechDocsIndexPage />}>
<DefaultTechDocsHome />
</Route>
<Route path="/docs" element={<TechDocsIndexPage />} />
<Route
path="/docs/:namespace/:kind/:name/*"
element={<TechDocsReaderPage />}
@@ -6,7 +6,7 @@ import {
catalogApiRef,
CATALOG_FILTER_EXISTS,
} from '@backstage/plugin-catalog-react';
import { DocsResultListItem } from '@backstage/plugin-techdocs';
import { TechDocsSearchResultListItem } from '@backstage/plugin-techdocs';
import {
SearchBar,
@@ -123,7 +123,7 @@ const SearchPage = () => {
);
case 'techdocs':
return (
<DocsResultListItem
<TechDocsSearchResultListItem
key={document.location}
result={document}
/>
+233 -229
View File
@@ -5,7 +5,6 @@
```ts
/// <reference types="react" />
import { Action } from '@material-table/core';
import { ApiRef } from '@backstage/core-plugin-api';
import { BackstagePlugin } from '@backstage/core-plugin-api';
import { Config } from '@backstage/config';
@@ -22,115 +21,69 @@ import { TableColumn } from '@backstage/core-components';
import { TableProps } from '@backstage/core-components';
import { UserListFilterKind } from '@backstage/plugin-catalog-react';
// Warning: (ae-missing-release-tag) "createCopyDocsUrlAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createCopyDocsUrlAction(copyToClipboard: Function): (
row: DocsTableRow,
) => {
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
// @public
export const DefaultTechDocsHome: (
props: DefaultTechDocsHomeProps,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "createNameColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createNameColumn(): TableColumn<DocsTableRow>;
// Warning: (ae-missing-release-tag) "createOwnerColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createOwnerColumn(): TableColumn<DocsTableRow>;
// Warning: (ae-missing-release-tag) "createStarEntityAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
): ({ entity }: DocsTableRow) => {
cellStyle: {
paddingLeft: string;
};
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
// Warning: (ae-missing-release-tag) "createTypeColumn" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
function createTypeColumn(): TableColumn<DocsTableRow>;
// 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<DocsTableRow>[] | undefined;
// @public
export type DefaultTechDocsHomeProps = {
initialFilter?: UserListFilterKind;
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['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)
export const DocsCardGrid: ({
entities,
}: {
// @public
export const DocsCardGrid: (props: DocsCardGridProps) => JSX.Element | null;
// @public
export type DocsCardGridProps = {
entities: Entity[] | undefined;
}) => JSX.Element | null;
};
// Warning: (ae-missing-release-tag) "DocsResultListItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const DocsResultListItem: ({
result,
lineClamp,
asListItem,
asLink,
title,
}: {
result: any;
lineClamp?: number | undefined;
asListItem?: boolean | undefined;
asLink?: boolean | undefined;
title?: string | undefined;
}) => JSX.Element;
// @public @deprecated (undocumented)
export const DocsResultListItem: (
props: TechDocsSearchResultListItemProps,
) => JSX.Element;
// Warning: (ae-missing-release-tag) "DocsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const DocsTable: ({
entities,
title,
loading,
columns,
actions,
}: {
// @public
export const DocsTable: {
(props: DocsTableProps): JSX.Element | null;
columns: {
createNameColumn(): TableColumn<DocsTableRow>;
createOwnerColumn(): TableColumn<DocsTableRow>;
createTypeColumn(): TableColumn<DocsTableRow>;
};
actions: {
createCopyDocsUrlAction(copyToClipboard: Function): (row: DocsTableRow) => {
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
): ({ entity }: DocsTableRow) => {
cellStyle: {
paddingLeft: string;
};
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
};
};
// @public
export type DocsTableProps = {
entities: Entity[] | undefined;
title?: string | undefined;
loading?: boolean | undefined;
columns?: TableColumn<DocsTableRow>[] | undefined;
actions?:
| (
| Action<DocsTableRow>
| {
action: (rowData: DocsTableRow) => Action<DocsTableRow>;
position: string;
}
| ((rowData: DocsTableRow) => Action<DocsTableRow>)
)[]
| undefined;
}) => JSX.Element | null;
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['actions'];
};
// Warning: (ae-missing-release-tag) "DocsTableRow" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type DocsTableRow = {
entity: Entity;
resolved: {
@@ -140,74 +93,101 @@ export type DocsTableRow = {
};
};
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "EmbeddedDocsRouter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EmbeddedDocsRouter: (_props: Props_2) => JSX.Element;
// @public
export const EmbeddedDocsRouter: () => JSX.Element;
// Warning: (ae-missing-release-tag) "EntityListDocsGrid" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const EntityListDocsGrid: () => JSX.Element;
// Warning: (ae-missing-release-tag) "EntityListDocsTable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const EntityListDocsTable: {
({
columns,
actions,
}: {
columns?: TableColumn<DocsTableRow>[] | undefined;
actions?: TableProps<DocsTableRow>['actions'];
}): JSX.Element;
columns: typeof columnFactories;
actions: typeof actionFactories;
(props: EntityListDocsTableProps): JSX.Element;
columns: {
createNameColumn(): TableColumn<DocsTableRow>;
createOwnerColumn(): TableColumn<DocsTableRow>;
createTypeColumn(): TableColumn<DocsTableRow>;
};
actions: {
createCopyDocsUrlAction(copyToClipboard: Function): (row: DocsTableRow) => {
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
): ({ entity }: DocsTableRow) => {
cellStyle: {
paddingLeft: string;
};
icon: () => JSX.Element;
tooltip: string;
onClick: () => any;
};
};
};
// Warning: (ae-missing-release-tag) "EntityTechdocsContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const EntityTechdocsContent: (_props: {}) => JSX.Element;
// @public
export type EntityListDocsTableProps = {
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['actions'];
};
// Warning: (ae-missing-release-tag) "isTechDocsAvailable" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export const EntityTechdocsContent: () => JSX.Element;
// @public
export const isTechDocsAvailable: (entity: Entity) => boolean;
// Warning: (ae-missing-release-tag) "PanelType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export interface PanelConfig {
// (undocumented)
description: string;
// (undocumented)
filterPredicate: ((entity: Entity) => boolean) | string;
// (undocumented)
panelCSS?: CSSProperties;
// (undocumented)
panelType: PanelType;
// (undocumented)
title: string;
}
// @public
export type PanelType = 'DocsCardGrid' | 'DocsTable';
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "Reader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const Reader: ({
entityRef,
onReady,
withSearch,
}: Props_3) => JSX.Element;
// @public
export const Reader: (props: ReaderProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
export type ReaderProps = {
entityRef: EntityName;
withSearch?: boolean;
onReady?: () => void;
};
// @public
export const Router: () => JSX.Element;
// @public
export type SyncResult = 'cached' | 'updated';
// @public
export interface TabConfig {
// (undocumented)
label: string;
// (undocumented)
panels: PanelConfig[];
}
// @public
export type TabsConfig = TabConfig[];
// @public
export interface TechDocsApi {
getApiOrigin(): Promise<string>;
// Warning: (ae-forgotten-export) The symbol "TechDocsEntityMetadata" needs to be exported by the entry point index.d.ts
//
// (undocumented)
getEntityMetadata(entityId: EntityName): Promise<TechDocsEntityMetadata>;
// Warning: (ae-forgotten-export) The symbol "TechDocsMetadata" needs to be exported by the entry point index.d.ts
//
// (undocumented)
getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata>;
}
@@ -232,84 +212,64 @@ export class TechDocsClient implements TechDocsApi {
getTechDocsMetadata(entityId: EntityName): Promise<TechDocsMetadata>;
}
// Warning: (ae-missing-release-tag) "TechDocsCustomHome" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TechDocsCustomHome: ({
tabsConfig,
}: {
// @public
export const TechDocsCustomHome: (
props: TechDocsCustomHomeProps,
) => JSX.Element;
// @public
export type TechDocsCustomHomeProps = {
tabsConfig: TabsConfig;
}) => JSX.Element;
// 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 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: ({ children }: TechDocsPageProps) => 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-missing-release-tag) "TechDocsPageHeader" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TechDocsPageHeader: ({
entityRef,
entityMetadata,
techDocsMetadata,
children,
}: TechDocsPageHeaderProps) => JSX.Element;
// Warning: (ae-missing-release-tag) "TechDocsPageHeaderProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TechDocsPageHeaderProps = PropsWithChildren<{
entityRef: EntityName;
entityMetadata?: TechDocsEntityMetadata;
techDocsMetadata?: TechDocsMetadata;
}>;
// Warning: (ae-missing-release-tag) "TechDocsPageProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TechDocsPageProps = {
children?: TechDocsPageRenderFunction | React_2.ReactNode;
};
// Warning: (ae-missing-release-tag) "TechDocsPageRenderFunction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export type TechDocsPageRenderFunction = ({
techdocsMetadataValue,
entityMetadataValue,
entityRef,
}: {
techdocsMetadataValue?: TechDocsMetadata | undefined;
entityMetadataValue?: TechDocsEntityMetadata | undefined;
entityRef: EntityName;
onReady: () => void;
}) => JSX.Element;
// @public
export type TechDocsEntityMetadata = Entity & {
locationMetadata?: {
type: string;
target: string;
};
};
// 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;
// @public
export const TechDocsIndexPage: () => 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)
// @public
export type TechDocsMetadata = {
site_name: string;
site_description: string;
};
// @public @deprecated (undocumented)
export const TechDocsPage: (props: TechDocsReaderPageProps) => JSX.Element;
// @public
export const TechdocsPage: () => JSX.Element;
// @public @deprecated (undocumented)
export const TechDocsPageHeader: (
props: TechDocsReaderPageHeaderProps,
) => JSX.Element;
// @public @deprecated (undocumented)
export type TechDocsPageHeaderProps = TechDocsReaderPageHeaderProps;
// @public @deprecated (undocumented)
export type TechDocsPageRenderFunction = TechDocsReaderPageRenderFunction;
// @public
export const TechDocsPageWrapper: (
props: TechDocsPageWrapperProps,
) => JSX.Element;
// @public
export type TechDocsPageWrapperProps = {
children?: React_2.ReactNode;
};
// @public
export const TechDocsPicker: () => null;
// Warning: (ae-missing-release-tag) "techdocsPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
// @public
const techdocsPlugin: BackstagePlugin<
{
root: RouteRef<undefined>;
@@ -325,12 +285,62 @@ const techdocsPlugin: BackstagePlugin<
export { techdocsPlugin as plugin };
export { techdocsPlugin };
// Warning: (ae-missing-release-tag) "TechDocsReaderPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const TechDocsReaderPage: ({
children,
}: TechDocsPageProps) => JSX.Element;
// @public
export const TechDocsReaderPage: (
props: TechDocsReaderPageProps,
) => JSX.Element;
// @public
export const TechDocsReaderPageHeader: (
props: TechDocsReaderPageHeaderProps,
) => JSX.Element;
// @public
export type TechDocsReaderPageHeaderProps = PropsWithChildren<{
entityRef: EntityName;
entityMetadata?: TechDocsEntityMetadata;
techDocsMetadata?: TechDocsMetadata;
}>;
// @public
export type TechDocsReaderPageProps = {
children?: TechDocsReaderPageRenderFunction | React_2.ReactNode;
};
// @public
export type TechDocsReaderPageRenderFunction = ({
techdocsMetadataValue,
entityMetadataValue,
entityRef,
}: {
techdocsMetadataValue?: TechDocsMetadata | undefined;
entityMetadataValue?: TechDocsEntityMetadata | undefined;
entityRef: EntityName;
onReady: () => void;
}) => JSX.Element;
// @public
export const TechDocsSearch: (props: TechDocsSearchProps) => JSX.Element;
// @public
export type TechDocsSearchProps = {
entityId: EntityName;
debounceTime?: number;
};
// @public
export const TechDocsSearchResultListItem: (
props: TechDocsSearchResultListItemProps,
) => JSX.Element;
// @public
export type TechDocsSearchResultListItemProps = {
result: any;
lineClamp?: number;
asListItem?: boolean;
asLink?: boolean;
title?: string;
};
// @public
export interface TechDocsStorageApi {
@@ -389,10 +399,4 @@ export class TechDocsStorageClient implements TechDocsStorageApi {
logHandler?: (line: string) => void,
): Promise<SyncResult>;
}
// Warnings were encountered during analysis:
//
// 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:27:5 - (ae-forgotten-export) The symbol "TabsConfig" needs to be exported by the entry point index.d.ts
```
+17 -4
View File
@@ -19,15 +19,25 @@ import { Entity } from '@backstage/catalog-model';
import { useEntity } from '@backstage/plugin-catalog-react';
import { Route, Routes } from 'react-router-dom';
import { TechDocsIndexPage } from './home/components/TechDocsIndexPage';
import { TechDocsPage as TechDocsReaderPage } from './reader/components/TechDocsPage';
import { TechDocsReaderPage } from './reader/components/TechDocsReaderPage';
import { EntityPageDocs } from './EntityPageDocs';
import { MissingAnnotationEmptyState } from '@backstage/core-components';
const TECHDOCS_ANNOTATION = 'backstage.io/techdocs-ref';
/**
* Helper that takes in entity and returns true/false if TechDocs is available for the entity
*
* @public
*/
export const isTechDocsAvailable = (entity: Entity) =>
Boolean(entity?.metadata?.annotations?.[TECHDOCS_ANNOTATION]);
/**
* Responsible for registering routes for TechDocs, TechDocs Homepage and separate TechDocs page
*
* @public
*/
export const Router = () => {
return (
<Routes>
@@ -40,9 +50,12 @@ export const Router = () => {
);
};
type Props = {};
export const EmbeddedDocsRouter = (_props: Props) => {
/**
* Responsible for registering route to view docs on Entity page
*
* @public
*/
export const EmbeddedDocsRouter = () => {
const { entity } = useEntity();
const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION];
@@ -34,20 +34,28 @@ import {
UserListFilterKind,
UserListPicker,
} from '@backstage/plugin-catalog-react';
import { EntityListDocsTable } from './EntityListDocsTable';
import { TechDocsPageWrapper } from './TechDocsPageWrapper';
import { TechDocsPicker } from './TechDocsPicker';
import { DocsTableRow } from './types';
import { DocsTableRow, EntityListDocsTable } from './Tables';
export const DefaultTechDocsHome = ({
initialFilter = 'all',
columns,
actions,
}: {
/**
* Props for {@link DefaultTechDocsHome}
*
* @public
*/
export type DefaultTechDocsHomeProps = {
initialFilter?: UserListFilterKind;
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['actions'];
}) => {
};
/**
* Component which renders a default documentation landing page.
*
* @public
*/
export const DefaultTechDocsHome = (props: DefaultTechDocsHomeProps) => {
const { initialFilter = 'all', columns, actions } = props;
return (
<TechDocsPageWrapper>
<Content>
@@ -18,7 +18,7 @@ import { configApiRef } from '@backstage/core-plugin-api';
import { wrapInTestApp } from '@backstage/test-utils';
import { render } from '@testing-library/react';
import React from 'react';
import { rootDocsRouteRef } from '../../routes';
import { rootDocsRouteRef } from '../../../routes';
import { DocsCardGrid } from './DocsCardGrid';
// Hacky way to mock a specific boolean config value.
@@ -14,25 +14,34 @@
* limitations under the License.
*/
import React from 'react';
import { rootDocsRouteRef } from '../../../routes';
import { toLowerMaybe } from '../../../helpers';
import { Entity } from '@backstage/catalog-model';
import { useApi, useRouteRef, configApiRef } from '@backstage/core-plugin-api';
import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';
import { rootDocsRouteRef } from '../../routes';
import {
Button,
ItemCardGrid,
ItemCardHeader,
} from '@backstage/core-components';
import { toLowerMaybe } from '../../helpers';
import { Card, CardActions, CardContent, CardMedia } from '@material-ui/core';
import React from 'react';
export const DocsCardGrid = ({
entities,
}: {
/**
* Props for {@link DocsCardGrid}
*
* @public
*/
export type DocsCardGridProps = {
entities: Entity[] | undefined;
}) => {
};
/**
* Component which accepts a list of entities and renders a item card for each entity
*
* @public
*/
export const DocsCardGrid = (props: DocsCardGridProps) => {
const { entities } = props;
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
const config = useApi(configApiRef);
if (!entities) return null;
@@ -13,6 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { DocsCardGrid } from './DocsCardGrid';
import {
CodeSnippet,
Progress,
@@ -20,8 +22,12 @@ import {
} from '@backstage/core-components';
import { useEntityList } from '@backstage/plugin-catalog-react';
import React from 'react';
import { DocsCardGrid } from './DocsCardGrid';
/**
* Component responsible to get entities from entity list context and pass down to DocsCardGrid
*
* @public
*/
export const EntityListDocsGrid = () => {
const { loading, error, entities } = useEntityList();
@@ -0,0 +1,18 @@
/*
* Copyright 2022 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.
*/
export * from './EntityListDocsGrid';
export * from './DocsCardGrid';
@@ -17,6 +17,9 @@
import React from 'react';
import { PanelType, TechDocsCustomHome } from './TechDocsCustomHome';
/**
* @deprecated Use {@link TechDocsCustomHome} instead.
*/
export const LegacyTechDocsHome = () => {
const tabsConfig = [
{
@@ -18,7 +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';
import { rootDocsRouteRef } from '../../../routes';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
// Hacky way to mock a specific boolean config value.
@@ -23,7 +23,7 @@ import {
formatEntityRefTitle,
getEntityRelations,
} from '@backstage/plugin-catalog-react';
import { rootDocsRouteRef } from '../../routes';
import { rootDocsRouteRef } from '../../../routes';
import {
Button,
EmptyState,
@@ -31,24 +31,31 @@ import {
TableColumn,
TableProps,
} from '@backstage/core-components';
import * as actionFactories from './actions';
import * as columnFactories from './columns';
import { actionFactories } from './actions';
import { columnFactories } from './columns';
import { toLowerMaybe } from '../../../helpers';
import { DocsTableRow } from './types';
import { toLowerMaybe } from '../../helpers';
export const DocsTable = ({
entities,
title,
loading,
columns,
actions,
}: {
/**
* Props for {@link DocsTable}.
*
* @public
*/
export type DocsTableProps = {
entities: Entity[] | undefined;
title?: string | undefined;
loading?: boolean | undefined;
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['actions'];
}) => {
};
/**
* Component which renders a table documents
*
* @public
*/
export const DocsTable = (props: DocsTableProps) => {
const { entities, title, loading, columns, actions } = props;
const [, copyToClipboard] = useCopyToClipboard();
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
const config = useApi(configApiRef);
@@ -124,3 +131,6 @@ export const DocsTable = ({
</>
);
};
DocsTable.columns = columnFactories;
DocsTable.actions = actionFactories;
@@ -28,17 +28,27 @@ import {
useStarredEntities,
} from '@backstage/plugin-catalog-react';
import { DocsTable } from './DocsTable';
import * as actionFactories from './actions';
import * as columnFactories from './columns';
import { actionFactories } from './actions';
import { columnFactories } from './columns';
import { DocsTableRow } from './types';
export const EntityListDocsTable = ({
columns,
actions,
}: {
/**
* Props for {@link EntityListDocsTable}.
*
* @public
*/
export type EntityListDocsTableProps = {
columns?: TableColumn<DocsTableRow>[];
actions?: TableProps<DocsTableRow>['actions'];
}) => {
};
/**
* Component which renders a table with entities from catalog.
*
* @public
*/
export const EntityListDocsTable = (props: EntityListDocsTableProps) => {
const { columns, actions } = props;
const { loading, error, entities, filters } = useEntityList();
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
const [, copyToClipboard] = useCopyToClipboard();
@@ -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 ShareIcon from '@material-ui/icons/Share';
import {
favoriteEntityIcon,
favoriteEntityTooltip,
} from '@backstage/plugin-catalog-react';
import { DocsTableRow } from './types';
/**
* Not directly exported, but through DocsTable.actions and EntityListDocsTable.actions
*
* @public
*/
export const actionFactories = {
createCopyDocsUrlAction(copyToClipboard: Function) {
return (row: DocsTableRow) => {
return {
icon: () => <ShareIcon fontSize="small" />,
tooltip: 'Click to copy documentation link to clipboard',
onClick: () =>
copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`),
};
};
},
createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
) {
return ({ entity }: DocsTableRow) => {
const isStarred = isStarredEntity(entity);
return {
cellStyle: { paddingLeft: '1em' },
icon: () => favoriteEntityIcon(isStarred),
tooltip: favoriteEntityTooltip(isStarred),
onClick: () => toggleStarredEntity(entity),
};
};
},
};
@@ -0,0 +1,66 @@
/*
* 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 { Link, SubvalueCell, TableColumn } from '@backstage/core-components';
import { EntityRefLinks } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { DocsTableRow } from './types';
function customTitle(entity: Entity): string {
return entity.metadata.title || entity.metadata.name;
}
/**
* Not directly exported, but through DocsTable.columns and EntityListDocsTable.columns
*
* @public
*/
export const columnFactories = {
createNameColumn(): TableColumn<DocsTableRow> {
return {
title: 'Document',
field: 'entity.metadata.name',
highlight: true,
render: (row: DocsTableRow) => (
<SubvalueCell
value={
<Link to={row.resolved.docsUrl}>{customTitle(row.entity)}</Link>
}
subvalue={row.entity.metadata.description}
/>
),
};
},
createOwnerColumn(): TableColumn<DocsTableRow> {
return {
title: 'Owner',
field: 'resolved.ownedByRelationsTitle',
render: ({ resolved }) => (
<EntityRefLinks
entityRefs={resolved.ownedByRelations}
defaultKind="group"
/>
),
};
},
createTypeColumn(): TableColumn<DocsTableRow> {
return {
title: 'Type',
field: 'entity.spec.type',
};
},
};
@@ -0,0 +1,19 @@
/*
* Copyright 2022 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.
*/
export * from './EntityListDocsTable';
export * from './DocsTable';
export * from './types';
@@ -16,6 +16,11 @@
import { Entity, EntityName } from '@backstage/catalog-model';
/**
* Generic representing the metadata structure for a docs table row.
*
* @public
*/
export type DocsTableRow = {
entity: Entity;
resolved: {
@@ -30,8 +30,8 @@ import {
parseEntityRef,
UserEntity,
} from '@backstage/catalog-model';
import { DocsTable } from './DocsTable';
import { DocsCardGrid } from './DocsCardGrid';
import { DocsTable } from './Tables';
import { DocsCardGrid } from './Grids';
import { TechDocsPageWrapper } from './TechDocsPageWrapper';
import {
@@ -51,8 +51,18 @@ const panels = {
DocsCardGrid: DocsCardGrid,
};
/**
* Available panel types
*
* @public
*/
export type PanelType = 'DocsCardGrid' | 'DocsTable';
/**
* Type representing a TechDocsCustomHome panel.
*
* @public
*/
export interface PanelConfig {
title: string;
description: string;
@@ -61,11 +71,21 @@ export interface PanelConfig {
filterPredicate: ((entity: Entity) => boolean) | string;
}
/**
* Type representing a TechDocsCustomHome tab.
*
* @public
*/
export interface TabConfig {
label: string;
panels: PanelConfig[];
}
/**
* Type representing a list of TechDocsCustomHome tabs.
*
* @public
*/
export type TabsConfig = TabConfig[];
const CustomPanel = ({
@@ -118,11 +138,17 @@ const CustomPanel = ({
);
};
export const TechDocsCustomHome = ({
tabsConfig,
}: {
/**
* Props for {@link TechDocsCustomHome}
*
* @public
*/
export type TechDocsCustomHomeProps = {
tabsConfig: TabsConfig;
}) => {
};
export const TechDocsCustomHome = (props: TechDocsCustomHomeProps) => {
const { tabsConfig } = props;
const [selectedTab, setSelectedTab] = useState<number>(0);
const catalogApi: CatalogApi = useApi(catalogApiRef);
@@ -24,8 +24,8 @@ jest.mock('react-router', () => ({
useOutlet: jest.fn().mockReturnValue('Route Children'),
}));
jest.mock('./LegacyTechDocsHome', () => ({
LegacyTechDocsHome: jest.fn().mockReturnValue('LegacyTechDocsHomeMock'),
jest.mock('./DefaultTechDocsHome', () => ({
DefaultTechDocsHome: jest.fn().mockReturnValue('DefaultTechDocsHomeMock'),
}));
describe('TechDocsIndexPage', () => {
@@ -35,10 +35,10 @@ describe('TechDocsIndexPage', () => {
expect(getByText('Route Children')).toBeInTheDocument();
});
it('renders legacy TechDocs home when no router children are provided', async () => {
it('renders DefaultTechDocsHome when no router children are provided', async () => {
(useOutlet as jest.Mock).mockReturnValueOnce(null);
const { getByText } = await renderInTestApp(<TechDocsIndexPage />);
expect(getByText('LegacyTechDocsHomeMock')).toBeInTheDocument();
expect(getByText('DefaultTechDocsHomeMock')).toBeInTheDocument();
});
});
@@ -16,10 +16,10 @@
import React from 'react';
import { useOutlet } from 'react-router';
import { LegacyTechDocsHome } from './LegacyTechDocsHome';
import { DefaultTechDocsHome } from './DefaultTechDocsHome';
export const TechDocsIndexPage = () => {
const outlet = useOutlet();
return outlet || <LegacyTechDocsHome />;
return outlet || <DefaultTechDocsHome />;
};
@@ -19,11 +19,22 @@ import React from 'react';
import { PageWithHeader } from '@backstage/core-components';
import { useApi, configApiRef } from '@backstage/core-plugin-api';
type Props = {
/**
* Props for {@link TechDocsPageWrapper}
*
* @public
*/
export type TechDocsPageWrapperProps = {
children?: React.ReactNode;
};
export const TechDocsPageWrapper = ({ children }: Props) => {
/**
* Component wrapping a techdocs page with Page and Header components
*
* @public
*/
export const TechDocsPageWrapper = (props: TechDocsPageWrapperProps) => {
const { children } = props;
const configApi = useApi(configApiRef);
const generatedSubtitle = `Documentation available in ${
configApi.getOptionalString('organization.name') ?? 'Backstage'
@@ -34,6 +34,11 @@ type CustomFilters = DefaultEntityFilters & {
techdocs?: TechDocsFilter;
};
/**
* Component responsible for updating TechDocs filters
*
* @public
*/
export const TechDocsPicker = () => {
const { updateFilters } = useEntityList<CustomFilters>();
@@ -1,49 +0,0 @@
/*
* 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 ShareIcon from '@material-ui/icons/Share';
import {
favoriteEntityIcon,
favoriteEntityTooltip,
} from '@backstage/plugin-catalog-react';
import { DocsTableRow } from './types';
export function createCopyDocsUrlAction(copyToClipboard: Function) {
return (row: DocsTableRow) => {
return {
icon: () => <ShareIcon fontSize="small" />,
tooltip: 'Click to copy documentation link to clipboard',
onClick: () =>
copyToClipboard(`${window.location.origin}${row.resolved.docsUrl}`),
};
};
}
export function createStarEntityAction(
isStarredEntity: Function,
toggleStarredEntity: Function,
) {
return ({ entity }: DocsTableRow) => {
const isStarred = isStarredEntity(entity);
return {
cellStyle: { paddingLeft: '1em' },
icon: () => favoriteEntityIcon(isStarred),
tooltip: favoriteEntityTooltip(isStarred),
onClick: () => toggleStarredEntity(entity),
};
};
}
@@ -1,59 +0,0 @@
/*
* 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 { Link, SubvalueCell, TableColumn } from '@backstage/core-components';
import { EntityRefLinks } from '@backstage/plugin-catalog-react';
import { Entity } from '@backstage/catalog-model';
import { DocsTableRow } from './types';
function customTitle(entity: Entity): string {
return entity.metadata.title || entity.metadata.name;
}
export function createNameColumn(): TableColumn<DocsTableRow> {
return {
title: 'Document',
field: 'entity.metadata.name',
highlight: true,
render: (row: DocsTableRow) => (
<SubvalueCell
value={<Link to={row.resolved.docsUrl}>{customTitle(row.entity)}</Link>}
subvalue={row.entity.metadata.description}
/>
),
};
}
export function createOwnerColumn(): TableColumn<DocsTableRow> {
return {
title: 'Owner',
field: 'resolved.ownedByRelationsTitle',
render: ({ resolved }) => (
<EntityRefLinks
entityRefs={resolved.ownedByRelations}
defaultKind="group"
/>
),
};
}
export function createTypeColumn(): TableColumn<DocsTableRow> {
return {
title: 'Type',
field: 'entity.spec.type',
};
}
+12 -7
View File
@@ -14,10 +14,15 @@
* limitations under the License.
*/
export { DefaultTechDocsHome } from './DefaultTechDocsHome';
export { EntityListDocsGrid } from './EntityListDocsGrid';
export { EntityListDocsTable } from './EntityListDocsTable';
export type { PanelType } from './TechDocsCustomHome';
export { TechDocsPageWrapper } from './TechDocsPageWrapper';
export { TechDocsPicker } from './TechDocsPicker';
export type { DocsTableRow } from './types';
export * from './Grids';
export * from './Tables';
export * from './DefaultTechDocsHome';
export type {
PanelType,
PanelConfig,
TabConfig,
TabsConfig,
TechDocsCustomHomeProps,
} from './TechDocsCustomHome';
export * from './TechDocsPageWrapper';
export * from './TechDocsPicker';
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2022 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.
*/
export * from './components';
+7 -17
View File
@@ -20,29 +20,19 @@
* @packageDocumentation
*/
export * from './types';
export * from './api';
export { techdocsApiRef, techdocsStorageApiRef } from './api';
export type { TechDocsApi, TechDocsStorageApi } from './api';
export { TechDocsClient, TechDocsStorageClient } from './client';
export * from './components/DocsResultListItem';
export * from './client';
export * from './reader';
export * from './search';
export * from './home';
export {
DefaultTechDocsHome,
EntityListDocsGrid,
EntityListDocsTable,
TechDocsPageWrapper,
TechDocsPicker,
} from './home/components';
export type { DocsTableRow, PanelType } from './home/components';
export {
DocsCardGrid,
DocsTable,
EntityTechdocsContent,
TechDocsCustomHome,
TechDocsIndexPage,
TechdocsPage,
TechDocsReaderPage,
techdocsPlugin as plugin,
techdocsPlugin,
TechDocsReaderPage,
} from './plugin';
export * from './reader';
export { EmbeddedDocsRouter, isTechDocsAvailable, Router } from './Router';
export * from './Router';
+33 -24
View File
@@ -24,7 +24,6 @@ import {
import {
configApiRef,
createApiFactory,
createComponentExtension,
createPlugin,
createRoutableExtension,
discoveryApiRef,
@@ -32,6 +31,11 @@ import {
identityApiRef,
} from '@backstage/core-plugin-api';
/**
* The Backstage plugin that renders technical documentation for your components
*
* @public
*/
export const techdocsPlugin = createPlugin({
id: 'techdocs',
apis: [
@@ -73,6 +77,11 @@ export const techdocsPlugin = createPlugin({
},
});
/**
* Routable extension used to render docs
*
* @public
*/
export const TechdocsPage = techdocsPlugin.provide(
createRoutableExtension({
name: 'TechdocsPage',
@@ -81,6 +90,11 @@ export const TechdocsPage = techdocsPlugin.provide(
}),
);
/**
* Routable extension used to render docs on Entity page
*
* @public
*/
export const EntityTechdocsContent = techdocsPlugin.provide(
createRoutableExtension({
name: 'EntityTechdocsContent',
@@ -89,28 +103,11 @@ export const EntityTechdocsContent = techdocsPlugin.provide(
}),
);
// takes a list of entities and renders documentation cards
export const DocsCardGrid = techdocsPlugin.provide(
createComponentExtension({
name: 'DocsCardGrid',
component: {
lazy: () =>
import('./home/components/DocsCardGrid').then(m => m.DocsCardGrid),
},
}),
);
// takes a list of entities and renders table listing documentation
export const DocsTable = techdocsPlugin.provide(
createComponentExtension({
name: 'DocsTable',
component: {
lazy: () => import('./home/components/DocsTable').then(m => m.DocsTable),
},
}),
);
// takes a custom tabs config object and renders a documentation landing page
/**
* Component which takes a custom tabs config object and renders a documentation landing page.
*
* @public
*/
export const TechDocsCustomHome = techdocsPlugin.provide(
createRoutableExtension({
name: 'TechDocsCustomHome',
@@ -122,6 +119,11 @@ export const TechDocsCustomHome = techdocsPlugin.provide(
}),
);
/**
* Responsible for rendering the provided router element
*
* @public
*/
export const TechDocsIndexPage = techdocsPlugin.provide(
createRoutableExtension({
name: 'TechDocsIndexPage',
@@ -133,11 +135,18 @@ export const TechDocsIndexPage = techdocsPlugin.provide(
}),
);
/**
* Component responsible for composing a TechDocs reader page experience
*
* @public
*/
export const TechDocsReaderPage = techdocsPlugin.provide(
createRoutableExtension({
name: 'TechDocsReaderPage',
component: () =>
import('./reader/components/TechDocsPage').then(m => m.TechDocsPage),
import('./reader/components/TechDocsReaderPage').then(
m => m.TechDocsReaderPage,
),
mountPoint: rootDocsRouteRef,
}),
);
@@ -22,8 +22,11 @@ import { TechDocsNotFound } from './TechDocsNotFound';
import { useApi } from '@backstage/core-plugin-api';
import { Page, Content } from '@backstage/core-components';
import { Reader } from './Reader';
import { TechDocsPageHeader } from './TechDocsPageHeader';
import { TechDocsReaderPageHeader } from './TechDocsReaderPageHeader';
/**
* @deprecated Use {@link TechDocsReaderPage} instead.
*/
export const LegacyTechDocsPage = () => {
const [documentReady, setDocumentReady] = useState<boolean>(false);
const { namespace, kind, name } = useParams();
@@ -53,7 +56,7 @@ export const LegacyTechDocsPage = () => {
return (
<Page themeId="documentation">
<TechDocsPageHeader
<TechDocsReaderPageHeader
techDocsMetadata={techdocsMetadataValue}
entityMetadata={entityMetadataValue}
entityRef={{
@@ -58,11 +58,16 @@ import {
copyToClipboard,
} from '../transformers';
import { TechDocsSearch } from './TechDocsSearch';
import { TechDocsSearch } from '../../search';
import { TechDocsStateIndicator } from './TechDocsStateIndicator';
import { useReaderState } from './useReaderState';
type Props = {
/**
* Props for {@link Reader}
*
* @public
*/
export type ReaderProps = {
entityRef: EntityName;
withSearch?: boolean;
onReady?: () => void;
@@ -840,7 +845,7 @@ const TheReader = ({
entityRef,
onReady = () => {},
withSearch = true,
}: Props) => {
}: ReaderProps) => {
const classes = useStyles();
const dom = useTechDocsReaderDom(entityRef);
const shadowDomRef = useRef<HTMLDivElement>(null);
@@ -877,16 +882,20 @@ const TheReader = ({
);
};
export const Reader = ({
entityRef,
onReady = () => {},
withSearch = true,
}: Props) => (
<TechDocsReaderProvider entityRef={entityRef}>
<TheReader
entityRef={entityRef}
onReady={onReady}
withSearch={withSearch}
/>
</TechDocsReaderProvider>
);
/**
* Component responsible for rendering TechDocs documentation
*
* @public
*/
export const Reader = (props: ReaderProps) => {
const { entityRef, onReady = () => {}, withSearch = true } = props;
return (
<TechDocsReaderProvider entityRef={entityRef}>
<TheReader
entityRef={entityRef}
onReady={onReady}
withSearch={withSearch}
/>
</TechDocsReaderProvider>
);
};
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import React from 'react';
import { TechDocsPage } from './TechDocsPage';
import { TechDocsReaderPage } from './TechDocsReaderPage';
import { render, act } from '@testing-library/react';
import { ConfigReader } from '@backstage/config';
import {
@@ -40,10 +40,10 @@ jest.mock('react-router-dom', () => {
};
});
jest.mock('./TechDocsPageHeader', () => {
jest.mock('./TechDocsReaderPageHeader', () => {
return {
__esModule: true,
TechDocsPageHeader: () => <div />,
TechDocsReaderPageHeader: () => <div />,
};
});
@@ -51,7 +51,7 @@ const { useParams }: { useParams: jest.Mock } =
jest.requireMock('react-router-dom');
global.scroll = jest.fn();
describe('<TechDocsPage />', () => {
describe('<TechDocsReaderPage />', () => {
it('should render techdocs page', async () => {
useParams.mockReturnValue({
entityRef: 'Component::backstage',
@@ -101,7 +101,7 @@ describe('<TechDocsPage />', () => {
const rendered = render(
wrapInTestApp(
<ApiProvider apis={apiRegistry}>
<TechDocsPage />
<TechDocsReaderPage />
</ApiProvider>,
),
);
@@ -158,7 +158,7 @@ describe('<TechDocsPage />', () => {
const rendered = render(
wrapInTestApp(
<ApiProvider apis={apiRegistry}>
<TechDocsPage>
<TechDocsReaderPage>
{({ techdocsMetadataValue }) => (
<Header
type="documentation"
@@ -166,7 +166,7 @@ describe('<TechDocsPage />', () => {
subtitle={techdocsMetadataValue?.site_name}
/>
)}
</TechDocsPage>
</TechDocsReaderPage>
</ApiProvider>,
),
);
@@ -25,7 +25,12 @@ import { EntityName } from '@backstage/catalog-model';
import { useApi, useApp } from '@backstage/core-plugin-api';
import { Page } from '@backstage/core-components';
export type TechDocsPageRenderFunction = ({
/**
* Helper function that gives the children of {@link TechDocsReaderPage} acccess to techdocs and entity metadata
*
* @public
*/
export type TechDocsReaderPageRenderFunction = ({
techdocsMetadataValue,
entityMetadataValue,
entityRef,
@@ -36,11 +41,17 @@ export type TechDocsPageRenderFunction = ({
onReady: () => void;
}) => JSX.Element;
export type TechDocsPageProps = {
children?: TechDocsPageRenderFunction | React.ReactNode;
/**
* Props for {@link TechDocsReaderPage}
*
* @public
*/
export type TechDocsReaderPageProps = {
children?: TechDocsReaderPageRenderFunction | React.ReactNode;
};
export const TechDocsPage = ({ children }: TechDocsPageProps) => {
export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => {
const { children } = props;
const { NotFoundErrorPage } = useApp().getComponents();
const outlet = useOutlet();
@@ -83,3 +94,16 @@ export const TechDocsPage = ({ children }: TechDocsPageProps) => {
</Page>
);
};
/**
* @public
* @deprecated use {@link TechDocsReaderPage} instead
*/
export const TechDocsPage = TechDocsReaderPage;
/**
* @public
* @deprecated use {@link TechDocsReaderPageRenderFunction} instead
*/
export type TechDocsPageRenderFunction = TechDocsReaderPageRenderFunction;
@@ -14,17 +14,17 @@
* limitations under the License.
*/
import React from 'react';
import { TechDocsPageHeader } from './TechDocsPageHeader';
import { TechDocsReaderPageHeader } from './TechDocsReaderPageHeader';
import { act } from '@testing-library/react';
import { renderInTestApp } from '@backstage/test-utils';
import { entityRouteRef } from '@backstage/plugin-catalog-react';
import { rootRouteRef } from '../../routes';
describe('<TechDocsPageHeader />', () => {
describe('<TechDocsReaderPageHeader />', () => {
it('should render a techdocs page header', async () => {
await act(async () => {
const rendered = await renderInTestApp(
<TechDocsPageHeader
<TechDocsReaderPageHeader
entityRef={{
kind: 'test',
name: 'test-name',
@@ -66,7 +66,7 @@ describe('<TechDocsPageHeader />', () => {
it('should render a techdocs page header even if metadata is missing', async () => {
await act(async () => {
const rendered = await renderInTestApp(
<TechDocsPageHeader
<TechDocsReaderPageHeader
entityRef={{
kind: 'test',
name: 'test-name',
@@ -88,7 +88,7 @@ describe('<TechDocsPageHeader />', () => {
it('should render a link back to the component page', async () => {
await act(async () => {
const rendered = await renderInTestApp(
<TechDocsPageHeader
<TechDocsReaderPageHeader
entityRef={{
kind: 'test',
name: 'test-name',
@@ -29,18 +29,26 @@ import {
import { rootRouteRef } from '../../routes';
import { TechDocsEntityMetadata, TechDocsMetadata } from '../../types';
export type TechDocsPageHeaderProps = PropsWithChildren<{
/**
* Props for {@link TechDocsReaderPageHeader}
*
* @public
*/
export type TechDocsReaderPageHeaderProps = PropsWithChildren<{
entityRef: EntityName;
entityMetadata?: TechDocsEntityMetadata;
techDocsMetadata?: TechDocsMetadata;
}>;
export const TechDocsPageHeader = ({
entityRef,
entityMetadata,
techDocsMetadata,
children,
}: TechDocsPageHeaderProps) => {
/**
* Component responsible for rendering a Header with metadata on TechDocs reader page.
*
* @public
*/
export const TechDocsReaderPageHeader = (
props: TechDocsReaderPageHeaderProps,
) => {
const { entityRef, entityMetadata, techDocsMetadata, children } = props;
const { name } = entityRef;
const { site_name: siteName, site_description: siteDescription } =
@@ -114,3 +122,16 @@ export const TechDocsPageHeader = ({
</Header>
);
};
/**
* @public
* @deprecated use {@link TechDocsReaderPageHeader} instead
*/
export const TechDocsPageHeader = TechDocsReaderPageHeader;
/**
* @public
* @deprecated use {@link TechDocsReaderPageHeader} instead
*/
export type TechDocsPageHeaderProps = TechDocsReaderPageHeaderProps;
@@ -15,8 +15,13 @@
*/
export * from './Reader';
export * from './TechDocsPage';
export * from './TechDocsPageHeader';
export type {
TechDocsReaderPageProps,
TechDocsPageRenderFunction,
TechDocsReaderPageRenderFunction,
} from './TechDocsReaderPage';
export { TechDocsPage } from './TechDocsReaderPage';
export * from './TechDocsReaderPageHeader';
export * from './TechDocsStateIndicator';
/**
@@ -31,4 +36,3 @@ export * from './TechDocsStateIndicator';
* todo: Make public or stop exporting (ctrl+f "altReaderExperiments")
* @internal
*/
export { TechDocsSearch } from './TechDocsSearch';
@@ -48,7 +48,7 @@ const singleResult = Promise.resolve({
],
});
describe('<TechDocsPage />', () => {
describe('<TechDocsSearch />', () => {
it('should render techdocs search bar', async () => {
const query = () => emptyResults;
const querySpy = jest.fn(query);
@@ -28,7 +28,7 @@ import Autocomplete from '@material-ui/lab/Autocomplete';
import React, { ChangeEvent, useEffect, useState } from 'react';
import { useNavigate } from 'react-router';
import useDebounce from 'react-use/lib/useDebounce';
import { DocsResultListItem } from '../../components/DocsResultListItem';
import { TechDocsSearchResultListItem } from './TechDocsSearchResultListItem';
const useStyles = makeStyles({
root: {
@@ -36,7 +36,12 @@ const useStyles = makeStyles({
},
});
type TechDocsSearchProps = {
/**
* Props for {@link TechDocsSearch}
*
* @public
*/
export type TechDocsSearchProps = {
entityId: EntityName;
debounceTime?: number;
};
@@ -55,10 +60,8 @@ type TechDocsSearchResult = {
document: TechDocsDoc;
};
const TechDocsSearchBar = ({
entityId,
debounceTime = 150,
}: TechDocsSearchProps) => {
const TechDocsSearchBar = (props: TechDocsSearchProps) => {
const { entityId, debounceTime = 150 } = props;
const [open, setOpen] = useState(false);
const navigate = useNavigate();
const {
@@ -138,7 +141,7 @@ const TechDocsSearchBar = ({
value={null}
options={options}
renderOption={({ document }) => (
<DocsResultListItem
<TechDocsSearchResultListItem
result={document}
lineClamp={3}
asListItem={false}
@@ -180,6 +183,11 @@ const TechDocsSearchBar = ({
);
};
/**
* Component used to render search bar on TechDocs page, scoped to
*
* @public
*/
export const TechDocsSearch = (props: TechDocsSearchProps) => {
const initialState = {
term: '',
@@ -16,7 +16,7 @@
import React from 'react';
import { render } from '@testing-library/react';
import { DocsResultListItem } from './DocsResultListItem';
import { TechDocsSearchResultListItem } from './TechDocsSearchResultListItem';
// Using canvas to render text..
jest.mock('react-text-truncate', () => {
@@ -44,9 +44,11 @@ const validResultWithTitle = {
lifecycle: 'production',
};
describe('DocsResultListItem test', () => {
describe('TechDocsSearchResultListItem test', () => {
it('should render search doc passed in', async () => {
const { findByText } = render(<DocsResultListItem result={validResult} />);
const { findByText } = render(
<TechDocsSearchResultListItem result={validResult} />,
);
expect(
await findByText('Documentation | Backstage docs'),
@@ -60,7 +62,10 @@ describe('DocsResultListItem test', () => {
it('should use title if defined', async () => {
const { findByText } = render(
<DocsResultListItem result={validResult} title="Count Dookumentation" />,
<TechDocsSearchResultListItem
result={validResult}
title="Count Dookumentation"
/>,
);
expect(await findByText('Count Dookumentation')).toBeInTheDocument();
@@ -73,7 +78,7 @@ describe('DocsResultListItem test', () => {
it('should use entity title if defined', async () => {
const { findByText } = render(
<DocsResultListItem result={validResultWithTitle} />,
<TechDocsSearchResultListItem result={validResultWithTitle} />,
);
expect(
@@ -29,19 +29,34 @@ const useStyles = makeStyles({
},
});
export const DocsResultListItem = ({
result,
lineClamp = 5,
asListItem = true,
asLink = true,
title,
}: {
/**
* Props for {@link TechDocsSearchResultListItem}.
*
* @public
*/
export type TechDocsSearchResultListItemProps = {
result: any;
lineClamp?: number;
asListItem?: boolean;
asLink?: boolean;
title?: string;
}) => {
};
/**
* Component which renders documentation and related metadata.
*
* @public
*/
export const TechDocsSearchResultListItem = (
props: TechDocsSearchResultListItemProps,
) => {
const {
result,
lineClamp = 5,
asListItem = true,
asLink = true,
title,
} = props;
const classes = useStyles();
const TextItem = () => (
<ListItemText
@@ -86,3 +101,9 @@ export const DocsResultListItem = ({
</LinkWrapper>
);
};
/**
* @public
* @deprecated use {@link TechDocsSearchResultListItem} instead
*/
export const DocsResultListItem = TechDocsSearchResultListItem;
@@ -14,4 +14,5 @@
* limitations under the License.
*/
export { DocsResultListItem } from './DocsResultListItem';
export * from './TechDocsSearchResultListItem';
export * from './TechDocsSearch';
+17
View File
@@ -0,0 +1,17 @@
/*
* Copyright 2020 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.
*/
export * from './components';
+10
View File
@@ -16,11 +16,21 @@
import { Entity } from '@backstage/catalog-model';
/**
* Metadata for TechDocs page
*
* @public
*/
export type TechDocsMetadata = {
site_name: string;
site_description: string;
};
/**
* Metadata for TechDocs Entity
*
* @public
*/
export type TechDocsEntityMetadata = Entity & {
locationMetadata?: { type: string; target: string };
};
+1
View File
@@ -241,6 +241,7 @@ const NO_WARNING_PACKAGES = [
'plugins/tech-insights-backend-module-jsonfc',
'plugins/tech-insights-common',
'plugins/tech-insights-node',
'plugins/techdocs',
'plugins/todo',
'plugins/todo-backend',
];