Skip to content

Commit 633aa5e

Browse files
committed
Added spectral
Added lint panel Fixed: core spec according to new oas schema Added: Close button to lint panel Added: Reaction to autolint spec on changes Added: Basic search functionality in panel Added: Minor improvements in panel Fixed: Minor mobx issues
1 parent 48cdeb9 commit 633aa5e

File tree

18 files changed

+2938
-214
lines changed

18 files changed

+2938
-214
lines changed

package-lock.json

Lines changed: 2438 additions & 183 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"@blueprintjs/select": "^3.18.3",
3030
"@reduxjs/toolkit": "^1.6.1",
3131
"@stoplight/path": "^1.3.2",
32+
"@stoplight/spectral-cli": "^6.1.0",
3233
"class-autobind-decorator": "^3.0.1",
3334
"classnames": "^2.3.1",
3435
"highlight.js": "^11.3.1",

src/Stores/graphStore.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {observable, action, makeObservable, runInAction} from 'mobx';
1+
import {observable, action, makeObservable, runInAction, reaction} from 'mobx';
22
import {join} from 'lodash';
33
import Graph from './graph';
44
import {recomputeGraphNodes} from './graph/addNode';
@@ -82,6 +82,13 @@ class GraphStore {
8282
]);
8383
};
8484

