@gitbook/react-openapi 1.1.4 → 1.1.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  # @gitbook/react-openapi
2
2
 
3
+ ## 1.1.6
4
+
5
+ ### Patch Changes
6
+
7
+ - 6eae764: Support body examples
8
+ - 7212973: Update scalar
9
+ - d2facb2: Mark properties as optional if not required
10
+ - 73e2b47: Fix write only properties in request example
11
+ - 70be2c6: Stringify default value
12
+ - fc00b51: Remove default value in generateSchemaExample
13
+ - a84b06b: Update resolveDescription and add minItems/maxItems
14
+ - Updated dependencies [48c18c0]
15
+ - Updated dependencies [7212973]
16
+ - @gitbook/openapi-parser@2.1.1
17
+
18
+ ## 1.1.5
19
+
20
+ ### Patch Changes
21
+
22
+ - 886e204: Update OpenAPI operation path design
23
+
3
24
  ## 1.1.4
4
25
 
5
26
  ### Patch Changes
@@ -1,3 +1,4 @@
1
+ import type { OpenAPIV3 } from '@gitbook/openapi-parser';
1
2
  import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
2
3
  /**
3
4
  * Display code samples to execute the operation.
@@ -7,3 +8,11 @@ export declare function OpenAPICodeSample(props: {
7
8
  data: OpenAPIOperationData;
8
9
  context: OpenAPIContextProps;
9
10
  }): import("react").JSX.Element | null;
11
+ export interface MediaTypeRenderer {
12
+ mediaType: string;
13
+ element: React.ReactNode;
14
+ examples: Array<{
15
+ example: OpenAPIV3.ExampleObject;
16
+ element: React.ReactNode;
17
+ }>;
18
+ }
@@ -9,19 +9,47 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
11
  };
12
+ import { OpenAPIMediaTypeExamplesBody, OpenAPIMediaTypeExamplesSelector, } from './OpenAPICodeSampleInteractive';
12
13
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
14
+ import { ScalarApiButton } from './ScalarApiButton';
13
15
  import { StaticSection } from './StaticSection';
14
16
  import { codeSampleGenerators } from './code-samples';
15
- import { generateMediaTypeExample, generateSchemaExample } from './generateSchemaExample';
17
+ import { generateMediaTypeExamples, generateSchemaExample } from './generateSchemaExample';
16
18
  import { stringifyOpenAPI } from './stringifyOpenAPI';
17
19
  import { getDefaultServerURL } from './util/server';
18
20
  import { checkIsReference, createStateKey } from './utils';
21
+ var CUSTOM_CODE_SAMPLES_KEYS = ['x-custom-examples', 'x-code-samples', 'x-codeSamples'];
19
22
  /**
20
23
  * Display code samples to execute the operation.
21
24
  * It supports the Redocly custom syntax as well (https://redocly.com/docs/api-reference-docs/specification-extensions/x-code-samples/)
22
25
  */
