@jpmorganchase/elemental 3.0.1 → 3.2.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.
@@ -12,6 +12,7 @@ declare type SidebarLayoutProps = {
12
12
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
13
13
  tryItCorsProxy?: string;
14
14
  tryItOutDefaultServer?: string;
15
+ useCustomNav?: boolean;
15
16
  };
16
17
  export declare const APIWithSidebarLayout: React.FC<SidebarLayoutProps>;
17
18
  export {};
@@ -18,6 +18,7 @@ export interface CommonAPIProps extends RoutingProps {
18
18
  tryItCredentialsPolicy?: 'omit' | 'include' | 'same-origin';
19
19
  tryItCorsProxy?: string;
20
20
  tryItOutDefaultServer?: string;
21
+ useCustomNav?: boolean;
21
22
  }
22
23
  export declare const APIImpl: React.FC<APIProps>;
23
24
  export declare const API: React.FC<APIProps>;
@@ -0,0 +1 @@
1
+ export declare const useGetOasNavTree: (apiDescriptionDocument: string | object) => any[];
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
package/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export type { APIProps } from './containers/API';
2
2
  export { API } from './containers/API';
3
+ export { useGetOasNavTree } from './hooks/oas-nav-tree/useGetOasNavTree';
package/index.esm.js CHANGED
@@ -157,13 +157,20 @@ const isInternal = (node) => {
157
157
  return !!data['x-internal'];
158
158
  };
159
159
 
160
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
160
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
161
161
  const container = React.useRef(null);
162
- const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
162
+ const tree = React.useMemo(() => {
163
+ if (!useCustomNav)
164
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
165
+ else
166
+ return [];
167
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
163
168
  const location = useLocation();
164
169
  const { pathname } = location;
165
170
  const isRootPath = !pathname || pathname === '/';
166
171
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
172
+ React.useEffect(() => {
173
+ }, [pathname]);
167
174
  const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node]);
168
175
  if (!node) {
169
176
  const firstSlug = findFirstNodeSlug(tree);
@@ -186,7 +193,7 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
186
193
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
187
194
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
188
195
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
189
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, 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 }))));
196
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, 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 }))));
190
197
  };
191
198
 
192
199
  const itemMatchesHash = (hash, item) => {
@@ -200,7 +207,7 @@ TryItContext.displayName = 'TryItContext';
200
207
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
201
208
  const location = useLocation();
202
209
  const { groups } = computeTagGroups(serviceNode);
203
- return (React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
210
+ return (React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy, tryItOutDefaultServer } },
204
211
  React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
205
212
  React.createElement(Box, { w: "full", borderB: true },
206
213
  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 })),
