@backstage/plugin-catalog-react 3.1.0 → 3.2.0-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # @backstage/plugin-catalog-react
2
2
 
3
+ ## 3.2.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 8a500d5: Fixed a regression where `EntityTypePicker`'s `initialFilter` prop was being cleared when used alongside `EntityKindPicker` inside `EntityListProvider`. The type filter is now correctly preserved after the available types load for the selected kind.
8
+ - Updated dependencies
9
+ - @backstage/core-components@0.18.12-next.1
10
+
11
+ ## 3.2.0-next.0
12
+
13
+ ### Minor Changes
14
+
15
+ - ba49e37: **BREAKING ALPHA**: The `EntityContextMenuItemBlueprint` now outputs menu item data instead of a rendered MUI element. The Catalog entity page consumes this data and renders BUI menu items.
16
+
17
+ The source-level `icon`, `useProps`, and filter authoring model remains, with `icon` now typed as `IconElement`. We recommend using Remix icons and checking that custom icons follow the standard icon sizing requirements.
18
+
19
+ Menu items close immediately when selected, including while asynchronous actions are pending.
20
+
21
+ - 15719cc: Added an alpha BUI-ready entity header layout extension point. Its loaded component receives Catalog-composed tabs and the active tab ID, allowing custom entity headers to preserve or customize entity-page navigation.
22
+
23
+ **DEPRECATED ALPHA**: The existing opaque entity header extension point is deprecated. It continues to work through a temporary Catalog legacy-layout fallback so adopters can migrate custom entity headers incrementally.
24
+
25
+ ### Patch Changes
26
+
27
+ - Updated dependencies
28
+ - @backstage/ui@0.17.0-next.0
29
+ - @backstage/filter-predicates@0.1.4-next.0
30
+ - @backstage/core-compat-api@0.5.13-next.0
31
+ - @backstage/frontend-plugin-api@0.17.3-next.0
32
+ - @backstage/frontend-test-utils@0.6.2-next.0
33
+ - @backstage/catalog-client@1.16.1-next.0
34
+ - @backstage/core-components@0.18.12-next.0
35
+ - @backstage/core-plugin-api@1.12.8-next.0
36
+ - @backstage/integration-react@1.2.20-next.0
37
+ - @backstage/plugin-permission-react@0.5.3-next.0
38
+
3
39
  ## 3.1.0
4
40
 
5
41
  ### Minor Changes
@@ -1,49 +1,26 @@
1
- import { jsx, jsxs } from 'react/jsx-runtime';
2
- import { createExtensionBlueprint, coreExtensionData, ExtensionBoundary } from '@backstage/frontend-plugin-api';
3
- import MenuItem from '@material-ui/core/MenuItem';
4
- import ListItemIcon from '@material-ui/core/ListItemIcon';
5
- import ListItemText from '@material-ui/core/ListItemText';
6
- import { useEntityContextMenu } from '../../hooks/useEntityContextMenu.esm.js';
1
+ import { createExtensionBlueprint } from '@backstage/frontend-plugin-api';
7
2
  import { filterPredicateToFilterFunction, createZodV4FilterPredicateSchema } from '@backstage/filter-predicates';
8
- import { entityFilterFunctionDataRef } from './extensionData.esm.js';
3
+ import { entityFilterFunctionDataRef, entityContextMenuItemDataRef } from './extensionData.esm.js';
9
4
 
