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(