23
26
  export function OpenAPICodeSample(props) {
24
- var _a;
27
+ var data = props.data;
28
+ // If code samples are disabled at operation level, we don't display the code samples.
29
+ if (data.operation['x-codeSamples'] === false) {
30
+ return null;
31
+ }
32
+ var customCodeSamples = getCustomCodeSamples(props);
33
+ // If code samples are disabled at the top-level and not custom code samples are defined,
34
+ // we don't display the code samples.
35
+ if (data['x-codeSamples'] === false && !customCodeSamples) {
36
+ return null;
37
+ }
38
+ var samples = customCodeSamples !== null && customCodeSamples !== void 0 ? customCodeSamples : generateCodeSamples(props);
39
+ if (samples.length === 0) {
40
+ return null;
41
+ }
42
+ return (<OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
43
+ <StaticSection header={<OpenAPITabsList />} className="openapi-codesample">
44
+ <OpenAPITabsPanels />
45
+ </StaticSection>
46
+ </OpenAPITabs>);
47
+ }
48
+ /**
49
+ * Generate code samples for the operation.
50
+ */
51
+ function generateCodeSamples(props) {
52
+ var _a, _b;
25
53
  var data = props.data, context = props.context;
26
54
  var searchParams = new URLSearchParams();
27
55
  var headersObject = {};
@@ -50,33 +78,92 @@ export function OpenAPICodeSample(props) {
50
78
  var requestBody = !checkIsReference(data.operation.requestBody)
51
79
  ? data.operation.requestBody
52
80
  : undefined;
53
- var requestBodyContentEntries = (requestBody === null || requestBody === void 0 ? void 0 : requestBody.content)
54
- ? Object.entries(requestBody.content)
55
- : undefined;
56
- var requestBodyContent = requestBodyContentEntries === null || requestBodyContentEntries === void 0 ? void 0 : requestBodyContentEntries[0];
57
- var input = {
58
- url: getDefaultServerURL(data.servers) +
59
- data.path +
60
- (searchParams.size ? "?".concat(searchParams.toString()) : ''),
61
- method: data.method,
62
- body: requestBodyContent ? generateMediaTypeExample(requestBodyContent[1]) : undefined,
63
- headers: __assign(__assign(__assign({}, getSecurityHeaders(data.securities)), headersObject), (requestBodyContent
64
- ? {
65
- 'Content-Type': requestBodyContent[0],
66
- }
67
- : undefined)),
68
- };
69
- var autoCodeSamples = codeSampleGenerators.map(function (generator) { return ({
70
- key: "default-".concat(generator.id),
71
- label: generator.label,
72
- body: context.renderCodeBlock({
73
- code: generator.generate(input),
74
- syntax: generator.syntax,
75
- }),
76
- }); });
77
- // Use custom samples if defined
81
+ var url = getDefaultServerURL(data.servers) +
82
+ data.path +
83
+ (searchParams.size ? "?".concat(searchParams.toString()) : '');
84
+ var genericHeaders = __assign(__assign({}, getSecurityHeaders(data.securities)), headersObject);
85
+ var mediaTypeRendererFactories = Object.entries((_b = requestBody === null || requestBody === void 0 ? void 0 : requestBody.content) !== null && _b !== void 0 ? _b : {}).map(function (_a) {
86
+ var mediaType = _a[0], mediaTypeObject = _a[1];
87
+ return function (generator) {
88
+ var mediaTypeHeaders = __assign(__assign({}, genericHeaders), { 'Content-Type': mediaType });
89
+ return {
90
+ mediaType: mediaType,
91
+ element: context.renderCodeBlock({
92
+ code: generator.generate({
93
+ url: url,
94
+ method: data.method,
95
+ body: undefined,
96
+ headers: mediaTypeHeaders,
97
+ }),
98
+ syntax: generator.syntax,
99
+ }),
100
+ examples: generateMediaTypeExamples(mediaTypeObject, {
101
+ mode: 'write',
102
+ }).map(function (example) { return ({
103
+ example: example,
104
+ element: context.renderCodeBlock({
105
+ code: generator.generate({
106
+ url: url,
107
+ method: data.method,
108
+ body: example.value,
109
+ headers: mediaTypeHeaders,
110
+ }),
111
+ syntax: generator.syntax,
112
+ }),
113
+ }); }),
114
+ };
115
+ };
116
+ });
117
+ return codeSampleGenerators.map(function (generator) {
118
+ if (mediaTypeRendererFactories.length > 0) {
119
+ var renderers = mediaTypeRendererFactories.map(function (generate) { return generate(generator); });
120
+ return {
121
+ key: "default-".concat(generator.id),
122
+ label: generator.label,
123
+ body: <OpenAPIMediaTypeExamplesBody data={data} renderers={renderers}/>,
124
+ footer: (<OpenAPICodeSampleFooter renderers={renderers} data={data} context={context}/>),
125
+ };
126
+ }
127
+ return {
128
+ key: "default-".concat(generator.id),
129
+ label: generator.label,
130
+ body: context.renderCodeBlock({
131
+ code: generator.generate({
132
+ url: url,
133
+ method: data.method,
134
+ body: undefined,
135
+ headers: genericHeaders,
136
+ }),
137
+ syntax: generator.syntax,
138
+ }),
139
+ footer: <OpenAPICodeSampleFooter data={data} renderers={[]} context={context}/>,
140
+ };
141
+ });
142
+ }
143
+ function OpenAPICodeSampleFooter(props) {
144
+ var data = props.data, context = props.context, renderers = props.renderers;
145
+ var method = data.method, path = data.path;
146
+ var specUrl = context.specUrl;
147
+ var hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
148
+ var hasMediaTypes = renderers.length > 0;
149
+ if (hideTryItPanel && !hasMediaTypes) {
150
+ return null;
151
+ }
152
+ if (!validateHttpMethod(method)) {
153
+ return null;
154
+ }
155
+ return (<div className="openapi-codesample-footer">
156
+ {hasMediaTypes ? (<OpenAPIMediaTypeExamplesSelector data={data} renderers={renderers}/>) : (<span />)}
157
+ {!hideTryItPanel && <ScalarApiButton method={method} path={path} specUrl={specUrl}/>}
158
+ </div>);
159
+ }
160
+ /**
161
+ * Get custom code samples for the operation.
162
+ */
163
+ function getCustomCodeSamples(props) {
164
+ var data = props.data, context = props.context;
78
165
  var customCodeSamples = null;
79
- ['x-custom-examples', 'x-code-samples', 'x-codeSamples'].forEach(function (key) {
166
+ CUSTOM_CODE_SAMPLES_KEYS.forEach(function (key) {
80
167
  var customSamples = data.operation[key];
81
168
  if (customSamples && Array.isArray(customSamples)) {
82
169
  customCodeSamples = customSamples
@@ -86,27 +173,17 @@ export function OpenAPICodeSample(props) {
86
173
  typeof sample.lang === 'string');
87
174
  })
88
175
  .map(function (sample, index) { return ({
89
- key: "redocly-".concat(sample.lang, "-").concat(index),
176
+ key: "custom-sample-".concat(sample.lang, "-").concat(index),
90
177
  label: sample.label,
91
178
  body: context.renderCodeBlock({
92
179
  code: sample.source,
93
180
  syntax: sample.lang,
94
181
  }),
182
+ footer: (<OpenAPICodeSampleFooter renderers={[]} data={data} context={context}/>),
95
183
  }); });
96
184
  }
97
185
  });
98
- // Code samples can be disabled at the top-level or at the operation level
99
- // If code samples are defined at the operation level, it will override the top-level setting
100
- var codeSamplesDisabled = data['x-codeSamples'] === false || data.operation['x-codeSamples'] === false;
101
- var samples = customCodeSamples !== null && customCodeSamples !== void 0 ? customCodeSamples : (!codeSamplesDisabled ? autoCodeSamples : []);
102
- if (samples.length === 0) {
103
- return null;
104
- }
105
- return (<OpenAPITabs stateKey={createStateKey('codesample')} items={samples}>
106
- <StaticSection header={<OpenAPITabsList />} className="openapi-codesample">
107
- <OpenAPITabsPanels />
108
- </StaticSection>
109
- </OpenAPITabs>);
186
+ return customCodeSamples;
110
187
  }
111
188
  function getSecurityHeaders(securities) {
112
189
  var _a;
@@ -146,3 +223,6 @@ function getSecurityHeaders(securities) {
146
223
  }
147
224
  }
148
225
  }
