Skip to content

Commit

Permalink
feat: added Checkbox component (#336)
Browse files Browse the repository at this point in the history
Nieuw component: Checkbox

---------

Co-authored-by: Vlad Afanasev <[email protected]>
  • Loading branch information
VladAfanasev and Vlad Afanasev authored Nov 19, 2024
1 parent 3b4c5ce commit 3636be3
Show file tree
Hide file tree
Showing 9 changed files with 312 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/rude-fishes-give.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: Checkbox
27 changes: 27 additions & 0 deletions packages/components-react/src/checkbox/Checkbox.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
.lux-checkbox:checked:focus {
border-color: var(--lux-checkbox-checked-focus-border-color);
background-color: var(--lux-checkbox-checked-focus-background-color);
color: var(--lux-checkbox-checked-focus-color);
}

.lux-checkbox:checked:hover {
border-color: var(--lux-checkbox-checked-hover-border-color);
background-color: var(--lux-checkbox-checked-hover-background-color);
color: var(--lux-checkbox-checked-hover-color);
}

.lux-checkbox:checked:active {
border-color: var(--lux-checkbox-checked-active-border-color);
background-color: var(--lux-checkbox-checked-active-background-color);
color: var(--lux-checkbox-checked-active-color);
}

.lux-checkbox:checked:disabled {
border-color: var(--lux-checkbox-checked-disabled-border-color);
background-color: var(--lux-checkbox-checked-disabled-background-color);
color: var(--lux-checkbox-checked-disabled-color);
}

.lux-checkbox--disabled {
cursor: not-allowed;
}
48 changes: 48 additions & 0 deletions packages/components-react/src/checkbox/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
Checkbox as UtrechtCheckbox,
type CheckboxProps as UtrechtCheckboxProps,
} from '@utrecht/component-library-react/dist/css-module';
import './Checkbox.css';
import clsx from 'clsx';
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';

export type LuxCheckboxProps = UtrechtCheckboxProps & {
invalid?: boolean;
name?: string;
checked?: boolean;
disabled?: boolean;
className?: string;
};

const CLASSNAME = {
checkbox: 'lux-checkbox',
disabled: 'lux-checkbox--disabled',
};

export const LuxCheckbox = forwardRef(
(
{ disabled, className, name, checked, ...restProps }: PropsWithChildren<LuxCheckboxProps>,
ref: ForwardedRef<HTMLInputElement>,
) => {
const combinedClassName = clsx(
CLASSNAME.checkbox,
{
[CLASSNAME.disabled]: disabled,
},
className,
);

return (
<UtrechtCheckbox
ref={ref}
name={name}
className={combinedClassName}
checked={checked}
disabled={disabled}
{...restProps}
/>
);
},
);

LuxCheckbox.displayName = 'LuxCheckbox';
66 changes: 66 additions & 0 deletions packages/components-react/src/checkbox/test/Checkbox.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, it } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import { LuxCheckbox } from '../Checkbox';

