@gitbook/react-openapi 1.1.10 → 1.2.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 (164) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/InteractiveSection.d.ts +4 -0
  3. package/dist/InteractiveSection.jsx +11 -11
  4. package/dist/OpenAPICodeSample.d.ts +2 -1
  5. package/dist/OpenAPICodeSample.jsx +6 -5
  6. package/dist/OpenAPICodeSampleInteractive.d.ts +3 -0
  7. package/dist/OpenAPICodeSampleInteractive.jsx +19 -43
  8. package/dist/OpenAPICodeSampleSelector.d.ts +3 -4
  9. package/dist/OpenAPICodeSampleSelector.jsx +6 -11
  10. package/dist/OpenAPICopyButton.d.ts +2 -0
  11. package/dist/OpenAPICopyButton.jsx +5 -2
  12. package/dist/OpenAPIDisclosure.d.ts +4 -3
  13. package/dist/OpenAPIDisclosure.jsx +8 -11
  14. package/dist/OpenAPIDisclosureGroup.d.ts +7 -3
  15. package/dist/OpenAPIDisclosureGroup.jsx +18 -18
  16. package/dist/OpenAPIExample.d.ts +4 -22
  17. package/dist/OpenAPIExample.jsx +5 -72
  18. package/dist/OpenAPIMediaType.d.ts +21 -0
  19. package/dist/OpenAPIMediaType.jsx +61 -0
  20. package/dist/OpenAPIOperation.d.ts +3 -2
  21. package/dist/OpenAPIOperation.jsx +9 -68
  22. package/dist/OpenAPIOperationDescription.d.ts +9 -0
  23. package/dist/OpenAPIOperationDescription.jsx +22 -0
  24. package/dist/OpenAPIOperationStability.d.ts +9 -0
  25. package/dist/OpenAPIOperationStability.jsx +27 -0
  26. package/dist/OpenAPIPath.d.ts +2 -0
  27. package/dist/OpenAPIPath.jsx +3 -2
  28. package/dist/OpenAPIRequestBody.d.ts +3 -1
  29. package/dist/OpenAPIRequestBody.jsx +4 -3
  30. package/dist/OpenAPIResponse.d.ts +1 -1
  31. package/dist/OpenAPIResponse.jsx +1 -1
  32. package/dist/OpenAPIResponseExample.d.ts +3 -2
  33. package/dist/OpenAPIResponseExample.jsx +24 -63
  34. package/dist/OpenAPIResponseExampleContent.d.ts +19 -0
  35. package/dist/OpenAPIResponseExampleContent.jsx +57 -0
  36. package/dist/OpenAPIResponses.d.ts +1 -1
  37. package/dist/OpenAPIResponses.jsx +49 -36
  38. package/dist/OpenAPISchema.d.ts +1 -1
  39. package/dist/OpenAPISchema.jsx +103 -15
  40. package/dist/OpenAPISchemaName.d.ts +2 -0
  41. package/dist/OpenAPISchemaName.jsx +19 -10
  42. package/dist/OpenAPISchemaServer.d.ts +1 -1
  43. package/dist/OpenAPISecurities.d.ts +2 -1
  44. package/dist/OpenAPISecurities.jsx +11 -10
  45. package/dist/OpenAPISelect.d.ts +10 -3
  46. package/dist/OpenAPISelect.jsx +20 -9
  47. package/dist/OpenAPISpec.d.ts +3 -2
  48. package/dist/OpenAPISpec.jsx +11 -9
  49. package/dist/OpenAPIWebhook.d.ts +10 -0
  50. package/dist/OpenAPIWebhook.jsx +23 -0
  51. package/dist/OpenAPIWebhookExample.d.ts +6 -0
  52. package/dist/OpenAPIWebhookExample.jsx +41 -0
  53. package/dist/ScalarApiButton.d.ts +2 -0
  54. package/dist/ScalarApiButton.jsx +4 -3
  55. package/dist/StaticSection.d.ts +4 -1
  56. package/dist/StaticSection.jsx +13 -4
  57. package/dist/code-samples.js +57 -39
  58. package/dist/common/OpenAPIColumnSpec.d.ts +6 -0
  59. package/dist/common/OpenAPIColumnSpec.jsx +20 -0
  60. package/dist/common/OpenAPIOperationDescription.d.ts +6 -0
  61. package/dist/common/OpenAPIOperationDescription.jsx +19 -0
  62. package/dist/common/OpenAPIStability.d.ts +4 -0
  63. package/dist/common/OpenAPIStability.jsx +15 -0
  64. package/dist/common/OpenAPISummary.d.ts +6 -0
  65. package/dist/common/OpenAPISummary.jsx +30 -0
  66. package/dist/context.d.ts +23 -2
  67. package/dist/context.js +32 -0
  68. package/dist/getOrCreateStoreByKey.d.ts +1 -1
  69. package/dist/getOrCreateStoreByKey.js +0 -1
  70. package/dist/index.d.ts +5 -1
  71. package/dist/index.js +3 -0
  72. package/dist/resolveOpenAPIWebhook.d.ts +11 -0
  73. package/dist/resolveOpenAPIWebhook.js +127 -0
  74. package/dist/schemas/OpenAPISchemas.d.ts +2 -2
  75. package/dist/schemas/OpenAPISchemas.jsx +19 -23
  76. package/dist/stringifyOpenAPI.d.ts +1 -1
  77. package/dist/stringifyOpenAPI.js +6 -3
  78. package/dist/translate.d.ts +10 -0
  79. package/dist/translate.jsx +75 -0
  80. package/dist/translations/de.d.ts +37 -0
  81. package/dist/translations/de.js +37 -0
  82. package/dist/translations/en.d.ts +37 -0
  83. package/dist/translations/en.js +37 -0
  84. package/dist/translations/es.d.ts +37 -0
  85. package/dist/translations/es.js +37 -0
  86. package/dist/translations/fr.d.ts +37 -0
  87. package/dist/translations/fr.js +37 -0
  88. package/dist/translations/index.d.ts +341 -0
  89. package/dist/translations/index.js +27 -0
  90. package/dist/translations/ja.d.ts +37 -0
  91. package/dist/translations/ja.js +37 -0
  92. package/dist/translations/nl.d.ts +37 -0
  93. package/dist/translations/nl.js +37 -0
  94. package/dist/translations/no.d.ts +37 -0
  95. package/dist/translations/no.js +37 -0
  96. package/dist/translations/pt-br.d.ts +37 -0
  97. package/dist/translations/pt-br.js +37 -0
  98. package/dist/translations/types.d.ts +5 -0
  99. package/dist/translations/types.js +1 -0
  100. package/dist/translations/zh.d.ts +37 -0
  101. package/dist/translations/zh.js +37 -0
  102. package/dist/tsconfig.build.tsbuildinfo +1 -1
  103. package/dist/types.d.ts +8 -50
  104. package/dist/util/example.d.ts +35 -0
  105. package/dist/util/example.jsx +103 -0
  106. package/dist/utils.d.ts +18 -0
  107. package/dist/utils.js +57 -0
  108. package/package.json +3 -3
  109. package/src/InteractiveSection.tsx +16 -14
  110. package/src/OpenAPICodeSample.tsx +22 -4
  111. package/src/OpenAPICodeSampleInteractive.tsx +38 -58
  112. package/src/OpenAPICodeSampleSelector.tsx +19 -12
  113. package/src/OpenAPICopyButton.tsx +7 -2
  114. package/src/OpenAPIDisclosure.tsx +20 -22
  115. package/src/OpenAPIDisclosureGroup.tsx +40 -22
  116. package/src/OpenAPIExample.tsx +8 -82
  117. package/src/OpenAPIMediaType.tsx +139 -0
  118. package/src/OpenAPIOperation.tsx +11 -100
  119. package/src/OpenAPIOperationDescription.tsx +34 -0
  120. package/src/OpenAPIOperationStability.tsx +39 -0
  121. package/src/OpenAPIPath.tsx +4 -1
  122. package/src/OpenAPIRequestBody.tsx +9 -4
  123. package/src/OpenAPIResponse.tsx +2 -2
  124. package/src/OpenAPIResponseExample.tsx +39 -108
  125. package/src/OpenAPIResponseExampleContent.tsx +123 -0
  126. package/src/OpenAPIResponses.tsx +83 -62
  127. package/src/OpenAPISchema.test.ts +80 -0
  128. package/src/OpenAPISchema.tsx +123 -16
  129. package/src/OpenAPISchemaName.tsx +26 -11
  130. package/src/OpenAPISchemaServer.tsx +1 -1
  131. package/src/OpenAPISecurities.tsx +33 -12
  132. package/src/OpenAPISelect.tsx +42 -16
  133. package/src/OpenAPISpec.tsx +21 -10
  134. package/src/OpenAPIWebhook.tsx +33 -0
  135. package/src/OpenAPIWebhookExample.tsx +60 -0
  136. package/src/ScalarApiButton.tsx +6 -6
  137. package/src/StaticSection.tsx +37 -5
  138. package/src/code-samples.test.ts +3 -1
  139. package/src/code-samples.ts +67 -54
  140. package/src/common/OpenAPIColumnSpec.tsx +31 -0
  141. package/src/common/OpenAPIOperationDescription.tsx +31 -0
  142. package/src/common/OpenAPIStability.tsx +23 -0
  143. package/src/common/OpenAPISummary.tsx +45 -0
  144. package/src/context.ts +37 -2
  145. package/src/getOrCreateStoreByKey.ts +1 -3
  146. package/src/index.ts +5 -1
  147. package/src/resolveOpenAPIWebhook.ts +99 -0
  148. package/src/schemas/OpenAPISchemas.tsx +34 -34
  149. package/src/stringifyOpenAPI.ts +11 -3
  150. package/src/translate.tsx +80 -0
  151. package/src/translations/de.ts +37 -0
  152. package/src/translations/en.ts +37 -0
  153. package/src/translations/es.ts +37 -0
  154. package/src/translations/fr.ts +37 -0
  155. package/src/translations/index.ts +33 -0
  156. package/src/translations/ja.ts +37 -0
  157. package/src/translations/nl.ts +37 -0
  158. package/src/translations/no.ts +37 -0
  159. package/src/translations/pt-br.ts +37 -0
  160. package/src/translations/types.ts +7 -0
  161. package/src/translations/zh.ts +37 -0
  162. package/src/types.ts +11 -53
  163. package/src/util/example.tsx +129 -0
  164. package/src/utils.ts +67 -0
