mirror of
https://github.com/penpot/penpot.git
synced 2026-09-09 13:39:02 +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
|
||||||
|
|
||||||
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
|
## 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} />
|
<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} />
|
<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
|
## Technical notes
|
||||||
|
|
||||||
### Icons
|
### 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.
|
These are available in the `app.main.ds.foundations.assets.icon` namespace.
|
||||||
|
|
||||||
```clj
|
```clj
|
||||||
@ -49,8 +53,6 @@ These are available in the `app.main.ds.foundations.assets.icon` namespace.
|
|||||||
]}]
|
]}]
|
||||||
```
|
```
|
||||||
|
|
||||||
<Canvas of={ComboboxStories.WithIcons} />
|
|
||||||
|
|
||||||
## Usage guidelines (design)
|
## Usage guidelines (design)
|
||||||
|
|
||||||
### Where to Use
|
### Where to Use
|
||||||
|
|||||||
@ -11,7 +11,27 @@ import { userEvent, within, expect } from "@storybook/test";
|
|||||||
|
|
||||||
const { Combobox } = Components;
|
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 {
|
export default {
|
||||||
title: "Controls/Combobox",
|
title: "Controls/Combobox",
|
||||||
@ -20,76 +40,46 @@ export default {
|
|||||||
disabled: { control: "boolean" },
|
disabled: { control: "boolean" },
|
||||||
maxLength: { control: "number" },
|
maxLength: { control: "number" },
|
||||||
hasError: { control: "boolean" },
|
hasError: { control: "boolean" },
|
||||||
|
emptyToEnd: { control: "boolean" },
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
maxLength: 10,
|
maxLength: 10,
|
||||||
hasError: false,
|
hasError: false,
|
||||||
placeholder: "Select a month",
|
placeholder: "Select a weekday",
|
||||||
options: [
|
emptyToEnd: false,
|
||||||
{ id: "January", label: "January" },
|
options: options,
|
||||||
{ id: "February", label: "February" },
|
defaultSelected: "Tuesday",
|
||||||
{ 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",
|
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
controls: {
|
controls: {
|
||||||
exclude: ["options", "defaultSelected"],
|
exclude: ["options", "defaultSelected"],
|
||||||
},
|
},
|
||||||
},
|
|
||||||
render: ({ ...args }) => (
|
|
||||||
<div style={{ padding: "5px" }}>
|
|
||||||
<Combobox {...args} />
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Default = {
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
docs: {
|
||||||
story: {
|
story: {
|
||||||
height: "450px",
|
height: "320px",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
render: ({ ...args }) => <Combobox {...args} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
|
|
||||||
export const WithIcons = {
|
export const WithIcons = {
|
||||||
args: {
|
args: {
|
||||||
options: [
|
options: optionsWithIcons,
|
||||||
{ 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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const EmptyToEnd = {
|
||||||
|
args: {
|
||||||
|
emptyToEnd: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let lastValue = null;
|
||||||
|
|
||||||
export const TestInteractions = {
|
export const TestInteractions = {
|
||||||
...WithIcons,
|
...WithIcons,
|
||||||
args: {
|
args: {
|
||||||
@ -167,8 +157,8 @@ export const TestInteractions = {
|
|||||||
await userEvent.keyboard("{ArrowDown}");
|
await userEvent.keyboard("{ArrowDown}");
|
||||||
await userEvent.keyboard("{Enter}");
|
await userEvent.keyboard("{Enter}");
|
||||||
|
|
||||||
expect(input).toHaveValue("February");
|
expect(input).toHaveValue("Tuesday");
|
||||||
expect(lastValue).toBe("February");
|
expect(lastValue).toBe("Tuesday");
|
||||||
await userEvent.clear(input);
|
await userEvent.clear(input);
|
||||||
|
|
||||||
// Arrow up
|
// Arrow up
|
||||||
@ -177,11 +167,11 @@ export const TestInteractions = {
|
|||||||
|
|
||||||
await userEvent.keyboard("{ArrowUp}");
|
await userEvent.keyboard("{ArrowUp}");
|
||||||
await userEvent.keyboard("{ArrowUp}");
|
await userEvent.keyboard("{ArrowUp}");
|
||||||
expect(combobox).toHaveAttribute("aria-activedescendant", "November");
|
expect(combobox).toHaveAttribute("aria-activedescendant", "Saturday");
|
||||||
await userEvent.keyboard("{Enter}");
|
await userEvent.keyboard("{Enter}");
|
||||||
|
|
||||||
expect(input).toHaveValue("November");
|
expect(input).toHaveValue("Saturday");
|
||||||
expect(lastValue).toBe("November");
|
expect(lastValue).toBe("Saturday");
|
||||||
await userEvent.clear(input);
|
await userEvent.clear(input);
|
||||||
|
|
||||||
// Home
|
// Home
|
||||||
@ -191,21 +181,21 @@ export const TestInteractions = {
|
|||||||
await userEvent.keyboard("{ArrowDown}");
|
await userEvent.keyboard("{ArrowDown}");
|
||||||
await userEvent.keyboard("{ArrowDown}");
|
await userEvent.keyboard("{ArrowDown}");
|
||||||
await userEvent.keyboard("{Home}");
|
await userEvent.keyboard("{Home}");
|
||||||
expect(combobox).toHaveAttribute("aria-activedescendant", "January");
|
expect(combobox).toHaveAttribute("aria-activedescendant", "Monday");
|
||||||
await userEvent.keyboard("{Enter}");
|
await userEvent.keyboard("{Enter}");
|
||||||
|
|
||||||
expect(input).toHaveValue("January");
|
expect(input).toHaveValue("Monday");
|
||||||
expect(lastValue).toBe("January");
|
expect(lastValue).toBe("Monday");
|
||||||
await userEvent.clear(input);
|
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.clear(input);
|
||||||
await userEvent.keyboard("{Escape}");
|
await userEvent.keyboard("{Escape}");
|
||||||
|
|
||||||
await userEvent.click(input);
|
await userEvent.click(input);
|
||||||
|
|
||||||
await userEvent.type(input, "Ju");
|
await userEvent.type(input, "es");
|
||||||
|
|
||||||
const options = await canvas.findAllByTestId("dropdown-option");
|
const options = await canvas.findAllByTestId("dropdown-option");
|
||||||
expect(options).toHaveLength(2);
|
expect(options).toHaveLength(2);
|
||||||
@ -215,8 +205,8 @@ export const TestInteractions = {
|
|||||||
|
|
||||||
await userEvent.keyboard("{Enter}");
|
await userEvent.keyboard("{Enter}");
|
||||||
|
|
||||||
expect(input).toHaveValue("July");
|
expect(input).toHaveValue("Wednesday");
|
||||||
expect(lastValue).toBe("July");
|
expect(lastValue).toBe("Wednesday");
|
||||||
});
|
});
|
||||||
|
|
||||||
await step("Close dropdown when focusing out", async () => {
|
await step("Close dropdown when focusing out", async () => {
|
||||||
|
|||||||
@ -11,17 +11,22 @@ import * as SelectStories from "./select.stories";
|
|||||||
|
|
||||||
# Select
|
# Select
|
||||||
|
|
||||||
Select lets users choose one option from an options menu.
|
The `select*` component lets users choose one option from an options menu.
|
||||||
|
|
||||||
## Variants
|
## 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} />
|
<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} />
|
<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
|
## Technical notes
|
||||||
|
|
||||||
### Icons
|
### Icons
|
||||||
@ -49,8 +54,6 @@ These are available in the `app.main.ds.foundations.assets.icon` namespace.
|
|||||||
]}]
|
]}]
|
||||||
```
|
```
|
||||||
|
|
||||||
<Canvas of={SelectStories.WithIcons} />
|
|
||||||
|
|
||||||
## Usage guidelines (design)
|
## Usage guidelines (design)
|
||||||
|
|
||||||
### Where to use
|
### Where to use
|
||||||
|
|||||||
@ -9,34 +9,42 @@ import Components from "@target/components";
|
|||||||
|
|
||||||
const { Select } = 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 {
|
export default {
|
||||||
title: "Controls/Select",
|
title: "Controls/Select",
|
||||||
component: Select,
|
component: Select,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
disabled: { control: "boolean" },
|
disabled: { control: "boolean" },
|
||||||
|
emptyToEnd: { control: "boolean" },
|
||||||
},
|
},
|
||||||
args: {
|
args: {
|
||||||
disabled: false,
|
disabled: false,
|
||||||
options: [
|
options: options,
|
||||||
{
|
emptyToEnd: false,
|
||||||
label: "Code",
|
|
||||||
id: "option-code",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Design",
|
|
||||||
id: "option-design",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Menu",
|
|
||||||
id: "option-menu",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
defaultSelected: "option-code",
|
defaultSelected: "option-code",
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
controls: {
|
controls: {
|
||||||
exclude: ["options", "defaultSelected"],
|
exclude: ["options", "defaultSelected"],
|
||||||
},
|
},
|
||||||
|
docs: {
|
||||||
|
story: {
|
||||||
|
height: "200px",
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
render: ({ ...args }) => <Select {...args} />,
|
render: ({ ...args }) => <Select {...args} />,
|
||||||
};
|
};
|
||||||
@ -45,21 +53,12 @@ export const Default = {};
|
|||||||
|
|
||||||
export const WithIcons = {
|
export const WithIcons = {
|
||||||
args: {
|
args: {
|
||||||
options: [
|
options: optionsWithIcons,
|
||||||
{
|
},
|
||||||
label: "Code",
|
};
|
||||||
id: "option-code",
|
|
||||||
icon: "fill-content",
|
export const EmptyToEnd = {
|
||||||
},
|
args: {
|
||||||
{
|
emptyToEnd: true,
|
||||||
label: "Design",
|
|
||||||
id: "option-design",
|
|
||||||
icon: "pentool",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Menu",
|
|
||||||
id: "option-menu",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user