10
5
  const EntityContextMenuItemBlueprint = createExtensionBlueprint({
11
6
  kind: "entity-context-menu-item",
12
7
  attachTo: { id: "page:catalog/entity", input: "contextMenuItems" },
13
8
  output: [
14
- coreExtensionData.reactElement,
9
+ entityContextMenuItemDataRef,
15
10
  entityFilterFunctionDataRef.optional()
16
11
  ],
17
12
  dataRefs: {
13
+ data: entityContextMenuItemDataRef,
18
14
  filterFunction: entityFilterFunctionDataRef
19
15
  },
20
16
  configSchema: {
21
17
  filter: createZodV4FilterPredicateSchema().optional()
22
18
  },
23
- *factory(params, { node, config }) {
24
- const loader = async () => {
25
- const Component = () => {
26
- const { onMenuClose } = useEntityContextMenu();
27
- const { title, ...menuItemProps } = params.useProps();
28
- let handleClick = void 0;
29
- if ("onClick" in menuItemProps) {
30
- handleClick = () => {
31
- const result = menuItemProps.onClick();
32
- if (result && "then" in result) {
33
- result.then(onMenuClose, onMenuClose);
34
- } else {
35
- onMenuClose();
36
- }
37
- };
38
- }
39
- return /* @__PURE__ */ jsxs(MenuItem, { ...menuItemProps, onClick: handleClick, children: [
40
- /* @__PURE__ */ jsx(ListItemIcon, { children: params.icon }),
41
- /* @__PURE__ */ jsx(ListItemText, { primary: title })
42
- ] });
43
- };
44
- return /* @__PURE__ */ jsx(Component, {});
45
- };
46
- yield coreExtensionData.reactElement(ExtensionBoundary.lazy(node, loader));
19
+ *factory(params, { config }) {
20
+ yield entityContextMenuItemDataRef({
21
+ icon: params.icon,
22
+ useProps: params.useProps
23
+ });
47
24
  if (config.filter) {
48
25
  yield entityFilterFunctionDataRef(
49
26
  filterPredicateToFilterFunction(config.filter)
@@ -1 +1 @@
1
- {"version":3,"file":"EntityContextMenuItemBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { ReactNode, JSX } from 'react';\nimport {\n coreExtensionData,\n createExtensionBlueprint,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\nimport MenuItem from '@material-ui/core/MenuItem';\nimport ListItemIcon from '@material-ui/core/ListItemIcon';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport { useEntityContextMenu } from '../../hooks/useEntityContextMenu';\nimport {\n FilterPredicate,\n filterPredicateToFilterFunction,\n createZodV4FilterPredicateSchema,\n} from '@backstage/filter-predicates';\nimport type { Entity } from '@backstage/catalog-model';\nimport { entityFilterFunctionDataRef } from './extensionData';\n/** @alpha */\nexport type UseProps = () =>\n | {\n title: ReactNode;\n href: string;\n disabled?: boolean;\n }\n | {\n title: ReactNode;\n onClick: () => void | Promise<void>;\n disabled?: boolean;\n };\n\n/** @alpha */\nexport type EntityContextMenuItemParams = {\n useProps: UseProps;\n icon: JSX.Element;\n filter?: FilterPredicate | ((entity: Entity) => boolean);\n};\n\n/** @alpha */\nexport const EntityContextMenuItemBlueprint = createExtensionBlueprint({\n kind: 'entity-context-menu-item',\n attachTo: { id: 'page:catalog/entity', input: 'contextMenuItems' },\n output: [\n coreExtensionData.reactElement,\n entityFilterFunctionDataRef.optional(),\n ],\n dataRefs: {\n filterFunction: entityFilterFunctionDataRef,\n },\n configSchema: {\n filter: createZodV4FilterPredicateSchema().optional(),\n },\n *factory(params: EntityContextMenuItemParams, { node, config }) {\n const loader = async () => {\n const Component = () => {\n const { onMenuClose } = useEntityContextMenu();\n const { title, ...menuItemProps } = params.useProps();\n let handleClick = undefined;\n\n if ('onClick' in menuItemProps) {\n handleClick = () => {\n const result = menuItemProps.onClick();\n if (result && 'then' in result) {\n result.then(onMenuClose, onMenuClose);\n } else {\n onMenuClose();\n }\n };\n }\n\n return (\n <MenuItem {...menuItemProps} onClick={handleClick}>\n <ListItemIcon>{params.icon}</ListItemIcon>\n <ListItemText primary={title} />\n </MenuItem>\n );\n };\n\n return <Component />;\n };\n\n yield coreExtensionData.reactElement(ExtensionBoundary.lazy(node, loader));\n\n if (config.filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(config.filter),\n );\n } else if (typeof params.filter === 'function') {\n yield entityFilterFunctionDataRef(params.filter);\n } else if (params.filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(params.filter),\n );\n }\n },\n});\n"],"names":[],"mappings":";;;;;;;;;AAsDO,MAAM,iCAAiC,wBAAA,CAAyB;AAAA,EACrE,IAAA,EAAM,0BAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,qBAAA,EAAuB,OAAO,kBAAA,EAAmB;AAAA,EACjE,MAAA,EAAQ;AAAA,IACN,iBAAA,CAAkB,YAAA;AAAA,IAClB,4BAA4B,QAAA;AAAS,GACvC;AAAA,EACA,QAAA,EAAU;AAAA,IACR,cAAA,EAAgB;AAAA,GAClB;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,MAAA,EAAQ,gCAAA,EAAiC,CAAE,QAAA;AAAS,GACtD;AAAA,EACA,CAAC,OAAA,CAAQ,MAAA,EAAqC,EAAE,IAAA,EAAM,QAAO,EAAG;AAC9D,IAAA,MAAM,SAAS,YAAY;AACzB,MAAA,MAAM,YAAY,MAAM;AACtB,QAAA,MAAM,EAAE,WAAA,EAAY,GAAI,oBAAA,EAAqB;AAC7C,QAAA,MAAM,EAAE,KAAA,EAAO,GAAG,aAAA,EAAc,GAAI,OAAO,QAAA,EAAS;AACpD,QAAA,IAAI,WAAA,GAAc,MAAA;AAElB,QAAA,IAAI,aAAa,aAAA,EAAe;AAC9B,UAAA,WAAA,GAAc,MAAM;AAClB,YAAA,MAAM,MAAA,GAAS,cAAc,OAAA,EAAQ;AACrC,YAAA,IAAI,MAAA,IAAU,UAAU,MAAA,EAAQ;AAC9B,cAAA,MAAA,CAAO,IAAA,CAAK,aAAa,WAAW,CAAA;AAAA,YACtC,CAAA,MAAO;AACL,cAAA,WAAA,EAAY;AAAA,YACd;AAAA,UACF,CAAA;AAAA,QACF;AAEA,QAAA,uBACE,IAAA,CAAC,QAAA,EAAA,EAAU,GAAG,aAAA,EAAe,SAAS,WAAA,EACpC,QAAA,EAAA;AAAA,0BAAA,GAAA,CAAC,YAAA,EAAA,EAAc,iBAAO,IAAA,EAAK,CAAA;AAAA,0BAC3B,GAAA,CAAC,YAAA,EAAA,EAAa,OAAA,EAAS,KAAA,EAAO;AAAA,SAAA,EAChC,CAAA;AAAA,MAEJ,CAAA;AAEA,MAAA,2BAAQ,SAAA,EAAA,EAAU,CAAA;AAAA,IACpB,CAAA;AAEA,IAAA,MAAM,kBAAkB,YAAA,CAAa,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,MAAM,CAAC,CAAA;AAEzE,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,MAAM,2BAAA;AAAA,QACJ,+BAAA,CAAgC,OAAO,MAAM;AAAA,OAC/C;AAAA,IACF,CAAA,MAAA,IAAW,OAAO,MAAA,CAAO,MAAA,KAAW,UAAA,EAAY;AAC9C,MAAA,MAAM,2BAAA,CAA4B,OAAO,MAAM,CAAA;AAAA,IACjD,CAAA,MAAA,IAAW,OAAO,MAAA,EAAQ;AACxB,MAAA,MAAM,2BAAA;AAAA,QACJ,+BAAA,CAAgC,OAAO,MAAM;AAAA,OAC/C;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"EntityContextMenuItemBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createExtensionBlueprint,\n type IconElement,\n} from '@backstage/frontend-plugin-api';\nimport {\n FilterPredicate,\n filterPredicateToFilterFunction,\n createZodV4FilterPredicateSchema,\n} from '@backstage/filter-predicates';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n entityContextMenuItemDataRef,\n entityFilterFunctionDataRef,\n type UseProps,\n} from './extensionData';\n\nexport type { EntityContextMenuItemData, UseProps } from './extensionData';\n\n/** @alpha */\nexport type EntityContextMenuItemParams = {\n useProps: UseProps;\n icon: IconElement;\n filter?: FilterPredicate | ((entity: Entity) => boolean);\n};\n\n/** @alpha */\nexport const EntityContextMenuItemBlueprint = createExtensionBlueprint({\n kind: 'entity-context-menu-item',\n attachTo: { id: 'page:catalog/entity', input: 'contextMenuItems' },\n output: [\n entityContextMenuItemDataRef,\n entityFilterFunctionDataRef.optional(),\n ],\n dataRefs: {\n data: entityContextMenuItemDataRef,\n filterFunction: entityFilterFunctionDataRef,\n },\n configSchema: {\n filter: createZodV4FilterPredicateSchema().optional(),\n },\n *factory(params: EntityContextMenuItemParams, { config }) {\n yield entityContextMenuItemDataRef({\n icon: params.icon,\n useProps: params.useProps,\n });\n\n if (config.filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(config.filter),\n );\n } else if (typeof params.filter === 'function') {\n yield entityFilterFunctionDataRef(params.filter);\n } else if (params.filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(params.filter),\n );\n }\n },\n});\n"],"names":[],"mappings":";;;;AA0CO,MAAM,iCAAiC,wBAAA,CAAyB;AAAA,EACrE,IAAA,EAAM,0BAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,qBAAA,EAAuB,OAAO,kBAAA,EAAmB;AAAA,EACjE,MAAA,EAAQ;AAAA,IACN,4BAAA;AAAA,IACA,4BAA4B,QAAA;AAAS,GACvC;AAAA,EACA,QAAA,EAAU;AAAA,IACR,IAAA,EAAM,4BAAA;AAAA,IACN,cAAA,EAAgB;AAAA,GAClB;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,MAAA,EAAQ,gCAAA,EAAiC,CAAE,QAAA;AAAS,GACtD;AAAA,EACA,CAAC,OAAA,CAAQ,MAAA,EAAqC,EAAE,QAAO,EAAG;AACxD,IAAA,MAAM,4BAAA,CAA6B;AAAA,MACjC,MAAM,MAAA,CAAO,IAAA;AAAA,MACb,UAAU,MAAA,CAAO;AAAA,KAClB,CAAA;AAED,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,MAAM,2BAAA;AAAA,QACJ,+BAAA,CAAgC,OAAO,MAAM;AAAA,OAC/C;AAAA,IACF,CAAA,MAAA,IAAW,OAAO,MAAA,CAAO,MAAA,KAAW,UAAA,EAAY;AAC9C,MAAA,MAAM,2BAAA,CAA4B,OAAO,MAAM,CAAA;AAAA,IACjD,CAAA,MAAA,IAAW,OAAO,MAAA,EAAQ;AACxB,MAAA,MAAM,2BAAA;AAAA,QACJ,+BAAA,CAAgC,OAAO,MAAM;AAAA,OAC/C;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"EntityHeaderBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityHeaderBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createExtensionBlueprint,\n coreExtensionData,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\nimport {\n FilterPredicate,\n createZodV4FilterPredicateSchema,\n} from '@backstage/filter-predicates';\nimport { Entity } from '@backstage/catalog-model';\nimport { resolveEntityFilterData } from './resolveEntityFilterData';\nimport {\n entityFilterExpressionDataRef,\n entityFilterFunctionDataRef,\n} from './extensionData';\n\n/** @alpha */\nexport const EntityHeaderBlueprint = createExtensionBlueprint({\n kind: 'entity-header',\n attachTo: { id: 'page:catalog/entity', input: 'headers' },\n dataRefs: {\n filterFunction: entityFilterFunctionDataRef,\n element: coreExtensionData.reactElement,\n },\n configSchema: {\n filter: createZodV4FilterPredicateSchema().optional(),\n },\n output: [\n entityFilterFunctionDataRef.optional(),\n entityFilterExpressionDataRef.optional(),\n coreExtensionData.reactElement.optional(),\n ],\n *factory(\n params: {\n loader: () => Promise<JSX.Element>;\n filter?: FilterPredicate | ((entity: Entity) => boolean);\n },\n { node, config },\n ) {\n const { loader, filter } = params;\n\n yield* resolveEntityFilterData(filter, config, node);\n\n if (loader) {\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, loader),\n );\n }\n },\n});\n"],"names":[],"mappings":";;;;;AAiCO,MAAM,wBAAwB,wBAAA,CAAyB;AAAA,EAC5D,IAAA,EAAM,eAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,qBAAA,EAAuB,OAAO,SAAA,EAAU;AAAA,EACxD,QAAA,EAAU;AAAA,IACR,cAAA,EAAgB,2BAAA;AAAA,IAChB,SAAS,iBAAA,CAAkB;AAAA,GAC7B;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,MAAA,EAAQ,gCAAA,EAAiC,CAAE,QAAA;AAAS,GACtD;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,4BAA4B,QAAA,EAAS;AAAA,IACrC,8BAA8B,QAAA,EAAS;AAAA,IACvC,iBAAA,CAAkB,aAAa,QAAA;AAAS,GAC1C;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAIA,EAAE,IAAA,EAAM,QAAO,EACf;AACA,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,MAAA;AAE3B,IAAA,OAAO,uBAAA,CAAwB,MAAA,EAAQ,MAAA,EAAQ,IAAI,CAAA;AAEnD,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,QACtB,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,MAAM;AAAA,OACrC;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
1
+ {"version":3,"file":"EntityHeaderBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityHeaderBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport {\n createExtensionBlueprint,\n coreExtensionData,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\nimport {\n FilterPredicate,\n createZodV4FilterPredicateSchema,\n} from '@backstage/filter-predicates';\nimport { Entity } from '@backstage/catalog-model';\nimport { resolveEntityFilterData } from './resolveEntityFilterData';\nimport {\n entityFilterExpressionDataRef,\n entityFilterFunctionDataRef,\n} from './extensionData';\n\n/**\n * @alpha\n * @deprecated Use {@link EntityHeaderLayoutBlueprint} instead. This legacy\n * blueprint renders an opaque header element through the temporary Catalog\n * entity-page fallback.\n */\nexport const EntityHeaderBlueprint = createExtensionBlueprint({\n kind: 'entity-header',\n attachTo: { id: 'page:catalog/entity', input: 'headers' },\n dataRefs: {\n filterFunction: entityFilterFunctionDataRef,\n element: coreExtensionData.reactElement,\n },\n configSchema: {\n filter: createZodV4FilterPredicateSchema().optional(),\n },\n output: [\n entityFilterFunctionDataRef.optional(),\n entityFilterExpressionDataRef.optional(),\n coreExtensionData.reactElement.optional(),\n ],\n *factory(\n params: {\n loader: () => Promise<JSX.Element>;\n filter?: FilterPredicate | ((entity: Entity) => boolean);\n },\n { node, config },\n ) {\n const { loader, filter } = params;\n\n yield* resolveEntityFilterData(filter, config, node);\n\n if (loader) {\n yield coreExtensionData.reactElement(\n ExtensionBoundary.lazy(node, loader),\n );\n }\n },\n});\n"],"names":[],"mappings":";;;;;AAsCO,MAAM,wBAAwB,wBAAA,CAAyB;AAAA,EAC5D,IAAA,EAAM,eAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,qBAAA,EAAuB,OAAO,SAAA,EAAU;AAAA,EACxD,QAAA,EAAU;AAAA,IACR,cAAA,EAAgB,2BAAA;AAAA,IAChB,SAAS,iBAAA,CAAkB;AAAA,GAC7B;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,MAAA,EAAQ,gCAAA,EAAiC,CAAE,QAAA;AAAS,GACtD;AAAA,EACA,MAAA,EAAQ;AAAA,IACN,4BAA4B,QAAA,EAAS;AAAA,IACrC,8BAA8B,QAAA,EAAS;AAAA,IACvC,iBAAA,CAAkB,aAAa,QAAA;AAAS,GAC1C;AAAA,EACA,CAAC,OAAA,CACC,MAAA,EAIA,EAAE,IAAA,EAAM,QAAO,EACf;AACA,IAAA,MAAM,EAAE,MAAA,EAAQ,MAAA,EAAO,GAAI,MAAA;AAE3B,IAAA,OAAO,uBAAA,CAAwB,MAAA,EAAQ,MAAA,EAAQ,IAAI,CAAA;AAEnD,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,iBAAA,CAAkB,YAAA;AAAA,QACtB,iBAAA,CAAkB,IAAA,CAAK,IAAA,EAAM,MAAM;AAAA,OACrC;AAAA,IACF;AAAA,EACF;AACF,CAAC;;;;"}
@@ -0,0 +1,44 @@
1
+ import { filterPredicateToFilterFunction, createZodV4FilterPredicateSchema } from '@backstage/filter-predicates';
2
+ import { createExtensionDataRef, createExtensionBlueprint, ExtensionBoundary } from '@backstage/frontend-plugin-api';
3
+ import { entityFilterFunctionDataRef } from './extensionData.esm.js';
4
+
5
+ const entityHeaderLayoutComponentDataRef = createExtensionDataRef().with({
6
+ id: "catalog.entity-header-layout.component"
7
+ });
8
+ const EntityHeaderLayoutBlueprint = createExtensionBlueprint({
9
+ kind: "entity-header-layout",
10
+ attachTo: { id: "page:catalog/entity", input: "headerLayouts" },
11
+ output: [
12
+ entityFilterFunctionDataRef.optional(),
13
+ entityHeaderLayoutComponentDataRef
14
+ ],
15
+ dataRefs: {
16
+ filterFunction: entityFilterFunctionDataRef,
17
+ component: entityHeaderLayoutComponentDataRef
18
+ },
19
+ configSchema: {
20
+ filter: createZodV4FilterPredicateSchema().optional()
21
+ },
22
+ *factory({
23
+ loader,
24
+ filter
25
+ }, { node, config }) {
26
+ if (config.filter) {
27
+ yield entityFilterFunctionDataRef(
28
+ filterPredicateToFilterFunction(config.filter)
29
+ );
30
+ } else if (typeof filter === "function") {
31
+ yield entityFilterFunctionDataRef(filter);
32
+ } else if (filter) {
33
+ yield entityFilterFunctionDataRef(
34
+ filterPredicateToFilterFunction(filter)
35
+ );
36
+ }
37
+ yield entityHeaderLayoutComponentDataRef(
38
+ ExtensionBoundary.lazyComponent(node, loader)
39
+ );
40
+ }
41
+ });
42
+
43
+ export { EntityHeaderLayoutBlueprint };
44
+ //# sourceMappingURL=EntityHeaderLayoutBlueprint.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EntityHeaderLayoutBlueprint.esm.js","sources":["../../../src/alpha/blueprints/EntityHeaderLayoutBlueprint.tsx"],"sourcesContent":["/*\n * Copyright 2026 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n FilterPredicate,\n createZodV4FilterPredicateSchema,\n filterPredicateToFilterFunction,\n} from '@backstage/filter-predicates';\nimport {\n createExtensionBlueprint,\n createExtensionDataRef,\n ExtensionBoundary,\n} from '@backstage/frontend-plugin-api';\nimport { JSX } from 'react';\nimport { entityFilterFunctionDataRef } from './extensionData';\n\n/** @alpha */\nexport interface EntityHeaderLayoutProps {\n tabs: Array<\n | {\n id: string;\n label: string;\n href: string;\n }\n | {\n id: string;\n label: string;\n items: Array<{\n id: string;\n label: string;\n href: string;\n }>;\n }\n >;\n activeTabId?: string;\n}\n\nconst entityHeaderLayoutComponentDataRef = createExtensionDataRef<\n (props: EntityHeaderLayoutProps) => JSX.Element\n>().with({\n id: 'catalog.entity-header-layout.component',\n});\n\n/** @alpha */\nexport const EntityHeaderLayoutBlueprint = createExtensionBlueprint({\n kind: 'entity-header-layout',\n attachTo: { id: 'page:catalog/entity', input: 'headerLayouts' },\n output: [\n entityFilterFunctionDataRef.optional(),\n entityHeaderLayoutComponentDataRef,\n ],\n dataRefs: {\n filterFunction: entityFilterFunctionDataRef,\n component: entityHeaderLayoutComponentDataRef,\n },\n configSchema: {\n filter: createZodV4FilterPredicateSchema().optional(),\n },\n *factory(\n {\n loader,\n filter,\n }: {\n filter?: FilterPredicate | ((entity: Entity) => boolean);\n loader: () => Promise<(props: EntityHeaderLayoutProps) => JSX.Element>;\n },\n { node, config },\n ) {\n if (config.filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(config.filter),\n );\n } else if (typeof filter === 'function') {\n yield entityFilterFunctionDataRef(filter);\n } else if (filter) {\n yield entityFilterFunctionDataRef(\n filterPredicateToFilterFunction(filter),\n );\n }\n yield entityHeaderLayoutComponentDataRef(\n ExtensionBoundary.lazyComponent(node, loader),\n );\n },\n});\n"],"names":[],"mappings":";;;;AAmDA,MAAM,kCAAA,GAAqC,sBAAA,EAEzC,CAAE,IAAA,CAAK;AAAA,EACP,EAAA,EAAI;AACN,CAAC,CAAA;AAGM,MAAM,8BAA8B,wBAAA,CAAyB;AAAA,EAClE,IAAA,EAAM,sBAAA;AAAA,EACN,QAAA,EAAU,EAAE,EAAA,EAAI,qBAAA,EAAuB,OAAO,eAAA,EAAgB;AAAA,EAC9D,MAAA,EAAQ;AAAA,IACN,4BAA4B,QAAA,EAAS;AAAA,IACrC;AAAA,GACF;AAAA,EACA,QAAA,EAAU;AAAA,IACR,cAAA,EAAgB,2BAAA;AAAA,IAChB,SAAA,EAAW;AAAA,GACb;AAAA,EACA,YAAA,EAAc;AAAA,IACZ,MAAA,EAAQ,gCAAA,EAAiC,CAAE,QAAA;AAAS,GACtD;AAAA,EACA,CAAC,OAAA,CACC;AAAA,IACE,MAAA;AAAA,IACA;AAAA,GACF,EAIA,EAAE,IAAA,EAAM,MAAA,EAAO,EACf;AACA,IAAA,IAAI,OAAO,MAAA,EAAQ;AACjB,MAAA,MAAM,2BAAA;AAAA,QACJ,+BAAA,CAAgC,OAAO,MAAM;AAAA,OAC/C;AAAA,IACF,CAAA,MAAA,IAAW,OAAO,MAAA,KAAW,UAAA,EAAY;AACvC,MAAA,MAAM,4BAA4B,MAAM,CAAA;AAAA,IAC1C,WAAW,MAAA,EAAQ;AACjB,MAAA,MAAM,2BAAA;AAAA,QACJ,gCAAgC,MAAM;AAAA,OACxC;AAAA,IACF;AACA,IAAA,MAAM,kCAAA;AAAA,MACJ,iBAAA,CAAkB,aAAA,CAAc,IAAA,EAAM,MAAM;AAAA,KAC9C;AAAA,EACF;AACF,CAAC;;;;"}
@@ -1,5 +1,8 @@
1
1
  import { createExtensionDataRef } from '@backstage/frontend-plugin-api';
