(['dog']);
+ return (
+ <>
+ setValue(details.value ?? [])}
+ >
+ Controlled combobox
+
+
+
+
+ Selected value: {value[0] ?? 'None'}
+
+ >
+ );
+ },
+};
+
+export const Highlight: Story = {
+ tags: ['!dev'],
+ render: () => (
+
+ Label
+
+
+
+ ),
+};
+
+export const CustomOptions: Story = {
+ tags: ['!dev'],
+ render: () => {
+ 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' } },
+ ];
+ const customOptionRenderer = ({ label, customData }: ComboboxCustomOptionRendererArg) => {
+ const data = (customData || {}) as Record;
+ const color = typeof data.color === 'string' ? data.color : undefined;
+ const info = typeof data.info === 'string' ? data.info : '';
+ return (
+
+ {label} ({info})
+
+ );
+ };
+ return (
+
+ Label
+
+
+
+ );
+ },
+};
+
+export const Empty: Story = {
+ tags: ['!dev'],
+ render: () => (
+
+ Label
+
+
+
+ ),
+};
+
+export const Placeholder: Story = {
+ tags: ['!dev'],
+ render: () => (
+
+ Label
+
+
+
+ ),
+};
diff --git a/packages/storybook/stories/components/combobox/documentation.mdx b/packages/storybook/stories/components/combobox/documentation.mdx
new file mode 100644
index 0000000000..1e3ec4ff74
--- /dev/null
+++ b/packages/storybook/stories/components/combobox/documentation.mdx
@@ -0,0 +1,153 @@
+import { Canvas, Meta } from '@storybook/blocks';
+import * as ComboboxStories from './combobox.stories';
+import { Banner } from '../../../src/components/banner/Banner';
+import { BestPractices } from '../../../src/components/bestPractices/BestPractices';
+import { Heading } from '../../../src/components/heading/Heading';
+import { IdentityCard } from '../../../src/components/identityCard/IdentityCard';
+import { StorybookLink } from '../../../src/components/storybookLink/StorybookLink';
+import { ATOMIC_TYPE } from '../../../src/constants/atomicDesign';
+import { ODS_COMPONENTS_TITLE, STORY } from '../../../src/constants/meta';
+
+
+
+
+
+_**Combobox** allows users to search, select, and add items from a dynamic or predefined list._
+
+
+
+
+
+
+ **Combobox** component allows users to search for and select items from a dynamic list of suggestions or a predefined set of allowed values. It supports both single and multiple selection modes and enables users to create new entries.
+
+
+
+
+
+
+1. Input where the user types the search query. It displays the current input value or selected tags (multiple selection mode)
+2. **Dropdown list** displaying a scrollable list of suggested items. Items can be customized using a custom renderer.
+3. Tag _(multiple selection mode)_ to display selected items as tags inside the Input field
+4. **Clearable** Button _(optional)_ to allow users to clear the input content
+5. Spinner _(optional)_ to indicate that data is being fetched
+6. **Add entry option** _(optional)_ allows users to create new entries when no matching result is found. The label is customizable
+7. **Empty state message** is a customizable message displayed when no suggestion match the query
+
+
+
+The **Combobox** is best suited when users need to:
+* search within a dataset and dynamically refine results
+* provide suggestions based on user input (e.g., domain names, tags, predictive search)
+* allow users to add custom values when applicable (e.g., creating tags)
+
+
+
+
+
+
+
+The dropdown is positioned below the Input field when there is enough space.
+
+The dropdown width should match the Input field width.
+
+In multiple selection mode, the Input field height grows dynamically to accommodate selected tags.
+
+
+
+
+
+The dropdown appears when the user clicks on the input field.
+
+
+
+Selecting an item triggers a custom event, allowing integrators to process the selected value(s).
+
+
+
+Clicking on an item selects it, closes the dropdown, and updates the Input field value.
+
+If the user exits the field without selecting an item, the input reverts to the placeholder or the last selected value (if any).
+
+
+
+User can create new entries when no matching result exists. An **"Add entry"** option appears at the top of the dropdown (label is customizable).
+
+New entries can be added by clicking on the "Add entry" option.
+
+
+
+* search input is case-insensitive (e.g., searching for "a" will match "A")
+
+* newly created entries are case-sensitive (e.g., adding "cat" will not conflict with an existing "Cat" item)
+
+Users cannot create an entry that is already selected as a tag.
+
+If a custom entry added via "Add Entry" option is removed, it does not reappear in the dropdown, as it was not part of the original list.
+
+
+
+If the clearable option is enabled, a dedicated Button appears inside the Input field when it contains text:
+
+* clicking the clearable Button resets the Input field, removing any entered text or selected value(s)
+* the dropdown opens after clearing the Input field
+* in multiple selection mode, only the current Input text is cleared; selected tags remain
+
+
+
+A Spinner can be displayed in the Input field when results are being fetched.
+
+
+
+When no matching results are found, a customizable message is displayed in the dropdown.
+
+This state can be combined with the "Add entry" option.
+
+
+
+Items can be categorized into groups in both single and multiple selection modes.
+
+Group titles cannot be selected, clicked and are excluded from search.
+
+
+
+
+
+The Input field can be focused using the `Tab` key. Pressing `Tab` again moves focus to the next element and closes the dropdown.
+
+If the Input field is clearable, pressing `Tab` first moves focus to the clear button, then to the next element.
+
+Pressing `Shift` + `Tab` moves focus to the previous interactive element without confirming any item.
+
+
+
+Pressing `Escape` closes the dropdown without selection.
+
+If the user has typed in the Input field but not made a new selection, pressing `Escape` resets the Input to the previously selected value. Any unsaved input is discarded.
+
+
+
+Pressing `Arrow Up/Down` navigates through items in the dropdown.
+
+Pressing `Enter` selects the hovered item and closes the dropdown.
+
+Pressing `Backspace` deletes the last character in the Input field (it does not clear the entire field at once).
diff --git a/packages/storybook/stories/components/combobox/technical-information.mdx b/packages/storybook/stories/components/combobox/technical-information.mdx
new file mode 100644
index 0000000000..ef1f932838
--- /dev/null
+++ b/packages/storybook/stories/components/combobox/technical-information.mdx
@@ -0,0 +1,58 @@
+import { Canvas, Meta } from '@storybook/blocks';
+import SpecificationsCombobox from '../../../../ods-react/src/components/combobox/documentation/combobox.json';
+import { Banner } from '../../../src/components/banner/Banner';
+import { Heading } from '../../../src/components/heading/Heading';
+import { TechnicalSpecification } from '../../../src/components/technicalSpecification/TechnicalSpecification';
+import * as ComboboxStories from './combobox.stories';
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/yarn.lock b/yarn.lock
index 99cbd63a30..8f604a4af4 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4025,6 +4025,12 @@ __metadata:
languageName: unknown
linkType: soft
+"@ovhcloud/ods-react-combobox@workspace:packages/ods-react/src/components/combobox":
+ version: 0.0.0-use.local
+ resolution: "@ovhcloud/ods-react-combobox@workspace:packages/ods-react/src/components/combobox"
+ languageName: unknown
+ linkType: soft
+
"@ovhcloud/ods-react-datepicker@workspace:packages/ods-react/src/components/datepicker":
version: 0.0.0-use.local
resolution: "@ovhcloud/ods-react-datepicker@workspace:packages/ods-react/src/components/datepicker"