Skip to content

Commit dd89bc4

Browse files
author
Chris Miaskowski
authored
feat: introduce rowRendererRight
- add `rowRendererRight` property - add `schemaControlsRenderer` property - clean up types
1 parent a7dae9b commit dd89bc4

File tree

6 files changed

+81
-25
lines changed

6 files changed

+81
-25
lines changed

src/__stories__/JsonSchemaViewer.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { boolean, number, object, select, text, withKnobs } from '@storybook/add
66
import { storiesOf } from '@storybook/react';
77
import { JsonSchemaViewer } from '../components';
88

9+
import { Checkbox } from '@stoplight/ui-kit';
910
import { JSONSchema4 } from 'json-schema';
1011
import * as allOfSchemaResolved from '../__fixtures__/allOf/allOf-resolved.json';
1112
import * as allOfSchema from '../__fixtures__/allOf/allOf-schema.json';
@@ -108,4 +109,19 @@ storiesOf('JsonSchemaViewer', module)
108109
onGoToRef={action('onGoToRef')}
109110
/>
110111
</div>
112+
))
113+
.add('with rowRendererRight', () => (
114+
<JsonSchemaViewer
115+
rowRendererRight={() => (
116+
<span style={{ position: 'relative', top: '5px' }}>
117+
<Checkbox />
118+
</span>
119+
)}
120+
name={text('name', 'my schema')}
121+
schema={schema as JSONSchema4}
122+
defaultExpandedDepth={number('defaultExpandedDepth', 2)}
123+
expanded={boolean('expanded', false)}
124+
hideTopBar={boolean('hideTopBar', false)}
125+
onGoToRef={action('onGoToRef')}
126+
/>
111127
));

src/components/JsonSchemaViewer.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { runInAction } from 'mobx';
44
import * as React from 'react';
55

66
import { JSONSchema4 } from 'json-schema';
7-
import { GoToRefHandler } from '../types';
7+
import { GoToRefHandler, IExtendableRenderers } from '../types';
88
import { isSchemaViewerEmpty, renderSchema } from '../utils';
99
import { SchemaTree } from './SchemaTree';
1010

1111
export type FallbackComponent = React.ComponentType<{ error: Error | null }>;
1212