85+
reaction(
86+
() => this.rootNode && this.rootNode.data.parsed,
87+
(spec) => {
88+
this.props.lintStore.lint(spec);
89+
},
90+
);
91+
8592
this.removeNode = (nodeId) => {
8693
//e
8794
const node = this.getNodeById(nodeId); //t

src/Stores/lintStore.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {makeObservable, observable, action} from 'mobx';
2+
import {DiagnosticSeverity} from '@stoplight/types';
3+
4+
class LintStore {
5+
errors = [];
6+
warning = [];
7+
info = [];
8+
hints = [];
9+
10+
constructor(stores) {
11+
makeObservable(this, {
12+
errors: observable,
13+
warning: observable,
14+
info: observable,
15+
hints: observable,
16+
handleWorkerMessage: action.bound,
17+
activate: action,
18+
});
19+
20+
this.stores = stores;
21+
this.info = [];
22+
this.worker = new Worker(new URL('../worker/lint.worker', import.meta.url));
23+
}
24+
25+
activate() {
26+
this.worker.onmessage = this.handleWorkerMessage;
27+
}
28+
29+
get spec() {
30+
return this.stores.graphStore.rootNode.data.parsed;
31+
}
32+
33+
lint(doc) {
34+
const spec = doc || this.spec;
35+
this.worker.postMessage({msg: 'abc', spec});
36+
}
37+
38+
handleWorkerMessage({data}) {
39+
const results = data.results;
40+
this.errors = results.filter(
41+
(r) => r.severity === DiagnosticSeverity.Error,
42+
);
43+
this.warning = results.filter(
44+
(r) => r.severity === DiagnosticSeverity.Warning,
45+
);
46+
this.hints = results.filter((r) => r.severity === DiagnosticSeverity.Hint);
47+
this.info = results.filter((r) => r.severity === DiagnosticSeverity.Info);
48+
}
49+
}
50+
51+
export default LintStore;

src/Stores/stores.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import JsonSchemaStore from './jsonSchemaStore';
77
import OasSchemaStore from './oasSchemaStore';
88
import BrowserStore from './browserStore';
99
import EventEmitter from '../EventEmitter';
10+
import LintStore from './lintStore';
1011

1112
class Stores {
1213
constructor() {
@@ -19,6 +20,7 @@ class Stores {
1920
this.jsonSchemaCollection = this.generateJsonSchemaCollection(this);
2021
this.oasSchemaCollection = this.generateOasSchemaCollection(this);
2122
this.editorStore = new EditorStore(this);
23+
this.lintStore = new LintStore(this);
2224

2325
this.registerEventListeners();
2426
this.activate();
@@ -120,6 +122,7 @@ class Stores {
120122
this.oasStore.activate();
121123
this.designTreeStore.activate();
122124
this.editorStore.doActivate();
125+
this.lintStore.activate();
123126
}
124127
}
125128

src/Stores/uiStore.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,18 @@ class UiStore {
1212
activeSymbolNode;
1313
fullscreen;
1414
activeView;
15+
activeWidget;
16+
1517
views = {
1618
code: 'code',
1719
form: 'form',
1820
preview: 'preview',
1921
};
2022

23+
widgets = {
24+
lint: 'lint',
25+
};
26+
2127
constructor(stores) {
2228
makeObservable(this, {
2329
toggleFullscreen: action.bound,
@@ -41,7 +47,10 @@ class UiStore {
4147
activeNode: computed,
4248
activeNodeId: computed,
4349
activeView: observable,
50+
activeWidget: observable,
4451
setActiveView: action,
52+
setActiveWidget: action,
53+
toggleWidget: action,
4554
});
4655

4756
this.stores = stores;
@@ -52,6 +61,7 @@ class UiStore {
5261
this._chosenSourceNodeUri = undefined;
5362
this._chosenSymbolNodeUri = undefined;
5463
this.activeView = this.views.form;
64+
this.activeWidget = null;
5565
this._preferences = observable.object(
5666
{
5767
activeSidebarTree: 'design',
@@ -282,6 +292,22 @@ class UiStore {
282292
}
283293
}
284294

295+
setActiveWidget(widget) {
296+
if (Object.keys(this.widgets).indexOf(widget) >= 0) {
297+
this.activeWidget = widget;
298+
}
299+
}
300+
301+
toggleWidget(widget) {
302+
if (Object.keys(this.widgets).indexOf(widget) >= 0) {
303+
if (widget === this.activeWidget) {
304+
this.activeWidget = null;
305+
} else {
306+
this.activeWidget = widget;
307+
}
308+
}
309+
}
310+
285311
get _storageKey() {
286312
return 'oasdesigner-ui';
287313
}

src/components/Content/index.js

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import Parameter from './parameter';
1212
import Response from './response';
1313
import RequestBody from './RequestBody';
1414
import MonacoEditor from '../Editor/Monaco';
15+
import LintWidget from '../Widgets/Lint';
1516
import {StoresContext} from '../Context';
1617
import {NodeCategories, NodeTypes} from '../../datasets/tree';
1718

@@ -39,12 +40,23 @@ const getComponentForNode = (node) => {
3940
};
4041

4142
const SubContent = observer(({node}) => {
43+
const stores = React.useContext(StoresContext);
44+
const {activeWidget, widgets} = stores.uiStore;
4245
let relativeJsonPath = [];
4346
if (node.category === NodeCategories.SourceMap) {
4447
relativeJsonPath = node.relativeJsonPath;
4548
}
4649
const RenderSubContent = getComponentForNode(node);
47-
return <RenderSubContent relativeJsonPath={relativeJsonPath} node={node} />;
50+
return activeWidget ? (
51+
<div className="flex-1 relative flex">
52+
<RenderSubContent relativeJsonPath={relativeJsonPath} node={node} />
53+
<div className="relative flex-1 border-l">
54+
{activeWidget === widgets.lint && <LintWidget />}
55+
</div>
56+
</div>
57+
) : (
58+
<RenderSubContent relativeJsonPath={relativeJsonPath} node={node} />
59+
);
4860
});
4961

5062
SubContent.propTypes = {
@@ -56,14 +68,13 @@ const Content = observer(() => {
5668
const {activeNode, activeView, views} = stores.uiStore;
5769
const sourceNode = stores.graphStore.rootNode;
5870

59-
const toggleView = (view) => stores.uiStore.setActiveView(view);
60-
6171
return (
6272
<StyledContent className={'flex flex-col flex-1'}>
6373
<div className="bp3-dark relative flex flex-1 flex-col bg-canvas">
6474
<Options
6575
view={activeView}
66-
onToggleView={toggleView}
76+
onToggleView={(v) => stores.uiStore.setActiveView(v)}
77+
onToggleWidget={(w) => stores.uiStore.setActiveWidget(w)}
6778
onDelete={() => {
6879
stores.graphStore.removeNode(activeNode.id);
6980
}}

src/components/Content/info/index.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,21 +48,6 @@ const Info = observer(() => {
4848
}
4949
/>
5050
</div>
51-
<div className="font-semibold text-gray-6 dark:text-lighten-8 ml-1 mb-2">
52-
Summary
53-
</div>
54-
<InputGroup
55-
className="mx-1 -mb-2 StudioInput"
56-
placeholder="summary"
57-
value={getValueFromStore(['info', 'summary']) || ''}
58-
onChange={(e) =>
59-
handlePatch(
60-
nodeOperations.Replace,
61-
['info', 'summary'],
62-
e.target.value,
63-
)
64-
}
65-
/>
6651
<div className="font-semibold text-gray-6 dark:text-lighten-8 ml-1 mt-8 mb-2">
6752
Description
6853
</div>

src/components/Content/options.js

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,33 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
33
import {observer} from 'mobx-react-lite';
4-
import {Button, ButtonGroup, Icon} from '@blueprintjs/core';
4+
import classnames from 'classnames';
5+
import {Button, ButtonGroup, Icon, Intent} from '@blueprintjs/core';
56
import {StoresContext} from '../Context';
67
import {NodeCategories} from '../../datasets/tree';
78

89
const Options = observer((props) => {
910
const stores = React.useContext(StoresContext);
10-
const {activeNode} = stores.uiStore;
11+
const {activeNode, activeWidget, widgets} = stores.uiStore;
1112
const [confirmDelete, setConfirmDelete] = React.useState(false);
13+
const {errors, hints, info, warning} = stores.lintStore;
14+
15+
const lintSpec = () => {
16+
if (activeWidget === widgets.lint) {
17+
stores.uiStore.toggleWidget(stores.uiStore.widgets.lint);
18+
} else {
19+
stores.uiStore.setActiveWidget(stores.uiStore.widgets.lint);
20+
}
21+
};
1222

1323
return (
14-
<div className="border-b border-transparent PanelActionBar w-full flex items-center px-5 py-2">
24+
<div
25+
className={classnames(
26+
'border-b PanelActionBar w-full flex items-center px-5 py-2',
27+
{
28+
'border-transparent': !activeWidget,
29+
},
30+
)}>
1531
{activeNode && activeNode.category === NodeCategories.SourceMap && (
1632
<div className="flex items-center">
1733
<Button
@@ -50,13 +66,54 @@ const Options = observer((props) => {
5066
text="Code"
5167
onClick={() => props.onToggleView('code')}
5268
/>
69+
</ButtonGroup>
70+
</div>
71+
<div className="ml-3">
72+
<ButtonGroup>
5373
<Button
5474
active={props.view === 'docs'}
5575
small
5676
icon={<Icon size={14} icon="book" />}
5777
text="Preview"
5878
onClick={() => props.onToggleView('preview')}
5979
/>
80+
<Button
81+
active={stores.uiStore.activeWidget === stores.uiStore.widgets.lint}
82+
small
83+
text={
84+
<div className="flex justify-center">
85+
<Icon
86+
size={14}
87+
icon="error"
88+
intent={Intent.DANGER}
89+
className="m-auto pl-2 pr-1"
90+
/>
91+
<span className="counter m-auto">{errors.length}</span>
92+
<Icon
93+
size={14}
94+
intent={Intent.WARNING}
95+
icon="warning-sign"
96+
className="m-auto pl-2 pr-1"
97+
/>
98+
<span className="counter m-auto">{warning.length}</span>
99+
<Icon
100+
size={14}
101+
icon="info-sign"
102+
intent={Intent.PRIMARY}
103+
className="m-auto pl-2 pr-1"
104+
/>
105+
<span className="counter m-auto">{info.length}</span>
106+
<Icon
107+
size={14}
108+
icon="lightbulb"
109+
intent={Intent.SUCCESS}
110+
className="m-auto pl-2 pr-1"
111+
/>
112+
<span className="counter m-auto">{hints.length}</span>
113+
</div>
114+
}
115+
onClick={() => lintSpec()}
116+
/>
60117
</ButtonGroup>
61118
</div>
62119
<div className="ml-3">

0 commit comments

Comments
 (0)