diff options
author | philippe-perrin-sonarsource <philippe.perrin@sonarsource.com> | 2019-07-17 11:52:13 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-07-18 20:21:12 +0200 |
commit | 6ddad7c6256d38730615495b0bf8e189fba147cc (patch) | |
tree | d3b0710175d8e181c7e8625c47fd8ac146683f92 /server/sonar-web | |
parent | e5129e41c9e33ae14fa8c753a2f1f58c5bfc1f84 (diff) | |
download | sonarqube-6ddad7c6256d38730615495b0bf8e189fba147cc.tar.gz sonarqube-6ddad7c6256d38730615495b0bf8e189fba147cc.zip |
Moved SelectList to sonar-ui-common
Diffstat (limited to 'server/sonar-web')
21 files changed, 35 insertions, 1375 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index 5c55d499fec..e5514be444a 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -35,7 +35,7 @@ "regenerator-runtime": "0.13.2", "remark-custom-blocks": "2.3.0", "remark-slug": "5.1.0", - "sonar-ui-common": "0.0.13", + "sonar-ui-common": "0.0.14", "unist-util-visit": "1.4.0", "valid-url": "1.0.9", "whatwg-fetch": "2.0.4" diff --git a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts index 991d3375ce3..288a9ca2001 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts @@ -63,6 +63,7 @@ import ActionsDropdown, { import RadioToggle from 'sonar-ui-common/components/controls/RadioToggle'; import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton'; import Select from 'sonar-ui-common/components/controls/Select'; +import SelectList from 'sonar-ui-common/components/controls/SelectList'; import SearchSelect from 'sonar-ui-common/components/controls/SearchSelect'; import throwGlobalError from '../../utils/throwGlobalError'; import addGlobalSuccessMessage from '../../utils/addGlobalSuccessMessage'; @@ -88,7 +89,6 @@ import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import Favorite from '../../../components/controls/Favorite'; import HomePageSelect from '../../../components/controls/HomePageSelect'; import BranchIcon from '../../../components/icons-components/BranchIcon'; -import SelectList from '../../../components/SelectList/SelectList'; import CoverageRating from '../../../components/ui/CoverageRating'; import NotFound from '../../../app/components/NotFound'; import A11ySkipTarget from '../a11y/A11ySkipTarget'; @@ -112,7 +112,11 @@ const exposeLibraries = () => { getRulesUrl }; global.SonarMeasures = { ...measures, formatMeasure }; - global.SonarRequest = { ...request, throwGlobalError, addGlobalSuccessMessage }; + global.SonarRequest = { + ...request, + throwGlobalError, + addGlobalSuccessMessage + }; global.SonarComponents = { A11ySkipTarget, ActionsDropdown, diff --git a/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx b/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx index 5bfeb5e20bf..71ef68106b4 100644 --- a/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx +++ b/server/sonar-web/src/main/js/apps/groups/components/EditMembersModal.tsx @@ -23,9 +23,9 @@ import { translate } from 'sonar-ui-common/helpers/l10n'; import { ResetButtonLink } from 'sonar-ui-common/components/controls/buttons'; import Modal from 'sonar-ui-common/components/controls/Modal'; import SelectList, { - Filter, + SelectListFilter, SelectListSearchParams -} from '../../../components/SelectList/SelectList'; +} from 'sonar-ui-common/components/controls/SelectList'; import { addUserToGroup, getUsersInGroup, removeUserFromGroup } from '../../../api/user_groups'; interface Props { @@ -154,7 +154,7 @@ export default class EditMembersModal extends React.PureComponent<Props, State> needToReload={ this.state.needToReload && this.state.lastSearchParams && - this.state.lastSearchParams.filter !== Filter.All + this.state.lastSearchParams.filter !== SelectListFilter.All } onSearch={this.fetchUsers} onSelect={this.handleSelect} diff --git a/server/sonar-web/src/main/js/apps/groups/components/__tests__/EditMembersModal-test.tsx b/server/sonar-web/src/main/js/apps/groups/components/__tests__/EditMembersModal-test.tsx index ed9e065877c..24e6f464d85 100644 --- a/server/sonar-web/src/main/js/apps/groups/components/__tests__/EditMembersModal-test.tsx +++ b/server/sonar-web/src/main/js/apps/groups/components/__tests__/EditMembersModal-test.tsx @@ -19,9 +19,9 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import SelectList, { SelectListFilter } from 'sonar-ui-common/components/controls/SelectList'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import EditMembersModal from '../EditMembersModal'; -import SelectList, { Filter } from '../../../../components/SelectList/SelectList'; import { getUsersInGroup, addUserToGroup, removeUserFromGroup } from '../../../../api/user_groups'; const organization = 'orga'; @@ -53,7 +53,7 @@ it('should render modal properly', async () => { .props() .onSearch({ query: '', - filter: Filter.Selected, + filter: SelectListFilter.Selected, page: 1, pageSize: 100 }); @@ -72,7 +72,7 @@ it('should render modal properly', async () => { p: 1, ps: 100, q: undefined, - selected: Filter.Selected + selected: SelectListFilter.Selected }) ); diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx index a64bf9ece87..05bb7d8e7d7 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/Projects.tsx @@ -21,9 +21,9 @@ import * as React from 'react'; import { find, without } from 'lodash'; import { translate } from 'sonar-ui-common/helpers/l10n'; import SelectList, { - Filter, + SelectListFilter, SelectListSearchParams -} from '../../../components/SelectList/SelectList'; +} from 'sonar-ui-common/components/controls/SelectList'; import { associateGateWithProject, dissociateGateWithProject, @@ -153,7 +153,7 @@ export default class Projects extends React.PureComponent<Props, State> { needToReload={ this.state.needToReload && this.state.lastSearchParams && - this.state.lastSearchParams.filter !== Filter.All + this.state.lastSearchParams.filter !== SelectListFilter.All } onSearch={this.fetchProjects} onSelect={this.handleSelect} diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Projects-test.tsx b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Projects-test.tsx index 572808e4466..fa12351ea98 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Projects-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/__tests__/Projects-test.tsx @@ -20,8 +20,8 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import SelectList, { SelectListFilter } from 'sonar-ui-common/components/controls/SelectList'; import Projects from '../Projects'; -import SelectList, { Filter } from '../../../../components/SelectList/SelectList'; import { mockQualityGate } from '../../../../helpers/testMocks'; import { searchProjects, @@ -56,7 +56,7 @@ it('should render correctly', async () => { .props() .onSearch({ query: '', - filter: Filter.Selected, + filter: SelectListFilter.Selected, page: 1, pageSize: 100 }); @@ -74,7 +74,7 @@ it('should render correctly', async () => { page: 1, pageSize: 100, query: undefined, - selected: Filter.Selected + selected: SelectListFilter.Selected }) ); expect(wrapper.state().needToReload).toBe(false); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.tsx index 5119c0a0b0d..d7ec7959131 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ChangeProjectsForm.tsx @@ -22,9 +22,9 @@ import { find, without } from 'lodash'; import { translate } from 'sonar-ui-common/helpers/l10n'; import Modal from 'sonar-ui-common/components/controls/Modal'; import SelectList, { - Filter, + SelectListFilter, SelectListSearchParams -} from '../../../components/SelectList/SelectList'; +} from 'sonar-ui-common/components/controls/SelectList'; import { Profile } from '../types'; import { associateProject, @@ -162,7 +162,7 @@ export default class ChangeProjectsForm extends React.PureComponent<Props, State needToReload={ this.state.needToReload && this.state.lastSearchParams && - this.state.lastSearchParams.filter !== Filter.All + this.state.lastSearchParams.filter !== SelectListFilter.All } onSearch={this.fetchProjects} onSelect={this.handleSelect} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx index 8bc50653e14..610f26ae122 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ChangeProjectsForm-test.tsx @@ -19,9 +19,9 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import SelectList, { SelectListFilter } from 'sonar-ui-common/components/controls/SelectList'; import { waitAndUpdate, click } from 'sonar-ui-common/helpers/testUtils'; import ChangeProjectsForm from '../ChangeProjectsForm'; -import SelectList, { Filter } from '../../../../components/SelectList/SelectList'; import { getProfileProjects, associateProject, @@ -55,7 +55,7 @@ it('should render correctly', async () => { .props() .onSearch({ query: '', - filter: Filter.Selected, + filter: SelectListFilter.Selected, page: 1, pageSize: 100 }); @@ -73,7 +73,7 @@ it('should render correctly', async () => { p: 1, ps: 100, q: undefined, - selected: Filter.Selected + selected: SelectListFilter.Selected }) ); expect(wrapper.state().needToReload).toBe(false); diff --git a/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx b/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx index 697c533e387..3de11517160 100644 --- a/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/GroupsForm.tsx @@ -22,9 +22,9 @@ import { find, without } from 'lodash'; import { translate } from 'sonar-ui-common/helpers/l10n'; import Modal from 'sonar-ui-common/components/controls/Modal'; import SelectList, { - Filter, + SelectListFilter, SelectListSearchParams -} from '../../../components/SelectList/SelectList'; +} from 'sonar-ui-common/components/controls/SelectList'; import { getUserGroups, UserGroup } from '../../../api/users'; import { addUserToGroup, removeUserFromGroup } from '../../../api/user_groups'; @@ -162,7 +162,7 @@ export default class GroupsForm extends React.PureComponent<Props, State> { needToReload={ this.state.needToReload && this.state.lastSearchParams && - this.state.lastSearchParams.filter !== Filter.All + this.state.lastSearchParams.filter !== SelectListFilter.All } onSearch={this.fetchUsers} onSelect={this.handleSelect} diff --git a/server/sonar-web/src/main/js/apps/users/components/__tests__/GroupsForm-test.tsx b/server/sonar-web/src/main/js/apps/users/components/__tests__/GroupsForm-test.tsx index e6cbf910c64..61c721b4ea8 100644 --- a/server/sonar-web/src/main/js/apps/users/components/__tests__/GroupsForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/users/components/__tests__/GroupsForm-test.tsx @@ -19,9 +19,9 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import SelectList, { SelectListFilter } from 'sonar-ui-common/components/controls/SelectList'; import { waitAndUpdate, click } from 'sonar-ui-common/helpers/testUtils'; import GroupsForm from '../GroupsForm'; -import SelectList, { Filter } from '../../../../components/SelectList/SelectList'; import { getUserGroups } from '../../../../api/users'; import { addUserToGroup, removeUserFromGroup } from '../../../../api/user_groups'; import { mockUser } from '../../../../helpers/testMocks'; @@ -70,7 +70,7 @@ it('should render correctly', async () => { .props() .onSearch({ query: '', - filter: Filter.Selected, + filter: SelectListFilter.Selected, page: 1, pageSize: 100 }); @@ -88,7 +88,7 @@ it('should render correctly', async () => { p: 1, ps: 100, q: undefined, - selected: Filter.Selected + selected: SelectListFilter.Selected }) ); expect(wrapper.state().needToReload).toBe(false); diff --git a/server/sonar-web/src/main/js/components/SelectList/SelectList.tsx b/server/sonar-web/src/main/js/components/SelectList/SelectList.tsx deleted file mode 100644 index 7cafc36df69..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/SelectList.tsx +++ /dev/null @@ -1,184 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import SearchBox from 'sonar-ui-common/components/controls/SearchBox'; -import RadioToggle from 'sonar-ui-common/components/controls/RadioToggle'; -import ListFooter from 'sonar-ui-common/components/controls/ListFooter'; -import SelectListListContainer from './SelectListListContainer'; -import './styles.css'; - -export enum Filter { - All = 'all', - Selected = 'selected', - Unselected = 'deselected' -} - -interface Props { - allowBulkSelection?: boolean; - elements: string[]; - elementsTotalCount?: number; - disabledElements?: string[]; - labelSelected?: string; - labelUnselected?: string; - labelAll?: string; - needToReload?: boolean; - onSearch: (searchParams: SelectListSearchParams) => Promise<void>; - onSelect: (element: string) => Promise<void>; - onUnselect: (element: string) => Promise<void>; - pageSize?: number; - readOnly?: boolean; - renderElement: (element: string) => React.ReactNode; - selectedElements: string[]; - withPaging?: boolean; -} - -export interface SelectListSearchParams { - filter: Filter; - page?: number; - pageSize?: number; - query: string; -} - -interface State { - lastSearchParams: SelectListSearchParams; - loading: boolean; -} - -const DEFAULT_PAGE_SIZE = 100; - -export default class SelectList extends React.PureComponent<Props, State> { - mounted = false; - - constructor(props: Props) { - super(props); - - this.state = { - lastSearchParams: { - filter: Filter.Selected, - page: 1, - pageSize: props.pageSize ? props.pageSize : DEFAULT_PAGE_SIZE, - query: '' - }, - loading: false - }; - } - - componentDidMount() { - this.mounted = true; - this.search({}); - } - - componentWillUnmount() { - this.mounted = false; - } - - getFilter = () => - this.state.lastSearchParams.query === '' ? this.state.lastSearchParams.filter : Filter.All; - - search = (searchParams: Partial<SelectListSearchParams>) => - this.setState( - prevState => ({ - loading: true, - lastSearchParams: { ...prevState.lastSearchParams, ...searchParams } - }), - () => - this.props - .onSearch({ - filter: this.getFilter(), - page: this.props.withPaging ? this.state.lastSearchParams.page : undefined, - pageSize: this.props.withPaging ? this.state.lastSearchParams.pageSize : undefined, - query: this.state.lastSearchParams.query - }) - .finally(() => { - if (this.mounted) { - this.setState({ loading: false }); - } - }) - ); - - changeFilter = (filter: Filter) => this.search({ filter, page: 1 }); - - handleQueryChange = (query: string) => this.search({ page: 1, query }); - - onLoadMore = () => - this.search({ - page: - this.state.lastSearchParams.page != null ? this.state.lastSearchParams.page + 1 : undefined - }); - - onReload = () => this.search({ page: 1 }); - - render() { - const { - labelSelected = translate('selected'), - labelUnselected = translate('unselected'), - labelAll = translate('all') - } = this.props; - const { filter } = this.state.lastSearchParams; - - const disabled = this.state.lastSearchParams.query !== ''; - - return ( - <div className="select-list"> - <div className="display-flex-center"> - <RadioToggle - className="spacer-right" - name="filter" - onCheck={this.changeFilter} - options={[ - { disabled, label: labelSelected, value: Filter.Selected }, - { disabled, label: labelUnselected, value: Filter.Unselected }, - { disabled, label: labelAll, value: Filter.All } - ]} - value={filter} - /> - <SearchBox - autoFocus={true} - loading={this.state.loading} - onChange={this.handleQueryChange} - placeholder={translate('search_verb')} - value={this.state.lastSearchParams.query} - /> - </div> - <SelectListListContainer - allowBulkSelection={this.props.allowBulkSelection} - disabledElements={this.props.disabledElements || []} - elements={this.props.elements} - filter={this.getFilter()} - onSelect={this.props.onSelect} - onUnselect={this.props.onUnselect} - readOnly={this.props.readOnly} - renderElement={this.props.renderElement} - selectedElements={this.props.selectedElements} - /> - {!!this.props.elementsTotalCount && ( - <ListFooter - count={this.props.elements.length} - loadMore={this.onLoadMore} - needReload={this.props.needToReload} - reload={this.onReload} - total={this.props.elementsTotalCount} - /> - )} - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/components/SelectList/SelectListListContainer.tsx b/server/sonar-web/src/main/js/components/SelectList/SelectListListContainer.tsx deleted file mode 100644 index 1398d3eb9f9..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/SelectListListContainer.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import * as classNames from 'classnames'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import Checkbox from 'sonar-ui-common/components/controls/Checkbox'; -import { Filter } from './SelectList'; -import SelectListListElement from './SelectListListElement'; - -interface Props { - allowBulkSelection?: boolean; - elements: string[]; - disabledElements: string[]; - filter: Filter; - onSelect: (element: string) => Promise<void>; - onUnselect: (element: string) => Promise<void>; - readOnly?: boolean; - renderElement: (element: string) => React.ReactNode; - selectedElements: string[]; -} - -interface State { - loading: boolean; -} - -export default class SelectListListContainer 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 }); - } - }; - - isDisabled = (element: string): boolean => { - return this.props.readOnly || this.props.disabledElements.includes(element); - }; - - isSelected = (element: string): boolean => { - return this.props.selectedElements.includes(element); - }; - - handleBulkChange = (checked: boolean) => { - this.setState({ loading: true }); - if (checked) { - Promise.all(this.props.elements.map(element => this.props.onSelect(element))) - .then(this.stopLoading) - .catch(this.stopLoading); - } else { - Promise.all(this.props.selectedElements.map(element => this.props.onUnselect(element))) - .then(this.stopLoading) - .catch(this.stopLoading); - } - }; - - 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')} - <DeferredSpinner className="spacer-left" loading={this.state.loading} timeout={10} /> - </span> - </Checkbox> - </li> - <li className="divider" /> - </> - ); - } - - render() { - const { allowBulkSelection, elements, filter } = this.props; - - return ( - <div className={classNames('select-list-list-container spacer-top')}> - <ul className="menu"> - {allowBulkSelection && - elements.length > 0 && - filter === Filter.All && - this.renderBulkSelector()} - {elements.map(element => ( - <SelectListListElement - disabled={this.isDisabled(element)} - element={element} - key={element} - onSelect={this.props.onSelect} - onUnselect={this.props.onUnselect} - renderElement={this.props.renderElement} - selected={this.isSelected(element)} - /> - ))} - </ul> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/components/SelectList/SelectListListElement.tsx b/server/sonar-web/src/main/js/components/SelectList/SelectListListElement.tsx deleted file mode 100644 index 2991f8b4f28..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/SelectListListElement.tsx +++ /dev/null @@ -1,76 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import * as classNames from 'classnames'; -import Checkbox from 'sonar-ui-common/components/controls/Checkbox'; - -interface Props { - active?: boolean; - disabled?: boolean; - element: string; - onSelect: (element: string) => Promise<void>; - onUnselect: (element: string) => Promise<void>; - renderElement: (element: string) => React.ReactNode; - 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() { - return ( - <li className={classNames({ 'select-list-list-disabled': this.props.disabled })}> - <Checkbox - checked={this.props.selected} - className={classNames('select-list-list-checkbox', { active: this.props.active })} - disabled={this.props.disabled} - loading={this.state.loading} - onCheck={this.handleCheck}> - <span className="little-spacer-left">{this.props.renderElement(this.props.element)}</span> - </Checkbox> - </li> - ); - } -} diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectList-test.tsx b/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectList-test.tsx deleted file mode 100644 index 2d1ab5a0c99..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectList-test.tsx +++ /dev/null @@ -1,148 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import { shallow } from 'enzyme'; -import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import SelectList, { Filter } 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); - - 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); -}); - -it('should cancel filter selection when search is active', async () => { - const spy = jest.fn().mockResolvedValue({}); - const wrapper = shallowRender({ onSearch: spy }); - wrapper.instance().changeFilter(Filter.Unselected); - await waitAndUpdate(wrapper); - - expect(spy).toHaveBeenCalledWith({ - query: '', - filter: Filter.Unselected, - page: undefined, - pageSize: undefined - }); - expect(wrapper).toMatchSnapshot(); - - const query = 'test'; - wrapper.instance().handleQueryChange(query); - expect(spy).toHaveBeenCalledWith({ - query, - filter: Filter.All, - page: undefined, - pageSize: undefined - }); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); - - wrapper.instance().handleQueryChange(''); - expect(spy).toHaveBeenCalledWith({ - query: '', - filter: Filter.Unselected, - page: undefined, - pageSize: undefined - }); - - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot(); -}); - -it('should display pagination element properly and call search method with correct parameters', () => { - const spy = jest.fn().mockResolvedValue({}); - const wrapper = shallowRender({ elementsTotalCount: 100, onSearch: spy, withPaging: true }); - expect(wrapper).toMatchSnapshot(); - expect(spy).toHaveBeenCalledWith({ - query: '', - filter: Filter.Selected, - page: 1, - pageSize: 100 - }); // Basic default call - - wrapper.instance().onLoadMore(); - expect(spy).toHaveBeenCalledWith({ - query: '', - filter: Filter.Selected, - page: 2, - pageSize: 100 - }); // Load more call - - wrapper.instance().onReload(); - expect(spy).toHaveBeenCalledWith({ - query: '', - filter: Filter.Selected, - page: 1, - pageSize: 100 - }); // Reload call - - wrapper.setProps({ needToReload: true }); - expect(wrapper).toMatchSnapshot(); -}); - -function shallowRender(props: Partial<SelectList['props']> = {}) { - return shallow<SelectList>( - <SelectList - disabledElements={disabledElements} - elements={elements} - onSearch={jest.fn(() => Promise.resolve())} - onSelect={jest.fn(() => Promise.resolve())} - onUnselect={jest.fn(() => Promise.resolve())} - renderElement={(foo: string) => foo} - selectedElements={selectedElements} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListContainer-test.tsx b/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListContainer-test.tsx deleted file mode 100644 index 496b9b49a9a..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListContainer-test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import { shallow } from 'enzyme'; -import SelectListListContainer from '../SelectListListContainer'; -import { Filter } from '../SelectList'; - -it('should render correctly', () => { - const wrapper = shallow( - <SelectListListContainer - allowBulkSelection={true} - disabledElements={[]} - elements={['foo', 'bar', 'baz']} - filter={Filter.All} - onSelect={jest.fn(() => Promise.resolve())} - onUnselect={jest.fn(() => Promise.resolve())} - renderElement={(foo: string) => foo} - selectedElements={['foo']} - /> - ); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListElement-test.tsx b/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListElement-test.tsx deleted file mode 100644 index 59569846591..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/SelectListListElement-test.tsx +++ /dev/null @@ -1,47 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 * as React from 'react'; -import { shallow } from 'enzyme'; -import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import SelectListListElement from '../SelectListListElement'; - -const listElement = ( - <SelectListListElement - element="foo" - key="foo" - onSelect={jest.fn(() => Promise.resolve())} - onUnselect={jest.fn(() => Promise.resolve())} - renderElement={(foo: string) => foo} - selected={false} - /> -); - -it('should display a loader when checking', async () => { - const wrapper = shallow<SelectListListElement>(listElement); - expect(wrapper).toMatchSnapshot(); - expect(wrapper.state().loading).toBe(false); - - (wrapper.instance() as SelectListListElement).handleCheck(true); - expect(wrapper.state().loading).toBe(true); - expect(wrapper).toMatchSnapshot(); - - await waitAndUpdate(wrapper); - expect(wrapper.state().loading).toBe(false); -}); diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectList-test.tsx.snap b/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectList-test.tsx.snap deleted file mode 100644 index f68fa52b1bf..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectList-test.tsx.snap +++ /dev/null @@ -1,558 +0,0 @@ -// 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" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="deselected" - /> - <SearchBox - autoFocus={true} - loading={false} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="deselected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "foo", - ] - } - /> -</div> -`; - -exports[`should cancel filter selection when search is active 2`] = ` -<div - className="select-list" -> - <div - className="display-flex-center" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": true, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": true, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": true, - "label": "all", - "value": "all", - }, - ] - } - value="deselected" - /> - <SearchBox - autoFocus={true} - loading={false} - onChange={[Function]} - placeholder="search_verb" - value="test" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="all" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "foo", - ] - } - /> -</div> -`; - -exports[`should cancel filter selection when search is active 3`] = ` -<div - className="select-list" -> - <div - className="display-flex-center" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="deselected" - /> - <SearchBox - autoFocus={true} - loading={false} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="deselected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "foo", - ] - } - /> -</div> -`; - -exports[`should display a loader when searching 1`] = ` -<div - className="select-list" -> - <div - className="display-flex-center" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="selected" - /> - <SearchBox - autoFocus={true} - loading={true} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="selected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "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" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="selected" - /> - <SearchBox - autoFocus={true} - loading={true} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="selected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "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" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="selected" - /> - <SearchBox - autoFocus={true} - loading={true} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="selected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "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" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="selected" - /> - <SearchBox - autoFocus={true} - loading={false} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - allowBulkSelection={true} - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="selected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - readOnly={true} - renderElement={[Function]} - selectedElements={ - Array [ - "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" - > - <RadioToggle - className="spacer-right" - disabled={false} - name="filter" - onCheck={[Function]} - options={ - Array [ - Object { - "disabled": false, - "label": "selected", - "value": "selected", - }, - Object { - "disabled": false, - "label": "unselected", - "value": "deselected", - }, - Object { - "disabled": false, - "label": "all", - "value": "all", - }, - ] - } - value="selected" - /> - <SearchBox - autoFocus={true} - loading={false} - onChange={[Function]} - placeholder="search_verb" - value="" - /> - </div> - <SelectListListContainer - disabledElements={ - Array [ - "bar", - ] - } - elements={ - Array [ - "foo", - "bar", - "baz", - ] - } - filter="selected" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selectedElements={ - Array [ - "foo", - ] - } - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListContainer-test.tsx.snap b/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListContainer-test.tsx.snap deleted file mode 100644 index 04b0553b72a..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListContainer-test.tsx.snap +++ /dev/null @@ -1,60 +0,0 @@ -// 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} - onCheck={[Function]} - thirdState={true} - > - <span - className="big-spacer-left" - > - bulk_change - <DeferredSpinner - className="spacer-left" - loading={false} - timeout={10} - /> - </span> - </Checkbox> - </li> - <li - className="divider" - /> - <SelectListListElement - disabled={false} - element="foo" - key="foo" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selected={true} - /> - <SelectListListElement - disabled={false} - element="bar" - key="bar" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selected={false} - /> - <SelectListListElement - disabled={false} - element="baz" - key="baz" - onSelect={[MockFunction]} - onUnselect={[MockFunction]} - renderElement={[Function]} - selected={false} - /> - </ul> -</div> -`; diff --git a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListElement-test.tsx.snap b/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListElement-test.tsx.snap deleted file mode 100644 index e5d4ba3601f..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/__tests__/__snapshots__/SelectListListElement-test.tsx.snap +++ /dev/null @@ -1,41 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should display a loader when checking 1`] = ` -<li - className="" -> - <Checkbox - checked={false} - className="select-list-list-checkbox" - loading={false} - onCheck={[Function]} - thirdState={false} - > - <span - className="little-spacer-left" - > - foo - </span> - </Checkbox> -</li> -`; - -exports[`should display a loader when checking 2`] = ` -<li - className="" -> - <Checkbox - checked={false} - className="select-list-list-checkbox" - loading={true} - onCheck={[Function]} - thirdState={false} - > - <span - className="little-spacer-left" - > - foo - </span> - </Checkbox> -</li> -`; diff --git a/server/sonar-web/src/main/js/components/SelectList/styles.css b/server/sonar-web/src/main/js/components/SelectList/styles.css deleted file mode 100644 index 40401ec06dd..00000000000 --- a/server/sonar-web/src/main/js/components/SelectList/styles.css +++ /dev/null @@ -1,62 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2019 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 { -} - -.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; -} diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index ede9c74a051..ec251b4e4e0 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -9263,10 +9263,10 @@ sockjs@0.3.19: faye-websocket "^0.10.0" uuid "^3.0.1" -sonar-ui-common@0.0.13: - version "0.0.13" - resolved "https://repox.jfrog.io/repox/api/npm/npm/sonar-ui-common/-/sonar-ui-common-0.0.13.tgz#b01255f32d7863e529117bfe715890edfcdb8c97" - integrity sha1-sBJV8y14Y+UpEXv+cViQ7fzbjJc= +sonar-ui-common@0.0.14: + version "0.0.14" + resolved "https://repox.jfrog.io/repox/api/npm/npm/sonar-ui-common/-/sonar-ui-common-0.0.14.tgz#56faa2ba62503c206e9894f55f36bd9ff4934257" + integrity sha1-VvqiumJQPCBumJT1Xza9n/STQlc= dependencies: "@types/react-select" "1.2.6" classnames "2.2.6" |