From e7ac74c5a0b73bf63ba6703e4d4f68da4ec549b5 Mon Sep 17 00:00:00 2001 From: akhilmhdh Date: Thu, 19 Jan 2023 23:16:59 +0530 Subject: [PATCH] feat(ui): implemented form control components --- .../v2/FormControl/FormControl.stories.tsx | 40 +++++++++++ .../components/v2/FormControl/FormControl.tsx | 72 +++++++++++++++++++ .../src/components/v2/FormControl/index.tsx | 2 + frontend/src/components/v2/index.tsx | 2 + 4 files changed, 116 insertions(+) create mode 100644 frontend/src/components/v2/FormControl/FormControl.stories.tsx create mode 100644 frontend/src/components/v2/FormControl/FormControl.tsx create mode 100644 frontend/src/components/v2/FormControl/index.tsx diff --git a/frontend/src/components/v2/FormControl/FormControl.stories.tsx b/frontend/src/components/v2/FormControl/FormControl.stories.tsx new file mode 100644 index 0000000000..2e36922289 --- /dev/null +++ b/frontend/src/components/v2/FormControl/FormControl.stories.tsx @@ -0,0 +1,40 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +// Be careful on dep cycle +import { Input } from '../Input/Input'; +import { FormControl } from './FormControl'; + +const meta: Meta = { + title: 'Components/FormControl', + component: FormControl, + tags: ['v2'], + argTypes: {} +}; + +export default meta; +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args +export const Basic: Story = { + args: { + children: , + label: 'Email', + id: 'email', + helperText: 'Type something..' + } +}; + +export const RequiredInput: Story = { + args: { + ...Basic.args, + isRequired: true + } +}; + +export const ErrorInput: Story = { + args: { + ...Basic.args, + errorText: 'Some random error', + isError: true + } +}; diff --git a/frontend/src/components/v2/FormControl/FormControl.tsx b/frontend/src/components/v2/FormControl/FormControl.tsx new file mode 100644 index 0000000000..d609b24f40 --- /dev/null +++ b/frontend/src/components/v2/FormControl/FormControl.tsx @@ -0,0 +1,72 @@ +import { cloneElement, ReactNode } from 'react'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import * as Label from '@radix-ui/react-label'; +import { twMerge } from 'tailwind-merge'; + +export type FormLabelProps = { + id?: string; + isRequired?: boolean; + label?: ReactNode; +}; + +export const FormLabel = ({ id, label, isRequired }: FormLabelProps) => ( + + {label} + {isRequired && *} + +); + +export type FormHelperTextProps = { + isError?: boolean; + text?: ReactNode; +}; + +export const FormHelperText = ({ isError, text }: FormHelperTextProps) => ( +
+ {isError && ( + + + + )} + {text} +
+); + +export type FormControlProps = { + id?: string; + isRequired?: boolean; + isError?: boolean; + label?: ReactNode; + helperText?: ReactNode; + errorText?: ReactNode; + children: JSX.Element; +}; + +export const FormControl = ({ + children, + isRequired, + label, + helperText, + errorText, + id, + isError +}: FormControlProps): JSX.Element => { + return ( +
+ {typeof label === 'string' ? ( + + ) : ( + label + )} + {cloneElement(children, { isRequired, 'data-required': isRequired, isError })} + {!isError && helperText && } + {isError && errorText && } +
+ ); +}; diff --git a/frontend/src/components/v2/FormControl/index.tsx b/frontend/src/components/v2/FormControl/index.tsx new file mode 100644 index 0000000000..806103759c --- /dev/null +++ b/frontend/src/components/v2/FormControl/index.tsx @@ -0,0 +1,2 @@ +export type { FormControlProps, FormHelperTextProps, FormLabelProps } from './FormControl'; +export { FormControl, FormHelperText, FormLabel } from './FormControl'; diff --git a/frontend/src/components/v2/index.tsx b/frontend/src/components/v2/index.tsx index 8b166a86e4..71d91fc1e0 100644 --- a/frontend/src/components/v2/index.tsx +++ b/frontend/src/components/v2/index.tsx @@ -1 +1,3 @@ export * from './Button'; +export * from './FormControl'; +export * from './Input';