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