# OVHcloud Design System - Complete Documentation

> OVHcloud Design System is a collection of assets, guidelines and UI components for building consistent user experiences across OVHcloud products.

This document contains the complete OVHcloud Design System documentation including all components, styling, theming, guides, and tools.

## Apply ODS Style

---

After installing ODS (following the  documentation), you'll be able to use every components with the correct style.

For the non-component related style, you need to update your app accordingly.

## Use the ODS font

---

ODS components does not enforce any font by default (minus a few exception like `ods-code`).

To use one of the ODS fonts in your app, add the expected variables to the top of your app:

```css
html {
  font-family: var(--ods-font-family-default);
}
```

## Use the ODS design tokens

---

All ODS components are using common design tokens, that are accessible through [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) .

If you want to use any of those on your own components, you can refer to the exhaustive list on the [Figma dedicated page](https://www.figma.com/design/tIKzHa5KvHHyosgIgyBswB/Design-Tokens?m=auto&node-id=0-1&t=s57Qt3pa7WuFWKEh-1) .

```css
.my-custom-title {
  color: var(--ods-color-text);
  ...
}
```

## Use ODS CSS reset

---

We recommend you to import the ODS CSS reset subset of rules to normalize the rendering of some elements, but it's not mandatory.

```typescript
import '@ovhcloud/ods-react/normalize-css';
```

## Design Tokens

---

Design tokens define reusable design values such as colors, borders, and outlines as variables. Instead of using hardcoded values, they provide a consistent, scalable, and maintainable way to style components across all products.

## Usage

---

Design tokens are available as CSS custom properties (variables) and can be used in your CSS files:

```css
.my-component {
  color: var(--ods-color-primary-500);
  border-radius: var(--ods-border-radius-sm);
}
```

## Theme Variables

---

### Color

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-anchor-text-color | #0050d7 | 
 |
| --ods-theme-anchor-text-color-hover | #002dbe | 

 |
| --ods-theme-anchor-text-color-visited | #000e9c | 

 |
| --ods-theme-background-color | #fff | 

 |
| --ods-theme-background-color-disabled | #e6e6e6 | 

 |
| --ods-theme-background-color-drag-over | var(--ods-color-primary-100) | 

 |
| --ods-theme-background-color-readonly | #f2f2f2 | 

 |
| --ods-theme-background-color-selected | #00185e | 

 |
| --ods-theme-border-color-disabled | #cccccc | 

 |
| --ods-theme-border-color-drag-over | var(--ods-color-primary-100) | 

 |
| --ods-theme-border-color-readonly | #f2f2f2 | 

 |
| --ods-theme-border-color-selected | #00185e | 

 |
| --ods-theme-heading-text-color | #00185e | 

 |
| --ods-theme-progress-background-color | var(--ods-color-primary-500) | 

 |
| --ods-theme-split-background-color | #e6e6e6 | 

 |
| --ods-theme-split-border-color | #e6e6e6 | 

 |
| --ods-theme-text-color | #4d5592 | 

 |
| --ods-theme-text-color-disabled | #808080 | 

 |
| --ods-theme-text-color-selected | #ffffff | 

 |
| --ods-theme-track-background-color | var(--ods-color-neutral-100) | 

 |
| --ods-theme-track-background-color-disabled | var(--ods-color-neutral-500) | 

 |
| --ods-theme-critical-color | var(--ods-color-critical-500) | 

 |
| --ods-theme-information-color | var(--ods-color-information-500) | 

 |
| --ods-theme-neutral-color | var(--ods-color-neutral-500) | 

 |
| --ods-theme-primary-color | var(--ods-color-primary-500) | 

 |
| --ods-theme-success-color | var(--ods-color-success-500) | 

 |
| --ods-theme-warning-color | var(--ods-color-warning-500) | 

 |

### Outline

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-outline-color | #000e9c | 
 |
| --ods-theme-outline-offset | 2px | 

 |
| --ods-theme-outline-style | solid | 

 |
| --ods-theme-outline-width | 2px | 

 |

### Overlay

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-backdrop-background-color | #0050d7 | 
 |
| --ods-theme-backdrop-opacity | 0.75 | 

 |
| --ods-theme-overlay-border-radius | calc(var(--ods-theme-border-radius) / 2) | 

 |
| --ods-theme-overlay-box-shadow | 0 2px 8px rgba(0, 14, 156, 0.2) | 

 |
| --ods-theme-overlay-z-index | 99 | 

 |

### Spacing

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-border-radius | 8px | 
 |
| --ods-theme-border-width | 1px | 

 |
| --ods-theme-column-gap | 8px | 

 |
| --ods-theme-padding-horizontal | 8px | 

 |
| --ods-theme-padding-vertical | 8px | 

 |
| --ods-theme-row-gap | 8px | 

 |

### Font Family

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-font-family | "Source Sans Pro", "Trebuchet MS", arial, "Segoe UI", sans-serif | 
Aa



 |
| --ods-theme-font-family-code | "Source Code Pro", arial | 

Aa



 |

### Form Element

| Token | Value | Preview |
| --- | --- | --- |
| --ods-theme-input-background-color-checked | #0050d7 | 
 |
| --ods-theme-input-background-color-checked-hover | #000e9c | 

 |
| --ods-theme-input-background-color-invalid | #bf0020 | 

 |
| --ods-theme-input-border-color | #b3b3b3 | 

 |
| --ods-theme-input-border-color-checked | #0050d7 | 

 |
| --ods-theme-input-border-color-checked-hover | #000e9c | 

 |
| --ods-theme-input-border-color-hover | #808080 | 

 |
| --ods-theme-input-border-color-invalid | #bf0020 | 

 |
| --ods-theme-input-border-radius | calc(var(--ods-theme-border-radius) / 2) | 

 |
| --ods-theme-input-border-width | var(--ods-theme-border-width) | 

 |
| --ods-theme-input-min-height | 32px | 

 |
| --ods-theme-input-option-background-color-hover | #bef1ff | 

 |
| --ods-theme-input-option-background-color-selected | #e6faff | 

 |
| --ods-theme-input-option-background-color-selected-hover | #bef1ff | 

 |
| --ods-theme-input-padding-horizontal | var(--ods-theme-padding-horizontal) | 

 |
| --ods-theme-input-padding-vertical | calc(var(--ods-theme-padding-vertical) / 4) | 

 |
| --ods-theme-input-placeholder-text-color | #666666 | 

 |
| --ods-theme-input-text-color | var(--ods-theme-text-color) | 

 |
| --ods-theme-input-text-color-checked | var(--ods-theme-background-color) | 

 |
| --ods-theme-input-text-color-invalid | #bf0020 | 

 |

## Color Palettes

---

  

## Previous tokens

Deprecated

---

Following tokens have been deprecated and will be removed in a future major release.  
Non spacing tokens have been renamed with the `--ods-theme` prefix.  
Spacing tokens have been reduced to one base value, you should set your values based on this base, for example:`--my-border-radius-sm: calc(var(--ods-theme-border-radius)) / 2;`.

| Token | Value | Preview |
| --- | --- | --- |
| --ods-border-radius-xs | 2px | 
 |
| --ods-border-radius-sm | 4px | 

 |
| --ods-border-radius-md | 8px | 

 |
| --ods-border-radius-lg | 16px | 

 |
| --ods-border-width-sm | 1px | 

 |
| --ods-border-width-md | 2px | 

 |
| --ods-color-background-disabled-default | #e6e6e6 | 

 |
| --ods-color-background-readonly-default | #f2f2f2 | 

 |
| --ods-color-border-disabled-default | #cccccc | 

 |
| --ods-color-border-readonly-default | #f2f2f2 | 

 |
| --ods-color-element-background-selected | #00185e | 

 |
| --ods-color-element-text-selected | #ffffff | 

 |
| --ods-color-form-element-background-critical | #ffffff | 

 |
| --ods-color-form-element-background-default | #ffffff | 

 |
| --ods-color-form-element-background-focus-default | #ffffff | 

 |
| --ods-color-form-element-background-hover-default | #ffffff | 

 |
| --ods-color-form-element-background-selected-critical | #bf0020 | 

 |
| --ods-color-form-element-border-critical | #bf0020 | 

 |
| --ods-color-form-element-border-default | #b3b3b3 | 

 |
| --ods-color-form-element-border-focus-default | #808080 | 

 |
| --ods-color-form-element-border-hover-default | #808080 | 

 |
| --ods-color-form-element-text-default | #4d5592 | 

 |
| --ods-color-form-element-text-placeholder-default | #666666 | 

 |
| --ods-color-heading | #00185e | 

 |
| --ods-color-text | #4d5592 | 

 |
| --ods-color-text-disabled-default | #808080 | 

 |
| --ods-font-family-code | "Source Code Pro", arial | 

Aa



 |
| --ods-font-family-default | "Source Sans Pro", "Trebuchet MS", arial, "Segoe UI", sans-serif | 

Aa



 |
| --ods-form-element-input-height | 32px | 

 |
| --ods-outline-color-default | #000e9c | 

 |
| --ods-outline-offset | 2px | 

 |
| --ods-outline-style-default | solid | 

 |
| --ods-outline-width | 2px | 

 |

## Style Customization

---

Although ODS components comes with the expected designs, it is sometime useful to customize the rendering of a component to fit your context.

Here we'll describe different ways to customize an ODS component.

## Apply style directly on the component

---

The ODS component host behave like any other React element, so you can apply a class directly to it.

For example, if you want to display a vertical list of `Link`, you can override the inline default display:

```html
<Link className="custom-link">
  My link 1
</Link>
<Link className="custom-link">
  My link 2
</Link>
<style>
  .custom-link {
    display: block;
  }
</style>
```

As there is no shadow DOM anymore, all of the component DOM can be targeted through CSS selectors. It is advised to not rely on cascading selector, as the DOM structure may evolve from one version to another.

  

To ensure reliable CSS, use a specific className on the component you want to customize.

  

If you want to specifically target underneath elements, you can rely on some [data attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/How_to/Use_data_attributes#css_access) , we'll update the documentation about those soon.

## Reuse existing ODS style

---

In some cases where you cannot use an ODS component, you may want one of your own components to look like an ODS component (for example, when using an external library that doesn't allow custom templating).

We did expose a bunch of [Sass](https://sass-lang.com) mixins that allows you to easily apply the ODS style to your own elements.

For example, if you need to make an anchor tag looks like a `Link`:

```css
@import '@ovhcloud/ods-components/style';
.my-link {
  @include ods-link();
  @include ods-link-color('primary');
}
```

```html
<a class="my-link"></a>
```

## CSS variables

---

All ODS components are using common design tokens, that are accessible through [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) .

We do advise to reuse those variables on your own style, this way your application will automatically be updated on current theme changes or when new themes will be released.

You can find the whole list on the  documentation page.

Example of CSS variables:

```html
<h1 class="my-own-page-title">
  Welcome page
</h1>
<style>
  .my-own-page-title {
    color: var(--ods-color-primary-500);
  }
</style>
```

## Tailwind CSS Integration

---

This guide covers how to integrate Tailwind CSS with the OVHcloud Design System (ODS) for both v3 and v4 versions.

Tailwind CSS is a utility-first CSS framework that can be integrated with ODS components and design tokens, but it is not mandatory as ODS is not based on Tailwind.

Technically, you don't need any configuration to make ODS work with Tailwind. These guides are here to add some auto-completion and best practices when using Tailwind, but you can directly use the design tokens right out of the box without any additional configuration.

## Tailwind CSS v3 Configuration

---

For Tailwind CSS v3, create a `tailwind.config.js` which contains a remapping of ODS variables:

```js
/** @type {import('tailwindcss').Config} */
export default {
  content: [
    "./index.html",
    "./src/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {
      borderRadius: {
        'ods-border-radius-xs': 'var(--ods-border-radius-xs)',
        'ods-border-radius-sm': 'var(--ods-border-radius-sm)',
        'ods-border-radius-md': 'var(--ods-border-radius-md)',
        'ods-border-radius-lg': 'var(--ods-border-radius-lg)',
      },
      colors: {
        'ods-color-primary-500': 'var(--ods-color-primary-500)',
        'ods-color-success-500': 'var(--ods-color-success-500)',
        'ods-color-warning-700': 'var(--ods-color-warning-700)',
      },
      fontFamily: {
        'ods-font-family-default': 'var(--ods-font-family-default)',
      },
    },
  },
  plugins: [],
}
```

### Integration with IDEs

#### Integration with JetBrains

This should work straight out of the box.

#### Integration with VSCode

Create a `./.vscode/tailwind.json` containing the classes you want to expose for autocompletion.

```json
{
    "customClasses": [
      "ods-color-primary-500",
      "ods-color-success-500",
      "ods-color-critical-700",
      "ods-border-radius-xs",
      "ods-border-radius-sm",
      "ods-border-radius-md",
      "ods-border-radius-lg",
      "ods-font-family-default",
    ]
  }
```

## Tailwind CSS v4 Configuration

---

For Tailwind CSS v4, create a CSS file using the [@theme](https://tailwindcss.com/docs/adding-custom-styles) property:

```css
@theme {
  --radius-ods-xs: var(--ods-border-radius-xs);
  --radius-ods-sm: var(--ods-border-radius-sm);
  --radius-ods-md: var(--ods-border-radius-md);
  --radius-ods-lg: var(--ods-border-radius-lg);
  --color-ods-primary: var(--ods-color-primary-500);
  --color-ods-success: var(--ods-color-success-500);
  --color-ods-critical: var(--ods-color-critical-700);
  --font-ods-default: var(--ods-font-family-default);
}
```

Then import that file after the main ODS theme and Tailwind CSS. In order for you to properly be able to overwrite the ODS theme, you need to redefine the order of the CSS layers. Base is optional.

```typescript
// index.css
@layer theme, base, components, ods-quarks, ods-atoms, ods-molecules, ods-organisms, molecules, utilities;
@import "tailwindcss";
@import '@ovhcloud/ods-themes/default/css';
@import '@ovhcloud/ods-themes/default/fonts';
@import './ods-tailwind.css';
```

## Usage Examples

---

You can use Tailwind utilities alongside ODS components:

```tsx
import { Button } from '@ovhcloud/ods-react';
function MyComponent() {
  return (
  <div>
      <p className="text-critical">Click on the button!</p>
      <Button className="bg-color-primary">Click me!</Button>
  </div>
  );
}
```

## Best Practices & Troubleshooting

---

### Color Usage

-   Use semantic color names (e.g., `ods-primary-500`, `ods-success-200`) instead of generic colors
-   **Issue**: Color classes not working? Verify that the Tailwind config includes the ODS color mappings and ensure the content paths include your component files.

### Spacing and Layout

-   Use ODS border radius tokens (`rounded-ods-sm`, `rounded-ods-md`, etc.)
-   Leverage ODS form element heights for consistent input sizing
-   **Issue**: CSS variables not loading? Ensure `@ovhcloud/ods-themes` is imported before Tailwind and check that the theme is properly initialized in your app.

### Typography

-   Use the ODS font family (`font-ods` for body text, `font-ods-code` for code)
-   Apply semantic text colors (`text-ods-text`, `text-ods-heading`)
-   Maintain proper contrast ratios with ODS color combinations

### Component Integration

-   ODS components can be styled with Tailwind utilities using the `className` prop
-   Avoid overriding core ODS component styles unless necessary
-   Use Tailwind for layout and positioning, ODS for component behavior
-   **Issue**: Responsive breakpoints not working? Confirm that the ODS breakpoint variables are properly defined and check that the screen configuration in Tailwind config is correct.

## Resources

---

-   [Tailwind CSS Documentation](https://tailwindcss.com/docs)
-   [Tailwind v4 Migration Guide](https://tailwindcss.com/docs/upgrade-guide)
-   
-

## Frequently Asked Questions

---

_Welcome to the F.A.Q. section of the OVHcloud Design System (ODS)._

_Here, we aim to address common questions and provide helpful guidance for developers._

## How can I contribute?

---

OVHcloud Design System is an evolving project, and we welcome contributions from the community. By keeping our repository open source, we aim to make it easier for anyone to suggest improvements, fix issues, or share ideas.

Whether it’s a small fix or a new feature, every contribution helps improve the quality and usability of the system. We appreciate the time and effort of anyone who chooses to take part.

Please note that while we welcome contributions, we encourage you to discuss major changes with our team to ensure alignment with the project's goals and overall quality. Additionally, if you're interested in proposing a new feature, please reach us out first to avoid duplicating effort, as we may already be working on a similar development.

Refer to our project [README](https://github.com/ovh/design-system?tab=contributing-ov-file#contributing-to-ods-project) to learn more about installation and pre-requisites.

## How are releases managed?

---

The OVHcloud Design System follows a structured release process to deliver improvements regularly while letting teams adopt updates at their own pace.

**Patch releases** deliver critical fixes or small improvements and can be adopted immediately.

**Minor releases** introduce new non-breaking features and start the deprecation process when needed.

**Major releases** contain breaking changes, removals of previously deprecated elements, and migration documentation.

Releases are published several times a year based on user needs and product evolution.

We support two major versions at the same time:

-   The current major version receives full support (features, improvements, fixes).
-   The previous major version receives maintenance support for six months (critical fixes only).
-   Older versions are no longer supported.

Deprecations follow a two-step workflow:

-   They are announced in a minor release, marked as "Deprecated" in Storybook, and paired with migration guidance.
-   They are removed in the next major release.

Teams may upgrade at their own pace and can skip versions. We recommend pinning a specific ODS version in your project to ensure consistent behaviour.

## I can't find a component I need

---

ODS provides base components that you can build on top of and implement in various contexts.

If you need a new component, we recommend working with the design team to request and design the component to ensure it aligns with the ODS guidelines.

## Is my browser supported?

---

ODS supports the latest 2 versions of the following browsers:

-   Google Chrome
-   Microsoft Edge
-   Mozilla Firefox
-   Opera
-   Safari

## Can I use another CSS framework on top of ODS?

---

You can use any CSS framework on top of ODS.

Note that some imperative CSS frameworks like Bootstrap may override ODS styling.

Ensure compatibility by testing the integration in your project.

## What about Web Components and Vue?

---

Starting from v19, ODS have moved to fully React-based components. Web Components and the Vue wrapper are now only available in v18.x, which is now in maintenance mode, so there won't be any new features for this version.

If you still need to access documentation about Web Components or Vue, use the version selector at the top left to switch to the latest 18.x documentation, where you'll find the most up-to-date information.

## TS2307: Cannot find module '@ovhcloud/ods-react' or its corresponding type declarations.

---

Check that you're using the `bundler` [module resolution](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#--moduleresolution-bundler) in your `tsconfig.json`.

```typescript
"moduleResolution": "bundler"
```

## My popover/tooltip is not displayed correctly

---

Check that you're not using a `Fragment` as your trigger.

The following will **not** work properly:

```tsx
<Tooltip>
  <TooltipTrigger asChild>
    <>
      <Component1 />
      <Component2 />
    </>
  </TooltipTrigger>
  <TooltipContent>
    ...  </TooltipContent>
</Tooltip>
```

Replace the `Fragment` with an actual node:

```tsx
<Tooltip>
  <TooltipTrigger asChild>
    <MyTrigger>
      ...    </MyTrigger>
  </TooltipTrigger>
  <TooltipContent>
    ...  </TooltipContent>
</Tooltip>
```

## Icons are not displayed

---

Ensure you did import the fonts in your application, check the  page to read more about how to achieve this.

## Warning: Function components cannot be given refs

---

When using components enabling dynamic component (through the `as` attribute), you may encounter the following error message in the console:

```
Warning: Function components cannot be given refs.
Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
```

This means you're passing a function component to the `as` attribute and it needs to be wrapped by a `forwardRef` to be able to manage the `ref` correctly.

```tsx
import { Link } from '@ovhcloud/ods-react';
import { forwardRef, useRef } from 'react';
const DummyLink = forwardRef(({ children, ...props }, ref) => {
  return (
    <a data-test="dummy" { ...props } ref={ ref }>{ children }</a>
  );
});
const MyApp = () => {
  const linkRef = useRef(null);
  return (
    <div>
      ...      <Link as={ DummyLink } href="#" ref={ linkRef }>...</Link>
    </div>
  );
};
```

## Get Started

---

OVHcloud Design System is a set of reusable React UI components to create a consistent user experience across OVHcloud pages and products, acting as a single source of truth.

All OVHcloud Design System packages are available on [NPM](https://www.npmjs.com) .

## Installation

---

Include **ODS** in your projects like any other npm/yarn module:

```bash
npm i --save-exact @ovhcloud/ods-react @ovhcloud/ods-themes
```

or

```bash
yarn add --exact @ovhcloud/ods-react @ovhcloud/ods-themes
```

## Usage

---

```tsx
import { Button } from '@ovhcloud/ods-react';
const App = () => {
  return (
    <Button>
      Some button    </Button>
  );
};
```

Components are compatible with React `v18.2+`, `v19+`.

## Import components constants

---

All enums, interfaces, events are available directly from the `ods-react` lib

```typescript
import { BUTTON_SIZE, type CheckboxProp } from '@ovhcloud/ods-react';
```

## Import theme & fonts

---

You need to import a theme to display all the components correctly. It defines all the colors, typographies, etc...

via ES import:

```typescript
import '@ovhcloud/ods-themes/default/css';
import '@ovhcloud/ods-themes/default/fonts';
```

via Sass import:

```css
@import '@ovhcloud/ods-themes/default/css';
@import '@ovhcloud/ods-themes/default/fonts';
```

In case you encounter some issues while importing the font files, you can fallback to the base64 import using `import '@ovhcloud/ods-themes/default`. Though this will increase the bundle size and prevent browser resources loading optimization.

## Typescript

---

To ensure the types are correctly resolved, you'll have to use the `bundler` module resolution, released with [Typescript 5.0](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#--moduleresolution-bundler) .

```typescript
"moduleResolution": "bundler"
```

## Accessibility Guide

---

## Introduction

---

This guide focuses on accessibility best practices specifically for developers integrating the **OVHcloud Design System**.

While our components are built with accessibility in mind, using semantic HTML elements and including the necessary ARIA attributes by default. Nevertheless, ensuring full accessibility in your specific use case might require you to implement additional ARIA attributes, roles, and behaviors at the integration level.

However, depending on the context, you might need to enhance a component's accessibility. This guide will help you understand:

-   When and how to use ARIA properly
-   Common mistakes and best practices
-   How screen readers interpret different ARIA attributes

For broader accessibility information beyond the **OVHcloud Design System**, consult our [Accessibility Statement](https://www.ovhcloud.com/fr/accessibility/) available on our official website.

### Before using ARIA

#### Consult the Accessibility section for each component

Each component in **OVHcloud Design System** comes with a dedicated **"Accessibility"** section that provides best practices and usage guidance.

#### Be careful when adding extra ARIA attributes as ODS components may already handle it

To be as close as possible to the accessible experiences provided by modern browsers, **OVHcloud Design System** aims to rely on semantic and native HTML elements whenever they are inherently well-suited for screen reader experiences.

In addition to the predefined accessibility features of our components, you can find more guidance regarding accessible integration in the **"Accessibility"** section.

## Essential ARIA attributes

---

### aria-label: Providing an accessible name

Use [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label) when an element needs a descriptive name but has no visible text.

```jsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button>
      <Icon name={ICON_NAME.xmark} />
    </Button>
}
```

Screen readers will announce a button.

```jsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button aria-label='Clear'>
        <Icon name={ICON_NAME.xmark} />
    </Button>
}
```

Screen readers will announce a button and the aria-label.

#### Related Sources

-   [MDN Documentation - aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label)
-   [RGAA - 6.1](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#6.1)
-   [RGAA - 7.2](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#7.2)
-   [RGAA - 11.1](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.1)
-   [RGAA - 11.2](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.2)
-   [RGAA - 11.9](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.9)

### aria-labelledby: Referencing another element as a label

Use [aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby) to associate an element with an existing label (instead of duplicating text).

```jsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button>
        <Icon name={ICON_NAME.filter} />
      </Button>
      <span>Filter your search results</span>
    </>
}
```

Screen readers will announce a button.

```jsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button aria-labelledby="filter-btn">
        <Icon name={ICON_NAME.filter} />
      </Button>
      <span id="filter-btn">Filter your search results</span>
    </>
}
```

When the **Button** is focused, the screen reader should announce the linked label.

Use [aria-label](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label) when there is no visible label, and [aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby) when there is one.

#### Related Sources

-   [MDN Documentation - aria-labelledby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby)
-   [RGAA - 6.1](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#6.1)
-   [RGAA - 7.2](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#7.2)
-   [RGAA - 11.1](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.1)
-   [RGAA - 11.2](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.2)
-   [RGAA - 11.5](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.5)
-   [RGAA - 11.9](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.9)

### aria-describedby: Providing additional context

[aria-describedby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby) associates an interactive element with extra information, ensuring screen reader users receive helpful guidance without disrupting the UI.

#### Difference between aria-labelledby and aria-describedby

| Attribute | Purpose | When to use it? | Reading order |
| --- | --- | --- | --- |
| aria-labelledby | Defines the main context of the element | When an element has no visible text or needs an external label | Read first by the screen reader |
| aria-describedby | Provides additional description | When secondary information is needed to clarify context | Read after the main label |

#### When to use aria-labelledby vs aria-describedby?

| Use case | `aria-labelledby` | `aria-describedby` |
| --- | --- | --- |
| Use an external / hidden label |  |  |
| Add complementary information |  |  |
| Associate multiple elements to form a label (field group) |  |  |
| Provide contextual help / information (e.g., input rules, field error) |  |  |

```jsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal        </Button>
      </ModalTrigger>
      <ModalContent>
        <ModalBody>
          <h2 id="modal-title">
            Delete item          </h2>
          <p id="modal-content">
            Are you sure you want to delete this item? This action cannot be undone.          </p>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

Screen readers won't announce the **Modal** content.

```jsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal        </Button>
      </ModalTrigger>
      <ModalContent aria-describedby="modal-content" aria-labelledby="modal-title">
        <ModalBody>
          <h2 id="modal-title">
            Delete item          </h2>
          <p id="modal-content">
            Are you sure you want to delete this item? This action cannot be undone.          </p>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

Screen readers will announce the **Modal** content.

#### Related Sources

-   [MDN Documentation - aria-describedby](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby)
-   [RGAA - 11.5](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.5)
-   [RGAA - 11.10](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.10)

### aria-hidden: Hiding elements from screen readers

Use [aria-hidden](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden) set to `"true"` on elements that are purely decorative and should not be announced by screen readers.

```jsx
<Icon name="home" />
```

Some screen readers will announce the icon and detect the presence of the image.

Depending on the screen reader, some additional information might be read aloud, which is confusing and unhelpful.

```jsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon name={ICON_NAME.home} />
}
```

Screen readers will announce nothing, the icon is ignored.

#### Related Sources

-   [MDN Documentation - aria-hidden](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-hidden)
-   [RGAA - 11.2](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.2)

### aria-busy: Indicating loading state

When a section of the UI is loading, users should be informed that the content is not yet ready.

[aria-busy](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-busy) set to `"true"` signals that an element is currently in a loading state. Once loading is complete, `aria-busy` should be set to `"false"`.

```jsx
{
  globals: {
    imports: 'import { Skeleton } from \'@ovhcloud/ods-react\';'
  },
  tags: ['!dev'],
  render: ({}) => <div>
      <Skeleton />
    </div>
}
```

Screen readers will give no indication that the content is loading. The screen reader may start reading the page without knowing that this section is waiting to be updated.

```jsx
{
  globals: {
    imports: 'import { Skeleton } from \'@ovhcloud/ods-react\';'
  },
  tags: ['!dev'],
  render: ({}) => <div aria-busy="true">
      <Skeleton />
    </div>
}
```

Screen readers understand that the content is not yet ready, so they delay announcing or interacting with it.

#### Related Sources

-   [MDN Documentation - aria-busy](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-busy)
-   [RGAA - 7.4](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#7.4)

### status role: Announcing dynamic status changes

Use [status role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/status_role) to ensure that important updates are announced by screen readers without disrupting focus.

```jsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [message, setMessage] = useState('');
    const handleClick = () => {
      setMessage('Copied to clipboard.');
    };
    return <>
        <Button onClick={handleClick}>
          Copy        </Button>
        <span style={{
        marginLeft: '1rem'
      }}>
          {message}
        </span>
      </>;
  }
}
```

Screen readers will announce the button and its label.

```jsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [message, setMessage] = useState('');
    const handleClick = () => {
      setMessage('Copied to clipboard.');
    };
    return <>
        <Button onClick={handleClick}>
          Copy        </Button>
        <span aria-live="polite" role="status" style={{
        marginLeft: '1rem'
      }}>
          {message}
        </span>
      </>;
  }
}
```

Screen readers will announce the button and its label, and the message on click or press.

It also matters since it keeps focus on the button, avoiding disruption for keyboard users.

#### Related Sources

-   [MDN Documentation - status role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/status_role)
-   [RGAA - 7.4](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#7.4)

### alert role: Announcing important messages

Use [alert role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/alert_role) for critical, time-sensitive messages that require immediate attention. Unlike `status role`, `alert role` is announced by screen readers, even if the user is focused elsewhere.

```jsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [error, setError] = useState('');
    const handleClick = () => {
      setError('A critical error occurred while saving.');
    };
    return <>
        <Button onClick={handleClick}>
          Save        </Button>
        <span style={{
        marginLeft: '1rem',
        color: 'red'
      }}>
          {error}
        </span>
      </>;
  }
}
```

Screen readers won’t immediately announce the error message when it appears, unless the user manually navigates to it.

```jsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [error, setError] = useState('');
    const handleClick = () => {
      setError('A critical error occurred while saving!');
    };
    return <>
        <Button onClick={handleClick}>
          Save        </Button>
        <span role="alert" style={{
        marginLeft: '1rem',
        color: 'red'
      }}>
          {error}
        </span>
      </>;
  }
}
```

Screen readers will announce the error message, even when the user is focused elsewhere.

#### Related Sources

-   [MDN Documentation - alert role](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/alert_role)
-   [RGAA - 7.4](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#7.4)

### radiogroup and radio roles: Making custom radio controls accessible

[radiogroup](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/radiogroup_role) and [radio](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/radio_role) roles ensure that screen readers correctly interpret and announce the selection.

```jsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <label htmlFor="rating">
        Rating      </label>
      <div id="rating">
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="one star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="two star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="three star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="four star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="five star" role="img" />
      </div>
    </>
}
```

Screen readers will read each star individually with no context of selection group.

```jsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <label htmlFor="rating" id="rating-label">
        Rating      </label>
      <div aria-labelledby="rating-label" id="rating" role="radiogroup">
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="one star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={0} aria-label="two star" aria-checked="true" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="three star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="four star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="five star" aria-checked="false" />
      </div>
    </>
}
```

Screen readers will announce a radio button, its label and aria-label, which item is focused on out of the total amount of items and that it is a radio group.

Please note that you should navigate within a radio group using `arrow` keys.

In the example above, `tabindex="-1"` is applied to all elements except the currently selected one. This is because only one item in a radio group should be focusable at a time. Users can navigate between options using the `arrow` keys, rather than the `Tab` key, which moves focus to the next interactive element outside the group.

For more details, see [Managing focus within components using a roving tabindex](https://www.w3.org/WAI/ARIA/apg/patterns/radio/examples/radio/)

#### Sources

-   [MDN Documentation - radiogroup](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/radiogroup_role)
-   [MDN Documentation - radio](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/radio_role)
-   [RGAA - 11.5](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests/#11.5)

## Final Accessibility check for ARIA roles

---

### Validate the screen reader behavior

Test with a screen reader to ensure that your changes are communicated correctly.

Ensure that actions such as button clicks, form submissions, and changes in state are clearly communicated to the user.

### Testing Environment

For consistent testing across different platforms and screen readers, here is the testing setup used by **OVHcloud Design System**, based on the recommendations from RGAA (France):

| Assistive Technology | Browser |
| --- | --- |
| NVDA | Firefox |
| JAWS | Firefox or Internet Explorer |
| Safari | VoiceOver |

## Resources to help you test accessibility

---

-   [Accessibility France Guidelines (FR)](https://accessibilite.numerique.gouv.fr) : The French government's official accessibility resource for best practices and testing criteria
-   [Web Content Accessibility Guidelines (WCAG) 2.1](https://www.w3.org/TR/WCAG21) : The internationally recognized standards for web accessibility, providing guidelines for creating more accessible web content
-   [WebAIM: Introduction to Web Accessibility](https://webaim.org/intro) : A guide to the principles of web accessibility from WebAIM
-   [WebAIM: Semantic Structure](https://webaim.org/techniques/semanticstructure) : Learn about the importance of semantic HTML and how it impacts accessibility
-   [WebAIM: Contrast Checker](https://webaim.org/resources/contrastchecker) : Tool to ensure sufficient color contrast between text and background for readability
-   [French Accessibility Testing Methodology (FR)](https://accessibilite.numerique.gouv.fr/methode/criteres-et-tests) : An in-depth guide to testing web accessibility in France
-   [French Testing Environment (FR)](https://accessibilite.numerique.gouv.fr/methode/environnement-de-test) : Official guidelines on the tools and technologies to use when testing accessibility on desktop computers
-   [W3C Tutorial on Alternative Text for Images](https://www.w3.org/WAI/tutorials/images/decision-tree) : A decision tree for writing appropriate alternative text for images

## Handling Form

---

ODS form elements can either be [controlled or uncontrolled](https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components) .

You can use each form element directly in your own form, or wrap them using the  component.

## Form Field

---

Using  component will enforce layout, design, behavior and accessibility through a few dedicated components:

-   `FormFieldLabel`
-   `FormFieldHelperText`
-   `FormFieldError`

You can also deport the `invalid` prop from the form element to the `FormField` component which will handle the error display automatically.

```tsx
<FormField invalid={ someCondition }>
  <FormFieldLabel>
    Input:  </FormFieldLabel>
  <Input name="input" />
  {/* Displayed only if invalid on FormField is true */}
  <FormFieldError>
    Error message  </FormFieldError>
</FormField>
```

## Uncontrolled Form

---

Uncontrolled component will not have the `value` property set.

You can use the `defaultValue` property to initialize the component with a value.

Example of uncontrolled form:

```tsx
import { Button, FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';
import { type FormEvent, useRef } from 'react';
const UncontrolledForm = () => {
  const formRef = useRef<HTMLFormElement>(null);
  function onSubmit(e: FormEvent) {
    e.preventDefault();
    if (formRef.current) {
      const formData = new FormData(formRef.current);
      console.log(formData);
    }
  }
  return (
    <form
      onSubmit={ onSubmit }
      ref={ formRef }>
      <FormField>
        <FormFieldLabel>
          Input:        </FormFieldLabel>
        <Input
          defaultValue="default"
          name="input" />
      </FormField>
      <Button type="submit">
        Submit button      </Button>
    </form>
  );
};
```

## Controlled Form

---

Controlled components will manage their `value` prop on their own and react to value change events.

Example of controlled form:

```tsx
import { Button, FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';
import { type ChangeEvent, useState } from 'react';
const ControlledForm = () => {
  const [inputValue, setInputValue] = useState('default');
  function onInputChange(e: ChangeEvent<HTMLInputElement>) {
    setInputValue(e.target.value);
  }
  function onSubmit(e: FormEvent) {
    e.preventDefault();
    console.log('input: ', inputValue);
  }
  return (
    <form onSubmit={ onSubmit }>
      <FormField>
        <FormFieldLabel>
          Input:        </FormFieldLabel>
        <Input
          name="input"
          onChange={ onInputChange }
          value={ inputValue } />
      </FormField>
      <Button type="submit">
        Submit button      </Button>
    </form>
  );
};
```

## Using form library

---

We'll demonstrate here a few form librairy, but you can plug ODS form elements with any library you like.

Regardless of the library used, you can also use a schema validator to manage your data (like [yup](https://github.com/jquense/yup) or [zod](https://zod.dev/) ).

### react-hook-form

[React Hook Form](https://react-hook-form.com) is thought around native uncontrolled form elements (using `register`).

These will mork on most ODS components, but some specific one may still need to rely on the lib `Controller` component.

```tsx
import { Button, Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';
import { Controller, useForm } from 'react-hook-form';
type FormData = {
  datepicker: string,
  input: string,
}
const FormHookForm = () => {
  const {
    control,
    formState: { errors },
    handleSubmit,
    register,
    setValue,
  } = useForm<FormData>({ mode: 'onBlur' });
  function onSubmit(data: FormData): void {
    console.log(data);
  }
  return (
    <form onSubmit={ handleSubmit(onSubmit) }>
      <FormField invalid={ !!errors.input }>
        <FormFieldLabel>
          Input:        </FormFieldLabel>
        <Input { ...register('input') } />
      </FormField>
      <Controller
        control={ control }
        name="datepicker"
        render={ ({ field} ) => (
          <FormField invalid={ !!errors.datepicker }>
            <FormFieldLabel>
              Datepicker:            </FormFieldLabel>
            <Datepicker onValueChange={ ({ value }) => setValue(field.name, value) }>
              <DatepickerControl />
              <DatepickerContent />
            </Datepicker>
          </FormField>
        )} />
      <Button type="submit">
        Submit button      </Button>
    </form>
  );
}
```

### formik

Here is another example using [Formik](https://formik.org) library.

```tsx
import { Button, Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';
import { useFormik } from 'formik';
type FormData = {
  datepicker: string,
  input: string,
}
const FormFormik = () => {
  const formik = useFormik<FormData>({
    onSubmit: (values) => {
      console.log(values);
    },
    validateOnMount: true,
    validationSchema,
  });
  return (
    <form onSubmit={ formik.handleSubmit }>
      <FormField invalid={ formik.touched.input && !!formik.errors.input }>
        <FormFieldLabel>
          Input:        </FormFieldLabel>
        <Input
          name="input"
          onBlur={ formik.handleBlur }
          onChange={ formik.handleChange } />
      </FormField>
      <FormField invalid={ formik.touched.datepicker && !!formik.errors.datepicker }>
        <FormFieldLabel>
          Datepicker:        </FormFieldLabel>
        <Datepicker
          name="datepicker"
          onBlur={ formik.handleBlur }
          onValueChange={ ({ value }) => {
            formik.setFieldValue('datepicker', value);
          }}>
          <DatepickerControl />
          <DatepickerContent />
        </Datepicker>
      </FormField>
      <Button type="submit">
        Submit button      </Button>
    </form>
  );
}
```

## Internationalization

---

ODS does not embed any i18n library, as most of the translatable contents are coming from the integration side.

There are two exceptions, components with full embedded translation and components with default translation on some accessibility attribute.

## Embedded translation

---

So far, only the following components internally manage a complete translation system:

-   
-   

Both are relying on the native [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) feature.

They do de-facto support the same locales as [Intl](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl) .

## ODS specific translation

---

Although you have complete control on label translation when using the ODS components, there are a few internal elements that need a textual description for accessibility purpose.

For example, take the  component:

Here, the close button needs to have the right `aria-label` value set for accessibility compliance. Though the element is not part of the composable components, it is internal.

By default, ODS will provide a default `aria-label` message for all locales that we currently support. So if you use the component as-is, it will be accessible already.

In case you want to change the locale to one supported by ODS, you can use the `locale` attribute of the component.

```tsx
// This will change the close button aria-label to the french version
<Message locale="fr">
  <MessageBody>
    Mon message  </MessageBody>
</Message>
```

If the `locale` is not set, the component will try to use the navigator languages by default.

In case you want to change to a locale not supported by ODS, or you want to override the default messages, you can set your own translation using the `i18n` attribute of the component.

```tsx
// This will change the close button aria-label to your own string
<Message i18n={{ [MESSAGE_I18N.closeButton]: 'Remove the notification' }}>
  <MessageBody>
    Mon message  </MessageBody>
</Message>
```

## ODS supported locale

---

Current locales supported by ODS:

-   de
-   en
-   es
-   fr
-   it
-   nl
-   pl
-   pt

## ## A collection of assets, guidelines and UI components for building consistent user experiences across OVHcloud products.

[Design Guidelines](https://zeroheight.com/6fc8a63f7/p/533db0-ovhcloud-design-system) [GitHub repository](https://github.com/ovh/design-system)

## [12.1.3](https://ovh.github.io/design-system/v12.1.3/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [13.0.0](https://ovh.github.io/design-system/v13.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/12.1...release/13.0))

## Select width

---

The Select component fixed width when non flex has been removed.

```css
width: calc(var(--ods-size-stack-08) * 11)
```

Should you need any width, you will have to set it on your component.

## [13.0.1](https://ovh.github.io/design-system/v13.0.1/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [14.0.0](https://ovh.github.io/design-system/v14.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/13.0...release/14.0))

## Tile interactive attribute

---

The Tile component `interactive` attribute has been removed.

```typescript
interactive?: boolean;
```

If you are using `interactive` attribute on a Tile in your project, you should remove it.

## [14.1.1](https://ovh.github.io/design-system/v14.1.1/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [15.0.0](https://ovh.github.io/design-system/v15.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/14.0...release/15.0))

## Text

---

### Dependency

Add a dependency to `@ovhcloud/ods-component-text` or `@ovhcloud/ods-components`.

### Import

Replace any imports like the followings:

```typescript
// Component
import { OsdsText } from '@ovhcloud/ods-stencil/components/text'
import { OsdsText } from '@ovhcloud/ods-stencil/components/text/react'
import { OsdsText } from '@ovhcloud/ods-stencil/components/text/vue'
// Enums
import { OdsTextLevel } from '@ovhcloud/ods-core'
import { OdsTextLevelList } from '@ovhcloud/ods-core'
import { OdsTextSize } from '@ovhcloud/ods-core'
import { OdsTextSizeList } from '@ovhcloud/ods-core'
// Interfaces
import { OdsTextAttributes } from '@ovhcloud/ods-core'
```

to:

```typescript
// Component
import { OsdsText } from '@ovhcloud/ods-component-text'
import { OsdsText } from '@ovhcloud/ods-component-text/react'
import { OsdsText } from '@ovhcloud/ods-component-text/vue'
// Enums
import { ODS_TEXT_LEVEL } from '@ovhcloud/ods-component-text'
import { ODS_TEXT_LEVELS } from '@ovhcloud/ods-component-text'
import { ODS_TEXT_SIZE } from '@ovhcloud/ods-component-text'
import { ODS_TEXT_SIZES } from '@ovhcloud/ods-component-text'
// Interfaces
import type { OdsTextAttribute } from '@ovhcloud/ods-component-text'
```

Following interfaces has been removed:

-   `OdsText`
-   `OdsTextEvents`
-   `OdsTextMethods`
-   `OdsTextLevelUnion`
-   `OdsTextSizeUnion`

## Textarea

---

### Dependency

Add a dependency to `@ovhcloud/ods-component-textarea` or `@ovhcloud/ods-components`.

### Import

Replace any imports like the followings:

```typescript
import { OsdsTextarea } from '@ovhcloud/ods-stencil/components/textarea'
import { OsdsTextarea } from '@ovhcloud/ods-stencil/components/textarea/react'
import { OsdsTextarea } from '@ovhcloud/ods-stencil/components/textarea/vue'
// Enums
import { OdsTextAreaSize } from '@ovhcloud/ods-core'
import { OdsTextAreaSizeList } from '@ovhcloud/ods-core'
// Interfaces
import { OdsTextAreaAttributes } from '@ovhcloud/ods-core'
import { OdsTextAreaEvents, OdsTextAreaChangeEventDetail } from '@ovhcloud/ods-core'
import { OdsTextAreaMethods } from '@ovhcloud/ods-core'
```

to:

```typescript
// Component
import { OsdsTextarea } from '@ovhcloud/ods-component-textarea'
import { OsdsTextarea } from '@ovhcloud/ods-component-textarea/react'
import { OsdsTextarea } from '@ovhcloud/ods-component-textarea/vue'
// Enums
import { ODS_TEXT_LEVEL } from '@ovhcloud/ods-component-textarea'
import { ODS_TEXT_LEVELS } from '@ovhcloud/ods-component-textarea'
import { ODS_TEXT_SIZE } from '@ovhcloud/ods-component-textarea'
import { ODS_TEXT_SIZES } from '@ovhcloud/ods-component-textarea'
// Interfaces
import type { OdsTextAreaAttribute } from '@ovhcloud/ods-component-textarea'
import type { OdsTextAreaEvent, OdsTextAreaChangeEvent } from '@ovhcloud/ods-component-textarea'
import type { OdsTextAreaMethod } from '@ovhcloud/ods-component-textarea'
```

Following interfaces has been removed:

-   `OdsTextArea`
-   `OdsTextAreaBehavior`
-   `OdsTextAreaSizeUnion`

## [15.0.1](https://ovh.github.io/design-system/v15.0.1/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [16.0.0](https://ovh.github.io/design-system/v16.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/15.0...release/16.0))

Presenting all changes, components per components will cause a very long and hard to use changelog.

Instead we'll present you the fundamental changes that have been applied for all of the existing components.

The same logics has been applied to each components.

If you have any issue finding a specific change, feel free to contact us directly and we'll help you migrate.

## All Components

---

### Dependency

To use the component add a dependency to either:

-   `@ovhcloud/ods-component-<component>` to import only the specific component.
-   `@ovhcloud/ods-components` to import all components.

### Import

Replace any imports like the followings:

```typescript
// Previously component were imported using one of:
import { OsdsSpinner } from '@ovhcloud/ods-stencil/components/spinner'
import { OsdsSpinner } from '@ovhcloud/ods-stencil/components/spinner/react'
import { OsdsSpinner } from '@ovhcloud/ods-stencil/components/spinner/vue'
// Enums were imported from the ods-core library
import { OdsSpinnerSize, OdsSpinnerSizeList } from '@ovhcloud/ods-core'
// Interfaces were imported from the ods-core library
import { OdsSpinnerAttributes } from '@ovhcloud/ods-core'
```

to:

```typescript
// Component are now imported using one of:
import { OsdsSpinner } from '@ovhcloud/ods-component-spinner'
import { OsdsSpinner } from '@ovhcloud/ods-component-spinner/react'
import { OsdsSpinner } from '@ovhcloud/ods-component-spinner/vue'
// Enums are now imported directly from the component
import { ODS_SPINNER_SIZE, ODS_SPINNER_SIZES } from '@ovhcloud/ods-component-spinner'
// Interfaces are now imported directly from the component
import type { OdsSpinnerAttribute } from '@ovhcloud/ods-component-spinner'
```

Some types have been removed from ODS, as they were empty or not used, if you can't find a type you were using, please contact us to see where it has been moved or how to replace it.

## [16.6.0](https://ovh.github.io/design-system/v16.6.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [17.0.0](https://ovh.github.io/design-system/v17.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/16.0...release/17.0))

Main focus on this version has been on simplifying ODS integration in existing projects (React, Vue, JS, ...).

If you have any issue finding a specific change, feel free to contact us directly and we'll help you migrate.

## All Components

---

### Dependency

We now only publish one `ods-components` library that embed all the ODS components.

You need to remove any component specific dependencies, ex:

```text
"ods-component-button": "16.6.0" // should be removed
```

Instead use only one dependency:

```text
"ods-components": "17.0.0"
```

### Import

You can see more details in the updated 

#### Native webcomponents

Using lazy-loading:

```typescript
import { defineCustomElements } from '@ovhcloud/ods-components/dist/loader';
defineCustomElements();
```

As module:

```typescript
import { defineCustomElement as defineButton } from '@ovhcloud/ods-components/dist/components/osds-button';
defineButton();
```

#### React components

```typescript
import { OsdsButton, OsdsText } from '@ovhcloud/ods-components/react';
```

#### Vue components

```typescript
import { OsdsButton, OsdsText } from '@ovhcloud/ods-components/vue';
```

#### Types and constants

All types and constants are accessible from the `ods-components` library:

```typescript
import type { OdsButtonAttribute } from '@ovhcloud/ods-components'
import { ODS_BUTTON_SIZE, ODS_BUTTON_SIZES } from '@ovhcloud/ods-components';
```

### About `ods-common-xxx` libs

We're trying to remove the need to add any `ods-common-xxx` lib to any project using ODS.

For now, you will still need to add `ods-common-theming` as a dependency to access the `ODS_THEME_COLOR_INTENT` values.

If you still ended up adding `ods-common-core` in your project, please give us the feedback so that we work on removing that need.

## [17.2.2](https://ovh.github.io/design-system/v17.2.2/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) to [18.0.0](https://ovh.github.io/design-system/v18.0.0/?path=/story/ovhcloud-design-system-what-s-new-changelog--page) ([compare](https://github.com/ovh/design-system/compare/release/17.0...release/18.0))

If you have any questions or issues regarding the new version, feel free to reach us directly.

## Libraries

---

The following libs have been removed:

-   @ovhcloud/ods-cdk
-   @ovhcloud/ods-common-core
-   @ovhcloud/ods-common-stencil
-   @ovhcloud/ods-common-testing
-   @ovhcloud/ods-common-theming
-   @ovhcloud/ods-theme-blue-jeans

If you were using part of them, please reach out to us so that we can get more information about your usage.

To use the new theme, replace `ods-theme-blue-jeans` with the following:

```typescript
import '@ovhcloud/ods-themes/default';
```

## Design tokens

---

Design tokens are now accessible through the `@ovhcloud/ods-themes` lib.

The list of available tokens has been updated with the latest design changes and match what you may find on the Figma screens.

## Components

---

As each component was refactored, there are too many changes to list everything here.

The main common breaking change is the following: `osds-xxx` component is now `ods-xxx`

A complete list of changes and a guide about how to migrate is available per component in the v18 documentation.

Use the top-left version selector to move to the latest v18.x documentation where you'll find the latest up-to-date information.

## Migrate to v19

---

Version 19 of ODS is not the direct follow-up of previous versions, it is a new library built 100% on top of [React](https://react.dev) .

Previous versions were providing a React version of each components too, but there were in fact a wrapping of the Web Component. Thus it was designed with the constraints related to Web Component (no virtual DOM, relying on DOM selector, ...).

With this new version we got rid of those constraints, which gave us some benefits like:

-   component composition.
-   easier style customization.
-   easier React integration (no more `ref` needed, nor literal templates).

We've also alleviate a lot of previous component restrictions, as most of them now accept React nodes instead of fixed property.

For example, Button will now accept any node (text, text + icon, triple icon, ...) and won't force you to set only a label and an optional icon.

Moving out of Web Component also means getting rid of the Shadow DOM. This means you now have direct access to the component DOM and it's easier to add you own style. Though this also means it is easier to break the component rendering, and some custom selectors may break from version to version (see  to know more).

Regarding components, we tried to stay as close as possible to their previous interface while keeping them easy to integrate. Though, the way you'll integrate them in your React app should be rethought, to ensure you use them as real React components and not wrapped Web Components anymore.

To check how to start with this new version, please follow the  documentation.

## What's new ?

---

## v19.3.0

---

### New components:

**Toaster**

### Design updates:

ODS theme design tokens have been reworked to be more semantic. It is now easier to understand how each token should be used. Each component have been updated accordingly and are now more normalized design-wise. Each component now also expose custom tokens that may be used to override rendering on a component-level.

All those changes are the first major step towards a complete reworked theme that aims to provide more consistency, easier customization and understanding.

[DEPRECATED] Previous design tokens should be replaced by the corresponding theme tokens.

**Accordion**

-   Border color has been changed and is now the same for every component states.

**Combobox**

-   `invalid` overlay now uses the critical color.

**File Upload**

-   A more compact version is now available (using `variant` attribute).
-   Existing layout has been updated to be more compact.

**Message**

-   Light messages now have a colored border.

**Range**

-   Track color now is the same as other track elements.
-   Cursor has been changed to default (as in native).
-   Thumb background color change on hover/focus/drag has been removed.

**Select**

-   `invalid` overlay now uses the critical color.
-   Groups are now separated by a border.

**Skeleton**

-   Default background color is now slightly darker.

**Table**

-   Default `min-width` has been removed on mobile view.

**Text**

-   Font size now uses `rem` instead of `px`. This will only impact app where the root element `font-size` is not `16px`.

**Toggle**

-   Hover effect on `off` label has been removed.

### Feature updates:

**Code**

-   New attribute `highlighter` has been added.
-   Documentation has been updated with how to setup a custom highlighter.

**Datepicker**

-   `value` now also accepts `null` as a value.
-   `DatepickerValueChangeDetail` `value` and `valueAsString` now may be `null`.

**Divider**

-   [DEPRECATED] Color attribute will now always be primary, if you need another color, prefer overriding it using css.

**File Upload**

-   New attribute `variant` has been added.

**Text**

-   New attribute `disabled` has been added.
-   New preset `small` has been added.

### Bug fixes:

**Combobox**

-   Clicking another combobox while focused on another one, now correctly closes the first one.
-   Controlled value changed externally is now correctly reflected.

**Datepicker**

-   Clear when controlled now works as expected.

**Link**

-   Disabled links do not have an underline animation when hovered.

**Tabs**

-   `tablist` role now correctly contains only valid children.
-   Now doesn't automatically scroll to the component on render.

### Documentation improvements:

A new tool has been added to the documentation, the `Theme Generator`. This allows users to experiment with the ODS tokens directly in the storybook. You can preview how each design tokens will impact the components, generate your own color palettes and export/import you custom theme.

ODS documentation is now available as LLM files to help AI tools to better understand our library and its usage.

-   Add a theme selector on the top left menu.
-   Implement dark mode documentation.
-   New section `AI Agents` added to the documentation.
-   Design Tokens have been updated and completed with color palettes.
-   All technical pages have been updated with components custom CSS variables.
-   Icon `All` page search control now accepts special character.
-   Update FAQ.

## v19.2.1

---

### Feature updates:

**Combobox**

-   New attribute `customFilter` has been added.

### Bug fixes:

**Button**

-   Use correct border radius for `sm` size.

**Input**

-   Normalize border style on hover & focus.

**Password**

-   Normalize border style on hover & focus.

**Tabs**

-   Container bottom border takes the whole width.

### Documentation improvements:

-   Update FAQ.
-   Remove ToC on Changelog page.

### Miscellaneous:

**Tabs**

-   Add a console warning when no `defaultValue` or `value` are set.

## v19.2.0

---

### New components:

**Meter**

**TreeView**

### Font changes:

Font files are now accessible as actual files instead of base64 conversion. This will reduce the size of the ODS style bundle and allow browser resource loading parallelization.

Thanks to [Ennoriel](https://github.com/ennoriel) for the contribution.

**@ovhcloud/ods-react**

-   Now exposes a css reset file (using `import '@ovhcloud/ods-react/normalize-css';`)

**@ovhcloud/ods-themes**

-   Now exposes css without fonts (using `import '@ovhcloud/ods-themes/default/css';`)
-   Now exposes font-faces separately (using `import '@ovhcloud/ods-themes/default/fonts';`)

### Feature updates:

**@ovhcloud/ods-themes**

-   Now exposes design tokens as JSON (using `import '@ovhcloud/ods-themes/default/tokens';`)

**File Upload**

-   Improve i18n default translations.

**Icon**

-   Add 40 new icons.

**Password**

-   Improve i18n default translations.

**Phone Number**

-   Improve i18n default translations.

**Popover**

-   New attribute `autoFocus` has been added.
-   New attribute `gutter` has been added.
-   New attribute `sameWidth` has been added.
-   New attribute `onPositionChange` has been added.
-   New attribute `triggerId` has been added.

**Range**

-   New attribute `displayBounds` has been added.
-   New attribute `displayTooltip` has been added.
-   Ticks now accepts objects with custom labels.
-   Range component size is now correctly computed, it will fit better in the page rendering flow.

**Tabs**

-   New attribute `withArrows` has been added.

**Timepicker**

-   Improve i18n default translations.

### Bug fixes:

**Combobox**

-   Has been rewritten internally, but the usage stays the same.
-   Fix selection behavior issues.
-   Add style on selected element.
-   Fix missing style on group separator.

**Input**

-   Clearable now always appears correctly when controlled.

**Quantity**

-   Fix default value stuck when used inside a FormField.

**Range**

-   Update ticks position computation to match thumb size.

### Documentation improvements:

A new tool has been added to the documentation, the `Code sandbox`. This allows users to experiment with the ODS library directly in the storybook. You can also share your sandbox code with others. This is especially useful when trying to reproduce some issue you encounter, but in a smaller dedicated context.

-   New section `Tools` added to the documentation.
-   A sandbox shortcut has been added to every examples.
-   New dedicated page about Tailwind integration.
-   Add badges when relevant near navigation menu items.
-   Update "Get Started" with fonts changes.
-   Update "Apply ODS Style" with CSS reset.
-   Update GitHub repository contributing page.
-   Update FAQ with contribution information.
-   Multiple typo fixes.

### ODS dependencies upgrade:

-   ArkUI bumps from `5.12.0` to `5.25.1`.

## v19.1.0

---

### Accessibility improvements:

#### a11y

This version focuses on improving the accessibility for each component.

We've implemented all the internal requirements on each component, validated using [axe-core](https://github.com/dequelabs/axe-core) and [lighthouse](https://www.npmjs.com/package/lighthouse) .

Though, ensuring accessibility for your context does require you to use the right `aria`/`role`/... attribute at the right place.

To help you with that, we've added:

-   a  best practices.
-   a dedicated `Accessibility` section on each component `Documentation` page.

#### i18n

Some components provide internal features that need an explicit context. For example the clear button on the `Input` which require an `aria-label` describing that the button will clear the value.

We do provide default values for each supported locales to ensure that integrating ODS component as is will already convey the minimal information to impaired users.

You can customize those value further with your own translations.

See the related storybook  for more information.

### Design updates:

Some design changes may impact your layouts.

**Breadcrumb**

-   `fontSize` is now inherited and no more fixed to `0.875rem`.

**Checkbox**

-   Added minimal vertical spacing on group.
-   Checkbox size is now `16px`.

**Design Tokens**

-   New color variants `-025` and `-075` have been added.

**Progress Bar**

-   Now defaults to full width.

**Tag**

-   `md` height is now `24px` by default.

### Feature updates:

**All components**

-   A new attribute `data-ods` has been added to each exposed component, allowing them to be identified in the DOM.

**Breadcrumb**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Button**

-   Improve button contrast for accessibility.

**Clipboard**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Combobox**

-   Added `customRendererData` on combobox items.
-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Datepicker**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**File Upload**

-   New attribute `i18n` has been added on `FileUploadItem`.
-   New attribute `locale` has been added.

**Input**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Message**

-   Improve color contrast for accessibility.
-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Modal**

-   New attribute `i18n` has been added.
-   New attribute `initialFocusedElement` has been added.
-   New attribute `locale` has been added.

**Password**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Phone Number**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Quantity**

-   Increment and decrement buttons are no longer focusable, allowing keyboard navigation to focus solely on the input field for consistent vocalization.

**Select**

-   Items `customData` now accept an optional generic type.

**Timepicker**

-   New attribute `i18n` has been added.
-   New attribute `locale` has been added.

**Toggle**

-   New component `ToggleControl` has been added.
-   New component `ToggleLabel` has been added.
-   [DEPRECATED] Using `<Toggle />` alone will still works as expected, though we advise to move to composition using `ToggleControl` and `ToggleLabel`.

### Bug fixes:

**Combobox**

-   Click on `FormField` label now behave like native.

**File Upload**

-   Click on `FormField` label now behave like native.

**Phone Number**

-   Following country codes `bq`, `gf`, `gp`, `io`, `pm`, `re`, `sj`, `sx` have been added.
-   Click on `FormField` label now focus input instead of country select.

**Popover**

-   Positioning is now correct if a custom ID is set on the trigger element.
-   The trigger is now focusable by default.

**Tooltip**

-   Positioning is now correct if a custom ID is set on the trigger element.
-   The trigger is now focusable by default.

### Documentation improvements:

-   New `Accessibility` page has been added in the `Guides` section.
-   New section `Accessibility` has been added on every component `Documentation` page with best practices and examples.
-   A `react-router` `Link` example has been added.
-   Github links on component `Documentation` pages have been fixed.
-   Atomic design link on component `Documentation` pages have been removed, as it was not relevant.
-   New link to previous major version doc has been added on component `Documentation` pages.
-   A link to form guide has been added on each `FormField` examples.
-   FAQ has been updated.

### Miscellaneous:

-   Fix Windows issues when cloning the repository and running the Storybook scripts.

## v19.0.1

---

### Feature updates:

**Pagination**

-   Add `onPageSizeChange` handler.
-   Add `pageSize` to `PaginationPageChangeDetail` detail object.

## v19.0.0

---

Version 19 of ODS is not the direct follow-up of previous versions, it is a new library built 100% on top of [React](https://react.dev) .

### Project changes:

Previous versions were providing a React version of each components too, but there were in fact a wrapping of the Web Component. Thus it was designed with the constraints related to Web Component (no virtual DOM, relying on DOM selector, ...).

With this new version we got rid of those constraints, which gave us some benefits like:

-   component composition.
-   easier style customization.
-   easier React integration (no more `ref` needed, nor literal templates).

Although, we tried to stay as close as possible to previous version behavior, the way you integrate ODS in your React app should be rethought, to ensure you use components as real React elements and not wrapped Web Components anymore.

To check how to start with this new version, please follow the  documentation.

Here is a non-exhaustive list of the major changes:

-   rework of all components as React elements.
-   tree-shaking.
-   less restriction on components customization and usage.
-   reduced CSS size by moving to variable fonts.
-   no more Ods prefix on all components/constants.
-   no more web-components nor Vue wrappers provided.
-   updated icons (now with default padding embed).
-   updated various component designs (datepicker, message, modal, spinner, ...).
-   new design tokens.
-   updated documentation.
-   card default color changed.
-   ...

## v18.6.4

---

### Bug fixes:

**Clipboard**

-   Container now inherent parent width.

**Code**

-   Now correctly render large text with fixed height.

**Combobox**

-   Clicking an item now correctly select it on Safari.

**File Upload**

-   Allow same file to be uploaded again after cancellation.

**Phone Number**

-   Add following country codes: `bq`, `gf`, `gp`, `io`, `pm`, `re`, `sj`, `sx`.

## v18.6.3

---

### Bug fixes:

**Pagination**

-   Now handles very large number of pages correctly.

## v18.6.2

---

### Bug fixes:

**Combobox**

-   Ensure "Add new entry" option is visible when no items are present.
-   Hide "Add new entry" option when input is cleared.
-   Prevent duplicates when using value or default value in allow multiple mode.

## v18.6.1

---

### Bug fixes:

**Clipboard**

-   Remove spacing between actions and copy button.

**Input**

-   Add spacing between value and actions.

## v18.6.0

---

### New components:

**Combobox**

### Feature updates:

**Badge**

-   New feature to allow icon to be aligned either on left or right side.
-   New attribute `iconAlignment` has been added.
-   New constant `ODS_BADGE_ICON_ALIGNMENT` has been added.

**Clipboard**

-   New attribute `isMasked` has been added.

**Icon**

-   `alt` attribute has been removed. This optional attribute was in fact doing nothing at all, it can safely be kept if you were using it, but you should remove it and follow the  instead.

### Bug fixes:

**Button**

-   Ensure button height stays the same for all variants.
-   Fix `sm` height on all variants.

**Modal**

-   Backdrop is no longer removed when pressing `Escape` on non-dismissible modal.

### Documentation improvements:

-   Add new dedicated page about design tokens under the `Design and Style` section.
-   Update the accessibility section of the  component, with best practices and examples.

## v18.5.3

---

### Feature updates:

**Select**

-   New attribute `strategy` added that let you manage different positioning, default strategy is `absolute`, you can change to `fixed` when in a fixed context (like in an ods-modal for example).
-   New constants `ODS_SELECT_STRATEGY` and `ODS_SELECT_STRATEGIES` are now available.

## v18.5.2

---

### Feature updates:

**Code**

-   Add tooltip message on copy button.
-   New attribute `labelCopy` added.
-   New attribute `labelCopySuccess` added.

**Design Tokens**

-   Add new design token `--ods-form-element-input-height`. Useful when trying to use any input (ods-input, ods-datepicker, ods-select, ...) default height.

**Modal**

-   New part `dialog-content` added that let you customize the content of the modal (ie, between header and actions).

**Phone Number**

-   Pressing a letter while focusing the country selector will now move to the first matching country.

### Bug fixes:

**Code**

-   Spacing will now be correctly preserved.

**Input**

-   Fix disabled style.

**Phone Number**

-   Countries are now correctly sorted when locale get changed.

**Spinner**

-   Spinner now use inline svg instead of svg file. This should remove some of the `eval` warnings on build.

**Tabs**

-   Tabs bottom border now takes the whole width on horizontal overflow context.

### Documentation improvements:

-   Add more documentation about managing overlays on a modal.

## v18.5.1

---

### Feature updates:

**Clipboard**

-   New `ariaLabel` prop has been added.
-   New `ariaLabelledby` prop has been added.
-   New `name` prop has been added.

**Datepicker**

-   New attribute `strategy` added that let you manage different positioning, default strategy is `absolute`, you can change to `fixed` when in a fixed context (like in an ods-modal for example).
-   New constants `ODS_DATEPICKER_STRATEGY` and `ODS_DATEPICKER_STRATEGIES` are now available.

**Medium**

-   Now expose an `image` part.

**Password**

-   New `maxlength` prop has been added.
-   New `minlength` prop has been added.

**Textarea**

-   New `maxlength` prop has been added.
-   New `minlength` prop has been added.

### Bug fixes:

**Accordion**

-   Clickable elements in the slots now behaves as expected when using the React component.

**Checkbox**

-   Fix focus style on error state.

**Code**

-   Copy button is now correctly positioned on the right side.

**Datepicker**

-   Clear button is now correctly displayed when `isClearable` is set, even for invalid value.

**Input**

-   Clear button is now correctly displayed when `isClearable` is set, even for invalid value.

**Password**

-   Clear button is now correctly displayed when `isClearable` is set, even for invalid value.

**Phone Number**

-   Reset now correctly reset the flag, when `countries` is set.
-   Clear button is now correctly displayed when `isClearable` is set, even for invalid value.

**Popover**

-   `triggerId` & `shadowDomTriggerId` now accepts id with special character (ex: `?`, `:`, ...).

**Radio**

-   Fix focus style on error state.

**Tooltip**

-   `triggerId` & `shadowDomTriggerId` now accepts id with special character (ex: `?`, `:`, ...).

### Documentation improvements:

-   Headings link url are now static, which means you can safely bookmark / share them.
-   Fix the `Open canvas in new tab` action in the toolbar on Demo pages.
-   Accessibility tabs has been removed from Demo pages, as it was giving wrong information (as demo are custom made and not valid use case).

## v18.5.0

---

### New components:

**Drawer**

### Feature updates:

**Accordion**

-   Update internal logic to use the native behavior of `details` / `summary` tags.
-   Interactive elements can now be used inside the accordion without toggling it.
-   New methods `close`, `open` and `toggle` have been added.
-   Add some documentation about usage with React state.

**Link**

-   Link will now display an ellipsis by default.
-   New part `label` has been added to allow label customization.

**Modal**

-   Update default width and height.
-   Update dimensions on mobile-size screen.
-   `z-index` has been updated to use ODS overlay z indexes ordering.

**Range**

-   Range can now display ticks.
-   New attribute `ticks` has been added.
-   Thumb hitbox has been improved.

**Select**

-   `z-index` has been updated to use ODS overlay z indexes ordering.

**Tooltip**

-   Tooltip now stays open for a short time on trigger leave, which allows user to move over it and keep it open (and for example select its content).

### Documentation improvements:

-   Add new guide about using components events.
-   Add new guide about using components methods.
-   Update Properties array in the technical page to more clearly display which props are required.
-   Update form-field caption examples.
-   Fix FAQ broken link.

## v18.4.1

---

### Feature updates:

**Tabs**

-   Apply new style on unselected tab text.
-   Apply new hovering style.

**Toggle**

-   Expose new method `toggle`.

### Bug fixes:

**Toggle**

-   Fix toggle style when selected and in an error state.

### Miscellaneous:

We did integrate [react-testing-library](https://testing-library.com/docs/react-testing-library/intro) to our React wrapper test suites, so that we can replicate more easily issues integrators may encounter.

## v18.4.0

---

### Changes on form elements:

This version is focused on implementing [ValidityState](https://developer.mozilla.org/en-US/docs/Web/API/ValidityState) to our form elements.

This allows you to setup a fully working form without the need of any external library or custom validation code, as everything behave as a native form.

Though you can of course use a lib on top of that if you want to. We've validated forms using native code (vanilla and React), [Formik](https://formik.org) and [React Hook Form](https://react-hook-form.com) on our tests and playgrounds.

The changes on each component are massive, but we did focus on ensuring bumping to this version would be transparent for current ODS users.

We won't go through an exhaustive list of changes, here are the most important ones for each form element:

-   Consistent behavior regarding blur and error state.
-   Consistent event triggering (check the guide about event lifecycle).
-   New event `odsInvalid` triggered when component internal validity change.
-   New methods available: `checkValidity`, `getValidationMessage`, `getValidity`, `reportValidity`, `willValidate`.
-   New interface `OdsFormElement` available with this methods.
-   Tons of unit & e2e tests have been added.

### Feature updates:

**Checkbox**

-   New `hasError` prop has been added.
-   New style when on error.

**Clipboard**

-   Now expose an `input` part.
-   Input now inherit component length.

**Datepicker**

-   `defaultValue` now accepts Date or string.
-   `max` now accepts Date or string.
-   `min` now accepts Date or string.
-   Added a new helper method `formatDate`.
-   Added a new helper method `parseDate`.

**Input**

-   New input type `search` has been added.

**Phone Number**

-   New `customValidityMessage` prop has been added.

**Radio**

-   New `hasError` prop has been added.
-   New style when on error.

**Range**

-   Now triggers an odsChange on thumb release, not on every move.

**Select**

-   Add a new method `updateCustomRenderer` if you need to update the custom template used.

**Switch**

-   Events are now send from `ods-switch` and not from `ods-switch-item` anymore. You may need to move your handlers if you were defining some on items.
-   New `hasError` prop to enforce error state.
-   Focus style has been updated.
-   New style when on error.

**Toggle**

-   New `hasError` prop to enforce error state.
-   New style when on error.

### Documentation improvements:

-   New Guide section, with a first one about form management.
-   Added new control to visualize ValidityState on each form elements Demo page.
-   Storybook update popup has been disabled.

## v18.3.1

---

### Feature updates:

**Icon**

-   New icon "columns" has been added.

**Link**

-   Update hover & visited color to a lighter one.

**Select**

-   Update hover style to differentiate it from selected option.

**Text**

-   `caption` preset does not use a `caption` tag anymore, as these is tight to table usage only.
-   New examples have been added regarding `caption` and `figcaption` usage.

### Bug fixes:

**Breadcrumb**

-   Separator is now always correctly aligned, regardless of the number of items.

**Modal**

-   Dialog content height is now correct on Safari

## v18.3.0

---

### Feature updates:

**All form elements**

-   Apply new style on readonly state.

**Breadcrumb**

-   A new part `last` is now available to customize the last item (which differ from other link items).

**Input**

-   You can now link a native `datalist` to an `ods-input` using the `list` attribute and the `list` slot. A full example is available in the  page.
-   New attribute `list` has been added.
-   New slot `list` is now available.

**Message**

-   Message can now be rendered without the close button, as a static message.
-   New attribute `isDismissible` has been added.

**Popover**

-   New attribute `strategy` added that let you manage different positioning, default strategy is `absolute`, you can change to `fixed` when in a fixed context (like in an ods-modal for example).
-   New constants `ODS_POPOVER_STRATEGY` and `ODS_POPOVER_STRATEGIES` are now available.

**Tooltip**

-   New attribute `strategy` added that let you manage different positioning, default strategy is `absolute`, you can change to `fixed` when in a fixed context (like in an ods-modal for example).
-   New constants `ODS_TOOLTIP_STRATEGY` and `ODS_TOOLTIP_STRATEGIES` are now available.

### Bug fixes:

**All form elements**

-   Pressing `Enter` on an action button (like the clear button) does not trigger a form submit anymore.

**Badge**

-   Now use the expected border-radius style.

**Breadcrumb**

-   Last attribute is now correctly reset on items changes.

**Modal**

-   `odsClose` event is no more emitted on component unmount. If you were relying on that, please reach out to us so we can find a better approach.
-   Content is now wrapped by a div to prevent flex weird rendering.
-   Modal height can now be customized using the `dialog` part.

**Range**

-   Apply correct style to hovered thumb on error state.

**Select**

-   Options are now correctly updated on change.
-   Disable state is now correctly updated on change.

### Internal changes:

We now use an `ods-button` instead of a native one for all our internal actions. Ex: input clear, message close, modal close, ... This shouldn't have any impacts.

## v18.2.0

---

### Feature updates:

**Button**

-   New color style `neutral` and new size `xs`are now available.
-   New color `neutral` added to `ODS_BUTTON_COLOR`.
-   New size `xs` added to `ODS_BUTTON_SIZE`.

**Spinner**

-   New size `xs` is now available.
-   New size `xs` added to `ODS_SPINNER_SIZE`.

### Bug fixes:

**Modal**

-   No `odsClose` event emitted anymore on element DOM disconnection if modal was already closed.

**Password / Phone Number / Quantity / Timepicker**

-   Setting a `width: 100%` to the ods element now correctly apply it to the underneath elements.

## v18.1.0

---

### Documentation improvements:

Storybook has been partially rewritten, so you may notice a few changes:

-   some style has been updated
-   broken links have been fixed
-   components documentation has been migrated from `typedoc` to `custom-element-manifest`

### Feature updates:

**Button**

-   New feature to allow icon to be aligned either on left or right side.
-   New attribute `iconAlignment` has been added.
-   New constant `ODS_BUTTON_ICON_ALIGNMENT` has been added.

**Icon**

-   New icons `bill` and `box` have been added.
-   `globe` icon has been updated.

**Link**

-   New feature to allow icon to be aligned either on left or right side.
-   New attribute `iconAlignment` has been added.
-   New constant `ODS_LINK_ICON_ALIGNMENT` has been added.

**Select**

-   Apply new style to multi-select counter.

**Table**

-   Update header color to neutral-050.

**Text**

-   No more default margin on any presets.

### Bug fixes:

**All**

-   Components now correctly inherits font-family.

**Modal**

-   Prevent undefined element when using with react testing library.

**Phone Number**

-   Locale fallback has been moved from french to english (fallback that is applied if no locale or a wrong locale is set and the locale of your browser is not managed).

## v18.0.0

---

As it is a major version, you'll have to follow the 

### Project changes:

From the beginning of the project to the latest v17 version, our vision of the project has greatly evolved. Thanks to real-user feedbacks, a better understanding of our use cases and how ODS fits in the OVHCloud ecosystem.

With this new version we've tried to tackle most of the known issues to offer a better end-user experience:

-   components are now behaving more like native html elements
-   components are now easier to use and to customize
-   no more invasive `console.log`, we only print meaningful warnings when needed
-   enum values can now be used directly instead of importing it
-   events are now normalized for all components
-   release process will be shorter

On top of that, the v18 does bring new features:

-   new components: file-upload and timepicker
-   latest design applied on all components
-   now sync with figma changes
-   a new set of icons
-   a reworked documentation

To achieve all this, we had to rewrite almost the whole library.

This is a major version change with tons of breaking changes. But this aims to be the last time.

To help you moving from a previous version to the v18, we've added a dedicated page on each component presenting the changes and some migration examples.

### ODS libraries

The following libs have been removed:

-   @ovhcloud/ods-cdk
-   @ovhcloud/ods-common-core
-   @ovhcloud/ods-common-stencil
-   @ovhcloud/ods-common-testing
-   @ovhcloud/ods-common-theming
-   @ovhcloud/ods-theme-blue-jeans

There is now only two library published that you should include:

-   `@ovhcloud/ods-components` that contain all web-components, react and vue wrappers and the sass mixins
-   `@ovhcloud/ods-themes` that contain the design tokens and the assets (fonts and icons)

The CDK has been removed and we do not plan on publishing it again. If you were using part of it, please reach out to us so that we can get more information about your usage.

All common libs have been and we do not plan on publishing them again. If you were using part of them, please reach out to us so that we can get more information about your usage.

The `blue-jeans` theme has been replaced by the default theme of `@ovhcloud/ods-themes`.

### ODS dependencies upgrade

The new version comes with the following upgrade on major library:

-   Stencil bumps from `4.12.0` to `4.16.0`
-   `node-sass` is not supported anymore and was replaced by `sass` (`1.71.0`)
-   Typescript bumps from `4.7.4` to `5.3.3`
-   Storybook bumps from `6.4.19` to `8.0.4`

### Style customization

Although the ODS component comes with the expected design, you can now apply your own style directly on some part of the web-component shadow DOM, thanks to the [part selector](https://developer.mozilla.org/en-US/docs/Web/CSS/::part) .

For example, you can add style on the native `input` element of an `ods-input` component through the following code:

```html
<style>
  .my-input::part(input) {
    width: 300px;
  }
</style>
<ods-input class="my-input" ... />
```

Most of the component exposes one or more part like this, you can find more information on each component documentation page.

### Sass helpers

In addition to the easier customization, we're also exposing the "basic" style of some core component as Sass mixins that you can use to apply on a native element to get the same ODS rendering.

For example, if you need to make an anchor tag looks like an `ods-link`:

```css
@import '@ovhcloud/ods-components/style';
.my-link {
  @include ods-link();
  @include ods-link-color('primary');
}
```

```html
<a class="my-link" ...>...</a>
```

This can be useful when you can't manage some part of the DOM as you want, for example using a library that will always render an anchor and that provide no custom templating.

Be careful though, this does only provide basic style, not all the feature of an ODS component, so we do recommend to use the web-component by default.

### New components:

**File Upload**

**Timepicker**

### Component changes:

Please check the  regarding all the component changes.

## v17.2.2

---

This update brings a few bug fixes across components, ensuring a smoother and more accessible user experience.

### Bug fixes:

**Button**

-   Fix the issue where the button was still clickable on the border while disabled.

### Feature updates:

**Radio-button**

-   Add 'XS' size to radio button.

## v17.2.1

---

This update brings a few bug fixes across components, ensuring a smoother and more accessible user experience.

### Bug fixes:

**Clipboard**

-   A white background is now properly displayed on the `Popover`.

**Menu**

-   A minimal height is now forced in the `Menu` component, giving proper styling on the bottom margin.

## v17.2.0

---

This update introduces our new `Table` component, enhances various features and improves documentation. It also brings numerous bug fixes across components, ensuring a smoother and more accessible user experience.

### New component:

**Table**

### Documentation improvements:

**Form Field**

-   Added an accessibility label.
-   Updated Storybook Usage Guidelines.

### Feature updates:

**Clipboard**

-   `Tooltip` now features a `3 seconds` closing delay.
-   `Tooltip` won't be displayed if `success-message` OR `error-message` is not defined.

**Datepicker**

-   `Datepicker` days are now displayed with 2 letters.

**Pagination**

-   New attribute `defaultCurrentPage` has been added.
-   New method `getCurrentPage()` has been added.
-   Old attribute `current` has been removed.
-   Navigation will be hidden if `totalItems` is lower than `itemPerPage`

### Bug fixes:

**Breadcrumb** **Link**

-   Multiple active states issue on Safari browsers has been fixed.

**Datepicker**

-   Error color on `Datepicker`'s border has been fixed.

**Input**

-   Clearing the `Input` now properly focuses it afterwards.

**Modal**

-   Overlaying elements can now overflow from the `Modal`.
-   Pressing the `ESCAPE` key when a `Select` is open inside a `Modal` no longer displays an unwanted backdrop.
-   Width value has been fixed on breakpoint.

**Popover**

-   Overlaying elements can now overflow from the `Popover`.

**Textarea**

-   `Textarea` style has been fixed to match the `Input`.

**Tile**

-   Checked `Tile` styling has been fixed on Safari.

**Search Bar**

-   `odsSearchSubmit` event is now properly triggered on `KEY_DOWN` input.

## v17.1.0

---

As we're working towards a massive uniformization of all ODS form elements (events, nav behavior, required state, validity state, ...), the amount of changes is too important to be released immediately.

This release brings the expected form-association of form elements, so that they act as native one for formData submit and reset.

It also fixes the missing types of event (`odsBlur`, `odsFocus`, `odsValueChange`).

### Documentation improvements:

Documentation usage guidelines have been completed with almost all components properties and slots:

-   tons of new components examples
-   dark background when `contrasted` prop is set
-   shortcuts have been disabled due to issue with some input preview

### Feature update:

**Pagination**

-   A new `defaultItemsPerPage` has been added, which allows to define the initial value of `itemsPerPage` on first render

### Bug fixes:

**Button / Checkbox / Radio / Range / Tile**

-   Prevent click event on click when disabled on Chromium & Safari.

**Modal**

-   Modal is now correctly cleaned up on disconnect

**Tile**

-   Ensure position stays consistent regarding end/start slot emptiness

### Miscellaneous:

-   Upgrade to StencilJS 4.12

## v17.0.3

---

### Bug fixes:

**Input**

-   Icon size are now displayed at the expected size

**Quantity**

-   Native arrow are now hidden on Safari

**Radio / Radio group**

-   Remove logger issue on documentation example page

**Toggle**

-   Toggle now shrink correctly when slot content are very large

**Tile**

-   Tile borders are now correctly rounded on Safari

## v17.0.2

---

### Bug fixes:

**Modal**

-   Added `enter` and `space` keys binding on the close button + cancel button on Storybook
-   Fixed display problems within the modal that could lead to some elements getting shrunk
-   Scroll is now prevented when a modal is open

## v17.0.1

---

### Bug fixes:

**Pagination**

-   Next & previous buttons have been fixed on Chrome & Safari where they could allow the user to go out of bound.

**Radio-Button**

-   Smaller text has been fixed and is now aligning vertically as well.

**Fonts**

-   Fonts have been fixed to display the correct font on Safari.

## v17.0.0

---

As it is a major version, you'll have to follow the 

### Project changes:

Main focus on this version has been on simplifying ODS integration in existing projects (React, Vue, JS, ...).

We now only publish one `ods-components` library that embed all the ODS components.

What this means for your code:

-   `ods-component-xxx` are no longer published and should be removed
-   only one dependency to add to your `package.json`

```text
"ods-components": "17.0.0"
```

ODS components can then be either imported using lazy-loading (the code of each component is loaded on demand) or as a module (the code of the component is tree-shaked by your own bundling system).

For more details on how to import ODS components, see:

-   the updated 
-   the 16.x to 17.x 

### Documentation improvements:

We went through each component to update:

-   the Demo controls to be more relevant
-   the Usage Guidelines, with more examples and the missing properties
-   with React example when relevant (ex: `datagrid` reactFormatter with React 18 or `tabs` with React Router)

### Component changes:

**Pagination**

-   Add `odsItemPerPageChange` when the item per page select change

### Bug fixes:

**Datagrid**

-   Overflowing elements can now correctly be displayed (see updated documentation)

**Menu**

-   Ellipsis is now correctly displayed on Safari

**Modal**

-   Inner element with overlay are now correctly displayed

**Tabs**

-   Fix hover rendering

## v16.6.0

---

### Feature update:

**Breadcrumb**

-   Add attributes on `breadcrumItem` : `disabled`, `rel`, `referrerpolicy`, `target`
-   Add event on `breadcrumItem` : `odsBreadcrumbItemClick`
-   Update `OdsHTMLAnchorElementRel` type

**Chip**

-   Added `odsChipRemoval` event
-   If `OsdsChip` is removable and is clicked, it will emit event `odsChipRemoval`
-   If `OsdsChip` is removable, focused on and key Enter is pressed, it will emit event `odsChipRemoval`

### Bug fixes:

**Button**

-   Fix position of end slot when start slot is empty in `OsdsButton`

**Datagrid**

-   If the `OsdsDatagrid` contains an overflowing on-interaction element (such as `OsdsMenu`, `OsdsTooltip`, `OsdsSelect` or `OsdsPopover`), the element is either being displayed at the top left corner of the `OsdsDatagrid` (native) or beneath the element, making it unvisible (React). This fix intends to correct this problem in both environments.

**Pagination**

-   Add `itemPerPage` in payload event `odsChange`
-   Watch `totalPages` & update it

**Phone Number**

-   Fix shadow of flag icon in the select that was cut behind overflow

**Components**

-   Some components were missing from package.json of components: `medium`, `range`, `toggle`

## v16.5.0

---

### Feature update:

**Documentation**

-   Multiple updates to clarify documentation. (Welcome page, Get started, Design tokens, Code, Contributing & libraries)
-   Removing Starter documentation, as it is deprecated for now.

### Bug fixes:

**Datagrid**

-   `OsdsDatagrid` now emits the event `onBottomScroll`.
-   `OsdsDatagrid` now supports React Formatters.
-   Row change has been fixed.

**Modal**

-   `OsdsModal` is now using a `HTMLDialogElement`,

**Phone Number**

-   Flags have been fixed to be displayed with proper width & height.

**Select**

-   Enhanced React support for `OsdsSelect`.

**Storybook**

-   Storybook errors with displaying `OsdsPhoneNumber` & `OsdsSearchBar` inputs has been fixed.

## v16.4.0

---

### New component:

**Datagrid**

-   See Documentation using version selector

### Bug fixes:

**Phone number**

-   Now consistently triggers `odsValueChange` event on type.

**Storybook**

-   The release list now correctly displays the name of the latest version.

### Feature update:

**Component Generator**

-   Component Generator has been upgraded to align with the new architecture (introduced in version 16.0.0).

## v16.3.2

---

### New component:

**Medium**

### Bug fixes:

**Phone number**

-   Emit event when the value is valid

**Accordion**

-   Can't open when the component is disabled

**Quantity**

-   Change the value onblur to be valid with step

**Modal**

-   Add event open & close (odsModalOpen, odsModalClose)

## v16.3.1

---

### Bug fixes:

**Datepicker**

-   The siblings months days are now updated after the datepicker was updated

**Select**

-   Keyboard navigation correction

## v16.3.0

---

### Component changes:

**Button**

-   Each variant now display correctly when using circle button

> ⚠️️ **Breaking change**

-   Circle button may render differently regarding the variant you're using

**Datepicker**

-   Component now close itself when value is selected
-   You can now hide adjacent month days with the attributes `showSiblingsMonthDays`

**Flag**

-   Component has been refactored internally, but this should not have any impacts

**Icon**

-   Component has been refactored internally, but this should not have any impacts

### Bug fixes:

**Menu**

-   Now correctly work when used with React

**Select**

-   On Safari the component is displayed correctly when using flex

**Textarea**

-   On Safari the component is displayed correctly when using flex

**Tile**

-   On Safari the component is displayed correctly when using flex

### Project changes:

-   The whole project is now built using Stenciljs v4.7

> ⚠️️ **Breaking change**

-   We don't generate the bundle `custom-elements-bundle` anymore

## v16.2.1

---

### Component changes:

**Datepicker**

-   New attribute `locale` to manage internationalization, accepted locales are for now:

-   French
-   English
-   German
-   Spanish
-   Italian
-   Polish
-   Portuguese
-   Dutch

**Text**

-   Component has been refactored internally, but this should not have any impacts

### Bug fixes:

**Datepicker**

-   Add missing constants and types exports

**Modal**

-   Is now correctly destroyed on page change

**Popover**

-   Now correctly work when used with React

**Select**

-   Selected value is now correctly updated on cnange (ex: translation change)

## v16.2.0

---

### New component:

**Datepicker**

### Component changes:

**Clipboard**

-   Added tooltip confirmation message on paste

### Bug fixes:

**Textarea**

-   Correctly emit `odsValueChange` event on value change.

### Documentation changes:

-   Improve typedoc documentation

## v16.1.1

---

### Documentation fixes:

-   Component specification pages now displays correct information
-   Tile sandbox now uses correct list of commands

## v16.1.0

---

### New component:

**Phone Number**

### Component changes:

**Input**

-   New attribute `prefixValue` that allow to display a fixed prefix in front of the input value.

**Progress Bar**

> ⚠️️ **Breaking change**

-   `max` and `value` attributes are now `number` and won`t accept` string` anymore.

### Bug fixes:

**Code**

-   Contrasted version have now the right color.

### Project changes:

-   The whole project is now built using node 18.

## v16.0.1

---

Fixes CI build issues when deploying Storybook.

## v16.0.0

---

As it is a major version, you'll have to follow the 

⚠️We're aware that not all the documentation is up-to-date with the latest technical changes ⚠️

⚠️We're currently working on improving the storybook but didn't want to delay the release much more ⚠️

⚠️If you have any issue migrating, reach us directly and we'll help you. ⚠️

### Project changes:

This version embed the whole new project architecture (initiated with the v15).

We now have seperations between:

-   `cdk` (@ovhcloud/ods-cdk) which provides public non component related utilities.
-   `common` libs (@ovhcloud/ods-common-[core|stencil|testing|theming]) which provides internal helpers.
-   `components` (@ovhcloud/ods-components or @ovhcloud/ods-component-`<component-name>`) which provide the agnostic components and their React, Vue wrapped versions.
-   `examples` (@ovhcloud/ods-starter-[react|react-vite|vue-vite]) which contains application examples that use some ODS components.
-   `shared-dependencies` to share common dependencies internally accross projects.
-   `storybook` (@ovhcloud/ods-storybook) which tends to centralize all the ODS documentation content.
-   `themes` (@ovhcloud/ods-theme-blue-jeans) which provides all existing themes that can be applied to ODS.

### Component changes:

**All Components**

-   `@ovhcloud/ods-stencil` package does not exists anymore.
    
-   Components are now accessible by adding either `@ovhcloud/ods-component-<component>` or `@ovhcloud/ods-components` dependency.
    
-   All `@ovhcloud/ods-stencil-<component>` libs have been replaced by `@ovhcloud/ods-component-<component>`.
    
-   All `@ovhcloud/ods-stencil-<component>-react` libs have been replaced by `@ovhcloud/ods-component-<component>-react`.
    
-   All `@ovhcloud/ods-stencil-<component>-vue` libs have been replaced by `@ovhcloud/ods-component-<component>- vue`.
    
-   Constants/Enums have been renamed to follow new naming convention:
    
-   All constants are now in UPPER_SNAKE_CASE.
    
-   All objects are now in singular form.
    
-   All lists are now in plural form.
    

Ex: `OdsSpinnerSize` is now `ODS_SPINNER_SIZE`. `OdsSpinnerSizeList` is now `ODS_SPINNER_SIZES`. `OdsSpinnerSizeUnion` has been removed.

-   Exported types follow also the same naming convention, thus most of them are now in singular form.

Ex: `OdsSpinnerAttributes` is now `OdsSpinnerAttribute`.

-   A lot of empty types who were exported previously have been removed.

Ex: `OdsSpinnerEvents` have been removed, as the component does not handle any events. `OdsSpinnerMethods` have been removed, as the component does not expose any methods.

### New component:

**Clipboard**

**Menu**

See documentation using version selector

**Modal**

**Search Bar**

See documentation using version selector

### Features changes:

**Button**

-   Add a new attribute `textAlign` which allows to choose the button text alignement between `center`, `end` and `start`.

### Bug fixes:

**Button**

-   Main slot is now correctly aligned vertically (visible when using icon).

**Flag**

-   Flags are now correctly displayed in the documentation.

**Link**

-   Outline is now correctly updated on viewport change.

## v15.0.1

---

-   Fix new packages release process.
-   Update documentation.

## v15.0.0

---

As it is a major version, you have to do the 

### Project changes:

This version is the first step towards the new ODS internal architecture. The goal of this refactoring is to simplify and improve the whole project to:

-   provide a better developer experience and thus reduce development time
-   normalize all components behavior / design / attributes
-   have better control on dependencies and lib version upgrade
-   have better control on what ODS expose (through component or CDK)
-   ...

This first step only impact two components `osds-text` and `osds-textarea`.

All other components will follow on next version.

Then common ODS lib and theming will be refactored on next version.

### Features changes:

-   **Text**
    
-   `OsdsText` is no longer part of `@ovhcloud/ods-stencil` package.
    
-   `OsdsText` is now accessible by adding either `@ovhcloud/ods-component-text` or `@ovhcloud/ods-components` dependency.
    
-   `@ovhcloud/ods-stencil-text` lib has been replaced by `@ovhcloud/ods-component-text`.
    
-   `@ovhcloud/ods-stencil-text-react` lib has been replaced by `@ovhcloud/ods-component-text-react`.
    
-   `@ovhcloud/ods-stencil-text-vue` lib has been replaced by `@ovhcloud/ods-component-text- vue`.
    
-   `OdsTextSize` has been removed from `@ovhcloud/ods-core` and has been replaced by `ODS_TEXT_SIZE` from `@ovhcloud/ods-component-text`.
    
-   `OdsTextSizeList` has been removed from `@ovhcloud/ods-core` and is replaced by `ODS_TEXT_SIZES` from `@ovhcloud/ods-component-text`.
    
-   `OdsTextSizeUnion` has been removed from `@ovhcloud/ods-core`.
    
-   `OdsTextLevel` has been removed from `@ovhcloud/ods-core` and has been replaced by `ODS_TEXT_LEVEL` from `@ovhcloud/ods-component-text`.
    
-   `OdsTextLevelList` has been removed from `@ovhcloud/ods-core` and is replaced by `ODS_TEXT_LEVELS` from `@ovhcloud/ods-component-text`.
    
-   `OdsTextLevelUnion` has been removed from `@ovhcloud/ods-core`.
    
-   `@ovhcloud/ods-component-text` exports only `OdsTextAttribute` as component interface.
    
-   **Textarea**
    
-   `OsdsTextarea` is no longer part of `@ovhcloud/ods-stencil` package.
    
-   `OsdsTextarea` is now accessible by adding either `@ovhcloud/ods-component-textarea` or `@ovhcloud/ods-components` dependency.
    
-   `@ovhcloud/ods-stencil-textarea` lib has been replaced by `@ovhcloud/ods-component-textarea`.
    
-   `@ovhcloud/ods-stencil-textarea-react` lib has been replaced by `@ovhcloud/ods-component-textarea-react`.
    
-   `@ovhcloud/ods-stencil-textarea-vue` lib has been replaced by `@ovhcloud/ods-component-textarea- vue`.
    
-   `OdsTextAreaSize` has been removed from `@ovhcloud/ods-core` and is replaced by `ODS_TEXTAREA_SIZE` from `@ovhcloud/ods-component-textarea`.
    
-   `OdsTextAreaSizeList` has been removed from `@ovhcloud/ods-core` and is replaced by `ODS_TEXTAREA_SIZES` from `@ovhcloud/ods-component-textarea`.
    
-   `OdsTextAreaSizeUnion` has been removed from `@ovhcloud/ods-core`.
    
-   `@ovhcloud/ods-component-textarea` exports only `OdsTextAttribute`, `OdsTextAreaEvent` and `OdsTextAreaValueChangeEvent` as component interfaces.
    
-   `OsdsTextarea` is now displayed as flex by default.
    
-   `OsdsTextarea` `flex` attribute has been removed.
    
-   `OsdsTextarea` `inline` attribute has been added.
    

## v14.1.0

---

### New features:

-   **CDK**
-   Manage surface position when located on the edge.
-   The surface can now be centered at the top or bottom of the trigger. See CDK Documentation using version selector.

We added the following components:

**Form Field**

**Switch**

### Features changes:

-   **Pagination**
    
-   Add tooltip to the arrows.
    
-   Can now show pages according to a numbers of items shown by page.
    

> For more information, check our documentation 

-   **Location-tile**
    
-   Component is now deprecated and won't be supported anymore. It will be deleted in a future release.
    

> For more information, check our replacement guide using version selector.

### Bug fixes:

-   **Radio**:
    
-   Radio button is now unfocusable when disabled
    
-   **Input**:
    
-   cursor is set to not-allowed when disabled
    

## v14.0.0

---

As it is a major version, you have to do the 

### New features:

We added the following components:

-   **Progress Bar**: 
-   **Popover**: 
-   **Tooltip**: 

Components version 2:

-   **Input V2**: 
-   new properties have been added
-   `loading`: it displays a Spinner
-   `clearable`: it clears the input value on click
-   `masked`: it indicates if the input value is hidden or not (to be used with password type)
-   new types have been added: `date`, `email`, `password`, `search`, `tel`, `text`, `time` and `url`
-   **Select V2**: it is now connected to our CDK and keyboard navigation has been added 

**Quantity** component accepts four digits in the input field:

**CDK** (Component Development Kit): new symmetry strategies have been added (left and right):

See CDK Documentation using version selector.

### Bug fixes:

-   **Tile**:
-   `hoverable` attribute has been added to avoid double focus
-   `interactive` attribute has been removed

> ⚠️️ **Breaking change**  
> For more information, check our migration guide

-   **Link**:
-   add missing color for active state
-   fix Link host container size to match its content
-   **Icon**: add `hoverable` attribute so component color can change when in selectable parent
-   **Checkbox Button**: aspect ratio has been added
-   **Quantity**: icons alignment has been fixed
-   **Textarea**: when component is resized and `flex` attribute is set to `true` and then, set back to `false`, Textarea width is no more blocked to 100%

## v13.0.0

---

As it is a major version, you have to do the 

### New features:

We added the following components:

-   Breadcrumb: 
-   Collapsible: See Documentation using version selector
-   Pagination: 
-   Spinner: 

Circle-shaped button has been added to Button Component (circle attribute)

### Bug fixes:

-   Button: `button' and` link`roles have been added (switching to`link` role when Button has a href property)
    
-   Chip: expected style on Chip focus has been applied
    
-   Icon: missing download icon has been added
    
-   Input: odsValueChange event won't emit on component init
    
-   Link:
    
-   all content in Link is now aligned as expected
    
-   href and target attributes are optional
    
-   Select :
    
-   check for outside click target inside its DOM
    
-   fixed width on non flex Select has been removed
    

> ⚠️️ **Breaking change**  
> For more information, check our migration guide

## v12.1.0

---

### New features:

We added the Tabs component.  

A component generator has been added. Now, you can use that tool to generate a new component easily.  

A coding Style has also been added so you can check our guidelines and how to write code for OVHcloud Design System project.  

The Contributing Documentation has been updated:

-   How to use a component in an other one
-   Theming: how to customize a style
-   some warning about pushing any sensitive content

### Bug fixes:

Range component color when in error has been fixed.

Some width issue on Button component has been fixed.  
When in flex, a Button can now display an ellipsis for text content when necessary.

## v12.0.5

---

### Bug fixes:

Button component of type submit in a form will now submit the form as expected (by click or Enter / Space keys).

Quantity component issue has been fixed regarding minus control that should be disabled when the input `min` attribute is equal to 0 and its value has reached 0 too.

## v12.0.0

---

First release on GitHub. There is no new feature in this version. It is now open source

## v11.0.0

---

Flag component has been modified; you’ll need to update as explained in the migration guide. For more details about assets, see  page. New attributes are available on this component: `lazy`, `assetPath` and `src`.

Before importing any component you now have to setup ods.

Dual range is now available for Range component; It offers two handles to set a min and max value.

To build resilient infrastructure and promote sustainable industrialization, we moved code that could be agnostic. Now, ODS components logic has been moved into controllers. If you want to contribute to ODS by developing new components, please refer to the how-to section for controllers in our Storybook.

Now, Button component has `download` and `rel` new attributes available when it is used as a link (href).

We had blur events on Quantity component to set the correct value regarding the Quantity bounds. It means when the user set manually a value that is out of bounds for Quantity, the value will be automatically reset to a correct value. When Quantity is out of bounds, the field value is now in error state.

## v10.2.0

---

A new library called `CDK` for `Component Development Kit` is available. The main objective is to make the development to be easier thanks to helpers. The first integrated feature is an overlay system that allows manipulating and displaying content on foreground of the page.

We added a new entire section for `contributions` in the storybook. Feel free to read the Contributing Documentation if you want to contribute.

We simplified our developer environment with a `global.dev.ts` file in order to make the developer tests isolated from the production build.

We optimized our CI/CD workflow for a more reactive and efficient build time and release time.

We fixed some issue about automated screenshot testing with some timeout.

## v10.1.0

---

We added the select component (version 1).

## v10.0.0

---

We updated a lot of third dependencies (stencilJs, typescript, jest, puppeteer, ...):

```text
@stencil/core ~2.6.0 to ~2.18.1
@stencil/sass ~1.4.1 to ~2.0.0
jest ~26.6.3 to ~27.4.5
@types/jest ~26.0.24 to ~27.5.2
jest-cli ~26.6.3 to ~27.4.5
jest-puppeteer ^5.0.4 to ~6.1.1
jest-puppeteer-preset ^5.0.4 to ~6.1.1
ts-jest ~26.5.6 to ~27.1.5
typescript 4.2.3 to 4.7.4
puppeteer ~5.5.0 to ~10.0.0
yarn-2.4.2 to yarn-3.2.4
node-fetch ~2.6.6 to ~2.6.7
ts-node ~9.1.1 to ~10.7.0
```

> If you are an `ODS` contributor, you may need to do some changes

We now build all of our components for `React` and `Vue` frameworks. They allow to use all the potential and features of your high-end JS framework. You can read the updated  to initialize an App with ODS and see how it works depending the framework.

Also, `ods-cart` is no more impacted by a parent container with `text-align:center`.

`ods-textarea` had an incorrect `spellcheck` property than could be equal to `spellcheck="false'` which is incorrect in HTML. Now it is in DOM only if it is `true`.

You can now disable animation on `ods-toggle` by defining a CSS value `--ods-toggle-transition-slider: none`.

Css variables `--ods-size-squish-n-*` and `--ods-size-stretch-n-*` are no more incorrect. It was previously set with only one number instead of two, accordingly with CSS3.

## v9.1.0

---

Adding 2 new components :

-   accordion: display a content that can be collapsed
-   code: display a snippet of code and copy it into the clipboard

If you already have customized themes, you have to declare the variables for this 2 new components. see more in the migration guide 9.x

## February 2022

---

The V1 **Design System** graphical charter is finalized. This charter contains :

-   Multiple color palettes (blue, gray, red, orange, green, availability & promotion extra colors)
-   A Typography system
-   All spacing options
-   Components:
-   **Buttons**
-   **Form items** (inputs, dropdowns, checkboxes, radios, radio buttons, toggle buttons, range, textarea, etc.)
-   **Chips & Badges**
-   **Cards**
-   **Cart**

## React Components/Accordion

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial value of the expanded accordion items. Use when you don't need to control the value of the accordion. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `multiple` | `` | No | true | Whether multiple accordion items can be expanded at the same time. |
| `onChange` | `` | No |  | Callback fired when the state of expanded/collapsed accordion items changes. |
| `value` | `` | No |  | The controlled value of the expanded accordion items. |


## Subcomponents


### AccordionContent




### AccordionItem



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `disabled` | `` | No |  | Whether the accordion item is disabled. |
| `value` | `` | Yes |  | The value of the accordion item. |



### AccordionTrigger



## Examples


### Controlled

```tsx
{
  globals: {
    imports: `import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [value, setValue] = useState(['0']);
    return <Accordion value={value} onChange={detail => setValue(detail.value)}>
        <AccordionItem value="0">
          <AccordionTrigger>
            Hello World!
          </AccordionTrigger>
          <AccordionContent>
            Lorem ipsum dolor sit amet.
          </AccordionContent>
        </AccordionItem>
        <AccordionItem value="1">
          <AccordionTrigger>
            Hello World!
          </AccordionTrigger>
          <AccordionContent>
            Lorem ipsum dolor sit amet.
          </AccordionContent>
        </AccordionItem>
      </Accordion>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Accordion>
      <AccordionItem value="0">
        <AccordionTrigger>
          Hello World!
        </AccordionTrigger>
        <AccordionContent>
          Lorem ipsum dolor sit amet.
        </AccordionContent>
      </AccordionItem>
    </Accordion>
}
```

### Demo

```tsx
{
  render: (arg: AccordionProp) => <Accordion disabled={arg.disabled} multiple={arg.multiple}>
      <AccordionItem value="0">
        <AccordionTrigger>
          <Text preset="paragraph">
            Hello World!
          </Text>
        </AccordionTrigger>
        <AccordionContent>
          <Text preset="paragraph">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit.
          </Text>
        </AccordionContent>
      </AccordionItem>
      <AccordionItem value="1">
        <AccordionTrigger>
          <Text preset="paragraph">
            Bye World!
          </Text>
        </AccordionTrigger>
        <AccordionContent>
          <Text preset="paragraph">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit.
          </Text>
        </AccordionContent>
      </AccordionItem>
    </Accordion>,
  argTypes: orderControls({
    multiple: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Accordion disabled>
      <AccordionItem value="0">
        <AccordionTrigger>
          Hello World!
        </AccordionTrigger>
        <AccordionContent>
          Lorem ipsum dolor sit amet.
        </AccordionContent>
      </AccordionItem>
    </Accordion>
}
```

### Item Disabled

```tsx
{
  globals: {
    imports: `import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Accordion>
      <AccordionItem value="0" disabled>
        <AccordionTrigger>
          Hello World!
        </AccordionTrigger>
        <AccordionContent>
          Lorem ipsum dolor sit amet.
        </AccordionContent>
      </AccordionItem>
      <AccordionItem value="1">
        <AccordionTrigger>
          Hello World!
        </AccordionTrigger>
        <AccordionContent>
          Lorem ipsum dolor sit amet.
        </AccordionContent>
      </AccordionItem>
    </Accordion>
}
```

### Multiple

```tsx
{
  globals: {
    imports: `import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Accordion>
        <AccordionItem value="1">
          <AccordionTrigger>
            <Text preset="heading-4">Multiple</Text>
          </AccordionTrigger>
          <AccordionContent>
            <Text preset="paragraph">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </Text>
          </AccordionContent>
        </AccordionItem>
        <AccordionItem value="2">
          <AccordionTrigger>
            <Text preset="heading-4">Multiple</Text>
          </AccordionTrigger>
          <AccordionContent>
            <Text preset="paragraph">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </Text>
          </AccordionContent>
        </AccordionItem>
      </Accordion>
      <br />
      <Accordion multiple={false}>
        <AccordionItem value="3">
          <AccordionTrigger>
            <Text preset="heading-4">Non-multiple</Text>
          </AccordionTrigger>
          <AccordionContent>
            <Text preset="paragraph">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </Text>
          </AccordionContent>
        </AccordionItem>
        <AccordionItem value="4">
          <AccordionTrigger>
            <Text preset="heading-4">Non-multiple</Text>
          </AccordionTrigger>
          <AccordionContent>
            <Text preset="paragraph">
              Lorem ipsum dolor sit amet, consectetur adipiscing elit.
            </Text>
          </AccordionContent>
        </AccordionItem>
      </Accordion>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => <Accordion>
      <AccordionItem value="0">
        <AccordionTrigger>
          <Text preset="paragraph">
            Hello World!
          </Text>
        </AccordionTrigger>
        <AccordionContent>
          <Text preset="paragraph">
            Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et
            dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex
            ea commodo consequat. Duis aute irure dolor in reprehenderit.
          </Text>
        </AccordionContent>
      </AccordionItem>
    </Accordion>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  render: ({}) => <Accordion>
      <AccordionItem value="0">
        <AccordionTrigger>
          <Text preset="paragraph">Hello World!</Text>
        </AccordionTrigger>
        <AccordionContent>
          <Text preset="paragraph">Lorem ipsum dolor sit amet.</Text>
        </AccordionContent>
      </AccordionItem>
      <AccordionItem value="1" disabled>
        <AccordionTrigger>
          <Text preset="paragraph">Disabled item</Text>
        </AccordionTrigger>
        <AccordionContent>
          <Text preset="paragraph">This item is disabled.</Text>
        </AccordionContent>
      </AccordionItem>
    </Accordion>,
  tags: ['!dev']
}
```

## React Components/Badge

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | BADGE_COLOR.information | The color preset to use. |
| `size` | `` | No | BADGE_SIZE.md | The size preset to use. |


## Examples


### Accessibility Alternative Grouping

```tsx
{
  globals: {
    imports: `import { Badge } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div role="list" style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>
      <Badge role="listitem">
        Item 1
      </Badge>
      <Badge role="listitem">
        Item 2
      </Badge>
    </div>
}
```

### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Badge, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Badge>
      <Icon aria-label="Promotion" name={ICON_NAME.tag} role="img" />
    </Badge>
}
```

### Accessibility Grouping

```tsx
{
  globals: {
    imports: `import { Badge } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ul style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    margin: 0,
    padding: 0,
    listStyle: 'none'
  }}>
      <li>
        <Badge>
          Item 1
        </Badge>
      </li>
      <li>
        <Badge>
          Item 2
        </Badge>
      </li>
    </ul>
}
```

### Accessibility With Tooltip

```tsx
{
  globals: {
    imports: `import { BADGE_COLOR, ICON_NAME, Badge, Icon, Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tooltip>
      <TooltipTrigger asChild>
        <Badge aria-labelledby="tooltip-a11y" color={BADGE_COLOR.promotion}>
          <Icon name={ICON_NAME.tag} />
        </Badge>
      </TooltipTrigger>

      <TooltipContent id="tooltip-a11y">
        Promotion valid from November 22 to 26
      </TooltipContent>
    </Tooltip>
}
```

### Color

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { BADGE_COLOR, Badge } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Badge color={BADGE_COLOR.alpha}>Alpha</Badge>
      <Badge color={BADGE_COLOR.beta}>Beta</Badge>
      <Badge color={BADGE_COLOR.critical}>Critical</Badge>
      <Badge color={BADGE_COLOR.information}>Information</Badge>
      <Badge color={BADGE_COLOR.neutral}>Neutral</Badge>
      <Badge color={BADGE_COLOR.new}>New</Badge>
      <Badge color={BADGE_COLOR.primary}>Primary</Badge>
      <Badge color={BADGE_COLOR.promotion}>Promotion</Badge>
      <Badge color={BADGE_COLOR.success}>Success</Badge>
      <Badge color={BADGE_COLOR.warning}>Warning</Badge>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Badge } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Badge>
      My badge
    </Badge>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'BADGE_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: BADGE_COLORS
    },
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'BADGE_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: BADGE_SIZES
    }
  }),
  args: {
    children: 'My badge'
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Badge>
      Badge
    </Badge>
}
```

### Size

```tsx
{
  globals: {
    imports: `import { BADGE_SIZE, Badge } from '@ovhcloud/ods-react';`
  },
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  tags: ['!dev'],
  render: ({}) => <>
      <Badge size={BADGE_SIZE.sm}>SM badge</Badge>
      <Badge size={BADGE_SIZE.md}>MD badge</Badge>
      <Badge size={BADGE_SIZE.lg}>LG badge</Badge>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '8px'
  }}>
      {BADGE_SIZES.map(size => <div key={size} style={{
      display: 'flex',
      flexFlow: 'row',
      flexWrap: 'wrap',
      gap: '8px',
      alignItems: 'center'
    }}>
          {BADGE_COLORS.map(color => <Badge key={`${String(size)}-${String(color)}`} size={size} color={color}>
              {String(color)}
            </Badge>)}
        </div>)}
    </div>
}
```

## React Components/Breadcrumb

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `collapseThreshold` | `` | No | 4 | The number of items when the component will collapse to an ellipsis. |
| `i18n` | `` | No |  | Internal translations override. |
| `id` | `` | No |  | Id for the breadcrumb nav (overrides auto-generated id) |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `nbItemsAfterEllipsis` | `` | No | 1 | The number of items to display before the ellipsis. |
| `nbItemsBeforeEllipsis` | `` | No | 1 | The number of items to display after the ellipsis. |
| `noCollapse` | `` | No | false | Whether the component should not collapse in an ellipsis regarding the number of items. |
| `onExpand` | `` | No |  | Callback fired when an ellipsis is expanded. |


## Subcomponents


### BreadcrumbItem



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `autoFocus` | `` | No |  | @internal |
| `isLast` | `` | No |  | @internal |



### BreadcrumbLink



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `as` | `` | No |  | @default-value='a' Pass a component you may want to use as custom Link component. Useful for example when using routing library like react-router. |
| `autoFocus` | `` | No |  | @internal |
| `isLast` | `` | No |  | @internal |


## Examples


### Accessibility With Aria Label

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Breadcrumb aria-label="Breadcrumb">
      <BreadcrumbItem>
        <BreadcrumbLink aria-label="Home" href="#">
          <Icon name={ICON_NAME.home} />
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Category
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Subcategory
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Current page
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Collapsed

```tsx
{
  globals: {
    imports: `import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb>
      <BreadcrumbItem>
        <BreadcrumbLink aria-label="Homepage" href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Products
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Hosting
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Servers
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Dedicated
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Rise
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          RISE-2
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Custom After Before Collapse

```tsx
{
  globals: {
    imports: `import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb nbItemsBeforeEllipsis={1} nbItemsAfterEllipsis={4}>
      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Products
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Hosting
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Servers
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Dedicated
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Rise
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          RISE-2
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Custom Collapse Threshold

```tsx
{
  globals: {
    imports: `import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb collapseThreshold={7}>
      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Products
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Hosting
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Servers
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Dedicated
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Rise
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          RISE-2
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb>
      <BreadcrumbItem>
        <BreadcrumbLink aria-label="Home" href="#">
          <Icon name={ICON_NAME.home} />
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Parent
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Current
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Demo

```tsx
{
  render: arg => <Breadcrumb collapseThreshold={arg.collapseThreshold} nbItemsAfterEllipsis={arg.nbItemsAfterEllipsis} nbItemsBeforeEllipsis={arg.nbItemsBeforeEllipsis}>
      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Page 1
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Page 2
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Page 3
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Current page
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>,
  argTypes: orderControls({
    collapseThreshold: {
      table: {
        category: CONTROL_CATEGORY.design
      }
    },
    nbItemsAfterEllipsis: {
      table: {
        category: CONTROL_CATEGORY.design
      }
    },
    nbItemsBeforeEllipsis: {
      table: {
        category: CONTROL_CATEGORY.design
      }
    }
  })
}
```

### No Collapse

```tsx
{
  globals: {
    imports: `import { Breadcrumb, BreadcrumbItem, BreadcrumbLink } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb noCollapse>
      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Products
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Hosting
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Servers
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Dedicated
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Rise
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          RISE-2
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Breadcrumb>
      <BreadcrumbItem>
        <BreadcrumbLink aria-label="Home" href="#">
          <Icon name={ICON_NAME.home} />
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Parent
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Current
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <Breadcrumb>
      <BreadcrumbItem>
        <BreadcrumbLink aria-label="Homepage" href="#">
          Home
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Products
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Hosting
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Servers
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Dedicated
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          Rise
        </BreadcrumbLink>
      </BreadcrumbItem>

      <BreadcrumbItem>
        <BreadcrumbLink href="#">
          RISE-2
        </BreadcrumbLink>
      </BreadcrumbItem>
    </Breadcrumb>
}
```

## React Components/Button

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | BUTTON_COLOR.primary | @type=BUTTON_COLOR The color preset to use. |
| `loading` | `` | No | false | Whether the component is in loading state, disabling it. |
| `size` | `` | No | BUTTON_SIZE.md | The size preset to use. |
| `variant` | `` | No | BUTTON_VARIANT.default | The variant preset to use. |


## Examples


### Accessibility Bad Practice Icon Only

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button>
      <Icon name={ICON_NAME.xmark} />
    </Button>
}
```

### Accessibility Bad Practice Labelled By

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button>
        <Icon name={ICON_NAME.filter} />
      </Button>
      <span>Filter your search results</span>
    </>
}
```

### Accessibility Bad Practices Role Alert

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [error, setError] = useState('');
    const handleClick = () => {
      setError('A critical error occurred while saving.');
    };
    return <>
        <Button onClick={handleClick}>
          Save
        </Button>

        <span style={{
        marginLeft: '1rem',
        color: 'red'
      }}>
          {error}
        </span>
      </>;
  }
}
```

### Accessibility Bad Practices Role Status

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [message, setMessage] = useState('');
    const handleClick = () => {
      setMessage('Copied to clipboard.');
    };
    return <>
        <Button onClick={handleClick}>
          Copy
        </Button>

        <span style={{
        marginLeft: '1rem'
      }}>
          {message}
        </span>
      </>;
  }
}
```

### Accessibility Explicit Text Content

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button>
      Clear
    </Button>
}
```

### Accessibility Icon Only

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button aria-label='Clear'>
        <Icon name={ICON_NAME.xmark} />
    </Button>
}
```

### Accessibility Labelled By

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button aria-labelledby="filter-btn">
        <Icon name={ICON_NAME.filter} />
      </Button>
      <span id="filter-btn">Filter your search results</span>
    </>
}
```

### Accessibility Role Alert

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [error, setError] = useState('');
    const handleClick = () => {
      setError('A critical error occurred while saving!');
    };
    return <>
        <Button onClick={handleClick}>
          Save
        </Button>

        <span role="alert" style={{
        marginLeft: '1rem',
        color: 'red'
      }}>
          {error}
        </span>
      </>;
  }
}
```

### Accessibility Role Status

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: () => {
    const [message, setMessage] = useState('');
    const handleClick = () => {
      setMessage('Copied to clipboard.');
    };
    return <>
        <Button onClick={handleClick}>
          Copy
        </Button>

        <span aria-live="polite" role="status" style={{
        marginLeft: '1rem'
      }}>
          {message}
        </span>
      </>;
  }
}
```

### Color

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { BUTTON_COLOR, Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button color={BUTTON_COLOR.critical}>Critical</Button>
      <Button color={BUTTON_COLOR.information}>Information</Button>
      <Button color={BUTTON_COLOR.neutral}>Neutral</Button>
      <Button color={BUTTON_COLOR.primary}>Primary</Button>
      <Button color={BUTTON_COLOR.success}>Success</Button>
      <Button color={BUTTON_COLOR.warning}>Warning</Button>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button>
      My button
    </Button>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'BUTTON_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: BUTTON_COLORS
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'BUTTON_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: BUTTON_SIZES
    },
    variant: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'BUTTON_VARIANT'
        }
      },
      control: {
        type: 'select'
      },
      options: BUTTON_VARIANTS
    }
  }),
  args: {
    children: 'My button'
  }
}
```

### Loading

```tsx
{
  globals: {
    imports: `import { Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Button loading={true}>
      Loading button
    </Button>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Button>
      Button
    </Button>
}
```

### Size

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { BUTTON_SIZE, Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button size={BUTTON_SIZE.md}>MD button</Button>
      <Button size={BUTTON_SIZE.sm}>SM button</Button>
      <Button size={BUTTON_SIZE.xs}>XS button</Button>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      {BUTTON_SIZES.map(size => <div key={size} style={{
      display: 'flex',
      flexDirection: 'column',
      gap: '8px'
    }}>
          {BUTTON_VARIANTS.map(variant => <div key={`${String(size)}-${String(variant)}`} style={{
        display: 'flex',
        flexFlow: 'row wrap',
        gap: '8px',
        alignItems: 'center'
      }}>
              {BUTTON_COLORS.map(color => <Button key={`${String(size)}-${String(variant)}-${String(color)}`} size={size} variant={variant} color={color}>
                  {`${String(variant)} ${String(color)}`}
                </Button>)}
            </div>)}
          <div style={{
        display: 'flex',
        flexFlow: 'row wrap',
        gap: '8px',
        alignItems: 'center'
      }}>
            {BUTTON_VARIANTS.map(variant => <Button key={`disabled-${String(size)}-${String(variant)}`} size={size} variant={variant} disabled>
                Disabled
              </Button>)}
          </div>
          <div style={{
        display: 'flex',
        flexFlow: 'row wrap',
        gap: '8px',
        alignItems: 'center'
      }}>
            {BUTTON_VARIANTS.map(variant => <Button key={`loading-${String(size)}-${String(variant)}`} size={size} variant={variant} loading>
                Loading
              </Button>)}
          </div>
        </div>)}

      <div>
        <Button>
          <Icon name={ICON_NAME.xmark} /> Close
        </Button>
      </div>
    </div>
}
```

### Variant

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { BUTTON_VARIANT, Button } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Button variant={BUTTON_VARIANT.default}>Default button</Button>
      <Button variant={BUTTON_VARIANT.outline}>Outline button</Button>
      <Button variant={BUTTON_VARIANT.ghost}>Ghost button</Button>
    </>
}
```

## React Components/Card

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | CARD_COLOR.primary | @type=CARD_COLOR The color preset to use. |


## Examples


### Accessibility Alternative Grouping

```tsx
{
  globals: {
    imports: `import { Card } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <div role="list" style={{
    display: 'flex',
    gap: '16px'
  }}>
      <Card role="listitem" style={{
      padding: '8px'
    }}>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />
        Interdum et malesuada fames ac ante ipsum primis in faucibus.
      </Card>
      <Card role="listitem" style={{
      padding: '8px'
    }}>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />
        Interdum et malesuada fames ac ante ipsum primis in faucibus.
      </Card>
    </div>
}
```

### Accessibility Grouping

```tsx
{
  globals: {
    imports: `import { Card } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <ul style={{
    display: 'flex',
    gap: '16px',
    padding: 0,
    margin: 0,
    listStyleType: 'none'
  }}>
      <li>
        <Card style={{
        padding: '8px'
      }}>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />
          Interdum et malesuada fames ac ante ipsum primis in faucibus.
        </Card>
      </li>
      <li>
        <Card style={{
        padding: '8px'
      }}>
          Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />
          Interdum et malesuada fames ac ante ipsum primis in faucibus.
        </Card>
      </li>
    </ul>
}
```

### Color

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    gap: '16px'
  }}>{story()}</div>],
  globals: {
    imports: `import { CARD_COLOR, Card } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Card color={CARD_COLOR.critical}>
        <p>Critical</p>
      </Card>

      <Card color={CARD_COLOR.information}>
        <p>Information</p>
      </Card>

      <Card color={CARD_COLOR.neutral}>
        <p>Neutral</p>
      </Card>

      <Card color={CARD_COLOR.primary}>
        <p>Primary</p>
      </Card>

      <Card color={CARD_COLOR.success}>
        <p>Success</p>
      </Card>

      <Card color={CARD_COLOR.warning}>
        <p>Warning</p>
      </Card>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Card } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Card>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />Interdum et malesuada fames ac ante ipsum primis in faucibus.</p>
    </Card>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'CARD_COLOR'
        }
      },
      control: 'select',
      options: CARD_COLORS
    },
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    }
  }),
  args: {
    children: 'Hello, world!'
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Card style={{
    padding: '8px'
  }}>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.<br />
      Interdum et malesuada fames ac ante ipsum primis in faucibus.
    </Card>
}
```

### ThemeGenerator

```tsx
{
  name: 'ThemeGenerator',
  parameters: {
    docs: {
      disable: true
    },
    layout: 'fullscreen',
    options: {
      showPanel: false
    }
  },
  tags: ['!dev', 'hidden'],
  render: ({}) => <div style={{
    display: 'flex',
    gap: '16px'
  }}>
      {CARD_COLORS.map(color => <Card key={String(color)} color={color}>
          <p style={{
        margin: 0
      }}>{String(color)}</p>
        </Card>)}
    </div>
}
```

## React Components/Checkbox

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `checked` | `` | No |  | The controlled checked state of the checkbox. |
| `defaultChecked` | `` | No |  | The initial checked state of the checkbox. Use when you don't need to control the checked state of the checkbox. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onCheckedChange` | `` | No |  | Callback fired when the checked state changes. |
| `required` | `` | No |  | Whether the component is required. |
| `value` | `` | No |  | The value of form element. Useful for form submission. |


## Subcomponents


### CheckboxControl




### CheckboxGroup



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial value of `value` when uncontrolled. |
| `disabled` | `` | No |  | Whether the group is disabled. |
| `invalid` | `` | No |  | Whether the group is in error. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `value` | `` | No |  | The controlled value of the checkbox group. |



### CheckboxLabel



## Examples


### Default

```tsx
{
  globals: {
    imports: `import { Checkbox, CheckboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Checkbox>
      <CheckboxControl />
    </Checkbox>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Checkbox disabled={arg.disabled} invalid={arg.invalid}>
      <CheckboxControl />

      <CheckboxLabel>
        {arg.label}
      </CheckboxLabel>
    </Checkbox>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    label: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    }
  }),
  args: {
    label: 'My checkbox'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Checkbox, CheckboxControl, CheckboxLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Checkbox disabled>
      <CheckboxControl />

      <CheckboxLabel>
        Checkbox
      </CheckboxLabel>
    </Checkbox>
}
```

### Group

```tsx
{
  globals: {
    imports: `import { Checkbox, CheckboxControl, CheckboxGroup, CheckboxLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <CheckboxGroup defaultValue={['marketing']} name="acknowledgments">
      <Checkbox value="term">
        <CheckboxControl />

        <CheckboxLabel>
          I agree to the terms and conditions.
        </CheckboxLabel>
      </Checkbox>

      <Checkbox value="marketing">
        <CheckboxControl />

        <CheckboxLabel>
          I agree to receive marketing communications.
        </CheckboxLabel>
      </Checkbox>
    </CheckboxGroup>
}
```

### In Form Field

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { TEXT_PRESET, Checkbox, CheckboxControl, CheckboxLabel, FormField, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Text preset={TEXT_PRESET.label}>
        Legal considerations:
      </Text>

      <FormField>
        <Checkbox>
          <CheckboxControl />

          <CheckboxLabel>
            I agree to the terms and conditions.
          </CheckboxLabel>
        </Checkbox>
      </FormField>

      <FormField>
        <Checkbox>
          <CheckboxControl />

          <CheckboxLabel>
            I agree to receive marketing communications.
          </CheckboxLabel>
        </Checkbox>
      </FormField>
    </>
}
```

### Invalid

```tsx
{
  globals: {
    imports: `import { Checkbox, CheckboxControl, CheckboxLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Checkbox invalid>
      <CheckboxControl />

      <CheckboxLabel>
        Checkbox
      </CheckboxLabel>
    </Checkbox>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Checkbox>
      <CheckboxControl />

      <CheckboxLabel>
        Checkbox
      </CheckboxLabel>
    </Checkbox>
}
```

### States

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Checkbox, CheckboxControl, CheckboxLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Checkbox checked={false}>
        <CheckboxControl />

        <CheckboxLabel>
          Unchecked
        </CheckboxLabel>
      </Checkbox>

      <Checkbox checked={true}>
        <CheckboxControl />

        <CheckboxLabel>
          Checked
        </CheckboxLabel>
      </Checkbox>

      <Checkbox checked="indeterminate">
        <CheckboxControl />

        <CheckboxLabel>
          Indeterminate
        </CheckboxLabel>
      </Checkbox>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '12px'
  }}>
      <div style={{
      display: 'flex',
      flexFlow: 'row',
      gap: '16px',
      alignItems: 'center'
    }}>
        <Checkbox>
          <CheckboxControl />
          <CheckboxLabel>Unchecked</CheckboxLabel>
        </Checkbox>
        <Checkbox disabled>
          <CheckboxControl />
          <CheckboxLabel>Unchecked disabled</CheckboxLabel>
        </Checkbox>
      </div>

      <div style={{
      display: 'flex',
      flexFlow: 'row',
      gap: '16px',
      alignItems: 'center'
    }}>
        <Checkbox checked>
          <CheckboxControl />
          <CheckboxLabel>Checked</CheckboxLabel>
        </Checkbox>
        <Checkbox checked disabled>
          <CheckboxControl />
          <CheckboxLabel>Checked disabled</CheckboxLabel>
        </Checkbox>
      </div>

      <div style={{
      display: 'flex',
      flexFlow: 'row',
      gap: '16px',
      alignItems: 'center'
    }}>
        <Checkbox checked="indeterminate">
          <CheckboxControl />
          <CheckboxLabel>Indeterminate</CheckboxLabel>
        </Checkbox>
        <Checkbox checked="indeterminate" disabled>
          <CheckboxControl />
          <CheckboxLabel>Indeterminate disabled</CheckboxLabel>
        </Checkbox>
      </div>
    </div>
}
```

## React Components/Clipboard

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `disabled` | `` | No |  | Whether the component is disabled. |
| `i18n` | `` | No |  | Internal translations override (see Input i18n keys). |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `onCopy` | `` | No |  | Callback fired when the input value is copied. |
| `value` | `` | No |  | The input value. |


## Subcomponents


### ClipboardControl



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `loading` | `` | No |  | Whether the component is in loading state. |
| `maskOption` | `` | No |  | Whether the masked display is active and its initial state. |



### ClipboardTrigger



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `labelCopy` | `` | No | 'Copy to clipboard' | The initial tooltip label on copy button. |
| `labelCopySuccess` | `` | No | 'Copied!' | The tooltip label on copy button after a successful copy. |


## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        API key:
      </FormFieldLabel>

      <Clipboard value="loremipsum">
        <ClipboardControl />

        <ClipboardTrigger />
      </Clipboard>
    </FormField>
}
```

### Custom Labels

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Custom labels">
      <ClipboardControl />

      <ClipboardTrigger labelCopy="Click to copy" labelCopySuccess="Successfully copied" />
    </Clipboard>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Clipboard">
      <ClipboardControl />

      <ClipboardTrigger />
    </Clipboard>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Clipboard disabled={arg.disabled} value={arg.value}>
      <ClipboardControl loading={arg.loading} maskOption={{
      enable: !!arg.masked
    }} />

      <ClipboardTrigger labelCopy={arg.labelCopy} labelCopySuccess={arg.labelCopySuccess} />
    </Clipboard>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    labelCopy: {
      table: {
        category: CONTROL_CATEGORY.general,
        defaultValue: {
          summary: 'Copy'
        }
      },
      control: 'text'
    },
    labelCopySuccess: {
      table: {
        category: CONTROL_CATEGORY.general,
        defaultValue: {
          summary: 'Copied'
        }
      },
      control: 'text'
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    masked: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    value: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    }
  }),
  args: {
    value: 'Clipboard'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Disabled" disabled>
      <ClipboardControl />

      <ClipboardTrigger />
    </Clipboard>
}
```

### Loading

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Loading">
      <ClipboardControl loading />

      <ClipboardTrigger />
    </Clipboard>
}
```

### Masked

```tsx
{
  globals: {
    imports: `import { Clipboard, ClipboardControl, ClipboardTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Masked">
      <ClipboardControl maskOption={{
      enable: true
    }} />

      <ClipboardTrigger />
    </Clipboard>
}
```

### Overview

```tsx
{
  parameters: {
    layout: 'centered'
  },
  tags: ['!dev'],
  render: ({}) => <Clipboard value="Clipboard">
      <ClipboardControl />

      <ClipboardTrigger />
    </Clipboard>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Clipboard value="Clipboard">
        <ClipboardControl />
        <ClipboardTrigger />
      </Clipboard>

      <Clipboard value="Masked">
        <ClipboardControl maskOption={{
        enable: true
      }} />
        <ClipboardTrigger />
      </Clipboard>

      <Clipboard value="Loading">
        <ClipboardControl loading />
        <ClipboardTrigger />
      </Clipboard>

      <Clipboard value="Disabled" disabled>
        <ClipboardControl />
        <ClipboardTrigger />
      </Clipboard>
    </div>
}
```

## React Components/Code

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `canCopy` | `` | No | false | Whether the copy button is displayed. |
| `highlighter` | `` | No | {} | Configuration of a specific code highlighter (see beneath for more details). |
| `labelCopy` | `` | No |  | The initial tooltip label on copy button. |
| `labelCopySuccess` | `` | No |  | The tooltip label on copy button after a successful copy. |
| `onCopy` | `` | No |  | Callback fired when the text is copied. |


## Examples


### Can Copy

```tsx
{
  globals: {
    imports: `import { Code } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Code canCopy>
      {`import { Text } from '@ovhcloud/ods-react';`}
    </Code>
}
```

### Custom Labels

```tsx
{
  globals: {
    imports: `import { Code } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Code canCopy labelCopy="Click to copy" labelCopySuccess="Successfully copied">
      console.log('Hello world');
    </Code>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Code } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Code>
      console.log('Hello world');
    </Code>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    canCopy: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    children: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    labelCopy: {
      table: {
        category: CONTROL_CATEGORY.general,
        defaultValue: {
          summary: 'Copy to clipboard'
        }
      },
      control: 'text'
    },
    labelCopySuccess: {
      table: {
        category: CONTROL_CATEGORY.general,
        defaultValue: {
          summary: 'Copied'
        }
      },
      control: 'text'
    }
  }),
  args: {
    children: `import { Text } from '@ovhcloud/ods-react';`
  }
}
```

### Highlighter

```tsx
{
  globals: {
    imports: `import { Code } from '@ovhcloud/ods-react';
import lang from '@shikijs/langs/typescript';
import theme from '@shikijs/themes/nord';`
  },
  tags: ['!dev'],
  render: ({}) => <Code highlighter={{
    language: lang,
    theme: theme
  }}>
      console.log('Hello World');
    </Code>
}
```

### Multiline

```tsx
{
  globals: {
    imports: `import { Code } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Code>
      {`function isTargetInElement(event, element) {
  if (!element) {
    return false;
  }

    return element.contains(event.target) || event.composedPath().includes(element);
  }`}
    </Code>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Code canCopy>
      {`import { Text } from '@ovhcloud/ods-react';`}
    </Code>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    maxWidth: 600
  }}>
      <Code>
        console.log('Hello world');
      </Code>

      <Code canCopy>
        {`import { Text } from '@ovhcloud/ods-react';`}
      </Code>

      <Code canCopy labelCopy="Copy" labelCopySuccess="Copied!">
        {`const sum = (a, b) => a + b;`}
      </Code>
    </div>
}
```

## React Components/Combobox

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `allowCustomValue` | `` | No | true | Whether to allow adding a value which is not part of the items. |
| `customFilter` | `` | No |  | Custom filter logic to apply to each item. |
| `customOptionRenderer` | `` | No |  | Custom render for each option item. |
| `defaultValue` | `` | No |  | The initial selected value(s). Use when you don't need to control the selected value(s) of the combobox. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `highlightResults` | `` | No | false | Whether to highlight the matching part of filtered items. |
| `i18n` | `` | No |  | Internal translations override (see Input i18n keys). |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `items` | `` | Yes |  | The list of items |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `multiple` | `` | No |  | Whether the multiple selection is allowed. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `newElementLabel` | `` | No | 'Add ' | Label displayed in front of a custom new value to add. |
| `noResultLabel` | `` | No | 'No results found' | Label displayed when no values match the current input value. |
| `onInputValueChange` | `` | No |  | Callback fired when the input value changes. |
| `onValueChange` | `` | No |  | Callback fired when the value(s) changes. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. /!\ Only work for single selection mode for now. |
| `value` | `` | No |  | The controlled selected value(s). |


## Subcomponents


### ComboboxContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |



### ComboboxControl



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clearable` | `` | No |  | Whether the clear button is displayed. |
| `loading` | `` | No |  | Whether the component is in loading state. |
| `placeholder` | `` | No |  | The placeholder text to display in the input. |


## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Favorite pet:
      </FormFieldLabel>

      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot'
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]}>
        <ComboboxControl />

        <ComboboxContent />
      </Combobox>
    </FormField>
}
```

### Accessibility I 18 N

```tsx
{
  globals: {
    imports: `import { INPUT_I18N, Combobox, ComboboxContent, ComboboxControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <FormField>
      <FormFieldLabel>
        Favorite pet:
      </FormFieldLabel>

      <Combobox i18n={{
      [INPUT_I18N.clearButton]: 'Clear favorite pet selection'
    }} items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot'
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]}>
        <ComboboxControl clearable />

        <ComboboxContent />
      </Combobox>
    </FormField>
}
```

### Clearable

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl clearable placeholder="Combobox" />

      <ComboboxContent />
    </Combobox>
}
```

### Controlled

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [value, setValue] = useState<string[]>(['dog']);
    return <>
        <Combobox items={[{
        label: 'Dog',
        value: 'dog'
      }, {
        label: 'Cat',
        value: 'cat'
      }, {
        label: 'Hamster',
        value: 'hamster'
      }, {
        label: 'Parrot',
        value: 'parrot'
      }, {
        label: 'Spider',
        value: 'spider'
      }, {
        label: 'Goldfish',
        value: 'goldfish'
      }]} onValueChange={details => setValue(details.value)} value={value}>
          <ComboboxControl placeholder="Select an animal" />

          <ComboboxContent />
        </Combobox>

        <div style={{
        marginTop: 8
      }}>
          <strong>Selected value:</strong> {value[0] ?? 'None'}
        </div>
      </>;
  }
}
```

### Custom Filter

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox customFilter={(label, inputValue) => {
    const reversedLabel = label.split('').reverse().join('');
    return new RegExp(`^${inputValue}`, 'i').test(reversedLabel);
  }} items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl placeholder="Search from right to left in each word" />

      <ComboboxContent />
    </Combobox>
}
```

### Custom Options

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    type MyData = {
      color?: string;
      info?: string;
    };
    const items = [{
      label: 'Apple',
      value: 'apple',
      customRendererData: {
        color: 'red',
        info: 'Fruit'
      }
    }, {
      label: 'Banana',
      value: 'banana',
      customRendererData: {
        color: 'yellow',
        info: 'Fruit'
      }
    }, {
      label: 'Carrot',
      value: 'carrot',
      customRendererData: {
        color: 'orange',
        info: 'Vegetable'
      }
    }, {
      label: 'Broccoli',
      value: 'broccoli',
      customRendererData: {
        color: 'green',
        info: 'Vegetable'
      }
    }, {
      label: 'Blueberry',
      value: 'blueberry',
      customRendererData: {
        color: 'blue',
        info: 'Fruit'
      }
    }];
    function customOptionRenderer(item: ComboboxItem<MyData>) {
      return <span style={{
        color: item.customRendererData?.color,
        fontWeight: 'bold'
      }}>
          {item.label} {item.customRendererData?.info && <span style={{
          fontWeight: 'normal',
          fontSize: 12,
          color: '#888'
        }}>({item.customRendererData.info})</span>}
        </span>;
    }
    return <Combobox customOptionRenderer={customOptionRenderer} highlightResults items={items}>
        <ComboboxControl />

        <ComboboxContent />
      </Combobox>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl />

      <ComboboxContent />
    </Combobox>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Combobox items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]} allowCustomValue={arg.allowCustomValue} defaultValue={arg.defaultValue} disabled={arg.disabled} highlightResults={arg.highlightResults} invalid={arg.invalid} multiple={arg.multiple} newElementLabel={arg.newElementLabel} noResultLabel={arg.noResultLabel} readOnly={arg.readOnly}>
      <ComboboxControl clearable={arg.clearable} placeholder={arg.placeholder} loading={arg.loading} />
      <ComboboxContent />
    </Combobox>,
  argTypes: orderControls({
    allowCustomValue: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    clearable: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    defaultValue: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    highlightResults: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    multiple: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    newElementLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    noResultLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  }),
  args: {
    placeholder: 'Start typing'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox disabled items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl placeholder="Combobox" />

      <ComboboxContent />
    </Combobox>
}
```

### Empty

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox items={[]}>
      <ComboboxControl />

      <ComboboxContent />
    </Combobox>
}
```

### Group

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox items={[{
    label: 'Europe',
    options: [{
      label: 'France',
      value: 'fr'
    }, {
      label: 'Germany',
      value: 'de'
    }, {
      label: 'Italy',
      value: 'it'
    }]
  }, {
    label: 'Asia',
    options: [{
      label: 'China',
      value: 'cn'
    }, {
      label: 'Japan',
      value: 'jp'
    }, {
      label: 'Russia',
      value: 'ru'
    }]
  }, {
    label: 'World',
    value: 'world'
  }]}>
      <ComboboxControl placeholder="Combobox" />

      <ComboboxContent />
    </Combobox>
}
```

### Highlight

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox highlightResults items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl />

      <ComboboxContent />
    </Combobox>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Combobox
      </FormFieldLabel>

      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl />

        <ComboboxContent />
      </Combobox>
    </FormField>
}
```

### Invalid

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox invalid items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }]}>
      <ComboboxControl />

      <ComboboxContent />
    </Combobox>
}
```

### Multiple

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox multiple items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl />

      <ComboboxContent />
    </Combobox>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => <Combobox items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl placeholder="Combobox" />

      <ComboboxContent />
    </Combobox>
}
```

### Placeholder

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster',
    disabled: true
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <ComboboxControl placeholder="Please select" />

      <ComboboxContent />
    </Combobox>
}
```

### Readonly

```tsx
{
  globals: {
    imports: `import { Combobox, ComboboxContent, ComboboxControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Combobox defaultValue={['parrot']} items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]} readOnly>
      <ComboboxControl placeholder="Combobox" />

      <ComboboxContent />
    </Combobox>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl placeholder="Default" />
        <ComboboxContent createPortal={false} />
      </Combobox>

      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl clearable placeholder="Clearable" />
        <ComboboxContent createPortal={false} />
      </Combobox>

      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl loading placeholder="Loading" />
        <ComboboxContent createPortal={false} />
      </Combobox>

      <Combobox disabled items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl placeholder="Disabled" />
        <ComboboxContent createPortal={false} />
      </Combobox>

      <Combobox readOnly items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <ComboboxControl placeholder="Read only" />
        <ComboboxContent createPortal={false} />
      </Combobox>

      <Combobox items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]} multiple>
        <ComboboxControl placeholder="Multiple" />
        <ComboboxContent createPortal={false} />
      </Combobox>
    </div>
}
```

## React Components/Datepicker

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `dateFormatter` | `` | No |  | Format the date to display in the input. |
| `defaultOpen` | `` | No |  | The initial open state of the datepicker. Use when you don't need to control the open state of the datepicker. |
| `defaultValue` | `` | No |  | The initial selected date. Use when you don't need to control the selected date of the datepicker. |
| `defaultView` | `` | No |  | The default view of the calendar (day, month, year). |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `disabledDates` | `` | No |  | List of dates that cannot be selected. |
| `disabledWeekDays` | `` | No |  | List of week days that cannot be selected. |
| `i18n` | `` | No |  | Internal translations override (see Input i18n keys). |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `locale` | `` | No |  | The locale to use when formatting the date. |
| `max` | `` | No |  | The maximum date that can be selected. |
| `maxView` | `` | No |  | The maximum view of the calendar (day, month, year). |
| `min` | `` | No |  | The minimum date that can be selected. |
| `minView` | `` | No |  | The minimum view of the calendar (day, month, year). |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `open` | `` | No |  | The controlled open state of the datepicker. |
| `placeholder` | `` | No |  | The placeholder text to display in the input. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. |
| `value` | `` | No |  | The controlled selected date. |
| `view` | `` | No |  | The controlled view of the calendar (day, month, year). |


## Subcomponents


### DatepickerContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |



### DatepickerControl



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clearable` | `` | No |  | Whether the clear button is displayed. |
| `loading` | `` | No |  | Whether the component is in loading state. |


## Examples


### Accessibility Date Format

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldHelper, FormFieldLabel, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Start date:
      </FormFieldLabel>

      <Datepicker>
        <DatepickerControl placeholder="DD-MM-YYYY" />

        <DatepickerContent />
      </Datepicker>

      <FormFieldHelper>
        <Text preset={TEXT_PRESET.caption}>
          Expected format: DD-MM-YYYY
        </Text>
      </FormFieldHelper>
    </FormField>
}
```

### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Start date:
      </FormFieldLabel>

      <Datepicker>
        <DatepickerControl />

        <DatepickerContent />
      </Datepicker>
    </FormField>
}
```

### Accessibility I 18 N

```tsx
{
  globals: {
    imports: `import { INPUT_I18N, Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <FormField>
      <FormFieldLabel>
        Start date:
      </FormFieldLabel>

      <Datepicker i18n={{
      [INPUT_I18N.clearButton]: 'Clear date'
    }}>
        <DatepickerControl clearable />

        <DatepickerContent />
      </Datepicker>
    </FormField>
}
```

### Date Formatter

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <Datepicker dateFormatter={({
    date
  }) => `${date.getFullYear()}`} placeholder="yyyy">
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Datepicker>
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Datepicker disabled={arg.disabled} invalid={arg.invalid} locale={arg.locale} placeholder={arg.placeholder} readOnly={arg.readOnly}>
      <DatepickerControl clearable={arg.clearable} loading={arg.loading} />

      <DatepickerContent />
    </Datepicker>,
  argTypes: orderControls({
    clearable: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    },
    locale: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'iso code'
        }
      },
      control: {
        type: 'select'
      },
      options: ['de', 'en', 'es', 'fr', 'it', 'nl', 'pl', 'pt']
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    }
  })
}
```

### Disabled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <p>Disabled:</p>

      <Datepicker disabled>
        <DatepickerControl />

        <DatepickerContent />
      </Datepicker>

      <p>Disabled Dates:</p>

      <Datepicker disabledDates={[new Date(Date.now() - 86400000), new Date(), new Date(Date.now() + 86400000)]}>
        <DatepickerControl />

        <DatepickerContent />
      </Datepicker>

      <p>Disabled Week Days:</p>

      <Datepicker disabledWeekDays={[0, 3]}>
        <DatepickerControl />

        <DatepickerContent />
      </Datepicker>
    </>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl, FormField, FormFieldLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Label:
      </FormFieldLabel>

      <Datepicker>
        <DatepickerControl />

        <DatepickerContent />
      </Datepicker>
    </FormField>
}
```

### Max View

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Datepicker maxView="day">
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Min Max

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Datepicker max={new Date(Date.now() + 86400000 * 10)} min={new Date(Date.now() - 86400000 * 10)}>
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Min View

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Datepicker minView="month">
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Datepicker defaultValue={new Date()}>
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Readonly

```tsx
{
  globals: {
    imports: `import { Datepicker, DatepickerContent, DatepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Datepicker readOnly>
      <DatepickerControl />

      <DatepickerContent />
    </Datepicker>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <Datepicker>
        <DatepickerControl placeholder="Default" />
        <DatepickerContent createPortal={false} />
      </Datepicker>

      <Datepicker>
        <DatepickerControl loading placeholder="Loading" />
        <DatepickerContent createPortal={false} />
      </Datepicker>

      <Datepicker>
        <DatepickerControl clearable placeholder="Clearable" />
        <DatepickerContent createPortal={false} />
      </Datepicker>

      <Datepicker invalid>
        <DatepickerControl placeholder="Invalid" />
        <DatepickerContent createPortal={false} />
      </Datepicker>

      <Datepicker disabled>
        <DatepickerControl placeholder="Disabled" />
        <DatepickerContent createPortal={false} />
      </Datepicker>

      <Datepicker readOnly>
        <DatepickerControl placeholder="Read only" />
        <DatepickerContent createPortal={false} />
      </Datepicker>
    </div>
}
```

## React Components/Divider

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No |  | @deprecated The color preset to use. DEPRECATED: Color will now always be primary, if you need another color, prefer overriding it using css. |
| `spacing` | `` | No | DIVIDER_SPACING._2 | The spacing preset to use. |


## Examples


### Color

```tsx
{
  globals: {
    imports: `import { DIVIDER_COLOR, Divider } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Divider color={DIVIDER_COLOR.neutral} />
      <Divider color={DIVIDER_COLOR.primary} />
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Divider } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Divider />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'DIVIDER_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: DIVIDER_COLORS
    },
    spacing: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'DIVIDER_SPACING'
        }
      },
      control: {
        type: 'select'
      },
      options: DIVIDER_SPACINGS
    }
  })
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
      <Divider color={DIVIDER_COLOR.primary} />
      <p>Interdum et malesuada fames ac ante ipsum primis in faucibus.</p>
    </>
}
```

### Spacing

```tsx
{
  globals: {
    imports: `import { DIVIDER_SPACING, Divider } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Divider spacing={DIVIDER_SPACING._0} />
      <Divider spacing={DIVIDER_SPACING._2} />
      <Divider spacing={DIVIDER_SPACING._4} />
      <Divider spacing={DIVIDER_SPACING._6} />
      <Divider spacing={DIVIDER_SPACING._8} />
      <Divider spacing={DIVIDER_SPACING._12} />
      <Divider spacing={DIVIDER_SPACING._16} />
      <Divider spacing={DIVIDER_SPACING._24} />
      <Divider spacing={DIVIDER_SPACING._32} />
      <Divider spacing={DIVIDER_SPACING._40} />
      <Divider spacing={DIVIDER_SPACING._48} />
      <Divider spacing={DIVIDER_SPACING._64} />
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <Divider />
}
```

## React Components/Drawer

## Subcomponents


### DrawerBody




### DrawerContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `position` | `` | No | DRAWER_POSITION.left | The drawer position in the screen. |



### DrawerTrigger



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `asChild` | `` | No |  | Use the provided child element as the default rendered element, combining their props and behavior. Be careful to pass an actual Node, not a Fragment. |


## Examples


### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Button, Drawer, DrawerBody, DrawerContent, DrawerTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Trigger Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent aria-describedby="drawer-content" aria-label="My drawer">
        <DrawerBody id="drawer-content">
          The drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>
}
```

### Accessibility Aria Labelled By

```tsx
{
  globals: {
    imports: `import { Button, Drawer, DrawerBody, DrawerContent, DrawerTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Trigger Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent aria-describedby="drawer-content" aria-labelledby="drawer-title">
        <DrawerBody>
          <h2 id="drawer-title">
            My drawer
          </h2>

          <p id="drawer-content">
            The drawer content
          </p>
        </DrawerBody>
      </DrawerContent>
    </Drawer>
}
```

### Controlled

```tsx
{
  globals: {
    imports: `import { Button, Drawer, DrawerBody, DrawerContent } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [isOpen, setIsOpen] = useState(false);
    function onOpenChange({
      open
    }: DrawerOpenChangeDetail) {
      setIsOpen(open);
    }
    function openDrawer() {
      setIsOpen(true);
    }
    return <>
        <Button onClick={openDrawer}>
          Trigger Drawer
        </Button>

        <Drawer onOpenChange={onOpenChange} open={isOpen}>
          <DrawerContent>
            <DrawerBody>
              Content
            </DrawerBody>
          </DrawerContent>
        </Drawer>
      </>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Button, Drawer, DrawerBody, DrawerContent, DrawerTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Trigger Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent>
        <DrawerBody>
          My drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Drawer closeOnEscape={arg.closeOnEscape} closeOnInteractOutside={arg.closeOnInteractOutside}>
      <DrawerTrigger asChild>
        <Button>
          Trigger Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={arg.position}>
        <DrawerBody>
          {arg.content}
        </DrawerBody>
      </DrawerContent>
    </Drawer>,
  argTypes: orderControls({
    closeOnEscape: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    closeOnInteractOutside: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    content: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    position: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'DRAWER_POSITION'
        }
      },
      control: {
        type: 'select'
      },
      options: DRAWER_POSITIONS
    }
  }),
  args: {
    content: 'My drawer content'
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Trigger Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={DRAWER_POSITION.left}>
        <DrawerBody>
          My drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>
}
```

### Position

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    alignItems: 'center',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { DRAWER_POSITION, Button, Drawer, DrawerBody, DrawerContent, DrawerTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
    <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Top drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={DRAWER_POSITION.top}>
        <DrawerBody>
          Top drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>

   <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Left drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={DRAWER_POSITION.left}>
        <DrawerBody>
          Left drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>

   <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Right Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={DRAWER_POSITION.right}>
        <DrawerBody>
          Right drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>

   <Drawer>
      <DrawerTrigger asChild>
        <Button>
          Bottom Drawer
        </Button>
      </DrawerTrigger>

      <DrawerContent position={DRAWER_POSITION.bottom}>
        <DrawerBody>
          Bottom drawer content
        </DrawerBody>
      </DrawerContent>
    </Drawer>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'row wrap',
    gap: '12px'
  }}>
      <Drawer>
        <DrawerTrigger asChild>
          <Button>Left</Button>
        </DrawerTrigger>
        <DrawerContent createPortal={false} position={DRAWER_POSITION.left}>
          <DrawerBody>Left drawer</DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer>
        <DrawerTrigger asChild>
          <Button>Right</Button>
        </DrawerTrigger>
        <DrawerContent createPortal={false} position={DRAWER_POSITION.right}>
          <DrawerBody>Right drawer</DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer>
        <DrawerTrigger asChild>
          <Button>Top</Button>
        </DrawerTrigger>
        <DrawerContent createPortal={false} position={DRAWER_POSITION.top}>
          <DrawerBody>Top drawer</DrawerBody>
        </DrawerContent>
      </Drawer>

      <Drawer>
        <DrawerTrigger asChild>
          <Button>Bottom</Button>
        </DrawerTrigger>
        <DrawerContent createPortal={false} position={DRAWER_POSITION.bottom}>
          <DrawerBody>Bottom drawer</DrawerBody>
        </DrawerContent>
      </Drawer>
    </div>
}
```

## React Components/File Upload

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `accept` | `` | No |  | The accepted file types. |
| `acceptedFileLabel` | `` | No |  | Label describing the accepted file types. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `dropzoneLabel` | `` | No | 'Drag & drop a file' | The dropzone label. |
| `error` | `` | No |  | The global error message to display. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `maxFile` | `` | No | Infinity | The maximum number of files that can be selected. |
| `maxFileLabel` | `` | No |  | Label describing the maximum number of files that can be selected. |
| `maxSize` | `` | No |  | The maximum size of selectable files. |
| `maxSizeLabel` | `` | No |  | Label describing the maximum size of selectable files. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onFileAccept` | `` | No |  | Callback fired when a some files have been successfully added. |
| `onFileReject` | `` | No |  | Callback fired when a some files have been rejected. |
| `required` | `` | No |  | Whether the component is required. |
| `triggerLabel` | `` | No | 'Browse files' | Upload button label. |
| `variant` | `` | No | FILE_UPLOAD_VARIANT.default | The variant preset to use. |


## Subcomponents


### FileUploadItem



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `error` | `` | No |  | The file error message to display. |
| `file` | `` | Yes |  | The current File object. |
| `i18n` | `` | No |  | Internal translations override. |
| `progress` | `` | No |  | The file upload progress. |
| `uploadSuccessLabel` | `` | No |  | @deprecated The label displayed after a successful upload. DEPRECATED: Latest design change removed the upload success label in favor of a visual icon update. |



### FileUploadList



## Examples


### Accept

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [error, setError] = useState<string>('');
    const [files, setFiles] = useState<File[]>([]);
    function onAccept({
      files
    }: FileUploadAcceptDetail): void {
      setFiles(files);
      setError('');
    }
    function onReject({
      files
    }: FileUploadRejectDetail): void {
      setError(files.length ? 'File(s) not of the expected format' : '');
    }
    return <FileUpload accept="image/png" acceptedFileLabel="Png files only" error={error} onFileAccept={onAccept} onFileReject={onReject} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Accessibility File Button

```tsx
{
  globals: {
    imports: `import { FILE_UPLOAD_I18N, FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FileUpload onFileAccept={({
      files
    }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} i18n={{
          [FILE_UPLOAD_I18N.cancelButton]: `Cancel uploading ${file.name}`,
          [FILE_UPLOAD_I18N.deleteButton]: `Remove ${file.name}`,
          [FILE_UPLOAD_I18N.progressBar]: `Uploading ${file.name}`
        }} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList, FormField, FormFieldLabel } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FormField>
        <FormFieldLabel>
          Files:
        </FormFieldLabel>

        <FileUpload onFileAccept={({
        files
      }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
          <FileUploadList>
            {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
          </FileUploadList>
        </FileUpload>
      </FormField>;
  }
}
```

### Compact

```tsx
{
  globals: {
    imports: `import { FILE_UPLOAD_VARIANT, FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FileUpload onFileAccept={({
      files
    }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Custom Labels

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FileUpload acceptedFileLabel="Formats acceptés : images" dropzoneLabel="Glisser-déposer des fichiers" maxFile={3} maxFileLabel="Nombre maximal de fichiers :" maxSize={524288000} maxSizeLabel="Taille de fichier max :" onFileAccept={({
      files
    }) => setFiles(files)} triggerLabel="Parcourir les fichiers" variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} progress={100} uploadSuccessLabel="Fichier uploadé" />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FileUpload onFileAccept={({
      files
    }) => setFiles(files)}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Demo

```tsx
{
  render: arg => {
    const [error, setError] = useState<string>('');
    const [files, setFiles] = useState<File[]>([]);
    function onAccept({
      files
    }: FileUploadAcceptDetail): void {
      setFiles(files);
      setError('');
    }
    function onReject({
      files
    }: FileUploadRejectDetail): void {
      setError(files.length ? 'File(s) rejected' : '');
    }
    return <FileUpload {...arg} error={arg.error || error} onFileAccept={onAccept} onFileReject={onReject}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  },
  argTypes: orderControls({
    acceptedFileLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    dropzoneLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    error: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    maxFile: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    },
    maxFileLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    maxSize: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    },
    maxSizeLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    triggerLabel: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    variant: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'FILE_UPLOAD_VARIANT'
        }
      },
      control: {
        type: 'select'
      },
      options: FILE_UPLOAD_VARIANTS
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FileUpload disabled variant={FILE_UPLOAD_VARIANT.compact}>
      <FileUploadList />
    </FileUpload>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList, FormField, FormFieldLabel } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FormField>
          <FormFieldLabel>
            Files:
          </FormFieldLabel>

          <FileUpload onFileAccept={({
        files
      }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
            <FileUploadList>
              {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
            </FileUploadList>
          </FileUpload>
        </FormField>;
  }
}
```

### Max File

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [error, setError] = useState<string>('');
    const [files, setFiles] = useState<File[]>([]);
    function onAccept({
      files
    }: FileUploadAcceptDetail): void {
      setFiles(files);
      setError('');
    }
    function onReject({
      files
    }: FileUploadRejectDetail): void {
      setError(files.length ? 'Too many files' : '');
    }
    return <FileUpload error={error} maxFile={3} maxFileLabel="Maximum file allowed:" onFileAccept={onAccept} onFileReject={onReject} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Max Size

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [error, setError] = useState<string>('');
    const [files, setFiles] = useState<File[]>([]);
    function onAccept({
      files
    }: FileUploadAcceptDetail): void {
      setFiles(files);
      setError('');
    }
    function onReject({
      files
    }: FileUploadRejectDetail): void {
      setError(files.length ? 'File(s) too large' : '');
    }
    return <FileUpload error={error} maxSize={1000000} maxSizeLabel="No file larger than:" onFileAccept={onAccept} onFileReject={onReject} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <FileUpload onFileAccept={({
      files
    }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => {
    const [files, setFiles] = useState<File[]>([]);
    return <div style={{
      display: 'flex',
      flexDirection: 'column',
      gap: '12px'
    }}>
        <FileUpload onFileAccept={({
        files
      }) => setFiles(files)}>
          <FileUploadList>
            {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
          </FileUploadList>
        </FileUpload>

        <FileUpload disabled>
          <FileUploadList />
        </FileUpload>

        <FileUpload invalid onFileAccept={({
        files
      }) => setFiles(files)}>
          <FileUploadList>
            {files.map((file: File, idx) => <FileUploadItem file={file} key={idx} />)}
          </FileUploadList>
        </FileUpload>
      </div>;
  }
}
```

### Upload

```tsx
{
  globals: {
    imports: `import { FileUpload, FileUploadItem, FileUploadList } from '@ovhcloud/ods-react';
import { useEffect, useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    type MyFile = File & {
      error?: string;
      progress?: number;
    };
    const [files, setFiles] = useState<MyFile[]>([]);
    useEffect(() => {
      files.forEach(file => {
        if (!file.progress) {
          uploadFile(file);
        }
      });
    }, [files]);
    function uploadFile(file: MyFile): void {
      const intervalId = setInterval(() => {
        setFiles(files => files.map(f => {
          if (f.name === file.name) {
            f.progress = (f.progress || 0) + Math.floor(Math.random() * 10 + 1);
            if (f.progress >= 100) {
              clearInterval(intervalId);
            }
          }
          return f;
        }));
      }, 100);
    }
    return <FileUpload onFileAccept={({
      files
    }) => setFiles(files)} variant={FILE_UPLOAD_VARIANT.compact}>
        <FileUploadList>
          {files.map((file, idx) => <FileUploadItem error={file.error} file={file} key={idx} progress={file.progress} />)}
        </FileUploadList>
      </FileUpload>;
  }
}
```

## React Components/Form Field

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `id` | `` | No |  | The field id. |
| `invalid` | `` | No |  | Whether the component is in error state. |


## Subcomponents


### FormFieldError




### FormFieldHelper




### FormFieldLabel



## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldHelper, FormFieldLabel, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Login:
      </FormFieldLabel>

      <Input name="input" />

      <FormFieldHelper>
        Username or email address
      </FormFieldHelper>
    </FormField>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { FormField, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <Textarea name="textarea" />
    </FormField>
}
```

### Demo

```tsx
{
  render: (args: DemoArg) => <FormField invalid={args.invalid}>
      <FormFieldLabel>
        {args.label}
      </FormFieldLabel>

      <Textarea name="demo" />

      <FormFieldHelper>
        {args.helperText}
      </FormFieldHelper>

      <FormFieldError>
        {args.errorText}
      </FormFieldError>
    </FormField>,
  argTypes: orderControls({
    errorText: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    helperText: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    label: {
      table: {
        category: CONTROL_CATEGORY.slot,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    }
  })
}
```

### Error

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldError, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField invalid>
      <Textarea name="textarea" />

      <FormFieldError>
        Error message
      </FormFieldError>
    </FormField>
}
```

### Helper

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, FormField, FormFieldHelper, Text, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <Textarea name="textarea" />

      <FormFieldHelper>
        <Text preset={TEXT_PRESET.caption}>
          Helper text
        </Text>
      </FormFieldHelper>
    </FormField>
}
```

### Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Description:
      </FormFieldLabel>

      <Textarea name="textarea" />
    </FormField>
}
```

### Overview

```tsx
{
  parameters: {
    layout: 'centered'
  },
  tags: ['!dev'],
  render: ({}) => {
    const MAX_COUNT = 200;
    const [count, setCount] = useState(0);
    function onInput(e: FormEvent): void {
      setCount((e.target as HTMLTextAreaElement).value.length);
    }
    return <FormField invalid={count > MAX_COUNT}>
        <FormFieldLabel>
          Description:
        </FormFieldLabel>

        <Textarea name="description" onInput={onInput} />

        <FormFieldHelper style={{
        display: 'flex',
        justifyContent: 'space-between'
      }}>
          <Text preset={TEXT_PRESET.caption}>
            Helper text
          </Text>

          <Text preset={TEXT_PRESET.caption}>
            {count}/{MAX_COUNT}
          </Text>
        </FormFieldHelper>

        <FormFieldError>
          Error message
        </FormFieldError>
      </FormField>;
  }
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <FormField>
        <FormFieldLabel>Label</FormFieldLabel>
        <Input name="input" />
      </FormField>

      <FormField>
        <Input name="input" />
        <FormFieldHelper>Helper text</FormFieldHelper>
      </FormField>

      <FormField invalid>
        <Input name="input" />
        <FormFieldError>Error message</FormFieldError>
      </FormField>
    </div>
}
```

## React Components

# Gallery

Badge

1.  
2.  
3.  Current

Checkbox

HTML

CSS

JavaScript

src

app.tsx

index.ts

components

Button.tsx

Card.tsx

package.json

README.md

## React Components/Icon

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `name` | `` | Yes |  | The icon name. |


## Examples


### Accessibility Bad Practice Decorative

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon name={ICON_NAME.home} />
}
```

### Accessibility Bad Practice Rating

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <label htmlFor="rating">
        Rating
      </label>

      <div id="rating">
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="one star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="two star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="three star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="four star" role="img" />
        <Icon name={ICON_NAME.star} tabIndex={0} aria-label="five star" role="img" />
      </div>
    </>
}
```

### Accessibility Informative

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon aria-label="home" name={ICON_NAME.home} role="img" />
}
```

### Accessibility Rating

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <label htmlFor="rating" id="rating-label">
        Rating
      </label>

      <div aria-labelledby="rating-label" id="rating" role="radiogroup">
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="one star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={0} aria-label="two star" aria-checked="true" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="three star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="four star" aria-checked="false" />
        <Icon name={ICON_NAME.star} role="radio" tabIndex={-1} aria-label="five star" aria-checked="false" />
      </div>
    </>
}
```

### All

```tsx
{
  render: (arg: AllArg) => {
    const regexp = new RegExp((arg.search || '').replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'));
    const names = arg.search ? Object.entries<string>(ICON_NAME).filter(([key, name]) => {
      return [name].concat(ODS_ICON_TAG[key as IconNameKey] || []).some(value => regexp.test(value));
    }).map(([_, name]) => name) : ICON_NAMES;
    return <div style={{
      display: 'flex',
      flexFlow: 'row',
      flexWrap: 'wrap',
      gap: '1rem'
    }}>
        {names.map(name => <Icon key={name} name={name as ICON_NAME} title={name} />)}
      </div>;
  },
  argTypes: {
    name: {
      table: {
        disable: true
      }
    },
    search: {
      control: {
        type: 'text'
      },
      description: 'Search for a specific icon name or tag'
    }
  }
}
```

### Decorative

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon name={ICON_NAME.home} />
}
```

### Default

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon name={ICON_NAME.home} />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    name: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'ICON_NAME'
        }
      },
      control: {
        type: 'select'
      },
      options: ICON_NAMES
    }
  }),
  args: {
    name: ICON_NAME.home
  }
}
```

### Informative

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Icon aria-label="Help" name={ICON_NAME.circleQuestion} role="img" />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Icon name="home" style={{
    fontSize: '2rem',
    color: 'var(--ods-color-primary-500)'
  }} />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'row wrap',
    gap: '16px',
    alignItems: 'center'
  }}>
      <Icon name={ICON_NAME.home} />
      <Icon name={ICON_NAME.circleInfo} />
      <Icon name={ICON_NAME.tag} />
      <Icon name={ICON_NAME.check} />
      <Icon name={ICON_NAME.triangleExclamation} />
    </div>
}
```

## React Components/Input

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clearable` | `` | No | false | Whether the clear button is displayed. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `i18n` | `` | No |  | Internal translations override. |
| `loading` | `` | No | false | Whether the component is in loading state. |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `maskOption` | `` | No | { enable: false, initialState: INPUT_MASK_STATE.close } | Whether the masked display is active and its initial state. |
| `onClear` | `` | No |  | Callback fired when the input value is cleared. |
| `type` | `` | No | INPUT_TYPE.text | The input type. |


## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Name:
      </FormFieldLabel>

      <Input />
    </FormField>
}
```

### Accessibility I 18 N

```tsx
{
  globals: {
    imports: `import { INPUT_I18N, FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <FormField>
      <FormFieldLabel>
        Search:
      </FormFieldLabel>

      <Input clearable defaultValue="my search" i18n={{
      [INPUT_I18N.clearButton]: 'Clear current search',
      [INPUT_I18N.searchButton]: 'Search in database'
    }} type='search' />
    </FormField>
}
```

### Clearable

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input clearable defaultValue="Clearable" />
}
```

### Datalist

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Input list="ice-cream-flavors" />

      <datalist id="ice-cream-flavors">
        <option value="Chocolate"></option>
        <option value="Coconut"></option>
        <option value="Mint"></option>
        <option value="Strawberry"></option>
        <option value="Vanilla"></option>
      </datalist>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input />
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => {
    const {
      masked,
      ...inputArg
    } = arg;
    return <Input maskOption={{
      enable: !!masked
    }} {...inputArg} />;
  },
  argTypes: orderControls({
    clearable: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    masked: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    type: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'INPUT_TYPE'
        }
      },
      control: {
        type: 'select'
      },
      options: INPUT_TYPES
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input disabled />
}
```

### Floating Number

```tsx
{
  globals: {
    imports: `import { INPUT_TYPE, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input defaultValue="9.99" step="any" type={INPUT_TYPE.number} />
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Name:
      </FormFieldLabel>

      <Input />
    </FormField>
}
```

### Loading

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input loading />
}
```

### Masked

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input maskOption={{
    enable: true
  }} />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Input placeholder="Input" />
}
```

### Read Only

```tsx
{
  globals: {
    imports: `import { Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Input defaultValue="Readonly" readOnly />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Input placeholder="Default" />
      <Input clearable defaultValue="Clearable" />
      <Input loading placeholder="Loading" />
      <Input disabled placeholder="Disabled" />
      <Input invalid placeholder="Invalid" />
      <Input readOnly defaultValue="Read only" />
      <Input clearable maskOption={{
      enable: true
    }} defaultValue="Clearable" />
    </div>
}
```

### Types

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px',
    alignItems: 'start'
  }}>{story()}</div>],
  globals: {
    imports: `import { INPUT_TYPE, Input } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Input type={INPUT_TYPE.email} placeholder="email" /><br />
      <Input type={INPUT_TYPE.number} placeholder="number" /><br />
      <Input type={INPUT_TYPE.password} placeholder="password" /><br />
      <Input type={INPUT_TYPE.search} placeholder="search" /><br />
      <Input type={INPUT_TYPE.text} placeholder="text" /><br />
      <Input type={INPUT_TYPE.time} placeholder="time" /><br />
      <Input type={INPUT_TYPE.url} placeholder="url" /><br />
    </>
}
```

## React Components/Link

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `as` | `` | No |  | @default-value='a' Pass a component you may want to use as custom Link component. Useful for example when using routing library like react-router. |
| `disabled` | `` | No | false | Whether the component is disabled. |


## Examples


### Accessibility File Download

```tsx
{
  decorators: [story => <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Icon, Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Link aria-label="Download WCAG20 Guidelines (PDF, 481 KB)" href="https://www.w3.org/TR/2024/REC-WCAG21-20241212.pdf">
        <Icon name={ICON_NAME.download} />
      </Link>

      <Link href="https://www.w3.org/TR/2024/REC-WCAG21-20241212.pdf">
        <Icon name={ICON_NAME.download} />

        <span>Download WCAG20 Guidelines (PDF, 481 KB)</span>
      </Link>
    </>
}
```

### Accessibility Icon Only Link

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon, Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Link aria-label="Go to homepage" href="https://www.ovhcloud.com">
      <Icon name={ICON_NAME.home} />
    </Link>
}
```

### Accessibility In A New Tab

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon, Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Link aria-label="Visit Example (opens in a new tab)" href="https://www.ovhcloud.com" target="_blank">
      <Icon name={ICON_NAME.externalLink} />
    </Link>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Link href="https://www.ovhcloud.com">
      Default Link
    </Link>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  }),
  args: {
    // @ts-ignore check when time to do so
    children: 'My link'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Link disabled href="https://www.ovhcloud.com">
      Disabled
    </Link>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Link href="https://www.ovhcloud.com" target="_blank">
      Link
    </Link>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>
      <Link href="https://www.ovhcloud.com">Default Link</Link>
      <Link disabled href="https://www.ovhcloud.com">Disabled</Link>
      <Link href="https://www.ovhcloud.com"><Icon name={ICON_NAME.arrowLeft} />Icon Left</Link>
      <Link href="https://www.ovhcloud.com" style={{
      justifySelf: 'right'
    }}>Icon Right<Icon name={ICON_NAME.arrowRight} /></Link>
    </div>
}
```

### With Icon

```tsx
{
  decorators: [story => <div style={{
    display: 'grid',
    gridTemplateColumns: '1fr 1fr'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Icon, Link } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Link href="https://www.ovhcloud.com">
        <Icon name={ICON_NAME.arrowLeft} />Icon Link
      </Link>

      <Link href="https://www.ovhcloud.com" style={{
      justifySelf: 'right'
    }}>
        Icon Link<Icon name={ICON_NAME.arrowRight} />
      </Link>
    </>
}
```

## React Components/Medium

## Examples


### Caption

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Medium, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <figure>
      <Medium alt="OVHcloud logo" src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" />

      <figcaption>
        <Text preset={TEXT_PRESET.caption}>
          © Copyright 1999-2025 OVH SA
        </Text>
      </figcaption>
    </figure>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Medium } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Medium alt="OVHcloud logo" src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    height: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    },
    src: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    width: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    }
  }),
  args: {
    src: 'https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4'
  }
}
```

### Height

```tsx
{
  globals: {
    imports: `import { Medium } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Medium alt="OVHcloud logo" height={20} src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Medium alt="OVHcloud logo" src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <Medium alt="OVHcloud logo" height={20} src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" />
}
```

### Width

```tsx
{
  globals: {
    imports: `import { Medium } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Medium alt="OVHcloud logo" src="https://images.crunchbase.com/image/upload/c_pad,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" width={300} />
}
```

## React Components/Message

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | MESSAGE_COLOR.information | @type=MESSAGE_COLOR The color preset to use. |
| `dismissible` | `` | No | true | Whether the remove button is displayed. |
| `i18n` | `` | No |  | Internal translations override. |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `onRemove` | `` | No |  | Callback fired when the close button is pressed. |
| `variant` | `` | No | MESSAGE_VARIANT.default | The variant preset to use. |


## Subcomponents


### MessageBody




### MessageIcon



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `name` | `` | Yes |  | The icon name. |


## Examples


### Accessibility Alternative Grouping

```tsx
{
  globals: {
    imports: `import { ICON_NAME, MESSAGE_COLOR, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div role="list" style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>
      <Message role="listitem">
        <MessageIcon name={ICON_NAME.circleCheck} />

        <MessageBody>
          Your changes have been saved.
        </MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.warning} role="listitem">
        <MessageIcon name={ICON_NAME.triangleExclamation} />

        <MessageBody>
          Some fields need your attention.
        </MessageBody>
      </Message>
    </div>
}
```

### Accessibility Grouping

```tsx
{
  globals: {
    imports: `import { ICON_NAME, MESSAGE_COLOR, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ul style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px',
    margin: 0,
    padding: 0,
    listStyle: 'none'
  }}>
      <li>
        <Message>
          <MessageIcon name={ICON_NAME.circleCheck} />

          <MessageBody>
            Your changes have been saved.
          </MessageBody>
        </Message>
      </li>

      <li>
        <Message color={MESSAGE_COLOR.warning}>
          <MessageIcon name={ICON_NAME.triangleExclamation} />

          <MessageBody>
            Some fields need your attention.
          </MessageBody>
        </Message>
      </li>
    </ul>
}
```

### Accessibility Roles

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { BUTTON_COLOR, ICON_NAME, MESSAGE_COLOR, Button, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [alerts, setAlerts] = useState<string[]>([]);
    const [statuses, setStatuses] = useState<string[]>([]);
    return <>
        <div>
          <Button onClick={() => setStatuses(s => s.concat([new Date().toString()]))}>
            Add status
          </Button>

          <Button color={BUTTON_COLOR.critical} onClick={() => setAlerts(a => a.concat([new Date().toString()]))}>
            Add alert
          </Button>
        </div>

        <div role="alert">
          {alerts.map(alert => <Message color={MESSAGE_COLOR.critical} key={alert}>
                <MessageIcon name={ICON_NAME.hexagonExclamation} />

                <MessageBody>
                  Alert: {alert}
                </MessageBody>
              </Message>)}
        </div>

        <div role="status">
          {statuses.map(status => <Message key={status}>
                <MessageIcon name={ICON_NAME.circleInfo} />

                <MessageBody>
                  Status: {status}
                </MessageBody>
              </Message>)}
        </div>
      </>;
  }
}
```

### Color

```tsx
{
  decorators: [story => <div style={{
    display: 'inline-flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, MESSAGE_COLOR, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Message color={MESSAGE_COLOR.critical}>
        <MessageIcon name={ICON_NAME.hexagonExclamation} />

        <MessageBody>Critical message</MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.information}>
        <MessageIcon name={ICON_NAME.circleInfo} />

        <MessageBody>Information message</MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.neutral}>
        <MessageIcon name={ICON_NAME.email} />

        <MessageBody>Neutral message</MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.primary}>
        <MessageIcon name={ICON_NAME.lightbulb} />

        <MessageBody>Primary message</MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.success}>
        <MessageIcon name={ICON_NAME.circleCheck} />

        <MessageBody>Success message</MessageBody>
      </Message>

      <Message color={MESSAGE_COLOR.warning}>
        <MessageIcon name={ICON_NAME.triangleExclamation} />

        <MessageBody>Warning message</MessageBody>
      </Message>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Message>
      <MessageIcon name={ICON_NAME.circleInfo} />

      <MessageBody>
        Default message
      </MessageBody>
    </Message>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Message color={arg.color} dismissible={arg.dismissible} variant={arg.variant}>
      <MessageIcon name={arg.name || ICON_NAME.circleInfo} />

      <MessageBody>
        {arg.children}
      </MessageBody>
    </Message>,
  argTypes: orderControls({
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'MESSAGE_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: MESSAGE_COLORS
    },
    dismissible: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    name: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'ICON_NAME'
        }
      },
      control: {
        type: 'select'
      },
      options: ICON_NAMES
    },
    variant: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'MESSAGE_VARIANT'
        }
      },
      control: {
        type: 'select'
      },
      options: MESSAGE_VARIANTS
    }
  }),
  args: {
    children: 'My message'
  }
}
```

### Multiline

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Message>
      <MessageIcon name={ICON_NAME.circleInfo} />

      <MessageBody>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer faucibus, libero et pharetra mattis, ipsum velit semper risus, non ultrices lacus massa sed arcu. Nulla sed tellus.
      </MessageBody>
    </Message>
}
```

### Non Dismissible

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Message dismissible={false}>
      <MessageIcon name={ICON_NAME.circleInfo} />

      <MessageBody>
        Default message
      </MessageBody>
    </Message>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Message>
      <MessageIcon name={ICON_NAME.circleInfo} />

      <MessageBody>
        Message
      </MessageBody>
    </Message>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'inline-flex',
    flexFlow: 'column',
    gap: '8px'
  }}>
      <Message>
        <MessageIcon name={ICON_NAME.circleInfo} />
        <MessageBody>Default message</MessageBody>
      </Message>
      <Message color={MESSAGE_COLOR.information}>
        <MessageIcon name={ICON_NAME.circleInfo} />
        <MessageBody>Information</MessageBody>
      </Message>
      <Message color={MESSAGE_COLOR.success}>
        <MessageIcon name={ICON_NAME.circleCheck} />
        <MessageBody>Success</MessageBody>
      </Message>
      <Message color={MESSAGE_COLOR.warning}>
        <MessageIcon name={ICON_NAME.triangleExclamation} />
        <MessageBody>Warning</MessageBody>
      </Message>
      <Message color={MESSAGE_COLOR.critical}>
        <MessageIcon name={ICON_NAME.hexagonExclamation} />
        <MessageBody>Critical</MessageBody>
      </Message>
      <Message variant={MESSAGE_VARIANT.light}>
        <MessageIcon name={ICON_NAME.circleInfo} />
        <MessageBody>Light variant</MessageBody>
      </Message>
    </div>
}
```

### Variant

```tsx
{
  decorators: [story => <div style={{
    display: 'inline-flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, MESSAGE_VARIANT, Message, MessageBody, MessageIcon } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Message variant={MESSAGE_VARIANT.default}>
        <MessageIcon name={ICON_NAME.circleInfo} />

        <MessageBody>
          Default variant Message
        </MessageBody>
      </Message>

      <Message variant={MESSAGE_VARIANT.light}>
        <MessageIcon name={ICON_NAME.circleInfo} />

        <MessageBody>
          Light variant Message
        </MessageBody>
      </Message>
    </>
}
```

## React Components/Meter

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `high` | `` | No |  | The lower numeric bound of the high end of the measured range. |
| `low` | `` | No |  | The upper numeric bound of the low end of the measured range. |
| `max` | `` | No | 100 | The upper numeric bound of the measured range. |
| `min` | `` | No | 0 | The lower numeric bound of the measured range. |
| `optimum` | `` | No |  | The optimal numeric value. Combined with low and high, it will changes the coloring behaviour. |
| `value` | `` | No | 0 | The current value of the meter |


## Examples


### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Meter } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Meter aria-label="Gauge" low={40} value={35} />
}
```

### Accessibility Aria Labelledby

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Meter, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Text id="meter-label" preset={TEXT_PRESET.label}>
        Gauge:
      </Text>

      <Meter aria-labelledby="meter-label" low={40} value={35} />
    </>
}
```

### Accessibility Aria Valuetext

```tsx
{
  globals: {
    imports: `import { Meter } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Meter aria-label="Gauge" aria-valuetext="35 files uploaded" low={40} value={35} />
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Meter } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Meter />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    high: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    low: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    max: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    min: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    optimum: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    value: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    }
  })
}
```

### Optimum

```tsx
{
  globals: {
    imports: `import { Meter } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <p>Low optimum and low value:</p>
      <Meter high={80} low={40} optimum={30} value={20} />

      <p>Low optimum and high value:</p>
      <Meter high={80} low={40} optimum={30} value={60} />

      <p>Low optimum and very high value:</p>
      <Meter high={80} low={40} optimum={30} value={90} />
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => <Meter low={40} value={35} />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <Meter />
      <Meter low={40} value={35} />
      <Meter high={80} low={40} value={60} />
      <Meter high={80} value={90} />
      <Meter optimum={30} value={20} />
    </div>
}
```

### Thresholds

```tsx
{
  globals: {
    imports: `import { Meter } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <p>Value under low threshold:</p>
      <Meter low={40} value={35} />

      <p>Value between both thresholds:</p>
      <Meter high={80} low={40} value={60} />

      <p>Value above high threshold:</p>
      <Meter high={80} value={90} />
    </>
}
```

## React Components/Modal

## Subcomponents


### ModalBody




### ModalContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | MODAL_COLOR.information | @type=MODAL_COLOR The color preset to use. |
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `dismissible` | `` | No | true | Whether the remove button is displayed. |



### ModalTrigger



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `asChild` | `` | No |  | Use the provided child element as the default rendered element, combining their props and behavior. Be careful to pass an actual Node, not a Fragment. |


## Examples


### Accessibility Actions

```tsx
{
  globals: {
    imports: `import { BUTTON_COLOR, BUTTON_VARIANT, MODAL_COLOR, Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent color={MODAL_COLOR.critical}>
        <ModalBody>
          <h2 id="modal-title">
            Delete item
          </h2>

          <p id="modal-content">
            Are you sure you want to delete this item?
          </p>

          <div style={{
          display: 'flex',
          gap: '8px',
          justifyContent: 'end'
        }}>
            <Button variant={BUTTON_VARIANT.ghost}>
              Cancel
            </Button>

            <Button color={BUTTON_COLOR.critical}>
              Delete
            </Button>
          </div>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent aria-describedby="modal-content" aria-label="Modal Content">
        <ModalBody id="modal-content">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Accessibility Aria Labelled By

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent aria-describedby="modal-content" aria-labelledby="modal-title">
        <ModalBody>
          <h2 id="modal-title">
            Delete item
          </h2>

          <p id="modal-content">
            Are you sure you want to delete this item? This action cannot be undone.
          </p>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Accessibility Bad Practices Aria

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent>
        <ModalBody>
          <h2 id="modal-title">
            Delete item
          </h2>

          <p id="modal-content">
            Are you sure you want to delete this item? This action cannot be undone.
          </p>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Actions

```tsx
{
  globals: {
    imports: `import { BUTTON_VARIANT, TEXT_PRESET, Button, Modal, ModalBody, ModalContent, ModalTrigger, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent>
        <ModalBody style={{
        display: 'flex',
        flexDirection: 'column',
        rowGap: '16px'
      }}>
          <Text preset={TEXT_PRESET.heading4}>
            Hosting removal
          </Text>

          <Text>
            You're about to remove the hosting "1 vCore 2,4 GHz, 2 Go RAM".
          </Text>

          <div style={{
          display: 'flex',
          alignSelf: 'flex-end',
          columnGap: '8px'
        }}>
            <Button>
              Confirm
            </Button>

            <Button variant={BUTTON_VARIANT.outline}>
              Cancel
            </Button>
          </div>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Colors

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { BUTTON_COLOR, MODAL_COLOR, Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.critical}>
            Critical
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.critical}>
          <ModalBody>
            Critical
          </ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.information}>
            Information
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.information}>
          <ModalBody>
            Information
          </ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.neutral}>
            Neutral
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.neutral}>
          <ModalBody>
            Neutral
          </ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.primary}>
            Primary
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.primary}>
          <ModalBody>
            Primary
          </ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.success}>
            Success
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.success}>
          <ModalBody>
            Success
          </ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.warning}>
            Warning
          </Button>
        </ModalTrigger>

        <ModalContent color={MODAL_COLOR.warning}>
          <ModalBody>
            Warning
          </ModalBody>
        </ModalContent>
      </Modal>
    </>
}
```

### Controlled

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [isOpen, setIsOpen] = useState(false);
    function onOpenChange({
      open
    }: ModalOpenChangeDetail) {
      setIsOpen(open);
    }
    function openModal() {
      setIsOpen(true);
    }
    return <>
        <Button onClick={openModal}>
          Trigger Modal
        </Button>

        <Modal onOpenChange={onOpenChange} open={isOpen}>
          <ModalContent>
            <ModalBody>
              Content
            </ModalBody>
          </ModalContent>
        </Modal>
      </>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger>
        Trigger Modal
      </ModalTrigger>

      <ModalContent>
        <ModalBody>
          My modal content
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Modal closeOnEscape={arg.closeOnEscape} closeOnInteractOutside={arg.closeOnInteractOutside}>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent color={arg.color} dismissible={arg.dismissible}>
        <ModalBody>
          {arg.content}
        </ModalBody>
      </ModalContent>
    </Modal>,
  argTypes: orderControls({
    closeOnEscape: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    closeOnInteractOutside: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        defaultValue: {
          summary: MODAL_COLOR.information
        },
        type: {
          summary: 'MODAL_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: MODAL_COLORS
    },
    content: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    dismissible: {
      table: {
        category: CONTROL_CATEGORY.general,
        defaultValue: {
          summary: true
        },
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    }
  }),
  args: {
    content: 'My modal content'
  }
}
```

