diff --git a/src/components/CodeMirrorInput/VariablesInput.jsx b/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx
similarity index 81%
rename from src/components/CodeMirrorInput/VariablesInput.jsx
rename to awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx
index 2d4e96e3f7..efd43f643e 100644
--- a/src/components/CodeMirrorInput/VariablesInput.jsx
+++ b/awx/ui_next/src/components/CodeMirrorInput/VariablesInput.jsx
@@ -3,7 +3,7 @@ import { string, func, bool, number } from 'prop-types';
import { Button, Split, SplitItem } from '@patternfly/react-core';
import styled from 'styled-components';
import ButtonGroup from '@components/ButtonGroup';
-import { yamlToJson, jsonToYaml } from '@util/yaml';
+import { yamlToJson, jsonToYaml, isJson } from '@util/yaml';
import CodeMirrorInput from './CodeMirrorInput';
const YAML_MODE = 'yaml';
@@ -15,8 +15,19 @@ const SmallButton = styled(Button)`
`;
function VariablesInput (props) {
- const { id, label, value, readOnly, rows, error, onChange, onError, className } = props;
- const [mode, setMode] = useState(YAML_MODE);
+ const { id, label, readOnly, rows, error, onError, className } = props;
+ // eslint-disable-next-line react/destructuring-assignment
+ const [value, setValue] = useState(props.value);
+ const [mode, setMode] = useState(isJson(value) ? JSON_MODE : YAML_MODE);
+ // eslint-disable-next-line react/destructuring-assignment
+ const isControlled = !!props.onChange;
+
+ const onChange = (newValue) => {
+ if (isControlled) {
+ props.onChange(newValue);
+ }
+ setValue(newValue);
+ };
return (
@@ -88,7 +99,7 @@ VariablesInput.propTypes = {
};
VariablesInput.defaultProps = {
readOnly: false,
- onChange: () => {},
+ onChange: null,
rows: 6,
error: null,
onError: () => {},
diff --git a/awx/ui_next/src/util/strings.js b/awx/ui_next/src/util/strings.js
index cc5e00e68e..588bc8b7b6 100644
--- a/awx/ui_next/src/util/strings.js
+++ b/awx/ui_next/src/util/strings.js
@@ -14,9 +14,13 @@ export function ucFirst(str) {
return `${str[0].toUpperCase()}${str.substr(1)}`;
}
-export const toTitleCase = string =>
- string
+export const toTitleCase = string => {
+ if (!string) {
+ return '';
+ }
+ return string
.toLowerCase()
.split('_')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
+};
diff --git a/awx/ui_next/src/util/strings.test.js b/awx/ui_next/src/util/strings.test.js
index ec2e6f3c13..b69db2f489 100644
--- a/awx/ui_next/src/util/strings.test.js
+++ b/awx/ui_next/src/util/strings.test.js
@@ -1,4 +1,4 @@
-import { pluralize, getArticle, ucFirst } from './strings';
+import { pluralize, getArticle, ucFirst, toTitleCase } from './strings';
describe('string utils', () => {
describe('pluralize', () => {
@@ -31,4 +31,14 @@ describe('string utils', () => {
expect(ucFirst('team')).toEqual('Team');
});
});
+
+ describe('toTitleCase', () => {
+ test('should upper case each word', () => {
+ expect(toTitleCase('a_string_of_words')).toEqual('A String Of Words');
+ });
+
+ test('should return empty string for undefined', () => {
+ expect(toTitleCase(undefined)).toEqual('');
+ });
+ });
});
diff --git a/awx/ui_next/src/util/yaml.js b/awx/ui_next/src/util/yaml.js
index 22a2cf9815..27b99d0b63 100644
--- a/awx/ui_next/src/util/yaml.js
+++ b/awx/ui_next/src/util/yaml.js
@@ -21,3 +21,15 @@ export function jsonToYaml(jsonString) {
}
return yaml.safeDump(value);
}
+
+export function isJson (jsonString) {
+ if (typeof jsonString !== 'string') { return false; }
+ let value;
+ try {
+ value = JSON.parse(jsonString);
+ } catch (e) {
+ return false;
+ }
+
+ return typeof value === 'object' && value !== null;
+}