diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json
index 88845b402b..c02b63f761 100644
--- a/plugins/scaffolder/package.json
+++ b/plugins/scaffolder/package.json
@@ -51,6 +51,7 @@
"humanize-duration": "^3.25.1",
"immer": "^9.0.1",
"json-schema": "^0.3.0",
+ "lodash": "^4.17.21",
"luxon": "^1.25.0",
"react": "^16.13.1",
"react-dom": "^16.13.1",
diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx
new file mode 100644
index 0000000000..fa74b34717
--- /dev/null
+++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2021 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 React from 'react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
+import { capitalize } from 'lodash';
+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 '../../filters';
+
+import { AlertApi, alertApiRef } from '@backstage/core-plugin-api';
+import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
+
+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 = 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 = 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/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx
new file mode 100644
index 0000000000..780f9d2d1e
--- /dev/null
+++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2021 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 React from 'react';
+import {
+ Typography,
+ List,
+ ListItem,
+ makeStyles,
+ Theme,
+ Checkbox,
+ ListItemText,
+} from '@material-ui/core';
+import { useEntityTypeFilter } from '@backstage/plugin-catalog-react';
+import { alertApiRef, useApi } from '@backstage/core-plugin-api';
+
+const useStyles = makeStyles(theme => ({
+ checkbox: {
+ padding: theme.spacing(0, 1, 0, 1),
+ },
+}));
+
+export const TemplateTypePicker = () => {
+ const classes = useStyles();
+ const alertApi = useApi(alertApiRef);
+ // TODO(timbonicus): Use new setTypes returned from the hook
+ const { error, types, selectedType } = useEntityTypeFilter();
+
+ if (!types) return null;
+
+ if (error) {
+ alertApi.post({
+ message: `Failed to load entity types`,
+ severity: 'error',
+ });
+ return null;
+ }
+
+ return (
+ <>
+ Categories
+
+ {types.map(type => {
+ const labelId = `checkbox-list-label-${type}`;
+ return (
+ {}}
+ // TODO(timbonicus): Update to use setTypes
+ // setSelectedCategories(
+ // selectedCategories.includes(type)
+ // ? selectedCategories.filter(
+ // selectedCategory => selectedCategory !== type,
+ // )
+ // : [...selectedCategories, type],
+ // )
+ // }
+ >
+
+
+
+ );
+ })}
+
+ >
+ );
+};
diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/index.ts b/plugins/scaffolder/src/components/TemplateTypePicker/index.ts
new file mode 100644
index 0000000000..2dcd091311
--- /dev/null
+++ b/plugins/scaffolder/src/components/TemplateTypePicker/index.ts
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2021 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 { TemplateTypePicker } from './TemplateTypePicker';