mirror of
https://github.com/Redocly/redoc.git
synced 2024-11-21 16:16:33 +03:00
Initial React rewrite
This commit is contained in:
parent
af81c33c99
commit
4c3b4aa589
|
@ -1,30 +1,10 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=lf
|
||||
insert_final_newline=false
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[{.eslintrc,.babelrc,.stylelintrc,jest.config,*.json,*.jsb3,*.jsb2,*.bowerrc}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[*.scss]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[*.styl]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[*.coffee]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[{.analysis_options,*.yml,*.yaml}]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
|
||||
[tslint.json]
|
||||
indent_style=space
|
||||
indent_size=2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
|
|
14
.gitignore
vendored
14
.gitignore
vendored
|
@ -20,20 +20,8 @@ npm-debug.log*
|
|||
# https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
|
||||
node_modules
|
||||
|
||||
# compiled css
|
||||
lib/**/*.css
|
||||
lib/
|
||||
|
||||
# files produced by ngc
|
||||
lib/**/*.ngfactory.ts
|
||||
lib/**/*.css.shim.ts
|
||||
**/*.ngsummary.json
|
||||
lib/**/*.shim.ngstyle.ts
|
||||
|
||||
# other
|
||||
/dist
|
||||
/demo/build
|
||||
.tmp
|
||||
compiled
|
||||
/coverage
|
||||
.ghpages-tmp
|
||||
stats.json
|
||||
|
|
12
.npmignore
12
.npmignore
|
@ -1,12 +1,8 @@
|
|||
.DS_Store
|
||||
**/.*
|
||||
compiled
|
||||
|
||||
node_modules
|
||||
jspm_packages
|
||||
node_modules/
|
||||
|
||||
tests
|
||||
lib
|
||||
demo
|
||||
build
|
||||
coverage
|
||||
perf/
|
||||
demo/
|
||||
coverage/
|
||||
|
|
3
.vscode/settings.json
vendored
Normal file
3
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"editor.formatOnSave": false
|
||||
}
|
|
@ -1,12 +1,3 @@
|
|||
<a name="1.19.1"></a>
|
||||
# [1.19.1](https://github.com/Rebilly/ReDoc/compare/v1.19.0...v1.19.1) (2017-10-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* snapshot crashing on `constructor` prop ([04e8606](https://github.com/Rebilly/ReDoc/commit/04e8606)), closes [#341](https://github.com/Rebilly/ReDoc/issues/341)
|
||||
|
||||
|
||||
<a name="1.19.0"></a>
|
||||
# [1.19.0](https://github.com/Rebilly/ReDoc/compare/v1.18.1...v1.19.0) (2017-09-21)
|
||||
|
||||
|
|
|
@ -10,6 +10,15 @@
|
|||
|
||||
[![Browser Compatibility](https://saucelabs.com/browser-matrix/redoc.svg)](https://saucelabs.com/u/redoc)
|
||||
|
||||
**REACT-REWRITE: WORK IN PROGRESS**
|
||||
|
||||
To try locally run:
|
||||
|
||||
```shell
|
||||
yarn install
|
||||
npm start
|
||||
```
|
||||
|
||||
![ReDoc demo](demo/redoc-demo.png)
|
||||
|
||||
## [Live demo](http://rebilly.github.io/ReDoc/)
|
||||
|
|
38
bower.json
38
bower.json
|
@ -1,38 +0,0 @@
|
|||
{
|
||||
"name": "redoc",
|
||||
"description": "Swagger-generated API Reference Documentation",
|
||||
"main": "dist/redoc.min.js",
|
||||
"authors": [
|
||||
"Roman Hotsiy"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Rebilly/ReDoc"
|
||||
},
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"OpenAPI",
|
||||
"OpenAPI Specification",
|
||||
"Swagger",
|
||||
"JSON-Schema",
|
||||
"API",
|
||||
"REST",
|
||||
"documentation",
|
||||
"Angular 2"
|
||||
],
|
||||
"homepage": "https://github.com/Rebilly/ReDoc",
|
||||
"moduleType": ["globals", "amd"],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"tests",
|
||||
"compiled",
|
||||
"lib",
|
||||
"demo",
|
||||
"build",
|
||||
"docs",
|
||||
"gulpfile.js",
|
||||
"*.conf.js",
|
||||
"*.config.js"
|
||||
]
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
const path = require('path');
|
||||
function root(args) {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
return path.join.apply(path, [__dirname, '..'].concat(args));
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
root: root
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
require('shelljs/global');
|
||||
|
||||
set('-e');
|
||||
set('-v');
|
||||
|
||||
|
||||
cat([
|
||||
'lib/components/Redoc/redoc-initial-styles.css',
|
||||
'node_modules/perfect-scrollbar/dist/css/perfect-scrollbar.css',
|
||||
'node_modules/dropkickjs/build/css/dropkick.css',
|
||||
'node_modules/prismjs/themes/prism-dark.css',
|
||||
'node_modules/hint.css/hint.base.css'
|
||||
]).to('dist/redoc.css')
|
|
@ -1,12 +0,0 @@
|
|||
var path = require('path');
|
||||
|
||||
var paths = {
|
||||
outputName: 'redoc.min.js',
|
||||
output: 'dist/',
|
||||
demo: 'demo/**/*',
|
||||
releases: 'demo/releases/'
|
||||
}
|
||||
|
||||
paths.redocBuilt = path.join(paths.output, paths.outputName);
|
||||
|
||||
module.exports = paths;
|
|
@ -1,28 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
require('shelljs/global');
|
||||
|
||||
var paths = require('./paths');
|
||||
var path = require('path');
|
||||
|
||||
set('-e');
|
||||
set('-v');
|
||||
|
||||
// build
|
||||
exec('npm run build-dist');
|
||||
cd('demo');
|
||||
mv('index-gh.html', 'index.html');
|
||||
mkdir('-p', 'dist');
|
||||
cp('-R', '../dist/*', './dist/');
|
||||
cd('..');
|
||||
|
||||
var version = require(path.join(__dirname, '../package.json')).version;
|
||||
var versionDir = path.join(paths.releases, 'v' + version + '/');
|
||||
var latestDir = path.join(paths.releases, 'latest/');
|
||||
var v1Dir = path.join(paths.releases, 'v' + version.split('.')[0] + '.x.x/');
|
||||
mkdir('-p', versionDir)
|
||||
mkdir('-p', latestDir);
|
||||
mkdir('-p', v1Dir);
|
||||
cp(paths.redocBuilt, versionDir);
|
||||
cp(paths.redocBuilt, latestDir);
|
||||
cp(paths.redocBuilt, v1Dir);
|
|
@ -1,33 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
'use strict';
|
||||
|
||||
require('shelljs/global');
|
||||
set('-e');
|
||||
|
||||
function isPR() {
|
||||
return process.env.TRAVIS_PULL_REQUEST && process.env.TRAVIS_PULL_REQUEST !== 'false';
|
||||
}
|
||||
|
||||
function isCI() {
|
||||
return !!process.env.CI;
|
||||
}
|
||||
|
||||
if (process.env.JOB === 'e2e-guru') {
|
||||
if (isPR()) {
|
||||
console.log('Skiping E2E tests on PR');
|
||||
return;
|
||||
}
|
||||
exec('npm run e2e');
|
||||
} else {
|
||||
exec('npm run unit');
|
||||
if (isPR()) {
|
||||
console.log('Skiping E2E tests on PR');
|
||||
return;
|
||||
}
|
||||
if (!isCI()) {
|
||||
console.log('Skiping E2E tests locally. Use `npm run e2e` to run');
|
||||
return;
|
||||
}
|
||||
console.log('Starting Basic E2E');
|
||||
exec('npm run e2e');
|
||||
}
|
|
@ -1,136 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
|
||||
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
|
||||
const StringReplacePlugin = require("string-replace-webpack-plugin");
|
||||
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
|
||||
|
||||
const VERSION = JSON.stringify(require('../package.json').version);
|
||||
|
||||
const root = require('./helpers').root;
|
||||
|
||||
module.exports = function (options) {
|
||||
const conf = {
|
||||
performance: { hints: false },
|
||||
|
||||
output: {
|
||||
path: root('dist'),
|
||||
filename: '[name].js',
|
||||
sourceMapFilename: '[name].[id].map',
|
||||
chunkFilename: '[id].chunk.js'
|
||||
},
|
||||
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js', '.json', '.css'],
|
||||
alias: {
|
||||
http: 'stream-http',
|
||||
https: 'https-browserify'
|
||||
}
|
||||
},
|
||||
|
||||
externals: {
|
||||
'jquery': 'jquery',
|
||||
'esprima': 'esprima' // optional dep of ys-yaml not needed for redoc
|
||||
},
|
||||
|
||||
module: {
|
||||
exprContextCritical: false,
|
||||
rules: [
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.ts$/,
|
||||
exclude: [
|
||||
/node_modules/
|
||||
],
|
||||
loader: StringReplacePlugin.replace({
|
||||
replacements: [
|
||||
{
|
||||
pattern: /styleUrls:\s*\[\s*'([\w\.\/-]*)\.css'\s*\][\s,]*$/gm,
|
||||
replacement: function (match, p1, offset, string) {
|
||||
return `styleUrls: ['${p1}.scss'],`;
|
||||
}
|
||||
},
|
||||
{
|
||||
pattern: /(\.\/components\/Redoc\/redoc-initial-styles\.css)/gm,
|
||||
replacement: function (match, p1, offset, string) {
|
||||
return p1.replace('.css', '.scss');
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
},
|
||||
{
|
||||
enforce: 'pre',
|
||||
test: /\.js$/,
|
||||
loader: 'source-map-loader',
|
||||
exclude: [
|
||||
/node_modules/
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
use: 'json-loader'
|
||||
},
|
||||
{
|
||||
test: /lib[\\\/].*\.css$/,
|
||||
loaders: ['raw-loader'],
|
||||
exclude: [/redoc-initial-styles\.css$/]
|
||||
}, {
|
||||
test: /\.css$/,
|
||||
loaders: ['style-loader', 'css-loader?-import'],
|
||||
exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.css$/]
|
||||
},
|
||||
{
|
||||
test: /lib[\\\/].*\.scss$/,
|
||||
loaders: ['raw-loader', 'sass-loader'],
|
||||
exclude: [/redoc-initial-styles\.scss$/]
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'style-loader',
|
||||
'css-loader?-import',
|
||||
'sass-loader'
|
||||
],
|
||||
exclude: [/lib[\\\/](?!.*redoc-initial-styles).*\.scss$/]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'raw-loader'
|
||||
}
|
||||
],
|
||||
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new CheckerPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'IS_PRODUCTION': options.IS_PRODUCTION,
|
||||
'LIB_VERSION': VERSION,
|
||||
'AOT': options.AOT
|
||||
}),
|
||||
|
||||
new StringReplacePlugin()
|
||||
],
|
||||
node: {
|
||||
global: true,
|
||||
crypto: 'empty',
|
||||
fs: 'empty',
|
||||
process: true,
|
||||
module: false,
|
||||
clearImmediate: false,
|
||||
setImmediate: false
|
||||
}
|
||||
};
|
||||
|
||||
// if (options.AOT) {
|
||||
// conf.plugins.push(
|
||||
// new ngcWebpack.NgcWebpackPlugin({
|
||||
// disable: !options.AOT,
|
||||
// tsConfig: root('tsconfig.webpack.json'),
|
||||
// resourceOverride: root('build/resource-override.js')
|
||||
// })
|
||||
// );
|
||||
// }
|
||||
|
||||
return conf;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
|
||||
const StringReplacePlugin = require("string-replace-webpack-plugin");
|
||||
|
||||
const root = require('./helpers').root;
|
||||
const VERSION = JSON.stringify(require('../package.json').version);
|
||||
const IS_PRODUCTION = process.env.NODE_ENV === "production";
|
||||
|
||||
const webpackMerge = require('webpack-merge'); // used to merge webpack configs
|
||||
const commonConfig = require('./webpack.common.js');
|
||||
|
||||
module.exports = webpackMerge(commonConfig({
|
||||
IS_PRODUCTION: IS_PRODUCTION,
|
||||
AOT: IS_PRODUCTION
|
||||
}), {
|
||||
devtool: '#inline-source-map',
|
||||
entry: {
|
||||
'polyfills': './lib/polyfills.ts',
|
||||
'redoc': './lib/index.ts',
|
||||
},
|
||||
devServer: {
|
||||
contentBase: root('demo'),
|
||||
watchContentBase: true,
|
||||
compress: true,
|
||||
watchOptions: {
|
||||
poll: true
|
||||
},
|
||||
port: 9000,
|
||||
hot: false,
|
||||
stats: 'errors-only'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: [
|
||||
'awesome-typescript-loader',
|
||||
'angular2-template-loader',
|
||||
],
|
||||
exclude: [/\.(spec|e2e)\.ts$/]
|
||||
},
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: ['vendor', 'polyfills'],
|
||||
minChunks: Infinity
|
||||
})
|
||||
]
|
||||
})
|
|
@ -1,83 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
|
||||
const VERSION = JSON.stringify(require('../package.json').version);
|
||||
|
||||
const root = require('./helpers').root;
|
||||
const BANNER =
|
||||
`ReDoc - OpenAPI/Swagger-generated API Reference Documentation
|
||||
-------------------------------------------------------------
|
||||
Version: ${VERSION}
|
||||
Repo: https://github.com/Rebilly/ReDoc`;
|
||||
|
||||
const IS_MODULE = process.env.IS_MODULE != null;
|
||||
|
||||
const webpackMerge = require('webpack-merge'); // used to merge webpack configs
|
||||
const commonConfig = require('./webpack.common.js');
|
||||
|
||||
const config = webpackMerge(commonConfig({
|
||||
IS_PRODUCTION: true,
|
||||
AOT: true
|
||||
}), {
|
||||
devtool: 'source-map',
|
||||
|
||||
entry: {
|
||||
'redoc': IS_MODULE ? ['./lib/redoc.module.ts'] : ['./lib/polyfills.ts', './lib/index.ts']
|
||||
},
|
||||
|
||||
output: {
|
||||
path: root('dist'),
|
||||
filename: IS_MODULE ? '[name]-module.js' : '[name].min.js',
|
||||
sourceMapFilename: IS_MODULE ? '[name]-module.map' : '[name].min.map',
|
||||
library: 'Redoc',
|
||||
libraryTarget: 'umd',
|
||||
umdNamedDefine: true
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: [
|
||||
'awesome-typescript-loader',
|
||||
'angular2-template-loader',
|
||||
],
|
||||
exclude: [/\.(spec|e2e)\.ts$/]
|
||||
}
|
||||
]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
minimize: true,
|
||||
debug: false
|
||||
}),
|
||||
new webpack.optimize.UglifyJsPlugin({
|
||||
compress: {
|
||||
warnings: false,
|
||||
screw_ie8: true
|
||||
},
|
||||
mangle: { screw_ie8 : true },
|
||||
output: {
|
||||
comments: false
|
||||
},
|
||||
sourceMap: true
|
||||
}),
|
||||
new webpack.optimize.ModuleConcatenationPlugin(),
|
||||
new webpack.BannerPlugin(BANNER)
|
||||
]
|
||||
})
|
||||
|
||||
if (IS_MODULE) {
|
||||
config.externals = {
|
||||
'jquery': 'jquery',
|
||||
'esprima': 'esprima', // optional dep of ys-yaml not needed for redoc
|
||||
'@angular/platform-browser-dynamic': '@angular/platform-browser-dynamic',
|
||||
'@angular/platform-browser': '@angular/platform-browser',
|
||||
'@angular/core': '@angular/core',
|
||||
'@angular/common': '@angular/common',
|
||||
'@angular/forms': '@angular/forms',
|
||||
'core-js': 'core-js',
|
||||
'rxjs': 'rxjs',
|
||||
'zone.js/dist/zone': 'zone.js/dist/zone'
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = config;
|
|
@ -1,61 +0,0 @@
|
|||
const webpack = require('webpack');
|
||||
|
||||
const root = require('./helpers').root;
|
||||
const path = require('path');
|
||||
|
||||
const webpackMerge = require('webpack-merge'); // used to merge webpack configs
|
||||
const commonConfig = require('./webpack.common.js');
|
||||
|
||||
module.exports = webpackMerge(commonConfig({
|
||||
IS_PRODUCTION: true,
|
||||
AOT: false
|
||||
}), {
|
||||
devtool: 'inline-source-map',
|
||||
|
||||
module: {
|
||||
exprContextCritical: false,
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: 'awesome-typescript-loader'
|
||||
},
|
||||
{
|
||||
test: /\.ts$/,
|
||||
use: [
|
||||
'angular2-template-loader',
|
||||
],
|
||||
exclude: [/\.(spec|e2e)\.ts$/]
|
||||
},
|
||||
{
|
||||
enforce: 'post',
|
||||
test: /\.(js|ts)$/, loader: 'istanbul-instrumenter-loader',
|
||||
include: root('lib'),
|
||||
exclude: [
|
||||
/\.(e2e|spec)\.ts$/,
|
||||
/node_modules/
|
||||
]
|
||||
}]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
test: /\.ts$/,
|
||||
sourceMap: false,
|
||||
inlineSourceMap: true,
|
||||
removeComments: true,
|
||||
module: "commonjs"
|
||||
}),
|
||||
// ignore changes during tests
|
||||
new webpack.WatchIgnorePlugin([
|
||||
/[\\\/]ReDoc$/i, // ignore change of ReDoc folder itself
|
||||
/node_modules[\\\/].*$/,
|
||||
/\.tmp[\\\/].*$/,
|
||||
/dist[\\\/].*$/,
|
||||
/(?:[^\\\/]*(?:[\\\/]|$))*[^\\\/]*\.css$/ // ignore css files
|
||||
]),
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/angular(\\|\/)core(\\|\/)(esm(\\|\/)src|src)(\\|\/)linker/,
|
||||
path.resolve(__dirname, '../src')
|
||||
)
|
||||
],
|
||||
})
|
27
custom.d.ts
vendored
27
custom.d.ts
vendored
|
@ -1,25 +1,20 @@
|
|||
declare module "*.css" {
|
||||
declare module '*.json' {
|
||||
const content: any;
|
||||
export = content;
|
||||
}
|
||||
|
||||
declare module '*.svg' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare module "*.json" {
|
||||
declare module '*.css' {
|
||||
const content: string;
|
||||
export default content;
|
||||
}
|
||||
|
||||
declare var LIB_VERSION: any;
|
||||
declare var IS_PRODUCTION: any;
|
||||
declare var AOT: any;
|
||||
declare var __DEV__: boolean;
|
||||
|
||||
interface ErrorStackTraceLimit {
|
||||
stackTraceLimit: number;
|
||||
}
|
||||
interface History {
|
||||
scrollRestoration: "auto"|"manual";
|
||||
}
|
||||
interface Window {
|
||||
HTMLElement: any
|
||||
}
|
||||
declare var safari: any;
|
||||
interface ErrorConstructor extends ErrorStackTraceLimit {}
|
||||
declare type Dict<T> = {
|
||||
[key: string]: T;
|
||||
};
|
||||
|
|
27362
demo/big-swagger.json
Normal file
27362
demo/big-swagger.json
Normal file
File diff suppressed because it is too large
Load Diff
|
@ -1,79 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ReDoc Demo: Multiple apis</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding-top: 40px;
|
||||
}
|
||||
|
||||
nav {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
}
|
||||
#links_container {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #0033a0;
|
||||
}
|
||||
|
||||
#links_container li {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Top navigation placeholder -->
|
||||
<nav>
|
||||
<ul id="links_container">
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<redoc scroll-y-offset="body > nav"></redoc>
|
||||
|
||||
<script src="https://rebilly.github.io/ReDoc/releases/v1.x.x/redoc.min.js"> </script>
|
||||
<script>
|
||||
// list of APIS
|
||||
var apis = [
|
||||
{
|
||||
name: 'PetStore',
|
||||
url: 'https://rebilly.github.io/ReDoc/swagger.yaml'
|
||||
},
|
||||
{
|
||||
name: 'Instagram',
|
||||
url: 'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml'
|
||||
},
|
||||
{
|
||||
name: 'Google Calendar',
|
||||
url: 'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/swagger.yaml'
|
||||
}
|
||||
];
|
||||
|
||||
// initially render first API
|
||||
Redoc.init(apis[0].url);
|
||||
|
||||
function onClick() {
|
||||
var url = this.getAttribute('data-link');
|
||||
Redoc.init(url);
|
||||
}
|
||||
|
||||
// dynamically building navigation items
|
||||
var $list = document.getElementById('links_container');
|
||||
apis.forEach(function(api) {
|
||||
var $listitem = document.createElement('li');
|
||||
$listitem.setAttribute('data-link', api.url);
|
||||
$listitem.innerText = api.name;
|
||||
$listitem.addEventListener('click', onClick);
|
||||
$list.appendChild($listitem);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,39 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ReDoc</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://cdn.vaadin.com/vaadin-core-elements/1.2.0/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<link rel="import" href="https://cdn.vaadin.com/vaadin-core-elements/1.2.0/vaadin-combo-box/vaadin-combo-box-light.html">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<header> <a href="/ReDoc"> ReDoc </a> </header>
|
||||
<template is="dom-bind" id="specs">
|
||||
<form id="schema-url-form" is="iron-form">
|
||||
<vaadin-combo-box-light id="spec-input" items="[[specs]]" allow-custom-value>
|
||||
<input placeholder="URL to a spec to try" id="schema-url-input" type="text" is="iron-input" value="https://rebilly.github.io/RebillyAPI/swagger.json">
|
||||
</vaadin-combo-box-light>
|
||||
<button type="submit"> Explore </button>
|
||||
</form>
|
||||
</template>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=Rebilly&repo=ReDoc&type=star&count=true&size=large"
|
||||
frameborder="0" scrolling="0" width="150px" height="30px"></iframe>
|
||||
</nav>
|
||||
|
||||
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering untrusted-spec></redoc>
|
||||
|
||||
<script src="main.js"> </script>
|
||||
<script src="./dist/redoc.min.js"> </script>
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-81703547-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,35 +1,24 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>ReDoc</title>
|
||||
<link rel="stylesheet" href="main.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<script src="https://cdn.vaadin.com/vaadin-core-elements/1.2.0/webcomponentsjs/webcomponents-lite.min.js"></script>
|
||||
<link rel="import" href="https://cdn.vaadin.com/vaadin-core-elements/1.2.0/vaadin-combo-box/vaadin-combo-box-light.html">
|
||||
</head>
|
||||
<body>
|
||||
<nav>
|
||||
<header> <a href="/ReDoc"> ReDoc </a> </header>
|
||||
<template is="dom-bind" id="specs">
|
||||
<form id="schema-url-form" is="iron-form">
|
||||
<vaadin-combo-box-light id="spec-input" items="[[specs]]" allow-custom-value>
|
||||
<input placeholder="URL to a spec to try" id="schema-url-input" type="text" is="iron-input" value="https://rebilly.github.io/RebillyAPI/swagger.json">
|
||||
</vaadin-combo-box-light>
|
||||
<button type="submit"> Explore </button>
|
||||
</form>
|
||||
</template>
|
||||
<iframe src="https://ghbtns.com/github-btn.html?user=Rebilly&repo=ReDoc&type=star&count=true&size=large"
|
||||
frameborder="0" scrolling="0" width="150px" height="30px"></iframe>
|
||||
</nav>
|
||||
|
||||
<redoc scroll-y-offset="body > nav" spec-url='swagger.yaml' lazy-rendering untrusted-spec></redoc>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>ReDoc</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
redoc {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<redoc id="example"></redoc>
|
||||
</body>
|
||||
|
||||
<script>
|
||||
window.__REDOC_DEV__ = true;
|
||||
</script>
|
||||
<script src="main.js"> </script>
|
||||
<script src="/webpack-dev-server.js"></script>
|
||||
<script src="/polyfills.js"></script>
|
||||
<script src="/redoc.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
133
demo/main.css
133
demo/main.css
|
@ -1,133 +0,0 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding-top: 50px;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
-moz-tap-highlight-color: rgba(0,0,0,0);
|
||||
-ms-tap-highlight-color: rgba(0,0,0,0);
|
||||
-o-tap-highlight-color: rgba(0,0,0,0);
|
||||
tap-highlight-color: rgba(0,0,0,0);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-smoothing: antialiased;
|
||||
-webkit-osx-font-smoothing: grayscale;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
||||
-ms-text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
||||
text-shadow: 1px 1px 1px rgba(0,0,0,0.004);
|
||||
text-rendering: optimizeSpeed!important;
|
||||
font-smooth: always;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
font-family: Monserrat, sans-serif;
|
||||
}
|
||||
|
||||
nav input, nav button {
|
||||
font-size: 16px;
|
||||
height: 28px;
|
||||
box-sizing: border-box;
|
||||
vertical-align: middle;
|
||||
line-height: 1;
|
||||
outline: none;
|
||||
}
|
||||
nav header {
|
||||
font-family: Monserrat, sans-serif;
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
font-size: 25px;
|
||||
color: #00329F;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
nav input {
|
||||
width: 50%;
|
||||
box-sizing: border-box;
|
||||
max-width: 500px;
|
||||
padding: 0 10px;
|
||||
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075);
|
||||
-webkit-transition: border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;
|
||||
-o-transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out .15s,box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
nav input:focus {
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);
|
||||
}
|
||||
|
||||
nav button {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
padding: 2px 10px;
|
||||
|
||||
touch-action: manipulation;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
nav button:hover {
|
||||
background-color: #e6e6e6;
|
||||
border-color: #adadad;
|
||||
}
|
||||
nav button:active {
|
||||
background-color: #d4d4d4;
|
||||
border-color: #8c8c8c;
|
||||
}
|
||||
|
||||
nav {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
text-align: center;
|
||||
background-color: white;
|
||||
border-bottom: 1px solid #ccc;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
nav iframe {
|
||||
margin: 10px 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
header a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
nav header {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
nav input {
|
||||
width: 70%;
|
||||
}
|
||||
nav header {
|
||||
display: none;
|
||||
}
|
||||
|
||||
nav iframe {
|
||||
display: none;
|
||||
}
|
||||
}
|
74
demo/main.js
74
demo/main.js
|
@ -1,74 +0,0 @@
|
|||
;(function() {
|
||||
'use strict';
|
||||
|
||||
var schemaUrlForm = document.getElementById('schema-url-form');
|
||||
var schemaUrlInput;
|
||||
|
||||
var url = window.location.search.match(/url=([^&]+)/);
|
||||
if (url && url.length > 1) {
|
||||
url = decodeURIComponent(url[1]);
|
||||
url = window.__REDOC_DEV__ ? url : '\\\\cors.apis.guru/' + url;
|
||||
document.getElementsByTagName('redoc')[0].setAttribute('spec-url', url);
|
||||
}
|
||||
|
||||
function updateQueryStringParameter(uri, key, value) {
|
||||
var re = new RegExp("([?|&])" + key + "=.*?(&|#|$)", "i");
|
||||
if (uri.match(re)) {
|
||||
return uri.replace(re, '$1' + key + "=" + value + '$2');
|
||||
} else {
|
||||
var hash = '';
|
||||
if( uri.indexOf('#') !== -1 ){
|
||||
hash = uri.replace(/.*#/, '#');
|
||||
uri = uri.replace(/#.*/, '');
|
||||
}
|
||||
var separator = uri.indexOf('?') !== -1 ? "&" : "?";
|
||||
return uri + separator + key + "=" + value + hash;
|
||||
}
|
||||
}
|
||||
|
||||
var specs = document.querySelector('#specs');
|
||||
specs.addEventListener('dom-change', function() {
|
||||
schemaUrlForm = document.getElementById('schema-url-form');
|
||||
schemaUrlInput = document.getElementById('schema-url-input');
|
||||
|
||||
schemaUrlForm.addEventListener('submit', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value)
|
||||
return false;
|
||||
})
|
||||
|
||||
schemaUrlInput.addEventListener('mousedown', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
schemaUrlInput.value = url;
|
||||
specs.specs = [
|
||||
'https://api.apis.guru/v2/specs/instagram.com/1.0.0/swagger.yaml',
|
||||
'https://api.apis.guru/v2/specs/googleapis.com/calendar/v3/swagger.yaml',
|
||||
'https://api.apis.guru/v2/specs/data2crm.com/1/swagger.yaml',
|
||||
'https://api.apis.guru/v2/specs/graphhopper.com/1.0/swagger.yaml'
|
||||
];
|
||||
|
||||
var $specInput = document.getElementById('spec-input');
|
||||
|
||||
$specInput.addEventListener('value-changed', function(e) {
|
||||
schemaUrlInput.value = e.detail.value;
|
||||
location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value);
|
||||
});
|
||||
|
||||
function selectItem() {
|
||||
let value = this.innerText.trim();
|
||||
schemaUrlInput.value = value;
|
||||
location.search = updateQueryStringParameter(location.search, 'url', schemaUrlInput.value);
|
||||
}
|
||||
|
||||
// for some reason events are not triggered so have to dirty fix this
|
||||
$specInput.addEventListener('click', function(event) {
|
||||
let $elems = document.querySelectorAll('.item.vaadin-combo-box-overlay');
|
||||
$elems.forEach(function($el) {
|
||||
$el.addEventListener('mousedown', selectItem);
|
||||
$el.addEventListener('mousedown', selectItem);
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
Binary file not shown.
Before Width: | Height: | Size: 8.8 KiB |
Binary file not shown.
Before Width: | Height: | Size: 198 KiB |
1021
demo/swagger.yaml
1021
demo/swagger.yaml
File diff suppressed because it is too large
Load Diff
|
@ -1,53 +0,0 @@
|
|||
module.exports = function(config) {
|
||||
const testWebpackConfig = require('./build/webpack.test.js');
|
||||
const travis = process.env.TRAVIS;
|
||||
|
||||
config.set({
|
||||
frameworks: ['jasmine', 'sinon', 'should'],
|
||||
preprocessors: {
|
||||
'./tests/spec-bundle.js': ['coverage', 'webpack', 'sourcemap'],
|
||||
},
|
||||
|
||||
coverageReporter: {
|
||||
type: 'in-memory',
|
||||
},
|
||||
|
||||
remapCoverageReporter: {
|
||||
'text-summary': null,
|
||||
'text-lcov': './coverage/lcov.info',
|
||||
html: './coverage/html',
|
||||
},
|
||||
webpack: testWebpackConfig,
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only',
|
||||
state: true,
|
||||
},
|
||||
client: {
|
||||
chai: {
|
||||
truncateThreshold: 0,
|
||||
},
|
||||
},
|
||||
files: [
|
||||
{ pattern: './tests/spec-bundle.js', watched: false },
|
||||
{ pattern: 'tests/schemas/**/*.json', included: false },
|
||||
{ pattern: 'tests/schemas/**/*.yml', included: false },
|
||||
{ pattern: 'lib/**/*.html', included: false },
|
||||
{ pattern: 'lib/**/*.css', included: false },
|
||||
],
|
||||
|
||||
proxies: {
|
||||
'/tests/schemas': '/base/tests/schemas',
|
||||
'/lib/': '/base/lib/',
|
||||
'/node_modules/': '/base/node_modules/',
|
||||
},
|
||||
colors: true,
|
||||
singleRun: true,
|
||||
reporters: travis
|
||||
? ['mocha', 'coverage', 'remap-coverage', 'coveralls']
|
||||
: ['mocha', 'coverage', 'remap-coverage'],
|
||||
|
||||
browsers: ['ChromeHeadless'],
|
||||
|
||||
browserNoActivityTimeout: 60000,
|
||||
});
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { RedocModule } from './redoc.module';
|
||||
import { Redoc } from './components/index';
|
||||
|
||||
@NgModule({
|
||||
imports: [ BrowserModule, RedocModule ],
|
||||
bootstrap: [ Redoc ],
|
||||
exports: [ Redoc ]
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
import { NgModuleRef } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
export function bootstrapRedoc(): Promise<NgModuleRef<AppModule>> {
|
||||
return platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
import { NgModuleRef } from '@angular/core';
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModule } from './app.module';
|
||||
import { AppModuleNgFactory } from '../compiled/lib/app.module.ngfactory';
|
||||
|
||||
export function bootstrapRedoc():Promise<NgModuleRef<AppModule>> {
|
||||
return platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
||||
}
|
|
@ -1,24 +0,0 @@
|
|||
<div class="api-info-wrapper">
|
||||
<h1>{{info.title}} <span class="api-info-version">({{info.version}})</span></h1>
|
||||
<p class="download-openapi" *ngIf="specUrl">
|
||||
Download OpenAPI specification:
|
||||
<a class="openapi-button" [attr.download]="downloadFilename" target="_blank" [attr.href]="specUrl"> Download </a>
|
||||
</p>
|
||||
<p>
|
||||
<!-- TODO: create separate components for contact and license ? -->
|
||||
<span *ngIf="info?.contact?.url || info?.contact?.email"> Contact:
|
||||
<a *ngIf="info.contact.url" href="{{info.contact.url}}">
|
||||
{{info.contact.name || info.contact.url}}</a>
|
||||
<a *ngIf="info.contact.email" href="mailto:{{info.contact.email}}">
|
||||
{{info.contact.email}}</a>
|
||||
</span>
|
||||
<span *ngIf="info.license"> License:
|
||||
<a *ngIf="info.license.url" href="{{info.license.url}}"> {{info.license.name}} </a>
|
||||
<span *ngIf="!info.license.url"> {{info.license.name}} </span>
|
||||
</span>
|
||||
<redoc-externalDocs [docs]="componentSchema.externalDocs"></redoc-externalDocs>
|
||||
</p>
|
||||
<span class="redoc-markdown-block">
|
||||
<dynamic-ng2-viewer [html]="info['x-redoc-html-description']"></dynamic-ng2-viewer>
|
||||
</span>
|
||||
</div>
|
|
@ -1,33 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host > .api-info-wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: $section-spacing;
|
||||
width: 60%;
|
||||
|
||||
|
||||
@media (max-width: $right-panel-squash-breakpoint) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||
padding-top: $section-spacing + 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.openapi-button {
|
||||
border: 1px solid $primary-color;
|
||||
color: $primary-color;
|
||||
font-weight: normal;
|
||||
margin-left: 0.5em;
|
||||
padding: 3px 8px 4px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
:host /deep/ [section] {
|
||||
padding-top: 2 * $section-spacing;
|
||||
}
|
||||
|
||||
:host /deep/ h2[section] {
|
||||
padding-top: $section-spacing;
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { Component } from '@angular/core';
|
||||
import { OptionsService } from '../../services/index';
|
||||
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
describe('ApiInfo Component', () => {
|
||||
let component;
|
||||
let fixture;
|
||||
let opts;
|
||||
let specMgr;
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
beforeEach(async(inject([SpecManager, OptionsService], (_specMgr, _opts) => {
|
||||
opts = _opts;
|
||||
opts.options = {
|
||||
scrollYOffset: () => 0,
|
||||
$scrollParent: window
|
||||
};
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/api-info-test.json').then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
component = getChildDebugElement(fixture.debugElement, 'api-info').componentInstance;
|
||||
fixture.detectChanges();
|
||||
}));
|
||||
|
||||
|
||||
it('should init component data', () => {
|
||||
expect(component).not.toBeNull();
|
||||
expect(component.info).not.toBeNull();
|
||||
component.info.title.should.be.equal('Swagger Petstore');
|
||||
});
|
||||
|
||||
it('should render api name and version', () => {
|
||||
let nativeElement = getChildDebugElement(fixture.debugElement, 'api-info').nativeElement;
|
||||
let headerElement = nativeElement.querySelector('h1');
|
||||
expect(headerElement.innerText).toContain('Swagger Petstore (v1.0.0)');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains an ApiInfo. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<api-info></api-info>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,44 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit, ElementRef } from '@angular/core';
|
||||
import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
|
||||
import { SpecManager, BaseComponent } from '../base';
|
||||
import { OptionsService, Marker } from '../../services/index';
|
||||
|
||||
@Component({
|
||||
selector: 'api-info',
|
||||
styleUrls: ['./api-info.css'],
|
||||
templateUrl: './api-info.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ApiInfo extends BaseComponent implements OnInit {
|
||||
info: any = {};
|
||||
specUrl: String | SafeResourceUrl;
|
||||
downloadFilename = '';
|
||||
constructor(specMgr: SpecManager,
|
||||
private optionsService: OptionsService,
|
||||
elRef: ElementRef,
|
||||
marker: Marker,
|
||||
private sanitizer: DomSanitizer
|
||||
) {
|
||||
super(specMgr);
|
||||
marker.addElement(elRef.nativeElement);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.info = this.componentSchema.info;
|
||||
this.specUrl = this.specMgr.specUrl;
|
||||
if (!this.specUrl && window.Blob && window.URL) {
|
||||
const blob = new Blob([JSON.stringify(this.specMgr.rawSpec, null, 2)], {type : 'application/json'});
|
||||
this.specUrl = this.sanitizer.bypassSecurityTrustResourceUrl(window.URL.createObjectURL(blob));
|
||||
this.downloadFilename = 'swagger.json';
|
||||
}
|
||||
|
||||
if (!isNaN(parseInt(this.info.version.toString().substring(0, 1)))) {
|
||||
this.info.version = 'v' + this.info.version;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<a *ngIf="logo.url" href="{{logo.url}}">
|
||||
<img *ngIf="logo.imgUrl" [attr.src]="logo.imgUrl" [ngStyle]="{'background-color': logo.bgColor}">
|
||||
</a>
|
||||
<img *ngIf="logo.imgUrl && !logo.url" [attr.src]="logo.imgUrl" [ngStyle]="{'background-color': logo.bgColor}">
|
|
@ -1,19 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-height: 150px;
|
||||
width: auto;
|
||||
display: inline-block;
|
||||
max-width: 100%;
|
||||
//padding: 0 5px;
|
||||
box-sizing: border-box;
|
||||
}
|
|
@ -1,73 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
|
||||
describe('Redoc components', () => {
|
||||
describe('ApiLogo Component', () => {
|
||||
let builder;
|
||||
let component;
|
||||
let fixture;
|
||||
let specMgr;
|
||||
|
||||
let schemaUrl = '/tests/schemas/api-info-test.json';
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
|
||||
beforeEach(async(inject([SpecManager], ( _specMgr) => {
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load(schemaUrl).then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
component = getChildDebugElement(fixture.debugElement, 'api-logo').componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
it('should init component data', () => {
|
||||
if (specMgr.a) return;
|
||||
expect(component).not.toBeNull();
|
||||
expect(component.logo).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should not display image when no x-logo', () => {
|
||||
component.logo.should.be.empty();
|
||||
let nativeElement = getChildDebugElement(fixture.debugElement, 'api-logo').nativeElement;
|
||||
let imgElement = nativeElement.querySelector('img');
|
||||
expect(imgElement).toBeNull();
|
||||
|
||||
// update schemaUrl to load other schema in the next test
|
||||
schemaUrl = '/tests/schemas/extended-petstore.yml';
|
||||
});
|
||||
|
||||
it('should load values from spec and use transparent bgColor by default', () => {
|
||||
component.logo.imgUrl.should.endWith('petstore-logo.png');
|
||||
component.logo.bgColor.should.be.equal('transparent');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains an ApiInfo. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<api-logo></api-logo>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
|
||||
@Component({
|
||||
selector: 'api-logo',
|
||||
styleUrls: ['./api-logo.css'],
|
||||
templateUrl: './api-logo.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ApiLogo extends BaseComponent implements OnInit {
|
||||
logo:any = {};
|
||||
|
||||
constructor(specMgr:SpecManager) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
const info = this.componentSchema.info;
|
||||
const logoInfo = info['x-logo'];
|
||||
if (!logoInfo) return;
|
||||
this.logo.imgUrl = logoInfo.url;
|
||||
this.logo.bgColor = logoInfo.backgroundColor || 'transparent';
|
||||
this.logo.url = info.contact && info.contact.url || null;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<div class="operation-endpoint" (click)="handleClick()">
|
||||
<h5 class="http-verb" [ngClass]="verb">{{verb}}</h5>
|
||||
<span><!--
|
||||
--><span class="operation-api-url-path">{{path}}</span><!--
|
||||
--></span>
|
||||
<svg class="expand-icon" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||
<polygon fill="white" points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="servers-overlay">
|
||||
<div *ngFor="let server of servers" class="server-item">
|
||||
<div class="description" [innerHtml]="server.description | marked"></div>
|
||||
<div select-on-click class="url">
|
||||
<span class="operation-api-url"> {{server.url}}</span>{{path}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,153 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.operation-endpoint {
|
||||
padding: 10px 30px 10px 20px;
|
||||
border-radius: $border-radius*2;
|
||||
background-color: darken($black, 2%);
|
||||
display: block;
|
||||
font-weight: $light;
|
||||
white-space: nowrap;
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.operation-endpoint > .operation-params-subheader {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 0;
|
||||
margin: 0;
|
||||
font-size: 12/14em;
|
||||
color: $black;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
border-radius: $border-radius;
|
||||
}
|
||||
|
||||
.operation-api-url {
|
||||
color: rgba($black, 0.8);
|
||||
&-path {
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
position: relative;
|
||||
top: 1px;
|
||||
color: #ffffff;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.http-verb {
|
||||
color: $black;
|
||||
background: #ffffff;
|
||||
padding: 3px 10px;
|
||||
text-transform: uppercase;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.servers-overlay {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
background: $side-bar-bg-color;
|
||||
color: $black;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 4px 4px 6px rgba(0, 0, 0, 0.33);
|
||||
overflow: hidden;
|
||||
border-bottom-left-radius: $border-radius*2;
|
||||
border-bottom-right-radius: $border-radius*2;
|
||||
}
|
||||
|
||||
.server-item {
|
||||
padding: 10px;
|
||||
//margin-bottom: 10px;
|
||||
|
||||
& > .url {
|
||||
padding: 5px;
|
||||
border: 1px solid $border-color;
|
||||
background: $background-color;
|
||||
word-break: break-all;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
float: right;
|
||||
background: darken($black, 2%);
|
||||
transform: rotateZ(0);
|
||||
transition: all 0.2s ease;
|
||||
top: 15px;
|
||||
right: 5px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.servers-overlay {
|
||||
transform: translateY(-50%) scaleY(0);
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
:host.expanded {
|
||||
> .operation-endpoint {
|
||||
border-color: $side-bar-bg-color;
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
|
||||
.servers-overlay {
|
||||
transform: translateY(0%) scaleY(1);
|
||||
}
|
||||
}
|
||||
|
||||
.http-verb {
|
||||
color: white;
|
||||
|
||||
&.get {
|
||||
background-color: $get-color;
|
||||
}
|
||||
|
||||
&.post {
|
||||
background-color: $post-color;
|
||||
}
|
||||
|
||||
&.put {
|
||||
background-color: $put-color;
|
||||
}
|
||||
|
||||
&.options {
|
||||
background-color: $options-color;
|
||||
}
|
||||
|
||||
&.patch {
|
||||
background-color: $patch-color;
|
||||
}
|
||||
|
||||
&.delete {
|
||||
background-color: $delete-color;
|
||||
}
|
||||
|
||||
&.basic {
|
||||
background-color: $basic-color;
|
||||
}
|
||||
|
||||
&.link {
|
||||
background-color: $link-color;
|
||||
}
|
||||
|
||||
&.head {
|
||||
background-color: $head-color;
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
import { EndpointLink } from './endpoint-link';
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
import { OptionsService } from '../../services/';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
describe('EndpointLink Component', () => {
|
||||
let builder;
|
||||
let component: EndpointLink;
|
||||
let specMgr: SpecManager;
|
||||
let opts: OptionsService;
|
||||
|
||||
beforeEach(async(inject([SpecManager, OptionsService], (_specMgr, _opts) => {
|
||||
specMgr = _specMgr;
|
||||
opts = _opts;
|
||||
})));
|
||||
|
||||
beforeEach(() => {
|
||||
specMgr.apiUrl = 'http://test.com/v1';
|
||||
specMgr._schema = {
|
||||
info: {},
|
||||
host: 'petstore.swagger.io',
|
||||
baseName: '/v2',
|
||||
schemes: ['https', 'http'],
|
||||
'x-servers': [
|
||||
{
|
||||
url: '//test.com/v2'
|
||||
},
|
||||
{
|
||||
url: 'ws://test.com/v3',
|
||||
description: 'test'
|
||||
}
|
||||
]
|
||||
};
|
||||
specMgr.init();
|
||||
|
||||
component = new EndpointLink(specMgr, opts);
|
||||
});
|
||||
|
||||
it('should replace // with appropriate protocol', () => {
|
||||
component.ngOnInit();
|
||||
component.servers[0].url.should.be.equal('https://test.com/v2');
|
||||
});
|
||||
|
||||
|
||||
it('should preserve other protocols', () => {
|
||||
component.ngOnInit();
|
||||
component.servers[1].url.should.be.equal('ws://test.com/v3');
|
||||
});
|
||||
|
||||
it('should fallback to host + basePath + schemas if no x-servers', () => {
|
||||
specMgr._schema['x-servers'] = null;
|
||||
specMgr.init();
|
||||
component.ngOnInit();
|
||||
component.servers.should.be.lengthOf(1);
|
||||
component.servers[0].url.should.be.equal('https://petstore.swagger.io');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains an Operation. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<operation pointer='#/paths/~1user~1{username}/put'></operation>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, Input, OnInit, HostListener, HostBinding} from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import { OptionsService } from '../../services/';
|
||||
import { stripTrailingSlash } from '../../utils/';
|
||||
|
||||
export interface ServerInfo {
|
||||
description: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'endpoint-link',
|
||||
styleUrls: ['./endpoint-link.css'],
|
||||
templateUrl: './endpoint-link.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class EndpointLink implements OnInit {
|
||||
@Input() path:string;
|
||||
@Input() verb:string;
|
||||
|
||||
apiUrl: string;
|
||||
servers: ServerInfo[];
|
||||
@HostBinding('class.expanded') expanded: boolean = false;
|
||||
|
||||
// @HostListener('click')
|
||||
handleClick() {
|
||||
this.expanded = !this.expanded;
|
||||
}
|
||||
|
||||
constructor(public specMgr:SpecManager, public optionsService: OptionsService) {
|
||||
this.expanded = false;
|
||||
}
|
||||
|
||||
init() {
|
||||
let servers:ServerInfo[] = this.specMgr.schema['x-servers'];
|
||||
if (servers) {
|
||||
this.servers = servers.map(({url, description}) => ({
|
||||
description,
|
||||
url: stripTrailingSlash(url.startsWith('//') ? `${this.specMgr.apiProtocol}:${url}` : url)
|
||||
}));
|
||||
} else {
|
||||
this.servers = [
|
||||
{
|
||||
description: 'Server URL',
|
||||
url: this.getBaseUrl()
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
getBaseUrl():string {
|
||||
if (this.optionsService.options.hideHostname) {
|
||||
return '';
|
||||
} else {
|
||||
return this.specMgr.apiUrl;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, Input, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
|
||||
@Component({
|
||||
selector: 'redoc-externalDocs',
|
||||
template: `<a *ngIf="docs" [href]="docs.url" [innerHtml]="docs.description | marked"></a>`,
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ExternalDocs implements OnInit {
|
||||
@Input() docs;
|
||||
|
||||
ngOnInit() {
|
||||
if (this.docs && !this.docs.description) {
|
||||
this.docs.description = 'External Docs';
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,260 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
$lines-width: 1px;
|
||||
$bullet-size: 1px;
|
||||
$cell-spacing: 25px;
|
||||
$cell-padding: 10px;
|
||||
$bullet-margin: 10px;
|
||||
$line-border: $lines-width solid $tree-lines-color;
|
||||
$line-border-erase: ($lines-width + 1px) solid $background-color;
|
||||
|
||||
$border-color: lighten($secondary-color, 50%);
|
||||
$nullable-color: #3195a6;
|
||||
$hint-border: 1px dotted rgba(38, 50, 56, 0.4);
|
||||
|
||||
$param-name-height: 20px;
|
||||
|
||||
$sub-schema-offset: ($bullet-size / 2) + $bullet-margin;
|
||||
|
||||
.param-name-wrap {
|
||||
display: inline-block;
|
||||
padding-right: $cell-spacing;
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
}
|
||||
|
||||
.param-info {
|
||||
border-bottom: 1px solid $border-color;
|
||||
padding: $cell-padding 0;
|
||||
width: 75%;
|
||||
|
||||
box-sizing: border-box;
|
||||
|
||||
> div {
|
||||
line-height: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.param-range {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 6px;
|
||||
margin-left: 6px;
|
||||
border-radius: $border-radius;
|
||||
background-color: rgba($primary-color, 0.1);
|
||||
padding: 0 4px;
|
||||
color: rgba($primary-color, 0.7);
|
||||
}
|
||||
|
||||
.param-description {
|
||||
//font-size: 14px;
|
||||
}
|
||||
|
||||
.param-required {
|
||||
vertical-align: middle;
|
||||
line-height: $param-name-height;
|
||||
color: $red;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.param-nullable {
|
||||
vertical-align: middle;
|
||||
line-height: $param-name-height;
|
||||
color: $nullable-color;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.param-type,
|
||||
.param-array-format {
|
||||
vertical-align: middle;
|
||||
line-height: $param-name-height;
|
||||
color: rgba($black, 0.4);
|
||||
font-size: 0.929em;
|
||||
}
|
||||
.param-type {
|
||||
font-weight: normal;
|
||||
word-break: break-all;
|
||||
&.array::before,
|
||||
&.tuple::before {
|
||||
color: $black;
|
||||
font-weight: $base-font-weight;
|
||||
.param-collection-format-multi + & {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
|
||||
&.array::before {
|
||||
content: $array-text;
|
||||
}
|
||||
&.tuple::before {
|
||||
content: $tuple-text;
|
||||
}
|
||||
|
||||
&.with-hint {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.4em;
|
||||
border-bottom: $hint-border;
|
||||
padding: 0;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
&-trivial {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-file {
|
||||
font-weight: bold;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
}
|
||||
|
||||
// tree
|
||||
|
||||
// Bullet
|
||||
|
||||
.param-name {
|
||||
border-left: $line-border;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
padding: $cell-padding 0;
|
||||
vertical-align: top;
|
||||
line-height: $param-name-height;
|
||||
|
||||
white-space: nowrap;
|
||||
font-size: 0.929em;
|
||||
font-weight: $regular;
|
||||
|
||||
> span::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: $bullet-size;
|
||||
height: $bullet-size + 6;
|
||||
background-color: $primary-color;
|
||||
margin: 0 $bullet-margin;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> span::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
border-top: $line-border;
|
||||
width: $bullet-margin;
|
||||
left: 0;
|
||||
top: ($param-name-height / 2) + $cell-padding + 1;
|
||||
}
|
||||
}
|
||||
|
||||
.param:first-of-type {
|
||||
> .param-name::before {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -$lines-width;
|
||||
top: 0;
|
||||
border-left: $line-border-erase;
|
||||
height: ($param-name-height / 2) + $cell-padding + 1;
|
||||
}
|
||||
}
|
||||
|
||||
.param:last-of-type,
|
||||
.param.last {
|
||||
> .param-name {
|
||||
position: relative;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -$lines-width - 1px;
|
||||
border-left: $line-border-erase;
|
||||
top: ($param-name-height / 2) + $cell-padding + $lines-width + 1;
|
||||
background-color: $background-color;
|
||||
bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.param-wrap:last-of-type > .param-schema {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.param-schema {
|
||||
.param-wrap:first-of-type {
|
||||
.param-name::before {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.param-schema.last {
|
||||
> td {
|
||||
border-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.param-enum {
|
||||
color: $text-color;
|
||||
font-size: 0.95em;
|
||||
|
||||
&::before {
|
||||
content: 'Valid values: ';
|
||||
}
|
||||
}
|
||||
|
||||
.param-enum {
|
||||
color: $text-color;
|
||||
font-size: 0.95em;
|
||||
|
||||
&::before {
|
||||
content: 'Valid values: ';
|
||||
}
|
||||
|
||||
.param-type.array ~ &::before {
|
||||
content: 'Valid items values: ';
|
||||
}
|
||||
}
|
||||
|
||||
.param-pattern {
|
||||
color: $nullable-color;
|
||||
white-space: nowrap;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
content: '/';
|
||||
margin: 0 3px;
|
||||
font-size: 1.2em;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.param-default {
|
||||
font-size: 0.95em;
|
||||
|
||||
&::before {
|
||||
content: 'Default: ';
|
||||
}
|
||||
}
|
||||
|
||||
.param-example {
|
||||
font-size: 0.95em;
|
||||
|
||||
&::before {
|
||||
content: 'Example: ';
|
||||
}
|
||||
}
|
||||
|
||||
.param-enum-value,
|
||||
.param-default-value,
|
||||
.param-example-value {
|
||||
font-family: Courier, monospace;
|
||||
background-color: rgba($secondary-color, 0.02);
|
||||
border: 1px solid rgba($secondary-color, 0.1);
|
||||
margin: 2px 3px;
|
||||
padding: 0.1em 0.2em 0.2em;
|
||||
border-radius: $border-radius;
|
||||
color: $text-color;
|
||||
display: inline-block;
|
||||
min-width: 20px;
|
||||
text-align: center;
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import {
|
||||
inject,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { JsonSchemaLazy } from './json-schema-lazy';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
|
||||
describe('JsonSchemaLazy Component', () => {
|
||||
let builder;
|
||||
let component;
|
||||
let fixture;
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema-lazy');
|
||||
component = <JsonSchemaLazy>debugEl.componentInstance;
|
||||
spyOn(component, '_loadAfterSelf').and.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
component._loadAfterSelf.and.callThrough();
|
||||
});
|
||||
|
||||
it('should init component', () => {
|
||||
expect(component).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should run loadNextToLocation on load', () => {
|
||||
component.pointer = '#/def';
|
||||
fixture.detectChanges();
|
||||
component.load();
|
||||
expect(component._loadAfterSelf).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains a lazy schema. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<json-schema-lazy></json-schema-lazy>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component, ElementRef, ViewContainerRef, OnDestroy, OnInit, Input,
|
||||
AfterViewInit, ComponentFactoryResolver, Renderer } from '@angular/core';
|
||||
|
||||
import { JsonSchema } from './json-schema';
|
||||
import { OptionsService } from '../../services/options.service';
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
var cache = {};
|
||||
|
||||
@Component({
|
||||
selector: 'json-schema-lazy',
|
||||
entryComponents: [ JsonSchema ],
|
||||
template: '',
|
||||
styles: [':host { display:none }']
|
||||
})
|
||||
export class JsonSchemaLazy implements OnDestroy, OnInit, AfterViewInit {
|
||||
@Input() pointer: string;
|
||||
@Input() absolutePointer: string;
|
||||
@Input() auto: boolean;
|
||||
@Input() isRequestSchema: boolean;
|
||||
@Input() final: boolean = false;
|
||||
@Input() nestOdd: boolean;
|
||||
@Input() childFor: string;
|
||||
@Input() isArray: boolean;
|
||||
disableLazy: boolean = false;
|
||||
loaded: boolean = false;
|
||||
constructor(private specMgr:SpecManager, private location:ViewContainerRef, private elementRef:ElementRef,
|
||||
private resolver:ComponentFactoryResolver, private optionsService:OptionsService, private _renderer: Renderer) {
|
||||
this.disableLazy = this.optionsService.options.disableLazySchemas;
|
||||
}
|
||||
|
||||
normalizePointer() {
|
||||
let schema = this.specMgr.byPointer(this.pointer);
|
||||
return schema && schema.$ref || this.pointer;
|
||||
}
|
||||
|
||||
private _loadAfterSelf() {
|
||||
var componentFactory = this.resolver.resolveComponentFactory(JsonSchema);
|
||||
let contextInjector = this.location.parentInjector;
|
||||
let compRef = this.location.createComponent(componentFactory, null, contextInjector, null);
|
||||
this.projectComponentInputs(compRef.instance);
|
||||
this._renderer.setElementAttribute(compRef.location.nativeElement, 'class', this.location.element.nativeElement.className);
|
||||
compRef.changeDetectorRef.detectChanges();
|
||||
this.loaded = true;
|
||||
return compRef;
|
||||
}
|
||||
|
||||
load() {
|
||||
if (this.disableLazy) return;
|
||||
if (this.loaded) return;
|
||||
if (this.pointer) {
|
||||
this._loadAfterSelf();
|
||||
}
|
||||
}
|
||||
|
||||
// cache JsonSchema view
|
||||
loadCached() {
|
||||
this.pointer = this.normalizePointer();
|
||||
if (cache[this.pointer]) {
|
||||
let compRef = cache[this.pointer];
|
||||
let $element = compRef.location.nativeElement;
|
||||
|
||||
// skip caching view with descendant schemas
|
||||
// as it needs attached controller
|
||||
let hasDescendants = compRef.instance.descendants && compRef.instance.descendants.length;
|
||||
if (!this.disableLazy && (hasDescendants || compRef.instance._hasSubSchemas)) {
|
||||
this._loadAfterSelf();
|
||||
return;
|
||||
}
|
||||
insertAfter($element.cloneNode(true), this.elementRef.nativeElement);
|
||||
this.loaded = true;
|
||||
} else {
|
||||
cache[this.pointer] = this._loadAfterSelf();
|
||||
}
|
||||
}
|
||||
|
||||
projectComponentInputs(instance:JsonSchema) {
|
||||
Object.assign(instance, this);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
if (!this.absolutePointer) this.absolutePointer = this.pointer;
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
if (!this.auto && !this.disableLazy) return;
|
||||
this.loadCached();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
// clear cache
|
||||
cache = {};
|
||||
}
|
||||
}
|
||||
|
||||
function insertAfter(newNode, referenceNode) {
|
||||
referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
|
||||
}
|
|
@ -1,107 +0,0 @@
|
|||
<ng-container [ngSwitch]="schema._widgetType">
|
||||
<ng-template ngSwitchCase="file">
|
||||
<span class="param-wrap">
|
||||
<span class="param-type-file">file</span>
|
||||
<div *ngIf="schema._produces && !isRequestSchema" class="file produces">
|
||||
<ul>
|
||||
<li *ngFor="let type of schema._produces">{{type}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div *ngIf="schema._consumes && isRequestSchema" class="file consume">
|
||||
<ul>
|
||||
<li *ngFor="let type of schema._consumes">{{type}}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</span>
|
||||
</ng-template>
|
||||
<ng-template ngSwitchCase="trivial">
|
||||
<span class="param-wrap">
|
||||
<span class="param-type param-type-trivial {{schema.type}}"
|
||||
[ngClass]="{'with-hint': schema._displayTypeHint, 'array': _isArray}"
|
||||
title="{{schema._displayTypeHint}}">{{schema._displayType}} {{schema._displayFormat}}
|
||||
<span class="param-range" *ngIf="schema._range"> {{schema._range}} </span>
|
||||
</span>
|
||||
<span *ngIf="schema['x-nullable']" class="param-nullable">Nullable</span>
|
||||
<div *ngIf="schema.enum" class="param-enum">
|
||||
<span *ngFor="let enumItem of schema.enum" class="param-enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
||||
</div>
|
||||
<span *ngIf="schema.pattern" class="param-pattern">{{schema.pattern}}</span>
|
||||
</span>
|
||||
</ng-template>
|
||||
<ng-template ngSwitchCase="tuple">
|
||||
<div class="params-wrap params-array array-tuple">
|
||||
<ng-template ngFor [ngForOf]="schema.items" let-item="$implicit" let-idx="index" [ngForTrackBy]="trackByIdx">
|
||||
<div class="tuple-item">
|
||||
<span class="tuple-item-index"> [{{idx}}]: </span>
|
||||
<json-schema class="nested-schema" [pointer]="item._pointer"
|
||||
[absolutePointer]="item._pointer"
|
||||
[nestOdd]="!nestOdd" [isRequestSchema]="isRequestSchema">
|
||||
</json-schema>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template ngSwitchCase="array">
|
||||
<json-schema class="nested-schema" [pointer]="schema._pointer"
|
||||
[nestOdd]="!nestOdd" [isRequestSchema]="isRequestSchema"> </json-schema>
|
||||
</ng-template>
|
||||
<ng-template ngSwitchCase="object">
|
||||
<table class="params-wrap" [ngClass]="{'params-array': _isArray}">
|
||||
<!-- <caption> {{_displayType}} </caption> -->
|
||||
<ng-template ngFor [ngForOf]="properties" let-prop="$implicit" let-last="last" [ngForTrackBy]="trackByName">
|
||||
<tr class="param"
|
||||
[class.last]="last"
|
||||
[class.discriminator] = "prop.isDiscriminator"
|
||||
[class.complex] = "prop._pointer"
|
||||
[class.additional] = "prop._additional"
|
||||
[class.expanded] = "subSchema.open">
|
||||
<td class="param-name">
|
||||
<span class="param-name-wrap" (click)="subSchema.toggle()">
|
||||
<span class="param-name-content">
|
||||
{{prop.name}}
|
||||
<span class="param-name-enumvalue" [hidden]="!prop._enumItem"> {{prop._enumItem?.val | json}} </span>
|
||||
</span>
|
||||
<svg *ngIf="prop._pointer" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||
<polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||
</svg>
|
||||
</span>
|
||||
</td>
|
||||
<td class="param-info">
|
||||
<div>
|
||||
<span class="param-type {{prop.type}}" [ngClass]="{'with-hint': prop._displayTypeHint, 'tuple': prop._isTuple, 'array': (prop._isArray || prop.type == 'array')}"
|
||||
title="{{prop._displayTypeHint}}"> {{prop._displayType}} {{prop._displayFormat}}
|
||||
<span class="param-range" *ngIf="prop._range"> {{prop._range}} </span>
|
||||
</span>
|
||||
<span *ngIf="prop._required" class="param-required">Required</span>
|
||||
<span *ngIf="prop['x-nullable']" class="param-nullable">Nullable</span>
|
||||
<div class="param-default" *ngIf="prop.default != null">
|
||||
<span class="param-default-value">{{prop.default | json}}</span>
|
||||
</div>
|
||||
<div *ngIf="prop.enum && !prop.isDiscriminator" class="param-enum">
|
||||
<span *ngFor="let enumItem of prop.enum" class="param-enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
||||
</div>
|
||||
<span *ngIf="prop.pattern" class="param-pattern">{{prop.pattern}}</span>
|
||||
</div>
|
||||
<div class="param-description" [innerHtml]="prop.description | marked"></div>
|
||||
<div class="discriminator-info" *ngIf="prop.isDiscriminator && descendants.length">
|
||||
<drop-down (change)="selectDescendantByIdx($event)" [active]="activeDescendant.idx">
|
||||
<option *ngFor="let descendant of descendants; let i=index"
|
||||
[value]="i" [attr.selected]="descendant.active ? '' : null" >{{descendant.name}}</option>
|
||||
</drop-down>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="param-schema" [ngClass]="{'last': last}" [hidden]="!prop._pointer">
|
||||
<td colspan="2">
|
||||
<zippy [attr.disabled]="prop.name" #subSchema title="Expand" [headless]="true" (openChange)="lazySchema.load()" [(open)]="prop.expanded">
|
||||
<json-schema-lazy #lazySchema [auto]="prop.expanded" class="nested-schema" [pointer]="prop._pointer"
|
||||
[nestOdd]="!nestOdd" [isRequestSchema]="isRequestSchema" absolutePointer="{{absolutePointer}}/properties/{{prop.name}}">
|
||||
</json-schema-lazy>
|
||||
</zippy>
|
||||
</td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</table>
|
||||
</ng-template>
|
||||
|
||||
</ng-container>
|
|
@ -1,221 +0,0 @@
|
|||
@import 'json-schema-common';
|
||||
|
||||
// styles for array-schema for array
|
||||
$array-marker-font-sz: 13px;
|
||||
$array-marker-line-height: 1.5;
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.param-schema > td {
|
||||
border-left: $line-border;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.derived-schema {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.derived-schema.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host.nested-schema {
|
||||
background-color: white;
|
||||
padding: 10px 20px;
|
||||
position: relative;
|
||||
border-radius: $border-radius;
|
||||
|
||||
&:before, &:after {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
border-width: 10px 15px 0;
|
||||
margin-left: -7.5px;
|
||||
border-top-color: $side-menu-active-bg-color;
|
||||
}
|
||||
&:before {
|
||||
left: 10%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
right: 10%;
|
||||
}
|
||||
|
||||
.param:first-of-type > .param-name:before, .param:last-of-type > .param-name:after {
|
||||
border-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
:host[nestodd="true"] {
|
||||
background-color: $side-menu-active-bg-color;
|
||||
border-radius: $border-radius;
|
||||
|
||||
&:before, &:after {
|
||||
border-top-color: white;
|
||||
}
|
||||
|
||||
> .params-wrap > .param:first-of-type > .param-name:before,
|
||||
> .params-wrap > .param:last-of-type > .param-name:after {
|
||||
border-color: $side-menu-active-bg-color;
|
||||
}
|
||||
|
||||
> .params-wrap > .param:last-of-type,
|
||||
> .params-wrap > .param.last {
|
||||
> .param-name:after {
|
||||
border-color: $side-menu-active-bg-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zippy {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.zippy-content-wrap {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.param.complex.expanded > .param-info {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.param.complex > .param-name .param-name-wrap {
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.param.complex > .param-name svg {
|
||||
height: 1.2em;
|
||||
width: 1.2em;
|
||||
vertical-align: middle;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.param.complex.expanded > .param-name svg{
|
||||
transform: rotateZ(-180deg);
|
||||
}
|
||||
|
||||
.param.additional > .param-name {
|
||||
color: rgba($black, 0.4);
|
||||
}
|
||||
|
||||
.params-wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.params-wrap.params-array:before, .params-wrap.params-array:after {
|
||||
display: block;
|
||||
font-weight: $base-font-weight;
|
||||
color: $black;
|
||||
font-size: $array-marker-font-sz;
|
||||
line-height: $array-marker-line-height;
|
||||
}
|
||||
|
||||
.params-wrap.params-array:after {
|
||||
content: "]";
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.params-wrap.params-array:before {
|
||||
content: "Array [";
|
||||
padding-top: 1em;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.params-wrap.params-array {
|
||||
padding-left: $bullet-margin;
|
||||
}
|
||||
|
||||
.param-schema.param-array:before {
|
||||
bottom: ($array-marker-font-sz * $array-marker-line-height) / 2;
|
||||
width: $bullet-margin;
|
||||
border-left-style: dashed;
|
||||
border-bottom: $lines-width dashed $tree-lines-color;
|
||||
}
|
||||
|
||||
.params-wrap.params-array > .param-wrap:first-of-type > .param > .param-name:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -$lines-width;
|
||||
top: 0;
|
||||
border-left: $line-border-erase;
|
||||
height: ($param-name-height/2) + $cell-padding;
|
||||
}
|
||||
|
||||
.params-wrap > .param > .param-schema.param-array {
|
||||
border-left-color: transparent;
|
||||
}
|
||||
|
||||
.discriminator-info {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.discriminator-wrap:not(.empty) > td {
|
||||
//border-left: $line-border;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
border-left: $line-border;
|
||||
height: ($param-name-height/2) + $cell-padding + 1;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
ul, li {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
li:before {
|
||||
content: "- ";
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.array-tuple > .tuple-item {
|
||||
margin-top: 1.5em;
|
||||
display: flex;
|
||||
|
||||
> span {
|
||||
flex: 0;
|
||||
padding: 10px 15px 10px 0;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
> json-schema {
|
||||
flex: 1;
|
||||
&:before, &:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.param-name-enumvalue {
|
||||
padding: 2px;
|
||||
background-color: #e6ebf6;
|
||||
|
||||
&:before {
|
||||
content: " = ";
|
||||
}
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
inject,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
import { SpecManager } from '../../utils/spec-manager';;
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
describe('JsonSchema Component', () => {
|
||||
let builder;
|
||||
let component;
|
||||
let fixture;
|
||||
let specMgr;
|
||||
|
||||
beforeEach(inject([SpecManager], ( _spec) => {
|
||||
|
||||
specMgr = _spec;
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
let debugEl = getChildDebugElement(fixture.debugElement, 'json-schema');
|
||||
component = debugEl.componentInstance;
|
||||
});
|
||||
|
||||
it('should init component', () => {
|
||||
component.pointer = '';
|
||||
(<SpecManager>specMgr)._schema = {type: 'object'};
|
||||
fixture.detectChanges();
|
||||
expect(component).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should set isTrivial for non-object/array types', () => {
|
||||
component.pointer = '#';
|
||||
(<any>specMgr)._schema = {type: 'string'};
|
||||
fixture.detectChanges();
|
||||
component.schema.isTrivial.should.be.true();
|
||||
});
|
||||
|
||||
it('should use < anything > notation for prop without type', () => {
|
||||
component.pointer = '#';
|
||||
(<any>specMgr)._schema = {type: 'object', properties: {
|
||||
test: {}
|
||||
}};
|
||||
fixture.detectChanges();
|
||||
component.schema._properties[0]._displayType.should.be.equal('< anything >');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains a json schema. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<json-schema></json-schema>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,203 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component,
|
||||
Input,
|
||||
Renderer,
|
||||
ElementRef,
|
||||
OnInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
|
||||
import { BaseSearchableComponent, SpecManager } from '../base';
|
||||
import { SchemaNormalizer, SchemaHelper, AppStateService, OptionsService } from '../../services/';
|
||||
import { JsonPointer, DescendantInfo } from '../../utils/';
|
||||
import { Zippy } from '../../shared/components';
|
||||
import { JsonSchemaLazy } from './json-schema-lazy';
|
||||
|
||||
@Component({
|
||||
selector: 'json-schema',
|
||||
templateUrl: './json-schema.html',
|
||||
styleUrls: ['./json-schema.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class JsonSchema extends BaseSearchableComponent implements OnInit {
|
||||
@Input() pointer: string;
|
||||
@Input() absolutePointer: string;
|
||||
@Input() final: boolean = false;
|
||||
@Input() nestOdd: boolean;
|
||||
@Input() childFor: string;
|
||||
@Input() isRequestSchema: boolean;
|
||||
|
||||
schema: any = {};
|
||||
activeDescendant:any = {};
|
||||
discriminator: string = null;
|
||||
_hasSubSchemas: boolean = false;
|
||||
properties: any;
|
||||
_isArray: boolean;
|
||||
normalizer: SchemaNormalizer;
|
||||
descendants: DescendantInfo[];
|
||||
|
||||
constructor(
|
||||
specMgr: SpecManager,
|
||||
app: AppStateService,
|
||||
private _renderer: Renderer,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private _elementRef: ElementRef,
|
||||
private optionsService: OptionsService) {
|
||||
super(specMgr, app);
|
||||
this.normalizer = new SchemaNormalizer(specMgr);
|
||||
}
|
||||
|
||||
get normPointer() {
|
||||
return this.schema._pointer || this.pointer;
|
||||
}
|
||||
|
||||
selectDescendantByIdx(idx) {
|
||||
this.selectDescendant(this.descendants[idx]);
|
||||
}
|
||||
|
||||
selectDescendant(activeDescendant: DescendantInfo) {
|
||||
if (!activeDescendant || activeDescendant.active) return;
|
||||
this.descendants.forEach(d => {
|
||||
d.active = false;
|
||||
});
|
||||
activeDescendant.active = true;
|
||||
|
||||
this.schema = this.specMgr.getDescendant(activeDescendant, this.componentSchema);
|
||||
this.pointer = this.schema._pointer || activeDescendant.$ref;
|
||||
this.normalizer.reset();
|
||||
this.schema = this.normalizer.normalize(this.schema, this.normPointer,
|
||||
{resolved: true});
|
||||
this.preprocessSchema();
|
||||
this.activeDescendant = activeDescendant;
|
||||
}
|
||||
|
||||
initDescendants() {
|
||||
this.descendants = this.specMgr.findDerivedDefinitions(this.normPointer, this.schema);
|
||||
if (!this.descendants.length) return;
|
||||
let discriminator = this.discriminator = this.schema.discriminator || this.schema['x-extendedDiscriminator'];
|
||||
let discrProperty = this.schema.properties &&
|
||||
this.schema.properties[discriminator];
|
||||
if (discrProperty && discrProperty.enum) {
|
||||
let enumOrder = {};
|
||||
discrProperty.enum.forEach((enumItem, idx) => {
|
||||
enumOrder[enumItem] = idx;
|
||||
});
|
||||
|
||||
this.descendants = this.descendants
|
||||
.filter(a => {
|
||||
return enumOrder[a.name] != undefined;
|
||||
}).sort((a, b) => {
|
||||
return enumOrder[a.name] > enumOrder[b.name] ? 1 : -1;
|
||||
});
|
||||
}
|
||||
this.descendants.forEach((d, idx) => d.idx = idx);
|
||||
this.selectDescendantByIdx(0);
|
||||
}
|
||||
|
||||
init() {
|
||||
if (!this.pointer) return;
|
||||
if (!this.absolutePointer) this.absolutePointer = this.pointer;
|
||||
|
||||
this.schema = this.componentSchema;
|
||||
if (!this.schema) {
|
||||
throw new Error(`Can't load component schema at ${this.pointer}`);
|
||||
}
|
||||
|
||||
this.applyStyling();
|
||||
|
||||
this.schema = this.normalizer.normalize(this.schema, this.normPointer, {resolved: true});
|
||||
this.schema = SchemaHelper.unwrapArray(this.schema, this.normPointer);
|
||||
this._isArray = this.schema._isArray;
|
||||
this.absolutePointer += (this._isArray ? '/items' : '');
|
||||
this.initDescendants();
|
||||
this.preprocessSchema();
|
||||
}
|
||||
|
||||
preprocessSchema() {
|
||||
SchemaHelper.preprocess(this.schema, this.normPointer, this.pointer);
|
||||
|
||||
if (!this.schema.isTrivial) {
|
||||
SchemaHelper.preprocessProperties(this.schema, this.normPointer, {
|
||||
childFor: this.childFor,
|
||||
discriminator: this.discriminator
|
||||
});
|
||||
}
|
||||
|
||||
this.properties = this.schema._properties || [];
|
||||
if (this.isRequestSchema) {
|
||||
this.properties = this.properties.filter(prop => !prop.readOnly);
|
||||
}
|
||||
|
||||
if (this.optionsService.options.requiredPropsFirst) {
|
||||
SchemaHelper.moveRequiredPropsFirst(this.properties, this.schema.required);
|
||||
}
|
||||
|
||||
this._hasSubSchemas = this.properties && this.properties.some(
|
||||
propSchema => {
|
||||
if (propSchema.type === 'array') {
|
||||
propSchema = propSchema.items;
|
||||
}
|
||||
return (propSchema && propSchema.type === 'object' && propSchema._pointer);
|
||||
});
|
||||
|
||||
if (this.properties.length === 1) {
|
||||
this.properties[0].expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
applyStyling() {
|
||||
if (this.nestOdd) {
|
||||
this._renderer.setElementAttribute(this._elementRef.nativeElement, 'nestodd', 'true');
|
||||
}
|
||||
}
|
||||
|
||||
trackByName(_: number, item: any): number {
|
||||
return item.name + (item._pointer || '');
|
||||
}
|
||||
|
||||
trackByIdx(idx: number, _: any): number {
|
||||
return idx;
|
||||
}
|
||||
|
||||
findDescendantWithField(fieldName: string): DescendantInfo {
|
||||
let res: DescendantInfo;
|
||||
for (let descendantInfo of this.descendants) {
|
||||
let schema = this.specMgr.getDescendant(descendantInfo, this.schema);
|
||||
this.normalizer.reset();
|
||||
schema = this.normalizer.normalize(schema, this.normPointer,
|
||||
{resolved: true});
|
||||
if (schema.properties && schema.properties[fieldName]) {
|
||||
res = descendantInfo;
|
||||
break;
|
||||
};
|
||||
};
|
||||
return res;
|
||||
}
|
||||
|
||||
ensureSearchIsShown(ptr: string) {
|
||||
if (ptr.startsWith(this.absolutePointer)) {
|
||||
let props = this.properties;
|
||||
if (!props) return;
|
||||
let relative = JsonPointer.relative(this.absolutePointer, ptr);
|
||||
let propName;
|
||||
if (relative.length > 1 && relative[0] === 'properties') {
|
||||
propName = relative[1];
|
||||
}
|
||||
let prop = props.find(p => p.name === propName);
|
||||
if (!prop) {
|
||||
let d = this.findDescendantWithField(propName);
|
||||
this.selectDescendant(d);
|
||||
prop = this.properties.find(p => p.name === propName);
|
||||
}
|
||||
if (prop && !prop.isTrivial) prop.expanded = true;
|
||||
this.cdr.markForCheck();
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
:host {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
display: block;
|
||||
|
||||
height: 5px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: attr(progress percentage);
|
||||
background-color: #5f7fc3;
|
||||
transition: right 0.2s linear;
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import {
|
||||
Component
|
||||
} from '@angular/core';
|
||||
|
||||
import {
|
||||
ComponentFixture,
|
||||
inject,
|
||||
fakeAsync,
|
||||
tick,
|
||||
TestBed,
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { LoadingBar } from './loading-bar';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
describe('Loading Bar', () => {
|
||||
let component: LoadingBar;
|
||||
|
||||
it('should init component', () => {
|
||||
let fixture = TestBed.createComponent(LoadingBar);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
should.exist(component);
|
||||
component.progress.should.be.equal(0);
|
||||
component.display.should.be.equal('block');
|
||||
});
|
||||
|
||||
it('should hide itself in 500ms if progress is 100', fakeAsync(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
let fixture = TestBed.createComponent(TestAppComponent);
|
||||
let parentComp = fixture.componentInstance;
|
||||
component = getChildDebugElement(fixture.debugElement, 'loading-bar').componentInstance;
|
||||
// need to pass update through parent component as ngOnChanges is run only for view changes
|
||||
parentComp.progress = 50;
|
||||
fixture.detectChanges();
|
||||
parentComp.progress = 100;
|
||||
fixture.detectChanges();
|
||||
|
||||
component.display.should.be.equal('block');
|
||||
tick(500);
|
||||
component.display.should.be.equal('none');
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains an ApiInfo. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<loading-bar [progress]="progress"></loading-bar>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
progress = 0;
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
'use strict';
|
||||
import { Input, HostBinding, Component, OnChanges } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'loading-bar',
|
||||
template: `
|
||||
<span [style.width]='progress + "%"'> </span>
|
||||
`,
|
||||
styleUrls: ['loading-bar.css']
|
||||
})
|
||||
export class LoadingBar implements OnChanges {
|
||||
@Input() progress:number = 0;
|
||||
@HostBinding('style.display') display = 'block';
|
||||
|
||||
ngOnChanges(ch) {
|
||||
if (ch.progress.currentValue === 100) {
|
||||
setTimeout(() => {
|
||||
this.display = 'none';
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
<div class="operation" *ngIf="operation">
|
||||
<div class="operation-content">
|
||||
<h2 class="operation-header sharable-header" [class.deprecated]="operation.deprecated">
|
||||
<a class="share-link" href="#{{operation.anchor}}"></a>{{operation.summary}}
|
||||
</h2>
|
||||
<endpoint-link *ngIf="pathInMiddlePanel"
|
||||
[verb]="operation.verb" [path]="operation.path"> </endpoint-link>
|
||||
<div class="operation-tags" *ngIf="operation.info.tags.length">
|
||||
<a *ngFor="let tag of operation.info.tags" attr.href="#tag/{{tag}}"> {{tag}} </a>
|
||||
</div>
|
||||
<p *ngIf="operation.info.description" class="operation-description"
|
||||
[innerHtml]="operation.info.description | marked">
|
||||
</p>
|
||||
<redoc-externalDocs [docs]="operation.externalDocs"></redoc-externalDocs>
|
||||
<params-list pointer="{{pointer}}/parameters"> </params-list>
|
||||
<responses-list pointer="{{pointer}}/responses"> </responses-list>
|
||||
</div>
|
||||
<div class="operation-samples">
|
||||
|
||||
<endpoint-link *ngIf="!pathInMiddlePanel"
|
||||
[verb]="operation.verb" [path]="operation.path"> </endpoint-link>
|
||||
|
||||
<div>
|
||||
<request-samples [pointer]="pointer" [schemaPointer]="operation.bodyParam?._pointer">
|
||||
</request-samples>
|
||||
</div>
|
||||
<div>
|
||||
<br>
|
||||
<responses-samples pointer="{{pointer}}/responses"> </responses-samples>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,148 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
|
||||
:host {
|
||||
padding-bottom: 100px;
|
||||
display: block;
|
||||
border-bottom: 1px solid rgba(127, 127, 127, 0.25);
|
||||
margin-top: 1em;
|
||||
transform: translateZ(0);
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
// :host:last-of-type {
|
||||
// border-bottom: 0;
|
||||
// }
|
||||
|
||||
.operation-header {
|
||||
margin-bottom: calc(1em - 6px);
|
||||
|
||||
&.deprecated {
|
||||
&:after {
|
||||
content: 'Deprecated';
|
||||
display: inline-block;
|
||||
padding: 0 5px;
|
||||
margin: 0;
|
||||
background-color: $yellow;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operation-tags {
|
||||
margin-top: 20px;
|
||||
|
||||
> a {
|
||||
font-size: 16px;
|
||||
color: #999;
|
||||
display: inline-block;
|
||||
padding: 0 0.5em;
|
||||
text-decoration: none;
|
||||
|
||||
&:before {
|
||||
content: '#';
|
||||
margin-right: -0.4em;
|
||||
}
|
||||
|
||||
&:first-of-type {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operation-content, .operation-samples {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.operation-content {
|
||||
width: 100% - $samples-panel-width;
|
||||
padding: $section-spacing;
|
||||
}
|
||||
|
||||
.operation-samples {
|
||||
color: $sample-panel-color;
|
||||
width: 40%;
|
||||
padding: $section-spacing;
|
||||
background: $samples-panel-bg-color;
|
||||
}
|
||||
|
||||
.operation-samples pre {
|
||||
color: $sample-panel-color;
|
||||
}
|
||||
|
||||
.operation-samples header,
|
||||
.operation-samples > h5 {
|
||||
color: $sample-panel-headers-color;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.operation-samples > h5 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.operation-samples schema-sample {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.operation:after {
|
||||
content: "";
|
||||
display: table;
|
||||
clear:both;
|
||||
}
|
||||
|
||||
.operation-description {
|
||||
padding: 6px 0 10px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
[select-on-click] {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: $right-panel-squash-breakpoint) {
|
||||
.operations:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.operation-samples, .operation-content {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.operation-samples {
|
||||
margin-top: 2em;
|
||||
}
|
||||
|
||||
:host {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.operation-content /deep/ endpoint-link {
|
||||
margin-bottom: 16px;
|
||||
|
||||
.operation-endpoint[class] {
|
||||
padding: 5px 30px 5px 5px;
|
||||
border: 0;
|
||||
border-bottom: 1px solid $border-color;
|
||||
border-radius: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.operation-api-url-path {
|
||||
color: $black;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
top: 8px;
|
||||
background-color: $border-color;
|
||||
}
|
||||
|
||||
.servers-overlay {
|
||||
border: 1px solid $border-color;
|
||||
border-top: 0;
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
import { Operation } from './operation';
|
||||
import { SpecManager } from '../../utils/spec-manager';;
|
||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';;
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
describe('Operation Component', () => {
|
||||
let builder;
|
||||
let component: Operation;
|
||||
let specMgr;
|
||||
|
||||
beforeEach(async(inject([SpecManager, LazyTasksService], (_specMgr, lazyTasks) => {
|
||||
lazyTasks.sync = true;
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
let fixture = TestBed.createComponent(TestAppComponent);
|
||||
component = getChildDebugElement(fixture.debugElement, 'operation').componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
it('should init component', () => {
|
||||
expect(component).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should init basic component data', () => {
|
||||
component.operation.verb.should.be.equal('put');
|
||||
component.operation.path.should.be.equal('/user/{username}');
|
||||
});
|
||||
|
||||
|
||||
it('should main tag', () => {
|
||||
component.operation.info.tags.should.be.empty();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/** Test component that contains a Operation. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<operation pointer='#/paths/~1user~1{username}/put'></operation>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,89 +0,0 @@
|
|||
'use strict';
|
||||
import { Input, HostBinding, Component, OnInit, ChangeDetectionStrategy, ElementRef } from '@angular/core';
|
||||
import JsonPointer from '../../utils/JsonPointer';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import { SchemaHelper } from '../../services/schema-helper.service';
|
||||
import { OptionsService, MenuService } from '../../services/';
|
||||
import { SwaggerBodyParameter } from '../../utils/swagger-typings';
|
||||
|
||||
export interface OperationInfo {
|
||||
deprecated: boolean;
|
||||
verb: string;
|
||||
path: string;
|
||||
info: {
|
||||
tags: string[];
|
||||
description: string;
|
||||
};
|
||||
bodyParam: any;
|
||||
summary: string;
|
||||
anchor: string;
|
||||
externalDocs?: {
|
||||
url: string;
|
||||
description?: string;
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'operation',
|
||||
templateUrl: './operation.html',
|
||||
styleUrls: ['./operation.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class Operation extends BaseComponent implements OnInit {
|
||||
@Input() pointer :string;
|
||||
@Input() parentTagId :string;
|
||||
|
||||
@HostBinding('attr.operation-id') operationId;
|
||||
|
||||
operation: OperationInfo;
|
||||
pathInMiddlePanel: boolean;
|
||||
|
||||
constructor(
|
||||
specMgr:SpecManager,
|
||||
private optionsService: OptionsService,
|
||||
private menu: MenuService) {
|
||||
super(specMgr);
|
||||
|
||||
this.pathInMiddlePanel = optionsService.options.pathInMiddlePanel;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.operationId = this.componentSchema.operationId;
|
||||
|
||||
this.operation = {
|
||||
deprecated: this.componentSchema.deprecated,
|
||||
verb: JsonPointer.baseName(this.pointer),
|
||||
path: JsonPointer.baseName(this.pointer, 2),
|
||||
info: {
|
||||
description: this.componentSchema.description,
|
||||
tags: this.filterMainTags(this.componentSchema.tags)
|
||||
},
|
||||
bodyParam: this.findBodyParam(),
|
||||
summary: SchemaHelper.operationSummary(this.componentSchema),
|
||||
anchor: this.buildAnchor(),
|
||||
externalDocs: this.componentSchema.externalDocs
|
||||
};
|
||||
}
|
||||
|
||||
buildAnchor():string {
|
||||
return this.menu.hashFor(this.pointer,
|
||||
{ type: 'operation', operationId: this.operationId, pointer: this.pointer },
|
||||
this.parentTagId );
|
||||
}
|
||||
|
||||
filterMainTags(tags) {
|
||||
var tagsMap = this.specMgr.getTagsMap();
|
||||
if (!tags) return [];
|
||||
return tags.filter(tag => tagsMap[tag] && tagsMap[tag]['x-traitTag']);
|
||||
}
|
||||
|
||||
findBodyParam():SwaggerBodyParameter {
|
||||
let params = this.specMgr.getOperationParams(this.pointer);
|
||||
let bodyParam = params.find(param => param.in === 'body');
|
||||
return bodyParam;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<div class="operations">
|
||||
<div class="tag" *ngFor="let tag of tags; trackBy:trackByTagName" [attr.section]="tag.id">
|
||||
<div class="tag-info" *ngIf="tag.name">
|
||||
<h1 class="sharable-header"> <a class="share-link" href="#{{tag.anchor}}"></a>{{tag.name}} </h1>
|
||||
<p *ngIf="tag.description" [innerHtml]="tag.description | marked"> </p>
|
||||
<redoc-externalDocs [docs]="tag.metadata.externalDocs"></redoc-externalDocs>
|
||||
</div>
|
||||
<operation *lazyFor="let operation of tag.items; let ready = ready;"
|
||||
[hidden]="!ready" [pointer]="operation.metadata.pointer"
|
||||
[parentTagId]="tag.id" [attr.section]="operation.id"></operation>
|
||||
</div>
|
||||
</div>
|
|
@ -1,37 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:host [hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tag-info {
|
||||
padding: $section-spacing;
|
||||
box-sizing: border-box;
|
||||
width: 60%;
|
||||
|
||||
@media (max-width: $right-panel-squash-breakpoint) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.tag-info:after, .tag-info:before {
|
||||
content: "";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.tag-info h1 {
|
||||
color: $headers-color;
|
||||
text-transform: capitalize;
|
||||
font-weight: normal;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.operations {
|
||||
display: block;
|
||||
position: relative;;
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
|
||||
import { OperationsList } from './operations-list';
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
describe('OperationsList Component', () => {
|
||||
let builder;
|
||||
let component;
|
||||
let fixture;
|
||||
let specMgr;
|
||||
|
||||
beforeEach(async(inject([SpecManager], (_specMgr) => {
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/operations-list-component.json').then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
component = getChildDebugElement(fixture.debugElement, 'operations-list').componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
it('should init component and component data', () => {
|
||||
expect(component).not.toBeNull();
|
||||
});
|
||||
|
||||
it('should build correct tags list', () => {
|
||||
expect(component.tags).not.toBeNull();
|
||||
component.tags.should.have.lengthOf(2);
|
||||
component.tags[0].name.should.be.equal('traitTag');
|
||||
should.not.exist(component.tags[0].items);
|
||||
component.tags[1].name.should.be.equal('tag1');
|
||||
component.tags[1].items.should.have.lengthOf(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<operations-list></operations-list>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import { MenuService } from '../../services/index';
|
||||
|
||||
@Component({
|
||||
selector: 'operations-list',
|
||||
templateUrl: './operations-list.html',
|
||||
styleUrls: ['./operations-list.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class OperationsList extends BaseComponent implements OnInit {
|
||||
@Input() pointer:string;
|
||||
|
||||
tags:Array<any> = [];
|
||||
|
||||
constructor(specMgr:SpecManager, private menu: MenuService) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
let flatMenuItems = this.menu.flatItems;
|
||||
this.tags = [];
|
||||
let emptyTag = {
|
||||
name: '',
|
||||
items: []
|
||||
};
|
||||
flatMenuItems.forEach(menuItem => {
|
||||
// skip items that are not bound to swagger tags/operations
|
||||
if (!menuItem.metadata) return;
|
||||
|
||||
if (menuItem.metadata.type === 'tag') {
|
||||
this.tags.push({
|
||||
...menuItem,
|
||||
anchor: this.buildAnchor(menuItem.id)
|
||||
});
|
||||
}
|
||||
if (menuItem.metadata.type === 'operation' && !menuItem.parent) {
|
||||
emptyTag.items.push(menuItem);
|
||||
}
|
||||
});
|
||||
if (emptyTag.items.length) this.tags.push(emptyTag);
|
||||
}
|
||||
|
||||
buildAnchor(tagId):string {
|
||||
return this.menu.hashFor(tagId,
|
||||
{ type: 'tag'});
|
||||
}
|
||||
|
||||
trackByTagName(_, el) {
|
||||
return el.name;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
<h5 class="param-list-header" *ngIf="params.length"> Parameters </h5>
|
||||
<ng-template ngFor [ngForOf]="params" let-paramType="$implicit">
|
||||
<header class="paramType">
|
||||
{{paramType.place}} Parameters
|
||||
<span class="hint--top-right hint--large" [attr.data-hint]="paramType.placeHint">?</span>
|
||||
</header>
|
||||
<div class="params-wrap">
|
||||
<div *ngFor="let param of paramType.params" class="param">
|
||||
<div class="param-name">
|
||||
<span class="param-name-wrap"> {{param.name}} </span>
|
||||
</div>
|
||||
<div class="param-info">
|
||||
<div>
|
||||
<span *ngIf='param.type === "array"'
|
||||
class="param-array-format param-collection-format-{{param.collectionFormat}}">
|
||||
{{param | collectionFormat}}
|
||||
</span>
|
||||
<span class="param-type {{param.type}}" [ngClass]="{'with-hint': param._displayTypeHint}"
|
||||
title="{{param._displayTypeHint}}"> {{param._displayType}} {{param._displayFormat}}</span>
|
||||
<span class="param-range" *ngIf="param._range"> {{param._range}} </span>
|
||||
<span *ngIf="param.required" class="param-required">Required</span>
|
||||
<div class="param-default" *ngIf="param.default != null">
|
||||
<span class="param-default-value">{{param.default | json}}</span>
|
||||
</div>
|
||||
<div class="param-example" *ngIf="param.example != null">
|
||||
<span class="param-example-value">{{param.example | json}}</span>
|
||||
</div>
|
||||
<div *ngIf="param.enum || param._enumItem" class="param-enum">
|
||||
<span *ngFor="let enumItem of param.enum" class="param-enum-value {{enumItem.type}}">
|
||||
{{enumItem.val | json}}
|
||||
</span>
|
||||
<span *ngIf="param._enumItem" class="param-enum-value {{param._enumItem.type}}">
|
||||
{{param._enumItem.val | json}}
|
||||
</span>
|
||||
</div>
|
||||
<span *ngIf="param.pattern" class="param-pattern">{{param.pattern}}</span>
|
||||
</div>
|
||||
<div class="param-description" [innerHtml]="param.description | marked"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<div *ngIf="bodyParam">
|
||||
<h5 class="param-list-header" *ngIf="bodyParam"> Request Body </h5>
|
||||
|
||||
<div class="body-param-description" [innerHtml]="bodyParam.description | marked"></div>
|
||||
<div>
|
||||
<br>
|
||||
<json-schema-lazy [isRequestSchema]="true" [auto]="true" pointer="{{bodyParam._pointer}}/schema">
|
||||
</json-schema-lazy>
|
||||
</div>
|
||||
</div>
|
|
@ -1,99 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
$hint-color: #999999;
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.param-list-header {
|
||||
border-bottom: 1px solid rgba($text-color, .3);
|
||||
// padding: 0.2em 0;
|
||||
margin: 3em 0 1em 0;
|
||||
color: rgba($text-color, .5);
|
||||
font-weight: normal;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
@import '../JsonSchema/json-schema-common';
|
||||
|
||||
header.paramType {
|
||||
margin: 25px 0 5px 0;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.param-array-format {
|
||||
color: black;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
// paramters can't be multilevel so table representation works for it without javascript
|
||||
.params-wrap {
|
||||
display: table;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.param-name {
|
||||
display: table-cell;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.param-info {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.param {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.param:last-of-type > .param-name {
|
||||
border-left: 0;
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
border-left: $line-border;
|
||||
height: ($param-name-height/2) + $cell-padding + $lines-width;
|
||||
background-color: white;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.param:first-of-type .param-name:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: -$lines-width;
|
||||
border-left: $line-border-erase;
|
||||
height: ($param-name-height/2) + $cell-padding;
|
||||
background-color: white;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
[data-hint] {
|
||||
width: 1.2em;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
vertical-align: middle;
|
||||
color: $hint-color;
|
||||
line-height: 1.2;
|
||||
text-transform: none;
|
||||
cursor: help;
|
||||
border: 1px solid $hint-color;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
@media (max-width: 520px) {
|
||||
[data-hint] {
|
||||
float: right;
|
||||
}
|
||||
|
||||
[data-hint]:after {
|
||||
margin-left: 12px;
|
||||
transform: translateX(-100%) translateY(-8px);
|
||||
-moz-transform: translateX(-100%) translateY(-8px);
|
||||
-webkit-transform: translateX(-100%) translateY(-8px);
|
||||
}
|
||||
}
|
|
@ -1,88 +0,0 @@
|
|||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
|
||||
import { OptionsService } from '../../services/options.service';
|
||||
import { SchemaHelper } from '../../services/schema-helper.service';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
|
||||
function safePush(obj, prop, item) {
|
||||
if (!obj[prop]) obj[prop] = [];
|
||||
obj[prop].push(item);
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'params-list',
|
||||
templateUrl: './params-list.html',
|
||||
styleUrls: ['./params-list.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ParamsList extends BaseComponent implements OnInit {
|
||||
@Input() pointer: string;
|
||||
|
||||
params: Array<any>;
|
||||
empty: boolean;
|
||||
bodyParam: any;
|
||||
|
||||
constructor(specMgr: SpecManager, private options: OptionsService) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.params = [];
|
||||
let paramsList = this.specMgr.getOperationParams(this.pointer);
|
||||
|
||||
const igrnoredHeaders =
|
||||
this.specMgr.schema['x-ignoredHeaderParameters'] ||
|
||||
this.options.options.ignoredHeaderParameters ||
|
||||
[];
|
||||
|
||||
paramsList = paramsList
|
||||
.map(paramSchema => {
|
||||
let propPointer = paramSchema._pointer;
|
||||
if (paramSchema.in === 'body') return paramSchema;
|
||||
return SchemaHelper.preprocess(paramSchema, propPointer, this.pointer);
|
||||
})
|
||||
.filter(param => {
|
||||
return param.in !== 'header' || igrnoredHeaders.indexOf(param.name) < 0;
|
||||
});
|
||||
|
||||
let paramsMap = this.orderParams(paramsList);
|
||||
|
||||
if (paramsMap.body && paramsMap.body.length) {
|
||||
let bodyParam = paramsMap.body[0];
|
||||
this.bodyParam = bodyParam;
|
||||
paramsMap.body = undefined;
|
||||
}
|
||||
|
||||
this.empty = !(Object.keys(paramsMap).length || this.bodyParam);
|
||||
|
||||
let paramsPlaces = ['path', 'query', 'formData', 'header', 'body'];
|
||||
let placeHint = {
|
||||
path: `Used together with Path Templating, where the parameter value is actually part
|
||||
of the operation's URL. This does not include the host or base path of the API.
|
||||
For example, in /items/{itemId}, the path parameter is itemId`,
|
||||
query: `Parameters that are appended to the URL.
|
||||
For example, in /items?id=###, the query parameter is id`,
|
||||
formData: `Parameters that are submitted through a form.
|
||||
application/x-www-form-urlencoded, multipart/form-data or both are usually
|
||||
used as the content type of the request`,
|
||||
header: 'Custom headers that are expected as part of the request'
|
||||
};
|
||||
let params = [];
|
||||
paramsPlaces.forEach(place => {
|
||||
if (paramsMap[place] && paramsMap[place].length) {
|
||||
params.push({place: place, placeHint: placeHint[place], params: paramsMap[place]});
|
||||
}
|
||||
});
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
orderParams(params):any {
|
||||
let res = {};
|
||||
params.forEach((param) => safePush(res, param.in, param));
|
||||
return res;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
@import url('//fonts.googleapis.com/css?family=Roboto:300,400,700');
|
||||
@import url('//fonts.googleapis.com/css?family=Montserrat:400,700');
|
||||
|
||||
redoc.loading {
|
||||
position: relative;
|
||||
display: block;
|
||||
min-height: 350px;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
0% {
|
||||
transform: rotate(0deg); }
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
redoc.loading:before {
|
||||
font-family: Helvetica;
|
||||
content: "Loading";
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
padding-top: 40px;
|
||||
color: #0033a0;
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: white;
|
||||
z-index: 9999;
|
||||
opacity: 1;
|
||||
transition: all 0.6s ease-out;
|
||||
}
|
||||
|
||||
redoc.loading:after {
|
||||
z-index: 10000;
|
||||
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="512" height="512" viewBox="0 0 512 512"><g></g><path d="M275.682 147.999c0 10.864-8.837 19.661-19.682 19.661v0c-10.875 0-19.681-8.796-19.681-19.661v-96.635c0-10.885 8.806-19.661 19.681-19.661v0c10.844 0 19.682 8.776 19.682 19.661v96.635z" fill="#0033a0"/><path d="M275.682 460.615c0 10.865-8.837 19.682-19.682 19.682v0c-10.875 0-19.681-8.817-19.681-19.682v-96.604c0-10.885 8.806-19.681 19.681-19.681v0c10.844 0 19.682 8.796 19.682 19.682v96.604z" fill="#0033a0"/><path d="M147.978 236.339c10.885 0 19.681 8.755 19.681 19.641v0c0 10.885-8.796 19.702-19.681 19.702h-96.624c-10.864 0-19.661-8.817-19.661-19.702v0c0-10.885 8.796-19.641 19.661-19.641h96.624z" fill="#0033a0"/><path d="M460.615 236.339c10.865 0 19.682 8.755 19.682 19.641v0c0 10.885-8.817 19.702-19.682 19.702h-96.584c-10.885 0-19.722-8.817-19.722-19.702v0c0-10.885 8.837-19.641 19.722-19.641h96.584z" fill="#0033a0"/><path d="M193.546 165.703c7.69 7.66 7.68 20.142 0 27.822v0c-7.701 7.701-20.162 7.701-27.853 0.020l-68.311-68.322c-7.68-7.701-7.68-20.142 0-27.863v0c7.68-7.68 20.121-7.68 27.822 0l68.342 68.342z" fill="#0033a0"/><path d="M414.597 386.775c7.7 7.68 7.7 20.163 0.021 27.863v0c-7.7 7.659-20.142 7.659-27.843-0.062l-68.311-68.26c-7.68-7.7-7.68-20.204 0-27.863v0c7.68-7.7 20.163-7.7 27.842 0l68.291 68.322z" fill="#0033a0"/><path d="M165.694 318.464c7.69-7.7 20.153-7.7 27.853 0v0c7.68 7.659 7.69 20.163 0 27.863l-68.342 68.322c-7.67 7.659-20.142 7.659-27.822-0.062v0c-7.68-7.68-7.68-20.122 0-27.801l68.311-68.322z" fill="#0033a0"/><path d="M386.775 97.362c7.7-7.68 20.142-7.68 27.822 0v0c7.7 7.68 7.7 20.183 0.021 27.863l-68.322 68.311c-7.68 7.68-20.163 7.68-27.843-0.020v0c-7.68-7.68-7.68-20.162 0-27.822l68.322-68.332z" fill="#0033a0"/></svg>');
|
||||
animation: 2s rotate linear infinite;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
content: "";
|
||||
left: 50%;
|
||||
margin-left: -25px;
|
||||
background-size: cover;
|
||||
top: 75px;
|
||||
transition: all 0.6s ease-out;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
redoc.loading-remove:before, redoc.loading-remove:after {
|
||||
opacity: 0;
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<div class="redoc-error" *ngIf="error">
|
||||
<h1>Oops... ReDoc failed to render this spec</h1>
|
||||
<div class='redoc-error-details'>{{error.message}}</div>
|
||||
</div>
|
||||
<loading-bar *ngIf="options.lazyRendering" [progress]="loadingProgress"> </loading-bar>
|
||||
<div class="redoc-wrap" *ngIf="specLoaded && !error">
|
||||
<div class="background">
|
||||
<div class="background-actual"> </div>
|
||||
</div>
|
||||
<div class="menu-content" sticky-sidebar [disable]="specLoading"
|
||||
[scrollParent]="options.$scrollParent" [scrollYOffset]="options.scrollYOffset">
|
||||
<div class="menu-header">
|
||||
<api-logo> </api-logo>
|
||||
<redoc-search> </redoc-search>
|
||||
</div>
|
||||
<side-menu> </side-menu>
|
||||
</div>
|
||||
<div class="api-content">
|
||||
<warnings></warnings>
|
||||
<api-info></api-info>
|
||||
<operations-list> </operations-list>
|
||||
<footer>
|
||||
<div class="powered-by-badge">
|
||||
<a href="https://github.com/Rebilly/ReDoc" title="Swagger-generated API Reference Documentation" target="_blank">
|
||||
Powered by <strong>ReDoc</strong>
|
||||
</a>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
|
@ -1,362 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-moz-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-ms-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-o-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
font-smoothing: antialiased;
|
||||
-webkit-osx-font-smoothing: grayscale;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
osx-font-smoothing: grayscale;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-moz-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
-webkit-text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
|
||||
-ms-text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.004);
|
||||
text-rendering: optimizeSpeed !important;
|
||||
font-smooth: always;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
.redoc-wrap {
|
||||
z-index: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: $base-font, $base-font-family;
|
||||
font-size: $em-size;
|
||||
line-height: $base-line-height;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
side-menu {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[sticky-sidebar] {
|
||||
width: $side-bar-width;
|
||||
background-color: $side-bar-bg-color;
|
||||
overflow-x: hidden;
|
||||
transform: translateZ(0);
|
||||
z-index: 75;
|
||||
|
||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||
width: 100%;
|
||||
bottom: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
.api-content {
|
||||
margin-left: $side-bar-width;
|
||||
z-index: 50;
|
||||
position: relative;
|
||||
// height: 100vh;
|
||||
// overflow: scroll;
|
||||
top: 0;
|
||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||
padding-top: 3em;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.background {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: $side-bar-width;
|
||||
z-index: 1;
|
||||
|
||||
&-actual {
|
||||
background: $samples-panel-bg-color;
|
||||
left: 100% - $samples-panel-width;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
@media (max-width: $right-panel-squash-breakpoint) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.redoc-error {
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: $red;
|
||||
> h2 {
|
||||
color: $red;
|
||||
font-size: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.redoc-error-details {
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
/* global menu items styles (search results + menu) */
|
||||
:host /deep/ {
|
||||
.menu-item-header > span {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.menu-item-header > .operation-type + .menu-item-title {
|
||||
width: calc(100% - 32px); // 32 = 26px image width + 6px margin left
|
||||
}
|
||||
|
||||
.menu-item-header > .operation-type {
|
||||
width: 26px;
|
||||
display: inline-block;
|
||||
height: 13px;
|
||||
background-color: #333;
|
||||
border-radius: 3px;
|
||||
vertical-align: top;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAACgCAMAAADZ0KclAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAZQTFRF////////VXz1bAAAAAJ0Uk5T/wDltzBKAAAA80lEQVR42uSWSwLCIAxEX+5/aa2QZBJw5UIt9QMdRqSPEAAw/TyvqzZf150NzdXL49qreXwXjeqz9bqN1tgJl/KLyaVrrL7K7gx+1vlNMqy+helOO4rfBGYZiEkq1ubQ3DeKvc97Et+d+e01vIZlLZZqb1WNJFd8ZKYsmv4Hh3H2fDgjMUI5WSExjiEZs7rEZ5T+/jQn9lhgsw53j/e9MQtxqPsbZY54M5fNl/MY/f1s7NbRSkYlYjc0KPsWMrmhIU9933ywxDiSE+upYNH8TdusUotllNvcAUzfnE/NC4OSYyklQhpdl9E4Tw0Cm4/G9xBgAO7VCkjWLOMfAAAAAElFTkSuQmCC');
|
||||
background-repeat: no-repeat;
|
||||
background-position: 6px 4px;
|
||||
text-indent: -9000px;
|
||||
margin-right: 6px;
|
||||
margin-top: 2px;
|
||||
|
||||
&.get {
|
||||
background-position: 8px -12px;
|
||||
background-color: $get-color;
|
||||
}
|
||||
|
||||
&.post {
|
||||
background-position: 6px 4px;
|
||||
background-color: $post-color;
|
||||
}
|
||||
|
||||
&.put {
|
||||
background-position: 8px -28px;
|
||||
background-color: $put-color;
|
||||
}
|
||||
|
||||
&.options {
|
||||
background-position: 4px -148px;
|
||||
background-color: $options-color;
|
||||
}
|
||||
|
||||
&.patch {
|
||||
background-position: 4px -114px;
|
||||
background-color: $patch-color;
|
||||
}
|
||||
|
||||
&.delete {
|
||||
background-position: 4px -44px;
|
||||
background-color: $delete-color;
|
||||
}
|
||||
|
||||
&.basic {
|
||||
background-position: 5px -79px;
|
||||
background-color: $basic-color;
|
||||
}
|
||||
|
||||
&.link {
|
||||
background-position: 4px -131px;
|
||||
background-color: $link-color;
|
||||
}
|
||||
|
||||
&.head {
|
||||
background-position: 6px -102px;
|
||||
background-color: $head-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* global redoc styles */
|
||||
|
||||
@for $index from 1 through 5 {
|
||||
:host /deep/ h#{$index} {
|
||||
margin-top: 0;
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
color: $secondary-color;
|
||||
font-weight: $headers-font-weight;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
:host /deep/ {
|
||||
h1 {
|
||||
font-size: $h1;
|
||||
color: $headers-color;
|
||||
}
|
||||
h2 {
|
||||
font-size: $h2;
|
||||
}
|
||||
h3 {
|
||||
font-size: $h3;
|
||||
}
|
||||
h4 {
|
||||
font-size: $h4;
|
||||
}
|
||||
h5 {
|
||||
font-size: $h5;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: $base-font, $base-font-family;
|
||||
font-weight: $base-font-weight;
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
line-height: $base-line-height;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: $primary-color;
|
||||
}
|
||||
|
||||
p > code {
|
||||
color: $red;
|
||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||
}
|
||||
|
||||
.hint--inversed {
|
||||
&:before {
|
||||
border-top-color: #fff;
|
||||
}
|
||||
|
||||
&:after {
|
||||
background: #fff;
|
||||
color: #383838;
|
||||
}
|
||||
}
|
||||
|
||||
@import '../../shared/styles/share-link';
|
||||
}
|
||||
|
||||
footer {
|
||||
position: relative;
|
||||
text-align: right;
|
||||
padding: 10px $section-spacing;
|
||||
font-size: 15px;
|
||||
margin-top: -35px;
|
||||
color: white;
|
||||
a {
|
||||
color: white;
|
||||
}
|
||||
strong {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
/* markdown elements */
|
||||
|
||||
:host /deep/ .redoc-markdown-block {
|
||||
pre {
|
||||
font-family: Courier, monospace;
|
||||
white-space: pre-wrap;
|
||||
background-color: #263238;
|
||||
color: white;
|
||||
padding: 12px 14px 15px 14px;
|
||||
overflow-x: auto;
|
||||
line-height: normal;
|
||||
border-radius: $border-radius;
|
||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||
|
||||
code {
|
||||
background-color: transparent;
|
||||
color: white;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: Courier, monospace;
|
||||
background-color: rgba(38, 50, 56, 0.04);
|
||||
padding: 0.1em 0.2em 0.2em;
|
||||
font-size: 1em;
|
||||
border-radius: $border-radius;
|
||||
color: $red;
|
||||
border: 1px solid rgba(38, 50, 56, 0.1);
|
||||
}
|
||||
|
||||
p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
padding: 0 15px;
|
||||
color: #777;
|
||||
border-left: 4px solid #ddd;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
padding-left: 2em;
|
||||
margin: 0;
|
||||
margin-bottom: 1em;
|
||||
font-family: $base-font, $base-font-family;
|
||||
font-weight: $base-font-weight;
|
||||
line-height: $base-line-height;
|
||||
> li {
|
||||
margin: 1em 0;
|
||||
}
|
||||
}
|
||||
|
||||
table {
|
||||
display: block;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
word-break: normal;
|
||||
word-break: keep-all;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
margin-top: 0.5em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
table tr {
|
||||
background-color: #fff;
|
||||
border-top: 1px solid #ccc;
|
||||
|
||||
&:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
}
|
||||
|
||||
table th,
|
||||
table td {
|
||||
padding: 6px 13px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import {
|
||||
inject,
|
||||
async
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Redoc } from './redoc';
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
import { OptionsService } from '../../services/index';
|
||||
|
||||
let optsMgr:OptionsService;
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent ] });
|
||||
});
|
||||
describe('Redoc Component', () => {
|
||||
let builder;
|
||||
let specMgr;
|
||||
|
||||
beforeEach(async(inject([SpecManager, OptionsService],
|
||||
( _specMgr, _optsMgr) => {
|
||||
optsMgr = _optsMgr;
|
||||
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail);
|
||||
})
|
||||
|
||||
it('should init component', () => {
|
||||
let fixture = TestBed.createComponent(TestAppComponent);
|
||||
let component = getChildDebugElement(fixture.debugElement, 'redoc').componentInstance;
|
||||
expect(component).not.toBeNull();
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should init components tree without errors', () => {
|
||||
let fixture = TestBed.createComponent(TestAppComponent);
|
||||
(() => fixture.detectChanges()).should.not.throw();
|
||||
fixture.destroy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/** Test component that contains a Redoc. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<redoc disable-lazy-schemas></redoc>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { ElementRef,
|
||||
ChangeDetectorRef,
|
||||
Input,
|
||||
Component,
|
||||
OnInit,
|
||||
OnDestroy,
|
||||
HostBinding
|
||||
} from '@angular/core';
|
||||
|
||||
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
|
||||
import { BaseComponent } from '../base';
|
||||
|
||||
import * as detectScollParent from 'scrollparent';
|
||||
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
import {
|
||||
SearchService,
|
||||
OptionsService,
|
||||
Options,
|
||||
Hash,
|
||||
AppStateService,
|
||||
SchemaHelper,
|
||||
MenuService,
|
||||
Marker
|
||||
} from '../../services/';
|
||||
import { LazyTasksService } from '../../shared/components/LazyFor/lazy-for';
|
||||
|
||||
function getPreOptions() {
|
||||
return Redoc._preOptions || {};
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redoc',
|
||||
templateUrl: './redoc.html',
|
||||
styleUrls: ['./redoc.css'],
|
||||
providers: [
|
||||
SpecManager,
|
||||
MenuService,
|
||||
SearchService,
|
||||
LazyTasksService,
|
||||
Marker
|
||||
]
|
||||
//changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class Redoc extends BaseComponent implements OnInit {
|
||||
static _preOptions: any = {};
|
||||
|
||||
error: any;
|
||||
specLoaded: boolean;
|
||||
options: Options;
|
||||
|
||||
loadingProgress: number;
|
||||
|
||||
@Input() specUrl: string;
|
||||
@HostBinding('class.loading') specLoading: boolean = false;
|
||||
@HostBinding('class.loading-remove') specLoadingRemove: boolean = false;
|
||||
|
||||
private element: HTMLElement;
|
||||
private $parent: Element;
|
||||
private $refElem: Element;
|
||||
|
||||
constructor(
|
||||
specMgr: SpecManager,
|
||||
optionsMgr: OptionsService,
|
||||
elementRef: ElementRef,
|
||||
private changeDetector: ChangeDetectorRef,
|
||||
private appState: AppStateService,
|
||||
private lazyTasksService: LazyTasksService,
|
||||
private hash: Hash
|
||||
) {
|
||||
super(specMgr);
|
||||
SchemaHelper.setSpecManager(specMgr);
|
||||
// merge options passed before init
|
||||
optionsMgr.options = getPreOptions();
|
||||
|
||||
this.element = elementRef.nativeElement;
|
||||
this.$parent = this.element.parentElement;
|
||||
this.$refElem = this.element.nextElementSibling;
|
||||
|
||||
//parse options (top level component doesn't support inputs)
|
||||
optionsMgr.parseOptions( this.element );
|
||||
let scrollParent = detectScollParent( this.element );
|
||||
if (scrollParent === (document.scrollingElement || document.documentElement)) scrollParent = window;
|
||||
optionsMgr.options.$scrollParent = scrollParent;
|
||||
this.options = optionsMgr.options;
|
||||
this.lazyTasksService.allSync = !this.options.lazyRendering;
|
||||
}
|
||||
|
||||
hideLoadingAnimation() {
|
||||
if (this.options.hideLoading) {
|
||||
return
|
||||
}
|
||||
requestAnimationFrame(() => {
|
||||
this.specLoadingRemove = true;
|
||||
setTimeout(() => {
|
||||
this.specLoadingRemove = false;
|
||||
this.specLoading = false;
|
||||
}, 400);
|
||||
});
|
||||
}
|
||||
|
||||
showLoadingAnimation() {
|
||||
if (this.options.hideLoading) {
|
||||
return
|
||||
}
|
||||
this.specLoading = true;
|
||||
this.specLoadingRemove = false;
|
||||
}
|
||||
|
||||
load() {
|
||||
// bunlde spec directly if passsed or load by URL
|
||||
this.specMgr.load(this.options.spec || this.options.specUrl).catch(err => {
|
||||
throw err;
|
||||
});
|
||||
|
||||
this.appState.loading.subscribe(loading => {
|
||||
if (loading) {
|
||||
this.showLoadingAnimation();
|
||||
} else {
|
||||
this.hideLoadingAnimation();
|
||||
}
|
||||
});
|
||||
|
||||
this.specMgr.spec.subscribe((spec) => {
|
||||
if (!spec) {
|
||||
this.appState.startLoading();
|
||||
} else {
|
||||
this.specLoaded = true;
|
||||
this.changeDetector.markForCheck();
|
||||
this.changeDetector.detectChanges();
|
||||
setTimeout(() => {
|
||||
this.hash.start();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.lazyTasksService.loadProgress.subscribe(progress => this.loadingProgress = progress)
|
||||
this.appState.error.subscribe(_err => {
|
||||
if (!_err) return;
|
||||
|
||||
this.appState.stopLoading();
|
||||
|
||||
if (this.loadingProgress === 100) return;
|
||||
this.error = _err;
|
||||
this.changeDetector.markForCheck();
|
||||
});
|
||||
|
||||
if (this.specUrl) {
|
||||
this.options.specUrl = this.specUrl;
|
||||
}
|
||||
this.load();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
let $clone = this.element.cloneNode();
|
||||
this.$parent.insertBefore($clone, this.$refElem);
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<header *ngIf="schemaPointer || samples.length"> Request samples </header>
|
||||
<schema-sample *ngIf="schemaPointer && !samples.length" [skipReadOnly]="true" [pointer]="schemaPointer"> </schema-sample>
|
||||
<tabs *ngIf="samples.length" [selected] = "selectedLang" (change)=changeLangNotify($event)>
|
||||
<tab *ngIf="schemaPointer" tabTitle="JSON">
|
||||
<schema-sample [pointer]="schemaPointer" [skipReadOnly]="true"> </schema-sample>
|
||||
</tab>
|
||||
<tab *ngFor="let sample of samples" [tabTitle]="sample.lang">
|
||||
<div class="code-sample">
|
||||
<div class="action-buttons">
|
||||
<span copy-button [copyText]="sample.source" class="hint--top-left hint--inversed"><a>Copy</a></span>
|
||||
</div>
|
||||
<pre [innerHtml]="sample.source | prism:sample.lang"></pre>
|
||||
</div>
|
||||
</tab>
|
||||
</tabs>
|
|
@ -1,81 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
transform: translateY(100%);
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
margin-top: -1em;
|
||||
|
||||
> span > a {
|
||||
padding: 2px 10px;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($black, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.code-sample:hover > .action-buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
header {
|
||||
font-family: $headers-font;
|
||||
font-size: $h5;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
color: $sample-panel-headers-color;
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
:host /deep/ > tabs > ul li {
|
||||
font-family: $headers-font;
|
||||
font-size: .9em;
|
||||
border-radius: $border-radius;
|
||||
margin: 2px 0;
|
||||
padding: 3px 10px 2px 10px;
|
||||
line-height: 16px;
|
||||
color: $sample-panel-headers-color;
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(white, .1);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #ffffff;
|
||||
color: $text-color;
|
||||
}
|
||||
}
|
||||
|
||||
:host /deep/ tabs ul {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.code-sample pre {
|
||||
overflow-x: auto;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
margin-top: 0;
|
||||
overflow-x: auto;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: #222d32;
|
||||
margin-bottom: 36px;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component, ViewChildren, QueryList, Input,
|
||||
ChangeDetectionStrategy, OnInit, HostBinding, ElementRef, NgZone } from '@angular/core';
|
||||
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import JsonPointer from '../../utils/JsonPointer';
|
||||
import { Tabs } from '../../shared/components/index';
|
||||
import { AppStateService, ScrollService } from '../../services/index';
|
||||
|
||||
@Component({
|
||||
selector: 'request-samples',
|
||||
templateUrl: './request-samples.html',
|
||||
styleUrls: ['./request-samples.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RequestSamples extends BaseComponent implements OnInit {
|
||||
@Input() pointer:string;
|
||||
@Input() schemaPointer:string;
|
||||
@ViewChildren(Tabs) childQuery:QueryList<Tabs>;
|
||||
@HostBinding('attr.hidden') hidden;
|
||||
|
||||
childTabs: Tabs;
|
||||
selectedLang: Subject<any>;
|
||||
samples: Array<any>;
|
||||
|
||||
constructor(
|
||||
specMgr:SpecManager,
|
||||
public appState:AppStateService,
|
||||
private scrollService: ScrollService,
|
||||
private el: ElementRef,
|
||||
private zone: NgZone
|
||||
) {
|
||||
super(specMgr);
|
||||
|
||||
this.selectedLang = this.appState.samplesLanguage;
|
||||
}
|
||||
|
||||
changeLangNotify(lang) {
|
||||
let relativeScrollPos = this.scrollService.relativeScrollPos(this.el.nativeElement);
|
||||
this.selectedLang.next(lang);
|
||||
// do scroll in the end of VM turn to have it seamless
|
||||
let subscription = this.zone.onMicrotaskEmpty.subscribe(() => {
|
||||
this.scrollService.scrollTo(this.el.nativeElement, relativeScrollPos);
|
||||
subscription.unsubscribe();
|
||||
});
|
||||
}
|
||||
|
||||
init() {
|
||||
this.schemaPointer = this.schemaPointer ? JsonPointer.join(this.schemaPointer, 'schema') : null;
|
||||
this.samples = this.componentSchema['x-code-samples'] || [];
|
||||
if (!this.schemaPointer && !this.samples.length) this.hidden = true;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
<h2 class="responses-list-header" *ngIf="responses.length"> Responses </h2>
|
||||
<zippy *ngFor="let response of responses;trackBy:trackByCode" [title]="response.code + ' ' + response.description | marked"
|
||||
[type]="response.type" [(open)]="response.expanded" [empty]="response.empty" (openChange)="lazySchema.load()">
|
||||
<div *ngIf="response.headers" class="response-headers">
|
||||
<header>
|
||||
Headers
|
||||
</header>
|
||||
<div class="header" *ngFor="let header of response.headers">
|
||||
<div class="header-name"> {{header.name}} </div>
|
||||
<div class="header-type {{header.type}}"> {{header._displayType}} {{header._displayFormat}}
|
||||
<span class="header-range" *ngIf="header._range"> {{header._range}} </span>
|
||||
</div>
|
||||
<div *ngIf="header.default" class="header-default"> Default: {{header.default}} </div>
|
||||
<div *ngIf="header.enum" class="header-enum">
|
||||
<span *ngFor="let enumItem of header.enum" class="enum-value {{enumItem.type}}"> {{enumItem.val | json}} </span>
|
||||
</div>
|
||||
<div class="header-description" [innerHtml]="header.description | marked"> </div>
|
||||
</div>
|
||||
</div>
|
||||
<header *ngIf="response.schema">
|
||||
Response Schema
|
||||
</header>
|
||||
<json-schema-lazy [auto]="response.expanded" #lazySchema
|
||||
pointer="{{response.schema ? response.pointer + '/schema' : null}}">
|
||||
</json-schema-lazy>
|
||||
</zippy>
|
|
@ -1,61 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.responses-list-header {
|
||||
font-size: 18px;
|
||||
padding: 0.2em 0;
|
||||
margin: 3em 0 1.1em;
|
||||
color: #253137;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
:host .zippy-title {
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
}
|
||||
|
||||
.header-name {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.header-type {
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
header {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 15px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.header-range {
|
||||
position: relative;
|
||||
top: 1px;
|
||||
margin-right: 6px;
|
||||
margin-left: 6px;
|
||||
border-radius: $border-radius;
|
||||
background-color: rgba($primary-color, 0.1);
|
||||
padding: 0 4px;
|
||||
color: rgba($primary-color, 0.7);
|
||||
}
|
||||
|
||||
.header-type.array::before {
|
||||
content: $array-text;
|
||||
color: $black;
|
||||
font-weight: $base-font-weight;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
import {
|
||||
inject,
|
||||
async,
|
||||
TestBed,
|
||||
ComponentFixture
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
|
||||
|
||||
import { ResponsesList } from './responses-list';
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
|
||||
describe('ResponsesList Component', () => {
|
||||
let builder;
|
||||
let component: ResponsesList;
|
||||
let fixture: ComponentFixture<ResponsesList>
|
||||
let specMgr;
|
||||
|
||||
beforeEach(async(inject([SpecManager], (_specMgr) => {
|
||||
specMgr = _specMgr;
|
||||
})));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/responses-list-component.json').then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ResponsesList);
|
||||
component = fixture.componentInstance;
|
||||
});
|
||||
|
||||
it('should instantiate without errors', () => {
|
||||
should.exist(component);
|
||||
});
|
||||
|
||||
it('should init repsonses list', () => {
|
||||
component.pointer = '#/paths/~1test1/get/responses';
|
||||
fixture.detectChanges();
|
||||
should.exist(component.responses);
|
||||
component.responses.should.be.lengthOf(2);
|
||||
});
|
||||
|
||||
it('should not overwrite codes for shared schemas', () => {
|
||||
component.pointer = '#/paths/~1test1/get/responses';
|
||||
fixture.detectChanges();
|
||||
let resp1 = component.responses[0];
|
||||
let resp2 = component.responses[1];
|
||||
resp1.code.should.not.be.equal(resp2.code);
|
||||
});
|
||||
|
||||
it('should set type of default as error if other 200-399 response is defined', () => {
|
||||
component.pointer = '#/paths/~1test2/get/responses';
|
||||
fixture.detectChanges();
|
||||
let resp1 = component.responses[1];
|
||||
resp1.type.should.be.equal('error');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,108 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component,
|
||||
Input,
|
||||
OnInit,
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
ChangeDetectorRef
|
||||
} from '@angular/core';
|
||||
import { BaseSearchableComponent, SpecManager } from '../base';
|
||||
import JsonPointer from '../../utils/JsonPointer';
|
||||
import { statusCodeType } from '../../utils/helpers';
|
||||
import { OptionsService, AppStateService } from '../../services/index';
|
||||
import { SchemaHelper } from '../../services/schema-helper.service';
|
||||
|
||||
function isNumeric(n) {
|
||||
return (!isNaN(parseFloat(n)) && isFinite(n));
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'responses-list',
|
||||
templateUrl: './responses-list.html',
|
||||
styleUrls: ['./responses-list.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ResponsesList extends BaseSearchableComponent implements OnInit {
|
||||
@Input() pointer:string;
|
||||
|
||||
responses: Array<any>;
|
||||
options: any;
|
||||
|
||||
constructor(specMgr:SpecManager,
|
||||
optionsMgr:OptionsService,
|
||||
app: AppStateService,
|
||||
private cdr: ChangeDetectorRef
|
||||
) {
|
||||
super(specMgr, app);
|
||||
this.options = optionsMgr.options;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.responses = [];
|
||||
|
||||
let responses = this.componentSchema;
|
||||
if (!responses) return;
|
||||
|
||||
let hasSuccessResponses = false;
|
||||
let respCodes = Object.keys(responses).filter(respCode => {
|
||||
if ((parseInt(respCode) >= 100) && (parseInt(respCode) <=399)) {
|
||||
hasSuccessResponses = true;
|
||||
}
|
||||
// only response-codes and "default"
|
||||
return ( isNumeric(respCode) || (respCode === 'default'));
|
||||
});
|
||||
|
||||
responses = respCodes.map(respCode => {
|
||||
let resp = responses[respCode];
|
||||
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
||||
if (resp.$ref) {
|
||||
let ref = resp.$ref;
|
||||
resp = Object.assign({}, this.specMgr.byPointer(resp.$ref));
|
||||
resp.pointer = ref;
|
||||
}
|
||||
|
||||
resp.empty = !resp.schema;
|
||||
resp.code = respCode;
|
||||
resp.type = statusCodeType(resp.code, hasSuccessResponses);
|
||||
|
||||
resp.expanded = false;
|
||||
if (this.options.expandResponses) {
|
||||
if (this.options.expandResponses === 'all' || this.options.expandResponses.has(respCode.toString())) {
|
||||
resp.expanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (resp.headers && !(resp.headers instanceof Array)) {
|
||||
resp.headers = Object.keys(resp.headers).map((k) => {
|
||||
let respInfo = resp.headers[k];
|
||||
respInfo.name = k;
|
||||
return SchemaHelper.preprocess(respInfo, this.pointer, this.pointer);
|
||||
});
|
||||
resp.empty = false;
|
||||
}
|
||||
resp.extendable = resp.headers || resp.length;
|
||||
return resp;
|
||||
});
|
||||
this.responses = responses;
|
||||
}
|
||||
|
||||
trackByCode(_, el) {
|
||||
return el.code;
|
||||
}
|
||||
|
||||
ensureSearchIsShown(ptr: string) {
|
||||
if (ptr.startsWith(this.pointer)) {
|
||||
let code = JsonPointer.relative(this.pointer, ptr)[0];
|
||||
if (code && this.componentSchema[code]) {
|
||||
this.componentSchema[code].expanded = true;
|
||||
this.cdr.markForCheck();
|
||||
this.cdr.detectChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<header *ngIf="data.responses.length"> Response samples </header>
|
||||
<tabs *ngIf="data.responses.length">
|
||||
<tab *ngFor="let response of data.responses" [tabTitle]="response.code + ' ' + response.description | marked"
|
||||
[tabStatus]="response.type">
|
||||
<schema-sample [pointer]="response.pointer"></schema-sample>
|
||||
</tab>
|
||||
</tabs>
|
|
@ -1,40 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
}
|
||||
|
||||
header {
|
||||
font-family: $headers-font;
|
||||
font-size: 0.929em;
|
||||
text-transform: uppercase;
|
||||
margin: 0;
|
||||
color: $sample-panel-headers-color;
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
:host /deep/ > tabs > ul li {
|
||||
font-family: $headers-font;
|
||||
font-size: 0.929em;
|
||||
border-radius: $border-radius;
|
||||
margin: 2px 0;
|
||||
padding: 2px 8px 3px 8px;
|
||||
color: $sample-panel-headers-color;
|
||||
line-height: 16px;
|
||||
|
||||
&:hover {
|
||||
color: #ffffff;
|
||||
background-color: rgba(white, .1);
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: white;
|
||||
color: $black;
|
||||
}
|
||||
}
|
||||
|
||||
:host /deep/ tabs ul {
|
||||
padding-top: 10px;
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component, Input, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import JsonPointer from '../../utils/JsonPointer';
|
||||
import { statusCodeType, getJsonLikeSample, getXmlLikeSample } from '../../utils/helpers';
|
||||
|
||||
|
||||
function isNumeric(n) {
|
||||
return (!isNaN(parseFloat(n)) && isFinite(n));
|
||||
}
|
||||
|
||||
function hasExample(response) {
|
||||
return response.schema || getXmlLikeSample(response.examples) || getJsonLikeSample(response.examples)
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'responses-samples',
|
||||
templateUrl: './responses-samples.html',
|
||||
styleUrls: ['./responses-samples.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class ResponsesSamples extends BaseComponent implements OnInit {
|
||||
@Input() pointer:string;
|
||||
|
||||
data: any;
|
||||
|
||||
constructor(specMgr:SpecManager) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.data = {};
|
||||
this.data.responses = [];
|
||||
|
||||
let responses = this.componentSchema;
|
||||
if (!responses) return;
|
||||
|
||||
let hasSuccessResponses = false;
|
||||
responses = Object.keys(responses).filter(respCode => {
|
||||
if ((parseInt(respCode) >= 100) && (parseInt(respCode) <=399)) {
|
||||
hasSuccessResponses = true;
|
||||
}
|
||||
// only response-codes and "default"
|
||||
return ( isNumeric(respCode) || (respCode === 'default'));
|
||||
}).map(respCode => {
|
||||
let resp = responses[respCode];
|
||||
resp.pointer = JsonPointer.join(this.pointer, respCode);
|
||||
if (resp.$ref) {
|
||||
let ref = resp.$ref;
|
||||
resp = this.specMgr.byPointer(resp.$ref);
|
||||
resp.pointer = ref;
|
||||
}
|
||||
|
||||
resp.code = respCode;
|
||||
resp.type = statusCodeType(resp.code, hasSuccessResponses);
|
||||
return resp;
|
||||
})
|
||||
.filter(response => hasExample(response));
|
||||
this.data.responses = responses;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
<ng-template #jsonSnippet>
|
||||
<div class="snippet">
|
||||
<!-- in case sample is not available for some reason -->
|
||||
<pre *ngIf="sample == undefined"> Sample unavailable </pre>
|
||||
<div class="action-buttons">
|
||||
<span copy-button [copyText]="sample" class="hint--top-left hint--inversed"> <a>Copy</a> </span>
|
||||
<span> <a *ngIf="enableButtons" (click)="expandAll()">Expand all</a> </span>
|
||||
<span> <a *ngIf="enableButtons" (click)="collapseAll()">Collapse all</a> </span>
|
||||
</div>
|
||||
<pre [innerHtml]="sample | jsonFormatter"></pre>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<tabs *ngIf="xmlSample; else jsonSnippet">
|
||||
<tab tabTitle="JSON" *ngIf="sample">
|
||||
<ng-container *ngTemplateOutlet="jsonSnippet"></ng-container>
|
||||
</tab>
|
||||
<tab tabTitle="XML" *ngIf="xmlSample">
|
||||
<div class="snippet">
|
||||
<div class="action-buttons">
|
||||
<span copy-button [copyText]="xmlSample" class="hint--top-left hint--inversed"> <a>Copy</a> </span>
|
||||
</div>
|
||||
<pre class="response-sample" [innerHtml]="xmlSample | prism:'xml'"></pre>
|
||||
</div>
|
||||
</tab>
|
||||
<tab tabTitle="text/plain" *ngIf="textSample">
|
||||
<div class="snippet">
|
||||
<div class="action-buttons">
|
||||
<span copy-button [copyText]="xmlSample" class="hint--top-left hint--inversed"> <a>Copy</a> </span>
|
||||
</div>
|
||||
<pre class="response-sample">{{textSample}}</pre>
|
||||
</div>
|
||||
</tab>
|
||||
</tabs>
|
|
@ -1,195 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
/* tabs */
|
||||
|
||||
:host /deep/ tabs {
|
||||
margin-top: 1em;
|
||||
> ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
padding: 2px 10px;
|
||||
display: inline-block;
|
||||
background: #131a1d;
|
||||
border-bottom: 1px solid trasparent;
|
||||
color: $sample-panel-headers-color;
|
||||
|
||||
&.active {
|
||||
color: white;
|
||||
border-bottom: 1px solid $sample-panel-headers-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
margin-top: -2em;
|
||||
}
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
clear: both;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
transform: translateY(100%);
|
||||
z-index: 3;
|
||||
position: relative;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
margin-top: -1em;
|
||||
|
||||
> span > a {
|
||||
padding: 2px 10px;
|
||||
color: #ffffff;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: lighten($black, 15%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.snippet:hover .action-buttons {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
:host /deep/ {
|
||||
.property {
|
||||
//font-weight: bold;
|
||||
}
|
||||
|
||||
.type-null {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.type-boolean {
|
||||
color: firebrick;
|
||||
}
|
||||
|
||||
.type-number {
|
||||
color: #4A8BB3;
|
||||
}
|
||||
|
||||
.type-string {
|
||||
color: #66B16E;
|
||||
& + a {
|
||||
color: #66B16E;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.callback-function {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.collapser:after {
|
||||
content: "-";
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapsed > .collapser:after {
|
||||
content: "+";
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ellipsis:after {
|
||||
content: " … ";
|
||||
}
|
||||
|
||||
.collapsible {
|
||||
margin-left: 2em;
|
||||
}
|
||||
|
||||
.hoverable {
|
||||
padding-top: 1px;
|
||||
padding-bottom: 1px;
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.hovered {
|
||||
background-color: rgba(235, 238, 249, 1);
|
||||
}
|
||||
|
||||
.collapser {
|
||||
padding-right: 6px;
|
||||
padding-left: 6px;
|
||||
}
|
||||
|
||||
.redoc-json, .response-sample {
|
||||
overflow-x: auto;
|
||||
padding: 20px;
|
||||
border-radius: $border-radius*2;
|
||||
background-color: darken($black, 2%);
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
ul, .redoc-json ul {
|
||||
list-style-type: none;
|
||||
padding: 0px;
|
||||
margin: 0px 0px 0px 26px;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.hoverable {
|
||||
transition: background-color .2s ease-out 0s;
|
||||
-webkit-transition: background-color .2s ease-out 0s;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.hovered {
|
||||
transition-delay: .2s;
|
||||
-webkit-transition-delay: .2s;
|
||||
}
|
||||
|
||||
.selected {
|
||||
outline-style: solid;
|
||||
outline-width: 1px;
|
||||
outline-style: dotted;
|
||||
}
|
||||
|
||||
.collapsed>.collapsible {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ellipsis {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.collapsed > .ellipsis {
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.collapser {
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: -1.5em;
|
||||
cursor: default;
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
// hide top-level collapser
|
||||
.redoc-json > .collapser {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -1,156 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component, ElementRef, Input, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
|
||||
import * as OpenAPISampler from 'openapi-sampler';
|
||||
import JsonPointer from '../../utils/JsonPointer';
|
||||
import { BaseComponent, SpecManager } from '../base';
|
||||
import { SchemaNormalizer } from '../../services/schema-normalizer.service';
|
||||
import { getJsonLikeSample, getXmlLikeSample, getTextLikeSample } from '../../utils/helpers';
|
||||
|
||||
@Component({
|
||||
selector: 'schema-sample',
|
||||
templateUrl: './schema-sample.html',
|
||||
styleUrls: ['./schema-sample.css'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SchemaSample extends BaseComponent implements OnInit {
|
||||
@Input() pointer:string;
|
||||
@Input() skipReadOnly:boolean;
|
||||
|
||||
element: any;
|
||||
sample: any;
|
||||
xmlSample: string;
|
||||
textSample: string;
|
||||
enableButtons: boolean = false;
|
||||
|
||||
private _normalizer:SchemaNormalizer;
|
||||
|
||||
constructor(specMgr:SpecManager, elementRef:ElementRef) {
|
||||
super(specMgr);
|
||||
this.element = elementRef.nativeElement;
|
||||
this._normalizer = new SchemaNormalizer(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.bindEvents();
|
||||
|
||||
let base:any = this.componentSchema;
|
||||
let sample, xmlSample;
|
||||
|
||||
// got pointer not directly to the schema but e.g. to the response obj
|
||||
if (this.componentSchema.schema) {
|
||||
base = this.componentSchema;
|
||||
this.componentSchema = this.componentSchema.schema;
|
||||
this.pointer += '/schema';
|
||||
}
|
||||
|
||||
// Support x-examples, allowing requests to specify an example.
|
||||
let examplePointer:string = JsonPointer.join(JsonPointer.dirName(this.pointer), 'x-examples');
|
||||
let requestExamples:any = this.specMgr.byPointer(examplePointer);
|
||||
if (requestExamples) {
|
||||
base.examples = requestExamples;
|
||||
}
|
||||
|
||||
this.xmlSample = base.examples && getXmlLikeSample(base.examples);
|
||||
this.textSample = base.examples && getTextLikeSample(base.examples);
|
||||
|
||||
let jsonLikeSample = base.examples && getJsonLikeSample(base.examples);
|
||||
if (jsonLikeSample) {
|
||||
sample = jsonLikeSample;
|
||||
} else {
|
||||
let selectedDescendant;
|
||||
|
||||
this.componentSchema = this._normalizer.normalize(this.componentSchema, this.pointer);
|
||||
|
||||
let discriminator = this.componentSchema.discriminator || this.componentSchema['x-discriminatorBasePointer'];
|
||||
if (discriminator) {
|
||||
let descendants = this.specMgr.findDerivedDefinitions(this.componentSchema._pointer || this.pointer, this.componentSchema);
|
||||
if (descendants.length) {
|
||||
// TODO: sync up with dropdown
|
||||
selectedDescendant = descendants[0];
|
||||
let descSchema = this.specMgr.getDescendant(selectedDescendant, this.componentSchema);
|
||||
this.componentSchema = this._normalizer.normalize(Object.assign({}, descSchema), selectedDescendant.$ref,
|
||||
{omitParent: false});
|
||||
}
|
||||
}
|
||||
if (this.fromCache()) {
|
||||
this.initButtons();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
sample = OpenAPISampler.sample(this.componentSchema, {
|
||||
skipReadOnly: this.skipReadOnly
|
||||
});
|
||||
} catch(e) {
|
||||
// no sample available
|
||||
}
|
||||
if (selectedDescendant) {
|
||||
sample[discriminator] = selectedDescendant.name;
|
||||
}
|
||||
}
|
||||
this.cache(sample);
|
||||
this.sample = sample;
|
||||
this.initButtons();
|
||||
}
|
||||
|
||||
initButtons() {
|
||||
if (typeof this.sample === 'object') {
|
||||
this.enableButtons = true;
|
||||
}
|
||||
}
|
||||
|
||||
cache(sample) {
|
||||
if (this.skipReadOnly) {
|
||||
this.componentSchema['x-redoc-ro-sample'] = sample;
|
||||
} else {
|
||||
this.componentSchema['x-redoc-rw-sample'] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
fromCache() {
|
||||
if (this.skipReadOnly && this.componentSchema['x-redoc-ro-sample']) {
|
||||
this.sample = this.componentSchema['x-redoc-ro-sample'];
|
||||
return true;
|
||||
} else if (!this.skipReadOnly && this.componentSchema['x-redoc-rw-sample']) {
|
||||
this.sample = this.componentSchema['x-redoc-rw-sample'];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bindEvents() {
|
||||
this.element.addEventListener('click', (event) => {
|
||||
var collapsed, target = event.target;
|
||||
if (event.target.className === 'collapser') {
|
||||
collapsed = target.parentNode.getElementsByClassName('collapsible')[0];
|
||||
if (collapsed.parentNode.classList.contains('collapsed')) {
|
||||
collapsed.parentNode.classList.remove('collapsed');
|
||||
} else {
|
||||
collapsed.parentNode.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
expandAll() {
|
||||
let elements = this.element.getElementsByClassName('collapsible');
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let collapsed = elements[i];
|
||||
collapsed.parentNode.classList.remove('collapsed');
|
||||
}
|
||||
}
|
||||
|
||||
collapseAll() {
|
||||
let elements = this.element.getElementsByClassName('collapsible');
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
let expanded = elements[i];
|
||||
if (expanded.parentNode.classList.contains('redoc-json')) continue;
|
||||
expanded.parentNode.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
<div class="search-input-wrap">
|
||||
<div class="clear-button" *ngIf="searchTerm" (click)="clearSearch()">×</div>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 1000 1000" enable-background="new 0 0 1000 1000" xml:space="preserve">
|
||||
<path d="M968.2,849.4L667.3,549c83.9-136.5,66.7-317.4-51.7-435.6C477.1-25,252.5-25,113.9,113.4c-138.5,138.3-138.5,362.6,0,501C219.2,730.1,413.2,743,547.6,666.5l301.9,301.4c43.6,43.6,76.9,14.9,104.2-12.4C981,928.3,1011.8,893,968.2,849.4z M524.5,522c-88.9,88.7-233,88.7-321.8,0c-88.9-88.7-88.9-232.6,0-321.3c88.9-88.7,233-88.7,321.8,0C613.4,289.4,613.4,433.3,524.5,522z"/>
|
||||
</svg>
|
||||
<input #search (keyup)="update($event, search.value)" [value]="searchTerm" placeholder="Search">
|
||||
</div>
|
||||
<ul class="search-results" [hidden]="!items.length">
|
||||
<li class="result menu-item-header" *ngFor="let item of items"
|
||||
ngClass="menu-item-depth-{{item.menuItem.depth}} {{item.menuItem.ready ? '' : 'disabled'}}"
|
||||
(click)="clickSearch(item)">
|
||||
<span class="operation-type" [ngClass]="item.menuItem?.metadata?.operation" *ngIf="item.menuItem?.metadata?.operation"> {{item.menuItem?.metadata?.operation}} </span><!--
|
||||
--><span class="menu-item-title">{{item.menuItem.name}}</span>
|
||||
</li>
|
||||
</ul>
|
|
@ -1,85 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.search-input-wrap {
|
||||
padding: 0 20px;
|
||||
|
||||
> svg {
|
||||
width: 13px;
|
||||
height: 27px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
|
||||
path {
|
||||
fill: lighten($text-color, 20%);
|
||||
}
|
||||
}
|
||||
|
||||
.clear-button {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
width: 13px;
|
||||
text-align: center;
|
||||
right: 20px;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 5px 20px 5px 20px;
|
||||
|
||||
border: 0;
|
||||
border-bottom: 1px solid darken($side-bar-bg-color, 10%);
|
||||
font-weight: bold;
|
||||
|
||||
font-size: 13px;
|
||||
color: $text-color;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.search-results {
|
||||
margin: 10px 0 0;
|
||||
list-style: none;
|
||||
padding: 10px 0;
|
||||
background-color: darken($side-bar-bg-color, 5%);
|
||||
max-height: 100px;
|
||||
overflow-y: auto;
|
||||
border-bottom: 1px solid darken($side-bar-bg-color, 10%);
|
||||
border-top: 1px solid darken($side-bar-bg-color, 10%);
|
||||
line-height: 1.2;
|
||||
|
||||
min-height: 150px;
|
||||
max-height: 250px;
|
||||
|
||||
> li {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
font-family: Montserrat, sans-serif;
|
||||
font-size: 13px;
|
||||
padding: 5px 20px;
|
||||
|
||||
&:hover {
|
||||
background-color: darken($side-bar-bg-color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
li.menu-item-depth-1 {
|
||||
color: $primary-color;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
> li.disabled {
|
||||
cursor: default;
|
||||
color: lighten($text-color, 60%);
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, OnInit, HostBinding } from '@angular/core';
|
||||
import { Marker, SearchService, MenuService, MenuItem } from '../../services/';
|
||||
import { throttle } from '../../utils/';
|
||||
|
||||
@Component({
|
||||
selector: 'redoc-search',
|
||||
styleUrls: ['./redoc-search.css'],
|
||||
templateUrl: './redoc-search.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class RedocSearch implements OnInit {
|
||||
logo:any = {};
|
||||
items: { menuItem: MenuItem, pointers: string[] }[] = [];
|
||||
searchTerm = '';
|
||||
throttledSearch: Function;
|
||||
|
||||
_subscription;
|
||||
|
||||
constructor(
|
||||
cdr: ChangeDetectorRef,
|
||||
private marker: Marker,
|
||||
public search: SearchService,
|
||||
public menu: MenuService) {
|
||||
this._subscription = menu.changed.subscribe(() => {
|
||||
cdr.markForCheck();
|
||||
cdr.detectChanges();
|
||||
});
|
||||
|
||||
this.throttledSearch = throttle(() => {
|
||||
this.updateSearch();
|
||||
cdr.markForCheck();
|
||||
cdr.detectChanges();
|
||||
}, 300, this);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.search.indexAll();
|
||||
}
|
||||
|
||||
clearSearch() {
|
||||
this.searchTerm = '';
|
||||
this.updateSearch();
|
||||
}
|
||||
|
||||
update(event:KeyboardEvent, val) {
|
||||
if (event && event.keyCode === 27) { // escape
|
||||
this.searchTerm = '';
|
||||
} else {
|
||||
this.searchTerm = val;
|
||||
}
|
||||
|
||||
this.throttledSearch();
|
||||
}
|
||||
|
||||
updateSearch() {
|
||||
if (!this.searchTerm || this.searchTerm.length < 2) {
|
||||
this.items = [];
|
||||
this.marker.unmark();
|
||||
return;
|
||||
}
|
||||
|
||||
let searchRes = this.search.search(this.searchTerm);
|
||||
this.items = Object.keys(searchRes).map(id => ({
|
||||
menuItem: this.menu.getItemById(id),
|
||||
pointers: searchRes[id].map(el => el.pointer)
|
||||
})).filter(res => !!res.menuItem);
|
||||
|
||||
this.items.sort((a, b) => {
|
||||
if (a.menuItem.depth > b.menuItem.depth) return 1;
|
||||
else if (a.menuItem.depth < b.menuItem.depth) return -1;
|
||||
else return 0;
|
||||
});
|
||||
this.marker.mark(this.searchTerm);
|
||||
}
|
||||
|
||||
clickSearch(item) {
|
||||
this.search.ensureSearchVisible(
|
||||
item.pointers
|
||||
);
|
||||
this.marker.remark();
|
||||
this.menu.activate(item.menuItem);
|
||||
this.menu.scrollToActive();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this._subscription.unsubscribe();
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<div class="security-definition" *ngFor="let def of defs">
|
||||
<h2 class="sharable-header" attr.section="section/Authentication/{{def.name}}">
|
||||
<a class="share-link" href="#section/Authentication/{{def.name}}"></a>{{def.name}}</h2>
|
||||
<div [innerHTML]="def.details.description | marked"></div>
|
||||
<table class="security-details">
|
||||
<tr>
|
||||
<th> Security scheme type: </th>
|
||||
<td> {{def.details._displayType}} </td>
|
||||
</tr>
|
||||
<tr *ngIf="def.details.type === 'apiKey'">
|
||||
<th> {{def.details.in}} parameter name:</th>
|
||||
<td> {{def.details.name}} </td>
|
||||
</tr>
|
||||
<ng-template [ngIf]="def.details.type === 'oauth2'">
|
||||
<tr>
|
||||
<th> OAuth2 Flow</th>
|
||||
<td> {{def.details.flow}} </td>
|
||||
</tr>
|
||||
<tr *ngIf="def.details.flow === 'implicit' || def.details.flow === 'accessCode'">
|
||||
<th> Authorization URL </th>
|
||||
<td> {{def.details.authorizationUrl}} </td>
|
||||
</tr>
|
||||
<tr *ngIf="def.details.flow !== 'implicit'">
|
||||
<th> Token URL </th>
|
||||
<td> {{def.details.tokenUrl}} </td>
|
||||
</tr>
|
||||
</ng-template>
|
||||
</table>
|
||||
<ng-template [ngIf]="def.details.type === 'oauth2'">
|
||||
<h3> OAuth2 Scopes </h3>
|
||||
<table class="security-scopes-details">
|
||||
<tr *ngFor="let scopeName of def.details.scopes | keys">
|
||||
<th> {{scopeName}} </th>
|
||||
<td> {{def.details.scopes[scopeName]}} </td>
|
||||
</tr>
|
||||
</table>
|
||||
</ng-template>
|
||||
</div>
|
|
@ -1,36 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.security-definition:not(:last-of-type) {
|
||||
border-bottom: 1px solid rgba($text-color, .3);
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
:host h2 {
|
||||
padding-top: $section-spacing;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
:host .security-scopes-details, :host .security-details {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
table.details th, table.details td {
|
||||
font-weight: bold;
|
||||
width: 200px;
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
table.details th {
|
||||
text-align: left;
|
||||
padding: 6px;
|
||||
text-transform: capitalize;
|
||||
font-weight: normal;
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
'use strict';
|
||||
import { Component, ChangeDetectionStrategy, OnInit } from '@angular/core';
|
||||
import { SpecManager, BaseComponent } from '../base';
|
||||
|
||||
import { ComponentParser } from '../../services/component-parser.service';
|
||||
|
||||
const AUTH_TYPES = {
|
||||
'oauth2': 'OAuth2',
|
||||
'apiKey': 'API Key',
|
||||
'basic': 'Basic Authorization'
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'security-definitions',
|
||||
styleUrls: ['./security-definitions.css'],
|
||||
templateUrl: './security-definitions.html',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class SecurityDefinitions extends BaseComponent implements OnInit {
|
||||
info: any = {};
|
||||
specUrl: String;
|
||||
defs: any[];
|
||||
|
||||
static insertTagIntoDescription(md:string) {
|
||||
if (ComponentParser.contains(md, 'security-definitions')) return md;
|
||||
if (/^#\s?Authentication\s*$/mi.test(md)) return md;
|
||||
return md + '\n# Authentication \n' + ComponentParser.build('security-definitions');
|
||||
}
|
||||
|
||||
constructor(specMgr:SpecManager) {
|
||||
super(specMgr);
|
||||
}
|
||||
|
||||
init() {
|
||||
this.componentSchema = this.componentSchema.securityDefinitions;
|
||||
this.defs = Object.keys(this.componentSchema).map(name => {
|
||||
let details = this.componentSchema[name];
|
||||
details._displayType = AUTH_TYPES[details.type];
|
||||
return {
|
||||
name,
|
||||
details
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
<li *ngFor="let item of items; let idx = index" class="menu-item"
|
||||
ngClass="menu-item-depth-{{item.depth}} {{item.active ? 'active' : ''}} menu-item-for-{{item.metadata?.type}}">
|
||||
<label class="menu-item-header" [ngClass]="{disabled: !item.ready, deprecated: item?.metadata?.deprecated}" (click)="activateItem(item)">
|
||||
<span class="operation-type" [ngClass]="item?.metadata?.operation" *ngIf="item?.metadata?.operation"> {{item?.metadata?.operation}} </span><!--
|
||||
--><span class="menu-item-title">{{item.name}}</span>
|
||||
<svg *ngIf="item.items?.length" xmlns="http://www.w3.org/2000/svg" version="1.1" x="0" y="0" viewBox="0 0 24 24" xml:space="preserve">
|
||||
<polygon points="17.3 8.3 12 13.6 6.7 8.3 5.3 9.7 12 16.4 18.7 9.7 "/>
|
||||
</svg>
|
||||
</label>
|
||||
<ul *ngIf="item.items" class="menu-subitems">
|
||||
<side-menu-items [items]="item.items" (activate)="activateItem($event)"> </side-menu-items>
|
||||
</ul>
|
||||
</li>
|
|
@ -1,134 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
|
||||
.menu-item-header {
|
||||
cursor: pointer;
|
||||
color: rgba($text-color, .9);
|
||||
-webkit-transition: all .15s ease-in-out;
|
||||
-moz-transition: all .15s ease-in-out;
|
||||
-ms-transition: all .15s ease-in-out;
|
||||
-o-transition: all .15s ease-in-out;
|
||||
transition: all .15s ease-in-out;
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: $side-menu-item-vpadding*2.5 $side-menu-item-hpadding;
|
||||
|
||||
&[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.disabled, &.disabled:hover {
|
||||
cursor: default;
|
||||
color: lighten($text-color, 60%);
|
||||
}
|
||||
|
||||
&.deprecated {
|
||||
text-decoration: line-through;
|
||||
color: lighten($text-color, 60%);
|
||||
}
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
> svg {
|
||||
height: 18px;
|
||||
vertical-align: middle;
|
||||
float: right;
|
||||
transform: rotateZ(-90deg);
|
||||
//transition: transform 0.3s ease-out;
|
||||
|
||||
polygon {
|
||||
fill: $border-color;
|
||||
}
|
||||
|
||||
.active > & {
|
||||
transform: rotateZ(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
-webkit-transition: all .15s ease-in-out;
|
||||
-moz-transition: all .15s ease-in-out;
|
||||
-ms-transition: all .15s ease-in-out;
|
||||
-o-transition: all .15s ease-in-out;
|
||||
transition: all .15s ease-in-out;
|
||||
list-style: none inside none;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-subitems {
|
||||
margin: 0;
|
||||
font-size: 0.929em;
|
||||
line-height: 1.2em;
|
||||
font-weight: $light;
|
||||
color: rgba($text-color, .9);
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
height: 0;
|
||||
|
||||
.active > & {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-depth-1 {
|
||||
> .menu-item-header {
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
font-weight: $light;
|
||||
font-size: $h5;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
// do not capitalize method summuary in level-1 menu
|
||||
&.menu-item-for-operation > .menu-item-header {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
> .menu-item-header:not(.disabled):hover,
|
||||
&.active > .menu-item-header {
|
||||
color: $primary-color;
|
||||
background: $side-menu-active-bg-color;
|
||||
}
|
||||
&.active {
|
||||
//background: $side-menu-active-bg-color;
|
||||
}
|
||||
}
|
||||
|
||||
.menu-item-depth-2 {
|
||||
> .menu-item-header {
|
||||
padding-left: $side-menu-item-hpadding;
|
||||
}
|
||||
|
||||
> .menu-item-header:hover,
|
||||
&.active > .menu-item-header {
|
||||
background: darken($side-menu-active-bg-color, 6%);
|
||||
}
|
||||
}
|
||||
|
||||
// group items
|
||||
.menu-item-depth-0 {
|
||||
margin-top: 15px;
|
||||
|
||||
> .menu-subitems {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
> .menu-item-header {
|
||||
font-family: $headers-font, $headers-font-family;
|
||||
color: rgba($text-color, .4);
|
||||
text-transform: uppercase;
|
||||
font-size: 0.8em;
|
||||
padding-bottom: 0;
|
||||
cursor: default;
|
||||
|
||||
> svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
&:hover,
|
||||
&.active {
|
||||
//background: none;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
<div #mobile class="mobile-nav" (click)="toggleMobileNav()">
|
||||
<span class="selected-item-info">
|
||||
<span class="selected-tag"> {{activeCatCaption}} </span>
|
||||
<span class="selected-endpoint">{{activeItemCaption}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<ng-template #default>
|
||||
<side-menu-items [items]="menuItems" (activate)="activateAndScroll($event)"></side-menu-items>
|
||||
</ng-template>
|
||||
|
||||
<div #desktop id="resources-nav" perfect-scrollbar>
|
||||
<ul class="menu-root">
|
||||
<div *ngIf="itemsTemplate; else default">
|
||||
<ng-container *ngTemplateOutlet="itemsTemplate; context: this"></ng-container>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,90 +0,0 @@
|
|||
@import '../../shared/styles/variables';
|
||||
$mobile-menu-compact-breakpoint: 550px;
|
||||
|
||||
:host {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#resources-nav {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
ul.menu-root {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: none;
|
||||
height: 3em;
|
||||
line-height: 3em;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid #ccc;
|
||||
cursor: pointer;
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
height: 3em;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 100 100" version="1.1" viewBox="0 0 100 100" xml:space="preserve"><polygon fill="#010101" points="23.1 34.1 51.5 61.7 80 34.1 81.5 35 51.5 64.1 21.5 35 23.1 34.1 "/></svg>');
|
||||
background-size: 70%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
||||
float: right;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: $side-menu-mobile-breakpoint) {
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.mobile-nav {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#resources-nav {
|
||||
height: 0;
|
||||
overflow-y: auto;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.menu-subitems {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.selected-tag {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.selected-endpoint:before {
|
||||
content: "/";
|
||||
padding: 0 2px;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.selected-endpoint:empty:before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.selected-item-info {
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
max-width: 350px;
|
||||
|
||||
@media (max-width: $mobile-menu-compact-breakpoint) {
|
||||
display: inline-block;
|
||||
padding: 0 20px;
|
||||
max-width: 80%;
|
||||
max-width: calc(100% - 4em);
|
||||
}
|
||||
}
|
|
@ -1,96 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { getChildDebugElement } from '../../../tests/helpers';
|
||||
import { Component } from '@angular/core';
|
||||
import { OptionsService, MenuItem } from '../../services/index';
|
||||
|
||||
import {
|
||||
inject,
|
||||
async
|
||||
} from '@angular/core/testing';
|
||||
|
||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
|
||||
import { OperationsList, SideMenu } from '../index';
|
||||
|
||||
import { SpecManager } from '../../utils/spec-manager';
|
||||
|
||||
let testOptions;
|
||||
|
||||
describe('Redoc components', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [ TestAppComponent, OperationsList ] });
|
||||
});
|
||||
describe('SideMenu Component', () => {
|
||||
let builder;
|
||||
let component: SideMenu;
|
||||
let fixture: ComponentFixture<TestAppComponent>;
|
||||
let specMgr;
|
||||
|
||||
beforeEach(inject([SpecManager, OptionsService],
|
||||
(_specMgr, opts) => {
|
||||
|
||||
testOptions = opts;
|
||||
testOptions.options = {
|
||||
scrollYOffset: () => 0,
|
||||
$scrollParent: window
|
||||
};
|
||||
specMgr = _specMgr;
|
||||
}));
|
||||
|
||||
beforeEach(done => {
|
||||
specMgr.load('/tests/schemas/extended-petstore.yml').then(done, done.fail);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TestAppComponent);
|
||||
component = getChildDebugElement(fixture.debugElement, 'side-menu').componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (fixture) fixture.destroy();
|
||||
});
|
||||
|
||||
it('should init component and component data', () => {
|
||||
should.exist(component);
|
||||
});
|
||||
|
||||
it('should clear active item and cat captions on change to null', () => {
|
||||
component.activeCatCaption = 'test';
|
||||
component.activeItemCaption = 'test';
|
||||
component.changed(null);
|
||||
component.activeCatCaption.should.be.equal('');
|
||||
component.activeItemCaption.should.be.equal('');
|
||||
});
|
||||
|
||||
it('should set active item and cat captions on change event', () => {
|
||||
let parentItem: MenuItem = {
|
||||
id: 'id',
|
||||
name: 'Item'
|
||||
};
|
||||
component.changed(parentItem);
|
||||
component.activeCatCaption.should.be.equal(parentItem.name);
|
||||
component.activeItemCaption.should.be.equal('');
|
||||
|
||||
let childItem: MenuItem = {
|
||||
id: 'id2',
|
||||
name: 'Child',
|
||||
parent: parentItem
|
||||
};
|
||||
component.changed(childItem);
|
||||
component.activeCatCaption.should.be.equal(parentItem.name);
|
||||
component.activeItemCaption.should.be.equal(childItem.name);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/** Test component that contains an ApiInfo. */
|
||||
@Component({
|
||||
selector: 'test-app',
|
||||
template:
|
||||
`<side-menu></side-menu>
|
||||
<operations-list></operations-list>`
|
||||
})
|
||||
class TestAppComponent {
|
||||
}
|
|
@ -1,163 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
Output,
|
||||
ElementRef,
|
||||
ChangeDetectorRef,
|
||||
ViewChild,
|
||||
OnInit,
|
||||
OnDestroy
|
||||
} from '@angular/core';
|
||||
|
||||
import { trigger, state, animate, transition, style } from '@angular/core';
|
||||
import { ScrollService, MenuService, OptionsService, MenuItem } from '../../services/';
|
||||
import { PerfectScrollbar } from '../../shared/components';
|
||||
import { BrowserDomAdapter as DOM } from '../../utils/browser-adapter';
|
||||
|
||||
const global = window;
|
||||
|
||||
@Component({
|
||||
selector: 'side-menu-items',
|
||||
templateUrl: './side-menu-items.html',
|
||||
styleUrls: ['./side-menu-items.css'],
|
||||
})
|
||||
export class SideMenuItems {
|
||||
@Input() items: MenuItem[];
|
||||
@Output() activate = new EventEmitter<MenuItem>();
|
||||
|
||||
activateItem(item) {
|
||||
this.activate.next(item);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'side-menu',
|
||||
templateUrl: './side-menu.html',
|
||||
styleUrls: ['./side-menu.css']
|
||||
})
|
||||
export class SideMenu implements OnInit, OnDestroy {
|
||||
activeCatCaption: string;
|
||||
activeItemCaption: string;
|
||||
menuItems: Array<MenuItem>;
|
||||
@Input() itemsTemplate;
|
||||
@ViewChild(PerfectScrollbar) PS:PerfectScrollbar;
|
||||
|
||||
private options: any;
|
||||
private $element: any;
|
||||
private $mobileNav: any;
|
||||
private $resourcesNav: any;
|
||||
private $scrollParent: any;
|
||||
|
||||
private changedActiveSubscription;
|
||||
private changedSubscription;
|
||||
|
||||
constructor(
|
||||
elementRef:ElementRef,
|
||||
private scrollService:ScrollService,
|
||||
private menuService:MenuService,
|
||||
optionsService:OptionsService,
|
||||
private detectorRef:ChangeDetectorRef,
|
||||
) {
|
||||
this.$element = elementRef.nativeElement;
|
||||
|
||||
this.activeCatCaption = '';
|
||||
this.activeItemCaption = '';
|
||||
|
||||
this.options = optionsService.options;
|
||||
|
||||
this.changedActiveSubscription = this.menuService.changedActiveItem.subscribe((evt) => this.changed(evt));
|
||||
this.changedSubscription = this.menuService.changed.subscribe((evt) => {
|
||||
this.update();
|
||||
});
|
||||
}
|
||||
|
||||
changed(item) {
|
||||
if (!item) {
|
||||
this.activeCatCaption = '';
|
||||
this.activeItemCaption = '';
|
||||
return;
|
||||
}
|
||||
if (item.parent) {
|
||||
this.activeItemCaption = item.name;
|
||||
this.activeCatCaption = item.parent.name;
|
||||
} else {
|
||||
this.activeCatCaption = item.name;
|
||||
this.activeItemCaption = '';
|
||||
}
|
||||
|
||||
// safari doesn't update bindings if not run changeDetector manually :(
|
||||
this.update();
|
||||
this.scrollActiveIntoView();
|
||||
}
|
||||
|
||||
update() {
|
||||
this.detectorRef.detectChanges();
|
||||
this.PS && this.PS.update();
|
||||
}
|
||||
|
||||
scrollActiveIntoView() {
|
||||
let $item = this.$element.querySelector('li.active, label.active');
|
||||
if ($item) $item.scrollIntoViewIfNeeded();
|
||||
}
|
||||
|
||||
activateAndScroll(item) {
|
||||
if (this.mobileMode) {
|
||||
this.toggleMobileNav();
|
||||
}
|
||||
|
||||
this.menuService.activate(item);
|
||||
this.menuService.scrollToActive();
|
||||
}
|
||||
|
||||
init() {
|
||||
this.menuItems = this.menuService.items;
|
||||
|
||||
this.$mobileNav = DOM.querySelector(this.$element, '.mobile-nav');
|
||||
this.$resourcesNav = DOM.querySelector(this.$element, '#resources-nav');
|
||||
|
||||
//decorate scrollYOffset to account mobile nav
|
||||
this.scrollService.scrollYOffset = () => {
|
||||
let mobileNavOffset = this.$mobileNav.clientHeight;
|
||||
return this.options.scrollYOffset() + mobileNavOffset;
|
||||
};
|
||||
}
|
||||
|
||||
get mobileMode() {
|
||||
return this.$mobileNav.clientHeight > 0;
|
||||
}
|
||||
|
||||
toggleMobileNav() {
|
||||
let $overflowParent = (this.options.$scrollParent === global) ? DOM.defaultDoc().body
|
||||
: this.$scrollParent;
|
||||
if (DOM.hasStyle(this.$resourcesNav, 'height')) {
|
||||
DOM.removeStyle(this.$resourcesNav, 'height');
|
||||
DOM.removeStyle($overflowParent, 'overflow-y');
|
||||
} else {
|
||||
let viewportHeight = this.options.$scrollParent.innerHeight
|
||||
|| this.options.$scrollParent.clientHeight;
|
||||
let height = viewportHeight - this.$mobileNav.getBoundingClientRect().bottom;
|
||||
DOM.setStyle($overflowParent, 'overflow-y', 'hidden');
|
||||
DOM.setStyle(this.$resourcesNav, 'height', height + 'px');
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.changedActiveSubscription.unsubscribe();
|
||||
this.changedSubscription.unsubscribe();
|
||||
this.scrollService.unbind();
|
||||
this.menuService.destroy();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
<div *ngIf="shown">
|
||||
<a class="warnings-close" (click)="close()">×</a>
|
||||
<div class="message" *ngFor="let message of warnings">{{message}}</div>
|
||||
</div>
|
|
@ -1,33 +0,0 @@
|
|||
:host {
|
||||
width: 60%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.message {
|
||||
padding: 5px 40px;
|
||||
background-color: #fcf8e3;
|
||||
color: #8a6d3b;
|
||||
|
||||
&:before {
|
||||
content: "Warning: ";
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.warnings-close {
|
||||
font-size: 150%;
|
||||
color: black;
|
||||
opacity: 0.4;
|
||||
float: right;
|
||||
margin: 5px 20px 0 0;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
display: inline;
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { SpecManager, BaseComponent } from '../base';
|
||||
import { WarningsService, OptionsService } from '../../services/index';
|
||||
|
||||
@Component({
|
||||
selector: 'warnings',
|
||||
styleUrls: ['./warnings.css'],
|
||||
templateUrl: './warnings.html'
|
||||
})
|
||||
export class Warnings extends BaseComponent implements OnInit {
|
||||
warnings: Array<string> = [];
|
||||
shown: boolean = false;
|
||||
suppressWarnings: boolean;
|
||||
constructor(specMgr:SpecManager, optionsMgr: OptionsService) {
|
||||
super(specMgr);
|
||||
this.suppressWarnings = optionsMgr.options.suppressWarnings;
|
||||
}
|
||||
|
||||
init() {
|
||||
this.shown = !this.suppressWarnings && !!this.warnings.length;
|
||||
WarningsService.warnings.subscribe((warns) => {
|
||||
this.warnings = warns;
|
||||
this.shown = !this.suppressWarnings && !!warns.length;
|
||||
});
|
||||
}
|
||||
|
||||
close() {
|
||||
this.shown = false;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.preinit();
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
'use strict';
|
||||
|
||||
import { SpecManager } from '../utils/spec-manager';
|
||||
import { BaseComponent } from '../components/base';
|
||||
import { OptionsService } from '../services/options.service';
|
||||
|
||||
describe('Redoc components', () => {
|
||||
describe('BaseComponent', () => {
|
||||
let specMgr;
|
||||
let component;
|
||||
|
||||
beforeAll(() => {
|
||||
specMgr = new SpecManager(new OptionsService());
|
||||
specMgr._schema = {tags: []};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
component = new BaseComponent(specMgr);
|
||||
});
|
||||
|
||||
it('should set instance properties', () => {
|
||||
component.specMgr.should.be.equal(specMgr);
|
||||
//component.schema.should.be.of.type('object');
|
||||
expect(component.componentSchema).toBeNull();
|
||||
});
|
||||
|
||||
it('should set componentSchema based on pointer on ngOnInit', () => {
|
||||
component.pointer = '/tags';
|
||||
component.ngOnInit();
|
||||
component.componentSchema.should.be.deepEqual(specMgr._schema.tags);
|
||||
});
|
||||
|
||||
it('should call init virtual methods after init', () => {
|
||||
spyOn(component, 'init');
|
||||
component.ngOnInit();
|
||||
|
||||
component.init.calls.count().should.be.equal(1);
|
||||
component.init.and.callThrough();
|
||||
});
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user