Review feedback 2: electric boogaloo

Co-authored-by: Otto Sichert <git@ottosichert.de>
Co-authored-by: Anders Näsman <realandersn@users.noreply.github.com>
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
Eric Peterson
2022-04-12 15:13:04 +02:00
parent a357c354af
commit 39a6eda01d
24 changed files with 271 additions and 245 deletions
@@ -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,
}),
);
+45 -32
View File
@@ -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<TComponentProps>(
export function createTechDocsAddonExtension<TComponentProps>(
options: TechDocsAddonOptions<TComponentProps>,
): Extension<ComponentType<TComponentProps>>;
@@ -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<TAddonProps = {}> = {
name: string;
location: TechDocsAddonLocations;
location: keyof typeof TechDocsAddonLocations;
component: ComponentType<TAddonProps>;
};
// @alpha
export const TechDocsAddons: React_2.ComponentType;
// @public
export interface TechDocsApi {
// (undocumented)
getApiOrigin(): Promise<string>;
// (undocumented)
getEntityMetadata(
entityId: CompoundEntityRef,
): Promise<TechDocsEntityMetadata>;
// (undocumented)
getTechDocsMetadata(entityId: CompoundEntityRef): Promise<TechDocsMetadata>;
}
// @public
export const techdocsApiRef: ApiRef<TechDocsApi>;
// @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<TechDocsMetadata>;
entityName: CompoundEntityRef;
entityRef: CompoundEntityRef;
entityMetadata: AsyncState<TechDocsEntityMetadata>;
shadowRoot?: ShadowRoot;
setShadowRoot: Dispatch<SetStateAction<ShadowRoot | undefined>>;
@@ -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<any>
> | null;
renderComponentsByLocation: (location: TechDocsAddonLocations) =>
| (React_2.ReactElement<
{
[name: string]: unknown;
},
string | React_2.JSXElementConstructor<any>
> | null)[]
| null;
renderComponentByName: (name: string) => JSX.Element | null;
renderComponentsByLocation: (
location: keyof typeof TechDocsAddonLocations,
) => (JSX.Element | null)[] | null;
};
// @alpha
-1
View File
@@ -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"
},
+6 -3
View File
@@ -51,7 +51,7 @@ const getDataKeyByName = (name: string) => {
* Create a TechDocs addon.
* @alpha
*/
export function createTechDocsAddon<TComponentProps>(
export function createTechDocsAddonExtension<TComponentProps>(
options: TechDocsAddonOptions<TComponentProps>,
): Extension<ComponentType<TComponentProps>> {
const { name, component: TechDocsAddon } = options;
@@ -67,7 +67,10 @@ export function createTechDocsAddon<TComponentProps>(
});
}
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;
},
+41
View File
@@ -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<string>;
getTechDocsMetadata(entityId: CompoundEntityRef): Promise<TechDocsMetadata>;
getEntityMetadata(
entityId: CompoundEntityRef,
): Promise<TechDocsEntityMetadata>;
}
/**
* Utility API reference for the {@link TechDocsApi}.
*
* @public
*/
export const techdocsApiRef = createApiRef<TechDocsApi>({
id: 'plugin.techdocs.service',
});
+5 -8
View File
@@ -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;
}) => (
<ThemeProvider theme={lightTheme}>
<TestApiProvider apis={[[techdocsApiRef, techdocsApiMock]]}>
<TechDocsReaderPageProvider entityName={entityName}>
<TechDocsReaderPageProvider entityRef={entityRef}>
{children}
</TechDocsReaderPageProvider>
</TestApiProvider>
+100 -10
View File
@@ -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<TechDocsMetadata>;
entityName: CompoundEntityRef;
entityRef: CompoundEntityRef;
entityMetadata: AsyncState<TechDocsEntityMetadata>;
shadowRoot?: ShadowRoot;
setShadowRoot: Dispatch<SetStateAction<ShadowRoot | undefined>>;
@@ -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<ShadowRoot | undefined>(
defaultTechDocsReaderPageValue.shadowRoot,
);
const value = {
metadata,
entityRef,
entityMetadata,
shadowRoot,
setShadowRoot,
title,
setTitle,
subtitle,
setSubtitle,
};
const versionedValue = createVersionedValueMap({ 1: value });
return (
<TechDocsReaderPageContext.Provider value={versionedValue}>
{children instanceof Function ? children(value) : children}
</TechDocsReaderPageContext.Provider>
);
},
(prevProps, nextProps) => {
return areEntityRefsEqual(prevProps.entityRef, nextProps.entityRef);
},
);
/**
* Hook used to get access to shared state between reader page components.
* @alpha
+9 -3
View File
@@ -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,
+9 -9
View File
@@ -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<TAddonProps = {}> = {
name: string;
location: TechDocsAddonLocations;
location: keyof typeof TechDocsAddonLocations;
component: ComponentType<TAddonProps>;
};
+5 -23
View File
@@ -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<string>;
@@ -219,7 +218,7 @@ export interface TechDocsApi {
getTechDocsMetadata(entityId: CompoundEntityRef): Promise<TechDocsMetadata_2>;
}
// @public
// @public @deprecated
export const techdocsApiRef: ApiRef<TechDocsApi>;
// @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,
+2 -2
View File
@@ -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 (
<TechDocsReaderPageProvider
entityName={{
entityRef={{
kind: 'Component',
namespace: 'default',
name: 'my-docs',
-1
View File
@@ -47,7 +47,6 @@
"@backstage/plugin-search-react": "^0.0.0",
"@backstage/plugin-techdocs-react": "^0.0.0",
"@backstage/theme": "^0.2.15",
"@backstage/version-bridge": "^1.0.0",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
+2
View File
@@ -34,6 +34,7 @@ export const techdocsStorageApiRef = createApiRef<TechDocsStorageApi>({
* Utility API reference for the {@link TechDocsApi}.
*
* @public
* @deprecated Import from `@backstage/plugin-techdocs-react` instead
*/
export const techdocsApiRef = createApiRef<TechDocsApi>({
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<string>;
@@ -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) || (
<TechDocsReaderPageProvider entityName={entityName}>
<TechDocsReaderPageProvider entityRef={entityRef}>
<TechDocsReaderLayout />
</TechDocsReaderPageProvider>
)
@@ -108,12 +106,12 @@ export const TechDocsReaderPage = ({
}
return (
<TechDocsReaderPageProvider entityName={entityName}>
<TechDocsReaderPageProvider entityRef={entityRef}>
{({ metadata, entityMetadata, onReady }) => (
<Page themeId="documentation">
{children instanceof Function
? children({
entityRef: entityName,
entityRef,
techdocsMetadataValue: metadata.value,
entityMetadataValue: entityMetadata.value,
onReady,
@@ -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;
}) => (
<ThemeProvider theme={lightTheme}>
<TestApiProvider apis={[[techdocsApiRef, techdocsApiMock]]}>
<TechDocsReaderPageProvider entityName={entityName}>
<TechDocsReaderPageProvider entityRef={entityRef}>
{children}
</TechDocsReaderPageProvider>
</TestApiProvider>
@@ -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<ShadowRoot | undefined>(
defaultTechDocsReaderPageValue.shadowRoot,
);
const value = {
metadata,
entityName,
entityMetadata,
shadowRoot,
setShadowRoot,
title,
setTitle,
subtitle,
setSubtitle,
};
const versionedValue = createVersionedValueMap({ 1: value });
return (
<TechDocsReaderPageContext.Provider value={versionedValue}>
{children instanceof Function ? children(value) : children}
</TechDocsReaderPageContext.Provider>
);
},
(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
@@ -19,8 +19,3 @@ export type {
TechDocsReaderPageProps,
TechDocsReaderLayoutProps,
} from './TechDocsReaderPage';
export { TechDocsReaderPageProvider } from './context';
export type {
TechDocsReaderPageProviderProps,
TechDocsReaderPageProviderRenderFunction,
} from './context';
@@ -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(
</Grid>
{withSearch && (
<Grid className={classes.search} xs="auto" item>
<TechDocsSearch entityId={entityName} />
<TechDocsSearch entityId={entityRef} />
</Grid>
)}
<Grid xs={12} item>
@@ -146,13 +146,13 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider(
<StylesProvider jss={jss} sheetsManager={new Map()}>
<div ref={ref} data-testid="techdocs-native-shadowroot" />
<Portal container={primarySidebarAddonLocation}>
{addons.renderComponentsByLocation(locations.PRIMARY_SIDEBAR)}
{addons.renderComponentsByLocation(locations.PrimarySidebar)}
</Portal>
<Portal container={contentElement}>
{addons.renderComponentsByLocation(locations.CONTENT)}
{addons.renderComponentsByLocation(locations.Content)}
</Portal>
<Portal container={secondarySidebarAddonLocation}>
{addons.renderComponentsByLocation(locations.SECONDARY_SIDEBAR)}
{addons.renderComponentsByLocation(locations.SecondarySidebar)}
</Portal>
</StylesProvider>
</Grid>
@@ -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 (
@@ -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';
@@ -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;
}) => (
<ThemeProvider theme={lightTheme}>
<TestApiProvider apis={[[techdocsApiRef, techdocsApiMock]]}>
<TechDocsReaderPageProvider entityName={entityName}>
<TechDocsReaderPageProvider entityRef={entityRef}>
{children}
</TechDocsReaderPageProvider>
</TestApiProvider>
@@ -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={
<EntityRefLink
color="inherit"
entityRef={entityName}
entityRef={entityRef}
defaultKind="Component"
/>
}
@@ -158,7 +158,7 @@ export const TechDocsReaderPageHeader = (
</Helmet>
{labels}
{children}
{addons.renderComponentsByLocation(locations.HEADER)}
{addons.renderComponentsByLocation(locations.Header)}
</Header>
);
};
@@ -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 (
<Toolbar {...toolbarProps}>
{addons.renderComponentsByLocation(locations.SUBHEADER) && (
{subheaderAddons && (
<Box
display="flex"
justifyContent="flex-end"
width="100%"
flexWrap="wrap"
>
{addons.renderComponentsByLocation(locations.SUBHEADER)}
{subheaderAddons}
</Box>
)}
</Toolbar>
@@ -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';