From 431ecaf1d527bb1c113aa3a968a42f346c263f3a Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 25 Oct 2022 14:50:18 +0200 Subject: [PATCH 1/5] this seems to solve the issue. actually get the nested element instead of the parent of that element Signed-off-by: Morgan --- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index 1c276b87a1..4cf2955f25 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { ReactNode, Children, ReactElement } from 'react'; +import React, { ReactNode, Children, ReactElement, ReactChild } from 'react'; import { useOutlet } from 'react-router-dom'; import { Page } from '@backstage/core-components'; @@ -35,6 +35,14 @@ import { useRouteRefParams, } from '@backstage/core-plugin-api'; +type Extension = ReactChild & { + type: { + __backstage_data: { + map: Map; + }; + }; +}; + /** * Props for {@link TechDocsReaderLayout} * @public @@ -88,19 +96,27 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { if (!children) { const childrenList = outlet ? Children.toArray(outlet.props.children) : []; - const page = childrenList.find(child => { + let page: React.ReactNode; + childrenList.forEach(child => { + // console.log({child: JSON.stringify(child)}) + // const { type } = child as Extension; + // console.log(!type?.__backstage_data?.map?.get(TECHDOCS_ADDONS_WRAPPER_KEY)); + if (getComponentData(child, TECHDOCS_ADDONS_WRAPPER_KEY)) { - return false; + return; } // react-router 6 stable wraps children in a routing context provider, so check one level deeper const nestedChildren = (child as ReactElement)?.props?.children; if (nestedChildren) { - return !Children.toArray(nestedChildren).some(nested => - getComponentData(nested, TECHDOCS_ADDONS_WRAPPER_KEY), - ); + Children.toArray(nestedChildren).forEach(nested => { + if (!getComponentData(nested, TECHDOCS_ADDONS_WRAPPER_KEY)) { + page = nested; + } + }); + return; } - return true; + page = child; }); return ( From 1a22b5f1b13aa4547b64bfe2f5aebebd892138eb Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 25 Oct 2022 16:19:21 +0200 Subject: [PATCH 2/5] rewrite loop code Signed-off-by: Morgan --- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index 4cf2955f25..4a6bfef89f 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -85,6 +85,7 @@ export type TechDocsReaderPageProps = { /** * An addon-aware implementation of the TechDocsReaderPage. + * * @public */ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { @@ -96,28 +97,12 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { if (!children) { const childrenList = outlet ? Children.toArray(outlet.props.children) : []; - let page: React.ReactNode; - childrenList.forEach(child => { - // console.log({child: JSON.stringify(child)}) - // const { type } = child as Extension; - // console.log(!type?.__backstage_data?.map?.get(TECHDOCS_ADDONS_WRAPPER_KEY)); - - if (getComponentData(child, TECHDOCS_ADDONS_WRAPPER_KEY)) { - return; - } - - // react-router 6 stable wraps children in a routing context provider, so check one level deeper - const nestedChildren = (child as ReactElement)?.props?.children; - if (nestedChildren) { - Children.toArray(nestedChildren).forEach(nested => { - if (!getComponentData(nested, TECHDOCS_ADDONS_WRAPPER_KEY)) { - page = nested; - } - }); - return; - } - page = child; - }); + const grandChildren = childrenList.flatMap( + child => (child as ReactElement)?.props?.children ?? [], + ); + const page: React.ReactNode = grandChildren.find( + grandChild => !getComponentData(grandChild, TECHDOCS_ADDONS_WRAPPER_KEY), + ); return ( From 5878dcd12038835a1bfb1863b09ffee09ba0a3e4 Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 25 Oct 2022 16:39:57 +0200 Subject: [PATCH 3/5] remove unused import Signed-off-by: Morgan --- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index 4a6bfef89f..e307564f9c 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import React, { ReactNode, Children, ReactElement, ReactChild } from 'react'; +import React, { ReactNode, Children, ReactElement } from 'react'; import { useOutlet } from 'react-router-dom'; import { Page } from '@backstage/core-components'; @@ -35,14 +35,6 @@ import { useRouteRefParams, } from '@backstage/core-plugin-api'; -type Extension = ReactChild & { - type: { - __backstage_data: { - map: Map; - }; - }; -}; - /** * Props for {@link TechDocsReaderLayout} * @public From 9e4d8e619898e64b9ddfaf2460d5fe2bb02f6edc Mon Sep 17 00:00:00 2001 From: Morgan Date: Tue, 25 Oct 2022 16:40:33 +0200 Subject: [PATCH 4/5] add changeset Signed-off-by: Morgan --- .changeset/beige-gorillas-sip.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/beige-gorillas-sip.md diff --git a/.changeset/beige-gorillas-sip.md b/.changeset/beige-gorillas-sip.md new file mode 100644 index 0000000000..b0dcaadfc6 --- /dev/null +++ b/.changeset/beige-gorillas-sip.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fix logic bug that broke techdocs-cli-embedded-app From cc8701d2fb7e3e996fa065576ffcc5af29c43b29 Mon Sep 17 00:00:00 2001 From: Otto Sichert Date: Tue, 25 Oct 2022 17:23:06 +0200 Subject: [PATCH 5/5] Add documentation for configuring TechDocsReaderPage Signed-off-by: Otto Sichert --- .../TechDocsReaderPage/TechDocsReaderPage.tsx | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx index e307564f9c..0e9b244d74 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPage/TechDocsReaderPage.tsx @@ -35,6 +35,82 @@ import { useRouteRefParams, } from '@backstage/core-plugin-api'; +/* An explanation for the multiple ways of customizing the TechDocs reader page + +Please refer to this page on the microsite for the latest recommended approach: +https://backstage.io/docs/features/techdocs/how-to-guides#how-to-customize-the-techdocs-reader-page + +The component is responsible for rendering the and +its contained version of a , which in turn renders the . + +Historically, there have been different approaches on how this can be customized, and how the + inside could be exchanged for a custom implementation (which was not +possible before). Also, the current implementation supports every scenario to avoid breaking default +configurations of TechDocs. + +In particular, there are 4 different TechDocs page configurations: + +CONFIGURATION 1: only, no children + +} > + +This is the simplest way to use TechDocs. Only a full page is passed, assuming that it comes with +its content inside. Since we allowed customizing it, we started providing as +a default implementation (which contains ). + +CONFIGURATION 2 (not advised): with element children + + + {techdocsPage} + + } +/> + +Previously, there were two ways of passing children to : either as elements (as +shown above), or as a render function (described below in CONFIGURATION 3). The "techdocsPage" is +located in packages/app/src/components/techdocs and is the default implementation of the content +inside. + +CONFIGURATION 3 (not advised): with render function as child + + + {({ metadata, entityMetadata, onReady }) => ( + techdocsPage + )} + + } +/> + +Similar to CONFIGURATION 2, the direct children will be passed to the but in +this case interpreted as render prop. + +CONFIGURATION 4: and provided content in + +} +> + {techDocsPage} + + + + + + + +This is the current state in packages/app/src/App.tsx and moved the location of children from inside +the element prop in the to the children of the . Then, in they +are retrieved using the useOutlet hook from React Router. + +NOTE: Render functions are no longer supported in this approach. +*/ + /** * Props for {@link TechDocsReaderLayout} * @public @@ -96,6 +172,7 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { grandChild => !getComponentData(grandChild, TECHDOCS_ADDONS_WRAPPER_KEY), ); + // As explained above, "page" is configuration 4 and is 1 return ( {(page as JSX.Element) || } @@ -103,6 +180,7 @@ export const TechDocsReaderPage = (props: TechDocsReaderPageProps) => { ); } + // As explained above, a render function is configuration 3 and React element is 2 return ( {({ metadata, entityMetadata, onReady }) => (