2
2
 
3
+ const entityContextMenuItemDataRef = createExtensionDataRef().with({
4
+ id: "catalog.entity-context-menu-item-data"
5
+ });
3
6
  const entityContentTitleDataRef = createExtensionDataRef().with({
4
7
  id: "catalog.entity-content-title"
5
8
  });
@@ -46,5 +49,5 @@ const entityCardTypeDataRef = createExtensionDataRef().with({
46
49
  id: "catalog.entity-card-type"
47
50
  });
48
51
 
49
- export { defaultEntityContentGroupDefinitions, defaultEntityContentGroups, entityCardTypeDataRef, entityCardTypes, entityContentGroupDataRef, entityContentIconDataRef, entityContentTitleDataRef, entityFilterExpressionDataRef, entityFilterFunctionDataRef };
52
+ export { defaultEntityContentGroupDefinitions, defaultEntityContentGroups, entityCardTypeDataRef, entityCardTypes, entityContentGroupDataRef, entityContentIconDataRef, entityContentTitleDataRef, entityContextMenuItemDataRef, entityFilterExpressionDataRef, entityFilterFunctionDataRef };
50
53
  //# sourceMappingURL=extensionData.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"extensionData.esm.js","sources":["../../../src/alpha/blueprints/extensionData.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport { createExtensionDataRef } from '@backstage/frontend-plugin-api';\nimport { ReactElement } from 'react';\n\n/** @internal */\nexport const entityContentTitleDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-title',\n});\n\n/** @internal */\nexport const entityContentIconDataRef = createExtensionDataRef<\n string | ReactElement\n>().with({\n id: 'catalog.entity-content-icon',\n});\n\n/** @internal */\nexport const entityFilterFunctionDataRef = createExtensionDataRef<\n (entity: Entity) => boolean\n>().with({ id: 'catalog.entity-filter-function' });\n\n/** @internal */\nexport const entityFilterExpressionDataRef =\n createExtensionDataRef<string>().with({\n id: 'catalog.entity-filter-expression',\n });\n\n/** @alpha */\nexport type EntityContentGroupDefinitions = Record<\n string,\n {\n title: string;\n icon?: string | ReactElement;\n /** Other group IDs that should be treated as aliases for this group. */\n aliases?: string[];\n /** How to sort the content items within this group. Overrides the page-level default. */\n contentOrder?: 'title' | 'natural';\n }\n>;\n\n/**\n * @alpha\n * Default entity content groups.\n */\nexport const defaultEntityContentGroupDefinitions = {\n overview: {\n title: 'Overview',\n },\n documentation: {\n title: 'Documentation',\n },\n development: {\n title: 'Development',\n },\n deployment: {\n title: 'Deployment',\n },\n operation: {\n title: 'Operation',\n },\n observability: {\n title: 'Observability',\n },\n} satisfies EntityContentGroupDefinitions;\n\n/**\n * @alpha\n * Default entity content groups.\n * @deprecated use defaultEntityContentGroupDefinitions\n */\nexport const defaultEntityContentGroups = Object.fromEntries(\n Object.entries(defaultEntityContentGroupDefinitions).map(\n ([key, { title }]) => [key, title],\n ),\n) as Record<keyof typeof defaultEntityContentGroupDefinitions, string>;\n\n/** @internal */\nexport const entityContentGroupDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-group',\n});\n\n/**\n * @internal\n * Available entity card types\n */\nexport const entityCardTypes = [\n 'info',\n 'content',\n] as const satisfies readonly EntityCardType[];\n\n/** @alpha */\nexport type EntityCardType = 'info' | 'content';\n\n/** @internal */\nexport const entityCardTypeDataRef =\n createExtensionDataRef<EntityCardType>().with({\n id: 'catalog.entity-card-type',\n });\n"],"names":[],"mappings":";;AAqBO,MAAM,yBAAA,GAA4B,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EAC7E,EAAA,EAAI;AACN,CAAC;AAGM,MAAM,wBAAA,GAA2B,sBAAA,EAEtC,CAAE,IAAA,CAAK;AAAA,EACP,EAAA,EAAI;AACN,CAAC;AAGM,MAAM,8BAA8B,sBAAA,EAEzC,CAAE,KAAK,EAAE,EAAA,EAAI,kCAAkC;AAG1C,MAAM,6BAAA,GACX,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EACpC,EAAA,EAAI;AACN,CAAC;AAmBI,MAAM,oCAAA,GAAuC;AAAA,EAClD,QAAA,EAAU;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA,aAAA,EAAe;AAAA,IACb,KAAA,EAAO;AAAA,GACT;AAAA,EACA,WAAA,EAAa;AAAA,IACX,KAAA,EAAO;AAAA,GACT;AAAA,EACA,UAAA,EAAY;AAAA,IACV,KAAA,EAAO;AAAA,GACT;AAAA,EACA,SAAA,EAAW;AAAA,IACT,KAAA,EAAO;AAAA,GACT;AAAA,EACA,aAAA,EAAe;AAAA,IACb,KAAA,EAAO;AAAA;AAEX;AAOO,MAAM,6BAA6B,MAAA,CAAO,WAAA;AAAA,EAC/C,MAAA,CAAO,OAAA,CAAQ,oCAAoC,CAAA,CAAE,GAAA;AAAA,IACnD,CAAC,CAAC,GAAA,EAAK,EAAE,OAAO,CAAA,KAAM,CAAC,GAAA,EAAK,KAAK;AAAA;AAErC;AAGO,MAAM,yBAAA,GAA4B,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EAC7E,EAAA,EAAI;AACN,CAAC;AAMM,MAAM,eAAA,GAAkB;AAAA,EAC7B,MAAA;AAAA,EACA;AACF;AAMO,MAAM,qBAAA,GACX,sBAAA,EAAuC,CAAE,IAAA,CAAK;AAAA,EAC5C,EAAA,EAAI;AACN,CAAC;;;;"}