@@ -0,0 +1,33 @@
1
+ import clsx from 'clsx';
2
+ import { OpenAPIWebhookExample } from './OpenAPIWebhookExample';
3
+ import { OpenAPIColumnSpec } from './common/OpenAPIColumnSpec';
4
+ import { OpenAPISummary } from './common/OpenAPISummary';
5
+ import { type OpenAPIContextInput, resolveOpenAPIContext } from './context';
6
+ import type { OpenAPIWebhookData } from './types';
7
+
8
+ /**
9
+ * Display an interactive OpenAPI webhook.
10
+ */
11
+ export function OpenAPIWebhook(props: {
12
+ className?: string;
13
+ data: OpenAPIWebhookData;
14
+ context: OpenAPIContextInput;
15
+ }) {
16
+ const { className, data, context: contextInput } = props;
17
+
18
+ const context = resolveOpenAPIContext(contextInput);
19
+
20
+ return (
21
+ <div className={clsx('openapi-webhook', className)}>
22
+ <OpenAPISummary data={data} context={context} />
23
+ <div className="openapi-columns">
24
+ <OpenAPIColumnSpec data={data} context={context} />
25
+ <div className="openapi-column-preview">
26
+ <div className="openapi-column-preview-body">
27
+ <OpenAPIWebhookExample data={data} context={context} />
28
+ </div>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ );
33
+ }
@@ -0,0 +1,60 @@
1
+ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
2
+ import { OpenAPIEmptyExample } from './OpenAPIExample';
3
+ import { OpenAPIMediaTypeContent } from './OpenAPIMediaType';
4
+ import { type OpenAPIContext, getOpenAPIClientContext } from './context';
5
+ import type { OpenAPIWebhookData } from './types';
6
+ import { getExamples } from './util/example';
7
+ import { createStateKey } from './utils';
8
+
9
+ export function OpenAPIWebhookExample(props: {
10
+ data: OpenAPIWebhookData;
11
+ context: OpenAPIContext;
12
+ }) {
13
+ const { data, context } = props;
14
+ const { operation } = data;
15
+
16
+ const items = (() => {
17
+ if (!operation.requestBody) {
18
+ return [];
19
+ }
20
+
21
+ return Object.entries(
22
+ operation.requestBody.content as Record<string, OpenAPIV3.MediaTypeObject>
23
+ ).map(([key, value]) => {
24
+ const schema = value.schema;
25
+
26
+ if (!schema) {
27
+ return {
28
+ key,
29
+ label: key,
30
+ body: <OpenAPIEmptyExample context={context} />,
31
+ };
32
+ }
33
+
34
+ return {
35
+ key,
36
+ label: key,
37
+ body: <></>,
38
+ examples: getExamples({
39
+ mediaTypeObject: value,
40
+ mediaType: key,
41
+ context,
42
+ }),
43
+ };
44
+ });
45
+ })();
46
+
47
+ return (
48
+ <div className="openapi-panel">
49
+ <h4 className="openapi-panel-heading">Payload</h4>
50
+ <div className="openapi-panel-body">
51
+ <OpenAPIMediaTypeContent
52
+ selectIcon={context.icons.chevronDown}
53
+ stateKey={createStateKey('request-body-media-type', context.blockKey)}
54
+ items={items}
55
+ context={getOpenAPIClientContext(context)}
56
+ />
57
+ </div>
58
+ </div>
59
+ );
60
+ }
@@ -6,6 +6,8 @@ import { createPortal } from 'react-dom';
6
6
 
