Skip to content

Commit d230bb9

Browse files
P0liplottamus
andauthored
feat: render deprecated next to general validations (#64)
* feat: render deprecated next to general validations * feat: orange color Co-Authored-By: Chris Lott <chrisnlott@gmail.com> Co-authored-by: Chris Lott <chrisnlott@gmail.com>
1 parent 3bb9abf commit d230bb9

File tree

5 files changed

+120
-46
lines changed

5 files changed

+120
-46
lines changed

src/components/__tests__/SchemaRow.spec.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Popover } from '@stoplight/ui-kit';
12
import { shallow } from 'enzyme';
23
import 'jest-enzyme';
34
import * as React from 'react';
@@ -30,6 +31,7 @@ describe('SchemaRow component', () => {
3031
const wrapper = shallow(shallow(<SchemaRow node={node as SchemaTreeListNode} rowOptions={rowOptions} />)
3132
.find(Validations)
3233
.shallow()
34+
.find(Popover)
3335
.prop('content') as React.ReactElement);
3436

3537
expect(wrapper).toHaveText('enum:null,0,false');
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Dictionary } from '@stoplight/types';
2+
import { Popover } from '@stoplight/ui-kit';
3+
import { shallow } from 'enzyme';
4+
import 'jest-enzyme';
5+
import * as React from 'react';
6+
7+
import { getValidations } from '../../utils/getValidations';
8+
import { Validations } from '../shared/Validations';
9+
10+
describe('Validations component', () => {
11+
describe('when property is deprecated', () => {
12+
let validations: Dictionary<unknown>;
13+
14+
beforeEach(() => {
15+
validations = getValidations({ 'x-deprecated': true, type: 'string', format: 'email', minLength: 2 });
16+
});
17+
18+
test('should exclude deprecated from general validations', () => {
19+
const wrapper = shallow(<Validations required={false} validations={validations} />).find(Popover);
20+
21+
expect(shallow(wrapper.prop('target') as React.ReactElement)).toHaveText('optional+2');
22+
expect(shallow(wrapper.prop('content') as React.ReactElement)).toHaveText('format:"email"minLength:2');
23+
});
24+
25+
test('should render deprecated box next to popover', () => {
26+
const wrapper = shallow(<Validations required={false} validations={validations} />).childAt(0);
27+
28+
expect(wrapper).toHaveText('deprecated');
29+
});
30+
});
31+
});

src/components/shared/Validations.tsx

Lines changed: 52 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@ import * as React from 'react';
55

66
export interface IValidations {
77
required: boolean;
8-
validations: Dictionary<unknown> | {};
8+
validations: (Dictionary<unknown> | {}) & { deprecated?: boolean };
99
}
1010

11-
export const Validations: React.FunctionComponent<IValidations> = ({ required, validations }) => {
11+
export const Validations: React.FunctionComponent<IValidations> = ({
12+
required,
13+
validations: { deprecated, ...validations },
14+
}) => {
1215
const validationCount = Object.keys(validations).length;
1316

1417
const requiredElem = (
@@ -18,49 +21,54 @@ export const Validations: React.FunctionComponent<IValidations> = ({ required, v
1821
</div>
1922
);
2023

21-
return validationCount ? (
22-
<Popover
23-
boundary="window"
24-
interactionKind="hover"
25-
content={
26-
<div className="p-5" style={{ maxHeight: 500, maxWidth: 400 }}>
27-
{Object.keys(validations).map((key, index) => {
28-
const validation = validations[key];
24+
return (
25+
<>
26+
{deprecated ? <span className="ml-2 text-orange-7 dark:text-orange-6">deprecated</span> : null}
27+
{validationCount ? (
28+
<Popover
29+
boundary="window"
30+
interactionKind="hover"
31+
content={
32+
<div className="p-5" style={{ maxHeight: 500, maxWidth: 400 }}>
33+
{Object.keys(validations).map((key, index) => {
34+
const validation = validations[key];
2935

30-
let elem = null;
31-
if (Array.isArray(validation)) {
32-
elem = validation.map((v, i) => (
33-
<div key={i} className="mt-1 mr-1 flex items-center">
34-
<div className="px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded">{String(v)}</div>
35-
{i < validation.length - 1 ? <div>,</div> : null}
36-
</div>
37-
));
38-
} else if (typeof validation === 'object') {
39-
elem = (
40-
<div className="m-1 px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded" key={index}>
41-
{'{...}'}
42-
</div>
43-
);
44-
} else {
45-
elem = (
46-
<div className="m-1 px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded" key={index}>
47-
{JSON.stringify(validation)}
48-
</div>
49-
);
50-
}
36+
let elem = null;
37+
if (Array.isArray(validation)) {
38+
elem = validation.map((v, i) => (
39+
<div key={i} className="mt-1 mr-1 flex items-center">
40+
<div className="px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded">{String(v)}</div>
41+
{i < validation.length - 1 ? <div>,</div> : null}
42+
</div>
43+
));
44+
} else if (typeof validation === 'object') {
45+
elem = (
46+
<div className="m-1 px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded" key={index}>
47+
{'{...}'}
48+
</div>
49+
);
50+
} else {
51+
elem = (
52+
<div className="m-1 px-1 bg-gray-2 dark:bg-gray-8 font-bold text-sm rounded" key={index}>
53+
{JSON.stringify(validation)}
54+
</div>
55+
);
56+
}
5157

52-
return (
53-
<div key={index} className="py-1 flex items-baseline">
54-
<div className="font-medium pr-2">{key}:</div>
55-
<div className="flex-1 flex flex-wrap justify-end">{elem}</div>
56-
</div>
57-
);
58-
})}
59-
</div>
60-
}
61-
target={requiredElem}
62-
/>
63-
) : (
64-
requiredElem
58+
return (
59+
<div key={index} className="py-1 flex items-baseline">
60+
<div className="font-medium pr-2">{key}:</div>
61+
<div className="flex-1 flex flex-wrap justify-end">{elem}</div>
62+
</div>
63+
);
64+
})}
65+
</div>
66+
}
67+
target={requiredElem}
68+
/>
69+
) : (
70+
requiredElem
71+
)}
72+
</>
6573
);
6674
};
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { getValidations } from '../getValidations';
2+
3+
describe('getValidations util', () => {
4+
describe('deprecated property', () => {
5+
test('given present x-deprecated, should include its value', () => {
6+
expect(getValidations({ 'x-deprecated': false })).toStrictEqual({ deprecated: false });
7+
expect(getValidations({ 'x-deprecated': false, deprecated: true })).toStrictEqual({ deprecated: false });
8+
expect(getValidations({ 'x-deprecated': true })).toStrictEqual({ deprecated: true });
9+
});
10+
11+
test('given present deprecated, should include its value', () => {
12+
expect(getValidations({ deprecated: false })).toStrictEqual({ deprecated: false });
13+
expect(getValidations({ deprecated: true })).toStrictEqual({ deprecated: true });
14+
});
15+
16+
test('given missing deprecated, should not include anything', () => {
17+
expect(getValidations({})).toStrictEqual({});
18+
});
19+
});
20+
});

src/utils/getValidations.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Dictionary } from '@stoplight/types';
1+
import { Dictionary, Optional } from '@stoplight/types';
22
import { JSONSchema4, JSONSchema4TypeName } from 'json-schema';
33
import { flatMap as _flatMap, pick as _pick } from 'lodash-es';
44

@@ -7,7 +7,6 @@ export const COMMON_VALIDATION_TYPES = [
77
'format', // https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-7
88
'default',
99
'example',
10-
'deprecated',
1110
'nullable',
1211
'discriminator',
1312
'readOnly',
@@ -23,6 +22,18 @@ const VALIDATION_TYPES = {
2322
array: ['additionalItems', 'minItems', 'maxItems', 'uniqueItems'],
2423
};
2524

25+
function getDeprecatedValue(node: JSONSchema4): Optional<boolean> {
26+
if ('x-deprecated' in node) {
27+
return !!node['x-deprecated'];
28+
}
29+
30+
if ('deprecated' in node) {
31+
return !!node.deprecated;
32+
}
33+
34+
return;
35+
}
36+
2637
function getTypeValidations(type: JSONSchema4TypeName | JSONSchema4TypeName[]): string[] {
2738
if (Array.isArray(type)) {
2839
return _flatMap(type, getTypeValidations);
@@ -33,8 +44,10 @@ function getTypeValidations(type: JSONSchema4TypeName | JSONSchema4TypeName[]):
3344

3445
export const getValidations = (node: JSONSchema4): Dictionary<unknown> => {
3546
const extraValidations = node.type && getTypeValidations(node.type);
47+
const deprecated = getDeprecatedValue(node);
3648
return {
3749
..._pick(node, COMMON_VALIDATION_TYPES),
3850
...(extraValidations && _pick(node, extraValidations)),
51+
...(deprecated !== void 0 && { deprecated }),
3952
};
4053
};

0 commit comments

Comments
 (0)