fix(client): give useful error in solutionform (#40225)
This commit is contained in:
@@ -79,12 +79,12 @@ test('should submit', () => {
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(1);
|
||||
expect(submit.mock.calls[0][0]).toEqual({ website: websiteValue });
|
||||
expect(submit.mock.calls[0][0].values).toEqual({ website: websiteValue });
|
||||
|
||||
fireEvent.change(websiteInput, { target: { value: `${websiteValue}///` } });
|
||||
expect(websiteInput).toHaveValue(`${websiteValue}///`);
|
||||
|
||||
fireEvent.click(button);
|
||||
expect(submit).toHaveBeenCalledTimes(2);
|
||||
expect(submit.mock.calls[1][0]).toEqual({ website: websiteValue });
|
||||
expect(submit.mock.calls[1][0].values).toEqual({ website: websiteValue });
|
||||
});
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import { kebabCase } from 'lodash';
|
||||
import normalizeUrl from 'normalize-url';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Alert,
|
||||
@@ -10,6 +11,11 @@ import {
|
||||
HelpBlock
|
||||
} from '@freecodecamp/react-bootstrap';
|
||||
import { Field } from 'react-final-form';
|
||||
import {
|
||||
editorValidator,
|
||||
localhostValidator,
|
||||
composeValidators
|
||||
} from './FormValidators';
|
||||
|
||||
const propTypes = {
|
||||
formFields: PropTypes.arrayOf(
|
||||
@@ -33,6 +39,28 @@ function FormFields(props) {
|
||||
types = {}
|
||||
} = options;
|
||||
|
||||
const nullOrWarning = (value, error, isURL) => {
|
||||
let validationError;
|
||||
if (value && isURL) {
|
||||
try {
|
||||
normalizeUrl(value, { stripWWW: false });
|
||||
} catch (err) {
|
||||
validationError = err.message;
|
||||
}
|
||||
}
|
||||
const validationWarning = composeValidators(
|
||||
editorValidator,
|
||||
localhostValidator
|
||||
)(value);
|
||||
const message = error || validationError || validationWarning;
|
||||
return message ? (
|
||||
<HelpBlock>
|
||||
<Alert bsStyle={error || validationError ? 'danger' : 'info'}>
|
||||
{message}
|
||||
</Alert>
|
||||
</HelpBlock>
|
||||
) : null;
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
{formFields
|
||||
@@ -44,6 +72,7 @@ function FormFields(props) {
|
||||
const type = name in types ? types[name] : 'text';
|
||||
const placeholder =
|
||||
name in placeholders ? placeholders[name] : '';
|
||||
const isURL = types[name] === 'url';
|
||||
return (
|
||||
<Col key={key} xs={12}>
|
||||
<FormGroup>
|
||||
@@ -61,11 +90,7 @@ function FormFields(props) {
|
||||
type={type}
|
||||
value={value}
|
||||
/>
|
||||
{error && !pristine ? (
|
||||
<HelpBlock>
|
||||
<Alert bsStyle='danger'>{error}</Alert>
|
||||
</HelpBlock>
|
||||
) : null}
|
||||
{nullOrWarning(value, !pristine && error, isURL)}
|
||||
</FormGroup>
|
||||
</Col>
|
||||
);
|
||||
|
14
client/src/components/formHelpers/FormValidators.js
Normal file
14
client/src/components/formHelpers/FormValidators.js
Normal file
@@ -0,0 +1,14 @@
|
||||
// Matches editor links for: Repl.it, Glitch, CodeSandbox
|
||||
const editorRegex = /repl\.it\/@|glitch\.com\/edit\/#!|codesandbox\.io\/s\//;
|
||||
const localhostRegex = /localhost:/;
|
||||
|
||||
export const editorValidator = value =>
|
||||
editorRegex.test(value) ? 'Remember to submit the Live App URL.' : null;
|
||||
|
||||
export const localhostValidator = value =>
|
||||
localhostRegex.test(value)
|
||||
? 'Remember to submit a publicly visible app URL.'
|
||||
: null;
|
||||
|
||||
export const composeValidators = (...validators) => value =>
|
||||
validators.reduce((error, validator) => error ?? validator(value), null);
|
@@ -1,4 +1,9 @@
|
||||
import normalizeUrl from 'normalize-url';
|
||||
import {
|
||||
localhostValidator,
|
||||
editorValidator,
|
||||
composeValidators
|
||||
} from './FormValidators';
|
||||
|
||||
export { default as BlockSaveButton } from './BlockSaveButton.js';
|
||||
export { default as BlockSaveWrapper } from './BlockSaveWrapper.js';
|
||||
@@ -10,11 +15,26 @@ const normalizeOptions = {
|
||||
};
|
||||
|
||||
export function formatUrlValues(values, options) {
|
||||
return Object.keys(values).reduce((result, key) => {
|
||||
const validatedValues = { values: {}, errors: [], invalidValues: [] };
|
||||
const urlValues = Object.keys(values).reduce((result, key) => {
|
||||
let value = values[key];
|
||||
const nullOrWarning = composeValidators(
|
||||
localhostValidator,
|
||||
editorValidator
|
||||
)(value);
|
||||
if (nullOrWarning) {
|
||||
validatedValues.invalidValues.push(nullOrWarning);
|
||||
}
|
||||
if (value && options.types[key] === 'url') {
|
||||
value = normalizeUrl(value, normalizeOptions);
|
||||
try {
|
||||
value = normalizeUrl(value, normalizeOptions);
|
||||
} catch (err) {
|
||||
// Not a valid URL for testing or submission
|
||||
validatedValues.errors.push({ error: err, value });
|
||||
}
|
||||
}
|
||||
return { ...result, [key]: value };
|
||||
}, {});
|
||||
validatedValues.values = urlValues;
|
||||
return validatedValues;
|
||||
}
|
||||
|
Reference in New Issue
Block a user