Skip to content

Commit

Permalink
Merge pull request #6752 from guardian/revert-6746-revert-6722-tom-ki…
Browse files Browse the repository at this point in the history
…ng-form-fields-refactor

Revert "Revert "Add validation errors to form fields for callouts""
  • Loading branch information
tkgnm committed Dec 12, 2022
2 parents b823173 + 9e91d8a commit 16afbca
Show file tree
Hide file tree
Showing 9 changed files with 96 additions and 49 deletions.
21 changes: 16 additions & 5 deletions dotcom-rendering/src/web/components/Callout/CheckboxSelect.tsx
@@ -1,17 +1,27 @@
import { Checkbox, CheckboxGroup } from '@guardian/source-react-components';
import { CampaignFieldCheckbox } from '../../../types/content';
import { FieldLabel } from './FieldLabel';
import type { CampaignFieldCheckbox } from 'src/types/content';

type Props = {
formField: CampaignFieldCheckbox;
formData: { [key in string]: string[] };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
validationErrors?: { [key in string]: string };
};

export const CheckboxSelect = ({ formField, formData, setFormData }: Props) => (
export const CheckboxSelect = ({
formField,
formData,
setFormData,
validationErrors,
}: Props) => (
<>
<FieldLabel formField={formField} />
<CheckboxGroup name={formField.name}>
<CheckboxGroup
error={validationErrors?.[formField.id]}
hideLabel={formField.hideLabel}
name={formField.name}
label={formField.label}
supporting={formField.description}
>
{formField.options.map((option, index) => {
// data related to this field is mapped to `formData` using `formField.id`
// We cannot assume that the data exists, so we need to check if `formField.id` key exists in `formData`
Expand Down Expand Up @@ -39,6 +49,7 @@ export const CheckboxSelect = ({ formField, formData, setFormData }: Props) => (
label={option.label}
value={option.value}
checked={isCheckboxChecked}
error={validationErrors?.[formField.id] ? true : false}
onChange={() => {
setFormData({
...formData,
Expand Down
25 changes: 18 additions & 7 deletions dotcom-rendering/src/web/components/Callout/FieldLabel.tsx
@@ -1,6 +1,10 @@
import { css } from '@emotion/react';
import { neutral, textSans } from '@guardian/source-foundations';
import { CampaignFieldType } from '../../../types/content';
import {
neutral,
textSans,
visuallyHidden,
} from '@guardian/source-foundations';
import type { CampaignFieldType } from 'src/types/content';

const fieldLabelStyles = css`
${textSans.medium({ fontWeight: 'bold' })}
Expand All @@ -16,15 +20,22 @@ const optionalTextStyles = css`
padding-left: 5px;
`;

export const FieldLabel = ({ formField }: { formField: CampaignFieldType }) => (
<label css={fieldLabelStyles} htmlFor={formField.name}>
type Props = {
formField: CampaignFieldType;
hideLabel?: boolean;
};

export const FieldLabel = ({ formField, hideLabel }: Props) => (
<label
css={[fieldLabelStyles, hideLabel && visuallyHidden]}
htmlFor={formField.name}
hidden={formField.hideLabel}
>
{formField.label}
{!formField.required && <span css={optionalTextStyles}>Optional</span>}
{!!formField.description && (
<div>
<span css={fieldDescription}>
{`(${formField.description})`}
</span>
<span css={fieldDescription}>{`${formField.description}`}</span>
</div>
)}
</label>
Expand Down
4 changes: 2 additions & 2 deletions dotcom-rendering/src/web/components/Callout/FileUpload.tsx
@@ -1,8 +1,8 @@
import { css } from '@emotion/react';
import { space, text, textSans } from '@guardian/source-foundations';
import { useState } from 'react';
import { CampaignFieldFile } from '../../../types/content';
import { stringifyFileBase64 } from '../../lib/stringifyFileBase64';
import type { CampaignFieldFile } from './../../../types/content';
import { stringifyFileBase64 } from './../../lib/stringifyFileBase64';
import { FieldLabel } from './FieldLabel';

const fileUploadInputStyles = css`
Expand Down
2 changes: 1 addition & 1 deletion dotcom-rendering/src/web/components/Callout/Form.tsx
Expand Up @@ -2,7 +2,7 @@ import { css } from '@emotion/react';
import { text, textSans } from '@guardian/source-foundations';
import { Button, Link } from '@guardian/source-react-components';
import { useState } from 'react';
import { CampaignFieldType } from '../../../types/content';
import type { CampaignFieldType } from '../../../types/content';
import { FileUpload } from './FileUpload';
import { MultiSelect } from './MultiSelect';
import { Select } from './Select';
Expand Down
6 changes: 5 additions & 1 deletion dotcom-rendering/src/web/components/Callout/MultiSelect.tsx
@@ -1,18 +1,20 @@
import type {
CampaignFieldCheckbox,
CampaignFieldRadio,
} from '../../../types/content';
} from 'src/types/content';
import { CheckboxSelect } from './CheckboxSelect';
import { RadioSelect } from './RadioSelect';

type Props = {
validationErrors?: { [key in string]: string };
formField: CampaignFieldCheckbox | CampaignFieldRadio;
formData: { [key in string]: any };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
multiple: boolean;
};

export const MultiSelect = ({
validationErrors,
formField,
formData,
setFormData,
Expand All @@ -21,12 +23,14 @@ export const MultiSelect = ({
<div data-testid={`form-field-${formField.id}`}>
{multiple ? (
<CheckboxSelect
validationErrors={validationErrors ?? undefined}
formField={formField as CampaignFieldCheckbox}
formData={formData}
setFormData={setFormData}
/>
) : (
<RadioSelect
validationErrors={validationErrors ?? undefined}
formField={formField as CampaignFieldRadio}
formData={formData}
setFormData={setFormData}
Expand Down
5 changes: 4 additions & 1 deletion dotcom-rendering/src/web/components/Callout/RadioSelect.tsx
@@ -1,15 +1,17 @@
import { css } from '@emotion/react';
import { Radio, RadioGroup } from '@guardian/source-react-components';
import type { CampaignFieldRadio } from '../../../types/content';
import type { CampaignFieldRadio } from 'src/types/content';
import { FieldLabel } from './FieldLabel';

type FieldProp = {
validationErrors?: { [key in string]: string };
formField: CampaignFieldRadio;
formData: { [key in string]: any };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
};

export const RadioSelect = ({
validationErrors,
formField,
formData,
setFormData,
Expand All @@ -25,6 +27,7 @@ export const RadioSelect = ({
>
<FieldLabel formField={formField} />
<RadioGroup
error={validationErrors?.[formField.id]}
name={formField.name}
orientation={
formField.options.length > 2 ? 'vertical' : 'horizontal'
Expand Down
57 changes: 29 additions & 28 deletions dotcom-rendering/src/web/components/Callout/Select.tsx
@@ -1,36 +1,37 @@
import { CampaignFieldSelect } from '../../../types/content';
import { FieldLabel } from './FieldLabel';
import { Select as SourceSelect } from '@guardian/source-react-components';
import type { CampaignFieldSelect } from 'src/types/content';

type Props = {
validationErrors?: { [key in string]: string };
formField: CampaignFieldSelect;
formData: { [key in string]: any };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
};

export const Select = ({ formField, formData, setFormData }: Props) => (
<>
<FieldLabel formField={formField} />
<select
data-testid={`form-field-${formField.id}`}
required={formField.required}
value={
formField.id && formField.id in formData
? formData[formField.id]
: ''
}
onChange={(e) =>
setFormData({
...formData,
[formField.id]: e.target.value,
})
}
>
{formField.options &&
formField.options.map((option, index) => (
<option key={index} value={option.value}>
{option.value}
</option>
))}
</select>
</>
export const Select = ({
validationErrors,
formField,
formData,
setFormData,
}: Props) => (
<SourceSelect
hideLabel={formField.hideLabel}
data-testid={`form-field-${formField.id}`}
error={validationErrors?.[formField.id]}
label={formField.label}
supporting={formField.description}
value={formField.id in formData ? formData[formField.id] : ''}
onChange={(e) =>
setFormData({
...formData,
[formField.id]: e.target.value,
})
}
optional={!formField.required}
children={formField.options.map((option, index) => (
<option key={index} value={option.value}>
{option.value}
</option>
))}
/>
);
13 changes: 11 additions & 2 deletions dotcom-rendering/src/web/components/Callout/TextArea.tsx
@@ -1,27 +1,36 @@
import { css } from '@emotion/react';
import { space } from '@guardian/source-foundations';
import { TextArea as SourceTextArea } from '@guardian/source-react-components';
import { CampaignFieldTextArea } from '../../../types/content';
import type { CampaignFieldTextArea } from 'src/types/content';

const textAreaStyles = css`
width: 100%;
margin-top: ${space[2]}px;
`;

type Props = {
validationErrors?: { [key in string]: string };
formField: CampaignFieldTextArea;
formData: { [key in string]: any };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
};

export const TextArea = ({ formField, formData, setFormData }: Props) => (
export const TextArea = ({
validationErrors,
formField,
formData,
setFormData,
}: Props) => (
<>
<SourceTextArea
hideLabel={formField.hideLabel}
data-testid={`form-field-${formField.id}`}
label={formField.label}
supporting={formField.description}
css={textAreaStyles}
optional={!formField.required}
value={formField.id in formData ? formData[formField.id] : ''}
error={validationErrors?.[formField.id]}
onChange={(e) =>
setFormData({
...formData,
Expand Down
12 changes: 10 additions & 2 deletions dotcom-rendering/src/web/components/Callout/TextInput.tsx
@@ -1,20 +1,28 @@
import { css } from '@emotion/react';
import { space } from '@guardian/source-foundations';
import { TextInput as SourceTextInput } from '@guardian/source-react-components';
import { CampaignFieldText } from '../../../types/content';
import type { CampaignFieldText } from 'src/types/content';

const textInputStyles = css`
margin-top: ${space[2]}px;
`;

type Props = {
validationErrors?: { [key in string]: string };
formField: CampaignFieldText;
formData: { [key in string]: any };
setFormData: React.Dispatch<React.SetStateAction<{ [x: string]: any }>>;
};

export const TextInput = ({ formField, formData, setFormData }: Props) => (
export const TextInput = ({
validationErrors,
formField,
formData,
setFormData,
}: Props) => (
<SourceTextInput
hideLabel={formField.hideLabel}
error={validationErrors?.[formField.id]}
css={textInputStyles}
data-testid={`form-field-${formField.id}`}
type={formField.type}
Expand Down

0 comments on commit 16afbca

Please sign in to comment.