Compare commits

..

No commits in common. "main" and "v2.4.0" have entirely different histories.
main ... v2.4.0

11 changed files with 391 additions and 562 deletions

View File

@ -1,17 +1,3 @@
# [2.5.0](https://github.com/Redocly/redoc/compare/v2.4.0...v2.5.0) (2025-04-14)
### Bug Fixes
* enhance accessibility for menu items with keyboard support ([#2655](https://github.com/Redocly/redoc/issues/2655)) ([2db293b](https://github.com/Redocly/redoc/commit/2db293bfb2973497dd33f31dc99e97f5bb90bbe8))
### Features
* add keyboard navigation support to JsonViewer component ([#2654](https://github.com/Redocly/redoc/issues/2654)) ([1b4126f](https://github.com/Redocly/redoc/commit/1b4126fde4531387f49c90f52efbd0c0e5f7b6ea))
# [2.4.0](https://github.com/Redocly/redoc/compare/v2.3.0...v2.4.0) (2025-02-07) # [2.4.0](https://github.com/Redocly/redoc/compare/v2.3.0...v2.4.0) (2025-02-07)

View File

@ -12,9 +12,9 @@ With Redocly CLI, you can bundle your OpenAPI definition and API documentation
First, you need to install the `@redocly/cli` package. First, you need to install the `@redocly/cli` package.
You can install it [globally](../../cli/installation#install-globally) using npm. You can install it [globally](../../cli/installation.md#install-globally) using npm or Yarn.
Or you can install it during [runtime](../../cli/installation#use-npx-at-runtime) using npx or Docker. Or you can install it during [runtime](../../cli/installation.md#use-npx-at-runtime) using npx or Docker.
## Step 2 - Build the HTML file ## Step 2 - Build the HTML file
@ -27,9 +27,9 @@ replacing `apis/openapi.yaml` with your API definition file's name and path:
redocly build-docs apis/openapi.yaml redocly build-docs apis/openapi.yaml
``` ```
See the [build-docs](../../cli/commands/build-docs) documentation for more information See the [build-docs](../../cli/commands/build-docs.md) documentation for more information
on the different options and ways you can use the command. on the different options and ways you can use the command.
Also, check out [Redocly CLI commands](../../cli/commands), for more Also, check out [Redocly CLI commands](../../cli/commands/index.md), for more
information on the different things you can do with Redocly CLI including information on the different things you can do with Redocly CLI including
linting, splitting, and bundling your API definition file. linting, splitting, and bundling your API definition file.

View File

@ -45,6 +45,34 @@ section in the documentation.
If you want to view your Redoc output locally, you can simulate an HTTP server. If you want to view your Redoc output locally, you can simulate an HTTP server.
#### Redocly CLI
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 [Redocly CLI](https://redocly.com/docs/cli/#installation-and-usage) installed, `cd` into your
project directory and run the following command:
```bash
redocly preview-docs openapi.yaml
```
Replace `openapi.yaml` in the example command with the file path to your OpenAPI definition.
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`.
You can alter the port if you are using 8080 already, for example:
```bash
redocly 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
[Redocly CLI commands](https://redocly.com/docs/cli/commands/preview-docs/#preview-docs) in the Redocly CLI documentation.
#### Python #### Python
If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your If you have [Python 3](https://www.python.org/downloads/) installed, `cd` into your

View File

@ -57,7 +57,7 @@ Check out the [deployment documentation](./deployment/intro.md) for more options
Redoc is highly configurable. Each deployment option accepts configuration in a way that's appropriate to that platform, but the options are the same for each. For example: Redoc is highly configurable. Each deployment option accepts configuration in a way that's appropriate to that platform, but the options are the same for each. For example:
* Using [Redocly CLI](../cli), configuration goes in the `redocly.yaml` file, or can be supplied as command line parameters, such as `--theme.openapi.disableSearch`. * Using [Redocly CLI](../cli/index.md), configuration goes in the `redocly.yaml` file, or can be supplied as command line parameters, such as `--theme.openapi.disableSearch`.
* Using HTML or React, configure by setting `option` in the tag. * Using HTML or React, configure by setting `option` in the tag.
Here's a sample `redocly.yaml` configuration file, showing a few common settings and tweaking some of the visual theme settings: Here's a sample `redocly.yaml` configuration file, showing a few common settings and tweaking some of the visual theme settings:
@ -85,13 +85,13 @@ theme:
Redocly CLI detects a file named `redocly.yaml` in the same directory as you run the command and uses it. See the documentation with a command like this: Redocly CLI detects a file named `redocly.yaml` in the same directory as you run the command and uses it. See the documentation with a command like this:
```sh ```sh
redocly build-docs openapi.yaml redocly preview-docs openapi.yaml
``` ```
There are many, many more options available. Visit the [configuration reference](./config.md) for a complete list. There are many, many more options available. Visit the [configuration reference](./config.md) for a complete list.
## Next steps ## Next steps
* If you are new to OpenAPI, try the [OpenAPI starter project](../cli/openapi-starter) for a great introduction. * If you are new to OpenAPI, try the [OpenAPI starter project](../cli/openapi-starter.md) for a great introduction.
* Ready to build documentation from an existing OpenAPI file? Go to the [Redoc quickstart](./quickstart.md) and get started. * Ready to build documentation from an existing OpenAPI file? Go to the [Redoc quickstart](./quickstart.md) and get started.
* Learn more about the project by visiting [Redoc on GitHub](https://github.com/Redocly/redoc). * Learn more about the project by visiting [Redoc on GitHub](https://github.com/Redocly/redoc).

View File

@ -12,39 +12,38 @@ You can use the following [vendor extensions](https://redocly.com/docs/openapi-v
- [x-tagGroups example](#x-taggroups-example) - [x-tagGroups example](#x-taggroups-example)
- [Info Object](#info-object) - [Info Object](#info-object)
- [x-logo](#x-logo) - [x-logo](#x-logo)
- [How to use with Redoc](#how-to-use-with-redoc-1) - [How to use with Redoc](#how-to-use-with-redoc-2)
- [Logo Object](#logo-object) - [Logo Object](#logo-object)
- [Fixed fields](#fixed-fields-1) - [Fixed fields](#fixed-fields-1)
- [x-logo example](#x-logo-example) - [x-logo example](#x-logo-example)
- [Tag Object](#tag-object) - [Tag Object](#tag-object)
- [x-traitTag](#x-traittag) - [x-traitTag](#x-traittag)
- [How to use with Redoc](#how-to-use-with-redoc-2) - [How to use with Redoc](#how-to-use-with-redoc-3)
- [x-traitTag example](#x-traittag-example) - [x-traitTag example](#x-traittag-example)
- [x-displayName](#x-displayname) - [x-displayName](#x-displayname)
- [Operation Object vendor extensions](#operation-object-vendor-extensions) - [Operation Object vendor extensions](#operation-object-vendor-extensions)
- [x-codeSamples](#x-codesamples) - [x-codeSamples](#x-codesamples)
- [How to use with Redoc](#how-to-use-with-redoc-3) - [How to use with Redoc](#how-to-use-with-redoc-4)
- [Code Sample Object](#code-sample-object) - [Code Sample Object](#code-sample-object)
- [Fixed fields](#fixed-fields-2) - [Fixed fields](#fixed-fields-2)
- [Code Sample Object example](#code-sample-object-example) - [Code Sample Object example](#code-sample-object-example)
- [x-badges](#x-badges)
- [Parameter Object](#parameter-object) - [Parameter Object](#parameter-object)
- [x-examples](#x-examples) - [x-examples](#x-examples)
- [How to use with Redoc](#how-to-use-with-redoc-4) - [How to use with Redoc](#how-to-use-with-redoc-5)
- [Response Object vendor extensions](#response-object-vendor-extensions) - [Response Object vendor extensions](#response-object-vendor-extensions)
- [x-summary](#x-summary) - [x-summary](#x-summary)
- [How to use with Redoc](#how-to-use-with-redoc-5) - [How to use with Redoc](#how-to-use-with-redoc-6)
- [Schema Object](#schema-object) - [Schema Object](#schema-object)
- [x-nullable](#x-nullable) - [x-nullable](#x-nullable)
- [How to use with Redoc](#how-to-use-with-redoc-6)
- [x-additionalPropertiesName](#x-additionalpropertiesname)
- [How to use with Redoc](#how-to-use-with-redoc-7) - [How to use with Redoc](#how-to-use-with-redoc-7)
- [x-additionalPropertiesName](#x-additionalpropertiesname)
- [How to use with Redoc](#how-to-use-with-redoc-9)
- [x-additionalPropertiesName example](#x-additionalpropertiesname-example) - [x-additionalPropertiesName example](#x-additionalpropertiesname-example)
- [x-explicitMappingOnly](#x-explicitmappingonly) - [x-explicitMappingOnly](#x-explicitmappingonly)
- [How to use with Redoc](#how-to-use-with-redoc-8) - [How to use with Redoc](#how-to-use-with-redoc-10)
- [x-explicitMappingOnly example](#x-explicitmappingonly-example) - [x-explicitMappingOnly example](#x-explicitmappingonly-example)
- [x-enumDescriptions](#x-enumdescriptions) - [x-enumDescriptions](#x-enumdescriptions)
- [How to use with Redoc](#how-to-use-with-redoc-9) - [How to use with Redoc](#how-to-use-with-redoc-11)
- [x-enumDescriptions example](#x-enumdescriptions-example) - [x-enumDescriptions example](#x-enumdescriptions-example)
## Swagger Object ## Swagger Object
@ -68,7 +67,6 @@ Before you use `x-tagGroups`, make sure you **add all tags to a group**, since a
#### Tag Group Object #### Tag Group Object
Information about tags group Information about tags group
##### Fixed fields ##### Fixed fields
| Field Name | Type | Description | | Field Name | Type | Description |
| :---------- | :--------: | :---------- | | :---------- | :--------: | :---------- |
| name | string | The group name | | name | string | The group name |
@ -122,7 +120,6 @@ Extends the OpenAPI [Info Object](https://redocly.com/docs/openapi-visual-refere
The information about API logo The information about API logo
#### Fixed fields #### Fixed fields
| Field Name | Type | Description | | Field Name | Type | Description |
| :-------------- | :------: | :---------- | | :-------------- | :------: | :---------- |
| url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location | | url | string | The URL pointing to the spec logo. MUST be in the format of a URL. It SHOULD be an absolute URL so your API definition is usable from any location |
@ -161,7 +158,6 @@ info:
Extends the OpenAPI [Tag Object](https://redocly.com/docs/openapi-visual-reference/tags/) Extends the OpenAPI [Tag Object](https://redocly.com/docs/openapi-visual-reference/tags/)
### x-traitTag ### x-traitTag
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) | | x-traitTag | boolean | In Swagger two operations can have multiple tags. This property distinguishes between tags that are used to group operations (default) from tags that are used to mark operation with certain trait (`true` value) |
@ -196,7 +192,6 @@ x-traitTag: true
Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-reference/operation/) Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-reference/operation/)
### x-codeSamples ### x-codeSamples
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-codeSamples | [ [Code Sample Object](#code-sample-object) ] | A list of code samples associated with operation | | x-codeSamples | [ [Code Sample Object](#code-sample-object) ] | A list of code samples associated with operation |
@ -210,7 +205,6 @@ Extends the OpenAPI [Operation Object](https://redocly.com/docs/openapi-visual-r
Operation code sample Operation code sample
#### Fixed fields #### Fixed fields
| Field Name | Type | Description | | Field Name | Type | Description |
| :---------- | :------: | :----------- | | :---------- | :------: | :----------- |
| lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) | | lang | string | Code sample language. Value should be one of the following [list](https://github.com/github/linguist/blob/master/lib/linguist/popular.yml) |
@ -233,7 +227,6 @@ source: console.log('Hello World');
``` ```
### x-badges ### x-badges
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-badges | [[Badge Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-badges#badge-object)] | A list of badges associated with the operation | | x-badges | [[Badge Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-badges#badge-object)] | A list of badges associated with the operation |
@ -242,7 +235,6 @@ source: console.log('Hello World');
Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/) Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-reference/parameter/)
### x-examples ### x-examples
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-examples | [Example Object](https://redocly.com/docs/openapi-visual-reference/example/) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` | | x-examples | [Example Object](https://redocly.com/docs/openapi-visual-reference/example/) | Object that contains examples for the request. Applies when `in` is `body` and mime-type is `application/json` |
@ -254,7 +246,6 @@ Extends the OpenAPI [Parameter Object](https://redocly.com/docs/openapi-visual-r
Extends the OpenAPI [Response Object](https://redocly.com/docs/openapi-visual-reference/response/). Extends the OpenAPI [Response Object](https://redocly.com/docs/openapi-visual-reference/response/).
### x-summary ### x-summary
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-summary | string | a short summary of the response | | x-summary | string | a short summary of the response |
@ -266,7 +257,6 @@ If specified, you can use `x-summary` as the response button text, with descript
Extends the OpenAPI [Schema Object](https://redocly.com/docs/openapi-visual-reference/schemas/) Extends the OpenAPI [Schema Object](https://redocly.com/docs/openapi-visual-reference/schemas/)
### x-nullable ### x-nullable
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-nullable | boolean | marks schema as a nullable | | x-nullable | boolean | marks schema as a nullable |
@ -335,7 +325,6 @@ Pet:
Shows in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from the `Pet` class. Shows in the selectpicker only the items `cat` and `bee`, even though the `Dog` class inherits from the `Pet` class.
### x-enumDescriptions ### x-enumDescriptions
| Field Name | Type | Description | | Field Name | Type | Description |
| :------------- | :------: | :---------- | | :------------- | :------: | :---------- |
| x-enumDescriptions | [[Enum Description Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-enum-descriptions#enum-description-object)] | A list of the enum values and descriptions to include in the documentation. | | x-enumDescriptions | [[Enum Description Object](https://redocly.com/docs/realm/author/reference/openapi-extensions/x-enum-descriptions#enum-description-object)] | A list of the enum values and descriptions to include in the documentation. |

787
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "redoc", "name": "redoc",
"version": "2.5.0", "version": "2.4.0",
"description": "ReDoc", "description": "ReDoc",
"repository": { "repository": {
"type": "git", "type": "git",
@ -93,7 +93,7 @@
"cypress": "^13.8.1", "cypress": "^13.8.1",
"enzyme": "^3.11.0", "enzyme": "^3.11.0",
"enzyme-to-json": "^3.6.2", "enzyme-to-json": "^3.6.2",
"esbuild-loader": "^4.3.0", "esbuild-loader": "^3.0.1",
"eslint": "^7.27.0", "eslint": "^7.27.0",
"eslint-plugin-import": "^2.23.4", "eslint-plugin-import": "^2.23.4",
"eslint-plugin-react": "^7.34.2", "eslint-plugin-react": "^7.34.2",
@ -141,7 +141,7 @@
"@redocly/openapi-core": "^1.4.0", "@redocly/openapi-core": "^1.4.0",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"decko": "^1.2.0", "decko": "^1.2.0",
"dompurify": "^3.2.4", "dompurify": "^3.0.6",
"eventemitter3": "^5.0.1", "eventemitter3": "^5.0.1",
"json-pointer": "^0.6.2", "json-pointer": "^0.6.2",
"lunr": "^2.3.9", "lunr": "^2.3.9",

View File

@ -41,7 +41,6 @@ const Json = (props: JsonProps) => {
<OptionsContext.Consumer> <OptionsContext.Consumer>
{options => ( {options => (
<PrismDiv <PrismDiv
tabIndex={0}
className={props.className} className={props.className}
// tslint:disable-next-line // tslint:disable-next-line
ref={node => setNode(node!)} ref={node => setNode(node!)}

View File

@ -6,14 +6,11 @@ import { StylingMarkdownProps } from './Markdown';
import { StyledMarkdownBlock } from './styled.elements'; import { StyledMarkdownBlock } from './styled.elements';
import styled from 'styled-components'; import styled from 'styled-components';
// Workaround for DOMPurify type issues (https://github.com/cure53/DOMPurify/issues/1034)
const dompurify = DOMPurify['default'] as DOMPurify.DOMPurify;
const StyledMarkdownSpan = styled(StyledMarkdownBlock)` const StyledMarkdownSpan = styled(StyledMarkdownBlock)`
display: inline; display: inline;
`; `;
const sanitize = (sanitize, html) => (sanitize ? dompurify.sanitize(html) : html); const sanitize = (sanitize, html) => (sanitize ? DOMPurify.sanitize(html) : html);
export function SanitizedMarkdownHTML({ export function SanitizedMarkdownHTML({
inline, inline,

View File

@ -2,14 +2,14 @@ import { observer } from 'mobx-react';
import * as React from 'react'; import * as React from 'react';
import { ShelfIcon } from '../../common-elements/shelfs'; import { ShelfIcon } from '../../common-elements/shelfs';
import type { IMenuItem } from '../../services';
import { OperationModel } from '../../services'; import { OperationModel } from '../../services';
import { l } from '../../services/Labels';
import { scrollIntoViewIfNeeded } from '../../utils';
import { shortenHTTPVerb } from '../../utils/openapi'; import { shortenHTTPVerb } from '../../utils/openapi';
import { OptionsContext } from '../OptionsProvider';
import { MenuItems } from './MenuItems'; import { MenuItems } from './MenuItems';
import { MenuItemLabel, MenuItemLi, MenuItemTitle, OperationBadge } from './styled.elements'; 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 { export interface MenuItemProps {
item: IMenuItem; item: IMenuItem;
@ -47,18 +47,9 @@ export class MenuItem extends React.Component<MenuItemProps> {
<MenuItemLi <MenuItemLi
tabIndex={0} tabIndex={0}
onClick={this.activate} onClick={this.activate}
onKeyDown={evt => {
// Space or Enter key will activate the menu item
if (evt.key === 'Enter' || evt.key === ' ') {
this.props.onActivate!(this.props.item);
evt.stopPropagation();
}
}}
depth={item.depth} depth={item.depth}
data-item-id={item.id} data-item-id={item.id}
role="menuitem" role="menuitem"
aria-label={item.sidebarLabel}
aria-expanded={item.expanded}
> >
{item.type === 'operation' ? ( {item.type === 'operation' ? (
<OperationMenuItemContent {...this.props} item={item as OperationModel} /> <OperationMenuItemContent {...this.props} item={item as OperationModel} />

View File

@ -2,7 +2,6 @@
import { mount, ReactWrapper } from 'enzyme'; import { mount, ReactWrapper } from 'enzyme';
import * as React from 'react'; import * as React from 'react';
import { act } from 'react';
import { JsonViewer } from '../'; import { JsonViewer } from '../';
import { withTheme } from '../testProviders'; import { withTheme } from '../testProviders';
@ -51,54 +50,5 @@ describe('Components', () => {
expect(flatDataComponent.html()).not.toContain('Expand all'); expect(flatDataComponent.html()).not.toContain('Expand all');
expect(flatDataComponent.html()).not.toContain('Collapse all'); expect(flatDataComponent.html()).not.toContain('Collapse all');
}); });
describe('Keyboard Navigation', () => {
let component: ReactWrapper;
const data = {
a: 1,
b: {
c:
// Long string to test horizontal scrolling
Array(100).fill('hello').join(''),
},
};
beforeEach(() => {
component = mount(withTheme(<JsonViewer data={data} />));
ClipboardService.copySelected = origCopySelected;
});
test('should handle arrow key navigation', () => {
const prismDiv = component.find('div[tabIndex=0]');
const divElement = prismDiv.getDOMNode();
// Mock scrollLeft before events
Object.defineProperty(divElement, 'scrollLeft', {
get: jest.fn(() => 0),
set: jest.fn(),
});
// Trigger events inside act()
act(() => {
divElement.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowRight',
bubbles: true,
}),
);
});
act(() => {
divElement.dispatchEvent(
new KeyboardEvent('keydown', {
key: 'ArrowLeft',
bubbles: true,
}),
);
});
expect(divElement.scrollLeft).toBe(0);
});
});
}); });
}); });