Skip to content

Commit 1a019bd

Browse files
committed
EIWFY23Q4-1 Add validation tab, fix up webpack dev server
1 parent 4f9085a commit 1a019bd

16 files changed

+1510
-590
lines changed

.github/workflows/node-lint.yml

+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# This workflow will do a clean installation of node dependencies, cache/restore them, and run linting
2+
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
3+
4+
name: Node.js CI
5+
6+
on:
7+
push:
8+
branches: [ $default-branch ]
9+
pull_request:
10+
branches: [ $default-branch ]
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
strategy:
18+
matrix:
19+
node-version: [16.x]
20+
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
21+
22+
steps:
23+
- uses: actions/checkout@v3
24+
- name: Use Node.js ${{ matrix.node-version }}
25+
uses: actions/setup-node@v3
26+
with:
27+
node-version: ${{ matrix.node-version }}
28+
cache: 'yarn'
29+
- run: yarn install --frozen-lockfile
30+
- run: yarn lint

.npmrc

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Use public registry instead of Atlassian internal
2+
registry=https://registry.yarnpkg.com

.nvmrc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
12
1+
16

.prettierrc

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"singleQuote": true,
3+
"printWidth": 100
4+
}

package.json

+9-4
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,15 @@
1818
"@atlaskit/menu": "^0.7.0",
1919
"@atlaskit/popup": "^1.0.6",
2020
"@atlaskit/spinner": "^15.0.6",
21+
"@atlaskit/table": "^0.4.6",
2122
"@atlaskit/tabs": "^12.1.2",
2223
"@atlaskit/textfield": "^5.0.0",
2324
"@atlaskit/theme": "^11.0.2",
2425
"@atlaskit/tooltip": "^17.1.2",
25-
"@monaco-editor/react": "^4.0.7",
26+
"@monaco-editor/react": "^4.5.1",
2627
"js-yaml": "^4.0.0",
2728
"jsonpointer": "^4.1.0",
28-
"monaco-editor": "^0.21.3",
29+
"monaco-editor": "^0.39.0",
2930
"react": "^16.8",
3031
"react-copy-to-clipboard": "^5.0.2",
3132
"react-dom": "^16.8",
@@ -60,18 +61,22 @@
6061
"http-server-spa": "^1.3.0",
6162
"json-schema-to-typescript": "^10.1.2",
6263
"npm-run-all": "^4.1.5",
64+
"prettier": "^2.8.8",
6365
"raw-loader": "^4.0.2",
6466
"source-map-loader": "^2.0.0",
6567
"style-loader": "^2.0.0",
6668
"ts-loader": "^8.0.14",
6769
"ts-node": "^9.1.1",
6870
"typescript": "^4.1.3",
6971
"webpack": "^5.12.2",
70-
"webpack-cli": "^4.3.1",
71-
"webpack-dev-server": "^3.11.1",
72+
"webpack-cli": "^5.1.4",
73+
"webpack-dev-server": "^4.15.1",
7274
"webpack-merge": "^5.7.3"
7375
},
7476
"scripts": {
77+
"start": "webpack serve -c webpack.dev.js",
78+
"lint": "tsc --noEmit",
79+
"postinstall": "npm run gen-schema",
7580
"build-dev": "webpack -c webpack.dev.js",
7681
"build-prod": "webpack -c webpack.prod.js",
7782
"build": "rm -fr dist && webpack -c webpack.prod.js",

src/SchemaApp.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ const RecentlyViewedMenu: React.FC<RecentlyViewedMenuProps> = (props) => {
5656
const ExampleMenu: React.FC<ContentPropsWithClose> = (props) => (
5757
<PopupMenuGroup>
5858
{Array.from(Object.entries(exampleSchemas)).map(([title, links]) => (
59-
<Section title={title}>
59+
<Section key={title} title={title}>
6060
{Array.from(Object.entries(links)).map(([linkTitle, url]) => (
61-
<NavigationButtonItem onClick={props.closePopup} exampleUrl={url}>{linkTitle}</NavigationButtonItem>
61+
<NavigationButtonItem key={url} onClick={props.closePopup} exampleUrl={url}>{linkTitle}</NavigationButtonItem>
6262
))}
6363
</Section>
6464
))}
@@ -169,4 +169,4 @@ class SchemaAppWR extends React.PureComponent<RouteComponentProps, SchemaAppStat
169169
}
170170
}
171171

172-
export const SchemaApp = withRouter<RouteComponentProps, typeof SchemaAppWR>(SchemaAppWR);
172+
export const SchemaApp = withRouter<RouteComponentProps, typeof SchemaAppWR>(SchemaAppWR);

src/SchemaEditor.tsx

+24-8
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
import React, { useEffect } from 'react';
22
import { JsonSchema } from './schema';
3-
import Editor, { useMonaco } from "@monaco-editor/react";
3+
import Editor, { OnValidate, useMonaco } from '@monaco-editor/react';
4+
import type { IRange } from 'monaco-editor';
5+
import { ScrollType } from "./monaco-helpers";
46

57
export type SchemaEditorProps = {
6-
initialContent: unknown;
7-
schema: JsonSchema;
8-
}
8+
initialContent: unknown;
9+
schema: JsonSchema;
10+
validationRange?: IRange;
11+
onValidate: OnValidate;
12+
};
913

