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:
parent
b5db7b8326
commit
bb8ceabbaf
@ -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,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -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>
|
||||||
|
@ -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();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user