226
+ function validateHttpMethod(method) {
227
+ return ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'].includes(method);
228
+ }
@@ -0,0 +1,10 @@
1
+ import type { MediaTypeRenderer } from './OpenAPICodeSample';
2
+ import type { OpenAPIOperationData } from './types';
3
+ export declare function OpenAPIMediaTypeExamplesSelector(props: {
4
+ data: OpenAPIOperationData;
5
+ renderers: MediaTypeRenderer[];
6
+ }): import("react").JSX.Element;
7
+ export declare function OpenAPIMediaTypeExamplesBody(props: {
8
+ data: OpenAPIOperationData;
9
+ renderers: MediaTypeRenderer[];
10
+ }): string | number | boolean | Iterable<import("react").ReactNode> | import("react").JSX.Element | null | undefined;
@@ -0,0 +1,78 @@
1
+ 'use client';
2
+ import clsx from 'clsx';
3
+ import { useCallback } from 'react';
4
+ import { useStore } from 'zustand';
5
+ import { getOrCreateTabStoreByKey } from './useSyncedTabsGlobalState';
6
+ function useMediaTypeState(data, defaultKey) {
7
+ var method = data.method, path = data.path;
8
+ var store = useStore(getOrCreateTabStoreByKey("media-type-".concat(method, "-").concat(path), defaultKey));
9
+ if (typeof store.tabKey !== 'string') {
10
+ throw new Error('Media type key is not a string');
11
+ }
12
+ return {
13
+ mediaType: store.tabKey,
14
+ setMediaType: useCallback(function (index) { return store.setTabKey(index); }, [store.setTabKey]),
15
+ };
16
+ }
17
+ function useMediaTypeSampleIndexState(data, mediaType) {
18
+ var method = data.method, path = data.path;
19
+ var store = useStore(getOrCreateTabStoreByKey("media-type-sample-".concat(mediaType, "-").concat(method, "-").concat(path), 0));
20
+ if (typeof store.tabKey !== 'number') {
21
+ throw new Error('Example key is not a number');
22
+ }
23
+ return {
24
+ index: store.tabKey,
25
+ setIndex: useCallback(function (index) { return store.setTabKey(index); }, [store.setTabKey]),
26
+ };
27
+ }
28
+ export function OpenAPIMediaTypeExamplesSelector(props) {
29
+ var data = props.data, renderers = props.renderers;
30
+ if (!renderers[0]) {
31
+ throw new Error('No renderers provided');
32
+ }
33
+ var state = useMediaTypeState(data, renderers[0].mediaType);
34
+ var selected = renderers.find(function (r) { return r.mediaType === state.mediaType; }) || renderers[0];
35
+ return (<div className="openapi-codesample-selectors">
36
+ <select className={clsx('openapi-select')} value={state.mediaType} onChange={function (e) { return state.setMediaType(e.target.value); }}>
37
+ {renderers.map(function (renderer) { return (<option key={renderer.mediaType} value={renderer.mediaType}>
38
+ {renderer.mediaType}
39
+ </option>); })}
40
+ </select>
41
+ <ExamplesSelector data={data} renderer={selected}/>
42
+ </div>);
43
+ }
44
+ function ExamplesSelector(props) {
45
+ var data = props.data, renderer = props.renderer;
46
+ var state = useMediaTypeSampleIndexState(data, renderer.mediaType);
47
+ if (renderer.examples.length < 2) {
48
+ return null;
49
+ }
50
+ return (<select className={clsx('openapi-select')} value={String(state.index)} onChange={function (e) { return state.setIndex(Number(e.target.value)); }}>
51
+ {renderer.examples.map(function (example, index) { return (<option key={index} value={index}>
52
+ {example.example.summary || "Example ".concat(index + 1)}
53
+ </option>); })}
54
+ </select>);
55
+ }
56
+ export function OpenAPIMediaTypeExamplesBody(props) {
57
+ var _a;
58
+ var renderers = props.renderers, data = props.data;
59
+ if (!renderers[0]) {
60
+ throw new Error('No renderers provided');
61
+ }
62
+ var mediaTypeState = useMediaTypeState(data, renderers[0].mediaType);
63
+ var selected = (_a = renderers.find(function (r) { return r.mediaType === mediaTypeState.mediaType; })) !== null && _a !== void 0 ? _a : renderers[0];
64
+ if (selected.examples.length === 0) {
65
+ return selected.element;
66
+ }
67
+ return <ExamplesBody data={data} renderer={selected}/>;
68
+ }
69
+ function ExamplesBody(props) {
70
+ var _a;
71
+ var data = props.data, renderer = props.renderer;
72
+ var exampleState = useMediaTypeSampleIndexState(data, renderer.mediaType);
73
+ var example = (_a = renderer.examples[exampleState.index]) !== null && _a !== void 0 ? _a : renderer.examples[0];
74
+ if (!example) {
75
+ throw new Error("No example found for index ".concat(exampleState.index));
76
+ }
77
+ return example.element;
78
+ }
@@ -0,0 +1,4 @@
1
+ import { type ButtonProps } from 'react-aria-components';
2
+ export declare function OpenAPICopyButton(props: ButtonProps & {
3
+ value: string;
4
+ }): import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+ import { useState } from 'react';
3
+ import { Button, Tooltip, TooltipTrigger } from 'react-aria-components';
4
+ export function OpenAPICopyButton(props) {
5
+ var value = props.value;
6
+ var children = props.children, onPress = props.onPress, className = props.className;
7
+ var _a = useState(false), copied = _a[0], setCopied = _a[1];
8
+ var _b = useState(false), isOpen = _b[0], setIsOpen = _b[1];
9
+ var handleCopy = function () {
10
+ if (!value)
11
+ return;
12
+ navigator.clipboard.writeText(value).then(function () {
13
+ setIsOpen(true);
14
+ setCopied(true);
15
+ setTimeout(function () {
16
+ setCopied(false);
17
+ }, 2000);
18
+ });
19
+ };
20
+ return (<TooltipTrigger isOpen={isOpen} onOpenChange={setIsOpen} closeDelay={200} delay={200}>
21
+ <Button type="button" preventFocusOnPress onPress={function (e) {
22
+ handleCopy();
23
+ onPress === null || onPress === void 0 ? void 0 : onPress(e);
24
+ }} className={"openapi-copy-button ".concat(className)} {...props}>
25
+ {children}
26
+ </Button>
27
+
28
+ <Tooltip isOpen={isOpen} onOpenChange={setIsOpen} placement="top" offset={4} className="openapi-tooltip">
29
+ {copied ? 'Copied' : 'Copy to clipboard'}{' '}
30
+ </Tooltip>
31
+ </TooltipTrigger>);
32
+ }
@@ -25,6 +25,7 @@ export function OpenAPIOperation(props) {
25
25
  title: operation.summary,
26
26
  })
