Skip to content

Commit

Permalink
WSTEAM1-851 - UGC form state management (#11425)
Browse files Browse the repository at this point in the history
  • Loading branch information
amoore108 authored Mar 27, 2024
1 parent c114689 commit 2c07eda
Show file tree
Hide file tree
Showing 18 changed files with 576 additions and 160 deletions.
7 changes: 7 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,13 @@ module.exports = {
}),
);

config.resolve.fallback = {
...config.resolve.fallback,
fs: false,
stream: false,
zlib: false,
};

config.resolve.extensions.push('.js', '.jsx', '.ts', '.tsx'); // resolves `import '../Foo'` to `../Foo/index.jsx`
config.resolve.alias = {
...config.resolve.alias,
Expand Down
Binary file not shown.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
"@types/react-router-dom": "5.3.3",
"@types/testing-library__react": "10.2.0",
"@types/url-parse": "^1.4.8",
"@types/uuid": "^9.0.8",
"@typescript-eslint/eslint-plugin": "5.59.6",
"@typescript-eslint/parser": "5.59.6",
"amphtml-validator": "1.0.35",
Expand Down
37 changes: 37 additions & 0 deletions ws-nextjs-app/pages/[service]/send/[id]/Form/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/** @jsx jsx */
import React from 'react';
import { jsx } from '@emotion/react';
import { useFormContext } from '../FormContext';
import { Field } from '../types';
import FormField from '../FormField';
import styles from './styles';
import Submit from '../SubmitButton';

export default function Form({ fields }: { fields: Field[] }) {
const { handleSubmit, submissionError } = useFormContext();

const formFields = fields?.map(({ id, label, type, htmlType, textArea }) => (
<FormField
key={id}
id={id}
label={label}
type={type}
htmlType={htmlType}
textArea={textArea}
/>
));

return (
<>
<form onSubmit={handleSubmit}>
{formFields}
<Submit />
</form>
{submissionError && (
<div css={styles.submissionError}>
{`${submissionError.message} - ${submissionError.status}`}
</div>
)}
</>
);
}
12 changes: 12 additions & 0 deletions ws-nextjs-app/pages/[service]/send/[id]/Form/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { css } from '@emotion/react';

export default {
submissionError: () =>
css({
color: 'black',
fontFamily: 'sans-serif',
backgroundColor: 'pink',
padding: '1rem',
margin: '1rem 0',
}),
};
104 changes: 104 additions & 0 deletions ws-nextjs-app/pages/[service]/send/[id]/FormContext/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, {
createContext,
FormEvent,
PropsWithChildren,
useContext,
useState,
} from 'react';
import { FetchError } from '#app/models/types/fetch';
import { v4 as uuid } from 'uuid';

import { useRouter } from 'next/router';
import { OK } from '#app/lib/statusCodes.const';
import {
Field,
OnChangeHandler,
OnChangeInputName,
OnChangeInputValue,
} from '../types';

type SubmissionError = {
message: string;
status: number;
} | null;

type ContextProps = {
formState: Record<OnChangeInputName, OnChangeInputValue | null>;
handleChange: OnChangeHandler;
handleSubmit: (event: FormEvent) => Promise<void>;
submissionError?: SubmissionError;
};

const FormContext = createContext({} as ContextProps);

const getInitialFormState = (
fields: Field[],
): Record<OnChangeInputName, OnChangeInputValue | null> =>
fields?.reduce((acc, field) => ({ ...acc, [field.id]: null }), {});

export const FormContextProvider = ({
fields,
children,
}: PropsWithChildren<{ fields: Field[] }>) => {
const {
query: { id },
} = useRouter();

const [formState, setFormState] = useState(getInitialFormState(fields));
const [submissionError, setSubmissionError] = useState<SubmissionError>(null);

const handleChange = (name: OnChangeInputName, value: OnChangeInputValue) => {
setFormState(prevState => {
return { ...prevState, [name]: value };
});
};

const handleSubmit = async (event: FormEvent) => {
event.preventDefault();

// Reset error state
setSubmissionError(null);

const formData = new FormData();

// TODO: This is a mock data, we should use the formState instead
Object.entries({ surname: 'BBC TEST NAME' }).forEach(([key, value]) => {
formData.append(key, value);
});

try {
const url = `https://www.bbc.com/ugc/send/${id}?said=${uuid()}`;

const req = new XMLHttpRequest();
req.open('POST', url, true);

req.onreadystatechange = () => {
if (req.readyState === XMLHttpRequest.DONE) {
if (req.status !== OK) {
setSubmissionError({
message: req.responseText,
status: req.status,
});
}
}
};

req.send(formData);
} catch (error) {
const { message, status } = error as FetchError;
setSubmissionError({ message, status });
}
};

return (
<FormContext.Provider
value={{ formState, handleChange, handleSubmit, submissionError }}
>
{children}
</FormContext.Provider>
);
};

export function useFormContext() {
return useContext(FormContext);
}
Loading

0 comments on commit 2c07eda

Please sign in to comment.