@jpmorganchase/elemental 6.0.1 → 7.0.1

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.
@@ -0,0 +1,19 @@
1
+ import { ExportButtonProps } from '@jpmorganchase/elemental-core';
2
+ import { ExtensionAddonRenderer } from '@jpmorganchase/elemental-core/components/Docs';
3
+ import * as React from 'react';
4
+ import { ServiceNode } from '../../utils/oas/types';
5
+ declare type SidebarLayoutProps = {
6
+ serviceNode: ServiceNode;
7
+ logo?: string;
8
+ hideTryIt?: boolean;
9
+ hideSchemas?: boolean;
10
+ hideInternal?: boolean;
11
+ hideExport?: boolean;
12
+ exportProps?: ExportButtonProps;
13
+ tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
14
+ tryItCorsProxy?: string;
15
+ compact?: number | boolean;
16
+ renderExtensionAddon?: ExtensionAddonRenderer;
17
+ };
18
+ export declare const APIWithResponsiveSidebarLayout: React.FC<SidebarLayoutProps>;
19
+ export {};
@@ -1,4 +1,5 @@
1
- import { ExportButtonProps } from '@jpmorganchase/elemental-core';
1
+ import { ExportButtonProps, TableOfContentsItem } from '@jpmorganchase/elemental-core';
2
+ import { ExtensionAddonRenderer } from '@jpmorganchase/elemental-core/components/Docs';
2
3
  import * as React from 'react';
3
4
  import { ServiceNode } from '../../utils/oas/types';