describe('Checkbox', () => {
it('renders a checkbox', () => {
render(<LuxCheckbox name="test-checkbox" />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toBeInTheDocument();
});

it('renders a checkbox with correct name attribute', () => {
render(<LuxCheckbox name="test-checkbox" />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toHaveAttribute('name', 'test-checkbox');
});

it('renders a checked checkbox when checked prop is true', () => {
render(<LuxCheckbox name="test-checkbox" checked={true} />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toBeChecked();
});

it('renders an unchecked checkbox when checked prop is false', () => {
render(<LuxCheckbox name="test-checkbox" checked={false} />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).not.toBeChecked();
});

it('renders a disabled checkbox when disabled prop is true', () => {
render(<LuxCheckbox name="test-checkbox" disabled={true} />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toBeDisabled();
expect(checkbox).toHaveClass('lux-checkbox--disabled');
});

it('applies custom className when provided', () => {
const customClass = 'custom-checkbox';
render(<LuxCheckbox name="test-checkbox" className={customClass} />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toHaveClass(customClass);
});

it('forwards additional props to the checkbox input', () => {
render(<LuxCheckbox name="test-checkbox" data-testid="test-id" aria-label="test label" />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toHaveAttribute('data-testid', 'test-id');
expect(checkbox).toHaveAttribute('aria-label', 'test label');
});

it('combines multiple classes correctly', () => {
render(<LuxCheckbox name="test-checkbox" className="custom-class" disabled={true} />);

const checkbox = screen.getByRole('checkbox');
expect(checkbox).toHaveClass('lux-checkbox');
expect(checkbox).toHaveClass('lux-checkbox--disabled');
expect(checkbox).toHaveClass('custom-class');
});
});
1 change: 1 addition & 0 deletions packages/components-react/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ export {
export { LuxTextbox, INPUT_TYPES, type LuxTextboxProps } from './textbox/Textbox';
export { LuxFormFieldTextbox, type LuxFormFieldTextboxProps } from './form-field-textbox/FormFieldTextbox';
export { LuxParagraph, type LuxParagraphProps } from './paragraph/Paragraph';
export { LuxCheckbox, type LuxCheckboxProps } from './checkbox/Checkbox';
export { LuxPreHeading, type LuxPreHeadingProps } from './pre-heading/PreHeading';
export { LuxSection, type LuxSectionProps } from './section/Section';
1 change: 1 addition & 0 deletions packages/storybook/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@types/react-dom": "18.3.0",
"@utrecht/alert-css": "1.1.0",
"@utrecht/button-css": "1.2.0",
"@utrecht/checkbox-css": "1.3.0",
"@utrecht/form-field-css": "1.3.0",
"@utrecht/form-field-description-css": "1.3.0",
"@utrecht/form-field-error-message-css": "1.3.1",
Expand Down
56 changes: 56 additions & 0 deletions packages/storybook/src/react-components/checkbox/checkbox.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { Canvas, Controls, Markdown, Meta } from "@storybook/blocks";
import markdown from "@utrecht/checkbox-css/README.md?raw";
import * as CheckboxStories from "./checkbox.stories.tsx";
import { CitationDocumentation } from "../../utils/CitationDocumentation.tsx";

<Meta of={CheckboxStories} />

# Checkbox

<CitationDocumentation
component="Utrecht Checkbox"
url="https://nl-design-system.github.io/utrecht/storybook-css/index.html?path=/docs/css-checkbox--docs"
/>

<Markdown>{markdown}</Markdown>

## Notes

- The checkbox supports different states: checked, disabled, invalid, required
- All states can be combined
- The component inherits its styling from the Utrecht Design System

## Playground

<Canvas of={CheckboxStories.Playground} />
<Controls of={CheckboxStories.Playground} />

## Default

<Canvas of={CheckboxStories.Default} />

## States

### Checked

<Canvas of={CheckboxStories.Checked} />

### Disabled

<Canvas of={CheckboxStories.Disabled} />

### Checked and Disabled

<Canvas of={CheckboxStories.CheckedAndDisabled} />

### Hover

<Canvas of={CheckboxStories.Hover} />

### Focus

<Canvas of={CheckboxStories.Focus} />

### Focus Visible

<Canvas of={CheckboxStories.FocusVisible} />
100 changes: 100 additions & 0 deletions packages/storybook/src/react-components/checkbox/checkbox.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { LuxCheckbox } from '@lux-design-system/components-react';
import tokens from '@lux-design-system/design-tokens/dist/index.json';
import type { Meta, StoryObj } from '@storybook/react';

type Story = StoryObj<typeof meta>;

const meta = {
title: 'React Components/Checkbox',
id: 'react-components-checkbox',
component: LuxCheckbox,
subcomponents: {},
parameters: {
tokens,
tokensPrefix: 'utrecht-checkbox',
},
argTypes: {
checked: {
description: 'Checked state',
control: 'boolean',
},
disabled: {
description: 'Disabled state',
control: 'boolean',
},
},
} satisfies Meta<typeof LuxCheckbox>;

export default meta;

const CheckboxTemplate: Story = {
args: {
checked: false,
disabled: false,
invalid: false,
required: false,
},
render: ({ ...args }) => <LuxCheckbox {...args} />,
};

export const Playground: Story = {
...CheckboxTemplate,
name: 'Playground',
parameters: {
docs: {
sourceState: 'shown',
},
},
tags: ['!autodocs'],
};

export const Default: Story = {
name: 'Default',
args: {},
};

export const Checked: Story = {
name: 'Checked',
args: {
checked: true,
},
};

export const Disabled: Story = {
name: 'Disabled',
args: {
disabled: true,
},
};

export const CheckedAndDisabled: Story = {
name: 'Checked and Disabled',
args: {
checked: true,
disabled: true,
},
};

export const Hover: Story = {
...CheckboxTemplate,
name: 'Hover',
parameters: {
pseudo: { hover: true },
},
};

export const Focus: Story = {
...CheckboxTemplate,
name: 'Focus',
parameters: {
pseudo: { focus: true, focusVisible: true },
},
};

export const FocusVisible: Story = {
...CheckboxTemplate,
name: 'Focus Visible',
parameters: {
pseudo: { focusVisible: true },
},
};
9 changes: 8 additions & 1 deletion pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3636be3

Please sign in to comment.