Skip to content

Commit 1b4e417

Browse files
authored
Merge pull request #17 from koptyaisky/12751-fix-simulation-props
update simulation
2 parents b1b5360 + 774148e commit 1b4e417

File tree

5 files changed

+157
-31
lines changed

5 files changed

+157
-31
lines changed

src/components/Simulations/Simulation/Simulation.tsx

Lines changed: 83 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
/* eslint-disable react/jsx-props-no-spreading */
12
import React, { useMemo, useEffect, useCallback, useState } from 'react';
23
import { Button, Checkbox, Header, Select, TextField } from '@megafon/ui-core';
34
import type { ISelectItem } from '@megafon/ui-core/dist/lib/components/Select/Select';
5+
import type { TextFieldProps } from '@megafon/ui-core/dist/lib/components/TextField/TextField';
46
import { cnCreate } from '@megafon/ui-helpers';
57
import { ReactComponent as ArrowIcon } from '@megafon/ui-icons/system-16-arrow_left_16.svg';
68
import { ReactComponent as Minus } from '@megafon/ui-icons/system-16-minus_16.svg';
@@ -10,26 +12,28 @@ import type { SimulationResponse, HoverflyMatcher, PairItemRequest, SimulationIt
1012
import CollapseWrapper from 'components/CollapseWrapper/CollapseWrapper';
1113
import './Simulation.pcss';
1214
import { useSelector } from 'store/hooks';
13-
import { hightlightHtml, MirrorBodyType } from 'utils';
15+
import { convertStringToInteger, hightlightHtml, MirrorBodyType } from 'utils';
1416
import {
1517
BODY_FORMATS,
18+
FIELDS_ERROR,
1619
headerEmpty,
1720
initialBodyState,
21+
initialFieldsError,
1822
initialHeaderQuery,
1923
initialPairState,
2024
initialServerState,
2125
initialState,
2226
MATCHES,
2327
METHODS,
2428
serverStateEmpty,
25-
STATUS_CODES,
2629
} from '../constants';
2730
import type {
2831
ServerState,
2932
SimulationsServerState,
3033
SimulationHeadersQueryState,
3134
SimulationHeaderState,
3235
SimulationHtmlState,
36+
SimulationFieldsErrorState,
3337
} from '../types';
3438
import {
3539
addOrRemoveEl,
@@ -44,9 +48,12 @@ import {
4448
getRequireStateList,
4549
getResponseHeaderStateList,
4650
getTransitionStateList,
51+
getVerificationFields,
52+
isFieldsError,
4753
mergeBodyStateToCurrentPair,
4854
mergeCurrentStateToMainState,
4955
mergeHeaderStateToCurrentPair,
56+
mergeMatcherValueToCurrentPair,
5057
mergeServerStateToCurrentPair,
5158
} from '../utils';
5259

@@ -58,6 +65,14 @@ require('codemirror/mode/javascript/javascript');
5865
require('codemirror/mode/htmlmixed/htmlmixed');
5966

6067
type InputChange = React.ChangeEvent<HTMLInputElement>;
68+
type InputFocus = React.FocusEvent<HTMLInputElement>;
69+
type InputPropsType = {
70+
placeholder?: string;
71+
name?: string;
72+
inputMask?: string;
73+
inputMode?: TextFieldProps['inputMode'];
74+
onBlurField?: (e: InputFocus) => void;
75+
};
6176
type ChangeCurrentNames = keyof Omit<PairItemRequest, 'headers' | 'query' | 'requiresState'>;
6277

6378
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -86,15 +101,23 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
86101
const [currentPair, setCurrentPair] = useState<SimulationItem | undefined>(initialPairState);
87102
const [headerQuery, setHeaderQuery] = useState<SimulationHeadersQueryState>(initialHeaderQuery);
88103
const [body, setBody] = useState<SimulationHtmlState>(initialBodyState);
104+
const [fieldsError, setFieldsError] = useState<SimulationFieldsErrorState>(initialFieldsError);
89105

90106
const method = currentPair?.request.method?.[0].value || 'GET';
91107
const { transitionsState, requiresState } = serverState;
92108

109+
const isAnyFieldError: boolean = useMemo(() => !!Object.values(fieldsError).find(error => !!error), [fieldsError]);
110+
const isDisabledButton: boolean =
111+
!currentPair?.request?.path?.[0]?.value || !currentPair?.response?.status || isAnyFieldError;
112+
93113
function handleSubmit() {
94114
if (state && simulationStore.type !== 'pending') {
95115
const pair = currentPair
96116
? mergeServerStateToCurrentPair(
97-
mergeHeaderStateToCurrentPair(mergeBodyStateToCurrentPair(currentPair, body), headerQuery),
117+
mergeHeaderStateToCurrentPair(
118+
mergeBodyStateToCurrentPair(mergeMatcherValueToCurrentPair(currentPair), body),
119+
headerQuery,
120+
),
98121
serverState,
99122
)
100123
: null;
@@ -153,9 +176,27 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
153176
return (e: InputChange) => setCurrentPair(prev => changeCurrentPairRequest(prev, name, key, e.target.value));
154177
}
155178

156-
function handleChooseCurrentResponse(name: keyof PairItemResponse) {
157-
return (_e: React.MouseEvent<HTMLDivElement>, dataItem?: ISelectItem<number>) => {
158-
setCurrentPair(prev => changeCurrentPairResponse(prev, name, dataItem?.value || 0));
179+
function handleChangeCurrentResponse(name: keyof PairItemResponse, isValueNumber?: boolean) {
180+
return (e: InputChange) => {
181+
const currentValue: string | number = isValueNumber
182+
? convertStringToInteger(e.target.value) || ''
183+
: e.target.value;
184+
185+
setCurrentPair(prev => changeCurrentPairResponse(prev, name, currentValue));
186+
};
187+
}
188+
189+
function handleBlurCurrentResponse(errorName: keyof SimulationFieldsErrorState) {
190+
return (e: InputFocus) => {
191+
const isFieldError: boolean = isFieldsError(errorName, e.target.value);
192+
193+
if (isFieldError) {
194+
setFieldsError(prev => ({ ...prev, [errorName]: FIELDS_ERROR[errorName] }));
195+
196+
return;
197+
}
198+
199+
setFieldsError(prev => ({ ...prev, [errorName]: '' }));
159200
};
160201
}
161202

@@ -223,25 +264,37 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
223264
);
224265

225266
const renderField = useCallback(
226-
(onChangeField: (e: InputChange) => void, value: string, placeholder?: string) => (
267+
(
268+
onChangeField: (e: InputChange) => void,
269+
value: string,
270+
{ placeholder, name, inputMask, inputMode, onBlurField }: InputPropsType,
271+
) => (
227272
<TextField
228273
className={cn('field')}
229274
classes={{ input: cn('input') }}
230275
placeholder={placeholder}
231276
value={value || ''}
277+
mask={inputMask}
278+
inputMode={inputMode}
232279
onChange={onChangeField}
280+
onBlur={onBlurField}
281+
{...getVerificationFields(fieldsError[name || ''])}
233282
/>
234283
),
235-
[],
284+
[fieldsError],
236285
);
237286

238287
const renderFieldList = (list: ServerState[], name: keyof SimulationsServerState) => (
239288
<div className={cn('field-list')}>
240289
{list.map((values, index) => (
241290
// eslint-disable-next-line react/no-array-index-key
242291
<div className={cn('fields')} key={index}>
243-
{renderField(handleChangeServerState(name, 'key', index), values.key || '', 'State key')}
244-
{renderField(handleChangeServerState(name, 'value', index), values.value || '', 'State value')}
292+
{renderField(handleChangeServerState(name, 'key', index), values.key || '', {
293+
placeholder: 'State key',
294+
})}
295+
{renderField(handleChangeServerState(name, 'value', index), values.value || '', {
296+
placeholder: 'State value',
297+
})}
245298
{renderDeleteButton(handleToggleServerState(name, index))}
246299
</div>
247300
))}
@@ -262,12 +315,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
262315
</Header>,
263316
)}
264317
<div className={cn('action-button')}>
265-
<Button
266-
sizeAll="medium"
267-
fullWidth
268-
disabled={!currentPair?.request?.path?.[0]?.value}
269-
onClick={handleSubmit}
270-
>
318+
<Button sizeAll="medium" fullWidth disabled={isDisabledButton} onClick={handleSubmit}>
271319
{routeIndex === undefined ? 'Create' : 'Update'}
272320
</Button>
273321
{routeIndex !== undefined && (
@@ -327,7 +375,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
327375
{renderField(
328376
handleChangeCurrentRequest('destination', 'value'),
329377
currentPair?.request.destination?.[0].value || '',
330-
'localhost:8080',
378+
{ placeholder: 'localhost:8080' },
331379
)}
332380
</div>
333381
</SimulationFieldsBlock>
@@ -342,7 +390,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
342390
{renderField(
343391
handleChangeCurrentRequest('path', 'value'),
344392
currentPair?.request.path?.[0].value || '',
345-
'api/v1/match',
393+
{ placeholder: 'api/v1/match' },
346394
)}
347395
</div>
348396
</SimulationFieldsBlock>
@@ -359,7 +407,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
359407
{renderField(
360408
handleChangeHeaderQuery('query', 'key', index),
361409
header.key,
362-
'Query key',
410+
{ placeholder: 'Query key' },
363411
)}
364412
<Select
365413
classes={{ control: cn('select-control') }}
@@ -370,7 +418,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
370418
{renderField(
371419
handleChangeHeaderQuery('query', 'value', index),
372420
header.value,
373-
'Query keys(s)',
421+
{ placeholder: 'Query keys(s)' },
374422
)}
375423
{renderDeleteButton(handleToggleHeaderQuery('query', index))}
376424
</div>
@@ -391,7 +439,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
391439
{renderField(
392440
handleChangeHeaderQuery('request', 'key', index),
393441
header.key,
394-
'Header key',
442+
{ placeholder: 'Header key' },
395443
)}
396444
<Select
397445
classes={{ control: cn('select-contol') }}
@@ -402,7 +450,7 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
402450
{renderField(
403451
handleChangeHeaderQuery('request', 'value', index),
404452
header.value,
405-
'Header keys(s)',
453+
{ placeholder: 'Header keys(s)' },
406454
)}
407455
{renderDeleteButton(handleToggleHeaderQuery('request', index))}
408456
</div>
@@ -440,12 +488,17 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
440488
<div className={cn('collapse-content')}>
441489
<SimulationFieldsBlock title="Status code">
442490
<div className={cn('fields')}>
443-
<Select
444-
classes={{ control: cn('select-contol') }}
445-
items={STATUS_CODES}
446-
currentValue={currentPair?.response.status || STATUS_CODES[0].value}
447-
onSelect={handleChooseCurrentResponse('status')}
448-
/>
491+
{renderField(
492+
handleChangeCurrentResponse('status', true),
493+
`${currentPair?.response.status}`,
494+
{
495+
placeholder: '200',
496+
name: 'statusCode',
497+
inputMask: '999',
498+
inputMode: 'numeric',
499+
onBlurField: handleBlurCurrentResponse('statusCode'),
500+
},
501+
)}
449502
</div>
450503
</SimulationFieldsBlock>
451504
<SimulationFieldsBlock
@@ -461,12 +514,12 @@ const Simulation: React.FC<ISimulationProps> = ({ onBack, onChange, onDelete })
461514
{renderField(
462515
handleChangeHeaderQuery('response', 'key', index),
463516
header.key,
464-
'Header key',
517+
{ placeholder: 'Header key' },
465518
)}
466519
{renderField(
467520
handleChangeHeaderQuery('response', 'value', index),
468521
header.value,
469-
'Header value(s)',
522+
{ placeholder: 'Header value(s)' },
470523
)}
471524
{renderDeleteButton(handleToggleHeaderQuery('response', index))}
472525
</div>

src/components/Simulations/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { SimulationResponse, SimulationItem } from 'api/types';
33
import type { MirrorBodyType } from 'utils';
44
import {
55
ServerState,
6+
SimulationFieldsErrorState,
67
SimulationHeadersQueryState,
78
SimulationHeaderState,
89
SimulationHtmlState,
@@ -74,6 +75,15 @@ export const STATUS_CODES: Array<ISelectItem<number>> = [
7475
},
7576
];
7677

78+
export const FIELDS_ERROR: SimulationFieldsErrorState = {
79+
statusCode: 'Response status code is not valid. Status code should between 100 and 599',
80+
};
81+
82+
export const STATUS_CODE_VALID_VALUE = {
83+
minValue: 100,
84+
maxValue: 600,
85+
} as const;
86+
7787
export const initialState: SimulationResponse = {
7888
data: {
7989
pairs: [],
@@ -123,6 +133,8 @@ export const initialBodyState: SimulationHtmlState = {
123133
response: { value: '', type: 'json' },
124134
};
125135

136+
export const initialFieldsError: SimulationFieldsErrorState = { statusCode: '' };
137+
126138
export const BODY_FORMATS: Array<ISelectItem<MirrorBodyType>> = [
127139
{
128140
value: 'json',

src/components/Simulations/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,12 @@ export type SimulationCodeMirrorOptions = {
4747
json: boolean;
4848
};
4949
};
50+
51+
export type SimulationFieldsErrorState = {
52+
statusCode: string;
53+
};
54+
55+
export type VerificationFieldsType = {
56+
verification?: 'error' | 'valid';
57+
noticeText?: string;
58+
};

0 commit comments

Comments
 (0)