Allow addons (except header location) to be used in Entity Reader
Signed-off-by: Eric Peterson <ericpeterson@spotify.com>
This commit is contained in:
committed by
Emma Indal
parent
413024e182
commit
ace749b785
@@ -3,8 +3,6 @@
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { AsyncState } from 'react-use/lib/useAsyncFn';
|
||||
import { ComponentType } from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
@@ -63,6 +61,8 @@ export const TechDocsReaderPage: (
|
||||
|
||||
// @public (undocumented)
|
||||
export type TechDocsReaderPageProps = {
|
||||
hideHeader?: boolean;
|
||||
addonConfig?: React_2.ReactNode;
|
||||
dom: Element | null;
|
||||
asyncEntityMetadata: AsyncState<TechDocsEntityMetadata>;
|
||||
asyncTechDocsMetadata: AsyncState<TechDocsMetadata>;
|
||||
|
||||
@@ -21,7 +21,13 @@ import {
|
||||
Extension,
|
||||
useElementFilter,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import React, { ComponentType, useCallback } from 'react';
|
||||
import React, {
|
||||
ComponentType,
|
||||
createContext,
|
||||
PropsWithChildren,
|
||||
useCallback,
|
||||
useContext,
|
||||
} from 'react';
|
||||
import { useOutlet } from 'react-router-dom';
|
||||
|
||||
import { TechDocsAddonLocations, TechDocsAddonOptions } from './types';
|
||||
@@ -90,8 +96,30 @@ const getAllTechDocsAddonsData = (collection: ElementCollection) => {
|
||||
});
|
||||
};
|
||||
|
||||
type TechDocsAddonConfig = {
|
||||
config?: React.ReactNode | null;
|
||||
};
|
||||
|
||||
const TechDocsAddonConfigContext = createContext<TechDocsAddonConfig>({});
|
||||
|
||||
export const TechDocsAddonConfigProvider = (
|
||||
props: PropsWithChildren<{ config?: React.ReactNode }>,
|
||||
) => {
|
||||
const fromOutlet = useOutlet();
|
||||
const config = props.config ?? fromOutlet;
|
||||
return (
|
||||
<TechDocsAddonConfigContext.Provider value={{ config }}>
|
||||
{props.children}
|
||||
</TechDocsAddonConfigContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const useTechDocsAddonsConfig = (): React.ReactNode | null => {
|
||||
return useContext(TechDocsAddonConfigContext).config || null;
|
||||
};
|
||||
|
||||
export const useTechDocsAddons = () => {
|
||||
const node = useOutlet();
|
||||
const node = useTechDocsAddonsConfig();
|
||||
|
||||
const collection = useElementFilter(node, getAllTechDocsAddons);
|
||||
const options = useElementFilter(node, getAllTechDocsAddonsData);
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Page } from '@backstage/core-components';
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { AsyncState } from 'react-use/lib/useAsyncFn';
|
||||
import { TechDocsAddonConfigProvider } from '../../addons';
|
||||
|
||||
import {
|
||||
TechDocsMetadataProvider,
|
||||
@@ -33,6 +34,8 @@ import { TechDocsReaderPageSubheader } from '../TechDocsReaderPageSubheader';
|
||||
* @public
|
||||
*/
|
||||
export type TechDocsReaderPageProps = {
|
||||
hideHeader?: boolean;
|
||||
addonConfig?: React.ReactNode;
|
||||
dom: Element | null;
|
||||
asyncEntityMetadata: AsyncState<TechDocsEntityMetadata>;
|
||||
asyncTechDocsMetadata: AsyncState<TechDocsMetadata>;
|
||||
@@ -43,21 +46,29 @@ export type TechDocsReaderPageProps = {
|
||||
* @public
|
||||
*/
|
||||
export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => {
|
||||
const { asyncEntityMetadata, asyncTechDocsMetadata, dom } = props;
|
||||
const {
|
||||
addonConfig,
|
||||
asyncEntityMetadata,
|
||||
asyncTechDocsMetadata,
|
||||
dom,
|
||||
hideHeader = false,
|
||||
} = props;
|
||||
const { namespace, kind, name } = useParams();
|
||||
const entityName = { namespace, kind, name };
|
||||
return (
|
||||
<TechDocsMetadataProvider asyncValue={asyncTechDocsMetadata}>
|
||||
<TechDocsEntityProvider asyncValue={asyncEntityMetadata}>
|
||||
<TechDocsReaderPageProvider entityName={entityName}>
|
||||
<Page themeId="documentation">
|
||||
<TechDocsReaderPageHeader />
|
||||
<TechDocsReaderPageSubheader />
|
||||
{/* todo(backstage/techdocs-core): handle state indicator */}
|
||||
{/* <TechDocReaderPageIndicator /> */}
|
||||
<TechDocsReaderPageContent dom={dom} />
|
||||
</Page>
|
||||
</TechDocsReaderPageProvider>
|
||||
<TechDocsAddonConfigProvider config={addonConfig}>
|
||||
<TechDocsReaderPageProvider entityName={entityName}>
|
||||
<Page themeId="documentation">
|
||||
{!hideHeader && <TechDocsReaderPageHeader />}
|
||||
<TechDocsReaderPageSubheader />
|
||||
{/* todo(backstage/techdocs-core): handle state indicator */}
|
||||
{/* <TechDocReaderPageIndicator /> */}
|
||||
<TechDocsReaderPageContent dom={dom} />
|
||||
</Page>
|
||||
</TechDocsReaderPageProvider>
|
||||
</TechDocsAddonConfigProvider>
|
||||
</TechDocsEntityProvider>
|
||||
</TechDocsMetadataProvider>
|
||||
);
|
||||
|
||||
@@ -16,6 +16,7 @@ import { FetchApi } from '@backstage/core-plugin-api';
|
||||
import { IdentityApi } from '@backstage/core-plugin-api';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ReactNode } from 'react';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
import { TableColumn } from '@backstage/core-components';
|
||||
import { TableProps } from '@backstage/core-components';
|
||||
@@ -89,7 +90,7 @@ export type DocsTableRow = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const EmbeddedDocsRouter: () => JSX.Element;
|
||||
export const EmbeddedDocsRouter: (props: PropsWithChildren<{}>) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const EntityListDocsGrid: () => JSX.Element;
|
||||
@@ -129,7 +130,9 @@ export type EntityListDocsTableProps = {
|
||||
};
|
||||
|
||||
// @public
|
||||
export const EntityTechdocsContent: () => JSX.Element;
|
||||
export const EntityTechdocsContent: (props: {
|
||||
children?: ReactNode;
|
||||
}) => JSX.Element;
|
||||
|
||||
// @public
|
||||
export const isTechDocsAvailable: (entity: Entity) => boolean;
|
||||
|
||||
@@ -14,22 +14,90 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Reader } from './reader';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import {
|
||||
CompoundEntityRef,
|
||||
DEFAULT_NAMESPACE,
|
||||
Entity,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
Reader,
|
||||
useTechDocsReaderDom,
|
||||
withTechDocsReaderProvider,
|
||||
} from './reader';
|
||||
import { toLowerMaybe } from './helpers';
|
||||
import { configApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
configApiRef,
|
||||
getComponentData,
|
||||
useApi,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
TechDocsReaderPage as AddonAwareReaderPage,
|
||||
TECHDOCS_ADDONS_WRAPPER_KEY,
|
||||
} from '@backstage/plugin-techdocs-addons';
|
||||
import { AsyncState } from 'react-use/lib/useAsyncFn';
|
||||
import { TechDocsEntityMetadata } from './types';
|
||||
import { techdocsApiRef } from '.';
|
||||
import useAsync from 'react-use/lib/useAsync';
|
||||
|
||||
type SpecialReaderPageProps = {
|
||||
entityName: CompoundEntityRef;
|
||||
asyncEntityMetadata: AsyncState<TechDocsEntityMetadata>;
|
||||
addonConfig?: React.ReactNode;
|
||||
};
|
||||
|
||||
// todo(backstage/techdocs-core): Combine with <SpecialReaderPage> and simplify
|
||||
// with the version in TechDocsReaderPage.tsx
|
||||
const SpecialReaderPage = (props: SpecialReaderPageProps) => {
|
||||
const techdocsApi = useApi(techdocsApiRef);
|
||||
const dom = useTechDocsReaderDom(props.entityName);
|
||||
const { kind, namespace, name } = props.entityName;
|
||||
|
||||
const asyncTechDocsMetadata = useAsync(() => {
|
||||
return techdocsApi.getTechDocsMetadata({ kind, namespace, name });
|
||||
}, [kind, namespace, name, techdocsApi]);
|
||||
|
||||
export const EntityPageDocs = ({ entity }: { entity: Entity }) => {
|
||||
const config = useApi(configApiRef);
|
||||
return (
|
||||
<Reader
|
||||
withSearch={false}
|
||||
entityRef={{
|
||||
namespace: toLowerMaybe(entity.metadata.namespace ?? 'default', config),
|
||||
kind: toLowerMaybe(entity.kind, config),
|
||||
name: toLowerMaybe(entity.metadata.name, config),
|
||||
}}
|
||||
<AddonAwareReaderPage
|
||||
asyncEntityMetadata={props.asyncEntityMetadata}
|
||||
asyncTechDocsMetadata={asyncTechDocsMetadata}
|
||||
addonConfig={props.addonConfig}
|
||||
dom={dom}
|
||||
hideHeader
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const EntityPageDocs = ({
|
||||
children,
|
||||
entity,
|
||||
}: PropsWithChildren<{ entity: Entity }>) => {
|
||||
const config = useApi(configApiRef);
|
||||
const entityName = {
|
||||
namespace: toLowerMaybe(
|
||||
entity.metadata.namespace ?? DEFAULT_NAMESPACE,
|
||||
config,
|
||||
),
|
||||
kind: toLowerMaybe(entity.kind, config),
|
||||
name: toLowerMaybe(entity.metadata.name, config),
|
||||
};
|
||||
|
||||
// Check if we were given a set of TechDocs addons.
|
||||
if (children && getComponentData(children, TECHDOCS_ADDONS_WRAPPER_KEY)) {
|
||||
const Component = withTechDocsReaderProvider(SpecialReaderPage, entityName);
|
||||
return (
|
||||
<Component
|
||||
entityName={entityName}
|
||||
asyncEntityMetadata={{
|
||||
loading: false,
|
||||
error: undefined,
|
||||
value: entity,
|
||||
}}
|
||||
addonConfig={children}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Otherwise, return a version of the reader that is not addon-aware.
|
||||
return <Reader withSearch={false} entityRef={entityName} />;
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { PropsWithChildren } from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { Route, Routes } from 'react-router-dom';
|
||||
@@ -55,7 +55,8 @@ export const Router = () => {
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export const EmbeddedDocsRouter = () => {
|
||||
export const EmbeddedDocsRouter = (props: PropsWithChildren<{}>) => {
|
||||
const { children } = props;
|
||||
const { entity } = useEntity();
|
||||
|
||||
const projectId = entity.metadata.annotations?.[TECHDOCS_ANNOTATION];
|
||||
@@ -66,7 +67,10 @@ export const EmbeddedDocsRouter = () => {
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/*" element={<EntityPageDocs entity={entity} />} />
|
||||
<Route
|
||||
path="/*"
|
||||
element={<EntityPageDocs entity={entity}>{children}</EntityPageDocs>}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -47,8 +47,8 @@ export type TechDocsReaderPageRenderFunction = ({
|
||||
|
||||
type SpecialReaderPageProps = {
|
||||
entityName: CompoundEntityRef;
|
||||
asyncEntityMetadata: AsyncState<any>;
|
||||
asyncTechDocsMetadata: AsyncState<any>;
|
||||
asyncEntityMetadata: AsyncState<TechDocsEntityMetadata>;
|
||||
asyncTechDocsMetadata: AsyncState<TechDocsMetadata>;
|
||||
};
|
||||
|
||||
const SpecialReaderPage = (props: SpecialReaderPageProps) => {
|
||||
|
||||
Reference in New Issue
Block a user