📚 Improve documentation for combobox and select in the storybook (#7006)

This commit is contained in:
luisδμ 2025-07-31 09:05:54 +02:00 committed by GitHub
parent 3b04cd37ff
commit 200b69fae2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 96 additions and 102 deletions

View File

@ -11,23 +11,27 @@ import * as ComboboxStories from "./combobox.stories";
# Combobox
Combobox lets users choose one option from an options menu or enter a custom value that is not listed in the menu. It combines the functionality of a dropdown menu and an input field, allowing for both selection and free-form input.
The `combobox*` component lets users choose one option from an options menu or enter a custom value that is not listed in the menu. It combines the functionality of a dropdown menu and an input field, allowing for both selection and free-form input.
## Variants
**Text**: We will use this variant when there are enough space and icons don't add any useful context.
We will use the text-only variant when there are enough space and icons don't add any useful context.
<Canvas of={ComboboxStories.Default} />
**Icon and text**: We will use this variant when there are enough space and icons add any useful context.
We will use the icon and text variant when there are enough space and icons add any useful context.
<Canvas of={ComboboxStories.WithIcons} />
If we consider that empty options have a special meaning, we can move them to the end of the list, to a section separate from the rest.
<Canvas of={ComboboxStories.EmptyToEnd} />
## Technical notes
### Icons
Each option of `combobox*` may accept an `icon`, which must contain an [icon ID](../foundations/assets/icon.mdx).
Each option of `combobox*` accepts an optional `icon`, which must contain an [icon ID](../foundations/assets/icon.mdx).
These are available in the `app.main.ds.foundations.assets.icon` namespace.
```clj
@ -49,8 +53,6 @@ These are available in the `app.main.ds.foundations.assets.icon` namespace.
]}]
```
<Canvas of={ComboboxStories.WithIcons} />
## Usage guidelines (design)
### Where to Use

View File

@ -11,7 +11,27 @@ import { userEvent, within, expect } from "@storybook/test";
const { Combobox } = Components;
let lastValue = null;
const options = [
{ id: "Monday", label: "Monday" },
{ id: "Tuesday", label: "Tuesday" },
{ id: "Wednesday", label: "Wednesday" },
{ id: "Thursday", label: "Thursday" },
{ id: "Friday", label: "Friday" },
{ id: "", label: "(Empty)" },
{ id: "Saturday", label: "Saturday" },
{ id: "Sunday", label: "Sunday" },
];
const optionsWithIcons = [
{ id: "Monday", label: "Monday", icon: "fill-content" },
{ id: "Tuesday", label: "Tuesday", icon: "pentool" },
{ id: "Wednesday", label: "Wednesday" },
{ id: "Thursday", label: "Thursday" },
{ id: "Friday", label: "Friday" },
{ id: "", label: "(Empty)" },
{ id: "Saturday", label: "Saturday" },
{ id: "Sunday", label: "Sunday" },
];
export default {
title: "Controls/Combobox",
@ -20,76 +40,46 @@ export default {
disabled: { control: "boolean" },
maxLength: { control: "number" },
hasError: { control: "boolean" },
emptyToEnd: { control: "boolean" },
},
args: {
disabled: false,
maxLength: 10,
hasError: false,
placeholder: "Select a month",
options: [
{ id: "January", label: "January" },
{ id: "February", label: "February" },
{ id: "March", label: "March" },
{ id: "April", label: "April" },
{ id: "May", label: "May" },
{ id: "June", label: "June" },
{ id: "July", label: "July" },
{ id: "August", label: "August" },
{ id: "September", label: "September" },
{ id: "October", label: "October" },
{ id: "November", label: "November" },
{ id: "December", label: "December" },
],
defaultSelected: "February",
placeholder: "Select a weekday",
emptyToEnd: false,
options: options,
defaultSelected: "Tuesday",
},
parameters: {
controls: {
exclude: ["options", "defaultSelected"],
},
},
render: ({ ...args }) => (
<div style={{ padding: "5px" }}>
<Combobox {...args} />
</div>
),
};
export const Default = {
parameters: {
docs: {
story: {
height: "450px",
height: "320px",
},
},
},
render: ({ ...args }) => <Combobox {...args} />,
};
export const Default = {};
export const WithIcons = {
args: {
options: [
{ id: "January", label: "January", icon: "fill-content" },
{ id: "February", label: "February", icon: "pentool" },
{ id: "March", label: "March" },
{ id: "April", label: "April" },
{ id: "May", label: "May" },
{ id: "June", label: "June" },
{ id: "July", label: "July" },
{ id: "August", label: "August" },
{ id: "September", label: "September" },
{ id: "October", label: "October" },
{ id: "November", label: "November" },
{ id: "December", label: "December" },
],
},
parameters: {
docs: {
story: {
height: "450px",
},
},
options: optionsWithIcons,
},
};
export const EmptyToEnd = {
args: {
emptyToEnd: true,
},
};
let lastValue = null;
export const TestInteractions = {
...WithIcons,
args: {
@ -167,8 +157,8 @@ export const TestInteractions = {
await userEvent.keyboard("{ArrowDown}");
await userEvent.keyboard("{Enter}");
expect(input).toHaveValue("February");
expect(lastValue).toBe("February");
expect(input).toHaveValue("Tuesday");
expect(lastValue).toBe("Tuesday");
await userEvent.clear(input);
// Arrow up
@ -177,11 +167,11 @@ export const TestInteractions = {
await userEvent.keyboard("{ArrowUp}");
await userEvent.keyboard("{ArrowUp}");
expect(combobox).toHaveAttribute("aria-activedescendant", "November");
expect(combobox).toHaveAttribute("aria-activedescendant", "Saturday");
await userEvent.keyboard("{Enter}");
expect(input).toHaveValue("November");
expect(lastValue).toBe("November");
expect(input).toHaveValue("Saturday");
expect(lastValue).toBe("Saturday");
await userEvent.clear(input);
// Home
@ -191,21 +181,21 @@ export const TestInteractions = {
await userEvent.keyboard("{ArrowDown}");
await userEvent.keyboard("{ArrowDown}");
await userEvent.keyboard("{Home}");
expect(combobox).toHaveAttribute("aria-activedescendant", "January");
expect(combobox).toHaveAttribute("aria-activedescendant", "Monday");
await userEvent.keyboard("{Enter}");
expect(input).toHaveValue("January");
expect(lastValue).toBe("January");
expect(input).toHaveValue("Monday");
expect(lastValue).toBe("Monday");
await userEvent.clear(input);
});
await step("Filter with 'Ju' and select July", async () => {
await step("Filter with 'es' (Tuesday, Wednesday) and select Wednesday", async () => {
await userEvent.clear(input);
await userEvent.keyboard("{Escape}");
await userEvent.click(input);
await userEvent.type(input, "Ju");
await userEvent.type(input, "es");
const options = await canvas.findAllByTestId("dropdown-option");
expect(options).toHaveLength(2);
@ -215,8 +205,8 @@ export const TestInteractions = {
await userEvent.keyboard("{Enter}");
expect(input).toHaveValue("July");
expect(lastValue).toBe("July");
expect(input).toHaveValue("Wednesday");
expect(lastValue).toBe("Wednesday");
});
await step("Close dropdown when focusing out", async () => {

View File

@ -11,17 +11,22 @@ import * as SelectStories from "./select.stories";
# Select
Select lets users choose one option from an options menu.
The `select*` component lets users choose one option from an options menu.
## Variants
**Text**: We will use this variant when there are enough space and icons don't add any useful context.
We will use the text-only variant when there are enough space and icons don't add any useful context.
<Canvas of={SelectStories.Default} />
**Icon and text**: We will use this variant when there are enough space and icons add any useful context.
We will use the icon and text variant when there are enough space and icons add any useful context.
<Canvas of={SelectStories.WithIcons} />
If we consider that empty options have a special meaning, we can move them to the end of the list, to a section separate from the rest.
<Canvas of={SelectStories.EmptyToEnd} />
## Technical notes
### Icons
@ -49,8 +54,6 @@ These are available in the `app.main.ds.foundations.assets.icon` namespace.
]}]
```
<Canvas of={SelectStories.WithIcons} />
## Usage guidelines (design)
### Where to use

View File

@ -9,34 +9,42 @@ import Components from "@target/components";
const { Select } = Components;
const options = [
{ id: "option-code", label: "Code" },
{ id: "option-design", label: "Design" },
{ id: "", label: "(Empty)" },
{ id: "option-menu", label: "Menu" },
];
const optionsWithIcons = [
{ id: "option-code", label: "Code", icon: "fill-content" },
{ id: "option-design", label: "Design", icon: "pentool" },
{ id: "", label: "(Empty)" },
{ id: "option-menu", label: "Menu" },
];
export default {
title: "Controls/Select",
component: Select,
argTypes: {
disabled: { control: "boolean" },
emptyToEnd: { control: "boolean" },
},
args: {
disabled: false,
options: [
{
label: "Code",
id: "option-code",
},
{
label: "Design",
id: "option-design",
},
{
label: "Menu",
id: "option-menu",
},
],
options: options,
emptyToEnd: false,
defaultSelected: "option-code",
},
parameters: {
controls: {
exclude: ["options", "defaultSelected"],
},
docs: {
story: {
height: "200px",
},
},
},
render: ({ ...args }) => <Select {...args} />,
};
@ -45,21 +53,12 @@ export const Default = {};
export const WithIcons = {
args: {
options: [
{
label: "Code",
id: "option-code",
icon: "fill-content",
},
{
label: "Design",
id: "option-design",
icon: "pentool",
},
{
label: "Menu",
id: "option-menu",
},
],
options: optionsWithIcons,
},
};
export const EmptyToEnd = {
args: {
emptyToEnd: true,
},
};