### Non Dismissible

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent dismissible={false}>
        <ModalBody>
          My modal content
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Non Escapable

```tsx
{
  globals: {
    imports: `import { Button, Modal, ModalBody, ModalContent, ModalTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <Modal closeOnEscape={false} closeOnInteractOutside={false}>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent>
        <ModalBody>
          My modal content
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Overlay Elements

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Icon, Modal, ModalBody, ModalContent, ModalTrigger, Select, SelectContent, SelectControl, Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent>
        <ModalBody style={{
        display: 'grid',
        columnGap: '8px',
        alignItems: 'center',
        gridTemplateColumns: '1fr max-content'
      }}>
          <Select items={[{
          label: 'Dog',
          value: 'dog'
        }, {
          label: 'Cat',
          value: 'cat'
        }, {
          label: 'Hamster',
          value: 'hamster'
        }, {
          label: 'Parrot',
          value: 'parrot'
        }, {
          label: 'Spider',
          value: 'spider'
        }, {
          label: 'Goldfish',
          value: 'goldfish'
        }]}>
            <SelectControl />

            <SelectContent createPortal={false} />
          </Select>

          <Tooltip>
            <TooltipTrigger asChild>
              <Icon name={ICON_NAME.circleQuestion} style={{
              fontSize: '24px'
            }} />
            </TooltipTrigger>

            <TooltipContent createPortal={false}>
              This is the tooltip content
            </TooltipContent>
          </Tooltip>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Modal>
      <ModalTrigger asChild>
        <Button variant={BUTTON_VARIANT.outline}>
          Trigger Modal
        </Button>
      </ModalTrigger>

      <ModalContent>
        <ModalBody>
          <Text preset={TEXT_PRESET.heading4}>
            Overview
          </Text>

          <Text>
            Lorem ipsum dolor sit amet, consectetur adipiscing elit sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
          </Text>
        </ModalBody>
      </ModalContent>
    </Modal>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '12px'
  }}>
      <Modal>
        <ModalTrigger asChild>
          <Button>Default</Button>
        </ModalTrigger>
        <ModalContent createPortal={false}>
          <ModalBody>Default</ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button>Non dismissible</Button>
        </ModalTrigger>
        <ModalContent createPortal={false} dismissible={false}>
          <ModalBody>Non dismissible</ModalBody>
        </ModalContent>
      </Modal>

      <Modal>
        <ModalTrigger asChild>
          <Button color={BUTTON_COLOR.critical}>Critical</Button>
        </ModalTrigger>
        <ModalContent color={MODAL_COLOR.critical} createPortal={false}>
          <ModalBody>Critical</ModalBody>
        </ModalContent>
      </Modal>
    </div>
}
```

## React Components/Pagination

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultPage` | `` | No |  | The initial active page. Use when you don't need to control the active page of the pagination. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `labelTooltipNext` | `` | No |  | The tooltip label on the "next page" button. |
| `labelTooltipPrev` | `` | No |  | The tooltip label on the "previous page" button. |
| `onPageChange` | `` | No |  | Callback fired when the active page changes. |
| `onPageSizeChange` | `` | No |  | Callback fired when the page size changes. |
| `page` | `` | No |  | The controlled active page |
| `pageSize` | `` | No | 10 | The number of items per page. |
| `renderTotalItemsLabel` | `` | No | ({ totalItems }) => `of ${totalItems} results` | Format the label displayed near the per-page selector. |
| `siblingCount` | `` | No |  | The number of pages to show beside active page. |
| `totalItems` | `` | Yes |  | The total number of items. |
| `withPageSizeSelector` | `` | No |  | Whether the per-page selector is displayed. |


## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination aria-label="Pagination" totalItems={5000} />
}
```

### Controlled

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [page, setPage] = useState(1);
    function handlePageChange({
      page
    }: PaginationPageChangeDetail) {
      setPage(page);
    }
    return <Pagination onPageChange={handlePageChange} page={page} totalItems={500} />;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination totalItems={5000} />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    labelTooltipNext: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    labelTooltipPrev: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    pageSize: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    page: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    siblingCount: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    totalItems: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    },
    withPageSizeSelector: {
      table: {
        category: CONTROL_CATEGORY.general
      }
    }
  }),
  args: {
    totalItems: 5000
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination disabled totalItems={500} />
}
```

### Items Per Page

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination pageSize={25} totalItems={500} />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Pagination totalItems={100} withPageSizeSelector />
}
```

### Sibling Count

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination defaultPage={5} siblingCount={2} totalItems={500} />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Pagination totalItems={100} />
      <Pagination totalItems={500} pageSize={25} />
      <Pagination totalItems={500} disabled />
      <Pagination totalItems={100} withPageSizeSelector />
    </div>
}
```

### Total Items

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <Pagination renderTotalItemsLabel={({
    totalItems
  }) => `of ${totalItems} results`} totalItems={500} withPageSizeSelector />
}
```

### With Labels

```tsx
{
  globals: {
    imports: `import { Pagination } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Pagination labelTooltipPrev={'Go to previous page'} labelTooltipNext={'Go to next page'} totalItems={500} />
}
```

## React Components/Password

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clearable` | `` | No |  | Whether the clear button is displayed. |
| `i18n` | `` | No |  | Internal translations override (see Input i18n keys). |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `loading` | `` | No |  | Whether the component is in loading state. |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `maskInitialState` | `` | No |  | The masked display initial state. |
| `onClear` | `` | No |  | Callback fired when the input value is cleared. |


## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Password:
      </FormFieldLabel>

      <Password />
    </FormField>
}
```

### Accessibility I 18 N

```tsx
{
  globals: {
    imports: `import { INPUT_I18N, FormField, FormFieldLabel, Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <FormField>
      <FormFieldLabel>
        Password:
      </FormFieldLabel>

      <Password i18n={{
      [INPUT_I18N.maskButtonHide]: 'Hide the password',
      [INPUT_I18N.maskButtonShow]: 'Show the password'
    }} />
    </FormField>
}
```

### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Password:
      </FormFieldLabel>

      <Password />
    </FormField>
}
```

### Clearable

```tsx
{
  globals: {
    imports: `import { Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Password clearable defaultValue="Clearable" />
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Password />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    clearable: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    loading: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Password disabled />
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Password:
      </FormFieldLabel>

      <Password />
    </FormField>
}
```

### Loading

```tsx
{
  globals: {
    imports: `import { Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Password loading />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Password />
}
```

### Read Only

```tsx
{
  globals: {
    imports: `import { Password } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Password defaultValue="Readonly" readOnly />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Password placeholder="Default" />
      <Password clearable defaultValue="Clearable" />
      <Password loading placeholder="Loading" />
      <Password disabled placeholder="Disabled" />
      <Password invalid placeholder="Invalid" />
      <Password readOnly defaultValue="Read only" />
    </div>
}
```

## React Components/Phone Number

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `countries` | `` | No |  | A specific or preset list of country to display in the selector. |
| `country` | `` | No |  | The controlled selected country. |
| `defaultValue` | `` | No |  | The initial phone number value. Use when you don't need to control the value of the phone number. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `i18n` | `` | No |  | Internal translations override. |
| `id` | `` | No |  | The field id. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `locale` | `` | No |  | The locale used for the translation of the country list and the internal elements. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onCountryChange` | `` | No |  | Callback fired when the country changes. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `pattern` | `` | No |  | The phone number input expected pattern. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. |
| `value` | `` | No |  | The controlled phone number value. |


## Subcomponents


### PhoneNumberControl



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `clearable` | `` | No |  | Whether the clear button is displayed. |
| `loading` | `` | No |  | Whether the component is in loading state. |



### PhoneNumberCountryList



## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Phone number:
      </FormFieldLabel>

      <PhoneNumber>
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>
    </FormField>
}
```

### Accessibility I 18 N

```tsx
{
  globals: {
    imports: `import { INPUT_I18N, FormField, FormFieldLabel, PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <FormField>
      <FormFieldLabel>
        Phone number:
      </FormFieldLabel>

      <PhoneNumber country="fr" defaultValue="06 01 02 03 04" i18n={{
      [INPUT_I18N.clearButton]: 'Clear phone number'
    }}>
        <PhoneNumberCountryList />

        <PhoneNumberControl clearable />
      </PhoneNumber>
    </FormField>
}
```

### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Phone number:
      </FormFieldLabel>

      <PhoneNumber>
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>
    </FormField>
}
```

### Clearable

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <PhoneNumber>
        <PhoneNumberControl clearable />
      </PhoneNumber>

      <PhoneNumber>
        <PhoneNumberCountryList />

        <PhoneNumberControl clearable />
      </PhoneNumber>
    </>
}
```

### Country List

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <span>All countries</span>

      <PhoneNumber>
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>

      <span>Subset of countries</span>

      <PhoneNumber countries={['de', 'fr', 'gb', 'it']}>
        <PhoneNumberCountryList />

        <PhoneNumberControl clearable />
      </PhoneNumber>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <PhoneNumber>
      <PhoneNumberControl />
    </PhoneNumber>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <PhoneNumber country={arg.country} disabled={arg.disabled} invalid={arg.invalid} locale={arg.locale} readOnly={arg.readOnly}>
      {arg.withCountries && <PhoneNumberCountryList />}

      <PhoneNumberControl clearable={arg.clearable} />
    </PhoneNumber>,
  argTypes: orderControls({
    clearable: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    country: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'PHONE_NUMBER_COUNTRY_ISO_CODE'
        }
      },
      control: {
        type: 'select'
      },
      options: PHONE_NUMBER_COUNTRY_ISO_CODES
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    locale: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'select'
      },
      options: PHONE_NUMBER_COUNTRY_ISO_CODES
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    withCountries: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <PhoneNumber disabled>
        <PhoneNumberControl />
      </PhoneNumber>

      <PhoneNumber disabled>
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>
    </>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, PhoneNumber, PhoneNumberControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Phone number:
      </FormFieldLabel>

      <PhoneNumber>
        <PhoneNumberControl />
      </PhoneNumber>
    </FormField>
}
```

### Loading

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <PhoneNumber>
        <PhoneNumberControl loading />
      </PhoneNumber>

      <PhoneNumber>
        <PhoneNumberCountryList />

        <PhoneNumberControl loading />
      </PhoneNumber>
    </>
}
```

### Locale

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <span>Locale "fr"</span>
      <PhoneNumber locale="fr">
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>

      <span>Locale "de"</span>
      <PhoneNumber locale="de">
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <PhoneNumber>
      <PhoneNumberCountryList />

      <PhoneNumberControl />
    </PhoneNumber>
}
```

### Readonly

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { PhoneNumber, PhoneNumberControl, PhoneNumberCountryList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <PhoneNumber readOnly>
        <PhoneNumberControl />
      </PhoneNumber>

      <PhoneNumber readOnly>
        <PhoneNumberCountryList />

        <PhoneNumberControl />
      </PhoneNumber>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <PhoneNumber>
        <PhoneNumberControl />
      </PhoneNumber>

      <PhoneNumber>
        <PhoneNumberCountryList />
        <PhoneNumberControl />
      </PhoneNumber>

      <PhoneNumber>
        <PhoneNumberControl loading />
      </PhoneNumber>

      <PhoneNumber disabled>
        <PhoneNumberCountryList />
        <PhoneNumberControl />
      </PhoneNumber>
    </div>
}
```