7
7
  import type { OpenAPIV3_1 } from '@gitbook/openapi-parser';
8
8
  import { useOpenAPIOperationContext } from './OpenAPIOperationContext';
9
+ import type { OpenAPIClientContext } from './context';
10
+ import { t } from './translate';
9
11
 
10
12
  /**
11
13
  * Button which launches the Scalar API Client
@@ -14,8 +16,9 @@ export function ScalarApiButton(props: {
14
16
  method: OpenAPIV3_1.HttpMethods;
15
17
  path: string;
16
18
  specUrl: string;
19
+ context: OpenAPIClientContext;
17
20
  }) {
18
- const { method, path, specUrl } = props;
21
+ const { method, path, specUrl, context } = props;
19
22
  const [isOpen, setIsOpen] = useState(false);
20
23
  const controllerRef = useRef<ScalarModalControllerRef>(null);
21
24
  return (
@@ -27,7 +30,7 @@ export function ScalarApiButton(props: {
27
30
  setIsOpen(true);
28
31
  }}
29
32
  >
30
- Test it
33
+ {t(context.translation, 'test_it')}
31
34
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
32
35
  <path
33
36
  stroke="currentColor"
@@ -59,10 +62,7 @@ function ScalarModal(props: {
59
62
  }) {
60
63
  const { method, path, specUrl, controllerRef } = props;
61
64
  return (
62
- <ApiClientModalProvider
63
- configuration={{ spec: { url: specUrl } }}
64
- initialRequest={{ method, path }}
65
- >
65
+ <ApiClientModalProvider configuration={{ url: specUrl }} initialRequest={{ method, path }}>
66
66
  <ScalarModalController method={method} path={path} controllerRef={controllerRef} />
67
67
  </ApiClientModalProvider>
68
68
  );
@@ -42,18 +42,50 @@ export const SectionBody = forwardRef(function SectionBody(
42
42
  );
43
43
  });
44
44
 
45
+ export function SectionFooter(props: ComponentPropsWithoutRef<'div'>) {
46
+ return (
47
+ <div
48
+ {...props}
49
+ className={clsx(
50
+ 'openapi-section-footer',
51
+ props.className && `${props.className}-footer`
52
+ )}
53
+ />
54
+ );
55
+ }
56
+
57
+ export function SectionFooterContent(props: ComponentPropsWithoutRef<'div'>) {
58
+ return (
59
+ <div
60
+ {...props}
61
+ className={clsx(
62
+ 'openapi-section-footer-content',
63
+ props.className && `${props.className}-footer-content`
64
+ )}
65
+ />
66
+ );
67
+ }
68
+
45
69
  export function StaticSection(props: {
46
70
  className: string;
47
- header: React.ReactNode;
71
+ header?: React.ReactNode;
48
72
  children: React.ReactNode;
73
+ footer?: React.ReactNode;
49
74
  }) {
50
- const { className, header, children } = props;
75
+ const { className, header, children, footer } = props;
51
76
  return (
52
77
  <Section className={className}>
53
- <SectionHeader className={className}>
54
- <SectionHeaderContent className={className}>{header}</SectionHeaderContent>
55
- </SectionHeader>
78
+ {header ? (
79
+ <SectionHeader className={className}>
80
+ <SectionHeaderContent className={className}>{header}</SectionHeaderContent>
81
+ </SectionHeader>
82
+ ) : null}
56
83
  <SectionBody className={className}>{children}</SectionBody>
84
+ {footer ? (
85
+ <SectionFooter className={className}>
86
+ <SectionFooterContent className={className}>{footer}</SectionFooterContent>
87
+ </SectionFooter>
88
+ ) : null}
57
89
  </Section>
58
90
  );
59
91
  }
@@ -413,13 +413,15 @@ describe('python code sample generator', () => {
413
413
  },
414
414
  body: {
415
415
  key: 'value',
416
+ truethy: true,
417
+ falsey: false,
416
418
  },
417
419
  };
418
420
 
419
421
  const output = generator?.generate(input);
420
422
 
421
423
  expect(output).toBe(
422
- 'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data={"key":"value"}\n)\n\ndata = response.json()'
424
+ 'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False})\n)\n\ndata = response.json()'
423
425
  );
424
426
  });
425
427
 
@@ -3,6 +3,7 @@ import {
3
3
  isFormData,
4
4
  isFormUrlEncoded,
5
5
  isGraphQL,
6
+ isJSON,
6
7
  isPDF,
7
8
  isPlainObject,
8
9
  isText,
@@ -26,6 +27,52 @@ export interface CodeSampleGenerator {
26
27
  }
27
28
 
28
29
  export const codeSampleGenerators: CodeSampleGenerator[] = [
30
+ {
31
+ id: 'http',
32
+ label: 'HTTP',
33
+ syntax: 'bash',
34
+ generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
35
+ const { host, path } = parseHostAndPath(url);
36
+
37
+ if (body) {
38
+ // if we had a body add a content length header
39
+ const bodyContent = body ? stringifyOpenAPI(body) : '';
40
+ // handle unicode chars with a text encoder
41
+ const encoder = new TextEncoder();
42
+
43
+ const bodyString = BodyGenerators.getHTTPBody(body, headers);
44
+
45
+ if (bodyString) {
46
+ body = bodyString;
47
+ }
48
+
49
+ headers = {
50
+ ...headers,
51
+ 'Content-Length': encoder.encode(bodyContent).length.toString(),
52
+ };
53
+ }
54
+
55
+ if (!headers.hasOwnProperty('Accept')) {
56
+ headers.Accept = '*/*';
57
+ }
58
+
59
+ const headerString = headers
60
+ ? `${Object.entries(headers)
61
+ .map(([key, value]) =>
62
+ key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
63
+ )
64
+ .join('\n')}\n`
65
+ : '';
66
+
67
+ const bodyString = body ? `\n${body}` : '';
68
+
69
+ const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
70
+ Host: ${host}
71
+ ${headerString}${bodyString}`;
72
+
73
+ return httpRequest;
74
+ },
75
+ },
29
76
  {
30
77
  id: 'curl',
31
78
  label: 'cURL',
@@ -127,11 +174,14 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
127
174
  code += indent(`headers=${stringifyOpenAPI(headers)},\n`, 4);
128
175
  }
129
176
 
177
+ const contentType = headers?.['Content-Type'];
130
178
  if (body) {
131
179
  if (body === 'files') {
132
180
  code += indent(`files=${body}\n`, 4);
181
+ } else if (isJSON(contentType)) {
182
+ code += indent(`data=json.dumps(${body})\n`, 4);
133
183
  } else {
134
- code += indent(`data=${stringifyOpenAPI(body)}\n`, 4);
184
+ code += indent(`data=${body}\n`, 4);
135
185
  }
136
186
  }
137
187
 
@@ -140,52 +190,6 @@ export const codeSampleGenerators: CodeSampleGenerator[] = [
140
190
  return code;
141
191
  },
142
192
  },
143
- {
144
- id: 'http',
145
- label: 'HTTP',
146
- syntax: 'bash',
147
- generate: ({ method, url, headers = {}, body }: CodeSampleInput) => {
148
- const { host, path } = parseHostAndPath(url);
149
-
150
- if (body) {
151
- // if we had a body add a content length header
152
- const bodyContent = body ? stringifyOpenAPI(body) : '';
153
- // handle unicode chars with a text encoder
154
- const encoder = new TextEncoder();
155
-
156
- const bodyString = BodyGenerators.getHTTPBody(body, headers);
157
-
158
- if (bodyString) {
159
- body = bodyString;
160
- }
161
-
162
- headers = {
163
- ...headers,
164
- 'Content-Length': encoder.encode(bodyContent).length.toString(),
165
- };
166
- }
167
-
168
- if (!headers.hasOwnProperty('Accept')) {
169
- headers.Accept = '*/*';
170
- }
171
-
172
- const headerString = headers
173
- ? `${Object.entries(headers)
174
- .map(([key, value]) =>
175
- key.toLowerCase() !== 'host' ? `${key}: ${value}` : ''
176
- )
177
- .join('\n')}\n`
178
- : '';
179
-
180
- const bodyString = body ? `\n${body}` : '';
181
-
182
- const httpRequest = `${method.toUpperCase()} ${decodeURI(path)} HTTP/1.1
183
- Host: ${host}
184
- ${headerString}${bodyString}`;
185
-
186
- return httpRequest;
187
- },
188
- },
189
193
  ];
190
194
 
191
195
  function indent(code: string, spaces: number) {
@@ -343,18 +347,27 @@ const BodyGenerators = {
343
347
  }
344
348
  code += '}\n\n';
345
349
  body = 'files';
346
- }
347
-
348
- if (isPDF(contentType)) {
350
+ } else if (isPDF(contentType)) {
349
351
  code += 'files = {\n';
350
352
  code += `${indent(`"file": "${body}",`, 4)}\n`;
351
353
  code += '}\n\n';
352
354
  body = 'files';
353
- }
354
-
355
- if (isXML(contentType)) {
355
+ } else if (isXML(contentType)) {
356
356
  // Convert JSON to XML if needed
357
- body = convertBodyToXML(body);
357
+ body = JSON.stringify(convertBodyToXML(body));
358
+ } else {
359
+ body = stringifyOpenAPI(body, (_key, value) => {
360
+ switch (value) {
361
+ case true:
362
+ return '$$__TRUE__$$';
363
+ case false:
364
+ return '$$__FALSE__$$';
365
+ default:
366
+ return value;
367
+ }
368
+ })
369
+ .replaceAll('"$$__TRUE__$$"', 'True')
370
+ .replaceAll('"$$__FALSE__$$"', 'False');
358
371
  }
359
372
 
360
373
  return { body, code, headers };
@@ -0,0 +1,31 @@
1
+ import { OpenAPISpec } from '../OpenAPISpec';
2
+ import { type OpenAPIContext, getOpenAPIClientContext } from '../context';
3
+ import { t } from '../translate';
4
+ import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
5
+ import { OpenAPIOperationDescription } from './OpenAPIOperationDescription';
6
+
7
+ export function OpenAPIColumnSpec(props: {
8
+ data: OpenAPIOperationData | OpenAPIWebhookData;
9
+ context: OpenAPIContext;
10
+ }) {
11
+ const { data, context } = props;
12
+ const { operation } = data;
13
+
14
+ const clientContext = getOpenAPIClientContext(context);
15
+
16
+ return (
17
+ <div className="openapi-column-spec">
18
+ {operation['x-deprecated-sunset'] ? (
19
+ <div className="openapi-deprecated-sunset openapi-description openapi-markdown">
20
+ {t(context.translation, 'deprecated_and_sunset_on', [
21
+ <span key="date" className="openapi-deprecated-sunset-date">
22
+ {operation['x-deprecated-sunset']}
23
+ </span>,
24
+ ])}
25
+ </div>
26
+ ) : null}
27
+ <OpenAPIOperationDescription operation={operation} context={context} />
28
+ <OpenAPISpec data={data} context={clientContext} />
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,31 @@
1
+ import type { OpenAPICustomOperationProperties, OpenAPIV3 } from '@gitbook/openapi-parser';
2
+ import { Markdown } from '../Markdown';
3
+ import type { OpenAPIContext } from '../context';
4
+ import { resolveDescription } from '../utils';
5
+
6
+ export function OpenAPIOperationDescription(props: {
7
+ operation: OpenAPIV3.OperationObject<OpenAPICustomOperationProperties>;
8
+ context: OpenAPIContext;
9
+ }) {
10
+ const { operation } = props;
11
+ if (operation['x-gitbook-description-document']) {
12
+ return (
13
+ <div className="openapi-intro">
14
+ {props.context.renderDocument({
15
+ document: operation['x-gitbook-description-document'],
16
+ })}
17
+ </div>
18
+ );
19
+ }
20
+
21
+ const description = resolveDescription(operation);
22
+ if (!description) {
23
+ return null;
24
+ }
25
+
26
+ return (
27
+ <div className="openapi-intro">
28
+ <Markdown className="openapi-description" source={description} />
29
+ </div>
30
+ );
31
+ }
@@ -0,0 +1,23 @@
1
+ import type { OpenAPIStability as OpenAPIStabilityType } from '@gitbook/openapi-parser';
2
+
3
+ const stabilityEnum: Record<OpenAPIStabilityType, string> = {
4
+ experimental: 'Experimental',
5
+ alpha: 'Alpha',
6
+ beta: 'Beta',
7
+ } as const;
8
+
9
+ export function OpenAPIStability(props: { stability: OpenAPIStabilityType }) {
10
+ const { stability } = props;
11
+
12
+ const foundStability = stabilityEnum[stability];
13
+
14
+ if (!foundStability) {
15
+ return null;
16
+ }
17
+
18
+ return (
19
+ <div className={`openapi-stability openapi-stability-${foundStability.toLowerCase()}`}>
20
+ {foundStability}
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,45 @@
1
+ import { OpenAPIPath } from '../OpenAPIPath';
2
+ import type { OpenAPIContext } from '../context';
3
+ import type { OpenAPIOperationData, OpenAPIWebhookData } from '../types';
4
+ import { OpenAPIStability } from './OpenAPIStability';
5
+
6
+ export function OpenAPISummary(props: {
7
+ data: OpenAPIOperationData | OpenAPIWebhookData;
8
+ context: OpenAPIContext;
9
+ }) {
10
+ const { data, context } = props;
11
+ const { operation } = data;
12
+
13
+ const title = (() => {
14
+ if (operation.summary) {
15
+ return operation.summary;
16
+ }
17
+
18
+ if ('name' in data) {
19
+ return data.name;
20
+ }
21
+
22
+ return undefined;
23
+ })();
24
+
25
+ return (
26
+ <div className="openapi-summary" id={operation.summary ? undefined : context.id}>
27
+ {(operation.deprecated || operation['x-stability']) && (
28
+ <div className="openapi-summary-tags">
29
+ {operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
30
+ {operation['x-stability'] && (
31
+ <OpenAPIStability stability={operation['x-stability']} />
32
+ )}
33
+ </div>
34
+ )}
35
+ {title
36
+ ? context.renderHeading({
37
+ deprecated: operation.deprecated ?? false,
38
+ stability: operation['x-stability'],
39
+ title,
40
+ })
41
+ : null}
42
+ {'path' in data ? <OpenAPIPath data={data} context={context} /> : null}
43
+ </div>
44
+ );
45
+ }
package/src/context.ts CHANGED
@@ -1,4 +1,11 @@
1
+ import { type Translation, type TranslationLocale, translations } from './translations';
2
+
1
3
  export interface OpenAPIClientContext {
4
+ /**
5
+ * The translation language to use.
6
+ */
7
+ translation: Translation;
8
+
2
9
  /**
3
10
  * Icons used in the block.
4
11
  */
@@ -23,9 +30,14 @@ export interface OpenAPIClientContext {
23
30
  * Optional id attached to the heading and used as an anchor.
24
31
  */
25
32
  id?: string;
33
+
34
+ /**
35
+ * Mark the context as a client context.
36
+ */
37
+ $$isClientContext$$: true;
26
38
  }
27
39
 
28
- export interface OpenAPIContext extends OpenAPIClientContext {
40
+ export interface OpenAPIContext extends Omit<OpenAPIClientContext, '$$isClientContext$$'> {
29
41
  /**
30
42
  * Render a code block.
31
43
  */
@@ -51,14 +63,37 @@ export interface OpenAPIContext extends OpenAPIClientContext {
51
63
  specUrl: string;
52
64
  }
53
65
 
66
+ export type OpenAPIUniversalContext = OpenAPIClientContext | OpenAPIContext;
67
+
68
+ export interface OpenAPIContextInput extends Omit<OpenAPIContext, 'translation'> {
69
+ /**
70
+ * The translation language to use.
71
+ * @default 'en'
72
+ */
73
+ locale?: TranslationLocale | undefined;
74
+ }
75
+
76
+ /**
77
+ * Resolve OpenAPI context from the input.
78
+ */
79
+ export function resolveOpenAPIContext(context: OpenAPIContextInput): OpenAPIContext {
80
+ const { locale, ...rest } = context;
81
+ return {
82
+ ...rest,
83
+ translation: translations[locale ?? 'en'],
84
+ };
85
+ }
86
+
54
87
  /**
55
88
  * Get the client context from the OpenAPI context.
56
89
  */
57
- export function getOpenAPIClientContext(context: OpenAPIContext): OpenAPIClientContext {
90
+ export function getOpenAPIClientContext(context: OpenAPIUniversalContext): OpenAPIClientContext {
58
91
  return {
92
+ translation: context.translation,
59
93
  icons: context.icons,
60
94
  defaultInteractiveOpened: context.defaultInteractiveOpened,
61
95
  blockKey: context.blockKey,
62
96
  id: context.id,
97
+ $$isClientContext$$: true,
63
98
  };
64
99
  }
@@ -1,5 +1,3 @@
1
- 'use client';
2
-
3
1
  import { createStore } from 'zustand';
4
2
 
5
3
  type Key = string | number;
@@ -10,7 +8,7 @@ type State = {
10
8
 
11
9
  type Actions = { setKey: (key: Key | null) => void };
12
10
 
13
- type Store = State & Actions;
11
+ export type Store = State & Actions;
14
12
 
15
13
  const createStateStore = (initial?: Key) => {
16
14
  return createStore<Store>()((set) => ({
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
1
  export * from './schemas';
2
2
  export * from './OpenAPIOperation';
3
+ export * from './OpenAPIWebhook';
3
4
  export * from './OpenAPIOperationContext';
4
5
  export * from './resolveOpenAPIOperation';
5
- export type { OpenAPIOperationData, OpenAPIContext } from './types';
6
+ export * from './resolveOpenAPIWebhook';
7
+ export type { OpenAPIOperationData, OpenAPIWebhookData } from './types';
8
+ export type { OpenAPIContextInput } from './context';
9
+ export { checkIsValidLocale } from './translations';