27
27
  : null}
28
+ <OpenAPIPath data={data} context={context}/>
28
29
  {operation.deprecated && <div className="openapi-deprecated">Deprecated</div>}
29
30
  </div>
30
31
  <div className="openapi-columns">
@@ -37,7 +38,6 @@ export function OpenAPIOperation(props) {
37
38
  {'.'}
38
39
  </div>) : null}
39
40
  <OpenAPIOperationDescription operation={operation} context={context}/>
40
- <OpenAPIPath data={data} context={context}/>
41
41
  <OpenAPISpec data={data} context={clientContext}/>
42
42
  </div>
43
43
  <div className="openapi-column-preview">
@@ -1,4 +1,3 @@
1
- import type React from 'react';
2
1
  import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
3
2
  /**
4
3
  * Display the path of an operation.
@@ -6,4 +5,4 @@ import type { OpenAPIContextProps, OpenAPIOperationData } from './types';
6
5
  export declare function OpenAPIPath(props: {
7
6
  data: OpenAPIOperationData;
8
7
  context: OpenAPIContextProps;
9
- }): React.JSX.Element;
8
+ }): import("react").JSX.Element;
@@ -1,47 +1,49 @@
1
- import { ScalarApiButton } from './ScalarApiButton';
1
+ import { OpenAPICopyButton } from './OpenAPICopyButton';
2
+ import { getDefaultServerURL } from './util/server';
2
3
  /**
3
4
  * Display the path of an operation.
4
5
  */
