docusaurus-theme-openapi-docs 4.1.0 → 4.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/lib/theme/ApiExplorer/ApiCodeBlock/Line/_Line.scss +0 -12
  2. package/lib/theme/ApiExplorer/Authorization/index.js +3 -0
  3. package/lib/theme/ApiExplorer/Body/index.js +11 -2
  4. package/lib/theme/ApiExplorer/CodeSnippets/index.js +2 -1
  5. package/lib/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  6. package/lib/theme/ApiItem/Layout/index.js +6 -2
  7. package/lib/theme/ApiItem/index.js +15 -4
  8. package/lib/theme/ApiTabs/_ApiTabs.scss +0 -1
  9. package/lib/theme/ArrayBrackets/index.d.ts +3 -0
  10. package/lib/theme/ArrayBrackets/index.js +50 -0
  11. package/lib/theme/Markdown/Details/_Details.scss +5 -2
  12. package/lib/theme/Markdown/index.js +160 -18
  13. package/lib/theme/ParamsDetails/index.d.ts +6 -0
  14. package/lib/theme/ParamsDetails/index.js +134 -0
  15. package/lib/theme/ParamsItem/index.d.ts +1 -0
  16. package/lib/theme/ParamsItem/index.js +11 -48
  17. package/lib/theme/RequestSchema/index.d.ts +15 -0
  18. package/lib/theme/RequestSchema/index.js +243 -0
  19. package/lib/theme/ResponseExamples/index.d.ts +18 -0
  20. package/lib/theme/ResponseExamples/index.js +194 -0
  21. package/lib/theme/ResponseHeaders/index.d.ts +13 -0
  22. package/lib/theme/ResponseHeaders/index.js +39 -0
  23. package/lib/theme/ResponseSchema/index.d.ts +15 -0
  24. package/lib/theme/ResponseSchema/index.js +208 -0
  25. package/lib/theme/Schema/index.d.ts +8 -0
  26. package/lib/theme/Schema/index.js +887 -0
  27. package/lib/theme/SchemaItem/index.d.ts +8 -8
  28. package/lib/theme/SchemaItem/index.js +11 -41
  29. package/lib/theme/SkeletonLoader/index.d.ts +6 -0
  30. package/lib/theme/SkeletonLoader/index.js +20 -0
  31. package/lib/theme/StatusCodes/index.d.ts +9 -0
  32. package/lib/theme/StatusCodes/index.js +81 -0
  33. package/lib/theme/styles.scss +56 -9
  34. package/package.json +13 -8
  35. package/src/theme/ApiExplorer/ApiCodeBlock/Line/_Line.scss +0 -12
  36. package/src/theme/ApiExplorer/Authorization/index.tsx +3 -0
  37. package/src/theme/ApiExplorer/Body/index.tsx +3 -2
  38. package/src/theme/ApiExplorer/CodeSnippets/index.tsx +2 -1
  39. package/src/theme/ApiExplorer/CodeTabs/_CodeTabs.scss +50 -0
  40. package/src/theme/ApiItem/Layout/index.tsx +5 -2
  41. package/src/theme/ApiItem/index.tsx +14 -2
  42. package/src/theme/ApiTabs/_ApiTabs.scss +0 -1
  43. package/src/theme/ArrayBrackets/index.tsx +37 -0
  44. package/src/theme/Markdown/Details/_Details.scss +5 -2
  45. package/src/theme/Markdown/index.js +160 -18
  46. package/src/theme/ParamsDetails/index.tsx +88 -0
  47. package/src/theme/ParamsItem/index.tsx +9 -36
  48. package/src/theme/RequestSchema/index.tsx +164 -0
  49. package/src/theme/ResponseExamples/index.tsx +192 -0
  50. package/src/theme/ResponseHeaders/index.tsx +49 -0
  51. package/src/theme/ResponseSchema/index.tsx +151 -0
  52. package/src/theme/Schema/index.tsx +935 -0
  53. package/src/theme/SchemaItem/index.tsx +21 -43
  54. package/src/theme/SkeletonLoader/index.tsx +18 -0
  55. package/src/theme/StatusCodes/index.tsx +72 -0
  56. package/src/theme/styles.scss +56 -9
@@ -23,10 +23,13 @@
23
23
 
24
24
  /* Top-Level Details Caret Styling */
25
25
  .openapi-left-panel__container > .openapi-markdown__details > summary::before,
