@stoplight/elements 9.0.11 → 9.0.12-beta-0.2

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 (68) hide show
  1. package/.DS_Store +0 -0
  2. package/.storybook/main.js +6 -0
  3. package/.storybook/manager.js +1 -0
  4. package/.storybook/preview.jsx +3 -0
  5. package/dist/LICENSE +190 -0
  6. package/dist/README.md +19 -0
  7. package/dist/package.json +52 -0
  8. package/jest.config.js +7 -0
  9. package/package.json +76 -16
  10. package/src/__fixtures__/api-descriptions/Instagram.ts +1859 -0
  11. package/src/__fixtures__/api-descriptions/badgesForSchema.ts +36 -0
  12. package/src/__fixtures__/api-descriptions/simpleApiWithInternalOperations.ts +253 -0
  13. package/src/__fixtures__/api-descriptions/simpleApiWithoutDescription.ts +243 -0
  14. package/src/__fixtures__/api-descriptions/todosApiBundled.ts +430 -0
  15. package/src/__fixtures__/api-descriptions/zoomApiYaml.ts +6083 -0
  16. package/src/components/API/APIWithResponsiveSidebarLayout.tsx +125 -0
  17. package/src/components/API/APIWithSidebarLayout.tsx +158 -0
  18. package/src/components/API/APIWithStackedLayout.tsx +286 -0
  19. package/src/components/API/__tests__/utils.test.ts +1323 -0
  20. package/src/components/API/utils.ts +206 -0
  21. package/src/containers/API.spec.tsx +122 -0
  22. package/src/containers/API.stories.tsx +117 -0
  23. package/src/containers/API.tsx +277 -0
  24. package/src/containers/story-helper.tsx +53 -0
  25. package/src/hooks/useExportDocumentProps.spec.tsx +68 -0
  26. package/src/hooks/useExportDocumentProps.tsx +48 -0
  27. package/src/index.ts +6 -0
  28. package/src/styles.css +1 -0
  29. package/src/utils/oas/__tests__/oas.spec.ts +411 -0
  30. package/src/utils/oas/index.ts +192 -0
  31. package/src/utils/oas/oas2.ts +31 -0
  32. package/src/utils/oas/oas3.ts +54 -0
  33. package/src/utils/oas/types.ts +34 -0
  34. package/src/web-components/__stories__/Api.stories.tsx +63 -0
  35. package/src/web-components/components.ts +26 -0
  36. package/src/web-components/index.ts +3 -0
  37. package/tsconfig.build.json +18 -0
  38. package/tsconfig.json +7 -0
  39. package/web-components.config.js +1 -0
  40. package/styles.min.css +0 -1
  41. package/web-components.min.js +0 -2
  42. package/web-components.min.js.LICENSE.txt +0 -176
  43. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/Instagram.d.ts +0 -0
  44. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/badgesForSchema.d.ts +0 -0
  45. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/simpleApiWithInternalOperations.d.ts +0 -0
  46. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/simpleApiWithoutDescription.d.ts +0 -0
  47. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/todosApiBundled.d.ts +0 -0
  48. /package/{__fixtures__ → dist/__fixtures__}/api-descriptions/zoomApiYaml.d.ts +0 -0
  49. /package/{components → dist/components}/API/APIWithResponsiveSidebarLayout.d.ts +0 -0
  50. /package/{components → dist/components}/API/APIWithSidebarLayout.d.ts +0 -0
  51. /package/{components → dist/components}/API/APIWithStackedLayout.d.ts +0 -0
  52. /package/{components → dist/components}/API/utils.d.ts +0 -0
  53. /package/{containers → dist/containers}/API.d.ts +0 -0
  54. /package/{containers → dist/containers}/API.spec.d.ts +0 -0
  55. /package/{containers → dist/containers}/API.stories.d.ts +0 -0
  56. /package/{containers → dist/containers}/story-helper.d.ts +0 -0
  57. /package/{hooks → dist/hooks}/useExportDocumentProps.d.ts +0 -0
  58. /package/{hooks → dist/hooks}/useExportDocumentProps.spec.d.ts +0 -0
  59. /package/{index.d.ts → dist/index.d.ts} +0 -0
  60. /package/{index.esm.js → dist/index.esm.js} +0 -0
  61. /package/{index.js → dist/index.js} +0 -0
  62. /package/{index.mjs → dist/index.mjs} +0 -0
  63. /package/{utils → dist/utils}/oas/index.d.ts +0 -0
  64. /package/{utils → dist/utils}/oas/oas2.d.ts +0 -0
  65. /package/{utils → dist/utils}/oas/oas3.d.ts +0 -0
  66. /package/{utils → dist/utils}/oas/types.d.ts +0 -0
  67. /package/{web-components → dist/web-components}/components.d.ts +0 -0
  68. /package/{web-components → dist/web-components}/index.d.ts +0 -0
