From 9edc686ab5b4954177c3f8293a4707c728c86464 Mon Sep 17 00:00:00 2001 From: Keith Grant Date: Tue, 27 Aug 2019 16:28:54 -0700 Subject: [PATCH] add generic onChange prop to MultiSelect --- .../CollapsibleSection/ExpandingContainer.jsx | 5 ++- .../components/MultiSelect/MultiSelect.jsx | 26 +++++++++---- .../MultiSelect/MultiSelect.test.jsx | 9 +++++ .../Template/shared/JobTemplateForm.jsx | 37 +++++++++++++++++++ 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/awx/ui_next/src/components/CollapsibleSection/ExpandingContainer.jsx b/awx/ui_next/src/components/CollapsibleSection/ExpandingContainer.jsx index 9ee27c52ff..7046a29f85 100644 --- a/awx/ui_next/src/components/CollapsibleSection/ExpandingContainer.jsx +++ b/awx/ui_next/src/components/CollapsibleSection/ExpandingContainer.jsx @@ -5,7 +5,9 @@ import styled from 'styled-components'; const Container = styled.div` margin: 15px 0; transition: all 0.2s ease-out; - overflow: hidden; + ${props => !props.isExpanded && ` + overflow: hidden; + `} `; function ExpandingContainer({ isExpanded, children }) { @@ -21,6 +23,7 @@ function ExpandingContainer({ isExpanded, children }) { css={` height: ${height}px; `} + isExpanded={isExpanded} > {children} diff --git a/awx/ui_next/src/components/MultiSelect/MultiSelect.jsx b/awx/ui_next/src/components/MultiSelect/MultiSelect.jsx index cb386b67b6..62352b0036 100644 --- a/awx/ui_next/src/components/MultiSelect/MultiSelect.jsx +++ b/awx/ui_next/src/components/MultiSelect/MultiSelect.jsx @@ -45,10 +45,17 @@ class MultiSelect extends Component { name: PropTypes.string.isRequired, }) ).isRequired, - onAddNewItem: PropTypes.func.isRequired, - onRemoveItem: PropTypes.func.isRequired, + onAddNewItem: PropTypes.func, + onRemoveItem: PropTypes.func, + onChange: PropTypes.func, }; + static defaultProps = { + onAddNewItem: () => {}, + onRemoveItem: () => {}, + onChange: () => {}, + } + constructor(props) { super(props); this.state = { @@ -92,19 +99,21 @@ class MultiSelect extends Component { handleSelection(e, item) { const { chipItems } = this.state; - const { onAddNewItem } = this.props; + const { onAddNewItem, onChange } = this.props; e.preventDefault(); + const items = chipItems.concat({ name: item.name, id: item.id }); this.setState({ - chipItems: chipItems.concat({ name: item.name, id: item.id }), + chipItems: items, isExpanded: false, }); onAddNewItem(item); + onChange(items); } handleAddItem(event) { const { input, chipItems } = this.state; - const { onAddNewItem } = this.props; + const { onAddNewItem, onChange } = this.props; const isIncluded = chipItems.some(chipItem => chipItem.name === input); if (!input) { @@ -120,12 +129,14 @@ class MultiSelect extends Component { } if (event.key === 'Enter') { event.preventDefault(); + const items = chipItems.concat({ name: input, id: input }); this.setState({ - chipItems: chipItems.concat({ name: input, id: input }), + chipItems: items, isExpanded: false, input: '', }); onAddNewItem(input); + onChange(items); } else if (event.key === 'Tab') { this.setState({ input: '' }); } @@ -136,12 +147,13 @@ class MultiSelect extends Component { } removeChip(e, item) { - const { onRemoveItem } = this.props; + const { onRemoveItem, onChange } = this.props; const { chipItems } = this.state; const chips = chipItems.filter(chip => chip.id !== item.id); this.setState({ chipItems: chips }); onRemoveItem(item); + onChange(chips); e.preventDefault(); } diff --git a/awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx b/awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx index 66996fcdb7..3832cea6fe 100644 --- a/awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx +++ b/awx/ui_next/src/components/MultiSelect/MultiSelect.test.jsx @@ -28,6 +28,7 @@ describe('', () => { expect(getInitialChipItems).toBeCalled(); expect(component.state().chipItems.length).toBe(2); }); + test('handleSelection add item to chipItems', async () => { const wrapper = mountWithContexts( ', () => { await sleep(1); expect(component.state().chipItems.length).toBe(2); }); + test('handleAddItem adds a chip only when Tab is pressed', () => { const onAddNewItem = jest.fn(); + const onChange = jest.fn(); const wrapper = mountWithContexts( @@ -68,14 +72,18 @@ describe('', () => { expect(component.state().input.length).toBe(0); expect(component.state().isExpanded).toBe(false); expect(onAddNewItem).toBeCalled(); + expect(onChange).toBeCalled(); }); + test('removeChip removes chip properly', () => { const onRemoveItem = jest.fn(); + const onChange = jest.fn(); const wrapper = mountWithContexts( @@ -89,5 +97,6 @@ describe('', () => { .removeChip(event, { name: 'Foo', id: 1, organization: 1 }); expect(component.state().chipItems.length).toBe(1); expect(onRemoveItem).toBeCalled(); + expect(onChange).toBeCalled(); }); }); diff --git a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx index c49a973976..518f0ebc72 100644 --- a/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx +++ b/awx/ui_next/src/screens/Template/shared/JobTemplateForm.jsx @@ -501,6 +501,7 @@ class JobTemplateForm extends Component { id="template-job-slicing" name="job_slice_count" type="number" + min="1" label={i18n._(t`Job Slicing`)} tooltip={i18n._(t`Divide the work done by this job template into the specified number of job slices, each running the @@ -551,6 +552,42 @@ class JobTemplateForm extends Component { t`Select the Instance Groups for this Organization to run on.` )} /> + + + + + + + + + + + +