26
- .openapi-markdown__details.mime > summary::before,
27
- .openapi-markdown__details.response > summary::before {
26
+ .openapi-markdown__details.mime > summary::before {
28
27
  top: 0.1rem;
29
28
  }
29
+
30
+ .openapi-markdown__details.response > summary::before {
31
+ top: 0.25rem; /* TODO: figure out why this is necessary */
32
+ }
30
33
  /* End of Top-Level Details Caret Styling */
31
34
 
32
35
  .openapi-markdown__details {
@@ -7,30 +7,172 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
+ import Admonition from "@theme/Admonition";
10
11
  import CodeBlock from "@theme/CodeBlock";
11
12
  import ReactMarkdown from "react-markdown";
12
13
  import rehypeRaw from "rehype-raw";
14
+ import remarkGfm from "remark-gfm";
15
+
16
+ function remarkAdmonition() {
17
+ return (tree) => {
18
+ const openingTagRegex = /^:::(\w+)(?:\[(.*?)\])?\s*$/;
19
+ const closingTagRegex = /^:::\s*$/;
20
+ const textOnlyAdmonition = /^:::(\w+)(?:\[(.*?)\])?\s*([\s\S]*?)\s*:::$/;
21
+
22
+ const nodes = [];
23
+ let bufferedChildren = [];
24
+
25
+ let insideAdmonition = false;
26
+ let type = null;
27
+ let title = null;
28
+
29
+ tree.children.forEach((node) => {
30
+ if (
31
+ node.type === "paragraph" &&
32
+ node.children.length === 1 &&
33
+ node.children[0].type === "text"
34
+ ) {
35
+ const text = node.children[0].value.trim();
36
+ const openingMatch = text.match(openingTagRegex);
37
+ const closingMatch = text.match(closingTagRegex);
38
+ const textOnlyAdmonitionMatch = text.match(textOnlyAdmonition);
39
+
40
+ if (textOnlyAdmonitionMatch) {
41
+ const type = textOnlyAdmonitionMatch[1];
42
+ const title = textOnlyAdmonitionMatch[2]
43
+ ? textOnlyAdmonitionMatch[2]?.trim()
44
+ : undefined;
45
+ const content = textOnlyAdmonitionMatch[3];
46
+
47
+ const admonitionNode = {
48
+ type: "admonition",
49
+ data: {
50
+ hName: "Admonition", // Tells ReactMarkdown to replace the node with Admonition component
51
+ hProperties: {
52
+ type, // Passed as a prop to the Admonition component
53
+ title,
54
+ },
55
+ },
56
+ children: [
57
+ {
58
+ type: "text",
59
+ value: content?.trim(), // Trim leading/trailing whitespace
60
+ },
61
+ ],
62
+ };
63
+ nodes.push(admonitionNode);
64
+ return;
65
+ }
66
+
67
+ if (openingMatch) {
68
+ type = openingMatch[1];
69
+ title = openingMatch[2] || type;
70
+ insideAdmonition = true;
71
+ return;
72
+ }
73
+
74
+ if (closingMatch && insideAdmonition) {
75
+ nodes.push({
76
+ type: "admonition",
77
+ data: {
78
+ hName: "Admonition",
79
+ hProperties: { type: type, title: title },
80
+ },
81
+ children: bufferedChildren,
82
+ });
83
+ bufferedChildren = [];
84
+ insideAdmonition = false;
85
+ type = null;
86
+ title = null;
87
+ return;
88
+ }
89
+ }
90
+
91
+ if (insideAdmonition) {
92
+ bufferedChildren.push(node);
93
+ } else {
94
+ nodes.push(node);
95
+ }
96
+ });
97
+
98
+ if (bufferedChildren.length > 0 && type) {
99
+ nodes.push({
100
+ type: "admonition",
101
+ data: {
102
+ hName: "Admonition",
103
+ hProperties: { type: type, title: title },
104
+ },
105
+ children: bufferedChildren,
106
+ });
107
+ }
108
+ tree.children = nodes;
109
+ };
110
+ }
111
+
112
+ function convertAstToHtmlStr(ast) {
113
+ if (!ast || !Array.isArray(ast)) {
114
+ return "";
115
+ }
116
+
117
+ const convertNode = (node) => {
118
+ switch (node.type) {
119
+ case "text":
120
+ return node.value;
121
+ case "element":
122
+ const { tagName, properties, children } = node;
123
+
124
+ // Convert attributes to a string
125
+ const attrs = properties
126
+ ? Object.entries(properties)
127
+ .map(([key, value]) => `${key}="${value}"`)
128
+ .join(" ")
129
+ : "";
130
+
131
+ // Convert children to HTML
132
+ const childrenHtml = children ? children.map(convertNode).join("") : "";
133
+
134
+ return `<${tagName} ${attrs}>${childrenHtml}</${tagName}>`;
135
+ default:
136
+ return "";
137
+ }
138
+ };
139
+
140
+ return ast.map(convertNode).join("");
141
+ }
13
142
 
14
143
  function Markdown({ children }) {
15
144
  return (
16
- <div>
17
- <ReactMarkdown
18
- children={children}
19
- rehypePlugins={[rehypeRaw]}
20
- components={{
21
- pre: "div",
22
- code({ node, inline, className, children, ...props }) {
23
- const match = /language-(\w+)/.exec(className || "");
24
- if (inline) return <code>{children}</code>;
25
- return !inline && match ? (
26
- <CodeBlock className={className}>{children}</CodeBlock>
27
- ) : (
28
- <CodeBlock>{children}</CodeBlock>
29
- );
30
- },
31
- }}
32
- />
33
- </div>
145
+ <ReactMarkdown
146
+ rehypePlugins={[rehypeRaw]}
147
+ remarkPlugins={[remarkGfm, remarkAdmonition]}
148
+ components={{
149
+ pre: (props) => <div {...props} />,
150
+ code({ node, inline, className, children, ...props }) {
151
+ const match = /language-(\w+)/.exec(className || "");
152
+ return match ? (
153
+ <CodeBlock className={className} language={match[1]} {...props}>
154
+ {children}
155
+ </CodeBlock>
156
+ ) : (
157
+ <code className={className} {...props}>
158
+ {children}
159
+ </code>
160
+ );
161
+ },
162
+ admonition: ({ node, ...props }) => {
163
+ const type = node.data?.hProperties?.type || "note";
164
+ const title = node.data?.hProperties?.title || type;
165
+ const content = convertAstToHtmlStr(node.children);
166
+ return (
167
+ <Admonition type={type} title={title} {...props}>
168
+ <div dangerouslySetInnerHTML={{ __html: content }} />
169
+ </Admonition>
170
+ );
171
+ },
172
+ }}
173
+ >
174
+ {children}
175
+ </ReactMarkdown>
34
176
  );
35
177
  }
36
178
 
@@ -0,0 +1,88 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import React, { Suspense } from "react";
9
+
10
+ import BrowserOnly from "@docusaurus/BrowserOnly";
11
+ import Details from "@theme/Details";
12
+ import ParamsItem from "@theme/ParamsItem";
13
+ import SkeletonLoader from "@theme/SkeletonLoader";
14
+
15
+ interface Props {
16
+ parameters: any[];
17
+ }
18
+
19
+ const ParamsDetailsComponent: React.FC<Props> = ({ parameters }) => {
20
+ const types = ["path", "query", "header", "cookie"];
21
+
22
+ return (
23
+ <>
24
+ {types.map((type) => {
25
+ const params = parameters?.filter((param: any) => param?.in === type);
26
+
27
+ if (!params || params.length === 0) {
28
+ return null;
29
+ }
30
+
31
+ const summaryElement = (
32
+ <summary>
33
+ <h3 className="openapi-markdown__details-summary-header-params">
34
+ {`${type.charAt(0).toUpperCase() + type.slice(1)} Parameters`}
35
+ </h3>
36
+ </summary>
37
+ );
38
+
39
+ return (
40
+ <Details
41
+ key={type}
42
+ className="openapi-markdown__details"
43
+ style={{ marginBottom: "1rem" }}
44
+ data-collapsed={false}
45
+ open={true}
46
+ summary={summaryElement}
47
+ >
48
+ <ul>
49
+ {params.map((param: any, index: number) => (
50
+ <ParamsItem
51
+ key={index}
52
+ className="paramsItem"
53
+ param={{
54
+ ...param,
55
+ enumDescriptions: Object.entries(
56
+ param?.schema?.["x-enumDescriptions"] ??
57
+ param?.schema?.items?.["x-enumDescriptions"] ??
58
+ {}
59
+ ),
60
+ }}
61
+ />
62
+ ))}
63
+ </ul>
64
+ </Details>
65
+ );
66
+ })}
67
+ </>
68
+ );
69
+ };
70
+
71
+ const ParamsDetails: React.FC<Props> = (props) => {
72
+ return (
73
+ <BrowserOnly fallback={<SkeletonLoader size="sm" />}>
74
+ {() => {
75
+ const LazyComponent = React.lazy(() =>
76
+ Promise.resolve({ default: ParamsDetailsComponent })
77
+ );
78
+ return (
79
+ <Suspense fallback={null}>
80
+ <LazyComponent {...props} />
81
+ </Suspense>
82
+ );
83
+ }}
84
+ </BrowserOnly>
85
+ );
86
+ };
87
+
88
+ export default ParamsDetails;
@@ -7,16 +7,12 @@
7
7
 
8
8
  import React from "react";
9
9
 
10
- import CodeBlock from "@theme/CodeBlock";
10
+ import Markdown from "@theme/Markdown";
11
11
  import SchemaTabs from "@theme/SchemaTabs";
12
12
  import TabItem from "@theme/TabItem";
13
13
  /* eslint-disable import/no-extraneous-dependencies*/
14
14
  import clsx from "clsx";
15
- import ReactMarkdown from "react-markdown";
16
- import rehypeRaw from "rehype-raw";
17
- import remarkGfm from "remark-gfm";
18
15
 
19
- import { createDescription } from "../../markdown/createDescription";
20
16
  import { getQualifierMessage, getSchemaName } from "../../markdown/schema";
21
17
  import { guard, toString } from "../../markdown/utils";
22
18
 
@@ -32,6 +28,7 @@ export interface ExampleObject {
32
28
  }
33
29
 
34
30
  export interface Props {
31
+ className: string;
35
32
  param: {
36
33
  description: string;
37
34
  example: any;
@@ -96,45 +93,21 @@ function ParamsItem({ param, ...rest }: Props) {
96
93
  <span className="openapi-schema__deprecated">deprecated</span>
97
94
  ));
98
95
 
99
- const renderSchema = guard(getQualifierMessage(schema), (message) => (
100
- <div>
101
- <ReactMarkdown
102
- children={createDescription(message)}
103
- rehypePlugins={[rehypeRaw]}
104
- />
105
- </div>
96
+ const renderQualifier = guard(getQualifierMessage(schema), (qualifier) => (
97
+ <Markdown>{qualifier}</Markdown>
106
98
  ));
107
99
 
108
100
  const renderDescription = guard(description, (description) => (
109
- <div>
110
- <ReactMarkdown
111
- children={createDescription(description)}
112
- components={{
113
- pre: "div",
114
- code({ node, inline, className, children, ...props }) {
115
- const match = /language-(\w+)/.exec(className || "");
116
- if (inline) return <code>{children}</code>;
117
- return !inline && match ? (
118
- <CodeBlock className={className}>{children}</CodeBlock>
119
- ) : (
120
- <CodeBlock>{children}</CodeBlock>
121
- );
122
- },
123
- }}
124
- rehypePlugins={[rehypeRaw]}
125
- />
126
- </div>
101
+ <Markdown>{description}</Markdown>
127
102
  ));
128
103
 
129
104
  const renderEnumDescriptions = guard(
130
105
  getEnumDescriptionMarkdown(enumDescriptions),
131
106
  (value) => {
132
107
  return (
133
- <ReactMarkdown
134
- rehypePlugins={[rehypeRaw]}
135
- remarkPlugins={[remarkGfm]}
136
- children={value}
137
- />
108
+ <div style={{ marginTop: ".5rem" }}>
109
+ <Markdown>{value}</Markdown>
110
+ </div>
138
111
  );
139
112
  }
140
113
  );
@@ -214,7 +187,7 @@ function ParamsItem({ param, ...rest }: Props) {
214
187
  {renderSchemaRequired}
215
188
  {renderDeprecated}
216
189
  </span>
217
- {renderSchema}
190
+ {renderQualifier}
218
191
  {renderDescription}
219
192
  {renderEnumDescriptions}
220
193
  {renderDefaultValue()}
@@ -0,0 +1,164 @@
1
+ /* ============================================================================
2
+ * Copyright (c) Palo Alto Networks
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ * ========================================================================== */
7
+
8
+ import React, { Suspense } from "react";
9
+
10
+ import BrowserOnly from "@docusaurus/BrowserOnly";
11
+ import Details from "@theme/Details";
12
+ import Markdown from "@theme/Markdown";
13
+ import MimeTabs from "@theme/MimeTabs"; // Assume these components exist
14
+ import SchemaNode from "@theme/Schema";
15
+ import SkeletonLoader from "@theme/SkeletonLoader";
16
+ import TabItem from "@theme/TabItem";
17
+ import { MediaTypeObject } from "docusaurus-plugin-openapi-docs/lib/openapi/types";
18
+
19
+ interface Props {
20
+ style?: React.CSSProperties;
21
+ title: string;
22
+ body: {
23
+ content?: {
24
+ [key: string]: MediaTypeObject;
25
+ };
26
+ description?: string;
27
+ required?: string[] | boolean;
28
+ };
29
+ }
30
+
31
+ const RequestSchemaComponent: React.FC<Props> = ({ title, body, style }) => {
32
+ if (
33
+ body === undefined ||
34
+ body.content === undefined ||
35
+ Object.keys(body).length === 0 ||
36
+ Object.keys(body.content).length === 0
37
+ ) {
38
+ return null;
39
+ }
40
+
41
+ const mimeTypes = Object.keys(body.content);
42
+
43
+ if (mimeTypes.length > 1) {
44
+ return (
45
+ <MimeTabs className="openapi-tabs__mime" schemaType="request">
46
+ {mimeTypes.map((mimeType) => {
47
+ const firstBody = body.content![mimeType].schema;
48
+ if (
49
+ firstBody === undefined ||
50
+ (firstBody.properties &&
51
+ Object.keys(firstBody.properties).length === 0)
52
+ ) {
53
+ return null;
54
+ }
55
+ return (
56
+ // @ts-ignore
57
+ <TabItem key={mimeType} label={mimeType} value={mimeType}>
58
+ <Details
59
+ className="openapi-markdown__details mime"
60
+ data-collapsed={false}
61
+ open={true}
62
+ style={style}
63
+ summary={
64
+ <>
65
+ <summary>
66
+ <h3 className="openapi-markdown__details-summary-header-body">
67
+ {title}
68
+ {body.required === true && (
69
+ <span className="openapi-schema__required">
70
+ required
71
+ </span>
72
+ )}
73
+ </h3>
74
+ </summary>
75
+ </>
76
+ }
77
+ >
78
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
79
+ {body.description && (
80
+ <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
81
+ <Markdown>{body.description}</Markdown>
82
+ </div>
83
+ )}
84
+ </div>
85
+ <ul style={{ marginLeft: "1rem" }}>
86
+ <SchemaNode schema={firstBody} schemaType="request" />
87
+ </ul>
88
+ </Details>
89
+ </TabItem>
90
+ );
91
+ })}
92
+ </MimeTabs>
93
+ );
94
+ }
95
+
96
+ const randomFirstKey = mimeTypes[0];
97
+ const firstBody =
98
+ body.content[randomFirstKey].schema ?? body.content![randomFirstKey];
99
+
100
+ if (firstBody === undefined) {
101
+ return null;
102
+ }
103
+
104
+ return (
105
+ <MimeTabs className="openapi-tabs__mime" schemaType="request">
106
+ {/* @ts-ignore */}
107
+ <TabItem label={randomFirstKey} value={`${randomFirstKey}-schema`}>
108
+ <Details
109
+ className="openapi-markdown__details mime"
110
+ data-collapsed={false}
111
+ open={true}
112
+ style={style}
113
+ summary={
114
+ <>
115
+ <summary>
116
+ <h3 className="openapi-markdown__details-summary-header-body">
117
+ {title}
118
+ {firstBody.type === "array" && (
119
+ <span style={{ opacity: "0.6" }}> array</span>
120
+ )}
121
+ {body.required && (
122
+ <strong className="openapi-schema__required">
123
+ required
124
+ </strong>
125
+ )}
126
+ </h3>
127
+ </summary>
128
+ </>
129
+ }
130
+ >
131
+ <div style={{ textAlign: "left", marginLeft: "1rem" }}>
132
+ {body.description && (
133
+ <div style={{ marginTop: "1rem", marginBottom: "1rem" }}>
134
+ <Markdown>{body.description}</Markdown>
135
+ </div>
136
+ )}
137
+ </div>
138
+ <ul style={{ marginLeft: "1rem" }}>
139
+ <SchemaNode schema={firstBody} schemaType="request" />
140
+ </ul>
141
+ </Details>
142
+ </TabItem>
143
+ </MimeTabs>
144
+ );
145
+ };
146
+
147
+ const RequestSchema: React.FC<Props> = (props) => {
148
+ return (
149
+ <BrowserOnly fallback={<SkeletonLoader size="sm" />}>
150
+ {() => {
151
+ const LazyComponent = React.lazy(() =>
152
+ Promise.resolve({ default: RequestSchemaComponent })
153
+ );
154
+ return (
155
+ <Suspense fallback={null}>
156
+ <LazyComponent {...props} />
157
+ </Suspense>
158
+ );
159
+ }}
160
+ </BrowserOnly>
161
+ );
162
+ };
163
+
164
+ export default RequestSchema;