Merge pull request #25775 from backstage/techdocs-extensible-empty-state

techdocs: add support for overriding the empty state in the entity page tab
This commit is contained in:
MT Lewis
2024-09-16 15:00:11 +01:00
committed by GitHub
7 changed files with 111 additions and 24 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Add `empty-state:techdocs/entity-content` extension to allow overriding the empty state for the entity page techdocs tab.
+39 -4
View File
@@ -164,9 +164,6 @@ const _default: FrontendPlugin<
inputs: {};
}>;
'entity-content:techdocs': ExtensionDefinition<{
kind: 'entity-content';
namespace: undefined;
name: undefined;
config: {
path: string | undefined;
title: string | undefined;
@@ -210,7 +207,45 @@ const _default: FrontendPlugin<
optional: true;
}
>;
inputs: {};
inputs: {
emptyState: ExtensionInput<
ConfigurableExtensionDataRef<
React_2.JSX.Element,
'core.reactElement',
{}
>,
{
singleton: true;
optional: true;
}
>;
};
kind: 'entity-content';
namespace: undefined;
name: undefined;
}>;
'empty-state:techdocs/entity-content': ExtensionDefinition<{
config: {};
configInput: {};
output: ConfigurableExtensionDataRef<
React_2.JSX.Element,
'core.reactElement',
{
optional: true;
}
>;
inputs: {
[x: string]: ExtensionInput<
AnyExtensionDataRef,
{
optional: boolean;
singleton: boolean;
}
>;
};
kind: 'empty-state';
namespace: undefined;
name: 'entity-content';
}>;
}
>;
+2 -2
View File
@@ -134,7 +134,7 @@ export type DocsTableRow = {
// @public
export const EmbeddedDocsRouter: (
props: PropsWithChildren<{}>,
) => React_2.JSX.Element | null;
) => React_2.JSX.Element;
// @public
export const EntityListDocsGrid: (
@@ -192,7 +192,7 @@ export type EntityListDocsTableProps = {
// @public
export const EntityTechdocsContent: (props: {
children?: ReactNode;
}) => JSX_2.Element | null;
}) => JSX_2.Element;
// @public
export const isTechDocsAvailable: (entity: Entity) => boolean;
+20 -8
View File
@@ -56,13 +56,10 @@ export const Router = () => {
);
};
/**
* Responsible for registering route to view docs on Entity page
*
* @public
*/
export const EmbeddedDocsRouter = (props: PropsWithChildren<{}>) => {
const { children } = props;
export const EmbeddedDocsRouter = (
props: PropsWithChildren<{ emptyState?: React.ReactElement }>,
) => {
const { children, emptyState } = props;
const { entity } = useEntity();
// Using objects instead of <Route> elements, otherwise "outlet" will be null on sub-pages and add-ons won't render
@@ -84,8 +81,23 @@ export const EmbeddedDocsRouter = (props: PropsWithChildren<{}>) => {
entity.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION];
if (!projectId) {
return <MissingAnnotationEmptyState annotation={[TECHDOCS_ANNOTATION]} />;
return (
emptyState ?? (
<MissingAnnotationEmptyState annotation={[TECHDOCS_ANNOTATION]} />
)
);
}
return element;
};
/**
* Responsible for registering route to view docs on Entity page
*
* @public
*/
export const LegacyEmbeddedDocsRouter = (props: PropsWithChildren<{}>) => {
// Wrap the Router to avoid exposing the emptyState prop in the non-alpha
// public API and make it easier for us to change later.
return <EmbeddedDocsRouter children={props.children} />;
};
+39 -8
View File
@@ -21,8 +21,10 @@ import {
ApiBlueprint,
PageBlueprint,
NavItemBlueprint,
createExtensionInput,
coreExtensionData,
createExtension,
} from '@backstage/frontend-plugin-api';
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
import {
configApiRef,
createApiFactory,
@@ -34,6 +36,8 @@ import {
convertLegacyRouteRef,
convertLegacyRouteRefs,
} from '@backstage/core-compat-api';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
import { SearchResultListItemBlueprint } from '@backstage/plugin-search-react/alpha';
import {
techdocsApiRef,
techdocsStorageApiRef,
@@ -44,7 +48,6 @@ import {
rootDocsRouteRef,
rootRouteRef,
} from './routes';
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
/** @alpha */
const techDocsStorageApi = ApiBlueprint.make({
@@ -152,13 +155,40 @@ const techDocsReaderPage = PageBlueprint.make({
*
* @alpha
*/
const techDocsEntityContent = EntityContentBlueprint.make({
params: {
defaultPath: 'docs',
defaultTitle: 'TechDocs',
loader: () =>
import('./Router').then(m => compatWrapper(<m.EmbeddedDocsRouter />)),
const techDocsEntityContent = EntityContentBlueprint.makeWithOverrides({
inputs: {
emptyState: createExtensionInput([coreExtensionData.reactElement], {
singleton: true,
optional: true,
}),
},
factory(originalFactory, context) {
return originalFactory(
{
defaultPath: 'docs',
defaultTitle: 'TechDocs',
loader: () =>
import('./Router').then(({ EmbeddedDocsRouter }) =>
compatWrapper(
<EmbeddedDocsRouter
emptyState={context.inputs.emptyState?.get(
coreExtensionData.reactElement,
)}
/>,
),
),
},
context,
);
},
});
const techDocsEntityContentEmptyState = createExtension({
kind: 'empty-state',
name: 'entity-content',
attachTo: { id: 'entity-content:techdocs', input: 'emptyState' },
output: [coreExtensionData.reactElement.optional()],
factory: () => [],
});
/** @alpha */
@@ -180,6 +210,7 @@ export default createFrontendPlugin({
techDocsPage,
techDocsReaderPage,
techDocsEntityContent,
techDocsEntityContentEmptyState,
techDocsSearchResultListItemExtension,
],
routes: convertLegacyRouteRefs({
+5 -1
View File
@@ -41,7 +41,11 @@ export {
techdocsPlugin as plugin,
techdocsPlugin,
} from './plugin';
export * from './Router';
export {
isTechDocsAvailable,
LegacyEmbeddedDocsRouter as EmbeddedDocsRouter,
Router,
} from './Router';
export type { TechDocsSearchResultListItemProps } from './search/components/TechDocsSearchResultListItem';
+1 -1
View File
@@ -103,7 +103,7 @@ export const TechdocsPage = techdocsPlugin.provide(
export const EntityTechdocsContent = techdocsPlugin.provide(
createRoutableExtension({
name: 'EntityTechdocsContent',
component: () => import('./Router').then(m => m.EmbeddedDocsRouter),
component: () => import('./Router').then(m => m.LegacyEmbeddedDocsRouter),
mountPoint: rootCatalogDocsRouteRef,
}),
);