feat: introduce subdomains card

Signed-off-by: Jano González <janog@spotify.com>
This commit is contained in:
Jano González
2024-08-02 15:58:50 +02:00
parent 8c1aa06bcc
commit 7e1dfe7bd6
7 changed files with 224 additions and 0 deletions
@@ -84,6 +84,14 @@ export const catalogHasSubcomponentsEntityCard = createEntityCardExtension({
),
});
export const catalogHasSubdomainsEntityCard = createEntityCardExtension({
name: 'has-subdomains',
loader: async () =>
import('../components/HasSubdomainsCard').then(m =>
compatWrapper(<m.HasSubdomainsCard variant="gridItem" />),
),
});
export const catalogHasSystemsEntityCard = createEntityCardExtension({
name: 'has-systems',
loader: async () =>
@@ -101,5 +109,6 @@ export default [
catalogHasComponentsEntityCard,
catalogHasResourcesEntityCard,
catalogHasSubcomponentsEntityCard,
catalogHasSubdomainsEntityCard,
catalogHasSystemsEntityCard,
];
@@ -0,0 +1,122 @@
/*
* 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 { Entity, RELATION_HAS_PART } from '@backstage/catalog-model';
import {
CatalogApi,
catalogApiRef,
EntityProvider,
entityRouteRef,
} from '@backstage/plugin-catalog-react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { waitFor, screen } from '@testing-library/react';
import React from 'react';
import { HasSubdomainsCard } from './HasSubdomainsCard';
describe('<HasSubdomainsCard />', () => {
const getEntitiesByRefs: jest.MockedFunction<
CatalogApi['getEntitiesByRefs']
> = jest.fn();
let Wrapper: React.ComponentType<React.PropsWithChildren<{}>>;
beforeEach(() => {
Wrapper = ({ children }: { children?: React.ReactNode }) => (
<TestApiProvider apis={[[catalogApiRef, { getEntitiesByRefs }]]}>
{children}
</TestApiProvider>
);
});
afterEach(() => jest.resetAllMocks());
it('shows empty list if no relations', async () => {
const entity: Entity = {
apiVersion: 'v1',
kind: 'Domain',
metadata: {
name: 'my-domain',
namespace: 'my-namespace',
},
relations: [],
};
await renderInTestApp(
<Wrapper>
<EntityProvider entity={entity}>
<HasSubdomainsCard />
</EntityProvider>
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
expect(screen.getByText('Has subdomains')).toBeInTheDocument();
expect(
screen.getByText(/No subdomain is part of this domain/i),
).toBeInTheDocument();
});
it('shows related subdomains', async () => {
const entity: Entity = {
apiVersion: 'v1',
kind: 'Domain',
metadata: {
name: 'my-domain',
namespace: 'my-namespace',
},
relations: [
{
targetRef: 'domain:my-namespace/target-name',
type: RELATION_HAS_PART,
},
],
};
getEntitiesByRefs.mockResolvedValue({
items: [
{
apiVersion: 'v1',
kind: 'Domain',
metadata: {
name: 'target-name',
namespace: 'my-namespace',
},
spec: {},
},
],
});
await renderInTestApp(
<Wrapper>
<EntityProvider entity={entity}>
<HasSubdomainsCard />
</EntityProvider>
</Wrapper>,
{
mountedRoutes: {
'/catalog/:namespace/:kind/:name': entityRouteRef,
},
},
);
await waitFor(() => {
expect(screen.getByText('Has subdomains')).toBeInTheDocument();
expect(screen.getByText(/target-name/i)).toBeInTheDocument();
});
});
});
@@ -0,0 +1,55 @@
/*
* 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 { RELATION_HAS_PART } from '@backstage/catalog-model';
import { InfoCardVariants, TableOptions } from '@backstage/core-components';
import React from 'react';
import {
asComponentEntities,
componentEntityColumns,
RelatedEntitiesCard,
} from '../RelatedEntitiesCard';
import { catalogTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
/** @public */
export interface HasSubdomainsCardProps {
variant?: InfoCardVariants;
tableOptions?: TableOptions;
title?: string;
}
export function HasSubdomainsCard(props: HasSubdomainsCardProps) {
const { t } = useTranslationRef(catalogTranslationRef);
const {
variant = 'gridItem',
tableOptions = {},
title = t('hasSubdomainsCard.title'),
} = props;
return (
<RelatedEntitiesCard
variant={variant}
title={title}
entityKind="Domain"
relationType={RELATION_HAS_PART}
columns={componentEntityColumns}
asRenderableEntities={asComponentEntities}
emptyMessage={t('hasSubdomainsCard.emptyMessage')}
emptyHelpLink="https://backstage.io/docs/features/software-catalog/descriptor-format/#specsubdomainof-optional"
tableOptions={tableOptions}
/>
);
}
@@ -0,0 +1,18 @@
/*
* 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.
*/
export { HasSubdomainsCard } from './HasSubdomainsCard';
export type { HasSubdomainsCardProps } from './HasSubdomainsCard';
+2
View File
@@ -49,6 +49,7 @@ export {
EntityHasComponentsCard,
EntityHasResourcesCard,
EntityHasSubcomponentsCard,
EntityHasSubdomainsCard,
EntityHasSystemsCard,
EntityLinksCard,
EntityLabelsCard,
@@ -71,6 +72,7 @@ export type { EntityContextMenuClassKey } from './components/EntityContextMenu';
export type { HasComponentsCardProps } from './components/HasComponentsCard';
export type { HasResourcesCardProps } from './components/HasResourcesCard';
export type { HasSubcomponentsCardProps } from './components/HasSubcomponentsCard';
export type { HasSubdomainsCardProps } from './components/HasSubdomainsCard';
export type { HasSystemsCardProps } from './components/HasSystemsCard';
export type { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard';
export type { CatalogSearchResultListItemProps } from './components/CatalogSearchResultListItem';
+14
View File
@@ -50,6 +50,7 @@ import { DependsOnResourcesCardProps } from './components/DependsOnResourcesCard
import { HasComponentsCardProps } from './components/HasComponentsCard';
import { HasResourcesCardProps } from './components/HasResourcesCard';
import { HasSubcomponentsCardProps } from './components/HasSubcomponentsCard';
import { HasSubdomainsCardProps } from './components/HasSubdomainsCard';
import { HasSystemsCardProps } from './components/HasSystemsCard';
import { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard';
import { CatalogSearchResultListItemProps } from './components/CatalogSearchResultListItem';
@@ -199,6 +200,19 @@ export const EntityHasSubcomponentsCard: (
}),
);
/** @public */
export const EntityHasSubdomainsCard: (
props: HasSubdomainsCardProps,
) => JSX.Element = catalogPlugin.provide(
createComponentExtension({
name: 'EntityHasSubdomainsCard',
component: {
lazy: () =>
import('./components/HasSubdomainsCard').then(m => m.HasSubdomainsCard),
},
}),
);
/** @public */
export const EntityHasResourcesCard: (
props: HasResourcesCardProps,
+4
View File
@@ -143,6 +143,10 @@ export const catalogTranslationRef = createTranslationRef({
title: 'Has subcomponents',
emptyMessage: 'No subcomponent is part of this component',
},
hasSubdomainsCard: {
title: 'Has subdomains',
emptyMessage: 'No subdomain is part of this domain',
},
hasSystemsCard: {
title: 'Has systems',
emptyMessage: 'No system is part of this domain',