diff --git a/awx/ui_next/src/components/FormField/TextAndCheckboxField.jsx b/awx/ui_next/src/components/FormField/TextAndCheckboxField.jsx
index e3ebe93acb..4d7a1c6f43 100644
--- a/awx/ui_next/src/components/FormField/TextAndCheckboxField.jsx
+++ b/awx/ui_next/src/components/FormField/TextAndCheckboxField.jsx
@@ -1,16 +1,19 @@
-import React, { useState } from 'react';
-import { useField, useFormikContext } from 'formik';
+import React from 'react';
+import { useField } from 'formik';
import { t } from '@lingui/macro';
-import { FormGroup, TextInput, Button } from '@patternfly/react-core';
+import {
+ FormGroup,
+ TextInput,
+ Button,
+ InputGroup as PFInputGroup,
+ Tooltip,
+} from '@patternfly/react-core';
import PFCheckIcon from '@patternfly/react-icons/dist/js/icons/check-icon';
import styled from 'styled-components';
import Popover from '../Popover';
-const InputWrapper = styled.span`
- && {
- display: flex;
- padding-bottom: 5px;
- }
+const InputGroup = styled(PFInputGroup)`
+ padding-bottom: 5px;
`;
const CheckIcon = styled(PFCheckIcon)`
color: var(--pf-c-button--m-plain--disabled--Color);
@@ -18,34 +21,17 @@ const CheckIcon = styled(PFCheckIcon)`
props.isSelected &&
`color: var(--pf-c-button--m-secondary--active--Color)`};
`;
-function TextAndCheckboxField({
- id,
- label,
- helperText,
- isRequired,
- isValid,
- tooltip,
- name,
- rows,
- ...rest
-}) {
- const { values: formikValues } = useFormikContext();
+function TextAndCheckboxField({ label, helperText, tooltip }) {
const [choicesField, choicesMeta, choicesHelpers] = useField('choices');
- // const [fields, setFields] = useState(choicesField.value.split('\n'));
- // const [defaultValue, setDefaultValue] = useState(
- // formikValues.default.split('\n')
- // );
- const [, , defaultHelpers] = useField('default');
-
- const [isNewValueChecked, setIsNewValueChecked] = useState(false);
- console.log('set');
+ const [typeField] = useField('type');
+ const [defaultField, , defaultHelpers] = useField('default');
const handleCheckboxChange = v =>
defaultSplit.includes(v)
? defaultHelpers.setValue(defaultSplit.filter(d => d !== v).join('\n'))
- : defaultHelpers.setValue(formikValues.default.concat(`\n${v}`));
+ : defaultHelpers.setValue(defaultField.value.concat(`\n${v}`));
const choicesSplit = choicesField.value.split('\n');
- const defaultSplit = formikValues.default.split('\n');
+ const defaultSplit = defaultField.value?.split('\n');
return (
}
>
- {choicesSplit
- .map((v, i) => (
-
- {
- defaultHelpers.setValue(
- defaultSplit.filter(d => d !== v).join('\n')
- );
+ {choicesSplit.map((v, i) => (
+
+ {
+ if (e.key === 'Enter' && i === choicesSplit.length - 1) {
+ choicesHelpers.setValue(choicesField.value.concat('\n'));
+ }
- const newFields = choicesSplit
- .map((choice, index) => (i === index ? value : choice))
+ if (e.key === 'Backspace' && v.length <= 1) {
+ const removeEmptyField = choicesSplit
+ .filter((choice, index) => index !== i)
.join('\n');
+ choicesHelpers.setValue(removeEmptyField);
+ }
+ }}
+ value={v}
+ onChange={value => {
+ defaultHelpers.setValue(
+ defaultSplit.filter(d => d !== v).join('\n')
+ );
- return value === ''
- ? choicesHelpers.setValue(
- choicesSplit.filter(d => d !== v).join('\n')
- )
- : choicesHelpers.setValue(newFields);
- }}
- />
+ const newFields = choicesSplit
+ .map((choice, index) => (i === index ? value : choice))
+ .join('\n');
+
+ return value === ''
+ ? choicesHelpers.setValue(
+ choicesSplit.filter(d => d !== v).join('\n')
+ )
+ : choicesHelpers.setValue(newFields);
+ }}
+ />
+
-
- ))
- .concat(
-
- {
- choicesHelpers.setValue([...choicesSplit, value].join('\n'));
- }}
- />
-
-
- )}
+
+
+ ))}
);
}
diff --git a/awx/ui_next/src/components/FormField/TextAndCheckboxField.test.jsx b/awx/ui_next/src/components/FormField/TextAndCheckboxField.test.jsx
new file mode 100644
index 0000000000..5db9cc954e
--- /dev/null
+++ b/awx/ui_next/src/components/FormField/TextAndCheckboxField.test.jsx
@@ -0,0 +1,168 @@
+import React from 'react';
+import { act } from 'react-dom/test-utils';
+import { Formik } from 'formik';
+import { mountWithContexts } from '../../../testUtils/enzymeHelpers';
+import TextAndCheckboxField from './TextAndCheckboxField';
+
+describe('', () => {
+ test('should activate default values, multiselect', async () => {
+ let wrapper;
+
+ act(() => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onChange')('alex')
+ );
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onKeyDown')({ key: 'Enter' })
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 3
+ );
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(1)
+ .prop('onChange')('spencer')
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 3
+ );
+ await act(() => wrapper.find('Button[ouiaId="spencer"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="spencer"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ });
+
+ test('should select default, multiplechoice', async () => {
+ let wrapper;
+
+ act(() => {
+ wrapper = mountWithContexts(
+
+
+
+ );
+ });
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onChange')('alex')
+ );
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 3
+ );
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onKeyDown')({ key: 'Enter' })
+ );
+ wrapper.update();
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(1)
+ .prop('onChange')('spencer')
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 3
+ );
+ await act(() => wrapper.find('Button[ouiaId="spencer"]').prop('onClick')());
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('Button[ouiaId="spencer"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ });
+});
diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx
index 99be1cffca..9eb2d6ebed 100644
--- a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx
+++ b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.jsx
@@ -104,20 +104,6 @@ function SurveyQuestionForm({
handleCancel,
submitError,
}) {
- const defaultIsNotAvailable = choices => {
- return defaultValue => {
- let errorMessage;
- const found = [...defaultValue].every(dA => {
- return choices.indexOf(dA) > -1;
- });
-
- if (!found) {
- errorMessage = t`Default choice must be answered from the choices listed.`;
- }
- return errorMessage;
- };
- };
-
return (
)}
{['multiplechoice', 'multiselect'].includes(formik.values.type) && (
- <>
-
-
- >
+
)}
diff --git a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.test.jsx b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.test.jsx
index ac52082b43..a063afabc2 100644
--- a/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.test.jsx
+++ b/awx/ui_next/src/screens/Template/Survey/SurveyQuestionForm.test.jsx
@@ -17,12 +17,15 @@ const noop = () => {};
async function selectType(wrapper, type) {
await act(async () => {
- wrapper.find('AnsibleSelect#question-type').invoke('onChange')({
- target: {
- name: 'type',
- value: type,
+ wrapper.find('AnsibleSelect#question-type').invoke('onChange')(
+ {
+ target: {
+ name: 'type',
+ value: type,
+ },
},
- });
+ type
+ );
});
wrapper.update();
}
@@ -146,12 +149,15 @@ describe('', () => {
});
await selectType(wrapper, 'multiplechoice');
- expect(wrapper.find('FormField#question-options').prop('type')).toEqual(
- 'textarea'
- );
- expect(wrapper.find('FormField#question-default').prop('type')).toEqual(
- 'text'
+ expect(wrapper.find('TextAndCheckboxField').length).toBe(1);
+ expect(wrapper.find('TextAndCheckboxField').find('TextInput').length).toBe(
+ 1
);
+ expect(
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('Button[aria-label="Click to toggle default value"]').length
+ ).toBe(1);
});
test('should provide fields for multi-select question', async () => {
@@ -168,12 +174,15 @@ describe('', () => {
});
await selectType(wrapper, 'multiselect');
- expect(wrapper.find('FormField#question-options').prop('type')).toEqual(
- 'textarea'
- );
- expect(wrapper.find('FormField#question-default').prop('type')).toEqual(
- 'textarea'
+ expect(wrapper.find('TextAndCheckboxField').length).toBe(1);
+ expect(wrapper.find('TextAndCheckboxField').find('TextInput').length).toBe(
+ 1
);
+ expect(
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('Button[aria-label="Click to toggle default value"]').length
+ ).toBe(1);
});
test('should provide fields for integer question', async () => {
@@ -225,7 +234,7 @@ describe('', () => {
wrapper.find('FormField#question-default input').prop('type')
).toEqual('number');
});
- test('should not throw validation error', async () => {
+ test('should activate default values, multiselect', async () => {
let wrapper;
act(() => {
@@ -239,25 +248,71 @@ describe('', () => {
});
await selectType(wrapper, 'multiselect');
await act(async () =>
- wrapper.find('textarea#question-options').simulate('change', {
- target: { value: 'a \n b', name: 'choices' },
- })
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onChange')('alex')
);
- await act(async () =>
- wrapper.find('textarea#question-options').simulate('change', {
- target: { value: 'b \n a', name: 'default' },
- })
- );
- wrapper.find('FormField#question-default').prop('validate')('b \n a', {});
wrapper.update();
expect(
wrapper
- .find('FormGroup[fieldId="question-default"]')
- .prop('helperTextInvalid')
- ).toBe(undefined);
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onKeyDown')({ key: 'Enter' })
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 2
+ );
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(1)
+ .prop('onChange')('spencer')
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 2
+ );
+ await act(() => wrapper.find('Button[ouiaId="spencer"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="spencer"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
});
- test('should throw validation error', async () => {
+ test('should select default, multiplechoice', async () => {
let wrapper;
act(() => {
@@ -269,23 +324,68 @@ describe('', () => {
/>
);
});
- await selectType(wrapper, 'multiselect');
+ await selectType(wrapper, 'multiplechoice');
await act(async () =>
- wrapper.find('textarea#question-options').simulate('change', {
- target: { value: 'a \n b', name: 'choices' },
- })
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onChange')('alex')
);
- await act(async () =>
- wrapper.find('textarea#question-default').simulate('change', {
- target: { value: 'c', name: 'default' },
- })
- );
- wrapper.find('FormField#question-default').prop('validate')('c', {});
wrapper.update();
expect(
wrapper
- .find('FormGroup[fieldId="question-default"]')
- .prop('helperTextInvalid')
- ).toBe('Default choice must be answered from the choices listed.');
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
+ await act(() => wrapper.find('Button[ouiaId="alex"]').prop('onClick')());
+ wrapper.update();
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 1
+ );
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(0)
+ .prop('onKeyDown')({ key: 'Enter' })
+ );
+ wrapper.update();
+ await act(async () =>
+ wrapper
+ .find('TextAndCheckboxField')
+ .find('TextAndCheckboxField')
+ .find('TextInput')
+ .at(1)
+ .prop('onChange')('spencer')
+ );
+ wrapper.update();
+ expect(wrapper.find('TextAndCheckboxField').find('InputGroup').length).toBe(
+ 2
+ );
+ await act(() => wrapper.find('Button[ouiaId="spencer"]').prop('onClick')());
+ wrapper.update();
+
+ expect(
+ wrapper
+ .find('Button[ouiaId="spencer"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(true);
+ expect(
+ wrapper
+ .find('Button[ouiaId="alex"]')
+ .find('CheckIcon')
+ .prop('isSelected')
+ ).toBe(false);
});
});