diff --git a/.betterer.results b/.betterer.results
index 5819e039e20..3d31f01d4ef 100644
--- a/.betterer.results
+++ b/.betterer.results
@@ -5720,9 +5720,6 @@ exports[`no undocumented stories`] = {
"packages/grafana-ui/src/components/ButtonCascader/ButtonCascader.story.tsx:5381": [
[0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"]
],
- "packages/grafana-ui/src/components/Combobox/Combobox.story.tsx:5381": [
- [0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"]
- ],
"packages/grafana-ui/src/components/DateTimePickers/RelativeTimeRangePicker/RelativeTimeRangePicker.story.tsx:5381": [
[0, 0, 0, "No undocumented stories are allowed, please add an .mdx file with some documentation", "5381"]
],
diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.mdx b/packages/grafana-ui/src/components/Combobox/Combobox.mdx
new file mode 100644
index 00000000000..7c93072b57a
--- /dev/null
+++ b/packages/grafana-ui/src/components/Combobox/Combobox.mdx
@@ -0,0 +1,101 @@
+import { Meta, Preview, ArgTypes } from '@storybook/blocks';
+
+import { Combobox } from './Combobox';
+
+
+
+## Usage
+
+**Do**
+
+- Use in inline query editors
+- Use when you require async calls from a select input
+
+**Don't**
+
+- Use the async functionality, when all items are only loaded on the initial load
+- Use when fewer than 4 items are needed, as a `RadioButtonGroup` may be more suitable (not for inline use cases)
+- Use this component if you need custom option styling
+
+## ComboboxOption
+
+The `ComboboxOption` currently supports 3 properties:
+
+- `label` - The text that is visible in the menu.
+- `value` (required) - The value that is selected.
+- `description` - A longer description that describes the choice.
+
+If no `label` is given, `value` will be used as a display value.
+
+## Sizing
+
+The recommended way to set the width is by sizing the container element. This is so it may reflect a similar size as other inputs in the context.
+
+If that is not possible, the width can be set directly on the component, by setting a number, which is a multiple of `8px`.
+
+For inline usage, such as in query editors, it may be useful to size the input based on the content. Set `width="auto"` to achieve this. In this case, it is also recommended to set `maxWidth` and `minWidth`.
+
+## Async Usage
+
+The `options` prop can accept an async function:
+
+- When the menu opens, the `options` function is called with `''`, to load all options.
+- When the user types, the `options` function is called with the current input value.
+
+Note: The calls are debounced. Old calls are invalidated when a new call is made.
+
+## Unit testing
+
+Writing unit tests with Combobox requires mocking the `getBoundingClientRect` method because of [the virtual list library](https://github.com/TanStack/virtual/issues/29#issuecomment-657519522)
+
+This code sets up the mocking before all tests:
+
+```js
+beforeAll(() => {
+ const mockGetBoundingClientRect = jest.fn(() => ({
+ width: 120,
+ height: 120,
+ top: 0,
+ left: 0,
+ bottom: 0,
+ right: 0,
+ }));
+
+ Object.defineProperty(Element.prototype, 'getBoundingClientRect', {
+ value: mockGetBoundingClientRect,
+ });
+});
+```
+
+### Selecting an option
+
+To select an option, you can use any `*ByRole` methods, as Combobox has proper roles for accessibility.
+
+#### Selecting option by clicking
+
+```js
+render();
+
+const input = screen.getByRole('combobox');
+await userEvent.click(input);
+
+const item = await screen.findByRole('option', { name: 'Option 1' });
+await userEvent.click(item);
+expect(screen.getByDisplayValue('Option 1')).toBeInTheDocument();
+```
+
+#### Selecting option by typing
+
+```js
+render();
+
+const input = screen.getByRole('combobox');
+await userEvent.type(input, 'Option 3');
+await userEvent.keyboard('{ArrowDown}{Enter}');
+
+expect(screen.getByDisplayValue('Option 3')).toBeInTheDocument();
+```
+
+## Props
+
+
diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx
index 967b7537b8d..1b63c36bec1 100644
--- a/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx
+++ b/packages/grafana-ui/src/components/Combobox/Combobox.story.tsx
@@ -11,12 +11,18 @@ import { Field } from '../Forms/Field';
import { AsyncSelect, Select } from '../Select/Select';
import { Combobox, ComboboxOption } from './Combobox';
+import mdx from './Combobox.mdx';
type PropsAndCustomArgs = ComponentProps & { numberOfOptions: number };
const meta: Meta = {
title: 'Forms/Combobox',
component: Combobox,
+ parameters: {
+ docs: {
+ page: mdx,
+ },
+ },
args: {
loading: undefined,
invalid: undefined,
diff --git a/packages/grafana-ui/src/components/Combobox/Combobox.tsx b/packages/grafana-ui/src/components/Combobox/Combobox.tsx
index eb8112ba6be..a0086614259 100644
--- a/packages/grafana-ui/src/components/Combobox/Combobox.tsx
+++ b/packages/grafana-ui/src/components/Combobox/Combobox.tsx
@@ -27,7 +27,13 @@ export type ComboboxOption = {
// then the onChange handler emits ComboboxOption with the label as non-undefined.
interface ComboboxBaseProps
extends Omit {
+ /**
+ * An `X` appears in the UI, which clears the input and sets the value to `null`. Do not use if you have no `null` case.
+ */
isClearable?: boolean;
+ /**
+ * Allows the user to set a value which is not in the list of options.
+ */
createCustomValue?: boolean;
options: Array> | ((inputValue: string) => Promise>>);
onChange: (option: ComboboxOption | null) => void;
@@ -45,7 +51,13 @@ interface ComboboxBaseProps
type AutoSizeConditionals =
| {
width: 'auto';
+ /**
+ * Needs to be set when width is 'auto' to prevent the input from shrinking too much
+ */
minWidth: number;
+ /**
+ * Recommended to set when width is 'auto' to prevent the input from growing too much.
+ */
maxWidth?: number;
}
| {