diff --git a/__tests__/components/Lookup.test.jsx b/__tests__/components/Lookup.test.jsx index 03555d868b..d5623aad55 100644 --- a/__tests__/components/Lookup.test.jsx +++ b/__tests__/components/Lookup.test.jsx @@ -3,34 +3,41 @@ import { mount } from 'enzyme'; import { I18nProvider } from '@lingui/react'; import Lookup from '../../src/components/Lookup'; -let mockData = [{ name: 'foo', id: 0, isChecked: false }]; +let mockData = [{ name: 'foo', id: 1, isChecked: false }]; describe('', () => { test('initially renders succesfully', () => { mount( { }} + onLookupSave={() => { }} data={mockData} selected={[]} /> ); }); - test('calls "onLookup" when search icon is clicked', () => { - const spy = jest.spyOn(Lookup.prototype, 'onLookup'); + test('Opens modal when search icon is clicked', () => { + const spy = jest.spyOn(Lookup.prototype, 'handleModalToggle'); + const mockSelected = [{ name: 'foo', id: 1 }]; const wrapper = mount( { }} + onLookupSave={() => { }} data={mockData} - selected={[]} + selected={mockSelected} /> - ); + ).find('Lookup'); expect(spy).not.toHaveBeenCalled(); + expect(wrapper.state('lookupSelectedItems')).toEqual([]); const searchItem = wrapper.find('.pf-c-input-group__text#search'); searchItem.first().simulate('click'); expect(spy).toHaveBeenCalled(); + expect(wrapper.state('lookupSelectedItems')).toEqual([{ + id: 1, + name: 'foo' + }]); + expect(wrapper.state('isModalOpen')).toEqual(true); }); test('calls "toggleSelected" when a user changes a checkbox', () => { const spy = jest.spyOn(Lookup.prototype, 'toggleSelected'); @@ -38,7 +45,7 @@ describe('', () => { { }} + onLookupSave={() => { }} data={mockData} selected={[]} /> @@ -51,14 +58,15 @@ describe('', () => { }); test('calls "toggleSelected" when remove icon is clicked', () => { const spy = jest.spyOn(Lookup.prototype, 'toggleSelected'); - mockData = [{ name: 'foo', id: 0, isChecked: false }, { name: 'bar', id: 1, isChecked: true }]; + mockData = [{ name: 'foo', id: 1, isChecked: false }, { name: 'bar', id: 2, isChecked: true }]; + const mockSelected = [{ name: 'foo', id: 1 }, { name: 'bar', id: 2 }]; const wrapper = mount( { }} + onLookupSave={() => { }} data={mockData} - selected={[]} + selected={mockSelected} /> ); @@ -73,7 +81,7 @@ describe('', () => { { }} + onLookupSave={() => { }} data={mockData} selected={[]} /> @@ -83,4 +91,57 @@ describe('', () => { const pill = wrapper.find('span.awx-c-tag--pill'); expect(pill).toHaveLength(0); }); + test('toggleSelected successfully adds/removes row from lookupSelectedItems state', () => { + mockData = [{ name: 'foo', id: 1 }]; + const wrapper = mount( + + { }} + data={mockData} + selected={[]} + /> + + ).find('Lookup'); + wrapper.instance().toggleSelected({ + id: 1, + name: 'foo' + }); + expect(wrapper.state('lookupSelectedItems')).toEqual([{ + id: 1, + name: 'foo' + }]); + wrapper.instance().toggleSelected({ + id: 1, + name: 'foo' + }); + expect(wrapper.state('lookupSelectedItems')).toEqual([]); + }); + test('saveModal calls callback with selected items', () => { + mockData = [{ name: 'foo', id: 1 }]; + const onLookupSaveFn = jest.fn(); + const wrapper = mount( + + + + ).find('Lookup'); + wrapper.instance().toggleSelected({ + id: 1, + name: 'foo' + }); + expect(wrapper.state('lookupSelectedItems')).toEqual([{ + id: 1, + name: 'foo' + }]); + wrapper.instance().saveModal(); + expect(onLookupSaveFn).toHaveBeenCalledWith([{ + id: 1, + name: 'foo' + }]); + }); }); diff --git a/__tests__/pages/Organizations/screens/OrganizationAdd.test.jsx b/__tests__/pages/Organizations/screens/OrganizationAdd.test.jsx index b035c21a60..27e0b7b07b 100644 --- a/__tests__/pages/Organizations/screens/OrganizationAdd.test.jsx +++ b/__tests__/pages/Organizations/screens/OrganizationAdd.test.jsx @@ -93,6 +93,26 @@ describe('', () => { }); }); + test('updateSelectedInstanceGroups successfully sets selectedInstanceGroups state', () => { + const wrapper = mount( + + + + ).find('OrganizationAdd'); + wrapper.instance().updateSelectedInstanceGroups([ + { + id: 1, + name: 'foo' + } + ]); + expect(wrapper.state('selectedInstanceGroups')).toEqual([ + { + id: 1, + name: 'foo' + } + ]); + }); + test('onSelectChange successfully sets custom_virtualenv state', () => { const wrapper = mount( @@ -103,41 +123,6 @@ describe('', () => { expect(wrapper.state('custom_virtualenv')).toBe('foobar'); }); - test('onLookupChange successfully adds/removes row from selectedInstanceGroups state', () => { - const wrapper = mount( - - - - ).find('OrganizationAdd'); - wrapper.setState({ results: [{ - id: 1, - name: 'foo' - }] }); - wrapper.instance().onLookupChange({ - id: 1, - name: 'foo' - }); - expect(wrapper.state('results')).toEqual([{ - id: 1, - name: 'foo', - isChecked: true - }]); - expect(wrapper.state('selectedInstanceGroups')).toEqual([{ - id: 1, - name: 'foo' - }]); - wrapper.instance().onLookupChange({ - id: 1, - name: 'foo' - }); - expect(wrapper.state('results')).toEqual([{ - id: 1, - name: 'foo', - isChecked: false - }]); - expect(wrapper.state('selectedInstanceGroups')).toEqual([]); - }); - test('onSubmit posts instance groups from selectedInstanceGroups', async () => { const createOrganizationFn = jest.fn().mockResolvedValue({ data: { diff --git a/src/components/Lookup/Lookup.jsx b/src/components/Lookup/Lookup.jsx index e1170702bf..c4ffe97030 100644 --- a/src/components/Lookup/Lookup.jsx +++ b/src/components/Lookup/Lookup.jsx @@ -9,7 +9,7 @@ import { ToolbarGroup, } from '@patternfly/react-core'; import { I18n } from '@lingui/react'; -import { t } from '@lingui/macro'; +import { t, Trans } from '@lingui/macro'; import CheckboxListItem from '../ListItem'; @@ -20,30 +20,51 @@ class Lookup extends React.Component { super(props); this.state = { isModalOpen: false, + lookupSelectedItems: [] }; this.handleModalToggle = this.handleModalToggle.bind(this); - this.onLookup = this.onLookup.bind(this); this.wrapTags = this.wrapTags.bind(this); this.toggleSelected = this.toggleSelected.bind(this); - } - - onLookup () { - this.handleModalToggle(); + this.saveModal = this.saveModal.bind(this); } toggleSelected (row) { - const { lookupChange } = this.props; - lookupChange(row); + const { lookupSelectedItems } = this.state; + const selectedIndex = lookupSelectedItems + .findIndex(selectedRow => selectedRow.id === row.id); + if (selectedIndex > -1) { + lookupSelectedItems.splice(selectedIndex, 1); + this.setState({ lookupSelectedItems }); + } else { + this.setState(prevState => ({ + lookupSelectedItems: [...prevState.lookupSelectedItems, row] + })); + } } handleModalToggle () { + const { isModalOpen } = this.state; + const { selected } = this.props; + // Resets the selected items from parent state whenever modal is opened + // This handles the case where the user closes/cancels the modal and + // opens it again + if (!isModalOpen) { + this.setState({ lookupSelectedItems: [...selected] }); + } this.setState((prevState) => ({ isModalOpen: !prevState.isModalOpen, })); } + saveModal () { + const { onLookupSave } = this.props; + const { lookupSelectedItems } = this.state; + onLookupSave(lookupSelectedItems); + this.handleModalToggle(); + } + wrapTags (tags) { - return tags.filter(tag => tag.isChecked).map((tag) => ( + return tags.map(tag => ( {tag.name} -
{this.wrapTags(data)}
+
{this.wrapTags(selected)}
item.id === i.id)} onSelect={() => this.toggleSelected(i)} /> ))} - {selected.length > 0 && ( + {lookupSelectedItems.length > 0 && ( {({ i18n }) => ( @@ -94,10 +115,14 @@ class Lookup extends React.Component { - + - + diff --git a/src/pages/Organizations/screens/OrganizationAdd.jsx b/src/pages/Organizations/screens/OrganizationAdd.jsx index f6d251373b..cadd5d7c9e 100644 --- a/src/pages/Organizations/screens/OrganizationAdd.jsx +++ b/src/pages/Organizations/screens/OrganizationAdd.jsx @@ -34,11 +34,11 @@ class OrganizationAdd extends React.Component { this.handleChange = this.handleChange.bind(this); this.onSelectChange = this.onSelectChange.bind(this); - this.onLookupChange = this.onLookupChange.bind(this); this.onSubmit = this.onSubmit.bind(this); this.resetForm = this.resetForm.bind(this); this.onSuccess = this.onSuccess.bind(this); this.onCancel = this.onCancel.bind(this); + this.updateSelectedInstanceGroups = this.updateSelectedInstanceGroups.bind(this); } state = { @@ -65,30 +65,6 @@ class OrganizationAdd extends React.Component { this.setState({ custom_virtualenv: value }); } - onLookupChange (row) { - const { results, selectedInstanceGroups } = this.state; - const selectedIndex = selectedInstanceGroups - .findIndex(instanceGroup => instanceGroup.id === row.id); - const resultsIndex = results.findIndex(instanceGroup => instanceGroup.id === row.id); - if (selectedIndex > -1) { - // Remove it from the list of selected instance groups - selectedInstanceGroups.splice(selectedIndex, 1); - if (resultsIndex > -1) { - results[resultsIndex].isChecked = false; - } - this.setState({ selectedInstanceGroups, results }); - } else { - // Add it to the list of selected instance groups - if (resultsIndex > -1) { - results[resultsIndex].isChecked = true; - } - this.setState(prevState => ({ - results, - selectedInstanceGroups: [...prevState.selectedInstanceGroups, row] - })); - } - } - async onSubmit () { const { api } = this.props; const { name, description, custom_virtualenv } = this.state; @@ -128,6 +104,10 @@ class OrganizationAdd extends React.Component { history.push(`/organizations/${id}`); } + updateSelectedInstanceGroups (selectedInstanceGroups) { + this.setState({ selectedInstanceGroups }); + } + handleChange (_, evt) { this.setState({ [evt.target.name]: evt.target.value }); } @@ -184,7 +164,7 @@ class OrganizationAdd extends React.Component {