diff --git a/.changeset/fruity-peaches-march.md b/.changeset/fruity-peaches-march.md
new file mode 100644
index 0000000000..34303d17e8
--- /dev/null
+++ b/.changeset/fruity-peaches-march.md
@@ -0,0 +1,5 @@
+---
+'@backstage/core-components': minor
+---
+
+The `IconLinkVertical` component now includes an optional hide property. When set to `true`, this property completely hides the icon element.
diff --git a/.changeset/large-baboons-prove.md b/.changeset/large-baboons-prove.md
new file mode 100644
index 0000000000..d9b964ef43
--- /dev/null
+++ b/.changeset/large-baboons-prove.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog': minor
+---
+
+Add support to customize the about card icon links via `EntityIconLinkBlueprint` and provide a default catalog view catalog source, launch scaffolder template and read techdocs docs icon links extensions.
diff --git a/.changeset/open-eyes-learn.md b/.changeset/open-eyes-learn.md
new file mode 100644
index 0000000000..60cc016bc7
--- /dev/null
+++ b/.changeset/open-eyes-learn.md
@@ -0,0 +1,71 @@
+---
+'@backstage/plugin-catalog-react': minor
+---
+
+Introduces a new `EntityIconLinkBlueprint` that customizes the `About` card icon links on the `Catalog` entity page.
+
+The blueprint currently accepts the following `params`:
+
+| Name | Description | Type | Default Value |
+| ---------- | --------------------------------------------------- | -------------------------- | ------------- |
+| `icon` | The icon to display. | `JSX.Element` | N/A |
+| `label` | The label for the element. | `string` | N/A |
+| `title` | The title for the element. | `string` | N/A |
+| `color` | The color of the element. | `'primary' \| 'secondary'` | `primary` |
+| `disabled` | Whether the element is disabled. | `boolean` | `false` |
+| `href` | The URL to navigate to when the element is clicked. | `string` | N/A |
+| `onClick` | A function to call when the element is clicked. | `() => void` | N/A |
+| `hidden` | Whether the element is hidden. | `boolean` | `false` |
+
+Here is an usage example:
+
+```tsx
+import { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha';
+//...
+
+// Defining the icon link properties using an object
+EntityIconLinkBlueprint.make({
+ name: 'my-icon-link',
+ params: {
+ props: {
+ label: 'My Icon Link Label',
+ icon: ,
+ href: '/my-plugin',
+ },
+ },
+});
+```
+
+or
+
+```tsx
+import { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha';
+//...
+
+// Defining the icon link properties using a function
+EntityIconLinkBlueprint.make({
+ name: 'my-icon-link',
+ params: {
+ props: function useMyIconLinkProps() {
+ // Use a function when you would like use a hook for defining the
+ // icon link props on runtime
+ const { t } = useTranslationRef(myIconLinkTranslationRef);
+ return {
+ label: t('myIconLink.label'),
+ icon: ,
+ href: '/my-plugin',
+ };
+ },
+ },
+});
+```
+
+Additionally, the `app-config.yaml` file allows you to override some of the default icon link parameters, including `label`, `title`, `color`, `href`, `disabled`, and `hidden` values. Here's how to set them:
+
+```yaml
+app:
+ extensions:
+ - entity-icon-link:my-plugin/my-icon-link:
+ config:
+ label: 'My Custom Icon Link label'
+```
diff --git a/packages/core-components/report.api.md b/packages/core-components/report.api.md
index 90fa9ced22..56d2662041 100644
--- a/packages/core-components/report.api.md
+++ b/packages/core-components/report.api.md
@@ -582,7 +582,8 @@ export function IconLinkVertical({
label,
onClick,
title,
-}: IconLinkVerticalProps): JSX_2.Element;
+ hidden,
+}: IconLinkVerticalProps): JSX_2.Element | null;
// @public (undocumented)
export type IconLinkVerticalClassKey =
@@ -603,6 +604,7 @@ export type IconLinkVerticalProps = {
label: string;
onClick?: MouseEventHandler;
title?: string;
+ hidden?: boolean;
};
// @public (undocumented)
diff --git a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
index a15e600e8c..817b7ad5c4 100644
--- a/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
+++ b/packages/core-components/src/components/HeaderIconLinkRow/IconLinkVertical.tsx
@@ -29,6 +29,7 @@ export type IconLinkVerticalProps = {
label: string;
onClick?: MouseEventHandler;
title?: string;
+ hidden?: boolean;
};
/** @public */
@@ -75,9 +76,14 @@ export function IconLinkVertical({
label,
onClick,
title,
+ hidden,
}: IconLinkVerticalProps) {
const classes = useIconStyles();
+ if (hidden) {
+ return null;
+ }
+
if (disabled) {
return (
diff --git a/plugins/catalog-react/report-alpha.api.md b/plugins/catalog-react/report-alpha.api.md
index 46d3030140..cdcfe99c77 100644
--- a/plugins/catalog-react/report-alpha.api.md
+++ b/plugins/catalog-react/report-alpha.api.md
@@ -9,6 +9,7 @@ import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api';
import { Entity } from '@backstage/catalog-model';
import { ExtensionBlueprint } from '@backstage/frontend-plugin-api';
import { ExtensionDefinition } from '@backstage/frontend-plugin-api';
+import { IconLinkVerticalProps } from '@backstage/core-components';
import { JsonValue } from '@backstage/types';
import { JSX as JSX_2 } from 'react';
import { ReactNode } from 'react';
@@ -390,6 +391,44 @@ export const EntityHeaderBlueprint: ExtensionBlueprint<{
};
}>;
+// @alpha (undocumented)
+export const EntityIconLinkBlueprint: ExtensionBlueprint<{
+ kind: 'entity-icon-link';
+ name: undefined;
+ params: {
+ props: IconLinkVerticalProps | (() => IconLinkVerticalProps);
+ };
+ output: ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >;
+ inputs: {};
+ config: {
+ label: string | undefined;
+ title: string | undefined;
+ color: 'primary' | 'secondary' | undefined;
+ href: string | undefined;
+ hidden: boolean | undefined;
+ disabled: boolean | undefined;
+ };
+ configInput: {
+ color?: 'primary' | 'secondary' | undefined;
+ hidden?: boolean | undefined;
+ label?: string | undefined;
+ title?: string | undefined;
+ disabled?: boolean | undefined;
+ href?: string | undefined;
+ };
+ dataRefs: {
+ props: ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >;
+ };
+}>;
+
// @alpha (undocumented)
export type EntityPredicate =
| EntityPredicateExpression
diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx
new file mode 100644
index 0000000000..01e49d5d16
--- /dev/null
+++ b/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx
@@ -0,0 +1,71 @@
+/*
+ * 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 { IconLinkVerticalProps } from '@backstage/core-components';
+import {
+ createExtensionBlueprint,
+ createExtensionDataRef,
+} from '@backstage/frontend-plugin-api';
+
+const entityIconLinkPropsDataRef = createExtensionDataRef<
+ () => IconLinkVerticalProps
+>().with({
+ id: 'entity-icon-link-props',
+});
+
+/** @alpha */
+export const EntityIconLinkBlueprint = createExtensionBlueprint({
+ kind: 'entity-icon-link',
+ attachTo: { id: 'entity-card:catalog/about', input: 'iconLinks' },
+ output: [entityIconLinkPropsDataRef],
+ dataRefs: {
+ props: entityIconLinkPropsDataRef,
+ },
+ config: {
+ schema: {
+ label: z => z.string().optional(),
+ title: z => z.string().optional(),
+ color: z => z.enum(['primary', 'secondary']).optional(),
+ href: z => z.string().optional(),
+ hidden: z => z.boolean().optional(),
+ disabled: z => z.boolean().optional(),
+ },
+ },
+ *factory(
+ params: {
+ props: IconLinkVerticalProps | (() => IconLinkVerticalProps);
+ },
+ { config },
+ ) {
+ yield entityIconLinkPropsDataRef(() => ({
+ ...(typeof params.props === 'function' ? params.props() : params.props),
+ ...Object.entries(config).reduce((rest, [key, value]) => {
+ // Only include properties that are defined in the config
+ // to avoid overriding defaults with undefined values
+ if (value !== undefined) {
+ return {
+ ...rest,
+ [key]: value,
+ // Removing the "onClick" handler if href is provided prevents the handler
+ // from redirecting to a different page
+ ...(key === 'href' ? { onClick: undefined } : {}),
+ };
+ }
+ return rest;
+ }, {}),
+ }));
+ },
+});
diff --git a/plugins/catalog-react/src/alpha/blueprints/index.ts b/plugins/catalog-react/src/alpha/blueprints/index.ts
index b21522331d..640ebb881c 100644
--- a/plugins/catalog-react/src/alpha/blueprints/index.ts
+++ b/plugins/catalog-react/src/alpha/blueprints/index.ts
@@ -28,3 +28,4 @@ export {
type EntityContextMenuItemParams,
type UseProps,
} from './EntityContextMenuItemBlueprint';
+export { EntityIconLinkBlueprint } from './EntityIconLinkBlueprint';
diff --git a/plugins/catalog/report-alpha.api.md b/plugins/catalog/report-alpha.api.md
index 7ef20d8228..ca501cb107 100644
--- a/plugins/catalog/report-alpha.api.md
+++ b/plugins/catalog/report-alpha.api.md
@@ -18,6 +18,7 @@ import { ExtensionInput } from '@backstage/frontend-plugin-api';
import { ExternalRouteRef } from '@backstage/frontend-plugin-api';
import { FrontendPlugin } from '@backstage/frontend-plugin-api';
import { IconComponent } from '@backstage/core-plugin-api';
+import { IconLinkVerticalProps } from '@backstage/core-components';
import { JSX as JSX_2 } from 'react';
import { RouteRef } from '@backstage/frontend-plugin-api';
import { SearchResultItemExtensionComponent } from '@backstage/plugin-search-react/alpha';
@@ -336,8 +337,6 @@ const _default: FrontendPlugin<
};
}>;
'entity-card:catalog/about': ExtensionDefinition<{
- kind: 'entity-card';
- name: 'about';
config: {
filter: EntityPredicate | undefined;
type: 'content' | 'summary' | 'info' | undefined;
@@ -369,7 +368,21 @@ const _default: FrontendPlugin<
optional: true;
}
>;
- inputs: {};
+ inputs: {
+ iconLinks: ExtensionInput<
+ ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >,
+ {
+ singleton: false;
+ optional: false;
+ }
+ >;
+ };
+ kind: 'entity-card';
+ name: 'about';
params: {
loader: () => Promise;
filter?: string | EntityPredicate | ((entity: Entity) => boolean);
@@ -923,6 +936,93 @@ const _default: FrontendPlugin<
inputs: {};
params: EntityContextMenuItemParams;
}>;
+ 'entity-icon-link:catalog/catalog-view-source': ExtensionDefinition<{
+ kind: 'entity-icon-link';
+ name: 'catalog-view-source';
+ config: {
+ label: string | undefined;
+ title: string | undefined;
+ color: 'primary' | 'secondary' | undefined;
+ href: string | undefined;
+ hidden: boolean | undefined;
+ disabled: boolean | undefined;
+ };
+ configInput: {
+ color?: 'primary' | 'secondary' | undefined;
+ hidden?: boolean | undefined;
+ label?: string | undefined;
+ title?: string | undefined;
+ disabled?: boolean | undefined;
+ href?: string | undefined;
+ };
+ output: ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >;
+ inputs: {};
+ params: {
+ props: IconLinkVerticalProps | (() => IconLinkVerticalProps);
+ };
+ }>;
+ 'entity-icon-link:catalog/scaffolder-launch-template': ExtensionDefinition<{
+ kind: 'entity-icon-link';
+ name: 'scaffolder-launch-template';
+ config: {
+ label: string | undefined;
+ title: string | undefined;
+ color: 'primary' | 'secondary' | undefined;
+ href: string | undefined;
+ hidden: boolean | undefined;
+ disabled: boolean | undefined;
+ };
+ configInput: {
+ color?: 'primary' | 'secondary' | undefined;
+ hidden?: boolean | undefined;
+ label?: string | undefined;
+ title?: string | undefined;
+ disabled?: boolean | undefined;
+ href?: string | undefined;
+ };
+ output: ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >;
+ inputs: {};
+ params: {
+ props: IconLinkVerticalProps | (() => IconLinkVerticalProps);
+ };
+ }>;
+ 'entity-icon-link:catalog/techdocs-view-documentation': ExtensionDefinition<{
+ kind: 'entity-icon-link';
+ name: 'techdocs-view-documentation';
+ config: {
+ label: string | undefined;
+ title: string | undefined;
+ color: 'primary' | 'secondary' | undefined;
+ href: string | undefined;
+ hidden: boolean | undefined;
+ disabled: boolean | undefined;
+ };
+ configInput: {
+ color?: 'primary' | 'secondary' | undefined;
+ hidden?: boolean | undefined;
+ label?: string | undefined;
+ title?: string | undefined;
+ disabled?: boolean | undefined;
+ href?: string | undefined;
+ };
+ output: ConfigurableExtensionDataRef<
+ () => IconLinkVerticalProps,
+ 'entity-icon-link-props',
+ {}
+ >;
+ inputs: {};
+ params: {
+ props: IconLinkVerticalProps | (() => IconLinkVerticalProps);
+ };
+ }>;
'nav-item:catalog': ExtensionDefinition<{
kind: 'nav-item';
name: undefined;
diff --git a/plugins/catalog/report.api.md b/plugins/catalog/report.api.md
index 10e120d208..556b540b92 100644
--- a/plugins/catalog/report.api.md
+++ b/plugins/catalog/report.api.md
@@ -42,6 +42,8 @@ import { UserListFilterKind } from '@backstage/plugin-catalog-react';
// @public
export interface AboutCardProps {
+ // (undocumented)
+ subheader?: JSX.Element;
// (undocumented)
variant?: InfoCardVariants;
}
diff --git a/plugins/catalog/src/alpha/entityCards.tsx b/plugins/catalog/src/alpha/entityCards.tsx
index 8662518d30..775959bef0 100644
--- a/plugins/catalog/src/alpha/entityCards.tsx
+++ b/plugins/catalog/src/alpha/entityCards.tsx
@@ -14,17 +14,37 @@
* limitations under the License.
*/
-import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha';
+import {
+ EntityIconLinkBlueprint,
+ EntityCardBlueprint,
+} from '@backstage/plugin-catalog-react/alpha';
import { compatWrapper } from '@backstage/core-compat-api';
+import { createExtensionInput } from '@backstage/frontend-plugin-api';
+import { HeaderIconLinkRow } from '@backstage/core-components';
-export const catalogAboutEntityCard = EntityCardBlueprint.make({
+export const catalogAboutEntityCard = EntityCardBlueprint.makeWithOverrides({
name: 'about',
- params: {
- type: 'info',
- loader: async () =>
- import('../components/AboutCard').then(m =>
- compatWrapper(),
- ),
+ inputs: {
+ iconLinks: createExtensionInput([EntityIconLinkBlueprint.dataRefs.props]),
+ },
+ factory(originalFactory, { inputs }) {
+ function Subheader() {
+ // The props input functions may be calling other hooks, so we need to
+ // call them in a component function to avoid breaking the rules of hooks.
+ const links = inputs.iconLinks.map(iconLink =>
+ iconLink.get(EntityIconLinkBlueprint.dataRefs.props)(),
+ );
+ return ;
+ }
+ return originalFactory({
+ type: 'info',
+ async loader() {
+ const { AboutCard } = await import('../components/AboutCard');
+ return compatWrapper(
+ } />,
+ );
+ },
+ });
},
});
diff --git a/plugins/catalog/src/alpha/entityIconLinks.tsx b/plugins/catalog/src/alpha/entityIconLinks.tsx
new file mode 100644
index 0000000000..21595ad258
--- /dev/null
+++ b/plugins/catalog/src/alpha/entityIconLinks.tsx
@@ -0,0 +1,49 @@
+/*
+ * 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 { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha';
+import {
+ useCatalogViewSourceEntityIconLinkProps,
+ useScaffolderLaunchTemplateEntityIconLinkProps,
+ useTechdocsViewDocumentionIconLinkProps,
+} from '../components/AboutCard/AboutCard';
+
+const catalogViewSourceEntityIconLink = EntityIconLinkBlueprint.make({
+ name: 'catalog-view-source',
+ params: {
+ props: useCatalogViewSourceEntityIconLinkProps,
+ },
+});
+
+const techdocsViewDocumentationAboutEntityLink = EntityIconLinkBlueprint.make({
+ name: 'techdocs-view-documentation',
+ params: {
+ props: useTechdocsViewDocumentionIconLinkProps,
+ },
+});
+
+const scaffolderLaunchTemplateEntityIconLink = EntityIconLinkBlueprint.make({
+ name: 'scaffolder-launch-template',
+ params: {
+ props: useScaffolderLaunchTemplateEntityIconLinkProps,
+ },
+});
+
+export default [
+ catalogViewSourceEntityIconLink,
+ techdocsViewDocumentationAboutEntityLink,
+ scaffolderLaunchTemplateEntityIconLink,
+];
diff --git a/plugins/catalog/src/alpha/plugin.tsx b/plugins/catalog/src/alpha/plugin.tsx
index c5dad31cf0..ff26d20003 100644
--- a/plugins/catalog/src/alpha/plugin.tsx
+++ b/plugins/catalog/src/alpha/plugin.tsx
@@ -33,6 +33,7 @@ import filters from './filters';
import navItems from './navItems';
import entityCards from './entityCards';
import entityContents from './entityContents';
+import entityIconLinks from './entityIconLinks';
import searchResultItems from './searchResultItems';
import contextMenuItems from './contextMenuItems';
@@ -57,6 +58,7 @@ export default createFrontendPlugin({
...navItems,
...entityCards,
...entityContents,
+ ...entityIconLinks,
...contextMenuItems,
...searchResultItems,
],
diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
index 88933b2a66..1c093a049c 100644
--- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx
+++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx
@@ -28,7 +28,6 @@ import { makeStyles } from '@material-ui/core/styles';
import {
AppIcon,
HeaderIconLinkRow,
- IconLinkVerticalProps,
InfoCardVariants,
Link,
} from '@backstage/core-components';
@@ -70,6 +69,63 @@ import {
TECHDOCS_EXTERNAL_ANNOTATION,
} from '@backstage/plugin-techdocs-common';
+export function useCatalogViewSourceEntityIconLinkProps() {
+ const { entity } = useEntity();
+ const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
+ const { t } = useTranslationRef(catalogTranslationRef);
+ const entitySourceLocation = getEntitySourceLocation(
+ entity,
+ scmIntegrationsApi,
+ );
+ return {
+ label: t('aboutCard.viewSource'),
+ disabled: !entitySourceLocation,
+ icon: ,
+ href: entitySourceLocation?.locationTargetUrl,
+ };
+}
+
+export function useTechdocsViewDocumentionIconLinkProps() {
+ const { entity } = useEntity();
+ const viewTechdocLink = useRouteRef(viewTechDocRouteRef);
+ const { t } = useTranslationRef(catalogTranslationRef);
+
+ return {
+ label: t('aboutCard.viewTechdocs'),
+ disabled:
+ !(
+ entity.metadata.annotations?.[TECHDOCS_ANNOTATION] ||
+ entity.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION]
+ ) || !viewTechdocLink,
+ icon: ,
+ href: buildTechDocsURL(entity, viewTechdocLink),
+ };
+}
+
+export function useScaffolderLaunchTemplateEntityIconLinkProps() {
+ const app = useApp();
+ const { entity } = useEntity();
+ const templateRoute = useRouteRef(createFromTemplateRouteRef);
+ const { t } = useTranslationRef(catalogTranslationRef);
+ const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon;
+ const { allowed: canCreateTemplateTask } = usePermission({
+ permission: taskCreatePermission,
+ });
+
+ return {
+ label: t('aboutCard.launchTemplate'),
+ icon: ,
+ hidden: !isTemplateEntityV1beta3(entity),
+ disabled: !templateRoute || !canCreateTemplateTask,
+ href:
+ templateRoute &&
+ templateRoute({
+ templateName: entity.metadata.name,
+ namespace: entity.metadata.namespace || DEFAULT_NAMESPACE,
+ }),
+ };
+}
+
const useStyles = makeStyles({
gridItemCard: {
display: 'flex',
@@ -97,6 +153,7 @@ const useStyles = makeStyles({
*/
export interface AboutCardProps {
variant?: InfoCardVariants;
+ subheader?: JSX.Element;
}
/**
@@ -107,15 +164,12 @@ export interface AboutCardProps {
* card in your own repository instead, that is perfect for your own needs.
*/
export function AboutCard(props: AboutCardProps) {
- const { variant } = props;
- const app = useApp();
+ const { variant, subheader } = props;
const classes = useStyles();
const { entity } = useEntity();
- const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
const catalogApi = useApi(catalogApiRef);
const alertApi = useApi(alertApiRef);
const errorApi = useApi(errorApiRef);
- const viewTechdocLink = useRouteRef(viewTechDocRouteRef);
const templateRoute = useRouteRef(createFromTemplateRouteRef);
const sourceTemplateRef = useSourceTemplateCompoundEntityRef(entity);
const { allowed: canRefresh } = useEntityPermission(
@@ -123,53 +177,14 @@ export function AboutCard(props: AboutCardProps) {
);
const { t } = useTranslationRef(catalogTranslationRef);
- const { allowed: canCreateTemplateTask } = usePermission({
- permission: taskCreatePermission,
- });
-
- const entitySourceLocation = getEntitySourceLocation(
- entity,
- scmIntegrationsApi,
- );
const entityMetadataEditUrl =
entity.metadata.annotations?.[ANNOTATION_EDIT_URL];
- const viewInSource: IconLinkVerticalProps = {
- label: t('aboutCard.viewSource'),
- disabled: !entitySourceLocation,
- icon: ,
- href: entitySourceLocation?.locationTargetUrl,
- };
- const viewInTechDocs: IconLinkVerticalProps = {
- label: t('aboutCard.viewTechdocs'),
- disabled:
- !(
- entity.metadata.annotations?.[TECHDOCS_ANNOTATION] ||
- entity.metadata.annotations?.[TECHDOCS_EXTERNAL_ANNOTATION]
- ) || !viewTechdocLink,
- icon: ,
- href: buildTechDocsURL(entity, viewTechdocLink),
- };
-
- const subHeaderLinks = [viewInSource, viewInTechDocs];
-
- if (isTemplateEntityV1beta3(entity)) {
- const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon;
-
- const launchTemplate: IconLinkVerticalProps = {
- label: t('aboutCard.launchTemplate'),
- icon: ,
- disabled: !templateRoute || !canCreateTemplateTask,
- href:
- templateRoute &&
- templateRoute({
- templateName: entity.metadata.name,
- namespace: entity.metadata.namespace || DEFAULT_NAMESPACE,
- }),
- };
-
- subHeaderLinks.push(launchTemplate);
- }
+ const viewCatalogSourceIconLink = useCatalogViewSourceEntityIconLinkProps();
+ const viewTechdocsDocumentationIconLink =
+ useTechdocsViewDocumentionIconLinkProps();
+ const launchScaffolderTemplateIconLink =
+ useScaffolderLaunchTemplateEntityIconLinkProps();
let cardClass = '';
if (variant === 'gridItem') {
@@ -240,7 +255,17 @@ export function AboutCard(props: AboutCardProps) {
)}
>
}
- subheader={}
+ subheader={
+ subheader ?? (
+
+ )
+ }
/>