## React Components/Popover

## Subcomponents


### PopoverContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `withArrow` | `` | No | false | Whether the component displays an arrow pointing to the trigger. |



### PopoverTrigger



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `asChild` | `` | No |  | Use the provided child element as the default rendered element, combining their props and behavior. Be careful to pass an actual Node, not a Fragment. |


## Examples


### Accessibility With Menu

```tsx
{
  globals: {
    imports: `import { BUTTON_COLOR, BUTTON_VARIANT, ICON_NAME, Button, Divider, Icon, Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Popover>
      <PopoverTrigger aria-haspopup="menu" asChild>
        <Button>
          <Icon name={ICON_NAME.ellipsisVertical} />
        </Button>
      </PopoverTrigger>

      <PopoverContent aria-label="Profile menu" withArrow>
        <div role="menu" style={{
        display: 'flex',
        flexDirection: 'column'
      }}>
          <Button role="menuitem" variant={BUTTON_VARIANT.ghost}>
            Information
          </Button>

          <Button role="menuitem" variant={BUTTON_VARIANT.ghost}>
            Notifications
          </Button>

          <Divider style={{
          width: '100%'
        }} />

          <Button color={BUTTON_COLOR.critical} role="menuitem" variant={BUTTON_VARIANT.ghost}>
            Sign out
          </Button>
        </div>
      </PopoverContent>
    </Popover>
}
```

### Controlled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon, Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [isOpen, setIsOpen] = useState(false);
    function togglePopover() {
      setIsOpen(isOpen => !isOpen);
    }
    return <>
        <Button onClick={togglePopover}>
          Toggle popover
        </Button>

        <Popover open={isOpen}>
          <PopoverTrigger asChild>
            <Icon name={ICON_NAME.cog} />
          </PopoverTrigger>

          <PopoverContent withArrow>
            This is the popover content
          </PopoverContent>
        </Popover>
      </>;
  }
}
```

