From fec2d7933243d88d09f692f51af0e75c8d2a1521 Mon Sep 17 00:00:00 2001 From: Jakub Jankowski Date: Tue, 22 Dec 2020 13:37:53 +0100 Subject: [PATCH] feat: parameters inputs (#755) * feat: parameter validations * chore: review suggestions * chore: cleanup and separete Parameter component * test: update tests --- .../src/__fixtures__/operations/put-todos.ts | 30 ++++++ .../src/components/TryIt/BasicSend.tsx | 6 +- .../components/TryIt/OperationParameters.tsx | 80 ++++++++++------ .../src/components/TryIt/Parameter.tsx | 94 +++++++++++++++++++ .../TryIt/__tests__/BasicSend.test.tsx | 41 +++++++- .../TryIt/__tests__/parameters.test.ts | 22 +++++ 6 files changed, 238 insertions(+), 35 deletions(-) create mode 100644 packages/elements/src/components/TryIt/Parameter.tsx create mode 100644 packages/elements/src/components/TryIt/__tests__/parameters.test.ts diff --git a/packages/elements/src/__fixtures__/operations/put-todos.ts b/packages/elements/src/__fixtures__/operations/put-todos.ts index 7373109ca..73ed5a969 100644 --- a/packages/elements/src/__fixtures__/operations/put-todos.ts +++ b/packages/elements/src/__fixtures__/operations/put-todos.ts @@ -369,10 +369,40 @@ export const httpOperation: IHttpOperation = { schema: { type: 'string', description: 'Your Stoplight account id', + default: 'account-id-default', }, name: 'account-id', style: HttpParamStyles.Simple, required: true, + examples: [ + { + value: 'example id', + key: 'example', + }, + ], + }, + { + schema: { + type: 'string', + description: 'Your Stoplight account id', + }, + name: 'message-id', + style: HttpParamStyles.Simple, + required: true, + examples: [ + { + value: 'example value', + key: 'example 1', + }, + { + value: 'another example', + key: 'example 2', + }, + { + value: 'something else', + key: 'example 3', + }, + ], }, ], path: [ diff --git a/packages/elements/src/components/TryIt/BasicSend.tsx b/packages/elements/src/components/TryIt/BasicSend.tsx index a6de8f178..3fa5dde6a 100644 --- a/packages/elements/src/components/TryIt/BasicSend.tsx +++ b/packages/elements/src/components/TryIt/BasicSend.tsx @@ -7,7 +7,7 @@ import * as React from 'react'; import { HttpCodeDescriptions } from '../../constants'; import { getHttpCodeColor } from '../../utils/http'; -import { OperationParameters } from './OperationParameters'; +import { initialParameterValues, OperationParameters } from './OperationParameters'; export interface BasicSendProps { httpOperation: IHttpOperation; @@ -33,7 +33,9 @@ export const BasicSend: React.FC = ({ httpOperation }) => { }; const allParameters = Object.values(operationParameters).flat(); - const [parameterValues, setParameterValues] = React.useState>({}); + const [parameterValues, setParameterValues] = React.useState>( + initialParameterValues(operationParameters), + ); if (!server) return null; diff --git a/packages/elements/src/components/TryIt/OperationParameters.tsx b/packages/elements/src/components/TryIt/OperationParameters.tsx index 0f3558b6f..fe824b83b 100644 --- a/packages/elements/src/components/TryIt/OperationParameters.tsx +++ b/packages/elements/src/components/TryIt/OperationParameters.tsx @@ -1,9 +1,11 @@ -import { Flex, Input, Panel, Text } from '@stoplight/mosaic'; -import { Dictionary, IHttpHeaderParam, IHttpPathParam, IHttpQueryParam } from '@stoplight/types'; +import { Panel } from '@stoplight/mosaic'; +import { Dictionary, IHttpHeaderParam, IHttpParam, IHttpPathParam, IHttpQueryParam } from '@stoplight/types'; import { sortBy } from 'lodash'; import * as React from 'react'; -interface OperationParameters { +import { exampleValue, Parameter } from './Parameter'; + +export interface OperationParameters { path?: IHttpPathParam[]; query?: IHttpQueryParam[]; headers?: IHttpHeaderParam[]; @@ -20,37 +22,59 @@ export const OperationParameters: React.FC = ({ values, onChangeValues, }) => { - const pathParameters = sortBy(operationParameters.path ?? [], ['name']); - const queryParameters = sortBy(operationParameters.query ?? [], ['name']); - const headerParameters = sortBy(operationParameters.headers ?? [], ['name']); - const parameters = [...pathParameters, ...queryParameters, ...headerParameters]; + const parameters = flattenParameters(operationParameters); + + const onChange = (parameter: IHttpParam) => ( + e: React.FormEvent | React.ChangeEvent, + ) => { + const newValue = e.currentTarget.value; + onChangeValues({ ...values, [parameter.name]: newValue }); + }; return ( Parameters - {parameters.map(parameter => { - return ( - - - : - { - const newValue = e.currentTarget.value; - onChangeValues({ ...values, [parameter.name]: newValue }); - }} - /> - - ); - })} + {parameters.map((parameter, i) => ( + + ))} ); }; + +function flattenParameters(parameters: OperationParameters) { + const pathParameters = sortBy(parameters.path ?? [], ['name']); + const queryParameters = sortBy(parameters.query ?? [], ['name']); + const headerParameters = sortBy(parameters.headers ?? [], ['name']); + return [...pathParameters, ...queryParameters, ...headerParameters]; +} + +export function initialParameterValues(operationParameters: OperationParameters) { + const parameters = flattenParameters(operationParameters); + + const enums = Object.fromEntries( + parameters + .map(p => [p.name, p.schema?.enum ?? []] as const) + .filter(([, enums]) => enums.length > 0) + .map(([name, enums]) => [name, String(enums[0])]), + ); + + const examples = Object.fromEntries( + parameters + .map(p => [p.name, p.examples ?? []] as const) + .filter(([, examples]) => examples.length > 0) + .map(([name, examples]) => [name, exampleValue(examples[0])]), + ); + + return { + // order matters - enums should be override examples + ...examples, + ...enums, + }; +} diff --git a/packages/elements/src/components/TryIt/Parameter.tsx b/packages/elements/src/components/TryIt/Parameter.tsx new file mode 100644 index 000000000..f1becf51d --- /dev/null +++ b/packages/elements/src/components/TryIt/Parameter.tsx @@ -0,0 +1,94 @@ +import { safeStringify } from '@stoplight/json'; +import { Flex, Input, Select, Text } from '@stoplight/mosaic'; +import { IHttpParam, INodeExample, INodeExternalExample } from '@stoplight/types'; +import { isObject, map } from 'lodash'; +import * as React from 'react'; + +interface ParameterProps { + parameter: IHttpParam; + value: string; + onChange: (e: React.FormEvent | React.ChangeEvent) => void; +} + +const booleanOptions = [ + { label: 'Not Set', value: '' }, + { label: 'False', value: 'false' }, + { label: 'True', value: 'true' }, +]; + +const selectExampleOption = { value: '', label: 'Pick an example' }; + +export const Parameter: React.FC = ({ parameter, value, onChange }) => { + const parameterValueOptions = parameterOptions(parameter); + const examples = exampleOptions(parameter); + const selectedExample = examples?.find(e => e.value === value) ?? selectExampleOption; + return ( + + + : + {parameterValueOptions ? ( + + {examples && ( +