1
+ {"version":3,"file":"extensionData.esm.js","sources":["../../../src/alpha/blueprints/extensionData.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity } from '@backstage/catalog-model';\nimport {\n createExtensionDataRef,\n type IconElement,\n} from '@backstage/frontend-plugin-api';\nimport { ReactElement, type ReactNode } from 'react';\n\n/** @alpha */\nexport type UseProps = () =>\n | {\n title: ReactNode;\n href: string;\n onClick?: () => void | Promise<void>;\n disabled?: boolean;\n }\n | {\n title: ReactNode;\n onClick: () => void | Promise<void>;\n disabled?: boolean;\n };\n\n/** @alpha */\nexport type EntityContextMenuItemData = {\n icon: IconElement;\n useProps: UseProps;\n};\n\n/** @internal */\nexport const entityContextMenuItemDataRef =\n createExtensionDataRef<EntityContextMenuItemData>().with({\n id: 'catalog.entity-context-menu-item-data',\n });\n\n/** @internal */\nexport const entityContentTitleDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-title',\n});\n\n/** @internal */\nexport const entityContentIconDataRef = createExtensionDataRef<\n string | ReactElement\n>().with({\n id: 'catalog.entity-content-icon',\n});\n\n/** @internal */\nexport const entityFilterFunctionDataRef = createExtensionDataRef<\n (entity: Entity) => boolean\n>().with({ id: 'catalog.entity-filter-function' });\n\n/** @internal */\nexport const entityFilterExpressionDataRef =\n createExtensionDataRef<string>().with({\n id: 'catalog.entity-filter-expression',\n });\n\n/** @alpha */\nexport type EntityContentGroupDefinitions = Record<\n string,\n {\n title: string;\n icon?: string | ReactElement;\n /** Other group IDs that should be treated as aliases for this group. */\n aliases?: string[];\n /** How to sort the content items within this group. Overrides the page-level default. */\n contentOrder?: 'title' | 'natural';\n }\n>;\n\n/**\n * @alpha\n * Default entity content groups.\n */\nexport const defaultEntityContentGroupDefinitions = {\n overview: {\n title: 'Overview',\n },\n documentation: {\n title: 'Documentation',\n },\n development: {\n title: 'Development',\n },\n deployment: {\n title: 'Deployment',\n },\n operation: {\n title: 'Operation',\n },\n observability: {\n title: 'Observability',\n },\n} satisfies EntityContentGroupDefinitions;\n\n/**\n * @alpha\n * Default entity content groups.\n * @deprecated use defaultEntityContentGroupDefinitions\n */\nexport const defaultEntityContentGroups = Object.fromEntries(\n Object.entries(defaultEntityContentGroupDefinitions).map(\n ([key, { title }]) => [key, title],\n ),\n) as Record<keyof typeof defaultEntityContentGroupDefinitions, string>;\n\n/** @internal */\nexport const entityContentGroupDataRef = createExtensionDataRef<string>().with({\n id: 'catalog.entity-content-group',\n});\n\n/**\n * @internal\n * Available entity card types\n */\nexport const entityCardTypes = [\n 'info',\n 'content',\n] as const satisfies readonly EntityCardType[];\n\n/** @alpha */\nexport type EntityCardType = 'info' | 'content';\n\n/** @internal */\nexport const entityCardTypeDataRef =\n createExtensionDataRef<EntityCardType>().with({\n id: 'catalog.entity-card-type',\n });\n"],"names":[],"mappings":";;AA4CO,MAAM,4BAAA,GACX,sBAAA,EAAkD,CAAE,IAAA,CAAK;AAAA,EACvD,EAAA,EAAI;AACN,CAAC;AAGI,MAAM,yBAAA,GAA4B,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EAC7E,EAAA,EAAI;AACN,CAAC;AAGM,MAAM,wBAAA,GAA2B,sBAAA,EAEtC,CAAE,IAAA,CAAK;AAAA,EACP,EAAA,EAAI;AACN,CAAC;AAGM,MAAM,8BAA8B,sBAAA,EAEzC,CAAE,KAAK,EAAE,EAAA,EAAI,kCAAkC;AAG1C,MAAM,6BAAA,GACX,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EACpC,EAAA,EAAI;AACN,CAAC;AAmBI,MAAM,oCAAA,GAAuC;AAAA,EAClD,QAAA,EAAU;AAAA,IACR,KAAA,EAAO;AAAA,GACT;AAAA,EACA,aAAA,EAAe;AAAA,IACb,KAAA,EAAO;AAAA,GACT;AAAA,EACA,WAAA,EAAa;AAAA,IACX,KAAA,EAAO;AAAA,GACT;AAAA,EACA,UAAA,EAAY;AAAA,IACV,KAAA,EAAO;AAAA,GACT;AAAA,EACA,SAAA,EAAW;AAAA,IACT,KAAA,EAAO;AAAA,GACT;AAAA,EACA,aAAA,EAAe;AAAA,IACb,KAAA,EAAO;AAAA;AAEX;AAOO,MAAM,6BAA6B,MAAA,CAAO,WAAA;AAAA,EAC/C,MAAA,CAAO,OAAA,CAAQ,oCAAoC,CAAA,CAAE,GAAA;AAAA,IACnD,CAAC,CAAC,GAAA,EAAK,EAAE,OAAO,CAAA,KAAM,CAAC,GAAA,EAAK,KAAK;AAAA;AAErC;AAGO,MAAM,yBAAA,GAA4B,sBAAA,EAA+B,CAAE,IAAA,CAAK;AAAA,EAC7E,EAAA,EAAI;AACN,CAAC;AAMM,MAAM,eAAA,GAAkB;AAAA,EAC7B,MAAA;AAAA,EACA;AACF;AAMO,MAAM,qBAAA,GACX,sBAAA,EAAuC,CAAE,IAAA,CAAK;AAAA,EAC5C,EAAA,EAAI;AACN,CAAC;;;;"}
package/dist/alpha.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
2
- import { RouteRef, ExtensionDefinition } from '@backstage/frontend-plugin-api';
2
+ import { IconElement, RouteRef, ExtensionDefinition } from '@backstage/frontend-plugin-api';
3
3
  import * as react from 'react';
