Repository: marigold-ui/marigold
Pull request #1914 (2022-03-25 11:23:27)

feat: Refactor <Label>
- no variants anymore
- no disabled (not possible anyway)
- improve a11y

Ref #1889

changeset-bot[bot]: ###  ⚠️  No Changeset found

Latest commit: 7092a2a0793298739099e752b606ff264be1eaf3

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/marigold-ui/marigold/new/redo-label?filename=.changeset/six-suns-perform.md&value=---%0A%22%40marigold%2Fcomponents%22%3A%20patch%0A---%0A%0Afeat%3A%20Refactor%20%3CLabel%3E%0A)


sebald: No changeset since it is not publicly available yet.

Patch:
diff --git a/packages/components/src/Field-Next/Label.stories.tsx b/packages/components/src/Field-Next/Label.stories.tsx
new file mode 100644
index 000000000..379892c86
--- /dev/null
+++ b/packages/components/src/Field-Next/Label.stories.tsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import type { Meta, ComponentStory } from '@storybook/react';
+import { Label } from './Label';
+
+export default {
+  title: 'Field/Label',
+  argTypes: {
+    children: {
+      control: {
+        type: 'text',
+      },
+      description: 'Text of the label',
+      defaultValue: 'Label',
+    },
+    required: {
+      control: {
+        type: 'boolean',
+      },
+      description: 'Hint that the related control required',
+      defaultValue: false,
+    },
+  },
+} as Meta;
+
+export const Basic: ComponentStory<typeof Label> = ({ children, ...args }) => (
+  <Label {...args}>{children}</Label>
+);
diff --git a/packages/components/src/Field-Next/Label.tsx b/packages/components/src/Field-Next/Label.tsx
new file mode 100644
index 000000000..07e9fbcd0
--- /dev/null
+++ b/packages/components/src/Field-Next/Label.tsx
@@ -0,0 +1,51 @@
+// TODO: But this back into the root, when we realease all the new fields!
+import React from 'react';
+import { Required } from '@marigold/icons';
+import { ComponentProps } from '@marigold/types';
+
+import { Box } from '../Box';
+
+// Theme Extension
+// ---------------
+export interface LabelThemeExtension<Value> {
+  label?: {
+    [key: string]: Value;
+  };
+}
+
+// Props
+// ---------------
+export interface LabelProps extends ComponentProps<'label'> {
+  as?: 'label' | 'span';
+  required?: boolean;
+}
+
+// Component
+// ---------------
+export interface LabelThemeExtension<Value> {
+  label?: {
+    [key: string]: Value;
+  };
+}
+export const Label: React.FC<LabelProps> = ({
+  as = 'label',
+  required,
+  children,
+  ...props
+}) => {
+  return (
+    <Box
+      {...props}
+      as={as}
+      variant="label"
+      __baseCSS={{ display: 'flex', alignItems: 'center', gap: 4 }}
+    >
+      {children}
+      {/*
+       * aria-required is set on the field and will already be announced,
+       * so we don't need to add it here.
+       */}
+      {required && <Required role="img" size={16} />}
+    </Box>
+  );
+};


Test patch:
diff --git a/packages/components/src/Field-Next/Label.test.tsx b/packages/components/src/Field-Next/Label.test.tsx
new file mode 100644
index 000000000..82b5fb012
--- /dev/null
+++ b/packages/components/src/Field-Next/Label.test.tsx
@@ -0,0 +1,74 @@
+import React from 'react';
+import { render, screen, within } from '@testing-library/react';
+import { ThemeProvider } from '@marigold/system';
+import { Label } from './Label';
+
+const theme = {
+  fonts: {
+    body: 'Inter Regular',
+    label: 'Oswald',
+  },
+  colors: {
+    text: 'black',
+  },
+  label: {
+    fontFamily: 'body',
+    color: 'text',
+  },
+};
+
+test('supports default variant and styles', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Label>label</Label>
+    </ThemeProvider>
+  );
+  const label = screen.getByText(/label/);
+
+  expect(label).toHaveStyle(`font-family: Inter Regular`);
+  expect(label).toHaveStyle(`color: black`);
+});
+
+test('supports htmlFor prop', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Label htmlFor="labelId">label</Label>
+    </ThemeProvider>
+  );
+  const label = screen.getByText(/label/);
+
+  expect(label).toHaveAttribute('for');
+});
+
+test('supports required prop', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Label data-testid="label" required>
+        label
+      </Label>
+    </ThemeProvider>
+  );
+  const label = screen.getByTestId(/label/);
+  const requiredIcon = within(label).getByRole('img');
+  expect(requiredIcon).toBeInTheDocument();
+});
+
+test('renders <label> element by default', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Label>label</Label>
+    </ThemeProvider>
+  );
+  const label = screen.getByText(/label/);
+  expect(label instanceof HTMLLabelElement).toBeTruthy();
+});
+
+test('can render as <span>', () => {
+  render(
+    <ThemeProvider theme={theme}>
+      <Label as="span">label</Label>
+    </ThemeProvider>
+  );
+  const label = screen.getByText(/label/);
+  expect(label instanceof HTMLSpanElement).toBeTruthy();
+});