fix: allow form label and name to differ

Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
Sky020
2020-09-09 17:12:48 +01:00
committed by Ahmad Abdolsaheb
parent d765fa704b
commit c4240cdf2f
6 changed files with 37 additions and 28 deletions

View File

@ -12,7 +12,10 @@ import {
const propTypes = { const propTypes = {
buttonText: PropTypes.string, buttonText: PropTypes.string,
enableSubmit: PropTypes.bool, enableSubmit: PropTypes.bool,
formFields: PropTypes.arrayOf(PropTypes.string).isRequired, formFields: PropTypes.arrayOf(
PropTypes.shape({ name: PropTypes.string, label: PropTypes.string })
.isRequired
).isRequired,
hideButton: PropTypes.bool, hideButton: PropTypes.bool,
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
initialValues: PropTypes.object, initialValues: PropTypes.object,
@ -47,7 +50,7 @@ function DynamicForm({
onSubmit={handleSubmit} onSubmit={handleSubmit}
style={{ width: '100%' }} style={{ width: '100%' }}
> >
<FormFields fields={formFields} options={options} /> <FormFields formFields={formFields} options={options} />
<BlockSaveWrapper> <BlockSaveWrapper>
{hideButton ? null : ( {hideButton ? null : (
<BlockSaveButton disabled={(pristine && !enableSubmit) || error}> <BlockSaveButton disabled={(pristine && !enableSubmit) || error}>

View File

@ -8,7 +8,10 @@ import Form from './Form';
const defaultTestProps = { const defaultTestProps = {
buttonText: 'Submit', buttonText: 'Submit',
formFields: ['name', 'website'], formFields: [
{ name: 'name', label: 'name Label' },
{ name: 'website', label: 'WebSite label' }
],
id: 'my-test-form', id: 'my-test-form',
options: { options: {
types: { types: {
@ -23,11 +26,11 @@ const defaultTestProps = {
test('should render', () => { test('should render', () => {
const { getByLabelText, getByText } = render(<Form {...defaultTestProps} />); const { getByLabelText, getByText } = render(<Form {...defaultTestProps} />);
const nameInput = getByLabelText(/name/i); const nameInput = getByLabelText(/name Label/);
expect(nameInput).not.toBeRequired(); expect(nameInput).not.toBeRequired();
expect(nameInput).toHaveAttribute('type', 'text'); expect(nameInput).toHaveAttribute('type', 'text');
const websiteInput = getByLabelText(/website/i); const websiteInput = getByLabelText(/WebSite label/);
expect(websiteInput).toBeRequired(); expect(websiteInput).toBeRequired();
expect(websiteInput).toHaveAttribute('type', 'url'); expect(websiteInput).toHaveAttribute('type', 'url');
@ -48,10 +51,10 @@ test('should render with default values', () => {
/> />
); );
const nameInput = getByLabelText(/name/i); const nameInput = getByLabelText(/name Label/);
expect(nameInput).toHaveValue(nameValue); expect(nameInput).toHaveValue(nameValue);
const websiteInput = getByLabelText(/website/i); const websiteInput = getByLabelText(/WebSite label/);
expect(websiteInput).toHaveValue(websiteValue); expect(websiteInput).toHaveValue(websiteValue);
const button = getByText(/submit/i); const button = getByText(/submit/i);
@ -68,7 +71,7 @@ test('should submit', () => {
const { getByLabelText, getByText } = render(<Form {...props} />); const { getByLabelText, getByText } = render(<Form {...props} />);
const websiteInput = getByLabelText(/website/i); const websiteInput = getByLabelText(/WebSite label/);
fireEvent.change(websiteInput, { target: { value: websiteValue } }); fireEvent.change(websiteInput, { target: { value: websiteValue } });
expect(websiteInput).toHaveValue(websiteValue); expect(websiteInput).toHaveValue(websiteValue);

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { kebabCase, startCase } from 'lodash'; import { kebabCase } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Alert, Alert,
@ -12,7 +12,10 @@ import {
import { Field } from 'react-final-form'; import { Field } from 'react-final-form';
const propTypes = { const propTypes = {
fields: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired, formFields: PropTypes.arrayOf(
PropTypes.shape({ name: PropTypes.string, label: PropTypes.string })
.isRequired
).isRequired,
options: PropTypes.shape({ options: PropTypes.shape({
ignored: PropTypes.arrayOf(PropTypes.string), ignored: PropTypes.arrayOf(PropTypes.string),
placeholders: PropTypes.objectOf(PropTypes.string), placeholders: PropTypes.objectOf(PropTypes.string),
@ -22,19 +25,20 @@ const propTypes = {
}; };
function FormFields(props) { function FormFields(props) {
const { fields, options = {} } = props; const { formFields, options = {} } = props;
const { const {
ignored = [], ignored = [],
placeholders = {}, placeholders = {},
required = [], required = [],
types = {} types = {}
} = options; } = options;
return ( return (
<div> <div>
{fields {formFields
.filter(field => !ignored.includes(field)) .filter(formField => !ignored.includes(formField.name))
.map(name => ( .map(({ name, label }) => (
<Field key={`${name}-field`} name={name}> <Field key={`${kebabCase(name)}-field`} name={name}>
{({ input: { value, onChange }, meta: { pristine, error } }) => { {({ input: { value, onChange }, meta: { pristine, error } }) => {
const key = kebabCase(name); const key = kebabCase(name);
const type = name in types ? types[name] : 'text'; const type = name in types ? types[name] : 'text';
@ -44,9 +48,7 @@ function FormFields(props) {
<Col key={key} xs={12}> <Col key={key} xs={12}>
<FormGroup> <FormGroup>
{type === 'hidden' ? null : ( {type === 'hidden' ? null : (
<ControlLabel htmlFor={key}> <ControlLabel htmlFor={key}>{label}</ControlLabel>
{startCase(name)}
</ControlLabel>
)} )}
<FormControl <FormControl
componentClass={type === 'textarea' ? type : 'input'} componentClass={type === 'textarea' ? type : 'input'}

View File

@ -9,7 +9,6 @@ export { default as FormFields } from './FormFields.js';
const normalizeOptions = { const normalizeOptions = {
stripWWW: false stripWWW: false
}; };
// callIfDefined(fn: (Any) => Any) => (value: Any) => Any // callIfDefined(fn: (Any) => Any) => (value: Any) => Any
export function callIfDefined(fn) { export function callIfDefined(fn) {
return value => (value ? fn(value) : value); return value => (value ? fn(value) : value);
@ -24,7 +23,6 @@ export function formatUrlValues(values, options) {
return { ...result, [key]: value }; return { ...result, [key]: value };
}, {}); }, {});
} }
// formatUrl(url: String) => String // formatUrl(url: String) => String
export function formatUrl(url) { export function formatUrl(url) {
if (typeof url === 'string' && url.length > 4 && url.indexOf('.') !== -1) { if (typeof url === 'string' && url.length > 4 && url.indexOf('.') !== -1) {
@ -41,21 +39,17 @@ export function formatUrl(url) {
} }
return url; return url;
} }
export function isValidURL(data) { export function isValidURL(data) {
/* eslint-disable camelcase */ /* eslint-disable camelcase */
return isURL(data, { require_protocol: true }); return isURL(data, { require_protocol: true });
/* eslint-enable camelcase */ /* eslint-enable camelcase */
} }
export function makeOptional(validator) { export function makeOptional(validator) {
return val => (val ? validator(val) : true); return val => (val ? validator(val) : true);
} }
export function makeRequired(validator) { export function makeRequired(validator) {
return val => (val ? validator(val) : false); return val => (val ? validator(val) : false);
} }
export function createFormValidator(fieldValidators) { export function createFormValidator(fieldValidators) {
const fieldKeys = Object.keys(fieldValidators); const fieldKeys = Object.keys(fieldValidators);
return values => return values =>
@ -69,7 +63,6 @@ export function createFormValidator(fieldValidators) {
.filter(Boolean) .filter(Boolean)
.reduce((errors, error) => ({ ...errors, ...error }), {}); .reduce((errors, error) => ({ ...errors, ...error }), {});
} }
export function getValidationState(field) { export function getValidationState(field) {
if (field.pristine) { if (field.pristine) {
return null; return null;

View File

@ -415,6 +415,11 @@ export class CertificationSettings extends Component {
{ types: {} } { types: {} }
); );
const formFields = challengeTitles.map(title => ({
name: title,
label: title
}));
const fullForm = filledforms === challengeTitles.length; const fullForm = filledforms === challengeTitles.length;
const createClickHandler = certLocation => e => { const createClickHandler = certLocation => e => {
@ -436,7 +441,7 @@ export class CertificationSettings extends Component {
<Form <Form
buttonText={fullForm ? 'Claim Certification' : 'Save Progress'} buttonText={fullForm ? 'Claim Certification' : 'Save Progress'}
enableSubmit={fullForm} enableSubmit={fullForm}
formFields={challengeTitles} formFields={formFields}
hideButton={isCertClaimed} hideButton={isCertClaimed}
id={superBlock} id={superBlock}
initialValues={{ initialValues={{

View File

@ -18,8 +18,11 @@ const propTypes = {
}; };
// back end challenges and front end projects use a single form field // back end challenges and front end projects use a single form field
const solutionField = ['solution']; const solutionField = [{ name: 'solution', label: 'Solution Link' }];
const backEndProjectFields = ['solution', 'githubLink']; const backEndProjectFields = [
{ name: 'solution', label: 'Solution Link' },
{ name: 'githubLink', label: 'GitHub Link' }
];
const options = { const options = {
types: { types: {