@jpmorganchase/elemental 1.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. package/__fixtures__/api-descriptions/Instagram.d.ts +1547 -0
  2. package/__fixtures__/api-descriptions/badgesForSchema.d.ts +1 -0
  3. package/__fixtures__/api-descriptions/simpleApiWithInternalOperations.d.ts +224 -0
  4. package/__fixtures__/api-descriptions/simpleApiWithoutDescription.d.ts +212 -0
  5. package/__fixtures__/api-descriptions/todosApiBundled.d.ts +1 -0
  6. package/__fixtures__/api-descriptions/zoomApiYaml.d.ts +1 -0
  7. package/components/API/APIWithSidebarLayout.d.ts +17 -0
  8. package/components/API/APIWithStackedLayout.d.ts +15 -0
  9. package/components/API/utils.d.ts +18 -0
  10. package/containers/API.d.ts +23 -0
  11. package/containers/API.spec.d.ts +3 -0
  12. package/containers/API.stories.d.ts +57 -0
  13. package/hooks/useExportDocumentProps.d.ts +11 -0
  14. package/hooks/useExportDocumentProps.spec.d.ts +1 -0
  15. package/index.esm.js +516 -0
  16. package/index.js +549 -0
  17. package/index.mjs +516 -0
  18. package/package.json +17 -76
  19. package/styles.min.css +1 -0
  20. package/utils/oas/index.d.ts +3 -0
  21. package/utils/oas/oas2.d.ts +2 -0
  22. package/utils/oas/oas3.d.ts +2 -0
  23. package/utils/oas/types.d.ts +30 -0
  24. package/web-components/components.d.ts +1 -0
  25. package/web-components/index.d.ts +1 -0
  26. package/web-components.min.js +2 -0
  27. package/web-components.min.js.LICENSE.txt +189 -0
  28. package/.storybook/main.js +0 -1
  29. package/.storybook/manager.js +0 -1
  30. package/.storybook/preview.jsx +0 -3
  31. package/jest.config.js +0 -7
  32. package/src/__fixtures__/api-descriptions/Instagram.ts +0 -1859
  33. package/src/__fixtures__/api-descriptions/badgesForSchema.ts +0 -36
  34. package/src/__fixtures__/api-descriptions/simpleApiWithInternalOperations.ts +0 -253
  35. package/src/__fixtures__/api-descriptions/simpleApiWithoutDescription.ts +0 -243
  36. package/src/__fixtures__/api-descriptions/todosApiBundled.ts +0 -430
  37. package/src/__fixtures__/api-descriptions/zoomApiYaml.ts +0 -6083
  38. package/src/components/API/APIWithSidebarLayout.tsx +0 -111
  39. package/src/components/API/APIWithStackedLayout.tsx +0 -220
  40. package/src/components/API/__tests__/utils.test.ts +0 -848
  41. package/src/components/API/utils.ts +0 -174
  42. package/src/containers/API.spec.tsx +0 -131
  43. package/src/containers/API.stories.tsx +0 -99
  44. package/src/containers/API.tsx +0 -200
  45. package/src/hooks/useExportDocumentProps.spec.tsx +0 -68
  46. package/src/hooks/useExportDocumentProps.tsx +0 -48
  47. package/src/styles.css +0 -1
  48. package/src/utils/oas/__tests__/oas.spec.ts +0 -272
  49. package/src/utils/oas/index.ts +0 -150
  50. package/src/utils/oas/oas2.ts +0 -31
  51. package/src/utils/oas/oas3.ts +0 -37
  52. package/src/utils/oas/types.ts +0 -31
  53. package/src/web-components/__stories__/Api.stories.tsx +0 -63
  54. package/src/web-components/components.ts +0 -20
  55. package/src/web-components/index.ts +0 -3
  56. package/tsconfig.build.json +0 -18
  57. package/tsconfig.json +0 -7
  58. package/web-components.config.js +0 -1
  59. /package/{src/index.ts → index.d.ts} +0 -0