4
5
  declare type SidebarLayoutProps = {
@@ -12,9 +13,18 @@ declare type SidebarLayoutProps = {
12
13
  exportProps?: ExportButtonProps;
13
14
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
14
15
  tryItCorsProxy?: string;
16
+ renderExtensionAddon?: ExtensionAddonRenderer;
15
17
  tryItOutDefaultServer?: string;
16
18
  useCustomNav?: boolean;
17
19
  layout?: 'sidebar' | 'drawer';
18
20
  };
19
21
  export declare const APIWithSidebarLayout: React.FC<SidebarLayoutProps>;
22
+ declare type SidebarProps = {
23
+ serviceNode: ServiceNode;
24
+ logo?: string;
25
+ container: React.RefObject<HTMLElement>;
26
+ pathname: string;
27
+ tree: TableOfContentsItem[];
28
+ };
29
+ export declare const Sidebar: React.FC<SidebarProps>;
20
30
  export {};
@@ -1,7 +1,15 @@
1
1
  import { ExportButtonProps } from '@jpmorganchase/elemental-core';
2
+ import { ExtensionAddonRenderer } from '@jpmorganchase/elemental-core/components/Docs';
2
3
  import * as React from 'react';
3
4
  import { ServiceNode } from '../../utils/oas/types';
4
5
  declare type TryItCredentialsPolicy = 'omit' | 'include' | 'same-origin';
6
+ interface Location {
7
+ pathname: string;
8
+ search: string;
9
+ hash: string;
10
+ state: unknown;
11
+ key: string;
12
+ }
5
13
  declare type StackedLayoutProps = {
6
14
  serviceNode: ServiceNode;
7
15
  hideTryIt?: boolean;
@@ -10,6 +18,9 @@ declare type StackedLayoutProps = {
10
18
  exportProps?: ExportButtonProps;
11
19
  tryItCredentialsPolicy?: TryItCredentialsPolicy;
12
20
  tryItCorsProxy?: string;
21
+ showPoweredByLink?: boolean;
22
+ location: Location;
23
+ renderExtensionAddon?: ExtensionAddonRenderer;
13
24
  tryItOutDefaultServer?: string;
14
25
  };
15
26
  export declare const APIWithStackedLayout: React.FC<StackedLayoutProps>;
@@ -1,12 +1,13 @@
1
1
  import { TableOfContentsItem } from '@jpmorganchase/elemental-core';
2
- import { OperationNode, ServiceChildNode, ServiceNode } from '../../utils/oas/types';
3
- export declare type TagGroup = {
2
+ import { OperationNode, SchemaNode, ServiceChildNode, ServiceNode, WebhookNode } from '../../utils/oas/types';
3
+ declare type GroupableNode = OperationNode | WebhookNode | SchemaNode;
4
+ export declare type TagGroup<T extends GroupableNode> = {
4
5
  title: string;
5
- items: OperationNode[];
6
+ items: T[];
6
7
  };
7
- export declare const computeTagGroups: (serviceNode: ServiceNode) => {
8
- groups: TagGroup[];
9
- ungrouped: OperationNode[];
8
+ export declare function computeTagGroups<T extends GroupableNode>(serviceNode: ServiceNode, nodeType: T['type']): {
9
+ groups: TagGroup<T>[];
10
+ ungrouped: T[];
10
11
  };
11
12
  interface ComputeAPITreeConfig {
12
13
  hideSchemas?: boolean;
@@ -1,4 +1,5 @@
1
1
  import { RoutingProps } from '@jpmorganchase/elemental-core';
2
+ import { ExtensionAddonRenderer } from '@jpmorganchase/elemental-core/components/Docs';
2
3
  import * as React from 'react';
3
4
  export declare type APIProps = APIPropsWithDocument | APIPropsWithUrl;
4
5
  export declare type APIPropsWithUrl = {
@@ -9,7 +10,7 @@ export declare type APIPropsWithDocument = {
9
10
  apiDescriptionUrl?: string;
10
11
  } & CommonAPIProps;
11
12
  export interface CommonAPIProps extends RoutingProps {
12
- layout?: 'sidebar' | 'stacked' | 'drawer';
13
+ layout?: 'sidebar' | 'stacked' | 'responsive' | 'drawer';
13
14
  logo?: string;
14
15
  hideTryIt?: boolean;
15
16
  hideSchemas?: boolean;
@@ -18,6 +19,8 @@ export interface CommonAPIProps extends RoutingProps {
18
19
  hideInlineExamples?: boolean;
19
20
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
20
21
  tryItCorsProxy?: string;
22
+ maxRefDepth?: number;
23
+ renderExtensionAddon?: ExtensionAddonRenderer;
21
24
  tryItOutDefaultServer?: string;
22
25
  useCustomNav?: boolean;
23
26
  }
@@ -50,7 +50,9 @@ export declare const APIWithInternalOperations: import("@storybook/types").Annot
50
50
  export declare const OpenApi3Schema: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
51
51
  export declare const BadgesForSchema: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
52
52
  export declare const StackedLayout: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
53
+ export declare const ResponsiveLayout: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
53
54
  export declare const Box: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
54
55
  export declare const DigitalOcean: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
55
56
  export declare const Github: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
56
57
  export declare const Instagram: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
58
+ export declare const WithExtensionRenderer: import("@storybook/types").AnnotatedStoryFn<import("@storybook/react/dist/types-0a347bb9").R, APIProps>;
@@ -0,0 +1,2 @@
1
+ export declare const renderExtensionRenderer: (props: any) => JSX.Element | null;
2
+ export declare const wrapOptionsContext: (story: any) => JSX.Element;
package/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
+ export { APIWithStackedLayout } from './components/API/APIWithStackedLayout';
1
2
  export type { APIProps } from './containers/API';
2
3
  export { API } from './containers/API';
3
4
  export { useGetOasNavTree } from './hooks/oas-nav-tree/useGetOasNavTree';
4
5
  export { useExportDocumentProps } from './hooks/useExportDocumentProps';
5
6
  export { transformOasToServiceNode } from './utils/oas';
7
+ export type { ServiceNode } from './utils/oas/types';
package/index.esm.js CHANGED
@@ -1,14 +1,15 @@
1
- import { isHttpOperation, isHttpService, Logo, TableOfContents, PoweredByLink, SidebarLayout, ParsedDocs, HttpMethodColors, DeprecatedBadge, TryItWithRequestSamples, Docs, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@jpmorganchase/elemental-core';
2
- import { Flex, Heading, Box, Icon, Tabs, TabList, Tab, TabPanels, TabPanel } from '@stoplight/mosaic';
3
- import flow from 'lodash/flow.js';
1
+ import { isHttpOperation, isHttpWebhookOperation, isHttpService, HttpMethodColors, DeprecatedBadge, ParsedDocs, TryItWithRequestSamples, Docs, ResponsiveSidebarLayout, ElementsOptionsProvider, SidebarLayout, Logo, TableOfContents, PoweredByLink, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useResponsiveLayout, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@jpmorganchase/elemental-core';
2
+ import { Box, Flex, Icon, Tabs, TabList, Tab, TabPanels, TabPanel, Heading } from '@stoplight/mosaic';
3
+ import { NodeType } from '@stoplight/types';
4
+ import cn from 'classnames';
4
5
  import * as React from 'react';
6
+ import defaults from 'lodash/defaults.js';
7
+ import flow from 'lodash/flow.js';
5
8
  import { useQuery } from 'react-query';
6
- import { NodeType } from '@stoplight/types';
7
9
  import { useLocation, Redirect, Link } from 'react-router-dom';
8
- import defaults from 'lodash/defaults.js';
9
- import cn from 'classnames';
10
10
  import { safeStringify } from '@stoplight/yaml';
11
11
  import saver from 'file-saver';
12
+ import { OPERATION_CONFIG, WEBHOOK_CONFIG } from '@stoplight/http-spec/oas';
12
13
  import { transformOas2Service, transformOas2Operation } from '@stoplight/http-spec/oas2';
13
14
  import { transformOas3Service, transformOas3Operation } from '@stoplight/http-spec/oas3';
14
15
  import { encodePointerFragment, pointerToPath } from '@stoplight/json';
@@ -16,13 +17,12 @@ import get from 'lodash/get.js';
16
17
  import isObject from 'lodash/isObject.js';
17
18
  import last from 'lodash/last.js';
18
19
 
19
- const computeTagGroups = (serviceNode) => {
20
+ function computeTagGroups(serviceNode, nodeType) {
20
21
  const groupsByTagId = {};
21
22
  const ungrouped = [];
22
23
  const lowerCaseServiceTags = serviceNode.tags.map(tn => tn.toLowerCase());
23
- for (const node of serviceNode.children) {
24
- if (node.type !== NodeType.HttpOperation)
25
- continue;
24
+ const groupableNodes = serviceNode.children.filter(n => n.type === nodeType);
25
+ for (const node of groupableNodes) {
26
26
  const tagName = node.tags[0];
27
27
  if (tagName) {
28
28
  const tagId = tagName.toLowerCase();
@@ -58,7 +58,7 @@ const computeTagGroups = (serviceNode) => {
58
58
  })
59
59
  .map(([, tagGroup]) => tagGroup);
60
60
  return { groups: orderedTagGroups, ungrouped };
61
- };
61
+ }
62
62
  const defaultComputerAPITreeConfig = {
63
63
  hideSchemas: false,
64
64
  hideInternal: false,
@@ -73,62 +73,32 @@ const computeAPITree = (serviceNode, config = {}) => {
73
73
  type: 'overview',
74
74
  meta: '',
75
75
  });
76
- const operationNodes = serviceNode.children.filter(node => node.type === NodeType.HttpOperation);
77
- if (operationNodes.length) {
76
+ const hasOperationNodes = serviceNode.children.some(node => node.type === NodeType.HttpOperation);
77
+ if (hasOperationNodes) {
78
78
  tree.push({
79
79
  title: 'Endpoints',
80
80
  });
81
- const { groups, ungrouped } = computeTagGroups(serviceNode);
82
- ungrouped.forEach(operationNode => {
83
- if (mergedConfig.hideInternal && operationNode.data.internal) {
84
- return;
85
- }
86
- tree.push({
87
- id: operationNode.uri,
88
- slug: operationNode.uri,
89
- title: operationNode.name,
90
- type: operationNode.type,
91
- meta: operationNode.data.method,
92
- });
93
- });
94
- groups.forEach(group => {
95
- const items = group.items.flatMap(operationNode => {
96
- if (mergedConfig.hideInternal && operationNode.data.internal) {
97
- return [];
98
- }
99
- return {
100
- id: operationNode.uri,
101
- slug: operationNode.uri,
102
- title: operationNode.name,
103
- type: operationNode.type,
104
- meta: operationNode.data.method,
105
- };
106
- });
107
- if (items.length > 0) {
108
- tree.push({
109
- title: group.title,
110
- items,
111
- });
112
- }
81
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpOperation);
82
+ addTagGroupsToTree(groups, ungrouped, tree, NodeType.HttpOperation, mergedConfig.hideInternal);
83
+ }
84
+ const hasWebhookNodes = serviceNode.children.some(node => node.type === NodeType.HttpWebhook);
85
+ if (hasWebhookNodes) {
86
+ tree.push({
87
+ title: 'Webhooks',
113
88
  });
89
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
90
+ addTagGroupsToTree(groups, ungrouped, tree, NodeType.HttpWebhook, mergedConfig.hideInternal);
114
91
  }
115
92
  let schemaNodes = serviceNode.children.filter(node => node.type === NodeType.Model);
116
93
  if (mergedConfig.hideInternal) {
117
- schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
94
+ schemaNodes = schemaNodes.filter(n => !isInternal(n));
118
95
  }
119
96
  if (!mergedConfig.hideSchemas && schemaNodes.length) {
120
97
  tree.push({
121
98
  title: 'Schemas',
122
99
  });
123
- schemaNodes.forEach(node => {
124
- tree.push({
125
- id: node.uri,
126
- slug: node.uri,
127
- title: node.name,
128
- type: node.type,
129
- meta: '',
130
- });
131
- });
100
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.Model);
101
+ addTagGroupsToTree(groups, ungrouped, tree, NodeType.Model, mergedConfig.hideInternal);
132
102
  }
133
103
  return tree;
134
104
  };
@@ -148,52 +118,48 @@ const findFirstNodeSlug = (tree) => {
148
118
  };
149
119
  const isInternal = (node) => {
150
120
  const data = node.data;
151
- if (isHttpOperation(data)) {
121
+ if (isHttpOperation(data) || isHttpWebhookOperation(data)) {
152
122
  return !!data.internal;
153
123
  }
154
124
  if (isHttpService(data)) {
155
125
  return false;
156
126
  }
157
127
  return !!data['x-internal'];
158
- };
159
-
160
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, hideInlineExamples = false, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, layout, }) => {
161
- const container = React.useRef(null);
162
- const tree = React.useMemo(() => {
163
- if (!useCustomNav)
164
- return computeAPITree(serviceNode, { hideSchemas, hideInternal });
165
- else
166
- return [];
167
- }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
168
- const location = useLocation();
169
- const { pathname } = location;
170
- const isRootPath = !pathname || pathname === '/';
171
- const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
172
- React.useEffect(() => {
173
- }, [pathname]);
174
- const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideInlineExamples, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node, hideInlineExamples]);
175
- if (!node) {
176
- const firstSlug = findFirstNodeSlug(tree);
177
- if (firstSlug) {
178
- return React.createElement(Redirect, { to: firstSlug });
128
+ };
129
+ const addTagGroupsToTree = (groups, ungrouped, tree, itemsType, hideInternal) => {
130
+ ungrouped.forEach(node => {
131
+ if (hideInternal && isInternal(node)) {
132
+ return;
179
133
  }
180
- }
181
- if (hideInternal && node && isInternal(node)) {
182
- return React.createElement(Redirect, { to: "/" });
183
- }
184
- const handleTocClick = () => {
185
- if (container.current) {
186
- container.current.scrollIntoView();
134
+ tree.push({
135
+ id: node.uri,
136
+ slug: node.uri,
137
+ title: node.name,
138
+ type: node.type,
139
+ meta: isHttpOperation(node.data) || isHttpWebhookOperation(node.data) ? node.data.method : '',
140
+ });
141
+ });
142
+ groups.forEach(group => {
143
+ const items = group.items.flatMap(node => {
144
+ if (hideInternal && isInternal(node)) {
145
+ return [];
146
+ }
147
+ return {
148
+ id: node.uri,
149
+ slug: node.uri,
150
+ title: node.name,
151
+ type: node.type,
152
+ meta: isHttpOperation(node.data) || isHttpWebhookOperation(node.data) ? node.data.method : '',
153
+ };
154
+ });
155
+ if (items.length > 0) {
156
+ tree.push({
157
+ title: group.title,
158
+ items,
159
+ itemsType,
160
+ });
187
161
  }
188
- };
189
- const sidebar = (React.createElement(React.Fragment, null,
190
- React.createElement(Flex, { ml: 4, mb: 5, alignItems: "center" },
191
- logo ? (React.createElement(Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React.createElement(Logo, { logo: serviceNode.data.logo })),
192
- React.createElement(Heading, { size: 4 }, serviceNode.name)),
193
- React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
194
- React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
195
- React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
196
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav, layout: layout }, node && (React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
162
+ });
197
163
  };
198
164
 
199
165
  const itemUriMatchesPathname = (itemUri, pathname) => itemUri === pathname;
@@ -202,24 +168,39 @@ const TryItContext = React.createContext({
202
168
  tryItCredentialsPolicy: 'omit',
203
169
  });
204
170
  TryItContext.displayName = 'TryItContext';
205
- const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, hideInlineExamples, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
206
- const location = useLocation();
207
- const { groups } = computeTagGroups(serviceNode);
208
- return (React.createElement(TryItContext.Provider, { value: {
209
- hideTryIt,
210
- hideInlineExamples,
211
- tryItCredentialsPolicy,
212
- corsProxy: tryItCorsProxy,
213
- tryItOutDefaultServer,
214
- } },
215
- React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
216
- React.createElement(Box, { w: "full", borderB: true },
217
- React.createElement(Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink: true, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer })),
218
- groups.map(group => (React.createElement(Group, { key: group.title, group: group }))))));
171
+ const LocationContext = React.createContext({
172
+ location: {
173
+ hash: '',
174
+ key: '',
175
+ pathname: '',
176
+ search: '',
177
+ state: '',
178
+ },
179
+ });
180
+ LocationContext.displayName = 'LocationContext';
181
+ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, hideInlineExamples, exportProps, tryItCredentialsPolicy, tryItCorsProxy, renderExtensionAddon, showPoweredByLink = true, location, tryItOutDefaultServer, }) => {
182
+ const { groups: operationGroups } = computeTagGroups(serviceNode, NodeType.HttpOperation);
183
+ const { groups: webhookGroups } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
184
+ return (React.createElement(LocationContext.Provider, { value: { location } },
185
+ React.createElement(TryItContext.Provider, { value: {
186
+ hideTryIt,
187
+ tryItCredentialsPolicy,
188
+ corsProxy: tryItCorsProxy,
189
+ hideInlineExamples,
190
+ tryItOutDefaultServer,
191
+ } },
192
+ React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
193
+ React.createElement(Box, { w: "full", borderB: true },
194
+ React.createElement(Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, renderExtensionAddon: renderExtensionAddon, tryItOutDefaultServer: tryItOutDefaultServer })),
195
+ operationGroups.length > 0 && webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Endpoints") : null,
196
+ operationGroups.map(group => (React.createElement(Group, { key: group.title, group: group }))),
197
+ webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Webhooks") : null,
198
+ webhookGroups.map(group => (React.createElement(Group, { key: group.title, group: group })))))));
219
199
  };
200
+ APIWithStackedLayout.displayName = 'APIWithStackedLayout';
220
201
  const Group = React.memo(({ group }) => {
221
202
  const [isExpanded, setIsExpanded] = React.useState(false);
222
- const { pathname } = useLocation();
203
+ const { location: { pathname }, } = React.useContext(LocationContext);
223
204
  const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
224
205
  const shouldExpand = React.useMemo(() => {
225
206
  return group.items.some(item => itemUriMatchesPathname(item.uri, pathname));
@@ -237,8 +218,9 @@ const Group = React.memo(({ group }) => {
237
218
  return React.createElement(Item, { key: item.uri, item: item });
238
219
  }))));
239
220
  });
221
+ Group.displayName = 'Group';
240
222
  const Item = React.memo(({ item }) => {
241
- const location = useLocation();
223
+ const { location } = React.useContext(LocationContext);
242
224
  const { pathname } = location;
243
225
  const [isExpanded, setIsExpanded] = React.useState(false);
244
226
  const scrollRef = React.useRef(null);
@@ -262,7 +244,7 @@ const Item = React.memo(({ item }) => {
262
244
  return (React.createElement(Box, { ref: scrollRef, w: "full", my: 2, border: true, borderColor: { default: isExpanded ? 'light' : 'transparent', hover: 'light' }, bg: { default: isExpanded ? 'code' : 'transparent', hover: 'code' } },
263
245
  React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
264
246
  React.createElement(Box, { w: 24, textTransform: "uppercase", textAlign: "center", fontWeight: "semibold", border: true, rounded: true, px: 2, bg: "canvas", className: cn(`sl-mr-5 sl-text-base`, `sl-text-${color}`, `sl-border-${color}`) }, item.data.method || 'UNKNOWN'),
265
- React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
247
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.type === NodeType.HttpOperation ? item.data.path : item.name),
266
248
  isDeprecated && React.createElement(DeprecatedBadge, null)),
267
249
  React.createElement(Collapse, { isOpen: isExpanded },
268
250
  React.createElement(Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
@@ -276,17 +258,91 @@ const Item = React.memo(({ item }) => {
276
258
  React.createElement(TabPanel, null,
277
259
  React.createElement(TryItWithRequestSamples, { httpOperation: item.data, hideInlineExamples: hideInlineExamples, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
278
260
  });
261
+ Item.displayName = 'Item';
279
262
  const Collapse = ({ isOpen, children }) => {
280
263
  if (!isOpen)
281
264
  return null;
282
265
  return React.createElement(Box, null, children);
266
+ };
267
+ Collapse.displayName = 'Collapse';
268
+
269
+ const APIWithResponsiveSidebarLayout = ({ serviceNode, logo, hideTryIt, compact, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, renderExtensionAddon, }) => {
270
+ const container = React.useRef(null);
271
+ const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
272
+ const location = useLocation();
273
+ const { pathname } = location;
274
+ const isRootPath = !pathname || pathname === '/';
275
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
276
+ const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, compact: compact, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node, compact]);
277
+ if (!node) {
278
+ const firstSlug = findFirstNodeSlug(tree);
279
+ if (firstSlug) {
280
+ return React.createElement(Redirect, { to: firstSlug });
281
+ }
282
+ }
283
+ if (hideInternal && node && isInternal(node)) {
284
+ return React.createElement(Redirect, { to: "/" });
285
+ }
286
+ const handleTocClick = () => {
287
+ if (container.current) {
288
+ container.current.scrollIntoView();
289
+ }
290
+ };
291
+ return (React.createElement(ResponsiveSidebarLayout, { onTocClick: handleTocClick, tree: tree, logo: logo !== null && logo !== void 0 ? logo : serviceNode.data.logo, ref: container, name: serviceNode.name }, node && (React.createElement(ElementsOptionsProvider, { renderExtensionAddon: renderExtensionAddon },
292
+ React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, renderExtensionAddon: renderExtensionAddon })))));
283
293
  };
284
294
 
295
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, hideInlineExamples = false, exportProps, tryItCredentialsPolicy, tryItCorsProxy, renderExtensionAddon, tryItOutDefaultServer, useCustomNav, layout, }) => {
296
+ const container = React.useRef(null);
297
+ const tree = React.useMemo(() => {
298
+ if (!useCustomNav)
299
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
300
+ else
301
+ return [];
302
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
303
+ const location = useLocation();
304
+ const { pathname } = location;
305
+ const isRootPath = !pathname || pathname === '/';
306
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
307
+ React.useEffect(() => {
308
+ }, [pathname]);
309
+ const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideInlineExamples, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node, hideInlineExamples]);
310
+ if (!node) {
311
+ const firstSlug = findFirstNodeSlug(tree);
312
+ if (firstSlug) {
313
+ return React.createElement(Redirect, { to: firstSlug });
314
+ }
315
+ }
316
+ if (hideInternal && node && isInternal(node)) {
317
+ return React.createElement(Redirect, { to: "/" });
318
+ }
319
+ const sidebar = (React.createElement(Sidebar, { serviceNode: serviceNode, logo: logo, container: container, pathname: pathname, tree: tree }));
320
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav, layout: layout }, node && (React.createElement(ElementsOptionsProvider, { renderExtensionAddon: renderExtensionAddon },
321
+ React.createElement(ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, renderExtensionAddon: renderExtensionAddon, tryItOutDefaultServer: tryItOutDefaultServer })))));
322
+ };
323
+ const Sidebar = ({ serviceNode, logo, container, pathname, tree }) => {
324
+ const handleTocClick = () => {
325
+ if (container.current) {
326
+ container.current.scrollIntoView();
327
+ }
328
+ };
329
+ return (React.createElement(React.Fragment, null,
330
+ React.createElement(Flex, { ml: 4, mb: 5, alignItems: "center" },
331
+ logo ? (React.createElement(Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React.createElement(Logo, { logo: serviceNode.data.logo })),
332
+ React.createElement(Heading, { size: 4 }, serviceNode.name)),
333
+ React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
334
+ React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
335
+ React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
336
+ };
337
+ Sidebar.displayName = 'Sidebar';
338
+
285
339
  var NodeTypes;
286
340
  (function (NodeTypes) {
287
341
  NodeTypes["Paths"] = "paths";
288
342
  NodeTypes["Path"] = "path";
289
343
  NodeTypes["Operation"] = "operation";
344
+ NodeTypes["Webhooks"] = "webhooks";
345
+ NodeTypes["Webhook"] = "webhook";
290
346
  NodeTypes["Components"] = "components";
291
347
  NodeTypes["Models"] = "models";
292
348
  NodeTypes["Model"] = "model";
@@ -338,6 +394,22 @@ const oas3SourceMap = [
338
394
  },
339
395
  ],
340
396
  },
397
+ {
398
+ match: 'webhooks',
399
+ type: NodeTypes.Webhooks,
400
+ children: [
401
+ {
402
+ notMatch: '^x-',
403
+ type: NodeTypes.Webhook,
404
+ children: [
405
+ {
406
+ match: 'get|post|put|delete|options|head|patch|trace',
407
+ type: NodeTypes.Webhook,
408
+ },
409
+ ],
410
+ },
411
+ ],
412
+ },
341
413
  {
342
414
  match: 'components',
343
415
  type: NodeTypes.Components,
@@ -392,7 +464,7 @@ function computeServiceNode(document, map, transformService, transformOperation)
392
464
  return serviceNode;
393
465
  }
394
466
  function computeChildNodes(document, data, map, transformer, parentUri = '') {
395
- var _a;
467
+ var _a, _b;
396
468
  const nodes = [];
397
469
  if (!isObject(data))
398
470
  return nodes;
@@ -405,7 +477,12 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
405
477
  if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
406
478
  const path = String(jsonPath[1]);
407
479
  const method = String(jsonPath[2]);
408
- const operationDocument = transformer({ document, path, method });
480
+ const operationDocument = transformer({
481
+ document,
482
+ name: path,
483
+ method,
484
+ config: OPERATION_CONFIG,
485
+ });
409
486
  let parsedUri;
410
487
  const encodedPath = String(encodePointerFragment(path));
411
488
  if (operationDocument.iid) {
@@ -422,6 +499,31 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
422
499
  tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
423
500
  });
424
501
  }
502
+ else if (match.type === NodeTypes.Webhook && jsonPath.length === 3) {
503
+ const name = String(jsonPath[1]);
504
+ const method = String(jsonPath[2]);
505
+ const webhookDocument = transformer({
506
+ document,
507
+ name,
508
+ method,
509
+ config: WEBHOOK_CONFIG,
510
+ });
511
+ let parsedUri;
512
+ const encodedPath = String(encodePointerFragment(name));
513
+ if (webhookDocument.iid) {
514
+ parsedUri = `/webhooks/${webhookDocument.iid}`;
515
+ }
516
+ else {
517
+ parsedUri = uri.replace(encodedPath, slugify(name));
518
+ }
519
+ nodes.push({
520
+ type: NodeType.HttpWebhook,
521
+ uri: parsedUri,
522
+ data: webhookDocument,
523
+ name: webhookDocument.summary || webhookDocument.name,
524
+ tags: ((_b = webhookDocument.tags) === null || _b === void 0 ? void 0 : _b.map(tag => tag.name)) || [],
525
+ });
526
+ }
425
527
  else if (match.type === NodeTypes.Model) {
426
528
  const schemaDocument = get(document, jsonPath);
427
529
  const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');
@@ -494,8 +596,10 @@ const propsAreWithDocument = (props) => {
494
596
  return props.hasOwnProperty('apiDescriptionDocument');
495
597
  };
496
598
  const APIImpl = props => {
497
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, hideInlineExamples, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
599
+ const { layout = 'sidebar', apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, hideInlineExamples, tryItCredentialsPolicy, tryItCorsProxy, maxRefDepth, renderExtensionAddon, tryItOutDefaultServer, useCustomNav, } = props;
600
+ const location = useLocation();
498
601
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
602
+ const { isResponsiveLayoutEnabled } = useResponsiveLayout();
499
603
  const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
500
604
  if (res.ok) {
501
605
  return res.text();
@@ -521,7 +625,10 @@ const APIImpl = props => {
521
625
  return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
522
626
  React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
523
627
  }
524
- return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, hideInlineExamples: hideInlineExamples, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, hideInlineExamples: hideInlineExamples, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav, layout: layout }))));
628
+ return (React.createElement(InlineRefResolverProvider, { document: parsedDocument, maxRefDepth: maxRefDepth },
629
+ layout === 'stacked' && (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, hideInlineExamples: hideInlineExamples, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, renderExtensionAddon: renderExtensionAddon, location: location, tryItOutDefaultServer: tryItOutDefaultServer })),
630
+ layout === 'sidebar' && (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, hideInlineExamples: hideInlineExamples, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, renderExtensionAddon: renderExtensionAddon, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav, layout: layout })),
631
+ layout === 'responsive' && (React.createElement(APIWithResponsiveSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, renderExtensionAddon: renderExtensionAddon, compact: isResponsiveLayoutEnabled }))));
525
632
  };
526
633
  const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
527
634
 
@@ -554,4 +661,4 @@ const useGetOasNavTree = (apiDescriptionDocument) => {
554
661
  return groupSchemas(apiTree);
555
662
  };
556
663
 
557
- export { API, transformOasToServiceNode, useExportDocumentProps, useGetOasNavTree };
664
+ export { API, APIWithStackedLayout, transformOasToServiceNode, useExportDocumentProps, useGetOasNavTree };