mirror of
https://github.com/ansible/awx.git
synced 2026-01-10 15:32:07 -03:30
Adds tests and refines chip interaction in MultiSelect component
This commit is contained in:
parent
a577be906e
commit
74a1ebff32
@ -1,5 +1,7 @@
|
||||
import React, { Component, Fragment } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withI18n } from '@lingui/react';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { Chip, ChipGroup } from '@components/Chip';
|
||||
import {
|
||||
Dropdown as PFDropdown,
|
||||
@ -123,7 +125,7 @@ class MultiSelect extends Component {
|
||||
removeChip(e, item) {
|
||||
const { onRemoveItem } = this.props;
|
||||
const { chipItems } = this.state;
|
||||
const chips = chipItems.filter(chip => chip.name !== item.name);
|
||||
const chips = chipItems.filter(chip => chip.id !== item.id);
|
||||
|
||||
this.setState({ chipItems: chips });
|
||||
onRemoveItem(item);
|
||||
@ -199,4 +201,5 @@ class MultiSelect extends Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
export default MultiSelect;
|
||||
export { MultiSelect as _MultiSelect };
|
||||
export default withI18n()(withRouter(MultiSelect));
|
||||
|
||||
86
awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx
Normal file
86
awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx
Normal file
@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import MultiSelect, { _MultiSelect } from './MultiSelect';
|
||||
import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
|
||||
|
||||
describe('<MultiSelect />', () => {
|
||||
const associatedItems = [
|
||||
{ name: 'Foo', id: 1, organization: 1 },
|
||||
{ name: 'Bar', id: 2, organization: 1 },
|
||||
];
|
||||
const options = [{ name: 'Angry', id: 3 }, { name: 'Potato', id: 4 }];
|
||||
|
||||
test('Initially render successfully', () => {
|
||||
const renderChips = jest.spyOn(_MultiSelect.prototype, 'renderChips');
|
||||
const wrapper = mountWithContexts(
|
||||
<MultiSelect
|
||||
onAddNewItem={jest.fn()}
|
||||
onRemoveItem={jest.fn()}
|
||||
associatedItems={associatedItems}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
const component = wrapper.find('MultiSelect');
|
||||
|
||||
expect(renderChips).toBeCalled();
|
||||
expect(component.state().chipItems.length).toBe(2);
|
||||
});
|
||||
test('handleSelection add item to chipItems', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<MultiSelect
|
||||
onAddNewItem={jest.fn()}
|
||||
onRemoveItem={jest.fn()}
|
||||
associatedItems={associatedItems}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
const event = { preventDefault: () => {} };
|
||||
const component = wrapper.find('MultiSelect');
|
||||
component.instance().handleSelection(event, { name: 'Apollo', id: 5 });
|
||||
expect(component.state().chipItems.length).toBe(3);
|
||||
});
|
||||
test('handleAddItem adds a chip only when Tab is pressed', () => {
|
||||
const onAddNewItem = jest.fn();
|
||||
const wrapper = mountWithContexts(
|
||||
<MultiSelect
|
||||
onAddNewItem={onAddNewItem}
|
||||
onRemoveItem={jest.fn()}
|
||||
associatedItems={associatedItems}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
const event = {
|
||||
preventDefault: () => {},
|
||||
key: 'Tab',
|
||||
};
|
||||
const component = wrapper.find('MultiSelect');
|
||||
|
||||
component.setState({ input: 'newLabel' });
|
||||
component.update();
|
||||
component.instance().handleAddItem(event);
|
||||
expect(component.state().chipItems.length).toBe(3);
|
||||
expect(component.state().input.length).toBe(0);
|
||||
expect(component.state().isExpanded).toBe(false);
|
||||
expect(onAddNewItem).toBeCalled();
|
||||
});
|
||||
test('removeChip removes chip properly', () => {
|
||||
const onRemoveItem = jest.fn();
|
||||
|
||||
const wrapper = mountWithContexts(
|
||||
<MultiSelect
|
||||
onAddNewItem={jest.fn()}
|
||||
onRemoveItem={onRemoveItem}
|
||||
associatedItems={associatedItems}
|
||||
options={options}
|
||||
/>
|
||||
);
|
||||
const event = {
|
||||
preventDefault: () => {},
|
||||
};
|
||||
const component = wrapper.find('MultiSelect');
|
||||
component
|
||||
.instance()
|
||||
.removeChip(event, { name: 'Foo', id: 1, organization: 1 });
|
||||
expect(component.state().chipItems.length).toBe(1);
|
||||
expect(onRemoveItem).toBeCalled();
|
||||
});
|
||||
});
|
||||
@ -75,19 +75,22 @@ class JobTemplateForm extends Component {
|
||||
}
|
||||
|
||||
async loadLabels(QueryConfig) {
|
||||
const { loadedLabels } = this.state;
|
||||
this.setState({ contentError: null, hasContentLoading: true });
|
||||
let loadedLabels;
|
||||
try {
|
||||
const { data } = await LabelsAPI.read(QueryConfig);
|
||||
const labels = [...data.results];
|
||||
this.setState({ loadedLabels: loadedLabels.concat(labels) });
|
||||
loadedLabels = [...data.results];
|
||||
if (data.next && data.next.includes('page=2')) {
|
||||
this.loadLabels({
|
||||
const {
|
||||
data: { results },
|
||||
} = await LabelsAPI.read({
|
||||
page: 2,
|
||||
page_size: 200,
|
||||
order_by: 'name',
|
||||
});
|
||||
loadedLabels = loadedLabels.concat(results);
|
||||
}
|
||||
this.setState({ loadedLabels });
|
||||
} catch (err) {
|
||||
this.setState({ contentError: err });
|
||||
} finally {
|
||||
@ -116,19 +119,22 @@ class JobTemplateForm extends Component {
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
newLabels: [...newLabels, { associate: true, id: label.id }],
|
||||
newLabels: [
|
||||
...newLabels,
|
||||
{ name: label.name, associate: true, id: label.id },
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
disassociateLabel(label) {
|
||||
const { removedLabels, newLabels } = this.state;
|
||||
const isNewCreatedLabel = newLabels.some(
|
||||
newLabel => newLabel === label.name
|
||||
const { removedLabels, loadedLabels, newLabels } = this.state;
|
||||
const isNewCreatedLabel = loadedLabels.some(
|
||||
loadedLabel => loadedLabel.name !== label.name
|
||||
);
|
||||
if (isNewCreatedLabel) {
|
||||
const filteredLabels = newLabels.filter(
|
||||
newLabel => newLabel !== label.name
|
||||
newLabel => newLabel.name !== label.name
|
||||
);
|
||||
this.setState({ newLabels: filteredLabels });
|
||||
} else {
|
||||
@ -277,21 +283,17 @@ class JobTemplateForm extends Component {
|
||||
>
|
||||
<QuestionCircleIcon />
|
||||
</Tooltip>
|
||||
<Field
|
||||
render={() => (
|
||||
<MultiSelect
|
||||
onAddNewItem={this.handleNewLabel}
|
||||
onRemoveItem={this.disassociateLabel}
|
||||
associatedItems={template.summary_fields.labels.results}
|
||||
options={loadedLabels}
|
||||
/>
|
||||
)}
|
||||
<MultiSelect
|
||||
onAddNewItem={this.handleNewLabel}
|
||||
onRemoveItem={this.disassociateLabel}
|
||||
associatedItems={template.summary_fields.labels.results}
|
||||
options={loadedLabels}
|
||||
/>
|
||||
</FormGroup>
|
||||
</FormRow>
|
||||
<FormActionGroup
|
||||
onCancel={handleCancel}
|
||||
onSubmit={values => handleSubmit(values)}
|
||||
onSubmit={formik.handleSubmit}
|
||||
/>
|
||||
</Form>
|
||||
)}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import React from 'react';
|
||||
import { mountWithContexts } from '@testUtils/enzymeHelpers';
|
||||
import { sleep } from '@testUtils/testUtils';
|
||||
import JobTemplateForm from './JobTemplateForm';
|
||||
import JobTemplateForm, { _JobTemplateForm } from './JobTemplateForm';
|
||||
import { LabelsAPI } from '@api';
|
||||
|
||||
jest.mock('@api');
|
||||
|
||||
@ -19,10 +20,16 @@ describe('<JobTemplateForm />', () => {
|
||||
inventory: {
|
||||
id: 2,
|
||||
name: 'foo',
|
||||
organization_id: 1,
|
||||
},
|
||||
labels: { results: [{ name: 'Sushi', id: 1 }, { name: 'Major', id: 2 }] },
|
||||
},
|
||||
};
|
||||
beforeEach(() => {
|
||||
LabelsAPI.read.mockReturnValue({
|
||||
data: mockData.summary_fields.labels,
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
@ -36,6 +43,7 @@ describe('<JobTemplateForm />', () => {
|
||||
handleCancel={jest.fn()}
|
||||
/>
|
||||
);
|
||||
expect(LabelsAPI.read).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should update form values on input changes', async () => {
|
||||
@ -104,4 +112,59 @@ describe('<JobTemplateForm />', () => {
|
||||
wrapper.find('button[aria-label="Cancel"]').prop('onClick')();
|
||||
expect(handleCancel).toBeCalled();
|
||||
});
|
||||
|
||||
test('handleNewLabel should arrange new labels properly', async () => {
|
||||
const handleNewLabel = jest.spyOn(
|
||||
_JobTemplateForm.prototype,
|
||||
'handleNewLabel'
|
||||
);
|
||||
const event = { key: 'Tab' };
|
||||
const wrapper = mountWithContexts(
|
||||
<JobTemplateForm
|
||||
template={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const multiSelect = wrapper.find('MultiSelect');
|
||||
const component = wrapper.find('JobTemplateForm');
|
||||
|
||||
wrapper.setState({ newLabels: [], loadedLabels: [], removedLabels: [] });
|
||||
multiSelect.setState({ input: 'Foo' });
|
||||
|
||||
wrapper.find('input[aria-label="labels"]').prop('onKeyDown')(event);
|
||||
expect(handleNewLabel).toHaveBeenCalledWith('Foo');
|
||||
|
||||
component.instance().handleNewLabel({ name: 'Bar', id: 2 });
|
||||
expect(component.state().newLabels).toEqual([
|
||||
{ name: 'Foo', organization: 1 },
|
||||
{ associate: true, id: 2, name: 'Bar' },
|
||||
]);
|
||||
});
|
||||
test('disassociateLabel should arrange new labels properly', async () => {
|
||||
const wrapper = mountWithContexts(
|
||||
<JobTemplateForm
|
||||
template={mockData}
|
||||
handleSubmit={jest.fn()}
|
||||
handleCancel={jest.fn()}
|
||||
/>
|
||||
);
|
||||
const multiSelect = wrapper.find('MultiSelect');
|
||||
const component = wrapper.find('JobTemplateForm');
|
||||
|
||||
component.setState({
|
||||
newLabels: [{ name: 'Foo', id: 1 }],
|
||||
loadedLabels: [{ name: 'Bar', id: 3 }],
|
||||
removedLabels: [],
|
||||
});
|
||||
component.update();
|
||||
multiSelect.setState({ input: 'Wowza' });
|
||||
component.instance().disassociateLabel({ name: 'Foo', id: 1 });
|
||||
expect(component.state().newLabels.length).toBe(0);
|
||||
expect(component.state().removedLabels.length).toBe(0);
|
||||
|
||||
component.instance().disassociateLabel({ name: 'Bar', id: 3 });
|
||||
expect(component.state().newLabels.length).toBe(0);
|
||||
expect(component.state().removedLabels.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user