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.`
)}
/>
+
+
+
+
+
+
+
+
+
+
+
+