@stoplight/elements 7.16.5 → 8.0.0

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/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { isHttpOperation, isHttpService, HttpMethodColors, DeprecatedBadge, ParsedDocs, TryItWithRequestSamples, Docs, Logo, TableOfContents, PoweredByLink, SidebarLayout, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@stoplight/elements-core';
1
+ import { isHttpOperation, isHttpWebhookOperation, isHttpService, HttpMethodColors, DeprecatedBadge, ParsedDocs, TryItWithRequestSamples, Docs, SidebarLayout, Logo, TableOfContents, PoweredByLink, slugify, withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider, useParsedValue, useBundleRefsIntoDocument, NonIdealState, InlineRefResolverProvider } from '@stoplight/elements-core';
2
2
  import { Box, Flex, Icon, Tabs, TabList, Tab, TabPanels, TabPanel, Heading } from '@stoplight/mosaic';
3
3
  import { NodeType } from '@stoplight/types';
4
4
  import cn from 'classnames';
@@ -9,6 +9,7 @@ import { useQuery } from 'react-query';
9
9
  import { useLocation, Redirect, Link } from 'react-router-dom';
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,12 +73,52 @@ 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);
81
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpOperation);
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
+ itemsType: 'http_operation',
112
+ });
113
+ }
114
+ });
115
+ }
116
+ const hasWebhookNodes = serviceNode.children.some(node => node.type === NodeType.HttpWebhook);
117
+ if (hasWebhookNodes) {
118
+ tree.push({
119
+ title: 'Webhooks',
120
+ });
121
+ const { groups, ungrouped } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
82
122
  ungrouped.forEach(operationNode => {
83
123
  if (mergedConfig.hideInternal && operationNode.data.internal) {
84
124
  return;
@@ -108,13 +148,14 @@ const computeAPITree = (serviceNode, config = {}) => {
108
148
  tree.push({
109
149
  title: group.title,
110
150
  items,
151
+ itemsType: 'http_webhook',
111
152
  });
112
153
  }
113
154
  });
114
155
  }
115
156
  let schemaNodes = serviceNode.children.filter(node => node.type === NodeType.Model);
116
157
  if (mergedConfig.hideInternal) {
117
- schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
158
+ schemaNodes = schemaNodes.filter(n => !isInternal(n));
118
159
  }
119
160
  if (!mergedConfig.hideSchemas && schemaNodes.length) {
120
161
  tree.push({
@@ -148,7 +189,7 @@ const findFirstNodeSlug = (tree) => {
148
189
  };
149
190
  const isInternal = (node) => {
150
191
  const data = node.data;
151
- if (isHttpOperation(data)) {
192
+ if (isHttpOperation(data) || isHttpWebhookOperation(data)) {
152
193
  return !!data.internal;
153
194
  }
154
195
  if (isHttpService(data)) {
@@ -158,7 +199,12 @@ const isInternal = (node) => {
158
199
  };
159
200
 
160
201
  const itemMatchesHash = (hash, item) => {
161
- return hash.substr(1) === `${item.data.path}-${item.data.method}`;
202
+ if (item.type === NodeType.HttpOperation) {
203
+ return hash.substr(1) === `${item.data.path}-${item.data.method}`;
204
+ }
205
+ else {
206
+ return hash.substr(1) === `${item.data.name}-${item.data.method}`;
207
+ }
162
208
  };
163
209
  const TryItContext = React.createContext({
164
210
  hideTryIt: false,
@@ -176,14 +222,19 @@ const LocationContext = React.createContext({
176
222
  });
177
223
  LocationContext.displayName = 'LocationContext';
178
224
  const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, showPoweredByLink = true, location, }) => {
179
- const { groups } = computeTagGroups(serviceNode);
225
+ const { groups: operationGroups } = computeTagGroups(serviceNode, NodeType.HttpOperation);
226
+ const { groups: webhookGroups } = computeTagGroups(serviceNode, NodeType.HttpWebhook);
180
227
  return (React.createElement(LocationContext.Provider, { value: { location } },
181
228
  React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
182
229
  React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
183
230
  React.createElement(Box, { w: "full", borderB: true },
184
231
  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 })),
185
- groups.map(group => (React.createElement(Group, { key: group.title, group: group })))))));
232
+ operationGroups.length > 0 && webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Endpoints") : null,
233
+ operationGroups.map(group => (React.createElement(Group, { key: group.title, group: group }))),
234
+ webhookGroups.length > 0 ? React.createElement(Heading, { size: 2 }, "Webhooks") : null,
235
+ webhookGroups.map(group => (React.createElement(Group, { key: group.title, group: group })))))));
186
236
  };