package/index.mjs ADDED
@@ -0,0 +1,516 @@
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';
4
+ import * as React from 'react';
5
+ import { useQuery } from 'react-query';
6
+ import { NodeType } from '@stoplight/types';
7
+ import { useLocation, Redirect, Link } from 'react-router-dom';
8
+ import defaults from 'lodash/defaults.js';
9
+ import cn from 'classnames';
10
+ import { safeStringify } from '@stoplight/yaml';
11
+ import saver from 'file-saver';
12
+ import { transformOas2Service, transformOas2Operation } from '@stoplight/http-spec/oas2';
13
+ import { transformOas3Service, transformOas3Operation } from '@stoplight/http-spec/oas3';
14
+ import { encodePointerFragment, pointerToPath } from '@stoplight/json';
15
+ import get from 'lodash/get.js';
16
+ import isObject from 'lodash/isObject.js';
17
+ import last from 'lodash/last.js';
18
+
19
+ const computeTagGroups = (serviceNode) => {
20
+ const groupsByTagId = {};
21
+ const ungrouped = [];
22
+ const lowerCaseServiceTags = serviceNode.tags.map(tn => tn.toLowerCase());
23
+ for (const node of serviceNode.children) {
24
+ if (node.type !== NodeType.HttpOperation)
25
+ continue;
26
+ const tagName = node.tags[0];
27
+ if (tagName) {
28
+ const tagId = tagName.toLowerCase();
29
+ if (groupsByTagId[tagId]) {
30
+ groupsByTagId[tagId].items.push(node);
31
+ }
32
+ else {
33
+ const serviceTagIndex = lowerCaseServiceTags.findIndex(tn => tn === tagId);
34
+ const serviceTagName = serviceNode.tags[serviceTagIndex];
35
+ groupsByTagId[tagId] = {
36
+ title: serviceTagName || tagName,
37
+ items: [node],
38
+ };
39
+ }
40
+ }
41
+ else {
42
+ ungrouped.push(node);
43
+ }
44
+ }
45
+ const orderedTagGroups = Object.entries(groupsByTagId)
46
+ .sort(([g1], [g2]) => {
47
+ const g1LC = g1.toLowerCase();
48
+ const g2LC = g2.toLowerCase();
49
+ const g1Idx = lowerCaseServiceTags.findIndex(tn => tn === g1LC);
50
+ const g2Idx = lowerCaseServiceTags.findIndex(tn => tn === g2LC);
51
+ if (g1Idx < 0 && g2Idx < 0)
52
+ return 0;
53
+ if (g1Idx < 0)
54
+ return 1;
55
+ if (g2Idx < 0)
56
+ return -1;
57
+ return g1Idx - g2Idx;
58
+ })
59
+ .map(([, tagGroup]) => tagGroup);
60
+ return { groups: orderedTagGroups, ungrouped };
61
+ };
62
+ const defaultComputerAPITreeConfig = {
63
+ hideSchemas: false,
64
+ hideInternal: false,
65
+ };
66
+ const computeAPITree = (serviceNode, config = {}) => {
67
+ const mergedConfig = defaults(config, defaultComputerAPITreeConfig);
68
+ const tree = [];
69
+ tree.push({
70
+ id: '/',
71
+ slug: '/',
72
+ title: 'Overview',
73
+ type: 'overview',
74
+ meta: '',
75
+ });
76
+ const operationNodes = serviceNode.children.filter(node => node.type === NodeType.HttpOperation);
77
+ if (operationNodes.length) {
78
+ tree.push({
79
+ title: 'Endpoints',
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
+ }
113
+ });
114
+ }
115
+ let schemaNodes = serviceNode.children.filter(node => node.type === NodeType.Model);
116
+ if (mergedConfig.hideInternal) {
117
+ schemaNodes = schemaNodes.filter(node => !node.data['x-internal']);
118
+ }
119
+ if (!mergedConfig.hideSchemas && schemaNodes.length) {
120
+ tree.push({
121
+ title: 'Schemas',
122
+ });
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
+ });
132
+ }
133
+ return tree;
134
+ };
135
+ const findFirstNodeSlug = (tree) => {
136
+ for (const item of tree) {
137
+ if ('slug' in item) {
138
+ return item.slug;
139
+ }
140
+ if ('items' in item) {
141
+ const slug = findFirstNodeSlug(item.items);
142
+ if (slug) {
143
+ return slug;
144
+ }
145
+ }
146
+ }
147
+ return;
148
+ };
149
+ const isInternal = (node) => {
150
+ const data = node.data;
151
+ if (isHttpOperation(data)) {
152
+ return !!data.internal;
153
+ }
154
+ if (isHttpService(data)) {
155
+ return false;
156
+ }
157
+ return !!data['x-internal'];
158
+ };
159
+
160
+ const APIWithSidebarLayout = ({ serviceNode, logo, hideTryIt, hideSchemas, hideInternal, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
161
+ const container = React.useRef(null);
162
+ const tree = React.useMemo(() => computeAPITree(serviceNode, { hideSchemas, hideInternal }), [serviceNode, hideSchemas, hideInternal]);
163
+ const location = useLocation();
164
+ const { pathname } = location;
165
+ const isRootPath = !pathname || pathname === '/';
166
+ const node = isRootPath ? serviceNode : serviceNode.children.find(child => child.uri === pathname);
167
+ const layoutOptions = React.useMemo(() => ({ hideTryIt: hideTryIt, hideExport: hideExport || (node === null || node === void 0 ? void 0 : node.type) !== NodeType.HttpService }), [hideTryIt, hideExport, node]);
168
+ if (!node) {
169
+ const firstSlug = findFirstNodeSlug(tree);
170
+ if (firstSlug) {
171
+ return React.createElement(Redirect, { to: firstSlug });
172
+ }
173
+ }
174
+ if (hideInternal && node && isInternal(node)) {
175
+ return React.createElement(Redirect, { to: "/" });
176
+ }
177
+ const handleTocClick = () => {
178
+ if (container.current) {
179
+ container.current.scrollIntoView();
180
+ }
181
+ };
182
+ const sidebar = (React.createElement(React.Fragment, null,
183
+ React.createElement(Flex, { ml: 4, mb: 5, alignItems: "center" },
184
+ logo ? (React.createElement(Logo, { logo: { url: logo, altText: 'logo' } })) : (serviceNode.data.logo && React.createElement(Logo, { logo: serviceNode.data.logo })),
185
+ React.createElement(Heading, { size: 4 }, serviceNode.name)),
186
+ React.createElement(Flex, { flexGrow: true, flexShrink: true, overflowY: "auto", direction: "col" },
187
+ React.createElement(TableOfContents, { tree: tree, activeId: pathname, Link: Link, onLinkClick: handleTocClick })),
188
+ 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 }))));
190
+ };
191
+
192
+ const itemMatchesHash = (hash, item) => {
193
+ return hash.substr(1) === `${item.name}-${item.data.method}`;
194
+ };
195
+ const TryItContext = React.createContext({
196
+ hideTryIt: false,
197
+ tryItCredentialsPolicy: 'omit',
198
+ });
199
+ TryItContext.displayName = 'TryItContext';
200
+ const APIWithStackedLayout = ({ serviceNode, hideTryIt, hideExport, exportProps, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, }) => {
201
+ const location = useLocation();
202
+ const { groups } = computeTagGroups(serviceNode);
203
+ return (React.createElement(TryItContext.Provider, { value: { hideTryIt, tryItCredentialsPolicy, corsProxy: tryItCorsProxy } },
204
+ React.createElement(Flex, { w: "full", flexDirection: "col", m: "auto", className: "sl-max-w-4xl" },
205
+ React.createElement(Box, { w: "full", borderB: true },
206
+ 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 })),
207
+ groups.map(group => (React.createElement(Group, { key: group.title, group: group }))))));
208
+ };
209
+ const Group = React.memo(({ group }) => {
210
+ const [isExpanded, setIsExpanded] = React.useState(false);
211
+ const { hash } = useLocation();
212
+ const scrollRef = React.useRef(null);
213
+ const urlHashMatches = hash.substr(1) === group.title;
214
+ const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
215
+ const shouldExpand = React.useMemo(() => {
216
+ return urlHashMatches || group.items.some(item => itemMatchesHash(hash, item));
217
+ }, [group, hash, urlHashMatches]);
218
+ React.useEffect(() => {
219
+ var _a;
220
+ if (shouldExpand) {
221
+ setIsExpanded(true);
222
+ if (urlHashMatches && ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop)) {
223
+ window.scrollTo(0, scrollRef.current.offsetTop);
224
+ }
225
+ }
226
+ }, [shouldExpand, urlHashMatches, group, hash]);
227
+ return (React.createElement(Box, null,
228
+ React.createElement(Flex, { ref: scrollRef, onClick: onClick, mx: "auto", justifyContent: "between", alignItems: "center", borderB: true, px: 2, py: 4, cursor: "pointer", color: { default: 'current', hover: 'muted' } },
229
+ React.createElement(Box, { fontSize: "lg", fontWeight: "medium" }, group.title),
230
+ React.createElement(Icon, { className: "sl-mr-2", icon: isExpanded ? 'chevron-down' : 'chevron-right', size: "sm" })),
231
+ React.createElement(Collapse, { isOpen: isExpanded }, group.items.map(item => {
232
+ return React.createElement(Item, { key: item.uri, item: item });
233
+ }))));
234
+ });
235
+ const Item = React.memo(({ item }) => {
236
+ const location = useLocation();
237
+ const { hash } = location;
238
+ const [isExpanded, setIsExpanded] = React.useState(false);
239
+ const scrollRef = React.useRef(null);
240
+ const color = HttpMethodColors[item.data.method] || 'gray';
241
+ const isDeprecated = !!item.data.deprecated;
242
+ const { hideTryIt, tryItCredentialsPolicy, corsProxy } = React.useContext(TryItContext);
243
+ const onClick = React.useCallback(() => setIsExpanded(!isExpanded), [isExpanded]);
244
+ React.useEffect(() => {
245
+ var _a;
246
+ if (itemMatchesHash(hash, item)) {
247
+ setIsExpanded(true);
248
+ if ((_a = scrollRef === null || scrollRef === void 0 ? void 0 : scrollRef.current) === null || _a === void 0 ? void 0 : _a.offsetTop) {
249
+ window.scrollTo(0, scrollRef.current.offsetTop);
250
+ }
251
+ }
252
+ }, [hash, item]);
253
+ 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' } },
254
+ React.createElement(Flex, { mx: "auto", alignItems: "center", cursor: "pointer", fontSize: "lg", p: 2, onClick: onClick, color: "current" },
255
+ 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'),
256
+ React.createElement(Box, { flex: 1, fontWeight: "medium", wordBreak: "all" }, item.name),
257
+ isDeprecated && React.createElement(DeprecatedBadge, null)),
258
+ React.createElement(Collapse, { isOpen: isExpanded }, hideTryIt ? (React.createElement(Box, { as: ParsedDocs, layoutOptions: { noHeading: true, hideTryItPanel: true }, node: item, p: 4 })) : (React.createElement(Tabs, { appearance: "line" },
259
+ React.createElement(TabList, null,
260
+ React.createElement(Tab, null, "Docs"),
261
+ React.createElement(Tab, null, "TryIt")),
262
+ React.createElement(TabPanels, null,
263
+ React.createElement(TabPanel, null,
264
+ React.createElement(ParsedDocs, { className: "sl-px-4", node: item, location: location, layoutOptions: { noHeading: true, hideTryItPanel: true } })),
265
+ React.createElement(TabPanel, null,
266
+ React.createElement(TryItWithRequestSamples, { httpOperation: item.data, tryItCredentialsPolicy: tryItCredentialsPolicy, corsProxy: corsProxy }))))))));
267
+ });
268
+ const Collapse = ({ isOpen, children }) => {
269
+ if (!isOpen)
270
+ return null;
271
+ return React.createElement(Box, null, children);
272
+ };
273
+
274
+ var NodeTypes;
275
+ (function (NodeTypes) {
276
+ NodeTypes["Paths"] = "paths";
277
+ NodeTypes["Path"] = "path";
278
+ NodeTypes["Operation"] = "operation";
279
+ NodeTypes["Components"] = "components";
280
+ NodeTypes["Models"] = "models";
281
+ NodeTypes["Model"] = "model";
282
+ })(NodeTypes || (NodeTypes = {}));
283
+
284
+ const oas2SourceMap = [
285
+ {
286
+ match: 'paths',
287
+ type: NodeTypes.Paths,
288
+ children: [
289
+ {
290
+ notMatch: '^x-',
291
+ type: NodeTypes.Path,
292
+ children: [
293
+ {
294
+ match: 'get|post|put|delete|options|head|patch|trace',
295
+ type: NodeTypes.Operation,
296
+ },
297
+ ],
298
+ },
299
+ ],
300
+ },
301
+ {
302
+ match: 'definitions',
303
+ type: NodeTypes.Models,
304
+ children: [
305
+ {
306
+ notMatch: '^x-',
307
+ type: NodeTypes.Model,
308
+ },
309
+ ],
310
+ },
311
+ ];
312
+
313
+ const oas3SourceMap = [
314
+ {
315
+ match: 'paths',
316
+ type: NodeTypes.Paths,
317
+ children: [
318
+ {
319
+ notMatch: '^x-',
320
+ type: NodeTypes.Path,
321
+ children: [
322
+ {
323
+ match: 'get|post|put|delete|options|head|patch|trace',
324
+ type: NodeTypes.Operation,
325
+ },
326
+ ],
327
+ },
328
+ ],
329
+ },
330
+ {
331
+ match: 'components',
332
+ type: NodeTypes.Components,
333
+ children: [
334
+ {
335
+ match: 'schemas',
336
+ type: NodeTypes.Models,
337
+ children: [
338
+ {
339
+ notMatch: '^x-',
340
+ type: NodeTypes.Model,
341
+ },
342
+ ],
343
+ },
344
+ ],
345
+ },
346
+ ];
347
+
348
+ const isOas2 = (parsed) => isObject(parsed) &&
349
+ 'swagger' in parsed &&
350
+ Number.parseInt(String(parsed.swagger)) === 2;
351
+ const isOas3 = (parsed) => isObject(parsed) &&
352
+ 'openapi' in parsed &&
353
+ Number.parseFloat(String(parsed.openapi)) >= 3;
354
+ const isOas31 = (parsed) => isObject(parsed) &&
355
+ 'openapi' in parsed &&
356
+ Number.parseFloat(String(parsed.openapi)) === 3.1;
357
+ const OAS_MODEL_REGEXP = /((definitions|components)\/?(schemas)?)\//;
358
+ function transformOasToServiceNode(apiDescriptionDocument) {
359
+ if (isOas31(apiDescriptionDocument)) {
360
+ return computeServiceNode(Object.assign(Object.assign({}, apiDescriptionDocument), { jsonSchemaDialect: 'http://json-schema.org/draft-07/schema#' }), oas3SourceMap, transformOas3Service, transformOas3Operation);
361
+ }
362
+ if (isOas3(apiDescriptionDocument)) {
363
+ return computeServiceNode(apiDescriptionDocument, oas3SourceMap, transformOas3Service, transformOas3Operation);
364
+ }
365
+ else if (isOas2(apiDescriptionDocument)) {
366
+ return computeServiceNode(apiDescriptionDocument, oas2SourceMap, transformOas2Service, transformOas2Operation);
367
+ }
368
+ return null;
369
+ }
370
+ function computeServiceNode(document, map, transformService, transformOperation) {
371
+ var _a;
372
+ const serviceDocument = transformService({ document });
373
+ const serviceNode = {
374
+ type: NodeType.HttpService,
375
+ uri: '/',
376
+ name: serviceDocument.name,
377
+ data: serviceDocument,
378
+ tags: ((_a = serviceDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
379
+ children: computeChildNodes(document, document, map, transformOperation),
380
+ };
381
+ return serviceNode;
382
+ }
383
+ function computeChildNodes(document, data, map, transformer, parentUri = '') {
384
+ var _a;
385
+ const nodes = [];
386
+ if (!isObject(data))
387
+ return nodes;
388
+ for (const key of Object.keys(data)) {
389
+ const sanitizedKey = encodePointerFragment(key);
390
+ const match = findMapMatch(sanitizedKey, map);
391
+ if (match) {
392
+ const uri = `${parentUri}/${sanitizedKey}`;
393
+ const jsonPath = pointerToPath(`#${uri}`);
394
+ if (match.type === NodeTypes.Operation && jsonPath.length === 3) {
395
+ const path = String(jsonPath[1]);
396
+ const method = String(jsonPath[2]);
397
+ const operationDocument = transformer({ document, path, method });
398
+ let parsedUri;
399
+ const encodedPath = String(encodePointerFragment(path));
400
+ if (operationDocument.iid) {
401
+ parsedUri = `/operations/${operationDocument.iid}`;
402
+ }
403
+ else {
404
+ parsedUri = uri.replace(encodedPath, slugify(path));
405
+ }
406
+ nodes.push({
407
+ type: NodeType.HttpOperation,
408
+ uri: parsedUri,
409
+ data: operationDocument,
410
+ name: operationDocument.summary || operationDocument.iid || operationDocument.path,
411
+ tags: ((_a = operationDocument.tags) === null || _a === void 0 ? void 0 : _a.map(tag => tag.name)) || [],
412
+ });
413
+ }
414
+ else if (match.type === NodeTypes.Model) {
415
+ const schemaDocument = get(document, jsonPath);
416
+ const parsedUri = uri.replace(OAS_MODEL_REGEXP, 'schemas/');
417
+ nodes.push({
418
+ type: NodeType.Model,
419
+ uri: parsedUri,
420
+ data: schemaDocument,
421
+ name: schemaDocument.title || last(uri.split('/')) || '',
422
+ tags: schemaDocument['x-tags'] || [],
423
+ });
424
+ }
425
+ if (match.children) {
426
+ nodes.push(...computeChildNodes(document, data[key], match.children, transformer, uri));
427
+ }
428
+ }
429
+ }
430
+ return nodes;
431
+ }
432
+ function findMapMatch(key, map) {
433
+ var _a;
434
+ if (typeof key === 'number')
435
+ return;
436
+ 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))) {
438
+ return entry;
439
+ }
440
+ }
441
+ }
442
+ function isJson(value) {
443
+ try {
444
+ JSON.parse(value);
445
+ }
446
+ catch (e) {
447
+ return false;
448
+ }
449
+ return true;
450
+ }
451
+
452
+ function useExportDocumentProps({ originalDocument, bundledDocument, }) {
453
+ const isJsonDocument = typeof originalDocument === 'object' || (!!originalDocument && isJson(originalDocument));
454
+ const exportDocument = React.useCallback((document) => {
455
+ const type = isJsonDocument ? 'json' : 'yaml';
456
+ const blob = new Blob([document], {
457
+ type: `application/${type}`,
458
+ });
459
+ saver.saveAs(blob, `document.${type}`);
460
+ }, [isJsonDocument]);
461
+ const exportOriginalDocument = React.useCallback(() => {
462
+ const stringifiedDocument = typeof originalDocument === 'object' ? JSON.stringify(originalDocument, null, 2) : originalDocument || '';
463
+ exportDocument(stringifiedDocument);
464
+ }, [originalDocument, exportDocument]);
465
+ const exportBundledDocument = React.useCallback(() => {
466
+ const stringifiedDocument = isJsonDocument
467
+ ? JSON.stringify(bundledDocument, null, 2)
468
+ : safeStringify(bundledDocument);
469
+ exportDocument(stringifiedDocument);
470
+ }, [bundledDocument, isJsonDocument, exportDocument]);
471
+ return {
472
+ original: {
473
+ onPress: exportOriginalDocument,
474
+ },
475
+ bundled: {
476
+ onPress: exportBundledDocument,
477
+ },
478
+ };
479
+ }
480
+
481
+ const propsAreWithDocument = (props) => {
482
+ return props.hasOwnProperty('apiDescriptionDocument');
483
+ };
484
+ const APIImpl = props => {
485
+ const { layout, apiDescriptionUrl = '', logo, hideTryIt, hideSchemas, hideInternal, hideExport, tryItCredentialsPolicy, tryItCorsProxy, tryItOutDefaultServer, } = props;
486
+ const apiDescriptionDocument = propsAreWithDocument(props) ? props.apiDescriptionDocument : undefined;
487
+ const { data: fetchedDocument, error } = useQuery([apiDescriptionUrl], () => fetch(apiDescriptionUrl).then(res => {
488
+ if (res.ok) {
489
+ return res.text();
490
+ }
491
+ throw new Error(`Unable to load description document, status code: ${res.status}`);
492
+ }), {
493
+ enabled: apiDescriptionUrl !== '' && !apiDescriptionDocument,
494
+ });
495
+ const document = apiDescriptionDocument || fetchedDocument || '';
496
+ const parsedDocument = useParsedValue(document);
497
+ const bundledDocument = useBundleRefsIntoDocument(parsedDocument, { baseUrl: apiDescriptionUrl });
498
+ const serviceNode = React.useMemo(() => transformOasToServiceNode(bundledDocument), [bundledDocument]);
499
+ const exportProps = useExportDocumentProps({ originalDocument: document, bundledDocument });
500
+ if (error) {
501
+ return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
502
+ React.createElement(NonIdealState, { title: "Document could not be loaded", description: "The API description document could not be fetched. This could indicate connectivity problems, or issues with the server hosting the spec.", icon: "exclamation-triangle" })));
503
+ }
504
+ if (!bundledDocument) {
505
+ return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen", color: "light" },
506
+ React.createElement(Box, { as: Icon, icon: ['fal', 'circle-notch'], size: "3x", spin: true })));
507
+ }
508
+ if (!serviceNode) {
509
+ return (React.createElement(Flex, { justify: "center", alignItems: "center", w: "full", minH: "screen" },
510
+ React.createElement(NonIdealState, { title: "Failed to parse OpenAPI file", description: "Please make sure your OpenAPI file is valid and try again" })));
511
+ }
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 }))));
513
+ };
514
+ const API = flow(withRouter, withStyles, withPersistenceBoundary, withMosaicProvider, withQueryClientProvider)(APIImpl);
515
+
516
+ export { API };
package/package.json CHANGED
@@ -1,67 +1,32 @@
1
1
  {
2
2
  "name": "@jpmorganchase/elemental",
3
- "version": "1.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "UI components for composing beautiful developer documentation.",
5
5
  "keywords": [],
6
+ "main": "./index.js",
6
7
  "sideEffects": [
7
8
  "web-components.min.js",
8
9
  "src/web-components/**",
9
10
  "**/*.css"
10
11
  ],
11
- "homepage": "https://github.com/jpmorganchase/elemental",
12
+ "files": [
13
+ "**/*"
14
+ ],
12
15
  "repository": {
13
16
  "type": "git",
14
17
  "url": "git@github.com:jpmorganchase/elemental.git",
15
18
  "directory": "packages/elements"
16
19
  },
17
20
  "license": "Apache-2.0",
18
- "type": "commonjs",
19
- "main": "./dist/index.js",
20
- "module": "./dist/index.esm.js",
21
- "exports": {
22
- "./styles.min.css": "./styles.min.css",
23
- "./web-components.min.js": "./web-components.min.js",
24
- ".": {
25
- "require": "./dist/index.js",
26
- "import": "./dist/index.mjs"
27
- }
28
- },
29
- "files": [
30
- "**/*"
31
- ],
32
21
  "engines": {
33
22
  "node": ">=14.13"
34
23
  },
35
- "scripts": {
36
- "build": "concurrently \"yarn:build.react\" \"yarn:build.webcomponents\"",
37
- "postbuild": "yarn build.styles",
38
- "build.react": "sl-scripts bundle",
39
- "build.styles": "postcss src/styles.css -o dist/styles.min.css",
40
- "build.webcomponents": "webpack -c ./web-components.config.js",
41
- "build.docs": "build-storybook -c .storybook -o dist-storybook",
42
- "commit": "git-cz",
43
- "release": "sl-scripts release",
44
- "release.docs": "sl-scripts release:docs",
45
- "release.dryRun": "sl-scripts release --dry-run --debug",
46
- "storybook": "start-storybook -p 9001",
47
- "test": "jest",
48
- "test.prod": "yarn test --coverage --maxWorkers=2",
49
- "test.update": "yarn test --updateSnapshot",
50
- "test.watch": "yarn test --watch",
51
- "test.packaging": "node --input-type=commonjs -e \"require('./dist/index.js')\" && node --input-type=module -e \"import './dist/index.mjs'\"",
52
- "type-check": "tsc --noEmit"
53
- },
54
24
  "peerDependencies": {
55
25
  "react": ">=16.8",
56
26
  "react-dom": ">=16.8"
57
27
  },
58
- "rollup": {
59
- "bundleDeps": [
60
- "@fortawesome/free-solid-svg-icons"
61
- ]
62
- },
63
28
  "dependencies": {
64
- "@stoplight/elements-core": "~7.7.18",
29
+ "@jpmorganchase/elemental-core": "^1.1.1",
65
30
  "@stoplight/http-spec": "^5.1.4",
66
31
  "@stoplight/json": "^3.18.1",
67
32
  "@stoplight/mosaic": "^1.33.0",
@@ -73,39 +38,15 @@
73
38
  "react-query": "^3.34.19",
74
39
  "react-router-dom": "^5.2.0"
75
40
  },
76
- "devDependencies": {
77
- "@fortawesome/free-solid-svg-icons": "^6.1.1",
78
- "@stoplight/scripts": "9.2.1",
79
- "@testing-library/dom": "^7.26.5",
80
- "@testing-library/jest-dom": "^5.16.4",
81
- "@testing-library/react": "^11.1.1",
82
- "@testing-library/react-hooks": "^3.4.2",
83
- "@testing-library/user-event": "^12.2.0",
84
- "@types/enzyme": "3.x.x",
85
- "@types/enzyme-adapter-react-16": "1.x.x",
86
- "@types/file-saver": "^2.0.5",
87
- "@types/json-schema": "^7.0.11",
88
- "@types/lodash": "^4.14.181",
89
- "@types/react": "16.9.56",
90
- "@types/react-dom": "16.9.12",
91
- "@types/react-router-dom": "^5.3.3",
92
- "enzyme": "3.x.x",
93
- "enzyme-adapter-react-16": "1.x.x",
94
- "enzyme-to-json": "3.x.x",
95
- "jest-enzyme": "^7.1.2",
96
- "jest-fetch-mock": "^3.0.3",
97
- "react": "16.14.0",
98
- "react-dom": "16.14.0",
99
- "react-test-renderer": "^16.8.0",
100
- "resolve-url-loader": "^3.1.2",
101
- "style-loader": "^2.0.0",
102
- "ts-loader": "^9.2.8"
103
- },
104
- "publishConfig": {
105
- "directory": "dist",
106
- "access": "public"
41
+ "type": "commonjs",
42
+ "exports": {
43
+ "./styles.min.css": "./styles.min.css",
44
+ "./web-components.min.js": "./web-components.min.js",
45
+ ".": {
46
+ "require": "./index.js",
47
+ "import": "./index.mjs"
48
+ }
107
49
  },
108
- "release": {
109
- "extends": "@stoplight/scripts/release"
110
- }
111
- }
50
+ "typings": "index.d.ts",
51
+ "module": "./index.esm.js"
52
+ }