From a6ff5eee45215effb3344cb5d631a27a7c0369aa Mon Sep 17 00:00:00 2001 From: Armand Philippot Date: Fri, 22 Sep 2023 19:34:01 +0200 Subject: refactor(components): rewrite form components --- .../forms/comment-form/comment-form.module.scss | 18 ++ .../forms/comment-form/comment-form.stories.tsx | 123 ++++++++++ .../forms/comment-form/comment-form.test.tsx | 23 ++ .../organisms/forms/comment-form/comment-form.tsx | 251 +++++++++++++++++++++ .../organisms/forms/comment-form/index.ts | 1 + 5 files changed, 416 insertions(+) create mode 100644 src/components/organisms/forms/comment-form/comment-form.module.scss create mode 100644 src/components/organisms/forms/comment-form/comment-form.stories.tsx create mode 100644 src/components/organisms/forms/comment-form/comment-form.test.tsx create mode 100644 src/components/organisms/forms/comment-form/comment-form.tsx create mode 100644 src/components/organisms/forms/comment-form/index.ts (limited to 'src/components/organisms/forms/comment-form') diff --git a/src/components/organisms/forms/comment-form/comment-form.module.scss b/src/components/organisms/forms/comment-form/comment-form.module.scss new file mode 100644 index 0000000..fbf8c96 --- /dev/null +++ b/src/components/organisms/forms/comment-form/comment-form.module.scss @@ -0,0 +1,18 @@ +.form { + display: flex; + flex-flow: column wrap; + gap: var(--spacing-xs); + + > * { + max-width: 45ch; + } +} + +.field { + width: 100%; +} + +.button { + display: block; + margin: var(--spacing-sm) auto 0; +} diff --git a/src/components/organisms/forms/comment-form/comment-form.stories.tsx b/src/components/organisms/forms/comment-form/comment-form.stories.tsx new file mode 100644 index 0000000..a6069e6 --- /dev/null +++ b/src/components/organisms/forms/comment-form/comment-form.stories.tsx @@ -0,0 +1,123 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { CommentForm } from './comment-form'; + +const saveComment = async () => { + /** Do nothing. */ +}; + +/** + * CommentForm - Storybook Meta + */ +export default { + title: 'Organisms/Forms', + component: CommentForm, + args: { + saveComment, + titleAlignment: 'left', + titleLevel: 2, + }, + 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, + }, + }, + parentId: { + control: { + type: null, + }, + description: 'The parent id if it is a reply.', + type: { + name: 'number', + required: false, + }, + }, + saveComment: { + control: { + type: null, + }, + description: 'A callback function to process the comment form data.', + table: { + category: 'Events', + }, + type: { + name: 'function', + required: true, + }, + }, + title: { + control: { + type: 'text', + }, + description: 'The form title.', + table: { + category: 'Options', + }, + type: { + name: 'string', + required: false, + }, + }, + titleAlignment: { + control: { + type: 'select', + }, + description: 'The heading alignment.', + options: ['center', 'left'], + table: { + category: 'Options', + defaultValue: { summary: 'left' }, + }, + type: { + name: 'string', + required: false, + }, + }, + titleLevel: { + control: { + type: 'number', + min: 1, + max: 6, + }, + description: 'The title level (hn).', + table: { + category: 'Options', + defaultValue: { summary: 2 }, + }, + type: { + name: 'number', + required: false, + }, + }, + }, +} as ComponentMeta; + +const Template: ComponentStory = (args) => ( + +); + +/** + * Forms Stories - Comment + */ +export const Comment = Template.bind({}); diff --git a/src/components/organisms/forms/comment-form/comment-form.test.tsx b/src/components/organisms/forms/comment-form/comment-form.test.tsx new file mode 100644 index 0000000..8aa38af --- /dev/null +++ b/src/components/organisms/forms/comment-form/comment-form.test.tsx @@ -0,0 +1,23 @@ +import { render, screen } from '../../../../../tests/utils'; +import { CommentForm } from './comment-form'; + +const saveComment = async () => { + /** Do nothing. */ +}; +const title = 'Cum voluptas voluptatibus'; + +describe('CommentForm', () => { + it('renders a form', () => { + render(); + expect(screen.getByRole('form')).toBeInTheDocument(); + }); + + it('renders an optional title', () => { + render( + + ); + expect( + screen.getByRole('heading', { level: 2, name: title }) + ).toBeInTheDocument(); + }); +}); diff --git a/src/components/organisms/forms/comment-form/comment-form.tsx b/src/components/organisms/forms/comment-form/comment-form.tsx new file mode 100644 index 0000000..be5d58f --- /dev/null +++ b/src/components/organisms/forms/comment-form/comment-form.tsx @@ -0,0 +1,251 @@ +import { ChangeEvent, FC, FormEvent, ReactNode, useState } from 'react'; +import { useIntl } from 'react-intl'; +import { + Button, + Form, + type FormProps, + Heading, + type HeadingLevel, + type HeadingProps, + Spinner, + Input, + TextArea, + Label, +} from '../../../atoms'; +import { LabelledField } from '../../../molecules'; +import styles from './comment-form.module.scss'; + +export type CommentFormData = { + author: string; + comment: string; + email: string; + parentId?: number; + website?: string; +}; + +export type CommentFormProps = Pick & { + /** + * Pass a component to print a success/error message. + */ + Notice?: ReactNode; + /** + * The comment parent id. + */ + parentId?: number; + /** + * A callback function to save comment. It takes a function as parameter to + * reset the form. + */ + saveComment: (data: CommentFormData, reset: () => void) => Promise; + /** + * The form title. + */ + title?: string; + /** + * The form title alignment. Default: left. + */ + titleAlignment?: HeadingProps['alignment']; + /** + * The title level. Default: 2. + */ + titleLevel?: HeadingLevel; +}; + +export const CommentForm: FC = ({ + className = '', + Notice, + parentId, + saveComment, + title, + titleAlignment, + titleLevel = 2, + ...props +}) => { + const formClass = `${styles.form} ${className}`; + const intl = useIntl(); + const emptyForm: CommentFormData = { + author: '', + comment: '', + email: '', + parentId, + website: '', + }; + const [data, setData] = useState(emptyForm); + const [isSubmitting, setIsSubmitting] = useState(false); + + /** + * Reset all the form fields. + */ + const resetForm = () => { + setData(emptyForm); + setIsSubmitting(false); + }; + + const nameLabel = intl.formatMessage({ + defaultMessage: 'Name:', + description: 'CommentForm: name label', + id: 'ZIrTee', + }); + + const emailLabel = intl.formatMessage({ + defaultMessage: 'Email:', + description: 'CommentForm: email label', + id: 'Bh7z5v', + }); + + const websiteLabel = intl.formatMessage({ + defaultMessage: 'Website:', + description: 'CommentForm: website label', + id: 'u41qSk', + }); + + const commentLabel = intl.formatMessage({ + defaultMessage: 'Comment:', + description: 'CommentForm: comment label', + id: 'A8hGaK', + }); + + const formTitle = intl.formatMessage({ + defaultMessage: 'Comment form', + description: 'CommentForm: aria label', + id: 'dz2kDV', + }); + + const formAriaLabel = title ? undefined : formTitle; + const formId = 'comment-form-title'; + const formLabelledBy = title ? formId : undefined; + + const updateForm = ( + e: ChangeEvent + ) => { + switch (e.target.name) { + case 'author': + setData((prevData) => { + return { ...prevData, author: e.target.value }; + }); + break; + case 'comment': + setData((prevData) => { + return { ...prevData, comment: e.target.value }; + }); + break; + case 'email': + setData((prevData) => { + return { ...prevData, email: e.target.value }; + }); + break; + case 'website': + setData((prevData) => { + return { ...prevData, website: e.target.value }; + }); + break; + default: + break; + } + }; + + const submitHandler = (e: FormEvent) => { + e.preventDefault(); + setIsSubmitting(true); + saveComment(data, resetForm).then(() => setIsSubmitting(false)); + }; + + return ( +
+ {title && ( + + {title} + + )} + + } + label={ + + } + /> + + } + label={ + + } + /> + + } + label={} + /> + + } + label={ + + } + /> + + {isSubmitting && ( + + )} + {Notice} + + ); +}; diff --git a/src/components/organisms/forms/comment-form/index.ts b/src/components/organisms/forms/comment-form/index.ts new file mode 100644 index 0000000..9e22bd9 --- /dev/null +++ b/src/components/organisms/forms/comment-form/index.ts @@ -0,0 +1 @@ +export * from './comment-form'; -- cgit v1.2.3