Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into react-components/select
Browse files Browse the repository at this point in the history
  • Loading branch information
MMeijerink committed Nov 12, 2024
2 parents dae7a70 + 8887460 commit 7cfeeb0
Show file tree
Hide file tree
Showing 56 changed files with 2,682 additions and 9 deletions.
7 changes: 7 additions & 0 deletions .changeset/beige-swans-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@lux-design-system/design-tokens": minor
---

In deze commit:

- Nieuwe tokens: utrecht component button group
8 changes: 8 additions & 0 deletions .changeset/good-windows-smash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@lux-design-system/components-react": major
---

In deze commit:

- Nieuw component: LuxPreHeading
- Nieuw component: LuxHeadingGroup
5 changes: 5 additions & 0 deletions .changeset/hungry-books-hug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: LuxAlert
5 changes: 5 additions & 0 deletions .changeset/nervous-eels-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: Form Field
5 changes: 5 additions & 0 deletions .changeset/rotten-eyes-poke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: Lux Form Field Text Input
5 changes: 5 additions & 0 deletions .changeset/thick-cats-agree.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: Form Field Error Message
5 changes: 5 additions & 0 deletions .changeset/violet-frogs-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@lux-design-system/components-react": minor
---

Nieuw component: Form Field Description
2 changes: 1 addition & 1 deletion .lux.stylelintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"order/properties-alphabetical-order": null,
"scss/dollar-variable-pattern": "^(lux)-[a-z0-9-]+$",
"scss/percent-placeholder-pattern": "^(lux)-[a-z0-9-]+$",
"custom-property-pattern": "^_?(lux)-[a-z0-9-]+$",
"custom-property-pattern": "^_?(lux|utrecht)-[a-z0-9-]+$",
"selector-class-pattern": "^(lux)-[a-z0-9_-]+|(force-state)--[a-z]+$",
"keyframes-name-pattern": "^(lux)-[a-z0-9-]+$"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/components-react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"dist/"
],
"dependencies": {
"@utrecht/component-library-css": "6.0.0",
"@utrecht/component-library-css": "6.1.0",
"@utrecht/component-library-react": "7.1.0",
"clsx": "2.1.1",
"date-fns": "3.6.0",
Expand Down
20 changes: 20 additions & 0 deletions packages/components-react/src/alert/Alert.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.lux-alert {
--utrecht-heading-1-color: var(--utrecht-alert-color);
--utrecht-heading-1-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-1-line-height: var(--lux-alert-heading-font-size);
--utrecht-heading-2-color: var(--utrecht-alert-color);
--utrecht-heading-2-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-2-line-height: var(--lux-alert-heading-font-size);
--utrecht-heading-3-color: var(--utrecht-alert-color);
--utrecht-heading-3-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-3-line-height: var(--lux-alert-heading-font-size);
--utrecht-heading-4-color: var(--utrecht-alert-color);
--utrecht-heading-4-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-4-line-height: var(--lux-alert-heading-font-size);
--utrecht-heading-5-color: var(--utrecht-alert-color);
--utrecht-heading-5-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-5-line-height: var(--lux-alert-heading-font-size);
--utrecht-heading-6-color: var(--utrecht-alert-color);
--utrecht-heading-6-font-size: var(--lux-alert-heading-font-size);
--utrecht-heading-6-line-height: var(--lux-alert-heading-font-size);
}
60 changes: 60 additions & 0 deletions packages/components-react/src/alert/Alert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import {
Alert as UtrechtAlert,
AlertProps as UtrechtAlertProps,
AlertType as UtrechtAlertType,
} from '@utrecht/component-library-react/dist/css-module';
import './Alert.css';

type AlertType = Exclude<UtrechtAlertType, 'ok'> | 'success';

export interface LuxAlertProps extends Omit<UtrechtAlertProps, 'type'> {
type: AlertType;
}

//TODO replace icons in #308
const InfoIcon = () => (
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg">
<circle r="7" cx="8" cy="8" fill="transparent" stroke="var(--utrecht-alert-icon-info-color)" />
</svg>
);
const SuccessIcon = () => (
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg">
<circle r="7" cx="8" cy="8" fill="transparent" stroke="var(--utrecht-alert-icon-ok-color)" />
</svg>
);
const WarningIcon = () => (
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg">
<circle r="7" cx="8" cy="8" fill="transparent" stroke="var(--utrecht-alert-icon-warning-color)" />
</svg>
);
const ErrorIcon = () => (
<svg height="16" width="16" xmlns="http://www.w3.org/2000/svg">
<circle r="7" cx="8" cy="8" fill="transparent" stroke="var(--utrecht-alert-icon-error-color)" />
</svg>
);

export const LuxAlert = (props: LuxAlertProps) => {
const { children, type, className, ...otherProps } = props;
const utrechtAlertType: UtrechtAlertType = type === 'success' ? 'ok' : type;

const icons = {
info: InfoIcon,
success: SuccessIcon,
warning: WarningIcon,
error: ErrorIcon,
};

const Icon = icons[type];
const icon = Icon ? <Icon /> : <></>;

return (
<UtrechtAlert
type={utrechtAlertType}
className={`lux-alert ${className !== undefined ? className : ''}`}
icon={icon}
{...otherProps}
>
{children}
</UtrechtAlert>
);
};
99 changes: 99 additions & 0 deletions packages/components-react/src/alert/test/Alert.spec.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { describe, expect, it } from '@jest/globals';
import { render } from '@testing-library/react';
import { LuxHeading1, LuxParagraph } from '../../index';
import { LuxAlert } from '../Alert';

describe('Alert', () => {
it('renders an info alert', () => {
const { container } = render(
<LuxAlert type="info">
<LuxHeading1>Heading</LuxHeading1>
<LuxParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quis massa lorem. Ut laoreet varius rhoncus.
</LuxParagraph>
</LuxAlert>,
);

const alert = container.querySelector(':only-child');
expect(alert).toBeInTheDocument();

expect(alert).toHaveClass('utrecht-alert--info');
});

it('renders an success alert', () => {
const { container } = render(
<LuxAlert type="success">
<LuxHeading1>Heading</LuxHeading1>
<LuxParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quis massa lorem. Ut laoreet varius rhoncus.
</LuxParagraph>
</LuxAlert>,
);

const alert = container.querySelector(':only-child');
expect(alert).toBeInTheDocument();

expect(alert).toHaveClass('utrecht-alert--ok');
});

it('renders an warning alert', () => {
const { container } = render(
<LuxAlert type="warning">
<LuxHeading1>Heading</LuxHeading1>
<LuxParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quis massa lorem. Ut laoreet varius rhoncus.
</LuxParagraph>
</LuxAlert>,
);

const alert = container.querySelector(':only-child');
expect(alert).toBeInTheDocument();

expect(alert).toHaveClass('utrecht-alert--warning');
});

it('renders an error alert', () => {
const { container } = render(
<LuxAlert type="error">
<LuxHeading1>Heading</LuxHeading1>
<LuxParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quis massa lorem. Ut laoreet varius rhoncus.
</LuxParagraph>
</LuxAlert>,
);

const alert = container.querySelector(':only-child');
expect(alert).toBeInTheDocument();

expect(alert).toHaveClass('utrecht-alert--error');
});

it('can have an additional class name', () => {
const { container } = render(
<LuxAlert type="info" className="custom-alert">
<LuxHeading1>Heading</LuxHeading1>
<LuxParagraph>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam quis massa lorem. Ut laoreet varius rhoncus.
</LuxParagraph>
</LuxAlert>,
);

const alert = container.querySelector(':only-child');

expect(alert).toHaveClass('custom-alert');

expect(alert).toHaveClass('lux-alert');
});

it('can have extra properties', () => {
const { container } = render(
<LuxAlert type="info" hidden={true}>
Lux Section
</LuxAlert>,
);

const alert = container.querySelector(':only-child');

expect(alert).not.toBeVisible();
});
});
2 changes: 1 addition & 1 deletion packages/components-react/src/button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const ICON_POSITIONS: { [key: string]: string } = {
export const LuxButton = (props: LuxButtonProps) => {
const { size, icon: iconNode, iconPosition, ...otherProps } = props;

const className = `lux-button ${size !== undefined ? SIZE_CLASSNAME[size] : ''}`;
const className = `lux-button ${size !== undefined ? SIZE_CLASSNAME[size] : ''} ${otherProps.className || ''}`;

const positionedIcon = React.Children.map(iconNode, (iconElement) => {
if (!iconElement) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import {
FormFieldDescription as UtrechtFormFieldDescription,
FormFieldDescriptionProps as UtrechtFormFieldDescriptionProps,
} from '@utrecht/component-library-react/dist/css-module';
import clsx from 'clsx';

const FORM_FIELD_DESCRIPTION_CLASSES: Record<LuxFormFieldDescriptionAppearance, string> = {
valid: 'utrecht-form-field-description--valid',
invalid: 'utrecht-form-field-description--invalid',
};

export type LuxFormFieldDescriptionAppearance = 'valid' | 'invalid';
// Extend the Utrecht props but omit valid and invalid since we're replacing them
export interface LuxFormFieldDescriptionProps extends Omit<UtrechtFormFieldDescriptionProps, 'valid' | 'invalid'> {
appearance?: LuxFormFieldDescriptionAppearance;
}

export const LuxFormFieldDescription = (props: LuxFormFieldDescriptionProps) => {
const { appearance, className, ...restProps } = props;

const classNames = clsx(
{
[FORM_FIELD_DESCRIPTION_CLASSES.valid]: appearance === 'valid',
[FORM_FIELD_DESCRIPTION_CLASSES.invalid]: appearance === 'invalid',
},
className,
);

return <UtrechtFormFieldDescription {...restProps} className={classNames} />;
};

LuxFormFieldDescription.displayName = 'LuxFormFieldDescription';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { describe, expect, it } from '@jest/globals';
import { render, screen } from '@testing-library/react';
import { LuxFormFieldDescription } from '../FormFieldDescription';

describe('Form Field Description', () => {
it('renders a basic description', () => {
render(<LuxFormFieldDescription>Test Description</LuxFormFieldDescription>);

const description = screen.getByText('Test Description');
expect(description).toBeInTheDocument();
});

it('applies the base class', () => {
render(<LuxFormFieldDescription>Test Description</LuxFormFieldDescription>);

const description = screen.getByText('Test Description');
expect(description).toHaveClass('utrecht-form-field-description');
});

it('can have an additional class name', () => {
render(<LuxFormFieldDescription className="custom-class">Test Description</LuxFormFieldDescription>);

const description = screen.getByText('Test Description');
expect(description).toHaveClass('utrecht-form-field-description');
expect(description).toHaveClass('custom-class');
});

it('passes through other HTML attributes', () => {
render(<LuxFormFieldDescription data-testid="test-description">Test Description</LuxFormFieldDescription>);

const description = screen.getByTestId('test-description');
expect(description).toBeInTheDocument();
});
it('renders content with a paragraph', () => {
render(
<LuxFormFieldDescription data-testid="rich-text-description">
<p>This is a paragraph</p>
</LuxFormFieldDescription>,
);
const description = screen.getByTestId('rich-text-description');
expect(description).toBeInTheDocument();

const paragraph = description.querySelector('p');
expect(paragraph).toBeInTheDocument();
expect(paragraph).toHaveTextContent('This is a paragraph');
});
it('renders rich text content', () => {
render(
<LuxFormFieldDescription data-testid="rich-text-description">
<strong>Bold</strong> Description
</LuxFormFieldDescription>,
);
const description = screen.getByTestId('rich-text-description');
expect(description).toBeInTheDocument();

const boldText = description.querySelector('strong');
expect(boldText).toBeInTheDocument();
expect(boldText).toHaveTextContent('Bold');
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
FormFieldErrorMessage as UtrechtFormFieldErrorMessage,
FormFieldErrorMessageProps as UtrechtFormFieldErrorMessageProps,
} from '@utrecht/component-library-react/dist/css-module';
import clsx from 'clsx';
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';

const FORM_FIELD_ERROR_MESSAGE_CLASSES: { [key: string]: string } = {
distanced: 'utrecht-form-field-error-message--distanced',
};

export type LuxFormFieldErrorMessageProps = UtrechtFormFieldErrorMessageProps & {
distanced?: boolean;
};

export const LuxFormFieldErrorMessage = forwardRef(
(
{ children, className, distanced, ...restProps }: PropsWithChildren<LuxFormFieldErrorMessageProps>,
ref: ForwardedRef<HTMLParagraphElement>,
) => {
const classNames = clsx(
{
[FORM_FIELD_ERROR_MESSAGE_CLASSES.distanced]: distanced,
},
className,
);

return (
<UtrechtFormFieldErrorMessage {...restProps} ref={ref} className={classNames}>
{children}
</UtrechtFormFieldErrorMessage>
);
},
);

LuxFormFieldErrorMessage.displayName = 'LuxFormFieldErrorMessage';
Loading

0 comments on commit 7cfeeb0

Please sign in to comment.