4
- import { ReactElement, JSX as JSX$1, ReactNode, ComponentType } from 'react';
4
+ import { ReactElement, ReactNode, JSX as JSX$1, ComponentType } from 'react';
5
5
  import { FilterPredicate } from '@backstage/filter-predicates';
6
6
  import { Entity } from '@backstage/catalog-model';
7
7
  import { IconLinkVerticalProps } from '@backstage/core-components';
@@ -37,6 +37,22 @@ declare const CatalogFilterBlueprint: _backstage_frontend_plugin_api.ExtensionBl
37
37
  dataRefs: never;
38
38
  }>;
39
39
 
40
+ /** @alpha */
41
+ type UseProps = () => {
42
+ title: ReactNode;
43
+ href: string;
44
+ onClick?: () => void | Promise<void>;
45
+ disabled?: boolean;
46
+ } | {
47
+ title: ReactNode;
48
+ onClick: () => void | Promise<void>;
49
+ disabled?: boolean;
50
+ };
51
+ /** @alpha */
52
+ type EntityContextMenuItemData = {
53
+ icon: IconElement;
54
+ useProps: UseProps;
55
+ };
40
56
  /** @alpha */
41
57
  type EntityContentGroupDefinitions = Record<string, {
42
58
  title: string;
@@ -210,7 +226,12 @@ declare const EntityContentLayoutBlueprint: _backstage_frontend_plugin_api.Exten
210
226
  };
211
227
  }>;
212
228
 
213
- /** @alpha */
229
+ /**
230
+ * @alpha
231
+ * @deprecated Use {@link EntityHeaderLayoutBlueprint} instead. This legacy
232
+ * blueprint renders an opaque header element through the temporary Catalog
233
+ * entity-page fallback.
234
+ */
214
235
  declare const EntityHeaderBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
215
236
  kind: "entity-header";
216
237
  params: {
@@ -238,28 +259,58 @@ declare const EntityHeaderBlueprint: _backstage_frontend_plugin_api.ExtensionBlu
238
259
  }>;
239
260
 
240
261
  /** @alpha */
241
- type UseProps = () => {
242
- title: ReactNode;
243
- href: string;
244
- disabled?: boolean;
245
- } | {
246
- title: ReactNode;
247
- onClick: () => void | Promise<void>;
248
- disabled?: boolean;
249
- };
262
+ interface EntityHeaderLayoutProps {
263
+ tabs: Array<{
264
+ id: string;
265
+ label: string;
266
+ href: string;
267
+ } | {
268
+ id: string;
269
+ label: string;
270
+ items: Array<{
271
+ id: string;
272
+ label: string;
273
+ href: string;
274
+ }>;
275
+ }>;
276
+ activeTabId?: string;
277
+ }
278
+ /** @alpha */
279
+ declare const EntityHeaderLayoutBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
280
+ kind: "entity-header-layout";
281
+ params: {
282
+ filter?: FilterPredicate | ((entity: Entity) => boolean);
283
+ loader: () => Promise<(props: EntityHeaderLayoutProps) => JSX$1.Element>;
284
+ };
285
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
286
+ optional: true;
287
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<(props: EntityHeaderLayoutProps) => JSX$1.Element, "catalog.entity-header-layout.component", {}>;
288
+ inputs: {};
289
+ config: {
290
+ filter: FilterPredicate | undefined;
291
+ };
292
+ configInput: {
293
+ filter?: FilterPredicate | undefined;
294
+ };
295
+ dataRefs: {
296
+ filterFunction: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {}>;
297
+ component: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(props: EntityHeaderLayoutProps) => JSX$1.Element, "catalog.entity-header-layout.component", {}>;
298
+ };
299
+ }>;
300
+
250
301
  /** @alpha */
251
302
  type EntityContextMenuItemParams = {
252
303
  useProps: UseProps;
253
- icon: JSX$1.Element;
304
+ icon: IconElement;
254
305
  filter?: FilterPredicate | ((entity: Entity) => boolean);
255
306
  };
256
307
  /** @alpha */
257
308
  declare const EntityContextMenuItemBlueprint: _backstage_frontend_plugin_api.ExtensionBlueprint<{
258
309
  kind: "entity-context-menu-item";
259
310
  params: EntityContextMenuItemParams;
260
- output: _backstage_frontend_plugin_api.ExtensionDataRef<JSX$1.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
311
+ output: _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
261
312
  optional: true;
262
- }>;
313
+ }> | _backstage_frontend_plugin_api.ExtensionDataRef<EntityContextMenuItemData, "catalog.entity-context-menu-item-data", {}>;
263
314
  inputs: {};
264
315
  config: {
265
316
  filter: FilterPredicate | undefined;
@@ -268,6 +319,7 @@ declare const EntityContextMenuItemBlueprint: _backstage_frontend_plugin_api.Ext
268
319
  filter?: FilterPredicate | undefined;
269
320
  };
270
321
  dataRefs: {
322
+ data: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<EntityContextMenuItemData, "catalog.entity-context-menu-item-data", {}>;
271
323
  filterFunction: _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {}>;
272
324
  };
273
325
  }>;
@@ -530,5 +582,5 @@ declare const catalogReactTranslationRef: _backstage_frontend_plugin_api.Transla
530
582
  readonly "missingAnnotationEmptyState.generateDescription_other": "The annotations {{annotations}} are missing. You need to add the annotations to your {{entityKind}} if you want to enable this tool.";
531
583
  }>;
532
584
 
533
- export { CatalogFilterBlueprint, EntityCardBlueprint, EntityContentBlueprint, EntityContentLayoutBlueprint, EntityContextMenuItemBlueprint, EntityDataTable, EntityHeaderBlueprint, EntityIconLinkBlueprint, EntityRelationCard, EntityTableColumnTitle, catalogReactTranslationRef, convertLegacyEntityCardExtension, convertLegacyEntityContentExtension, defaultEntityContentGroupDefinitions, defaultEntityContentGroups, entityColumnPresets, columnFactories as entityDataTableColumns, isOwnerOf, useEntityPermission };
534
- export type { EntityCardType, EntityColumnConfig, EntityContentGroupDefinitions, EntityContentLayoutProps, EntityContextMenuItemParams, EntityDataTableProps, EntityRelationCardProps, EntityRow, EntityTableColumnTitleProps, UseProps };
585
+ export { CatalogFilterBlueprint, EntityCardBlueprint, EntityContentBlueprint, EntityContentLayoutBlueprint, EntityContextMenuItemBlueprint, EntityDataTable, EntityHeaderBlueprint, EntityHeaderLayoutBlueprint, EntityIconLinkBlueprint, EntityRelationCard, EntityTableColumnTitle, catalogReactTranslationRef, convertLegacyEntityCardExtension, convertLegacyEntityContentExtension, defaultEntityContentGroupDefinitions, defaultEntityContentGroups, entityColumnPresets, columnFactories as entityDataTableColumns, isOwnerOf, useEntityPermission };
586
+ export type { EntityCardType, EntityColumnConfig, EntityContentGroupDefinitions, EntityContentLayoutProps, EntityContextMenuItemData, EntityContextMenuItemParams, EntityDataTableProps, EntityHeaderLayoutProps, EntityRelationCardProps, EntityRow, EntityTableColumnTitleProps, UseProps };
package/dist/alpha.esm.js CHANGED
@@ -3,6 +3,7 @@ export { EntityCardBlueprint } from './alpha/blueprints/EntityCardBlueprint.esm.
3
3
  export { EntityContentBlueprint } from './alpha/blueprints/EntityContentBlueprint.esm.js';