@@ -239,7 +246,7 @@ const Item = React.memo(({ item }) => {
239
246
  const scrollRef = React.useRef(null);
240
247
  const color = HttpMethodColors[item.data.method] || 'gray';
241
248
  const isDeprecated = !!item.data.deprecated;
242
- const { hideTryIt, tryItCredentialsPolicy, corsProxy } = React.useContext(TryItContext);
249
+ const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React.useContext(TryItContext);
243
250
  const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
244
251
  React.useEffect(() => {
245
252
  var _a;
@@ -263,7 +270,7 @@ const Item = React.memo(({ item }) => {
263
270
  React.createElement(TabPanel, null,
264
271
  React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
265
272
  React.createElement(TabPanel, null,
266
- React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
273
+ React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
267
274
  });
268
275
  const Collapse = ({ isOpen, children }) => {
269
276
  if (!isOpen)
@@ -434,7 +441,8 @@ function findMapMatch(key, map) {
434
441
  if (typeof key === 'number')
435
442
  return;
436
443
  for (const entry of map) {
437
- if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(key)) || (entry.notMatch !== void 0 && !entry.notMatch.match(key))) {
444
+ const escapedKey = key.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
445
+ if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(escapedKey)) || (entry.notMatch !== void 0 && !entry.notMatch.match(escapedKey))) {
438
446
  return entry;
439
447
  }
440
448
  }
@@ -482,7 +490,7 @@ const propsAreWithDocument = (props) => {
482
490
  return props.hasOwnProperty('apiDescriptionDocument');
483
491
  };
484
492
  const APIImpl = props => {
485
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
493
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
486
494
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
487
495
  const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
488
496
  if (res.ok) {
@@ -509,8 +517,37 @@ const APIImpl = props => {
509
517
  return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
510
518
  React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
511
519
  }
512
- return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
520
+ return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
513
521
  };
514
522
  const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
515
523
 
516
- export { API };
524
+ const useGetOasNavTree = (apiDescriptionDocument) => {
525
+ const parsedDocument = useParsedValue(apiDescriptionDocument);
526
+ const bundledDocument = useBundleRefsIntoDocument(parsedDocument);
527
+ if (!bundledDocument)
528
+ return [];
529
+ const groupSchemas = (tree) => {
530
+ const targetTitle = 'Schemas';
531
+ const newTree = tree.reduce((accumulator, currentObject) => {
532
+ var _a;
533
+ if (currentObject.title === targetTitle) {
534
+ accumulator.matchedObject = currentObject;
535
+ }
536
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
537
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
538
+ accumulator.matchedObject.items.push(currentObject);
539
+ }
540
+ else {
541
+ accumulator.others = accumulator.others || [];
542
+ accumulator.others.push(currentObject);
543
+ }
544
+ return accumulator;
545
+ }, {});
546
+ const navTree = [...newTree.others, newTree.matchedObject];
547
+ return navTree;
548
+ };
549
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
550
+ return groupSchemas(apiTree);
551
+ };
552
+
553
+ export { API, useGetOasNavTree };
package/index.js CHANGED
@@ -190,13 +190,20 @@ const isInternal = (node) => {
190
190
  return !!data['x-internal'];
191
191
  };
192
192
 
193
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
193
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
194
194
  const container = React__namespace.useRef(null);
195
- const tree = React__namespace.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
195
+ const tree = React__namespace.useMemo(() => {
196
+ if (!useCustomNav)
197
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
198
+ else
199
+ return [];
200
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
196
201
  const location = reactRouterDom.useLocation();
197
202
  const { pathname } = location;
198
203
  const isRootPath = !pathname || pathname === '/';
199
204
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
205
+ React__namespace.useEffect(() => {
206
+ }, [pathname]);
200
207
  const layoutOptions = React__namespace.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== types.NodeType.HttpService }), [hideTryIt, hideExport, node]);
201
208
  if (!node) {
202
209
  const firstSlug = findFirstNodeSlug(tree);
@@ -219,7 +226,7 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
219
226
  React__namespace.createElement(mosaic.Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
220
227
  React__namespace.createElement(elementalCore.TableOfContents, { tree: tree, activeId: pathname, Link: reactRouterDom.Link, onLinkClick: handleTocClick })),
221
228
  React__namespace.createElement(elementalCore.PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
222
- return (React__namespace.createElement(elementalCore.SidebarLayout, { ref: container, sidebar: sidebar }, node && (React__namespace.createElement(elementalCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
229
+ return (React__namespace.createElement(elementalCore.SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, node && (React__namespace.createElement(elementalCore.ParsedDocs, { key: pathname, uri: pathname, node: node, nodeTitle: node.name, layoutOptions: layoutOptions, location: location, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
223
230
  };
224
231
 
225
232
  const itemMatchesHash = (hash, item) => {
@@ -233,7 +240,7 @@ TryItContext.displayName = 'TryItContext';
233
240
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
234
241
  const location = reactRouterDom.useLocation();
235
242
  const { groups } = computeTagGroups(serviceNode);
236
- return (React__namespace.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
243
+ return (React__namespace.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy, tryItOutDefaultServer } },
237
244
  React__namespace.createElement(mosaic.Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
238
245
  React__namespace.createElement(mosaic.Box, { w: "full", borderB: true },
239
246
  React__namespace.createElement(elementalCore.Docs, { className: "sl-mx-auto", nodeData: serviceNode.data, nodeTitle: serviceNode.name, nodeType: types.NodeType.HttpService, location: location, layoutOptions: { showPoweredByLink: true, hideExport }, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer })),
@@ -272,7 +279,7 @@ const Item = React__namespace.memo(({ item }) => {
272
279
  const scrollRef = React__namespace.useRef(null);
273
280
  const color = elementalCore.HttpMethodColors[item.data.method] || 'gray';
274
281
  const isDeprecated = !!item.data.deprecated;
275
- const { hideTryIt, tryItCredentialsPolicy, corsProxy } = React__namespace.useContext(TryItContext);
282
+ const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React__namespace.useContext(TryItContext);
276
283
  const onClick = React__namespace.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
277
284
  React__namespace.useEffect(() => {
278
285
  var _a;
@@ -296,7 +303,7 @@ const Item = React__namespace.memo(({ item }) => {
296
303
  React__namespace.createElement(mosaic.TabPanel, null,
297
304
  React__namespace.createElement(elementalCore.ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
298
305
  React__namespace.createElement(mosaic.TabPanel, null,
299
- React__namespace.createElement(elementalCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
306
+ React__namespace.createElement(elementalCore.TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
300
307
  });
301
308
  const Collapse = ({ isOpen, children }) => {
302
309
  if (!isOpen)
@@ -467,7 +474,8 @@ function findMapMatch(key, map) {
467
474
  if (typeof key === 'number')
468
475
  return;
469
476
  for (const entry of map) {
470
- if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(key)) || (entry.notMatch !== void 0 && !entry.notMatch.match(key))) {
477
+ const escapedKey = key.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
478
+ if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(escapedKey)) || (entry.notMatch !== void 0 && !entry.notMatch.match(escapedKey))) {
471
479
  return entry;
472
480
  }
473
481
  }
@@ -515,7 +523,7 @@ const propsAreWithDocument = (props) => {
515
523
  return props.hasOwnProperty('apiDescriptionDocument');
516
524
  };
517
525
  const APIImpl = props => {
518
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
526
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
519
527
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
520
528
  const { data: fetchedDocument, error } = reactQuery.useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
521
529
  if (res.ok) {
@@ -542,8 +550,38 @@ const APIImpl = props => {
542
550
  return (React__namespace.createElement(mosaic.Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
543
551
  React__namespace.createElement(elementalCore.NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
544
552
  }
545
- return (React__namespace.createElement(elementalCore.InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React__namespace.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React__namespace.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
553
+ return (React__namespace.createElement(elementalCore.InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React__namespace.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React__namespace.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
546
554
  };
547
555
  const API = flow__default["default"](elementalCore.withRouter, elementalCore.withStyles, elementalCore.withPersistenceBoundary, elementalCore.withMosaicProvider, elementalCore.withQueryClientProvider)(APIImpl);
548
556
 
557
+ const useGetOasNavTree = (apiDescriptionDocument) => {
558
+ const parsedDocument = elementalCore.useParsedValue(apiDescriptionDocument);
559
+ const bundledDocument = elementalCore.useBundleRefsIntoDocument(parsedDocument);
560
+ if (!bundledDocument)
561
+ return [];
562
+ const groupSchemas = (tree) => {
563
+ const targetTitle = 'Schemas';
564
+ const newTree = tree.reduce((accumulator, currentObject) => {
565
+ var _a;
566
+ if (currentObject.title === targetTitle) {
567
+ accumulator.matchedObject = currentObject;
568
+ }
569
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
570
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
571
+ accumulator.matchedObject.items.push(currentObject);
572
+ }
573
+ else {
574
+ accumulator.others = accumulator.others || [];
575
+ accumulator.others.push(currentObject);
576
+ }
577
+ return accumulator;
578
+ }, {});
579
+ const navTree = [...newTree.others, newTree.matchedObject];
580
+ return navTree;
581
+ };
582
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
583
+ return groupSchemas(apiTree);
584
+ };
585
+
549
586
  exports.API = API;
587
+ exports.useGetOasNavTree = useGetOasNavTree;
package/index.mjs CHANGED
@@ -157,13 +157,20 @@ const isInternal = (node) => {
157
157
  return !!data['x-internal'];
158
158
  };
159
159
 
160
- const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
160
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, }) => {
161
161
  const container = React.useRef(null);
162
- const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
162
+ const tree = React.useMemo(() => {
163
+ if (!useCustomNav)
164
+ return computeAPITree(serviceNode, { hideSchemas, hideInternal });
165
+ else
166
+ return [];
167
+ }, [serviceNode, hideSchemas, hideInternal, useCustomNav]);
163
168
  const location = useLocation();
164
169
  const { pathname } = location;
165
170
  const isRootPath = !pathname || pathname === '/';
166
171
  const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
172
+ React.useEffect(() => {
173
+ }, [pathname]);
167
174
  const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node]);
168
175
  if (!node) {
169
176
  const firstSlug = findFirstNodeSlug(tree);
@@ -186,7 +193,7 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
186
193
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
187
194
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
188
195
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
189
- return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar }, 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 }))));
196
+ return (React.createElement(SidebarLayout, { ref: container, sidebar: sidebar, renderSideBar: !useCustomNav }, 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 }))));
190
197
  };
191
198
 
192
199
  const itemMatchesHash = (hash, item) => {
@@ -200,7 +207,7 @@ TryItContext.displayName = 'TryItContext';
200
207
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
201
208
  const location = useLocation();
202
209
  const { groups } = computeTagGroups(serviceNode);
203
- return (React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
210
+ return (React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy, tryItOutDefaultServer } },
204
211
  React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
205
212
  React.createElement(Box, { w: "full", borderB: true },
206
213
  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 })),
@@ -239,7 +246,7 @@ const Item = React.memo(({ item }) => {
239
246
  const scrollRef = React.useRef(null);
240
247
  const color = HttpMethodColors[item.data.method] || 'gray';
241
248
  const isDeprecated = !!item.data.deprecated;
242
- const { hideTryIt, tryItCredentialsPolicy, corsProxy } = React.useContext(TryItContext);
249
+ const { hideTryIt, tryItCredentialsPolicy, corsProxy, tryItOutDefaultServer } = React.useContext(TryItContext);
243
250
  const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
244
251
  React.useEffect(() => {
245
252
  var _a;
@@ -263,7 +270,7 @@ const Item = React.memo(({ item }) => {
263
270
  React.createElement(TabPanel, null,
264
271
  React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
265
272
  React.createElement(TabPanel, null,
266
- React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
273
+ React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItOutDefaultServer: tryItOutDefaultServer, corsProxy: corsProxy }))))))));
267
274
  });
268
275
  const Collapse = ({ isOpen, children }) => {
269
276
  if (!isOpen)
@@ -434,7 +441,8 @@ function findMapMatch(key, map) {
434
441
  if (typeof key === 'number')
435
442
  return;
436
443
  for (const entry of map) {
437
- if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(key)) || (entry.notMatch !== void 0 && !entry.notMatch.match(key))) {
444
+ const escapedKey = key.replace(/[\\^$*+?.()|[\]{}]/g, '\\$&');
445
+ if (!!((_a = entry.match) === null || _a === void 0 ? void 0 : _a.match(escapedKey)) || (entry.notMatch !== void 0 && !entry.notMatch.match(escapedKey))) {
438
446
  return entry;
439
447
  }
440
448
  }
@@ -482,7 +490,7 @@ const propsAreWithDocument = (props) => {
482
490
  return props.hasOwnProperty('apiDescriptionDocument');
483
491
  };
484
492
  const APIImpl = props => {
485
- const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
493
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, useCustomNav, } = props;
486
494
  const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
487
495
  const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
488
496
  if (res.ok) {
@@ -509,8 +517,37 @@ const APIImpl = props => {
509
517
  return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
510
518
  React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
511
519
  }
512
- return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer }))));
520
+ return (React.createElement(InlineRefResolverProvider, { document: parsedDocument }, layout === 'stacked' ? (React.createElement(APIWithStackedLayout, { serviceNode: serviceNode, hideTryIt: hideTryIt, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer })) : (React.createElement(APIWithSidebarLayout, { logo: logo, serviceNode: serviceNode, hideTryIt: hideTryIt, hideSchemas: hideSchemas, hideInternal: hideInternal, hideExport: hideExport, exportProps: exportProps, tryItCredentialsPolicy: tryItCredentialsPolicy, tryItCorsProxy: tryItCorsProxy, tryItOutDefaultServer: tryItOutDefaultServer, useCustomNav: useCustomNav }))));
513
521
  };
514
522
  const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
515
523
 
516
- export { API };
524
+ const useGetOasNavTree = (apiDescriptionDocument) => {
525
+ const parsedDocument = useParsedValue(apiDescriptionDocument);
526
+ const bundledDocument = useBundleRefsIntoDocument(parsedDocument);
527
+ if (!bundledDocument)
528
+ return [];
529
+ const groupSchemas = (tree) => {
530
+ const targetTitle = 'Schemas';
531
+ const newTree = tree.reduce((accumulator, currentObject) => {
532
+ var _a;
533
+ if (currentObject.title === targetTitle) {
534
+ accumulator.matchedObject = currentObject;
535
+ }
536
+ else if ((_a = currentObject.id) === null || _a === void 0 ? void 0 : _a.includes(targetTitle.toLowerCase())) {
537
+ accumulator.matchedObject.items = accumulator.matchedObject.items || [];
538
+ accumulator.matchedObject.items.push(currentObject);
539
+ }
540
+ else {
541
+ accumulator.others = accumulator.others || [];
542
+ accumulator.others.push(currentObject);
543
+ }
544
+ return accumulator;
545
+ }, {});
546
+ const navTree = [...newTree.others, newTree.matchedObject];
547
+ return navTree;
548
+ };
549
+ const apiTree = computeAPITree(transformOasToServiceNode(bundledDocument));
550
+ return groupSchemas(apiTree);
551
+ };
552
+
553
+ export { API, useGetOasNavTree };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jpmorganchase/elemental",
3
- "version": "3.0.1",
3
+ "version": "3.2.1",
4
4
  "description": "UI components for composing beautiful developer documentation.",
5
5
  "keywords": [],
6
6
  "main": "./index.js",
@@ -26,11 +26,11 @@
26
26
  "react-dom": ">=16.8"
27
27
  },
28
28
  "dependencies": {
29
- "@jpmorganchase/elemental-core": "^1.1.1",
30
- "@stoplight/http-spec": "^5.1.4",
29
+ "@jpmorganchase/elemental-core": "^1.3.1",
30
+ "@stoplight/http-spec": "^6.0.0",
31
31
  "@stoplight/json": "^3.18.1",
32
- "@stoplight/mosaic": "^1.33.0",
33
- "@stoplight/types": "^13.7.0",
32
+ "@stoplight/mosaic": "^1.44.3",
33
+ "@stoplight/types": "^14.0.0",
34
34
  "@stoplight/yaml": "^4.2.3",
35
35
  "classnames": "^2.2.6",
36
36
  "file-saver": "^2.0.5",