Move CategoryPicker to scaffolder-react

Signed-off-by: Min Kim <minkimcello@gmail.com>
This commit is contained in:
Min Kim
2023-03-17 15:25:57 -04:00
parent 9793d8bab1
commit 7d51e8cb0a
6 changed files with 34 additions and 11 deletions
@@ -1,152 +0,0 @@
/*
* 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(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
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(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
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']);
});
it('should render the selectedTypes already in the document', async () => {
const mockAvailableTypes = ['foo', 'bar'];
const mockSelectedTypes = ['foo'];
(useEntityTypeFilter as jest.Mock).mockReturnValue({
availableTypes: mockAvailableTypes,
selectedTypes: mockSelectedTypes,
});
const { getByRole } = await renderInTestApp(
<TestApiProvider apis={[[alertApiRef, mockAlertApi]]}>
<CategoryPicker />
</TestApiProvider>,
);
const openButton = getByRole('button', { name: 'Open' });
await fireEvent(openButton, new MouseEvent('click', { bubbles: true }));
const fooCheckbox = getByRole('checkbox', { name: 'Foo' });
expect(fooCheckbox).toBeChecked();
});
});
@@ -1,85 +0,0 @@
/*
* 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 = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;
/**
* The Category Picker that is rendered on the left side for picking
* categories and filtering the template list.
*/
export const CategoryPicker = () => {
const alertApi = useApi(alertApiRef);
const { error, loading, availableTypes, selectedTypes, setSelectedTypes } =
useEntityTypeFilter();
if (loading) return <Progress />;
if (error) {
alertApi.post({
message: `Failed to load entity types with error: ${error}`,
severity: 'error',
});
return null;
}
if (!availableTypes) return null;
return (
<Box pb={1} pt={1}>
<Typography variant="button">Categories</Typography>
<Autocomplete
multiple
aria-label="Categories"
options={availableTypes}
value={selectedTypes}
onChange={(_: object, value: string[]) => setSelectedTypes(value)}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
label={capitalize(option)}
/>
)}
size="small"
popupIcon={<ExpandMoreIcon />}
renderInput={params => <TextField {...params} variant="outlined" />}
/>
</Box>
);
};
@@ -32,7 +32,8 @@ import {
CatalogFilterLayout,
UserListPicker,
} from '@backstage/plugin-catalog-react';
import { CategoryPicker } from './CategoryPicker';
import { TemplateCategoryPicker } from '@backstage/plugin-scaffolder-react/alpha';
import { RegisterExistingButton } from './RegisterExistingButton';
import { useRouteRef } from '@backstage/core-plugin-api';
import { TemplateGroupFilter, TemplateGroups } from './TemplateGroups';
@@ -110,7 +111,7 @@ export const TemplateListPage = (props: TemplateListPageProps) => {
initialFilter="all"
availableFilters={['all', 'starred']}
/>
<CategoryPicker />
<TemplateCategoryPicker />
<EntityTagPicker />
</CatalogFilterLayout.Filters>
<CatalogFilterLayout.Content>