1014
/**
1115
* No more than 50 characters per line.
@@ -17,6 +21,7 @@ const editorPreamble = `
1721
`.trim();
1822

1923
export const SchemaEditor: React.FC<SchemaEditorProps> = (props) => {
24+
const { initialContent, schema, validationRange, onValidate } = props;
2025
const monaco = useMonaco();
2126

2227
useEffect(() => {
@@ -26,19 +31,30 @@ export const SchemaEditor: React.FC<SchemaEditorProps> = (props) => {
2631
schemas: [{
2732
uri: "https://json-schema.app/example.json", // id of the first schema
2833
fileMatch: ['a://b/example.json'],
29-
schema: props.schema
34+
schema: schema
3035
}]
3136
});
32-
}, [monaco, props.schema]);
37+
}, [monaco, schema]);
38+
useEffect(() => {
39+
if (!validationRange || !monaco) {
40+
return;
41+
}
42+
monaco.editor.getEditors()
43+
.forEach((codeEditor) => {
44+
codeEditor.setSelection(validationRange)
45+
codeEditor.revealRangeAtTop(validationRange, ScrollType.Smooth)
46+
});
47+
}, [monaco, validationRange])
3348

3449
return (
3550
<Editor
3651
height="97vh"
3752
defaultLanguage="json"
38-
value={editorPreamble + '\n' + JSON.stringify(props.initialContent, null, 2)}
53+
value={editorPreamble + '\n' + JSON.stringify(initialContent, null, 2)}
3954
path="a://b/example.json"
4055
theme="vs-dark"
4156
saveViewState={false}
57+
onValidate={onValidate}
4258
/>
4359
);
44-
};
60+
};

src/SchemaExplorer.tsx

+60-46
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import { getTitle, findTitle } from './title';
2020
import { LinkPreservingSearch, NavLinkPreservingSearch } from './search-preserving-link';
2121
import { dump } from 'js-yaml';
2222
import { isExternalReference } from './type-inference';
23+
import { SchemaValidator } from './SchemaValidator';
24+
import type { editor, IRange } from 'monaco-editor';
2325

2426
interface SEPHeadProps {
2527
basePathSegments: Array<string>;
@@ -416,29 +418,19 @@ export type SchemaExplorerProps = {
416418
schema: JsonSchema1;
417419
stage: Stage;
418420
lookup: Lookup;
421+
onSelectValidationRange: (range: IRange) => void;
422+
validationResults: editor.IMarker[]
419423
};
420424

421-
export type ViewType = 'details' | 'example-json' | 'example-yaml';
425+
export type ViewType = 'details' | 'example-json' | 'example-yaml' | 'validator';
422426

423427
export type SchemaExplorerState = {
424428
pathExpanded: boolean;
425429
view: ViewType;
426430
};
427431

428-
const LabelToViewType: { [label: string]: ViewType } = {
429-
'Details': 'details',
430-
'Example (JSON)': 'example-json',
431-
'Example (YAML)': 'example-yaml'
432-
};
433-
434-
const ViewTypeToTab: { [viewType: string]: number } = {
435-
'details': 0,
436-
'example-json': 1,
437-
'example-yaml': 2
438-
};
439-
440432
export class SchemaExplorer extends React.PureComponent<SchemaExplorerProps, SchemaExplorerState> {
441-
private static Container = styled.section`
433+
public static Container = styled.section`
442434
display: flex;
443435
flex-direction: column;
444436
flex-grow: 1;
@@ -447,61 +439,79 @@ export class SchemaExplorer extends React.PureComponent<SchemaExplorerProps, Sch
447439
max-width: 100%;
448440
`;
449441

450-
private static HeadingContainer = styled.div`
442+
public static HeadingContainer = styled.div`
451443
display: flex;
452444
flex-direction: row;
453445
justify-content: space-between;
454446
`;
455447

456-
private static Heading = styled.h1`
448+
public static Heading = styled.h1`
457449
font-size: 16px;
458450
font-weight: 600;
459451
padding-top: 24px;
460452
margin: 5px 8px;
461453
`;
462454

463-
UNSAFE_componentWillMount() {
464-
this.setState({
455+
constructor(props: SchemaExplorerProps) {
456+
super(props);
457+
this.state = {
465458
pathExpanded: false,
466459
view: 'details'
467-
});
460+
}
468461
}
469462

470463
render() {
471-
const { path, schema, lookup, stage, basePathSegments } = this.props;
464+
const { path, schema, lookup, stage, basePathSegments, validationResults, onSelectValidationRange } = this.props;
472465
const { pathExpanded } = this.state;
473466
if (path.length === 0) {
474467
return <div>TODO What do we do when the reference could not be found? Error maybe?</div>;
475468
}
476469

477470
const currentPathElement = path[path.length - 1];
478471

479-
const tabData: TabData[] = [{
480-
label: 'Details',
481-
content: (
482-
<SchemaExplorerDetails
483-
schema={schema}
484-
reference={currentPathElement.reference}
485-
lookup={lookup}
486-
stage={stage}
487-
clickElement={createClickElement({ basePathSegments, path })}
488-
/>
489-
)
490-
}, {
491-
label: 'Example (JSON)',
492-
content: (
493-
<SchemaExplorerExample schema={schema} lookup={lookup} stage={stage} format="json" />
494-
)
495-
}, {
496-
label: 'Example (YAML)',
497-
content: (
498-
<SchemaExplorerExample schema={schema} lookup={lookup} stage={stage} format="yaml" />
499-
)
500-
}];
501-
502-
const onTabSelect: OnSelectCallback = tab => {
472+
type ExtendedTabData = TabData & {
473+
view: ViewType;
474+
}
475+
const tabData: ExtendedTabData[] = [
476+
{
477+
view: 'details',
478+
label: 'Details',
479+
content: (
480+
<SchemaExplorerDetails
481+
schema={schema}
482+
reference={currentPathElement.reference}
483+
lookup={lookup}
484+
stage={stage}
485+
clickElement={createClickElement({ basePathSegments, path })}
486+
/>
487+
),
488+
},
489+
{
490+
view: 'example-json',
491+
label: 'Example (JSON)',
492+
content: (
493+
<SchemaExplorerExample schema={schema} lookup={lookup} stage={stage} format="json" />
494+
),
495+
},
496+
{
497+
view: 'example-yaml',
498+
label: 'Example (YAML)',
499+
content: (
500+
<SchemaExplorerExample schema={schema} lookup={lookup} stage={stage} format="yaml" />
501+
),
502+
},
503+
{
504+
view: 'validator',
505+
label: `Validation results (${validationResults.length})`,
506+
content: (
507+
<SchemaValidator results={validationResults} onSelectRange={onSelectValidationRange} />
508+
),
509+
},
510+
];
511+
512+
const onTabSelect: OnSelectCallback = (tab) => {
503513
this.setState({
504-
view: LabelToViewType[tab.label || 'Details']
514+
view: (tab as ExtendedTabData).view
505515
});
506516
};
507517

@@ -517,7 +527,11 @@ export class SchemaExplorer extends React.PureComponent<SchemaExplorerProps, Sch
517527
<SchemaExplorer.Heading>{getTitle(currentPathElement.reference, schema)}</SchemaExplorer.Heading>
518528
<Permalink />
519529
</SchemaExplorer.HeadingContainer>
520-
<Tabs tabs={tabData} selected={ViewTypeToTab[this.state.view || 'details']} onSelect={onTabSelect} />
530+
<Tabs
531+
tabs={tabData}
532+
onSelect={onTabSelect}
533+
selected={tabData.findIndex((tab) => tab.view === (this.state.view || 'details'))}
534+
/>
521535
</SchemaExplorer.Container>
522536
);
523537
}

0 commit comments

Comments
 (0)