From 8afeb82cc35d0b7528b5231b4f9d656fe597525f Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 4 Feb 2022 15:43:20 +0100 Subject: [PATCH] feat: adding a new TemplateListPage and CategoryPicker with tests Signed-off-by: blam --- .../TemplateListPage/CategoryPicker.test.tsx | 130 ++++++++++++++++++ .../next/TemplateListPage/CategoryPicker.tsx | 81 +++++++++++ .../TemplateListPage/TemplateListPage.tsx | 121 ++++++++++++++++ .../src/next/TemplateListPage/index.ts | 16 +++ 4 files changed, 348 insertions(+) create mode 100644 plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.test.tsx create mode 100644 plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx create mode 100644 plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx create mode 100644 plugins/scaffolder/src/next/TemplateListPage/index.ts diff --git a/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.test.tsx b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.test.tsx new file mode 100644 index 0000000000..a04d382445 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.test.tsx @@ -0,0 +1,130 @@ +/* + * Copyright 2022 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 { useEntityTypeFilter } from '@backstage/plugin-catalog-react'; +import { CategoryPicker } from './CategoryPicker'; +import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; +import { alertApiRef } from '@backstage/core-plugin-api'; +import { fireEvent } from '@testing-library/react'; + +jest.mock('@backstage/plugin-catalog-react', () => ({ + useEntityTypeFilter: jest.fn(), +})); + +describe('CategoryPicker', () => { + const mockAlertApi = { post: jest.fn() }; + + beforeEach(() => { + mockAlertApi.post.mockClear(); + }); + + it('should post the error to errorApi if an errors is returned', async () => { + (useEntityTypeFilter as jest.Mock).mockReturnValue({ + error: new Error('something broked'), + }); + + await renderInTestApp( + + + , + ); + + expect(mockAlertApi.post).toHaveBeenCalledWith({ + message: expect.stringContaining('something broked'), + severity: 'error', + }); + }); + + it('should render loading if the hook is loading', async () => { + (useEntityTypeFilter as jest.Mock).mockReturnValue({ + loading: true, + }); + + const { findByTestId } = await renderInTestApp( + + + , + ); + + expect(await findByTestId('progress')).toBeInTheDocument(); + }); + + it('should not render if there is no available types', async () => { + (useEntityTypeFilter as jest.Mock).mockReturnValue({ + availableTypes: null, + }); + + const { queryByText } = await renderInTestApp( + + + , + ); + + expect(queryByText('Categories')).not.toBeInTheDocument(); + }); + + it('renders the autocomplete with the availableTypes', async () => { + const mockAvailableTypes = ['foo', 'bar']; + + (useEntityTypeFilter as jest.Mock).mockReturnValue({ + availableTypes: mockAvailableTypes, + }); + + const { getByRole } = await renderInTestApp( + + + , + ); + + const openButton = getByRole('button', { name: 'Open' }); + openButton.click(); + + expect(getByRole('checkbox', { name: 'Foo' })).toBeInTheDocument(); + expect(getByRole('checkbox', { name: 'Bar' })).toBeInTheDocument(); + }); + + it('should call setSelectedTypes when one of the options are called', async () => { + const mockAvailableTypes = ['foo', 'bar']; + const mockSetSelectedTypes = jest.fn(); + + (useEntityTypeFilter as jest.Mock).mockReturnValue({ + availableTypes: mockAvailableTypes, + setSelectedTypes: mockSetSelectedTypes, + }); + + const { getByRole } = await renderInTestApp( + + + , + ); + + const openButton = getByRole('button', { name: 'Open' }); + await fireEvent(openButton, new MouseEvent('click', { bubbles: true })); + + const fooCheckbox = getByRole('checkbox', { name: 'Foo' }); + await fireEvent(fooCheckbox, new MouseEvent('click', { bubbles: true })); + + expect(mockSetSelectedTypes).toHaveBeenCalledWith(['foo']); + + await fireEvent(openButton, new MouseEvent('click', { bubbles: true })); + + const barCheckbox = getByRole('checkbox', { name: 'Bar' }); + await fireEvent(barCheckbox, new MouseEvent('click', { bubbles: true })); + + expect(mockSetSelectedTypes).toHaveBeenCalledWith(['foo', 'bar']); + }); +}); diff --git a/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx new file mode 100644 index 0000000000..94f0df2565 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/CategoryPicker.tsx @@ -0,0 +1,81 @@ +/* + * 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 capitalize from 'lodash/capitalize'; +import { Progress } from '@backstage/core-components'; +import { + Box, + Checkbox, + FormControlLabel, + TextField, + Typography, +} from '@material-ui/core'; +import CheckBoxIcon from '@material-ui/icons/CheckBox'; +import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; +import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; +import { Autocomplete } from '@material-ui/lab'; +import { useEntityTypeFilter } from '@backstage/plugin-catalog-react'; +import { alertApiRef, useApi } from '@backstage/core-plugin-api'; + +const icon = ; +const checkedIcon = ; + +export const CategoryPicker = () => { + const alertApi = useApi(alertApiRef); + const { error, loading, availableTypes, selectedTypes, setSelectedTypes } = + useEntityTypeFilter(); + + if (loading) return ; + + if (error) { + alertApi.post({ + message: `Failed to load entity types with error: ${error}`, + severity: 'error', + }); + return null; + } + + if (!availableTypes) return null; + + return ( + + Categories + setSelectedTypes(value)} + renderOption={(option, { selected }) => ( + + } + label={capitalize(option)} + /> + )} + size="small" + popupIcon={} + renderInput={params => } + /> + + ); +}; diff --git a/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx new file mode 100644 index 0000000000..dc736408ac --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/TemplateListPage.tsx @@ -0,0 +1,121 @@ +/* + * Copyright 2022 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 { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { Entity } from '@backstage/catalog-model'; +import { makeStyles } from '@material-ui/core'; +import { + Content, + ContentHeader, + CreateButton, + Header, + Lifecycle, + Page, + SupportButton, +} from '@backstage/core-components'; +import { + EntityKindPicker, + EntityListProvider, + EntitySearchBar, + EntityTagPicker, + UserListPicker, +} from '@backstage/plugin-catalog-react'; +import { CategoryPicker } from './CategoryPicker'; + +export type TemplateListGroup = { + title?: string; + titleComponent?: React.ReactNode; + filter: (entity: Entity) => boolean; +}; + +export type TemplateListPageProps = { + TemplateCardComponent?: React.ComponentType<{ + template: TemplateEntityV1beta3; + }>; + groups?: TemplateListGroup[]; +}; + +const useStyles = makeStyles(theme => ({ + contentWrapper: { + display: 'grid', + gridTemplateAreas: "'filters' 'grid'", + gridTemplateColumns: '250px 1fr', + gridColumnGap: theme.spacing(2), + }, +})); + +export const TemplateListPage = (props: TemplateListPageProps) => { + const styles = useStyles(); + return ( + + +
+ Create a New Component + + } + subtitle="Create new software components using standard templates" + /> + + + {/* {allowed && ( + + )} */} + + Create new software components using standard templates. Different + templates create different kinds of components (services, + websites, documentation, ...). + + + +
+
+ +
+
+ {/* {groups && + groups.map((group, index) => ( + + ))} + */} +
+
+
+ + + ); +}; diff --git a/plugins/scaffolder/src/next/TemplateListPage/index.ts b/plugins/scaffolder/src/next/TemplateListPage/index.ts new file mode 100644 index 0000000000..efe9f13d08 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateListPage/index.ts @@ -0,0 +1,16 @@ +/* + * Copyright 2022 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 { TemplateListPage } from './TemplateListPage';