diff --git a/.changeset/many-books-refuse.md b/.changeset/many-books-refuse.md
new file mode 100644
index 0000000000..8455254439
--- /dev/null
+++ b/.changeset/many-books-refuse.md
@@ -0,0 +1,5 @@
+---
+'@backstage/plugin-catalog': patch
+---
+
+Add `EntityLabelsCard` to show the labels for an entity.
diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx
index 02b00f038c..3566800f0b 100644
--- a/packages/app/src/components/catalog/EntityPage.tsx
+++ b/packages/app/src/components/catalog/EntityPage.tsx
@@ -52,6 +52,7 @@ import {
EntityHasSystemsCard,
EntityLayout,
EntityLinksCard,
+ EntityLabelsCard,
EntityOrphanWarning,
EntityProcessingErrorsPanel,
EntitySwitch,
@@ -59,6 +60,7 @@ import {
isComponentType,
isKind,
isOrphan,
+ hasLabels,
} from '@internal/plugin-catalog-customized';
import {
Direction,
@@ -364,6 +366,14 @@ const overviewContent = (
+
+
+
+
+
+
+
+
{cicdCard}
diff --git a/packages/catalog-model/examples/components/shuffle-api-component.yaml b/packages/catalog-model/examples/components/shuffle-api-component.yaml
index 6328ebdf3b..afac3d472d 100644
--- a/packages/catalog-model/examples/components/shuffle-api-component.yaml
+++ b/packages/catalog-model/examples/components/shuffle-api-component.yaml
@@ -3,6 +3,9 @@ kind: Component
metadata:
name: shuffle-api
description: Shuffle API
+ labels:
+ goVersion: go1.15.3
+ category: music
tags:
- go
spec:
diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md
index 74f64882a8..82df851239 100644
--- a/plugins/catalog/api-report.md
+++ b/plugins/catalog/api-report.md
@@ -277,6 +277,17 @@ export const EntityHasSubcomponentsCard: (
// @public (undocumented)
export const EntityHasSystemsCard: (props: HasSystemsCardProps) => JSX.Element;
+// @public (undocumented)
+export const EntityLabelsCard: (props: EntityLabelsCardProps) => JSX.Element;
+
+// @public (undocumented)
+export interface EntityLabelsCardProps {
+ // (undocumented)
+ title?: string;
+ // (undocumented)
+ variant?: InfoCardVariants;
+}
+
// @public
export const EntityLayout: {
(props: EntityLayoutProps): JSX.Element;
@@ -385,6 +396,9 @@ export interface HasComponentsCardProps {
variant?: InfoCardVariants;
}
+// @public
+export function hasLabels(entity: Entity): boolean;
+
// @public (undocumented)
export interface HasResourcesCardProps {
// (undocumented)
diff --git a/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsCard.tsx b/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsCard.tsx
new file mode 100644
index 0000000000..500c651cc0
--- /dev/null
+++ b/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsCard.tsx
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2022 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 { useEntity } from '@backstage/plugin-catalog-react';
+import React from 'react';
+import {
+ InfoCard,
+ InfoCardVariants,
+ Table,
+ TableColumn,
+} from '@backstage/core-components';
+import { EntityLabelsEmptyState } from './EntityLabelsEmptyState';
+import { makeStyles, Typography } from '@material-ui/core';
+
+/** @public */
+export interface EntityLabelsCardProps {
+ variant?: InfoCardVariants;
+ title?: string;
+}
+
+const useStyles = makeStyles(_ => ({
+ key: {
+ fontWeight: 'bold',
+ },
+}));
+
+export const EntityLabelsCard = (props: EntityLabelsCardProps) => {
+ const { variant, title } = props;
+ const { entity } = useEntity();
+ const classes = useStyles();
+
+ const columns: TableColumn<{ key: string; value: string }>[] = [
+ {
+ render: row => {
+ return (
+
+ {row.key}
+
+ );
+ },
+ },
+ {
+ field: 'value',
+ },
+ ];
+
+ const labels = entity?.metadata?.labels;
+
+ return (
+
+ {!labels || Object.keys(labels).length === 0 ? (
+
+ ) : (
+
({
+ key: labelKey,
+ value: labels[labelKey],
+ }))}
+ options={{
+ search: false,
+ showTitle: true,
+ loadingType: 'linear',
+ header: false,
+ padding: 'dense',
+ pageSize: 5,
+ toolbar: false,
+ paging: Object.keys(labels).length > 5,
+ }}
+ />
+ )}
+
+ );
+};
diff --git a/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsEmptyState.tsx b/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsEmptyState.tsx
new file mode 100644
index 0000000000..a875f542a7
--- /dev/null
+++ b/plugins/catalog/src/components/EntityLabelsCard/EntityLabelsEmptyState.tsx
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2020 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 { BackstageTheme } from '@backstage/theme';
+import { Button, makeStyles, Typography } from '@material-ui/core';
+import React from 'react';
+import { CodeSnippet } from '@backstage/core-components';
+
+const ENTITY_YAML = `metadata:
+ name: example
+ labels:
+ javaVersion: 1.2.3`;
+
+const useStyles = makeStyles(
+ theme => ({
+ code: {
+ borderRadius: 6,
+ margin: `${theme.spacing(2)}px 0px`,
+ background: theme.palette.type === 'dark' ? '#444' : '#fff',
+ },
+ }),
+ { name: 'PluginCatalogEntityLabelsEmptyState' },
+);
+
+export function EntityLabelsEmptyState() {
+ const classes = useStyles();
+
+ return (
+ <>
+
+ No labels defined for this entity. You can add labels to your entity
+ YAML as shown in the highlighted example below:
+
+
+
+
+
+ >
+ );
+}
diff --git a/plugins/catalog/src/components/EntityLabelsCard/conditions.ts b/plugins/catalog/src/components/EntityLabelsCard/conditions.ts
new file mode 100644
index 0000000000..fb4678cded
--- /dev/null
+++ b/plugins/catalog/src/components/EntityLabelsCard/conditions.ts
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2023 The Backstage Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Entity } from '@backstage/catalog-model';
+
+/**
+ * Returns true if the given entity has labels annotation given by the
+ * catalog. For use by EntitySwitch
+ *
+ * @public
+ */
+export function hasLabels(entity: Entity) {
+ return entity?.metadata?.labels
+ ? Object.keys(entity?.metadata?.labels).some(Boolean)
+ : false;
+}
diff --git a/plugins/catalog/src/components/EntityLabelsCard/index.ts b/plugins/catalog/src/components/EntityLabelsCard/index.ts
new file mode 100644
index 0000000000..870819f9a7
--- /dev/null
+++ b/plugins/catalog/src/components/EntityLabelsCard/index.ts
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2022 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.
+ */
+
+export { EntityLabelsCard } from './EntityLabelsCard';
+export * from './conditions';
+export type { EntityLabelsCardProps } from './EntityLabelsCard';
diff --git a/plugins/catalog/src/index.ts b/plugins/catalog/src/index.ts
index 886039e329..7de332cf51 100644
--- a/plugins/catalog/src/index.ts
+++ b/plugins/catalog/src/index.ts
@@ -37,6 +37,7 @@ export * from './components/EntityProcessingErrorsPanel';
export * from './components/EntitySwitch';
export * from './components/FilteredEntityLayout';
export * from './overridableComponents';
+export * from './components/EntityLabelsCard';
export {
CatalogEntityPage,
CatalogIndexPage,
@@ -50,6 +51,7 @@ export {
EntityHasSubcomponentsCard,
EntityHasSystemsCard,
EntityLinksCard,
+ EntityLabelsCard,
RelatedEntitiesCard,
} from './plugin';
diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts
index 5419854cb6..c83657b85a 100644
--- a/plugins/catalog/src/plugin.ts
+++ b/plugins/catalog/src/plugin.ts
@@ -126,6 +126,17 @@ export const EntityLinksCard = catalogPlugin.provide(
}),
);
+/** @public */
+export const EntityLabelsCard = catalogPlugin.provide(
+ createComponentExtension({
+ name: 'EntityLabelsCard',
+ component: {
+ lazy: () =>
+ import('./components/EntityLabelsCard').then(m => m.EntityLabelsCard),
+ },
+ }),
+);
+
/** @public */
export const EntityHasSystemsCard: (props: HasSystemsCardProps) => JSX.Element =
catalogPlugin.provide(