Skip to content

Add ApiOperation component #416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/pretty-apples-relate.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'docusaurus-theme-redoc': minor
---

Add ApiOperation component to import and display the definitions of API operations within your Docusaurus docs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import React, { useEffect } from 'react';
import clsx from 'clsx';
import { ThemeProvider } from 'styled-components';
import '../../global';
import { Operation, OperationModel, Section } from 'redoc';
import { useSpec } from '../../utils/useSpec';
import { useSpecData } from '../useSpecData';
import type { ApiOperationProps as Props } from '../../types/common';
import '../Redoc/styles.css';
import './styles.css';

const ApiOperation: React.FC<Props> = ({
id,
example,
pointer,
...rest
}: Props): JSX.Element => {
const specProps = useSpecData(id);
const { store } = useSpec(specProps);

// The # at the start is not included
const operationPointer =
pointer.charAt(0) === '#' ? pointer.substring(1) : pointer;

// The menu contains a flattened list of spec items for easy searching
const model = store.menu.flatItems.find(
(item) =>
item instanceof OperationModel && item.pointer === operationPointer,
) as OperationModel | undefined;

if (!model) {
throw new Error(`Failed to resolve reference "${pointer}"`);
}

useEffect(() => {
/**
* @see https://github.com/Redocly/redoc/blob/823be24b313c3a2445df7e0801a0cc79c20bacd1/src/services/MenuStore.ts#L273-L276
*/
store.menu.dispose();
}, [store]);

return (
<ThemeProvider theme={store.options.theme}>
<div
className={clsx([
'redocusaurus',
'redocusaurus-operation',
example ? null : 'hide-example',
])}
>
<Section id={model.id} $underlined={true}>
<Operation operation={model} {...rest} />
</Section>
</div>
</ThemeProvider>
);
};

ApiOperation.defaultProps = {
example: false,
};

export default ApiOperation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ApiOperation from './ApiOperation';

export default ApiOperation;
14 changes: 14 additions & 0 deletions packages/docusaurus-theme-redoc/src/theme/ApiOperation/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.redocusaurus-operation.hide-example > div > div > div:nth-child(2) {
display: none;
}
.redocusaurus-operation.hide-example > div > div > div:nth-child(1) {
width: 100%;
}

.redocusaurus-operation h3 {
color: var(--ifm-color-white);
}

.redocusaurus-operation > div:last-child {
min-height: 0;
}
12 changes: 12 additions & 0 deletions packages/docusaurus-theme-redoc/src/types/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ export type ApiSchemaProps = Omit<
pointer: ObjectDescriptionProps['schemaRef'];
};

export type ApiOperationProps = MdxProps & {
/**
* Show the example or not
*/
example?: boolean;

/**
* Ref to the operation
*/
pointer: string;
};

export type ApiDocProps = {
specProps: SpecProps;
layoutProps?: Omit<LayoutProps, 'children'>;
Expand Down
23 changes: 23 additions & 0 deletions packages/docusaurus-theme-redoc/src/types/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ declare module '@theme/ApiSchema' {
export default ApiSchema;
}

declare module '@theme/ApiOperation' {
interface ApiOperationProps {
/**
* If you have multiple apis, then add a `id` field in the specs array
* And pass the same here
*/
id?: string;

/**
* Show the example or not
*/
example?: boolean;

/**
* Ref to the operation
*/
pointer: string;
}

const ApiOperation: (props: ApiOperationProps) => JSX.Element;
export default ApiOperation;
}

declare module '@theme/useSpecData' {
/**
* Load redocusaurus plugin data by ID
Expand Down
2 changes: 1 addition & 1 deletion website/docs/guides/build-time-rendering.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: Build Time Rendering
description: Parse the OpenAPI schema at build time and skip the loading screen
sidebar_position: 3
sidebar_position: 4
---

:::warning
Expand Down
2 changes: 1 addition & 1 deletion website/docs/guides/migrating-to-v1.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Migrating to V1
sidebar_position: 4
sidebar_position: 5
---

## Options Changed
Expand Down
2 changes: 1 addition & 1 deletion website/docs/guides/multiple-apis.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: Showing Multiple APIs
sidebar_position: 2
sidebar_position: 3
---

## Nested View with MDX
Expand Down
96 changes: 96 additions & 0 deletions website/docs/guides/operation-imports.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
---
title: Operation Imports
sidebar_position: 2
---

import ApiOperation from '@theme/ApiOperation';

# Operation Imports

You can import operation definitions from your API schema and render them in your Docusaurus Docs. You'll need to create an `.mdx` file and import the React Component. Read more [here about MDX in Docusaurus](https://docusaurus.io/docs/markdown-features/react).

# Import Operation Model in Docs

The `pointer` prop is passed on to [Redoc](https://redoc.ly/docs/resources/ref-guide/#pointer).

Note that `/` is escaped as `~1`.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a Redoc convention?


```tsx
import ApiOperation from '@theme/ApiOperation';

<ApiOperation pointer="#/paths/~1pet/post" />;
```

### Results

<ApiOperation pointer="#/paths/~1pet/post" />

## Import Operation Model (with example) in Docs

```tsx
import ApiOperation from '@theme/ApiOperation';

<ApiOperation example pointer="#/paths/~1pet/post" />;
```

### Results

<ApiOperation example pointer="#/paths/~1pet/post" />

## Importing Operation Model with multiple OpenAPI schemas

If you have multiple APIs loaded with redocusaurus, then it is recommended to add `id`s to the config so that you can refer them when loading operation models.

```js title="docusaurus.config.js"
const config = {
presets: [
'@docusaurus/preset-classic',
[
'redocusaurus',
{
specs: [
{
id: 'using-single-yaml',
spec: 'openapi/single-file/openapi.yaml',
route: '/examples/using-single-yaml/',
},
{
id: 'using-remote-url',
spec: 'https://redocly.github.io/redoc/openapi.yaml',
route: '/examples/using-remote-url/',
},
],
theme: {
/**
* Highlight color for docs
*/
primaryColor: '#1890ff',
/**
* Options to pass to redoc
* @see https://github.com/redocly/redoc#redoc-options-object
*/
options: { disableSearch: true },
},
},
],
],
title: 'Redocusaurus',
};
```

```tsx
import ApiOperation from '@theme/ApiOperation';

<ApiOperation id="using-single-yaml" pointer="#/paths/~1pet/post" />
<ApiOperation id="using-remote-url" pointer="#/paths/~1pet/post" />
```

### Results

#### For ID `id="using-single-yaml"`

<ApiOperation id="using-single-yaml" pointer="#/paths/~1pet/post" />

#### For ID `id="using-remote-url"`

<ApiOperation id="using-remote-url" pointer="#/paths/~1pet/post" />
Loading