Merge pull request #5459 from kuangp/feat/entityLayoutRoute

feat(EntityLayout): add 'if' prop to Route component
This commit is contained in:
Fredrik Adelöw
2021-04-29 13:26:13 +02:00
committed by GitHub
4 changed files with 104 additions and 5 deletions
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/core': patch
'@backstage/plugin-catalog': patch
---
Add `if` prop to `EntityLayout.Route` to conditionally render tabs
@@ -14,3 +14,4 @@
* limitations under the License.
*/
export { TabbedLayout } from './TabbedLayout';
export { RoutedTabs } from './RoutedTabs';
@@ -101,4 +101,39 @@ describe('EntityLayout', () => {
expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument();
});
it('should conditionally render tabs', async () => {
const shouldRenderTab = (e: Entity) => e.metadata.name === 'my-entity';
const shouldNotRenderTab = (e: Entity) => e.metadata.name === 'some-entity';
const rendered = await renderInTestApp(
<ApiProvider apis={mockApis}>
<EntityContext.Provider value={mockEntityData}>
<EntityLayout>
<EntityLayout.Route path="/" title="tabbed-test-title">
<div>tabbed-test-content</div>
</EntityLayout.Route>
<EntityLayout.Route
path="/some-other-path"
title="tabbed-test-title-2"
if={shouldNotRenderTab}
>
<div>tabbed-test-content-2</div>
</EntityLayout.Route>
<EntityLayout.Route
path="/some-other-other-path"
title="tabbed-test-title-3"
if={shouldRenderTab}
>
<div>tabbed-test-content-3</div>
</EntityLayout.Route>
</EntityLayout>
</EntityContext.Provider>
</ApiProvider>,
);
expect(rendered.queryByText('tabbed-test-title')).toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title-2')).not.toBeInTheDocument();
expect(rendered.queryByText('tabbed-test-title-3')).toBeInTheDocument();
});
});
@@ -20,13 +20,14 @@ import {
RELATION_OWNED_BY,
} from '@backstage/catalog-model';
import {
attachComponentData,
Content,
Header,
HeaderLabel,
IconComponent,
Page,
Progress,
TabbedLayout,
RoutedTabs,
} from '@backstage/core';
import {
EntityContext,
@@ -34,14 +35,69 @@ import {
getEntityRelations,
useEntityCompoundName,
} from '@backstage/plugin-catalog-react';
import { Box } from '@material-ui/core';
import { Box, TabProps } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
import { default as React, useContext, useState } from 'react';
import {
Children,
default as React,
Fragment,
isValidElement,
useContext,
useState,
} from 'react';
import { useNavigate } from 'react-router';
import { EntityContextMenu } from '../EntityContextMenu/EntityContextMenu';
import { FavouriteEntity } from '../FavouriteEntity/FavouriteEntity';
import { UnregisterEntityDialog } from '../UnregisterEntityDialog/UnregisterEntityDialog';
type SubRoute = {
path: string;
title: string;
children: JSX.Element;
if?: (entity: Entity) => boolean;
tabProps?: TabProps<React.ElementType, { component?: React.ElementType }>;
};
const Route: (props: SubRoute) => null = () => null;
// This causes all mount points that are discovered within this route to use the path of the route itself
attachComponentData(Route, 'core.gatherMountPoints', true);
function createSubRoutesFromChildren(
childrenProps: React.ReactNode,
entity: Entity | undefined,
): SubRoute[] {
// Directly comparing child.type with Route will not work with in
// combination with react-hot-loader in storybook
// https://github.com/gaearon/react-hot-loader/issues/304
const routeType = (
<Route path="" title="">
<div />
</Route>
).type;
return Children.toArray(childrenProps).flatMap(child => {
if (!isValidElement(child)) {
return [];
}
if (child.type === Fragment) {
return createSubRoutesFromChildren(child.props.children, entity);
}
if (child.type !== routeType) {
throw new Error('Child of EntityLayout must be an EntityLayout.Route');
}
const { path, title, children, if: condition, tabProps } = child.props;
if (condition && entity && !condition(entity)) {
return [];
}
return [{ path, title, children, tabProps }];
});
}
const EntityLayoutTitle = ({
entity,
title,
@@ -139,6 +195,7 @@ export const EntityLayout = ({
const { kind, namespace, name } = useEntityCompoundName();
const { entity, loading, error } = useContext(EntityContext);
const routes = createSubRoutesFromChildren(children, entity);
const { headerTitle, headerType } = headerProps(
kind,
namespace,
@@ -175,7 +232,7 @@ export const EntityLayout = ({
{loading && <Progress />}
{entity && <TabbedLayout>{children}</TabbedLayout>}
{entity && <RoutedTabs routes={routes} />}
{error && (
<Content>
@@ -192,4 +249,4 @@ export const EntityLayout = ({
);
};
EntityLayout.Route = TabbedLayout.Route;
EntityLayout.Route = Route;