### Custom Trigger

```tsx
{
  globals: {
    imports: `import { Button, Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Popover>
      <PopoverTrigger asChild>
        <Button>
          Custom Trigger
        </Button>
      </PopoverTrigger>

      <PopoverContent>
        This is the popover content
      </PopoverContent>
    </Popover>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Popover>
      <PopoverTrigger>
        Show popover
      </PopoverTrigger>

      <PopoverContent>
        This is the popover content
      </PopoverContent>
    </Popover>
}
```

### Demo

```tsx
{
  parameters: {
    layout: 'centered'
  },
  render: (arg: DemoArg) => <Popover gutter={arg.gutter} position={arg.position} sameWidth={arg.sameWidth}>
      <PopoverTrigger>
        Show popover
      </PopoverTrigger>

      <PopoverContent withArrow={arg.withArrow}>
        {arg.content}
      </PopoverContent>
    </Popover>,
  argTypes: orderControls({
    content: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    gutter: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    },
    position: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'POPOVER_POSITION'
        }
      },
      control: {
        type: 'select'
      },
      options: POPOVER_POSITIONS
    },
    sameWidth: {
      table: {
        category: CONTROL_CATEGORY.design
      },
      control: {
        type: 'boolean'
      }
    },
    withArrow: {
      table: {
        category: CONTROL_CATEGORY.design,
        defaultValue: {
          summary: false
        },
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    }
  }),
  args: {
    content: 'My popover content'
  }
}
```

### Grid

```tsx
{
  decorators: [story => <div style={{
    display: 'grid',
    gridTemplateColumns: 'repeat(3, 1fr)',
    gridTemplateRows: 'repeat(3, 1fr)',
    gap: '20px',
    padding: '50px 150px'
  }}>
      {story()}
    </div>],
  globals: {
    imports: `import { Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Popover position="top-start">
        <PopoverTrigger>
          Top Left
        </PopoverTrigger>
        <PopoverContent withArrow>
          Top Left popover
        </PopoverContent>
      </Popover>

      <Popover position="top">
        <PopoverTrigger>
          Top
        </PopoverTrigger>
        <PopoverContent withArrow>
          Top popover
        </PopoverContent>
      </Popover>

      <Popover position="top-end">
        <PopoverTrigger>
          Top Right
        </PopoverTrigger>
        <PopoverContent withArrow>
          Top Right popover
        </PopoverContent>
      </Popover>

      <Popover position="left">
        <PopoverTrigger>
          Middle Left
        </PopoverTrigger>
        <PopoverContent withArrow>
          Middle Left popover
        </PopoverContent>
      </Popover>

      <div />

      <Popover position="right">
        <PopoverTrigger>
          Middle Right
        </PopoverTrigger>
        <PopoverContent withArrow>
          Middle Right popover
        </PopoverContent>
      </Popover>

      <Popover position="bottom-start">
        <PopoverTrigger>
          Bottom Left
        </PopoverTrigger>
        <PopoverContent withArrow>
          Bottom Left popover
        </PopoverContent>
      </Popover>

      <Popover position="bottom">
        <PopoverTrigger>
          Bottom
        </PopoverTrigger>
        <PopoverContent withArrow>
          Bottom popover
        </PopoverContent>
      </Popover>

      <Popover position="bottom-end">
        <PopoverTrigger>
          Bottom Right
        </PopoverTrigger>
        <PopoverContent withArrow>
          Bottom Right popover
        </PopoverContent>
      </Popover>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Popover>
      <PopoverTrigger asChild>
        <Button>
          Show popover
        </Button>
      </PopoverTrigger>

      <PopoverContent>
        This is the popover content
      </PopoverContent>
    </Popover>
}
```

### Same Width

```tsx
{
  globals: {
    imports: `import { Popover, PopoverContent, PopoverTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Popover sameWidth>
      <PopoverTrigger>
        Show popover that will take this width as reference
      </PopoverTrigger>

      <PopoverContent>
        The popover content
      </PopoverContent>
    </Popover>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'row wrap',
    gap: '12px'
  }}>
      <Popover>
        <PopoverTrigger asChild>
          <Button>Default</Button>
        </PopoverTrigger>
        <PopoverContent createPortal={false}>This is the popover content</PopoverContent>
      </Popover>

      <Popover>
        <PopoverTrigger asChild>
          <Button>With Arrow</Button>
        </PopoverTrigger>
        <PopoverContent createPortal={false} withArrow>This is the popover content</PopoverContent>
      </Popover>
    </div>
}
```

## React Components/Progress Bar

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `max` | `` | No | 100 | The maximum value of the progress bar. |
| `value` | `` | No | 0 | The current value of the progress bar |


## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { ProgressBar } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ProgressBar aria-label="Converting" />
}
```

