mirror of
https://github.com/ansible/awx.git
synced 2026-05-16 13:57:39 -02:30
Add JSON/YAML components (#267)
Add CodeMirrorInput and VariablesField Add components for syntax highlighting, YAML/JSON toggle
This commit is contained in:
70
__tests__/util/yaml.test.js
Normal file
70
__tests__/util/yaml.test.js
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { yamlToJson, jsonToYaml } from '../../src/util/yaml';
|
||||||
|
|
||||||
|
describe('yamlToJson', () => {
|
||||||
|
test('should convert to json', () => {
|
||||||
|
const yaml = `
|
||||||
|
---
|
||||||
|
one: 1
|
||||||
|
two: two
|
||||||
|
`;
|
||||||
|
expect(yamlToJson(yaml)).toEqual(`{
|
||||||
|
"one": 1,
|
||||||
|
"two": "two"
|
||||||
|
}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should remove comments', () => {
|
||||||
|
const yaml = `
|
||||||
|
---
|
||||||
|
one: 1
|
||||||
|
# comment
|
||||||
|
two: two
|
||||||
|
# comment two
|
||||||
|
`;
|
||||||
|
expect(yamlToJson(yaml)).toEqual(`{
|
||||||
|
"one": 1,
|
||||||
|
"two": "two"
|
||||||
|
}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert empty string to {}', () => {
|
||||||
|
expect(yamlToJson('')).toEqual('{}');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert null to {}', () => {
|
||||||
|
expect(yamlToJson(null)).toEqual('{}');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert empty yaml to {}', () => {
|
||||||
|
expect(yamlToJson('---')).toEqual('{}');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw if invalid yaml given', () => {
|
||||||
|
expect(() => yamlToJson('foo')).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('jsonToYaml', () => {
|
||||||
|
test('should convert to yaml', () => {
|
||||||
|
const json = `{
|
||||||
|
"one": 1,
|
||||||
|
"two": "two"
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
expect(jsonToYaml(json)).toEqual(`one: 1
|
||||||
|
two: two
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert empty object to empty yaml doc', () => {
|
||||||
|
expect(jsonToYaml('{}')).toEqual('---\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should convert empty string to empty yaml doc', () => {
|
||||||
|
expect(jsonToYaml('')).toEqual('---\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw if invalid json given', () => {
|
||||||
|
expect(() => jsonToYaml('bad data')).toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -8,12 +8,15 @@ module.exports = {
|
|||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.(css|scss|less)$': '<rootDir>/__mocks__/styleMock.js'
|
'\\.(css|scss|less)$': '<rootDir>/__mocks__/styleMock.js'
|
||||||
},
|
},
|
||||||
|
setupFiles: [
|
||||||
|
'@nteract/mockument'
|
||||||
|
],
|
||||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||||
snapshotSerializers: [
|
snapshotSerializers: [
|
||||||
"enzyme-to-json/serializer"
|
"enzyme-to-json/serializer"
|
||||||
],
|
],
|
||||||
testMatch: [
|
testMatch: [
|
||||||
'<rootDir>/__tests__/**/*.test.{js,jsx}'
|
'<rootDir>/**/*.test.{js,jsx}'
|
||||||
],
|
],
|
||||||
testEnvironment: 'jsdom',
|
testEnvironment: 'jsdom',
|
||||||
testURL: 'http://127.0.0.1:3001',
|
testURL: 'http://127.0.0.1:3001',
|
||||||
|
|||||||
18
package-lock.json
generated
18
package-lock.json
generated
@@ -1774,6 +1774,12 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@nteract/mockument": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@nteract/mockument/-/mockument-1.0.4.tgz",
|
||||||
|
"integrity": "sha1-9/hf2T5Dgo7HQcX0xXMRgu2w7LI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
"@patternfly/patternfly": {
|
"@patternfly/patternfly": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@patternfly/patternfly/-/patternfly-2.7.0.tgz",
|
||||||
@@ -3295,7 +3301,7 @@
|
|||||||
},
|
},
|
||||||
"babel-plugin-syntax-object-rest-spread": {
|
"babel-plugin-syntax-object-rest-spread": {
|
||||||
"version": "6.13.0",
|
"version": "6.13.0",
|
||||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
|
"resolved": "http://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz",
|
||||||
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
|
"integrity": "sha1-/WU28rzhODb/o6VFjEkDpZe7O/U="
|
||||||
},
|
},
|
||||||
"babel-plugin-syntax-trailing-function-commas": {
|
"babel-plugin-syntax-trailing-function-commas": {
|
||||||
@@ -4575,6 +4581,11 @@
|
|||||||
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"codemirror": {
|
||||||
|
"version": "5.47.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.47.0.tgz",
|
||||||
|
"integrity": "sha512-kV49Fr+NGFHFc/Imsx6g180hSlkGhuHxTSDDmDHOuyln0MQYFLixDY4+bFkBVeCEiepYfDimAF/e++9jPJk4QA=="
|
||||||
|
},
|
||||||
"collection-visit": {
|
"collection-visit": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
|
||||||
@@ -12984,6 +12995,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"react-codemirror2": {
|
||||||
|
"version": "6.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-codemirror2/-/react-codemirror2-6.0.0.tgz",
|
||||||
|
"integrity": "sha512-D7y9qZ05FbUh9blqECaJMdDwKluQiO3A9xB+fssd5jKM7YAXucRuEOlX32mJQumUvHUkHRHqXIPBjm6g0FW0Ag=="
|
||||||
|
},
|
||||||
"react-dom": {
|
"react-dom": {
|
||||||
"version": "16.8.6",
|
"version": "16.8.6",
|
||||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
"@babel/preset-react": "^7.0.0",
|
"@babel/preset-react": "^7.0.0",
|
||||||
"@lingui/cli": "^2.7.4",
|
"@lingui/cli": "^2.7.4",
|
||||||
"@lingui/macro": "^2.7.2",
|
"@lingui/macro": "^2.7.2",
|
||||||
|
"@nteract/mockument": "^1.0.4",
|
||||||
"babel-core": "^7.0.0-bridge.0",
|
"babel-core": "^7.0.0-bridge.0",
|
||||||
"babel-eslint": "^10.0.1",
|
"babel-eslint": "^10.0.1",
|
||||||
"babel-jest": "^24.7.1",
|
"babel-jest": "^24.7.1",
|
||||||
@@ -56,9 +57,12 @@
|
|||||||
"@patternfly/react-icons": "^3.7.5",
|
"@patternfly/react-icons": "^3.7.5",
|
||||||
"@patternfly/react-tokens": "^2.3.3",
|
"@patternfly/react-tokens": "^2.3.3",
|
||||||
"axios": "^0.18.0",
|
"axios": "^0.18.0",
|
||||||
|
"codemirror": "^5.47.0",
|
||||||
"formik": "^1.5.1",
|
"formik": "^1.5.1",
|
||||||
|
"js-yaml": "^3.13.1",
|
||||||
"prop-types": "^15.6.2",
|
"prop-types": "^15.6.2",
|
||||||
"react": "^16.4.1",
|
"react": "^16.4.1",
|
||||||
|
"react-codemirror2": "^6.0.0",
|
||||||
"react-dom": "^16.4.1",
|
"react-dom": "^16.4.1",
|
||||||
"react-router-dom": "^4.3.1",
|
"react-router-dom": "^4.3.1",
|
||||||
"styled-components": "^4.2.0"
|
"styled-components": "^4.2.0"
|
||||||
|
|||||||
32
src/components/ButtonGroup.jsx
Normal file
32
src/components/ButtonGroup.jsx
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Group = styled.div`
|
||||||
|
display: inline-flex;
|
||||||
|
|
||||||
|
& > .pf-c-button:not(:last-child) {
|
||||||
|
&,
|
||||||
|
&::after {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .pf-c-button:not(:first-child) {
|
||||||
|
&,
|
||||||
|
&::after {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function ButtonGroup ({ children }) {
|
||||||
|
return (
|
||||||
|
<Group>
|
||||||
|
{children}
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ButtonGroup;
|
||||||
66
src/components/CodeMirrorInput/CodeMirrorInput.jsx
Normal file
66
src/components/CodeMirrorInput/CodeMirrorInput.jsx
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { oneOf, bool, number, string, func } from 'prop-types';
|
||||||
|
import { Controlled as ReactCodeMirror } from 'react-codemirror2';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import 'codemirror/mode/javascript/javascript';
|
||||||
|
import 'codemirror/mode/yaml/yaml';
|
||||||
|
import 'codemirror/mode/jinja2/jinja2';
|
||||||
|
import 'codemirror/lib/codemirror.css';
|
||||||
|
|
||||||
|
const LINE_HEIGHT = 24;
|
||||||
|
const PADDING = 12;
|
||||||
|
|
||||||
|
const CodeMirror = styled(ReactCodeMirror)`
|
||||||
|
&& {
|
||||||
|
height: initial;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .CodeMirror {
|
||||||
|
height: ${props => (props.rows * LINE_HEIGHT + PADDING)}px;
|
||||||
|
font-family: var(--pf-global--FontFamily--monospace);
|
||||||
|
}
|
||||||
|
|
||||||
|
${props => props.hasErrors && `
|
||||||
|
&& {
|
||||||
|
--pf-c-form-control--PaddingRight: var(--pf-c-form-control--invalid--PaddingRight);
|
||||||
|
--pf-c-form-control--BorderBottomColor: var(--pf-c-form-control--invalid--BorderBottomColor);
|
||||||
|
padding-right: 24px;
|
||||||
|
padding-bottom: var(--pf-c-form-control--invalid--PaddingBottom);
|
||||||
|
background: var(--pf-c-form-control--invalid--Background);
|
||||||
|
border-bottom-width: var(--pf-c-form-control--invalid--BorderBottomWidth);
|
||||||
|
}`}
|
||||||
|
`;
|
||||||
|
|
||||||
|
function CodeMirrorInput ({ value, onChange, mode, readOnly, hasErrors, rows }) {
|
||||||
|
return (
|
||||||
|
<CodeMirror
|
||||||
|
className="pf-c-form-control"
|
||||||
|
value={value}
|
||||||
|
onBeforeChange={(editor, data, val) => onChange(val)}
|
||||||
|
mode={mode}
|
||||||
|
hasErrors={hasErrors}
|
||||||
|
options={{
|
||||||
|
smartIndent: false,
|
||||||
|
lineNumbers: true,
|
||||||
|
readOnly
|
||||||
|
}}
|
||||||
|
rows={rows}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
CodeMirrorInput.propTypes = {
|
||||||
|
value: string.isRequired,
|
||||||
|
onChange: func.isRequired,
|
||||||
|
mode: oneOf(['javascript', 'yaml', 'jinja2']).isRequired,
|
||||||
|
readOnly: bool,
|
||||||
|
hasErrors: bool,
|
||||||
|
rows: number,
|
||||||
|
};
|
||||||
|
CodeMirrorInput.defaultProps = {
|
||||||
|
readOnly: false,
|
||||||
|
rows: 6,
|
||||||
|
hasErrors: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CodeMirrorInput;
|
||||||
39
src/components/CodeMirrorInput/CodeMirrorInput.test.jsx
Normal file
39
src/components/CodeMirrorInput/CodeMirrorInput.test.jsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import CodeMirrorInput from './CodeMirrorInput';
|
||||||
|
|
||||||
|
describe('CodeMirrorInput', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.createTextRange = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trigger onChange prop', () => {
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const wrapper = mount(
|
||||||
|
<CodeMirrorInput
|
||||||
|
value="---\n"
|
||||||
|
onChange={onChange}
|
||||||
|
mode="yaml"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const codemirror = wrapper.find('Controlled');
|
||||||
|
expect(codemirror.prop('mode')).toEqual('yaml');
|
||||||
|
expect(codemirror.prop('options').readOnly).toEqual(false);
|
||||||
|
codemirror.prop('onBeforeChange')(null, null, 'newvalue');
|
||||||
|
expect(onChange).toHaveBeenCalledWith('newvalue');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render in read only mode', () => {
|
||||||
|
const onChange = jest.fn();
|
||||||
|
const wrapper = mount(
|
||||||
|
<CodeMirrorInput
|
||||||
|
value="---\n"
|
||||||
|
onChange={onChange}
|
||||||
|
mode="yaml"
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const codemirror = wrapper.find('Controlled');
|
||||||
|
expect(codemirror.prop('options').readOnly).toEqual(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
95
src/components/CodeMirrorInput/VariablesField.jsx
Normal file
95
src/components/CodeMirrorInput/VariablesField.jsx
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { string, bool } from 'prop-types';
|
||||||
|
import { Field } from 'formik';
|
||||||
|
import { Button, Split, SplitItem } from '@patternfly/react-core';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import ButtonGroup from '../ButtonGroup';
|
||||||
|
import CodeMirrorInput from './CodeMirrorInput';
|
||||||
|
import { yamlToJson, jsonToYaml } from '../../util/yaml';
|
||||||
|
|
||||||
|
const YAML_MODE = 'yaml';
|
||||||
|
const JSON_MODE = 'javascript';
|
||||||
|
|
||||||
|
const SmallButton = styled(Button)`
|
||||||
|
padding: 3px 8px;
|
||||||
|
font-size: var(--pf-global--FontSize--xs);
|
||||||
|
`;
|
||||||
|
|
||||||
|
function VariablesField ({ id, name, label, readOnly }) {
|
||||||
|
const [mode, setMode] = useState(YAML_MODE);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Field
|
||||||
|
name={name}
|
||||||
|
render={({ field, form }) => (
|
||||||
|
<div className="pf-c-form__group">
|
||||||
|
<Split gutter="sm">
|
||||||
|
<SplitItem>
|
||||||
|
<label htmlFor={id} className="pf-c-form__label">{label}</label>
|
||||||
|
</SplitItem>
|
||||||
|
<SplitItem>
|
||||||
|
<ButtonGroup>
|
||||||
|
<SmallButton
|
||||||
|
onClick={() => {
|
||||||
|
if (mode === YAML_MODE) { return; }
|
||||||
|
try {
|
||||||
|
form.setFieldValue(name, jsonToYaml(field.value));
|
||||||
|
setMode(YAML_MODE);
|
||||||
|
} catch (err) {
|
||||||
|
form.setFieldError(name, err.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant={mode === YAML_MODE ? 'primary' : 'secondary'}
|
||||||
|
>
|
||||||
|
YAML
|
||||||
|
</SmallButton>
|
||||||
|
<SmallButton
|
||||||
|
onClick={() => {
|
||||||
|
if (mode === JSON_MODE) { return; }
|
||||||
|
try {
|
||||||
|
form.setFieldValue(name, yamlToJson(field.value));
|
||||||
|
setMode(JSON_MODE);
|
||||||
|
} catch (err) {
|
||||||
|
form.setFieldError(name, err.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
variant={mode === JSON_MODE ? 'primary' : 'secondary'}
|
||||||
|
>
|
||||||
|
JSON
|
||||||
|
</SmallButton>
|
||||||
|
</ButtonGroup>
|
||||||
|
</SplitItem>
|
||||||
|
</Split>
|
||||||
|
<CodeMirrorInput
|
||||||
|
mode={mode}
|
||||||
|
readOnly={readOnly}
|
||||||
|
{...field}
|
||||||
|
onChange={(value) => {
|
||||||
|
form.setFieldValue(name, value);
|
||||||
|
}}
|
||||||
|
hasErrors={!!form.errors[field.name]}
|
||||||
|
/>
|
||||||
|
{form.errors[field.name] ? (
|
||||||
|
<div
|
||||||
|
className="pf-c-form__helper-text pf-m-error"
|
||||||
|
aria-live="polite"
|
||||||
|
>
|
||||||
|
{form.errors[field.name]}
|
||||||
|
</div>
|
||||||
|
) : null }
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
VariablesField.propTypes = {
|
||||||
|
id: string.isRequired,
|
||||||
|
name: string.isRequired,
|
||||||
|
label: string.isRequired,
|
||||||
|
readOnly: bool,
|
||||||
|
};
|
||||||
|
VariablesField.defaultProps = {
|
||||||
|
readOnly: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default VariablesField;
|
||||||
111
src/components/CodeMirrorInput/VariablesField.test.jsx
Normal file
111
src/components/CodeMirrorInput/VariablesField.test.jsx
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import { Formik } from 'formik';
|
||||||
|
import { sleep } from '../../../__tests__/testUtils';
|
||||||
|
import VariablesField from './VariablesField';
|
||||||
|
|
||||||
|
describe('VariablesField', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
document.body.createTextRange = jest.fn();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render code mirror input', () => {
|
||||||
|
const value = '---\n';
|
||||||
|
const wrapper = mount(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ variables: value }}
|
||||||
|
render={() => (
|
||||||
|
<VariablesField
|
||||||
|
id="the-field"
|
||||||
|
name="variables"
|
||||||
|
label="Variables"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const codemirror = wrapper.find('Controlled');
|
||||||
|
expect(codemirror.prop('value')).toEqual(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render yaml/json toggles', () => {
|
||||||
|
const value = '---\n';
|
||||||
|
const wrapper = mount(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ variables: value }}
|
||||||
|
render={() => (
|
||||||
|
<VariablesField
|
||||||
|
id="the-field"
|
||||||
|
name="variables"
|
||||||
|
label="Variables"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
const buttons = wrapper.find('Button');
|
||||||
|
expect(buttons).toHaveLength(2);
|
||||||
|
expect(buttons.at(0).prop('variant')).toEqual('primary');
|
||||||
|
expect(buttons.at(1).prop('variant')).toEqual('secondary');
|
||||||
|
|
||||||
|
buttons.at(1).simulate('click');
|
||||||
|
wrapper.update(0);
|
||||||
|
expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('javascript');
|
||||||
|
const buttons2 = wrapper.find('Button');
|
||||||
|
expect(buttons2.at(0).prop('variant')).toEqual('secondary');
|
||||||
|
expect(buttons2.at(1).prop('variant')).toEqual('primary');
|
||||||
|
buttons2.at(0).simulate('click');
|
||||||
|
wrapper.update(0);
|
||||||
|
expect(wrapper.find('CodeMirrorInput').prop('mode')).toEqual('yaml');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set Formik error if yaml is invalid', () => {
|
||||||
|
const value = '---\nfoo bar\n';
|
||||||
|
const wrapper = mount(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ variables: value }}
|
||||||
|
render={() => (
|
||||||
|
<VariablesField
|
||||||
|
id="the-field"
|
||||||
|
name="variables"
|
||||||
|
label="Variables"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
wrapper.find('Button').at(1).simulate('click');
|
||||||
|
wrapper.update();
|
||||||
|
|
||||||
|
const field = wrapper.find('CodeMirrorInput');
|
||||||
|
expect(field.prop('hasErrors')).toEqual(true);
|
||||||
|
expect(wrapper.find('.pf-m-error')).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should submit value through Formik', async () => {
|
||||||
|
const value = '---\nfoo: bar\n';
|
||||||
|
const handleSubmit = jest.fn();
|
||||||
|
const wrapper = mount(
|
||||||
|
<Formik
|
||||||
|
initialValues={{ variables: value }}
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
render={(formik) => (
|
||||||
|
<form onSubmit={formik.handleSubmit}>
|
||||||
|
<VariablesField
|
||||||
|
id="the-field"
|
||||||
|
name="variables"
|
||||||
|
label="Variables"
|
||||||
|
/>
|
||||||
|
<button type="submit" id="submit">Submit</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
wrapper.find('CodeMirrorInput').prop('onChange')('---\nnewval: changed');
|
||||||
|
wrapper.find('form').simulate('submit');
|
||||||
|
await sleep(1);
|
||||||
|
await sleep(1);
|
||||||
|
|
||||||
|
expect(handleSubmit).toHaveBeenCalled();
|
||||||
|
expect(handleSubmit.mock.calls[0][0]).toEqual({
|
||||||
|
variables: '---\nnewval: changed'
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
4
src/components/CodeMirrorInput/index.js
Normal file
4
src/components/CodeMirrorInput/index.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import CodeMirrorInput from './CodeMirrorInput';
|
||||||
|
|
||||||
|
export default CodeMirrorInput;
|
||||||
|
export { default as VariablesField } from './VariablesField';
|
||||||
17
src/util/yaml.js
Normal file
17
src/util/yaml.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import yaml from 'js-yaml';
|
||||||
|
|
||||||
|
export function yamlToJson (yamlString) {
|
||||||
|
const value = yaml.safeLoad(yamlString);
|
||||||
|
if (!value) { return '{}'; }
|
||||||
|
if (typeof value !== 'object') {
|
||||||
|
throw new Error('yaml is not in object format');
|
||||||
|
}
|
||||||
|
return JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function jsonToYaml (jsonString) {
|
||||||
|
if (jsonString.trim() === '') { return '---\n'; }
|
||||||
|
const value = JSON.parse(jsonString);
|
||||||
|
if (Object.entries(value).length === 0) { return '---\n'; }
|
||||||
|
return yaml.safeDump(value);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user