Add new core component Autocomplete and use for entity pickers

Signed-off-by: Jonathan Roebuck <jroebuck@spotify.com>
This commit is contained in:
Jonathan Roebuck
2024-10-25 15:41:59 +01:00
parent eb1262a2e4
commit 4dc54873e2
11 changed files with 479 additions and 240 deletions
+14
View File
@@ -6,6 +6,7 @@
/// <reference types="react" />
import { ApiRef } from '@backstage/core-plugin-api';
import { AutocompleteProps } from '@material-ui/lab/Autocomplete';
import { BackstageIdentityApi } from '@backstage/core-plugin-api';
import { BackstagePalette } from '@backstage/theme';
import { BackstageUserIdentity } from '@backstage/core-plugin-api';
@@ -33,6 +34,7 @@ import { MaterialTableProps } from '@material-table/core';
import { NavLinkProps } from 'react-router-dom';
import { Options } from 'react-markdown';
import { Options as Options_2 } from '@material-table/core';
import { OutlinedTextFieldProps } from '@material-ui/core/TextField';
import { Overrides } from '@material-ui/core/styles/overrides';
import { ProfileInfo } from '@backstage/core-plugin-api';
import { ProfileInfoApi } from '@backstage/core-plugin-api';
@@ -76,6 +78,18 @@ export type AppIconProps = IconComponentProps & {
Fallback?: IconComponent;
};
// Warning: (ae-forgotten-export) The symbol "AutocompleteComponentProps" needs to be exported by the entry point index.d.ts
//
// @public (undocumented)
export function Autocomplete<
T,
Multiple extends boolean | undefined = undefined,
DisableClearable extends boolean | undefined = undefined,
FreeSolo extends boolean | undefined = undefined,
>(
props: AutocompleteComponentProps<T, Multiple, DisableClearable, FreeSolo>,
): React_2.JSX.Element;
// @public
export const AutoLogout: (props: AutoLogoutProps) => JSX.Element | null;
@@ -0,0 +1,33 @@
/*
* Copyright 2024 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 { AutocompleteComponent as Autocomplete } from './Autocomplete';
export default {
title: 'Inputs/Autocomplete',
component: Autocomplete,
};
export const Default = (args: any) => {
return <Autocomplete {...args} />;
};
Default.args = {
multiple: true,
label: 'Default',
name: 'default',
options: ['test 1', 'test 2', 'test 3'],
};
@@ -0,0 +1,117 @@
/*
* Copyright 2024 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 { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { AutocompleteComponent as Autocomplete } from './Autocomplete';
describe('Autocomplete', () => {
const user = userEvent.setup();
const mockOptions = ['Option 1', 'Option 2', 'Option 3'];
it('renders without exploding', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
/>,
);
expect(screen.getByRole('textbox')).toBeInTheDocument();
});
it('renders the expand icon', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
/>,
);
const expandIcon = screen.getByTestId('test-autocomplete-expand');
expect(expandIcon).toBeInTheDocument();
});
it('displays options when clicked', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
/>,
);
const input = screen.getByRole('textbox');
user.click(input);
mockOptions.forEach(option => {
expect(screen.getByText(option)).toBeInTheDocument();
});
});
it('supports required input', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
inputProps={{ required: true }}
/>,
);
const input = screen.getByRole('textbox');
expect(input).toBeRequired();
});
it('displays helper text when provided', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
inputProps={{ helperText: 'Helper text' }}
/>,
);
expect(screen.getByText('Helper text')).toBeInTheDocument();
});
it('renders without label', () => {
render(<Autocomplete name="test-autocomplete" options={mockOptions} />);
const input = screen.getByRole('textbox');
expect(input).toBeInTheDocument();
});
it('displays correct option on selection', () => {
render(
<Autocomplete
name="test-autocomplete"
options={mockOptions}
label="Test Label"
/>,
);
const input = screen.getByRole('textbox');
user.click(input);
const optionToSelect = screen.getByText('Option 1');
user.click(optionToSelect);
expect(input).toHaveValue('Option 1');
});
});
@@ -0,0 +1,171 @@
/*
* Copyright 2024 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 Box from '@material-ui/core/Box';
import Typography from '@material-ui/core/Typography';
import Paper, { PaperProps } from '@material-ui/core/Paper';
import Popper, { PopperProps } from '@material-ui/core/Popper';
import TextField, { OutlinedTextFieldProps } from '@material-ui/core/TextField';
import Grow from '@material-ui/core/Grow';
import {
createStyles,
makeStyles,
Theme,
withStyles,
} from '@material-ui/core/styles';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Autocomplete, { AutocompleteProps } from '@material-ui/lab/Autocomplete';
import React, { ReactNode } from 'react';
const useStyles = makeStyles(
theme => ({
root: {},
label: {
position: 'relative',
fontWeight: 'bold',
fontSize: theme.typography.body2.fontSize,
fontFamily: theme.typography.fontFamily,
color: theme.palette.text.primary,
'& > span': {
top: 0,
left: 0,
position: 'absolute',
},
},
input: {},
}),
{ name: 'BackstageAutocomplete' },
);
const BootstrapAutocomplete = withStyles(
(theme: Theme) =>
createStyles({
root: {},
paper: {
margin: 0,
},
hasClearIcon: {},
hasPopupIcon: {},
focused: {},
inputRoot: {
marginTop: 24,
backgroundColor: theme.palette.background.paper,
'$root$hasClearIcon$hasPopupIcon &': {
padding: `${theme.spacing(0.75, 7, 0.75, 1.5)}`,
},
'$root$focused &': {
outline: 'none',
},
'$root &:hover > fieldset': {
borderColor: '#ced4da',
},
'$root$focused & > fieldset': {
borderWidth: 1,
borderColor: theme.palette.primary.main,
},
},
popupIndicator: {
padding: 0,
margin: 0,
color: theme.palette.text.primary,
'& [class*="MuiTouchRipple-root"]': {
display: 'none',
},
},
endAdornment: {
'$root$hasClearIcon$hasPopupIcon &': {
right: 4,
},
},
input: {
'$root$hasClearIcon$hasPopupIcon &': {
height: 32,
fontSize: theme.typography.body1.fontSize,
padding: 0,
},
},
}),
{ name: 'BackstageAutocompleteBase' },
)(Autocomplete) as typeof Autocomplete;
const PopperComponent = (props: PopperProps) => (
<Popper {...props} transition placement="bottom-start">
{({ TransitionProps }) => (
<Grow {...TransitionProps} style={{ transformOrigin: '0 0 0' }}>
<Box>{props.children as ReactNode}</Box>
</Grow>
)}
</Popper>
);
const PaperComponent = (props: PaperProps) => (
<Paper {...props} elevation={8} />
);
export type AutocompleteComponentProps<
T,
Multiple extends boolean | undefined = undefined,
DisableClearable extends boolean | undefined = undefined,
FreeSolo extends boolean | undefined = undefined,
> = {
name: string;
label?: string;
inputProps?: Omit<OutlinedTextFieldProps, 'variant'>;
} & Omit<
AutocompleteProps<T, Multiple, DisableClearable, FreeSolo>,
'PopperComponent' | 'PaperComponent' | 'renderInput' | 'size' | 'popupIcon'
>;
/** @public */
export function AutocompleteComponent<
T,
Multiple extends boolean | undefined = undefined,
DisableClearable extends boolean | undefined = undefined,
FreeSolo extends boolean | undefined = undefined,
>(props: AutocompleteComponentProps<T, Multiple, DisableClearable, FreeSolo>) {
const { label, name, inputProps, ...rest } = props;
const classes = useStyles();
const autocomplete = (
<BootstrapAutocomplete
{...rest}
size="small"
popupIcon={<ExpandMoreIcon data-testid={`${name}-expand`} />}
PaperComponent={PaperComponent}
PopperComponent={PopperComponent}
renderInput={params => (
<TextField
{...inputProps}
{...params}
className={classes.input}
variant="outlined"
/>
)}
/>
);
return (
<Box className={classes.root}>
{label ? (
<Typography className={classes.label} component="label">
<Box component="span">{label}</Box>
{autocomplete}
</Typography>
) : (
autocomplete
)}
</Box>
);
}
@@ -0,0 +1,16 @@
/*
* Copyright 2024 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 { AutocompleteComponent as Autocomplete } from './Autocomplete';
@@ -16,6 +16,7 @@
export * from './AlertDisplay';
export * from './AutoLogout';
export * from './Autocomplete';
export * from './Avatar';
export * from './LinkButton';
export * from './CodeSnippet';
+1 -1
View File
@@ -205,7 +205,7 @@ export type EntityAutocompletePickerProps<
Filter: {
new (values: string[]): NonNullable<T[Name]>;
};
InputProps?: TextFieldProps;
InputProps?: TextFieldProps['InputProps'];
initialSelectedOptions?: string[];
filtersForAvailableValues?: Array<keyof T>;
};
@@ -16,21 +16,18 @@
import Box from '@material-ui/core/Box';
import { TextFieldProps } from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import Autocomplete from '@material-ui/lab/Autocomplete';
import React, { useEffect, useMemo, useState, ReactNode } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useApi } from '@backstage/core-plugin-api';
import useAsync from 'react-use/esm/useAsync';
import { catalogApiRef } from '../../api';
import { EntityAutocompletePickerOption } from './EntityAutocompletePickerOption';
import { EntityAutocompletePickerInput } from './EntityAutocompletePickerInput';
import {
DefaultEntityFilters,
useEntityList,
} from '../../hooks/useEntityListProvider';
import { EntityFilter } from '../../types';
import { Autocomplete } from '@backstage/core-components';
import { reduceBackendCatalogFilters } from '../../utils/filters';
/** @public */
@@ -52,7 +49,7 @@ export type EntityAutocompletePickerProps<
path: string;
showCounts?: boolean;
Filter: { new (values: string[]): NonNullable<T[Name]> };
InputProps?: TextFieldProps;
InputProps?: TextFieldProps['InputProps'];
initialSelectedOptions?: string[];
filtersForAvailableValues?: Array<keyof T>;
};
@@ -82,11 +79,9 @@ export function EntityAutocompletePicker<
path,
showCounts,
Filter,
InputProps,
initialSelectedOptions = [],
filtersForAvailableValues = ['kind'],
} = props;
const classes = useStyles();
const {
@@ -152,36 +147,25 @@ export function EntityAutocompletePicker<
return (
<Box className={classes.root} pb={1} pt={1}>
<Typography className={classes.label} variant="button" component="label">
{label}
<Autocomplete<string, true>
PopperComponent={popperProps => (
<div {...popperProps}>{popperProps.children as ReactNode}</div>
)}
multiple
disableCloseOnSelect
options={availableOptions}
value={selectedOptions}
onChange={(_event: object, options: string[]) =>
setSelectedOptions(options)
}
renderOption={(option, { selected }) => (
<EntityAutocompletePickerOption
selected={selected}
value={option}
availableOptions={availableValues}
showCounts={!!showCounts}
/>
)}
size="small"
popupIcon={
<ExpandMoreIcon data-testid={`${String(name)}-picker-expand`} />
}
renderInput={params => (
<EntityAutocompletePickerInput {...params} {...InputProps} />
)}
/>
</Typography>
<Autocomplete<string, true>
multiple
disableCloseOnSelect
label={label}
name={`${String(name)}-picker`}
options={availableOptions}
value={selectedOptions}
onChange={(_event: object, options: string[]) =>
setSelectedOptions(options)
}
renderOption={(option, { selected }) => (
<EntityAutocompletePickerOption
selected={selected}
value={option}
availableOptions={availableValues}
showCounts={!!showCounts}
/>
)}
/>
</Box>
);
}
@@ -1,43 +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 TextField, { TextFieldProps } from '@material-ui/core/TextField';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import React from 'react';
import classnames from 'classnames';
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
input: {
backgroundColor: theme.palette.background.paper,
},
}),
{
name: 'CatalogReactEntityAutocompletePickerInput',
},
);
export function EntityAutocompletePickerInput(params: TextFieldProps) {
const classes = useStyles();
return (
<TextField
variant="outlined"
{...params}
className={classnames(classes.input, params.className)}
/>
);
}
@@ -22,15 +22,13 @@ import {
import Box from '@material-ui/core/Box';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import Tooltip from '@material-ui/core/Tooltip';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
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/Autocomplete';
import React, { useEffect, useMemo, useState, ReactNode } from 'react';
import { Autocomplete } from '@backstage/core-components';
import React, { useEffect, useMemo, useState } from 'react';
import { useEntityList } from '../../hooks/useEntityListProvider';
import { EntityOwnerFilter } from '../../filters';
import { useDebouncedEffect } from '@react-hookz/web';
@@ -42,29 +40,20 @@ import { withStyles } from '@material-ui/core/styles';
import { useEntityPresentation } from '../../apis';
import { catalogReactTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
import { PopperProps } from '@material-ui/core/Popper';
/** @public */
export type CatalogReactEntityOwnerPickerClassKey = 'input';
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
root: {},
label: {
textTransform: 'none',
fontWeight: 'bold',
},
input: {
backgroundColor: theme.palette.background.paper,
},
fullWidth: { width: '100%' },
boxLabel: {
width: '100%',
textOverflow: 'ellipsis',
overflow: 'hidden',
},
}),
{
root: {},
fullWidth: { width: '100%' },
boxLabel: {
width: '100%',
textOverflow: 'ellipsis',
overflow: 'hidden',
},
},
{ name: 'CatalogReactEntityOwnerPicker' },
);
@@ -147,7 +136,7 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => {
[ownersParameter],
);
const [selectedOwners, setSelectedOwners] = useState(
const [selectedOwners, setSelectedOwners] = useState<string[]>(
queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [],
);
@@ -186,84 +175,67 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => {
return (
<Box className={classes.root} pb={1} pt={1}>
<Typography className={classes.label} variant="button" component="label">
{t('entityOwnerPicker.title')}
<Autocomplete
PopperComponent={Popper}
multiple
disableCloseOnSelect
loading={loading}
options={availableOwners}
value={selectedOwners as unknown as Entity[]}
getOptionSelected={(o, v) => {
if (typeof v === 'string') {
return stringifyEntityRef(o) === v;
}
return o === v;
}}
getOptionLabel={o => {
const entity =
typeof o === 'string'
? cache.getEntity(o) ||
parseEntityRef(o, {
defaultKind: 'group',
defaultNamespace: 'default',
})
: o;
return humanizeEntity(entity, humanizeEntityRef(entity));
}}
onChange={(_: object, owners) => {
setText('');
setSelectedOwners(
owners.map(e => {
const entityRef =
typeof e === 'string' ? e : stringifyEntityRef(e);
<Autocomplete<Entity, true>
label={t('entityOwnerPicker.title')}
multiple
disableCloseOnSelect
loading={loading}
options={availableOwners}
value={selectedOwners as unknown as Entity[]}
getOptionSelected={(o, v) => {
if (typeof v === 'string') {
return stringifyEntityRef(o) === v;
}
return o === v;
}}
getOptionLabel={o => {
const entity =
typeof o === 'string'
? cache.getEntity(o) ||
parseEntityRef(o, {
defaultKind: 'group',
defaultNamespace: 'default',
})
: o;
return humanizeEntity(entity, humanizeEntityRef(entity));
}}
onChange={(_: object, owners) => {
setText('');
setSelectedOwners(
owners.map(e => {
const entityRef =
typeof e === 'string' ? e : stringifyEntityRef(e);
if (typeof e !== 'string') {
cache.setEntity(e);
}
return entityRef;
}),
);
}}
filterOptions={x => x}
renderOption={(entity, { selected }) => {
return <RenderOptionLabel entity={entity} isSelected={selected} />;
}}
size="small"
popupIcon={<ExpandMoreIcon data-testid="owner-picker-expand" />}
renderInput={params => (
<TextField
{...params}
className={classes.input}
onChange={e => {
setText(e.currentTarget.value);
}}
variant="outlined"
/>
)}
ListboxProps={{
onScroll: (e: React.MouseEvent) => {
const element = e.currentTarget;
const hasReachedEnd =
Math.abs(
element.scrollHeight -
element.clientHeight -
element.scrollTop,
) < 1;
if (hasReachedEnd && value?.cursor) {
handleFetch({ items: value.items, cursor: value.cursor });
if (typeof e !== 'string') {
cache.setEntity(e);
}
},
'data-testid': 'owner-picker-listbox',
}}
/>
</Typography>
return entityRef;
}),
);
}}
filterOptions={x => x}
renderOption={(entity, { selected }) => {
return <RenderOptionLabel entity={entity} isSelected={selected} />;
}}
name="owner-picker"
onInputChange={(_e, inputValue) => {
setText(inputValue);
}}
ListboxProps={{
onScroll: (e: React.MouseEvent) => {
const element = e.currentTarget;
const hasReachedEnd =
Math.abs(
element.scrollHeight - element.clientHeight - element.scrollTop,
) < 1;
if (hasReachedEnd && value?.cursor) {
handleFetch({ items: value.items, cursor: value.cursor });
}
},
'data-testid': 'owner-picker-listbox',
}}
/>
</Box>
);
};
function Popper({ children }: PopperProps) {
return <div>{children as ReactNode}</div>;
}
@@ -18,15 +18,12 @@ import { EntityErrorFilter, EntityOrphanFilter } from '../../filters';
import Box from '@material-ui/core/Box';
import Checkbox from '@material-ui/core/Checkbox';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import TextField from '@material-ui/core/TextField';
import Typography from '@material-ui/core/Typography';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import { makeStyles } from '@material-ui/core/styles';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
import React, { useState, ReactNode } from 'react';
import React, { useState } from 'react';
import { useEntityList } from '../../hooks';
import Autocomplete from '@material-ui/lab/Autocomplete';
import { Autocomplete } from '@backstage/core-components';
import { catalogReactTranslationRef } from '../../translation';
import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
@@ -34,17 +31,9 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha';
export type CatalogReactEntityProcessingStatusPickerClassKey = 'input';
const useStyles = makeStyles(
(theme: Theme) =>
createStyles({
root: {},
input: {
backgroundColor: theme.palette.background.paper,
},
label: {
textTransform: 'none',
fontWeight: 'bold',
},
}),
{
root: {},
},
{ name: 'CatalogReactEntityProcessingStatusPickerPicker' },
);
@@ -77,47 +66,32 @@ export const EntityProcessingStatusPicker = () => {
return (
<Box className={classes.root} pb={1} pt={1}>
<Typography className={classes.label} variant="button" component="label">
{t('entityProcessingStatusPicker.title')}
<Autocomplete<string, true>
PopperComponent={popperProps => (
<div {...popperProps}>{popperProps.children as ReactNode}</div>
)}
multiple
disableCloseOnSelect
options={availableAdvancedItems}
value={selectedAdvancedItems}
onChange={(_: object, value: string[]) => {
setSelectedAdvancedItems(value);
orphanChange(value.includes('Is Orphan'));
errorChange(value.includes('Has Error'));
}}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
onClick={event => event.preventDefault()}
label={option}
/>
)}
size="small"
popupIcon={
<ExpandMoreIcon data-testid="processing-status-picker-expand" />
}
renderInput={params => (
<TextField
{...params}
className={classes.input}
variant="outlined"
/>
)}
/>
</Typography>
<Autocomplete<string, true>
label={t('entityProcessingStatusPicker.title')}
multiple
disableCloseOnSelect
options={availableAdvancedItems}
value={selectedAdvancedItems}
onChange={(_: object, value: string[]) => {
setSelectedAdvancedItems(value);
orphanChange(value.includes('Is Orphan'));
errorChange(value.includes('Has Error'));
}}
renderOption={(option, { selected }) => (
<FormControlLabel
control={
<Checkbox
icon={icon}
checkedIcon={checkedIcon}
checked={selected}
/>
}
onClick={event => event.preventDefault()}
label={option}
/>
)}
name="processing-status-picker"
/>
</Box>
);
};