diff --git a/packages/techdocs-cli-embedded-app/src/App.tsx b/packages/techdocs-cli-embedded-app/src/App.tsx index 04a068d989..194d26943d 100644 --- a/packages/techdocs-cli-embedded-app/src/App.tsx +++ b/packages/techdocs-cli-embedded-app/src/App.tsx @@ -24,7 +24,7 @@ import { techdocsPlugin, } from '@backstage/plugin-techdocs'; import { - createTechDocsAddon, + createTechDocsAddonExtension, TechDocsAddons, TechDocsAddonLocations, } from '@backstage/plugin-techdocs-react'; @@ -48,10 +48,10 @@ const AppProvider = app.getProvider(); const AppRouter = app.getRouter(); const ThemeToggleAddon = techdocsPlugin.provide( - createTechDocsAddon({ + createTechDocsAddonExtension({ name: 'ThemeToggleAddon', component: TechDocsThemeToggle, - location: TechDocsAddonLocations.HEADER, + location: TechDocsAddonLocations.Header, }), ); diff --git a/plugins/techdocs-react/api-report.md b/plugins/techdocs-react/api-report.md index f9566e6f9b..29bbf7234b 100644 --- a/plugins/techdocs-react/api-report.md +++ b/plugins/techdocs-react/api-report.md @@ -3,19 +3,19 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { ApiRef } from '@backstage/core-plugin-api'; import { AsyncState } from 'react-use/lib/useAsync'; import { ComponentType } from 'react'; import { CompoundEntityRef } from '@backstage/catalog-model'; -import { Context } from 'react'; import { Dispatch } from 'react'; import { Entity } from '@backstage/catalog-model'; import { Extension } from '@backstage/core-plugin-api'; import { default as React_2 } from 'react'; +import { ReactNode } from 'react'; import { SetStateAction } from 'react'; -import { VersionedValue } from '@backstage/version-bridge'; // @alpha -export function createTechDocsAddon( +export function createTechDocsAddonExtension( options: TechDocsAddonOptions, ): Extension>; @@ -26,24 +26,39 @@ export const defaultTechDocsReaderPageValue: TechDocsReaderPageValue; export const TECHDOCS_ADDONS_WRAPPER_KEY = 'techdocs.addons.wrapper.v1'; // @alpha -export enum TechDocsAddonLocations { - CONTENT = 'content', - HEADER = 'header', - PRIMARY_SIDEBAR = 'primary sidebar', - SECONDARY_SIDEBAR = 'secondary sidebar', - SUBHEADER = 'subheader', -} +export const TechDocsAddonLocations: Readonly<{ + readonly Header: 'Header'; + readonly Subheader: 'Subheader'; + readonly PrimarySidebar: 'PrimarySidebar'; + readonly SecondarySidebar: 'SecondarySidebar'; + readonly Content: 'Content'; +}>; // @alpha export type TechDocsAddonOptions = { name: string; - location: TechDocsAddonLocations; + location: keyof typeof TechDocsAddonLocations; component: ComponentType; }; // @alpha export const TechDocsAddons: React_2.ComponentType; +// @public +export interface TechDocsApi { + // (undocumented) + getApiOrigin(): Promise; + // (undocumented) + getEntityMetadata( + entityId: CompoundEntityRef, + ): Promise; + // (undocumented) + getTechDocsMetadata(entityId: CompoundEntityRef): Promise; +} + +// @public +export const techdocsApiRef: ApiRef; + // @public export type TechDocsEntityMetadata = Entity & { locationMetadata?: { @@ -58,18 +73,26 @@ export type TechDocsMetadata = { site_description: string; }; -// @alpha (undocumented) -export const TechDocsReaderPageContext: Context< - | VersionedValue<{ - 1: TechDocsReaderPageValue; - }> - | undefined +// @public +export const TechDocsReaderPageProvider: React_2.MemoExoticComponent< + ({ entityRef, children }: TechDocsReaderPageProviderProps) => JSX.Element >; +// @public +export type TechDocsReaderPageProviderProps = { + entityRef: CompoundEntityRef; + children: TechDocsReaderPageProviderRenderFunction | ReactNode; +}; + +// @public +export type TechDocsReaderPageProviderRenderFunction = ( + value: TechDocsReaderPageValue, +) => JSX.Element; + // @public export type TechDocsReaderPageValue = { metadata: AsyncState; - entityName: CompoundEntityRef; + entityRef: CompoundEntityRef; entityMetadata: AsyncState; shadowRoot?: ShadowRoot; setShadowRoot: Dispatch>; @@ -95,20 +118,10 @@ export const useShadowRootSelection: (wait?: number) => Selection | null; // @alpha export const useTechDocsAddons: () => { - renderComponentByName: (name: string) => React_2.ReactElement< - { - [name: string]: unknown; - }, - string | React_2.JSXElementConstructor - > | null; - renderComponentsByLocation: (location: TechDocsAddonLocations) => - | (React_2.ReactElement< - { - [name: string]: unknown; - }, - string | React_2.JSXElementConstructor - > | null)[] - | null; + renderComponentByName: (name: string) => JSX.Element | null; + renderComponentsByLocation: ( + location: keyof typeof TechDocsAddonLocations, + ) => (JSX.Element | null)[] | null; }; // @alpha diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index cec985baa3..8ef881d406 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -55,7 +55,6 @@ "devDependencies": { "@testing-library/react": "^12.1.3", "@testing-library/react-hooks": "^7.0.2", - "@backstage/plugin-techdocs": "^1.0.1-next.2", "@backstage/test-utils": "^1.0.1-next.1", "@backstage/theme": "^0.2.15" }, diff --git a/plugins/techdocs-react/src/addons.tsx b/plugins/techdocs-react/src/addons.tsx index 8304319a88..54d4f92e2f 100644 --- a/plugins/techdocs-react/src/addons.tsx +++ b/plugins/techdocs-react/src/addons.tsx @@ -51,7 +51,7 @@ const getDataKeyByName = (name: string) => { * Create a TechDocs addon. * @alpha */ -export function createTechDocsAddon( +export function createTechDocsAddonExtension( options: TechDocsAddonOptions, ): Extension> { const { name, component: TechDocsAddon } = options; @@ -67,7 +67,10 @@ export function createTechDocsAddon( }); } -const getTechDocsAddonByName = (collection: ElementCollection, key: string) => { +const getTechDocsAddonByName = ( + collection: ElementCollection, + key: string, +): JSX.Element | undefined => { return collection.selectByComponentData({ key }).getElements()[0]; }; @@ -118,7 +121,7 @@ export const useTechDocsAddons = () => { ); const renderComponentsByLocation = useCallback( - (location: TechDocsAddonLocations) => { + (location: keyof typeof TechDocsAddonLocations) => { const data = options.filter(option => option.location === location); return data.length ? data.map(findAddonByData) : null; }, diff --git a/plugins/techdocs-react/src/api.ts b/plugins/techdocs-react/src/api.ts new file mode 100644 index 0000000000..0d3c0b931a --- /dev/null +++ b/plugins/techdocs-react/src/api.ts @@ -0,0 +1,41 @@ +/* + * 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. + */ + +import { CompoundEntityRef } from '@backstage/catalog-model'; +import { createApiRef } from '@backstage/core-plugin-api'; +import { TechDocsEntityMetadata, TechDocsMetadata } from './types'; + +/** + * API to talk to techdocs-backend. + * + * @public + */ +export interface TechDocsApi { + getApiOrigin(): Promise; + getTechDocsMetadata(entityId: CompoundEntityRef): Promise; + getEntityMetadata( + entityId: CompoundEntityRef, + ): Promise; +} + +/** + * Utility API reference for the {@link TechDocsApi}. + * + * @public + */ +export const techdocsApiRef = createApiRef({ + id: 'plugin.techdocs.service', +}); diff --git a/plugins/techdocs-react/src/context.test.tsx b/plugins/techdocs-react/src/context.test.tsx index c51c9b519f..14669dc336 100644 --- a/plugins/techdocs-react/src/context.test.tsx +++ b/plugins/techdocs-react/src/context.test.tsx @@ -21,12 +21,9 @@ import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { TestApiProvider } from '@backstage/test-utils'; import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; -import { - techdocsApiRef, - TechDocsReaderPageProvider, -} from '@backstage/plugin-techdocs'; -import { useTechDocsReaderPage } from './context'; +import { techdocsApiRef } from './api'; +import { useTechDocsReaderPage, TechDocsReaderPageProvider } from './context'; import { TechDocsMetadata } from './types'; const mockShadowRoot = () => { @@ -59,19 +56,19 @@ const techdocsApiMock = { }; const wrapper = ({ - entityName = { + entityRef = { kind: mockEntityMetadata.kind, name: mockEntityMetadata.metadata.name, namespace: mockEntityMetadata.metadata.namespace!!, }, children, }: { - entityName?: CompoundEntityRef; + entityRef?: CompoundEntityRef; children: React.ReactNode; }) => ( - + {children} diff --git a/plugins/techdocs-react/src/context.tsx b/plugins/techdocs-react/src/context.tsx index 9348855f39..67bf416818 100644 --- a/plugins/techdocs-react/src/context.tsx +++ b/plugins/techdocs-react/src/context.tsx @@ -14,20 +14,45 @@ * limitations under the License. */ -import { Dispatch, SetStateAction, useContext } from 'react'; -import { AsyncState } from 'react-use/lib/useAsync'; +import React, { + Dispatch, + SetStateAction, + useContext, + useState, + memo, + ReactNode, +} from 'react'; +import useAsync, { AsyncState } from 'react-use/lib/useAsync'; -import { CompoundEntityRef } from '@backstage/catalog-model'; -import { createVersionedContext } from '@backstage/version-bridge'; +import { + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { + createVersionedContext, + createVersionedValueMap, +} from '@backstage/version-bridge'; +import { useApi } from '@backstage/core-plugin-api'; + +import { techdocsApiRef } from './api'; import { TechDocsEntityMetadata, TechDocsMetadata } from './types'; +const areEntityRefsEqual = ( + prevEntityRef: CompoundEntityRef, + nextEntityRef: CompoundEntityRef, +) => { + return ( + stringifyEntityRef(prevEntityRef) === stringifyEntityRef(nextEntityRef) + ); +}; + /** * @public type for the value of the TechDocsReaderPageContext */ export type TechDocsReaderPageValue = { metadata: AsyncState; - entityName: CompoundEntityRef; + entityRef: CompoundEntityRef; entityMetadata: AsyncState; shadowRoot?: ShadowRoot; setShadowRoot: Dispatch>; @@ -52,15 +77,80 @@ export const defaultTechDocsReaderPageValue: TechDocsReaderPageValue = { setShadowRoot: () => {}, metadata: { loading: true }, entityMetadata: { loading: true }, - entityName: { kind: '', name: '', namespace: '' }, + entityRef: { kind: '', name: '', namespace: '' }, +}; + +const TechDocsReaderPageContext = createVersionedContext<{ + 1: TechDocsReaderPageValue; +}>('techdocs-reader-page-context'); + +/** + * render function for {@link TechDocsReaderPageProvider} + * + * @public + */ +export type TechDocsReaderPageProviderRenderFunction = ( + value: TechDocsReaderPageValue, +) => JSX.Element; + +/** + * Props for {@link TechDocsReaderPageProvider} + * + * @public + */ +export type TechDocsReaderPageProviderProps = { + entityRef: CompoundEntityRef; + children: TechDocsReaderPageProviderRenderFunction | ReactNode; }; /** - * @alpha + * A context to store the reader page state + * @public */ -export const TechDocsReaderPageContext = createVersionedContext<{ - 1: TechDocsReaderPageValue; -}>('techdocs-reader-page-context'); +export const TechDocsReaderPageProvider = memo( + ({ entityRef, children }: TechDocsReaderPageProviderProps) => { + const techdocsApi = useApi(techdocsApiRef); + + const metadata = useAsync(async () => { + return techdocsApi.getTechDocsMetadata(entityRef); + }, [entityRef]); + + const entityMetadata = useAsync(async () => { + return techdocsApi.getEntityMetadata(entityRef); + }, [entityRef]); + + const [title, setTitle] = useState(defaultTechDocsReaderPageValue.title); + const [subtitle, setSubtitle] = useState( + defaultTechDocsReaderPageValue.subtitle, + ); + const [shadowRoot, setShadowRoot] = useState( + defaultTechDocsReaderPageValue.shadowRoot, + ); + + const value = { + metadata, + entityRef, + entityMetadata, + shadowRoot, + setShadowRoot, + title, + setTitle, + subtitle, + setSubtitle, + }; + const versionedValue = createVersionedValueMap({ 1: value }); + + return ( + + {children instanceof Function ? children(value) : children} + + ); + }, + (prevProps, nextProps) => { + return areEntityRefsEqual(prevProps.entityRef, nextProps.entityRef); + }, +); + /** * Hook used to get access to shared state between reader page components. * @alpha diff --git a/plugins/techdocs-react/src/index.ts b/plugins/techdocs-react/src/index.ts index 691ed9a082..8b46c9d717 100644 --- a/plugins/techdocs-react/src/index.ts +++ b/plugins/techdocs-react/src/index.ts @@ -22,16 +22,22 @@ export { useTechDocsAddons, - createTechDocsAddon, + createTechDocsAddonExtension, TechDocsAddons, TECHDOCS_ADDONS_WRAPPER_KEY, } from './addons'; +export { techdocsApiRef } from './api'; +export type { TechDocsApi } from './api'; export { defaultTechDocsReaderPageValue, - TechDocsReaderPageContext, + TechDocsReaderPageProvider, useTechDocsReaderPage, } from './context'; -export type { TechDocsReaderPageValue } from './context'; +export type { + TechDocsReaderPageProviderProps, + TechDocsReaderPageProviderRenderFunction, + TechDocsReaderPageValue, +} from './context'; export { useShadowRoot, useShadowRootElements, diff --git a/plugins/techdocs-react/src/types.ts b/plugins/techdocs-react/src/types.ts index 87dd9687da..de74d4f284 100644 --- a/plugins/techdocs-react/src/types.ts +++ b/plugins/techdocs-react/src/types.ts @@ -40,34 +40,34 @@ export type TechDocsEntityMetadata = Entity & { * Locations for which TechDocs addons may be declared and rendered. * @alpha */ -export enum TechDocsAddonLocations { +export const TechDocsAddonLocations = Object.freeze({ /** * These addons fill up the header from the right, on the same line as the * title. */ - HEADER = 'header', + Header: 'Header', /** * These addons appear below the header and above all content; tooling addons * can be inserted for convenience. */ - SUBHEADER = 'subheader', + Subheader: 'Subheader', /** * These addons appear left of the content and above the navigation. */ - PRIMARY_SIDEBAR = 'primary sidebar', + PrimarySidebar: 'PrimarySidebar', /** * These addons appear right of the content and above the table of contents. */ - SECONDARY_SIDEBAR = 'secondary sidebar', + SecondarySidebar: 'SecondarySidebar', /** * A virtual location which allows mutation of all content within the shadow * root by transforming DOM nodes. These addons should return null on render. */ - CONTENT = 'content', + Content: 'Content', /** * todo(backstage/community): This is a proposed virtual location which would @@ -97,8 +97,8 @@ export enum TechDocsAddonLocations { * addon, then replace them with component instances of the addon component, * passing any attributes from the tag as props to the component. */ - // COMPONENT = 'component', -} + // Component: 'Component', +} as const); /** * Options for creating a TechDocs addon. @@ -106,6 +106,6 @@ export enum TechDocsAddonLocations { */ export type TechDocsAddonOptions = { name: string; - location: TechDocsAddonLocations; + location: keyof typeof TechDocsAddonLocations; component: ComponentType; }; diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index f26ff3ec8e..4575ef7b28 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/api-report.md @@ -23,7 +23,6 @@ import { TableColumn } from '@backstage/core-components'; import { TableProps } from '@backstage/core-components'; import { TechDocsEntityMetadata as TechDocsEntityMetadata_2 } from '@backstage/plugin-techdocs-react'; import { TechDocsMetadata as TechDocsMetadata_2 } from '@backstage/plugin-techdocs-react'; -import { TechDocsReaderPageValue } from '@backstage/plugin-techdocs-react'; import { ToolbarProps } from '@material-ui/core'; import { UserListFilterKind } from '@backstage/plugin-catalog-react'; @@ -207,7 +206,7 @@ export interface TabConfig { // @public export type TabsConfig = TabConfig[]; -// @public +// @public @deprecated export interface TechDocsApi { // (undocumented) getApiOrigin(): Promise; @@ -219,7 +218,7 @@ export interface TechDocsApi { getTechDocsMetadata(entityId: CompoundEntityRef): Promise; } -// @public +// @public @deprecated export const techdocsApiRef: ApiRef; // @public @@ -305,10 +304,9 @@ export type TechDocsReaderLayoutProps = { }; // @public -export const TechDocsReaderPage: ({ - entityRef, - children, -}: TechDocsReaderPageProps) => JSX.Element; +export const TechDocsReaderPage: ( + props: TechDocsReaderPageProps, +) => JSX.Element; // @public export const TechDocsReaderPageContent: ( @@ -340,22 +338,6 @@ export type TechDocsReaderPageProps = { children?: TechDocsReaderPageRenderFunction | ReactNode; }; -// @public -export const TechDocsReaderPageProvider: React_2.MemoExoticComponent< - ({ entityName, children }: TechDocsReaderPageProviderProps) => JSX.Element ->; - -// @public -export type TechDocsReaderPageProviderProps = { - entityName: CompoundEntityRef; - children: TechDocsReaderPageProviderRenderFunction | ReactNode; -}; - -// @public -export type TechDocsReaderPageProviderRenderFunction = ( - value: TechDocsReaderPageValue, -) => JSX.Element; - // @public export type TechDocsReaderPageRenderFunction = ({ techdocsMetadataValue, diff --git a/plugins/techdocs/dev/index.tsx b/plugins/techdocs/dev/index.tsx index aa4b7bde6c..9cf0364c63 100644 --- a/plugins/techdocs/dev/index.tsx +++ b/plugins/techdocs/dev/index.tsx @@ -19,7 +19,6 @@ import { NotFoundError } from '@backstage/errors'; import React from 'react'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { - TechDocsReaderPageProvider, TechDocsReaderPageContent, SyncResult, TechDocsStorageApi, @@ -31,6 +30,7 @@ import { discoveryApiRef, identityApiRef, } from '@backstage/core-plugin-api'; +import { TechDocsReaderPageProvider } from '@backstage/plugin-techdocs-react'; import { Header, Page, TabbedLayout } from '@backstage/core-components'; // used so each route can provide it's own implementation in the constructor of the react component @@ -114,7 +114,7 @@ function createPage({ render() { return ( ({ * Utility API reference for the {@link TechDocsApi}. * * @public + * @deprecated Import from `@backstage/plugin-techdocs-react` instead */ export const techdocsApiRef = createApiRef({ id: 'plugin.techdocs.service', @@ -71,6 +72,7 @@ export interface TechDocsStorageApi { * API to talk to techdocs-backend. * * @public + * @deprecated Import from `@backstage/plugin-techdocs-react` instead */ export interface TechDocsApi { getApiOrigin(): Promise; diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index ea8493a183..a84c0a8122 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -19,7 +19,10 @@ import { useOutlet, useParams } from 'react-router-dom'; import { Page } from '@backstage/core-components'; import { CompoundEntityRef } from '@backstage/catalog-model'; -import { TECHDOCS_ADDONS_WRAPPER_KEY } from '@backstage/plugin-techdocs-react'; +import { + TECHDOCS_ADDONS_WRAPPER_KEY, + TechDocsReaderPageProvider, +} from '@backstage/plugin-techdocs-react'; import { TechDocsReaderPageRenderFunction } from '../../../types'; @@ -27,8 +30,6 @@ import { TechDocsReaderPageContent } from '../TechDocsReaderPageContent'; import { TechDocsReaderPageHeader } from '../TechDocsReaderPageHeader'; import { TechDocsReaderPageSubheader } from '../TechDocsReaderPageSubheader'; -import { TechDocsReaderPageProvider } from './context'; - type Extension = ReactChild & { type: { __backstage_data: { @@ -81,14 +82,11 @@ export type TechDocsReaderPageProps = { * An addon-aware implementation of the TechDocsReaderPage. * @public */ -export const TechDocsReaderPage = ({ - entityRef, - children, -}: TechDocsReaderPageProps) => { +export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { const { kind, name, namespace } = useParams(); + const { children, entityRef = { kind, name, namespace } } = props; const outlet = useOutlet(); - const entityName = entityRef ?? { kind, name, namespace }; if (!children) { const childrenList = outlet ? Children.toArray(outlet.props.children) : []; @@ -100,7 +98,7 @@ export const TechDocsReaderPage = ({ return ( (page as JSX.Element) || ( - + ) @@ -108,12 +106,12 @@ export const TechDocsReaderPage = ({ } return ( - + {({ metadata, entityMetadata, onReady }) => ( {children instanceof Function ? children({ - entityRef: entityName, + entityRef, techdocsMetadataValue: metadata.value, entityMetadataValue: entityMetadata.value, onReady, diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.test.tsx index 29fab166ca..148b740205 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.test.tsx @@ -22,15 +22,13 @@ import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { TestApiProvider } from '@backstage/test-utils'; import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; -import { TechDocsMetadata } from '@backstage/plugin-techdocs-react'; - -import { techdocsApiRef } from '../../../api'; - import { - useEntityMetadata, - useTechDocsMetadata, + techdocsApiRef, + TechDocsMetadata, TechDocsReaderPageProvider, -} from './context'; +} from '@backstage/plugin-techdocs-react'; + +import { useEntityMetadata, useTechDocsMetadata } from './context'; const mockEntityMetadata: Entity = { apiVersion: 'v1', @@ -55,19 +53,19 @@ const techdocsApiMock = { }; const wrapper = ({ - entityName = { + entityRef = { kind: mockEntityMetadata.kind, name: mockEntityMetadata.metadata.name, namespace: mockEntityMetadata.metadata.namespace!!, }, children, }: { - entityName?: CompoundEntityRef; + entityRef?: CompoundEntityRef; children: React.ReactNode; }) => ( - + {children} diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx index e342b43af4..899eb8475a 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/context.tsx @@ -14,103 +14,7 @@ * limitations under the License. */ -import React, { ReactNode, memo, useState } from 'react'; -import useAsync from 'react-use/lib/useAsync'; - -import { useApi } from '@backstage/core-plugin-api'; -import { CompoundEntityRef } from '@backstage/catalog-model'; -import { - TechDocsReaderPageValue, - defaultTechDocsReaderPageValue, - TechDocsReaderPageContext, - useTechDocsReaderPage, -} from '@backstage/plugin-techdocs-react'; -import { createVersionedValueMap } from '@backstage/version-bridge'; - -import { techdocsApiRef } from '../../../api'; - -const areEntityNamesEqual = ( - prevEntityName: CompoundEntityRef, - nextEntityName: CompoundEntityRef, -) => { - if (prevEntityName.kind !== nextEntityName.kind) { - return false; - } - if (prevEntityName.name !== nextEntityName.name) { - return false; - } - if (prevEntityName.namespace !== nextEntityName.namespace) { - return false; - } - return true; -}; - -/** - * render function for {@link TechDocsReaderPageProvider} - * - * @public - */ -export type TechDocsReaderPageProviderRenderFunction = ( - value: TechDocsReaderPageValue, -) => JSX.Element; - -/** - * Props for {@link TechDocsReaderPageProvider} - * - * @public - */ -export type TechDocsReaderPageProviderProps = { - entityName: CompoundEntityRef; - children: TechDocsReaderPageProviderRenderFunction | ReactNode; -}; - -/** - * A context to store the reader page state - * @public - */ -export const TechDocsReaderPageProvider = memo( - ({ entityName, children }: TechDocsReaderPageProviderProps) => { - const techdocsApi = useApi(techdocsApiRef); - - const metadata = useAsync(async () => { - return techdocsApi.getTechDocsMetadata(entityName); - }, [entityName]); - - const entityMetadata = useAsync(async () => { - return techdocsApi.getEntityMetadata(entityName); - }, [entityName]); - - const [title, setTitle] = useState(defaultTechDocsReaderPageValue.title); - const [subtitle, setSubtitle] = useState( - defaultTechDocsReaderPageValue.subtitle, - ); - const [shadowRoot, setShadowRoot] = useState( - defaultTechDocsReaderPageValue.shadowRoot, - ); - - const value = { - metadata, - entityName, - entityMetadata, - shadowRoot, - setShadowRoot, - title, - setTitle, - subtitle, - setSubtitle, - }; - const versionedValue = createVersionedValueMap({ 1: value }); - - return ( - - {children instanceof Function ? children(value) : children} - - ); - }, - (prevProps, nextProps) => { - return areEntityNamesEqual(prevProps.entityName, nextProps.entityName); - }, -); +import { useTechDocsReaderPage } from '@backstage/plugin-techdocs-react'; /** * Hook for sub-components to retrieve Entity Metadata for the current TechDocs diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/index.ts b/plugins/techdocs/src/reader/components/TechDocsReaderPage/index.ts index 6bff83aad8..8defb196f6 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/index.ts +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/index.ts @@ -19,8 +19,3 @@ export type { TechDocsReaderPageProps, TechDocsReaderLayoutProps, } from './TechDocsReaderPage'; -export { TechDocsReaderPageProvider } from './context'; -export type { - TechDocsReaderPageProviderProps, - TechDocsReaderPageProviderRenderFunction, -} from './context'; diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx index b3e9682bcf..c34b04ab8c 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx @@ -72,8 +72,8 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider( const { withSearch = true, onReady } = props; const classes = useStyles(); const addons = useTechDocsAddons(); - const { entityName, shadowRoot, setShadowRoot } = useTechDocsReaderPage(); - const dom = useTechDocsReaderDom(entityName); + const { entityRef, shadowRoot, setShadowRoot } = useTechDocsReaderPage(); + const dom = useTechDocsReaderDom(entityRef); const [jss, setJss] = useState( create({ @@ -138,7 +138,7 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider( {withSearch && ( - + )} @@ -146,13 +146,13 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider(
- {addons.renderComponentsByLocation(locations.PRIMARY_SIDEBAR)} + {addons.renderComponentsByLocation(locations.PrimarySidebar)} - {addons.renderComponentsByLocation(locations.CONTENT)} + {addons.renderComponentsByLocation(locations.Content)} - {addons.renderComponentsByLocation(locations.SECONDARY_SIDEBAR)} + {addons.renderComponentsByLocation(locations.SecondarySidebar)} diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/context.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/context.tsx index ce9a6feffc..5779ade90c 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/context.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/context.tsx @@ -62,8 +62,8 @@ export const TechDocsReaderProvider = ({ children, }: TechDocsReaderProviderProps) => { const { '*': path = '' } = useParams(); - const { entityName } = useTechDocsReaderPage(); - const { kind, namespace, name } = entityName; + const { entityRef } = useTechDocsReaderPage(); + const { kind, namespace, name } = entityRef; const value = useReaderState(kind, namespace, name, path); return ( diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index de791a7068..e8d15e5231 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -75,7 +75,7 @@ export const useTechDocsReaderDom = ( const techdocsStorageApi = useApi(techdocsStorageApiRef); const scmIntegrationsApi = useApi(scmIntegrationsApiRef); const techdocsSanitizer = useApi(configApiRef); - const { namespace = '', kind = '', name = '' } = entityRef; + const { namespace, kind, name } = entityRef; const { state, path, content: rawPage } = useTechDocsReader(); const isDarkTheme = theme.palette.type === 'dark'; diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx index 0ace77c2ed..288e3bec82 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.test.tsx @@ -21,13 +21,14 @@ import { ThemeProvider } from '@material-ui/core'; import { lightTheme } from '@backstage/theme'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; +import { + techdocsApiRef, + TechDocsReaderPageProvider, +} from '@backstage/plugin-techdocs-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { techdocsApiRef } from '../../../api'; import { rootRouteRef } from '../../../routes'; -import { TechDocsReaderPageProvider } from '../TechDocsReaderPage'; - import { TechDocsReaderPageHeader } from './TechDocsReaderPageHeader'; const mockEntityMetadata = { @@ -60,19 +61,19 @@ const techdocsApiMock = { }; const Wrapper = ({ - entityName = { + entityRef = { kind: mockEntityMetadata.kind, name: mockEntityMetadata.metadata.name, namespace: mockEntityMetadata.metadata.namespace!!, }, children, }: { - entityName?: CompoundEntityRef; + entityRef?: CompoundEntityRef; children: React.ReactNode; }) => ( - + {children} diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx index bd900a3915..5835121d2e 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageHeader/TechDocsReaderPageHeader.tsx @@ -70,7 +70,7 @@ export const TechDocsReaderPageHeader = ( setTitle, subtitle, setSubtitle, - entityName, + entityRef, metadata: { value: metadata }, entityMetadata: { value: entityMetadata }, } = useTechDocsReaderPage(); @@ -109,7 +109,7 @@ export const TechDocsReaderPageHeader = ( value={ } @@ -158,7 +158,7 @@ export const TechDocsReaderPageHeader = ( {labels} {children} - {addons.renderComponentsByLocation(locations.HEADER)} + {addons.renderComponentsByLocation(locations.Header)} ); }; diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx index 6b8beef8d2..62d64fa858 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageSubheader/TechDocsReaderPageSubheader.tsx @@ -37,19 +37,22 @@ export const TechDocsReaderPageSubheader = withStyles(theme => ({ }, }))(({ toolbarProps }: { toolbarProps?: ToolbarProps }) => { const addons = useTechDocsAddons(); + const subheaderAddons = addons.renderComponentsByLocation( + locations.Subheader, + ); - if (!addons.renderComponentsByLocation(locations.SUBHEADER)) return null; + if (!subheaderAddons) return null; return ( - {addons.renderComponentsByLocation(locations.SUBHEADER) && ( + {subheaderAddons && ( - {addons.renderComponentsByLocation(locations.SUBHEADER)} + {subheaderAddons} )} diff --git a/plugins/techdocs/src/reader/components/index.ts b/plugins/techdocs/src/reader/components/index.ts index 1c0e4edca9..406d623bda 100644 --- a/plugins/techdocs/src/reader/components/index.ts +++ b/plugins/techdocs/src/reader/components/index.ts @@ -17,13 +17,8 @@ export type { TechDocsReaderPageProps, TechDocsReaderLayoutProps, - TechDocsReaderPageProviderProps, - TechDocsReaderPageProviderRenderFunction, -} from './TechDocsReaderPage'; -export { - TechDocsReaderLayout, - TechDocsReaderPageProvider, } from './TechDocsReaderPage'; +export { TechDocsReaderLayout } from './TechDocsReaderPage'; export * from './TechDocsReaderPageHeader'; export * from './TechDocsReaderPageContent'; export * from './TechDocsReaderPageSubheader';