### Default

```tsx
{
  globals: {
    imports: `import { ProgressBar } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ProgressBar />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    max: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    },
    value: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    }
  })
}
```

### Max

```tsx
{
  globals: {
    imports: `import { ProgressBar } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ProgressBar max="500" value="50" />
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => <ProgressBar value="50" />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    width: '300px'
  }}>
      <ProgressBar />
      <ProgressBar value="25" />
      <ProgressBar value="50" />
      <ProgressBar value="75" />
      <ProgressBar max="500" value="50" />
    </div>
}
```

### Value

```tsx
{
  globals: {
    imports: `import { ProgressBar } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ProgressBar value="50" />
}
```

## React Components/Quantity

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial quantity value. Use when you don't need to control the value of the quantity. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `max` | `` | No |  | The maximum quantity that can be selected. |
| `min` | `` | No |  | The minimum quantity that can be selected. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. |
| `step` | `` | No |  | The amount to increment or decrement the value by. |
| `value` | `` | No |  | The controlled quantity value. |


## Subcomponents


### QuantityControl




### QuantityInput



## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Number of CPUs:
      </FormFieldLabel>

      <Quantity max={10} min={0}>
        <QuantityControl>
          <QuantityInput />
        </QuantityControl>
      </Quantity>
    </FormField>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Quantity disabled={arg.disabled} invalid={arg.invalid} max={arg.max} min={arg.min} readOnly={arg.readOnly} step={arg.step}>
      <QuantityControl>
        <QuantityInput placeholder={arg.placeholder} />
      </QuantityControl>
    </Quantity>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    max: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'number'
      }
    },
    min: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'number'
      }
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    step: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'number'
      }
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity disabled>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Set a quantity:
      </FormFieldLabel>

      <Quantity>
        <QuantityControl>
          <QuantityInput />
        </QuantityControl>
      </Quantity>
    </FormField>
}
```

### Max

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity max={10}>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Min

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity min={0}>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Quantity defaultValue="0" min={0}>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Readonly

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity readOnly>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Step

```tsx
{
  globals: {
    imports: `import { Quantity, QuantityControl, QuantityInput } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Quantity step={10}>
      <QuantityControl>
        <QuantityInput />
      </QuantityControl>
    </Quantity>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Quantity>
        <QuantityControl>
          <QuantityInput />
        </QuantityControl>
      </Quantity>

      <Quantity disabled>
        <QuantityControl>
          <QuantityInput />
        </QuantityControl>
      </Quantity>

      <Quantity readOnly>
        <QuantityControl>
          <QuantityInput defaultValue="3" />
        </QuantityControl>
      </Quantity>

      <Quantity invalid>
        <QuantityControl>
          <QuantityInput />
        </QuantityControl>
      </Quantity>
    </div>
}
```

## React Components/Radio Group

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial value of the checked radio. Use when you don't need to control the value of the radio group. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `orientation` | `` | No |  | The orientation of the radio group. |
| `value` | `` | No |  | The controlled value of the radio group. |


## Subcomponents


### Radio



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `disabled` | `` | No |  | Whether the radio is disabled. |
| `invalid` | `` | No |  | Whether the radio is in error state. |
| `required` | `` | No |  | Whether the radio is required. |
| `value` | `` | Yes |  | The value of the radio. |



### RadioControl




### RadioLabel



## Examples


### Default

```tsx
{
  globals: {
    imports: `import { Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <RadioGroup>
      <Radio value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <RadioGroup disabled={arg.disabled} orientation={arg.orientation}>
      <Radio invalid={arg.invalid} value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio invalid={arg.invalid} value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio invalid={arg.invalid} value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    orientation: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: ['horizontal', 'vertical']
        }
      },
      control: {
        type: 'select'
      },
      options: ['horizontal', 'vertical']
    }
  })
}
```

### Disabled Group

```tsx
{
  globals: {
    imports: `import { Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <RadioGroup disabled>
      <Radio value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### Disabled Item

```tsx
{
  globals: {
    imports: `import { Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <RadioGroup>
      <Radio value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio disabled value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Pick a language:
      </FormFieldLabel>

      <RadioGroup>
        <Radio value="html">
          <RadioControl />

          <RadioLabel>HTML</RadioLabel>
        </Radio>

        <Radio value="css">
          <RadioControl />

          <RadioLabel>CSS</RadioLabel>
        </Radio>

        <Radio value="js">
          <RadioControl />

          <RadioLabel>JavaScript</RadioLabel>
        </Radio>
      </RadioGroup>
    </FormField>
}
```

### Invalid

```tsx
{
  globals: {
    imports: `import { Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <RadioGroup defaultValue="html">
      <Radio value="html" invalid>
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio invalid value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### Orientation

```tsx
{
  globals: {
    imports: `import { Radio, RadioControl, RadioGroup, RadioLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <RadioGroup orientation="horizontal">
      <Radio value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <RadioGroup>
      <Radio value="html">
        <RadioControl />

        <RadioLabel>HTML</RadioLabel>
      </Radio>

      <Radio value="css">
        <RadioControl />

        <RadioLabel>CSS</RadioLabel>
      </Radio>

      <Radio value="js">
        <RadioControl />

        <RadioLabel>JavaScript</RadioLabel>
      </Radio>
    </RadioGroup>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    gap: '24px'
  }}>
      <RadioGroup>
        <Radio value="html"><RadioControl /><RadioLabel>HTML</RadioLabel></Radio>
        <Radio value="css"><RadioControl /><RadioLabel>CSS</RadioLabel></Radio>
        <Radio value="js"><RadioControl /><RadioLabel>JavaScript</RadioLabel></Radio>
      </RadioGroup>

      <RadioGroup disabled>
        <Radio value="html"><RadioControl /><RadioLabel>HTML</RadioLabel></Radio>
        <Radio value="css"><RadioControl /><RadioLabel>CSS</RadioLabel></Radio>
        <Radio value="js"><RadioControl /><RadioLabel>JavaScript</RadioLabel></Radio>
      </RadioGroup>

      <RadioGroup>
        <Radio disabled value="html"><RadioControl /><RadioLabel>HTML</RadioLabel></Radio>
        <Radio value="css"><RadioControl /><RadioLabel>CSS</RadioLabel></Radio>
        <Radio value="js"><RadioControl /><RadioLabel>JavaScript</RadioLabel></Radio>
      </RadioGroup>
    </div>
}
```

## React Components/Range

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `aria-label` | `` | No |  | The aria-label of each slider thumb. Useful for providing an accessible name to the slider. |
| `aria-labelledby` | `` | No |  | The id of the elements that labels each slider thumb. Useful for providing an accessible name to the slider. |
| `defaultValue` | `` | No |  | The initial selected value(s). Use when you don't need to control the value(s) of the range. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `displayBounds` | `` | No |  | Whether the range bounds are displayed under the track. |
| `displayTooltip` | `` | No |  | Whether a tooltip with the current thumb value is displayed on drag. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `max` | `` | No |  | The maximum value that can be selected. |
| `min` | `` | No |  | The minimum value that can be selected. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onDragging` | `` | No |  | Callback fired when the thumb moves. |
| `onValueChange` | `` | No |  | Callback fired when the thumb is released. |
| `step` | `` | No |  | The amount to increment or decrement the value by. |
| `ticks` | `` | No |  | List of tick indicators to display alongside the range. |
| `value` | `` | No |  | The controlled selected value(s). |


## Examples


### Accessibility Descriptive Sub Label

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, FormField, FormFieldLabel, Range, Text } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const [values, setValues] = useState([30, 70]);
    return <FormField>
        <FormFieldLabel id="range-label">
          Price range
        </FormFieldLabel>

        <Text aria-live="polite" id="range-sublabel" preset={TEXT_PRESET.caption}>
          Selected values: {values[0]} - {values[1]}€
        </Text>

        <Range aria-labelledby={['range-label', 'range-sublabel']} onDragging={({
        value
      }) => setValues(value)} value={values} />
      </FormField>;
  }
}
```

### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Volume
      </FormFieldLabel>

      <Range defaultValue={[50]} />
    </FormField>
}
```

### Controlled

```tsx
{
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [draggingValue, setDraggingValue] = useState<number>();
    const [value, setValue] = useState<number>();
    function onDragging(detail: RangeValueChangeDetail) {
      setDraggingValue(detail.value[0]);
    }
    function onValueChange(detail: RangeValueChangeDetail) {
      setValue(detail.value[0]);
    }
    return <>
        <p>
          <span>Final value: {value}</span>
          <br />
          <span>Dragged value: {draggingValue}</span>
        </p>

        <Range onDragging={onDragging} onValueChange={onValueChange} value={draggingValue ? [draggingValue] : undefined} />
      </>;
  }
}
```

### Default

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Range />

      <Range defaultValue={[50, 75]} />
    </>
}
```

### Demo

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    justifyContent: 'center',
    height: '80vh'
  }}>{story()}</div>],
  render: ({
    dualRange,
    ...arg
  }: DemoArg) => {
    const MAX_VALUE = 100;
    const [values, setValues] = useState([0]);
    useEffect(() => {
      if (dualRange) {
        const step = arg.step || 1;
        const newValue = values[0] === MAX_VALUE ? values[0] - step : values[0];
        setValues([newValue, newValue + step]);
      } else {
        setValues([values[0]]);
      }
    }, [dualRange]);
    return <Range {...arg} max={MAX_VALUE} onDragging={({
      value
    }) => setValues(value)} value={values} />;
  },
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    displayBounds: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    displayTooltip: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    dualRange: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    step: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    }
  })
}
```

### Disabled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Range defaultValue={[20]} disabled />

      <Range defaultValue={[50, 75]} disabled />
    </>
}
```

