fix: allow form label and name to differ
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
		| @@ -12,7 +12,10 @@ import { | ||||
| const propTypes = { | ||||
|   buttonText: PropTypes.string, | ||||
|   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, | ||||
|   id: PropTypes.string.isRequired, | ||||
|   initialValues: PropTypes.object, | ||||
| @@ -47,7 +50,7 @@ function DynamicForm({ | ||||
|           onSubmit={handleSubmit} | ||||
|           style={{ width: '100%' }} | ||||
|         > | ||||
|           <FormFields fields={formFields} options={options} /> | ||||
|           <FormFields formFields={formFields} options={options} /> | ||||
|           <BlockSaveWrapper> | ||||
|             {hideButton ? null : ( | ||||
|               <BlockSaveButton disabled={(pristine && !enableSubmit) || error}> | ||||
|   | ||||
| @@ -8,7 +8,10 @@ import Form from './Form'; | ||||
|  | ||||
| const defaultTestProps = { | ||||
|   buttonText: 'Submit', | ||||
|   formFields: ['name', 'website'], | ||||
|   formFields: [ | ||||
|     { name: 'name', label: 'name Label' }, | ||||
|     { name: 'website', label: 'WebSite label' } | ||||
|   ], | ||||
|   id: 'my-test-form', | ||||
|   options: { | ||||
|     types: { | ||||
| @@ -23,11 +26,11 @@ const defaultTestProps = { | ||||
| test('should render', () => { | ||||
|   const { getByLabelText, getByText } = render(<Form {...defaultTestProps} />); | ||||
|  | ||||
|   const nameInput = getByLabelText(/name/i); | ||||
|   const nameInput = getByLabelText(/name Label/); | ||||
|   expect(nameInput).not.toBeRequired(); | ||||
|   expect(nameInput).toHaveAttribute('type', 'text'); | ||||
|  | ||||
|   const websiteInput = getByLabelText(/website/i); | ||||
|   const websiteInput = getByLabelText(/WebSite label/); | ||||
|   expect(websiteInput).toBeRequired(); | ||||
|   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); | ||||
|  | ||||
|   const websiteInput = getByLabelText(/website/i); | ||||
|   const websiteInput = getByLabelText(/WebSite label/); | ||||
|   expect(websiteInput).toHaveValue(websiteValue); | ||||
|  | ||||
|   const button = getByText(/submit/i); | ||||
| @@ -68,7 +71,7 @@ test('should submit', () => { | ||||
|  | ||||
|   const { getByLabelText, getByText } = render(<Form {...props} />); | ||||
|  | ||||
|   const websiteInput = getByLabelText(/website/i); | ||||
|   const websiteInput = getByLabelText(/WebSite label/); | ||||
|   fireEvent.change(websiteInput, { target: { value: websiteValue } }); | ||||
|   expect(websiteInput).toHaveValue(websiteValue); | ||||
|  | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import React from 'react'; | ||||
| import { kebabCase, startCase } from 'lodash'; | ||||
| import { kebabCase } from 'lodash'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import { | ||||
|   Alert, | ||||
| @@ -12,7 +12,10 @@ import { | ||||
| import { Field } from 'react-final-form'; | ||||
|  | ||||
| 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({ | ||||
|     ignored: PropTypes.arrayOf(PropTypes.string), | ||||
|     placeholders: PropTypes.objectOf(PropTypes.string), | ||||
| @@ -22,19 +25,20 @@ const propTypes = { | ||||
| }; | ||||
|  | ||||
| function FormFields(props) { | ||||
|   const { fields, options = {} } = props; | ||||
|   const { formFields, options = {} } = props; | ||||
|   const { | ||||
|     ignored = [], | ||||
|     placeholders = {}, | ||||
|     required = [], | ||||
|     types = {} | ||||
|   } = options; | ||||
|  | ||||
|   return ( | ||||
|     <div> | ||||
|       {fields | ||||
|         .filter(field => !ignored.includes(field)) | ||||
|         .map(name => ( | ||||
|           <Field key={`${name}-field`} name={name}> | ||||
|       {formFields | ||||
|         .filter(formField => !ignored.includes(formField.name)) | ||||
|         .map(({ name, label }) => ( | ||||
|           <Field key={`${kebabCase(name)}-field`} name={name}> | ||||
|             {({ input: { value, onChange }, meta: { pristine, error } }) => { | ||||
|               const key = kebabCase(name); | ||||
|               const type = name in types ? types[name] : 'text'; | ||||
| @@ -44,9 +48,7 @@ function FormFields(props) { | ||||
|                 <Col key={key} xs={12}> | ||||
|                   <FormGroup> | ||||
|                     {type === 'hidden' ? null : ( | ||||
|                       <ControlLabel htmlFor={key}> | ||||
|                         {startCase(name)} | ||||
|                       </ControlLabel> | ||||
|                       <ControlLabel htmlFor={key}>{label}</ControlLabel> | ||||
|                     )} | ||||
|                     <FormControl | ||||
|                       componentClass={type === 'textarea' ? type : 'input'} | ||||
|   | ||||
| @@ -9,7 +9,6 @@ export { default as FormFields } from './FormFields.js'; | ||||
| const normalizeOptions = { | ||||
|   stripWWW: false | ||||
| }; | ||||
|  | ||||
| // callIfDefined(fn: (Any) => Any) => (value: Any) => Any | ||||
| export function callIfDefined(fn) { | ||||
|   return value => (value ? fn(value) : value); | ||||
| @@ -24,7 +23,6 @@ export function formatUrlValues(values, options) { | ||||
|     return { ...result, [key]: value }; | ||||
|   }, {}); | ||||
| } | ||||
|  | ||||
| // formatUrl(url: String) => String | ||||
| export function formatUrl(url) { | ||||
|   if (typeof url === 'string' && url.length > 4 && url.indexOf('.') !== -1) { | ||||
| @@ -41,21 +39,17 @@ export function formatUrl(url) { | ||||
|   } | ||||
|   return url; | ||||
| } | ||||
|  | ||||
| export function isValidURL(data) { | ||||
|   /* eslint-disable camelcase */ | ||||
|   return isURL(data, { require_protocol: true }); | ||||
|   /* eslint-enable camelcase */ | ||||
| } | ||||
|  | ||||
| export function makeOptional(validator) { | ||||
|   return val => (val ? validator(val) : true); | ||||
| } | ||||
|  | ||||
| export function makeRequired(validator) { | ||||
|   return val => (val ? validator(val) : false); | ||||
| } | ||||
|  | ||||
| export function createFormValidator(fieldValidators) { | ||||
|   const fieldKeys = Object.keys(fieldValidators); | ||||
|   return values => | ||||
| @@ -69,7 +63,6 @@ export function createFormValidator(fieldValidators) { | ||||
|       .filter(Boolean) | ||||
|       .reduce((errors, error) => ({ ...errors, ...error }), {}); | ||||
| } | ||||
|  | ||||
| export function getValidationState(field) { | ||||
|   if (field.pristine) { | ||||
|     return null; | ||||
|   | ||||
| @@ -415,6 +415,11 @@ export class CertificationSettings extends Component { | ||||
|       { types: {} } | ||||
|     ); | ||||
|  | ||||
|     const formFields = challengeTitles.map(title => ({ | ||||
|       name: title, | ||||
|       label: title | ||||
|     })); | ||||
|  | ||||
|     const fullForm = filledforms === challengeTitles.length; | ||||
|  | ||||
|     const createClickHandler = certLocation => e => { | ||||
| @@ -436,7 +441,7 @@ export class CertificationSettings extends Component { | ||||
|         <Form | ||||
|           buttonText={fullForm ? 'Claim Certification' : 'Save Progress'} | ||||
|           enableSubmit={fullForm} | ||||
|           formFields={challengeTitles} | ||||
|           formFields={formFields} | ||||
|           hideButton={isCertClaimed} | ||||
|           id={superBlock} | ||||
|           initialValues={{ | ||||
|   | ||||
| @@ -18,8 +18,11 @@ const propTypes = { | ||||
| }; | ||||
|  | ||||
| // back end challenges and front end projects use a single form field | ||||
| const solutionField = ['solution']; | ||||
| const backEndProjectFields = ['solution', 'githubLink']; | ||||
| const solutionField = [{ name: 'solution', label: 'Solution Link' }]; | ||||
| const backEndProjectFields = [ | ||||
|   { name: 'solution', label: 'Solution Link' }, | ||||
|   { name: 'githubLink', label: 'GitHub Link' } | ||||
| ]; | ||||
|  | ||||
| const options = { | ||||
|   types: { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user