diff options
Diffstat (limited to 'src/components/atoms/forms/label')
| -rw-r--r-- | src/components/atoms/forms/label/index.ts | 1 | ||||
| -rw-r--r-- | src/components/atoms/forms/label/label.module.scss | 18 | ||||
| -rw-r--r-- | src/components/atoms/forms/label/label.stories.tsx | 119 | ||||
| -rw-r--r-- | src/components/atoms/forms/label/label.test.tsx | 9 | ||||
| -rw-r--r-- | src/components/atoms/forms/label/label.tsx | 62 |
5 files changed, 209 insertions, 0 deletions
diff --git a/src/components/atoms/forms/label/index.ts b/src/components/atoms/forms/label/index.ts new file mode 100644 index 0000000..301fbde --- /dev/null +++ b/src/components/atoms/forms/label/index.ts @@ -0,0 +1 @@ +export * from './label'; diff --git a/src/components/atoms/forms/label/label.module.scss b/src/components/atoms/forms/label/label.module.scss new file mode 100644 index 0000000..21ba9d3 --- /dev/null +++ b/src/components/atoms/forms/label/label.module.scss @@ -0,0 +1,18 @@ +.label { + color: var(--color-primary-darker); + font-weight: 600; + cursor: pointer; + + &--sm { + font-size: var(--font-size-sm); + font-variant: small-caps; + } + + &--md { + font-size: var(--font-size-md); + } +} + +.required { + color: var(--color-secondary); +} diff --git a/src/components/atoms/forms/label/label.stories.tsx b/src/components/atoms/forms/label/label.stories.tsx new file mode 100644 index 0000000..8460c45 --- /dev/null +++ b/src/components/atoms/forms/label/label.stories.tsx @@ -0,0 +1,119 @@ +import { ComponentMeta, ComponentStory } from '@storybook/react'; +import { Label as LabelComponent } from './label'; + +/** + * Label - Storybook Meta + */ +export default { + title: 'Atoms/Forms', + component: LabelComponent, + args: { + isHidden: false, + isRequired: false, + size: 'sm', + }, + argTypes: { + 'aria-label': { + control: { + type: 'text', + }, + description: 'Define an accessible name.', + table: { + category: 'Accessibility', + }, + type: { + name: 'string', + required: false, + }, + }, + className: { + control: { + type: 'text', + }, + description: 'Add classnames to the label.', + table: { + category: 'Styles', + }, + type: { + name: 'string', + required: false, + }, + }, + children: { + control: { + type: 'text', + }, + description: 'The label body.', + type: { + name: 'string', + required: true, + }, + }, + htmlFor: { + control: { + type: 'text', + }, + description: 'The field id.', + type: { + name: 'string', + required: true, + }, + }, + isHidden: { + control: { + type: 'boolean', + }, + description: 'Set to true if the label should be visually hidden.', + table: { + category: 'Options', + defaultValue: { summary: false }, + }, + type: { + name: 'boolean', + required: false, + }, + }, + isRequired: { + control: { + type: 'boolean', + }, + description: 'Set to true if the field is required.', + table: { + category: 'Options', + defaultValue: { summary: false }, + }, + type: { + name: 'boolean', + required: false, + }, + }, + size: { + control: { + type: 'select', + }, + description: 'The label size.', + options: ['md', 'sm'], + table: { + category: 'Options', + defaultValue: { summary: 'sm' }, + }, + type: { + name: 'string', + required: false, + }, + }, + }, +} as ComponentMeta<typeof LabelComponent>; + +const Template: ComponentStory<typeof LabelComponent> = ({ + children, + ...args +}) => <LabelComponent {...args}>{children}</LabelComponent>; + +/** + * Label Story + */ +export const Label = Template.bind({}); +Label.args = { + children: 'A label', +}; diff --git a/src/components/atoms/forms/label/label.test.tsx b/src/components/atoms/forms/label/label.test.tsx new file mode 100644 index 0000000..afdbb94 --- /dev/null +++ b/src/components/atoms/forms/label/label.test.tsx @@ -0,0 +1,9 @@ +import { render, screen } from '../../../../../tests/utils'; +import { Label } from './label'; + +describe('Label', () => { + it('renders a field label', () => { + render(<Label>A label</Label>); + expect(screen.getByText('A label')).toBeInTheDocument(); + }); +}); diff --git a/src/components/atoms/forms/label/label.tsx b/src/components/atoms/forms/label/label.tsx new file mode 100644 index 0000000..5087325 --- /dev/null +++ b/src/components/atoms/forms/label/label.tsx @@ -0,0 +1,62 @@ +import { FC, LabelHTMLAttributes, ReactNode } from 'react'; +import styles from './label.module.scss'; + +export type LabelSize = 'md' | 'sm'; + +export type LabelProps = Omit< + LabelHTMLAttributes<HTMLLabelElement>, + 'hidden' | 'size' +> & { + /** + * The label body. + */ + children: ReactNode; + /** + * Should the label be hidden? + * + * @default false + */ + isHidden?: boolean; + /** + * Is the field required? + * + * @default false + */ + isRequired?: boolean; + /** + * The label size. + * + * @default 'sm' + */ + size?: LabelSize; +}; + +/** + * Label Component + * + * Render a HTML label element. + */ +export const Label: FC<LabelProps> = ({ + children, + className = '', + isHidden = false, + isRequired = false, + size = 'sm', + ...props +}) => { + const visibilityClass = isHidden ? 'screen-reader-text' : ''; + const sizeClass = styles[`label--${size}`]; + const labelClass = `${styles.label} ${sizeClass} ${visibilityClass} ${className}`; + const requiredSymbol = ' *'; + + return ( + <label {...props} className={labelClass}> + {children} + {isRequired ? ( + <span aria-hidden className={styles.required}> + {requiredSymbol} + </span> + ) : null} + </label> + ); +}; |
