mirror of
https://github.com/Redocly/redoc.git
synced 2025-07-31 02:19:47 +03:00
Merge remote-tracking branch 'upstream/main'
# Conflicts: # src/components/RedocStandalone.tsx # src/services/models/Field.ts # src/services/models/Schema.ts
This commit is contained in:
commit
fbda09e3b1
2
.github/CODEOWNERS
vendored
Normal file
2
.github/CODEOWNERS
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
* @Redocly/keyboard-warriors
|
||||
/docs/ @Redocly/technical-writers
|
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
|
@ -14,7 +14,7 @@ Hi! We're really excited that you are interested in contributing to ReDoc. Befor
|
|||
## Pull Request Guidelines
|
||||
Before submitting a pull request, please make sure the following is done:
|
||||
|
||||
1. Fork the repository and create your branch from master.
|
||||
1. Fork the repository and create your branch from main.
|
||||
2. Run `npm install` in the repository root.
|
||||
3. If you’ve fixed a bug or added code that should be tested, add tests!
|
||||
4. Ensure the test suite passes (`npm test`). Tip: `npm test -- --watch TestName` is helpful in development.
|
||||
|
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
|
@ -2,7 +2,7 @@
|
|||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: 'type: bug'
|
||||
labels: 'Type: Bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
|
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
4
.github/ISSUE_TEMPLATE/feature_request.md
vendored
|
@ -2,7 +2,7 @@
|
|||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
labels: 'Type: Enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
@ -17,4 +17,4 @@ A clear and concise description of what you want to happen.
|
|||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
|
8
.github/workflows/e2e-tests.yml
vendored
8
.github/workflows/e2e-tests.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
|||
build-and-e2e:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- run: npm ci
|
||||
- run: npm run bundle
|
||||
- run: npm run e2e
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm run bundle
|
||||
- run: npm run e2e
|
||||
|
|
104
.github/workflows/main.yml
vendored
104
.github/workflows/main.yml
vendored
|
@ -3,43 +3,77 @@ on:
|
|||
release:
|
||||
types: [published]
|
||||
jobs:
|
||||
push_to_registry:
|
||||
name: Push Docker image to GitHub Packages
|
||||
# push_to_registry:
|
||||
# name: Push Docker image to GitHub Packages
|
||||
# runs-on: ubuntu-latest
|
||||
# permissions:
|
||||
# packages: write
|
||||
# contents: read
|
||||
# steps:
|
||||
# - name: Check out the repo
|
||||
# uses: actions/checkout@v3
|
||||
# - name: Login to GitHub Container Registry
|
||||
# uses: docker/login-action@v1
|
||||
# with:
|
||||
# registry: ghcr.io
|
||||
# username: ${{ github.repository_owner }}
|
||||
# password: ${{ secrets.GITHUB_TOKEN }}
|
||||
# - name: Prepare
|
||||
# id: prep
|
||||
# run: |
|
||||
# DOCKER_IMAGE=ghcr.io/redocly/redoc/cli
|
||||
# VERSION=edge
|
||||
# if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
# VERSION=${GITHUB_REF#refs/tags/}
|
||||
# elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
# VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
# elif [[ $GITHUB_REF == refs/pull/* ]]; then
|
||||
# VERSION=pr-${{ github.event.number }}
|
||||
# fi
|
||||
# TAGS="${DOCKER_IMAGE}:${VERSION},${DOCKER_IMAGE}:latest"
|
||||
# if [ "${{ github.event_name }}" = "push" ]; then
|
||||
# TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
|
||||
# fi
|
||||
# echo ::set-output name=version::${VERSION}
|
||||
# echo ::set-output name=tags::${TAGS}
|
||||
# echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
# - name: Push to GitHub Packages
|
||||
# uses: docker/build-push-action@v3
|
||||
# with:
|
||||
# context: ./cli
|
||||
# push: ${{ github.event_name != 'pull_request' }}
|
||||
# tags: ${{ steps.prep.outputs.tags }}
|
||||
dockerhub:
|
||||
name: Publish redoc image to DockerHub
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
- name: Login to GitHub Container Registry
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: docker_meta
|
||||
uses: crazy-max/ghaction-docker-meta@v1
|
||||
with:
|
||||
images: redocly/redoc
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v1
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Prepare
|
||||
id: prep
|
||||
run: |
|
||||
DOCKER_IMAGE=ghcr.io/redocly/redoc/cli
|
||||
VERSION=edge
|
||||
if [[ $GITHUB_REF == refs/tags/* ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/}
|
||||
elif [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||
VERSION=$(echo ${GITHUB_REF#refs/heads/} | sed -r 's#/+#-#g')
|
||||
elif [[ $GITHUB_REF == refs/pull/* ]]; then
|
||||
VERSION=pr-${{ github.event.number }}
|
||||
fi
|
||||
TAGS="${DOCKER_IMAGE}:${VERSION}"
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
TAGS="$TAGS,${DOCKER_IMAGE}:sha-${GITHUB_SHA::8}"
|
||||
fi
|
||||
echo ::set-output name=version::${VERSION}
|
||||
echo ::set-output name=tags::${TAGS}
|
||||
echo ::set-output name=created::$(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
- name: Push to GitHub Packages
|
||||
uses: docker/build-push-action@v2
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v3
|
||||
with:
|
||||
context: ./cli
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.prep.outputs.tags }}
|
||||
context: .
|
||||
file: ./config/docker/Dockerfile
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: ${{ steps.docker_meta.outputs.tags }}
|
||||
labels: ${{ steps.docker_meta.outputs.labels }}
|
||||
|
|
44
.github/workflows/publish-cli.yml
vendored
44
.github/workflows/publish-cli.yml
vendored
|
@ -3,7 +3,7 @@ name: Publish cli
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
|
||||
jobs:
|
||||
bundle:
|
||||
|
@ -11,20 +11,20 @@ jobs:
|
|||
if: needs.check-version-cli.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
npm-${{ hashFiles('package-lock.json') }}
|
||||
npm-
|
||||
- run: npm ci
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm run bundle
|
||||
- name: Store bundle artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bundles-cli
|
||||
path: bundles
|
||||
|
@ -34,17 +34,17 @@ jobs:
|
|||
if: needs.check-version-cli.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: npm ci
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm test
|
||||
e2e-tests:
|
||||
needs: [bundle]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: npm ci
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- name: Download bundled artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: bundles-cli
|
||||
path: bundles
|
||||
|
@ -54,10 +54,10 @@ jobs:
|
|||
if: needs.check-version-cli.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
|
@ -65,11 +65,11 @@ jobs:
|
|||
npm-${{ hashFiles('package-lock.json') }}
|
||||
npm-
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
run: npm ci && npm ci --prefix cli
|
||||
- name: Bundle
|
||||
run: npm run compile:cli
|
||||
- name: Store bundle artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: cli
|
||||
path: cli
|
||||
|
@ -81,9 +81,9 @@ jobs:
|
|||
changed: ${{ steps.check.outputs.changed }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
- name: Check if version has been updated
|
||||
id: check
|
||||
uses: EndBug/version-check@v2.0.1
|
||||
|
@ -96,18 +96,18 @@ jobs:
|
|||
if: needs.check-version-cli.outputs.changed == 'true'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download cli bundled artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: cli
|
||||
path: cli
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
|
|
91
.github/workflows/publish.yml
vendored
91
.github/workflows/publish.yml
vendored
|
@ -9,20 +9,20 @@ jobs:
|
|||
bundle:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
restore-keys: |
|
||||
npm-${{ hashFiles('package-lock.json') }}
|
||||
npm-
|
||||
- run: npm ci
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm run bundle
|
||||
- name: Store bundle artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: bundles
|
||||
path: bundles
|
||||
|
@ -30,62 +30,38 @@ jobs:
|
|||
unit-tests:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: npm ci
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm test
|
||||
e2e-tests:
|
||||
needs: [bundle]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: npm ci
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- name: Download bundled artifact
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: bundles
|
||||
path: bundles
|
||||
- run: npm run e2e
|
||||
# disable this for now
|
||||
# deploy-demo:
|
||||
# needs: [bundle, unit-tests, e2e-tests]
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v1
|
||||
# - name: Configure AWS Credentials
|
||||
# uses: aws-actions/configure-aws-credentials@v1
|
||||
# with:
|
||||
# aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
# aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
# aws-region: us-east-1
|
||||
# - name: Install dependencies
|
||||
# run: npm ci
|
||||
# - name: Download bundled artifacts
|
||||
# uses: actions/download-artifact@v2
|
||||
# with:
|
||||
# name: bundles
|
||||
# path: bundles
|
||||
# - name: Build package
|
||||
# run: npm run build:demo
|
||||
# - name: Deploy to S3 bucket
|
||||
# run: npm run deploy:demo
|
||||
# - name: Invalidate
|
||||
# run: aws cloudfront create-invalidation --distribution-id ${{ secrets.CF_DEMO_DISTRIBUTION_ID }} --paths "/*"
|
||||
publish:
|
||||
name: Publish to NPM
|
||||
needs: [bundle, unit-tests, e2e-tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-node@v1
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: '14.x'
|
||||
registry-url: 'https://registry.npmjs.org'
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Download bundled artifacts
|
||||
uses: actions/download-artifact@v2
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
name: bundles
|
||||
path: bundles
|
||||
- name: Cache node modules
|
||||
uses: actions/cache@v2
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
||||
key: npm-${{ hashFiles('package-lock.json') }}
|
||||
|
@ -98,3 +74,40 @@ jobs:
|
|||
run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||
|
||||
publish-cdn:
|
||||
name: Publish to CDN
|
||||
needs: [bundle, unit-tests, e2e-tests]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Configure AWS
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Download all artifact
|
||||
uses: actions/download-artifact@v3
|
||||
- name: Publish to S3
|
||||
run: npm run publish-cdn
|
||||
|
||||
invalidate-cache:
|
||||
name: Clear cache
|
||||
runs-on: ubuntu-latest
|
||||
needs: [publish, publish-cdn]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Configure AWS
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-east-1
|
||||
- name: Invalidate cache
|
||||
run: ./scripts/invalidate-cache.sh
|
||||
shell: bash
|
||||
env:
|
||||
DISTRIBUTION: ${{ secrets.DISTRIBUTION }}
|
||||
|
|
8
.github/workflows/sync.yml
vendored
8
.github/workflows/sync.yml
vendored
|
@ -2,17 +2,17 @@ name: Sync Files
|
|||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
workflow_dispatch:
|
||||
jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@master
|
||||
uses: actions/checkout@v3
|
||||
- name: Run GitHub File Sync
|
||||
uses: Redocly/repo-file-sync-action@master
|
||||
uses: Redocly/repo-file-sync-action@main
|
||||
with:
|
||||
GH_PAT: ${{ secrets.GH_PAT }}
|
||||
COMMIT_PREFIX: "sync:"
|
||||
COMMIT_PREFIX: 'sync:'
|
||||
SKIP_PR: true
|
||||
|
|
8
.github/workflows/unit-tests.yml
vendored
8
.github/workflows/unit-tests.yml
vendored
|
@ -6,7 +6,7 @@ jobs:
|
|||
build-and-unit:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: npm ci
|
||||
- run: npm run bundle
|
||||
- run: npm test
|
||||
- uses: actions/checkout@v3
|
||||
- run: npm ci && npm ci --prefix cli
|
||||
- run: npm run bundle
|
||||
- run: npm test
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -28,6 +28,7 @@ bundles/
|
|||
typings/*
|
||||
!typings/styled-patch.d.ts
|
||||
cli/index.js
|
||||
cli/__test__/*/**/*.html
|
||||
|
||||
/benchmark/revisions
|
||||
|
||||
|
|
186
CHANGELOG.md
186
CHANGELOG.md
|
@ -1,3 +1,185 @@
|
|||
# [2.0.0](https://github.com/Redocly/redoc/compare/v2.0.0-rc.77...v2.0.0) (2022-09-12)
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.77](https://github.com/Redocly/redoc/compare/v2.0.0-rc.76...v2.0.0-rc.77) (2022-09-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add hard limit on deref depth to prevent crashes ([ddde105](https://github.com/Redocly/redoc/commit/ddde105acaf0a77b0bb5d13df5fd6180bc8169e9))
|
||||
* do not use discriminator when specific schema was referenced in oneOf or anyOf ([#2153](https://github.com/Redocly/redoc/issues/2153)) ([6ac1e1e](https://github.com/Redocly/redoc/commit/6ac1e1eb183e97e2cd67ad14d8a39fac8289ebcc))
|
||||
* hoistOneOf missing refs stack and improve allOf for same $ref ([bb325d0](https://github.com/Redocly/redoc/commit/bb325d0d285c4cf4ee7c6d70878d2dd0dc9c6ed7))
|
||||
* latest docker cli tag ([#2140](https://github.com/Redocly/redoc/issues/2140)) ([8dc03eb](https://github.com/Redocly/redoc/commit/8dc03eb7ed262d6b1d460425ce43990710470845))
|
||||
* markdown parent name ([#2062](https://github.com/Redocly/redoc/issues/2062)) ([da9ed0b](https://github.com/Redocly/redoc/commit/da9ed0b4d1a4070d326ecb472459f0ff916c6036))
|
||||
|
||||
### Features
|
||||
|
||||
* feet: search feature to support path ([#2145](https://github.com/Redocly/redoc/issues/2145)) ([c52ee83f](https://github.com/Redocly/redoc/commit/c52ee83f77ccfc79137c85deafe8d93e68465d45))
|
||||
|
||||
|
||||
# [2.0.0-rc.76](https://github.com/Redocly/redoc/compare/v2.0.0-rc.75...v2.0.0-rc.76) (2022-08-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* "API Docs By Redocly" overlapping last element in sidebar ([#2132](https://github.com/Redocly/redoc/issues/2132)) ([c60c6f5](https://github.com/Redocly/redoc/commit/c60c6f58917563d57c0eef650b9dfcece2e15049))
|
||||
* encoding issue in CDN responses ([#2130](https://github.com/Redocly/redoc/issues/2130)) ([7816902](https://github.com/Redocly/redoc/commit/781690284a45b2b8af9eb525757632d0d19ef453))
|
||||
* Optional authentication not rendered properly ([#2117](https://github.com/Redocly/redoc/issues/2117)) ([#2134](https://github.com/Redocly/redoc/issues/2134)) ([efd5e09](https://github.com/Redocly/redoc/commit/efd5e09c907b36a3999f4c9c3165b6b2bdc1d536))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add clear cache for publish action ([#2129](https://github.com/Redocly/redoc/issues/2129)) ([d8093e3](https://github.com/Redocly/redoc/commit/d8093e3e2086874242eac82ddd202f35d5b8d558))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.75](https://github.com/Redocly/redoc/compare/v2.0.0-rc.74...v2.0.0-rc.75) (2022-08-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* duplication of title ([#2119](https://github.com/Redocly/redoc/issues/2119)) ([40ebfd2](https://github.com/Redocly/redoc/commit/40ebfd2d63758b37665e2e4447732f671811e2a5))
|
||||
* handle error if security scopes is invalid ([#2113](https://github.com/Redocly/redoc/issues/2113)) ([428fd69](https://github.com/Redocly/redoc/commit/428fd6983dc257f524121d98aeb1c58b39cf81f7))
|
||||
* publishing docker image to github packages ([#2115](https://github.com/Redocly/redoc/issues/2115)) ([250f6d1](https://github.com/Redocly/redoc/commit/250f6d12b2d31d2166990bd9cb83ca1c63509686))
|
||||
* Redocly logo ([#2109](https://github.com/Redocly/redoc/issues/2109)) ([a35bb3f](https://github.com/Redocly/redoc/commit/a35bb3ff26bf10b0e54383222df283800d6ee2c8))
|
||||
* search and navigate error ([cfd810f](https://github.com/Redocly/redoc/commit/cfd810fdf9d37862e07458fa1c3c04046e22f315))
|
||||
* sibling for openapi 3.1 ([#2112](https://github.com/Redocly/redoc/issues/2112)) ([0b1a790](https://github.com/Redocly/redoc/commit/0b1a79009010f0640a3030093b7c0dcf8caa49e4))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add notification about new version available ([#2100](https://github.com/Redocly/redoc/issues/2100)) ([d6ca8cc](https://github.com/Redocly/redoc/commit/d6ca8cc53b9667f09ce8fef88dfac1039c562b78))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.74](https://github.com/Redocly/redoc/compare/v2.0.0-rc.73...v2.0.0-rc.74) (2022-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* invalid url when href is empty ([#2105](https://github.com/Redocly/redoc/issues/2105)) ([e5f0235](https://github.com/Redocly/redoc/commit/e5f02359851a3797283ee513d734ab8e27266b92))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.73](https://github.com/Redocly/redoc/compare/v2.0.0-rc.72...v2.0.0-rc.73) (2022-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* add label API docs by Redocly ([#2099](https://github.com/Redocly/redoc/issues/2099)) ([dcdab83](https://github.com/Redocly/redoc/commit/dcdab838903a5d923c5e327d07d7743214769a61))
|
||||
* add the latest tag for the CLI docker image ([#2087](https://github.com/Redocly/redoc/issues/2087)) ([80ecd0f](https://github.com/Redocly/redoc/commit/80ecd0f19746379b056bfb1b11950693f3dc3724))
|
||||
* correct URLs of OperationModel servers for static site generation ([#2081](https://github.com/Redocly/redoc/issues/2081)) ([b1afd08](https://github.com/Redocly/redoc/commit/b1afd08bcf83770b537ed1eb9c90341de0162a1c))
|
||||
* enum duplication values when schema uses a specific combination of oneOf and allOf([#2088](https://github.com/Redocly/redoc/issues/2088)) ([e411847](https://github.com/Redocly/redoc/commit/e4118479f69209c5dd09a2be0e978834dcd9eb8f))
|
||||
* highlight text syntax ([#2069](https://github.com/Redocly/redoc/issues/2069)) ([4fc6aa0](https://github.com/Redocly/redoc/commit/4fc6aa0859c94e25fd30c4a4250455e44cc76488))
|
||||
* merge reference for openapi 3.1 ([#2063](https://github.com/Redocly/redoc/issues/2063)) ([87541e4](https://github.com/Redocly/redoc/commit/87541e45dc2526696deb32a6350a14a44a709b54))
|
||||
* nested patternProperties ([#2073](https://github.com/Redocly/redoc/issues/2073)) ([9920991](https://github.com/Redocly/redoc/commit/99209910806b85289a89fb3131049ed79118bc72))
|
||||
* operation url in static page ([#2093](https://github.com/Redocly/redoc/issues/2093)) ([98eec19](https://github.com/Redocly/redoc/commit/98eec19647b63f3598ec30fdeb428f614cf93ad4))
|
||||
* property with nested allOf ([#2083](https://github.com/Redocly/redoc/issues/2083)) ([7cc0500](https://github.com/Redocly/redoc/commit/7cc0500f3c1ddd1da17ee31278468207093f9281))
|
||||
* recursion for boolean items ([#2097](https://github.com/Redocly/redoc/issues/2097)) ([a5804db](https://github.com/Redocly/redoc/commit/a5804db1ce60ee6d90db8a3b54138eb1ca420c6f))
|
||||
* resolve dependency conflict in installing ([#2060](https://github.com/Redocly/redoc/issues/2060)) ([e26c8b2](https://github.com/Redocly/redoc/commit/e26c8b23d9b36abd5572bd0fe350d74a5cf65afb))
|
||||
* restore old variant security injections ([#2075](https://github.com/Redocly/redoc/issues/2075)) ([1a1bc26](https://github.com/Redocly/redoc/commit/1a1bc26503c06b6a7022289e5b9353bd59e48a9a))
|
||||
* rewrite recursive checks ([#2072](https://github.com/Redocly/redoc/issues/2072)) ([2970f95](https://github.com/Redocly/redoc/commit/2970f959cfa31cb4d5288ca23ca05cd34357dcec))
|
||||
* Scrolling keeps rewriting url after a Redoc element was removed [#2051](https://github.com/Redocly/redoc/issues/2051) ([#2085](https://github.com/Redocly/redoc/issues/2085)) ([0045be0](https://github.com/Redocly/redoc/commit/0045be0b753b8fb7d8d58a4e511783a6ba858444))
|
||||
* mis-nesting of aria roles on sidebar navigation ([#2050](https://github.com/Redocly/redoc/issues/2050)) ([7ca10da](https://github.com/Redocly/redoc/commit/7ca10daf12f2cac9fecf559b11f0f0c8bd21ae43))
|
||||
* 404 on the documentation page ([#2092](https://github.com/Redocly/redoc/issues/2050)) ([17bb08](https://github.com/Redocly/redoc/commit/17bb08909a1734e6e59c83ce29f31ae7cf6fc784))
|
||||
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.72](https://github.com/Redocly/redoc/compare/v2.0.0-rc.71...v2.0.0-rc.72) (2022-06-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* handled style change in ServerUrl and ServersOverlay dynamically ([#1989](https://github.com/Redocly/redoc/issues/1989)) ([a366de4](https://github.com/Redocly/redoc/commit/a366de4cf67fb94baa33b7b5c311cc1f54a63e53))
|
||||
* nested items with refs ([#2035](https://github.com/Redocly/redoc/issues/2035)) ([51127aa](https://github.com/Redocly/redoc/commit/51127aadc3e6b0f8e4066afb1c3b2ea6db453da2))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.71](https://github.com/Redocly/redoc/compare/v2.0.0-rc.70...v2.0.0-rc.71) (2022-05-31)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* constraints label details ([eb0917d](https://github.com/Redocly/redoc/commit/eb0917d002e57353027fee9c8f07605de8f1ff6f))
|
||||
* merge allOf in correct order ([#2020](https://github.com/Redocly/redoc/issues/2020)) ([1e4ea03](https://github.com/Redocly/redoc/commit/1e4ea03d4a9b7eddf3e4cc7cbdbd4d913583e837))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add hideSecuritySection option allowing to disable the Security panel ([#2027](https://github.com/Redocly/redoc/issues/2027)) ([49cc11d](https://github.com/Redocly/redoc/commit/49cc11d91795653ca870e9276a1e0cd617964e25))
|
||||
* add Redoc to Redocly CDN ([#2026](https://github.com/Redocly/redoc/issues/2026)) ([77104d6](https://github.com/Redocly/redoc/commit/77104d6c0d6f457aa08a158e93b52a45877be84e))
|
||||
* add support prefix items ([27a9dba](https://github.com/Redocly/redoc/commit/27a9dbaf46aded01a6512645dab27870a85cc73b))
|
||||
* remove auth section ([#2022](https://github.com/Redocly/redoc/issues/2022)) ([a863302](https://github.com/Redocly/redoc/commit/a863302cc803bdf27187c613157ba90af1040fc4))
|
||||
* show minProperties maxProperties ([#2015](https://github.com/Redocly/redoc/issues/2015)) ([82712c5](https://github.com/Redocly/redoc/commit/82712c5b408dc6bc142307d45fb962de2a43ffba))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.70](https://github.com/Redocly/redoc/compare/2.0.0-rc.69...2.0.0-rc.70) (2022-05-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* display patternProperties ([#2008](https://github.com/Redocly/redoc/issues/2008)) ([660cc85](https://github.com/Redocly/redoc/commit/660cc857bc86787e16237b407fe5f5d7a493bb48))
|
||||
* support conditional operators ([#1939](https://github.com/Redocly/redoc/issues/1939)) ([291b62a](https://github.com/Redocly/redoc/commit/291b62a206b68f8b4d98e4b74b71c0cad20a8b9b))
|
||||
* theme add links textDecoration options ([#1599](https://github.com/Redocly/redoc/issues/1599)) ([ba06485](https://github.com/Redocly/redoc/commit/ba06485ece27acbb6b846500817f4bff3e4997ba))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.69](https://github.com/Redocly/redoc/compare/v2.0.0-rc.68.1...v2.0.0-rc.69) (2022-05-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* wrong base url format causing error when constructing new URL ([#1996](https://github.com/Redocly/redoc/issues/1996)) ([d2cdaa1](https://github.com/Redocly/redoc/commit/d2cdaa1221b6a5e7b5da2418414bce1586069deb))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add download file option ([#1699](https://github.com/Redocly/redoc/issues/1699)) ([b601c9a](https://github.com/Redocly/redoc/commit/b601c9ae9e3288286f28e06854bd93cb3507706e))
|
||||
* add option to display verb in webhooks ([#1994](https://github.com/Redocly/redoc/issues/1994)) ([311d2ce](https://github.com/Redocly/redoc/commit/311d2ce64dcf1e68c2563a276b34dda0e08b709c))
|
||||
* support .redocly.yaml for options for redoc-cli ([#1981](https://github.com/Redocly/redoc/issues/1981)) ([1f417d6](https://github.com/Redocly/redoc/commit/1f417d67c6b2e0b49e41c713958c100d8e1ad19d))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.68](https://github.com/Redocly/redoc/compare/v2.0.0-rc.67...v2.0.0-rc.68) (2022-05-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* examples in json schema object([5b9aa27](https://github.com/Redocly/redoc/commit/5b9aa27af03a1c4616f7e0195afeba47d1deeaa0))
|
||||
* handle error when definition load fails ([#1979](https://github.com/Redocly/redoc/issues/1979)) ([508ebd5](https://github.com/Redocly/redoc/commit/508ebd58a3d66f2337e9641852322458a1bd9e6b))
|
||||
* large text in examples value ([#1974](https://github.com/Redocly/redoc/issues/1974)) ([60bc603](https://github.com/Redocly/redoc/commit/60bc603e9bb85a0c9c7ac38f7014876d397f0191))
|
||||
* not show scopes if keys empty or not exist ([#1975](https://github.com/Redocly/redoc/issues/1975)) ([4e793f0](https://github.com/Redocly/redoc/commit/4e793f07a81fa8bcd4ad384d1f87b3e6c290edb7))
|
||||
* remove dropdown-aria and use native select ([#1954](https://github.com/Redocly/redoc/issues/1954)) ([186f5a9](https://github.com/Redocly/redoc/commit/186f5a98bd466b1820121aadb865291bef8c6755))
|
||||
* make Redoc lib compatible with Webpack 5 ([#1982](https://github.com/Redocly/redoc/issues/1982)) ([867861](https://github.com/Redocly/redoc/commit/8678615a0e19c9484b4cd495d70293b542d196a5))
|
||||
|
||||
### Features
|
||||
|
||||
* implement configurable minimum characer length to init search ([#1402](https://github.com/Redocly/redoc/issues/1402)) ([0fa08fa](https://github.com/Redocly/redoc/commit/0fa08faab1c176a4bfc5a553e8e8f8b07aca659f))
|
||||
* support OAS 3.1 unevaluatedProperties ([#1978](https://github.com/Redocly/redoc/issues/1978)) ([0755ac6](https://github.com/Redocly/redoc/commit/0755ac6f04514eb0c08f90afceeda7858206b435))
|
||||
* publish dockerhub ([#1971](https://github.com/Redocly/redoc/issues/1971)) ([7e01a0](https://github.com/Redocly/redoc/commit/7e01a0cfe2ad8d06075bfc66ef3860edbef033f8))
|
||||
|
||||
|
||||
# [2.0.0-rc.67](https://github.com/Redocly/redoc/compare/v2.0.0-rc.66...v2.0.0-rc.67) (2022-04-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Expand/Collapse all buttons disappears for flat structures ([#1424](https://github.com/Redocly/redoc/issues/1424)) ([2ca8e08](https://github.com/Redocly/redoc/commit/2ca8e081baea6996eb01b5df27b8cd88331d5c96))
|
||||
* improve markdown render with CRLF ([#1953](https://github.com/Redocly/redoc/issues/1953)) ([aba2d1a](https://github.com/Redocly/redoc/commit/aba2d1ad2d8dda9f52055c36ebde1323457dfd3e))
|
||||
* issue with navigation when operationId contains backslash or quotes ([#1513](https://github.com/Redocly/redoc/issues/1513)) ([8f7e56c](https://github.com/Redocly/redoc/commit/8f7e56c747d88be5c5eb5c4bbaee0ff69e9cb2ec))
|
||||
* prefix operation ids with parent id ([#1245](https://github.com/Redocly/redoc/issues/1245)) ([fd8917e](https://github.com/Redocly/redoc/commit/fd8917e5c109840c1bfa4c2c0902b6dcec200286))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add optional BASE_PATH to Docker config ([#1378](https://github.com/Redocly/redoc/issues/1378)) ([90f71c0](https://github.com/Redocly/redoc/commit/90f71c0d77719871910cfba883a32ad131bef059))
|
||||
* theme add sidebar activeBackgroundColor and activeTextColor ([#1600](https://github.com/Redocly/redoc/issues/1600)) ([6716b08](https://github.com/Redocly/redoc/commit/6716b08e8871d95880e9f5a6c5491038002754e8))
|
||||
|
||||
|
||||
|
||||
# [2.0.0-rc.66](https://github.com/Redocly/redoc/compare/v2.0.0-rc.65...v2.0.0-rc.66) (2022-03-30)
|
||||
|
||||
|
||||
|
@ -12,6 +194,7 @@
|
|||
|
||||
* add support for displaying operationId in the sidebar ([#1927](https://github.com/Redocly/redoc/issues/1927)) ([09786f2](https://github.com/Redocly/redoc/commit/09786f2a5ade6303ea00512483b172347721ca70))
|
||||
* add nonce support ([#1566](https://github.com/Redocly/redoc/issues/1566)) ([c75ac9c](https://github.com/Redocly/redoc/commit/c75ac9cf70012e2d539b379aab2f0974d088db07))
|
||||
* h2 set color form theme.colors.text.primary ([#1491](https://github.com/Redocly/redoc/pull/1491)) ([25be93](https://github.com/Redocly/redoc/commit/25be934bb184d7b2b6b47d004b3c83ce4d16a2c6))
|
||||
|
||||
|
||||
|
||||
|
@ -23,7 +206,8 @@
|
|||
* auth link scroll for Firerox ([#1922](https://github.com/Redocly/redoc/issues/1922)) ([fe67e9c](https://github.com/Redocly/redoc/commit/fe67e9c332fee716582a00d60fdf34767bff22d4))
|
||||
* improve customization fab ([#1891](https://github.com/Redocly/redoc/issues/1891)) ([635f379](https://github.com/Redocly/redoc/commit/635f379eb086268c91eef715148eca8f080cfb86))
|
||||
* sanitize array of items ([#1920](https://github.com/Redocly/redoc/issues/1920)) ([059bd80](https://github.com/Redocly/redoc/commit/059bd8000e5fd65753d5ca9e0c47940394e0c79b))
|
||||
* use x-displayName in securityDefinitions [#1444](https://github.com/Redocly/redoc/pull/1444)) ([ac6fb4](https://github.com/Redocly/redoc/commit/
|
||||
* use x-displayName in securityDefinitions ([#1444](https://github.com/Redocly/redoc/pull/1444)) ([ac6fb4](https://github.com/Redocly/redoc/commit/ac6fb458a4eee8d0da4b63f9bafc7669adc8af03))
|
||||
* deprecated badge on one of any of buttons ([#1930](https://github.com/Redocly/redoc/pull/1930)) ([f60b47](https://github.com/Redocly/redoc/commit/f60b4758330dd756d670309827da60d3465b672a))
|
||||
|
||||
|
||||
|
||||
|
|
79
README.md
79
README.md
|
@ -1,15 +1,15 @@
|
|||
<div align="center">
|
||||
<img alt="Redoc logo" src="https://raw.githubusercontent.com/Redocly/redoc/master//docs/images/redoc.png" width="400px" />
|
||||
<img alt="Redoc logo" src="https://raw.githubusercontent.com/Redocly/redoc/main//docs/images/redoc.png" width="400px" />
|
||||
|
||||
# Generate interactive API documentation from OpenAPI definitions
|
||||
|
||||
[](https://travis-ci.com/Redocly/redoc) [](https://coveralls.io/github/Redocly/redoc?branch=master) [](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/master/LICENSE)
|
||||
[](https://coveralls.io/github/Redocly/redoc?branch=main) [](https://www.npmjs.com/package/redoc) [](https://github.com/Redocly/redoc/blob/main/LICENSE)
|
||||
|
||||
[](https://cdn.jsdelivr.net/npm/redoc/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc) [](https://hub.docker.com/r/redocly/redoc/)
|
||||
[](https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js) [](https://www.npmjs.com/package/redoc) [](https://www.jsdelivr.com/package/npm/redoc) [](https://hub.docker.com/r/redocly/redoc/)
|
||||
</div>
|
||||
|
||||
**This is the README for the `2.x` version of Redoc (React-based).**
|
||||
**The README for the `1.x` version is on the [v1.x](https://github.com/Redocly/redoc/tree/v1.x) branch**
|
||||
**The README for the `1.x` version is on the [v1.x](https://github.com/Redocly/redoc/tree/v1.x) branch.**
|
||||
|
||||
## About Redoc
|
||||
|
||||
|
@ -21,7 +21,7 @@ By default Redoc offers a three-panel, responsive layout:
|
|||
- The central panel contains the documentation.
|
||||
- The right panel contains request and response examples.
|
||||
|
||||

|
||||

|
||||
|
||||
## Live demo
|
||||
|
||||
|
@ -72,16 +72,16 @@ Checkout the following feature comparison of Redocly's premium products versus R
|
|||
|
||||
Refer to the Redocly's documentation for more information on these products:
|
||||
|
||||
- [Portals](https://redoc.ly/docs/developer-portal/introduction/)
|
||||
- [Reference](https://redoc.ly/docs/api-reference-docs/getting-started/)
|
||||
- [Redoc](https://redoc.ly/docs/redoc/quickstart/intro/)
|
||||
- [Portals](https://redocly.com/docs/developer-portal/introduction/)
|
||||
- [Reference](https://redocly.com/docs/api-reference-docs/getting-started/)
|
||||
- [Redoc](https://redocly.com/docs/redoc/quickstart/intro/)
|
||||
|
||||
## Features
|
||||
- Responsive three-panel design with menu/scrolling synchronization
|
||||
- [Multiple deployment options](https://redoc.ly/docs/redoc/quickstart/intro/)
|
||||
- [Server-side rendering (SSR) ready](https://redoc.ly/docs/redoc/quickstart/cli/#redoc-cli-commands)
|
||||
- [Multiple deployment options](https://redocly.com/docs/redoc/quickstart/intro/)
|
||||
- [Server-side rendering (SSR) ready](https://redocly.com/docs/redoc/quickstart/cli/#redoc-cli-commands)
|
||||
- Ability to integrate your API introduction into the side menu
|
||||
- [Simple integration with `create-react-app`](https://redoc.ly/docs/redoc/quickstart/react/)
|
||||
- [Simple integration with `create-react-app`](https://redocly.com/docs/redoc/quickstart/react/)
|
||||
|
||||
[Example repo](https://github.com/APIs-guru/create-react-app-redoc)
|
||||
- [Command-line interface to bundle your docs into a **zero-dependency** HTML file](https://redoc.ly/docs/redoc/quickstart/cli/)
|
||||
|
@ -89,9 +89,9 @@ Refer to the Redocly's documentation for more information on these products:
|
|||

|
||||
|
||||
## Customization options
|
||||
[<img alt="Customization services" src="http://i.imgur.com/c4sUF7M.png" height="60px">](https://redoc.ly/#services)
|
||||
- High-level grouping in side-menu with the [`x-tagGroups`](https://redoc.ly/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension
|
||||
- Branding/customizations using the [`theme` option](https://redoc.ly/docs/api-reference-docs/configuration/theming/)
|
||||
[<img alt="Customization services" src="http://i.imgur.com/c4sUF7M.png" height="60px">](https://redocly.com/#services)
|
||||
- High-level grouping in side-menu with the [`x-tagGroups`](https://redocly.com/docs/api-reference-docs/specification-extensions/x-tag-groups/) specification extension
|
||||
- Branding/customizations using the [`theme` option](https://redocly.com/docs/api-reference-docs/configuration/theming/)
|
||||
|
||||
## Support
|
||||
- OpenAPI v3.0 support
|
||||
|
@ -102,9 +102,9 @@ Refer to the Redocly's documentation for more information on these products:
|
|||

|
||||
|
||||
## Releases
|
||||
**Important:** all the 2.x releases are deployed to npm and can be used with jsdeliver:
|
||||
- particular release, for example, `v2.0.0-alpha.15`: https://cdn.jsdelivr.net/npm/redoc@2.0.0-alpha.17/bundles/redoc.standalone.js
|
||||
- `next` release: https://cdn.jsdelivr.net/npm/redoc@next/bundles/redoc.standalone.js
|
||||
**Important:** all the 2.x releases are deployed to npm and can be used with Redocly-cdn:
|
||||
- particular release, for example, `v2.0.0`: https://cdn.redoc.ly/redoc/v2.0.0/bundles/redoc.standalone.js
|
||||
- `latest` release: https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js
|
||||
|
||||
Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(deprecated)**:
|
||||
- particular release, for example `v1.2.0`: https://rebilly.github.io/ReDoc/releases/v1.2.0/redoc.min.js
|
||||
|
@ -131,11 +131,11 @@ Additionally, all the 1.x releases are hosted on our GitHub Pages-based CDN **(d
|
|||
|
||||
## Lint OpenAPI definitions
|
||||
|
||||
Redocly's OpenAPI CLI is an open source command-line tool that you can use to lint
|
||||
Redocly's CLI is an [open source command-line tool](https://github.com/Redocly/redocly-cli) that you can use to lint
|
||||
your OpenAPI definition. Linting helps you to catch errors and inconsistencies in your
|
||||
OpenAPI definition before publishing.
|
||||
|
||||
Refer to [Lint configuration](https://redoc.ly/docs/cli/guides/lint/) in the OpenAPI documentation for more information.
|
||||
Refer to [Redocly configuration](https://redocly.com/docs/cli/configuration/) in the OpenAPI documentation for more information.
|
||||
|
||||
## Deployment
|
||||
|
||||
|
@ -166,7 +166,7 @@ replace the `spec-url` attribute with the url or local file address to your defi
|
|||
</head>
|
||||
<body>
|
||||
<redoc spec-url='http://petstore.swagger.io/v2/swagger.json'></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
|
||||
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
@ -174,11 +174,11 @@ replace the `spec-url` attribute with the url or local file address to your defi
|
|||
|
||||
For step-by-step instructions for how to get started using Redoc
|
||||
to render your OpenAPI definition, refer to the
|
||||
[**Redoc quickstart guide**](https://redoc.ly/docs/redoc/quickstart/intro/).
|
||||
[**Redoc quickstart guide**](https://redocly.com/docs/redoc/quickstart/) and [**How to use the HTML element**](https://redocly.com/docs/redoc/deployment/html/).
|
||||
|
||||
## Redoc CLI
|
||||
For more information on Redoc's commmand-line interface, refer to
|
||||
[**Using the Redoc CLI**](https://redoc.ly/docs/redoc/quickstart/cli/).
|
||||
[**Using the Redoc CLI**](https://redocly.com/docs/redoc/deployment/cli/).
|
||||
|
||||
|
||||
## Configuration
|
||||
|
@ -188,7 +188,7 @@ You can inject the Security Definitions widget into any place in your definition
|
|||
For more information, refer to [Security definitions injection](docs/security-definitions-injection.md).
|
||||
|
||||
### OpenAPI specification extensions
|
||||
Redoc uses the following [specification extensions](https://swagger.io/specification/#specificationExtensions):
|
||||
Redoc uses the following [specification extensions](https://redocly.com/docs/api-reference-docs/spec-extensions/):
|
||||
* [`x-logo`](docs/redoc-vendor-extensions.md#x-logo) - is used to specify API logo
|
||||
* [`x-traitTag`](docs/redoc-vendor-extensions.md#x-traitTag) - useful for handling out common things like Pagination, Rate-Limits, etc
|
||||
* [`x-codeSamples`](docs/redoc-vendor-extensions.md#x-codeSamples) - specify operation code samples
|
||||
|
@ -207,11 +207,14 @@ Redoc uses the following [specification extensions](https://swagger.io/specifica
|
|||
You can use all of the following options with the standalone version of the <redoc> tag by kebab-casing them. For example, `scrollYOffset` becomes `scroll-y-offset`, and `expandResponses` becomes `expand-responses`.
|
||||
|
||||
* `disableSearch` - disable search indexing and search box.
|
||||
* `minCharacterLengthToInitSearch` - set minimal characters length to init search, default `3`, minimal `1`.
|
||||
* `expandDefaultServerVariables` - enable expanding default server variables, default `false`.
|
||||
* `expandResponses` - specify which responses to expand by default by response codes. Values should be passed as comma-separated list without spaces e.g. `expandResponses="200,201"`. Special value `"all"` expands all responses by default. Be careful: this option can slow-down documentation rendering time.
|
||||
* `generatedPayloadSamplesMaxDepth` - set the maximum render depth for JSON payload samples (responses and request body). The default value is `10`.
|
||||
* `maxDisplayedEnumValues` - display only specified number of enum values. hide rest values under spoiler.
|
||||
* `hideDownloadButton` - do not show "Download" spec button. **THIS DOESN'T MAKE YOUR SPEC PRIVATE**, it just hides the button.
|
||||
* `downloadFileName` - set a custom file name for the downloaded API definition file.
|
||||
* `downloadDefinitionUrl` - If the 'Download' button is visible in the API reference documentation (hideDownloadButton=false), the URL configured here will open when that button is selected. Provide it as an absolute URL with the full URI scheme.
|
||||
* `hideHostname` - if set, the protocol and hostname is not shown in the operation definition.
|
||||
* `hideLoading` - do not show loading animation. Useful for small docs.
|
||||
* `hideFab` - do not show FAB in mobile view. Useful for implementing a custom floating action button.
|
||||
|
@ -229,7 +232,6 @@ You can use all of the following options with the standalone version of the <red
|
|||
* `lazyRendering` - _Not implemented yet_ ~~if set, enables lazy rendering mode in ReDoc. This mode is useful for APIs with big number of operations (e.g. > 50). In this mode ReDoc shows initial screen ASAP and then renders the rest operations asynchronously while showing progress bar on the top. Check out the [demo](\\redocly.github.io/redoc) for the example.~~
|
||||
* `menuToggle` - if true clicking second time on expanded menu item will collapse it, default `true`.
|
||||
* `nativeScrollbars` - use native scrollbar for sidemenu instead of perfect-scroll (scrolling performance optimization for big specs).
|
||||
* `noAutoAuth` - do not inject Authentication section automatically.
|
||||
* `onlyRequiredInSamples` - shows only required fields in request samples.
|
||||
* `pathInMiddlePanel` - show path link and HTTP verb in the middle panel instead of the right one.
|
||||
* `requiredPropsFirst` - show required properties first ordered in the same order as in `required` array.
|
||||
|
@ -238,16 +240,17 @@ You can use all of the following options with the standalone version of the <red
|
|||
* **number**: A fixed number of pixels to be used as offset.
|
||||
* **selector**: selector of the element to be used for specifying the offset. The distance from the top of the page to the element's bottom will be used as offset.
|
||||
* **function**: A getter function. Must return a number representing the offset (in pixels).
|
||||
* `showExtensions` - show vendor extensions ("x-" fields). Extensions used by ReDoc are ignored. Can be boolean or an array of `string` with names of extensions to display.
|
||||
* `showExtensions` - show vendor extensions ("x-" fields). Extensions used by Redoc are ignored. Can be boolean or an array of `string` with names of extensions to display.
|
||||
* `sortPropsAlphabetically` - sort properties alphabetically.
|
||||
* `payloadSampleIdx` - if set, payload sample will be inserted at this index or last. Indexes start from 0.
|
||||
* `theme` - ReDoc theme. For details check [theme docs](#redoc-theme-object).
|
||||
* `theme` - Redoc theme. For details check [theme docs](#redoc-theme-object).
|
||||
* `untrustedSpec` - if set, the spec is considered untrusted and all HTML/markdown is sanitized to prevent XSS. **Disabled by default** for performance reasons. **Enable this option if you work with untrusted user data!**
|
||||
* `nonce` - if set, the provided value will be injected in every injected HTML element in the `nonce` attribute. Useful when using CSP, see https://webpack.js.org/guides/csp/.
|
||||
* `sideNavStyle` - can be specified in various ways:
|
||||
* **summary-only**: displays a summary in the sidebar navigation item. (**default**)
|
||||
* **path-only**: displays a path in the sidebar navigation item.
|
||||
* **id-only**: displays the operation id with a fallback to the path in the sidebar navigation item.
|
||||
* `showWebhookVerb` - when set to `true`, shows the HTTP request method for webhooks in operations and in the sidebar.
|
||||
|
||||
### `<redoc>` theme object
|
||||
* `spacing`
|
||||
|
@ -285,26 +288,38 @@ You can use all of the following options with the standalone version of the <red
|
|||
* `color`: # COMPUTED: colors.primary.main
|
||||
* `visited`: # COMPUTED: typography.links.color
|
||||
* `hover`: # COMPUTED: lighten(0.2 typography.links.color)
|
||||
* `menu`
|
||||
* `textDecoration`: 'auto'
|
||||
* `hoverTextDecoration`: 'auto'
|
||||
* `sidebar`
|
||||
* `width`: '260px'
|
||||
* `backgroundColor`: '#fafafa'
|
||||
* `textColor`: '#333333'
|
||||
* `activeTextColor`: # COMPUTED: theme.menu.textColor (if set by user) or theme.colors.primary.main
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.textColor (if set by user) or theme.colors.primary.main
|
||||
* `groupItems` # Group headings
|
||||
* `activeBackgroundColor`: # COMPUTED: theme.sidebar.backgroundColor
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.activeTextColor
|
||||
* `textTransform`: 'uppercase'
|
||||
* `level1Items` # Level 1 items like tags or section 1st level items
|
||||
* `activeBackgroundColor`: # COMPUTED: theme.sidebar.backgroundColor
|
||||
* `activeTextColor`: # COMPUTED: theme.sidebar.activeTextColor
|
||||
* `textTransform`: 'none'
|
||||
* `arrow` # menu arrow
|
||||
* `arrow` # sidebar arrow
|
||||
* `size`: '1.5em'
|
||||
* `color`: # COMPUTED: theme.menu.textColor
|
||||
* `color`: # COMPUTED: theme.sidebar.textColor
|
||||
* `logo`
|
||||
* `maxHeight`: # COMPUTED: menu.width
|
||||
* `maxWidth`: # COMPUTED: menu.width
|
||||
* `maxHeight`: # COMPUTED: sidebar.width
|
||||
* `maxWidth`: # COMPUTED: sidebar.width
|
||||
* `gutter`: '2px' # logo image padding
|
||||
* `rightPanel`
|
||||
* `backgroundColor`: '#263238'
|
||||
* `width`: '40%'
|
||||
* `textColor`: '#ffffff'
|
||||
* `servers`
|
||||
* `overlay`
|
||||
* `backgroundColor`: '#fafafa'
|
||||
* `textColor`: '#263238'
|
||||
* `url`
|
||||
* `backgroundColor`: '#fff'
|
||||
* `fab`
|
||||
* `backgroundColor`: '#263238'
|
||||
* `color`: '#ffffff'
|
||||
|
|
|
@ -14,7 +14,7 @@ The two following commands are available:
|
|||
- `redoc-cli serve [spec]` - starts the server with `spec` rendered with ReDoc.
|
||||
Supports a server-side rendering mode (`--ssr`)
|
||||
and can watch the spec (`--watch`) to automatically reload the page whenever it changes.\
|
||||
Deprecated. Use `npx @redocly/openapi-cli preview-docs [spec]`
|
||||
Deprecated. Use `npx @redocly/cli preview-docs [spec]`
|
||||
- `redoc-cli bundle [spec]` - bundles `spec` and Redoc into a **zero-dependency** HTML file.\
|
||||
Deprecated. Use Use "build" command instead.
|
||||
- `redoc-cli build [spec]` - build `spec` and Redoc into a **zero-dependency** HTML file.
|
||||
|
@ -26,9 +26,15 @@ Some examples:
|
|||
- Serve with the `nativeScrollbars` option set to true:<br/>
|
||||
`$ redoc-cli serve [spec] --options.nativeScrollbars`
|
||||
- Bundle using a custom [Handlebars](https://handlebarsjs.com/) template
|
||||
(check the [default template](https://github.com/Redocly/redoc/blob/master/cli/template.hbs) for an example):<br/>
|
||||
(check the [default template](https://github.com/Redocly/redoc/blob/main/cli/template.hbs) for an example):<br/>
|
||||
`$ redoc-cli build [spec] -t custom.hbs`
|
||||
- Bundle using a custom template and add custom `templateOptions`:<br/>
|
||||
`$ redoc-cli build [spec] -t custom.hbs --templateOptions.metaDescription "Page meta description"`
|
||||
|
||||
#### With a Redocly configuration file ([more info](https://redocly.com/docs/cli/configuration/#redocly-configuration-file)):
|
||||
|
||||
1. Go to folder with your Redocly configuration file (`.redocly.yaml` or `redocly.yaml`) and your OpenAPI definition file.
|
||||
2. Build the site using the `build` command (options from the Redocly configuration file will be automatically fetched):
|
||||
`redoc build openapi.yaml`
|
||||
|
||||
For more details, run `redoc-cli --help`.
|
||||
|
|
2
cli/__test__/build/configRedoc/.redocly.yaml
Normal file
2
cli/__test__/build/configRedoc/.redocly.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
features.openapi:
|
||||
disableSearch: true
|
30
cli/__test__/build/configRedoc/index.test.ts
Normal file
30
cli/__test__/build/configRedoc/index.test.ts
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { spawnSync } from 'child_process';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
describe('build', () => {
|
||||
it('should use .redocly.yaml', () => {
|
||||
const r = spawnSync(
|
||||
'ts-node',
|
||||
['../../../index.ts', 'build', ' ../../../../demo/openapi.yaml', '--output=redoc-test.html'],
|
||||
{
|
||||
cwd: __dirname,
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
|
||||
const out = r.stdout.toString('utf-8');
|
||||
const err = r.stderr.toString('utf-8');
|
||||
const result = `${out}\n${err}`;
|
||||
|
||||
try {
|
||||
const redocStaticFile = readFileSync(`${__dirname}/redoc-test.html`, 'utf8');
|
||||
expect(redocStaticFile).toContain('"options":{"disableSearch":true}');
|
||||
expect(redocStaticFile).not.toContain('role="search"');
|
||||
} catch (err) {
|
||||
expect(err.toString()).toContain('{"options":{"disableSearch":"true"}');
|
||||
}
|
||||
|
||||
expect(result).toContain('Found .redocly.yaml and using features.openapi options');
|
||||
expect(result).toContain('bundled successfully');
|
||||
});
|
||||
});
|
34
cli/__test__/build/configRedoc/inline-options.test.ts
Normal file
34
cli/__test__/build/configRedoc/inline-options.test.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { spawnSync } from 'child_process';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
describe('build with inline options', () => {
|
||||
it('should use inline options and ignore .redocly.yaml', () => {
|
||||
const r = spawnSync(
|
||||
'ts-node',
|
||||
[
|
||||
'../../../index.ts',
|
||||
'build',
|
||||
' ../../../../demo/openapi.yaml',
|
||||
'--options.disableSearch="false" ',
|
||||
],
|
||||
{
|
||||
cwd: __dirname,
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
|
||||
const out = r.stdout.toString('utf-8');
|
||||
const err = r.stderr.toString('utf-8');
|
||||
const result = `${out}\n${err}`;
|
||||
expect(result).not.toContain('Found .redocly.yaml and using features.openapi options');
|
||||
expect(result).toContain('bundled successfully');
|
||||
|
||||
try {
|
||||
const redocStaticFile = readFileSync(`${__dirname}/redoc-static.html`, 'utf8');
|
||||
expect(redocStaticFile).toContain('"options":{"disableSearch":"false"}');
|
||||
expect(redocStaticFile).toContain('role="search"');
|
||||
} catch (err) {
|
||||
expect(err.toString()).toContain('"options":{"disableSearch":"false"}');
|
||||
}
|
||||
});
|
||||
});
|
24
cli/__test__/build/configRedoc/url.test.ts
Normal file
24
cli/__test__/build/configRedoc/url.test.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { spawnSync } from 'child_process';
|
||||
|
||||
describe('build with url', () => {
|
||||
it('should not fail on resolving url', () => {
|
||||
const r = spawnSync(
|
||||
'ts-node',
|
||||
[
|
||||
'../../../index.ts',
|
||||
'build',
|
||||
'http://petstore.swagger.io/v2/swagger.json',
|
||||
'--output=url-test.html',
|
||||
],
|
||||
{
|
||||
cwd: __dirname,
|
||||
shell: true,
|
||||
},
|
||||
);
|
||||
|
||||
const out = r.stdout.toString('utf-8');
|
||||
const err = r.stderr.toString('utf-8');
|
||||
const result = `${out}\n${err}`;
|
||||
expect(result).toContain('bundled successfully');
|
||||
});
|
||||
});
|
38
cli/index.ts
38
cli/index.ts
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/env node
|
||||
/* tslint:disable:no-implicit-dependencies */
|
||||
import * as React from 'react';
|
||||
import * as updateNotifier from 'update-notifier';
|
||||
import { renderToString } from 'react-dom/server';
|
||||
import { ServerStyleSheet } from 'styled-components';
|
||||
|
||||
|
@ -25,6 +26,12 @@ import {
|
|||
import * as mkdirp from 'mkdirp';
|
||||
|
||||
import * as YargsParser from 'yargs';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { findConfig } from '@redocly/openapi-core';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { parseYaml } from '@redocly/openapi-core';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { Config } from '@redocly/openapi-core';
|
||||
|
||||
interface Options {
|
||||
ssr?: boolean;
|
||||
|
@ -105,6 +112,7 @@ const handlerForBuildCommand = async (argv: any) => {
|
|||
};
|
||||
|
||||
try {
|
||||
notifyUpdateCliVersion();
|
||||
await bundle(argv.spec, config);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
|
@ -168,6 +176,7 @@ YargsParser.command(
|
|||
};
|
||||
|
||||
try {
|
||||
notifyUpdateCliVersion();
|
||||
await serve(argv.host as string, argv.port as number, argv.spec as string, config);
|
||||
} catch (e) {
|
||||
handleError(e);
|
||||
|
@ -176,7 +185,7 @@ YargsParser.command(
|
|||
[
|
||||
res => {
|
||||
console.log(
|
||||
`\n⚠️ This command is deprecated. Use "npx @redocly/openapi-cli preview-docs petstore.yaml"\n`,
|
||||
`\n⚠️ This command is deprecated. Use "npx @redocly/cli preview-docs petstore.yaml"\n`,
|
||||
);
|
||||
return res;
|
||||
},
|
||||
|
@ -334,6 +343,7 @@ async function getPageHTML(
|
|||
const specUrl = redocOptions.specUrl || (isURL(pathToSpec) ? pathToSpec : undefined);
|
||||
const store = await createStore(spec, specUrl, redocOptions);
|
||||
const sheet = new ServerStyleSheet();
|
||||
// @ts-ignore
|
||||
html = renderToString(sheet.collectStyles(React.createElement(Redoc, { store })));
|
||||
css = sheet.getStyleTags();
|
||||
state = await store.toJS();
|
||||
|
@ -361,7 +371,7 @@ async function getPageHTML(
|
|||
</script>`,
|
||||
redocHead: ssr
|
||||
? (cdn
|
||||
? '<script src="https://unpkg.com/redoc@next/bundles/redoc.standalone.js"></script>'
|
||||
? '<script src="https://unpkg.com/redoc@latest/bundles/redoc.standalone.js"></script>'
|
||||
: `<script>${redocStandaloneSrc}</script>`) + css
|
||||
: '<script src="redoc.standalone.js"></script>',
|
||||
title: title || spec.info.title || 'ReDoc documentation',
|
||||
|
@ -447,6 +457,30 @@ function getObjectOrJSON(options) {
|
|||
handleError(e);
|
||||
}
|
||||
default:
|
||||
const configFile = findConfig();
|
||||
if (configFile) {
|
||||
console.log(`Found ${configFile} and using features.openapi options`);
|
||||
try {
|
||||
const config = parseYaml(readFileSync(configFile, 'utf-8')) as Config;
|
||||
|
||||
return config['features.openapi'];
|
||||
} catch (e) {
|
||||
console.warn(`Found ${configFile} but failed to parse: ${e.message}`);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
function notifyUpdateCliVersion() {
|
||||
const pkg = require('./package.json');
|
||||
const notifier = updateNotifier({
|
||||
pkg,
|
||||
updateCheckInterval: 0,
|
||||
shouldNotifyInNpmScript: true,
|
||||
});
|
||||
notifier.notify({
|
||||
message:
|
||||
'Run `{updateCommand}` to update.\nChangelog: https://github.com/Redocly/redoc/releases/tag/{latestVersion}',
|
||||
});
|
||||
}
|
||||
|
|
2156
cli/npm-shrinkwrap.json
generated
2156
cli/npm-shrinkwrap.json
generated
File diff suppressed because it is too large
Load Diff
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "redoc-cli",
|
||||
"version": "0.13.10",
|
||||
"version": "0.13.20",
|
||||
"description": "ReDoc's Command Line Interface",
|
||||
"main": "index.js",
|
||||
"bin": "index.js",
|
||||
|
@ -19,16 +19,15 @@
|
|||
"node-libs-browser": "^2.2.1",
|
||||
"react": "^17.0.1",
|
||||
"react-dom": "^17.0.1",
|
||||
"redoc": "2.0.0-rc.66",
|
||||
"redoc": "2.0.0-rc.77",
|
||||
"styled-components": "^5.3.0",
|
||||
"update-notifier": "^5.0.1",
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chokidar": "^2.1.3",
|
||||
"@types/handlebars": "^4.1.0",
|
||||
"@types/mkdirp": "^1.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
# npm i -g http-server
|
||||
# http-server -p 8000 --cors
|
||||
|
||||
FROM node:alpine
|
||||
FROM node:12-alpine
|
||||
|
||||
RUN apk update && apk add --no-cache git
|
||||
|
||||
|
@ -26,6 +26,7 @@ FROM nginx:alpine
|
|||
|
||||
ENV PAGE_TITLE="ReDoc"
|
||||
ENV PAGE_FAVICON="favicon.png"
|
||||
ENV BASE_PATH=
|
||||
ENV SPEC_URL="http://petstore.swagger.io/v2/swagger.json"
|
||||
ENV PORT=80
|
||||
ENV REDOC_OPTIONS=
|
||||
|
|
|
@ -45,9 +45,10 @@ Another issue with OpenShift is that the default exposed port `80` cannot be use
|
|||
|
||||
- `PAGE_TITLE` (default `"ReDoc"`) - page title
|
||||
- `PAGE_FAVICON` (default `"favicon.png"`) - URL to page favicon
|
||||
- `BASE_PATH` (optional) - prepend favicon & standalone bundle with this path
|
||||
- `SPEC_URL` (default `"http://petstore.swagger.io/v2/swagger.json"`) - URL to spec
|
||||
- `PORT` (default `80`) - nginx port
|
||||
- `REDOC_OPTIONS` - [`<redoc>` tag attributes](https://github.com/Redocly/redoc#redoc-tag-attributes)
|
||||
- `REDOC_OPTIONS` (optional) - [`<redoc>` tag attributes](https://github.com/Redocly/redoc#redoc-tag-attributes)
|
||||
|
||||
## Build
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ set -e
|
|||
|
||||
sed -i -e "s|%PAGE_TITLE%|$PAGE_TITLE|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%PAGE_FAVICON%|$PAGE_FAVICON|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%BASE_PATH%|$BASE_PATH|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%SPEC_URL%|$SPEC_URL|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|%REDOC_OPTIONS%|${REDOC_OPTIONS}|g" /usr/share/nginx/html/index.html
|
||||
sed -i -e "s|\(listen\s*\) [0-9]*|\1 ${PORT}|g" /etc/nginx/nginx.conf
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>%PAGE_TITLE%</title>
|
||||
<link rel="icon" href="%PAGE_FAVICON%" />
|
||||
<link rel="icon" href="%BASE_PATH%%PAGE_FAVICON%" />
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
|
@ -23,6 +23,6 @@
|
|||
|
||||
<body>
|
||||
<redoc spec-url="%SPEC_URL%" %REDOC_OPTIONS%></redoc>
|
||||
<script src="redoc.standalone.js"></script>
|
||||
<script src="%BASE_PATH%redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import * as React from 'react';
|
||||
import { render } from 'react-dom';
|
||||
import styled from 'styled-components';
|
||||
import { resolve as urlResolve } from 'url';
|
||||
import { RedocStandalone } from '../src';
|
||||
import ComboBox from './ComboBox';
|
||||
import FileInput from './components/FileInput';
|
||||
|
@ -22,7 +21,7 @@ const demos = [
|
|||
];
|
||||
|
||||
class DemoApp extends React.Component<
|
||||
{},
|
||||
Record<string, unknown>,
|
||||
{ spec: object | undefined; specUrl: string; dropdownOpen: boolean; cors: boolean }
|
||||
> {
|
||||
constructor(props) {
|
||||
|
@ -87,7 +86,7 @@ class DemoApp extends React.Component<
|
|||
let proxiedUrl = specUrl;
|
||||
if (specUrl !== DEFAULT_SPEC) {
|
||||
proxiedUrl = cors
|
||||
? '\\\\cors.redoc.ly/' + urlResolve(window.location.href, specUrl)
|
||||
? '\\\\cors.redoc.ly/' + new URL(specUrl, window.location.href).href
|
||||
: specUrl;
|
||||
}
|
||||
return (
|
||||
|
@ -95,7 +94,7 @@ class DemoApp extends React.Component<
|
|||
<Heading>
|
||||
<a href=".">
|
||||
<Logo
|
||||
src="https://github.com/Redocly/redoc/raw/master/docs/images/redoc-logo.png"
|
||||
src="https://github.com/Redocly/redoc/raw/main/docs/images/redoc.png"
|
||||
alt="Redoc logo"
|
||||
/>
|
||||
</a>
|
||||
|
|
|
@ -16,14 +16,14 @@ info:
|
|||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
|
||||
# OpenAPI Specification
|
||||
This API is documented in **OpenAPI format** and is based on
|
||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
|
||||
# Cross-Origin Resource Sharing
|
||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||
|
@ -88,12 +88,14 @@ x-tagGroups:
|
|||
tags:
|
||||
- pet_model
|
||||
- store_model
|
||||
security:
|
||||
- {}
|
||||
paths:
|
||||
/pet:
|
||||
parameters:
|
||||
- name: Accept-Language
|
||||
in: header
|
||||
description: "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US"
|
||||
description: 'The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US'
|
||||
example: en-US
|
||||
required: false
|
||||
schema:
|
||||
|
@ -182,6 +184,16 @@ paths:
|
|||
}
|
||||
requestBody:
|
||||
$ref: '#/components/requestBodies/Pet'
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: OperationId with quotes
|
||||
operationId: deletePetBy"Id
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: OperationId with backslash
|
||||
operationId: delete\PetById
|
||||
'/pet/{petId}':
|
||||
get:
|
||||
tags:
|
||||
|
@ -259,7 +271,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "Bearer <TOKEN>"
|
||||
example: 'Bearer <TOKEN>'
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
|
@ -295,6 +307,9 @@ paths:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
unevaluatedProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
$ref: '#/components/schemas/ApiResponse'
|
||||
security:
|
||||
- petstore_auth:
|
||||
|
@ -432,7 +447,7 @@ paths:
|
|||
application/json:
|
||||
example:
|
||||
status: 400
|
||||
message: "Invalid Order"
|
||||
message: 'Invalid Order'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
|
@ -894,11 +909,11 @@ paths:
|
|||
type: string
|
||||
examples:
|
||||
response:
|
||||
value: <Message> OK </Message>
|
||||
value: <Message> OK </Message>
|
||||
text/plain:
|
||||
examples:
|
||||
response:
|
||||
value: OK
|
||||
value: OK
|
||||
'400':
|
||||
description: Invalid username/password supplied
|
||||
/user/logout:
|
||||
|
@ -925,7 +940,7 @@ components:
|
|||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Cat"
|
||||
$ref: '#/components/schemas/Cat'
|
||||
responses:
|
||||
'200':
|
||||
description: update Cat details
|
||||
|
@ -940,13 +955,40 @@ components:
|
|||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Cat"
|
||||
$ref: '#/components/schemas/Cat'
|
||||
responses:
|
||||
'200':
|
||||
description: create Cat details
|
||||
schemas:
|
||||
ApiResponse:
|
||||
type: object
|
||||
patternProperties:
|
||||
^S_\\w+\\.[1-9]{2,4}$:
|
||||
description: The measured skill for hunting
|
||||
if:
|
||||
x-displayName: fieldName === 'status'
|
||||
else:
|
||||
minLength: 1
|
||||
maxLength: 10
|
||||
then:
|
||||
format: url
|
||||
type: string
|
||||
enum:
|
||||
- success
|
||||
- failed
|
||||
^O_\\w+\\.[1-9]{2,4}$:
|
||||
type: object
|
||||
properties:
|
||||
nestedProperty:
|
||||
type: [string, boolean]
|
||||
description: The measured skill for hunting
|
||||
default: lazy
|
||||
example: adventurous
|
||||
enum:
|
||||
- clueless
|
||||
- lazy
|
||||
- adventurous
|
||||
- aggressive
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
|
@ -962,7 +1004,7 @@ components:
|
|||
- type: object
|
||||
properties:
|
||||
huntingSkill:
|
||||
type: string
|
||||
type: [string, boolean]
|
||||
description: The measured skill for hunting
|
||||
default: lazy
|
||||
example: adventurous
|
||||
|
@ -1073,8 +1115,8 @@ components:
|
|||
properties:
|
||||
id:
|
||||
externalDocs:
|
||||
description: "Find more info here"
|
||||
url: "https://example.com"
|
||||
description: 'Find more info here'
|
||||
url: 'https://example.com'
|
||||
description: Pet ID
|
||||
$ref: '#/components/schemas/Id'
|
||||
category:
|
||||
|
@ -1086,15 +1128,26 @@ components:
|
|||
example: Guru
|
||||
photoUrls:
|
||||
description: The list of URL to a cute photos featuring pet
|
||||
type: [string, integer, 'null', array]
|
||||
type: [string, integer, 'null']
|
||||
minItems: 1
|
||||
maxItems: 20
|
||||
maxItems: 10
|
||||
xml:
|
||||
name: photoUrl
|
||||
wrapped: true
|
||||
items:
|
||||
type: string
|
||||
format: url
|
||||
if:
|
||||
x-displayName: isString
|
||||
type: string
|
||||
then:
|
||||
minItems: 1
|
||||
maxItems: 15
|
||||
else:
|
||||
x-displayName: notString
|
||||
type: [integer, 'null']
|
||||
minItems: 1
|
||||
maxItems: 20
|
||||
friend:
|
||||
$ref: '#/components/schemas/Pet'
|
||||
tags:
|
||||
|
@ -1118,12 +1171,19 @@ components:
|
|||
petType:
|
||||
description: Type of a pet
|
||||
type: string
|
||||
huntingSkill:
|
||||
type: [integer]
|
||||
enum:
|
||||
- 0
|
||||
- 1
|
||||
- 2
|
||||
xml:
|
||||
name: Pet
|
||||
Tag:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: number
|
||||
description: Tag ID
|
||||
$ref: '#/components/schemas/Id'
|
||||
name:
|
||||
|
@ -1185,6 +1245,35 @@ components:
|
|||
type: string
|
||||
contentEncoding: base64
|
||||
contentMediaType: image/png
|
||||
addresses:
|
||||
type: array
|
||||
minItems: 0
|
||||
maxLength: 10
|
||||
prefixItems:
|
||||
- type: object
|
||||
properties:
|
||||
city:
|
||||
type: string
|
||||
minLength: 0
|
||||
country:
|
||||
type: string
|
||||
minLength: 0
|
||||
street:
|
||||
description: includes build/apartment number
|
||||
type: string
|
||||
minLength: 0
|
||||
- type: number
|
||||
items:
|
||||
type: string
|
||||
if:
|
||||
title: userStatus === 10
|
||||
properties:
|
||||
userStatus:
|
||||
enum: [10]
|
||||
then:
|
||||
required: ['phone']
|
||||
else:
|
||||
required: []
|
||||
xml:
|
||||
name: User
|
||||
requestBodies:
|
||||
|
@ -1251,9 +1340,9 @@ webhooks:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
$ref: '#/components/schemas/Pet'
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Return a 200 status to indicate that the data was received successfully
|
||||
myWebhook:
|
||||
$ref: '#/components/pathItems/webhooks'
|
||||
|
|
|
@ -16,14 +16,14 @@ info:
|
|||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
|
||||
# OpenAPI Specification
|
||||
This API is documented in **OpenAPI format** and is based on
|
||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
|
||||
# Cross-Origin Resource Sharing
|
||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||
|
@ -38,7 +38,7 @@ info:
|
|||
OAuth2 - an open protocol to allow secure authorization in a simple
|
||||
and standard method from web, mobile and desktop applications.
|
||||
|
||||
<SecurityDefinitions />
|
||||
<!-- ReDoc-Inject: <security-definitions> -->
|
||||
|
||||
version: 1.0.0
|
||||
title: Swagger Petstore
|
||||
|
@ -83,12 +83,14 @@ x-tagGroups:
|
|||
tags:
|
||||
- pet_model
|
||||
- store_model
|
||||
security:
|
||||
- {}
|
||||
paths:
|
||||
/pet:
|
||||
parameters:
|
||||
- name: Accept-Language
|
||||
in: header
|
||||
description: "The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US"
|
||||
description: 'The language you prefer for messages. Supported values are en-AU, en-CA, en-GB, en-US'
|
||||
example: en-US
|
||||
required: false
|
||||
schema:
|
||||
|
@ -254,7 +256,7 @@ paths:
|
|||
required: false
|
||||
schema:
|
||||
type: string
|
||||
example: "Bearer <TOKEN>"
|
||||
example: 'Bearer <TOKEN>'
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
|
@ -401,6 +403,7 @@ paths:
|
|||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
minProperties: 2
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
|
@ -429,7 +432,7 @@ paths:
|
|||
application/json:
|
||||
example:
|
||||
status: 400
|
||||
message: "Invalid Order"
|
||||
message: 'Invalid Order'
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
|
@ -877,11 +880,11 @@ paths:
|
|||
type: string
|
||||
examples:
|
||||
response:
|
||||
value: <Message> OK </Message>
|
||||
value: <Message> OK </Message>
|
||||
text/plain:
|
||||
examples:
|
||||
response:
|
||||
value: OK
|
||||
value: OK
|
||||
'400':
|
||||
description: Invalid username/password supplied
|
||||
/user/logout:
|
||||
|
@ -1027,8 +1030,8 @@ components:
|
|||
properties:
|
||||
id:
|
||||
externalDocs:
|
||||
description: "Find more info here"
|
||||
url: "https://example.com"
|
||||
description: 'Find more info here'
|
||||
url: 'https://example.com'
|
||||
description: Pet ID
|
||||
allOf:
|
||||
- $ref: '#/components/schemas/Id'
|
||||
|
@ -1134,6 +1137,26 @@ components:
|
|||
description: User status
|
||||
type: integer
|
||||
format: int32
|
||||
addresses:
|
||||
type: array
|
||||
minItems: 0
|
||||
maxLength: 10
|
||||
items:
|
||||
- type: object
|
||||
properties:
|
||||
city:
|
||||
type: string
|
||||
minLength: 0
|
||||
country:
|
||||
type: string
|
||||
minLength: 0
|
||||
street:
|
||||
description: includes build/apartment number
|
||||
type: string
|
||||
minLength: 0
|
||||
- type: number
|
||||
additionalItems:
|
||||
type: string
|
||||
xml:
|
||||
name: User
|
||||
requestBodies:
|
||||
|
@ -1201,7 +1224,7 @@ x-webhooks:
|
|||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
$ref: '#/components/schemas/Pet'
|
||||
responses:
|
||||
"200":
|
||||
'200':
|
||||
description: Return a 200 status to indicate that the data was received successfully
|
||||
|
|
|
@ -15,13 +15,13 @@ info:
|
|||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
# OpenAPI Specification
|
||||
This API is documented in **OpenAPI format** and is based on
|
||||
[Petstore sample](http://petstore.swagger.io/) provided by [swagger.io](http://swagger.io) team.
|
||||
It was **extended** to illustrate features of [generator-openapi-repo](https://github.com/Rebilly/generator-openapi-repo)
|
||||
tool and [ReDoc](https://github.com/Redocly/redoc) documentation. In addition to standard
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/master/docs/redoc-vendor-extensions.md).
|
||||
OpenAPI syntax we use a few [vendor extensions](https://github.com/Redocly/redoc/blob/main/docs/redoc-vendor-extensions.md).
|
||||
# Cross-Origin Resource Sharing
|
||||
This API features Cross-Origin Resource Sharing (CORS) implemented in compliance with [W3C spec](https://www.w3.org/TR/cors/).
|
||||
And that allows cross-domain communication from the browser.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Use the Redoc CLI
|
||||
redirectFrom:
|
||||
- /docs/quickstart/cli/
|
||||
- /docs/redoc/quickstart/cli/
|
||||
---
|
||||
|
||||
# How to use the Redoc CLI
|
||||
|
@ -54,9 +54,9 @@ The CLI includes the following commands:
|
|||
a reference to an OpenAPI definition. Options include:
|
||||
- `--ssr`: Implements a server-side rendering model.
|
||||
- `--watch`: Automatically reloads the server while you edit your OpenAPI definition.
|
||||
- `--options`: Customizes your output using [Redoc options](https://redocly.com/docs/api-reference-docs/configuration/).
|
||||
- `--options`: Customizes your output using [Redoc functionality options](https://redocly.com/docs/api-reference-docs/configuration/functionality) or [Redoc theming options](https://redocly.com/docs/api-reference-docs/configuration/theming).
|
||||
To add nested options, use dot notation.
|
||||
- **`redoc-cli bundle [spec]`:** Bundles `spec` and Redoc into a zero-dependency HTML file. Options include:
|
||||
- **`redoc-cli build [spec]`:** Builds `spec` and Redoc into a zero-dependency HTML file. Options include:
|
||||
- `-t` or `--template`: Uses custom [Handlebars](https://handlebarsjs.com/) templates to render your OpenAPI definition.
|
||||
- `--templateOptions`: Adds template options you want to pass to your
|
||||
custom Handlebars template. To add options, use dot notation.
|
||||
|
@ -70,13 +70,13 @@ The CLI includes the following commands:
|
|||
Bundle with the main color changed to `orange`:
|
||||
|
||||
```bash
|
||||
redoc-cli bundle openapi.yaml --options.theme.colors.primary.main=orange
|
||||
redoc-cli build openapi.yaml --options.theme.colors.primary.main=orange
|
||||
```
|
||||
|
||||
Bundle using a custom Handlebars template and add custom `templateOptions`:
|
||||
|
||||
```bash
|
||||
redoc-cli bundle http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description"
|
||||
redoc-cli build http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description"
|
||||
```
|
||||
|
||||
Sample Handlebars template:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Use the Redoc Docker image
|
||||
redirectFrom:
|
||||
- /docs/quickstart/docker/
|
||||
- /docs/redoc/quickstart/docker/
|
||||
---
|
||||
|
||||
# How to use the Redoc Docker image
|
||||
|
@ -37,5 +37,5 @@ docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/r
|
|||
## Create a Dockerfile
|
||||
|
||||
You can also create a Dockerfile with some predefined environment variables. Check out
|
||||
a sample [Dockerfile](https://github.com/Redocly/redoc/blob/master/config/docker/Dockerfile)
|
||||
a sample [Dockerfile](https://github.com/Redocly/redoc/blob/main/config/docker/Dockerfile)
|
||||
in our code repo.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Use the Redoc HTML element
|
||||
redirectFrom:
|
||||
- /docs/quickstart/html/
|
||||
- /docs/redoc/quickstart/html/
|
||||
---
|
||||
|
||||
# How to use the Redoc HTML element
|
||||
|
@ -51,7 +51,7 @@ or the files located in your `node modules` folder.
|
|||
To reference the Redoc script with a CDN link:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
|
||||
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"> </script>
|
||||
```
|
||||
|
||||
### Node modules link
|
||||
|
@ -97,7 +97,7 @@ Redoc.init(specOrSpecUrl, options, element, callback)
|
|||
```
|
||||
- `specOrSpecUrl`: Either a JSON object with the OpenAPI definition or a URL to the
|
||||
definition in JSON or YAML format.
|
||||
- `options`: See [options object](https://redocly.com/docs/api-reference-docs/configuration/) reference.
|
||||
- `options`: See [features.openapi object](/docs/api-reference-docs/configuration/functionality.mdx) reference.
|
||||
- `element`: DOM element Redoc will be inserted into.
|
||||
- `callback`(optional): Callback to be called after Redoc has been fully rendered.
|
||||
It is also called on errors with `error` as the first argument.
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Redoc deployment guide
|
||||
redirectFrom:
|
||||
- /docs/quickstart/intro/
|
||||
- /docs/redoc/quickstart/intro/
|
||||
---
|
||||
|
||||
# Redoc deployment guide
|
||||
|
@ -46,12 +46,12 @@ section in the documentation.
|
|||
|
||||
If you want to view your Redoc output locally, you can simulate an HTTP server.
|
||||
|
||||
#### Redocly OpenAPI CLI
|
||||
#### Redocly CLI
|
||||
|
||||
Redocly OpenAPI CLI is an open source command-line tool that includes a command
|
||||
Redocly CLI is an open source command-line tool that includes a command
|
||||
for simulating an HTTP server to provide a preview of your OpenAPI definition locally.
|
||||
|
||||
If you have [OpenAPI CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
If you have [Redocly CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```bash
|
||||
|
@ -72,7 +72,7 @@ openapi preview-docs -p 8888 openapi.yaml
|
|||
Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition.
|
||||
|
||||
For more information about the `preview-docs` command, refer to
|
||||
[OpenAPI CLI commands](https://redocly.com/docs/cli/commands/preview-docs/#preview-docs) in the OpenAPI CLI documentation.
|
||||
[Redocly CLI commands](https://redocly.com/docs/cli/commands/preview-docs/#preview-docs) in the Redocly CLI documentation.
|
||||
|
||||
#### Python
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
---
|
||||
title: Use the Redoc React component
|
||||
redirectFrom:
|
||||
- /docs/quickstart/react/
|
||||
- /docs/redoc/quickstart/react/
|
||||
---
|
||||
|
||||
# How to use the Redoc React component
|
||||
|
@ -59,7 +59,7 @@ For example:
|
|||
```
|
||||
|
||||
For more information on configuration options, refer to the
|
||||
[Configuration options for Reference docs](https://redocly.com/docs/api-reference-docs/configuration/)
|
||||
[Configuration options for Reference docs](https://redocly.com/docs/api-reference-docs/configuration/functionality/)
|
||||
section of the documentation. Options available for Redoc are noted,
|
||||
"Supported in Redoc CE".
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ replace the `spec-url` attribute with the URL or local file address to your defi
|
|||
<!--
|
||||
Link to Redoc JavaScript on CDN for rendering standalone element
|
||||
-->
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"></script>
|
||||
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
---
|
||||
title: Using the Redoc CLI
|
||||
---
|
||||
|
||||
# Using the Redoc CLI
|
||||
|
||||
With Redoc's command-line interface you can bundle your OpenAPI definition and API documentation
|
||||
(made with Redoc) into a zero-dependency HTML file and locally render your
|
||||
OpenAPI definition with Redoc.
|
||||
|
||||
## Step 1 - Install Redoc CLI
|
||||
|
||||
You can install the `redoc-cli` package globally using one of the following package managers:
|
||||
|
||||
- [npm](https://docs.npmjs.com/about-npm)
|
||||
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
|
||||
|
||||
Or you can install `redoc-cli` using [npx](https://www.freecodecamp.org/news/npm-vs-npx-whats-the-difference/).
|
||||
|
||||
### Install Redoc CLI with yarn
|
||||
|
||||
To install the `redoc-cli` package globally with yarn:
|
||||
|
||||
```bash
|
||||
yarn global add redoc-cli
|
||||
```
|
||||
|
||||
### Install Redoc with npm
|
||||
|
||||
To install the `redoc-cli` package globally with npm:
|
||||
|
||||
```bash
|
||||
npm i -g redoc-cli
|
||||
```
|
||||
|
||||
### Install with `npx`
|
||||
|
||||
To install the `redoc-cli` package locally with `npx`, navigate to your project
|
||||
directory in your terminal, then use the following command:
|
||||
|
||||
```bash
|
||||
npx redoc-cli
|
||||
```
|
||||
|
||||
## Step 2 - Use the CLI
|
||||
|
||||
### Redoc CLI commands
|
||||
|
||||
The CLI includes the following commands:
|
||||
|
||||
- **`redoc-cli serve [spec]`:** Starts a local server with Redoc. You must include the required parameter, spec, which is
|
||||
a reference to an OpenAPI definition. Options include:
|
||||
- `--ssr`: Implements a server-side rendering model.
|
||||
- `--watch`: Automatically reloads the server while you edit your OpenAPI definition.
|
||||
- `--options`: Customizes your output using [Redoc options](https://redocly.com/docs/api-reference-docs/configuration/).
|
||||
To add nested options, use dot notation.
|
||||
- **`redoc-cli bundle [spec]`:** Bundles `spec` and Redoc into a zero-dependency HTML file. Options include:
|
||||
- `-t` or `--template`: Uses custom [Handlebars](https://handlebarsjs.com/) templates to render your OpenAPI definition.
|
||||
- `--templateOptions`: Adds template options you want to pass to your
|
||||
custom Handlebars template. To add options, use dot notation.
|
||||
- **`--help`:** Prints help text for the Redoc CLI commands and options.
|
||||
- **`--version`:** Prints the version of the `redoc-cli` package you have installed.
|
||||
|
||||
### Redoc CLI examples
|
||||
|
||||
#### Bundle
|
||||
|
||||
Bundle with the main color changed to `orange`:
|
||||
|
||||
```bash
|
||||
redoc-cli bundle openapi.yaml --options.theme.colors.primary.main=orange
|
||||
```
|
||||
|
||||
Bundle using a custom Handlebars template and add custom `templateOptions`:
|
||||
|
||||
```bash
|
||||
redoc-cli bundle http://petstore.swagger.io/v2/swagger.json -t custom.hbs --templateOptions.metaDescription "Page meta description"
|
||||
```
|
||||
|
||||
Sample Handlebars template:
|
||||
|
||||
```handlebars
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf8" />
|
||||
<title>{{title}}</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta description="{{{templateOptions.metaDescription}}}">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
{{{redocHead}}}
|
||||
{{#unless disableGoogleFont}}<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">{{/unless}}
|
||||
</head>
|
||||
<body>
|
||||
{{{redocHTML}}}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
#### Serve
|
||||
|
||||
Serve with the `nativeScrollbars` option set to `true`:
|
||||
|
||||
```bash
|
||||
redoc-cli serve openapi/dist.yaml --options.nativeScrollbars
|
||||
```
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
title: Using the Redoc Docker image
|
||||
---
|
||||
|
||||
# Using the Redoc Docker image
|
||||
|
||||
Redoc is available as a pre-built Docker image in [Docker Hub](https://hub.docker.com/r/redocly/redoc/).
|
||||
|
||||
If you have [Docker](https://docs.docker.com/get-docker/) installed, pull the image with the following command:
|
||||
|
||||
```docker
|
||||
docker pull redocly/redoc
|
||||
```
|
||||
|
||||
Then run the image with the following command:
|
||||
|
||||
```docker
|
||||
docker run -p 8080:80 redocly/redoc
|
||||
```
|
||||
|
||||
The preview starts on port 8080, based on the port used in the command,
|
||||
and can be accessed at `http://localhost:8080`.
|
||||
To exit the preview, use `control+C`.
|
||||
|
||||
By default Redoc starts with a demo Swagger Petstore OpenAPI definition located at
|
||||
http://petstore.swagger.io/v2/swagger.json. You can update this URL using
|
||||
the environment variable `SPEC_URL`.
|
||||
|
||||
For example:
|
||||
|
||||
```bash
|
||||
docker run -p 8080:80 -e SPEC_URL=https://api.example.com/openapi.json redocly/redoc
|
||||
```
|
||||
|
||||
## Using a Dockerfile
|
||||
|
||||
You can also create a Dockerfile with some predefined environment variables. Check out
|
||||
a sample [Dockerfile](https://github.com/Redocly/redoc/blob/master/config/docker/Dockerfile)
|
||||
in our code repo.
|
|
@ -1,214 +0,0 @@
|
|||
---
|
||||
title: Using the Redoc HTML element
|
||||
---
|
||||
|
||||
# Using the Redoc HTML element
|
||||
|
||||
## TL;DR final code example
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Redoc</title>
|
||||
<!-- needed for adaptive design -->
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
|
||||
<!--
|
||||
Redoc doesn't change outer page styles
|
||||
-->
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url='http://petstore.swagger.io/v2/swagger.json'></redoc>
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
```
|
||||
|
||||
:::attention Running Redoc locally requires an HTTP server
|
||||
Loading local OpenAPI definitions is impossible without running a web server because of issues with
|
||||
[same-origin policy](https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy) and
|
||||
other security reasons.
|
||||
:::
|
||||
|
||||
### Running Redoc locally
|
||||
|
||||
If you want to view your Redoc output locally, you can simulate an HTTP server.
|
||||
|
||||
#### Using Redocly OpenAPI CLI
|
||||
|
||||
Redocly OpenAPI CLI is an open source command-line tool that includes a command
|
||||
for simulating an HTTP server to provide a preview of your OpenAPI definition locally.
|
||||
|
||||
If you have [OpenAPI CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```bash
|
||||
openapi preview-docs openapi.yaml
|
||||
```
|
||||
|
||||
By default, without providing a port, the preview starts on port 8080, and can be accessed at `http://localhost:8080`.
|
||||
To exit the preview, use `control+C`.
|
||||
|
||||
#### Using Python
|
||||
|
||||
If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```python
|
||||
python3 -m http.server
|
||||
```
|
||||
|
||||
If you have [Python 2](https://www.python.org/downloads/) installed, `cd` into your
|
||||
project directory and run the following command:
|
||||
|
||||
```python
|
||||
python -m SimpleHTTPServer 8000
|
||||
```
|
||||
|
||||
The output after entering the command provides the local URL where the preview can be accessed.
|
||||
To exit the preview, use `control-C`.
|
||||
|
||||
#### Using Node.js
|
||||
|
||||
If you have [Node.js](https://nodejs.org/en/download/) installed, install `http-server`
|
||||
using the following npm command:
|
||||
|
||||
```bash
|
||||
npm install -g http-server
|
||||
```
|
||||
|
||||
Then, `cd` into your project directory and run the following command:
|
||||
|
||||
```node
|
||||
http-server
|
||||
```
|
||||
|
||||
The output after entering the command provides the local URL where the preview can be accessed.
|
||||
To exit the preview, use `control-C`.
|
||||
|
||||
## Step 1 - Install Redoc
|
||||
|
||||
You can install Redoc using one of the following package managers:
|
||||
|
||||
- [npm](https://docs.npmjs.com/about-npm)
|
||||
- [yarn](https://classic.yarnpkg.com/en/docs/getting-started)
|
||||
|
||||
:::attention Initialize your package manager
|
||||
If you do not have a `package.json` file in your project directory,
|
||||
you need to add one by initializing npm or yarn in your project. Use the command `npm init` for npm,
|
||||
or `yarn init` for yarn. These initialization commands will lead you through the process
|
||||
of creating a `package.json` file in your project.
|
||||
|
||||
For more information, see
|
||||
[Creating a package.json file](https://docs.npmjs.com/creating-a-package-json-file)
|
||||
in the npm documentation or [Yarn init](https://classic.yarnpkg.com/en/docs/cli/init/)
|
||||
in the yarn documentation.
|
||||
|
||||
:::
|
||||
|
||||
### Install Redoc with yarn
|
||||
|
||||
After navigating to your project directory in your terminal, use the following command:
|
||||
|
||||
```bash
|
||||
yarn add redoc
|
||||
```
|
||||
|
||||
### Install Redoc with npm
|
||||
|
||||
After navigating to your project directory in your terminal, use the following command:
|
||||
|
||||
```bash
|
||||
npm i redoc
|
||||
```
|
||||
|
||||
## Step 2 - Reference the Redoc script
|
||||
|
||||
You can reference the Redoc script using either a link to the files hosted on a CDN
|
||||
or the files located in your `node modules` folder.
|
||||
|
||||
### CDN link
|
||||
|
||||
To reference the Redoc script with a CDN link:
|
||||
|
||||
```html
|
||||
<script src="https://cdn.jsdelivr.net/npm/redoc@latest/bundles/redoc.standalone.js"> </script>
|
||||
```
|
||||
|
||||
### Node modules link
|
||||
|
||||
To reference the Redoc script with a node modules link:
|
||||
|
||||
```html
|
||||
<script src="node_modules/redoc/bundles/redoc.standalone.js"> </script>
|
||||
```
|
||||
|
||||
## Step 3 - Add the <redoc> element
|
||||
|
||||
You can add the <redoc> element to your HTML page and reference your OpenAPI
|
||||
definition using the `spec-url` attribute, or you can initialize Redoc using
|
||||
a globally exposed Redoc object.
|
||||
|
||||
### Using the `spec-url` attribute
|
||||
|
||||
To add the <redoc> element with the `spec-url` attribute:
|
||||
|
||||
```html
|
||||
<redoc spec-url="url/to/your/spec"></redoc>
|
||||
```
|
||||
|
||||
#### Examples
|
||||
|
||||
```html
|
||||
<redoc spec-url="http://petstore.swagger.io/v2/swagger.json"></redoc>
|
||||
```
|
||||
|
||||
You can also use a local file (JSON or YAML) in your project, for instance:
|
||||
|
||||
```html
|
||||
<redoc spec-url="dist.json"></redoc>
|
||||
```
|
||||
|
||||
### Using a Redoc object
|
||||
|
||||
To add the <redoc> element with a globally exposed Redoc object:
|
||||
|
||||
```js
|
||||
Redoc.init(specOrSpecUrl, options, element, callback)
|
||||
```
|
||||
- `specOrSpecUrl`: Either a JSON object with the OpenAPI definition or a URL to the
|
||||
definition in JSON or YAML format.
|
||||
- `options`: See [options object](https://redocly.com/docs/api-reference-docs/configuration/) reference.
|
||||
- `element`: DOM element Redoc will be inserted into.
|
||||
- `callback`(optional): Callback to be called after Redoc has been fully rendered.
|
||||
It is also called on errors with `error` as the first argument.
|
||||
|
||||
#### Examples
|
||||
|
||||
```html
|
||||
<script>
|
||||
Redoc.init('http://petstore.swagger.io/v2/swagger.json', {
|
||||
scrollYOffset: 50
|
||||
}, document.getElementById('redoc-container'))
|
||||
</script>
|
||||
```
|
||||
|
||||
You can also use a local file (JSON or YAML) in your project, for instance:
|
||||
|
||||
```html
|
||||
<script>
|
||||
Redoc.init('dist.yaml', {
|
||||
scrollYOffset: 50
|
||||
}, document.getElementById('redoc-container'))
|
||||
</script>
|
||||
```
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
title: Redoc quickstart guide
|
||||
---
|
||||
|
||||
# Redoc quickstart guide
|
||||
|
||||
This guide includes step-by-step instructions for how to get started using
|
||||
Redoc to render your OpenAPI definition.
|
||||
|
||||
Redoc offers multiple options for rendering your OpenAPI definition.
|
||||
You should select the option that best fits your needs.
|
||||
|
||||
The following options are supported:
|
||||
|
||||
- **[Live demo](https://redocly.github.io/redoc/):**
|
||||
The live demo offers a fast way to see how your OpenAPI will render with Redoc.
|
||||
- **[HTML element](./html.md):**
|
||||
Using the HTML element works well for typical website deployments.
|
||||
- **[React component](./react.md):**
|
||||
Using the React component is an option for users with a React-based application.
|
||||
- **[Docker image](./docker.md):**
|
||||
Using the Docker image works in a container-based deployment.
|
||||
- **[CLI](./cli.md):**
|
||||
Using the CLI is an option for users who prefer to use a command-line interface.
|
||||
|
||||
## Before you start
|
||||
|
||||
You will need an OpenAPI definition. For testing purposes, you can use one of the following sample OpenAPI definitions:
|
||||
- OpenAPI 3.0
|
||||
- [Rebilly Users OpenAPI Definition](https://raw.githubusercontent.com/Rebilly/api-definitions/main/openapi/users.yaml)
|
||||
- [Swagger Petstore Sample OpenAPI Definition](https://petstore3.swagger.io/api/v3/openapi.json)
|
||||
- OpenAPI 2.0
|
||||
- [Thingful OpenAPI Definition](https://raw.githubusercontent.com/thingful/openapi-spec/master/spec/swagger.yaml)
|
||||
- [Fitbit Plus OpenAPI Definition](https://raw.githubusercontent.com/TwineHealth/TwineDeveloperDocs/master/spec/swagger.yaml)
|
||||
|
||||
For more information on the OpenAPI specification, refer to the [Learning OpenAPI 3](https://redocly.com/docs/resources/learning-openapi/)
|
||||
section in the documentation.
|
||||
|
||||
## Live demo online
|
||||
|
||||
If you want to see how ReDoc will render your OpenAPI definition, you can try it out online at https://redocly.github.io/redoc/.
|
||||
|
||||
A version of the Swagger Petstore API is displayed by default. To test it with your own OpenAPI definition, enter the URL for your
|
||||
definition and select the **TRY IT** button.
|
|
@ -1,78 +0,0 @@
|
|||
---
|
||||
title: Using the Redoc React component
|
||||
---
|
||||
|
||||
# Using the Redoc React component
|
||||
|
||||
## Before you start
|
||||
|
||||
Install the following dependencies required by Redoc if you do not already have them installed:
|
||||
|
||||
- `react`
|
||||
- `react-dom`
|
||||
- `mobx`
|
||||
- `styled-components`
|
||||
- `core-js`
|
||||
|
||||
If you have npm installed, you can install these dependencies using the following command:
|
||||
|
||||
```js
|
||||
npm i react react-dom mobx styled-components core-js
|
||||
```
|
||||
|
||||
## Step 1 - Import the `RedocStandalone` component
|
||||
|
||||
```js
|
||||
import { RedocStandalone } from 'redoc';
|
||||
```
|
||||
|
||||
## Step 2 - Use the component
|
||||
|
||||
You can either link to your OpenAPI definition with a URL, using the following format:
|
||||
|
||||
```react
|
||||
<RedocStandalone specUrl="url/to/your/spec"/>
|
||||
```
|
||||
|
||||
Or you can pass your OpenAPI definition as an object, using the following format:
|
||||
|
||||
```js
|
||||
<RedocStandalone spec={/* spec as an object */}/>
|
||||
```
|
||||
|
||||
## Optional - Pass options
|
||||
|
||||
Options can be passed into the RedocStandalone component to alter how it renders.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
<RedocStandalone
|
||||
specUrl="http://petstore.swagger.io/v2/swagger.json"
|
||||
options={{
|
||||
nativeScrollbars: true,
|
||||
theme: { colors: { primary: { main: '#dd5522' } } },
|
||||
}}
|
||||
/>
|
||||
```
|
||||
|
||||
For more information on configuration options, refer to the
|
||||
[Configuration options for Reference docs](https://redocly.com/docs/api-reference-docs/configuration/)
|
||||
section of the documentation. Options available for Redoc are noted,
|
||||
"Supported in Redoc CE".
|
||||
|
||||
## Optional - Specify `onLoaded` callback
|
||||
|
||||
You can also specify the `onLoaded` callback, which is called each time Redoc
|
||||
is fully rendered or when an error occurs (with an error as the first argument).
|
||||
|
||||
```js
|
||||
<RedocStandalone
|
||||
specUrl="http://petstore.swagger.io/v2/swagger.json"
|
||||
onLoaded={error => {
|
||||
if (!error) {
|
||||
console.log('Yay!');
|
||||
}
|
||||
}}
|
||||
/>
|
||||
```
|
|
@ -49,7 +49,7 @@ describe('Menu', () => {
|
|||
cy.location('hash').should('equal', '#tag/pet');
|
||||
|
||||
cy.contains('[role=menuitem]', 'Find pet by ID').click({ force: true });
|
||||
cy.location('hash').should('equal', '#operation/getPetById');
|
||||
cy.location('hash').should('equal', '#tag/pet/operation/getPetById');
|
||||
});
|
||||
|
||||
it('should deactivate tag when other is activated', () => {
|
||||
|
@ -76,4 +76,20 @@ describe('Menu', () => {
|
|||
.then($h5 => $h5[0].firstChild!.nodeValue!.trim())
|
||||
.should('eq', 'Response Schema:');
|
||||
});
|
||||
|
||||
it('should be able to open the operation details when the operation IDs have quotes', () => {
|
||||
cy.visit('e2e/standalone-3-1.html');
|
||||
cy.get('label span[title="pet"]').click({ multiple: true, force: true });
|
||||
cy.get('li').contains('OperationId with quotes').click({ multiple: true, force: true });
|
||||
cy.get('h2').contains('OperationId with quotes').should('be.visible');
|
||||
cy.url().should('include', 'deletePetBy%22Id');
|
||||
});
|
||||
|
||||
it.only('should encode URL when the operation IDs have backslashes', () => {
|
||||
cy.visit('e2e/standalone-3-1.html');
|
||||
cy.get('label span[title="pet"]').click({ multiple: true, force: true });
|
||||
cy.get('li').contains('OperationId with backslash').click({ multiple: true, force: true });
|
||||
cy.get('h2').contains('OperationId with backslash').should('be.visible');
|
||||
cy.url().should('include', 'delete%5CPetById');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -21,12 +21,12 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://petstore.swagger.io/v2/pet',
|
||||
);
|
||||
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://petstore.swagger.io/sandbox/pet',
|
||||
);
|
||||
|
@ -40,7 +40,7 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://localhost:' + win.location.port + '/pet',
|
||||
);
|
||||
|
@ -55,7 +55,7 @@ describe('Servers', () => {
|
|||
initReDoc(win, spec, {});
|
||||
|
||||
// TODO add cy-data attributes
|
||||
cy.get('[data-section-id="operation/addPet"]').should(
|
||||
cy.get('[data-section-id="tag/pet/operation/addPet"]').should(
|
||||
'contain',
|
||||
'http://localhost:' + win.location.port + '/pet',
|
||||
);
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('Search', () => {
|
|||
|
||||
getSearchInput().type('{enter}', { force: true });
|
||||
|
||||
cy.contains('[role=navigation] [role=menuitem]', 'Introduction').should('have.class', 'active');
|
||||
cy.contains('[role=menu] [role=menuitem]', 'Introduction').should('have.class', 'active');
|
||||
});
|
||||
|
||||
it('should mark search results', () => {
|
||||
|
@ -59,4 +59,20 @@ describe('Search', () => {
|
|||
getSearchInput().type('xzss', { force: true });
|
||||
getSearchResults().should('exist').should('contain', 'No results found');
|
||||
});
|
||||
|
||||
it('should allow search by path or keywords in path', () => {
|
||||
getSearchInput().clear().type('uploadImage', { force: true });
|
||||
cy.get('[role=search] [role=menuitem]')
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('contain', 'uploads an image');
|
||||
|
||||
getSearchInput()
|
||||
.clear()
|
||||
.type('/pet/{petId}/uploadImage', { force: true, parseSpecialCharSequences: false });
|
||||
cy.get('[role=search] [role=menuitem]')
|
||||
.should('have.length', 1)
|
||||
.first()
|
||||
.should('contain', 'uploads an image');
|
||||
});
|
||||
});
|
||||
|
|
19
e2e/integration/urls.e2e.ts
Normal file
19
e2e/integration/urls.e2e.ts
Normal file
|
@ -0,0 +1,19 @@
|
|||
describe('Supporting both operation/* and parent/*/operation* urls', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('e2e/standalone.html');
|
||||
});
|
||||
|
||||
it('should supporting operation/* url', () => {
|
||||
cy.url().then(loc => {
|
||||
cy.visit(loc + '#operation/updatePet');
|
||||
cy.get('li[data-item-id="tag/pet/operation/updatePet"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
||||
it('should supporting parent/*/operation url', () => {
|
||||
cy.url().then(loc => {
|
||||
cy.visit(loc + '#tag/pet/operation/addPet');
|
||||
cy.get('li[data-item-id="tag/pet/operation/addPet"]').should('be.visible');
|
||||
});
|
||||
});
|
||||
});
|
1148
package-lock.json
generated
1148
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
|
@ -1,14 +1,13 @@
|
|||
{
|
||||
"name": "redoc",
|
||||
"version": "2.0.0-rc.66",
|
||||
"version": "2.0.0",
|
||||
"description": "ReDoc",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Redocly/redoc"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults",
|
||||
"ie 11"
|
||||
"defaults"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=6.9",
|
||||
|
@ -33,8 +32,9 @@
|
|||
"start": "webpack serve --mode=development --env playground --hot --config demo/webpack.config.ts",
|
||||
"start:prod": "webpack serve --env playground --mode=production --config demo/webpack.config.ts",
|
||||
"start:benchmark": "webpack serve --mode=production --env.bench --config demo/webpack.config.ts",
|
||||
"test": "npm run lint && npm run unit && npm run license-check",
|
||||
"test": "npm run unit && npm run license-check",
|
||||
"unit": "jest --coverage",
|
||||
"test:update-snapshot": "jest --updateSnapshot",
|
||||
"e2e": "cypress run",
|
||||
"e2e-ci": "cypress run --record",
|
||||
"bundlesize": "size-limit",
|
||||
|
@ -44,17 +44,18 @@
|
|||
"bundle:standalone": "webpack --env production --env standalone --mode=production",
|
||||
"bundle:lib": "webpack --mode=production && npm run declarations",
|
||||
"bundle:browser": "webpack --env production --env browser --mode=production",
|
||||
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:browser && npm run bundle:standalone",
|
||||
"bundle": "npm run bundle:clean && npm run bundle:lib && npm run bundle:browser && npm run bundle:standalone && npm run compile:cli",
|
||||
"declarations": "tsc --emitDeclarationOnly -p tsconfig.lib.json && cp -R src/types typings/",
|
||||
"stats": "webpack --env production --env standalone --json --profile --mode=production > stats.json",
|
||||
"prettier": "prettier --write \"cli/index.ts\" \"src/**/*.{ts,tsx}\"",
|
||||
"changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1",
|
||||
"lint": "eslint 'src/**/*.{js,ts,tsx}' --cache",
|
||||
"lint": "eslint --fix 'src/**/*.{js,ts,tsx}' --cache",
|
||||
"benchmark": "node ./benchmark/benchmark.js",
|
||||
"start:demo": "webpack serve --hot --config demo/webpack.config.ts --mode=development",
|
||||
"compile:cli": "tsc custom.d.ts cli/index.ts --target es6 --module commonjs --types yargs",
|
||||
"build:sofico-version": "webpack --env playground='false' bench='false' --mode=production --config demo/webpack.config.ts ",
|
||||
"build:demo": "webpack --mode=production --config demo/webpack.config.ts",
|
||||
"publish-cdn": "scripts/publish-cdn.sh",
|
||||
"deploy:demo": "aws s3 sync demo/dist s3://production-redoc-demo --acl=public-read",
|
||||
"license-check": "license-checker --production --onlyAllow 'MIT;ISC;Apache-2.0;BSD;BSD-2-Clause;BSD-3-Clause;CC-BY-4.0;Python-2.0' --summary",
|
||||
"docker:build": "docker build -f config/docker/Dockerfile -t redoc .",
|
||||
|
@ -62,8 +63,8 @@
|
|||
"pre-commit": "pretty-quick --staged"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/webpack-preprocessor": "^5.9.0",
|
||||
"@hot-loader/react-dom": "^17.0.1",
|
||||
"@cypress/webpack-preprocessor": "^5.12.0",
|
||||
"@hot-loader/react-dom": "^17.0.2",
|
||||
"@size-limit/preset-app": "^7.0.4",
|
||||
"@types/chai": "^4.2.18",
|
||||
"@types/dompurify": "^2.2.2",
|
||||
|
@ -73,7 +74,7 @@
|
|||
"@types/json-pointer": "^1.0.30",
|
||||
"@types/lunr": "^2.3.3",
|
||||
"@types/mark.js": "^8.11.5",
|
||||
"@types/marked": "^4.0.1",
|
||||
"@types/marked": "^4.0.3",
|
||||
"@types/node": "^15.6.1",
|
||||
"@types/prismjs": "^1.16.5",
|
||||
"@types/prop-types": "^15.7.3",
|
||||
|
@ -110,6 +111,7 @@
|
|||
"license-checker": "^25.0.1",
|
||||
"lodash.noop": "^3.0.1",
|
||||
"mobx": "^6.3.2",
|
||||
"outdent": "^0.8.0",
|
||||
"prettier": "^2.3.2",
|
||||
"pretty-quick": "^3.0.0",
|
||||
"raf": "^3.4.1",
|
||||
|
@ -123,10 +125,10 @@
|
|||
"ts-jest": "^27.0.2",
|
||||
"ts-loader": "^9.2.6",
|
||||
"ts-node": "^10.0.0",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "~4.1.0",
|
||||
"unfetch": "^4.2.0",
|
||||
"url-polyfill": "^1.1.12",
|
||||
"webpack": "^5.38.1",
|
||||
"webpack": "^5.50.1",
|
||||
"webpack-cli": "^4.7.2",
|
||||
"webpack-dev-server": "^4.6.0",
|
||||
"webpack-node-externals": "^3.0.0",
|
||||
|
@ -140,8 +142,7 @@
|
|||
"styled-components": "^4.1.1 || ^5.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@redocly/openapi-core": "^1.0.0-beta.88",
|
||||
"@redocly/react-dropdown-aria": "^2.0.11",
|
||||
"@redocly/openapi-core": "^1.0.0-beta.104",
|
||||
"classnames": "^2.3.1",
|
||||
"decko": "^1.2.0",
|
||||
"dompurify": "^2.2.8",
|
||||
|
@ -149,11 +150,11 @@
|
|||
"json-pointer": "^0.6.2",
|
||||
"lunr": "^2.3.9",
|
||||
"mark.js": "^8.11.1",
|
||||
"marked": "^4.0.10",
|
||||
"marked": "^4.0.15",
|
||||
"mobx-react": "^7.2.0",
|
||||
"openapi-sampler": "^1.2.1",
|
||||
"openapi-sampler": "^1.3.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"perfect-scrollbar": "^1.5.1",
|
||||
"perfect-scrollbar": "^1.5.5",
|
||||
"polished": "^4.1.3",
|
||||
"prismjs": "^1.27.0",
|
||||
"prop-types": "^15.7.2",
|
||||
|
@ -168,6 +169,14 @@
|
|||
{
|
||||
"path": "./bundles/redoc.standalone.js",
|
||||
"limit": "350 kB"
|
||||
},
|
||||
{
|
||||
"path": "./bundles/redoc.lib.js",
|
||||
"limit": "100 kB"
|
||||
},
|
||||
{
|
||||
"path": "./bundles/redoc.browser.lib.js",
|
||||
"limit": "100 kB"
|
||||
}
|
||||
],
|
||||
"jest": {
|
||||
|
@ -187,10 +196,12 @@
|
|||
"coveragePathIgnorePatterns": [
|
||||
"\\.d\\.ts$",
|
||||
"/benchmark/",
|
||||
"/node_modules/"
|
||||
"/node_modules/",
|
||||
"src/services/__tests__/models/helpers.ts"
|
||||
],
|
||||
"modulePathIgnorePatterns": [
|
||||
"/benchmark/"
|
||||
"/benchmark/",
|
||||
"src/services/__tests__/models/helpers.ts"
|
||||
],
|
||||
"snapshotSerializers": [
|
||||
"enzyme-to-json/serializer"
|
||||
|
|
24
scripts/invalidate-cache.sh
Executable file
24
scripts/invalidate-cache.sh
Executable file
|
@ -0,0 +1,24 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e # exit on error
|
||||
|
||||
echo jsdelivr clearing cache
|
||||
curl -i -X POST https://purge.jsdelivr.net/ \
|
||||
-H 'cache-control: no-cache' \
|
||||
-H 'content-type: application/json' \
|
||||
-d '{
|
||||
"path": [
|
||||
"npm/redoc@latest/bundles/redoc.browser.lib.js",
|
||||
"npm/redoc@latest/bundles/redoc.lib.js",
|
||||
"npm/redoc@latest/bundles/redoc.standalone.js"
|
||||
]
|
||||
}'
|
||||
|
||||
echo
|
||||
echo start invalidate cloudfront
|
||||
|
||||
aws cloudfront create-invalidation --distribution-id $DISTRIBUTION --paths "/redoc/*"
|
||||
|
||||
echo Cache cleared successfully
|
||||
|
||||
exit 0
|
40
scripts/publish-cdn.sh
Executable file
40
scripts/publish-cdn.sh
Executable file
|
@ -0,0 +1,40 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e # exit on error
|
||||
|
||||
# TODO: Update script!
|
||||
|
||||
VERSION=$(node scripts/version.js)
|
||||
VERSION_TAG=v${VERSION:0:1}.x
|
||||
|
||||
copy_to_s3 () {
|
||||
aws s3 cp --exclude "*" --include "*.js" --content-type "application/javascript; charset=utf-8" bundles "s3://redocly-cdn/redoc/$1/bundles" --recursive
|
||||
aws s3 cp --exclude "*" --include "*.map" --content-type "application/json" bundles "s3://redocly-cdn/redoc/$1/bundles" --recursive
|
||||
aws s3 cp --exclude "*" --include "*.txt" bundles "s3://redocly-cdn/redoc/$1/bundles" --recursive
|
||||
aws s3 cp CHANGELOG.md "s3://redocly-cdn/redoc/$1/CHANGELOG.md"
|
||||
aws s3 cp LICENSE "s3://redocly-cdn/redoc/$1/LICENSE"
|
||||
aws s3 cp package.json "s3://redocly-cdn/redoc/$1/package.json"
|
||||
aws s3 cp README.md "s3://redocly-cdn/redoc/$1/README.md"
|
||||
}
|
||||
|
||||
if aws s3 ls "redocly-cdn/redoc/v$VERSION/" "$@"; then
|
||||
echo "Version $VERSION already exists"
|
||||
exit 1
|
||||
else
|
||||
echo Releasing $VERSION
|
||||
|
||||
echo Uploading to S3 $VERSION
|
||||
copy_to_s3 "v$VERSION"
|
||||
|
||||
echo Uploading to S3 $VERSION_TAG
|
||||
copy_to_s3 "$VERSION_TAG" $@
|
||||
|
||||
if [[ "$VERSION_TAG" == "v2.x" ]]; then
|
||||
echo Uploading to S3 latest
|
||||
copy_to_s3 latest $@
|
||||
fi
|
||||
|
||||
echo
|
||||
echo Deployed successfully
|
||||
exit 0
|
||||
fi
|
1
scripts/version.js
Normal file
1
scripts/version.js
Normal file
|
@ -0,0 +1 @@
|
|||
console.log(require('../package.json').version);
|
|
@ -8,36 +8,24 @@ export interface CopyButtonWrapperProps {
|
|||
children: (props: { renderCopyButton: () => React.ReactNode }) => React.ReactNode;
|
||||
}
|
||||
|
||||
export class CopyButtonWrapper extends React.PureComponent<
|
||||
CopyButtonWrapperProps,
|
||||
{ tooltipShown: boolean }
|
||||
> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
tooltipShown: false,
|
||||
};
|
||||
}
|
||||
export const CopyButtonWrapper = (
|
||||
props: CopyButtonWrapperProps & { tooltipShown?: boolean },
|
||||
): JSX.Element => {
|
||||
const [tooltipShown, setTooltipShown] = React.useState(false);
|
||||
|
||||
render() {
|
||||
return this.props.children({ renderCopyButton: this.renderCopyButton });
|
||||
}
|
||||
|
||||
copy = () => {
|
||||
const copy = () => {
|
||||
const content =
|
||||
typeof this.props.data === 'string'
|
||||
? this.props.data
|
||||
: JSON.stringify(this.props.data, null, 2);
|
||||
typeof props.data === 'string' ? props.data : JSON.stringify(props.data, null, 2);
|
||||
ClipboardService.copyCustom(content);
|
||||
this.showTooltip();
|
||||
showTooltip();
|
||||
};
|
||||
|
||||
renderCopyButton = () => {
|
||||
const renderCopyButton = () => {
|
||||
return (
|
||||
<button onClick={this.copy}>
|
||||
<button onClick={copy}>
|
||||
<Tooltip
|
||||
title={ClipboardService.isSupported() ? 'Copied' : 'Not supported in your browser'}
|
||||
open={this.state.tooltipShown}
|
||||
open={tooltipShown}
|
||||
>
|
||||
Copy
|
||||
</Tooltip>
|
||||
|
@ -45,15 +33,12 @@ export class CopyButtonWrapper extends React.PureComponent<
|
|||
);
|
||||
};
|
||||
|
||||
showTooltip() {
|
||||
this.setState({
|
||||
tooltipShown: true,
|
||||
});
|
||||
const showTooltip = () => {
|
||||
setTooltipShown(true);
|
||||
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
tooltipShown: false,
|
||||
});
|
||||
setTooltipShown(false);
|
||||
}, 1500);
|
||||
}
|
||||
}
|
||||
};
|
||||
return props.children({ renderCopyButton: renderCopyButton }) as JSX.Element;
|
||||
};
|
||||
|
|
68
src/common-elements/Dropdown/Dropdown.tsx
Normal file
68
src/common-elements/Dropdown/Dropdown.tsx
Normal file
|
@ -0,0 +1,68 @@
|
|||
import * as React from 'react';
|
||||
import styled from '../../styled-components';
|
||||
import { ArrowIconProps, DropdownProps, DropdownOption } from './types';
|
||||
|
||||
const ArrowSvg = ({ className, style }: ArrowIconProps): JSX.Element => (
|
||||
<svg
|
||||
className={className}
|
||||
style={style}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<polyline points="6 9 12 15 18 9" />
|
||||
</svg>
|
||||
);
|
||||
|
||||
const ArrowIcon = styled(ArrowSvg)`
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
-webkit-transform: translateY(-50%);
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
right: 8px;
|
||||
margin: auto;
|
||||
text-align: center;
|
||||
polyline {
|
||||
color: ${props => props.variant === 'dark' && 'white'};
|
||||
}
|
||||
`;
|
||||
|
||||
const DropdownComponent = (props: DropdownProps): JSX.Element => {
|
||||
const { options, onChange, placeholder, value = '', variant, className } = props;
|
||||
|
||||
const handleOnChange = event => {
|
||||
const { selectedIndex } = event.target;
|
||||
const index = placeholder ? selectedIndex - 1 : selectedIndex;
|
||||
onChange(options[index]);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={className}>
|
||||
<ArrowIcon variant={variant} />
|
||||
<select onChange={handleOnChange} value={value} className="dropdown-select">
|
||||
{placeholder && (
|
||||
<option disabled hidden value={placeholder}>
|
||||
{placeholder}
|
||||
</option>
|
||||
)}
|
||||
{options.map(({ idx, value, title }: DropdownOption, index) => (
|
||||
<option key={idx || value + index} value={value}>
|
||||
{title || value}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<label>{value}</label>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Dropdown = React.memo<DropdownProps>(DropdownComponent);
|
2
src/common-elements/Dropdown/index.ts
Normal file
2
src/common-elements/Dropdown/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export * from './styled';
|
||||
export * from './types';
|
94
src/common-elements/Dropdown/styled.ts
Normal file
94
src/common-elements/Dropdown/styled.ts
Normal file
|
@ -0,0 +1,94 @@
|
|||
import styled from 'styled-components';
|
||||
|
||||
import { Dropdown as DropdownComponent } from './Dropdown';
|
||||
|
||||
export const Dropdown = styled(DropdownComponent)<{
|
||||
fullWidth?: boolean;
|
||||
}>`
|
||||
label {
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
vertical-align: bottom;
|
||||
width: ${({ fullWidth }) => (fullWidth ? '100%' : 'auto')};
|
||||
text-transform: none;
|
||||
padding: 0 22px 0 4px;
|
||||
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
font-family: inherit;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dropdown-select {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
opacity: 0;
|
||||
border: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
|
||||
color: ${({ theme }) => theme.colors.text.primary};
|
||||
line-height: inherit;
|
||||
font-family: inherit;
|
||||
}
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(38, 50, 56, 0.5);
|
||||
vertical-align: bottom;
|
||||
padding: 2px 0px 2px 6px;
|
||||
position: relative;
|
||||
width: auto;
|
||||
background: white;
|
||||
color: #263238;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
cursor: pointer;
|
||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
`;
|
||||
|
||||
export const SimpleDropdown = styled(Dropdown)`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.969em;
|
||||
|
||||
font-size: 1em;
|
||||
border: none;
|
||||
padding: 0 1.2em 0 0;
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
label {
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MimeLabel = styled.span`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
color: black;
|
||||
`;
|
25
src/common-elements/Dropdown/types.ts
Normal file
25
src/common-elements/Dropdown/types.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
export interface DropdownOption {
|
||||
idx?: number;
|
||||
value: string;
|
||||
title?: string;
|
||||
serverUrl?: string;
|
||||
label?: string;
|
||||
}
|
||||
|
||||
export interface DropdownProps {
|
||||
options: DropdownOption[];
|
||||
onChange: (option: DropdownOption) => void;
|
||||
ariaLabel?: string;
|
||||
className?: string;
|
||||
placeholder?: string;
|
||||
value?: string;
|
||||
dense?: boolean;
|
||||
fullWidth?: boolean;
|
||||
variant?: 'dark' | 'light';
|
||||
}
|
||||
|
||||
export interface ArrowIconProps {
|
||||
className?: string;
|
||||
variant?: 'light' | 'dark';
|
||||
style?: React.CSSProperties;
|
||||
}
|
|
@ -1,143 +0,0 @@
|
|||
import Dropdown from '@redocly/react-dropdown-aria';
|
||||
|
||||
import styled from '../styled-components';
|
||||
|
||||
export interface DropdownOption {
|
||||
idx: number;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export interface DropdownProps {
|
||||
options: DropdownOption[];
|
||||
value: string;
|
||||
onChange: (option: DropdownOption) => void;
|
||||
ariaLabel: string;
|
||||
}
|
||||
|
||||
export const StyledDropdown = styled(Dropdown)`
|
||||
&& {
|
||||
box-sizing: border-box;
|
||||
min-width: 100px;
|
||||
outline: none;
|
||||
display: inline-block;
|
||||
border-radius: 2px;
|
||||
border: 1px solid rgba(38, 50, 56, 0.5);
|
||||
vertical-align: bottom;
|
||||
padding: 2px 0px 2px 6px;
|
||||
position: relative;
|
||||
width: auto;
|
||||
background: white;
|
||||
color: #263238;
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
font-size: 0.929em;
|
||||
line-height: 1.5em;
|
||||
cursor: pointer;
|
||||
transition: border 0.25s ease, color 0.25s ease, box-shadow 0.25s ease;
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: 1px solid ${props => props.theme.colors.primary.main};
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
box-shadow: 0px 0px 0px 1px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
.dropdown-selector {
|
||||
display: inline-flex;
|
||||
padding: 0;
|
||||
height: auto;
|
||||
padding-right: 20px;
|
||||
position: relative;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.dropdown-selector-value {
|
||||
font-family: ${props => props.theme.typography.headings.fontFamily};
|
||||
position: relative;
|
||||
font-size: 0.929em;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
vertical-align: middle;
|
||||
color: #263238;
|
||||
left: 0;
|
||||
transition: color 0.25s ease, text-shadow 0.25s ease;
|
||||
}
|
||||
.dropdown-arrow {
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-color: ${props => props.theme.colors.primary.main} transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 0.35em 0.35em 0;
|
||||
width: 0;
|
||||
svg {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-selector-content {
|
||||
position: absolute;
|
||||
margin-top: 2px;
|
||||
left: -2px;
|
||||
right: 0;
|
||||
|
||||
z-index: 10;
|
||||
min-width: 100px;
|
||||
|
||||
background: white;
|
||||
border: 1px solid rgba(38, 50, 56, 0.2);
|
||||
box-shadow: 0px 2px 4px 0px rgba(34, 36, 38, 0.12), 0px 2px 10px 0px rgba(34, 36, 38, 0.08);
|
||||
|
||||
max-height: 220px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dropdown-option {
|
||||
font-size: 0.9em;
|
||||
color: #263238;
|
||||
cursor: pointer;
|
||||
padding: 0.4em;
|
||||
background-color: #ffffff;
|
||||
|
||||
&[aria-selected='true'] {
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(38, 50, 56, 0.12);
|
||||
}
|
||||
}
|
||||
input {
|
||||
cursor: pointer;
|
||||
height: 1px;
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const SimpleDropdown = styled(StyledDropdown)`
|
||||
&& {
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.969em;
|
||||
|
||||
font-size: 1em;
|
||||
border: none;
|
||||
padding: 0 1.2em 0 0;
|
||||
background: transparent;
|
||||
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
.dropdown-selector-value {
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
text-shadow: 0px 0px 0px ${props => props.theme.colors.primary.main};
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const MimeLabel = styled.span`
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
color: black;
|
||||
`;
|
|
@ -1,4 +1,4 @@
|
|||
import styled, { extensionsHook, media } from '../styled-components';
|
||||
import styled, { extensionsHook, media, css } from '../styled-components';
|
||||
import { deprecatedCss } from './mixins';
|
||||
|
||||
export const PropertiesTableCaption = styled.caption`
|
||||
|
@ -72,7 +72,26 @@ export const PropertyNameCell = styled(PropertyCell)`
|
|||
${deprecatedCss};
|
||||
}
|
||||
|
||||
${({ kind }) => (kind !== 'field' ? 'font-style: italic' : '')};
|
||||
${({ kind }) =>
|
||||
kind === 'patternProperties' &&
|
||||
css`
|
||||
> span.property-name {
|
||||
display: inline-table;
|
||||
white-space: break-spaces;
|
||||
margin-right: 20px;
|
||||
|
||||
::before,
|
||||
::after {
|
||||
content: '/';
|
||||
filter: opacity(0.2);
|
||||
}
|
||||
}
|
||||
`}
|
||||
|
||||
${({ kind = '' }) =>
|
||||
['field', 'additionalProperties', 'patternProperties'].includes(kind)
|
||||
? ''
|
||||
: 'font-style: italic'};
|
||||
|
||||
${extensionsHook('PropertyNameCell')};
|
||||
`;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { transparentize } from 'polished';
|
||||
|
||||
import styled, { extensionsHook } from '../styled-components';
|
||||
import styled, { extensionsHook, css } from '../styled-components';
|
||||
import { PropertyNameCell } from './fields-layout';
|
||||
import { ShelfIcon } from './shelfs';
|
||||
|
||||
|
@ -17,6 +17,27 @@ export const ClickablePropertyNameCell = styled(PropertyNameCell)`
|
|||
&:focus {
|
||||
font-weight: ${({ theme }) => theme.typography.fontWeightBold};
|
||||
}
|
||||
${({ kind }) =>
|
||||
kind === 'patternProperties' &&
|
||||
css`
|
||||
display: inline-flex;
|
||||
margin-right: 20px;
|
||||
|
||||
> span.property-name {
|
||||
white-space: break-spaces;
|
||||
text-align: left;
|
||||
|
||||
::before,
|
||||
::after {
|
||||
content: '/';
|
||||
filter: opacity(0.2);
|
||||
}
|
||||
}
|
||||
|
||||
> svg {
|
||||
align-self: center;
|
||||
}
|
||||
`}
|
||||
}
|
||||
${ShelfIcon} {
|
||||
height: ${({ theme }) => theme.schema.arrow.size};
|
||||
|
@ -56,6 +77,10 @@ export const RequiredLabel = styled(FieldLabel.withComponent('div'))`
|
|||
line-height: 1;
|
||||
`;
|
||||
|
||||
export const PropertyLabel = styled(RequiredLabel)`
|
||||
color: ${props => props.theme.colors.primary.light};
|
||||
`;
|
||||
|
||||
export const RecursiveLabel = styled(FieldLabel)`
|
||||
color: ${({ theme }) => theme.colors.warning.main};
|
||||
font-size: 13px;
|
||||
|
@ -71,6 +96,7 @@ export const PatternLabel = styled(FieldLabel)`
|
|||
|
||||
export const ExampleValue = styled(FieldLabel)`
|
||||
border-radius: 2px;
|
||||
word-break: break-word;
|
||||
${({ theme }) => `
|
||||
background-color: ${transparentize(0.95, theme.colors.text.primary)};
|
||||
color: ${transparentize(0.1, theme.colors.text.primary)};
|
||||
|
|
|
@ -4,8 +4,8 @@ export * from './linkify';
|
|||
export * from './shelfs';
|
||||
export * from './fields-layout';
|
||||
export * from './schema';
|
||||
export * from './dropdown';
|
||||
export * from './mixins';
|
||||
export * from './tabs';
|
||||
export * from './samples';
|
||||
export * from './perfect-scrollbar';
|
||||
export * from './Dropdown';
|
||||
|
|
|
@ -67,7 +67,7 @@ function navigate(history: HistoryService, event: React.MouseEvent<HTMLAnchorEle
|
|||
!isModifiedEvent(event) // ignore clicks with modifier keys
|
||||
) {
|
||||
event.preventDefault();
|
||||
history.replace(to);
|
||||
history.replace(encodeURI(to));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,35 +8,34 @@ const directionMap = {
|
|||
down: '0',
|
||||
};
|
||||
|
||||
class IntShelfIcon extends React.PureComponent<{
|
||||
const IntShelfIcon = (props: {
|
||||
className?: string;
|
||||
float?: 'left' | 'right';
|
||||
size?: string;
|
||||
color?: string;
|
||||
direction: 'left' | 'right' | 'up' | 'down';
|
||||
style?: React.CSSProperties;
|
||||
}> {
|
||||
render() {
|
||||
return (
|
||||
<svg
|
||||
className={this.props.className}
|
||||
style={this.props.style}
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
x="0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
y="0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
}): JSX.Element => {
|
||||
return (
|
||||
<svg
|
||||
className={props.className}
|
||||
style={props.style}
|
||||
version="1.1"
|
||||
viewBox="0 0 24 24"
|
||||
x="0"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
y="0"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
export const ShelfIcon = styled(IntShelfIcon)`
|
||||
height: ${props => props.size || '18px'};
|
||||
width: ${props => props.size || '18px'};
|
||||
min-width: ${props => props.size || '18px'};
|
||||
vertical-align: middle;
|
||||
float: ${props => props.float || ''};
|
||||
transition: transform 0.2s ease-out;
|
||||
|
|
|
@ -21,7 +21,14 @@ export class CallbackSamples extends React.Component<CallbackSamplesProps> {
|
|||
context: RedocNormalizedOptions;
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
@ -17,20 +17,18 @@ export interface CallbackTitleProps {
|
|||
onClick?: () => void;
|
||||
}
|
||||
|
||||
export class CallbackTitle extends React.PureComponent<CallbackTitleProps> {
|
||||
render() {
|
||||
const { name, opened, className, onClick, httpVerb, deprecated } = this.props;
|
||||
export const CallbackTitle = (props: CallbackTitleProps) => {
|
||||
const { name, opened, className, onClick, httpVerb, deprecated } = props;
|
||||
|
||||
return (
|
||||
<CallbackTitleWrapper className={className} onClick={onClick || undefined}>
|
||||
<OperationBadgeStyled type={httpVerb}>{shortenHTTPVerb(httpVerb)}</OperationBadgeStyled>
|
||||
<ShelfIcon size={'1.5em'} direction={opened ? 'down' : 'right'} float={'left'} />
|
||||
<CallbackName deprecated={deprecated}>{name}</CallbackName>
|
||||
{deprecated ? <Badge type="warning"> {l('deprecated')} </Badge> : null}
|
||||
</CallbackTitleWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<CallbackTitleWrapper className={className} onClick={onClick || undefined}>
|
||||
<OperationBadgeStyled type={httpVerb}>{shortenHTTPVerb(httpVerb)}</OperationBadgeStyled>
|
||||
<ShelfIcon size={'1.5em'} direction={opened ? 'down' : 'right'} float={'left'} />
|
||||
<CallbackName deprecated={deprecated}>{name}</CallbackName>
|
||||
{deprecated ? <Badge type="warning"> {l('deprecated')} </Badge> : null}
|
||||
</CallbackTitleWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
const CallbackTitleWrapper = styled.button`
|
||||
border: 0;
|
||||
|
|
|
@ -4,8 +4,8 @@ import * as React from 'react';
|
|||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||
import { AdvancedMarkdown } from '../Markdown/AdvancedMarkdown';
|
||||
import { H1, H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||
import { ContentItemModel } from '../../services/MenuBuilder';
|
||||
import { GroupModel, OperationModel } from '../../services/models';
|
||||
import type { ContentItemModel } from '../../services';
|
||||
import type { GroupModel, OperationModel } from '../../services/models';
|
||||
import { Operation } from '../Operation/Operation';
|
||||
|
||||
@observer
|
||||
|
@ -79,7 +79,11 @@ export class SectionItem extends React.Component<ContentItemProps> {
|
|||
</Header>
|
||||
</MiddlePanel>
|
||||
</Row>
|
||||
<AdvancedMarkdown source={description || ''} htmlWrap={middlePanelWrap} />
|
||||
<AdvancedMarkdown
|
||||
parentId={this.props.item.id}
|
||||
source={description || ''}
|
||||
htmlWrap={middlePanelWrap}
|
||||
/>
|
||||
{externalDocs && (
|
||||
<Row>
|
||||
<MiddlePanel>
|
||||
|
|
|
@ -1,10 +1,18 @@
|
|||
import * as React from 'react';
|
||||
import { StyledComponent } from 'styled-components';
|
||||
|
||||
import { DropdownProps, MimeLabel, SimpleDropdown } from '../../common-elements/dropdown';
|
||||
import { DropdownProps, MimeLabel, SimpleDropdown } from '../../common-elements/Dropdown';
|
||||
|
||||
export interface DropdownOrLabelProps extends DropdownProps {
|
||||
Label?: React.ComponentClass;
|
||||
Dropdown?: React.ComponentClass;
|
||||
Label?: StyledComponent<any, any, Record<string, any>, never>;
|
||||
Dropdown?: StyledComponent<
|
||||
React.NamedExoticComponent<DropdownProps>,
|
||||
any,
|
||||
{
|
||||
fullWidth?: boolean | undefined;
|
||||
},
|
||||
never
|
||||
>;
|
||||
}
|
||||
|
||||
export function DropdownOrLabel(props: DropdownOrLabelProps): JSX.Element {
|
||||
|
@ -12,5 +20,5 @@ export function DropdownOrLabel(props: DropdownOrLabelProps): JSX.Element {
|
|||
if (props.options.length === 1) {
|
||||
return <Label>{props.options[0].value}</Label>;
|
||||
}
|
||||
return <Dropdown {...props} searchable={false} />;
|
||||
return <Dropdown {...props} />;
|
||||
}
|
||||
|
|
|
@ -59,8 +59,8 @@ export const ServersOverlay = styled.div<{ expanded: boolean }>`
|
|||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
background: #fafafa;
|
||||
color: #263238;
|
||||
background: ${props => props.theme.rightPanel.servers.overlay.backgroundColor};
|
||||
color: ${props => props.theme.rightPanel.servers.overlay.textColor};
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0px 0px 6px rgba(0, 0, 0, 0.33);
|
||||
overflow: hidden;
|
||||
|
@ -78,7 +78,7 @@ export const ServerItem = styled.div`
|
|||
export const ServerUrl = styled.div`
|
||||
padding: 5px;
|
||||
border: 1px solid #ccc;
|
||||
background: #fff;
|
||||
background: ${props => props.theme.rightPanel.servers.url.backgroundColor};
|
||||
word-break: break-all;
|
||||
color: ${props => props.theme.colors.primary.main};
|
||||
> span {
|
||||
|
|
|
@ -37,6 +37,6 @@ export class ErrorBoundary extends React.Component<
|
|||
</ErrorWrapper>
|
||||
);
|
||||
}
|
||||
return React.Children.only(this.props.children);
|
||||
return <React.Fragment>{React.Children.only(this.props.children)}</React.Fragment>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,20 @@ import { ConstraintsView } from './FieldContstraints';
|
|||
import { Pattern } from './Pattern';
|
||||
import { SchemaModel } from '../../services';
|
||||
import styled from '../../styled-components';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
|
||||
export function ArrayItemDetails({ schema }: { schema: SchemaModel }) {
|
||||
if (!schema || (schema.type === 'string' && !schema.constraints.length)) return null;
|
||||
const { hideSchemaPattern } = React.useContext(OptionsContext);
|
||||
if (
|
||||
!schema ||
|
||||
(schema.type === 'string' && !schema.constraints.length) ||
|
||||
((!schema?.pattern || hideSchemaPattern) &&
|
||||
!schema.items &&
|
||||
!schema.displayFormat &&
|
||||
!schema.constraints.length) // return null for cases where all constraints are empty
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { FieldLabel, ExampleValue } from '../../common-elements/fields';
|
||||
import { getSerializedValue } from '../../utils';
|
||||
import { getSerializedValue, isArray } from '../../utils';
|
||||
|
||||
import { l } from '../../services/Labels';
|
||||
import { FieldModel } from '../../services';
|
||||
|
@ -15,22 +15,31 @@ export function Examples({ field }: { field: FieldModel }) {
|
|||
return (
|
||||
<>
|
||||
<FieldLabel> {l('examples')}: </FieldLabel>
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => {
|
||||
{isArray(field.examples) ? (
|
||||
field.examples.map((example, idx) => {
|
||||
const value = getSerializedValue(field, example);
|
||||
const stringifyValue = field.in ? String(value) : JSON.stringify(value);
|
||||
return (
|
||||
<li key={idx}>
|
||||
<React.Fragment key={idx}>
|
||||
<ExampleValue>{stringifyValue}</ExampleValue>{' '}
|
||||
</React.Fragment>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<ExamplesList>
|
||||
{Object.values(field.examples).map((example, idx) => (
|
||||
<li key={idx + example.value}>
|
||||
<ExampleValue>{getSerializedValue(field, example.value)}</ExampleValue> -{' '}
|
||||
{example.summary || example.description}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ExamplesList>
|
||||
))}
|
||||
</ExamplesList>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const ExamplesList = styled.ul`
|
||||
margin-top: 1em;
|
||||
padding-left: 0;
|
||||
list-style-position: inside;
|
||||
list-style-position: outside;
|
||||
`;
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { ClickablePropertyNameCell, RequiredLabel } from '../../common-elements/fields';
|
||||
import {
|
||||
ClickablePropertyNameCell,
|
||||
PropertyLabel,
|
||||
RequiredLabel,
|
||||
} from '../../common-elements/fields';
|
||||
import { FieldDetails } from './FieldDetails';
|
||||
|
||||
import {
|
||||
InnerPropertiesWrap,
|
||||
PropertyBullet,
|
||||
|
@ -11,11 +14,11 @@ import {
|
|||
PropertyDetailsCell,
|
||||
PropertyNameCell,
|
||||
} from '../../common-elements/fields-layout';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements/';
|
||||
import { Schema } from '../Schema/Schema';
|
||||
|
||||
import { FieldModel } from '../../services/models';
|
||||
import { Schema, SchemaOptions } from '../Schema/Schema';
|
||||
import type { SchemaOptions } from '../Schema/Schema';
|
||||
import type { FieldModel } from '../../services/models';
|
||||
|
||||
export interface FieldProps extends SchemaOptions {
|
||||
className?: string;
|
||||
|
@ -46,12 +49,20 @@ export class Field extends React.Component<FieldProps> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { className, field, isLast, expandByDefault } = this.props;
|
||||
const { className = '', field, isLast, expandByDefault } = this.props;
|
||||
const { name, deprecated, required, kind } = field;
|
||||
const withSubSchema = !field.schema.isPrimitive && !field.schema.isCircular;
|
||||
|
||||
const expanded = field.expanded === undefined ? expandByDefault : field.expanded;
|
||||
|
||||
const labels = (
|
||||
<>
|
||||
{kind === 'additionalProperties' && <PropertyLabel>additional property</PropertyLabel>}
|
||||
{kind === 'patternProperties' && <PropertyLabel>pattern property</PropertyLabel>}
|
||||
{required && <RequiredLabel>required</RequiredLabel>}
|
||||
</>
|
||||
);
|
||||
|
||||
const paramName = withSubSchema ? (
|
||||
<ClickablePropertyNameCell
|
||||
className={deprecated ? 'deprecated' : ''}
|
||||
|
@ -64,16 +75,16 @@ export class Field extends React.Component<FieldProps> {
|
|||
onKeyPress={this.handleKeyPress}
|
||||
aria-label="expand properties"
|
||||
>
|
||||
<span>{name}</span>
|
||||
<span className="property-name">{name}</span>
|
||||
<ShelfIcon direction={expanded ? 'down' : 'right'} />
|
||||
</button>
|
||||
{required && <RequiredLabel> required </RequiredLabel>}
|
||||
{labels}
|
||||
</ClickablePropertyNameCell>
|
||||
) : (
|
||||
<PropertyNameCell className={deprecated ? 'deprecated' : undefined} kind={kind} title={name}>
|
||||
<PropertyBullet />
|
||||
<span>{name}</span>
|
||||
{required && <RequiredLabel> required </RequiredLabel>}
|
||||
<span className="property-name">{name}</span>
|
||||
{labels}
|
||||
</PropertyNameCell>
|
||||
);
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import {
|
||||
RecursiveLabel,
|
||||
|
@ -7,7 +8,7 @@ import {
|
|||
TypePrefix,
|
||||
TypeTitle,
|
||||
} from '../../common-elements/fields';
|
||||
import { getSerializedValue } from '../../utils';
|
||||
import { getSerializedValue, isObject } from '../../utils';
|
||||
import { ExternalDocumentation } from '../ExternalDocumentation/ExternalDocumentation';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { EnumValues } from './EnumValues';
|
||||
|
@ -25,7 +26,7 @@ import { OptionsContext } from '../OptionsProvider';
|
|||
import { Pattern } from './Pattern';
|
||||
import { ArrayItemDetails } from './ArrayItemDetails';
|
||||
|
||||
function FieldDetailsComponent(props: FieldProps) {
|
||||
export const FieldDetailsComponent = observer((props: FieldProps) => {
|
||||
const { enumSkipQuotes, hideSchemaTitles } = React.useContext(OptionsContext);
|
||||
|
||||
const { showExamples, field, renderDiscriminatorSwitch } = props;
|
||||
|
@ -60,6 +61,10 @@ function FieldDetailsComponent(props: FieldProps) {
|
|||
return null;
|
||||
}, [field, showExamples]);
|
||||
|
||||
const defaultValue = isObject(schema.default)
|
||||
? getSerializedValue(field, schema.default).replace(`${field.name}=`, '')
|
||||
: schema.default;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div>
|
||||
|
@ -100,7 +105,7 @@ function FieldDetailsComponent(props: FieldProps) {
|
|||
<Badge type="warning"> {l('deprecated')} </Badge>
|
||||
</div>
|
||||
)}
|
||||
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={schema.default} />
|
||||
<FieldDetail raw={rawDefault} label={l('default') + ':'} value={defaultValue} />
|
||||
{!renderDiscriminatorSwitch && (
|
||||
<EnumValues isArrayType={isArrayType} values={schema.enum} />
|
||||
)}{' '}
|
||||
|
@ -117,6 +122,6 @@ function FieldDetailsComponent(props: FieldProps) {
|
|||
{(_const && <FieldDetail label={l('const') + ':'} value={_const} />) || null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export const FieldDetails = React.memo<FieldProps>(FieldDetailsComponent);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownProps, DropdownOption } from '../../common-elements/dropdown';
|
||||
import { DropdownProps, DropdownOption } from '../../common-elements/Dropdown';
|
||||
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||
|
||||
export interface GenericChildrenSwitcherProps<T> {
|
||||
|
@ -32,8 +32,8 @@ export class GenericChildrenSwitcher<T> extends React.Component<
|
|||
};
|
||||
}
|
||||
|
||||
switchItem = ({ idx }) => {
|
||||
if (this.props.items) {
|
||||
switchItem = ({ idx }: DropdownOption) => {
|
||||
if (this.props.items && idx !== undefined) {
|
||||
this.setState({
|
||||
activeItemIdx: idx,
|
||||
});
|
||||
|
|
|
@ -19,37 +19,43 @@ const JsonViewerWrap = styled.div`
|
|||
}
|
||||
`;
|
||||
|
||||
class Json extends React.PureComponent<JsonProps> {
|
||||
node: HTMLDivElement;
|
||||
const Json = (props: JsonProps) => {
|
||||
const [node, setNode] = React.useState<HTMLDivElement>();
|
||||
|
||||
render() {
|
||||
return <CopyButtonWrapper data={this.props.data}>{this.renderInner}</CopyButtonWrapper>;
|
||||
}
|
||||
const renderInner = ({ renderCopyButton }) => {
|
||||
const showFoldingButtons =
|
||||
props.data &&
|
||||
Object.values(props.data).some(value => typeof value === 'object' && value !== null);
|
||||
|
||||
renderInner = ({ renderCopyButton }) => (
|
||||
<JsonViewerWrap>
|
||||
<SampleControls>
|
||||
{renderCopyButton()}
|
||||
<button onClick={this.expandAll}> Expand all </button>
|
||||
<button onClick={this.collapseAll}> Collapse all </button>
|
||||
</SampleControls>
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<PrismDiv
|
||||
className={this.props.className}
|
||||
// tslint:disable-next-line
|
||||
ref={node => (this.node = node!)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: jsonToHTML(this.props.data, options.jsonSampleExpandLevel),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</OptionsContext.Consumer>
|
||||
</JsonViewerWrap>
|
||||
);
|
||||
return (
|
||||
<JsonViewerWrap>
|
||||
<SampleControls>
|
||||
{renderCopyButton()}
|
||||
{showFoldingButtons && (
|
||||
<>
|
||||
<button onClick={expandAll}> Expand all </button>
|
||||
<button onClick={collapseAll}> Collapse all </button>
|
||||
</>
|
||||
)}
|
||||
</SampleControls>
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<PrismDiv
|
||||
className={props.className}
|
||||
// tslint:disable-next-line
|
||||
ref={node => setNode(node!)}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: jsonToHTML(props.data, options.jsonSampleExpandLevel),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</OptionsContext.Consumer>
|
||||
</JsonViewerWrap>
|
||||
);
|
||||
};
|
||||
|
||||
expandAll = () => {
|
||||
const elements = this.node.getElementsByClassName('collapsible');
|
||||
const expandAll = () => {
|
||||
const elements = node?.getElementsByClassName('collapsible');
|
||||
for (const collapsed of Array.prototype.slice.call(elements)) {
|
||||
const parentNode = collapsed.parentNode as Element;
|
||||
parentNode.classList.remove('collapsed');
|
||||
|
@ -57,8 +63,8 @@ class Json extends React.PureComponent<JsonProps> {
|
|||
}
|
||||
};
|
||||
|
||||
collapseAll = () => {
|
||||
const elements = this.node.getElementsByClassName('collapsible');
|
||||
const collapseAll = () => {
|
||||
const elements = node?.getElementsByClassName('collapsible');
|
||||
// skip first item to avoid collapsing whole object/array
|
||||
const elementsArr = Array.prototype.slice.call(elements, 1);
|
||||
|
||||
|
@ -69,7 +75,7 @@ class Json extends React.PureComponent<JsonProps> {
|
|||
}
|
||||
};
|
||||
|
||||
collapseElement = (target: HTMLElement) => {
|
||||
const collapseElement = (target: HTMLElement) => {
|
||||
let collapsed;
|
||||
if (target.className === 'collapser') {
|
||||
collapsed = target.parentElement!.getElementsByClassName('collapsible')[0];
|
||||
|
@ -83,26 +89,27 @@ class Json extends React.PureComponent<JsonProps> {
|
|||
}
|
||||
};
|
||||
|
||||
clickListener = (event: MouseEvent) => {
|
||||
this.collapseElement(event.target as HTMLElement);
|
||||
};
|
||||
const clickListener = React.useCallback((event: MouseEvent) => {
|
||||
collapseElement(event.target as HTMLElement);
|
||||
}, []);
|
||||
|
||||
focusListener = (event: KeyboardEvent) => {
|
||||
const focusListener = React.useCallback((event: KeyboardEvent) => {
|
||||
if (event.key === 'Enter') {
|
||||
this.collapseElement(event.target as HTMLElement);
|
||||
collapseElement(event.target as HTMLElement);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
componentDidMount() {
|
||||
this.node!.addEventListener('click', this.clickListener);
|
||||
this.node!.addEventListener('focus', this.focusListener);
|
||||
}
|
||||
React.useEffect(() => {
|
||||
node?.addEventListener('click', clickListener);
|
||||
node?.addEventListener('focus', focusListener);
|
||||
return () => {
|
||||
node?.removeEventListener('click', clickListener);
|
||||
node?.removeEventListener('focus', focusListener);
|
||||
};
|
||||
}, [clickListener, focusListener, node]);
|
||||
|
||||
componentWillUnmount() {
|
||||
this.node!.removeEventListener('click', this.clickListener);
|
||||
this.node!.removeEventListener('focus', this.focusListener);
|
||||
}
|
||||
}
|
||||
return <CopyButtonWrapper data={props.data}>{renderInner}</CopyButtonWrapper>;
|
||||
};
|
||||
|
||||
export const JsonViewer = styled(Json)`
|
||||
${jsonStyles};
|
||||
|
|
|
@ -9,6 +9,7 @@ import { StoreConsumer } from '../StoreBuilder';
|
|||
|
||||
export interface AdvancedMarkdownProps extends BaseMarkdownProps {
|
||||
htmlWrap?: (part: JSX.Element) => JSX.Element;
|
||||
parentId?: string;
|
||||
}
|
||||
|
||||
export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
|
||||
|
@ -28,7 +29,7 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
|
|||
throw new Error('When using components in markdown, store prop must be provided');
|
||||
}
|
||||
|
||||
const renderer = new MarkdownRenderer(options);
|
||||
const renderer = new MarkdownRenderer(options, this.props.parentId);
|
||||
const parts = renderer.renderMdWithComponents(source);
|
||||
|
||||
if (!parts.length) {
|
||||
|
@ -42,7 +43,8 @@ export class AdvancedMarkdown extends React.Component<AdvancedMarkdownProps> {
|
|||
{ key: idx },
|
||||
);
|
||||
}
|
||||
return <part.component key={idx} {...{ ...part.props, ...part.propsSelector(store) }} />;
|
||||
const PartComponent = part.component as React.FunctionComponent;
|
||||
return <PartComponent key={idx} {...{ ...part.props, ...part.propsSelector(store) }} />;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ import { StyledComponent } from 'styled-components';
|
|||
|
||||
export const linksCss = css`
|
||||
a {
|
||||
text-decoration: none;
|
||||
text-decoration: ${props => props.theme.typography.links.textDecoration};
|
||||
color: ${props => props.theme.typography.links.color};
|
||||
|
||||
&:visited {
|
||||
|
@ -15,6 +15,7 @@ export const linksCss = css`
|
|||
|
||||
&:hover {
|
||||
color: ${props => props.theme.typography.links.hover};
|
||||
text-decoration: ${props => props.theme.typography.links.hoverTextDecoration};
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownProps } from '../../common-elements/dropdown';
|
||||
import { DropdownOption, DropdownProps } from '../../common-elements/Dropdown';
|
||||
import { MediaContentModel, MediaTypeModel, SchemaModel } from '../../services/models';
|
||||
import { DropdownLabel, DropdownWrapper } from '../PayloadSamples/styled.elements';
|
||||
|
||||
|
@ -20,8 +20,8 @@ export interface MediaTypesSwitchProps {
|
|||
|
||||
@observer
|
||||
export class MediaTypesSwitch extends React.Component<MediaTypesSwitchProps> {
|
||||
switchMedia = ({ idx }) => {
|
||||
if (this.props.content) {
|
||||
switchMedia = ({ idx }: DropdownOption) => {
|
||||
if (this.props.content && idx !== undefined) {
|
||||
this.props.content.activate(idx);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -17,6 +17,7 @@ import { RequestSamples } from '../RequestSamples/RequestSamples';
|
|||
import { ResponsesList } from '../Responses/ResponsesList';
|
||||
import { ResponseSamples } from '../ResponseSamples/ResponseSamples';
|
||||
import { SecurityRequirements } from '../SecurityRequirement/SecurityRequirement';
|
||||
import { SECTION_ATTR } from '../../services';
|
||||
|
||||
const Description = styled.div`
|
||||
margin-bottom: ${({ theme }) => theme.spacing.unit * 6}px;
|
||||
|
@ -26,48 +27,48 @@ export interface OperationProps {
|
|||
operation: OperationModel;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class Operation extends React.Component<OperationProps> {
|
||||
render() {
|
||||
const { operation } = this.props;
|
||||
|
||||
const { name: summary, description, deprecated, externalDocs, isWebhook } = operation;
|
||||
const hasDescription = !!(description || externalDocs);
|
||||
|
||||
return (
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<Row>
|
||||
<MiddlePanel>
|
||||
<H2>
|
||||
<ShareLink to={operation.id} />
|
||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||
{isWebhook && <Badge type="primary"> Webhook </Badge>}
|
||||
</H2>
|
||||
{options.pathInMiddlePanel && !isWebhook && (
|
||||
<Endpoint operation={operation} inverted={true} />
|
||||
export const Operation = observer(({ operation }: OperationProps): JSX.Element => {
|
||||
const { name: summary, description, deprecated, externalDocs, isWebhook, httpVerb } = operation;
|
||||
const hasDescription = !!(description || externalDocs);
|
||||
const { showWebhookVerb } = React.useContext(OptionsContext);
|
||||
return (
|
||||
<OptionsContext.Consumer>
|
||||
{options => (
|
||||
<Row {...{ [SECTION_ATTR]: operation.operationHash }} id={operation.operationHash}>
|
||||
<MiddlePanel>
|
||||
<H2>
|
||||
<ShareLink to={operation.id} />
|
||||
{summary} {deprecated && <Badge type="warning"> Deprecated </Badge>}
|
||||
{isWebhook && (
|
||||
<Badge type="primary">
|
||||
{' '}
|
||||
Webhook {showWebhookVerb && httpVerb && '| ' + httpVerb.toUpperCase()}
|
||||
</Badge>
|
||||
)}
|
||||
{hasDescription && (
|
||||
<Description>
|
||||
{description !== undefined && <Markdown source={description} />}
|
||||
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||
</Description>
|
||||
)}
|
||||
<Extensions extensions={operation.extensions} />
|
||||
<SecurityRequirements securities={operation.security} />
|
||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||
<ResponsesList responses={operation.responses} />
|
||||
<CallbacksList callbacks={operation.callbacks} />
|
||||
</MiddlePanel>
|
||||
<DarkRightPanel>
|
||||
{!options.pathInMiddlePanel && !isWebhook && <Endpoint operation={operation} />}
|
||||
<RequestSamples operation={operation} />
|
||||
<ResponseSamples operation={operation} />
|
||||
<CallbackSamples callbacks={operation.callbacks} />
|
||||
</DarkRightPanel>
|
||||
</Row>
|
||||
)}
|
||||
</OptionsContext.Consumer>
|
||||
);
|
||||
}
|
||||
}
|
||||
</H2>
|
||||
{options.pathInMiddlePanel && !isWebhook && (
|
||||
<Endpoint operation={operation} inverted={true} />
|
||||
)}
|
||||
{hasDescription && (
|
||||
<Description>
|
||||
{description !== undefined && <Markdown source={description} />}
|
||||
{externalDocs && <ExternalDocumentation externalDocs={externalDocs} />}
|
||||
</Description>
|
||||
)}
|
||||
<Extensions extensions={operation.extensions} />
|
||||
<SecurityRequirements securities={operation.security} />
|
||||
<Parameters parameters={operation.parameters} body={operation.requestBody} />
|
||||
<ResponsesList responses={operation.responses} />
|
||||
<CallbacksList callbacks={operation.callbacks} />
|
||||
</MiddlePanel>
|
||||
<DarkRightPanel>
|
||||
{!options.pathInMiddlePanel && !isWebhook && <Endpoint operation={operation} />}
|
||||
<RequestSamples operation={operation} />
|
||||
<ResponseSamples operation={operation} />
|
||||
<CallbackSamples callbacks={operation.callbacks} />
|
||||
</DarkRightPanel>
|
||||
</Row>
|
||||
)}
|
||||
</OptionsContext.Consumer>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -10,6 +10,7 @@ import { MediaTypesSwitch } from '../MediaTypeSwitch/MediaTypesSwitch';
|
|||
import { Schema } from '../Schema';
|
||||
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { ConstraintsView } from '../Fields/FieldContstraints';
|
||||
|
||||
function safePush(obj, prop, item) {
|
||||
if (!obj[prop]) {
|
||||
|
@ -79,6 +80,9 @@ export function BodyContent(props: {
|
|||
return (
|
||||
<>
|
||||
{description !== undefined && <Markdown source={description} />}
|
||||
{schema?.type === 'object' && (
|
||||
<ConstraintsView constraints={schema?.constraints || []} />
|
||||
)}
|
||||
<Schema
|
||||
skipReadOnly={isRequestType}
|
||||
skipWriteOnly={!isRequestType}
|
||||
|
|
|
@ -2,7 +2,7 @@ import * as React from 'react';
|
|||
|
||||
import styled from '../../styled-components';
|
||||
|
||||
import { DropdownProps } from '../../common-elements';
|
||||
import { DropdownOption, DropdownProps } from '../../common-elements';
|
||||
import { MediaTypeModel } from '../../services/models';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { Example } from './Example';
|
||||
|
@ -21,10 +21,12 @@ export class MediaTypeSamples extends React.Component<PayloadSamplesProps, Media
|
|||
state = {
|
||||
activeIdx: 0,
|
||||
};
|
||||
switchMedia = ({ idx }) => {
|
||||
this.setState({
|
||||
activeIdx: idx,
|
||||
});
|
||||
switchMedia = ({ idx }: DropdownOption) => {
|
||||
if (idx !== undefined) {
|
||||
this.setState({
|
||||
activeIdx: idx,
|
||||
});
|
||||
}
|
||||
};
|
||||
render() {
|
||||
const { activeIdx } = this.state;
|
||||
|
|
|
@ -33,6 +33,13 @@ export class PayloadSamples extends React.Component<PayloadSamplesProps> {
|
|||
}
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { transparentize } from 'polished';
|
||||
import styled from '../../styled-components';
|
||||
import { StyledDropdown } from '../../common-elements';
|
||||
import { Dropdown } from '../../common-elements/Dropdown';
|
||||
|
||||
export const MimeLabel = styled.div`
|
||||
padding: 0.9em;
|
||||
|
@ -27,46 +27,27 @@ export const DropdownWrapper = styled.div`
|
|||
position: relative;
|
||||
`;
|
||||
|
||||
export const InvertedSimpleDropdown = styled(StyledDropdown)`
|
||||
&& {
|
||||
margin-left: 10px;
|
||||
text-transform: none;
|
||||
font-size: 0.929em;
|
||||
margin: 0 0 10px 0;
|
||||
display: block;
|
||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||
export const InvertedSimpleDropdown = styled(Dropdown)`
|
||||
label {
|
||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
font-size: 1em;
|
||||
text-transform: none;
|
||||
border: none;
|
||||
}
|
||||
margin: 0 0 10px 0;
|
||||
display: block;
|
||||
background-color: ${({ theme }) => transparentize(0.6, theme.rightPanel.backgroundColor)};
|
||||
border: none;
|
||||
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||
box-shadow: none;
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
padding: 0.9em 1.6em 0.9em 0.9em;
|
||||
box-shadow: none;
|
||||
&:hover,
|
||||
&:focus-within {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
&:focus-within {
|
||||
background-color: ${({ theme }) => transparentize(0.3, theme.rightPanel.backgroundColor)};
|
||||
}
|
||||
|
||||
.dropdown-arrow {
|
||||
border-top-color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
}
|
||||
.dropdown-selector-value {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
color: ${({ theme }) => theme.rightPanel.textColor};
|
||||
}
|
||||
|
||||
.dropdown-selector-content {
|
||||
margin: 0;
|
||||
margin-top: 2px;
|
||||
.dropdown-option {
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
background-color: ${({ theme }) => transparentize(0.3, theme.rightPanel.backgroundColor)};
|
||||
}
|
||||
`;
|
||||
|
||||
|
|
|
@ -27,13 +27,19 @@ export const RedocStandalone = function (props: RedocStandaloneProps) {
|
|||
|
||||
if (normalizedOpts.nonce !== undefined) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
__webpack_nonce__ = normalizedOpts.nonce;
|
||||
} catch {} // If we have exception, Webpack was not used to run this.
|
||||
}
|
||||
|
||||
return (
|
||||
<ErrorBoundary>
|
||||
<StoreBuilder spec={spec} specUrl={specUrl} options={options} onLoaded={onLoaded}>
|
||||
<StoreBuilder
|
||||
spec={spec ? { ...spec } : undefined}
|
||||
specUrl={specUrl}
|
||||
options={options}
|
||||
onLoaded={onLoaded}
|
||||
>
|
||||
{({ loading, store }) =>
|
||||
!loading ? (
|
||||
<Redoc store={store!} />
|
||||
|
|
|
@ -10,6 +10,7 @@ import { Schema } from '../Schema';
|
|||
import { Extensions } from '../Fields/Extensions';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { ResponseHeaders } from './ResponseHeaders';
|
||||
import { ConstraintsView } from '../Fields/FieldContstraints';
|
||||
|
||||
export class ResponseDetails extends React.PureComponent<{ response: ResponseModel }> {
|
||||
render() {
|
||||
|
@ -21,7 +22,14 @@ export class ResponseDetails extends React.PureComponent<{ response: ResponseMod
|
|||
<ResponseHeaders headers={headers} />
|
||||
<MediaTypesSwitch content={content} renderDropdown={this.renderDropdown}>
|
||||
{({ schema }) => {
|
||||
return <Schema skipWriteOnly={true} key="schema" schema={schema} />;
|
||||
return (
|
||||
<>
|
||||
{schema?.type === 'object' && (
|
||||
<ConstraintsView constraints={schema?.constraints || []} />
|
||||
)}
|
||||
<Schema skipWriteOnly={true} key="schema" schema={schema} />
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</MediaTypesSwitch>
|
||||
</>
|
||||
|
|
|
@ -6,6 +6,7 @@ import { ArrayClosingLabel, ArrayOpenningLabel } from '../../common-elements';
|
|||
import styled from '../../styled-components';
|
||||
import { humanizeConstraints } from '../../utils';
|
||||
import { TypeName } from '../../common-elements/fields';
|
||||
import { ObjectSchema } from './ObjectSchema';
|
||||
|
||||
const PaddedSchema = styled.div`
|
||||
padding-left: ${({ theme }) => theme.spacing.unit * 2}px;
|
||||
|
@ -21,6 +22,9 @@ export class ArraySchema extends React.PureComponent<SchemaProps> {
|
|||
? ''
|
||||
: `(${humanizeConstraints(schema)})`;
|
||||
|
||||
if (schema.fields) {
|
||||
return <ObjectSchema {...(this.props as any)} level={this.props.level} />;
|
||||
}
|
||||
if (schema.displayType && !itemsSchema && !minMaxItems.length) {
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { DropdownOption, StyledDropdown } from '../../common-elements/dropdown';
|
||||
import { DropdownOption, Dropdown } from '../../common-elements/Dropdown';
|
||||
import { SchemaModel } from '../../services/models';
|
||||
|
||||
@observer
|
||||
|
@ -43,7 +43,7 @@ export class DiscriminatorDropdown extends React.Component<{
|
|||
this.sortOptions(options, enumValues);
|
||||
|
||||
return (
|
||||
<StyledDropdown
|
||||
<Dropdown
|
||||
value={activeValue}
|
||||
options={options}
|
||||
onChange={this.changeActiveChild}
|
||||
|
@ -53,6 +53,8 @@ export class DiscriminatorDropdown extends React.Component<{
|
|||
}
|
||||
|
||||
changeActiveChild = (option: DropdownOption) => {
|
||||
this.props.parent.activateOneOf(option.idx);
|
||||
if (option.idx !== undefined) {
|
||||
this.props.parent.activateOneOf(option.idx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from '../../common-elements/schema';
|
||||
import { Badge } from '../../common-elements/shelfs';
|
||||
import { SchemaModel } from '../../services/models';
|
||||
import { ConstraintsView } from '../Fields/FieldContstraints';
|
||||
import { Schema, SchemaProps } from './Schema';
|
||||
|
||||
export interface OneOfButtonProps {
|
||||
|
@ -47,6 +48,8 @@ export class OneOfSchema extends React.Component<SchemaProps> {
|
|||
if (oneOf === undefined) {
|
||||
return null;
|
||||
}
|
||||
const activeSchema = oneOf[schema.activeOneOf];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<OneOfLabel> {schema.oneOfType} </OneOfLabel>
|
||||
|
@ -58,7 +61,8 @@ export class OneOfSchema extends React.Component<SchemaProps> {
|
|||
<div>
|
||||
{oneOf[schema.activeOneOf].deprecated && <Badge type="warning">Deprecated</Badge>}
|
||||
</div>
|
||||
<Schema {...this.props} schema={oneOf[schema.activeOneOf]} />
|
||||
<ConstraintsView constraints={activeSchema.constraints} />
|
||||
<Schema {...this.props} schema={activeSchema} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
16
src/components/Schema/RecursiveSchema.tsx
Normal file
16
src/components/Schema/RecursiveSchema.tsx
Normal file
|
@ -0,0 +1,16 @@
|
|||
import * as React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
|
||||
import { RecursiveLabel, TypeName, TypeTitle } from '../../common-elements/fields';
|
||||
import { l } from '../../services/Labels';
|
||||
import type { SchemaProps } from '.';
|
||||
|
||||
export const RecursiveSchema = observer(({ schema }: SchemaProps) => {
|
||||
return (
|
||||
<div>
|
||||
<TypeName>{schema.displayType}</TypeName>
|
||||
{schema.title && <TypeTitle> {schema.title} </TypeTitle>}
|
||||
<RecursiveLabel> {l('recursive')} </RecursiveLabel>
|
||||
</div>
|
||||
);
|
||||
});
|
|
@ -1,7 +1,6 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { RecursiveLabel, TypeName, TypeTitle } from '../../common-elements/fields';
|
||||
import { FieldDetails } from '../Fields/FieldDetails';
|
||||
|
||||
import { FieldModel, SchemaModel } from '../../services/models';
|
||||
|
@ -9,8 +8,9 @@ import { FieldModel, SchemaModel } from '../../services/models';
|
|||
import { ArraySchema } from './ArraySchema';
|
||||
import { ObjectSchema } from './ObjectSchema';
|
||||
import { OneOfSchema } from './OneOfSchema';
|
||||
import { RecursiveSchema } from './RecursiveSchema';
|
||||
|
||||
import { l } from '../../services/Labels';
|
||||
import { isArray } from '../../utils/helpers';
|
||||
|
||||
export interface SchemaOptions {
|
||||
showTitle?: boolean;
|
||||
|
@ -35,13 +35,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
|||
const { type, oneOf, discriminatorProp, isCircular } = schema;
|
||||
|
||||
if (isCircular) {
|
||||
return (
|
||||
<div>
|
||||
<TypeName>{schema.displayType}</TypeName>
|
||||
{schema.title && <TypeTitle> {schema.title} </TypeTitle>}
|
||||
<RecursiveLabel> {l('recursive')} </RecursiveLabel>
|
||||
</div>
|
||||
);
|
||||
return <RecursiveSchema schema={schema} />;
|
||||
}
|
||||
|
||||
if (discriminatorProp !== undefined) {
|
||||
|
@ -51,11 +45,14 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
|||
);
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
const activeSchema = oneOf[schema.activeOneOf];
|
||||
return activeSchema.isCircular ? (
|
||||
<RecursiveSchema schema={activeSchema} />
|
||||
) : (
|
||||
<ObjectSchema
|
||||
{...rest}
|
||||
level={level}
|
||||
schema={oneOf![schema.activeOneOf]}
|
||||
schema={activeSchema}
|
||||
discriminator={{
|
||||
fieldName: discriminatorProp,
|
||||
parentSchema: schema,
|
||||
|
@ -68,7 +65,7 @@ export class Schema extends React.Component<Partial<SchemaProps>> {
|
|||
return <OneOfSchema schema={schema} {...rest} />;
|
||||
}
|
||||
|
||||
const types = Array.isArray(type) ? type : [type];
|
||||
const types = isArray(type) ? type : [type];
|
||||
if (types.includes('object')) {
|
||||
if (schema.fields?.length) {
|
||||
return <ObjectSchema {...(this.props as any)} level={level} />;
|
||||
|
|
|
@ -14,6 +14,7 @@ export interface ObjectDescriptionProps {
|
|||
exampleRef?: string;
|
||||
showReadOnly?: boolean;
|
||||
showWriteOnly?: boolean;
|
||||
showExample?: boolean;
|
||||
parser: OpenAPIParser;
|
||||
options: RedocNormalizedOptions;
|
||||
}
|
||||
|
@ -53,7 +54,7 @@ export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps
|
|||
}
|
||||
|
||||
render() {
|
||||
const { showReadOnly = true, showWriteOnly = false } = this.props;
|
||||
const { showReadOnly = true, showWriteOnly = false, showExample = true } = this.props;
|
||||
return (
|
||||
<Section>
|
||||
<Row>
|
||||
|
@ -64,18 +65,30 @@ export class SchemaDefinition extends React.PureComponent<ObjectDescriptionProps
|
|||
schema={this.mediaModel.schema}
|
||||
/>
|
||||
</MiddlePanel>
|
||||
<DarkRightPanel>
|
||||
<MediaSamplesWrap>
|
||||
<MediaTypeSamples renderDropdown={this.renderDropdown} mediaType={this.mediaModel} />
|
||||
</MediaSamplesWrap>
|
||||
</DarkRightPanel>
|
||||
{showExample && (
|
||||
<DarkRightPanel>
|
||||
<MediaSamplesWrap>
|
||||
<MediaTypeSamples
|
||||
renderDropdown={this.renderDropdown}
|
||||
mediaType={this.mediaModel}
|
||||
/>
|
||||
</MediaSamplesWrap>
|
||||
</DarkRightPanel>
|
||||
)}
|
||||
</Row>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
private renderDropdown = props => {
|
||||
return <DropdownOrLabel Label={MimeLabel} Dropdown={InvertedSimpleDropdown} {...props} />;
|
||||
return (
|
||||
<DropdownOrLabel
|
||||
Label={MimeLabel}
|
||||
Dropdown={InvertedSimpleDropdown}
|
||||
{...props}
|
||||
variant="dark"
|
||||
/>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { IMenuItem } from '../../services/MenuStore';
|
||||
import { SearchStore } from '../../services/SearchStore';
|
||||
import type { IMenuItem, SearchResult } from '../../services/types';
|
||||
import type { SearchStore } from '../../services/SearchStore';
|
||||
import type { MarkerService } from '../../services/MarkerService';
|
||||
|
||||
import { MenuItem } from '../SideMenu/MenuItem';
|
||||
|
||||
import { MarkerService } from '../../services/MarkerService';
|
||||
import { SearchResult } from '../../services/SearchWorker.worker';
|
||||
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import { bind, debounce } from 'decko';
|
||||
import { PerfectScrollbarWrap } from '../../common-elements/perfect-scrollbar';
|
||||
import {
|
||||
|
@ -37,6 +36,8 @@ export interface SearchBoxState {
|
|||
export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxState> {
|
||||
activeItemRef: MenuItem | null = null;
|
||||
|
||||
static contextType = OptionsContext;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
|
@ -114,8 +115,9 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
|||
}
|
||||
|
||||
search = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { minCharacterLengthToInitSearch } = this.context;
|
||||
const q = event.target.value;
|
||||
if (q.length < 3) {
|
||||
if (q.length < minCharacterLengthToInitSearch) {
|
||||
this.clearResults(q);
|
||||
return;
|
||||
}
|
||||
|
@ -130,12 +132,13 @@ export class SearchBox extends React.PureComponent<SearchBoxProps, SearchBoxStat
|
|||
|
||||
render() {
|
||||
const { activeItemIdx } = this.state;
|
||||
const results = this.state.results.map(res => ({
|
||||
item: this.props.getItemById(res.meta)!,
|
||||
score: res.score,
|
||||
}));
|
||||
|
||||
results.sort((a, b) => b.score - a.score);
|
||||
const results = this.state.results
|
||||
.filter(res => this.props.getItemById(res.meta))
|
||||
.map(res => ({
|
||||
item: this.props.getItemById(res.meta)!,
|
||||
score: res.score,
|
||||
}))
|
||||
.sort((a, b) => b.score - a.score);
|
||||
|
||||
return (
|
||||
<SearchWrap role="search">
|
||||
|
|
71
src/components/SecurityRequirement/OAuthFlow.tsx
Normal file
71
src/components/SecurityRequirement/OAuthFlow.tsx
Normal file
|
@ -0,0 +1,71 @@
|
|||
import * as React from 'react';
|
||||
import { OpenAPISecurityScheme } from '../../types';
|
||||
import { SecurityRow } from './styled.elements';
|
||||
import { SeeMore } from '../SeeMore/SeeMore';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
|
||||
export interface OAuthFlowProps {
|
||||
type: string;
|
||||
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
|
||||
RequiredScopes?: JSX.Element;
|
||||
}
|
||||
|
||||
export function OAuthFlowComponent(props: OAuthFlowProps) {
|
||||
const { type, flow, RequiredScopes } = props;
|
||||
const scopesNames = Object.keys(flow?.scopes || {});
|
||||
|
||||
return (
|
||||
<>
|
||||
<SecurityRow>
|
||||
<b>Flow type: </b>
|
||||
<code>{type} </code>
|
||||
</SecurityRow>
|
||||
{(type === 'implicit' || type === 'authorizationCode') && (
|
||||
<SecurityRow>
|
||||
<strong> Authorization URL: </strong>
|
||||
<code>
|
||||
<a target="_blank" rel="noopener noreferrer" href={(flow as any).authorizationUrl}>
|
||||
{(flow as any).authorizationUrl}
|
||||
</a>
|
||||
</code>
|
||||
</SecurityRow>
|
||||
)}
|
||||
{(type === 'password' || type === 'clientCredentials' || type === 'authorizationCode') && (
|
||||
<SecurityRow>
|
||||
<b> Token URL: </b>
|
||||
<code>{(flow as any).tokenUrl}</code>
|
||||
</SecurityRow>
|
||||
)}
|
||||
{flow!.refreshUrl && (
|
||||
<SecurityRow>
|
||||
<strong> Refresh URL: </strong>
|
||||
{flow!.refreshUrl}
|
||||
</SecurityRow>
|
||||
)}
|
||||
{!!scopesNames.length && (
|
||||
<>
|
||||
{RequiredScopes || null}
|
||||
<SecurityRow>
|
||||
<b> Scopes: </b>
|
||||
</SecurityRow>
|
||||
<SeeMore height="4em">
|
||||
<ul>
|
||||
{scopesNames.map(scope => (
|
||||
<li key={scope}>
|
||||
<code>{scope}</code> -{' '}
|
||||
<Markdown
|
||||
className={'redoc-markdown'}
|
||||
inline={true}
|
||||
source={flow!.scopes[scope] || ''}
|
||||
/>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</SeeMore>
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export const OAuthFlow = React.memo<OAuthFlowProps>(OAuthFlowComponent);
|
18
src/components/SecurityRequirement/RequiredScopesRow.tsx
Normal file
18
src/components/SecurityRequirement/RequiredScopesRow.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import * as React from 'react';
|
||||
|
||||
export const RequiredScopesRow = ({ scopes }: { scopes: string[] }): JSX.Element | null => {
|
||||
if (!scopes.length) return null;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<b>Required scopes: </b>
|
||||
{scopes.map((scope, idx) => {
|
||||
return (
|
||||
<React.Fragment key={idx}>
|
||||
<code>{scope}</code>{' '}
|
||||
</React.Fragment>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
65
src/components/SecurityRequirement/SecurityDetails.tsx
Normal file
65
src/components/SecurityRequirement/SecurityDetails.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import * as React from 'react';
|
||||
import { SecuritySchemeModel } from '../../services';
|
||||
import { titleize } from '../../utils';
|
||||
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||
import { SecurityRow } from './styled.elements';
|
||||
import { OAuthFlow } from './OAuthFlow';
|
||||
|
||||
interface SecuritySchemaProps {
|
||||
RequiredScopes?: JSX.Element;
|
||||
scheme: SecuritySchemeModel;
|
||||
}
|
||||
export function SecurityDetails(props: SecuritySchemaProps) {
|
||||
const { RequiredScopes, scheme } = props;
|
||||
|
||||
return (
|
||||
<StyledMarkdownBlock>
|
||||
{scheme.apiKey ? (
|
||||
<>
|
||||
<SecurityRow>
|
||||
<b>{titleize(scheme.apiKey.in || '')} parameter name: </b>
|
||||
<code>{scheme.apiKey.name}</code>
|
||||
</SecurityRow>
|
||||
{RequiredScopes}
|
||||
</>
|
||||
) : scheme.http ? (
|
||||
<>
|
||||
<SecurityRow>
|
||||
<b>HTTP Authorization Scheme: </b>
|
||||
<code>{scheme.http.scheme}</code>
|
||||
</SecurityRow>
|
||||
<SecurityRow>
|
||||
{scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
|
||||
<>
|
||||
<b>Bearer format: </b>
|
||||
<code>{scheme.http.bearerFormat}</code>
|
||||
</>
|
||||
)}
|
||||
</SecurityRow>
|
||||
{RequiredScopes}
|
||||
</>
|
||||
) : scheme.openId ? (
|
||||
<>
|
||||
<SecurityRow>
|
||||
<b>Connect URL: </b>
|
||||
<code>
|
||||
<a target="_blank" rel="noopener noreferrer" href={scheme.openId.connectUrl}>
|
||||
{scheme.openId.connectUrl}
|
||||
</a>
|
||||
</code>
|
||||
</SecurityRow>
|
||||
{RequiredScopes}
|
||||
</>
|
||||
) : scheme.flows ? (
|
||||
Object.keys(scheme.flows).map(type => (
|
||||
<OAuthFlow
|
||||
key={type}
|
||||
type={type}
|
||||
RequiredScopes={RequiredScopes}
|
||||
flow={scheme.flows[type]}
|
||||
/>
|
||||
))
|
||||
) : null}
|
||||
</StyledMarkdownBlock>
|
||||
);
|
||||
}
|
45
src/components/SecurityRequirement/SecurityHeader.tsx
Normal file
45
src/components/SecurityRequirement/SecurityHeader.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
|
||||
import {
|
||||
ScopeName,
|
||||
SecurityRequirementAndWrap,
|
||||
SecurityRequirementOrWrap,
|
||||
} from './styled.elements';
|
||||
import * as React from 'react';
|
||||
import { AUTH_TYPES } from '../SecuritySchemes/SecuritySchemes';
|
||||
|
||||
export interface SecurityRequirementProps {
|
||||
security: SecurityRequirementModel;
|
||||
showSecuritySchemeType?: boolean;
|
||||
expanded: boolean;
|
||||
}
|
||||
|
||||
export function SecurityHeader(props: SecurityRequirementProps) {
|
||||
const { security, showSecuritySchemeType, expanded } = props;
|
||||
|
||||
const grouping = security.schemes.length > 1;
|
||||
if (security.schemes.length === 0)
|
||||
return <SecurityRequirementOrWrap expanded={expanded}>None</SecurityRequirementOrWrap>;
|
||||
return (
|
||||
<SecurityRequirementOrWrap expanded={expanded}>
|
||||
{grouping && '('}
|
||||
{security.schemes.map(scheme => {
|
||||
return (
|
||||
<SecurityRequirementAndWrap key={scheme.id}>
|
||||
{showSecuritySchemeType && `${AUTH_TYPES[scheme.type] || scheme.type}: `}
|
||||
<i>{scheme.displayName}</i>
|
||||
{expanded && scheme.scopes.length
|
||||
? [
|
||||
' (',
|
||||
scheme.scopes.map<React.ReactNode>(scope => (
|
||||
<ScopeName key={scope}>{scope}</ScopeName>
|
||||
)),
|
||||
') ',
|
||||
]
|
||||
: null}
|
||||
</SecurityRequirementAndWrap>
|
||||
);
|
||||
})}
|
||||
{grouping && ') '}
|
||||
</SecurityRequirementOrWrap>
|
||||
);
|
||||
}
|
|
@ -1,153 +1,102 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import styled, { media } from '../../styled-components';
|
||||
|
||||
import { Link, UnderlinedHeader } from '../../common-elements/';
|
||||
import { useState } from 'react';
|
||||
import { SecurityRequirementModel } from '../../services/models/SecurityRequirement';
|
||||
import { linksCss } from '../Markdown/styled.elements';
|
||||
|
||||
const ScopeNameList = styled.ul`
|
||||
display: inline;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: inherit;
|
||||
|
||||
&:after {
|
||||
content: ',';
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ScopeName = styled.code`
|
||||
font-size: ${props => props.theme.typography.code.fontSize};
|
||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||
border: 1px solid ${({ theme }) => theme.colors.border.dark};
|
||||
margin: 0 3px;
|
||||
padding: 0.2em;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
`;
|
||||
|
||||
const SecurityRequirementAndWrap = styled.span`
|
||||
&:after {
|
||||
content: ' AND ';
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
${linksCss};
|
||||
`;
|
||||
|
||||
const SecurityRequirementOrWrap = styled.span`
|
||||
&:before {
|
||||
content: '( ';
|
||||
font-weight: bold;
|
||||
}
|
||||
&:after {
|
||||
content: ' ) OR ';
|
||||
font-weight: bold;
|
||||
}
|
||||
&:last-child:after {
|
||||
content: ' )';
|
||||
}
|
||||
|
||||
&:only-child:before,
|
||||
&:only-child:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
${linksCss};
|
||||
`;
|
||||
|
||||
export interface SecurityRequirementProps {
|
||||
security: SecurityRequirementModel;
|
||||
}
|
||||
|
||||
export class SecurityRequirement extends React.PureComponent<SecurityRequirementProps> {
|
||||
render() {
|
||||
const security = this.props.security;
|
||||
return (
|
||||
<SecurityRequirementOrWrap>
|
||||
{security.schemes.length ? (
|
||||
security.schemes.map(scheme => {
|
||||
return (
|
||||
<SecurityRequirementAndWrap key={scheme.id}>
|
||||
<Link to={scheme.sectionId}>{scheme.displayName}</Link>
|
||||
{scheme.scopes.length > 0 && ' ('}
|
||||
<ScopeNameList>
|
||||
{scheme.scopes.map(scope => (
|
||||
<li key={scope}>
|
||||
<ScopeName>{scope}</ScopeName>
|
||||
</li>
|
||||
))}
|
||||
</ScopeNameList>
|
||||
{scheme.scopes.length > 0 && ') '}
|
||||
</SecurityRequirementAndWrap>
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<SecurityRequirementAndWrap>None</SecurityRequirementAndWrap>
|
||||
)}
|
||||
</SecurityRequirementOrWrap>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const AuthHeaderColumn = styled.div`
|
||||
flex: 1 1 auto;
|
||||
`;
|
||||
|
||||
const SecuritiesColumn = styled.div`
|
||||
width: ${props => props.theme.schema.defaultDetailsWidth};
|
||||
${media.lessThan('small')`
|
||||
margin-top: 10px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const AuthHeader = styled(UnderlinedHeader)`
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
const Wrap = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin: 1em 0;
|
||||
|
||||
${media.lessThan('small')`
|
||||
flex-direction: column;
|
||||
`}
|
||||
`;
|
||||
import {
|
||||
AuthHeader,
|
||||
AuthHeaderColumn,
|
||||
SecuritiesColumn,
|
||||
SecurityDetailsStyle,
|
||||
Wrap,
|
||||
} from './styled.elements';
|
||||
import { useStore } from '../StoreBuilder';
|
||||
import { SecurityHeader } from './SecurityHeader';
|
||||
import { RequiredScopesRow } from './RequiredScopesRow';
|
||||
import { AUTH_TYPES } from '../SecuritySchemes/SecuritySchemes';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { SecurityDetails } from './SecurityDetails';
|
||||
import { ShelfIcon } from '../../common-elements';
|
||||
|
||||
export interface SecurityRequirementsProps {
|
||||
securities: SecurityRequirementModel[];
|
||||
}
|
||||
|
||||
export class SecurityRequirements extends React.PureComponent<SecurityRequirementsProps> {
|
||||
render() {
|
||||
const securities = this.props.securities;
|
||||
if (!securities.length) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Wrap>
|
||||
<AuthHeaderColumn>
|
||||
<AuthHeader>Authorizations: </AuthHeader>
|
||||
export function SecurityRequirements(props: SecurityRequirementsProps) {
|
||||
const store = useStore();
|
||||
const showSecuritySchemeType = store?.options.showSecuritySchemeType;
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
const { securities } = props;
|
||||
|
||||
if (!securities?.length || store?.options.hideSecuritySection) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const operationSecuritySchemes = store?.spec.securitySchemes.schemes.filter(({ id }) => {
|
||||
return securities.find(security => security.schemes.find(scheme => scheme.id === id));
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Wrap expanded={expanded}>
|
||||
<AuthHeaderColumn onClick={() => setExpanded(!expanded)}>
|
||||
<AuthHeader>Authorizations:</AuthHeader>
|
||||
<ShelfIcon size={'1.3em'} direction={expanded ? 'down' : 'right'} />
|
||||
</AuthHeaderColumn>
|
||||
<SecuritiesColumn>
|
||||
<SecuritiesColumn expanded={expanded}>
|
||||
{securities.map((security, idx) => (
|
||||
<SecurityRequirement key={idx} security={security} />
|
||||
<SecurityHeader
|
||||
key={idx}
|
||||
expanded={expanded}
|
||||
showSecuritySchemeType={showSecuritySchemeType}
|
||||
security={security}
|
||||
/>
|
||||
))}
|
||||
</SecuritiesColumn>
|
||||
</Wrap>
|
||||
);
|
||||
}
|
||||
{expanded &&
|
||||
operationSecuritySchemes?.length &&
|
||||
operationSecuritySchemes.map((scheme, idx) => (
|
||||
<SecurityDetailsStyle key={idx}>
|
||||
<h5>
|
||||
<LockIcon /> {AUTH_TYPES[scheme.type] || scheme.type}: {scheme.id}
|
||||
</h5>
|
||||
<Markdown source={scheme.description || ''} />
|
||||
<SecurityDetails
|
||||
key={scheme.id}
|
||||
scheme={scheme}
|
||||
RequiredScopes={
|
||||
<RequiredScopesRow scopes={getRequiredScopes(scheme.id, securities)} />
|
||||
}
|
||||
/>
|
||||
</SecurityDetailsStyle>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const LockIcon = () => (
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="11" height="11">
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M18 10V6A6 6 0 0 0 6 6v4H3v14h18V10h-3zM8 6c0-2.206 1.794-4 4-4s4 1.794 4 4v4H8V6zm11 16H5V12h14v10z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function getRequiredScopes(id: string, securities: SecurityRequirementModel[]): string[] {
|
||||
const allScopes: string[] = [];
|
||||
let securitiesLength = securities.length;
|
||||
|
||||
while (securitiesLength--) {
|
||||
const security = securities[securitiesLength];
|
||||
let schemesLength = security.schemes.length;
|
||||
while (schemesLength--) {
|
||||
const scheme = security.schemes[schemesLength];
|
||||
if (scheme.id === id && Array.isArray(scheme.scopes)) {
|
||||
allScopes.push(...scheme.scopes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(new Set(allScopes));
|
||||
}
|
||||
|
|
129
src/components/SecurityRequirement/styled.elements.ts
Normal file
129
src/components/SecurityRequirement/styled.elements.ts
Normal file
|
@ -0,0 +1,129 @@
|
|||
import styled from 'styled-components';
|
||||
import { linksCss } from '../Markdown/styled.elements';
|
||||
import { media } from '../../styled-components';
|
||||
import { UnderlinedHeader } from '../../common-elements';
|
||||
|
||||
export const Header = styled.div`
|
||||
background-color: #e4e7eb;
|
||||
`;
|
||||
|
||||
export const ScopeNameList = styled.ul`
|
||||
display: inline;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: inherit;
|
||||
|
||||
&:after {
|
||||
content: ',';
|
||||
}
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const ScopeName = styled.code`
|
||||
font-size: ${props => props.theme.typography.code.fontSize};
|
||||
font-family: ${props => props.theme.typography.code.fontFamily};
|
||||
margin: 0 3px;
|
||||
padding: 0.2em;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
|
||||
&:after {
|
||||
content: ',';
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const SecurityRequirementAndWrap = styled.span`
|
||||
&:after {
|
||||
content: ' and ';
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&:last-child:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
${linksCss};
|
||||
`;
|
||||
|
||||
export const SecurityRequirementOrWrap = styled.span<{ expanded?: boolean }>`
|
||||
${p => !p.expanded && `white-space: nowrap;`}
|
||||
&:after {
|
||||
content: ' or ';
|
||||
${p => p.expanded && `content: ' or \\a';`}
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
&:last-child:after,
|
||||
&:only-child:after {
|
||||
content: none;
|
||||
}
|
||||
|
||||
${linksCss};
|
||||
`;
|
||||
|
||||
export const AuthHeaderColumn = styled.div`
|
||||
flex: 1 1 auto;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
export const SecuritiesColumn = styled.div<{ expanded?: boolean }>`
|
||||
width: ${props => props.theme.schema.defaultDetailsWidth};
|
||||
text-overflow: ellipsis;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
${p =>
|
||||
p.expanded &&
|
||||
`background: ${p.theme.colors.gray['100']};
|
||||
padding: 8px 9.6px;
|
||||
margin: 20px 0;
|
||||
width: 100%;
|
||||
`};
|
||||
${media.lessThan('small')`
|
||||
margin-top: 10px;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const AuthHeader = styled(UnderlinedHeader)`
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
`;
|
||||
|
||||
export const Wrap = styled.div<{ expanded?: boolean }>`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
margin: 1em 0;
|
||||
flex-direction: ${p => (p.expanded ? 'column' : 'row')};
|
||||
${media.lessThan('small')`
|
||||
flex-direction: column;
|
||||
`}
|
||||
`;
|
||||
|
||||
export const SecurityRow = styled.div`
|
||||
margin: 0.5em 0;
|
||||
`;
|
||||
|
||||
export const SecurityDetailsStyle = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.colors.border.dark};
|
||||
margin-bottom: 1.5em;
|
||||
padding-bottom: 0.7em;
|
||||
|
||||
h5 {
|
||||
line-height: 1em;
|
||||
margin: 0 0 0.6em;
|
||||
font-size: ${({ theme }) => theme.typography.fontSize};
|
||||
}
|
||||
|
||||
.redoc-markdown p:first-child {
|
||||
display: inline;
|
||||
}
|
||||
`;
|
|
@ -1,66 +1,18 @@
|
|||
import * as React from 'react';
|
||||
|
||||
import { SecuritySchemesModel } from '../../services/models';
|
||||
|
||||
import { H2, MiddlePanel, Row, Section, ShareLink } from '../../common-elements';
|
||||
import { OpenAPISecurityScheme } from '../../types';
|
||||
import { titleize } from '../../utils/helpers';
|
||||
import { SecuritySchemesModel } from '../../services';
|
||||
import { H2, Row, ShareLink, MiddlePanel, Section } from '../../common-elements';
|
||||
import { Markdown } from '../Markdown/Markdown';
|
||||
import { StyledMarkdownBlock } from '../Markdown/styled.elements';
|
||||
import { SecurityDetails } from '../SecurityRequirement/SecurityDetails';
|
||||
import { SecurityDetailsStyle, SecurityRow } from '../SecurityRequirement/styled.elements';
|
||||
|
||||
const AUTH_TYPES = {
|
||||
export const AUTH_TYPES = {
|
||||
oauth2: 'OAuth2',
|
||||
apiKey: 'API Key',
|
||||
http: 'HTTP',
|
||||
openIdConnect: 'OpenID Connect',
|
||||
};
|
||||
|
||||
export interface OAuthFlowProps {
|
||||
type: string;
|
||||
flow: OpenAPISecurityScheme['flows'][keyof OpenAPISecurityScheme['flows']];
|
||||
}
|
||||
|
||||
export class OAuthFlow extends React.PureComponent<OAuthFlowProps> {
|
||||
render() {
|
||||
const { type, flow } = this.props;
|
||||
return (
|
||||
<tr>
|
||||
<th> {type} OAuth Flow </th>
|
||||
<td>
|
||||
{type === 'implicit' || type === 'authorizationCode' ? (
|
||||
<div>
|
||||
<strong> Authorization URL: </strong>
|
||||
{(flow as any).authorizationUrl}
|
||||
</div>
|
||||
) : null}
|
||||
{type === 'password' || type === 'clientCredentials' || type === 'authorizationCode' ? (
|
||||
<div>
|
||||
<strong> Token URL: </strong>
|
||||
{(flow as any).tokenUrl}
|
||||
</div>
|
||||
) : null}
|
||||
{flow!.refreshUrl && (
|
||||
<div>
|
||||
<strong> Refresh URL: </strong>
|
||||
{flow!.refreshUrl}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<strong> Scopes: </strong>
|
||||
</div>
|
||||
<ul>
|
||||
{Object.keys(flow!.scopes || {}).map(scope => (
|
||||
<li key={scope}>
|
||||
<code>{scope}</code> - <Markdown inline={true} source={flow!.scopes[scope] || ''} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SecurityDefsProps {
|
||||
securitySchemes: SecuritySchemesModel;
|
||||
}
|
||||
|
@ -76,52 +28,13 @@ export class SecurityDefs extends React.PureComponent<SecurityDefsProps> {
|
|||
{scheme.displayName}
|
||||
</H2>
|
||||
<Markdown source={scheme.description || ''} />
|
||||
<StyledMarkdownBlock>
|
||||
<table className="security-details">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th> Security Scheme Type </th>
|
||||
<td> {AUTH_TYPES[scheme.type] || scheme.type} </td>
|
||||
</tr>
|
||||
{scheme.apiKey ? (
|
||||
<tr>
|
||||
<th> {titleize(scheme.apiKey.in || '')} parameter name:</th>
|
||||
<td> {scheme.apiKey.name} </td>
|
||||
</tr>
|
||||
) : scheme.http ? (
|
||||
[
|
||||
<tr key="scheme">
|
||||
<th> HTTP Authorization Scheme </th>
|
||||
<td> {scheme.http.scheme} </td>
|
||||
</tr>,
|
||||
scheme.http.scheme === 'bearer' && scheme.http.bearerFormat && (
|
||||
<tr key="bearer">
|
||||
<th> Bearer format </th>
|
||||
<td> "{scheme.http.bearerFormat}" </td>
|
||||
</tr>
|
||||
),
|
||||
]
|
||||
) : scheme.openId ? (
|
||||
<tr>
|
||||
<th> Connect URL </th>
|
||||
<td>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
href={scheme.openId.connectUrl}
|
||||
>
|
||||
{scheme.openId.connectUrl}
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
) : scheme.flows ? (
|
||||
Object.keys(scheme.flows).map(type => (
|
||||
<OAuthFlow key={type} type={type} flow={scheme.flows[type]} />
|
||||
))
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</StyledMarkdownBlock>
|
||||
<SecurityDetailsStyle>
|
||||
<SecurityRow>
|
||||
<b>Security Scheme Type: </b>
|
||||
<span>{AUTH_TYPES[scheme.type] || scheme.type}</span>
|
||||
</SecurityRow>
|
||||
<SecurityDetails scheme={scheme} />
|
||||
</SecurityDetailsStyle>
|
||||
</MiddlePanel>
|
||||
</Row>
|
||||
</Section>
|
||||
|
|
65
src/components/SeeMore/SeeMore.tsx
Normal file
65
src/components/SeeMore/SeeMore.tsx
Normal file
|
@ -0,0 +1,65 @@
|
|||
import * as React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
const TOLERANCE_PX = 20;
|
||||
|
||||
interface SeeMoreProps {
|
||||
children?: React.ReactNode;
|
||||
height: string;
|
||||
}
|
||||
|
||||
export function SeeMore({ children, height }: SeeMoreProps): JSX.Element {
|
||||
const ref = React.createRef() as React.RefObject<HTMLDivElement>;
|
||||
const [showMore, setShowMore] = React.useState(false);
|
||||
const [showLink, setShowLink] = React.useState(false);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (ref.current && ref.current.clientHeight + TOLERANCE_PX < ref.current.scrollHeight) {
|
||||
setShowLink(true);
|
||||
}
|
||||
}, [ref]);
|
||||
|
||||
const onClickMore = () => {
|
||||
setShowMore(!showMore);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Container
|
||||
ref={ref}
|
||||
className={showMore ? '' : 'container'}
|
||||
style={{ height: showMore ? 'auto' : height }}
|
||||
>
|
||||
{children}
|
||||
</Container>
|
||||
<ButtonContainer dimmed={!showMore}>
|
||||
{showLink && (
|
||||
<ButtonLinkStyled onClick={onClickMore}>
|
||||
{showMore ? 'See less' : 'See more'}
|
||||
</ButtonLinkStyled>
|
||||
)}
|
||||
</ButtonContainer>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const Container = styled.div`
|
||||
overflow-y: hidden;
|
||||
`;
|
||||
|
||||
const ButtonContainer = styled.div<{ dimmed?: boolean }>`
|
||||
text-align: center;
|
||||
line-height: 1.5em;
|
||||
${({ dimmed }) =>
|
||||
dimmed &&
|
||||
`background-image: linear-gradient(to bottom, transparent,rgb(255 255 255));
|
||||
position: relative;
|
||||
top: -0.5em;
|
||||
padding-top: 0.5em;
|
||||
background-position-y: -1em;
|
||||
`}
|
||||
`;
|
||||
|
||||
const ButtonLinkStyled = styled.a`
|
||||
cursor: pointer;
|
||||
`;
|
18
src/components/SideMenu/Logo.tsx
Normal file
18
src/components/SideMenu/Logo.tsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import * as React from 'react';
|
||||
|
||||
export default function RedoclyLogo(): JSX.Element | null {
|
||||
const [isDisplay, setDisplay] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
setDisplay(true);
|
||||
}, []);
|
||||
|
||||
return isDisplay ? (
|
||||
<img
|
||||
alt={'redocly logo'}
|
||||
onError={() => setDisplay(false)}
|
||||
src={'https://cdn.redoc.ly/redoc/logo-mini.svg'}
|
||||
/>
|
||||
) : null;
|
||||
}
|
|
@ -2,17 +2,20 @@ import { observer } from 'mobx-react';
|
|||
import * as React from 'react';
|
||||
|
||||
import { ShelfIcon } from '../../common-elements/shelfs';
|
||||
import { IMenuItem, OperationModel } from '../../services';
|
||||
import { OperationModel } from '../../services';
|
||||
import { shortenHTTPVerb } from '../../utils/openapi';
|
||||
import { MenuItems } from './MenuItems';
|
||||
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements';
|
||||
import { l } from '../../services/Labels';
|
||||
import { scrollIntoViewIfNeeded } from '../../utils';
|
||||
import { OptionsContext } from '../OptionsProvider';
|
||||
import type { IMenuItem } from '../../services';
|
||||
|
||||
export interface MenuItemProps {
|
||||
item: IMenuItem;
|
||||
onActivate?: (item: IMenuItem) => void;
|
||||
withoutChildren?: boolean;
|
||||
children?: React.ReactChild;
|
||||
}
|
||||
|
||||
@observer
|
||||
|
@ -70,37 +73,33 @@ export class MenuItem extends React.Component<MenuItemProps> {
|
|||
|
||||
export interface OperationMenuItemContentProps {
|
||||
item: OperationModel;
|
||||
children?: React.ReactChild;
|
||||
}
|
||||
|
||||
@observer
|
||||
export class OperationMenuItemContent extends React.Component<OperationMenuItemContentProps> {
|
||||
ref = React.createRef<HTMLLabelElement>();
|
||||
export const OperationMenuItemContent = observer((props: OperationMenuItemContentProps) => {
|
||||
const { item } = props;
|
||||
const ref = React.createRef<HTMLLabelElement>();
|
||||
const { showWebhookVerb } = React.useContext(OptionsContext);
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.props.item.active && this.ref.current) {
|
||||
scrollIntoViewIfNeeded(this.ref.current);
|
||||
React.useEffect(() => {
|
||||
if (props.item.active && ref.current) {
|
||||
scrollIntoViewIfNeeded(ref.current);
|
||||
}
|
||||
}
|
||||
}, [props.item.active, ref]);
|
||||
|
||||
render() {
|
||||
const { item } = this.props;
|
||||
return (
|
||||
<MenuItemLabel
|
||||
depth={item.depth}
|
||||
active={item.active}
|
||||
deprecated={item.deprecated}
|
||||
ref={this.ref}
|
||||
>
|
||||
{item.isWebhook ? (
|
||||
<OperationBadge type="hook">{l('webhook')}</OperationBadge>
|
||||
) : (
|
||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||
)}
|
||||
<MenuItemTitle width="calc(100% - 38px)">
|
||||
{item.sidebarLabel}
|
||||
{this.props.children}
|
||||
</MenuItemTitle>
|
||||
</MenuItemLabel>
|
||||
);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<MenuItemLabel depth={item.depth} active={item.active} deprecated={item.deprecated} ref={ref}>
|
||||
{item.isWebhook ? (
|
||||
<OperationBadge type="hook">
|
||||
{showWebhookVerb ? item.httpVerb : l('webhook')}
|
||||
</OperationBadge>
|
||||
) : (
|
||||
<OperationBadge type={item.httpVerb}>{shortenHTTPVerb(item.httpVerb)}</OperationBadge>
|
||||
)}
|
||||
<MenuItemTitle width="calc(100% - 38px)">
|
||||
{item.sidebarLabel}
|
||||
{props.children}
|
||||
</MenuItemTitle>
|
||||
</MenuItemLabel>
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { observer } from 'mobx-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { IMenuItem } from '../../services';
|
||||
import type { IMenuItem } from '../../services';
|
||||
|
||||
import { MenuItem } from './MenuItem';
|
||||
import { MenuItemUl } from './styled.elements';
|
||||
|
@ -26,7 +26,7 @@ export class MenuItems extends React.Component<MenuItemsProps> {
|
|||
className={className}
|
||||
style={this.props.style}
|
||||
expanded={expanded}
|
||||
{...(root ? { role: 'navigation' } : {})}
|
||||
{...(root ? { role: 'menu' } : {})}
|
||||
>
|
||||
{items.map((item, idx) => (
|
||||
<MenuItem key={idx} item={item} onActivate={this.props.onActivate} />
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user