<Wrapper aria-label={label} role={role}>
{options.map((option) => (
<OptionButton
+ aria-checked={isRadioGroup ? option.value === value : undefined}
aria-controls={isRadioGroup ? undefined : getTabPanelId(String(option.value))}
aria-current={option.value === value}
data-value={option.value}
createGroupButton: byRole('button', { name: 'groups.create_group' }),
infoManageMode: byText(/groups\.page\.managed_description/),
description: byText('user_groups.page.description'),
- allFilter: byRole('button', { name: 'all' }),
- selectedFilter: byRole('button', { name: 'selected' }),
- unselectedFilter: byRole('button', { name: 'unselected' }),
+ allFilter: byRole('radio', { name: 'all' }),
+ selectedFilter: byRole('radio', { name: 'selected' }),
+ unselectedFilter: byRole('radio', { name: 'unselected' }),
+ localAndManagedFilter: byRole('button', { name: 'all' }),
managedFilter: byRole('button', { name: 'managed' }),
localFilter: byRole('button', { name: 'local' }),
searchInput: byRole('searchbox', { name: 'search.search_by_name' }),
it('should render list of all groups', async () => {
renderGroupsApp();
- await act(async () => expect(await ui.allFilter.find()).toBeInTheDocument());
+ await act(async () => expect(await ui.localAndManagedFilter.find()).toBeInTheDocument());
expect(ui.localGroupRowWithLocalBadge.get()).toBeInTheDocument();
expect(ui.managedGroupRow.get()).toBeInTheDocument();
placeholder={translate('search_verb')}
value={query}
/>
- <div className="select-list-list-container spacer-top">
+ <div className="select-list-list-container spacer-top sw-overflow-auto">
<Spinner loading={loading}>
<ul className="menu">
{users.map((user) => (
expect(screen.getAllByRole('checkbox')).toHaveLength(2);
// change tabs to show deselected projects
- await user.click(screen.getByRole('button', { name: 'quality_gates.projects.without' }));
+ await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.without' }));
expect(screen.getAllByRole('checkbox')).toHaveLength(2);
// change tabs to show all projects
- await user.click(screen.getByRole('button', { name: 'quality_gates.projects.all' }));
+ await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.all' }));
expect(screen.getAllByRole('checkbox')).toHaveLength(4);
});
expect(screen.getAllByRole('checkbox')).toHaveLength(1);
// change tabs to show deselected projects
- await user.click(screen.getByRole('button', { name: 'quality_gates.projects.without' }));
+ await user.click(screen.getByRole('radio', { name: 'quality_gates.projects.without' }));
const uncheckedProjects = screen.getAllByRole('checkbox')[0];
expect(screen.getAllByRole('checkbox')).toHaveLength(3);
closeButton: byRole('button', { name: 'close' }),
changeProjectsButton: byRole('button', { name: 'quality_profiles.change_projects' }),
changeButton: byRole('button', { name: 'change_verb' }),
- withoutFilterButton: byRole('button', { name: 'quality_gates.projects.without' }),
+ withoutFilterButton: byRole('radio', { name: 'quality_gates.projects.without' }),
changeParentButton: byRole('button', { name: 'quality_profiles.change_parent' }),
qualityProfileActions: byRole('button', {
name: /quality_profiles.actions/,
const ui = {
createUserButton: byRole('button', { name: 'users.create_user' }),
- allFilter: byRole('button', { name: 'all' }),
- selectedFilter: byRole('button', { name: 'selected' }),
- unselectedFilter: byRole('button', { name: 'unselected' }),
+ localAndManagedFilter: byRole('button', { name: 'all' }),
managedFilter: byRole('button', { name: 'managed' }),
localFilter: byRole('button', { name: 'local' }),
showMore: byRole('button', { name: 'show_more' }),
jackRow: byRole('row', { name: /Jack/ }),
dialogGroups: byRole('dialog', { name: 'users.update_groups' }),
+ allFilter: byRole('radio', { name: 'all' }),
+ selectedFilter: byRole('radio', { name: 'selected' }),
+ unselectedFilter: byRole('radio', { name: 'unselected' }),
+
getGroups: () => within(ui.dialogGroups.get()).getAllByRole('checkbox'),
dialogTokens: byRole('dialog', { name: 'users.tokens' }),
dialogPasswords: byRole('dialog', { name: 'my_profile.password.title' }),
const user = userEvent.setup();
renderUsersApp();
- await act(async () => user.click(await ui.allFilter.find()));
+ await user.click(await ui.localAndManagedFilter.find());
await act(async () => {
await selectEvent.select(ui.activityFilter.get(), 'users.activity_filter.inactive_users');
});
it('should render list of all users', async () => {
renderUsersApp();
- await act(async () => expect(await ui.allFilter.find()).toBeInTheDocument());
+ await act(async () => expect(await ui.localAndManagedFilter.find()).toBeInTheDocument());
expect(ui.aliceRowWithLocalBadge.get()).toBeInTheDocument();
expect(ui.bobRow.get()).toBeInTheDocument();
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import SelectListItem from './SelectListItem';
-
-interface Props {
- className?: string;
- items: string[];
- currentItem: string;
- onSelect: (item: string) => void;
-}
-
-interface State {
- active: string;
-}
-
-export default class SelectList extends React.PureComponent<Props, State> {
- constructor(props: Props) {
- super(props);
- this.state = {
- active: props.currentItem,
- };
- }
-
- componentDidUpdate(prevProps: Props) {
- if (
- prevProps.currentItem !== this.props.currentItem &&
- !this.props.items.includes(this.state.active)
- ) {
- this.setState({ active: this.props.currentItem });
- }
- }
-
- handleSelect = (item: string) => {
- this.props.onSelect(item);
- };
-
- renderChild = (child: any) => {
- if (child == null) {
- return null;
- }
- // do not pass extra props to children like `<li className="divider" />`
- if (child.type !== SelectListItem) {
- return child;
- }
- return React.cloneElement(child, {
- active: this.state.active,
- onSelect: this.handleSelect,
- });
- };
-
- render() {
- const { children } = this.props;
- const hasChildren = React.Children.count(children) > 0;
- return (
- <ul className={classNames('menu', this.props.className)}>
- {hasChildren && React.Children.map(children, this.renderChild)}
- {!hasChildren &&
- this.props.items.map((item) => (
- <SelectListItem
- active={this.state.active}
- item={item}
- key={item}
- onSelect={this.handleSelect}
- />
- ))}
- </ul>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import classNames from 'classnames';
-import * as React from 'react';
-import Tooltip from '../../components/controls/Tooltip';
-import { ButtonPlain } from '../controls/buttons';
-
-interface Props {
- active?: string;
- className?: string;
- item: string;
- onSelect?: (item: string) => void;
- title?: React.ReactNode;
-}
-
-interface State {
- selected: boolean;
-}
-
-export default class SelectListItem extends React.PureComponent<Props, State> {
- constructor(props: Props) {
- super(props);
- this.state = {
- selected: false,
- };
- }
-
- handleSelect = () => {
- if (this.props.onSelect) {
- this.props.onSelect(this.props.item);
- }
- };
-
- handleHover = () => {
- this.setState({ selected: true });
- };
-
- handleBlur = () => {
- this.setState({ selected: false });
- };
-
- renderLink() {
- const children = this.props.children || this.props.item;
- const { selected } = this.state;
- return (
- <li>
- <ButtonPlain
- preventDefault
- aria-selected={this.props.active === this.props.item}
- className={classNames(
- {
- active: this.props.active === this.props.item,
- hover: selected,
- },
- this.props.className,
- )}
- onClick={this.handleSelect}
- onFocus={this.handleHover}
- onBlur={this.handleBlur}
- onMouseOver={this.handleHover}
- onMouseLeave={this.handleBlur}
- >
- {children}
- </ButtonPlain>
- </li>
- );
- }
-
- render() {
- return (
- <Tooltip overlay={this.props.title || undefined} placement="right">
- {this.renderLink()}
- </Tooltip>
- );
- }
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import SelectList from '../SelectList';
-import SelectListItem from '../SelectListItem';
-
-it('should render correctly without children', () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render correctly with children', () => {
- const items = ['item', 'seconditem', 'third'];
- const children = items.map((item) => (
- <SelectListItem item={item} key={item}>
- <i className="myicon" />
- item
- </SelectListItem>
- ));
- const wrapper = shallowRender({ items }, children);
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SelectList['props']> = {}, children?: React.ReactNode) {
- return shallow<SelectList>(
- <SelectList
- currentItem="seconditem"
- items={['item', 'seconditem', 'third']}
- onSelect={jest.fn()}
- {...props}
- >
- {children}
- </SelectList>,
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import SelectListItem from '../SelectListItem';
-
-it('should render correctly without children', () => {
- expect(shallow(<SelectListItem item="myitem" />)).toMatchSnapshot();
-});
-
-it('should render correctly with children', () => {
- expect(
- shallow(
- <SelectListItem active="myitem" item="seconditem">
- <i className="custom-icon" />
- <p>seconditem</p>
- </SelectListItem>,
- ),
- ).toMatchSnapshot();
-});
-
-it('should render correctly with a tooltip', () => {
- expect(shallow(<SelectListItem item="myitem" title="my custom tooltip" />)).toMatchSnapshot();
-});
-
-it('should render with the active class', () => {
- expect(shallow(<SelectListItem active="myitem" item="myitem" />)).toMatchSnapshot();
-});
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly with children 1`] = `
-<ul
- className="menu"
->
- <SelectListItem
- active="seconditem"
- item="item"
- key=".$item"
- onSelect={[Function]}
- >
- <i
- className="myicon"
- />
- item
- </SelectListItem>
- <SelectListItem
- active="seconditem"
- item="seconditem"
- key=".$seconditem"
- onSelect={[Function]}
- >
- <i
- className="myicon"
- />
- item
- </SelectListItem>
- <SelectListItem
- active="seconditem"
- item="third"
- key=".$third"
- onSelect={[Function]}
- >
- <i
- className="myicon"
- />
- item
- </SelectListItem>
-</ul>
-`;
-
-exports[`should render correctly without children 1`] = `
-<ul
- className="menu"
->
- <SelectListItem
- active="seconditem"
- item="item"
- key="item"
- onSelect={[Function]}
- />
- <SelectListItem
- active="seconditem"
- item="seconditem"
- key="seconditem"
- onSelect={[Function]}
- />
- <SelectListItem
- active="seconditem"
- item="third"
- key="third"
- onSelect={[Function]}
- />
-</ul>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly with a tooltip 1`] = `
-<Tooltip
- overlay="my custom tooltip"
- placement="right"
->
- <li>
- <ButtonPlain
- aria-selected={false}
- onBlur={[Function]}
- onClick={[Function]}
- onFocus={[Function]}
- onMouseLeave={[Function]}
- onMouseOver={[Function]}
- preventDefault={true}
- >
- myitem
- </ButtonPlain>
- </li>
-</Tooltip>
-`;
-
-exports[`should render correctly with children 1`] = `
-<Tooltip
- placement="right"
->
- <li>
- <ButtonPlain
- aria-selected={false}
- onBlur={[Function]}
- onClick={[Function]}
- onFocus={[Function]}
- onMouseLeave={[Function]}
- onMouseOver={[Function]}
- preventDefault={true}
- >
- <i
- className="custom-icon"
- />
- <p>
- seconditem
- </p>
- </ButtonPlain>
- </li>
-</Tooltip>
-`;
-
-exports[`should render correctly without children 1`] = `
-<Tooltip
- placement="right"
->
- <li>
- <ButtonPlain
- aria-selected={false}
- onBlur={[Function]}
- onClick={[Function]}
- onFocus={[Function]}
- onMouseLeave={[Function]}
- onMouseOver={[Function]}
- preventDefault={true}
- >
- myitem
- </ButtonPlain>
- </li>
-</Tooltip>
-`;
-
-exports[`should render with the active class 1`] = `
-<Tooltip
- placement="right"
->
- <li>
- <ButtonPlain
- aria-selected={true}
- className="active"
- onBlur={[Function]}
- onClick={[Function]}
- onFocus={[Function]}
- onMouseLeave={[Function]}
- onMouseOver={[Function]}
- preventDefault={true}
- >
- myitem
- </ButtonPlain>
- </li>
-</Tooltip>
-`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-.select-list-container {
- min-width: 500px;
- box-sizing: border-box;
-}
-
-.select-list-control {
- margin-bottom: 10px;
- box-sizing: border-box;
-}
-
-.select-list-list-container {
- border: 1px solid #bfbfbf;
- box-sizing: border-box;
- height: 400px;
- overflow: auto;
-}
-
-.select-list-list-checkbox {
- display: flex !important;
- align-items: center;
-}
-
-.select-list-list-checkbox i {
- display: inline-block;
- vertical-align: middle;
- margin-right: 10px;
-}
-
-.select-list-list-disabled {
- cursor: not-allowed;
-}
-
-.select-list-list-disabled > a {
- pointer-events: none;
-}
-
-.select-list-list-item {
- display: inline-block;
- vertical-align: middle;
-}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { InputSearch, PageContentFontWrapper, ToggleButton } from 'design-system';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
-import ButtonToggle from './ButtonToggle';
import ListFooter from './ListFooter';
-import SearchBox from './SearchBox';
-import './SelectList.css';
import SelectListListContainer from './SelectListListContainer';
export enum SelectListFilter {
const disabled = this.state.lastSearchParams.query !== '';
return (
- <div className="select-list">
- <div className="display-flex-center">
- <span className="select-list-filter spacer-right">
- <ButtonToggle
- onCheck={this.changeFilter}
+ <PageContentFontWrapper className="it__select-list">
+ <div className="sw-flex sw-items-center">
+ <span className="sw-mr-2">
+ <ToggleButton
+ onChange={this.changeFilter}
disabled={disabled}
options={[
{ label: labelSelected, value: SelectListFilter.Selected },
value={filter}
/>
</span>
- <SearchBox
+ <InputSearch
autoFocus={autoFocusSearch}
loading={this.state.loading}
onChange={this.handleQueryChange}
needReload={this.props.needToReload}
reload={this.onReload}
total={this.props.elementsTotalCount}
+ useMIUIButtons
/>
)}
- </div>
+ </PageContentFontWrapper>
);
}
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
+import styled from '@emotion/styled';
+import { Checkbox, ListItem, UnorderedList, themeBorder } from 'design-system';
import { uniqueId } from 'lodash';
import * as React from 'react';
import { translate } from '../../helpers/l10n';
-import Spinner from '../ui/Spinner';
-import Checkbox from './Checkbox';
import { SelectListFilter } from './SelectList';
import SelectListListElement from './SelectListListElement';
renderBulkSelector() {
const { elements, readOnly, selectedElements } = this.props;
return (
- <>
- <li>
- <Checkbox
- checked={selectedElements.length > 0}
- disabled={this.state.loading || readOnly}
- onCheck={this.handleBulkChange}
- thirdState={selectedElements.length > 0 && elements.length !== selectedElements.length}
- >
- <span className="big-spacer-left">
- {translate('bulk_change')}
- <Spinner className="spacer-left" loading={this.state.loading} />
- </span>
- </Checkbox>
- </li>
- <li className="divider" />
- </>
+ <BorderedListItem className="sw-pb-4">
+ <Checkbox
+ checked={selectedElements.length > 0}
+ disabled={this.state.loading || readOnly}
+ onCheck={this.handleBulkChange}
+ thirdState={selectedElements.length > 0 && elements.length !== selectedElements.length}
+ loading={this.state.loading}
+ >
+ <span className="sw-ml-4">{translate('bulk_change')}</span>
+ </Checkbox>
+ </BorderedListItem>
);
}
const { allowBulkSelection, elements, filter } = this.props;
return (
- <div className={classNames('select-list-list-container spacer-top')}>
- <ul className="menu">
+ <ListContainer className="sw-mt-2 sw-p-3 sw-rounded-1 it__select-list-list-container">
+ <UnorderedList className="-sw-mt-3">
{allowBulkSelection &&
elements.length > 0 &&
filter === SelectListFilter.All &&
selected={this.isSelected(element)}
/>
))}
- </ul>
- </div>
+ </UnorderedList>
+ </ListContainer>
);
}
}
+
+const BorderedListItem = styled(ListItem)`
+ border-bottom: ${themeBorder('default', 'discreetBorder')};
+`;
+
+const ListContainer = styled.div`
+ overflow: auto;
+ height: 330px;
+ border: ${themeBorder('default')};
+`;
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import classNames from 'classnames';
+import { Checkbox, ListItem } from 'design-system';
import * as React from 'react';
-import Checkbox from './Checkbox';
interface Props {
- active?: boolean;
disabled?: boolean;
element: string;
onSelect: (element: string) => Promise<void>;
selected: boolean;
}
-interface State {
- loading: boolean;
-}
-
-export default class SelectListListElement extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: false };
-
- componentDidMount() {
- this.mounted = true;
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- stopLoading = () => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
- };
-
- handleCheck = (checked: boolean) => {
- this.setState({ loading: true });
- const request = checked ? this.props.onSelect : this.props.onUnselect;
- request(this.props.element).then(this.stopLoading, this.stopLoading);
- };
-
- render() {
- let item = this.props.renderElement(this.props.element);
- let extra;
- if (Array.isArray(item)) {
- extra = item[1];
- item = item[0];
- }
- return (
- <li
- className={classNames('display-flex-center', {
- 'select-list-list-disabled': this.props.disabled,
- })}
- >
- <Checkbox
- checked={this.props.selected}
- className={classNames('select-list-list-checkbox flex-1', {
- active: this.props.active,
- })}
- disabled={this.props.disabled}
- loading={this.state.loading}
- onCheck={this.handleCheck}
- >
- <span className="little-spacer-left">{item}</span>
- </Checkbox>
- {extra && <span className="select-list-list-extra">{extra}</span>}
- </li>
- );
+export default function SelectListListElement(props: Props) {
+ const { disabled, element, onSelect, onUnselect, renderElement, selected } = props;
+
+ const [loading, setLoading] = React.useState(false);
+
+ const handleCheck = React.useCallback(
+ (checked: boolean) => {
+ setLoading(true);
+ const request = checked ? onSelect : onUnselect;
+ request(element)
+ .then(() => setLoading(false))
+ .catch(() => setLoading(false));
+ },
+ [element, setLoading, onSelect, onUnselect],
+ );
+
+ let item = renderElement(element);
+ let extra;
+ if (Array.isArray(item)) {
+ extra = item[1];
+ item = item[0];
}
+ return (
+ <ListItem className="sw-flex sw-justify-between">
+ <Checkbox checked={selected} disabled={disabled} loading={loading} onCheck={handleCheck}>
+ <span className="sw-ml-4">{item}</span>
+ </Checkbox>
+ {extra && <span>{extra}</span>}
+ </ListItem>
+ );
}
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { shallow } from 'enzyme';
+import userEvent from '@testing-library/user-event';
import * as React from 'react';
-import { waitAndUpdate } from '../../../helpers/testUtils';
+import { renderComponent } from '../../../helpers/testReactTestingUtils';
+import { byRole, byText } from '../../../helpers/testSelector';
import SelectList, { SelectListFilter } from '../SelectList';
const elements = ['foo', 'bar', 'baz'];
const selectedElements = [elements[0]];
const disabledElements = [elements[1]];
-it('should display properly with basics features', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper.instance().mounted).toBe(true);
+it('should render with extra', () => {
+ renderSelectList({ renderElement: (element) => [element, 'with extra'] });
- expect(wrapper).toMatchSnapshot();
-
- wrapper.instance().componentWillUnmount();
- expect(wrapper.instance().mounted).toBe(false);
-});
-
-it('should display properly with advanced features', async () => {
- const wrapper = shallowRender({
- allowBulkSelection: true,
- elementsTotalCount: 125,
- pageSize: 10,
- readOnly: true,
- withPaging: true,
- });
- await waitAndUpdate(wrapper);
-
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should display a loader when searching', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper.state().loading).toBe(false);
-
- wrapper.instance().search({});
- expect(wrapper.state().loading).toBe(true);
- expect(wrapper).toMatchSnapshot();
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().loading).toBe(false);
+ expect(byText('with extra').getAll()).toHaveLength(3);
});
it('should cancel filter selection when search is active', async () => {
+ const user = userEvent.setup();
+
const spy = jest.fn().mockResolvedValue({});
- const wrapper = shallowRender({ onSearch: spy });
- wrapper.instance().changeFilter(SelectListFilter.Unselected);
- await waitAndUpdate(wrapper);
+ renderSelectList({ onSearch: spy });
+
+ await user.click(ui.filterToggle(SelectListFilter.Unselected).get());
expect(spy).toHaveBeenCalledWith({
query: '',
page: undefined,
pageSize: undefined,
});
- expect(wrapper).toMatchSnapshot();
const query = 'test';
- wrapper.instance().handleQueryChange(query);
+
+ await user.type(ui.searchInput.get(), query);
+
expect(spy).toHaveBeenCalledWith({
query,
filter: SelectListFilter.All,
pageSize: undefined,
});
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
+ await user.clear(ui.searchInput.get());
- wrapper.instance().handleQueryChange('');
expect(spy).toHaveBeenCalledWith({
query: '',
filter: SelectListFilter.Unselected,
page: undefined,
pageSize: undefined,
});
-
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
});
-it('should display pagination element properly and call search method with correct parameters', () => {
+it('should display pagination element properly and call search method with correct parameters', async () => {
+ const user = userEvent.setup();
const spy = jest.fn().mockResolvedValue({});
- const wrapper = shallowRender({ elementsTotalCount: 100, onSearch: spy, withPaging: true });
- expect(wrapper).toMatchSnapshot();
+ renderSelectList({
+ elementsTotalCount: 100,
+ onSearch: spy,
+ withPaging: true,
+ });
expect(spy).toHaveBeenCalledWith({
query: '',
filter: SelectListFilter.Selected,
pageSize: 100,
}); // Basic default call
- wrapper.instance().onLoadMore();
+ await user.click(ui.footerLoadMore.get());
+
expect(spy).toHaveBeenCalledWith({
query: '',
filter: SelectListFilter.Selected,
page: 2,
pageSize: 100,
}); // Load more call
+});
+
+it('should allow to reload when needed', async () => {
+ const user = userEvent.setup();
+ const spy = jest.fn().mockResolvedValue({});
+
+ renderSelectList({
+ elementsTotalCount: 100,
+ onSearch: spy,
+ needToReload: true,
+ withPaging: true,
+ });
- wrapper.instance().onReload();
expect(spy).toHaveBeenCalledWith({
query: '',
filter: SelectListFilter.Selected,
page: 1,
pageSize: 100,
- }); // Reload call
+ }); // Basic default call
+
+ await user.click(await ui.footerReload.find());
- wrapper.setProps({ needToReload: true });
- expect(wrapper).toMatchSnapshot();
+ expect(spy).toHaveBeenCalledWith({
+ query: '',
+ filter: SelectListFilter.Selected,
+ page: 1,
+ pageSize: 100,
+ }); // Reload call
});
-function shallowRender(props: Partial<SelectList['props']> = {}) {
- return shallow<SelectList>(
+function renderSelectList(props: Partial<SelectList['props']> = {}) {
+ return renderComponent(
<SelectList
disabledElements={disabledElements}
elements={elements}
/>,
);
}
+
+const ui = {
+ filterToggle: (filter: SelectListFilter) =>
+ byRole('radio', { name: filter === SelectListFilter.Unselected ? 'unselected' : filter }),
+
+ searchInput: byRole('searchbox', { name: 'search_verb' }),
+
+ footerLoadMore: byRole('button', { name: 'show_more' }),
+ footerReload: byRole('button', { name: 'reload' }),
+};
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { SelectListFilter } from '../SelectList';
-import SelectListListContainer from '../SelectListListContainer';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<SelectListListContainer['props']> = {}) {
- return shallow(
- <SelectListListContainer
- allowBulkSelection
- disabledElements={[]}
- elements={['foo', 'bar', 'baz']}
- filter={SelectListFilter.All}
- onSelect={jest.fn(() => Promise.resolve())}
- onUnselect={jest.fn(() => Promise.resolve())}
- readOnly={false}
- renderElement={(foo: string) => foo}
- selectedElements={['foo']}
- {...props}
- />,
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-import { shallow } from 'enzyme';
-import * as React from 'react';
-import { waitAndUpdate } from '../../../helpers/testUtils';
-import SelectListListElement from '../SelectListListElement';
-
-it('should display a loader when checking', async () => {
- const wrapper = shallowRender();
- expect(wrapper).toMatchSnapshot('default');
- expect(wrapper.state().loading).toBe(false);
-
- wrapper.instance().handleCheck(true);
- expect(wrapper.state().loading).toBe(true);
- expect(wrapper).toMatchSnapshot('loading');
-
- await waitAndUpdate(wrapper);
- expect(wrapper.state().loading).toBe(false);
-});
-
-it('should correctly handle a render callback that returns 2 elements', () => {
- const wrapper = shallowRender({
- renderElement: (foo: string) => [foo, 'extra info'],
- });
- expect(wrapper.find('.select-list-list-extra').exists()).toBe(true);
-});
-
-function shallowRender(props: Partial<SelectListListElement['props']> = {}) {
- return shallow<SelectListListElement>(
- <SelectListListElement
- element="foo"
- key="foo"
- onSelect={jest.fn(() => Promise.resolve())}
- onUnselect={jest.fn(() => Promise.resolve())}
- renderElement={(foo: string) => foo}
- selected={false}
- {...props}
- />,
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should cancel filter selection when search is active 1`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="deselected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={false}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="deselected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
-</div>
-`;
-
-exports[`should cancel filter selection when search is active 2`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={true}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="deselected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={false}
- onChange={[Function]}
- placeholder="search_verb"
- value="test"
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="all"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
-</div>
-`;
-
-exports[`should cancel filter selection when search is active 3`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="deselected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={false}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="deselected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
-</div>
-`;
-
-exports[`should display a loader when searching 1`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="selected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={true}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="selected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
-</div>
-`;
-
-exports[`should display pagination element properly and call search method with correct parameters 1`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="selected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={true}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="selected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
- <ListFooter
- count={3}
- loadMore={[Function]}
- reload={[Function]}
- total={100}
- />
-</div>
-`;
-
-exports[`should display pagination element properly and call search method with correct parameters 2`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="selected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={true}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="selected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
- <ListFooter
- count={3}
- loadMore={[Function]}
- needReload={true}
- reload={[Function]}
- total={100}
- />
-</div>
-`;
-
-exports[`should display properly with advanced features 1`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="selected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={false}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- allowBulkSelection={true}
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="selected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- readOnly={true}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
- <ListFooter
- count={3}
- loadMore={[Function]}
- reload={[Function]}
- total={125}
- />
-</div>
-`;
-
-exports[`should display properly with basics features 1`] = `
-<div
- className="select-list"
->
- <div
- className="display-flex-center"
- >
- <span
- className="select-list-filter spacer-right"
- >
- <ButtonToggle
- disabled={false}
- onCheck={[Function]}
- options={
- [
- {
- "label": "selected",
- "value": "selected",
- },
- {
- "label": "unselected",
- "value": "deselected",
- },
- {
- "label": "all",
- "value": "all",
- },
- ]
- }
- value="selected"
- />
- </span>
- <SearchBox
- autoFocus={true}
- loading={false}
- onChange={[Function]}
- placeholder="search_verb"
- value=""
- />
- </div>
- <SelectListListContainer
- disabledElements={
- [
- "bar",
- ]
- }
- elements={
- [
- "foo",
- "bar",
- "baz",
- ]
- }
- filter="selected"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selectedElements={
- [
- "foo",
- ]
- }
- />
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
- className="select-list-list-container spacer-top"
->
- <ul
- className="menu"
- >
- <li>
- <Checkbox
- checked={true}
- disabled={false}
- onCheck={[Function]}
- thirdState={true}
- >
- <span
- className="big-spacer-left"
- >
- bulk_change
- <Spinner
- className="spacer-left"
- loading={false}
- />
- </span>
- </Checkbox>
- </li>
- <li
- className="divider"
- />
- <SelectListListElement
- disabled={false}
- element="foo"
- key="1"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selected={true}
- />
- <SelectListListElement
- disabled={false}
- element="bar"
- key="2"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selected={false}
- />
- <SelectListListElement
- disabled={false}
- element="baz"
- key="3"
- onSelect={[MockFunction]}
- onUnselect={[MockFunction]}
- renderElement={[Function]}
- selected={false}
- />
- </ul>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display a loader when checking: default 1`] = `
-<li
- className="display-flex-center"
->
- <Checkbox
- checked={false}
- className="select-list-list-checkbox flex-1"
- loading={false}
- onCheck={[Function]}
- thirdState={false}
- >
- <span
- className="little-spacer-left"
- >
- foo
- </span>
- </Checkbox>
-</li>
-`;
-
-exports[`should display a loader when checking: loading 1`] = `
-<li
- className="display-flex-center"
->
- <Checkbox
- checked={false}
- className="select-list-list-checkbox flex-1"
- loading={true}
- onCheck={[Function]}
- thirdState={false}
- >
- <span
- className="little-spacer-left"
- >
- foo
- </span>
- </Checkbox>
-</li>
-`;