feat: creat entity content layout extension
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
@@ -26,11 +26,14 @@ app:
|
||||
title: Custom
|
||||
|
||||
# Entity page cards
|
||||
- entity-card:catalog/about
|
||||
- entity-card:catalog/about:
|
||||
config:
|
||||
area: info
|
||||
- entity-card:catalog/labels
|
||||
- entity-card:catalog/links:
|
||||
config:
|
||||
filter: kind:component has:links
|
||||
area: info
|
||||
# - entity-card:linguist/languages
|
||||
- entity-card:catalog-graph/relations:
|
||||
config:
|
||||
@@ -68,6 +71,11 @@ app:
|
||||
# - entity-content:azure-devops/pull-requests
|
||||
# - entity-content:azure-devops/git-tags
|
||||
|
||||
- catalog-overview-entity-content-layout:app/sticky:
|
||||
config:
|
||||
# this layout will apply to entities of kind component
|
||||
filter: 'kind:component'
|
||||
|
||||
# scmAuthExtension: >-
|
||||
# createScmAuthExtension({
|
||||
# id: 'apis.scmAuth.addons.ghe',
|
||||
|
||||
@@ -44,6 +44,7 @@ import kubernetesPlugin from '@backstage/plugin-kubernetes/alpha';
|
||||
import { convertLegacyPlugin } from '@backstage/core-compat-api';
|
||||
import { convertLegacyPageExtension } from '@backstage/core-compat-api';
|
||||
import { convertLegacyEntityContentExtension } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { customEntityContentOverviewLayoutModule } from './EntityPages';
|
||||
|
||||
/*
|
||||
|
||||
@@ -131,6 +132,7 @@ const app = createApp({
|
||||
kubernetesPlugin,
|
||||
notFoundErrorPageModule,
|
||||
customHomePageModule,
|
||||
customEntityContentOverviewLayoutModule,
|
||||
...collectedLegacyPlugins,
|
||||
],
|
||||
/* Handled through config instead */
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useCallback } from 'react';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import { createFrontendModule } from '@backstage/frontend-plugin-api';
|
||||
import {
|
||||
OverviewEntityContentLauyoutBlueprint,
|
||||
OverviewEntityContentLayoutProps,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
[theme.breakpoints.up('sm')]: {
|
||||
infoArea: {
|
||||
order: 1,
|
||||
},
|
||||
card: {
|
||||
alignSelf: 'stretch',
|
||||
'& > *': {
|
||||
height: '100%',
|
||||
minHeight: 400,
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
function StickyEntityContentOverviewLayout(
|
||||
props: OverviewEntityContentLayoutProps,
|
||||
) {
|
||||
const { cards, buildFilterFn } = props;
|
||||
|
||||
const classes = useStyles();
|
||||
|
||||
const { entity } = useEntity();
|
||||
|
||||
const catalogEntityFilter = useCallback(
|
||||
(options: {
|
||||
filterFunction?: (entity: Entity) => boolean;
|
||||
filterExpression?: string;
|
||||
}) => {
|
||||
const { filterFunction, filterExpression } = options;
|
||||
return buildFilterFn(filterFunction, filterExpression)(entity);
|
||||
},
|
||||
[entity, buildFilterFn],
|
||||
);
|
||||
|
||||
return (
|
||||
<Grid container spacing={3}>
|
||||
<Grid
|
||||
className={classes.infoArea}
|
||||
xs={12}
|
||||
md={4}
|
||||
style={{
|
||||
position: 'sticky',
|
||||
top: -16,
|
||||
alignSelf: 'flex-start',
|
||||
}}
|
||||
item
|
||||
>
|
||||
<Grid container spacing={3}>
|
||||
{cards
|
||||
.filter(catalogEntityFilter)
|
||||
.filter(card => card.area === 'info')
|
||||
.map((card, index) => (
|
||||
<Grid key={index} xs={12} item>
|
||||
{card.element}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid xs={12} md={8} item>
|
||||
<Grid container spacing={3}>
|
||||
{cards
|
||||
.filter(catalogEntityFilter)
|
||||
.filter(card => card.area === 'glance')
|
||||
.map((card, index) => (
|
||||
<Grid key={index} className={classes.card} xs={12} md={6} item>
|
||||
{card.element}
|
||||
</Grid>
|
||||
))}
|
||||
{cards
|
||||
.filter(catalogEntityFilter)
|
||||
.filter(card => !card.area || card.area === 'main')
|
||||
.map((card, index) => (
|
||||
<Grid key={index} className={classes.card} xs={12} md={6} item>
|
||||
{card.element}
|
||||
</Grid>
|
||||
))}
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
||||
export const customEntityContentOverviewLayoutModule = createFrontendModule({
|
||||
pluginId: 'app',
|
||||
extensions: [
|
||||
OverviewEntityContentLauyoutBlueprint.make({
|
||||
name: 'sticky',
|
||||
params: {
|
||||
areas: ['info', 'glance', 'main'],
|
||||
defaultArea: 'main',
|
||||
loader: async () => StickyEntityContentOverviewLayout,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -108,9 +108,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-apis';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -131,11 +133,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:api-docs/definition': ExtensionDefinition<{
|
||||
@@ -143,9 +153,11 @@ const _default: FrontendPlugin<
|
||||
name: 'definition';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -166,11 +178,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:api-docs/consumed-apis': ExtensionDefinition<{
|
||||
@@ -178,9 +198,11 @@ const _default: FrontendPlugin<
|
||||
name: 'consumed-apis';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -201,11 +223,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:api-docs/provided-apis': ExtensionDefinition<{
|
||||
@@ -213,9 +243,11 @@ const _default: FrontendPlugin<
|
||||
name: 'provided-apis';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -236,11 +268,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:api-docs/consuming-components': ExtensionDefinition<{
|
||||
@@ -248,9 +288,11 @@ const _default: FrontendPlugin<
|
||||
name: 'consuming-components';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -271,11 +313,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:api-docs/providing-components': ExtensionDefinition<{
|
||||
@@ -283,9 +333,11 @@ const _default: FrontendPlugin<
|
||||
name: 'providing-components';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -306,11 +358,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-content:api-docs/definition': ExtensionDefinition<{
|
||||
|
||||
@@ -43,6 +43,7 @@ const _default: FrontendPlugin<
|
||||
height: number | undefined;
|
||||
} & {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
height?: number | undefined;
|
||||
@@ -58,6 +59,7 @@ const _default: FrontendPlugin<
|
||||
relationPairs?: [string, string][] | undefined;
|
||||
} & {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -78,6 +80,13 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {
|
||||
[x: string]: ExtensionInput<
|
||||
@@ -93,6 +102,7 @@ const _default: FrontendPlugin<
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'page:catalog-graph': ExtensionDefinition<{
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Entity } from '@backstage/catalog-model';
|
||||
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
|
||||
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { default as React_2 } from 'react';
|
||||
import { ResourcePermission } from '@backstage/plugin-permission-common';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { TranslationRef } from '@backstage/core-plugin-api/alpha';
|
||||
@@ -113,6 +114,7 @@ export const EntityCardBlueprint: ExtensionBlueprint<{
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -129,13 +131,22 @@ export const EntityCardBlueprint: ExtensionBlueprint<{
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
dataRefs: {
|
||||
filterFunction: ConfigurableExtensionDataRef<
|
||||
@@ -148,6 +159,7 @@ export const EntityCardBlueprint: ExtensionBlueprint<{
|
||||
'catalog.entity-filter-expression',
|
||||
{}
|
||||
>;
|
||||
area: ConfigurableExtensionDataRef<string, 'catalog.entity-card-area', {}>;
|
||||
};
|
||||
}>;
|
||||
|
||||
@@ -240,6 +252,98 @@ export const EntityContentBlueprint: ExtensionBlueprint<{
|
||||
// @alpha
|
||||
export function isOwnerOf(owner: Entity, entity: Entity): boolean;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export const OverviewEntityContentLauyoutBlueprint: ExtensionBlueprint<{
|
||||
kind: 'catalog-overview-entity-content-layout';
|
||||
name: undefined;
|
||||
params: {
|
||||
areas: Array<string>;
|
||||
defaultArea: string;
|
||||
defaultFilter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
loader: () => Promise<
|
||||
(props: OverviewEntityContentLayoutProps) => React_2.JSX.Element
|
||||
>;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<string, 'catalog.entity-card-area', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string[],
|
||||
'catalog.overview-entity-content.layout.areas',
|
||||
{}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(props: OverviewEntityContentLayoutProps) => React_2.JSX.Element,
|
||||
'catalog.overview-entity-content.layout.page-component',
|
||||
{}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.overview-entity-content.layout.filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.overview-entity-content.layout.filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
config: {
|
||||
area: string | undefined;
|
||||
filter: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
dataRefs: {
|
||||
areas: ConfigurableExtensionDataRef<
|
||||
string[],
|
||||
'catalog.overview-entity-content.layout.areas',
|
||||
{}
|
||||
>;
|
||||
defaultArea: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{}
|
||||
>;
|
||||
filterFunction: ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.overview-entity-content.layout.filter-function',
|
||||
{}
|
||||
>;
|
||||
filterExpression: ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.overview-entity-content.layout.filter-expression',
|
||||
{}
|
||||
>;
|
||||
component: ConfigurableExtensionDataRef<
|
||||
(props: OverviewEntityContentLayoutProps) => React_2.JSX.Element,
|
||||
'catalog.overview-entity-content.layout.page-component',
|
||||
{}
|
||||
>;
|
||||
};
|
||||
}>;
|
||||
|
||||
// @alpha (undocumented)
|
||||
export interface OverviewEntityContentLayoutProps {
|
||||
// (undocumented)
|
||||
buildFilterFn: (
|
||||
filterFunction?: (entity: Entity) => boolean,
|
||||
filterExpression?: string,
|
||||
) => (entity: Entity) => boolean;
|
||||
// (undocumented)
|
||||
cards: Array<{
|
||||
area?: string;
|
||||
element: React_2.JSX.Element;
|
||||
filterFunction?: (entity: Entity) => boolean;
|
||||
filterExpression?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
// @alpha
|
||||
export function useEntityPermission(
|
||||
permission: ResourcePermission<'catalog-entity'>,
|
||||
|
||||
@@ -51,6 +51,9 @@ describe('EntityCardBlueprint', () => {
|
||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"area": {
|
||||
"type": "string",
|
||||
},
|
||||
"filter": {
|
||||
"type": "string",
|
||||
},
|
||||
@@ -83,6 +86,15 @@ describe('EntityCardBlueprint', () => {
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
{
|
||||
"$$type": "@backstage/ExtensionDataRef",
|
||||
"config": {
|
||||
"optional": true,
|
||||
},
|
||||
"id": "catalog.entity-card-area",
|
||||
"optional": [Function],
|
||||
"toString": [Function],
|
||||
},
|
||||
],
|
||||
"override": [Function],
|
||||
"toString": [Function],
|
||||
|
||||
@@ -22,6 +22,8 @@ import {
|
||||
import {
|
||||
entityFilterFunctionDataRef,
|
||||
entityFilterExpressionDataRef,
|
||||
entityCardAreaDataRef,
|
||||
defaultEntityCardArea,
|
||||
} from './extensionData';
|
||||
|
||||
/**
|
||||
@@ -35,25 +37,30 @@ export const EntityCardBlueprint = createExtensionBlueprint({
|
||||
coreExtensionData.reactElement,
|
||||
entityFilterFunctionDataRef.optional(),
|
||||
entityFilterExpressionDataRef.optional(),
|
||||
entityCardAreaDataRef.optional(),
|
||||
],
|
||||
dataRefs: {
|
||||
filterFunction: entityFilterFunctionDataRef,
|
||||
filterExpression: entityFilterExpressionDataRef,
|
||||
area: entityCardAreaDataRef,
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
filter: z => z.string().optional(),
|
||||
area: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
*factory(
|
||||
{
|
||||
loader,
|
||||
filter,
|
||||
defaultArea,
|
||||
}: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?:
|
||||
| typeof entityFilterFunctionDataRef.T
|
||||
| typeof entityFilterExpressionDataRef.T;
|
||||
defaultArea?: (typeof defaultEntityCardArea)[number];
|
||||
},
|
||||
{ node, config },
|
||||
) {
|
||||
@@ -66,5 +73,10 @@ export const EntityCardBlueprint = createExtensionBlueprint({
|
||||
} else if (typeof filter === 'function') {
|
||||
yield entityFilterFunctionDataRef(filter);
|
||||
}
|
||||
|
||||
const area = config.area ?? defaultArea;
|
||||
if (area) {
|
||||
yield entityCardAreaDataRef(area);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright 2025 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import {
|
||||
createExtensionDataRef,
|
||||
createExtensionBlueprint,
|
||||
ExtensionBoundary,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import React, { lazy as reactLazy, ComponentProps } from 'react';
|
||||
import { EntityCardBlueprint } from './EntityCardBlueprint';
|
||||
|
||||
/** @alpha */
|
||||
export interface OverviewEntityContentLayoutProps {
|
||||
buildFilterFn: (
|
||||
filterFunction?: (entity: Entity) => boolean,
|
||||
filterExpression?: string,
|
||||
) => (entity: Entity) => boolean;
|
||||
cards: Array<{
|
||||
area?: string;
|
||||
element: React.JSX.Element;
|
||||
filterFunction?: (entity: Entity) => boolean;
|
||||
filterExpression?: string;
|
||||
}>;
|
||||
}
|
||||
|
||||
const catalogOverviewEntityContentLayoutAreasDataRef = createExtensionDataRef<
|
||||
Array<string>
|
||||
>().with({
|
||||
id: 'catalog.overview-entity-content.layout.areas',
|
||||
});
|
||||
|
||||
const catalogOverviewEntityContentLayoutFilterFunctionDataRef =
|
||||
createExtensionDataRef<(entity: Entity) => boolean>().with({
|
||||
id: 'catalog.overview-entity-content.layout.filter-function',
|
||||
});
|
||||
|
||||
const catalogOverviewEntityContentLayoutFilterExpressionDataRef =
|
||||
createExtensionDataRef<string>().with({
|
||||
id: 'catalog.overview-entity-content.layout.filter-expression',
|
||||
});
|
||||
|
||||
const catalogOverviewEntityContentLayoutComponentDataRef =
|
||||
createExtensionDataRef<
|
||||
(props: OverviewEntityContentLayoutProps) => React.JSX.Element
|
||||
>().with({
|
||||
id: 'catalog.overview-entity-content.layout.page-component',
|
||||
});
|
||||
|
||||
/** @alpha */
|
||||
export const OverviewEntityContentLauyoutBlueprint = createExtensionBlueprint({
|
||||
kind: 'catalog-overview-entity-content-layout',
|
||||
attachTo: { id: 'entity-content:catalog/overview', input: 'layouts' },
|
||||
output: [
|
||||
catalogOverviewEntityContentLayoutAreasDataRef,
|
||||
EntityCardBlueprint.dataRefs.area,
|
||||
catalogOverviewEntityContentLayoutFilterFunctionDataRef.optional(),
|
||||
catalogOverviewEntityContentLayoutFilterExpressionDataRef.optional(),
|
||||
catalogOverviewEntityContentLayoutComponentDataRef,
|
||||
],
|
||||
dataRefs: {
|
||||
areas: catalogOverviewEntityContentLayoutAreasDataRef,
|
||||
defaultArea: EntityCardBlueprint.dataRefs.area,
|
||||
filterFunction: catalogOverviewEntityContentLayoutFilterFunctionDataRef,
|
||||
filterExpression: catalogOverviewEntityContentLayoutFilterExpressionDataRef,
|
||||
component: catalogOverviewEntityContentLayoutComponentDataRef,
|
||||
},
|
||||
config: {
|
||||
schema: {
|
||||
area: z => z.string().optional(),
|
||||
filter: z => z.string().optional(),
|
||||
},
|
||||
},
|
||||
*factory(
|
||||
{
|
||||
loader,
|
||||
areas,
|
||||
defaultArea,
|
||||
defaultFilter = () => false, // do not use the layout if a default filter is undefined
|
||||
}: {
|
||||
areas: Array<string>;
|
||||
defaultArea: (typeof areas)[number]; // fixed typescript area type
|
||||
defaultFilter?:
|
||||
| typeof catalogOverviewEntityContentLayoutFilterFunctionDataRef.T
|
||||
| typeof catalogOverviewEntityContentLayoutFilterExpressionDataRef.T;
|
||||
loader: () => Promise<
|
||||
(props: OverviewEntityContentLayoutProps) => React.JSX.Element
|
||||
>;
|
||||
},
|
||||
{ node, config },
|
||||
) {
|
||||
yield catalogOverviewEntityContentLayoutAreasDataRef(areas);
|
||||
|
||||
yield EntityCardBlueprint.dataRefs.area(config.area ?? defaultArea);
|
||||
|
||||
if (config.filter) {
|
||||
yield catalogOverviewEntityContentLayoutFilterExpressionDataRef(
|
||||
config.filter,
|
||||
);
|
||||
} else if (typeof defaultFilter === 'string') {
|
||||
yield catalogOverviewEntityContentLayoutFilterExpressionDataRef(
|
||||
defaultFilter,
|
||||
);
|
||||
} else if (typeof defaultFilter === 'function') {
|
||||
yield catalogOverviewEntityContentLayoutFilterFunctionDataRef(
|
||||
defaultFilter,
|
||||
);
|
||||
}
|
||||
|
||||
const ExtensionComponent = reactLazy(() =>
|
||||
loader().then(component => ({ default: component })),
|
||||
);
|
||||
|
||||
yield catalogOverviewEntityContentLayoutComponentDataRef(
|
||||
(props: ComponentProps<typeof ExtensionComponent>) => {
|
||||
return (
|
||||
<ExtensionBoundary node={node}>
|
||||
<ExtensionComponent {...props} />
|
||||
</ExtensionBoundary>
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
@@ -50,3 +50,14 @@ export const entityContentGroupDataRef = createExtensionDataRef<
|
||||
>().with({
|
||||
id: 'catalog.entity-content-group',
|
||||
});
|
||||
|
||||
/**
|
||||
* @alpha
|
||||
* Default entity content groups.
|
||||
*/
|
||||
export const defaultEntityCardArea = ['info', 'glance', 'main'] as const;
|
||||
|
||||
/** @internal */
|
||||
export const entityCardAreaDataRef = createExtensionDataRef<string>().with({
|
||||
id: 'catalog.entity-card-area',
|
||||
});
|
||||
|
||||
@@ -15,4 +15,8 @@
|
||||
*/
|
||||
export { EntityCardBlueprint } from './EntityCardBlueprint';
|
||||
export { EntityContentBlueprint } from './EntityContentBlueprint';
|
||||
export {
|
||||
OverviewEntityContentLauyoutBlueprint,
|
||||
type OverviewEntityContentLayoutProps,
|
||||
} from './OverviewEntityContentLauyoutBlueprint';
|
||||
export { defaultEntityContentGroups } from './extensionData';
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
|
||||
import { IconComponent } from '@backstage/core-plugin-api';
|
||||
import { JSX as JSX_2 } from 'react';
|
||||
import { OverviewEntityContentLayoutProps } from '@backstage/plugin-catalog-react/alpha';
|
||||
import { RouteRef } from '@backstage/frontend-plugin-api';
|
||||
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
|
||||
import { SearchResultItemExtensionPredicate } from '@backstage/plugin-search-react/alpha';
|
||||
@@ -217,9 +218,11 @@ const _default: FrontendPlugin<
|
||||
name: 'about';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -236,11 +239,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/links': ExtensionDefinition<{
|
||||
@@ -248,9 +259,11 @@ const _default: FrontendPlugin<
|
||||
name: 'links';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -267,11 +280,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/labels': ExtensionDefinition<{
|
||||
@@ -279,9 +300,11 @@ const _default: FrontendPlugin<
|
||||
name: 'labels';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -298,11 +321,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/depends-on-components': ExtensionDefinition<{
|
||||
@@ -310,9 +341,11 @@ const _default: FrontendPlugin<
|
||||
name: 'depends-on-components';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -329,11 +362,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/depends-on-resources': ExtensionDefinition<{
|
||||
@@ -341,9 +382,11 @@ const _default: FrontendPlugin<
|
||||
name: 'depends-on-resources';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -360,11 +403,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/has-components': ExtensionDefinition<{
|
||||
@@ -372,9 +423,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-components';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -391,11 +444,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/has-resources': ExtensionDefinition<{
|
||||
@@ -403,9 +464,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-resources';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -422,11 +485,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/has-subcomponents': ExtensionDefinition<{
|
||||
@@ -434,9 +505,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-subcomponents';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -453,11 +526,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/has-subdomains': ExtensionDefinition<{
|
||||
@@ -465,9 +546,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-subdomains';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -484,11 +567,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:catalog/has-systems': ExtensionDefinition<{
|
||||
@@ -496,9 +587,11 @@ const _default: FrontendPlugin<
|
||||
name: 'has-systems';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
@@ -515,11 +608,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-content:catalog/overview': ExtensionDefinition<{
|
||||
@@ -572,6 +673,37 @@ const _default: FrontendPlugin<
|
||||
}
|
||||
>;
|
||||
inputs: {
|
||||
layouts: ExtensionInput<
|
||||
| ConfigurableExtensionDataRef<string, 'catalog.entity-card-area', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string[],
|
||||
'catalog.overview-entity-content.layout.areas',
|
||||
{}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(props: OverviewEntityContentLayoutProps) => JSX_2.Element,
|
||||
'catalog.overview-entity-content.layout.page-component',
|
||||
{}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
(entity: Entity) => boolean,
|
||||
'catalog.overview-entity-content.layout.filter-function',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.overview-entity-content.layout.filter-expression',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{
|
||||
singleton: false;
|
||||
optional: false;
|
||||
}
|
||||
>;
|
||||
cards: ExtensionInput<
|
||||
| ConfigurableExtensionDataRef<JSX_2.Element, 'core.reactElement', {}>
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -587,6 +719,13 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>,
|
||||
{
|
||||
singleton: false;
|
||||
|
||||
@@ -19,38 +19,85 @@ import {
|
||||
coreExtensionData,
|
||||
createExtensionInput,
|
||||
} from '@backstage/frontend-plugin-api';
|
||||
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
||||
import {
|
||||
EntityCardBlueprint,
|
||||
EntityContentBlueprint,
|
||||
OverviewEntityContentLauyoutBlueprint,
|
||||
} from '@backstage/plugin-catalog-react/alpha';
|
||||
import { buildFilterFn } from './filter/FilterWrapper';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
|
||||
export const catalogOverviewEntityContent =
|
||||
EntityContentBlueprint.makeWithOverrides({
|
||||
name: 'overview',
|
||||
inputs: {
|
||||
cards: createExtensionInput([
|
||||
coreExtensionData.reactElement,
|
||||
EntityContentBlueprint.dataRefs.filterFunction.optional(),
|
||||
EntityContentBlueprint.dataRefs.filterExpression.optional(),
|
||||
]),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
return originalFactory({
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
loader: async () =>
|
||||
import('./EntityOverviewPage').then(m => (
|
||||
<m.EntityOverviewPage
|
||||
cards={inputs.cards.map(c => ({
|
||||
element: c.get(coreExtensionData.reactElement),
|
||||
filterFunction: c.get(
|
||||
const catalogOverviewEntityContent = EntityContentBlueprint.makeWithOverrides({
|
||||
name: 'overview',
|
||||
inputs: {
|
||||
layouts: createExtensionInput([
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.areas,
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.defaultArea,
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.filterFunction.optional(),
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.filterExpression.optional(),
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.component,
|
||||
]),
|
||||
cards: createExtensionInput([
|
||||
coreExtensionData.reactElement,
|
||||
EntityContentBlueprint.dataRefs.filterFunction.optional(),
|
||||
EntityContentBlueprint.dataRefs.filterExpression.optional(),
|
||||
EntityCardBlueprint.dataRefs.area.optional(),
|
||||
]),
|
||||
},
|
||||
factory: (originalFactory, { inputs }) => {
|
||||
return originalFactory({
|
||||
defaultPath: '/',
|
||||
defaultTitle: 'Overview',
|
||||
loader: async () => {
|
||||
const defaultLayout = await import('./EntityOverviewPage').then(
|
||||
m => m.EntityOverviewPage,
|
||||
);
|
||||
|
||||
const Component = () => {
|
||||
const { entity } = useEntity();
|
||||
|
||||
// Use the first layout that matches the entity filter
|
||||
const layout = inputs.layouts.find(l => {
|
||||
const filterFunction = l.get(
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.filterFunction,
|
||||
);
|
||||
const filterExpression = l.get(
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.filterExpression,
|
||||
);
|
||||
|
||||
return buildFilterFn(filterFunction, filterExpression)(entity);
|
||||
});
|
||||
|
||||
const Layout =
|
||||
layout?.get(
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.component,
|
||||
) ?? defaultLayout;
|
||||
|
||||
return (
|
||||
<Layout
|
||||
buildFilterFn={buildFilterFn}
|
||||
cards={inputs.cards.map(card => ({
|
||||
element: card.get(coreExtensionData.reactElement),
|
||||
filterFunction: card.get(
|
||||
EntityContentBlueprint.dataRefs.filterFunction,
|
||||
),
|
||||
filterExpression: c.get(
|
||||
filterExpression: card.get(
|
||||
EntityContentBlueprint.dataRefs.filterExpression,
|
||||
),
|
||||
area:
|
||||
card.get(EntityCardBlueprint.dataRefs.area) ??
|
||||
layout?.get(
|
||||
OverviewEntityContentLauyoutBlueprint.dataRefs.defaultArea,
|
||||
),
|
||||
}))}
|
||||
/>
|
||||
)),
|
||||
});
|
||||
},
|
||||
});
|
||||
);
|
||||
};
|
||||
|
||||
return <Component />;
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export default [catalogOverviewEntityContent];
|
||||
|
||||
@@ -22,9 +22,11 @@ const _default: FrontendPlugin<
|
||||
name: 'group-profile';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -45,11 +47,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:org/members-list': ExtensionDefinition<{
|
||||
@@ -57,9 +67,11 @@ const _default: FrontendPlugin<
|
||||
name: 'members-list';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -80,11 +92,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:org/ownership': ExtensionDefinition<{
|
||||
@@ -92,9 +112,11 @@ const _default: FrontendPlugin<
|
||||
name: 'ownership';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -115,11 +137,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
'entity-card:org/user-profile': ExtensionDefinition<{
|
||||
@@ -127,9 +157,11 @@ const _default: FrontendPlugin<
|
||||
name: 'user-profile';
|
||||
config: {
|
||||
filter: string | undefined;
|
||||
area: string | undefined;
|
||||
};
|
||||
configInput: {
|
||||
filter?: string | undefined;
|
||||
area?: string | undefined;
|
||||
};
|
||||
output:
|
||||
| ConfigurableExtensionDataRef<
|
||||
@@ -150,11 +182,19 @@ const _default: FrontendPlugin<
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>
|
||||
| ConfigurableExtensionDataRef<
|
||||
string,
|
||||
'catalog.entity-card-area',
|
||||
{
|
||||
optional: true;
|
||||
}
|
||||
>;
|
||||
inputs: {};
|
||||
params: {
|
||||
loader: () => Promise<JSX.Element>;
|
||||
filter?: string | ((entity: Entity) => boolean) | undefined;
|
||||
defaultArea?: 'main' | 'info' | 'glance' | undefined;
|
||||
};
|
||||
}>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user