### In Form Field

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { FormField, FormFieldLabel, Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Range:
      </FormFieldLabel>

      <Range />
    </FormField>
}
```

### Max Min

```tsx
{
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <p>Max 500</p>
      <Range defaultValue={[50]} max={500} />
      <Range defaultValue={[50, 75]} max={500} />

      <p>Min 25</p>
      <Range defaultValue={[50]} min={25} />
      <Range defaultValue={[50, 75]} min={25} />

      <p>Max 75 & Min 25</p>
      <Range defaultValue={[50]} max={75} min={25} />
      <Range defaultValue={[50, 75]} max={75} min={25} />
    </>
}
```

### Overview

```tsx
{
  decorators: [story => <div style={{
    width: '160px'
  }}>{story()}</div>],
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Range defaultValue={[50]} />
}
```

### Step

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Range defaultValue={[20]} step={5} />

      <Range defaultValue={[50, 75]} step={5} />
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    width: '240px'
  }}>
      <Range />
      <Range defaultValue={[50, 75]} />
      <Range disabled defaultValue={[20]} />
      <Range disabled defaultValue={[50, 75]} />
      <Range step={5} defaultValue={[20]} />
      <Range ticks={[10, 20, 30, 40, 50, 60, 70, 80, 90]} defaultValue={[20]} />
    </div>
}
```

### Ticks

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Range defaultValue={[20]} ticks={[10, 20, 30, 40, 50, 60, 70, 80, 90]} />

      <Range defaultValue={[50, 75]} ticks={[10, 20, 30, 40, 50, 60, 70, 80, 90]} />
    </>
}
```

### Ticks Labels

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Range } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => <>
      <Range ticks={[{
      label: 'Low',
      value: 25
    }, {
      label: 'Medium',
      value: 50
    }, {
      label: 'High',
      value: 75
    }]} />

      <Range displayBounds={false} displayTooltip={false} max={5} min={1} ticks={[{
      label: 'Very Poor',
      value: 1
    }, {
      label: 'Poor',
      value: 2
    }, {
      label: 'Average',
      value: 3
    }, {
      label: 'Good',
      value: 4
    }, {
      label: 'Excellent',
      value: 5
    }]} />
    </>
}
```

## React Components/Select

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial selected value(s). Use when you don't need to control the selected value(s) of the select. |
| `disabled` | `` | No | false | Whether the component is disabled. |
| `fitControlWidth` | `` | No | true | Whether the dropdown width should stay the same as the input. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `items` | `` | No | [] | The list of items |
| `multiple` | `` | No | false | Allows multiple selection and define how it should be rendered. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onValueChange` | `` | No |  | Callback fired when the value(s) changes. |
| `readOnly` | `` | No | false | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. |
| `value` | `` | No |  | The controlled selected value(s). |


## Subcomponents


### SelectContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `customGroupRenderer` | `` | No |  | Custom render for each group item. |
| `customOptionRenderer` | `` | No |  | Custom render for each option item. |



### SelectControl



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `customItemRenderer` | `` | No |  | Custom render for the selected item(s). |
| `multipleSelectionLabel` | `` | No |  | Label displayed on multiple selection when in "merge" mode. |
| `placeholder` | `` | No |  | The placeholder text to display in the select. |


## Examples


### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Select a Web hosting
      </FormFieldLabel>

      <Select items={[{
      label: '1 vCore 2,4 GHz, 2 Go RAM',
      value: 'hosting-1'
    }, {
      label: '1 vCore 2,4 GHz, 4 Go RAM',
      value: 'hosting-2'
    }, {
      label: '2 vCores 2,4 GHz, 8 Go RAM',
      value: 'hosting-3'
    }]}>
        <SelectControl />

        <SelectContent />
      </Select>
    </FormField>
}
```

### Custom Renderer

```tsx
{
  globals: {
    imports: `import { Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    type CustomData = {
      description?: string;
      flag?: string;
      logo?: string;
    };
    const items: SelectItem<CustomData>[] = [{
      customRendererData: {
        flag: 'https://upload.wikimedia.org/wikipedia/commons/b/b7/Flag_of_Europe.svg'
      },
      label: 'EU providers',
      options: [{
        customRendererData: {
          description: 'OVH, legally OVH Groupe SA, is a French cloud computing company which offers VPS, dedicated servers and other web services. As of 2016 OVH owned the world\'s largest data center in surface area. As of 2019, it was the largest hosting provider in Europe, and the third largest in the world based on physical servers.',
          logo: 'https://ovh.github.io/manager/ovhcloud-logo.webp'
        },
        label: 'OVH Cloud',
        value: 'ovh'
      }]
    }, {
      customRendererData: {
        flag: 'https://upload.wikimedia.org/wikipedia/en/a/a4/Flag_of_the_United_States.svg'
      },
      label: 'US providers',
      options: [{
        customRendererData: {
          description: 'Amazon Web Services, Inc. is a subsidiary of Amazon that provides on-demand cloud computing platforms and APIs to individuals, companies, and governments, on a metered, pay-as-you-go basis. Clients will often use this in combination with autoscaling.',
          logo: 'https://cdn.icon-icons.com/icons2/2407/PNG/512/aws_icon_146074.png'
        },
        label: 'Amazon Web Services',
        value: 'aws'
      }, {
        customRendererData: {
          description: 'Microsoft Azure, often referred to as just Azure, is a cloud computing platform developed by Microsoft. It offers management, access and development of applications and services through its global infrastructure.',
          logo: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Microsoft_Azure.svg/2048px-Microsoft_Azure.svg.png'
        },
        label: 'Microsoft Azure',
        value: 'azure'
      }, {
        customRendererData: {
          description: 'Google Cloud Platform, offered by Google, is a suite of cloud computing services that provides a series of modular cloud services including computing, data storage, data analytics, and machine learning, alongside a set of management tools.',
          logo: 'https://upload.wikimedia.org/wikipedia/commons/0/01/Google-cloud-platform.svg'
        },
        label: 'Google Cloud Platform',
        value: 'gcp'
      }]
    }];
    return <Select items={items} multiple>
        <SelectControl customItemRenderer={({
        selectedItems
      }) => <span style={{
        display: 'flex',
        flexFlow: 'row',
        gap: '8px',
        flexWrap: 'wrap'
      }}>
          {selectedItems.map((item, idx) => <span style={{
          display: 'flex',
          flexFlow: 'row',
          gap: '4px',
          alignItems: 'center'
        }} key={item.value}>
                <img alt={item.label} height={15} src={item.customRendererData?.logo} width={15} />

                <span>{item.label}{idx < selectedItems.length - 1 && ', '}</span>
              </span>)}
        </span>} />

        <SelectContent customGroupRenderer={({
        customData,
        label
      }) => <div style={{
        display: 'flex',
        flexFlow: 'row',
        columnGap: '8px',
        alignItems: 'center'
      }}>
              <img alt="flag" height={20} src={customData?.flag} width={30} />

              <span>{label}</span>
            </div>} customOptionRenderer={({
        customData,
        label
      }) => <div style={{
        display: 'flex',
        flexFlow: 'row',
        columnGap: '8px',
        alignItems: 'center',
        padding: '8px 0'
      }}>
              <img alt={label} height={50} src={customData?.logo} width={50} />
              <div style={{
          display: 'flex',
          flexFlow: 'column',
          rowGap: '8px'
        }}>
                <span style={{
            fontWeight: 'bold'
          }}>{label}</span>
                <span>{customData?.description}</span>
              </div>
            </div>} />
      </Select>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Select items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <SelectControl />

      <SelectContent />
    </Select>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Select disabled={arg.disabled} fitControlWidth={arg.fitControlWidth} invalid={arg.invalid} items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]} multiple={arg.multiple} readOnly={arg.readOnly}>
      <SelectControl multipleSelectionLabel={arg.multipleSelectionLabel} placeholder={arg.placeholder} />

      <SelectContent />
    </Select>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    fitControlWidth: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    multiple: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'select'
      },
      options: [true, false, 'merge']
    },
    multipleSelectionLabel: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { TEXT_PRESET, Select, SelectContent, SelectControl, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Select disabled items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot'
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]}>
        <Text htmlFor="disabled" preset={TEXT_PRESET.label}>
          Disabled
        </Text>

        <SelectControl id="disabled" placeholder="Select one or more pets" />

        <SelectContent />
      </Select>

      <Select items={[{
      label: 'Dog',
      value: 'dog',
      disabled: true
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot',
      disabled: true
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]}>
        <Text htmlFor="disabled-options" preset={TEXT_PRESET.label}>
          Disabled options
        </Text>

        <SelectControl id="disabled-options" placeholder="Select one or more pets" />

        <SelectContent />
      </Select>

      <Select items={[{
      label: 'Europe',
      options: [{
        label: 'France',
        value: 'fr'
      }, {
        label: 'Germany',
        value: 'de',
        disabled: true
      }, {
        label: 'Italy',
        value: 'it'
      }]
    }, {
      disabled: true,
      label: 'Asia',
      options: [{
        label: 'China',
        value: 'cn',
        disabled: true
      }, {
        label: 'Japan',
        value: 'jp',
        disabled: true
      }, {
        label: 'Russia',
        value: 'ru',
        disabled: true
      }]
    }, {
      label: 'North America',
      options: [{
        label: 'Canada',
        value: 'ca'
      }, {
        label: 'Mexico',
        value: 'mx'
      }, {
        label: 'United States of America',
        value: 'us'
      }]
    }]}>
        <Text htmlFor="disabled-group" preset={TEXT_PRESET.label}>
          Disabled group or group option
        </Text>

        <SelectControl id="disabled-group" />

        <SelectContent />
      </Select>
    </>
}
```

### Group

```tsx
{
  globals: {
    imports: `import { Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Select items={[{
    label: 'Europe',
    options: [{
      label: 'France',
      value: 'fr'
    }, {
      label: 'Germany',
      value: 'de'
    }, {
      label: 'Italy',
      value: 'it'
    }]
  }, {
    label: 'Asia',
    options: [{
      label: 'China',
      value: 'cn'
    }, {
      label: 'Japan',
      value: 'jp'
    }, {
      label: 'Russia',
      value: 'ru'
    }]
  }, {
    label: 'North America',
    options: [{
      label: 'Canada',
      value: 'ca'
    }, {
      label: 'Mexico',
      value: 'mx'
    }, {
      label: 'United States of America',
      value: 'us'
    }]
  }]}>
      <SelectControl />

      <SelectContent />
    </Select>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Select a Web hosting
      </FormFieldLabel>

      <Select items={[{
      label: '1 vCore 2,4 GHz, 2 Go RAM',
      value: 'hosting-1'
    }, {
      label: '1 vCore 2,4 GHz, 4 Go RAM',
      value: 'hosting-2'
    }, {
      label: '2 vCores 2,4 GHz, 8 Go RAM',
      value: 'hosting-3'
    }]}>
        <SelectControl />

        <SelectContent />
      </Select>
    </FormField>
}
```

### Multiple

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { TEXT_PRESET, Select, SelectContent, SelectControl, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Select items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot'
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]} multiple>
        <Text htmlFor="multiple" preset={TEXT_PRESET.label}>
          Default multiple selection
        </Text>

        <SelectControl id="multiple" placeholder="Select one or more pets" />

        <SelectContent />
      </Select>

      <Select items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }, {
      label: 'Hamster',
      value: 'hamster'
    }, {
      label: 'Parrot',
      value: 'parrot'
    }, {
      label: 'Spider',
      value: 'spider'
    }, {
      label: 'Goldfish',
      value: 'goldfish'
    }]} multiple="merge">
        <Text htmlFor="multiple-merged" preset={TEXT_PRESET.label}>
          Merged multiple selection
        </Text>

        <SelectControl id="multiple-merged" placeholder="Select one or more pets" />

        <SelectContent />
      </Select>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Select items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]}>
      <SelectControl placeholder="Select one or more pets" />

      <SelectContent />
    </Select>
}
```

### Readonly

```tsx
{
  globals: {
    imports: `import { Select, SelectContent, SelectControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Select items={[{
    label: 'Dog',
    value: 'dog'
  }, {
    label: 'Cat',
    value: 'cat'
  }, {
    label: 'Hamster',
    value: 'hamster'
  }, {
    label: 'Parrot',
    value: 'parrot'
  }, {
    label: 'Spider',
    value: 'spider'
  }, {
    label: 'Goldfish',
    value: 'goldfish'
  }]} readOnly>
      <SelectControl placeholder="Select one or more pets" />

      <SelectContent />
    </Select>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Select items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <SelectControl placeholder="Default" />
        <SelectContent createPortal={false} />
      </Select>

      <Select multiple items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <SelectControl placeholder="Multiple" />
        <SelectContent createPortal={false} />
      </Select>

      <Select disabled items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <SelectControl placeholder="Disabled" />
        <SelectContent createPortal={false} />
      </Select>

      <Select readOnly items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <SelectControl placeholder="Read only" />
        <SelectContent createPortal={false} />
      </Select>

      <Select invalid items={[{
      label: 'Dog',
      value: 'dog'
    }, {
      label: 'Cat',
      value: 'cat'
    }]}>
        <SelectControl placeholder="Invalid" />
        <SelectContent createPortal={false} />
      </Select>
    </div>
}
```

## React Components/Skeleton

## Examples


### Accessibility Bad Practice Loading

```tsx
{
  globals: {
    imports: 'import { Skeleton } from \'@ovhcloud/ods-react\';'
  },
  tags: ['!dev'],
  render: ({}) => <div>
      <Skeleton />
    </div>
}
```

### Accessibility Loading

```tsx
{
  globals: {
    imports: 'import { Skeleton } from \'@ovhcloud/ods-react\';'
  },
  tags: ['!dev'],
  render: ({}) => <div aria-busy="true">
      <Skeleton />
    </div>
}
```

### Default

```tsx
{
  globals: {
    imports: 'import { Skeleton } from \'@ovhcloud/ods-react\';'
  },
  tags: ['!dev'],
  render: ({}) => <Skeleton />
}
```

### Demo

```tsx
{}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => <Skeleton />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px'
  }}>
      <Skeleton />
      <Skeleton />
      <Skeleton />
    </div>
}
```

## React Components/Spinner

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | SPINNER_COLOR.primary | The color preset to use. |
| `size` | `` | No | SPINNER_SIZE.md | The size preset to use. |


## Examples


### Accessibility Aria Busy Aria Live

```tsx
{
  globals: {
    imports: `import { Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div aria-busy="true" aria-live="polite">
      <Spinner />
    </div>
}
```

### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Spinner aria-label="Loading user profile" />
}
```

### Accessibility Aria Labelled By

```tsx
{
  globals: {
    imports: `import { Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div>
      <span id="loading-text">
        Loading user profile
      </span>

      <Spinner aria-labelledby="loading-text" />
    </div>
}
```

### Color

```tsx
{
  globals: {
    imports: `import { SPINNER_COLOR, Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Spinner color={SPINNER_COLOR.neutral} />
      <Spinner color={SPINNER_COLOR.primary} />
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Spinner />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'SPINNER_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: SPINNER_COLORS
    },
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'SPINNER_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: SPINNER_SIZES
    }
  })
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Spinner />
}
```

### Size

```tsx
{
  globals: {
    imports: `import { SPINNER_SIZE, Spinner } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Spinner size={SPINNER_SIZE.xs} />
      <Spinner size={SPINNER_SIZE.sm} />
      <Spinner size={SPINNER_SIZE.md} />
      <Spinner size={SPINNER_SIZE.lg} />
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'inline-flex',
    flexFlow: 'row',
    gap: '12px',
    alignItems: 'center'
  }}>
      <Spinner />
      <Spinner size={SPINNER_SIZE.sm} />
      <Spinner size={SPINNER_SIZE.lg} />
      <Spinner color={SPINNER_COLOR.neutral} />
      <Spinner color={SPINNER_COLOR.primary} />
    </div>
}
```

## React Components/Switch

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial value of the selected item. Use when you don't need to control the value of the switch. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `size` | `` | No | SWITCH_SIZE.md | The size preset to use. |
| `value` | `` | No |  | The controlled value of the selected item. |


## Subcomponents


### SwitchItem



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `value` | `` | Yes |  | The value of the switch item. |


## Examples


### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Switch, SwitchItem } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Switch aria-label="Select an item">
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>
}
```

### Accessibility Aria Labelledby

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    alignItems: 'start'
  }}>{story()}</div>],
  globals: {
    imports: `import { TEXT_PRESET, Switch, SwitchItem, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Text id="switch-label" preset={TEXT_PRESET.label}>
        Select an item:
      </Text>

      <Switch aria-labelledby="switch-label">
        <SwitchItem value="item-1">Item 1</SwitchItem>
        <SwitchItem value="item-2">Item 2</SwitchItem>
        <SwitchItem value="item-3">Item 3</SwitchItem>
      </Switch>
    </>
}
```

### Checked

```tsx
{
  globals: {
    imports: `import { Switch, SwitchItem } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Switch defaultValue="item-1">
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Switch, SwitchItem } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Switch>
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>
}
```

### Demo

```tsx
{
  render: arg => <Switch {...arg}>
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'SWITCH_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: SWITCH_SIZES
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Switch, SwitchItem } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Switch disabled>
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Switch defaultValue="item-1">
      <SwitchItem value="item-1">Item 1</SwitchItem>
      <SwitchItem value="item-2">Item 2</SwitchItem>
      <SwitchItem value="item-3">Item 3</SwitchItem>
    </Switch>
}
```

### Sizes

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { SWITCH_SIZE, Switch, SwitchItem } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Switch size={SWITCH_SIZE.sm}>
        <SwitchItem value="item-1">Item 1</SwitchItem>
        <SwitchItem value="item-2">Item 2</SwitchItem>
        <SwitchItem value="item-3">Item 3</SwitchItem>
      </Switch>

      <Switch size={SWITCH_SIZE.md}>
        <SwitchItem value="item-1">Item 1</SwitchItem>
        <SwitchItem value="item-2">Item 2</SwitchItem>
        <SwitchItem value="item-3">Item 3</SwitchItem>
      </Switch>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'column',
    rowGap: '16px'
  }}>
      <Switch style={{
      alignSelf: 'start'
    }}>
        <SwitchItem value="item-1">Item 1</SwitchItem>
        <SwitchItem value="item-2">Item 2</SwitchItem>
        <SwitchItem value="item-3">Item 3</SwitchItem>
      </Switch>

      <Switch disabled style={{
      alignSelf: 'start'
    }}>
        <SwitchItem value="item-1">Item 1</SwitchItem>
        <SwitchItem value="item-2">Item 2</SwitchItem>
        <SwitchItem value="item-3">Item 3</SwitchItem>
      </Switch>

      <Switch size={SWITCH_SIZE.sm} style={{
      alignSelf: 'start'
    }}>
        <SwitchItem value="item-1">Small 1</SwitchItem>
        <SwitchItem value="item-2">Small 2</SwitchItem>
      </Switch>
    </div>
}
```

## React Components/Table

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `size` | `` | No | TABLE_SIZE.md | The size preset to use. |
| `variant` | `` | No | TABLE_VARIANT.default | The variant preset to use. |


## Examples


### Custom Caption

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Table, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Table>
      <caption>
        <Text preset={TEXT_PRESET.caption}>
          Front-end web developer course 2021
        </Text>
      </caption>
      <thead>
      <tr>
        <th scope="col">Person</th>
        <th scope="col">Most interest in</th>
        <th scope="col">Age</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <th scope="row">Chris</th>
        <td>HTML tables</td>
        <td>22</td>
      </tr>
      <tr>
        <th scope="row">Dennis</th>
        <td>Web accessibility</td>
        <td>45</td>
      </tr>
      <tr>
        <th scope="row">Sarah</th>
        <td>JavaScript frameworks</td>
        <td>29</td>
      </tr>
      <tr>
        <th scope="row">Karen</th>
        <td>Web performance</td>
        <td>36</td>
      </tr>
      </tbody>
    </Table>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Table } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Table>
      <caption>
        Front-end web developer course 2021
      </caption>
      <thead>
      <tr>
        <th scope="col">Person</th>
        <th scope="col">Most interest in</th>
        <th scope="col">Age</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <th scope="row">Chris</th>
        <td>HTML tables</td>
        <td>22</td>
      </tr>
      <tr>
        <th scope="row">Dennis</th>
        <td>Web accessibility</td>
        <td>45</td>
      </tr>
      <tr>
        <th scope="row">Sarah</th>
        <td>JavaScript frameworks</td>
        <td>29</td>
      </tr>
      <tr>
        <th scope="row">Karen</th>
        <td>Web performance</td>
        <td>36</td>
      </tr>
      </tbody>
    </Table>
}
```

### Demo

```tsx
{
  render: prop => <Table {...prop}>
      <caption>
        Front-end web developer course 2021
      </caption>
      <thead>
      <tr>
        <th scope="col">Person</th>
        <th scope="col">Most interest in</th>
        <th scope="col">Age</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <th scope="row">Chris</th>
        <td>HTML tables</td>
        <td>22</td>
      </tr>
      <tr>
        <th scope="row">Dennis</th>
        <td>Web accessibility</td>
        <td>45</td>
      </tr>
      <tr>
        <th scope="row">Sarah</th>
        <td>JavaScript frameworks</td>
        <td>29</td>
      </tr>
      <tr>
        <th scope="row">Karen</th>
        <td>Web performance</td>
        <td>36</td>
      </tr>
      </tbody>
    </Table>,
  argTypes: orderControls({
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'TABLE_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: TABLE_SIZES
    },
    variant: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'TABLE_VARIANT'
        }
      },
      control: {
        type: 'select'
      },
      options: TABLE_VARIANTS
    }
  })
}
```

### Overview

```tsx
{
  parameters: {
    layout: 'centered'
  },
  tags: ['!dev'],
  render: ({}) => <Table>
      <caption>
        Front-end web developer course 2021
      </caption>
      <thead>
      <tr>
        <th scope="col">Person</th>
        <th scope="col">Most interest in</th>
        <th scope="col">Age</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <th scope="row">Chris</th>
        <td>HTML tables</td>
        <td>22</td>
      </tr>
      <tr>
        <th scope="row">Dennis</th>
        <td>Web accessibility</td>
        <td>45</td>
      </tr>
      <tr>
        <th scope="row">Sarah</th>
        <td>JavaScript frameworks</td>
        <td>29</td>
      </tr>
      <tr>
        <th scope="row">Karen</th>
        <td>Web performance</td>
        <td>36</td>
      </tr>
      </tbody>
    </Table>
}
```

### Size

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexDirection: 'column',
    rowGap: '16px'
  }}>
      {story()}
    </div>],
  globals: {
    imports: `import { TABLE_SIZE, Table } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Table size={TABLE_SIZE.sm}>
        <caption>
          Front-end web developer course 2021
        </caption>
        <thead>
        <tr>
          <th scope="col">Person</th>
          <th scope="col">Most interest in</th>
          <th scope="col">Age</th>
        </tr>
        </thead>
        <tbody>
        <tr>
          <th scope="row">Chris</th>
          <td>HTML tables</td>
          <td>22</td>
        </tr>
        <tr>
          <th scope="row">Dennis</th>
          <td>Web accessibility</td>
          <td>45</td>
        </tr>
        <tr>
          <th scope="row">Sarah</th>
          <td>JavaScript frameworks</td>
          <td>29</td>
        </tr>
        <tr>
          <th scope="row">Karen</th>
          <td>Web performance</td>
          <td>36</td>
        </tr>
        </tbody>
      </Table>

      <Table size={TABLE_SIZE.md}>
        <caption>
          Front-end web developer course 2021
        </caption>
        <thead>
        <tr>
          <th scope="col">Person</th>
          <th scope="col">Most interest in</th>
          <th scope="col">Age</th>
        </tr>
        </thead>
        <tbody>
        <tr>
          <th scope="row">Chris</th>
          <td>HTML tables</td>
          <td>22</td>
        </tr>
        <tr>
          <th scope="row">Dennis</th>
          <td>Web accessibility</td>
          <td>45</td>
        </tr>
        <tr>
          <th scope="row">Sarah</th>
          <td>JavaScript frameworks</td>
          <td>29</td>
        </tr>
        <tr>
          <th scope="row">Karen</th>
          <td>Web performance</td>
          <td>36</td>
        </tr>
        </tbody>
      </Table>

      <Table size={TABLE_SIZE.lg}>
        <caption>
          Front-end web developer course 2021
        </caption>
        <thead>
        <tr>
          <th scope="col">Person</th>
          <th scope="col">Most interest in</th>
          <th scope="col">Age</th>
        </tr>
        </thead>
        <tbody>
        <tr>
          <th scope="row">Chris</th>
          <td>HTML tables</td>
          <td>22</td>
        </tr>
        <tr>
          <th scope="row">Dennis</th>
          <td>Web accessibility</td>
          <td>45</td>
        </tr>
        <tr>
          <th scope="row">Sarah</th>
          <td>JavaScript frameworks</td>
          <td>29</td>
        </tr>
        <tr>
          <th scope="row">Karen</th>
          <td>Web performance</td>
          <td>36</td>
        </tr>
        </tbody>
      </Table>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    rowGap: '16px'
  }}>
      <Table>
        <caption>Default</caption>
        <thead>
          <tr><th scope="col">Person</th><th scope="col">Most interest in</th><th scope="col">Age</th></tr>
        </thead>
        <tbody>
          <tr><th scope="row">Chris</th><td>HTML tables</td><td>22</td></tr>
          <tr><th scope="row">Dennis</th><td>Web accessibility</td><td>45</td></tr>
        </tbody>
      </Table>

      <Table variant={TABLE_VARIANT.striped}>
        <caption>Striped</caption>
        <thead>
          <tr><th scope="col">Person</th><th scope="col">Most interest in</th><th scope="col">Age</th></tr>
        </thead>
        <tbody>
          <tr><th scope="row">Sarah</th><td>JavaScript frameworks</td><td>29</td></tr>
          <tr><th scope="row">Karen</th><td>Web performance</td><td>36</td></tr>
        </tbody>
      </Table>
    </div>
}
```

### Variant

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexDirection: 'column',
    rowGap: '16px'
  }}>
      {story()}
    </div>],
  globals: {
    imports: `import { TABLE_VARIANT, Table } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Table variant={TABLE_VARIANT.default}>
        <caption>
          Front-end web developer course 2021
        </caption>
        <thead>
        <tr>
          <th scope="col">Person</th>
          <th scope="col">Most interest in</th>
          <th scope="col">Age</th>
        </tr>
        </thead>
        <tbody>
        <tr>
          <th scope="row">Chris</th>
          <td>HTML tables</td>
          <td>22</td>
        </tr>
        <tr>
          <th scope="row">Dennis</th>
          <td>Web accessibility</td>
          <td>45</td>
        </tr>
        <tr>
          <th scope="row">Sarah</th>
          <td>JavaScript frameworks</td>
          <td>29</td>
        </tr>
        <tr>
          <th scope="row">Karen</th>
          <td>Web performance</td>
          <td>36</td>
        </tr>
        </tbody>
      </Table>

      <Table variant={TABLE_VARIANT.striped}>
        <caption>
          Front-end web developer course 2021
        </caption>
        <thead>
        <tr>
          <th scope="col">Person</th>
          <th scope="col">Most interest in</th>
          <th scope="col">Age</th>
        </tr>
        </thead>
        <tbody>
        <tr>
          <th scope="row">Chris</th>
          <td>HTML tables</td>
          <td>22</td>
        </tr>
        <tr>
          <th scope="row">Dennis</th>
          <td>Web accessibility</td>
          <td>45</td>
        </tr>
        <tr>
          <th scope="row">Sarah</th>
          <td>JavaScript frameworks</td>
          <td>29</td>
        </tr>
        <tr>
          <th scope="row">Karen</th>
          <td>Web performance</td>
          <td>36</td>
        </tr>
        </tbody>
      </Table>
    </>
}
```

## React Components/Tabs

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial value of the selected tab. Use when you don't need to control the value of the tabs. |
| `onValueChange` | `` | No |  | Callback fired when the state of selected tab changes. |
| `value` | `` | No |  | The controlled value of the selected tab. |
| `withArrows` | `` | No |  | Whether the component displays navigation arrows around the tabs. |


## Subcomponents


### TabList




### Tab



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `value` | `` | Yes |  | The value of the tab item. |



### TabContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `value` | `` | Yes |  | The value of the tab content item. |


## Examples


### Controlled

```tsx
{
  globals: {
    imports: `import { Tabs, TabList, Tab } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [value, setValue] = useState('tab1');
    const handleValueChange = (event: TabsValueChangeEvent) => {
      setValue(event.value);
    };
    return <Tabs onValueChange={handleValueChange} value={value}>
        <TabList>
          <Tab value="tab1">Tab 1</Tab>
          <Tab value="tab2">Tab 2</Tab>
          <Tab value="tab3">Tab 3</Tab>
        </TabList>
      </Tabs>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Tabs, TabList, Tab } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tabs defaultValue='tab1'>
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2">Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
      </TabList>
    </Tabs>
}
```

### Demo

```tsx
{
  render: arg => <Tabs defaultValue='tab1' withArrows={arg.withArrows}>
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2">Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
        <Tab value="tab4">Tab 4</Tab>
        <Tab value="tab5">Tab 5</Tab>
        <Tab value="tab6">Tab 6</Tab>
      </TabList>

      <TabContent value="tab1">
        <p>Tab 1 Content</p>
      </TabContent>

      <TabContent value="tab2">
        <p>Tab 2 Content</p>
      </TabContent>

      <TabContent value="tab3">
        <p>Tab 3 Content</p>
      </TabContent>

      <TabContent value="tab4">
        <p>Tab 4 Content</p>
      </TabContent>

      <TabContent value="tab5">
        <p>Tab 5 Content</p>
      </TabContent>

      <TabContent value="tab6">
        <p>Tab 6 Content</p>
      </TabContent>
    </Tabs>,
  argTypes: orderControls({
    withArrows: {
      table: {
        category: CONTROL_CATEGORY.design
      },
      control: {
        type: 'boolean'
      }
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Tabs, TabList, Tab } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tabs defaultValue="tab1">
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2" disabled>Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
      </TabList>
    </Tabs>
}
```

### Overflow

```tsx
{
  globals: {
    imports: `import { Tabs, TabList, Tab } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    width: '300px'
  }}>
      <Tabs defaultValue="tab1">
        <TabList>
          <Tab value="tab1">Tab 1</Tab>
          <Tab value="tab2">Tab 2</Tab>
          <Tab value="tab3">Tab 3</Tab>
          <Tab value="tab4">Tab 4</Tab>
          <Tab value="tab5">Tab 5</Tab>
          <Tab value="tab6">Tab 6</Tab>
        </TabList>
      </Tabs>
    </div>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Tabs defaultValue="tab1">
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2">Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
      </TabList>
    </Tabs>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <Tabs withArrows defaultValue="tab1">
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2" disabled>Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
        <Tab value="tab4">Tab 4</Tab>
        <Tab value="tab5">Tab 5</Tab>
        <Tab value="tab6">Tab 6</Tab>
        <Tab value="tab7">Tab 7</Tab>
        <Tab value="tab8">Tab 8</Tab>
        <Tab value="tab9">Tab 9</Tab>
        <Tab value="tab10">Tab 10</Tab>
        <Tab value="tab11">Tab 11</Tab>
        <Tab value="tab12">Tab 12</Tab>
        <Tab value="tab13">Tab 13</Tab>
        <Tab value="tab14">Tab 14</Tab>
        <Tab value="tab15">Tab 15</Tab>
      </TabList>
      <TabContent value="tab1"><p>Content 1</p></TabContent>
      <TabContent value="tab2"><p>Content 2</p></TabContent>
      <TabContent value="tab3"><p>Content 3</p></TabContent>
      <TabContent value="tab4"><p>Content 4</p></TabContent>
      <TabContent value="tab5"><p>Content 5</p></TabContent>
      <TabContent value="tab6"><p>Content 6</p></TabContent>
      <TabContent value="tab7"><p>Content 7</p></TabContent>
      <TabContent value="tab8"><p>Content 8</p></TabContent>
      <TabContent value="tab9"><p>Content 9</p></TabContent>
      <TabContent value="tab10"><p>Content 10</p></TabContent>
      <TabContent value="tab11"><p>Content 11</p></TabContent>
      <TabContent value="tab12"><p>Content 12</p></TabContent>
      <TabContent value="tab13"><p>Content 13</p></TabContent>
      <TabContent value="tab14"><p>Content 14</p></TabContent>
      <TabContent value="tab15"><p>Content 15</p></TabContent>
    </Tabs>
}
```

### With Arrows

```tsx
{
  globals: {
    imports: `import { Tabs, TabList, Tab } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tabs defaultValue="tab1" withArrows>
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2">Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
        <Tab value="tab4">Tab 4</Tab>
        <Tab value="tab5">Tab 5</Tab>
        <Tab value="tab6">Tab 6</Tab>
        <Tab value="tab7">Tab 7</Tab>
        <Tab value="tab8">Tab 8</Tab>
        <Tab value="tab9">Tab 9</Tab>
        <Tab value="tab10">Tab 10</Tab>
        <Tab value="tab11">Tab 11</Tab>
        <Tab value="tab12">Tab 12</Tab>
        <Tab value="tab13">Tab 13</Tab>
        <Tab value="tab14">Tab 14</Tab>
        <Tab value="tab15">Tab 15</Tab>
      </TabList>
    </Tabs>
}
```

### With Content

```tsx
{
  globals: {
    imports: `import { Tabs, TabContent, TabList, Tab } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tabs defaultValue="tab1">
      <TabList>
        <Tab value="tab1">Tab 1</Tab>
        <Tab value="tab2">Tab 2</Tab>
        <Tab value="tab3">Tab 3</Tab>
      </TabList>
      <TabContent value="tab1">
        <p>Content 1</p>
      </TabContent>
      <TabContent value="tab2">
        <p>Content 2</p>
      </TabContent>
      <TabContent value="tab3">
        <p>Content 3</p>
      </TabContent>
    </Tabs>
}
```

## React Components/Tag

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `color` | `` | No | TAG_COLOR.information | @type=TAG_COLOR The color preset to use. |
| `size` | `` | No | TAG_SIZE.md | The size preset to use. |


## Examples


### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tag aria-label="Remove my tag">
      My tag
    </Tag>
}
```

### Accessibility Aria Roles

```tsx
{
  globals: {
    imports: `import { Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <div role="list">
      <div role="listitem">
        <Tag>Design</Tag>
      </div>

      <div role="listitem">
        <Tag>Development</Tag>
      </div>

      <div role="listitem">
        <Tag>Accessibility</Tag>
      </div>
    </div>
}
```

### Accessibility List

```tsx
{
  globals: {
    imports: `import { Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <ul>
      <li>
        <Tag>Design</Tag>
      </li>

      <li>
        <Tag>Development</Tag>
      </li>

      <li>
        <Tag>Accessibility</Tag>
      </li>
    </ul>
}
```

### Color

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { TAG_COLOR, Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <>
      <Tag color={TAG_COLOR.critical}>Critical</Tag>
      <Tag color={TAG_COLOR.information}>Information</Tag>
      <Tag color={TAG_COLOR.neutral}>Neutral</Tag>
      <Tag color={TAG_COLOR.primary}>Primary</Tag>
      <Tag color={TAG_COLOR.success}>Success</Tag>
      <Tag color={TAG_COLOR.warning}>Warning</Tag>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Tag>
      My tag
    </Tag>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    color: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'TAG_COLOR'
        }
      },
      control: {
        type: 'select'
      },
      options: TAG_COLORS
    },
    children: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'text'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    },
    size: {
      table: {
        category: CONTROL_CATEGORY.design,
        type: {
          summary: 'TAG_SIZE'
        }
      },
      control: {
        type: 'select'
      },
      options: TAG_SIZES
    }
  }),
  args: {
    children: 'My tag'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Tag disabled>
      My tag
    </Tag>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Tag>
      My tag
    </Tag>
}
```

### Size

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { TAG_SIZE, Tag } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <>
      <Tag size={TAG_SIZE.md}>MD tag</Tag>
      <Tag size={TAG_SIZE.lg}>LG tag</Tag>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexFlow: 'row wrap',
    gap: '8px',
    alignItems: 'center'
  }}>
      <Tag>Default</Tag>
      <Tag disabled>Disabled</Tag>
      <Tag size={TAG_SIZE.lg}>Large</Tag>
      <Tag color={TAG_COLOR.primary}>Primary</Tag>
      <Tag color={TAG_COLOR.success}>Success</Tag>
      <Tag color={TAG_COLOR.warning}>Warning</Tag>
      <Tag color={TAG_COLOR.critical}>Critical</Tag>
      <Tag color={TAG_COLOR.information}>Info</Tag>
      <Tag color={TAG_COLOR.neutral}>Neutral</Tag>
    </div>
}
```

