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:
@@ -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,
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
|
||||
@@ -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',
|
||||
});
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>;
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
|
||||
+6
-6
@@ -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';
|
||||
|
||||
|
||||
+7
-6
@@ -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>
|
||||
|
||||
+3
-3
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
+6
-3
@@ -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';
|
||||
|
||||
Reference in New Issue
Block a user