From 64570357f9608ad6638b1f8cc283ee9dd1cc3264 Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 15 Apr 2022 14:49:41 +0200 Subject: chore: add a ContactForm component --- src/components/atoms/layout/notice.tsx | 5 + .../organisms/forms/contact-form.module.scss | 8 ++ .../organisms/forms/contact-form.stories.tsx | 59 ++++++++ .../organisms/forms/contact-form.test.tsx | 46 +++++++ src/components/organisms/forms/contact-form.tsx | 150 +++++++++++++++++++++ 5 files changed, 268 insertions(+) create mode 100644 src/components/organisms/forms/contact-form.module.scss create mode 100644 src/components/organisms/forms/contact-form.stories.tsx create mode 100644 src/components/organisms/forms/contact-form.test.tsx create mode 100644 src/components/organisms/forms/contact-form.tsx (limited to 'src') diff --git a/src/components/atoms/layout/notice.tsx b/src/components/atoms/layout/notice.tsx index e919182..b6e09c5 100644 --- a/src/components/atoms/layout/notice.tsx +++ b/src/components/atoms/layout/notice.tsx @@ -14,6 +14,11 @@ export type NoticeProps = { message: string; }; +/** + * Notice component + * + * Render a colored message depending on notice kind. + */ const Notice: VFC = ({ kind, message }) => { const kindClass = `wrapper--${kind}`; diff --git a/src/components/organisms/forms/contact-form.module.scss b/src/components/organisms/forms/contact-form.module.scss new file mode 100644 index 0000000..f3f2646 --- /dev/null +++ b/src/components/organisms/forms/contact-form.module.scss @@ -0,0 +1,8 @@ +.field { + width: 100%; +} + +.button { + display: block; + margin: auto; +} diff --git a/src/components/organisms/forms/contact-form.stories.tsx b/src/components/organisms/forms/contact-form.stories.tsx new file mode 100644 index 0000000..2c8ab32 --- /dev/null +++ b/src/components/organisms/forms/contact-form.stories.tsx @@ -0,0 +1,59 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { IntlProvider } from 'react-intl'; +import ContactFormComponent from './contact-form'; + +export default { + title: 'Organisms/Forms', + component: ContactFormComponent, + argTypes: { + className: { + control: { + type: 'text', + }, + description: 'Set additional classnames to the form wrapper.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, + Notice: { + control: { + type: null, + }, + description: 'A component to display a success or error message.', + table: { + category: 'Options', + }, + type: { + name: 'function', + required: false, + }, + }, + sendMail: { + control: { + type: null, + }, + description: 'A callback function to process the contact form data.', + type: { + name: 'function', + required: true, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + + + +); + +export const ContactForm = Template.bind({}); +ContactForm.args = { + sendMail: (reset: () => void) => { + reset(); + }, +}; diff --git a/src/components/organisms/forms/contact-form.test.tsx b/src/components/organisms/forms/contact-form.test.tsx new file mode 100644 index 0000000..744f147 --- /dev/null +++ b/src/components/organisms/forms/contact-form.test.tsx @@ -0,0 +1,46 @@ +import { render, screen } from '@test-utils'; +import ContactForm from './contact-form'; + +const props = { + sendMail: () => null, +}; + +describe('ContactForm', () => { + it('renders a contact form', () => { + render(); + expect( + screen.getByRole('form', { name: 'Contact form' }) + ).toBeInTheDocument(); + }); + + it('renders a name field', () => { + render(); + expect(screen.getByRole('textbox', { name: /^Name:/ })).toBeInTheDocument(); + }); + + it('renders an email field', () => { + render(); + expect( + screen.getByRole('textbox', { name: /^Email:/ }) + ).toBeInTheDocument(); + }); + + it('renders an object field', () => { + render(); + expect( + screen.getByRole('textbox', { name: /^Object:/ }) + ).toBeInTheDocument(); + }); + + it('renders a message field', () => { + render(); + expect( + screen.getByRole('textbox', { name: /^Message:/ }) + ).toBeInTheDocument(); + }); + + it('renders a submit button', () => { + render(); + expect(screen.getByRole('button', { name: /^Send/ })).toBeInTheDocument(); + }); +}); diff --git a/src/components/organisms/forms/contact-form.tsx b/src/components/organisms/forms/contact-form.tsx new file mode 100644 index 0000000..994244a --- /dev/null +++ b/src/components/organisms/forms/contact-form.tsx @@ -0,0 +1,150 @@ +import Button from '@components/atoms/buttons/button'; +import Form from '@components/atoms/forms/form'; +import Spinner from '@components/atoms/loaders/spinner'; +import LabelledField from '@components/molecules/forms/labelled-field'; +import { ReactNode, useState, VFC } from 'react'; +import { useIntl } from 'react-intl'; +import styles from './contact-form.module.scss'; + +export type ContactFormProps = { + /** + * Set additional classnames to the form wrapper. + */ + className?: string; + /** + * Pass a component to print a success/error message. + */ + Notice?: ReactNode; + /** + * A callback function to send mail. It takes a function as parameter to + * reset the form. + */ + sendMail: (reset: () => void) => void; +}; + +/** + * ContactForm component + * + * Render a contact form. + */ +const ContactForm: VFC = ({ + className = '', + Notice, + sendMail, +}) => { + const intl = useIntl(); + const [name, setName] = useState(''); + const [email, setEmail] = useState(''); + const [object, setObject] = useState(''); + const [message, setMessage] = useState(''); + const [isSubmitting, setIsSubmitting] = useState(false); + + /** + * Reset all the form fields. + */ + const resetForm = () => { + setName(''); + setEmail(''); + setObject(''); + setMessage(''); + setIsSubmitting(false); + }; + + const formName = intl.formatMessage({ + defaultMessage: 'Contact form', + description: 'ContactForm: form accessible name', + id: 'HFdzae', + }); + + const nameLabel = intl.formatMessage({ + defaultMessage: 'Name:', + description: 'ContactForm: name label', + id: '1dCuCx', + }); + + const emailLabel = intl.formatMessage({ + defaultMessage: 'Email:', + description: 'ContactForm: email label', + id: 'w4B5PA', + }); + + const objectLabel = intl.formatMessage({ + defaultMessage: 'Object:', + description: 'ContactForm: object label', + id: 's8/tyz', + }); + + const messageLabel = intl.formatMessage({ + defaultMessage: 'Message:', + description: 'ContactForm: message label', + id: 'yN5P+m', + }); + + const submitHandler = () => { + setIsSubmitting(true); + sendMail(resetForm); + }; + + return ( +
+ + + + + + {isSubmitting && ( + + )} + {Notice} + + ); +}; + +export default ContactForm; -- cgit v1.2.3