feat(ui): implemented form control components

This commit is contained in:
akhilmhdh
2023-01-19 23:16:59 +05:30
parent b80504ae00
commit e7ac74c5a0
4 changed files with 116 additions and 0 deletions

View File

@ -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<typeof FormControl> = {
title: 'Components/FormControl',
component: FormControl,
tags: ['v2'],
argTypes: {}
};
export default meta;
type Story = StoryObj<typeof FormControl>;
// More on writing stories with args: https://storybook.js.org/docs/7.0/react/writing-stories/args
export const Basic: Story = {
args: {
children: <Input />,
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
}
};

View File

@ -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.Root className="text-mineshaft-300 text-sm font-medium block mb-1 ml-0.5" htmlFor={id}>
{label}
{isRequired && <span className="text-red ml-1">*</span>}
</Label.Root>
);
export type FormHelperTextProps = {
isError?: boolean;
text?: ReactNode;
};
export const FormHelperText = ({ isError, text }: FormHelperTextProps) => (
<div
className={twMerge(
'text-xs flex items-center opacity-90 text-mineshaft-300 mt-2',
isError && 'text-red-600'
)}
>
{isError && (
<span>
<FontAwesomeIcon icon={faExclamationTriangle} size="sm" className="mr-1" />
</span>
)}
<span>{text}</span>
</div>
);
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 (
<div>
{typeof label === 'string' ? (
<FormLabel label={label} isRequired={isRequired} id={id} />
) : (
label
)}
{cloneElement(children, { isRequired, 'data-required': isRequired, isError })}
{!isError && helperText && <FormHelperText isError={isError} text={helperText} />}
{isError && errorText && <FormHelperText isError={isError} text={errorText} />}
</div>
);
};

View File

@ -0,0 +1,2 @@
export type { FormControlProps, FormHelperTextProps, FormLabelProps } from './FormControl';
export { FormControl, FormHelperText, FormLabel } from './FormControl';

View File

@ -1 +1,3 @@
export * from './Button';
export * from './FormControl';
export * from './Input';