Skip to content

Commit 1433699

Browse files
authored
Merge pull request temando#73 from brendo/issue-36-handle-mixed-data-type
Support 'type' being an array. Closes temando#36
2 parents e1543ba + 89e2ca5 commit 1433699

File tree

16 files changed

+683
-8427
lines changed

16 files changed

+683
-8427
lines changed

src/components/BodySchema/BodySchema.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,10 @@ export default class BodySchema extends Component {
6969
type={property.type}
7070
subtype={property.subtype}
7171
description={property.description}
72-
required={property.required}
7372
enumValues={property.enum}
7473
defaultValue={property.defaultValue}
7574
onClick={this.onClick.bind(this, property.name)}
75+
isRequired={property.required}
7676
isOpen={isOpen}
7777
isLast={isLast}
7878
/>

src/components/Description/Description.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React, { Component } from 'react';
2-
import PropTypes from 'prop-types';
2+
import classNames from 'classnames';
33
import markdown from 'markdown-it';
4+
import PropTypes from 'prop-types';
45

56
const cm = markdown('commonmark');
67

@@ -14,7 +15,9 @@ export default class Description extends Component {
1415
};
1516

1617
return (
17-
<div className={isInline ? 'description description-inline' : 'description'} dangerouslySetInnerHTML={text} />
18+
<div className={classNames('description', {
19+
'description-inline': isInline
20+
})} dangerouslySetInnerHTML={text} />
1821
);
1922
}
2023
}

src/components/Property/Property.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@ import './Property.scss';
99

