1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01: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 => ({
selectOptionsLink: {
cursor: 'pointer',
fontSize: theme.fontSizes.bodySize,
},
}));

View File

@ -15,7 +15,11 @@ export const SelectAllButton: FC<SelectAllButtonProps> = ({
return (
<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'}
</Link>
</Box>

View File

@ -1,5 +1,5 @@
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 { render } from 'utils/testRenderer';
import {
@ -14,6 +14,7 @@ const mockProps: ISelectProjectInputProps = {
options: [
{ label: 'Project1', value: 'project1' },
{ label: 'Project2', value: 'project2' },
{ label: 'Project3', value: 'project3' },
],
defaultValue: ['*'],
onChange,
@ -59,4 +60,106 @@ describe('SelectProjectInput', () => {
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 { useStyles } from '../ApiTokenForm.styles';
import { SelectAllButton } from './SelectAllButton/SelectAllButton';
import ConditionallyRender from 'component/common/ConditionallyRender';
const ALL_PROJECTS = '*';
@ -47,7 +48,10 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
const [isWildcardSelected, selectWildcard] = useState(
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 = (
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 = (
option: IAutocompleteBoxOption,
{ selected }: AutocompleteRenderOptionState
@ -79,13 +91,14 @@ export const SelectProjectInput: VFC<ISelectProjectInputProps> = ({
const renderGroup = ({ key, children }: AutocompleteRenderGroupParams) => (
<Fragment key={key}>
<SelectAllButton
isAllSelected={isAllSelected}
onClick={() => {
setProjects(
isAllSelected ? [] : options.map(({ value }) => value)
);
}}
<ConditionallyRender
condition={options.length > 2}
show={
<SelectAllButton
isAllSelected={isAllSelected}
onClick={onSelectAllClick}
/>
}
/>
{children}
</Fragment>