diff --git a/frontend/src/app/main/ui/ds/controls/combobox.mdx b/frontend/src/app/main/ui/ds/controls/combobox.mdx
index ce8f347c0d..93e6adf682 100644
--- a/frontend/src/app/main/ui/ds/controls/combobox.mdx
+++ b/frontend/src/app/main/ui/ds/controls/combobox.mdx
@@ -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.
-**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.
+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.
+
+
+
## 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.
]}]
```
-
-
## Usage guidelines (design)
### Where to Use
diff --git a/frontend/src/app/main/ui/ds/controls/combobox.stories.jsx b/frontend/src/app/main/ui/ds/controls/combobox.stories.jsx
index 88126a5bb1..448ba3ac57 100644
--- a/frontend/src/app/main/ui/ds/controls/combobox.stories.jsx
+++ b/frontend/src/app/main/ui/ds/controls/combobox.stories.jsx
@@ -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 }) => (
-
-
-
- ),
-};
-
-export const Default = {
- parameters: {
docs: {
story: {
- height: "450px",
+ height: "320px",
},
},
},
+ render: ({ ...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 () => {
diff --git a/frontend/src/app/main/ui/ds/controls/select.mdx b/frontend/src/app/main/ui/ds/controls/select.mdx
index 8d8b879f5d..0ecdf1e7af 100644
--- a/frontend/src/app/main/ui/ds/controls/select.mdx
+++ b/frontend/src/app/main/ui/ds/controls/select.mdx
@@ -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.
-**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.
+
+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.
+
+
+
## Technical notes
### Icons
@@ -49,8 +54,6 @@ These are available in the `app.main.ds.foundations.assets.icon` namespace.
]}]
```
-
-
## Usage guidelines (design)
### Where to use
diff --git a/frontend/src/app/main/ui/ds/controls/select.stories.jsx b/frontend/src/app/main/ui/ds/controls/select.stories.jsx
index 68774e833e..2529b851dc 100644
--- a/frontend/src/app/main/ui/ds/controls/select.stories.jsx
+++ b/frontend/src/app/main/ui/ds/controls/select.stories.jsx
@@ -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 }) => ,
};
@@ -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,
},
};