Merge pull request #29760 from csuich2/techdocs-entity-path
feat: add techdocs-entity-path annotation for techdocs deep linking
This commit is contained in:
@@ -20,6 +20,7 @@ import {
|
||||
parseEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import { TECHDOCS_EXTERNAL_ANNOTATION } from '@backstage/plugin-techdocs-common';
|
||||
import { getEntityRootTechDocsPath } from '@backstage/plugin-techdocs-react';
|
||||
|
||||
import { TechDocsReaderPage } from './plugin';
|
||||
import { TechDocsReaderPageContent } from './reader/components/TechDocsReaderPageContent';
|
||||
@@ -52,12 +53,15 @@ export const EntityPageDocs = ({
|
||||
}
|
||||
}
|
||||
|
||||
const defaultPath = getEntityRootTechDocsPath(entity);
|
||||
|
||||
return (
|
||||
<TechDocsReaderPage entityRef={entityRef}>
|
||||
<TechDocsReaderPageSubheader />
|
||||
<TechDocsReaderPageContent
|
||||
withSearch={withSearch}
|
||||
searchResultUrlMapper={searchResultUrlMapper}
|
||||
defaultPath={defaultPath}
|
||||
/>
|
||||
</TechDocsReaderPage>
|
||||
);
|
||||
|
||||
+31
-1
@@ -16,7 +16,10 @@
|
||||
import { ReactNode } from 'react';
|
||||
import { waitFor } from '@testing-library/react';
|
||||
|
||||
import { CompoundEntityRef } from '@backstage/catalog-model';
|
||||
import {
|
||||
CompoundEntityRef,
|
||||
getCompoundEntityRef,
|
||||
} from '@backstage/catalog-model';
|
||||
import {
|
||||
techdocsApiRef,
|
||||
TechDocsReaderPageProvider,
|
||||
@@ -121,6 +124,33 @@ describe('<TechDocsReaderPageContent />', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should render techdocs page content with default path', async () => {
|
||||
getEntityMetadata.mockResolvedValue(mockEntityMetadata);
|
||||
getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata);
|
||||
useTechDocsReaderDom.mockReturnValue(document.createElement('html'));
|
||||
useReaderState.mockReturnValue({ state: 'cached' });
|
||||
|
||||
const defaultPath = '/some/path';
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<Wrapper>
|
||||
<TechDocsReaderPageContent
|
||||
withSearch={false}
|
||||
defaultPath={defaultPath}
|
||||
/>
|
||||
</Wrapper>,
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
rendered.getByTestId('techdocs-native-shadowroot'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
const entityRef = getCompoundEntityRef(mockEntityMetadata);
|
||||
expect(useTechDocsReaderDom).toHaveBeenCalledWith(entityRef, defaultPath);
|
||||
});
|
||||
|
||||
it('should not render techdocs content if entity metadata is missing', async () => {
|
||||
getEntityMetadata.mockResolvedValue(undefined);
|
||||
useTechDocsReaderDom.mockReturnValue(document.createElement('html'));
|
||||
|
||||
+6
-1
@@ -61,6 +61,11 @@ export type TechDocsReaderPageContentProps = {
|
||||
* @deprecated No need to pass down entityRef as property anymore. Consumes the entityName from `TechDocsReaderPageContext`. Use the {@link @backstage/plugin-techdocs-react#useTechDocsReaderPage} hook for custom reader page content.
|
||||
*/
|
||||
entityRef?: CompoundEntityRef;
|
||||
/**
|
||||
* Path in the docs to render by default. This should be used when rendering docs for an entity that specifies the
|
||||
* "backstage.io/techdocs-entity-path" annotation for deep linking into another entities docs.
|
||||
*/
|
||||
defaultPath?: string;
|
||||
/**
|
||||
* Show or hide the search bar, defaults to true.
|
||||
*/
|
||||
@@ -93,7 +98,7 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider(
|
||||
setShadowRoot,
|
||||
} = useTechDocsReaderPage();
|
||||
const { state } = useTechDocsReader();
|
||||
const dom = useTechDocsReaderDom(entityRef);
|
||||
const dom = useTechDocsReaderDom(entityRef, props.defaultPath);
|
||||
const path = window.location.pathname;
|
||||
const hash = window.location.hash;
|
||||
const isStyleLoading = useShadowDomStylesLoading(dom);
|
||||
|
||||
@@ -14,7 +14,13 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
// useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
|
||||
import useMediaQuery from '@material-ui/core/useMediaQuery';
|
||||
import { useTheme } from '@material-ui/core/styles';
|
||||
@@ -47,10 +53,27 @@ import {
|
||||
handleMetaRedirects,
|
||||
} from '../../transformers';
|
||||
import { useNavigateUrl } from './useNavigateUrl';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useLocation, useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)';
|
||||
|
||||
// If a defaultPath is specified then we should navigate to that path replacing the
|
||||
// current location in the history. This should only happen on the initial load so
|
||||
// navigating to the root of the docs doesn't also redirect.
|
||||
const useInitialRedirect = (defaultPath?: string) => {
|
||||
// const hasRun = useRef(false);
|
||||
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const { '*': currPath = '' } = useParams();
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (currPath === '' && defaultPath !== '') {
|
||||
navigate(`${location.pathname}${defaultPath}`, { replace: true });
|
||||
}
|
||||
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook that encapsulates the behavior of getting raw HTML and applying
|
||||
* transforms to it in order to make it function at a basic level in the
|
||||
@@ -58,6 +81,7 @@ const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)';
|
||||
*/
|
||||
export const useTechDocsReaderDom = (
|
||||
entityRef: CompoundEntityRef,
|
||||
defaultPath?: string,
|
||||
): Element | null => {
|
||||
const navigate = useNavigateUrl();
|
||||
const theme = useTheme();
|
||||
@@ -76,6 +100,8 @@ export const useTechDocsReaderDom = (
|
||||
const [dom, setDom] = useState<HTMLElement | null>(null);
|
||||
const isStyleLoading = useShadowDomStylesLoading(dom);
|
||||
|
||||
useInitialRedirect(defaultPath);
|
||||
|
||||
const updateSidebarPositionAndHeight = useCallback(() => {
|
||||
if (!dom) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user