@@ -0,0 +1,125 @@
1
+ import {
2
+ ElementsOptionsProvider,
3
+ ExportButtonProps,
4
+ ParsedDocs,
5
+ resolveRelativeLink,
6
+ ResponsiveSidebarLayout,
7
+ } from '@stoplight/elements-core';
8
+ import { ExtensionAddonRenderer } from '@stoplight/elements-core/components/Docs';
9
+ import { NodeType } from '@stoplight/types';
10
+ import * as React from 'react';
11
+ import { Navigate, useLocation } from 'react-router-dom';
12
+
13
+ import { ServiceNode } from '../../utils/oas/types';
14
+ import { computeAPITree, findFirstNodeSlug, isInternal, resolveRelativePath } from './utils';
15
+
16
+ type SidebarLayoutProps = {
17
+ serviceNode: ServiceNode;
18
+ logo?: string;
19
+ hideTryItPanel?: boolean;
20
+ hideTryIt?: boolean;
21
+ hideSamples?: boolean;
22
+ hideSchemas?: boolean;
23
+ hideInternal?: boolean;
24
+ hideExport?: boolean;
25
+ hideServerInfo?: boolean;
26
+ hideSecurityInfo?: boolean;
27
+ exportProps?: ExportButtonProps;
28
+ tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
29
+ tryItCorsProxy?: string;
30
+ compact?: number | boolean;
31
+ renderExtensionAddon?: ExtensionAddonRenderer;
32
+ basePath?: string;
33
+ outerRouter?: boolean;
34
+ };
35
+
36
+ export const APIWithResponsiveSidebarLayout: React.FC<SidebarLayoutProps> = ({
37
+ serviceNode,
38
+ logo,
39
+ hideTryItPanel,
40
+ hideTryIt,
41
+ hideSamples,
42
+ compact,
43
+ hideSchemas,
44
+ hideInternal,
45
+ hideExport,
46
+ hideServerInfo,
47
+ hideSecurityInfo,
48
+ exportProps,
49
+ tryItCredentialsPolicy,
50
+ tryItCorsProxy,
51
+ renderExtensionAddon,
52
+ basePath = '/',
53
+ outerRouter = false,
54
+ }) => {
55
+ const container = React.useRef<HTMLDivElement>(null);
56
+ const tree = React.useMemo(
57
+ () => computeAPITree(serviceNode, { hideSchemas, hideInternal }),
58
+ [serviceNode, hideSchemas, hideInternal],
59
+ );
60
+ const location = useLocation();
61
+ const { pathname: currentPath } = location;
62
+ const relativePath = resolveRelativePath(currentPath, basePath, outerRouter);
63
+
64
+ const isRootPath = relativePath === '/';
65
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === relativePath);
66
+
67
+ const layoutOptions = React.useMemo(
68
+ () => ({
69
+ hideTryIt: hideTryIt,
70
+ hideTryItPanel,
71
+ hideSamples,
72
+ hideSecurityInfo: hideSecurityInfo,
73
+ hideServerInfo: hideServerInfo,
74
+ compact: compact,
75
+ hideExport: hideExport || node?.type !== NodeType.HttpService,
76
+ }),
77
+ [hideTryIt, hideSecurityInfo, hideServerInfo, compact, hideExport, hideTryItPanel, hideSamples, node?.type],
78
+ );
79
+
80
+ if (!node) {
81
+ // Redirect to the first child if node doesn't exist
82
+ const firstSlug = findFirstNodeSlug(tree);
83
+
84
+ if (firstSlug) {
85
+ return <Navigate to={resolveRelativeLink(firstSlug)} replace />;
86
+ }
87
+ }
88
+
89
+ if (hideInternal && node && isInternal(node)) {
90
+ return <Navigate to="." replace />;
91
+ }
92
+
93
+ const handleTocClick = () => {
94
+ if (container.current) {
95
+ container.current.scrollIntoView();
96
+ }
97
+ };
98
+
99
+ return (
100
+ <ResponsiveSidebarLayout
101
+ onTocClick={handleTocClick}
102
+ tree={tree}
103
+ logo={logo ?? serviceNode.data.logo}
104
+ ref={container}
105
+ name={serviceNode.name}
106
+ >
107
+ {node && (
108
+ <ElementsOptionsProvider renderExtensionAddon={renderExtensionAddon}>
109
+ <ParsedDocs
110
+ key={relativePath}
111
+ uri={relativePath}
112
+ node={node}
113
+ nodeTitle={node.name}
114
+ layoutOptions={layoutOptions}
115
+ location={location}
116
+ exportProps={exportProps}
117
+ tryItCredentialsPolicy={tryItCredentialsPolicy}
118
+ tryItCorsProxy={tryItCorsProxy}
119
+ renderExtensionAddon={renderExtensionAddon}
120
+ />
121
+ </ElementsOptionsProvider>
122
+ )}
123
+ </ResponsiveSidebarLayout>
124
+ );
125
+ };
@@ -0,0 +1,158 @@
1
+ import {
2
+ type CustomLinkComponent,
3
+ ElementsOptionsProvider,
4
+ ExportButtonProps,
5
+ Logo,
6
+ ParsedDocs,
7
+ PoweredByLink,
8
+ resolveRelativeLink,
9
+ SidebarLayout,
10
+ TableOfContents,
11
+ TableOfContentsItem,
12
+ } from '@stoplight/elements-core';
13
+ import { ExtensionAddonRenderer } from '@stoplight/elements-core/components/Docs';
14
+ import { Flex, Heading } from '@stoplight/mosaic';
15
+ import { NodeType } from '@stoplight/types';
16
+ import * as React from 'react';
17
+ import { Link, Navigate, useLocation } from 'react-router-dom';
18
+
19
+ import { ServiceNode } from '../../utils/oas/types';
20
+ import { computeAPITree, findFirstNodeSlug, isInternal, resolveRelativePath } from './utils';
21
+
22
+ type SidebarLayoutProps = {
23
+ serviceNode: ServiceNode;
24
+ logo?: string;
25
+ hideTryItPanel?: boolean;
26
+ hideTryIt?: boolean;
27
+ hideSamples?: boolean;
28
+ hideSchemas?: boolean;
29
+ hideInternal?: boolean;
30
+ hideServerInfo?: boolean;
31
+ hideSecurityInfo?: boolean;
32
+ hideExport?: boolean;
33
+ exportProps?: ExportButtonProps;
34
+ tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
35
+ tryItCorsProxy?: string;
36
+ renderExtensionAddon?: ExtensionAddonRenderer;
37
+ basePath?: string;
38
+ outerRouter?: boolean;
39
+ };
40
+
41
+ export const APIWithSidebarLayout: React.FC<SidebarLayoutProps> = ({
42
+ serviceNode,
43
+ logo,
44
+ hideTryItPanel,
45
+ hideTryIt,
46
+ hideSamples,
47
+ hideSchemas,
48
+ hideSecurityInfo,
49
+ hideServerInfo,
50
+ hideInternal,
51
+ hideExport,
52
+ exportProps,
53
+ tryItCredentialsPolicy,
54
+ tryItCorsProxy,
55
+ renderExtensionAddon,
56
+ basePath = '/',
57
+ outerRouter = false,
58
+ }) => {
59
+ const container = React.useRef<HTMLDivElement>(null);
60
+ const tree = React.useMemo(
61
+ () => computeAPITree(serviceNode, { hideSchemas, hideInternal }),
62
+ [serviceNode, hideSchemas, hideInternal],
63
+ );
64
+ const location = useLocation();
65
+ const { pathname: currentPath } = location;
66
+ const relativePath = resolveRelativePath(currentPath, basePath, outerRouter);
67
+ const isRootPath = relativePath === '/';
68
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === relativePath);
69
+
70
+ const layoutOptions = React.useMemo(
71
+ () => ({
72
+ hideTryIt: hideTryIt,
73
+ hideTryItPanel,
74
+ hideSamples,
75
+ hideServerInfo: hideServerInfo,
76
+ hideSecurityInfo: hideSecurityInfo,
77
+ hideExport: hideExport || node?.type !== NodeType.HttpService,
78
+ }),
79
+ [hideTryIt, hideServerInfo, hideSecurityInfo, hideExport, hideTryItPanel, hideSamples, node?.type],
80
+ );
81
+
82
+ if (!node) {
83
+ // Redirect to the first child if node doesn't exist
84
+ const firstSlug = findFirstNodeSlug(tree);
85
+
86
+ if (firstSlug) {
87
+ return <Navigate to={resolveRelativeLink(firstSlug)} replace />;
88
+ }
89
+ }
90
+
91
+ if (hideInternal && node && isInternal(node)) {
92
+ return <Navigate to="." replace />;
93
+ }
94
+
95
+ const sidebar = (
96
+ <Sidebar serviceNode={serviceNode} logo={logo} container={container} pathname={relativePath} tree={tree} />
97
+ );
98
+
99
+ return (
100
+ <SidebarLayout ref={container} sidebar={sidebar}>
101
+ {node && (
102
+ <ElementsOptionsProvider renderExtensionAddon={renderExtensionAddon}>
103
+ <ParsedDocs
104
+ key={relativePath}
105
+ uri={relativePath}
106
+ node={node}
107
+ nodeTitle={node.name}
108
+ layoutOptions={layoutOptions}
109
+ location={location}
110
+ exportProps={exportProps}
111
+ tryItCredentialsPolicy={tryItCredentialsPolicy}
112
+ tryItCorsProxy={tryItCorsProxy}
113
+ renderExtensionAddon={renderExtensionAddon}
114
+ />
115
+ </ElementsOptionsProvider>
116
+ )}
117
+ </SidebarLayout>
118
+ );
119
+ };
120
+
121
+ type SidebarProps = {
122
+ serviceNode: ServiceNode;
123
+ logo?: string;
124
+ container: React.RefObject<HTMLElement>;
125
+ pathname: string;
126
+ tree: TableOfContentsItem[];
127
+ };
128
+
129
+ export const Sidebar: React.FC<SidebarProps> = ({ serviceNode, logo, container, pathname, tree }) => {
130
+ const handleTocClick = () => {
131
+ if (container.current) {
132
+ container.current.scrollIntoView();
133
+ }
134
+ };
135
+
136
+ return (
137
+ <>
138
+ <Flex ml={4} mb={5} alignItems="center">
139
+ {logo ? (
140
+ <Logo logo={{ url: logo, altText: 'logo' }} />
141
+ ) : (
142
+ serviceNode.data.logo && <Logo logo={serviceNode.data.logo} />
143
+ )}
144
+ <Heading size={4}>{serviceNode.name}</Heading>
145
+ </Flex>
146
+ <Flex flexGrow flexShrink overflowY="auto" direction="col">
147
+ <TableOfContents
148
+ tree={tree}
149
+ activeId={pathname}
150
+ Link={Link as CustomLinkComponent}
151
+ onLinkClick={handleTocClick}
152
+ />
153
+ </Flex>
154
+ <PoweredByLink source={serviceNode.name} pathname={pathname} packageType="elements" />
155
+ </>
156
+ );
157
+ };
158
+ Sidebar.displayName = 'Sidebar';
@@ -0,0 +1,286 @@
1
+ import {
2
+ DeprecatedBadge,
3
+ Docs,
4
+ ExportButtonProps,
5
+ HttpMethodColors,
6
+ ParsedDocs,
7
+ TryItWithRequestSamples,
8
+ } from '@stoplight/elements-core';
9
+ import { ExtensionAddonRenderer } from '@stoplight/elements-core/components/Docs';
10
+ import { Box, Flex, Heading, Icon, Tab, TabList, TabPanel, TabPanels, Tabs } from '@stoplight/mosaic';
11
+ import { HttpMethod, NodeType } from '@stoplight/types';
12
+ import cn from 'classnames';
13
+ import * as React from 'react';
14
+
15
+ import { OperationNode, ServiceNode, WebhookNode } from '../../utils/oas/types';
16
+ import { computeTagGroups, TagGroup } from './utils';
17
+
18
+ type TryItCredentialsPolicy = 'omit' | 'include' | 'same-origin';
19
+
20
+ interface Location {
21
+ pathname: string;
22
+ search: string;
23
+ hash: string;
24
+ state: unknown;
25
+ key: string;
26
+ }
27
+
28
+ type StackedLayoutProps = {
29
+ serviceNode: ServiceNode;
30
+ hideTryItPanel?: boolean;
31
+ hideTryIt?: boolean;
32
+ hideSamples?: boolean;
33
+ hideExport?: boolean;
34
+ hideServerInfo?: boolean;
35
+ hideSecurityInfo?: boolean;
36
+ exportProps?: ExportButtonProps;
37
+ tryItCredentialsPolicy?: TryItCredentialsPolicy;
38
+ tryItCorsProxy?: string;
39
+ showPoweredByLink?: boolean;
40
+ location: Location;
41
+ renderExtensionAddon?: ExtensionAddonRenderer;
42
+ };
43
+
44
+ const itemMatchesHash = (hash: string, item: OperationNode | WebhookNode) => {
45
+ if (item.type === NodeType.HttpOperation) {
46
+ return hash.substr(1) === `${item.data.path}-${item.data.method}`;
47
+ } else {
48
+ return hash.substr(1) === `${item.data.name}-${item.data.method}`;
49
+ }
50
+ };
51
+
52
+ const TryItContext = React.createContext<{
53
+ hideTryIt?: boolean;
54
+ hideTryItPanel?: boolean;
55
+ hideSamples?: boolean;
56
+ tryItCredentialsPolicy?: TryItCredentialsPolicy;
57
+ corsProxy?: string;
58
+ }>({
59
+ hideTryIt: false,
60
+ hideTryItPanel: false,
61
+ hideSamples: false,
62
+ tryItCredentialsPolicy: 'omit',
63
+ });
64
+ TryItContext.displayName = 'TryItContext';
65
+
66
+ const LocationContext = React.createContext<{
67
+ location: Location;
68
+ }>({
69
+ location: {
70
+ hash: '',
71
+ key: '',
72
+ pathname: '',
73
+ search: '',
74
+ state: '',
75
+ },
76
+ });
77
+ LocationContext.displayName = 'LocationContext';
78
+
79
+ export const APIWithStackedLayout: React.FC<StackedLayoutProps> = ({
80
+ serviceNode,
81
+ hideTryItPanel,
82
+ hideTryIt,
83
+ hideSamples,
84
+ hideExport,
85
+ hideSecurityInfo,
86
+ hideServerInfo,
87
+ exportProps,
88
+ tryItCredentialsPolicy,
89
+ tryItCorsProxy,
90
+ renderExtensionAddon,
91
+ showPoweredByLink = true,
92
+ location,
93
+ }) => {
94
+ const { groups: operationGroups } = computeTagGroups<OperationNode>(serviceNode, NodeType.HttpOperation);
95
+ const { groups: webhookGroups } = computeTagGroups<WebhookNode>(serviceNode, NodeType.HttpWebhook);
96
+
97
+ return (
98
+ <LocationContext.Provider value={{ location }}>
99
+ <TryItContext.Provider
100
+ value={{ hideTryItPanel, hideTryIt, hideSamples, tryItCredentialsPolicy, corsProxy: tryItCorsProxy }}
101
+ >
102
+ <Flex w="full" flexDirection="col" m="auto" className="sl-max-w-4xl">
103
+ <Box w="full" borderB>
104
+ <Docs
105
+ className="sl-mx-auto"
106
+ nodeData={serviceNode.data}
107
+ nodeTitle={serviceNode.name}
108
+ nodeType={NodeType.HttpService}
109
+ location={location}
110
+ layoutOptions={{ showPoweredByLink, hideExport, hideSecurityInfo, hideServerInfo }}
111
+ exportProps={exportProps}
112
+ tryItCredentialsPolicy={tryItCredentialsPolicy}
113
+ renderExtensionAddon={renderExtensionAddon}
114
+ />
115
+ </Box>
116
+ {operationGroups.length > 0 && webhookGroups.length > 0 ? <Heading size={2}>Endpoints</Heading> : null}
117
+ {operationGroups.map(group => (
118
+ <Group key={group.title} group={group} />
119
+ ))}
120
+ {webhookGroups.length > 0 ? <Heading size={2}>Webhooks</Heading> : null}
121
+ {webhookGroups.map(group => (
122
+ <Group key={group.title} group={group} />
123
+ ))}
124
+ </Flex>
125
+ </TryItContext.Provider>
126
+ </LocationContext.Provider>
127
+ );
128
+ };
129
+ APIWithStackedLayout.displayName = 'APIWithStackedLayout';
130
+
131
+ const Group = React.memo<{ group: TagGroup<OperationNode | WebhookNode> }>(({ group }) => {
132
+ const [isExpanded, setIsExpanded] = React.useState(false);
133
+ const scrollRef = React.useRef<HTMLDivElement | null>(null);
134
+ const {
135
+ location: { hash },
136
+ } = React.useContext(LocationContext);
137
+ const urlHashMatches = hash.substr(1) === group.title;
138
+
139
+ const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
140
+
141
+ const shouldExpand = React.useMemo(() => {
142
+ return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
143
+ }, [group, hash, urlHashMatches]);
144
+
145
+ React.useEffect(() => {
146
+ if (shouldExpand) {
147
+ setIsExpanded(true);
148
+ if (urlHashMatches && scrollRef?.current?.offsetTop) {
149
+ // scroll only if group is active
150
+ window.scrollTo(0, scrollRef.current.offsetTop);
151
+ }
152
+ }
153
+ }, [shouldExpand, urlHashMatches, group, hash]);
154
+
155
+ return (
156
+ <Box>
157
+ <Flex
158
+ ref={scrollRef}
159
+ onClick={onClick}
160
+ mx="auto"
161
+ justifyContent="between"
162
+ alignItems="center"
163
+ borderB
164
+ px={2}
165
+ py={4}
166
+ cursor="pointer"
167
+ color={{ default: 'current', hover: 'muted' }}
168
+ >
169
+ <Box fontSize="lg" fontWeight="medium">
170
+ {group.title}
171
+ </Box>
172
+ <Icon className="sl-mr-2" icon={isExpanded ? 'chevron-down' : 'chevron-right'} size="sm" />
173
+ </Flex>
174
+
175
+ <Collapse isOpen={isExpanded}>
176
+ {group.items.map(item => {
177
+ return <Item key={item.uri} item={item} />;
178
+ })}
179
+ </Collapse>
180
+ </Box>
181
+ );
182
+ });
183
+ Group.displayName = 'Group';
184
+
185
+ const Item = React.memo<{ item: OperationNode | WebhookNode }>(({ item }) => {
186
+ const { location } = React.useContext(LocationContext);
187
+ const { hash } = location;
188
+ const [isExpanded, setIsExpanded] = React.useState(false);
189
+ const scrollRef = React.useRef<HTMLDivElement | null>(null);
190
+ const color = HttpMethodColors[item.data.method as HttpMethod] || 'gray';
191
+ const isDeprecated = !!item.data.deprecated;
192
+ const { hideTryIt, hideSamples, hideTryItPanel, tryItCredentialsPolicy, corsProxy } = React.useContext(TryItContext);
193
+
194
+ const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
195
+
196
+ React.useEffect(() => {
197
+ if (itemMatchesHash(hash, item)) {
198
+ setIsExpanded(true);
199
+ if (scrollRef?.current?.offsetTop) {
200
+ window.scrollTo(0, scrollRef.current.offsetTop);
201
+ }
202
+ }
203
+ }, [hash, item]);
204
+
205
+ return (
206
+ <Box
207
+ ref={scrollRef}
208
+ w="full"
209
+ my={2}
210
+ border
211
+ borderColor={{ default: isExpanded ? 'light' : 'transparent', hover: 'light' }}
212
+ bg={{ default: isExpanded ? 'code' : 'transparent', hover: 'code' }}
213
+ >
214
+ <Flex mx="auto" alignItems="center" cursor="pointer" fontSize="lg" p={2} onClick={onClick} color="current">
215
+ <Box
216
+ w={24}
217
+ textTransform="uppercase"
218
+ textAlign="center"
219
+ fontWeight="semibold"
220
+ border
221
+ rounded
222
+ px={2}
223
+ bg="canvas"
224
+ className={cn(`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`)}
225
+ >
226
+ {item.data.method || 'UNKNOWN'}
227
+ </Box>
228
+
229
+ <Box flex={1} fontWeight="medium" wordBreak="all">
230
+ {item.type === NodeType.HttpOperation ? item.data.path : item.name}
231
+ </Box>
232
+ {isDeprecated && <DeprecatedBadge />}
233
+ </Flex>
234
+
235
+ <Collapse isOpen={isExpanded}>
236
+ <Box flex={1} p={2} fontWeight="medium" mx="auto" fontSize="xl">
237
+ {item.name}
238
+ </Box>
239
+ {hideTryItPanel ? (
240
+ <Box
241
+ as={ParsedDocs}
242
+ layoutOptions={{ noHeading: true, hideTryItPanel: true, hideSamples, hideTryIt }}
243
+ node={item}
244
+ p={4}
245
+ />
246
+ ) : (
247
+ <Tabs appearance="line">
248
+ <TabList>
249
+ <Tab>Docs</Tab>
250
+ <Tab>TryIt</Tab>
251
+ </TabList>
252
+
253
+ <TabPanels>
254
+ <TabPanel>
255
+ <ParsedDocs
256
+ className="sl-px-4"
257
+ node={item}
258
+ location={location}
259
+ layoutOptions={{ noHeading: true, hideTryItPanel: false, hideSamples, hideTryIt }}
260
+ />
261
+ </TabPanel>
262
+
263
+ <TabPanel>
264
+ <TryItWithRequestSamples
265
+ httpOperation={item.data}
266
+ tryItCredentialsPolicy={tryItCredentialsPolicy}
267
+ corsProxy={corsProxy}
268
+ hideSamples={hideSamples}
269
+ hideTryIt={hideTryIt}
270
+ />
271
+ </TabPanel>
272
+ </TabPanels>
273
+ </Tabs>
274
+ )}
275
+ </Collapse>
276
+ </Box>
277
+ );
278
+ });
279
+ Item.displayName = 'Item';
280
+
281
+ const Collapse: React.FC<{ isOpen: boolean }> = ({ isOpen, children }) => {
282
+ if (!isOpen) return null;
283
+
284
+ return <Box>{children}</Box>;
285
+ };
286
+ Collapse.displayName = 'Collapse';