From 5a128f3f406dbfe9080069149ef284202257d082 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Wed, 10 Jun 2020 10:25:22 +0200 Subject: [PATCH] chore(catalog): clean up ComponentPage, use only Entity --- .../ComponentMetadataCard.test.tsx | 17 +-- .../ComponentMetadataCard.tsx | 29 ++-- .../ComponentPage/ComponentPage.test.tsx | 4 +- .../ComponentPage/ComponentPage.tsx | 128 +++++++++++------- .../ComponentRemovalDialog.tsx | 21 ++- 5 files changed, 105 insertions(+), 94 deletions(-) diff --git a/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.test.tsx b/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.test.tsx index aaedf1bcba..ef12e52363 100644 --- a/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.test.tsx +++ b/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.test.tsx @@ -13,28 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; +import { render } from '@testing-library/react'; import React from 'react'; import { ComponentMetadataCard } from './ComponentMetadataCard'; -import { Component } from '../../data/component'; -import { render } from '@testing-library/react'; describe('ComponentMetadataCard component', () => { it('should display component name if provided', async () => { - const testComponent: Component = { - name: 'test', + const testEntity: Entity = { + apiVersion: 'backstage.io/v1beta1', kind: 'Component', metadata: { name: 'test' }, - description: 'Placeholder', }; const rendered = await render( - , + , ); expect(await rendered.findByText('test')).toBeInTheDocument(); }); - it('should display loader when loading is set to true', async () => { - const rendered = await render( - , - ); - expect(await rendered.findByRole('progressbar')).toBeInTheDocument(); - }); }); diff --git a/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.tsx b/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.tsx index d765b9cc0b..60c9284121 100644 --- a/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.tsx +++ b/plugins/catalog/src/components/ComponentMetadataCard/ComponentMetadataCard.tsx @@ -13,29 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard, Progress, StructuredMetadataTable } from '@backstage/core'; +import { Entity } from '@backstage/catalog-model'; +import { InfoCard, StructuredMetadataTable } from '@backstage/core'; import React, { FC } from 'react'; -import { Component } from '../../data/component'; type Props = { - loading: boolean; - component: Component | undefined; + entity: Entity; }; -export const ComponentMetadataCard: FC = ({ loading, component }) => { - if (loading) { - return ( - - - - ); - } - if (!component) { - return null; - } - return ( - - - - ); -}; +export const ComponentMetadataCard: FC = ({ entity }) => ( + + + +); diff --git a/plugins/catalog/src/components/ComponentPage/ComponentPage.test.tsx b/plugins/catalog/src/components/ComponentPage/ComponentPage.test.tsx index e0d5c99e4d..75393bd10e 100644 --- a/plugins/catalog/src/components/ComponentPage/ComponentPage.test.tsx +++ b/plugins/catalog/src/components/ComponentPage/ComponentPage.test.tsx @@ -47,8 +47,8 @@ describe('ComponentPage', () => { [ catalogApiRef, ({ - async getEntity() {}, - } as unknown) as CatalogApi, + async getEntityByName() {}, + } as Partial) as CatalogApi, ], ])} > diff --git a/plugins/catalog/src/components/ComponentPage/ComponentPage.tsx b/plugins/catalog/src/components/ComponentPage/ComponentPage.tsx index 3f3ecfa006..17ad2d880d 100644 --- a/plugins/catalog/src/components/ComponentPage/ComponentPage.tsx +++ b/plugins/catalog/src/components/ComponentPage/ComponentPage.tsx @@ -13,23 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Entity } from '@backstage/catalog-model'; import { Content, - ErrorApi, errorApiRef, Header, HeaderTabs, Page, pageTheme, + Progress, useApi, } from '@backstage/core'; import { SentryIssuesWidget } from '@backstage/plugin-sentry'; import { Grid } from '@material-ui/core'; +import { Alert } from '@material-ui/lab'; import React, { FC, useEffect, useState } from 'react'; import { useAsync } from 'react-use'; import { catalogApiRef } from '../..'; -import { Component } from '../../data/component'; -import { entityToComponent } from '../../data/utils'; import { ComponentContextMenu } from '../ComponentContextMenu/ComponentContextMenu'; import { ComponentMetadataCard } from '../ComponentMetadataCard/ComponentMetadataCard'; import { ComponentRemovalDialog } from '../ComponentRemovalDialog/ComponentRemovalDialog'; @@ -48,48 +49,61 @@ type ComponentPageProps = { }; }; +function headerProps( + kind: string, + namespace: string | undefined, + name: string, + entity: Entity | undefined, +): { headerTitle: string; headerType: string } { + return { + headerTitle: `${name}${namespace ? ` in ${namespace}` : ''}`, + headerType: (() => { + let t = kind.toLowerCase(); + if (entity && entity.spec && 'type' in entity.spec) { + t += ' — '; + t += (entity.spec as { type: string }).type.toLowerCase(); + } + return t; + })(), + }; +} + export const ComponentPage: FC = ({ match, history }) => { - const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); - const [removingPending, setRemovingPending] = useState(false); - const showRemovalDialog = () => setConfirmationDialogOpen(true); - const hideRemovalDialog = () => setConfirmationDialogOpen(false); const { optionalNamespaceAndName, kind } = match.params; const [name, namespace] = optionalNamespaceAndName.split(':').reverse(); - const errorApi = useApi(errorApiRef); + const errorApi = useApi(errorApiRef); const catalogApi = useApi(catalogApiRef); - const { value: component, error, loading } = useAsync(async () => { - const entity = await catalogApi.getEntityByName({ name, namespace, kind }); - if (!entity) { - throw new Error(`No entity found with that name`); - } - const location = await catalogApi.getLocationByEntity(entity); - return { ...entityToComponent(entity), location }; - }); + + const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false); + const { value: entity, error, loading } = useAsync( + () => catalogApi.getEntityByName({ kind, namespace, name }), + [catalogApi, kind, namespace, name], + ); useEffect(() => { - if (error) { + if (!error && !loading && !entity) { errorApi.post(new Error('Component not found!')); setTimeout(() => { history.push('/'); }, REDIRECT_DELAY); } - }, [error, errorApi, history]); + }, [errorApi, history, error, loading, entity]); - if (name === '') { + if (!name) { history.push('/catalog'); return null; } const removeComponent = async () => { setConfirmationDialogOpen(false); - setRemovingPending(true); // await componentFactory.removeComponentByName(componentName); - - await catalogApi; history.push('/'); }; + const showRemovalDialog = () => setConfirmationDialogOpen(true); + const hideRemovalDialog = () => setConfirmationDialogOpen(false); + // TODO - Replace with proper tabs implementation const tabs = [ { @@ -118,38 +132,56 @@ export const ComponentPage: FC = ({ match, history }) => { }, ]; + const { headerTitle, headerType } = headerProps( + kind, + namespace, + name, + entity, + ); + return ( // TODO: Switch theme and type props based on component type (website, library, ...) -
- +
+ {entity && ( + + )}
- - {confirmationDialogOpen && component && ( - + {loading && } + + {error && ( + + {error.toString()} + + )} + + {entity && ( + <> + + + + + + + + + + + + + + + )} - - - - - - - - - - ); }; diff --git a/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx b/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx index 9ea4e64908..aa8f99e01e 100644 --- a/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx +++ b/plugins/catalog/src/components/ComponentRemovalDialog/ComponentRemovalDialog.tsx @@ -32,37 +32,36 @@ import React, { FC } from 'react'; import { useAsync } from 'react-use'; import { AsyncState } from 'react-use/lib/useAsync'; import { catalogApiRef } from '../../api/types'; -import { Component } from '../../data/component'; type ComponentRemovalDialogProps = { + open: boolean; onConfirm: () => any; - onCancel: () => any; onClose: () => any; - component: Component; + entity: Entity; }; -function useColocatedEntities(component: Component): AsyncState { +function useColocatedEntities(entity: Entity): AsyncState { const catalogApi = useApi(catalogApiRef); return useAsync(async () => { - const myLocation = component.metadata.annotations?.[LOCATION_ANNOTATION]; + const myLocation = entity.metadata.annotations?.[LOCATION_ANNOTATION]; return myLocation ? await catalogApi.getEntities({ [LOCATION_ANNOTATION]: myLocation }) : []; - }, [catalogApi, component]); + }, [catalogApi, entity]); } export const ComponentRemovalDialog: FC = ({ + open, onConfirm, - onCancel, onClose, - component, + entity, }) => { - const { value: entities, loading, error } = useColocatedEntities(component); + const { value: entities, loading, error } = useColocatedEntities(entity); const theme = useTheme(); const fullScreen = useMediaQuery(theme.breakpoints.down('sm')); return ( - + Are you sure you want to unregister this component? @@ -102,7 +101,7 @@ export const ComponentRemovalDialog: FC = ({ ) : null} - +