Skip to content

Commit 97d1aae

Browse files
committed
fix: retain required properties for properties of referenced objects
1 parent 1aff735 commit 97d1aae

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

src/components/SchemaRow.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import cn from 'classnames';
33
import * as React from 'react';
44

55
import { getNodeMetadata, metadataStore } from '../tree/metadata';
6-
import { GoToRefHandler, SchemaTreeListNode } from '../types';
6+
import { GoToRefHandler, SchemaKind, SchemaTreeListNode } from '../types';
7+
import { getPrimaryType } from '../utils/getPrimaryType';
78
import { Caret, Description, Divider, Property, Validations } from './shared';
89

910
export interface ISchemaRow {
@@ -17,9 +18,29 @@ const ICON_SIZE = 12;
1718
const ICON_DIMENSION = 20;
1819
const ROW_OFFSET = 7;
1920

21+
function isRequired(treeNode: SchemaTreeListNode) {
22+
if (treeNode.parent === null) return false;
23+
try {
24+
const { path } = getNodeMetadata(treeNode);
25+
if (path.length === 0) {
26+
return false;
27+
}
28+
29+
const { schema } = getNodeMetadata(treeNode.parent);
30+
31+
return (
32+
getPrimaryType(schema) === SchemaKind.Object &&
33+
Array.isArray(schema.required) &&
34+
schema.required.includes(String(path[path.length - 1]))
35+
);
36+
} catch {
37+
return false;
38+
}
39+
}
40+
2041
export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ className, node, rowOptions, onGoToRef }) => {
2142
const metadata = getNodeMetadata(node);
22-
const { path, schemaNode } = metadata;
43+
const { schemaNode } = metadata;
2344

2445
const parentSchemaNode = (node.parent !== null && metadataStore.get(node.parent)?.schemaNode) || null;
2546
const description = 'annotations' in schemaNode ? schemaNode.annotations.description : null;
@@ -56,12 +77,7 @@ export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ className, node
5677
</div>
5778

5879
<Validations
59-
required={
60-
parentSchemaNode !== null &&
61-
'required' in parentSchemaNode &&
62-
Array.isArray(parentSchemaNode.required) &&
63-
parentSchemaNode.required.includes(String(path[path.length - 1]))
64-
}
80+
required={isRequired(node)}
6581
validations={{
6682
...('annotations' in schemaNode &&
6783
schemaNode.annotations.default && { default: schemaNode.annotations.default }),

src/components/__tests__/SchemaRow.spec.tsx

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
import { TreeListParentNode, TreeState } from '@stoplight/tree-list';
12
import { Popover } from '@stoplight/ui-kit';
23
import { shallow } from 'enzyme';
34
import 'jest-enzyme';
5+
import { JSONSchema4 } from 'json-schema';
46
import * as React from 'react';
7+
import { SchemaTree } from '../../tree';
58
import { metadataStore } from '../../tree/metadata';
69
import { SchemaKind, SchemaTreeListNode } from '../../types';
710
import { SchemaRow } from '../SchemaRow';
@@ -45,4 +48,63 @@ describe('SchemaRow component', () => {
4548

4649
expect(wrapper).toHaveText('enum:null,0,false');
4750
});
51+
52+
describe('required property', () => {
53+
let tree: SchemaTree;
54+
55+
beforeEach(() => {
56+
const schema: JSONSchema4 = {
57+
type: 'object',
58+
properties: {
59+
user: {
60+
$ref: '#/properties/id',
61+
},
62+
id: {
63+
type: 'object',
64+
required: ['foo'],
65+
properties: {
66+
foo: {
67+
type: 'string',
68+
},
69+
bar: {
70+
type: 'number',
71+
},
72+
},
73+
},
74+
},
75+
};
76+
77+
tree = new SchemaTree(schema, new TreeState(), {
78+
expandedDepth: Infinity,
79+
mergeAllOf: false,
80+
resolveRef: void 0,
81+
});
82+
83+
tree.populate();
84+
tree.unwrap(Array.from(tree)[1] as TreeListParentNode);
85+
});
86+
87+
test('should preserve the required validation', () => {
88+
const wrapper = shallow(<SchemaRow node={Array.from(tree)[5]} rowOptions={{}} />);
89+
expect(wrapper.find(Validations)).toHaveProp('required', true);
90+
});
91+
92+
test('should preserve the optional validation', () => {
93+
const wrapper = shallow(<SchemaRow node={Array.from(tree)[6]} rowOptions={{}} />);
94+
expect(wrapper.find(Validations)).toHaveProp('required', false);
95+
});
96+
97+
describe('given a referenced object', () => {
98+
test('should preserve the required validation', () => {
99+
const wrapper = shallow(<SchemaRow node={Array.from(tree)[2]} rowOptions={{}} />);
100+
101+
expect(wrapper.find(Validations)).toHaveProp('required', true);
102+
});
103+
104+
test('should preserve the optional validation', () => {
105+
const wrapper = shallow(<SchemaRow node={Array.from(tree)[3]} rowOptions={{}} />);
106+
expect(wrapper.find(Validations)).toHaveProp('required', false);
107+
});
108+
});
109+
});
48110
});

0 commit comments

Comments
 (0)