5
6
  export function OpenAPIPath(props) {
6
- var data = props.data, context = props.context;
7
- var method = data.method, path = data.path;
8
- var specUrl = context.specUrl;
9
- var hideTryItPanel = data['x-hideTryItPanel'] || data.operation['x-hideTryItPanel'];
7
+ var data = props.data;
8
+ var method = data.method, path = data.path, operation = data.operation;
9
+ var server = getDefaultServerURL(data.servers);
10
+ var formattedPath = formatPath(path);
10
11
  return (<div className="openapi-path">
11
12
  <div className={"openapi-method openapi-method-".concat(method)}>{method}</div>
12
- <div className="openapi-path-title" data-deprecated={data.operation.deprecated}>
13
- <p>{formatPath(path)}</p>
14
- </div>
15
- {!hideTryItPanel && validateHttpMethod(method) && (<ScalarApiButton method={method} path={path} specUrl={specUrl}/>)}
13
+
14
+ <OpenAPICopyButton value={server + path} className="openapi-path-title" data-deprecated={operation.deprecated}>
15
+ <span className="openapi-path-server">{server}</span>
16
+ {formattedPath}
17
+ </OpenAPICopyButton>
16
18
  </div>);
17
19
  }
18
- function validateHttpMethod(method) {
19
- return ['get', 'post', 'put', 'delete', 'patch', 'head', 'options', 'trace'].includes(method);
20
- }
21
- // Format the path to highlight placeholders
20
+ /**
21
+ * Format the path by wrapping placeholders in <span> tags.
22
+ */
22
23
  function formatPath(path) {
23
24
  // Matches placeholders like {id}, {userId}, etc.
24
- var regex = /\{(\w+)\}/g;
25
+ var regex = /\{\s*(\w+)\s*\}|:\w+/g;
25
26
  var parts = [];
26
27
  var lastIndex = 0;
27
- // Replace placeholders with <em> tags
28
- path.replace(regex, function (match, key, offset) {
29
- parts.push(path.slice(lastIndex, offset));
30
- parts.push(<em key={key}>{"{".concat(key, "}")}</em>);
28
+ //Wrap the variables in <span> tags and maintain either {variable} or :variable
29
+ path.replace(regex, function (match, _, offset) {
30
+ if (offset > lastIndex) {
31
+ parts.push(path.slice(lastIndex, offset));
32
+ }
33
+ parts.push(<span key={offset} className="openapi-path-variable">
34
+ {match}
35
+ </span>);
31
36
  lastIndex = offset + match.length;
32
37
  return match;
33
38
  });
34
- // Push remaining text after the last placeholder
35
- parts.push(path.slice(lastIndex));
36
- // Join parts with separators wrapped in <span>
37
- var formattedPath = parts.reduce(function (acc, part, index) {
38
- if (typeof part === 'string' && index > 0 && part === '/') {
39
- acc.push(<span className="openapi-path-separator" key={"sep-".concat(index)}>
40
- /
41
- </span>);
39
+ if (lastIndex < path.length) {
40
+ parts.push(path.slice(lastIndex));
41
+ }
42
+ var formattedPath = parts.map(function (part, index) {
43
+ if (typeof part === 'string') {
44
+ return <span key={index}>{part}</span>;
42
45
  }
43
- acc.push(part);
44
- return acc;
45
- }, []);
46
- return <span>{formattedPath}</span>;
46
+ return part;
47
+ });
48
+ return formattedPath;
47
49
  }
@@ -1,3 +1,4 @@
1
+ import { Markdown } from './Markdown';
1
2
  import { OpenAPITabs, OpenAPITabsList, OpenAPITabsPanels } from './OpenAPITabs';
2
3
  import { StaticSection } from './StaticSection';
3
4
  import { generateSchemaExample } from './generateSchemaExample';
@@ -32,35 +33,31 @@ export function OpenAPIResponseExample(props) {
32
33
  }
33
34
  return Number(a) - Number(b);
34
35
  });
35
- var tabs = responses
36
- .map(function (_a) {
36
+ var tabs = responses.map(function (_a) {
37
37
  var key = _a[0], responseObject = _a[1];
38
38
  var description = resolveDescription(responseObject);
39
39
  if (checkIsReference(responseObject)) {
40
40
  return {
41
41
  key: key,
42
42
  label: key,
43
- description: description,
44
43
  body: (<OpenAPIExample example={getExampleFromReference(responseObject)} context={context} syntax="json"/>),
44
+ footer: description ? <Markdown source={description}/> : undefined,
45
45
  };
46
46
  }
47
47
  if (!responseObject.content || Object.keys(responseObject.content).length === 0) {
48
48
  return {
49
49
  key: key,
50
50
  label: key,
51
- description: description,
52
51
  body: <OpenAPIEmptyResponseExample />,
52
+ footer: description ? <Markdown source={description}/> : undefined,
53
53
  };
54
54
  }
55
55
  return {
56
56
  key: key,
57
57
  label: key,
58
- description: resolveDescription(responseObject),
59
58
  body: <OpenAPIResponse context={context} content={responseObject.content}/>,
59
+ footer: description ? <Markdown source={description}/> : undefined,
60
60
  };
61
- })
62
- .filter(function (val) {
63
- return Boolean(val);
64
61
  });
65
62
  if (tabs.length === 0) {
66
63
  return null;
@@ -1,3 +1,4 @@
1
+ import { stringifyOpenAPI } from './stringifyOpenAPI';
1
2
  /**
2
3
  * Display the schema name row.
3
4
  * It includes the property name, type, required and deprecated status.
@@ -14,21 +15,21 @@ export function OpenAPISchemaName(props) {
14
15
  {additionalItems ? (<span className="openapi-schema-type">{additionalItems}</span>) : null}
15
16
  </span>
16
17
  {(schema === null || schema === void 0 ? void 0 : schema.readOnly) ? <span className="openapi-schema-readonly">read-only</span> : null}
17
- {required ? <span className="openapi-schema-required">required</span> : null}
18
+ {required ? (<span className="openapi-schema-required">required</span>) : (<span className="openapi-schema-optional">optional</span>)}
18
19
  {(schema === null || schema === void 0 ? void 0 : schema.deprecated) ? <span className="openapi-deprecated">Deprecated</span> : null}
19
20
  </div>);
20
21
  }
21
22
  function getAdditionalItems(schema) {
22
23
  var additionalItems = '';
23
- if (schema.minimum || schema.minLength) {
24
- additionalItems += " \u00B7 min: ".concat(schema.minimum || schema.minLength);
24
+ if (schema.minimum || schema.minLength || schema.minItems) {
25
+ additionalItems += " \u00B7 min: ".concat(schema.minimum || schema.minLength || schema.minItems);
25
26
  }
26
- if (schema.maximum || schema.maxLength) {
27
- additionalItems += " \u00B7 max: ".concat(schema.maximum || schema.maxLength);
27
+ if (schema.maximum || schema.maxLength || schema.maxItems) {
28
+ additionalItems += " \u00B7 max: ".concat(schema.maximum || schema.maxLength || schema.maxItems);
28
29
  }
29
30
  // If the schema has a default value, we display it
30
31
  if (typeof schema.default !== 'undefined') {
31
- additionalItems += " \u00B7 default: ".concat(schema.default);
32
+ additionalItems += " \u00B7 default: ".concat(stringifyOpenAPI(schema.default));
32
33
  }
33
34
  if (schema.nullable) {
34
35
  additionalItems = ' | nullable';
@@ -3,7 +3,7 @@ export type TabItem = {
3
3
  key: Key;
4
4
  label: string;
5
5
  body: React.ReactNode;
6
- description?: string;
6
+ footer?: React.ReactNode;
7
7
  };
8
8
  /**
9
9
  * The OpenAPI Tabs wrapper component.
@@ -2,7 +2,6 @@
2
2
  import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react';
3
3
  import { Tab, TabList, TabPanel, Tabs } from 'react-aria-components';
4
4
  import { useEventCallback } from 'usehooks-ts';
5
- import { Markdown } from './Markdown';
6
5
  import { getOrCreateTabStoreByKey } from './useSyncedTabsGlobalState';
7
6
  var OpenAPITabsContext = createContext(null);
8
7
  function useOpenAPITabsContext() {
@@ -105,6 +104,14 @@ export function OpenAPITabsPanels() {
105
104
  var key = selectedTab.key.toString();
106
105
  return (<TabPanel key={key} id={key} className="openapi-tabs-panel">
107
106
  {selectedTab.body}
108
- {selectedTab.description ? (<Markdown source={selectedTab.description} className="openapi-tabs-footer"/>) : null}
107
+ {selectedTab.footer ? (<OpenAPITabsPanelFooter>{selectedTab.footer}</OpenAPITabsPanelFooter>) : null}
109
108
  </TabPanel>);
110
109
  }
110
+ /**
111
+ * The OpenAPI Tabs panel footer component.
112
+ * This component should be used as a child of the OpenAPITabs component.
113
+ */
114
+ function OpenAPITabsPanelFooter(props) {
115
+ var children = props.children;
116
+ return <div className="openapi-tabs-footer">{children}</div>;
117
+ }
@@ -16,10 +16,10 @@ export function ScalarApiButton(props) {
16
16
  (_b = (_a = controllerRef.current) === null || _a === void 0 ? void 0 : _a.openClient) === null || _b === void 0 ? void 0 : _b.call(_a);
17
17
  setIsOpen(true);
18
18
  }}>
19
- <svg xmlns="http://www.w3.org/2000/svg" width="10" height="12" fill="none">
19
+ Test it
20
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 12" fill="currentColor">
20
21
  <path stroke="currentColor" strokeWidth="1.5" d="M1 10.05V1.43c0-.2.2-.31.37-.22l7.26 4.08c.17.1.17.33.01.43l-7.26 4.54a.25.25 0 0 1-.38-.21Z"/>
21
22
  </svg>
22
- Test it
23
23
  </button>
24
24
 
25
25
  {isOpen &&
@@ -4,7 +4,7 @@ export interface CodeSampleInput {
4
4
  headers?: Record<string, string>;
5
5
  body?: any;
6
6
  }
7
- interface CodeSampleGenerator {
7
+ export interface CodeSampleGenerator {
8
8
  id: string;
9
9
  label: string;
10
10
  syntax: string;
@@ -15,4 +15,3 @@ export declare function parseHostAndPath(url: string): {
15
15
  host: string | undefined;
16
16
  path: string;
17
17
  };
18
- export {};
@@ -203,7 +203,7 @@ var BodyGenerators = {
203
203
  }
204
204
  else if (isXML(contentType) || isCSV(contentType)) {
205
205
  // We use --data-binary to avoid cURL converting newlines to \r\n
206
- body = "--data-binary $'".concat(stringifyOpenAPI(body).replace(/"/g, ''), "'");
206
+ body = "--data-binary $'".concat(stringifyOpenAPI(body).replace(/"/g, '').replace(/\\n/g, '\n'), "'");
207
207
  }
208
208
  else if (isGraphQL(contentType)) {
209
209
  body = "--data '".concat(stringifyOpenAPI(body), "'");
@@ -215,7 +215,7 @@ var BodyGenerators = {
215
215
  body = "--data-binary '@".concat(String(body), "'");
216
216
  }
217
217
  else {
218
- body = "--data '".concat(stringifyOpenAPI(body, null, 2), "'");
218
+ body = "--data '".concat(stringifyOpenAPI(body, null, 2).replace(/\\n/g, '\n'), "'");
219
219
  }
220
220
  return {
221
221
  body: body,