diff --git a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorField.jsx b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorField.jsx
new file mode 100644
index 0000000000..d08ab12674
--- /dev/null
+++ b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorField.jsx
@@ -0,0 +1,74 @@
+import React from 'react';
+import {
+ string,
+ oneOfType,
+ object,
+ func,
+ bool,
+ node,
+ oneOf,
+ number,
+} from 'prop-types';
+import { useField } from 'formik';
+import { FormGroup } from '@patternfly/react-core';
+import CodeMirrorInput from './CodeMirrorInput';
+import { FieldTooltip } from '../FormField';
+
+function CodeMirrorField({
+ id,
+ name,
+ label,
+ tooltip,
+ helperText,
+ validate,
+ isRequired,
+ mode,
+ ...rest
+}) {
+ const [field, meta, helpers] = useField({ name, validate });
+ const isValid = !(meta.touched && meta.error);
+
+ return (
+ }
+ >
+ {
+ helpers.setValue(value);
+ }}
+ mode={mode}
+ />
+
+ );
+}
+CodeMirrorField.propTypes = {
+ helperText: string,
+ id: string.isRequired,
+ name: string.isRequired,
+ label: oneOfType([object, string]).isRequired,
+ validate: func,
+ isRequired: bool,
+ tooltip: node,
+ mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired,
+ rows: number,
+};
+
+CodeMirrorField.defaultProps = {
+ helperText: '',
+ validate: () => {},
+ isRequired: false,
+ tooltip: null,
+ rows: 5,
+};
+
+export default CodeMirrorField;
diff --git a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx
index d945462987..92a9071332 100644
--- a/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx
+++ b/awx/ui_next/src/components/CodeMirrorInput/CodeMirrorInput.jsx
@@ -107,6 +107,7 @@ CodeMirrorInput.propTypes = {
hasErrors: bool,
fullHeight: bool,
rows: number,
+ className: string,
};
CodeMirrorInput.defaultProps = {
readOnly: false,
@@ -114,6 +115,7 @@ CodeMirrorInput.defaultProps = {
rows: 6,
fullHeight: false,
hasErrors: false,
+ className: '',
};
export default CodeMirrorInput;
diff --git a/awx/ui_next/src/components/CodeMirrorInput/index.js b/awx/ui_next/src/components/CodeMirrorInput/index.js
index 9cad016228..2c60b806f5 100644
--- a/awx/ui_next/src/components/CodeMirrorInput/index.js
+++ b/awx/ui_next/src/components/CodeMirrorInput/index.js
@@ -1,6 +1,7 @@
import CodeMirrorInput from './CodeMirrorInput';
export default CodeMirrorInput;
+export { default as CodeMirrorField } from './CodeMirrorField';
export { default as VariablesDetail } from './VariablesDetail';
export { default as VariablesInput } from './VariablesInput';
export { default as VariablesField } from './VariablesField';
diff --git a/awx/ui_next/src/components/FormField/ArrayTextField.jsx b/awx/ui_next/src/components/FormField/ArrayTextField.jsx
new file mode 100644
index 0000000000..862d48901b
--- /dev/null
+++ b/awx/ui_next/src/components/FormField/ArrayTextField.jsx
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useField } from 'formik';
+import { FormGroup, TextArea } from '@patternfly/react-core';
+import FieldTooltip from './FieldTooltip';
+
+function ArrayTextField(props) {
+ const {
+ id,
+ helperText,
+ name,
+ label,
+ tooltip,
+ tooltipMaxWidth,
+ validate,
+ isRequired,
+ type,
+ ...rest
+ } = props;
+
+ const [field, meta, helpers] = useField({ name, validate });
+ const isValid = !(meta.touched && meta.error);
+ const value = field.value || [];
+
+ return (
+ }
+ >
+