## React Components/Text

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `disabled` | `` | No |  | Whether the text is displayed in a disabled context. |
| `preset` | `` | No | TEXT_PRESET.paragraph | The text preset to use. |


## Examples


### Default

```tsx
{
  globals: {
    imports: `import { Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Text>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    </Text>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    children: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    preset: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'TEXT_PRESET'
        }
      },
      control: {
        type: 'select'
      },
      options: TEXT_PRESETS
    }
  }),
  args: {
    children: 'Lorem ipsum dolor sit amet'
  }
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Text disabled>
      Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    </Text>
}
```

### Fig Caption

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <figure>
      <img alt="OVHcloud logo" src="https://images.crunchbase.com/image/upload/c_pad,h_256,w_256,f_auto,q_auto:eco,dpr_1/ayzwkdawmlyzvuummuf4" style={{
      height: '100px'
    }} />

      <figcaption>
        <Text preset={TEXT_PRESET.caption}>
          My picture title
        </Text>
      </figcaption>
    </figure>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Text>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</Text>
}
```

### Preset

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Text preset={TEXT_PRESET.caption}>Caption</Text><br />
      <Text preset={TEXT_PRESET.code}>Code</Text><br />
      <Text preset={TEXT_PRESET.label}>Label</Text>
      <Text preset={TEXT_PRESET.paragraph}>Paragraph</Text>
      <Text preset={TEXT_PRESET.small}>Small</Text><br />
      <Text preset={TEXT_PRESET.span}>Span</Text><br />
      <Text preset={TEXT_PRESET.heading1}>Heading-1</Text>
      <Text preset={TEXT_PRESET.heading2}>Heading-2</Text>
      <Text preset={TEXT_PRESET.heading3}>Heading-3</Text>
      <Text preset={TEXT_PRESET.heading4}>Heading-4</Text>
      <Text preset={TEXT_PRESET.heading5}>Heading-5</Text>
      <Text preset={TEXT_PRESET.heading6}>Heading-6</Text>
    </>
}
```

### Table Caption

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, Text } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <table style={{
    border: '2px solid rgb(140 140 140)',
    borderCollapse: 'collapse'
  }}>
      <caption style={{
      captionSide: 'bottom'
    }}>
        <Text preset="caption">
          My table title
        </Text>
      </caption>

      <thead>
      <tr>
        <th scope="col">Person</th>
        <th scope="col">Age</th>
      </tr>
      </thead>
      <tbody>
      <tr>
        <th scope="row">Chris</th>
        <td>22</td>
      </tr>
      </tbody>
    </table>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div>
      <Text preset={TEXT_PRESET.caption}>Caption</Text><br />
      <Text preset={TEXT_PRESET.code}>Code</Text><br />
      <Text preset={TEXT_PRESET.label}>Label</Text>
      <Text preset={TEXT_PRESET.paragraph}>Paragraph</Text>
      <Text preset={TEXT_PRESET.small}>Small</Text><br />
      <Text preset={TEXT_PRESET.span}>Span</Text><br />
      <Text preset={TEXT_PRESET.heading1}>Heading-1</Text>
      <Text preset={TEXT_PRESET.heading2}>Heading-2</Text>
      <Text preset={TEXT_PRESET.heading3}>Heading-3</Text>
      <Text preset={TEXT_PRESET.heading4}>Heading-4</Text>
      <Text preset={TEXT_PRESET.heading5}>Heading-5</Text>
      <Text preset={TEXT_PRESET.heading6}>Heading-6</Text>
    </div>
}
```

## React Components/Textarea

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `invalid` | `` | No |  | Whether the component is in error state. |


## Examples


### Accessibility Described By

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldHelper, FormFieldLabel, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Description:
      </FormFieldLabel>

      <Textarea />

      <FormFieldHelper>
        Enter a brief description
      </FormFieldHelper>
    </FormField>
}
```

### Accessibility Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Description:
      </FormFieldLabel>

      <Textarea />
    </FormField>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Textarea />
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    cols: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    },
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    placeholder: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'string'
        }
      },
      control: 'text'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    rows: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'number'
        }
      },
      control: 'number'
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Textarea disabled />
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { TEXT_PRESET, FormField, FormFieldError, FormFieldHelper, FormFieldLabel, Text, Textarea } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const MAX_COUNT = 200;
    const [count, setCount] = useState(0);
    function onInput(e: FormEvent): void {
      setCount((e.target as HTMLTextAreaElement).value.length);
    }
    return <FormField invalid={count > MAX_COUNT}>
        <FormFieldLabel>
          Description:
        </FormFieldLabel>

        <Textarea name="description" onInput={onInput} />

        <FormFieldHelper style={{
        display: 'flex',
        justifyContent: 'space-between'
      }}>
          <Text preset={TEXT_PRESET.caption}>
            Helper text
          </Text>

          <Text preset={TEXT_PRESET.caption}>
            {count}/{MAX_COUNT}
          </Text>
        </FormFieldHelper>

        <FormFieldError>
          Error message
        </FormFieldError>
      </FormField>;
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Textarea placeholder="Textarea" />
}
```

### Read Only

```tsx
{
  globals: {
    imports: `import { Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Textarea defaultValue="Readonly" readOnly />
}
```

### Resizable

```tsx
{
  globals: {
    imports: `import { Textarea } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Textarea style={{
    resize: 'both'
  }} />
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Textarea placeholder="Default" />
      <Textarea disabled placeholder="Disabled" />
      <Textarea invalid placeholder="Invalid" />
      <Textarea readOnly defaultValue="Read only" />
    </div>
}
```

## React Components/Timepicker

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultValue` | `` | No |  | The initial time value. Use when you don't need to control the value of the timepicker. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `i18n` | `` | No |  | Internal translations override. |
| `id` | `` | No |  | The field id. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `locale` | `` | No |  | The locale used for the translation of the internal elements. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onTimezoneChange` | `` | No |  | Callback fired when the timezone changes. |
| `onValueChange` | `` | No |  | Callback fired when the value changes. |
| `readOnly` | `` | No |  | Whether the component is readonly. |
| `required` | `` | No |  | Whether the component is required. |
| `timezone` | `` | No |  | The controlled selected timezone. |
| `timezones` | `` | No |  | A specific or preset list of timezone to display in the selector. |
| `value` | `` | No |  | The controlled timepicker value. |
| `withSeconds` | `` | No |  | Whether the time input allows seconds selection. |


## Subcomponents


### TimepickerControl




### TimepickerTimezoneList



## Examples


### Accessibility Label

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Starting time:
      </FormFieldLabel>

      <Timepicker withSeconds>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>
    </FormField>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Timepicker, TimepickerControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Timepicker>
      <TimepickerControl />
    </Timepicker>
}
```

### Demo

```tsx
{
  render: (arg: DemoArg) => <Timepicker disabled={arg.disabled} invalid={arg.invalid} readOnly={arg.readOnly} withSeconds={arg.withSeconds}>
      <TimepickerControl />

      {arg.withTimezones && <TimepickerTimezoneList />}
    </Timepicker>,
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: {
        type: 'boolean'
      }
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    readOnly: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    withSeconds: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    },
    withTimezones: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'boolean'
        }
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Timepicker disabled>
        <TimepickerControl />
      </Timepicker>

      <Timepicker disabled>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>
    </>
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <FormField>
      <FormFieldLabel>
        Timepicker:
      </FormFieldLabel>

      <Timepicker>
        <TimepickerControl />
      </Timepicker>
    </FormField>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Timepicker defaultValue="12:00">
      <TimepickerControl />

      <TimepickerTimezoneList />
    </Timepicker>
}
```

### Readonly

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Timepicker readOnly>
        <TimepickerControl />
      </Timepicker>

      <Timepicker readOnly>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    flexDirection: 'column',
    gap: '12px',
    alignItems: 'flex-start'
  }}>
      <Timepicker>
        <TimepickerControl />
      </Timepicker>

      <Timepicker>
        <TimepickerControl />
        <TimepickerTimezoneList />
      </Timepicker>

      <Timepicker disabled>
        <TimepickerControl />
      </Timepicker>

      <Timepicker readOnly>
        <TimepickerControl />
      </Timepicker>
    </div>
}
```

### Timezone List

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <span>All timezones</span>

      <Timepicker>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>

      <span>Subset of timezone</span>

      <Timepicker timezones={['UTC-10', 'UTC+0', 'UTC+10']}>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>
    </>
}
```

### With Seconds

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { Timepicker, TimepickerControl, TimepickerTimezoneList } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Timepicker withSeconds>
        <TimepickerControl />
      </Timepicker>

      <Timepicker withSeconds>
        <TimepickerControl />

        <TimepickerTimezoneList />
      </Timepicker>
    </>
}
```

## React Components/Toaster

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `dismissible` | `` | No |  | Whether toasts can be manually removed. |
| `duration` | `` | No | 3000 | Toast duration before being removed. |
| `max` | `` | No | 3 | The maximum number of toast displayed at the same time. |
| `position` | `` | No | TOASTER_POSITION.topEnd | The position on screen where the toasts will appear. |


## Examples


### Colors

```tsx
{
  globals: {
    imports: `import { BUTTON_COLOR, Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster id="colors" />

      <div style={{
      display: 'flex',
      gap: '8px',
      flexWrap: 'wrap'
    }}>
        <Button color={BUTTON_COLOR.critical} onClick={() => toast.critical('Critical', {
        toasterId: 'colors'
      })}>
          Critical toast
        </Button>

        <Button color={BUTTON_COLOR.information} onClick={() => toast.information('Information', {
        toasterId: 'colors'
      })}>
          Information toast
        </Button>

        <Button color={BUTTON_COLOR.neutral} onClick={() => toast.neutral('Neutral', {
        toasterId: 'colors'
      })}>
          Neutral toast
        </Button>

        <Button color={BUTTON_COLOR.primary} onClick={() => toast.primary('Primary', {
        toasterId: 'colors'
      })}>
          Primary toast
        </Button>

        <Button color={BUTTON_COLOR.success} onClick={() => toast.success('Success', {
        toasterId: 'colors'
      })}>
          Success toast
        </Button>

        <Button color={BUTTON_COLOR.warning} onClick={() => toast.warning('Warning', {
        toasterId: 'colors'
      })}>
          Warning toast
        </Button>
      </div>
    </>
}
```

### Custom Content

```tsx
{
  globals: {
    imports: `import { ICON_NAME, TEXT_PRESET, Button, Link, Text, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster id="custom-content" />

      <Button onClick={() => toast(<div>
            <Text preset={TEXT_PRESET.label}>
              Toast title
            </Text>

            <Text>
              Toast text helps users providing feedback or information.
            </Text>

            <Link>
              Some Link
            </Link>
          </div>, {
      icon: ICON_NAME.circleInfo,
      toasterId: 'custom-content'
    })}>
        Trigger toast
      </Button>
    </>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster />

      <Button onClick={() => toast('Notification message')}>
        Trigger toast
      </Button>
    </>
}
```

### Demo

```tsx
{
  render: arg => <>
      <Toaster {...arg} />

      <Button onClick={() => toast('Notification message')}>
        Trigger toast
      </Button>
    </>,
  argTypes: orderControls({
    dismissible: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    duration: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    },
    position: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'TOASTER_POSITION'
        }
      },
      control: {
        type: 'select'
      },
      options: TOASTER_POSITIONS
    },
    max: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    }
  })
}
```

### Dismissible

```tsx
{
  globals: {
    imports: `import { Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster dismissible={false} id="dismissible" />

      <div style={{
      display: 'flex',
      gap: '8px',
      flexWrap: 'wrap'
    }}>
        <Button onClick={() => toast('Non dismissible toast', {
        toasterId: 'dismissible'
      })}>
          Trigger non dismissible toast
        </Button>

        <Button onClick={() => toast('Dismissible toast', {
        dismissible: true,
        toasterId: 'dismissible'
      })}>
          Trigger dismissible toast
        </Button>
      </div>
    </>
}
```

### Duration

```tsx
{
  globals: {
    imports: `import { Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster duration={Infinity} id="duration" />

      <div style={{
      display: 'flex',
      gap: '8px',
      flexWrap: 'wrap'
    }}>
        <Button onClick={() => toast('Infinite toast', {
        toasterId: 'duration'
      })}>
          Trigger infinite toast
        </Button>

        <Button onClick={() => toast('3 seconds toast', {
        duration: 3000,
        toasterId: 'duration'
      })}>
          Trigger 3 seconds toast
        </Button>
      </div>
    </>
}
```

### Icon

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster id="icon" />

      <Button onClick={() => toast('Notification message with icon', {
      icon: ICON_NAME.circleInfo,
      toasterId: 'icon'
    })}>
        Trigger toast
      </Button>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => {
    const [count, setCount] = useState(1);
    function createToast(): void {
      toast(`Toast message ${count}`, {
        toasterId: 'overview'
      });
      setCount(c => c + 1);
    }
    return <>
        <Toaster id="overview" />

        <Button onClick={createToast}>
          Trigger toast
        </Button>
      </>;
  }
}
```

### Position

```tsx
{
  globals: {
    imports: `import { ICON_NAME, TOASTER_POSITION, Button, Toaster, toast } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster id="top-start" position={TOASTER_POSITION.topStart} />
      <Toaster id="top" position={TOASTER_POSITION.top} />
      <Toaster id="top-end" position={TOASTER_POSITION.topEnd} />
      <Toaster id="bottom-start" position={TOASTER_POSITION.bottomStart} />
      <Toaster id="bottom" position={TOASTER_POSITION.bottom} />
      <Toaster id="bottom-end" position={TOASTER_POSITION.bottomEnd} />

      <div style={{
      display: 'grid',
      gridTemplateColumns: 'repeat(3, 1fr)',
      gridTemplateRows: 'repeat(2, 1fr)',
      gap: '20px'
    }}>
        <Button onClick={() => toast('Top Start', {
        toasterId: 'top-start'
      })}>
          Top Start
        </Button>

        <Button onClick={() => toast('Top', {
        toasterId: 'top'
      })}>
          Top
        </Button>

        <Button onClick={() => toast('Top End', {
        toasterId: 'top-end'
      })}>
          Top End
        </Button>

        <Button onClick={() => toast('Bottom Start', {
        toasterId: 'bottom-start'
      })}>
          Bottom Start
        </Button>

        <Button onClick={() => toast('Bottom', {
        toasterId: 'bottom'
      })}>
          Bottom
        </Button>

        <Button onClick={() => toast('Bottom End', {
        toasterId: 'bottom-end'
      })}>
          Bottom End
        </Button>
      </div>
    </>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Toaster duration={Infinity} max={Infinity} />

      <div style={{
      display: 'flex',
      gap: '8px',
      flexWrap: 'wrap'
    }}>
        <Button color={BUTTON_COLOR.critical} onClick={() => toast.critical('Critical', {
        icon: ICON_NAME.circleXmark
      })}>
          Critical toast
        </Button>

        <Button color={BUTTON_COLOR.information} onClick={() => toast.information('Information', {
        icon: ICON_NAME.circleInfo
      })}>
          Information toast
        </Button>

        <Button color={BUTTON_COLOR.neutral} onClick={() => toast.neutral('Neutral', {
        icon: ICON_NAME.email
      })}>
          Neutral toast
        </Button>

        <Button color={BUTTON_COLOR.primary} onClick={() => toast.primary('Primary', {
        icon: ICON_NAME.lightbulb
      })}>
          Primary toast
        </Button>

        <Button color={BUTTON_COLOR.success} onClick={() => toast.success('Success', {
        icon: ICON_NAME.circleCheck
      })}>
          Success toast
        </Button>

        <Button color={BUTTON_COLOR.warning} onClick={() => toast.warning('Warning', {
        icon: ICON_NAME.triangleExclamation
      })}>
          Warning toast
        </Button>
      </div>
    </>
}
```

## React Components/Toggle

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `checked` | `` | No |  | The controlled checked state of the toggle. |
| `defaultChecked` | `` | No |  | The initial checked state of the toggle. Use when you don't need to control the checked state of the toggle. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `invalid` | `` | No |  | Whether the component is in error state. |
| `name` | `` | No |  | The name of the form element. Useful for form submission. |
| `onCheckedChange` | `` | No |  | Callback fired when the checked state changes. |
| `required` | `` | No |  | Whether the component is required. |
| `value` | `` | No |  | The value of form element. Useful for form submission. |
| `withLabels` | `` | No |  | Whether the component displays "ON/OFF" labels. |


## Subcomponents


### ToggleControl




### ToggleLabel



## Examples


### Accessibility Aria Label

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl, ToggleLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle aria-label="Enable dark mode">
      <ToggleControl />

      <ToggleLabel>
        Dark mode
      </ToggleLabel>
    </Toggle>
}
```

### Accessibility Label

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl, ToggleLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle>
      <ToggleControl />

      <ToggleLabel>
        Enable dark mode
      </ToggleLabel>
    </Toggle>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle>
      <ToggleControl />
    </Toggle>
}
```

### Demo

```tsx
{
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    invalid: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    withLabels: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle disabled>
      <ToggleControl />
    </Toggle>
}
```

### In Form Field

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'column',
    gap: '8px'
  }}>{story()}</div>],
  globals: {
    imports: `import { TEXT_PRESET, FormField, Text, Toggle, ToggleControl, ToggleLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Text preset={TEXT_PRESET.label}>
        Notification settings:
      </Text>

      <FormField>
        <Toggle>
          <ToggleControl />

          <ToggleLabel>
            General Information
          </ToggleLabel>
        </Toggle>
      </FormField>

      <FormField>
        <Toggle>
          <ToggleControl />

          <ToggleLabel>
            Promotions
          </ToggleLabel>
        </Toggle>
      </FormField>
    </>
}
```

### Invalid

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle invalid>
      <ToggleControl />
    </Toggle>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Toggle>
      <ToggleControl />
    </Toggle>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    gap: '24px',
    alignItems: 'center'
  }}>
      <Toggle>
        <ToggleControl />
      </Toggle>

      <Toggle disabled>
        <ToggleControl />
      </Toggle>

      <Toggle invalid>
        <ToggleControl />
      </Toggle>

      <Toggle withLabels>
        <ToggleControl />
      </Toggle>

      <Toggle>
        <ToggleControl />
        <ToggleLabel>With label</ToggleLabel>
      </Toggle>
    </div>
}
```

### With Label

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl, ToggleLabel } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle>
      <ToggleControl />

      <ToggleLabel>
        Enable dark mode
      </ToggleLabel>
    </Toggle>
}
```

### With Labels

```tsx
{
  globals: {
    imports: `import { Toggle, ToggleControl } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Toggle withLabels>
      <ToggleControl />
    </Toggle>
}
```

## React Components/Tooltip

## Subcomponents


### TooltipContent



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `createPortal` | `` | No | true | Whether the component should be rendered in the DOM close to the body tag. |
| `withArrow` | `` | No | false | Whether the component displays an arrow pointing to the trigger. |



### TooltipTrigger



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `asChild` | `` | No |  | Use the provided child element as the default rendered element, combining their props and behavior. Be careful to pass an actual Node, not a Fragment. |


## Examples


### Accessibility Tooltip

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon, Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tooltip>
      <TooltipTrigger asChild>
        <Icon aria-label="Open tooltip" name={ICON_NAME.circleInfo} role="img" style={{
        fontSize: '24px'
      }} />
      </TooltipTrigger>

      <TooltipContent>
        Some additional context.
      </TooltipContent>
    </Tooltip>
}
```

### Controlled

```tsx
{
  decorators: [story => <div style={{
    display: 'flex',
    flexFlow: 'row',
    gap: '8px',
    alignItems: 'center'
  }}>{story()}</div>],
  globals: {
    imports: `import { ICON_NAME, Button, Icon, Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  tags: ['!dev'],
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  render: ({}) => {
    const [isOpen, setIsOpen] = useState(false);
    function toggleTooltip() {
      setIsOpen(isOpen => !isOpen);
    }
    return <>
        <Button onClick={toggleTooltip}>
          Toggle tooltip
        </Button>

        <Tooltip open={isOpen}>
          <TooltipTrigger asChild>
            <Icon name={ICON_NAME.circleQuestion} style={{
            fontSize: '24px'
          }} />
          </TooltipTrigger>

          <TooltipContent withArrow>
            This is the tooltip content
          </TooltipContent>
        </Tooltip>
      </>;
  }
}
```

### Custom Trigger

```tsx
{
  globals: {
    imports: `import { ICON_NAME, Icon, Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tooltip>
      <TooltipTrigger asChild>
        <Icon name={ICON_NAME.circleQuestion} style={{
        fontSize: '24px'
      }} />
      </TooltipTrigger>

      <TooltipContent>
        This is the tooltip content
      </TooltipContent>
    </Tooltip>
}
```

### Default

```tsx
{
  globals: {
    imports: `import { Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <Tooltip>
      <TooltipTrigger>
        Show tooltip
      </TooltipTrigger>

      <TooltipContent>
        This is the tooltip content
      </TooltipContent>
    </Tooltip>
}
```

### Demo

```tsx
{
  parameters: {
    layout: 'centered'
  },
  render: (arg: DemoArg) => <Tooltip closeDelay={arg.closeDelay} openDelay={arg.openDelay} position={arg.position}>
      <TooltipTrigger asChild>
        <Icon name={ICON_NAME.circleQuestion} style={{
        fontSize: '24px'
      }} />
      </TooltipTrigger>

      <TooltipContent withArrow={arg.withArrow}>
        {arg.content}
      </TooltipContent>
    </Tooltip>,
  argTypes: orderControls({
    closeDelay: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    },
    content: {
      table: {
        category: CONTROL_CATEGORY.slot
      },
      control: 'text'
    },
    openDelay: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'number'
    },
    position: {
      table: {
        category: CONTROL_CATEGORY.general,
        type: {
          summary: 'TOOLTIP_POSITION'
        }
      },
      control: {
        type: 'select'
      },
      options: TOOLTIP_POSITIONS
    },
    withArrow: {
      table: {
        category: CONTROL_CATEGORY.design,
        defaultValue: {
          summary: false
        },
        type: {
          summary: 'boolean'
        }
      },
      control: {
        type: 'boolean'
      }
    }
  }),
  args: {
    content: 'My tooltip content'
  }
}
```

### Grid

```tsx
{
  decorators: [story => <div style={{
    display: 'grid',
    gridTemplateColumns: 'repeat(3, 1fr)',
    gridTemplateRows: 'repeat(3, 1fr)',
    gap: '20px',
    padding: '50px 150px'
  }}>
      {story()}
    </div>],
  globals: {
    imports: `import { Tooltip, TooltipContent, TooltipTrigger } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => <>
      <Tooltip position="top-start">
        <TooltipTrigger>
          Top Left
        </TooltipTrigger>
        <TooltipContent withArrow>
          Top Left tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="top">
        <TooltipTrigger>
          Top
        </TooltipTrigger>
        <TooltipContent withArrow>
          Top tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="top-end">
        <TooltipTrigger>
          Top Right
        </TooltipTrigger>
        <TooltipContent withArrow>
          Top Right tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="left">
        <TooltipTrigger>
          Middle Left
        </TooltipTrigger>
        <TooltipContent withArrow>
          Middle Left tooltip
        </TooltipContent>
      </Tooltip>

      <div />

      <Tooltip position="right">
        <TooltipTrigger>
          Middle Right
        </TooltipTrigger>
        <TooltipContent withArrow>
          Middle Right tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="bottom-start">
        <TooltipTrigger>
          Bottom Left
        </TooltipTrigger>
        <TooltipContent withArrow>
          Bottom Left tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="bottom">
        <TooltipTrigger>
          Bottom
        </TooltipTrigger>
        <TooltipContent withArrow>
          Bottom tooltip
        </TooltipContent>
      </Tooltip>

      <Tooltip position="bottom-end">
        <TooltipTrigger>
          Bottom Right
        </TooltipTrigger>
        <TooltipContent withArrow>
          Bottom Right tooltip
        </TooltipContent>
      </Tooltip>
    </>
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  parameters: {
    layout: 'centered'
  },
  render: ({}) => <Tooltip>
      <TooltipTrigger asChild>
        <Icon name={ICON_NAME.circleQuestion} style={{
        fontSize: '24px'
      }} />
      </TooltipTrigger>

      <TooltipContent>
        This is the tooltip content
      </TooltipContent>
    </Tooltip>
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => <div style={{
    display: 'flex',
    gap: '16px',
    alignItems: 'center'
  }}>
      <Tooltip>
        <TooltipTrigger asChild>
          <Icon name={ICON_NAME.circleQuestion} style={{
          fontSize: '24px'
        }} />
        </TooltipTrigger>
        <TooltipContent createPortal={false}>This is the tooltip content</TooltipContent>
      </Tooltip>

      <Tooltip>
        <TooltipTrigger asChild>
          <Icon name={ICON_NAME.circleInfo} style={{
          fontSize: '24px'
        }} />
        </TooltipTrigger>
        <TooltipContent createPortal={false} withArrow>This is the tooltip content</TooltipContent>
      </Tooltip>
    </div>
}
```

## React Components/Tree View

## Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `defaultExpandedValue` | `` | No |  | The initial expanded value(s). Use when you don't need to control the expanded value(s) of the tree view. |
| `defaultValue` | `` | No |  | The initial selected value(s). Use when you don't need to control the selected value(s) of the tree view. |
| `disabled` | `` | No |  | Whether the component is disabled. |
| `expandedValue` | `` | No |  | The controlled expanded value(s). |
| `items` | `` | Yes |  | The list of items |
| `multiple` | `` | No |  | Whether the multiple selection is allowed. |
| `onExpandedChange` | `` | No |  | Callback fired when the expanded value(s) changes. |
| `onValueChange` | `` | No |  | Callback fired when the value(s) changes. |
| `value` | `` | No |  | The controlled selected value(s). |


## Subcomponents


### TreeViewNode



#### Props


| Name | Type | Required | Default | Description |
|------|------|----------|---------|-------------|
| `indexPath` | `` | No |  | @internal * |
| `item` | `` | Yes |  | The tree node to render. |


## Examples


### Controlled

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes, type TreeViewValueChangeDetail } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    const [selectedId, setSelectedId] = useState<string | undefined>('package.json');
    return <>
        <TreeView items={items} onValueChange={(d: TreeViewValueChangeDetail) => setSelectedId(d.value[0])} value={selectedId ? [selectedId] : undefined}>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item} />)}
          </TreeViewNodes>
        </TreeView>
        <div style={{
        marginTop: 8
      }}>Selected: {selectedId ?? 'None'}</div>
      </>;
  }
}
```

### Controlled Multiple

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes, type TreeViewValueChangeDetail } from '@ovhcloud/ods-react';
import { useState } from 'react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    const [selectedIds, setSelectedIds] = useState<string[]>(['package.json', 'index.ts']);
    return <>
        <TreeView items={items} multiple onValueChange={(d: TreeViewValueChangeDetail) => setSelectedIds(Array.isArray(d.value) ? d.value : [d.value].filter(Boolean) as string[])} value={selectedIds}>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item} />)}
          </TreeViewNodes>
        </TreeView>
        <div style={{
        marginTop: 8
      }}>Selected: {selectedIds.length ? selectedIds.join(', ') : 'None'}</div>
      </>;
  }
}
```

### Custom Render

```tsx
{
  globals: {
    imports: `import { Icon, ICON_NAME, TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item}>
              {({
            item,
            isBranch,
            isExpanded
          }) => <span style={{
            display: 'inline-flex',
            alignItems: 'center',
            gap: 6
          }}>
                  {isBranch ? isExpanded ? <Icon name={ICON_NAME.folderMinus} /> : <Icon name={ICON_NAME.folderPlus} /> : <Icon name={ICON_NAME.file} />}
                  <span>{item.name}</span>
                </span>}
            </TreeViewNode>)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Default

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Default Expanded Value

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView defaultExpandedValue={["src", "components"]} items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Demo

```tsx
{
  render: arg => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView defaultExpandedValue={arg.defaultExpandedValue} disabled={arg.disabled} expandedValue={arg.expandedValue} items={items} multiple={arg.multiple}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  },
  argTypes: orderControls({
    disabled: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    },
    multiple: {
      table: {
        category: CONTROL_CATEGORY.general
      },
      control: 'boolean'
    }
  })
}
```

### Disabled

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView disabled items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Disabled Items

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts',
        disabled: true
      }, {
        id: 'components',
        name: 'components',
        disabled: true,
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx',
          disabled: true
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json',
      disabled: true
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Dynamic Children

```tsx
{
  globals: {
    imports: `import { Button, BUTTON_COLOR, BUTTON_SIZE, BUTTON_VARIANT, Icon, ICON_NAME, TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';
import { useRef, useState } from 'react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    type Item = {
      id: string;
      name: string;
      children?: Item[];
    };
    const [items, setItems] = useState<Item[]>([{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: []
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }]);
    const counter = useRef(1);
    function addChildTo(collection: Item[], parentId: string, newNode: Item): Item[] {
      return collection.map(node => {
        if (node.id === parentId) {
          const nextChildren = Array.isArray(node.children) ? [...node.children, newNode] : [newNode];
          return {
            ...node,
            children: nextChildren
          };
        }
        if (node.children?.length) {
          return {
            ...node,
            children: addChildTo(node.children, parentId, newNode)
          };
        }
        return node;
      });
    }
    function removeNodeFrom(collection: Item[], nodeId: string): Item[] {
      return collection.filter(node => node.id !== nodeId).map(node => node.children?.length ? {
        ...node,
        children: removeNodeFrom(node.children, nodeId)
      } : node);
    }
    function handleAddChild(parentId: string): void {
      const id = `new-file-${counter.current++}.txt`;
      const newNode = {
        id,
        name: id
      };
      setItems(prev => addChildTo(prev, parentId, newNode));
    }
    function handleDelete(nodeId: string): void {
      setItems(prev => removeNodeFrom(prev, nodeId));
    }
    function handleAddRootFile(): void {
      const id = `new-file-${counter.current++}.txt`;
      const newNode = {
        id,
        name: id
      };
      setItems(prev => [...prev, newNode]);
    }
    return <div>
        <div style={{
        marginBottom: 16
      }}>
          <Button aria-label="Add file at root level" onClick={handleAddRootFile} size={BUTTON_SIZE.xs} variant={BUTTON_VARIANT.outline}>
            <Icon name={ICON_NAME.plus} />
            Add file at root level
          </Button>
        </div>
        <TreeView items={items} multiple>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item}>
                {({
              item,
              isBranch
            }) => <div style={{
              display: 'flex',
              alignItems: 'center',
              width: '100%',
              justifyContent: 'space-between'
            }}>
                    <span style={{
                display: 'inline-flex',
                alignItems: 'center',
                gap: 6
              }}>
                      {isBranch ? <Icon name={ICON_NAME.folder} /> : <Icon name={ICON_NAME.file} />}
                      <span>{item.name}</span>
                    </span>
                    <div style={{
                display: 'inline-flex',
                marginLeft: 'auto',
                alignItems: 'center',
                gap: 8
              }}>
                      {isBranch ? <Button aria-label="Add child" onClick={e => {
                  e.stopPropagation();
                  handleAddChild(item.id);
                }} size={BUTTON_SIZE.xs} onKeyDown={e => {
                  e.stopPropagation();
                }} variant={BUTTON_VARIANT.outline}>
                          <Icon name={ICON_NAME.plus} />
                        </Button> : null}
                      <Button aria-label="Delete" color={BUTTON_COLOR.critical} onClick={e => {
                  e.stopPropagation();
                  handleDelete(item.id);
                }} onMouseDown={e => {
                  e.stopPropagation();
                }} onKeyDown={e => {
                  e.stopPropagation();
                }} size={BUTTON_SIZE.xs} variant={BUTTON_VARIANT.outline}>
                        <Icon name={ICON_NAME.trash} />
                      </Button>
                    </div>
                  </div>}
              </TreeViewNode>)}
          </TreeViewNodes>
        </TreeView>
      </div>;
  }
}
```

### In Form Field

```tsx
{
  globals: {
    imports: `import { FormField, FormFieldLabel, TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <FormField>
        <FormFieldLabel>Choose a file</FormFieldLabel>
        <TreeView items={items}>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item} />)}
          </TreeViewNodes>
        </TreeView>
      </FormField>;
  }
}
```

### Multiple

```tsx
{
  globals: {
    imports: `import { TreeView, TreeViewNode, TreeViewNodes } from '@ovhcloud/ods-react';`
  },
  parameters: {
    docs: {
      source: {
        ...staticSourceRenderConfig()
      }
    }
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView items={items} multiple>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Overview

```tsx
{
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'app.tsx',
        name: 'app.tsx'
      }, {
        id: 'index.ts',
        name: 'index.ts'
      }, {
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'Card.tsx',
          name: 'Card.tsx'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }, {
      id: 'readme.md',
      name: 'README.md'
    }];
    return <TreeView items={items}>
        <TreeViewNodes>
          {items.map(item => <TreeViewNode key={item.id} item={item} />)}
        </TreeViewNodes>
      </TreeView>;
  }
}
```

### Theme Generator

```tsx
{
  parameters: {
    layout: 'fullscreen'
  },
  tags: ['!dev'],
  render: ({}) => {
    const items = [{
      id: 'src',
      name: 'src',
      children: [{
        id: 'components',
        name: 'components',
        children: [{
          id: 'Button.tsx',
          name: 'Button.tsx'
        }, {
          id: 'button.scss',
          name: 'button.scss'
        }]
      }]
    }, {
      id: 'package.json',
      name: 'package.json'
    }];
    return <div style={{
      display: 'flex',
      gap: '24px'
    }}>
        <TreeView items={items}>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item} />)}
          </TreeViewNodes>
        </TreeView>

        <TreeView disabled items={items}>
          <TreeViewNodes>
            {items.map(item => <TreeViewNode key={item.id} item={item} />)}
          </TreeViewNodes>
        </TreeView>
      </div>;
  }
}
```
