mirror of
https://github.com/penpot/penpot.git
synced 2026-04-26 19:58:09 +00:00
📚 Improve documentation for combobox and select in the storybook (#7006)
This commit is contained in:
parent
3b04cd37ff
commit
200b69fae2
@ -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
|
||||
|
||||
@ -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 () => {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
},
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user