aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/atoms/forms/label
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/atoms/forms/label')
-rw-r--r--src/components/atoms/forms/label/index.ts1
-rw-r--r--src/components/atoms/forms/label/label.module.scss18
-rw-r--r--src/components/atoms/forms/label/label.stories.tsx119
-rw-r--r--src/components/atoms/forms/label/label.test.tsx9
-rw-r--r--src/components/atoms/forms/label/label.tsx62
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>
+ );
+};