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) {