@stoplight/elements-core 7.13.7 → 7.14.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.
@@ -0,0 +1,3 @@
1
+ import { IHttpOperation } from '@stoplight/types';
2
+ export declare const httpOperation: IHttpOperation;
3
+ export default httpOperation;
@@ -1,7 +1,8 @@
1
+ import { Choice } from '@stoplight/json-schema-viewer';
1
2
  import { IMediaTypeContent } from '@stoplight/types';
2
3
  import * as React from 'react';
3
4
  import { BodyParameterValues, ParameterOptional } from './request-body-utils';
4
- interface FormDataBodyProps {
5
+ export interface FormDataBodyProps {
5
6
  specification: IMediaTypeContent;
6
7
  values: BodyParameterValues;
7
8
  onChangeValues: (newValues: BodyParameterValues) => void;
@@ -9,4 +10,9 @@ interface FormDataBodyProps {
9
10
  isAllowedEmptyValues: ParameterOptional;
10
11
  }
11
12
  export declare const FormDataBody: React.FC<FormDataBodyProps>;
12
- export {};
13
+ export interface OneOfMenuProps {
14
+ choices: Choice[];
15
+ choice: Choice;
16
+ onChange: (choice: Choice) => void;
17
+ }
18
+ export declare function OneOfMenu({ choices: subSchemas, choice, onChange }: OneOfMenuProps): JSX.Element | null;
@@ -1,5 +1,6 @@
1
+ import { RegularNode, SchemaNode } from '@stoplight/json-schema-tree';
1
2
  import type { IHttpParam, INodeExample, INodeExternalExample } from '@stoplight/types';
2
- import { JSONSchema7Definition } from 'json-schema';
3
+ import { JSONSchema7, JSONSchema7Definition } from 'json-schema';
3
4
  export declare type ParameterSpec = Pick<IHttpParam, 'name' | 'schema' | 'required'> & {
4
5
  examples?: (Omit<INodeExample, 'id'> | Omit<INodeExternalExample, 'id'>)[];
5
6
  };
@@ -17,7 +18,7 @@ export declare function exampleOptions(parameter: ParameterSpec): {
17
18
  value: string;
18
19
  label: string;
19
20
  }[] | null;
20
- export declare function parameterSupportsFileUpload(parameter: Pick<ParameterSpec, 'schema'>): boolean;
21
+ export declare function parameterSupportsFileUpload(parameter?: Pick<ParameterSpec, 'schema'>): boolean | undefined;
21
22
  export declare function getPlaceholderForParameter(parameter: ParameterSpec): string;
22
23
  export declare const initialParameterValues: (params: readonly ParameterSpec[]) => Record<string, string>;
23
24
  export declare function mapSchemaPropertiesToParameters(properties: {
@@ -25,9 +26,11 @@ export declare function mapSchemaPropertiesToParameters(properties: {
25
26
  }, required: string[] | undefined): {
26
27
  required?: boolean | undefined;
27
28
  name: string;
28
- schema: import("json-schema").JSONSchema7 | undefined;
29
+ schema: JSONSchema7 | undefined;
29
30
  examples: {
30
31
  key: string;
31
32
  value: any;
32
33
  }[] | undefined;
33
34
  }[];
35
+ export declare function toParameterSpec(jsonTreeNode: RegularNode): ParameterSpec;
36
+ export declare function isRequired(n: SchemaNode): boolean | undefined;
@@ -6,6 +6,7 @@ export declare const SimpleGET: Story<TryItProps>;
6
6
  export declare const WithParameters: Story<TryItProps>;
7
7
  export declare const WithVariables: Story<TryItProps>;
8
8
  export declare const UrlEncoded: Story<TryItProps>;
9
+ export declare const UrlEncodedOneOf: Story<TryItProps>;
9
10
  export declare const Multipart: Story<TryItProps>;
10
11
  export declare const RequestBodySchema: Story<TryItProps>;
11
12
  export declare const RequestBodyExamples: Story<TryItProps>;
package/index.esm.js CHANGED
@@ -25,8 +25,11 @@ import filter from 'lodash/filter.js';
25
25
  import flatten from 'lodash/flatten.js';
26
26
  import { nanoid } from 'nanoid';
27
27
  import curry from 'lodash/curry.js';
28
+ import { isRegularNode, SchemaTree } from '@stoplight/json-schema-tree';
29
+ import { useChoices, visibleChildren, JsonSchemaViewer } from '@stoplight/json-schema-viewer';
28
30
  import omit from 'lodash/omit.js';
29
31
  import keyBy from 'lodash/keyBy.js';
32
+ import last from 'lodash/last.js';
30
33
  import map from 'lodash/map.js';
31
34
  import mapValues from 'lodash/mapValues.js';
32
35
  import isString from 'lodash/isString.js';
@@ -40,7 +43,6 @@ import uniqBy from 'lodash/uniqBy.js';
40
43
  import formatXml from 'xml-formatter';
41
44
  import entries from 'lodash/entries.js';
42
45
  import keys from 'lodash/keys.js';
43
- import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
44
46
  import sortBy from 'lodash/sortBy.js';
45
47
  import isEmpty from 'lodash/isEmpty.js';
46
48
  import isNil from 'lodash/isNil.js';
@@ -495,21 +497,6 @@ function createNamedContext(name, defaultValue) {
495
497
  return context;
496
498
  }
497
499
 
498
- const ALPHANUMERIC = /[^A-Za-z0-9]+$/;
499
- function useChosenServerUrl(chosenServerUrl) {
500
- const match = ALPHANUMERIC.exec(chosenServerUrl);
501
- if (match === null) {
502
- return {
503
- leading: chosenServerUrl,
504
- trailing: null,
505
- };
506
- }
507
- return {
508
- leading: chosenServerUrl.substring(0, match.index),
509
- trailing: chosenServerUrl.substring(match.index),
510
- };
511
- }
512
-
513
500
  const getBreakpoints = (compact) => {
514
501
  if (!compact)
515
502
  return undefined;
@@ -1191,7 +1178,9 @@ function exampleOptions(parameter) {
1191
1178
  }
1192
1179
  function parameterSupportsFileUpload(parameter) {
1193
1180
  var _a, _b, _c;
1194
- return (((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1181
+ return (parameter &&
1182
+ parameter.schema &&
1183
+ ((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1195
1184
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1196
1185
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1197
1186
  }
@@ -1247,6 +1236,35 @@ function mapSchemaPropertiesToParameters(properties, required) {
1247
1236
  return Object.entries(properties).map(([name, schema]) => (Object.assign({ name, schema: typeof schema !== 'boolean' ? schema : undefined, examples: typeof schema !== 'boolean' && schema.examples && schema.examples[0]
1248
1237
  ? [{ key: 'example', value: schema.examples[0] }]
1249
1238
  : undefined }, ((required === null || required === void 0 ? void 0 : required.includes(name)) && { required: true }))));
1239
+ }
1240
+ function toParameterSpec(jsonTreeNode) {
1241
+ var _a;
1242
+ const isBoolean = jsonTreeNode.primaryType === 'boolean';
1243
+ const schema = !isBoolean ? jsonTreeNode.fragment : undefined;
1244
+ const examples = !isBoolean && jsonTreeNode.fragment.examples && jsonTreeNode.fragment.examples[0]
1245
+ ? [{ key: 'example', value: jsonTreeNode.fragment.examples[0] }]
1246
+ : undefined;
1247
+ const lastJsonPathSegment = (_a = last(jsonTreeNode.path)) !== null && _a !== void 0 ? _a : '<<UNKNOWN>>';
1248
+ return {
1249
+ name: lastJsonPathSegment,
1250
+ schema,
1251
+ examples,
1252
+ required: isRequired(jsonTreeNode),
1253
+ };
1254
+ }
1255
+ function isRequired(n) {
1256
+ if (!isRegularNode(n)) {
1257
+ return undefined;
1258
+ }
1259
+ const name = last(n.path);
1260
+ if (name === undefined) {
1261
+ return undefined;
1262
+ }
1263
+ const parent = n.parent;
1264
+ if (parent === null || !isRegularNode(parent)) {
1265
+ return undefined;
1266
+ }
1267
+ return parent.required !== null && parent.required.includes(name);
1250
1268
  }
1251
1269
 
1252
1270
  const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptional, canChangeOptional, validate, }) => {
@@ -1276,31 +1294,53 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1276
1294
  };
1277
1295
 
1278
1296
  const FormDataBody = ({ specification, values, onChangeValues, onChangeParameterAllow, isAllowedEmptyValues, }) => {
1279
- const schema = specification.schema;
1280
- const parameters = schema === null || schema === void 0 ? void 0 : schema.properties;
1281
- const required = schema === null || schema === void 0 ? void 0 : schema.required;
1282
- React.useEffect(() => {
1283
- if (parameters === undefined) {
1284
- console.warn(`Invalid schema in form data spec: ${safeStringify(schema)}`);
1285
- }
1286
- }, [parameters, schema]);
1287
- if (parameters === undefined) {
1288
- return null;
1289
- }
1297
+ const schema = React.useMemo(() => {
1298
+ var _a;
1299
+ const schema = (_a = specification.schema) !== null && _a !== void 0 ? _a : {};
1300
+ const tree = new SchemaTree(schema, { mergeAllOf: true, refResolver: null });
1301
+ tree.populate();
1302
+ return tree.root.children[0];
1303
+ }, [specification]);
1304
+ const { selectedChoice, choices, setSelectedChoice } = useChoices(schema);
1305
+ const formFieldRows = visibleChildren(selectedChoice.type);
1306
+ const onSchemaChange = (choice) => {
1307
+ onChangeValues({});
1308
+ setSelectedChoice(choice);
1309
+ };
1290
1310
  return (React.createElement(Panel, { defaultIsOpen: true },
1291
- React.createElement(Panel.Titlebar, null, "Body"),
1292
- React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, mapSchemaPropertiesToParameters(parameters, required).map(parameter => {
1293
- var _a;
1311
+ React.createElement(Panel.Titlebar, { rightComponent: React.createElement(OneOfMenu, { choices: choices, choice: selectedChoice, onChange: onSchemaChange }) }, "Body"),
1312
+ React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, formFieldRows
1313
+ .filter(isRegularNode)
1314
+ .map(toParameterSpec)
1315
+ .map(parameter => {
1316
+ var _a, _b;
1294
1317
  const supportsFileUpload = parameterSupportsFileUpload(parameter);
1295
- const value = values[parameter.name];
1318
+ const value = values[(_a = parameter.name) !== null && _a !== void 0 ? _a : ''];
1296
1319
  if (supportsFileUpload) {
1297
1320
  return (React.createElement(FileUploadParameterEditor, { key: parameter.name, parameter: parameter, value: value instanceof File ? value : undefined, onChange: newValue => newValue
1298
1321
  ? onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: newValue }))
1299
1322
  : onChangeValues(omit(values, parameter.name)) }));
1300
1323
  }
1301
- return (React.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_a = isAllowedEmptyValues[parameter.name]) !== null && _a !== void 0 ? _a : false }));
1324
+ return (React.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_b = isAllowedEmptyValues[parameter.name]) !== null && _b !== void 0 ? _b : false }));
1302
1325
  }))));
1303
- };
1326
+ };
1327
+ function OneOfMenu({ choices: subSchemas, choice, onChange }) {
1328
+ var _a;
1329
+ const onSubSchemaSelect = React.useCallback(onChange, [onChange]);
1330
+ const menuItems = React.useMemo(() => subSchemas.map(subSchema => {
1331
+ const label = subSchema.title;
1332
+ return {
1333
+ id: `request-subschema-${label}`,
1334
+ title: label,
1335
+ onPress: () => onSubSchemaSelect(subSchema),
1336
+ };
1337
+ }), [subSchemas, onSubSchemaSelect]);
1338
+ if (!subSchemas || subSchemas.length < 2) {
1339
+ return null;
1340
+ }
1341
+ const title = (_a = choice === null || choice === void 0 ? void 0 : choice.title) !== null && _a !== void 0 ? _a : 'Variants';
1342
+ return (React.createElement(Menu, { "aria-label": title, items: menuItems, renderTrigger: ({ isOpen }) => (React.createElement(Button, { appearance: "minimal", size: "sm", iconRight: ['fas', 'sort'], active: isOpen, "data-testid": "oneof-menu" }, title)) }));
1343
+ }
1304
1344
 
