Skip to content

Commit c056446

Browse files
committed
chore: Merge branch 'master' into vendor-extensions
* master: fix: improve handling of array + dictionary combo (stoplightio#251) feat: support dictionaries (stoplightio#249) # Conflicts: # src/components/SchemaRow/TopLevelSchemaRow.tsx
2 parents 8225deb + 0737d66 commit c056446

16 files changed

+519
-168
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
},
4747
"dependencies": {
4848
"@stoplight/json": "^3.20.1",
49-
"@stoplight/json-schema-tree": "^3.0.0",
49+
"@stoplight/json-schema-tree": "^4.0.0",
5050
"@stoplight/react-error-boundary": "^2.0.0",
5151
"@types/json-schema": "^7.0.7",
5252
"classnames": "^2.2.6",

Diff for: src/__fixtures__/formats-schema.json

+17-13
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,7 @@
33
"type": "object",
44
"properties": {
55
"date-of-birth": {
6-
"type": [
7-
"number",
8-
"string",
9-
"array"
10-
],
6+
"type": ["number", "string", "array"],
117
"format": "date-time",
128
"items": {}
139
},
@@ -23,20 +19,28 @@
2319
"format": "int32"
2420
},
2521
"size": {
26-
"type": [
27-
"number",
28-
"string"
29-
],
22+
"type": ["number", "string"],
3023
"format": "byte"
3124
},
3225
"notype": {
3326
"format": "date-time"
3427
},
28+
"array-of-integers": {
29+
"type": "array",
30+
"items": {
31+
"type": "integer",
32+
"format": "int32"
33+
}
34+
},
35+
"map-of-ids": {
36+
"type": "object",
37+
"additionalProperties": {
38+
"type": "integer",
39+
"format": "int32"
40+
}
41+
},
3542
"permissions": {
36-
"type": [
37-
"string",
38-
"object"
39-
],
43+
"type": ["string", "object"],
4044
"format": "password",
4145
"properties": {
4246
"ids": {

Diff for: src/__tests__/index.spec.tsx

+223-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import 'jest-enzyme';
22

33
import { mount, ReactWrapper } from 'enzyme';
4-
import { JSONSchema4 } from 'json-schema';
4+
import { JSONSchema4, JSONSchema7 } from 'json-schema';
55
import * as React from 'react';
66

77
import { JsonSchemaViewer } from '../components';
@@ -165,6 +165,228 @@ describe('HTML Output', () => {
165165
expect(dumpDom(<JsonSchemaViewer schema={schema} />)).toMatchSnapshot();
166166
});
167167

168+
it('given dictionary with defined properties, should not render them', () => {
169+
const schema: JSONSchema7 = {
170+
type: ['object', 'null'],
171+
properties: {
172+
id: {
173+
type: 'string',
174+
readOnly: true,
175+
},
176+
description: {
177+
type: 'string',
178+
writeOnly: true,
179+
},
180+
},
181+
additionalProperties: {
182+
type: 'string',
183+
},
184+
};
185+
186+
expect(dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />)).toMatchInlineSnapshot(`
187+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
188+
<div data-overlay-container=\\"true\\">
189+
<div class=\\"JsonSchemaViewer\\">
190+
<div></div>
191+
<div data-id=\\"bf8b96e78f11d\\" data-test=\\"schema-row\\">
192+
<div>
193+
<div>
194+
<div>
195+
<div>
196+
<span data-test=\\"property-type\\">dictionary[string, string]</span>
197+
<span>or</span>
198+
<span data-test=\\"property-type\\">null</span>
199+
</div>
200+
</div>
201+
</div>
202+
</div>
203+
</div>
204+
</div>
205+
</div>
206+
</div>
207+
"
208+
`);
209+
});
210+
211+
it('should render top-level dictionary', () => {
212+
const schema: JSONSchema7 = {
213+
type: 'object',
214+
additionalProperties: {
215+
type: 'string',
216+
},
217+
};
218+
219+
expect(dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />)).toMatchInlineSnapshot(`
220+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
221+
<div data-overlay-container=\\"true\\">
222+
<div class=\\"JsonSchemaViewer\\">
223+
<div></div>
224+
<div data-id=\\"bf8b96e78f11d\\" data-test=\\"schema-row\\">
225+
<div>
226+
<div>
227+
<div><span data-test=\\"property-type\\">dictionary[string, string]</span></div>
228+
</div>
229+
</div>
230+
</div>
231+
</div>
232+
</div>
233+
</div>
234+
"
235+
`);
236+
});
237+
238+
it('should not merge array of dictionaries', () => {
239+
const schema: JSONSchema7 = {
240+
type: 'array',
241+
items: {
242+
type: 'object',
243+
additionalProperties: {
244+
type: 'string',
245+
},
246+
},
247+
};
248+
249+
expect(dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />)).toMatchInlineSnapshot(`
250+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
251+
<div data-overlay-container=\\"true\\">
252+
<div class=\\"JsonSchemaViewer\\">
253+
<div></div>
254+
<div data-id=\\"bf8b96e78f11d\\" data-test=\\"schema-row\\">
255+
<div>
256+
<div>
257+
<div role=\\"button\\"></div>
258+
<div><span data-test=\\"property-type\\">array</span></div>
259+
</div>
260+
</div>
261+
</div>
262+
<div data-level=\\"0\\">
263+
<div data-id=\\"98538b996305d\\" data-test=\\"schema-row\\">
264+
<div>
265+
<div>
266+
<div><span data-test=\\"property-type\\">dictionary[string, string]</span></div>
267+
</div>
268+
</div>
269+
</div>
270+
</div>
271+
</div>
272+
</div>
273+
</div>
274+
"
275+
`);
276+
});
277+
278+
it('should merge dictionaries with array values', () => {
279+
const schema: JSONSchema7 = {
280+
type: 'object',
281+
additionalProperties: {
282+
type: 'array',
283+
items: {
284+
type: 'string',
285+
},
286+
},
287+
};
288+
289+
expect(dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />)).toMatchInlineSnapshot(`
290+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
291+
<div data-overlay-container=\\"true\\">
292+
<div class=\\"JsonSchemaViewer\\">
293+
<div></div>
294+
<div data-id=\\"bf8b96e78f11d\\" data-test=\\"schema-row\\">
295+
<div>
296+
<div>
297+
<div role=\\"button\\"></div>
298+
<div><span data-test=\\"property-type\\">dictionary[string, array]</span></div>
299+
</div>
300+
</div>
301+
</div>
302+
<div data-level=\\"0\\">
303+
<div data-id=\\"98538b996305d\\" data-test=\\"schema-row\\">
304+
<div>
305+
<div>
306+
<div><span data-test=\\"property-type\\">string</span></div>
307+
</div>
308+
</div>
309+
</div>
310+
</div>
311+
</div>
312+
</div>
313+
</div>
314+
"
315+
`);
316+
});
317+
318+
it('should not render true/false additionalProperties', () => {
319+
const schema: JSONSchema7 = {
320+
type: 'object',
321+
properties: {
322+
id: {
323+
type: 'string',
324+
},
325+
},
326+
additionalProperties: true,
327+
};
328+
329+
const additionalTrue = dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />);
330+
const additionalFalse = dumpDom(
331+
<JsonSchemaViewer schema={{ ...schema, additionalProperties: false }} defaultExpandedDepth={Infinity} />,
332+
);
333+
expect(additionalTrue).toEqual(additionalFalse);
334+
expect(additionalTrue).toMatchInlineSnapshot(`
335+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
336+
<div data-overlay-container=\\"true\\">
337+
<div class=\\"JsonSchemaViewer\\">
338+
<div></div>
339+
<div data-level=\\"0\\">
340+
<div data-id=\\"8074f410d9775\\" data-test=\\"schema-row\\">
341+
<div>
342+
<div>
343+
<div>
344+
<div data-test=\\"property-name-id\\">id</div>
345+
<span data-test=\\"property-type\\">string</span>
346+
</div>
347+
</div>
348+
</div>
349+
</div>
350+
</div>
351+
</div>
352+
</div>
353+
</div>
354+
"
355+
`);
356+
});
357+
358+
it('should not render additionalItems', () => {
359+
const schema: JSONSchema7 = {
360+
type: 'array',
361+
additionalItems: {
362+
type: 'object',
363+
properties: {
364+
id: {
365+
type: 'string',
366+
},
367+
},
368+
},
369+
};
370+
371+
expect(dumpDom(<JsonSchemaViewer schema={schema} defaultExpandedDepth={Infinity} />)).toMatchInlineSnapshot(`
372+
"<div class=\\"\\" id=\\"mosaic-provider-react-aria-0-1\\">
373+
<div data-overlay-container=\\"true\\">
374+
<div class=\\"JsonSchemaViewer\\">
375+
<div></div>
376+
<div data-id=\\"bf8b96e78f11d\\" data-test=\\"schema-row\\">
377+
<div>
378+
<div>
379+
<div><span data-test=\\"property-type\\">array</span></div>
380+
</div>
381+
</div>
382+
</div>
383+
</div>
384+
</div>
385+
</div>
386+
"
387+
`);
388+
});
389+
168390
describe('top level descriptions', () => {
169391
const schema: JSONSchema4 = {
170392
description: 'This is a description that should be rendered',

Diff for: src/components/JsonSchemaViewer.tsx

+3-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
isRegularNode,
33
RootNode,
4-
SchemaNode,
54
SchemaTree as JsonSchemaTree,
65
SchemaTreeRefDereferenceFn,
76
} from '@stoplight/json-schema-tree';
@@ -13,7 +12,8 @@ import { useUpdateAtom } from 'jotai/utils';
1312
import * as React from 'react';
1413

1514
import { JSVOptions, JSVOptionsContextProvider } from '../contexts';
16-
import type { JSONSchema } from '../types';
15+
import { shouldNodeBeIncluded } from '../tree/utils';
16+
import { JSONSchema } from '../types';
1717
import { PathCrumbs } from './PathCrumbs';
1818
import { TopLevelSchemaRow } from './SchemaRow';
1919
import { hoveredNodeAtom } from './SchemaRow/state';
@@ -117,20 +117,9 @@ const JsonSchemaViewerInner = ({
117117
});
118118

119119
let nodeCount = 0;
120-
const shouldNodeBeIncluded = (node: SchemaNode) => {
121-
if (!isRegularNode(node)) return true;
122-
123-
const { validations } = node;
124-
125-
if (!!validations.writeOnly === !!validations.readOnly) {
126-
return true;
127-
}
128-
129-
return !((viewMode === 'read' && !!validations.writeOnly) || (viewMode === 'write' && !!validations.readOnly));
130-
};
131120

132121
jsonSchemaTree.walker.hookInto('filter', node => {
133-
if (shouldNodeBeIncluded(node)) {
122+
if (shouldNodeBeIncluded(node, viewMode)) {
134123
nodeCount++;
135124
return true;
136125
}

Diff for: src/components/SchemaRow/TopLevelSchemaRow.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import * as React from 'react';
77
import { COMBINER_NAME_MAP } from '../../consts';
88
import { useJSVOptionsContext } from '../../contexts';
99
import { useIsOnScreen } from '../../hooks/useIsOnScreen';
10-
import { isComplexArray, visibleChildren } from '../../tree';
10+
import { isComplexArray, isDictionaryNode, visibleChildren } from '../../tr
1111
import { extractVendorExtensions } from '../../utils/extractVendorExtensions';
1212
import { showPathCrumbsAtom } from '../PathCrumbs/state';
1313
import { Description, getValidationsFromSchema, Validations } from '../shared';
@@ -152,5 +152,5 @@ function ScrollCheck() {
152152
}
153153

154154
function isPureObjectNode(schemaNode: RegularNode) {
155-
return schemaNode.primaryType === 'object' && schemaNode.types?.length === 1;
155+
return schemaNode.primaryType === 'object' && schemaNode.types?.length === 1 && !isDictionaryNode(schemaNode);
156156
}

Diff for: src/components/shared/Format.tsx

-10
This file was deleted.

0 commit comments

Comments
 (0)