1010
export default class Property extends Component {
1111
render() {
12-
const { name, type, description, required, enumValues, defaultValue, onClick, isOpen, isLast } = this.props;
12+
const { name, type, description, isRequired, enumValues, defaultValue, onClick, isOpen, isLast } = this.props;
1313

1414
let subtype;
15-
if (type === 'array') {
15+
if (type.includes('array')) {
1616
subtype = this.props.subtype;
1717
}
1818

@@ -24,6 +24,7 @@ export default class Property extends Component {
2424
if (isOpen) {
2525
status = 'open';
2626
}
27+
2728
return (
2829
<tr
2930
className={classNames('property', {
@@ -40,8 +41,8 @@ export default class Property extends Component {
4041
}
4142
</td>
4243
<td className="property-info">
43-
<span>{type}</span>{subtype && <span> of {subtype}</span>}
44-
{required && <span className="property-required">Required</span>}
44+
<span>{type.join(', ')}</span>{subtype && <span> of {subtype}</span>}
45+
{isRequired && <span className="property-required">Required</span>}
4546
{enumValues && this.renderEnumValues(enumValues)}
4647
{defaultValue && this.renderDefaultValue(defaultValue)}
4748
{description && <Description description={description}/>}
@@ -89,14 +90,14 @@ export default class Property extends Component {
8990
}
9091

9192
Property.propTypes = {
92-
name: PropTypes.string,
93-
type: PropTypes.string,
93+
name: PropTypes.string.isRequired,
94+
type: PropTypes.arrayOf(PropTypes.string).isRequired,
9495
subtype: PropTypes.string,
9596
description: PropTypes.string,
96-
required: PropTypes.bool,
9797
enumValues: PropTypes.array,
9898
defaultValue: PropTypes.any,
99-
onClick: PropTypes.func,
99+
isRequired: PropTypes.bool,
100100
isOpen: PropTypes.bool,
101-
isLast: PropTypes.bool
101+
isLast: PropTypes.bool,
102+
onClick: PropTypes.func
102103
};

src/parser/open-api/schemaParser.js

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { resolveAllOf } from './allOfResolver';
33
const literalTypes = ['string', 'integer', 'number', 'boolean'];
44

55
/**
6-
* Construct UI ready property object from given inputs
6+
* Construct UI ready property object from given inputs.
77
*
88
* @param {String} nodeName
99
* @param {Object} propertyNode
@@ -12,7 +12,11 @@ const literalTypes = ['string', 'integer', 'number', 'boolean'];
1212
* @return {Object}
1313
*/
1414
function getPropertyNode(nodeName, propertyNode, required = false) {
15-
const nodeType = propertyNode.type || 'string';
15+
let nodeType = propertyNode.type || 'string';
16+
17+
if (!Array.isArray(nodeType)) {
18+
nodeType = [ nodeType ];
19+
}
1620

1721
const outputNode = {
1822
name: nodeName,
@@ -28,24 +32,25 @@ function getPropertyNode(nodeName, propertyNode, required = false) {
2832
outputNode.defaultValue = propertyNode.default;
2933
}
3034

31-
if (literalTypes.indexOf(nodeType) >= 0) {
32-
// Literal types
35+
// Are all the possible types for this property literals?
36+
// TODO: Currently do not handle multiple types that are not all literals
37+
if (nodeType.every((type) => literalTypes.includes(type))) {
3338
if (propertyNode.enum) {
3439
outputNode.enum = propertyNode.enum;
3540
}
3641

3742
return outputNode;
38-
} else if (nodeType === 'object') {
39-
// Object type
43+
// Otherwise, let's see if there's an object in there..
44+
} else if (nodeType.length === 1 && nodeType.includes('object')) {
4045
const propertiesNode = getPropertiesNode(propertyNode.properties, propertyNode.required);
4146

4247
if (propertiesNode !== undefined && propertiesNode.length > 0) {
4348
outputNode.properties = propertiesNode;
4449
}
4550

4651
return outputNode;
47-
} else if (nodeType === 'array') {
48-
// Array type
52+
// Is there an array?
53+
} else if (nodeType.length === 1 && nodeType.includes('array')) {
4954
if (propertyNode.items) {
5055
const arrayItemType = propertyNode.items.type;
5156

src/parser/open-api/v3/open-api-v3-parser.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,10 +147,13 @@ function getUIParametersForLocation(parameters, location) {
147147
required: parameter.required
148148
};
149149

150+
// TODO: We set the type to be an array because the Property component
151+
// handles this. Property should eventually be split and this won't be
152+
// necessary...
150153
if (parameter.type) {
151-
uiParameter.type = parameter.type;
154+
uiParameter.type = [ parameter.type ];
152155
} else if (parameter.schema && parameter.schema.type) {
153-
uiParameter.type = parameter.schema.type;
156+
uiParameter.type = [ parameter.schema.type ];
154157
}
155158

156159
if (parameter.schema && parameter.schema.default !== undefined) {

test/components/Description.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ describe('<Description />', () => {
77
const text = 'This method has zero markdown.';
88
const tree = renderer.create(
99
<Description description={text} />
10-
).toJSON();
10+
);
1111

1212
expect(tree).toMatchSnapshot();
1313
});
@@ -16,7 +16,7 @@ describe('<Description />', () => {
1616
const text = 'This method has some `var i = 0` _markdown_, including a [link](http://www.google.com).';
1717
const tree = renderer.create(
1818
<Description description={text} />
19-
).toJSON();
19+
);
2020

2121
expect(tree).toMatchSnapshot();
2222
});

test/components/Property.test.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import React from 'react';
2+
import Property from './../../src/components/Property/Property';
3+
import renderer from 'react-test-renderer';
4+
import ReactShallowRenderer from 'react-test-renderer/shallow';
5+
6+
describe('<Property />', () => {
7+
it('can render a basic property', () => {
8+
const tree = renderer.create(
9+
<Property name={'type'} type={['string']} isRequired isLast />
10+
);
11+
12+
expect(tree).toMatchSnapshot();
13+
});
14+
15+
it('can render a property with enum', () => {
16+
const tree = renderer.create(
17+
<Property
18+
name={'packagingType'}
19+
type={['string']}
20+
enumValues={['box', 'carton']}
21+
isRequired />
22+
);
23+
24+
expect(tree).toMatchSnapshot();
25+
});
26+
27+
it('can render a property with a subtype', () => {
28+
const tree = renderer.create(
29+
<Property
30+
name={'data'}
31+
type={['array']}
32+
subtype={'object'}
33+
isRequired={false} />
34+
);
35+
36+
expect(tree).toMatchSnapshot();
37+
});
38+
39+
it('can render a property with description', () => {
40+
const shallow = new ReactShallowRenderer();
41+
const tree = shallow.render(
42+
<Property
43+
name={'type'}
44+
type={['string']}
45+
description={'This is _markdown_ text'}
46+
isRequired />
47+
);
48+
49+
expect(tree).toMatchSnapshot();
50+
});
51+
52+
it('can render a property with multiple types', () => {
53+
const tree = renderer.create(
54+
<Property
55+
name={'value'}
56+
type={['string', 'number']}
57+
isRequired={false} />
58+
);
59+
60+
expect(tree).toMatchSnapshot();
61+
});
62+
});
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`<Property /> can render a basic property 1`] = `
4+
<tr
5+
className="property last"
6+
onClick={undefined}
7+
>
8+
<td
9+
className="property-name"
10+
>
11+
<span>
12+
type
13+
</span>
14+
</td>
15+
<td
16+
className="property-info"
17+
>
18+
<span>
19+
string
20+
</span>
21+
<span
22+
className="property-required"
23+
>
24+
Required
25+
</span>
26+
</td>
27+
</tr>
28+
`;
29+
30+
exports[`<Property /> can render a property with a subtype 1`] = `
31+
<tr
32+
className="property"
33+
onClick={undefined}
34+
>
35+
<td
36+
className="property-name"
37+
>
38+
<span>
39+
data
40+
</span>
41+
</td>
42+
<td
43+
className="property-info"
44+
>
45+
<span>
46+
array
47+
</span>
48+
<span>
49+
of
50+
object
51+
</span>
52+
</td>
53+
</tr>
54+
`;
55+
56+
exports[`<Property /> can render a property with description 1`] = `
57+
<tr
58+
className="property"
59+
onClick={undefined}
60+
>
61+
<td
62+
className="property-name"
63+
>
64+
<span>
65+
type
66+
</span>
67+
</td>
68+
<td
69+
className="property-info"
70+
>
71+
<span>
72+
string
73+
</span>
74+
<span
75+
className="property-required"
76+
>
77+
Required
78+
</span>
79+
<Description
80+
description="This is _markdown_ text"
81+
/>
82+
</td>
83+
</tr>
84+
`;
85+
86+
exports[`<Property /> can render a property with enum 1`] = `
87+
<tr
88+
className="property"
89+
onClick={undefined}
90+
>
91+
<td
92+
className="property-name"
93+
>
94+
<span>
95+
packagingType
96+
</span>
97+
</td>
98+
<td
99+
className="property-info"
100+
>
101+
<span>
102+
string
103+
</span>
104+
<span
105+
className="property-required"
106+
>
107+
Required
108+
</span>
109+
<div>
110+
<span>
111+
Valid values:
112+
</span>
113+
<span
114+
className="enum"
115+
>
116+
box
117+
</span>
118+
<span
119+
className="enum"
120+
>
121+
carton
122+
</span>
123+
</div>
124+
</td>
125+
</tr>
126+
`;
127+
128+
exports[`<Property /> can render a property with multiple types 1`] = `
129+
<tr
130+
className="property"
131+
onClick={undefined}
132+
>
133+
<td
134+
className="property-name"
135+
>
136+
<span>
137+
value
138+
</span>
139+
</td>
140+
<td
141+
className="property-info"
142+
>
143+
<span>
144+
string, number
145+
</span>
146+
</td>
147+
</tr>
148+
`;

0 commit comments

Comments
 (0)