4
4
  export { EntityContentLayoutBlueprint } from './alpha/blueprints/EntityContentLayoutBlueprint.esm.js';
5
5
  export { EntityHeaderBlueprint } from './alpha/blueprints/EntityHeaderBlueprint.esm.js';
6
+ export { EntityHeaderLayoutBlueprint } from './alpha/blueprints/EntityHeaderLayoutBlueprint.esm.js';
6
7
  export { defaultEntityContentGroupDefinitions, defaultEntityContentGroups } from './alpha/blueprints/extensionData.esm.js';
7
8
  export { EntityContextMenuItemBlueprint } from './alpha/blueprints/EntityContextMenuItemBlueprint.esm.js';
8
9
  export { EntityIconLinkBlueprint } from './alpha/blueprints/EntityIconLinkBlueprint.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"alpha.esm.js","sources":["../src/alpha/index.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport * from './blueprints';\nexport * from './converters';\nimport { catalogReactTranslationRef as _catalogReactTranslationRef } from '../translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-catalog-react` instead.\n */\nexport const catalogReactTranslationRef = _catalogReactTranslationRef;\nexport { isOwnerOf } from '../utils/isOwnerOf';\nexport { useEntityPermission } from '../hooks/useEntityPermission';\nexport * from '../components/EntityTable/TitleColumn';\nexport * from '../components/EntityDataTable';\nexport * from '../components/EntityRelationCard';\n"],"names":["_catalogReactTranslationRef"],"mappings":";;;;;;;;;;;;;;;;;;;AAwBO,MAAM,0BAAA,GAA6BA;;;;"}
1
+ {"version":3,"file":"alpha.esm.js","sources":["../src/alpha/index.ts"],"sourcesContent":["/*\n * Copyright 2024 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nexport * from './blueprints';\nexport * from './converters';\nimport { catalogReactTranslationRef as _catalogReactTranslationRef } from '../translation';\n\n/**\n * @alpha\n * @deprecated Import from `@backstage/plugin-catalog-react` instead.\n */\nexport const catalogReactTranslationRef = _catalogReactTranslationRef;\nexport { isOwnerOf } from '../utils/isOwnerOf';\nexport { useEntityPermission } from '../hooks/useEntityPermission';\nexport * from '../components/EntityTable/TitleColumn';\nexport * from '../components/EntityDataTable';\nexport * from '../components/EntityRelationCard';\n"],"names":["_catalogReactTranslationRef"],"mappings":";;;;;;;;;;;;;;;;;;;;AAwBO,MAAM,0BAAA,GAA6BA;;;;"}
@@ -40,7 +40,7 @@ function useEntityTypeFilter() {
40
40
  }).then((response) => response.facets["spec.type"] || []);
41
41
  return items;
42
42
  }
43
- return [];
43
+ return void 0;
44
44
  }, [kind, catalogApi]);
45
45
  const facetsRef = useRef(facets);
46
46
  useEffect(() => {
@@ -1 +1 @@
1
- {"version":3,"file":"useEntityTypeFilter.esm.js","sources":["../../src/hooks/useEntityTypeFilter.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport isEqual from 'lodash/isEqual';\nimport sortBy from 'lodash/sortBy';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityList } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\n/**\n * A hook built on top of `useEntityList` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n * @public\n */\nexport function useEntityTypeFilter(): {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters: { type: typeParameter },\n updateFilters,\n } = useEntityList();\n\n const flattenedQueryTypes = useMemo(\n () => [typeParameter].flat().filter(Boolean) as string[],\n [typeParameter],\n );\n\n const [selectedTypes, setSelectedTypes] = useState(\n flattenedQueryTypes.length\n ? flattenedQueryTypes\n : typeFilter?.getTypes() ?? [],\n );\n\n // Set selected types on query parameter updates; this happens at initial page load and from\n // external updates to the page location.\n useEffect(() => {\n if (flattenedQueryTypes.length) {\n setSelectedTypes(flattenedQueryTypes);\n }\n }, [flattenedQueryTypes]);\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: facets,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntityFacets({\n filter: { kind },\n facets: ['spec.type'],\n })\n .then(response => response.facets['spec.type'] || []);\n return items;\n }\n return [];\n }, [kind, catalogApi]);\n\n const facetsRef = useRef(facets);\n useEffect(() => {\n const oldFacets = facetsRef.current;\n facetsRef.current = facets;\n // Delay processing hook until kind and facets load updates have settled to generate list of types;\n // This prevents resetting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to facets\n if (loading || !kind || oldFacets === facets || !facets) {\n return;\n }\n\n // Sort by facet count descending, so the most common types appear on top\n const newTypes = [\n ...new Set(\n sortBy(facets, f => -f.count).map(f =>\n f.value.toLocaleLowerCase('en-US'),\n ),\n ),\n ];\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n if (!isEqual(selectedTypes, stillValidTypes)) {\n setSelectedTypes(stillValidTypes);\n }\n }, [loading, kind, selectedTypes, setSelectedTypes, facets]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useEntityTypeFilter.esm.js","sources":["../../src/hooks/useEntityTypeFilter.tsx"],"sourcesContent":["/*\n * Copyright 2021 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { useEffect, useMemo, useRef, useState } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport isEqual from 'lodash/isEqual';\nimport sortBy from 'lodash/sortBy';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { catalogApiRef } from '../api';\nimport { useEntityList } from './useEntityListProvider';\nimport { EntityTypeFilter } from '../filters';\n\n/**\n * A hook built on top of `useEntityList` for enabling selection of valid `spec.type` values\n * based on the selected EntityKindFilter.\n * @public\n */\nexport function useEntityTypeFilter(): {\n loading: boolean;\n error?: Error;\n availableTypes: string[];\n selectedTypes: string[];\n setSelectedTypes: (types: string[]) => void;\n} {\n const catalogApi = useApi(catalogApiRef);\n const {\n filters: { kind: kindFilter, type: typeFilter },\n queryParameters: { type: typeParameter },\n updateFilters,\n } = useEntityList();\n\n const flattenedQueryTypes = useMemo(\n () => [typeParameter].flat().filter(Boolean) as string[],\n [typeParameter],\n );\n\n const [selectedTypes, setSelectedTypes] = useState(\n flattenedQueryTypes.length\n ? flattenedQueryTypes\n : typeFilter?.getTypes() ?? [],\n );\n\n // Set selected types on query parameter updates; this happens at initial page load and from\n // external updates to the page location.\n useEffect(() => {\n if (flattenedQueryTypes.length) {\n setSelectedTypes(flattenedQueryTypes);\n }\n }, [flattenedQueryTypes]);\n\n const [availableTypes, setAvailableTypes] = useState<string[]>([]);\n const kind = useMemo(() => kindFilter?.value, [kindFilter]);\n\n // Load all valid spec.type values straight from the catalogApi, paying attention to only the\n // kind filter for a complete list.\n const {\n error,\n loading,\n value: facets,\n } = useAsync(async () => {\n if (kind) {\n const items = await catalogApi\n .getEntityFacets({\n filter: { kind },\n facets: ['spec.type'],\n })\n .then(response => response.facets['spec.type'] || []);\n return items;\n }\n return undefined;\n }, [kind, catalogApi]);\n\n const facetsRef = useRef(facets);\n useEffect(() => {\n const oldFacets = facetsRef.current;\n facetsRef.current = facets;\n // Delay processing hook until kind and facets load updates have settled to generate list of types;\n // This prevents resetting the type filter due to saved type value from query params not matching the\n // empty set of type values while values are still being loaded; also only run this hook on changes\n // to facets\n if (loading || !kind || oldFacets === facets || !facets) {\n return;\n }\n\n // Sort by facet count descending, so the most common types appear on top\n const newTypes = [\n ...new Set(\n sortBy(facets, f => -f.count).map(f =>\n f.value.toLocaleLowerCase('en-US'),\n ),\n ),\n ];\n setAvailableTypes(newTypes);\n\n // Update type filter to only valid values when the list of available types has changed\n const stillValidTypes = selectedTypes.filter(value =>\n newTypes.includes(value),\n );\n if (!isEqual(selectedTypes, stillValidTypes)) {\n setSelectedTypes(stillValidTypes);\n }\n }, [loading, kind, selectedTypes, setSelectedTypes, facets]);\n\n useEffect(() => {\n updateFilters({\n type: selectedTypes.length\n ? new EntityTypeFilter(selectedTypes)\n : undefined,\n });\n }, [selectedTypes, updateFilters]);\n\n return {\n loading,\n error,\n availableTypes,\n selectedTypes,\n setSelectedTypes,\n };\n}\n"],"names":[],"mappings":"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"}
@@ -8,6 +8,7 @@ import { EntityCardBlueprint } from '../alpha/blueprints/EntityCardBlueprint.esm
8
8
  import { EntityContentBlueprint } from '../alpha/blueprints/EntityContentBlueprint.esm.js';
9
9
  import '../alpha/blueprints/EntityContentLayoutBlueprint.esm.js';
10
10
  import '../alpha/blueprints/EntityHeaderBlueprint.esm.js';
11
+ import '../alpha/blueprints/EntityHeaderLayoutBlueprint.esm.js';
11
12
  import '../alpha/blueprints/extensionData.esm.js';
12
13
  import '../alpha/blueprints/EntityContextMenuItemBlueprint.esm.js';
13
14
  import '../alpha/blueprints/EntityIconLinkBlueprint.esm.js';
@@ -1 +1 @@
1
- {"version":3,"file":"createTestEntityPage.esm.js","sources":["../../src/testUtils/createTestEntityPage.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment, ReactNode } from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport {\n coreExtensionData,\n createExtensionInput,\n PageBlueprint,\n ExtensionDefinition,\n useApiHolder,\n} from '@backstage/frontend-plugin-api';\nimport { Routes, Route } from 'react-router-dom';\nimport { EntityProvider } from '../hooks/useEntity';\nimport {\n EntityCardBlueprint,\n EntityContentBlueprint,\n} from '../alpha/blueprints';\nimport { entityRouteRef } from '../routes';\nimport { catalogApiRef } from '../api';\nimport { catalogApiMock } from './catalogApiMock';\nimport { TestApiProvider } from '@backstage/frontend-test-utils';\n\n/**\n * Options for creating a test entity page.\n * @public\n */\nexport interface TestEntityPageOptions {\n /**\n * The entity to provide in the EntityProvider context.\n */\n entity: Entity;\n}\n\n/**\n * Creates a test entity page extension that provides EntityProvider context\n * and accepts entity extensions (cards, content) as inputs.\n *\n * This utility simplifies testing entity extensions by eliminating the need\n * for manual EntityProvider wrapping and route configuration.\n *\n * @remarks\n *\n * The test page uses input redirects to accept both:\n * - Entity cards (normally attach to `entity-content:catalog/overview`)\n * - Entity content (normally attach to `page:catalog/entity`)\n *\n * Cards are rendered in a simple container. Content extensions follow\n * standard routing behavior, with a single content optimization that\n * renders directly without routing.\n *\n * @example\n * ```tsx\n * import { createTestEntityPage } from '@backstage/plugin-catalog-react/testUtils';\n * import { renderTestApp } from '@backstage/frontend-test-utils';\n *\n * const mockEntity = {\n * apiVersion: 'backstage.io/v1alpha1',\n * kind: 'Component',\n * metadata: { name: 'test' },\n * spec: { type: 'service' },\n * };\n *\n * // Testing an entity card\n * renderTestApp({\n * extensions: [createTestEntityPage({ entity: mockEntity }), myEntityCard],\n * });\n *\n * // Testing entity content\n * renderTestApp({\n * extensions: [createTestEntityPage({ entity: mockEntity }), myEntityContent],\n * });\n * ```\n *\n * @public\n */\nexport function createTestEntityPage(\n options: TestEntityPageOptions,\n): ExtensionDefinition {\n const { entity } = options;\n\n return PageBlueprint.makeWithOverrides({\n name: 'test-entity',\n inputs: {\n // Redirect cards from entity-content:catalog/overview\n cards: createExtensionInput(\n [\n coreExtensionData.reactElement,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityCardBlueprint.dataRefs.type.optional(),\n ],\n {\n replaces: [{ id: 'entity-content:catalog/overview', input: 'cards' }],\n },\n ),\n // Redirect contents from page:catalog/entity\n contents: createExtensionInput(\n [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef.optional(),\n EntityContentBlueprint.dataRefs.title,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityContentBlueprint.dataRefs.group.optional(),\n ],\n {\n replaces: [{ id: 'page:catalog/entity', input: 'contents' }],\n },\n ),\n },\n factory: (originalFactory, { inputs }) => {\n return originalFactory({\n path: '/',\n routeRef: entityRouteRef,\n loader: async () => {\n // Process cards with entity filtering\n const cards = inputs.cards\n .filter(card =>\n (\n card.get(EntityContentBlueprint.dataRefs.filterFunction) ??\n (() => true)\n )(entity),\n )\n .map(card => ({\n element: card.get(coreExtensionData.reactElement),\n type: card.get(EntityCardBlueprint.dataRefs.type),\n }));\n\n // Process contents with entity filtering\n const contents = inputs.contents\n .filter(content =>\n (\n content.get(EntityContentBlueprint.dataRefs.filterFunction) ??\n (() => true)\n )(entity),\n )\n .map(content => ({\n element: content.get(coreExtensionData.reactElement),\n path: content.get(coreExtensionData.routePath),\n title: content.get(EntityContentBlueprint.dataRefs.title),\n }));\n\n if (contents.length === 0 && cards.length === 0) {\n return <div data-testid=\"empty-entity-page\" />;\n }\n\n return (\n <MockEntityApiProvider entity={entity}>\n <EntityProvider entity={entity}>\n {cards.map((card, index) => (\n <Fragment key={index}>{card.element}</Fragment>\n ))}\n {contents.length === 1 && contents[0].element}\n {contents.length > 1 && (\n <Routes>\n {contents.map(content => (\n <Route\n key={content.path}\n path={\n content.path === '/'\n ? '/'\n : `${content.path.replace(/^\\//, '')}/*`\n }\n element={content.element}\n />\n ))}\n <Route path=\"*\" element={contents[0].element} />\n </Routes>\n )}\n </EntityProvider>\n </MockEntityApiProvider>\n );\n },\n });\n },\n });\n}\n\n/**\n * A mock provider that injects a catalog API with the single entity of the test entity page.\n *\n * Users can still install their own catalog API implementations and they will take precedence over the mock.\n * @internal\n */\nfunction MockEntityApiProvider({\n entity,\n children,\n}: {\n entity: Entity;\n children: ReactNode;\n}) {\n const parentHolder = useApiHolder();\n\n // If catalog API is already provided, don't override it\n if (parentHolder.get(catalogApiRef)) {\n return <>{children}</>;\n }\n\n // Provide a mock catalog API with the single entity\n const mockApi = catalogApiMock({ entities: [entity] });\n return (\n <TestApiProvider apis={[[catalogApiRef, mockApi]]}>\n {children}\n </TestApiProvider>\n );\n}\n"],"names":["Fragment"],"mappings":"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"}
1
+ {"version":3,"file":"createTestEntityPage.esm.js","sources":["../../src/testUtils/createTestEntityPage.tsx"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Fragment, ReactNode } from 'react';\nimport { Entity } from '@backstage/catalog-model';\nimport {\n coreExtensionData,\n createExtensionInput,\n PageBlueprint,\n ExtensionDefinition,\n useApiHolder,\n} from '@backstage/frontend-plugin-api';\nimport { Routes, Route } from 'react-router-dom';\nimport { EntityProvider } from '../hooks/useEntity';\nimport {\n EntityCardBlueprint,\n EntityContentBlueprint,\n} from '../alpha/blueprints';\nimport { entityRouteRef } from '../routes';\nimport { catalogApiRef } from '../api';\nimport { catalogApiMock } from './catalogApiMock';\nimport { TestApiProvider } from '@backstage/frontend-test-utils';\n\n/**\n * Options for creating a test entity page.\n * @public\n */\nexport interface TestEntityPageOptions {\n /**\n * The entity to provide in the EntityProvider context.\n */\n entity: Entity;\n}\n\n/**\n * Creates a test entity page extension that provides EntityProvider context\n * and accepts entity extensions (cards, content) as inputs.\n *\n * This utility simplifies testing entity extensions by eliminating the need\n * for manual EntityProvider wrapping and route configuration.\n *\n * @remarks\n *\n * The test page uses input redirects to accept both:\n * - Entity cards (normally attach to `entity-content:catalog/overview`)\n * - Entity content (normally attach to `page:catalog/entity`)\n *\n * Cards are rendered in a simple container. Content extensions follow\n * standard routing behavior, with a single content optimization that\n * renders directly without routing.\n *\n * @example\n * ```tsx\n * import { createTestEntityPage } from '@backstage/plugin-catalog-react/testUtils';\n * import { renderTestApp } from '@backstage/frontend-test-utils';\n *\n * const mockEntity = {\n * apiVersion: 'backstage.io/v1alpha1',\n * kind: 'Component',\n * metadata: { name: 'test' },\n * spec: { type: 'service' },\n * };\n *\n * // Testing an entity card\n * renderTestApp({\n * extensions: [createTestEntityPage({ entity: mockEntity }), myEntityCard],\n * });\n *\n * // Testing entity content\n * renderTestApp({\n * extensions: [createTestEntityPage({ entity: mockEntity }), myEntityContent],\n * });\n * ```\n *\n * @public\n */\nexport function createTestEntityPage(\n options: TestEntityPageOptions,\n): ExtensionDefinition {\n const { entity } = options;\n\n return PageBlueprint.makeWithOverrides({\n name: 'test-entity',\n inputs: {\n // Redirect cards from entity-content:catalog/overview\n cards: createExtensionInput(\n [\n coreExtensionData.reactElement,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityCardBlueprint.dataRefs.type.optional(),\n ],\n {\n replaces: [{ id: 'entity-content:catalog/overview', input: 'cards' }],\n },\n ),\n // Redirect contents from page:catalog/entity\n contents: createExtensionInput(\n [\n coreExtensionData.reactElement,\n coreExtensionData.routePath,\n coreExtensionData.routeRef.optional(),\n EntityContentBlueprint.dataRefs.title,\n EntityContentBlueprint.dataRefs.filterFunction.optional(),\n EntityContentBlueprint.dataRefs.filterExpression.optional(),\n EntityContentBlueprint.dataRefs.group.optional(),\n ],\n {\n replaces: [{ id: 'page:catalog/entity', input: 'contents' }],\n },\n ),\n },\n factory: (originalFactory, { inputs }) => {\n return originalFactory({\n path: '/',\n routeRef: entityRouteRef,\n loader: async () => {\n // Process cards with entity filtering\n const cards = inputs.cards\n .filter(card =>\n (\n card.get(EntityContentBlueprint.dataRefs.filterFunction) ??\n (() => true)\n )(entity),\n )\n .map(card => ({\n element: card.get(coreExtensionData.reactElement),\n type: card.get(EntityCardBlueprint.dataRefs.type),\n }));\n\n // Process contents with entity filtering\n const contents = inputs.contents\n .filter(content =>\n (\n content.get(EntityContentBlueprint.dataRefs.filterFunction) ??\n (() => true)\n )(entity),\n )\n .map(content => ({\n element: content.get(coreExtensionData.reactElement),\n path: content.get(coreExtensionData.routePath),\n title: content.get(EntityContentBlueprint.dataRefs.title),\n }));\n\n if (contents.length === 0 && cards.length === 0) {\n return <div data-testid=\"empty-entity-page\" />;\n }\n\n return (\n <MockEntityApiProvider entity={entity}>\n <EntityProvider entity={entity}>\n {cards.map((card, index) => (\n <Fragment key={index}>{card.element}</Fragment>\n ))}\n {contents.length === 1 && contents[0].element}\n {contents.length > 1 && (\n <Routes>\n {contents.map(content => (\n <Route\n key={content.path}\n path={\n content.path === '/'\n ? '/'\n : `${content.path.replace(/^\\//, '')}/*`\n }\n element={content.element}\n />\n ))}\n <Route path=\"*\" element={contents[0].element} />\n </Routes>\n )}\n </EntityProvider>\n </MockEntityApiProvider>\n );\n },\n });\n },\n });\n}\n\n/**\n * A mock provider that injects a catalog API with the single entity of the test entity page.\n *\n * Users can still install their own catalog API implementations and they will take precedence over the mock.\n * @internal\n */\nfunction MockEntityApiProvider({\n entity,\n children,\n}: {\n entity: Entity;\n children: ReactNode;\n}) {\n const parentHolder = useApiHolder();\n\n // If catalog API is already provided, don't override it\n if (parentHolder.get(catalogApiRef)) {\n return <>{children}</>;\n }\n\n // Provide a mock catalog API with the single entity\n const mockApi = catalogApiMock({ entities: [entity] });\n return (\n <TestApiProvider apis={[[catalogApiRef, mockApi]]}>\n {children}\n </TestApiProvider>\n );\n}\n"],"names":["Fragment"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@backstage/plugin-catalog-react",
3
- "version": "3.1.0",
3
+ "version": "3.2.0-next.1",
4
4
  "description": "A frontend library that helps other Backstage plugins interact with the catalog",
5
5
  "backstage": {
6
6
  "role": "web-library",
@@ -73,20 +73,20 @@
73
73
  "test": "backstage-cli package test"
74
74
  },
75
75
  "dependencies": {
76
- "@backstage/catalog-client": "^1.16.0",
77
- "@backstage/catalog-model": "^1.9.0",
78
- "@backstage/core-compat-api": "^0.5.12",
79
- "@backstage/core-components": "^0.18.11",
80
- "@backstage/core-plugin-api": "^1.12.7",
81
- "@backstage/errors": "^1.3.1",
82
- "@backstage/filter-predicates": "^0.1.3",
83
- "@backstage/frontend-plugin-api": "^0.17.2",
84
- "@backstage/integration-react": "^1.2.19",
85
- "@backstage/plugin-permission-common": "^0.9.9",
86
- "@backstage/plugin-permission-react": "^0.5.2",
87
- "@backstage/types": "^1.2.2",
88
- "@backstage/ui": "^0.16.0",
89
- "@backstage/version-bridge": "^1.0.12",
76
+ "@backstage/catalog-client": "1.16.1-next.0",
77
+ "@backstage/catalog-model": "1.9.0",
78
+ "@backstage/core-compat-api": "0.5.13-next.0",
79
+ "@backstage/core-components": "0.18.12-next.1",
80
+ "@backstage/core-plugin-api": "1.12.8-next.0",
81
+ "@backstage/errors": "1.3.1",
82
+ "@backstage/filter-predicates": "0.1.4-next.0",
83
+ "@backstage/frontend-plugin-api": "0.17.3-next.1",
84
+ "@backstage/integration-react": "1.2.20-next.0",
85
+ "@backstage/plugin-permission-common": "0.9.9",
86
+ "@backstage/plugin-permission-react": "0.5.3-next.0",
87
+ "@backstage/types": "1.2.2",
88
+ "@backstage/ui": "0.17.0-next.1",
89
+ "@backstage/version-bridge": "1.0.12",
90
90
  "@material-ui/core": "^4.12.2",
91
91
  "@material-ui/icons": "^4.9.1",
92
92
  "@material-ui/lab": "4.0.0-alpha.61",
@@ -102,12 +102,12 @@
102
102
  "zod": "^4.0.0"
103
103
  },
104
104
  "devDependencies": {
105
- "@backstage/cli": "^0.36.3",
106
- "@backstage/core-app-api": "^1.20.2",
107
- "@backstage/frontend-test-utils": "^0.6.1",
108
- "@backstage/plugin-catalog-common": "^1.1.10",
109
- "@backstage/plugin-scaffolder-common": "^2.2.1",
110
- "@backstage/test-utils": "^1.7.19",
105
+ "@backstage/cli": "0.36.4-next.2",
106
+ "@backstage/core-app-api": "1.20.3-next.0",
107
+ "@backstage/frontend-test-utils": "0.6.2-next.0",
108
+ "@backstage/plugin-catalog-common": "1.1.10",
109
+ "@backstage/plugin-scaffolder-common": "2.2.1",
110
+ "@backstage/test-utils": "1.7.20-next.0",
111
111
  "@testing-library/dom": "^10.0.0",
112
112
  "@testing-library/jest-dom": "^6.0.0",
113
113
  "@testing-library/react": "^16.0.0",
@@ -120,7 +120,7 @@
120
120
  "react-test-renderer": "^16.13.1"
121
121
  },
122
122
  "peerDependencies": {
123
- "@backstage/frontend-test-utils": "^0.6.1",
123
+ "@backstage/frontend-test-utils": "0.6.2-next.0",
124
124
  "@types/react": "^17.0.0 || ^18.0.0",
125
125
  "react": "^17.0.0 || ^18.0.0",
126
126
  "react-dom": "^17.0.0 || ^18.0.0",
@@ -1,18 +0,0 @@
1
- import { useVersionedContext } from '@backstage/version-bridge';
2
-
3
- function useEntityContextMenu() {
4
- const versionedHolder = useVersionedContext("entity-context-menu-context");
5
- if (!versionedHolder) {
6
- throw new Error(
7
- "useEntityContextMenu must be used within an EntityContextMenuProvider"
8
- );
9
- }
10
- const value = versionedHolder.atVersion(1);
11
- if (!value) {
12
- throw new Error("EntityContextMenu v1 is not available");
13
- }
14
- return value;
15
- }
16
-
17
- export { useEntityContextMenu };
18
- //# sourceMappingURL=useEntityContextMenu.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useEntityContextMenu.esm.js","sources":["../../src/hooks/useEntityContextMenu.ts"],"sourcesContent":["/*\n * Copyright 2025 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { useVersionedContext } from '@backstage/version-bridge';\n\n/** @internal */\nexport type EntityContextMenuContextValue = {\n onMenuClose: () => void;\n};\n\n/** @internal */\nexport function useEntityContextMenu() {\n const versionedHolder = useVersionedContext<{\n 1: EntityContextMenuContextValue;\n }>('entity-context-menu-context');\n\n if (!versionedHolder) {\n throw new Error(\n 'useEntityContextMenu must be used within an EntityContextMenuProvider',\n );\n }\n\n const value = versionedHolder.atVersion(1);\n if (!value) {\n throw new Error('EntityContextMenu v1 is not available');\n }\n\n return value;\n}\n"],"names":[],"mappings":";;AAuBO,SAAS,oBAAA,GAAuB;AACrC,EAAA,MAAM,eAAA,GAAkB,oBAErB,6BAA6B,CAAA;AAEhC,EAAA,IAAI,CAAC,eAAA,EAAiB;AACpB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,eAAA,CAAgB,SAAA,CAAU,CAAC,CAAA;AACzC,EAAA,IAAI,CAAC,KAAA,EAAO;AACV,IAAA,MAAM,IAAI,MAAM,uCAAuC,CAAA;AAAA,EACzD;AAEA,EAAA,OAAO,KAAA;AACT;;;;"}