Skip to content

Commit 7837eb3

Browse files
authored
fix: render required validation corectly for array[object] (#80)
1 parent e8dba5f commit 7837eb3

File tree

2 files changed

+117
-2
lines changed

2 files changed

+117
-2
lines changed

src/components/SchemaRow.tsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { IRowRendererOptions, isParentNode, Tree } from '@stoplight/tree-list';
22
import cn from 'classnames';
3+
import { JSONSchema4 } from 'json-schema';
34
import * as React from 'react';
45

56
import { getNodeMetadata, getSchemaNodeMetadata } from '../tree/metadata';
67
import { GoToRefHandler, SchemaKind, SchemaTreeListNode } from '../types';
78
import { getPrimaryType } from '../utils/getPrimaryType';
8-
import { hasRefItems, isRefNode } from '../utils/guards';
9+
import { hasRefItems, isArrayNodeWithItems, isRefNode } from '../utils/guards';
910
import { Caret, Description, Divider, Property, Validations } from './shared';
1011

1112
export interface ISchemaRow {
@@ -19,6 +20,16 @@ const ICON_SIZE = 12;
1920
const ICON_DIMENSION = 20;
2021
const ROW_OFFSET = 7;
2122

23+
function getRelevantSchemaForRequireCheck(treeNode: SchemaTreeListNode): JSONSchema4 | JSONSchema4[] | null {
24+
const metadata = getNodeMetadata(treeNode);
25+
if (!('schemaNode' in metadata)) return null;
26+
if (isArrayNodeWithItems(metadata.schemaNode)) {
27+
return metadata.schemaNode.items;
28+
}
29+
30+
return metadata.schema;
31+
}
32+
2233
function isRequired(treeNode: SchemaTreeListNode) {
2334
if (treeNode.parent === null) return false;
2435
try {
@@ -27,9 +38,11 @@ function isRequired(treeNode: SchemaTreeListNode) {
2738
return false;
2839
}
2940

30-
const { schema } = getSchemaNodeMetadata(treeNode.parent);
41+
const schema = getRelevantSchemaForRequireCheck(treeNode.parent);
3142

3243
return (
44+
schema !== null &&
45+
!Array.isArray(schema) &&
3346
getPrimaryType(schema) === SchemaKind.Object &&
3447
Array.isArray(schema.required) &&
3548
schema.required.includes(String(path[path.length - 1]))

src/components/__tests__/SchemaRow.spec.tsx

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,5 +231,107 @@ describe('SchemaRow component', () => {
231231
expect(wrapper.find(Validations)).toHaveProp('required', false);
232232
});
233233
});
234+
235+
describe('given array with items', () => {
236+
beforeEach(() => {
237+
const schema: JSONSchema4 = {
238+
title: 'test',
239+
type: 'array',
240+
items: {
241+
type: 'object',
242+
properties: {
243+
code: {
244+
type: 'number',
245+
},
246+
msg: {
247+
type: 'string',
248+
},
249+
ref: {
250+
type: 'string',
251+
},
252+
},
253+
required: ['code', 'msg'],
254+
},
255+
};
256+
257+
tree = new SchemaTree(schema, new TreeState(), {
258+
expandedDepth: Infinity,
259+
mergeAllOf: false,
260+
resolveRef: void 0,
261+
shouldResolveEagerly: false,
262+
onPopulate: void 0,
263+
});
264+
265+
tree.populate();
266+
});
267+
268+
test.each([1, 2])('should preserve the required validation for %i item', pos => {
269+
const wrapper = shallow(<SchemaRow node={tree.itemAt(pos)!} rowOptions={{}} />)
270+
.find(SchemaPropertyRow)
271+
.shallow();
272+
273+
expect(wrapper.find(Validations)).toHaveProp('required', true);
274+
});
275+
276+
test('should preserve the optional validation', () => {
277+
const wrapper = shallow(<SchemaRow node={tree.itemAt(3)!} rowOptions={{}} />)
278+
.find(SchemaPropertyRow)
279+
.shallow();
280+
281+
expect(wrapper.find(Validations)).toHaveProp('required', false);
282+
});
283+
});
284+
285+
describe('given array with arrayish items', () => {
286+
beforeEach(() => {
287+
const schema: JSONSchema4 = {
288+
title: 'test',
289+
type: 'array',
290+
items: [
291+
{
292+
type: 'object',
293+
properties: {
294+
code: {
295+
type: 'number',
296+
},
297+
msg: {
298+
type: 'string',
299+
},
300+
ref: {
301+
type: 'string',
302+
},
303+
},
304+
required: ['code', 'msg'],
305+
},
306+
],
307+
};
308+
309+
tree = new SchemaTree(schema, new TreeState(), {
310+
expandedDepth: Infinity,
311+
mergeAllOf: false,
312+
resolveRef: void 0,
313+
shouldResolveEagerly: false,
314+
onPopulate: void 0,
315+
});
316+
317+
tree.populate();
318+
});
319+
320+
test.each([2, 3])('should preserve the required validation for %i item', pos => {
321+
const wrapper = shallow(<SchemaRow node={tree.itemAt(pos)!} rowOptions={{}} />)
322+
.find(SchemaPropertyRow)
323+
.shallow();
324+
325+
expect(wrapper.find(Validations)).toHaveProp('required', true);
326+
});
327+
328+
test('should preserve the optional validation', () => {
329+
const wrapper = shallow(<SchemaRow node={tree.itemAt(4)!} rowOptions={{}} />)
330+
.find(SchemaPropertyRow)
331+
.shallow();
332+
333+
expect(wrapper.find(Validations)).toHaveProp('required', false);
334+
});
335+
});
234336
});
235337
});

0 commit comments

Comments
 (0)