237
+ APIWithStackedLayout.displayName = 'APIWithStackedLayout';
187
238
  const Group = React.memo(({ group }) => {
188
239
  const [isExpanded, setIsExpanded] = React.useState(false);
189
240
  const scrollRef = React.useRef(null);
@@ -210,6 +261,7 @@ const Group = React.memo(({ group }) => {
210
261
  return React.createElement(Item, { key: item.uri, item: item });
211
262
  }))));
212
263
  });
264
+ Group.displayName = 'Group';
213
265
  const Item = React.memo(({ item }) => {
214
266
  const { location } = React.useContext(LocationContext);
215
267
  const { hash } = location;
@@ -231,7 +283,7 @@ const Item = React.memo(({ item }) => {
231
283
  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' } },
232
284
  React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
233
285
  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'),
234
- React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.data.path),
286
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.type === NodeType.HttpOperation ? item.data.path : item.name),
235
287
  isDeprecated && React.createElement(DeprecatedBadge, null)),
236
288
  React.createElement(Collapse, { isOpen: isExpanded },
237
289
  React.createElement(Box, { flex: 1, p: 2, fontWeight: "medium", mx: "auto", fontSize: "xl" }, item.name),
@@ -245,11 +297,13 @@ const Item = React.memo(({ item }) => {
245
297
  React.createElement(TabPanel, null,
246
298
  React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
247
299
  });
300
+ Item.displayName = 'Item';
248
301
  const Collapse = ({ isOpen, children }) => {
249
302
  if (!isOpen)
250
303
  return null;
251
304
  return React.createElement(Box, null, children);
252
- };
305
+ };
306
+ Collapse.displayName = 'Collapse';
253
307
 
254
308
  const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, }) => {
255
309
  const container = React.useRef(null);
@@ -268,26 +322,32 @@ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideI
268
322
  if (hideInternal && node && isInternal(node)) {
269
323
  return React.createElement(Redirect, { to: "/" });
270
324
  }
325
+ const sidebar = (React.createElement(Sidebar, { serviceNode: serviceNode, logo: logo, container: container, pathname: pathname, tree: tree }));
326
+ 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 }))));
327
+ };
328
+ const Sidebar = ({ serviceNode, logo, container, pathname, tree }) => {
271
329
  const handleTocClick = () => {
272
330
  if (container.current) {
273
331
  container.current.scrollIntoView();
274
332
  }
275
333
  };
276
- const sidebar = (React.createElement(React.Fragment, null,
334
+ return (React.createElement(React.Fragment, null,
277
335
  React.createElement(Flex, { ml: 4, mb: 5, alignItems: "center" },
278
336
  logo ? (React.createElement(Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React.createElement(Logo, { logo: serviceNode.data.logo })),
279
337
  React.createElement(Heading, { size: 4 }, serviceNode.name)),
280
338
  React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
281
339
  React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
282
340
  React.createElement(PoweredByLink, { source: serviceNode.name, pathname: pathname, packageType: "elements" })));
283
- 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 }))));
284
- };
341
+ };
342
+ Sidebar.displayName = 'Sidebar';
285
343
 
286
344
  var NodeTypes;
287
345
  (function (NodeTypes) {
288
346
  NodeTypes["Paths"] = "paths";
289
347
  NodeTypes["Path"] = "path";
290
348
  NodeTypes["Operation"] = "operation";
349
+ NodeTypes["Webhooks"] = "webhooks";
350
+ NodeTypes["Webhook"] = "webhook";
291
351
  NodeTypes["Components"] = "components";
292
352
  NodeTypes["Models"] = "models";
293
353
  NodeTypes["Model"] = "model";
@@ -339,6 +399,22 @@ const oas3SourceMap = [
339
399
  },
340
400
  ],
341
401
  },
402
+ {
403
+ match: 'webhooks',
404
+ type: NodeTypes.Webhooks,
405
+ children: [
406
+ {
407
+ notMatch: '^x-',
408
+ type: NodeTypes.Webhook,
409
+ children: [
410
+ {
411
+ match: 'get|post|put|delete|options|head|patch|trace',
412
+ type: NodeTypes.Webhook,
413
+ },
414
+ ],
415
+ },
416
+ ],
417
+ },
342
418
  {
343
419
  match: 'components',
344
420
  type: NodeTypes.Components,
@@ -393,7 +469,7 @@ function computeServiceNode(document, map, transformService, transformOperation)
393
469
  return serviceNode;
394
470
  }
