1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-24 01:18:01 +02:00

fix: improve selecting projects

Adds tests to the form for creating API tokens.
This commit is contained in:
Tymoteusz Czech 2022-04-13 13:14:20 +02:00 committed by Tymoteusz Czech
parent b5db7b8326
commit bb8ceabbaf
4 changed files with 131 additions and 10 deletions

View File

@ -3,5 +3,6 @@ import { makeStyles } from '@material-ui/core/styles';
export const useStyles = makeStyles(theme => ({ export const useStyles = makeStyles(theme => ({
selectOptionsLink: { selectOptionsLink: {
cursor: 'pointer', cursor: 'pointer',
fontSize: theme.fontSizes.bodySize,
}, },
})); }));

View File

@ -15,7 +15,11 @@ export const SelectAllButton: FC<SelectAllButtonProps> = ({
return ( return (
<Box sx={{ ml: 3.5, my: 0.5 }}> <Box sx={{ ml: 3.5, my: 0.5 }}>
<Link onClick={onClick} className={styles.selectOptionsLink}> <Link
onClick={onClick}
className={styles.selectOptionsLink}
component="button"
>
{isAllSelected ? 'Deselect all' : 'Select all'} {isAllSelected ? 'Deselect all' : 'Select all'}
</Link> </Link>
</Box> </Box>

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { screen, within } from '@testing-library/react'; import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event'; import userEvent from '@testing-library/user-event';
import { render } from 'utils/testRenderer'; import { render } from 'utils/testRenderer';
import { import {
@ -14,6 +14,7 @@ const mockProps: ISelectProjectInputProps = {
options: [ options: [
{ label: 'Project1', value: 'project1' }, { label: 'Project1', value: 'project1' },
{ label: 'Project2', value: 'project2' }, { label: 'Project2', value: 'project2' },
{ label: 'Project3', value: 'project3' },
], ],
defaultValue: ['*'], defaultValue: ['*'],
onChange, onChange,
@ -59,4 +60,106 @@ describe('SelectProjectInput', () => {
expect(screen.getByLabelText('Projects')).toBeDisabled(); expect(screen.getByLabelText('Projects')).toBeDisabled();
}); });
it('renders with autocomplete enabled if default value is not a wildcard', () => {
render(
<SelectProjectInput {...mockProps} defaultValue={['project1']} />
);
const checkbox = screen.getByLabelText(
/all current and future projects/i
);
expect(checkbox).not.toBeChecked();
const selectInputContainer = screen.getByTestId('select-input');
const input = within(selectInputContainer).getByRole('textbox');
expect(input).toBeEnabled();
});
describe('Select/Deselect projects in dropdown', () => {
it('selects and deselects all options', async () => {
const user = userEvent.setup();
render(<SelectProjectInput {...mockProps} defaultValue={[]} />);
await user.click(screen.getByLabelText('Projects'));
let button = screen.getByRole('button', {
name: /select all/i,
});
expect(button).toBeInTheDocument();
await user.click(button);
expect(onChange).toHaveBeenCalledWith([
'project1',
'project2',
'project3',
]);
button = screen.getByRole('button', {
name: /deselect all/i,
});
expect(button).toBeInTheDocument();
await user.click(button);
expect(onChange).toHaveBeenCalledWith([]);
});
it("doesn't show up for less than 3 options", async () => {
const user = userEvent.setup();
render(
<SelectProjectInput
{...mockProps}
defaultValue={[]}
options={[
{ label: 'Project1', value: 'project1' },
{ label: 'Project2', value: 'project2' },
]}
/>
);
await user.click(screen.getByLabelText('Projects'));
const button = screen.queryByRole('button', {
name: /select all/i,
});
expect(button).not.toBeInTheDocument();
});
});
it('can filter options', async () => {
const user = userEvent.setup();
render(
<SelectProjectInput
{...mockProps}
defaultValue={[]}
options={[
{ label: 'Alpha', value: 'alpha' },
{ label: 'Bravo', value: 'bravo' },
{ label: 'Charlie', value: 'charlie' },
{ label: 'Alpaca', value: 'alpaca' },
]}
/>
);
const input = await screen.findByLabelText('Projects');
user.type(input, 'alp');
await waitFor(() => {
expect(screen.getByText('Alpha')).toBeVisible();
});
await waitFor(() => {
expect(screen.queryByText('Bravo')).not.toBeInTheDocument();
});
await waitFor(() => {
expect(screen.queryByText('Charlie')).not.toBeInTheDocument();
});
await waitFor(() => {
expect(screen.getByText('Alpaca')).toBeVisible();
});
user.clear(input);
user.type(input, 'bravo');
await waitFor(() => {
expect(screen.getByText('Bravo')).toBeVisible();
});
await waitFor(() => {
expect(screen.queryByText('Alpha')).not.toBeInTheDocument();
});
});
}); });

View File

@ -17,6 +17,7 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox';
import { IAutocompleteBoxOption } from 'component/common/AutocompleteBox/AutocompleteBox'; import { IAutocompleteBoxOption } from 'component/common/AutocompleteBox/AutocompleteBox';
import { useStyles } from '../ApiTokenForm.styles'; import { useStyles } from '../ApiTokenForm.styles';
import { SelectAllButton } from './SelectAllButton/SelectAllButton'; import { SelectAllButton } from './SelectAllButton/SelectAllButton';
import ConditionallyRender from 'component/common/ConditionallyRender';
const ALL_PROJECTS = '*'; const ALL_PROJECTS = '*';
@ -47,7 +48,10 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
const [isWildcardSelected, selectWildcard] = useState( const [isWildcardSelected, selectWildcard] = useState(
typeof defaultValue === 'string' || defaultValue.includes(ALL_PROJECTS) typeof defaultValue === 'string' || defaultValue.includes(ALL_PROJECTS)
); );
const isAllSelected = projects.length === options.length; const isAllSelected =
projects.length > 0 &&
projects.length === options.length &&
projects[0] !== ALL_PROJECTS;
const onAllProjectsChange = ( const onAllProjectsChange = (
e: ChangeEvent<HTMLInputElement>, e: ChangeEvent<HTMLInputElement>,
@ -62,6 +66,14 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
} }
}; };
const onSelectAllClick = () => {
const newProjects = isAllSelected
? []
: options.map(({ value }) => value);
setProjects(newProjects);
onChange(newProjects);
};
const renderOption = ( const renderOption = (
option: IAutocompleteBoxOption, option: IAutocompleteBoxOption,
{ selected }: AutocompleteRenderOptionState { selected }: AutocompleteRenderOptionState
@ -79,13 +91,14 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => ( const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
<Fragment key={key}> <Fragment key={key}>
<SelectAllButton <ConditionallyRender
isAllSelected={isAllSelected} condition={options.length > 2}
onClick={() => { show={
setProjects( <SelectAllButton
isAllSelected ? [] : options.map(({ value }) => value) isAllSelected={isAllSelected}
); onClick={onSelectAllClick}
}} />
}
/> />
{children} {children}
</Fragment> </Fragment>