-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: simplify JSV #109
Changes from 22 commits
01df0f9
5e2ae49
54c98ba
bab293b
c5b3b7f
29639fc
d0c9cac
530b91b
28e0fab
9565f61
eab86cc
2bb1af5
2436b1e
f76ab36
52a5053
9df1bf2
3c64ecb
fb6b545
98d0a2f
7a4d70b
d12d468
b5e209f
c37d3f7
667072b
6980a5b
e0eae6b
075c68b
a1d48f7
758a26d
81505de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,77 @@ | ||
import type { SchemaTreeRefDereferenceFn } from '@stoplight/json-schema-tree'; | ||
import { isRegularNode } from '@stoplight/json-schema-tree'; | ||
import { Box, Flex } from '@stoplight/mosaic'; | ||
import { ErrorBoundaryForwardedProps, FallbackComponent, withErrorBoundary } from '@stoplight/react-error-boundary'; | ||
import { Tree, TreeState, TreeStore } from '@stoplight/tree-list'; | ||
import { isRegularNode, SchemaTree as JsonSchemaTree, SchemaTreeRefDereferenceFn } from '@stoplight/json-schema-tree'; | ||
import { Box, VStack } from '@stoplight/mosaic'; | ||
import { ErrorBoundaryForwardedProps, FallbackProps, withErrorBoundary } from '@stoplight/react-error-boundary'; | ||
import cn from 'classnames'; | ||
import type { JSONSchema4 } from 'json-schema'; | ||
import { action } from 'mobx'; | ||
import * as React from 'react'; | ||
|
||
import { SchemaTreeContext, ViewModeContext } from '../contexts'; | ||
import { SchemaTreeListTree, SchemaTreeOptions } from '../tree'; | ||
import type { GoToRefHandler, RowRenderer, ViewMode } from '../types'; | ||
import { SchemaTree as SchemaTreeComponent } from './SchemaTree'; | ||
import { JSVOptions, JSVOptionsContextProvider } from '../contexts'; | ||
import { SchemaRow } from './SchemaRow'; | ||
|
||
export interface IJsonSchemaViewer { | ||
export type JsonSchemaProps = Partial<JSVOptions> & { | ||
schema: JSONSchema4; | ||
emptyText?: string; | ||
defaultExpandedDepth?: number; | ||
className?: string; | ||
maxRows?: number; | ||
onGoToRef?: GoToRefHandler; | ||
mergeAllOf?: boolean; | ||
FallbackComponent?: typeof FallbackComponent; | ||
rowRenderer?: RowRenderer; | ||
resolveRef?: SchemaTreeRefDereferenceFn; | ||
viewMode?: ViewMode; | ||
} | ||
|
||
export class JsonSchemaViewerComponent extends React.PureComponent< | ||
IJsonSchemaViewer & ErrorBoundaryForwardedProps, | ||
{ isEmpty: boolean } | ||
> { | ||
protected readonly treeStore: TreeStore; | ||
protected readonly tree: SchemaTreeListTree; | ||
protected readonly treeState: TreeState; | ||
|
||
public readonly state = { | ||
isEmpty: true, | ||
}; | ||
|
||
constructor(props: IJsonSchemaViewer & ErrorBoundaryForwardedProps) { | ||
super(props); | ||
}; | ||
|
||
this.treeState = new TreeState(); | ||
this.tree = new SchemaTreeListTree(props.schema, this.treeState, this.treeOptions); | ||
this.treeStore = new TreeStore(this.tree, this.treeState, { | ||
defaultExpandedDepth: this.expandedDepth, | ||
const JsonSchemaViewerComponent: React.FC<JsonSchemaProps & ErrorBoundaryForwardedProps> = ({ | ||
schema, | ||
viewMode = 'standalone', | ||
className, | ||
resolveRef, | ||
emptyText = 'No schema defined', | ||
defaultExpandedDepth = 2, | ||
onGoToRef, | ||
renderRowAddon, | ||
}) => { | ||
const jsonSchemaTreeRoot = React.useMemo(() => { | ||
const jsonSchemaTree = new JsonSchemaTree(schema, { | ||
mergeAllOf: true, | ||
refResolver: resolveRef, | ||
}); | ||
} | ||
jsonSchemaTree.walker.hookInto('filter', node => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You lost |
||
if (!isRegularNode(node)) return true; | ||
|
||
protected get treeOptions(): SchemaTreeOptions { | ||
return { | ||
expandedDepth: this.expandedDepth, | ||
mergeAllOf: this.mergeAllOf, | ||
resolveRef: this.props.resolveRef, | ||
viewMode: this.props.viewMode, | ||
}; | ||
} | ||
const { validations } = node; | ||
|
||
protected get mergeAllOf() { | ||
return this.props.mergeAllOf !== false; | ||
} | ||
|
||
protected get expandedDepth(): number { | ||
return this.props.defaultExpandedDepth ?? 1; | ||
} | ||
if (!!validations.writeOnly === !!validations.readOnly) { | ||
return true; | ||
} | ||
|
||
protected renderSchema() { | ||
if (this.tree.count > 0) { | ||
this.tree.setRoot(Tree.createArtificialRoot()); | ||
} | ||
|
||
this.tree.populate(); | ||
this.setState({ | ||
isEmpty: this.tree.jsonSchemaTree.root.children.every(node => !isRegularNode(node) || node.unknown), | ||
return !((viewMode === 'read' && !!validations.writeOnly) || (viewMode === 'write' && !!validations.readOnly)); | ||
}); | ||
jsonSchemaTree.populate(); | ||
return jsonSchemaTree.root; | ||
}, [schema, resolveRef, viewMode]); | ||
|
||
const isEmpty = React.useMemo(() => jsonSchemaTreeRoot.children.every(node => !isRegularNode(node) || node.unknown), [ | ||
jsonSchemaTreeRoot, | ||
]); | ||
|
||
const options = React.useMemo(() => ({ defaultExpandedDepth, viewMode, onGoToRef, renderRowAddon }), [ | ||
defaultExpandedDepth, | ||
viewMode, | ||
onGoToRef, | ||
renderRowAddon, | ||
]); | ||
|
||
if (isEmpty) { | ||
return <Box className={cn(className, 'JsonSchemaViewer')}>{emptyText}</Box>; | ||
} | ||
|
||
public componentDidMount() { | ||
this.renderSchema(); | ||
} | ||
|
||
@action | ||
public componentDidUpdate(prevProps: Readonly<IJsonSchemaViewer>) { | ||
if (prevProps.resolveRef !== this.props.resolveRef) { | ||
this.tree.treeOptions.resolveRef = this.props.resolveRef; | ||
} | ||
|
||
if ( | ||
this.treeStore.defaultExpandedDepth !== this.expandedDepth || | ||
prevProps.schema !== this.props.schema || | ||
prevProps.mergeAllOf !== this.props.mergeAllOf || | ||
prevProps.viewMode !== this.props.viewMode | ||
) { | ||
this.treeStore.defaultExpandedDepth = this.expandedDepth; | ||
this.tree.treeOptions = this.treeOptions; | ||
this.tree.schema = this.props.schema; | ||
this.renderSchema(); | ||
} | ||
} | ||
|
||
public render() { | ||
const { | ||
props: { emptyText = 'No schema defined', schema, defaultExpandedDepth, className, ...props }, | ||
} = this; | ||
|
||
if (this.state.isEmpty) { | ||
return <div>{emptyText}</div>; | ||
} | ||
|
||
return ( | ||
<Flex pos="relative" h="full" className={cn(className, 'JsonSchemaViewer')}> | ||
<SchemaTreeContext.Provider value={this.tree}> | ||
<ViewModeContext.Provider value={this.props.viewMode ?? 'standalone'}> | ||
<SchemaTreeComponent schema={schema} treeStore={this.treeStore} {...props} /> | ||
</ViewModeContext.Provider> | ||
</SchemaTreeContext.Provider> | ||
</Flex> | ||
); | ||
} | ||
} | ||
return ( | ||
<JSVOptionsContextProvider value={options}> | ||
<VStack divider className={cn(className, 'JsonSchemaViewer')}> | ||
{jsonSchemaTreeRoot.children.map(childJsonSchemaNode => ( | ||
<SchemaRow key={childJsonSchemaNode.id} schemaNode={childJsonSchemaNode} nestingLevel={0} /> | ||
))} | ||
</VStack> | ||
</JSVOptionsContextProvider> | ||
); | ||
}; | ||
|
||
const JsonSchemaFallbackComponent: typeof FallbackComponent = ({ error }) => { | ||
const JsonSchemaFallbackComponent: React.FC<FallbackProps> = ({ error }) => { | ||
return ( | ||
<Box p={4}> | ||
<b className="text-danger">Error</b> | ||
|
@@ -130,7 +80,7 @@ const JsonSchemaFallbackComponent: typeof FallbackComponent = ({ error }) => { | |
); | ||
}; | ||
|
||
export const JsonSchemaViewer = withErrorBoundary<IJsonSchemaViewer>(JsonSchemaViewerComponent, { | ||
export const JsonSchemaViewer = withErrorBoundary<JsonSchemaProps>(JsonSchemaViewerComponent, { | ||
FallbackComponent: JsonSchemaFallbackComponent, | ||
recoverableProps: ['schema'], | ||
reportErrors: false, | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we still rely on UI-Kit in JSV?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unfortunately it's still a peer of MDV :\
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I removed the include from the storybook
_styles.scss
. That at least make Storybook rebuilds faster.... which is a win I guess... 😅