395
471
  function computeChildNodes(document, data, map, transformer, parentUri = '') {
396
- var _a;
472
+ var _a, _b;
397
473
  const nodes = [];
398
474
  if (!isObject(data))
399
475
  return nodes;
@@ -406,7 +482,12 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
406
482
  if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
407
483
  const path = String(jsonPath[1]);
408
484
  const method = String(jsonPath[2]);
409
- const operationDocument = transformer({ document, path, method });
485
+ const operationDocument = transformer({
486
+ document,
487
+ name: path,
488
+ method,
489
+ config: OPERATION_CONFIG,
490
+ });
410
491
  let parsedUri;
411
492
  const encodedPath = String(encodePointerFragment(path));
412
493
  if (operationDocument.iid) {
@@ -423,6 +504,31 @@ function computeChildNodes(document, data, map, transformer, parentUri = '') {
423
504
  tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
424
505
  });
425
506
  }
507
+ else if (match.type === NodeTypes.Webhook && jsonPath.length === 3) {
508
+ const name = String(jsonPath[1]);
509
+ const method = String(jsonPath[2]);
510
+ const webhookDocument = transformer({
511
+ document,
512
+ name,
513
+ method,
514
+ config: WEBHOOK_CONFIG,
515
+ });
516
+ let parsedUri;
517
+ const encodedPath = String(encodePointerFragment(name));
518
+ if (webhookDocument.iid) {
519
+ parsedUri = `/webhooks/${webhookDocument.iid}`;
520
+ }
521
+ else {
522
+ parsedUri = uri.replace(encodedPath, slugify(name));
523
+ }
524
+ nodes.push({
525
+ type: NodeType.HttpWebhook,
526
+ uri: parsedUri,
527
+ data: webhookDocument,
528
+ name: webhookDocument.summary || webhookDocument.name,
529
+ tags: ((_b = webhookDocument.tags) === null || _b === void 0 ? void 0 : _b.map(tag => tag.name)) || [],
530
+ });
531
+ }
426
532
  else if (match.type === NodeTypes.Model) {
427
533
  const schemaDocument = get(document, jsonPath);
428
534
  const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoplight/elements",
3
- "version": "7.16.5",
3
+ "version": "8.0.0",
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
- "@stoplight/elements-core": "~7.16.5",
30
- "@stoplight/http-spec": "^6.0.0",
29
+ "@stoplight/elements-core": "~8.0.0",
30
+ "@stoplight/http-spec": "^7.0.1",
31
31
  "@stoplight/json": "^3.18.1",
32
32
  "@stoplight/mosaic": "^1.46.1",
33
- "@stoplight/types": "^14.0.0",
33
+ "@stoplight/types": "^14.1.1",
34
34
  "@stoplight/yaml": "^4.2.3",
35
35
  "classnames": "^2.2.6",
36
36
  "file-saver": "^2.0.5",
@@ -1,9 +1,11 @@
1
- import { IHttpOperation, IHttpService, NodeType } from '@stoplight/types';
1
+ import { IHttpOperation, IHttpService, IHttpWebhookOperation, NodeType } from '@stoplight/types';
2
2
  import { JSONSchema7 } from 'json-schema';
3
3
  export declare enum NodeTypes {
4
4
  Paths = "paths",
5
5
  Path = "path",
6
6
  Operation = "operation",
7
+ Webhooks = "webhooks",
8
+ Webhook = "webhook",
7
9
  Components = "components",
8
10
  Models = "models",
9
11
  Model = "model"
@@ -24,7 +26,8 @@ declare type Node<T, D> = {
24
26
  export declare type ServiceNode = Node<NodeType.HttpService, IHttpService> & {
25
27
  children: ServiceChildNode[];
26
28
  };
27
- export declare type ServiceChildNode = OperationNode | SchemaNode;
29
+ export declare type ServiceChildNode = OperationNode | WebhookNode | SchemaNode;
28
30
  export declare type OperationNode = Node<NodeType.HttpOperation, IHttpOperation>;
31
+ export declare type WebhookNode = Node<NodeType.HttpWebhook, IHttpWebhookOperation>;
29
32
  export declare type SchemaNode = Node<NodeType.Model, JSONSchema7>;
30
33
  export {};