Adjustments after review
Signed-off-by: Andreas Berger <andreas@berger-ecommerce.com>
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': minor
|
||||
'@backstage/plugin-kubernetes': minor
|
||||
'@backstage/plugin-api-docs': minor
|
||||
'@backstage/plugin-techdocs': minor
|
||||
'@backstage/plugin-catalog': minor
|
||||
---
|
||||
|
||||
|
||||
@@ -725,7 +725,7 @@ app:
|
||||
|
||||
Notes:
|
||||
|
||||
- Icons for groups and tabs are resolved via the app's IconsApi. When using a string icon id (for example `"dashboard"`), ensure that the corresponding icon bundles are enabled/installed in your app (see the Icons blueprint in the frontend system documentation).
|
||||
- Icons for groups and tabs are resolved via the app's IconsApi. When using a string icon id (for example `"dashboard"`), ensure that the corresponding icon bundles are enabled/installed in your app (see the [IconBundleBlueprint documentation](../../reference/frontend-plugin-api.iconbundleblueprint.md)).
|
||||
- Group icons are only rendered if `showIcons` is set to `true`.
|
||||
|
||||
### Overriding or disabling a tab's group (per extension)
|
||||
@@ -741,7 +741,7 @@ app:
|
||||
config:
|
||||
# Move this tab to a custom group you defined above
|
||||
group: custom
|
||||
# Show an icon for this entity content page but only if
|
||||
# Show an icon for this entity content page but only if `showIcons` is enabled for the `page:catalog/entity` extension
|
||||
icon: my-icon
|
||||
|
||||
# Disassociate from any group and show as a standalone tab
|
||||
|
||||
@@ -73,7 +73,7 @@ Avoid using `convertLegacyEntityCardExtension` from `@backstage/core-compat-api`
|
||||
|
||||
Creates entity content to be displayed on the entity pages of the catalog plugin. Exported as `EntityContentBlueprint`.
|
||||
|
||||
Supports optional params such as `group` and `icon`to:
|
||||
Supports optional params such as `group` and `icon` to:
|
||||
|
||||
- group: string | false — associates the content with a tab group on the entity page (for example "overview", "quality", "deployment", or any custom id). You can override or disable this per-installation via app-config using `app.extensions[...].config.group`, where `false` removes the grouping.
|
||||
- icon: string — sets the tab icon. Note: when providing a string, the icon is looked up via the app's IconsApi; make sure icon bundles are enabled/installed in your app (see the Icons blueprint reference above) so that the icon id you use is available.
|
||||
|
||||
@@ -47,7 +47,7 @@ import { pluginInfoResolver } from './pluginInfoResolver';
|
||||
import { appModuleNav } from './modules/appModuleNav';
|
||||
import devtoolsPlugin from '@backstage/plugin-devtools/alpha';
|
||||
import { unprocessedEntitiesDevToolsContent } from '@backstage/plugin-catalog-unprocessed-entities/alpha';
|
||||
import { default as catalogPlugin } from '@backstage/plugin-catalog/alpha';
|
||||
import catalogPlugin from '@backstage/plugin-catalog/alpha';
|
||||
import InfoIcon from '@material-ui/icons/Info';
|
||||
|
||||
/*
|
||||
|
||||
@@ -152,15 +152,33 @@ export function convertLegacyEntityContentExtension(
|
||||
): ExtensionDefinition;
|
||||
|
||||
// @alpha
|
||||
export const defaultEntityContentGroups: {
|
||||
overview: string;
|
||||
documentation: string;
|
||||
development: string;
|
||||
deployment: string;
|
||||
operation: string;
|
||||
observability: string;
|
||||
export const defaultEntityContentGroupDefinitions: {
|
||||
overview: {
|
||||
title: string;
|
||||
};
|
||||
documentation: {
|
||||
title: string;
|
||||
};
|
||||
development: {
|
||||
title: string;
|
||||
};
|
||||
deployment: {
|
||||
title: string;
|
||||
};
|
||||
operation: {
|
||||
title: string;
|
||||
};
|
||||
observability: {
|
||||
title: string;
|
||||
};
|
||||
};
|
||||
|
||||
// @alpha @deprecated
|
||||
export const defaultEntityContentGroups: Record<
|
||||
keyof typeof defaultEntityContentGroupDefinitions,
|
||||
string
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export const EntityCardBlueprint: ExtensionBlueprint<{
|
||||
kind: 'entity-card';
|
||||
@@ -604,6 +622,15 @@ export type EntityTableColumnTitleProps = {
|
||||
| 'domain';
|
||||
};
|
||||
|
||||
// @alpha (undocumented)
|
||||
export type GroupDefinitions = Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
}
|
||||
>;
|
||||
|
||||
// @alpha
|
||||
export function isOwnerOf(owner: Entity, entity: Entity): boolean;
|
||||
|
||||
|
||||
@@ -41,18 +41,50 @@ export const entityFilterExpressionDataRef =
|
||||
id: 'catalog.entity-filter-expression',
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export type GroupDefinitions = Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
}
|
||||
>;
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Default entity content groups.
|
||||
*/
|
||||
export const defaultEntityContentGroups = {
|
||||
overview: 'Overview',
|
||||
documentation: 'Documentation',
|
||||
development: 'Development',
|
||||
deployment: 'Deployment',
|
||||
operation: 'Operation',
|
||||
observability: 'Observability',
|
||||
};
|
||||
export const defaultEntityContentGroupDefinitions = {
|
||||
overview: {
|
||||
title: 'Overview',
|
||||
},
|
||||
documentation: {
|
||||
title: 'Documentation',
|
||||
},
|
||||
development: {
|
||||
title: 'Development',
|
||||
},
|
||||
deployment: {
|
||||
title: 'Deployment',
|
||||
},
|
||||
operation: {
|
||||
title: 'Operation',
|
||||
},
|
||||
observability: {
|
||||
title: 'Observability',
|
||||
},
|
||||
} satisfies GroupDefinitions;
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Default entity content groups.
|
||||
* @deprecated use defaultEntityContentGroupDefinitions
|
||||
*/
|
||||
export const defaultEntityContentGroups = Object.fromEntries(
|
||||
Object.entries(defaultEntityContentGroupDefinitions).map(
|
||||
([key, { title }]) => [key, title],
|
||||
),
|
||||
) as Record<keyof typeof defaultEntityContentGroupDefinitions, string>;
|
||||
|
||||
/** @internal */
|
||||
export const entityContentGroupDataRef = createExtensionDataRef<string>().with({
|
||||
|
||||
@@ -21,7 +21,11 @@ export {
|
||||
type EntityContentLayoutProps,
|
||||
} from './EntityContentLayoutBlueprint';
|
||||
export { EntityHeaderBlueprint } from './EntityHeaderBlueprint';
|
||||
export { defaultEntityContentGroups } from './extensionData';
|
||||
export {
|
||||
defaultEntityContentGroups,
|
||||
defaultEntityContentGroupDefinitions,
|
||||
type GroupDefinitions,
|
||||
} from './extensionData';
|
||||
export type { EntityCardType } from './extensionData';
|
||||
export {
|
||||
EntityContextMenuItemBlueprint,
|
||||
|
||||
@@ -40,11 +40,12 @@ import {
|
||||
import { catalogTranslationRef } from '../../translation';
|
||||
import { EntityHeader } from '../EntityHeader';
|
||||
import { EntityTabs } from '../EntityTabs';
|
||||
import { GroupDefinitions } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
export type EntityLayoutRouteProps = {
|
||||
path: string;
|
||||
title: string;
|
||||
group: string | { title: string; icon?: string };
|
||||
group?: string;
|
||||
icon?: string | ReactElement;
|
||||
children: JSX.Element;
|
||||
if?: (entity: Entity) => boolean;
|
||||
@@ -78,6 +79,8 @@ export interface EntityLayoutProps {
|
||||
* It adds breadcrumbs in the Entity page to enhance user navigation and context awareness.
|
||||
*/
|
||||
parentEntityRelations?: string[];
|
||||
groupDefinitions: GroupDefinitions;
|
||||
showIcons?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,6 +109,8 @@ export const EntityLayout = (props: EntityLayoutProps) => {
|
||||
header,
|
||||
NotFoundComponent,
|
||||
parentEntityRelations,
|
||||
groupDefinitions,
|
||||
showIcons,
|
||||
} = props;
|
||||
const { kind } = useRouteRefParams(entityRouteRef);
|
||||
const { entity, loading, error } = useAsyncEntity();
|
||||
@@ -155,7 +160,13 @@ export const EntityLayout = (props: EntityLayoutProps) => {
|
||||
|
||||
{loading && <Progress />}
|
||||
|
||||
{entity && <EntityTabs routes={routes} />}
|
||||
{entity && (
|
||||
<EntityTabs
|
||||
routes={routes}
|
||||
groupDefinitions={groupDefinitions}
|
||||
showIcons={showIcons}
|
||||
/>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Content>
|
||||
|
||||
@@ -18,9 +18,10 @@ import { Helmet } from 'react-helmet';
|
||||
import { matchRoutes, useParams, useRoutes, Outlet } from 'react-router-dom';
|
||||
import { EntityTabsPanel } from './EntityTabsPanel';
|
||||
import { EntityTabsList } from './EntityTabsList';
|
||||
import { GroupDefinitions } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
type SubRoute = {
|
||||
group: string | { title: string; icon?: string };
|
||||
group?: string;
|
||||
path: string;
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
@@ -78,10 +79,12 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): {
|
||||
|
||||
type EntityTabsProps = {
|
||||
routes: SubRoute[];
|
||||
groupDefinitions: GroupDefinitions;
|
||||
showIcons?: boolean;
|
||||
};
|
||||
|
||||
export function EntityTabs(props: EntityTabsProps) {
|
||||
const { routes } = props;
|
||||
const { routes, groupDefinitions, showIcons } = props;
|
||||
|
||||
const { index, route, element } = useSelectedSubRoute(routes);
|
||||
|
||||
@@ -95,7 +98,7 @@ export function EntityTabs(props: EntityTabsProps) {
|
||||
// And remove leading / for relative navigation
|
||||
to = to.replace(/^\//, '');
|
||||
return {
|
||||
group: typeof group === 'string' ? { title: group } : group,
|
||||
group,
|
||||
id: path,
|
||||
path: to,
|
||||
label: title,
|
||||
@@ -107,7 +110,12 @@ export function EntityTabs(props: EntityTabsProps) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<EntityTabsList tabs={tabs} selectedIndex={index} />
|
||||
<EntityTabsList
|
||||
tabs={tabs}
|
||||
selectedIndex={index}
|
||||
showIcons={showIcons}
|
||||
groupDefinitions={groupDefinitions}
|
||||
/>
|
||||
<EntityTabsPanel>
|
||||
<Helmet title={route?.title} />
|
||||
{element}
|
||||
|
||||
@@ -142,7 +142,6 @@ const styles = (theme: Theme) =>
|
||||
|
||||
type EntityTabsGroupItem = {
|
||||
id: string;
|
||||
index: number;
|
||||
label: string;
|
||||
path: string;
|
||||
icon?: string | ReactElement;
|
||||
@@ -151,15 +150,20 @@ type EntityTabsGroupItem = {
|
||||
type EntityTabsGroupProps = TabProps & {
|
||||
classes?: Partial<ReturnType<typeof styles>>;
|
||||
indicator?: ReactNode;
|
||||
highlightedButton?: number;
|
||||
highlightedButton?: string;
|
||||
items: EntityTabsGroupItem[];
|
||||
onSelectTab: MouseEventHandler<HTMLAnchorElement>;
|
||||
onSelectTab?: MouseEventHandler<HTMLAnchorElement>;
|
||||
showIcons?: boolean;
|
||||
};
|
||||
|
||||
function resolveIcon(
|
||||
icon: string | ReactElement | undefined,
|
||||
iconsApi: IconsApi,
|
||||
showIcons: boolean,
|
||||
) {
|
||||
if (!showIcons) {
|
||||
return undefined;
|
||||
}
|
||||
const itemIcon = icon;
|
||||
if (typeof itemIcon === 'string') {
|
||||
const Icon = iconsApi.getIcon(itemIcon);
|
||||
@@ -191,9 +195,10 @@ const Tab = forwardRef(function Tab(props: EntityTabsGroupProps, ref: any) {
|
||||
textColor = 'inherit',
|
||||
wrapped = false,
|
||||
highlightedButton,
|
||||
showIcons = false,
|
||||
} = props;
|
||||
|
||||
const groupIcon = resolveIcon(props.icon, iconsApi);
|
||||
const groupIcon = resolveIcon(props.icon, iconsApi, showIcons);
|
||||
const testId = 'data-testid' in props && props['data-testid'];
|
||||
|
||||
const handleMenuClose = () => {
|
||||
@@ -235,7 +240,7 @@ const Tab = forwardRef(function Tab(props: EntityTabsGroupProps, ref: any) {
|
||||
component={Link}
|
||||
onClick={onSelectTab}
|
||||
to={items[0]?.path}
|
||||
startIcon={resolveIcon(items[0].icon, iconsApi) ?? groupIcon}
|
||||
startIcon={resolveIcon(items[0].icon, iconsApi, showIcons)}
|
||||
>
|
||||
<Typography className={classes?.wrapper} variant="button">
|
||||
{items[0].label}
|
||||
@@ -244,7 +249,7 @@ const Tab = forwardRef(function Tab(props: EntityTabsGroupProps, ref: any) {
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
const hasIcons = items.some(i => i.icon);
|
||||
const hasIcons = showIcons && items.some(i => i.icon);
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@@ -278,11 +283,11 @@ const Tab = forwardRef(function Tab(props: EntityTabsGroupProps, ref: any) {
|
||||
}}
|
||||
>
|
||||
<List component="nav">
|
||||
{items.map((i, idx) => {
|
||||
const itemIcon = resolveIcon(i.icon, iconsApi);
|
||||
{items.map(i => {
|
||||
const itemIcon = resolveIcon(i.icon, iconsApi, showIcons);
|
||||
return (
|
||||
<ListItem
|
||||
key={`popover_item_${idx}`}
|
||||
key={`popover_item_${i.id}`}
|
||||
button
|
||||
focusRipple={!disableFocusRipple}
|
||||
classes={{
|
||||
@@ -293,11 +298,11 @@ const Tab = forwardRef(function Tab(props: EntityTabsGroupProps, ref: any) {
|
||||
ref={ref}
|
||||
aria-selected={selected}
|
||||
disabled={disabled}
|
||||
selected={highlightedButton === i.index}
|
||||
selected={highlightedButton === i.id}
|
||||
component={Link}
|
||||
onClick={e => {
|
||||
handleMenuClose();
|
||||
onSelectTab(e);
|
||||
onSelectTab?.(e);
|
||||
}}
|
||||
to={i.path}
|
||||
>
|
||||
|
||||
@@ -14,11 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { ReactElement, useMemo } from 'react';
|
||||
import Box from '@material-ui/core/Box';
|
||||
import Tabs from '@material-ui/core/Tabs';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import { EntityTabsGroup } from './EntityTabsGroup';
|
||||
import { GroupDefinitions } from '@backstage/plugin-catalog-react/alpha';
|
||||
|
||||
/** @public */
|
||||
export type HeaderTabsClassKey =
|
||||
@@ -59,60 +60,46 @@ type Tab = {
|
||||
id: string;
|
||||
label: string;
|
||||
path: string;
|
||||
group: { title: string; icon?: string };
|
||||
group?: string;
|
||||
icon?: string | ReactElement;
|
||||
};
|
||||
|
||||
type TabItem = Tab & {
|
||||
index: number;
|
||||
type TabGroup = {
|
||||
group?: {
|
||||
title: string;
|
||||
icon?: string | ReactElement;
|
||||
};
|
||||
items: Array<Omit<Tab, 'group'>>;
|
||||
};
|
||||
|
||||
type EntityTabsListProps = {
|
||||
tabs: Tab[];
|
||||
groupDefinitions: GroupDefinitions;
|
||||
showIcons?: boolean;
|
||||
selectedIndex?: number;
|
||||
onChange?: (index: number) => void;
|
||||
};
|
||||
|
||||
export function EntityTabsList(props: EntityTabsListProps) {
|
||||
const styles = useStyles();
|
||||
|
||||
const { tabs: items, onChange, selectedIndex: selectedItem = 0 } = props;
|
||||
const { tabs: items, selectedIndex = 0, showIcons, groupDefinitions } = props;
|
||||
|
||||
const groups = useMemo(
|
||||
() =>
|
||||
Object.values(
|
||||
items.reduce((result, i) => {
|
||||
result[i.group.title] = i.group;
|
||||
return result;
|
||||
}, {} as Record<string, Tab['group']>),
|
||||
),
|
||||
[items],
|
||||
items.reduce((result, tab) => {
|
||||
const group = tab.group ? groupDefinitions[tab.group] : undefined;
|
||||
const groupOrId = group && tab.group ? tab.group : tab.id;
|
||||
result[groupOrId] = result[groupOrId] ?? {
|
||||
group,
|
||||
items: [],
|
||||
};
|
||||
result[groupOrId].items.push(tab);
|
||||
return result;
|
||||
}, {} as Record<string, TabGroup>),
|
||||
[items, groupDefinitions],
|
||||
);
|
||||
|
||||
const [selectedGroup, setSelectedGroup] = useState<number>(
|
||||
selectedItem && items[selectedItem]
|
||||
? groups.findIndex(
|
||||
({ title }) => title === items[selectedItem].group.title,
|
||||
)
|
||||
: 0,
|
||||
);
|
||||
|
||||
const handleChange = useCallback(
|
||||
(index: number) => {
|
||||
if (selectedItem !== index) onChange?.(index);
|
||||
},
|
||||
[selectedItem, onChange],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedItem === undefined || !items[selectedItem]) return;
|
||||
setSelectedGroup(
|
||||
groups.findIndex(
|
||||
({ title }) => title === items[selectedItem].group.title,
|
||||
),
|
||||
);
|
||||
}, [items, selectedItem, groups, setSelectedGroup]);
|
||||
|
||||
const selectedItem = items[selectedIndex];
|
||||
return (
|
||||
<Box className={styles.tabsWrapper}>
|
||||
<Tabs
|
||||
@@ -122,33 +109,22 @@ export function EntityTabsList(props: EntityTabsListProps) {
|
||||
variant="scrollable"
|
||||
scrollButtons="auto"
|
||||
aria-label="tabs"
|
||||
value={selectedGroup}
|
||||
value={selectedItem?.group ?? selectedItem?.id}
|
||||
>
|
||||
{groups.map((group, groupIndex) => {
|
||||
const groupItems: TabItem[] = [];
|
||||
items.forEach((item, itemIndex) => {
|
||||
if (item.group.title === group.title) {
|
||||
groupItems.push({
|
||||
...item,
|
||||
index: itemIndex,
|
||||
});
|
||||
}
|
||||
});
|
||||
return (
|
||||
<EntityTabsGroup
|
||||
data-testid={`header-tab-${groupIndex}`}
|
||||
className={styles.defaultTab}
|
||||
classes={{ selected: styles.selected, root: styles.tabRoot }}
|
||||
key={group.title}
|
||||
label={group.title}
|
||||
icon={group.icon}
|
||||
value={groupIndex}
|
||||
items={groupItems}
|
||||
highlightedButton={selectedItem}
|
||||
onSelectTab={() => handleChange(groupIndex)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{Object.entries(groups).map(([id, tabGroup]) => (
|
||||
<EntityTabsGroup
|
||||
data-testid={`header-tab-${id}`}
|
||||
className={styles.defaultTab}
|
||||
classes={{ selected: styles.selected, root: styles.tabRoot }}
|
||||
key={id}
|
||||
label={tabGroup.group?.title}
|
||||
icon={tabGroup.group?.icon}
|
||||
value={id}
|
||||
items={tabGroup.items}
|
||||
highlightedButton={selectedItem?.id}
|
||||
showIcons={showIcons}
|
||||
/>
|
||||
))}
|
||||
</Tabs>
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -25,10 +25,11 @@ import {
|
||||
entityRouteRef,
|
||||
} from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
EntityHeaderBlueprint,
|
||||
defaultEntityContentGroupDefinitions,
|
||||
EntityContentBlueprint,
|
||||
defaultEntityContentGroups,
|
||||
EntityContextMenuItemBlueprint,
|
||||
EntityHeaderBlueprint,
|
||||
GroupDefinitions,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { rootRouteRef } from '../routes';
|
||||
import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl';
|
||||
@@ -132,15 +133,6 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
|
||||
(() => true),
|
||||
}));
|
||||
|
||||
type Groups = Record<
|
||||
string,
|
||||
{
|
||||
title: string;
|
||||
icon?: string;
|
||||
items: Array<(typeof inputs.contents)[0]>;
|
||||
}
|
||||
>;
|
||||
|
||||
// Get available headers, sorted by if they have a filter function or not.
|
||||
// TODO(blam): we should really have priority or some specificity here which can be used to sort the headers.
|
||||
// That can be done with embedding the priority in the dataRef alongside the filter function.
|
||||
@@ -155,43 +147,11 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
|
||||
return 0;
|
||||
});
|
||||
|
||||
let groups = Object.entries(defaultEntityContentGroups).reduce<Groups>(
|
||||
(rest, group) => {
|
||||
const [groupId, groupValue] = group;
|
||||
return {
|
||||
...rest,
|
||||
[groupId]: { title: groupValue, items: [] },
|
||||
};
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
// config groups override default groups
|
||||
if (config.groups) {
|
||||
groups = config.groups.reduce<Groups>((rest, group) => {
|
||||
const [groupId, groupValue] = Object.entries(group)[0];
|
||||
return {
|
||||
...rest,
|
||||
[groupId]: {
|
||||
title: groupValue.title,
|
||||
icon: config.showIcons ? groupValue.icon : undefined,
|
||||
items: [],
|
||||
},
|
||||
};
|
||||
}, {});
|
||||
}
|
||||
|
||||
for (const output of inputs.contents) {
|
||||
const itemId = output.node.spec.id;
|
||||
const itemTitle = output.get(EntityContentBlueprint.dataRefs.title);
|
||||
const itemGroup = output.get(EntityContentBlueprint.dataRefs.group);
|
||||
const group = itemGroup && groups[itemGroup];
|
||||
if (!group) {
|
||||
groups[itemId] = { title: itemTitle, items: [output] };
|
||||
continue;
|
||||
}
|
||||
group.items.push(output);
|
||||
}
|
||||
const groupDefinitions =
|
||||
config.groups?.reduce(
|
||||
(rest, group) => ({ ...rest, ...group }),
|
||||
{} as GroupDefinitions,
|
||||
) ?? defaultEntityContentGroupDefinitions;
|
||||
|
||||
const Component = () => {
|
||||
const entityFromUrl = useEntityFromUrl();
|
||||
@@ -209,32 +169,28 @@ export const catalogEntityPage = PageBlueprint.makeWithOverrides({
|
||||
<EntityLayout
|
||||
header={header}
|
||||
contextMenuItems={filteredMenuItems}
|
||||
groupDefinitions={groupDefinitions}
|
||||
showIcons={config.showIcons}
|
||||
>
|
||||
{Object.values(groups).flatMap(({ title, icon, items }) =>
|
||||
items.map(output => (
|
||||
<EntityLayout.Route
|
||||
group={{ title, icon }}
|
||||
key={output.get(coreExtensionData.routePath)}
|
||||
path={output.get(coreExtensionData.routePath)}
|
||||
title={output.get(EntityContentBlueprint.dataRefs.title)}
|
||||
icon={
|
||||
config.showIcons
|
||||
? output.get(EntityContentBlueprint.dataRefs.icon)
|
||||
: undefined
|
||||
}
|
||||
if={buildFilterFn(
|
||||
output.get(
|
||||
EntityContentBlueprint.dataRefs.filterFunction,
|
||||
),
|
||||
output.get(
|
||||
EntityContentBlueprint.dataRefs.filterExpression,
|
||||
),
|
||||
)}
|
||||
>
|
||||
{output.get(coreExtensionData.reactElement)}
|
||||
</EntityLayout.Route>
|
||||
)),
|
||||
)}
|
||||
{inputs.contents.map(output => (
|
||||
<EntityLayout.Route
|
||||
group={output.get(EntityContentBlueprint.dataRefs.group)}
|
||||
key={output.get(coreExtensionData.routePath)}
|
||||
path={output.get(coreExtensionData.routePath)}
|
||||
title={output.get(EntityContentBlueprint.dataRefs.title)}
|
||||
icon={output.get(EntityContentBlueprint.dataRefs.icon)}
|
||||
if={buildFilterFn(
|
||||
output.get(
|
||||
EntityContentBlueprint.dataRefs.filterFunction,
|
||||
),
|
||||
output.get(
|
||||
EntityContentBlueprint.dataRefs.filterExpression,
|
||||
),
|
||||
)}
|
||||
>
|
||||
{output.get(coreExtensionData.reactElement)}
|
||||
</EntityLayout.Route>
|
||||
))}
|
||||
</EntityLayout>
|
||||
</AsyncEntityProvider>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user