1305
1345
  const fileToBase64 = (file) => new Promise((resolve, reject) => {
1306
1346
  const reader = new FileReader();
@@ -2662,11 +2702,9 @@ function MethodPath({ method, path }) {
2662
2702
  function MethodPathInner({ method, path, chosenServerUrl }) {
2663
2703
  const isDark = useThemeIsDark();
2664
2704
  const fullUrl = `${chosenServerUrl}${path}`;
2665
- const { leading, trailing } = useChosenServerUrl(chosenServerUrl);
2666
2705
  const pathElem = (React.createElement(Flex, { overflowX: "hidden", fontSize: "lg", userSelect: "all" },
2667
2706
  React.createElement(Box, { dir: "rtl", color: "muted", textOverflow: "truncate", overflowX: "hidden" },
2668
- leading,
2669
- trailing !== null && (React.createElement(Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, trailing))),
2707
+ React.createElement(Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, chosenServerUrl)),
2670
2708
  React.createElement(Box, { fontWeight: "semibold", flex: 1 }, path)));
2671
2709
  return (React.createElement(HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2672
2710
  React.createElement(Box, { py: 1, px: 2.5, rounded: "lg", bg: !isDark ? HttpMethodColors[method] : 'canvas-100', color: !isDark ? 'on-primary' : 'body', fontSize: "lg", fontWeight: "semibold", textTransform: "uppercase" }, method),
package/index.js CHANGED
@@ -27,8 +27,11 @@ var filter = require('lodash/filter.js');
27
27
  var flatten = require('lodash/flatten.js');
28
28
  var nanoid = require('nanoid');
29
29
  var curry = require('lodash/curry.js');
30
+ var jsonSchemaTree = require('@stoplight/json-schema-tree');
31
+ var jsonSchemaViewer = require('@stoplight/json-schema-viewer');
30
32
  var omit = require('lodash/omit.js');
31
33
  var keyBy = require('lodash/keyBy.js');
34
+ var last = require('lodash/last.js');
32
35
  var map = require('lodash/map.js');
33
36
  var mapValues = require('lodash/mapValues.js');
34
37
  var isString = require('lodash/isString.js');
@@ -42,7 +45,6 @@ var uniqBy = require('lodash/uniqBy.js');
42
45
  var formatXml = require('xml-formatter');
43
46
  var entries = require('lodash/entries.js');
44
47
  var keys = require('lodash/keys.js');
45
- var jsonSchemaViewer = require('@stoplight/json-schema-viewer');
46
48
  var sortBy = require('lodash/sortBy.js');
47
49
  var isEmpty = require('lodash/isEmpty.js');
48
50
  var isNil = require('lodash/isNil.js');
@@ -87,6 +89,7 @@ var flatten__default = /*#__PURE__*/_interopDefaultLegacy(flatten);
87
89
  var curry__default = /*#__PURE__*/_interopDefaultLegacy(curry);
88
90
  var omit__default = /*#__PURE__*/_interopDefaultLegacy(omit);
89
91
  var keyBy__default = /*#__PURE__*/_interopDefaultLegacy(keyBy);
92
+ var last__default = /*#__PURE__*/_interopDefaultLegacy(last);
90
93
  var map__default = /*#__PURE__*/_interopDefaultLegacy(map);
91
94
  var mapValues__default = /*#__PURE__*/_interopDefaultLegacy(mapValues);
92
95
  var isString__default = /*#__PURE__*/_interopDefaultLegacy(isString);
@@ -551,21 +554,6 @@ function createNamedContext(name, defaultValue) {
551
554
  return context;
552
555
  }
553
556
 
554
- const ALPHANUMERIC = /[^A-Za-z0-9]+$/;
555
- function useChosenServerUrl(chosenServerUrl) {
556
- const match = ALPHANUMERIC.exec(chosenServerUrl);
557
- if (match === null) {
558
- return {
559
- leading: chosenServerUrl,
560
- trailing: null,
561
- };
562
- }
563
- return {
564
- leading: chosenServerUrl.substring(0, match.index),
565
- trailing: chosenServerUrl.substring(match.index),
566
- };
567
- }
568
-
569
557
  const getBreakpoints = (compact) => {
570
558
  if (!compact)
571
559
  return undefined;
@@ -1247,7 +1235,9 @@ function exampleOptions(parameter) {
1247
1235
  }
1248
1236
  function parameterSupportsFileUpload(parameter) {
1249
1237
  var _a, _b, _c;
1250
- return (((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1238
+ return (parameter &&
1239
+ parameter.schema &&
1240
+ ((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1251
1241
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1252
1242
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1253
1243
  }
@@ -1303,6 +1293,35 @@ function mapSchemaPropertiesToParameters(properties, required) {
1303
1293
  return Object.entries(properties).map(([name, schema]) => (Object.assign({ name, schema: typeof schema !== 'boolean' ? schema : undefined, examples: typeof schema !== 'boolean' && schema.examples && schema.examples[0]
1304
1294
  ? [{ key: 'example', value: schema.examples[0] }]
1305
1295
  : undefined }, ((required === null || required === void 0 ? void 0 : required.includes(name)) && { required: true }))));
1296
+ }
1297
+ function toParameterSpec(jsonTreeNode) {
1298
+ var _a;
1299
+ const isBoolean = jsonTreeNode.primaryType === 'boolean';
1300
+ const schema = !isBoolean ? jsonTreeNode.fragment : undefined;
1301
+ const examples = !isBoolean && jsonTreeNode.fragment.examples && jsonTreeNode.fragment.examples[0]
1302
+ ? [{ key: 'example', value: jsonTreeNode.fragment.examples[0] }]
1303
+ : undefined;
1304
+ const lastJsonPathSegment = (_a = last__default["default"](jsonTreeNode.path)) !== null && _a !== void 0 ? _a : '<<UNKNOWN>>';
1305
+ return {
1306
+ name: lastJsonPathSegment,
1307
+ schema,
1308
+ examples,
1309
+ required: isRequired(jsonTreeNode),
1310
+ };
1311
+ }
1312
+ function isRequired(n) {
1313
+ if (!jsonSchemaTree.isRegularNode(n)) {
1314
+ return undefined;
1315
+ }
1316
+ const name = last__default["default"](n.path);
1317
+ if (name === undefined) {
1318
+ return undefined;
1319
+ }
1320
+ const parent = n.parent;
1321
+ if (parent === null || !jsonSchemaTree.isRegularNode(parent)) {
1322
+ return undefined;
1323
+ }
1324
+ return parent.required !== null && parent.required.includes(name);
1306
1325
  }
1307
1326
 
1308
1327
  const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptional, canChangeOptional, validate, }) => {
@@ -1332,31 +1351,53 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1332
1351
  };
1333
1352
 
1334
1353
  const FormDataBody = ({ specification, values, onChangeValues, onChangeParameterAllow, isAllowedEmptyValues, }) => {
1335
- const schema = specification.schema;
1336
- const parameters = schema === null || schema === void 0 ? void 0 : schema.properties;
1337
- const required = schema === null || schema === void 0 ? void 0 : schema.required;
1338
- React__namespace.useEffect(() => {
1339
- if (parameters === undefined) {
1340
- console.warn(`Invalid schema in form data spec: ${json.safeStringify(schema)}`);
1341
- }
1342
- }, [parameters, schema]);
1343
- if (parameters === undefined) {
1344
- return null;
1345
- }
1354
+ const schema = React__namespace.useMemo(() => {
1355
+ var _a;
1356
+ const schema = (_a = specification.schema) !== null && _a !== void 0 ? _a : {};
1357
+ const tree = new jsonSchemaTree.SchemaTree(schema, { mergeAllOf: true, refResolver: null });
1358
+ tree.populate();
1359
+ return tree.root.children[0];
1360
+ }, [specification]);
1361
+ const { selectedChoice, choices, setSelectedChoice } = jsonSchemaViewer.useChoices(schema);
1362
+ const formFieldRows = jsonSchemaViewer.visibleChildren(selectedChoice.type);
1363
+ const onSchemaChange = (choice) => {
1364
+ onChangeValues({});
1365
+ setSelectedChoice(choice);
1366
+ };
1346
1367
  return (React__namespace.createElement(mosaic.Panel, { defaultIsOpen: true },
1347
- React__namespace.createElement(mosaic.Panel.Titlebar, null, "Body"),
1348
- React__namespace.createElement(mosaic.Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, mapSchemaPropertiesToParameters(parameters, required).map(parameter => {
1349
- var _a;
1368
+ React__namespace.createElement(mosaic.Panel.Titlebar, { rightComponent: React__namespace.createElement(OneOfMenu, { choices: choices, choice: selectedChoice, onChange: onSchemaChange }) }, "Body"),
1369
+ React__namespace.createElement(mosaic.Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, formFieldRows
1370
+ .filter(jsonSchemaTree.isRegularNode)
1371
+ .map(toParameterSpec)
1372
+ .map(parameter => {
1373
+ var _a, _b;
1350
1374
  const supportsFileUpload = parameterSupportsFileUpload(parameter);
1351
- const value = values[parameter.name];
1375
+ const value = values[(_a = parameter.name) !== null && _a !== void 0 ? _a : ''];
1352
1376
  if (supportsFileUpload) {
1353
1377
  return (React__namespace.createElement(FileUploadParameterEditor, { key: parameter.name, parameter: parameter, value: value instanceof File ? value : undefined, onChange: newValue => newValue
1354
1378
  ? onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: newValue }))
1355
1379
  : onChangeValues(omit__default["default"](values, parameter.name)) }));
1356
1380
  }
1357
- return (React__namespace.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_a = isAllowedEmptyValues[parameter.name]) !== null && _a !== void 0 ? _a : false }));
1381
+ return (React__namespace.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_b = isAllowedEmptyValues[parameter.name]) !== null && _b !== void 0 ? _b : false }));
1358
1382
  }))));
1359
- };
1383
+ };
1384
+ function OneOfMenu({ choices: subSchemas, choice, onChange }) {
1385
+ var _a;
1386
+ const onSubSchemaSelect = React__namespace.useCallback(onChange, [onChange]);
1387
+ const menuItems = React__namespace.useMemo(() => subSchemas.map(subSchema => {
1388
+ const label = subSchema.title;
1389
+ return {
1390
+ id: `request-subschema-${label}`,
1391
+ title: label,
1392
+ onPress: () => onSubSchemaSelect(subSchema),
1393
+ };
1394
+ }), [subSchemas, onSubSchemaSelect]);
1395
+ if (!subSchemas || subSchemas.length < 2) {
1396
+ return null;
1397
+ }
1398
+ const title = (_a = choice === null || choice === void 0 ? void 0 : choice.title) !== null && _a !== void 0 ? _a : 'Variants';
1399
+ return (React__namespace.createElement(mosaic.Menu, { "aria-label": title, items: menuItems, renderTrigger: ({ isOpen }) => (React__namespace.createElement(mosaic.Button, { appearance: "minimal", size: "sm", iconRight: ['fas', 'sort'], active: isOpen, "data-testid": "oneof-menu" }, title)) }));
1400
+ }
1360
1401
 
1361
1402
  const fileToBase64 = (file) => new Promise((resolve, reject) => {
1362
1403
  const reader = new FileReader();
@@ -2718,11 +2759,9 @@ function MethodPath({ method, path }) {
2718
2759
  function MethodPathInner({ method, path, chosenServerUrl }) {
2719
2760
  const isDark = mosaic.useThemeIsDark();
2720
2761
  const fullUrl = `${chosenServerUrl}${path}`;
2721
- const { leading, trailing } = useChosenServerUrl(chosenServerUrl);
2722
2762
  const pathElem = (React__namespace.createElement(mosaic.Flex, { overflowX: "hidden", fontSize: "lg", userSelect: "all" },
2723
2763
  React__namespace.createElement(mosaic.Box, { dir: "rtl", color: "muted", textOverflow: "truncate", overflowX: "hidden" },
2724
- leading,
2725
- trailing !== null && (React__namespace.createElement(mosaic.Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, trailing))),
2764
+ React__namespace.createElement(mosaic.Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, chosenServerUrl)),
2726
2765
  React__namespace.createElement(mosaic.Box, { fontWeight: "semibold", flex: 1 }, path)));
2727
2766
  return (React__namespace.createElement(mosaic.HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2728
2767
  React__namespace.createElement(mosaic.Box, { py: 1, px: 2.5, rounded: "lg", bg: !isDark ? HttpMethodColors[method] : 'canvas-100', color: !isDark ? 'on-primary' : 'body', fontSize: "lg", fontWeight: "semibold", textTransform: "uppercase" }, method),
package/index.mjs CHANGED
@@ -25,8 +25,11 @@ import filter from 'lodash/filter.js';
25
25
  import flatten from 'lodash/flatten.js';
26
26
  import { nanoid } from 'nanoid';
27
27
  import curry from 'lodash/curry.js';
28
+ import { isRegularNode, SchemaTree } from '@stoplight/json-schema-tree';
29
+ import { useChoices, visibleChildren, JsonSchemaViewer } from '@stoplight/json-schema-viewer';
28
30
  import omit from 'lodash/omit.js';
29
31
  import keyBy from 'lodash/keyBy.js';
32
+ import last from 'lodash/last.js';
30
33
  import map from 'lodash/map.js';
31
34
  import mapValues from 'lodash/mapValues.js';
32
35
  import isString from 'lodash/isString.js';
@@ -40,7 +43,6 @@ import uniqBy from 'lodash/uniqBy.js';
40
43
  import formatXml from 'xml-formatter';
41
44
  import entries from 'lodash/entries.js';
42
45
  import keys from 'lodash/keys.js';
43
- import { JsonSchemaViewer } from '@stoplight/json-schema-viewer';
44
46
  import sortBy from 'lodash/sortBy.js';
45
47
  import isEmpty from 'lodash/isEmpty.js';
46
48
  import isNil from 'lodash/isNil.js';
@@ -495,21 +497,6 @@ function createNamedContext(name, defaultValue) {
495
497
  return context;
496
498
  }
497
499
 
498
- const ALPHANUMERIC = /[^A-Za-z0-9]+$/;
499
- function useChosenServerUrl(chosenServerUrl) {
500
- const match = ALPHANUMERIC.exec(chosenServerUrl);
501
- if (match === null) {
502
- return {
503
- leading: chosenServerUrl,
504
- trailing: null,
505
- };
506
- }
507
- return {
508
- leading: chosenServerUrl.substring(0, match.index),
509
- trailing: chosenServerUrl.substring(match.index),
510
- };
511
- }
512
-
513
500
  const getBreakpoints = (compact) => {
514
501
  if (!compact)
515
502
  return undefined;
@@ -1191,7 +1178,9 @@ function exampleOptions(parameter) {
1191
1178
  }
1192
1179
  function parameterSupportsFileUpload(parameter) {
1193
1180
  var _a, _b, _c;
1194
- return (((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1181
+ return (parameter &&
1182
+ parameter.schema &&
1183
+ ((_a = parameter.schema) === null || _a === void 0 ? void 0 : _a.type) === 'string' &&
1195
1184
  (((_b = parameter.schema) === null || _b === void 0 ? void 0 : _b.contentEncoding) === 'base64' ||
1196
1185
  ((_c = parameter.schema) === null || _c === void 0 ? void 0 : _c.contentMediaType) === 'application/octet-stream'));
1197
1186
  }
@@ -1247,6 +1236,35 @@ function mapSchemaPropertiesToParameters(properties, required) {
1247
1236
  return Object.entries(properties).map(([name, schema]) => (Object.assign({ name, schema: typeof schema !== 'boolean' ? schema : undefined, examples: typeof schema !== 'boolean' && schema.examples && schema.examples[0]
1248
1237
  ? [{ key: 'example', value: schema.examples[0] }]
1249
1238
  : undefined }, ((required === null || required === void 0 ? void 0 : required.includes(name)) && { required: true }))));
1239
+ }
1240
+ function toParameterSpec(jsonTreeNode) {
1241
+ var _a;
1242
+ const isBoolean = jsonTreeNode.primaryType === 'boolean';
1243
+ const schema = !isBoolean ? jsonTreeNode.fragment : undefined;
1244
+ const examples = !isBoolean && jsonTreeNode.fragment.examples && jsonTreeNode.fragment.examples[0]
1245
+ ? [{ key: 'example', value: jsonTreeNode.fragment.examples[0] }]
1246
+ : undefined;
1247
+ const lastJsonPathSegment = (_a = last(jsonTreeNode.path)) !== null && _a !== void 0 ? _a : '<<UNKNOWN>>';
1248
+ return {
1249
+ name: lastJsonPathSegment,
1250
+ schema,
1251
+ examples,
1252
+ required: isRequired(jsonTreeNode),
1253
+ };
1254
+ }
1255
+ function isRequired(n) {
1256
+ if (!isRegularNode(n)) {
1257
+ return undefined;
1258
+ }
1259
+ const name = last(n.path);
1260
+ if (name === undefined) {
1261
+ return undefined;
1262
+ }
1263
+ const parent = n.parent;
1264
+ if (parent === null || !isRegularNode(parent)) {
1265
+ return undefined;
1266
+ }
1267
+ return parent.required !== null && parent.required.includes(name);
1250
1268
  }
1251
1269
 
1252
1270
  const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptional, canChangeOptional, validate, }) => {
@@ -1276,31 +1294,53 @@ const ParameterEditor = ({ parameter, value, onChange, isOptional, onChangeOptio
1276
1294
  };
1277
1295
 
1278
1296
  const FormDataBody = ({ specification, values, onChangeValues, onChangeParameterAllow, isAllowedEmptyValues, }) => {
1279
- const schema = specification.schema;
1280
- const parameters = schema === null || schema === void 0 ? void 0 : schema.properties;
1281
- const required = schema === null || schema === void 0 ? void 0 : schema.required;
1282
- React.useEffect(() => {
1283
- if (parameters === undefined) {
1284
- console.warn(`Invalid schema in form data spec: ${safeStringify(schema)}`);
1285
- }
1286
- }, [parameters, schema]);
1287
- if (parameters === undefined) {
1288
- return null;
1289
- }
1297
+ const schema = React.useMemo(() => {
1298
+ var _a;
1299
+ const schema = (_a = specification.schema) !== null && _a !== void 0 ? _a : {};
1300
+ const tree = new SchemaTree(schema, { mergeAllOf: true, refResolver: null });
1301
+ tree.populate();
1302
+ return tree.root.children[0];
1303
+ }, [specification]);
1304
+ const { selectedChoice, choices, setSelectedChoice } = useChoices(schema);
1305
+ const formFieldRows = visibleChildren(selectedChoice.type);
1306
+ const onSchemaChange = (choice) => {
1307
+ onChangeValues({});
1308
+ setSelectedChoice(choice);
1309
+ };
1290
1310
  return (React.createElement(Panel, { defaultIsOpen: true },
1291
- React.createElement(Panel.Titlebar, null, "Body"),
1292
- React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, mapSchemaPropertiesToParameters(parameters, required).map(parameter => {
1293
- var _a;
1311
+ React.createElement(Panel.Titlebar, { rightComponent: React.createElement(OneOfMenu, { choices: choices, choice: selectedChoice, onChange: onSchemaChange }) }, "Body"),
1312
+ React.createElement(Panel.Content, { className: "sl-overflow-y-auto ParameterGrid OperationParametersContent" }, formFieldRows
1313
+ .filter(isRegularNode)
1314
+ .map(toParameterSpec)
1315
+ .map(parameter => {
1316
+ var _a, _b;
1294
1317
  const supportsFileUpload = parameterSupportsFileUpload(parameter);
1295
- const value = values[parameter.name];
1318
+ const value = values[(_a = parameter.name) !== null && _a !== void 0 ? _a : ''];
1296
1319
  if (supportsFileUpload) {
1297
1320
  return (React.createElement(FileUploadParameterEditor, { key: parameter.name, parameter: parameter, value: value instanceof File ? value : undefined, onChange: newValue => newValue
1298
1321
  ? onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: newValue }))
1299
1322
  : onChangeValues(omit(values, parameter.name)) }));
1300
1323
  }
1301
- return (React.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_a = isAllowedEmptyValues[parameter.name]) !== null && _a !== void 0 ? _a : false }));
1324
+ return (React.createElement(ParameterEditor, { key: parameter.name, parameter: parameter, value: typeof value === 'string' ? value : undefined, onChange: value => onChangeValues(Object.assign(Object.assign({}, values), { [parameter.name]: typeof value === 'number' ? String(value) : value })), onChangeOptional: value => onChangeParameterAllow(Object.assign(Object.assign({}, isAllowedEmptyValues), { [parameter.name]: value })), canChangeOptional: true, isOptional: (_b = isAllowedEmptyValues[parameter.name]) !== null && _b !== void 0 ? _b : false }));
1302
1325
  }))));
1303
- };
1326
+ };
1327
+ function OneOfMenu({ choices: subSchemas, choice, onChange }) {
1328
+ var _a;
1329
+ const onSubSchemaSelect = React.useCallback(onChange, [onChange]);
1330
+ const menuItems = React.useMemo(() => subSchemas.map(subSchema => {
1331
+ const label = subSchema.title;
1332
+ return {
1333
+ id: `request-subschema-${label}`,
1334
+ title: label,
1335
+ onPress: () => onSubSchemaSelect(subSchema),
1336
+ };
1337
+ }), [subSchemas, onSubSchemaSelect]);
1338
+ if (!subSchemas || subSchemas.length < 2) {
1339
+ return null;
1340
+ }
1341
+ const title = (_a = choice === null || choice === void 0 ? void 0 : choice.title) !== null && _a !== void 0 ? _a : 'Variants';
1342
+ return (React.createElement(Menu, { "aria-label": title, items: menuItems, renderTrigger: ({ isOpen }) => (React.createElement(Button, { appearance: "minimal", size: "sm", iconRight: ['fas', 'sort'], active: isOpen, "data-testid": "oneof-menu" }, title)) }));
1343
+ }
1304
1344
 
1305
1345
  const fileToBase64 = (file) => new Promise((resolve, reject) => {
1306
1346
  const reader = new FileReader();
@@ -2662,11 +2702,9 @@ function MethodPath({ method, path }) {
2662
2702
  function MethodPathInner({ method, path, chosenServerUrl }) {
2663
2703
  const isDark = useThemeIsDark();
2664
2704
  const fullUrl = `${chosenServerUrl}${path}`;
2665
- const { leading, trailing } = useChosenServerUrl(chosenServerUrl);
2666
2705
  const pathElem = (React.createElement(Flex, { overflowX: "hidden", fontSize: "lg", userSelect: "all" },
2667
2706
  React.createElement(Box, { dir: "rtl", color: "muted", textOverflow: "truncate", overflowX: "hidden" },
2668
- leading,
2669
- trailing !== null && (React.createElement(Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, trailing))),
2707
+ React.createElement(Box, { as: "span", dir: "ltr", style: { unicodeBidi: 'bidi-override' } }, chosenServerUrl)),
2670
2708
  React.createElement(Box, { fontWeight: "semibold", flex: 1 }, path)));
2671
2709
  return (React.createElement(HStack, { spacing: 3, pl: 2.5, pr: 4, py: 2, bg: "canvas-50", rounded: "lg", fontFamily: "mono", display: "inline-flex", maxW: "full", title: fullUrl },
2672
2710
  React.createElement(Box, { py: 1, px: 2.5, rounded: "lg", bg: !isDark ? HttpMethodColors[method] : 'canvas-100', color: !isDark ? 'on-primary' : 'body', fontSize: "lg", fontWeight: "semibold", textTransform: "uppercase" }, method),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stoplight/elements-core",
3
- "version": "7.13.7",
3
+ "version": "7.14.0",
4
4
  "main": "./index.js",
5
5
  "sideEffects": [
6
6
  "web-components.min.js",
@@ -28,17 +28,18 @@
28
28
  "@stoplight/json": "^3.18.1",
29
29
  "@stoplight/json-schema-ref-parser": "^9.0.5",
30
30
  "@stoplight/json-schema-sampler": "0.2.3",
31
- "@stoplight/json-schema-viewer": "^4.12.1",
31
+ "@stoplight/json-schema-tree": "^2.2.5",
32
+ "@stoplight/json-schema-viewer": "^4.13.0",
32
33
  "@stoplight/markdown-viewer": "^5.6.0",
33
- "@stoplight/mosaic": "^1.44.3",
34
- "@stoplight/mosaic-code-editor": "^1.44.3",
35
- "@stoplight/mosaic-code-viewer": "^1.44.3",
34
+ "@stoplight/mosaic": "^1.44.4",
35
+ "@stoplight/mosaic-code-editor": "^1.44.4",
36
+ "@stoplight/mosaic-code-viewer": "^1.44.4",
36
37
  "@stoplight/path": "^1.3.2",
37
38
  "@stoplight/react-error-boundary": "^2.0.0",
38
39
  "@stoplight/types": "^14.0.0",
39
40
  "@stoplight/yaml": "^4.2.3",
40
41
  "classnames": "^2.2.6",
41
- "httpsnippet-lite": "^3.0.1",
42
+ "httpsnippet-lite": "^3.0.5",
42
43
  "jotai": "1.3.9",
43
44
  "json-schema": "^0.4.0",
44
45
  "lodash": "^4.17.19",
@@ -1,6 +0,0 @@
1
- declare type ChosenServerUrl = {
2
- leading: string;
3
- trailing: string | null;
4
- };
5
- export declare function useChosenServerUrl(chosenServerUrl: string): ChosenServerUrl;
6
- export {};