13-
export interface IJsonSchemaViewer {
13+
export interface IJsonSchemaViewer extends IExtendableRenderers {
1414
schema: JSONSchema4;
1515
dereferencedSchema?: JSONSchema4;
1616
style?: object;
@@ -22,6 +22,7 @@ export interface IJsonSchemaViewer {
2222
hideTopBar?: boolean;
2323
maxRows?: number;
2424
onGoToRef?: GoToRefHandler;
25+
mergeAllOf?: boolean;
2526
FallbackComponent?: FallbackComponent;
2627
}
2728

@@ -33,7 +34,16 @@ export class JsonSchemaViewerComponent extends React.PureComponent<IJsonSchemaVi
3334

3435
this.treeStore = new TreeStore({
3536
defaultExpandedDepth: this.expandedDepth,
36-
nodes: Array.from(renderSchema(props.dereferencedSchema || props.schema, 0, { path: [] }, { mergeAllOf: true })),
37+
nodes: Array.from(
38+
renderSchema(
39+
props.dereferencedSchema || props.schema,
40+
0,
41+
{ path: [] },
42+
{
43+
mergeAllOf: props.mergeAllOf === undefined ? true : props.mergeAllOf,
44+
},
45+
),
46+
),
3747
});
3848
}
3949

src/components/SchemaRow.tsx

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,27 @@ import get = require('lodash/get');
88
import map = require('lodash/map');
99
import size = require('lodash/size');
1010

11-
import { GoToRefHandler, SchemaNodeWithMeta, SchemaTreeListNode } from '../types';
11+
import { GoToRefHandler, IExtendableRenderers, SchemaNodeWithMeta, SchemaTreeListNode } from '../types';
1212
import { isCombiner, isRef } from '../utils';
1313
import { Types } from './';
1414

15-
export interface ISchemaRow {
15+
export interface ISchemaRow extends IExtendableRenderers {
1616
node: SchemaTreeListNode;
1717
rowOptions: IRowRendererOptions;
1818
onGoToRef?: GoToRefHandler;
19+
toggleExpand: () => void;
1920
}
2021

2122
const ICON_SIZE = 12;
2223
const ICON_DIMENSION = 20;
23-
const ROW_OFFSET = 7;
2424

25-
export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ node, rowOptions, onGoToRef }) => {
25+
export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({
26+
node,
27+
rowOptions,
28+
onGoToRef,
29+
rowRendererRight,
30+
toggleExpand,
31+
}) => {
2632
const schemaNode = node.metadata as SchemaNodeWithMeta;
2733
const { name, $ref, subtype, required } = schemaNode;
2834

@@ -58,20 +64,22 @@ export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ node, rowOption
5864
</div>
5965
);
6066

67+
const combinerOffset = ICON_DIMENSION * node.level;
6168
return (
62-
<div className="px-2 flex-1 w-full">
69+
<div onClick={toggleExpand} className="px-6 flex-1 w-full">
70+
{/* Do not set position: relative. Divider must be relative to the parent container in order to avoid bugs related to this container calculated height changes. */}
6371
<div
64-
className="flex items-center text-sm relative"
72+
className="flex items-center text-sm"
6573
style={{
66-
marginLeft: ICON_DIMENSION * node.level, // offset for spacing
74+
marginLeft: combinerOffset,
6775
}}
6876
>
6977
{node.canHaveChildren &&
7078
node.level > 0 && (
7179
<div
7280
className="absolute flex justify-center cursor-pointer p-1 rounded hover:bg-darken-3"
7381
style={{
74-
left: ICON_DIMENSION * -1 + ROW_OFFSET / -2,
82+
left: combinerOffset,
7583
width: ICON_DIMENSION,
7684
height: ICON_DIMENSION,
7785
}}
@@ -85,7 +93,14 @@ export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ node, rowOption
8593
)}
8694

8795
{schemaNode.divider && (
88-
<div className="flex items-center w-full absolute" style={{ top: -9, height: 1 }}>
96+
<div
97+
className="flex items-center absolute"
98+
style={{
99+
top: 0,
100+
height: 1,
101+
width: `calc(100% - ${combinerOffset}px - 1.5rem)`,
102+
}}
103+
>
89104
<div className="text-darken-7 dark:text-lighten-8 uppercase text-xs pr-2 -ml-4">{schemaNode.divider}</div>
90105
<div className="flex-1 bg-darken-5 dark:bg-lighten-5" style={{ height: 1 }} />
91106
</div>
@@ -171,6 +186,7 @@ export const SchemaRow: React.FunctionComponent<ISchemaRow> = ({ node, rowOption
171186
) : (
172187
requiredElem
173188
)}
189+
{rowRendererRight && <div className="ml-2">{rowRendererRight(node)}</div>}
174190
</div>
175191
</div>
176192
);

src/components/SchemaTree.tsx

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { TreeList, TreeListEvents, TreeStore } from '@stoplight/tree-list';
1+
import { TreeList, TreeStore } from '@stoplight/tree-list';
22
import * as cn from 'classnames';
33
import { JSONSchema4 } from 'json-schema';
44
import { observer } from 'mobx-react-lite';
55
import * as React from 'react';
6-
7-
import { GoToRefHandler } from '../types';
6+
import { GoToRefHandler, IExtendableRenderers, SchemaTreeListNode } from '../types';
87
import { SchemaRow } from './';
98

10-
export interface ISchemaTree {
9+
export interface ISchemaTree extends IExtendableRenderers {
1110
treeStore: TreeStore;
1211
schema: JSONSchema4;
1312
className?: string;
@@ -24,19 +23,12 @@ const canDrag = () => false;
2423
export const SchemaTree = observer<ISchemaTree>(props => {
2524
const { hideTopBar, name, treeStore, maxRows, className, onGoToRef } = props;
2625

27-
treeStore.on(TreeListEvents.NodeClick, (e, node) => treeStore.toggleExpand(node));
28-
2926
const itemData = {
3027
treeStore,
3128
count: treeStore.nodes.length,
3229
onGoToRef,
3330
};
3431

35-
const rowRenderer = React.useCallback(
36-
(node, rowOptions) => <SchemaRow node={node} rowOptions={rowOptions} {...itemData} />,
37-
[itemData.count],
38-
);
39-
4032
return (
4133
<div className={cn(className, 'flex flex-col h-full w-full')}>
4234
{name &&
@@ -50,9 +42,24 @@ export const SchemaTree = observer<ISchemaTree>(props => {
5042
striped
5143
maxRows={maxRows !== undefined ? maxRows + 0.5 : maxRows}
5244
store={treeStore}
53-
rowRenderer={rowRenderer}
45+
rowRenderer={(node, rowOptions) => {
46+
// TODO: add a React.useCallback to rerender only when either itemData.count or maskProps (to be found in studio) change
47+
48+
return (
49+
<SchemaRow
50+
toggleExpand={() => {
51+
treeStore.toggleExpand(node);
52+
}}
53+
rowRendererRight={props.rowRendererRight}
54+
node={node as SchemaTreeListNode}
55+
rowOptions={rowOptions}
56+
{...itemData}
57+
/>
58+
);
59+
}}
5460
canDrag={canDrag}
5561
/>
62+
{props.schemaControlsRenderer && props.schemaControlsRenderer()}
5663
</div>
5764
);
5865
});

src/components/__tests__/SchemaRow.spec.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ describe('SchemaRow component', () => {
2727
isExpanded: true,
2828
};
2929

30-
const wrapper = shallow(shallow(<SchemaRow node={node as SchemaTreeListNode} rowOptions={rowOptions} />)
30+
const wrapper = shallow(shallow(
31+
<SchemaRow toggleExpand={() => null} node={node as SchemaTreeListNode} rowOptions={rowOptions} />,
32+
)
3133
.find(Popover)
3234
.prop('content') as React.ReactElement);
3335

src/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@ import { TreeListNode } from '@stoplight/tree-list';
22
import { Dictionary, JsonPath } from '@stoplight/types';
33
import { JSONSchema4, JSONSchema4TypeName } from 'json-schema';
44

5+
export interface IExtendableRenderers {
6+
rowRendererRight?: (node: SchemaTreeListNode) => React.ReactElement;
7+
schemaControlsRenderer?: () => React.ReactElement;
8+
}
9+
510
export const enum SchemaKind {
611
Any = 'any',
712
String = 'string',

0 commit comments

Comments
 (0)