mirror of
https://github.com/Kong/insomnia.git
synced 2026-10-25 08:09:06 -04:00
Sidebar code mirror interactions (#2433)
* Lifting path clicked up to sidebar root * Abstracting out path click to story (and app) * Adding click to scroll, updating sidebar components * Scroll fix for sidebar * Cleaning up log * PR feedback * PR Feedback, fix schemas & request body rendering * PR feedback & cleanup * Prop value checking, selection positioning * Pulling remote import * Styled components into insomnia-app, obj destructuring, typing * Refactoring item path mapping * Abstracting the mapping of specs for position, bumping down styled components version. * Directly passing ... rest args initial work * Pulling logs, removing array concatenation * Pulling click handler out of render method * Rolling position mapping into scroll positining method * Opening up type, could be number/string/array * Update package-lock.json * Grabbing height from window, typing API vars, checking path * Creating scroll method dedicated to design mode * Moving typing of spec related content to Props def. * Type checking on sections, invalid section component
This commit is contained in:
1 parent
e9fb34490a
commit
784357f089
21 files changed
+2981
-8445
No files matched your search
Generated
+2446
-8158
File diff suppressed because it is too large.
Load diff
+1
-2
@@ -69,6 +69,5 @@
|
||||
"lint-staged": "^10.2.2",
|
||||
"prettier": "^1.16.4",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
}
|
||||
@@ -160,6 +160,19 @@ class CodeEditor extends React.Component {
|
||||
setSelection(chStart, chEnd, lineStart, lineEnd) {
|
||||
if (this.codeMirror) {
|
||||
this.codeMirror.setSelection({ line: lineStart, ch: chStart }, { line: lineEnd, ch: chEnd });
|
||||
this.codeMirror.scrollIntoView({ line: lineStart, char: chStart });
|
||||
}
|
||||
}
|
||||
|
||||
scrollToSelection(chStart, chEnd, lineStart, lineEnd) {
|
||||
const selectionFocusPos = window.innerHeight / 2 - 100;
|
||||
if (this.codeMirror) {
|
||||
this.codeMirror.setSelection({ line: lineStart, ch: chStart }, { line: lineEnd, ch: chEnd });
|
||||
this.codeMirror.scrollIntoView(
|
||||
{ line: lineStart, char: chStart },
|
||||
// If sizing permits, position selection just above center
|
||||
selectionFocusPos,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import autobind from 'autobind-decorator';
|
||||
|
||||
type Props = {|
|
||||
name: string,
|
||||
onClick: (SyntheticEvent<HTMLButtonElement>) => void,
|
||||
children?: React.Node,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
open: boolean,
|
||||
|};
|
||||
|
||||
@autobind
|
||||
class SpecEditorSidebarItem extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
open: false,
|
||||
};
|
||||
}
|
||||
|
||||
_handleExpand(e: SyntheticEvent<HTMLButtonElement>) {
|
||||
this.setState(state => ({ open: !state.open }));
|
||||
}
|
||||
|
||||
render() {
|
||||
const { name, onClick, children } = this.props;
|
||||
const { open } = this.state;
|
||||
return (
|
||||
<li>
|
||||
<button onClick={this._handleExpand}>
|
||||
{children ? (
|
||||
<i className={classnames('fa', open ? 'fa-caret-down' : 'fa-caret-right')} />
|
||||
) : (
|
||||
<i className="fa fa-empty" />
|
||||
)}
|
||||
</button>
|
||||
<button onClick={onClick}>{name}</button>
|
||||
{open ? children : null}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SpecEditorSidebarItem;
|
||||
@@ -1,8 +1,9 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import autobind from 'autobind-decorator';
|
||||
import YAML from 'yaml';
|
||||
import SpecEditorSidebarItem from './spec-editor-sidebar-item';
|
||||
import YAMLSourceMap from 'yaml-source-map';
|
||||
import { Sidebar } from 'insomnia-components';
|
||||
import type { ApiSpec } from '../../../models/api-spec';
|
||||
import { trackEvent } from '../../../common/analytics';
|
||||
@@ -16,8 +17,13 @@ type State = {|
|
||||
error: string,
|
||||
|};
|
||||
|
||||
const StyledSpecEditorSidebar: React.ComponentType<{}> = styled.div`
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
`;
|
||||
|
||||
@autobind
|
||||
class SpecEditorSidebar extends React.PureComponent<Props, State> {
|
||||
class SpecEditorSidebar extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
@@ -37,120 +43,46 @@ class SpecEditorSidebar extends React.PureComponent<Props, State> {
|
||||
handleSetSelection(pos.start.col - 1, pos.end.col - 1, pos.start.line - 1, pos.end.line - 1);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { apiSpec } = this.props;
|
||||
const { error } = this.state;
|
||||
|
||||
if (apiSpec.type === 'json') {
|
||||
this.setState({
|
||||
error:
|
||||
'Tree navigation is not yet supported for JSON-formatted spec ' +
|
||||
'files. Please use YAML instead.',
|
||||
});
|
||||
} else if (error) {
|
||||
this.setState({ error: '' });
|
||||
}
|
||||
_mapPosition(itemPath: Array<any>) {
|
||||
const sourceMap = new YAMLSourceMap();
|
||||
const specMap = sourceMap.index(
|
||||
YAML.parseDocument(this.props.apiSpec.contents, { keepCstNodes: true }),
|
||||
);
|
||||
const itemMappedPosition = sourceMap.lookup(itemPath, specMap);
|
||||
const isServersSection = itemPath[0] === 'servers';
|
||||
const scrollPosition = {
|
||||
start: {
|
||||
line: 0,
|
||||
col: 0,
|
||||
},
|
||||
end: {
|
||||
line: 0,
|
||||
col: 200,
|
||||
},
|
||||
};
|
||||
isServersSection
|
||||
? (scrollPosition.start.line = itemMappedPosition.start.line)
|
||||
: (scrollPosition.start.line = itemMappedPosition.start.line - 1);
|
||||
scrollPosition.end.line = scrollPosition.start.line;
|
||||
this._handleScrollEditor(scrollPosition);
|
||||
}
|
||||
|
||||
renderMap(collection: Object, path: string) {
|
||||
const children = [];
|
||||
for (let i = 0; i < collection.items.length; i++) {
|
||||
const curr: Object = collection.items[i];
|
||||
const next: ?Object = collection.items[i + 1];
|
||||
|
||||
// If the next one is a map value, we know we have a key in current
|
||||
if (!next || next.type !== 'MAP_VALUE') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newPath = `${path}.${curr.strValue}`;
|
||||
children.push(
|
||||
<SpecEditorSidebarItem
|
||||
key={newPath}
|
||||
name={curr.strValue}
|
||||
onClick={() => this._handleScrollEditor(curr.rangeAsLinePos)}>
|
||||
{this.renderNext(next, newPath)}
|
||||
</SpecEditorSidebarItem>,
|
||||
);
|
||||
|
||||
// Bump one more since we used both curr and next
|
||||
i++;
|
||||
}
|
||||
|
||||
return <ul key={path}>{children}</ul>;
|
||||
}
|
||||
|
||||
renderSequence(collection: Object, path: string) {
|
||||
const children = [];
|
||||
|
||||
for (let i = 0; i < collection.items.length; i++) {
|
||||
const item = collection.items[i];
|
||||
if (item.type !== 'SEQ_ITEM') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const newPath = `${path}[${i}]`;
|
||||
|
||||
children.push(
|
||||
<SpecEditorSidebarItem
|
||||
key={newPath}
|
||||
name={String(i)}
|
||||
onClick={() => this._handleScrollEditor(item.rangeAsLinePos)}>
|
||||
{this.renderNext(item, newPath)}
|
||||
</SpecEditorSidebarItem>,
|
||||
);
|
||||
}
|
||||
|
||||
return <ul key={path}>{children}</ul>;
|
||||
}
|
||||
|
||||
renderNext(thing: Object, path: string) {
|
||||
if (thing.type === 'MAP') {
|
||||
return this.renderMap(thing, path);
|
||||
} else if (thing.type === 'SEQ') {
|
||||
return this.renderSequence(thing, path);
|
||||
} else if (thing.node) {
|
||||
// Try rendering child node if it has one
|
||||
return this.renderNext(thing.node, path);
|
||||
}
|
||||
|
||||
// Everything else terminates recursion (eg. FLOW_SEQ, MAP_SEQ)
|
||||
return null;
|
||||
}
|
||||
_handleItemClick = (...itemPath): void => {
|
||||
this._mapPosition(itemPath);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { error } = this.state;
|
||||
|
||||
if (error) {
|
||||
return <p className="notice error margin-sm">{error}</p>;
|
||||
}
|
||||
|
||||
const { apiSpec } = this.props;
|
||||
const specCst = YAML.parseCST(apiSpec.contents);
|
||||
|
||||
let specJSON = {};
|
||||
|
||||
if (apiSpec.contentType === 'yaml') {
|
||||
specJSON = YAML.parse(apiSpec.contents);
|
||||
}
|
||||
|
||||
if (!specCst) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [document] = specCst;
|
||||
|
||||
if (!document.contents) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// const navigationEl = document.contents.map(v => this.renderNext(v, '$'));
|
||||
const specJSON = YAML.parse(this.props.apiSpec.contents);
|
||||
|
||||
return (
|
||||
<div className="spec-editor-sidebar">
|
||||
<Sidebar jsonData={specJSON} />
|
||||
{/* navigationEl */}
|
||||
</div>
|
||||
<StyledSpecEditorSidebar>
|
||||
<Sidebar jsonData={specJSON} onClick={this._handleItemClick} />
|
||||
</StyledSpecEditorSidebar>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class WrapperDesign extends React.PureComponent<Props, State> {
|
||||
return;
|
||||
}
|
||||
|
||||
editor.setSelection(chStart, chEnd, lineStart, lineEnd);
|
||||
editor.scrollToSelection(chStart, chEnd, lineStart, lineEnd);
|
||||
}
|
||||
|
||||
_handleLintClick(notice: {}) {
|
||||
|
||||
@@ -49,6 +49,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
.spec-editor-sidebar {
|
||||
overflow: hidden;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#swagger-ui-wrapper {
|
||||
border-left: 1px solid var(--hl-sm);
|
||||
overflow-y: scroll;
|
||||
|
||||
Generated
+97
-21
@@ -79,7 +79,6 @@
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz",
|
||||
"integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.5",
|
||||
"jsesc": "^2.5.1",
|
||||
@@ -89,8 +88,7 @@
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -98,7 +96,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz",
|
||||
"integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
@@ -205,7 +202,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
|
||||
"integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-get-function-arity": "^7.10.4",
|
||||
"@babel/template": "^7.10.4",
|
||||
@@ -216,7 +212,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
|
||||
"integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
@@ -243,7 +238,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz",
|
||||
"integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
@@ -326,7 +320,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz",
|
||||
"integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.10.4"
|
||||
}
|
||||
@@ -1141,7 +1134,6 @@
|
||||
"version": "7.10.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
|
||||
"integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/parser": "^7.10.4",
|
||||
@@ -1152,7 +1144,6 @@
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz",
|
||||
"integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/code-frame": "^7.10.4",
|
||||
"@babel/generator": "^7.10.5",
|
||||
@@ -1169,7 +1160,6 @@
|
||||
"version": "7.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz",
|
||||
"integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-validator-identifier": "^7.10.4",
|
||||
"lodash": "^4.17.19",
|
||||
@@ -1348,8 +1338,7 @@
|
||||
"@emotion/unitless": {
|
||||
"version": "0.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==",
|
||||
"dev": true
|
||||
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||
},
|
||||
"@emotion/utils": {
|
||||
"version": "0.11.3",
|
||||
@@ -4040,11 +4029,21 @@
|
||||
"recast": "^0.14.7"
|
||||
}
|
||||
},
|
||||
"babel-plugin-styled-components": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.11.1.tgz",
|
||||
"integrity": "sha512-YwrInHyKUk1PU3avIRdiLyCpM++18Rs1NgyMXEAQC33rIXs/vro0A+stf4sT0Gf22Got+xRWB8Cm0tw+qkRzBA==",
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.0.0",
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"lodash": "^4.17.11"
|
||||
}
|
||||
},
|
||||
"babel-plugin-syntax-jsx": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=",
|
||||
"dev": true
|
||||
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
|
||||
},
|
||||
"babel-plugin-transform-inline-consecutive-adds": {
|
||||
"version": "0.4.3",
|
||||
@@ -5092,6 +5091,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"camelize": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz",
|
||||
"integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs="
|
||||
},
|
||||
"can-use-dom": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/can-use-dom/-/can-use-dom-0.1.0.tgz",
|
||||
@@ -6140,6 +6144,11 @@
|
||||
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
|
||||
"dev": true
|
||||
},
|
||||
"css-color-keywords": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||
"integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU="
|
||||
},
|
||||
"css-loader": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/css-loader/-/css-loader-2.1.1.tgz",
|
||||
@@ -6206,6 +6215,23 @@
|
||||
"integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==",
|
||||
"dev": true
|
||||
},
|
||||
"css-to-react-native": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.2.tgz",
|
||||
"integrity": "sha512-VOFaeZA053BqvvvqIA8c9n0+9vFppVBAHCp6JgFTtTMU3Mzi+XnelJ9XC9ul3BqFzZyQ5N+H0SnwsWT2Ebchxw==",
|
||||
"requires": {
|
||||
"camelize": "^1.0.0",
|
||||
"css-color-keywords": "^1.0.0",
|
||||
"postcss-value-parser": "^3.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"postcss-value-parser": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz",
|
||||
"integrity": "sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"css-tree": {
|
||||
"version": "1.0.0-alpha.37",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz",
|
||||
@@ -9674,8 +9700,7 @@
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||
},
|
||||
"globalthis": {
|
||||
"version": "1.0.1",
|
||||
@@ -11307,6 +11332,11 @@
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
},
|
||||
"is-what": {
|
||||
"version": "3.10.0",
|
||||
"resolved": "https://registry.npmjs.org/is-what/-/is-what-3.10.0.tgz",
|
||||
"integrity": "sha512-U4RYCXNOmATQHlOPlOCHCfXyKEFIPqvyaKDqYRuLbD6EYKcTTfc3YXkAYjzOVxO3zt34L+Wh2feIyWrYiZ7kng=="
|
||||
},
|
||||
"is-window": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-window/-/is-window-1.0.2.tgz",
|
||||
@@ -11604,8 +11634,7 @@
|
||||
"jsesc": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
|
||||
"dev": true
|
||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
|
||||
},
|
||||
"jshint": {
|
||||
"version": "2.11.1",
|
||||
@@ -12511,6 +12540,11 @@
|
||||
"integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
|
||||
"dev": true
|
||||
},
|
||||
"memoize-one": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.1.1.tgz",
|
||||
"integrity": "sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA=="
|
||||
},
|
||||
"memoizee": {
|
||||
"version": "0.4.14",
|
||||
"resolved": "https://registry.npmjs.org/memoizee/-/memoizee-0.4.14.tgz",
|
||||
@@ -12579,6 +12613,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"merge-anything": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-2.4.4.tgz",
|
||||
"integrity": "sha512-l5XlriUDJKQT12bH+rVhAHjwIuXWdAIecGwsYjv2LJo+dA1AeRTmeQS+3QBpO6lEthBMDi2IUMpLC1yyRvGlwQ==",
|
||||
"requires": {
|
||||
"is-what": "^3.3.1"
|
||||
}
|
||||
},
|
||||
"merge-deep": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.2.tgz",
|
||||
@@ -17607,6 +17649,26 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"styled-components": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.4.1.tgz",
|
||||
"integrity": "sha512-RNqj14kYzw++6Sr38n7197xG33ipEOktGElty4I70IKzQF1jzaD1U4xQ+Ny/i03UUhHlC5NWEO+d8olRCDji6g==",
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@babel/traverse": "^7.0.0",
|
||||
"@emotion/is-prop-valid": "^0.8.1",
|
||||
"@emotion/unitless": "^0.7.0",
|
||||
"babel-plugin-styled-components": ">= 1",
|
||||
"css-to-react-native": "^2.2.2",
|
||||
"memoize-one": "^5.0.0",
|
||||
"merge-anything": "^2.2.4",
|
||||
"prop-types": "^15.5.4",
|
||||
"react-is": "^16.6.0",
|
||||
"stylis": "^3.5.0",
|
||||
"stylis-rule-sheet": "^0.0.10",
|
||||
"supports-color": "^5.5.0"
|
||||
}
|
||||
},
|
||||
"stylefire": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stylefire/-/stylefire-7.0.3.tgz",
|
||||
@@ -17619,6 +17681,16 @@
|
||||
"tslib": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"stylis": {
|
||||
"version": "3.5.4",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz",
|
||||
"integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q=="
|
||||
},
|
||||
"stylis-rule-sheet": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz",
|
||||
"integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw=="
|
||||
},
|
||||
"sumchecker": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
|
||||
@@ -18104,8 +18176,7 @@
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=",
|
||||
"dev": true
|
||||
"integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4="
|
||||
},
|
||||
"to-object-path": {
|
||||
"version": "0.3.0",
|
||||
@@ -20902,6 +20973,11 @@
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.0.tgz",
|
||||
"integrity": "sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg=="
|
||||
},
|
||||
"yaml-source-map": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yaml-source-map/-/yaml-source-map-2.1.1.tgz",
|
||||
"integrity": "sha512-eILAwrW4K1WA83mJXF0PLyeBhL4t6v9xyesRYhncG1zh9UPc43mMRgFTCi2FRYpOgUvGECNlNjoiksiX2xBKVg=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "14.2.3",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz",
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
"redux-thunk": "^2.3.0",
|
||||
"reselect": "^4.0.0",
|
||||
"srp-js": "^0.2.1",
|
||||
"styled-components": "^4.4.1",
|
||||
"swagger-ui-react": "^3.24.3",
|
||||
"tough-cookie": "^2.3.1",
|
||||
"url-join": "^4.0.1",
|
||||
@@ -171,6 +172,7 @@
|
||||
"vkbeautify": "^0.99.1",
|
||||
"whatwg-fetch": "^2.0.1",
|
||||
"yaml": "^1.5.0",
|
||||
"yaml-source-map": "^2.1.1",
|
||||
"zprint-clj": "0.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -21,6 +21,21 @@ import { useToggle } from 'react-use';
|
||||
type Props = {|
|
||||
className?: string,
|
||||
jsonData: Object,
|
||||
onClick: (section: string, path: any) => void,
|
||||
jsonData: {
|
||||
servers?: Object,
|
||||
info?: Object,
|
||||
paths?: Object,
|
||||
components?: {
|
||||
requestBodies?: Object,
|
||||
responses?: Object,
|
||||
parameters?: Object,
|
||||
headers?: Object,
|
||||
schemas?: Object,
|
||||
securitySchemes?: Object,
|
||||
},
|
||||
},
|
||||
pathItems: Array<Object>,
|
||||
|};
|
||||
|
||||
const StyledSidebar: React.ComponentType<{}> = styled.div`
|
||||
@@ -67,16 +82,13 @@ function Sidebar(props: Props) {
|
||||
if (props.jsonData === null) {
|
||||
return null;
|
||||
}
|
||||
const { servers, info } = props.jsonData;
|
||||
const {
|
||||
requestBodies,
|
||||
responses,
|
||||
parameters,
|
||||
headers,
|
||||
schemas,
|
||||
securitySchemes,
|
||||
} = props.jsonData.components;
|
||||
const paths = Object.entries(props.jsonData.paths);
|
||||
const { servers, info, paths } = props.jsonData || {};
|
||||
const { requestBodies, responses, parameters, headers, schemas, securitySchemes } =
|
||||
props.jsonData.components || {};
|
||||
let pathItems = {};
|
||||
if (typeof paths !== 'string') {
|
||||
pathItems = Object.entries(paths || {});
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledSidebar className="theme--sidebar">
|
||||
@@ -150,14 +162,24 @@ function Sidebar(props: Props) {
|
||||
<SidebarInfo childrenVisible={infoSec} info={info} />
|
||||
</StyledSection>
|
||||
)}
|
||||
{serversVisible && servers && <SidebarServers servers={servers} />}
|
||||
{pathsVisible && paths && <SidebarPaths paths={paths} />}
|
||||
{requestsVisible && requestBodies && <SidebarRequests requests={requestBodies} />}
|
||||
{responsesVisible && responses && <SidebarResponses responses={responses} />}
|
||||
{parametersVisible && parameters && <SidebarParameters parameters={parameters} />}
|
||||
{headersVisible && headers && <SidebarHeaders headers={headers} />}
|
||||
{schemasVisible && schemas && <SidebarSchemas schemas={schemas} />}
|
||||
{securityVisible && schemas && <SidebarSecurity security={securitySchemes} />}
|
||||
{serversVisible && servers && <SidebarServers servers={servers} onClick={props.onClick} />}
|
||||
{pathsVisible && pathItems && pathItems.length && (
|
||||
<SidebarPaths paths={pathItems} onClick={props.onClick} />
|
||||
)}
|
||||
{requestsVisible && requestBodies && (
|
||||
<SidebarRequests requests={requestBodies} onClick={props.onClick} />
|
||||
)}
|
||||
{responsesVisible && responses && (
|
||||
<SidebarResponses responses={responses} onClick={props.onClick} />
|
||||
)}
|
||||
{parametersVisible && parameters && (
|
||||
<SidebarParameters parameters={parameters} onClick={props.onClick} />
|
||||
)}
|
||||
{headersVisible && headers && <SidebarHeaders headers={headers} onClick={props.onClick} />}
|
||||
{schemasVisible && schemas && <SidebarSchemas schemas={schemas} onClick={props.onClick} />}
|
||||
{securityVisible && securitySchemes && (
|
||||
<SidebarSecurity security={securitySchemes} onClick={props.onClick} />
|
||||
)}
|
||||
</StyledSidebar>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -4,22 +4,24 @@ import Tooltip from '../tooltip';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
headers: Object,
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('header');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
};
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarHeaders extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.headers).filter(header =>
|
||||
const { headers, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(headers) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'header'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(headers).filter(header =>
|
||||
header.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -35,8 +37,8 @@ export default class SidebarHeaders extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([header])}>
|
||||
<Tooltip message={this.props.headers[header].description} position="right">
|
||||
<span onClick={() => onClick('components', 'headers', header)}>
|
||||
<Tooltip message={headers[header].description} position="right">
|
||||
{header}
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
@@ -28,7 +28,7 @@ function SidebarInfo(props: Props) {
|
||||
<SidebarTextItem label={'Version:'} headline={version} />
|
||||
</SidebarItem>
|
||||
)}
|
||||
{license.name && (
|
||||
{license && license.name && (
|
||||
<SidebarItem>
|
||||
<SidebarTextItem label={'License:'} headline={license.name} />
|
||||
</SidebarItem>
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
type Props = {
|
||||
name: string,
|
||||
};
|
||||
|
||||
const StyledInvalidSection: React.ComponentType<{}> = styled.div`
|
||||
padding: var(--padding-xs) var(--padding-xs) var(--padding-md) var(--padding-md);
|
||||
color: var(--color-warning);
|
||||
`;
|
||||
|
||||
const SidebarInvalidSection = ({ name }: Props) => (
|
||||
<StyledInvalidSection>Error: Invalid {name} specification.</StyledInvalidSection>
|
||||
);
|
||||
|
||||
export default SidebarInvalidSection;
|
||||
@@ -4,23 +4,24 @@ import Tooltip from '../tooltip';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
parameters: Object,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('parameter');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarParameters extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.parameters).filter(parameter =>
|
||||
const { parameters, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(parameters) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'parameter'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(parameters).filter(parameter =>
|
||||
parameter.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -36,8 +37,8 @@ export default class SidebarParameters extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([parameter])}>
|
||||
<Tooltip message={this.props.parameters[parameter].description} position="right">
|
||||
<span onClick={() => onClick('components', 'parameters', parameter)}>
|
||||
<Tooltip message={parameters[parameter].description} position="right">
|
||||
{parameter}
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
@@ -4,16 +4,11 @@ import styled from 'styled-components';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
paths: Array<any>,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('path');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
paths: Object,
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
const StyledMethods: React.ComponentType<{}> = styled.span`
|
||||
@@ -24,7 +19,11 @@ const StyledMethods: React.ComponentType<{}> = styled.span`
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarPaths extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = this.props.paths.filter(pathDetail =>
|
||||
const { paths, onClick } = this.props;
|
||||
if (Object.prototype.toString.call(paths) !== '[object Array]') {
|
||||
return <StyledInvalidSection name={'path'} />;
|
||||
}
|
||||
const filteredValues = paths.filter(pathDetail =>
|
||||
pathDetail[0].toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -40,7 +39,7 @@ export default class SidebarPaths extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([route])}>{route}</span>
|
||||
<span onClick={() => onClick('paths', route)}>{route}</span>
|
||||
</SidebarItem>
|
||||
<SidebarItem>
|
||||
<StyledMethods>
|
||||
@@ -48,7 +47,7 @@ export default class SidebarPaths extends React.Component<Props> {
|
||||
<span
|
||||
key={method}
|
||||
className={`method-${method}`}
|
||||
onClick={() => handleClick([route, method])}>
|
||||
onClick={() => onClick('paths', route, method)}>
|
||||
{method}
|
||||
</span>
|
||||
))}
|
||||
|
||||
@@ -5,16 +5,11 @@ import Tooltip from '../tooltip';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
requests: Object,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('request');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
const StyledRequestExample: React.ComponentType<{}> = styled.span`
|
||||
@@ -32,7 +27,13 @@ const StyledRequestFormat: React.ComponentType<{}> = styled.span`
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarRequests extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.requests).filter(requestName =>
|
||||
const { requests, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(requests) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'request'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(requests).filter(requestName =>
|
||||
requestName.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -43,14 +44,14 @@ export default class SidebarRequests extends React.Component<Props> {
|
||||
return (
|
||||
<div>
|
||||
{filteredValues.map(requestName => {
|
||||
const { description, content } = this.props.requests[requestName];
|
||||
const { description, content } = requests[requestName];
|
||||
return (
|
||||
<React.Fragment key={requestName}>
|
||||
<SidebarItem gridLayout>
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.folderOpen} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([requestName])}>
|
||||
<span onClick={() => onClick('components', 'requestBodies', requestName)}>
|
||||
<Tooltip message={description} position="right">
|
||||
{requestName}
|
||||
</Tooltip>
|
||||
@@ -61,18 +62,41 @@ export default class SidebarRequests extends React.Component<Props> {
|
||||
<SidebarItem>
|
||||
<StyledRequestFormat>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
<span onClick={() => handleClick([requestFormat])}>{requestFormat}</span>
|
||||
<span
|
||||
onClick={() =>
|
||||
onClick(
|
||||
'components',
|
||||
'requestBodies',
|
||||
requestName,
|
||||
'content',
|
||||
requestFormat,
|
||||
)
|
||||
}>
|
||||
{requestFormat}
|
||||
</span>
|
||||
</StyledRequestFormat>
|
||||
</SidebarItem>
|
||||
<SidebarItem>
|
||||
{Object.keys(content[requestFormat].examples).map(requestExample => (
|
||||
<StyledRequestExample
|
||||
onClick={() => handleClick([requestExample])}
|
||||
key={requestExample}>
|
||||
{requestExample}
|
||||
</StyledRequestExample>
|
||||
))}
|
||||
</SidebarItem>
|
||||
{content[requestFormat].examples && (
|
||||
<SidebarItem>
|
||||
{Object.keys(content[requestFormat].examples).map(requestExample => (
|
||||
<StyledRequestExample
|
||||
onClick={() =>
|
||||
onClick(
|
||||
'components',
|
||||
'requestBodies',
|
||||
requestName,
|
||||
'content',
|
||||
requestFormat,
|
||||
'examples',
|
||||
requestExample,
|
||||
)
|
||||
}
|
||||
key={requestExample}>
|
||||
{requestExample}
|
||||
</StyledRequestExample>
|
||||
))}
|
||||
</SidebarItem>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</React.Fragment>
|
||||
|
||||
@@ -4,23 +4,24 @@ import Tooltip from '../tooltip';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
responses: Object,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('response');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarResponses extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.responses).filter(response =>
|
||||
const { responses, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(responses) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'response'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(responses).filter(response =>
|
||||
response.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -35,8 +36,8 @@ export default class SidebarResponses extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([response])}>
|
||||
<Tooltip message={this.props.responses[response].description} position="right">
|
||||
<span onClick={() => onClick('components', 'responses', response)}>
|
||||
<Tooltip message={responses[response].description} position="right">
|
||||
{response}
|
||||
</Tooltip>
|
||||
</span>
|
||||
|
||||
@@ -3,23 +3,24 @@ import * as React from 'react';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
schemas: Object,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('schema');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarSchemas extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.schemas).filter(schema =>
|
||||
const { schemas, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(schemas) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'schema'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(schemas).filter(schema =>
|
||||
schema.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -34,7 +35,7 @@ export default class SidebarSchemas extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.brackets} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([schema])}>{schema}</span>
|
||||
<span onClick={() => onClick('components', 'schemas', schema)}>{schema}</span>
|
||||
</SidebarItem>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@@ -3,22 +3,24 @@ import * as React from 'react';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
security: Object,
|
||||
onClick: (section: string, ...args: any) => void,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('security');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
};
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarSecurity extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = Object.keys(this.props.security).filter(scheme =>
|
||||
const { security, onClick } = this.props;
|
||||
|
||||
if (Object.prototype.toString.call(security) !== '[object Object]') {
|
||||
return <StyledInvalidSection name={'security'} />;
|
||||
}
|
||||
|
||||
const filteredValues = Object.keys(security).filter(scheme =>
|
||||
scheme.toLowerCase().includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -34,7 +36,7 @@ export default class SidebarSecurity extends React.Component<Props> {
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.key} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([scheme])}>{scheme}</span>
|
||||
<span onClick={() => onClick('components', 'securitySchemes', scheme)}>{scheme}</span>
|
||||
</SidebarItem>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
@@ -3,22 +3,22 @@ import * as React from 'react';
|
||||
import SidebarItem from './sidebar-item';
|
||||
import SvgIcon, { IconEnum } from '../svg-icon';
|
||||
import SidebarSection from './sidebar-section';
|
||||
import StyledInvalidSection from './sidebar-invalid-section';
|
||||
|
||||
type Props = {
|
||||
servers: Array<any>,
|
||||
onClick: (section: string, path: string | number) => void,
|
||||
};
|
||||
|
||||
let itemPath = [];
|
||||
const handleClick = items => {
|
||||
itemPath.push('server');
|
||||
itemPath.push.apply(itemPath, items);
|
||||
itemPath = [];
|
||||
};
|
||||
// Implemented as a class component because of a caveat with render props
|
||||
// https://reactjs.org/docs/render-props.html#be-careful-when-using-render-props-with-reactpurecomponent
|
||||
export default class SidebarServers extends React.Component<Props> {
|
||||
renderBody = (filter: string): null | React.Node => {
|
||||
const filteredValues = this.props.servers.filter(server =>
|
||||
const { servers, onClick } = this.props;
|
||||
if (!Array.isArray(servers)) {
|
||||
return <StyledInvalidSection name={'server'} />;
|
||||
}
|
||||
const filteredValues = servers.filter(server =>
|
||||
server.url.includes(filter.toLocaleLowerCase()),
|
||||
);
|
||||
|
||||
@@ -28,13 +28,13 @@ export default class SidebarServers extends React.Component<Props> {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{filteredValues.map(server => (
|
||||
{filteredValues.map((server, index) => (
|
||||
<React.Fragment key={server.url}>
|
||||
<SidebarItem>
|
||||
<div>
|
||||
<SvgIcon icon={IconEnum.indentation} />
|
||||
</div>
|
||||
<span onClick={() => handleClick([server.url])}>{server.url}</span>
|
||||
<span onClick={() => onClick('servers', index)}>{server.url}</span>
|
||||
</SidebarItem>
|
||||
</React.Fragment>
|
||||
))}
|
||||
|
||||
@@ -7,6 +7,7 @@ export default {
|
||||
title: 'Navigation | Sidebar',
|
||||
decorators: [withKnobs, withDesign],
|
||||
};
|
||||
|
||||
const apiSpec = {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
@@ -2944,11 +2945,209 @@ const apiSpec = {
|
||||
security: [],
|
||||
};
|
||||
|
||||
export const _default = () => (
|
||||
<div style={{ width: '350px' }}>
|
||||
<Sidebar jsonData={apiSpec} />
|
||||
</div>
|
||||
);
|
||||
const apiSpec2 = {
|
||||
openapi: '3.0.0',
|
||||
info: {
|
||||
title: 'Simple API overview',
|
||||
version: '2.0.0',
|
||||
},
|
||||
paths: {
|
||||
'/': {
|
||||
get: {
|
||||
operationId: 'listVersionsv2',
|
||||
summary: 'List API versions',
|
||||
responses: {
|
||||
'200': {
|
||||
description: '200 response',
|
||||
content: {
|
||||
'application/json': {
|
||||
examples: {
|
||||
foo: {
|
||||
value: {
|
||||
versions: [
|
||||
{
|
||||
status: 'CURRENT',
|
||||
updated: '2011-01-21T11:33:21Z',
|
||||
id: 'v2.0',
|
||||
links: [
|
||||
{
|
||||
href: 'http://127.0.0.1:8774/v2/',
|
||||
rel: 'self',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
status: 'EXPERIMENTAL',
|
||||
updated: '2013-07-23T11:33:21Z',
|
||||
id: 'v3.0',
|
||||
links: [
|
||||
{
|
||||
href: 'http://127.0.0.1:8774/v3/',
|
||||
rel: 'self',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'300': {
|
||||
description: '300 response',
|
||||
content: {
|
||||
'application/json': {
|
||||
examples: {
|
||||
foo: {
|
||||
value:
|
||||
'{\n "versions": [\n {\n "status": "CURRENT",\n "updated": "2011-01-21T11:33:21Z",\n "id": "v2.0",\n "links": [\n {\n "href": "http://127.0.0.1:8774/v2/",\n "rel": "self"\n }\n ]\n },\n {\n "status": "EXPERIMENTAL",\n "updated": "2013-07-23T11:33:21Z",\n "id": "v3.0",\n "links": [\n {\n "href": "http://127.0.0.1:8774/v3/",\n "rel": "self"\n }\n ]\n }\n ]\n}\n',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/v2': {
|
||||
get: {
|
||||
operationId: 'getVersionDetailsv2',
|
||||
summary: 'Show API version details',
|
||||
responses: {
|
||||
'200': {
|
||||
description: '200 response',
|
||||
content: {
|
||||
'application/json': {
|
||||
examples: {
|
||||
foo: {
|
||||
value: {
|
||||
version: {
|
||||
status: 'CURRENT',
|
||||
updated: '2011-01-21T11:33:21Z',
|
||||
'media-types': [
|
||||
{
|
||||
base: 'application/xml',
|
||||
type: 'application/vnd.openstack.compute+xml;version=2',
|
||||
},
|
||||
{
|
||||
base: 'application/json',
|
||||
type: 'application/vnd.openstack.compute+json;version=2',
|
||||
},
|
||||
],
|
||||
id: 'v2.0',
|
||||
links: [
|
||||
{
|
||||
href: 'http://127.0.0.1:8774/v2/',
|
||||
rel: 'self',
|
||||
},
|
||||
{
|
||||
href:
|
||||
'http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf',
|
||||
type: 'application/pdf',
|
||||
rel: 'describedby',
|
||||
},
|
||||
{
|
||||
href:
|
||||
'http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl',
|
||||
type: 'application/vnd.sun.wadl+xml',
|
||||
rel: 'describedby',
|
||||
},
|
||||
{
|
||||
href:
|
||||
'http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl',
|
||||
type: 'application/vnd.sun.wadl+xml',
|
||||
rel: 'describedby',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'203': {
|
||||
description: '203 response',
|
||||
content: {
|
||||
'application/json': {
|
||||
examples: {
|
||||
foo: {
|
||||
value: {
|
||||
version: {
|
||||
status: 'CURRENT',
|
||||
updated: '2011-01-21T11:33:21Z',
|
||||
'media-types': [
|
||||
{
|
||||
base: 'application/xml',
|
||||
type: 'application/vnd.openstack.compute+xml;version=2',
|
||||
},
|
||||
{
|
||||
base: 'application/json',
|
||||
type: 'application/vnd.openstack.compute+json;version=2',
|
||||
},
|
||||
],
|
||||
id: 'v2.0',
|
||||
links: [
|
||||
{
|
||||
href: 'http://23.253.228.211:8774/v2/',
|
||||
rel: 'self',
|
||||
},
|
||||
{
|
||||
href:
|
||||
'http://docs.openstack.org/api/openstack-compute/2/os-compute-devguide-2.pdf',
|
||||
type: 'application/pdf',
|
||||
rel: 'describedby',
|
||||
},
|
||||
{
|
||||
href:
|
||||
'http://docs.openstack.org/api/openstack-compute/2/wadl/os-compute-2.wadl',
|
||||
type: 'application/vnd.sun.wadl+xml',
|
||||
rel: 'describedby',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const _default = () => {
|
||||
const [targetPath, setTargetPath] = React.useState('Awaiting click...');
|
||||
|
||||
const _handleItemClick = (...items) => {
|
||||
setTargetPath(items.join(' > '));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '350px' }}>
|
||||
<div style={{ display: 'block', margin: '0px 0px 20px 0px' }}>Target: {targetPath}</div>
|
||||
<Sidebar jsonData={apiSpec} onClick={_handleItemClick} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const SimpleAPI = () => {
|
||||
const [targetPath, setTargetPath] = React.useState('Awaiting click...');
|
||||
|
||||
const _handleItemClick = (...items) => {
|
||||
setTargetPath(items.join(' > '));
|
||||
};
|
||||
|
||||
return (
|
||||
<div style={{ width: '350px' }}>
|
||||
<div style={{ display: 'block', margin: '0px 0px 20px 0px' }}>Target: {targetPath}</div>
|
||||
<Sidebar jsonData={apiSpec2} onClick={_handleItemClick} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
_default.story = {
|
||||
parameters: {
|
||||
|
||||
Reference in new issue
Block a user