npm package: @highlight-ui/button
Description: The button
Author: Personio GmbH & Co. KG
Latest version: 12.2.9

[![npm](https://img.shields.io/npm/v/@highlight-ui/button)](https://www.npmjs.com/package/@highlight-ui/button)
[![personio.design](https://img.shields.io/static/v1?label=personio&message=zeroheight&color=yellow)](https://www.personio.design/40d648a47/p/49fde0-button/b/556994)
[![storybook.personio.design](https://img.shields.io/static/v1?label=personio&message=storybook&color=red)](https://storybook.personio.design/?path=/story/forms-and-input-button--default-button)

# @highlight-ui/button

Versatile and customizable `Button` and `IconButton` components designed to handle user click interactions seamlessly. Enables actions to be triggered with a built-in loading feature, providing asynchronous feedback for improved user experience.

## Features

- **Button**
  - Supports different variants: `default`, `emphasized`, and `plain`
  - Supports different tones: `default`, `critical`, and `success`
  - Supports an optional icon placed on the left of the label
  - Optional loading state with the spinner
- **IconButton**
  - A required icon property is provided
  - Supports different variants: `default`, `emphasized` and `plain`
  - Optional loading state with the spinner

## Installation

Using npm:

```bash
npm install @highlight-ui/button
```

Using yarn:

```bash
yarn add @highlight-ui/button
```

Using pnpm:

```bash
pnpm install @highlight-ui/button
```

In your (S)CSS file:

```scss
@import url('@highlight-ui/button');
```

Once the package is installed, you can import the components from the library:

**Button**

```javascript
import { Button } from '@highlight-ui/button';
```

**Icon Button**

```javascript
import { IconButton } from '@highlight-ui/button';
```

## Usage

```tsx
import React from 'react';
import { Button, IconButton } from '@highlight-ui/button';

export default function ButtonExample() {
  return (
    <>
      <Button icon="plus" variant="emphasized" tone="critical" onClick={() => console.log('Button clicked')}>
        Click me!
      </Button>

      <IconButton icon="plus" variant="emphasized" onClick={() => console.log('Button clicked')}/>
    </>
  );
}
```

## Props 📜

> All `HTMLButtonElement`, `React.PropsWithChildren` and [PropsWithMetadata](https://gitlab.personio-internal.de/personio/platform/highlight-ui/-/blob/master/packages/utils/commons/src/types.ts#L24) props are accepted with this component. In addition to these, this component has the following props

**Button Props**

| Prop         | Type                                      | Required | Default     | Description                                                                                                                                                                                                                       |
| :----------- | :---------------------------------------- | :------- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `variant`    | `'default'` , `'emphasized'` , `'plain'`      | No       | `'default'` | Determines the button's appearance (label color, background color)                                                                                                                                                |
| `tone`       | `'default'` , `'critical'` , `'success'` (Success is only available for the `emphasized` variant)      | No       | `'default'` | Determines the button's tone, which is used to convey the importance or context of the action (e.g., color for success, error, etc.)                                                                                               |
| `onClick`    | `(event: React.MouseEvent) => void`       | Yes      |             | Function called when the button is clicked                                                                                                                                                                                          |
| `children`   | `React.ReactNode`                        | Yes      |             | The content of the button, usually a text label                                                                                                                                                                                      |
| `ref`        | `React.Ref<HTMLButtonElement>`            | No       |             | Ref for the button element                                                                                                                                                                                                          |
| `block`      | `boolean`                                 | No       |             | Makes the component behave as a block element (take up the whole available space)                                                                                                                                                   |
| `buttonState`| `'default'` , `'loading'` , `'disabled'`   | No       |             | Determines the state of the button                                                                                                                                                                                                 |
| `icon`       | `string`                                  | No       |             | The icon to be placed beside the label                                                                                                                                                                                               |

**IconButton Props**

| Prop         | Type                                      | Required | Default     | Description                                                                                                                                                                                                                       |
| :----------- | :---------------------------------------- | :------- | :---------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `icon`       | `string`                                  | Yes       |             | The icon to be placed beside the label                                                                                                                                                                                               |
| `variant`    | `'default'` , `'emphasized'`, `'plain'`      | No       | `'default'` | Determines the button's appearance (label color, background color)                                                                                                                                                |
| `onClick`    | `(event: React.MouseEvent) => void`       | Yes      |             | Function called when the button is clicked                                                                                                                                                                                          |
| `ref`        | `React.Ref<HTMLButtonElement>`            | No       |             | Ref for the button element                                                                                                                                                                                                          |
| `block`      | `boolean`                                 | No       |             | Makes the component behave as a block element (take up the whole available space)                                                                                                                                                   |
| `buttonState`| `'default'` , `'loading'` , `'disabled'`  | No       |             | Determines the state of the button                                                                                                                                                                                                 |

## Accessibility ♿️

The button component follows best practices for accessibility:

- Uses a native `<button>` element to ensure proper keyboard navigation and focus handling
- Uses aria attributes for accessibility features, such as `aria-hidden` for loading and disabled states or passing the optional `aria-label` prop to the IconButton
- Supports keyboard navigation with the outline ring and trigger keys `Enter` and `Space`

## Testing

The test below shows how the button and icon button can be tested when used within another component.

**Button Test**

```tsx
import React, { useState } from 'react';
import { render, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { Button } from '@highlight-ui/button';

function ButtonWrapper({children}) {
  const handleClick = () => console.log('button clicked);

  return (
    <Button
      onClick={handleClick}
    >{children}</Button>
  );
}

describe('ButtonWrapperTest', () => {
  const renderButtonWrapper = () => {
    return render(<ButtonWrapper />);
  };

  it('renders the ButtonWrapper and handles the button click', () => {
    const { getByText } = renderButtonWrapper({ children: 'Click me' });

    const buttonElement = getByText('Click me');
    expect(buttonElement).toBeInTheDocument();

    userEvent.click(buttonElement);
    // Add any other expects or assertions based on your wrapper component's behavior
  });
});

```

**IconButton Test**
```tsx
import React, { useState } from 'react';
import { render, fireEvent } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IconButton, IconButtonProps } from '@highlight-ui/button';

type IconButtonWrapperProps = Omit<IconButtonProps, 'onClick'>;

function IconButtonWrapper(props: IconButtonWrapperProps) {
  const handleClick = () => console.log('button clicked');

  return <IconButton onClick={handleClick} {...props} />;
}

describe('IconButtonWrapperTest', () => {
  const renderIconButtonWrapper = (props) => {
    return render(<IconButtonWrapper {...props} />);
  };

  it('renders the IconButtonWrapper and calls the onClick handler', () => {
    const icon = 'plus';
    const { getByRole } = renderIconButtonWrapper({ icon });

    const buttonElement = getByRole('button');
    expect(buttonElement).toBeInTheDocument();

    userEvent.click(buttonElement);
    // Add any other expects or assertions based on your wrapper component's behavior
  });
});

```

## Place in design system 💻

The button component is a foundational element of the design system and can be used across various services and contexts. It provides a consistent look and feel for user interactions and actions.

Some examples of when to use the button component:

- Submitting forms
- Triggering actions like adding, deleting, or editing items
- Revealing additional content (Modals, dialogs, etc.)

Please visit the [design system documentation](https://www.personio.design/40d648a47/p/49fde0-button/b/556994) for usage guidelines and visual examples.

## Contributing 🖌️

To contribute to the development of the button component, please follow the [contribution guidelines](https://www.personio.design/40d648a47/v/0/p/22ac59-support--contribute/b/463fdd). Your input and expertise are welcome and appreciated!