@djangocfg/ui-tools 2.1.289 → 2.1.291
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/README.md +14 -3
- package/dist/{DocsLayout-YDR7DSMM.cjs → DocsLayout-IKH7BLSU.cjs} +1537 -682
- package/dist/DocsLayout-IKH7BLSU.cjs.map +1 -0
- package/dist/{DocsLayout-TKJQ5W5E.mjs → DocsLayout-JPXFUKAR.mjs} +1429 -574
- package/dist/DocsLayout-JPXFUKAR.mjs.map +1 -0
- package/dist/{PrettyCode.client-5GABIN2I.cjs → PrettyCode.client-RPDIE5CH.cjs} +104 -3
- package/dist/PrettyCode.client-RPDIE5CH.cjs.map +1 -0
- package/dist/{PrettyCode.client-IZTXXYHG.mjs → PrettyCode.client-SPMTQEG4.mjs} +106 -5
- package/dist/PrettyCode.client-SPMTQEG4.mjs.map +1 -0
- package/dist/{chunk-IULI4XII.cjs → chunk-5Q4UMSWB.cjs} +355 -9
- package/dist/chunk-5Q4UMSWB.cjs.map +1 -0
- package/dist/{chunk-VZGQC3NG.mjs → chunk-EFWOJPA6.mjs} +349 -9
- package/dist/chunk-EFWOJPA6.mjs.map +1 -0
- package/dist/index.cjs +18 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.mjs +13 -5
- package/dist/index.mjs.map +1 -1
- package/package.json +20 -15
- package/src/components/markdown/MarkdownMessage.tsx +46 -0
- package/src/tools/MarkdownEditor/MarkdownEditor.tsx +42 -1
- package/src/tools/OpenapiViewer/OpenapiViewer.story.tsx +87 -178
- package/src/tools/OpenapiViewer/README.md +114 -6
- package/src/tools/OpenapiViewer/components/DocsLayout/ApiIntroSection.tsx +20 -6
- package/src/tools/OpenapiViewer/components/DocsLayout/DocsView.tsx +6 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/LanguageTabs.tsx +36 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/index.tsx +56 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/CodeSamples/useCodeSnippet.ts +77 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MetaActions.tsx +146 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/MethodBadge.tsx +6 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/PathDisplay.tsx +26 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Header/index.tsx +87 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/ParamGroup.tsx +30 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/ParamRow.tsx +36 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Parameters/index.tsx +22 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/RequestBody/index.tsx +33 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/ResponseBody.tsx +76 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/ResponseRow.tsx +80 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/StatusTag.tsx +32 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Responses/index.tsx +21 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +106 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +127 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/index.tsx +31 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/types.ts +28 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/SectionHeader.tsx +87 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/defaults.ts +27 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/Section/index.tsx +45 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/context.tsx +56 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/hooks/useSectionHash.ts +63 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +96 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/index.ts +133 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/store/selectors.ts +40 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/types.ts +17 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/SchemaCopyMenu.tsx +8 -2
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/BrandHeader.tsx +48 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/CategoryBlock.tsx +33 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/EndpointRow.tsx +73 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/MethodChips.tsx +43 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SchemaSection.tsx +27 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SearchInput.tsx +45 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/SidebarBody.tsx +50 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/Toolbar.tsx +64 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/buildVM.ts +126 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/index.tsx +112 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/types.ts +42 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar/useDebouncedValue.ts +14 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/SlideInPlayground.tsx +10 -7
- package/src/tools/OpenapiViewer/components/DocsLayout/TryItSheet.tsx +9 -6
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/PrettyView.tsx +55 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/PreviewView.tsx +115 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/RawView.tsx +24 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/StatusBar.tsx +63 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/ViewTabs.tsx +45 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/detectContent.ts +97 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/index.tsx +93 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/types.ts +26 -0
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel/useResponseView.ts +62 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +41 -71
- package/src/tools/OpenapiViewer/types.ts +10 -0
- package/src/tools/OpenapiViewer/utils/codeSamples.ts +287 -0
- package/src/tools/OpenapiViewer/utils/index.ts +3 -0
- package/src/tools/OpenapiViewer/utils/operationToHar.ts +119 -0
- package/src/tools/OpenapiViewer/utils/sampler.ts +72 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +88 -1
- package/src/tools/PrettyCode/PrettyCode.story.tsx +114 -361
- package/src/tools/PrettyCode/index.tsx +13 -0
- package/src/tools/PrettyCode/lazy.tsx +5 -0
- package/src/tools/PrettyCode/registerPrismLanguages.ts +111 -0
- package/dist/DocsLayout-TKJQ5W5E.mjs.map +0 -1
- package/dist/DocsLayout-YDR7DSMM.cjs.map +0 -1
- package/dist/PrettyCode.client-5GABIN2I.cjs.map +0 -1
- package/dist/PrettyCode.client-IZTXXYHG.mjs.map +0 -1
- package/dist/chunk-IULI4XII.cjs.map +0 -1
- package/dist/chunk-VZGQC3NG.mjs.map +0 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc.tsx +0 -273
- package/src/tools/OpenapiViewer/components/DocsLayout/Sidebar.tsx +0 -439
- package/src/tools/OpenapiViewer/components/shared/ResponsePanel.tsx +0 -127
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-tools",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.291",
|
|
4
4
|
"description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-tools",
|
|
@@ -74,7 +74,8 @@
|
|
|
74
74
|
"import": "./src/tools/CodeEditor/index.ts",
|
|
75
75
|
"require": "./src/tools/CodeEditor/index.ts"
|
|
76
76
|
},
|
|
77
|
-
"./styles": "./src/styles/index.css"
|
|
77
|
+
"./styles": "./src/styles/index.css",
|
|
78
|
+
"./dist.css": "./dist/index.css"
|
|
78
79
|
},
|
|
79
80
|
"files": [
|
|
80
81
|
"dist",
|
|
@@ -90,8 +91,8 @@
|
|
|
90
91
|
"check": "tsc --noEmit"
|
|
91
92
|
},
|
|
92
93
|
"peerDependencies": {
|
|
93
|
-
"@djangocfg/i18n": "^2.1.
|
|
94
|
-
"@djangocfg/ui-core": "^2.1.
|
|
94
|
+
"@djangocfg/i18n": "^2.1.291",
|
|
95
|
+
"@djangocfg/ui-core": "^2.1.291",
|
|
95
96
|
"consola": "^3.4.2",
|
|
96
97
|
"lodash-es": "^4.18.1",
|
|
97
98
|
"lucide-react": "^0.545.0",
|
|
@@ -101,30 +102,34 @@
|
|
|
101
102
|
"zustand": "^5.0.0"
|
|
102
103
|
},
|
|
103
104
|
"dependencies": {
|
|
104
|
-
"@tiptap/core": "^3.20.1",
|
|
105
|
-
"@tiptap/react": "^3.20.1",
|
|
106
|
-
"@tiptap/starter-kit": "^3.20.1",
|
|
107
|
-
"@tiptap/extension-placeholder": "^3.20.1",
|
|
108
|
-
"@tiptap/extension-mention": "^3.20.1",
|
|
109
|
-
"@tiptap/suggestion": "^3.20.1",
|
|
110
|
-
"@tiptap/markdown": "^3.20.1",
|
|
111
|
-
"@tiptap/pm": "^3.20.1",
|
|
112
105
|
"@rjsf/core": "^6.1.2",
|
|
113
106
|
"@rjsf/utils": "^6.1.2",
|
|
114
107
|
"@rjsf/validator-ajv8": "^6.1.2",
|
|
115
108
|
"@rpldy/uploady": "^1.8.5",
|
|
109
|
+
"@tiptap/core": "^3.20.1",
|
|
110
|
+
"@tiptap/extension-mention": "^3.20.1",
|
|
111
|
+
"@tiptap/extension-placeholder": "^3.20.1",
|
|
112
|
+
"@tiptap/markdown": "^3.20.1",
|
|
113
|
+
"@tiptap/pm": "^3.20.1",
|
|
114
|
+
"@tiptap/react": "^3.20.1",
|
|
115
|
+
"@tiptap/starter-kit": "^3.20.1",
|
|
116
|
+
"@tiptap/suggestion": "^3.20.1",
|
|
116
117
|
"@vidstack/react": "next",
|
|
117
118
|
"@wavesurfer/react": "^1.0.12",
|
|
118
119
|
"maplibre-gl": "^4.7.1",
|
|
119
120
|
"media-icons": "next",
|
|
120
121
|
"mermaid": "^11.12.0",
|
|
121
122
|
"monaco-editor": "^0.55.1",
|
|
123
|
+
"openapi-sampler": "^1.7.2",
|
|
122
124
|
"prism-react-renderer": "^2.4.1",
|
|
125
|
+
"prismjs": "^1.30.0",
|
|
123
126
|
"react-json-tree": "^0.20.0",
|
|
124
127
|
"react-lottie-player": "^2.1.0",
|
|
125
128
|
"react-map-gl": "^8.1.0",
|
|
126
129
|
"react-markdown": "10.1.0",
|
|
127
130
|
"react-zoom-pan-pinch": "^3.7.0",
|
|
131
|
+
"rehype-raw": "^7.0.0",
|
|
132
|
+
"rehype-sanitize": "^6.0.0",
|
|
128
133
|
"remark-gfm": "4.0.1",
|
|
129
134
|
"vidstack": "next",
|
|
130
135
|
"wavesurfer.js": "^7.12.1"
|
|
@@ -134,10 +139,10 @@
|
|
|
134
139
|
"@maplibre/maplibre-gl-geocoder": "^1.7.0"
|
|
135
140
|
},
|
|
136
141
|
"devDependencies": {
|
|
137
|
-
"@djangocfg/i18n": "^2.1.
|
|
142
|
+
"@djangocfg/i18n": "^2.1.291",
|
|
138
143
|
"@djangocfg/playground": "workspace:*",
|
|
139
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
140
|
-
"@djangocfg/ui-core": "^2.1.
|
|
144
|
+
"@djangocfg/typescript-config": "^2.1.291",
|
|
145
|
+
"@djangocfg/ui-core": "^2.1.291",
|
|
141
146
|
"@types/lodash-es": "^4.17.12",
|
|
142
147
|
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
|
143
148
|
"@types/node": "^24.7.2",
|
|
@@ -2,8 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import ReactMarkdown from 'react-markdown';
|
|
5
|
+
import rehypeRaw from 'rehype-raw';
|
|
6
|
+
import rehypeSanitize, { defaultSchema } from 'rehype-sanitize';
|
|
5
7
|
import remarkGfm from 'remark-gfm';
|
|
6
8
|
|
|
9
|
+
// Allow-list HTML that OpenAPI descriptions commonly use — we want
|
|
10
|
+
// ``<b>``/``<code>``/``<br>`` to render, not to appear as literal text.
|
|
11
|
+
// Built on top of ``rehype-sanitize``'s default schema so we keep the
|
|
12
|
+
// XSS protection (no ``<script>``, no ``on*`` handlers, no ``javascript:``
|
|
13
|
+
// URLs); we just extend the tag allow-list with safe presentational
|
|
14
|
+
// elements that are in wide use in API docs.
|
|
15
|
+
const HTML_SCHEMA = {
|
|
16
|
+
...defaultSchema,
|
|
17
|
+
tagNames: [
|
|
18
|
+
...(defaultSchema.tagNames ?? []),
|
|
19
|
+
'br',
|
|
20
|
+
'b',
|
|
21
|
+
'i',
|
|
22
|
+
'u',
|
|
23
|
+
's',
|
|
24
|
+
'sub',
|
|
25
|
+
'sup',
|
|
26
|
+
'small',
|
|
27
|
+
'mark',
|
|
28
|
+
'kbd',
|
|
29
|
+
'code',
|
|
30
|
+
'pre',
|
|
31
|
+
'details',
|
|
32
|
+
'summary',
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
|
|
7
36
|
import { CopyButton } from '@djangocfg/ui-core/components';
|
|
8
37
|
import { useResolvedTheme } from '@djangocfg/ui-core/hooks';
|
|
9
38
|
|
|
@@ -331,6 +360,16 @@ const hasMarkdownSyntax = (text: string): boolean => {
|
|
|
331
360
|
return true;
|
|
332
361
|
}
|
|
333
362
|
|
|
363
|
+
// Inline HTML tags (``<br>``, ``<b>``, ``<code>``, …) — used widely
|
|
364
|
+
// in OpenAPI descriptions. Without this branch the plain-text fast
|
|
365
|
+
// path would escape them and the user sees literal angle brackets.
|
|
366
|
+
// The regex matches opening, closing, and self-closing tags without
|
|
367
|
+
// being too clever about malformed HTML; it's an affordance test,
|
|
368
|
+
// not a validator.
|
|
369
|
+
if (/<\/?[a-zA-Z][a-zA-Z0-9-]*(\s[^>]*)?\/?>/.test(text)) {
|
|
370
|
+
return true;
|
|
371
|
+
}
|
|
372
|
+
|
|
334
373
|
// Common markdown patterns
|
|
335
374
|
const markdownPatterns = [
|
|
336
375
|
/^#{1,6}\s/m, // Headers
|
|
@@ -549,6 +588,13 @@ export const MarkdownMessage: React.FC<MarkdownMessageProps> = ({
|
|
|
549
588
|
>
|
|
550
589
|
<ReactMarkdown
|
|
551
590
|
remarkPlugins={[remarkGfm]}
|
|
591
|
+
// ``rehype-raw`` parses inline HTML in the source (OpenAPI
|
|
592
|
+
// ``description`` fields often contain ``<br>`` / ``<b>`` /
|
|
593
|
+
// ``<code>``). ``rehype-sanitize`` with our extended schema
|
|
594
|
+
// runs after it so the allowed tags pass through while
|
|
595
|
+
// anything risky (scripts, event handlers, javascript: urls)
|
|
596
|
+
// is stripped.
|
|
597
|
+
rehypePlugins={[rehypeRaw, [rehypeSanitize, HTML_SCHEMA]]}
|
|
552
598
|
components={components}
|
|
553
599
|
>
|
|
554
600
|
{displayContent}
|
|
@@ -47,7 +47,23 @@ export interface MarkdownEditorProps {
|
|
|
47
47
|
className?: string;
|
|
48
48
|
disabled?: boolean;
|
|
49
49
|
showToolbar?: boolean;
|
|
50
|
-
/**
|
|
50
|
+
/**
|
|
51
|
+
* `@`-mention autocomplete config.
|
|
52
|
+
*
|
|
53
|
+
* IMPORTANT: Tiptap's `useEditor` initialises the editor exactly once.
|
|
54
|
+
* The `Mention` extension is only registered when `mentions` is truthy
|
|
55
|
+
* on the FIRST render — handing in a real config later (e.g. after an
|
|
56
|
+
* async items fetch) silently does nothing, and typing `@` will not
|
|
57
|
+
* open the popover.
|
|
58
|
+
*
|
|
59
|
+
* If you want mentions even with async-loaded items, pass
|
|
60
|
+
* `{ items: [] }` from the very first render and update the array
|
|
61
|
+
* when data arrives. Either keep the `MentionConfig` object identity
|
|
62
|
+
* stable across renders and mutate `items` in place (the suggestion
|
|
63
|
+
* plugin captures the config by closure and reads `items` on each
|
|
64
|
+
* query), or accept that swapping the whole object reference is a
|
|
65
|
+
* no-op for the live editor.
|
|
66
|
+
*/
|
|
51
67
|
mentions?: MentionConfig;
|
|
52
68
|
/** Called when mentioned IDs change */
|
|
53
69
|
onMentionIdsChange?: (ids: string[]) => void;
|
|
@@ -68,6 +84,31 @@ export function MarkdownEditor({
|
|
|
68
84
|
}: MarkdownEditorProps) {
|
|
69
85
|
const isExternalUpdate = useRef(false);
|
|
70
86
|
|
|
87
|
+
// ── Dev-mode trap detector ──
|
|
88
|
+
// Tiptap initialises the editor once with the extensions array from
|
|
89
|
+
// first render. If `mentions` is undefined on mount and becomes
|
|
90
|
+
// truthy later, the Mention extension is never installed and the
|
|
91
|
+
// user-visible @-trigger silently does nothing. Catch this early so
|
|
92
|
+
// future consumers don't spend hours debugging why @ does nothing.
|
|
93
|
+
const initialMentionsDefinedRef = useRef<boolean>(mentions !== undefined);
|
|
94
|
+
const warnedRef = useRef(false);
|
|
95
|
+
if (
|
|
96
|
+
process.env.NODE_ENV !== 'production' &&
|
|
97
|
+
!initialMentionsDefinedRef.current &&
|
|
98
|
+
mentions !== undefined &&
|
|
99
|
+
!warnedRef.current
|
|
100
|
+
) {
|
|
101
|
+
warnedRef.current = true;
|
|
102
|
+
// eslint-disable-next-line no-console
|
|
103
|
+
console.warn(
|
|
104
|
+
'[MarkdownEditor] `mentions` flipped from undefined to a config ' +
|
|
105
|
+
'after mount. Tiptap only installs the Mention extension on first ' +
|
|
106
|
+
'render — the @-popover will NOT work for this editor instance. ' +
|
|
107
|
+
'Pass `{ items: [] }` from the very first render and mutate `.items` ' +
|
|
108
|
+
'in place instead.',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
71
112
|
const extensions = useMemo(() => {
|
|
72
113
|
const exts: AnyExtension[] = [
|
|
73
114
|
StarterKit.configure({ heading: { levels: [1, 2, 3] } }),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineStory, useSelect, useValue } from '@djangocfg/playground';
|
|
1
|
+
import { defineStory, useSelect, useValue, useBoolean } from '@djangocfg/playground';
|
|
2
2
|
import { Playground } from './index';
|
|
3
3
|
import { LazyOpenapiViewer } from './lazy';
|
|
4
4
|
import type { PlaygroundConfig } from './types';
|
|
@@ -11,13 +11,18 @@ export default defineStory({
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
// ============================================================================
|
|
14
|
-
// Schema
|
|
14
|
+
// Schema fixtures
|
|
15
15
|
// ============================================================================
|
|
16
16
|
|
|
17
|
+
// Petstore's own ``servers[0].url`` is just ``/api/v3`` (relative),
|
|
18
|
+
// which makes code samples look like they target ``localhost`` when
|
|
19
|
+
// viewed from the playground. Override with the canonical public host
|
|
20
|
+
// so the generated curl/fetch/etc snippets are runnable as-is.
|
|
17
21
|
const PETSTORE_SCHEMA = {
|
|
18
22
|
id: 'petstore',
|
|
19
23
|
name: 'Petstore API',
|
|
20
24
|
url: 'https://petstore3.swagger.io/api/v3/openapi.json',
|
|
25
|
+
baseUrl: 'https://petstore3.swagger.io/api/v3',
|
|
21
26
|
} as const;
|
|
22
27
|
|
|
23
28
|
const HTTPBIN_SCHEMA = {
|
|
@@ -28,15 +33,18 @@ const HTTPBIN_SCHEMA = {
|
|
|
28
33
|
|
|
29
34
|
const MULTI_SCHEMAS = [PETSTORE_SCHEMA, HTTPBIN_SCHEMA] as const;
|
|
30
35
|
|
|
31
|
-
// Simulates a real multi-group setup (like cmdop with 5+ API groups)
|
|
36
|
+
// Simulates a real multi-group setup (like cmdop with 5+ API groups).
|
|
37
|
+
// All clones share the canonical Petstore host so code samples
|
|
38
|
+
// render with a real runnable URL.
|
|
39
|
+
const PETSTORE_BASE = 'https://petstore3.swagger.io/api/v3';
|
|
32
40
|
const MANY_SCHEMAS = [
|
|
33
41
|
PETSTORE_SCHEMA,
|
|
34
42
|
HTTPBIN_SCHEMA,
|
|
35
|
-
{ id: 'machines', name: 'Machines API', url: 'https://petstore3.swagger.io/api/v3/openapi.json' },
|
|
36
|
-
{ id: 'terminal', name: 'Terminal API', url: 'https://petstore3.swagger.io/api/v3/openapi.json' },
|
|
37
|
-
{ id: 'skills', name: 'Skills API', url: 'https://petstore3.swagger.io/api/v3/openapi.json' },
|
|
38
|
-
{ id: 'system', name: 'System API', url: 'https://petstore3.swagger.io/api/v3/openapi.json' },
|
|
39
|
-
{ id: 'site', name: 'Site API', url: 'https://petstore3.swagger.io/api/v3/openapi.json' },
|
|
43
|
+
{ id: 'machines', name: 'Machines API', url: 'https://petstore3.swagger.io/api/v3/openapi.json', baseUrl: PETSTORE_BASE },
|
|
44
|
+
{ id: 'terminal', name: 'Terminal API', url: 'https://petstore3.swagger.io/api/v3/openapi.json', baseUrl: PETSTORE_BASE },
|
|
45
|
+
{ id: 'skills', name: 'Skills API', url: 'https://petstore3.swagger.io/api/v3/openapi.json', baseUrl: PETSTORE_BASE },
|
|
46
|
+
{ id: 'system', name: 'System API', url: 'https://petstore3.swagger.io/api/v3/openapi.json', baseUrl: PETSTORE_BASE },
|
|
47
|
+
{ id: 'site', name: 'Site API', url: 'https://petstore3.swagger.io/api/v3/openapi.json', baseUrl: PETSTORE_BASE },
|
|
40
48
|
] as const;
|
|
41
49
|
|
|
42
50
|
// ============================================================================
|
|
@@ -44,60 +52,34 @@ const MANY_SCHEMAS = [
|
|
|
44
52
|
// ============================================================================
|
|
45
53
|
|
|
46
54
|
/**
|
|
47
|
-
*
|
|
55
|
+
* Default — single-schema mode with the most common configuration knobs
|
|
56
|
+
* exposed as toggles. Replaces the earlier Interactive / SingleSchema /
|
|
57
|
+
* WithBaseUrl / NoDefault / CustomUrl stories, which were all "this
|
|
58
|
+
* component with different props" variations.
|
|
48
59
|
*/
|
|
49
|
-
export const
|
|
50
|
-
const [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
description: 'Which schema to load first',
|
|
60
|
+
export const Default = () => {
|
|
61
|
+
const [schemaUrl] = useValue('schemaUrl', {
|
|
62
|
+
defaultValue: PETSTORE_SCHEMA.url,
|
|
63
|
+
label: 'Schema URL',
|
|
64
|
+
description: 'Any OpenAPI 3.x JSON endpoint',
|
|
55
65
|
});
|
|
56
66
|
|
|
57
|
-
const config: PlaygroundConfig = {
|
|
58
|
-
schemas: [...MULTI_SCHEMAS],
|
|
59
|
-
defaultSchemaId: defaultSchema,
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
return (
|
|
63
|
-
<div className="min-h-[600px]">
|
|
64
|
-
<Playground config={config} />
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* With baseUrl override — forces a specific request host, ignoring
|
|
71
|
-
* `schema.servers[0].url`. Useful when docs are hosted on a different
|
|
72
|
-
* origin than the API, or when the schema has no servers entry.
|
|
73
|
-
*/
|
|
74
|
-
export const WithBaseUrl = () => {
|
|
75
67
|
const [baseUrl] = useValue('baseUrl', {
|
|
76
|
-
defaultValue: '
|
|
77
|
-
label: 'Base URL',
|
|
78
|
-
description: '
|
|
68
|
+
defaultValue: '',
|
|
69
|
+
label: 'Base URL override',
|
|
70
|
+
description: 'Empty = use servers[0].url from the schema',
|
|
79
71
|
});
|
|
80
72
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
return (
|
|
88
|
-
<div className="min-h-[600px]">
|
|
89
|
-
<Playground config={config} />
|
|
90
|
-
</div>
|
|
91
|
-
);
|
|
92
|
-
};
|
|
73
|
+
const [autoSelect] = useBoolean('autoSelectDefault', {
|
|
74
|
+
defaultValue: true,
|
|
75
|
+
label: 'Auto-select default schema',
|
|
76
|
+
description: 'When off, tests the "no defaultSchemaId" code path',
|
|
77
|
+
});
|
|
93
78
|
|
|
94
|
-
/**
|
|
95
|
-
* Single schema — no Combobox switcher shown (schemas.length === 1).
|
|
96
|
-
*/
|
|
97
|
-
export const SingleSchema = () => {
|
|
98
79
|
const config: PlaygroundConfig = {
|
|
99
|
-
schemas: [
|
|
100
|
-
defaultSchemaId: '
|
|
80
|
+
schemas: [{ id: 'custom', name: 'Schema', url: schemaUrl }],
|
|
81
|
+
defaultSchemaId: autoSelect ? 'custom' : undefined,
|
|
82
|
+
baseUrl: baseUrl || undefined,
|
|
101
83
|
};
|
|
102
84
|
|
|
103
85
|
return (
|
|
@@ -108,12 +90,20 @@ export const SingleSchema = () => {
|
|
|
108
90
|
};
|
|
109
91
|
|
|
110
92
|
/**
|
|
111
|
-
*
|
|
112
|
-
*
|
|
93
|
+
* MultiSchema — multiple schemas with the Combobox switcher. Count
|
|
94
|
+
* is adjustable to exercise both the short-list layout and the
|
|
95
|
+
* long-list behaviour (cmdop-style setups with 7+ APIs).
|
|
113
96
|
*/
|
|
114
|
-
export const
|
|
97
|
+
export const MultiSchema = () => {
|
|
98
|
+
const [count] = useSelect('count', {
|
|
99
|
+
options: ['2', '7'] as const,
|
|
100
|
+
defaultValue: '2',
|
|
101
|
+
label: 'Schema count',
|
|
102
|
+
description: '2 uses Petstore+HTTPBin; 7 stress-tests the Combobox',
|
|
103
|
+
});
|
|
104
|
+
|
|
115
105
|
const config: PlaygroundConfig = {
|
|
116
|
-
schemas: [...MULTI_SCHEMAS],
|
|
106
|
+
schemas: count === '2' ? [...MULTI_SCHEMAS] : [...MANY_SCHEMAS],
|
|
117
107
|
defaultSchemaId: 'petstore',
|
|
118
108
|
};
|
|
119
109
|
|
|
@@ -125,14 +115,14 @@ export const MultipleSchemas = () => {
|
|
|
125
115
|
};
|
|
126
116
|
|
|
127
117
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
118
|
+
* SectionsGrouping — alternate layout: every schema's endpoints get
|
|
119
|
+
* rendered on the same page as top-level sections, with a sidebar
|
|
120
|
+
* that stacks them. URL hash sync writes ``#<schemaId>/<anchor>``
|
|
121
|
+
* as the user scrolls so deep-linking keeps working.
|
|
131
122
|
*/
|
|
132
123
|
export const SectionsGrouping = () => {
|
|
133
|
-
const [urlSync] =
|
|
134
|
-
|
|
135
|
-
defaultValue: 'on',
|
|
124
|
+
const [urlSync] = useBoolean('urlSync', {
|
|
125
|
+
defaultValue: true,
|
|
136
126
|
label: 'URL hash sync',
|
|
137
127
|
description: 'Writes #<schemaId>/<anchor> as you scroll',
|
|
138
128
|
});
|
|
@@ -141,7 +131,7 @@ export const SectionsGrouping = () => {
|
|
|
141
131
|
schemas: [...MULTI_SCHEMAS],
|
|
142
132
|
defaultSchemaId: 'petstore',
|
|
143
133
|
schemaGrouping: 'sections',
|
|
144
|
-
urlSync
|
|
134
|
+
urlSync,
|
|
145
135
|
};
|
|
146
136
|
|
|
147
137
|
return (
|
|
@@ -152,91 +142,37 @@ export const SectionsGrouping = () => {
|
|
|
152
142
|
};
|
|
153
143
|
|
|
154
144
|
/**
|
|
155
|
-
*
|
|
156
|
-
*
|
|
145
|
+
* Errors — consolidated failure paths. Pick a scenario via the toggle:
|
|
146
|
+
* - ``broken``: single schema that 404s → full-panel error state.
|
|
147
|
+
* - ``mixed``: valid + broken side-by-side → tests switching between.
|
|
148
|
+
* - ``empty``: no schemas configured at all → empty-state UI.
|
|
157
149
|
*/
|
|
158
|
-
export const
|
|
159
|
-
const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return (
|
|
165
|
-
<div className="min-h-[600px]">
|
|
166
|
-
<Playground config={config} />
|
|
167
|
-
</div>
|
|
168
|
-
);
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* No defaultSchemaId — should auto-select the first schema.
|
|
173
|
-
*/
|
|
174
|
-
export const NoDefault = () => {
|
|
175
|
-
const config: PlaygroundConfig = {
|
|
176
|
-
schemas: [...MULTI_SCHEMAS],
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
return (
|
|
180
|
-
<div className="min-h-[600px]">
|
|
181
|
-
<Playground config={config} />
|
|
182
|
-
</div>
|
|
183
|
-
);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Error state — invalid schema URL that will fail to fetch.
|
|
188
|
-
* Tests error handling and UI feedback.
|
|
189
|
-
*/
|
|
190
|
-
export const ErrorState = () => {
|
|
191
|
-
const config: PlaygroundConfig = {
|
|
192
|
-
schemas: [
|
|
193
|
-
{
|
|
194
|
-
id: 'broken',
|
|
195
|
-
name: 'Broken API',
|
|
196
|
-
url: 'https://httpbin.org/status/404',
|
|
197
|
-
},
|
|
198
|
-
],
|
|
199
|
-
defaultSchemaId: 'broken',
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
return (
|
|
203
|
-
<div className="min-h-[600px]">
|
|
204
|
-
<Playground config={config} />
|
|
205
|
-
</div>
|
|
206
|
-
);
|
|
207
|
-
};
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Mixed valid + broken — one schema works, one fails.
|
|
211
|
-
* Tests switching between working and broken schemas.
|
|
212
|
-
*/
|
|
213
|
-
export const MixedValidBroken = () => {
|
|
214
|
-
const config: PlaygroundConfig = {
|
|
215
|
-
schemas: [
|
|
216
|
-
PETSTORE_SCHEMA,
|
|
217
|
-
{
|
|
218
|
-
id: 'broken',
|
|
219
|
-
name: 'Broken API (404)',
|
|
220
|
-
url: 'https://httpbin.org/status/404',
|
|
221
|
-
},
|
|
222
|
-
],
|
|
223
|
-
defaultSchemaId: 'petstore',
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
return (
|
|
227
|
-
<div className="min-h-[600px]">
|
|
228
|
-
<Playground config={config} />
|
|
229
|
-
</div>
|
|
230
|
-
);
|
|
231
|
-
};
|
|
150
|
+
export const Errors = () => {
|
|
151
|
+
const [scenario] = useSelect('scenario', {
|
|
152
|
+
options: ['broken', 'mixed', 'empty'] as const,
|
|
153
|
+
defaultValue: 'broken',
|
|
154
|
+
label: 'Failure scenario',
|
|
155
|
+
});
|
|
232
156
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
157
|
+
const config: PlaygroundConfig = (() => {
|
|
158
|
+
switch (scenario) {
|
|
159
|
+
case 'broken':
|
|
160
|
+
return {
|
|
161
|
+
schemas: [{ id: 'broken', name: 'Broken API', url: 'https://httpbin.org/status/404' }],
|
|
162
|
+
defaultSchemaId: 'broken',
|
|
163
|
+
};
|
|
164
|
+
case 'mixed':
|
|
165
|
+
return {
|
|
166
|
+
schemas: [
|
|
167
|
+
PETSTORE_SCHEMA,
|
|
168
|
+
{ id: 'broken', name: 'Broken API (404)', url: 'https://httpbin.org/status/404' },
|
|
169
|
+
],
|
|
170
|
+
defaultSchemaId: 'petstore',
|
|
171
|
+
};
|
|
172
|
+
case 'empty':
|
|
173
|
+
return { schemas: [] };
|
|
174
|
+
}
|
|
175
|
+
})();
|
|
240
176
|
|
|
241
177
|
return (
|
|
242
178
|
<div className="min-h-[600px]">
|
|
@@ -246,8 +182,9 @@ export const EmptySchemas = () => {
|
|
|
246
182
|
};
|
|
247
183
|
|
|
248
184
|
/**
|
|
249
|
-
*
|
|
250
|
-
*
|
|
185
|
+
* Lazy — production lazy-loaded variant. Shows the loading spinner
|
|
186
|
+
* while the ~400 KB OpenAPI viewer chunk is fetched. Only path you
|
|
187
|
+
* should use in an actual app bundle.
|
|
251
188
|
*/
|
|
252
189
|
export const Lazy = () => {
|
|
253
190
|
const config: PlaygroundConfig = {
|
|
@@ -261,31 +198,3 @@ export const Lazy = () => {
|
|
|
261
198
|
</div>
|
|
262
199
|
);
|
|
263
200
|
};
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Custom schema URL — paste any OpenAPI 3.x schema URL to test.
|
|
267
|
-
*/
|
|
268
|
-
export const CustomUrl = () => {
|
|
269
|
-
const [url] = useValue('schemaUrl', {
|
|
270
|
-
defaultValue: 'https://petstore3.swagger.io/api/v3/openapi.json',
|
|
271
|
-
label: 'Schema URL',
|
|
272
|
-
description: 'Full URL to an OpenAPI 3.x JSON schema',
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
const config: PlaygroundConfig = {
|
|
276
|
-
schemas: [
|
|
277
|
-
{
|
|
278
|
-
id: 'custom',
|
|
279
|
-
name: 'Custom Schema',
|
|
280
|
-
url,
|
|
281
|
-
},
|
|
282
|
-
],
|
|
283
|
-
defaultSchemaId: 'custom',
|
|
284
|
-
};
|
|
285
|
-
|
|
286
|
-
return (
|
|
287
|
-
<div className="min-h-[600px]">
|
|
288
|
-
<Playground config={config} />
|
|
289
|
-
</div>
|
|
290
|
-
);
|
|
291
|
-
};
|