diff --git a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx new file mode 100644 index 0000000000..9b40af0dbd --- /dev/null +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.test.tsx @@ -0,0 +1,137 @@ +/* + * Copyright 2021 Spotify AB + * + * 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 React from 'react'; +import { fireEvent, render, waitFor } from '@testing-library/react'; +import { capitalize } from 'lodash'; +import { + AlertApi, + alertApiRef, + ApiProvider, + ApiRegistry, +} from '@backstage/core'; +import { CatalogApi } from '@backstage/catalog-client'; +import { Entity } from '@backstage/catalog-model'; +import { EntityTypePicker } from './EntityTypePicker'; +import { MockEntityListContextProvider } from '../../testUtils/providers'; +import { catalogApiRef } from '../../api'; +import { EntityKindFilter, EntityTypeFilter } from '../../types'; + +const entities: Entity[] = [ + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'component-1', + }, + spec: { + type: 'service', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'component-2', + }, + spec: { + type: 'website', + }, + }, + { + apiVersion: '1', + kind: 'Component', + metadata: { + name: 'component-3', + }, + spec: { + type: 'library', + }, + }, +]; + +const apis = ApiRegistry.from([ + [ + catalogApiRef, + ({ + getEntities: jest + .fn() + .mockImplementation(() => Promise.resolve({ items: entities })), + } as unknown) as CatalogApi, + ], + [ + alertApiRef, + ({ + post: jest.fn(), + } as unknown) as AlertApi, + ], +]); + +describe('', () => { + it('renders available entity types', async () => { + const rendered = render( + + + + + , + ); + expect(rendered.getByText('Type')).toBeInTheDocument(); + + const input = await rendered.getByTestId('select'); + fireEvent.click(input); + + await waitFor(() => rendered.getByText('Service')); + + entities.forEach(entity => { + expect( + rendered.getByText(capitalize(entity.spec!.type as string)), + ).toBeInTheDocument(); + }); + }); + + it('sets the selected type filter', async () => { + const updateFilters = jest.fn(); + const rendered = render( + + + + + , + ); + const input = await rendered.getByTestId('select'); + fireEvent.click(input); + + await waitFor(() => rendered.getByText('Service')); + fireEvent.click(rendered.getByText('Service')); + + expect(updateFilters).toHaveBeenLastCalledWith({ + type: new EntityTypeFilter('service'), + }); + + fireEvent.click(input); + fireEvent.click(rendered.getByText('All')); + + expect(updateFilters).toHaveBeenLastCalledWith({ type: undefined }); + }); +}); diff --git a/plugins/catalog/src/components/EntityTypePicker/EntityTypePicker.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx similarity index 95% rename from plugins/catalog/src/components/EntityTypePicker/EntityTypePicker.tsx rename to plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx index d404c8ea85..f5a21d4284 100644 --- a/plugins/catalog/src/components/EntityTypePicker/EntityTypePicker.tsx +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { capitalize } from 'lodash'; import { Box } from '@material-ui/core'; import { alertApiRef, Select, useApi } from '@backstage/core'; -import { useEntityTypeFilter } from '@backstage/plugin-catalog-react'; +import { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter'; export const EntityTypePicker = () => { const alertApi = useApi(alertApiRef); diff --git a/plugins/catalog/src/components/EntityTypePicker/index.ts b/plugins/catalog-react/src/components/EntityTypePicker/index.ts similarity index 100% rename from plugins/catalog/src/components/EntityTypePicker/index.ts rename to plugins/catalog-react/src/components/EntityTypePicker/index.ts diff --git a/plugins/catalog-react/src/components/index.ts b/plugins/catalog-react/src/components/index.ts index f427d3ac63..dea6f56095 100644 --- a/plugins/catalog-react/src/components/index.ts +++ b/plugins/catalog-react/src/components/index.ts @@ -17,4 +17,5 @@ export * from './EntityProvider'; export * from './EntityRefLink'; export * from './EntityTable'; export * from './EntityTagPicker'; +export * from './EntityTypePicker'; export * from './UserListPicker'; diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index 7117b9b932..a25828ab1e 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -98,7 +98,8 @@ export const EntityListProvider = ({ const { value: user } = useOwnUser(); const { isStarredEntity } = useStarredEntities(); - // TODO(timbonicus): should query params be registered as initialFilters when present? + // TODO(timbonicus): is it possible to register initial filters from query params? e.g. if the + // query key matches the generic definition, call a constructor with the value const [filters, setFilters] = useState( initialFilters ?? ({} as EntityFilters), ); diff --git a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx index e442122987..3569a3792c 100644 --- a/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx +++ b/plugins/catalog/src/components/CatalogPage/CatalogPage.tsx @@ -28,6 +28,7 @@ import { EntityKindFilter, EntityListProvider, EntityTagPicker, + EntityTypePicker, UserListFilter, UserListFilterKind, UserListPicker, @@ -37,7 +38,6 @@ import { createComponentRouteRef } from '../../routes'; import { CatalogTable } from '../CatalogTable'; import { EntityRow } from '../CatalogTable/types'; import CatalogLayout from './CatalogLayout'; -import { EntityTypePicker } from '../EntityTypePicker'; const useStyles = makeStyles(theme => ({ contentWrapper: { diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index ca45e2d07b..69872c3870 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -58,11 +58,10 @@ type CatalogTableProps = { export const CatalogTable = ({ columns }: CatalogTableProps) => { const { isStarredEntity, toggleStarredEntity } = useStarredEntities(); - // TODO(timbonicus): should the component loading entities register which fields it's interested in? const { loading, error, entities, filters } = useEntityListProvider(); const showTypeColumn = filters.type !== undefined; - // TODO(timbonicus): this makes less sense with more complex filters, should we show filter chips instead? + // TODO(timbonicus): we should show filter chips for all filters instead const titlePreamble = capitalize(filters.user?.value ?? 'all'); if (error) {