Skip to content

Commit

Permalink
feat(ui): implemented form control components
Browse files Browse the repository at this point in the history
  • Loading branch information
akhilmhdh committed Jan 23, 2023
1 parent b80504a commit e7ac74c
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 0 deletions.
40 changes: 40 additions & 0 deletions frontend/src/components/v2/FormControl/FormControl.stories.tsx
Original file line number Diff line number Diff line change
@@ -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
}
};
72 changes: 72 additions & 0 deletions frontend/src/components/v2/FormControl/FormControl.tsx
Original file line number Diff line number Diff line change
@@ -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>
);
};
2 changes: 2 additions & 0 deletions frontend/src/components/v2/FormControl/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export type { FormControlProps, FormHelperTextProps, FormLabelProps } from './FormControl';
export { FormControl, FormHelperText, FormLabel } from './FormControl';
2 changes: 2 additions & 0 deletions frontend/src/components/v2/index.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
export * from './Button';
export * from './FormControl';
export * from './Input';

0 comments on commit e7ac74c

Please sign in to comment.