@@ -33,7 +33,7 @@ interface Props { | |||
interface State { | |||
loading: boolean; | |||
query?: string; | |||
query: string; | |||
searchResults: Array<UserBase | Group>; | |||
selection?: UserBase | Group; | |||
} | |||
@@ -44,6 +44,7 @@ export default class QualityGatePermissionsAddModal extends React.Component<Prop | |||
mounted = false; | |||
state: State = { | |||
loading: false, | |||
query: '', | |||
searchResults: [] | |||
}; | |||
@@ -53,7 +54,9 @@ export default class QualityGatePermissionsAddModal extends React.Component<Prop | |||
} | |||
componentDidMount() { | |||
const { query } = this.state; | |||
this.mounted = true; | |||
this.handleSearch(query); | |||
} | |||
componentWillUnmount() { | |||
@@ -85,10 +88,11 @@ export default class QualityGatePermissionsAddModal extends React.Component<Prop | |||
} | |||
}; | |||
handleInputChange = (query: string) => { | |||
this.setState({ query }); | |||
if (query.length > 1) { | |||
this.handleSearch(query); | |||
handleInputChange = (newQuery: string) => { | |||
const { query } = this.state; | |||
if (query !== newQuery) { | |||
this.setState({ query: newQuery }); | |||
this.handleSearch(newQuery); | |||
} | |||
}; | |||
@@ -23,7 +23,7 @@ import Modal from '../../../components/controls/Modal'; | |||
import SelectLegacy from '../../../components/controls/SelectLegacy'; | |||
import GroupIcon from '../../../components/icons/GroupIcon'; | |||
import Avatar from '../../../components/ui/Avatar'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { Group, isUser } from '../../../types/quality-gates'; | |||
import { UserBase } from '../../../types/types'; | |||
@@ -44,12 +44,11 @@ type Option = (UserBase | Group) & { value: string }; | |||
export default function QualityGatePermissionsAddModalRenderer( | |||
props: QualityGatePermissionsAddModalRendererProps | |||
) { | |||
const { loading, query = '', searchResults, selection, submitting } = props; | |||
const { loading, searchResults, selection, submitting } = props; | |||
const header = translate('quality_gates.permissions.grant'); | |||
const noResultsText = | |||
query.length === 1 ? translateWithParameters('select2.tooShort', 2) : translate('no_results'); | |||
const noResultsText = translate('no_results'); | |||
return ( | |||
<Modal contentLabel={header} onRequestClose={props.onClose}> |
@@ -38,12 +38,31 @@ it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should fetch users and groups on mount', async () => { | |||
(searchUsers as jest.Mock).mockResolvedValue({ users: [mockUserBase()] }); | |||
(searchGroups as jest.Mock).mockResolvedValue({ groups: [{ name: 'group' }] }); | |||
const wrapper = shallowRender(); | |||
expect(wrapper.state().loading).toBe(true); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper.state().loading).toBe(false); | |||
expect(searchUsers).toBeCalledWith({ gateName: 'qualitygate', q: '', selected: 'deselected' }); | |||
expect(searchGroups).toBeCalledWith({ | |||
gateName: 'qualitygate', | |||
q: '', | |||
selected: 'deselected' | |||
}); | |||
expect(wrapper.state().searchResults).toHaveLength(2); | |||
}); | |||
it('should fetch users and groups', async () => { | |||
(searchUsers as jest.Mock).mockResolvedValueOnce({ users: [mockUserBase()] }); | |||
(searchGroups as jest.Mock).mockResolvedValueOnce({ groups: [{ name: 'group' }] }); | |||
const wrapper = shallowRender(); | |||
const query = 'query'; | |||
wrapper.instance().handleSearch(query); | |||
@@ -71,13 +90,19 @@ it('should handle input change', () => { | |||
wrapper.instance().handleInputChange('a'); | |||
expect(wrapper.state().query).toBe('a'); | |||
expect(handleSearch).not.toBeCalled(); | |||
expect(handleSearch).toBeCalled(); | |||
const query = 'query'; | |||
wrapper.instance().handleInputChange(query); | |||
expect(wrapper.state().query).toBe(query); | |||
expect(handleSearch).toBeCalledWith(query); | |||
jest.clearAllMocks(); | |||
wrapper.instance().handleInputChange(query); // input change with same parameter | |||
expect(wrapper.state().query).toBe(query); | |||
expect(handleSearch).not.toBeCalled(); | |||
}); | |||
it('should handleSelection', () => { |
@@ -2,7 +2,7 @@ | |||
exports[`should render correctly 1`] = ` | |||
<QualityGatePermissionsAddModalRenderer | |||
loading={false} | |||
loading={true} | |||
onClose={[MockFunction]} | |||
onInputChange={[Function]} | |||
onSelection={[Function]} |
@@ -223,7 +223,7 @@ exports[`should render correctly: short query 1`] = ` | |||
clearable={false} | |||
filterOptions={[Function]} | |||
isLoading={false} | |||
noResultsText="select2.tooShort.2" | |||
noResultsText="no_results" | |||
onChange={[MockFunction]} | |||
onInputChange={[MockFunction]} | |||
optionRenderer={[Function]} |
@@ -22,7 +22,7 @@ import * as React from 'react'; | |||
import SelectLegacy from '../../../components/controls/SelectLegacy'; | |||
import GroupIcon from '../../../components/icons/GroupIcon'; | |||
import Avatar from '../../../components/ui/Avatar'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { UserSelected } from '../../../types/types'; | |||
import { Group } from './ProfilePermissions'; | |||
@@ -75,18 +75,16 @@ export default class ProfilePermissionsFormSelect extends React.PureComponent<Pr | |||
); | |||
}; | |||
handleInputChange = (query: string) => { | |||
this.setState({ query }); | |||
if (query.length > 1) { | |||
this.handleSearch(query); | |||
handleInputChange = (newQuery: string) => { | |||
const { query } = this.state; | |||
if (query !== newQuery) { | |||
this.setState({ query: newQuery }); | |||
this.handleSearch(newQuery); | |||
} | |||
}; | |||
render() { | |||
const noResultsText = | |||
this.state.query.length === 1 | |||
? translateWithParameters('select2.tooShort', 2) | |||
: translate('no_results'); | |||
const noResultsText = translate('no_results'); | |||
// create a uniq string both for users and groups | |||
const options = this.state.searchResults.map(r => ({ ...r, value: getStringValue(r) })); |
@@ -52,8 +52,13 @@ it('searches', () => { | |||
onSearch.mockClear(); | |||
wrapper.prop<Function>('onInputChange')('f'); | |||
expect(onSearch).not.toBeCalled(); | |||
expect(onSearch).toBeCalled(); | |||
wrapper.prop<Function>('onInputChange')('foo'); | |||
expect(onSearch).toBeCalledWith('foo'); | |||
onSearch.mockClear(); | |||
wrapper.prop<Function>('onInputChange')('foo'); | |||
expect(onSearch).not.toBeCalled(); | |||
}); |