* swagger-ui-react alpha.0 * alpha.1 * alpha.2 * alpha.3 * begin updating README * alpha.4 * WIP: `displayOperationId` support * move loading error readouts to BaseLayout * add `url` prop * export React component as default * add interceptor support * modify docs markup * add `onComplete` prop * add `spec` prop * Update README.md * alpha.6 * remove independent manifest; build releasable exclusively from template * ensure dist is present; drop config field in manifest * drop alpha field this script is now able to release to npm! * remove unused selector references * Update README.mdbubble
@@ -11,6 +11,8 @@ selenium-debug.log | |||
test/e2e/db.json | |||
docs/_book | |||
flavors/**/dist/* | |||
# Cypress | |||
test/e2e-cypress/screenshots | |||
test/e2e-cypress/videos |
@@ -7,10 +7,11 @@ | |||
**🕰️ Looking for the older version of Swagger UI?** Refer to the [*2.x* branch](https://github.com/swagger-api/swagger-ui/tree/2.x). | |||
This repository publishes to two different NPM modules: | |||
This repository publishes to three different NPM modules: | |||
* [swagger-ui](https://www.npmjs.com/package/swagger-ui) is a traditional npm module intended for use in single-page applications that are capable of resolving dependencies (via Webpack, Browserify, etc). | |||
* [swagger-ui-dist](https://www.npmjs.com/package/swagger-ui-dist) is a dependency-free module that includes everything you need to serve Swagger UI in a server-side project, or a single-page application that can't resolve npm module dependencies. | |||
* [swagger-ui-react](https://www.npmjs.com/package/swagger-ui-react) is Swagger UI packaged as a React component for use in React applciations. | |||
We strongly suggest that you use `swagger-ui` instead of `swagger-ui-dist` if you're building a single-page application, since `swagger-ui-dist` is significantly larger. | |||
@@ -67,7 +68,6 @@ To help with the migration, here are the currently known issues with 3.X. This l | |||
- Support for `collectionFormat` is partial. | |||
- l10n (translations) is not implemented. | |||
- Relative path support for external files is not implemented. | |||
- There are compatibility [issues](https://github.com/swagger-api/swagger-ui/labels/epic%3A%20usage%20in%20react%4016) with React 16.x. | |||
## Security contact | |||
@@ -0,0 +1,79 @@ | |||
# `swagger-ui-react` | |||
[![NPM version](https://badge.fury.io/js/swagger-ui-react.svg)](http://badge.fury.io/js/swagger-ui-react) | |||
`swagger-ui-react` is a flavor of Swagger UI suitable for use in React applications. | |||
It has a few differences from the main version of Swagger UI: | |||
* Declares `react` and `react-dom` as peerDependencies instead of production dependencies | |||
* Exports a component instead of a constructor function | |||
Versions of this module mirror the version of Swagger UI included in the distribution. | |||
## Quick start | |||
Install `swagger-ui-react`: | |||
``` | |||
$ npm i --save swagger-ui-react | |||
``` | |||
Use it in your React application: | |||
```js | |||
import SwaggerUI from "swagger-ui-react" | |||
import "swagger-ui-react/swagger-ui.css" | |||
export default App = () => <SwaggerUI url="https://petstore.swagger.io/v2/swagger.json" /> | |||
``` | |||
## Props | |||
These props map to [Swagger UI configuration options](https://github.com/swagger-api/swagger-ui/blob/master/docs/usage/configuration.md) of the same name. | |||
#### `spec`: PropTypes.object | |||
An OpenAPI document respresented as a JavaScript object, JSON string, or YAML string for Swagger UI to display. | |||
⚠️ Don't use this in conjunction with `url` - unpredictable behavior may occur. | |||
#### `url`: PropTypes.string | |||
Remote URL to an OpenAPI document that Swagger UI will fetch, parse, and display. | |||
⚠️ Don't use this in conjunction with `spec` - unpredictable behavior may occur. | |||
#### `onComplete`: PropTypes.func | |||
A callback function that is triggered when Swagger-UI finishes rendering an OpenAPI document. | |||
#### `requestInterceptor`: PropTypes.func | |||
> `req => req` or `req => Promise<req>`. | |||
A function that accepts a request object, and returns either a request object | |||
or a Promise that resolves to a request object. | |||
#### `responseInterceptor`: PropTypes.func | |||
> `res => res` or `res => Promise<res>`. | |||
A function that accepts a response object, and returns either a response object | |||
or a Promise that resolves to a response object. | |||
## Limitations | |||
* Not all configuration bindings are available. | |||
* OAuth redirection handling is not supported. | |||
* Topbar/Standalone mode is not supported. | |||
* Custom plugins are not supported. | |||
We intend to address these limitations based on user demand, so please open an issue or pull request if you have a specific request. | |||
## Notes | |||
* The `package.json` in the same folder as this README is _not_ the manifest that should be used for releases - another manifest is generated at build-time and can be found in `./dist/`. | |||
--- | |||
For anything else, check the [Swagger-UI](https://github.com/swagger-api/swagger-ui) repository. |
@@ -0,0 +1,83 @@ | |||
import React from "react" | |||
import PropTypes from "prop-types" | |||
import swaggerUIConstructor from "./swagger-ui" | |||
export default class SwaggerUI extends React.Component { | |||
constructor (props) { | |||
super(props) | |||
this.SwaggerUIComponent = null | |||
this.system = null | |||
} | |||
componentDidMount() { | |||
const ui = swaggerUIConstructor({ | |||
spec: this.props.spec, | |||
url: this.props.url, | |||
requestInterceptor: this.requestInterceptor, | |||
responseInterceptor: this.responseInterceptor, | |||
onComplete: this.onComplete, | |||
}) | |||
this.system = ui | |||
this.SwaggerUIComponent = ui.getComponent("App", "root") | |||
this.forceUpdate() | |||
} | |||
render() { | |||
return this.SwaggerUIComponent ? <this.SwaggerUIComponent /> : null | |||
} | |||
componentDidUpdate(prevProps) { | |||
if(this.props.url !== prevProps.url) { | |||
// flush current content | |||
this.system.specActions.updateSpec("") | |||
if(this.props.url) { | |||
// update the internal URL | |||
this.system.specActions.updateUrl(this.props.url) | |||
// trigger remote definition fetch | |||
this.system.specActions.download(this.props.url) | |||
} | |||
} | |||
if(this.props.spec !== prevProps.spec && this.props.spec) { | |||
if(typeof this.props.spec === "object") { | |||
this.system.specActions.updateSpec(JSON.stringify(this.props.spec)) | |||
} else { | |||
this.system.specActions.updateSpec(this.props.spec) | |||
} | |||
} | |||
} | |||
requestInterceptor = (req) => { | |||
if (typeof this.props.requestInterceptor === "function") { | |||
return this.props.requestInterceptor(req) | |||
} | |||
return req | |||
} | |||
responseInterceptor = (res) => { | |||
if (typeof this.props.responseInterceptor === "function") { | |||
return this.props.responseInterceptor(res) | |||
} | |||
return res | |||
} | |||
onComplete = () => { | |||
if (typeof this.props.onComplete === "function") { | |||
return this.props.onComplete() | |||
} | |||
} | |||
} | |||
SwaggerUI.propTypes = { | |||
spec: PropTypes.oneOf([ | |||
PropTypes.string, | |||
PropTypes.object, | |||
]), | |||
url: PropTypes.string, | |||
requestInterceptor: PropTypes.func, | |||
responseInterceptor: PropTypes.func, | |||
onComplete: PropTypes.func, | |||
} |
@@ -0,0 +1,5 @@ | |||
var jsonMerger = require("json-merger") | |||
var fs = require("fs") | |||
var result = jsonMerger.mergeFiles(["../../../package.json", "template.json"]) | |||
process.stdout.write(JSON.stringify(result, null, 2)) |
@@ -0,0 +1,28 @@ | |||
# Deploy `swagger-ui-react` to npm. | |||
# Parameter Expansion: http://stackoverflow.com/questions/6393551/what-is-the-meaning-of-0-in-a-bash-script | |||
cd "${0%/*}" | |||
mkdir ../dist | |||
# Copy UI's dist files to our directory | |||
cp ../../../dist/swagger-ui.js ../dist | |||
cp ../../../dist/swagger-ui.css ../dist | |||
# Create a releasable package manifest | |||
node create-manifest.js > ../dist/package.json | |||
# Transpile our top-level component | |||
../../../node_modules/.bin/babel ../index.js > ../dist/index.js | |||
# Copy our README into the dist folder for npm | |||
cp ../README.md ../dist | |||
# Run the release from the dist folder | |||
cd ../dist | |||
if [ "$PUBLISH_FLAVOR_REACT" = "true" ] ; then | |||
npm publish . | |||
else | |||
npm pack . | |||
fi |
@@ -0,0 +1,45 @@ | |||
{ | |||
"dependencies": { | |||
"react": { | |||
"$remove": true | |||
}, | |||
"react-dom": { | |||
"$remove": true | |||
} | |||
}, | |||
"scripts": { | |||
"$remove": true | |||
}, | |||
"devDependencies": { | |||
"$remove": true | |||
}, | |||
"bundlesize": { | |||
"$remove": true | |||
}, | |||
"nyc": { | |||
"$remove": true | |||
}, | |||
"browserslist": { | |||
"$remove": true | |||
}, | |||
"config": { | |||
"$remove": true | |||
}, | |||
"name": "swagger-ui-react", | |||
"main": "index.js", | |||
"repository": "git@github.com:swagger-api/swagger-ui.git", | |||
"contributors": [ | |||
"(in alphabetical order)", | |||
"Anna Bodnia <anna.bodnia@gmail.com>", | |||
"Buu Nguyen <buunguyen@gmail.com>", | |||
"Josh Ponelat <jponelat@gmail.com>", | |||
"Kyle Shockey <kyleshockey1@gmail.com>", | |||
"Robert Barnwell <robert@robertismy.name>", | |||
"Sahar Jafari <shr.jafari@gmail.com>" | |||
], | |||
"license": "Apache-2.0", | |||
"peerDependencies": { | |||
"react": ">=15.6.2", | |||
"react-dom": ">=15.6.2" | |||
} | |||
} |
@@ -83,6 +83,7 @@ | |||
}, | |||
"devDependencies": { | |||
"autoprefixer": "^8.4.1", | |||
"babel-cli": "^6.26.0", | |||
"babel-core": "^6.23.1", | |||
"babel-eslint": "^7.1.1", | |||
"babel-loader": "^7.1.0", | |||
@@ -118,6 +119,7 @@ | |||
"imports-loader": "^0.8.0", | |||
"jsdom": "^11.10.0", | |||
"json-loader": "^0.5.7", | |||
"json-merger": "^1.1.0", | |||
"json-server": "^0.12.2", | |||
"less": "^3.0.2", | |||
"license-checker": "^19.0.0", | |||
@@ -13,7 +13,7 @@ export default class BaseLayout extends React.Component { | |||
} | |||
render() { | |||
let {specSelectors, getComponent} = this.props | |||
let {errSelectors, specSelectors, getComponent} = this.props | |||
let SvgAssets = getComponent("SvgAssets") | |||
let InfoContainer = getComponent("InfoContainer", true) | |||
@@ -33,15 +33,43 @@ export default class BaseLayout extends React.Component { | |||
const isSpecEmpty = !specSelectors.specStr() | |||
if(isSpecEmpty) { | |||
let loadingMessage | |||
let isLoading = specSelectors.loadingStatus() === "loading" | |||
if(isLoading) { | |||
loadingMessage = <div className="loading"></div> | |||
} else { | |||
loadingMessage = <h4>No API definition provided.</h4> | |||
} | |||
const loadingStatus = specSelectors.loadingStatus() | |||
let loadingMessage = null | |||
if(loadingStatus === "loading") { | |||
loadingMessage = <div className="info"> | |||
<div className="loading-container"> | |||
<div className="loading"></div> | |||
</div> | |||
</div> | |||
} | |||
if(loadingStatus === "failed") { | |||
loadingMessage = <div className="info"> | |||
<div className="loading-container"> | |||
<h4 className="title">Failed to load API definition.</h4> | |||
<Errors /> | |||
</div> | |||
</div> | |||
} | |||
if (loadingStatus === "failedConfig") { | |||
const lastErr = errSelectors.lastError() | |||
const lastErrMsg = lastErr ? lastErr.get("message") : "" | |||
loadingMessage = <div className="info" style={{ maxWidth: "880px", marginLeft: "auto", marginRight: "auto", textAlign: "center" }}> | |||
<div className="loading-container"> | |||
<h4 className="title">Failed to load remote configuration.</h4> | |||
<p>{lastErrMsg}</p> | |||
</div> | |||
</div> | |||
} | |||
if(!loadingMessage && isSpecEmpty) { | |||
loadingMessage = <h4>No API definition provided.</h4> | |||
} | |||
if(loadingMessage) { | |||
return <div className="swagger-ui"> | |||
<div className="loading-container"> | |||
{loadingMessage} | |||
@@ -16,49 +16,22 @@ export default class StandaloneLayout extends React.Component { | |||
} | |||
render() { | |||
let { getComponent, specSelectors, errSelectors } = this.props | |||
let { getComponent } = this.props | |||
let Container = getComponent("Container") | |||
let Row = getComponent("Row") | |||
let Col = getComponent("Col") | |||
let Errors = getComponent("errors", true) | |||
const Topbar = getComponent("Topbar", true) | |||
const BaseLayout = getComponent("BaseLayout", true) | |||
const OnlineValidatorBadge = getComponent("onlineValidatorBadge", true) | |||
const loadingStatus = specSelectors.loadingStatus() | |||
const lastErr = errSelectors.lastError() | |||
const lastErrMsg = lastErr ? lastErr.get("message") : "" | |||
return ( | |||
<Container className='swagger-ui'> | |||
{ Topbar ? <Topbar /> : null } | |||
{ loadingStatus === "loading" && | |||
<div className="info"> | |||
<div className="loading-container"> | |||
<div className="loading"></div> | |||
</div> | |||
</div> | |||
} | |||
{ loadingStatus === "failed" && | |||
<div className="info"> | |||
<div className="loading-container"> | |||
<h4 className="title">Failed to load API definition.</h4> | |||
<Errors/> | |||
</div> | |||
</div> | |||
} | |||
{ loadingStatus === "failedConfig" && | |||
<div className="info" style={{ maxWidth: "880px", marginLeft: "auto", marginRight: "auto", textAlign: "center" }}> | |||
<div className="loading-container"> | |||
<h4 className="title">Failed to load remote configuration.</h4> | |||
<p>{lastErrMsg}</p> | |||
</div> | |||
</div> | |||
} | |||
{ !loadingStatus || loadingStatus === "success" && <BaseLayout /> } | |||
{Topbar ? <Topbar /> : null} | |||
<BaseLayout /> | |||
<Row> | |||
<Col> | |||
<OnlineValidatorBadge /> | |||