diff --git a/.changeset/nasty-pianos-bake.md b/.changeset/nasty-pianos-bake.md
new file mode 100644
index 0000000..abc924f
--- /dev/null
+++ b/.changeset/nasty-pianos-bake.md
@@ -0,0 +1,5 @@
+---
+"@openfun/cunningham-react": minor
+---
+
+add select menu empty placeholder
diff --git a/packages/react/src/components/Forms/Select/index.scss b/packages/react/src/components/Forms/Select/index.scss
index 18fb8a2..5434ec0 100644
--- a/packages/react/src/components/Forms/Select/index.scss
+++ b/packages/react/src/components/Forms/Select/index.scss
@@ -141,6 +141,11 @@
cursor: default;
}
}
+
+ &__empty-placeholder {
+ color: var(--c--theme--colors--greyscale-600);
+ font-style: italic;
+ }
}
/** Modifiers */
diff --git a/packages/react/src/components/Forms/Select/mono-common.tsx b/packages/react/src/components/Forms/Select/mono-common.tsx
index ea00f9a..0f15a6e 100644
--- a/packages/react/src/components/Forms/Select/mono-common.tsx
+++ b/packages/react/src/components/Forms/Select/mono-common.tsx
@@ -174,27 +174,35 @@ export const SelectMonoAux = ({
{...downshiftReturn.getMenuProps()}
>
- {downshiftReturn.isOpen &&
- options.map((item, index) => {
- const isActive = index === downshiftReturn.highlightedIndex;
- return (
- -
- {item.label}
+ {downshiftReturn.isOpen && (
+ <>
+ {options.map((item, index) => {
+ const isActive = index === downshiftReturn.highlightedIndex;
+ return (
+
-
+ {item.label}
+
+ );
+ })}
+ {options.length === 0 && (
+ -
+ {t("components.forms.select.menu_empty_placeholder")}
- );
- })}
+ )}
+ >
+ )}
diff --git a/packages/react/src/components/Forms/Select/mono.spec.tsx b/packages/react/src/components/Forms/Select/mono.spec.tsx
index 3700fec..2be4e60 100644
--- a/packages/react/src/components/Forms/Select/mono.spec.tsx
+++ b/packages/react/src/components/Forms/Select/mono.spec.tsx
@@ -1138,5 +1138,19 @@ describe("", () => {
const label = screen.getByText("City");
expect(Array.from(label.classList)).toContain("offscreen");
});
+
+ it("renders menu empty placeholder when there are no options to display", async () => {
+ render(
+
+
+ ,
+ );
+ const input = screen.getByRole("combobox", {
+ name: "City",
+ });
+ const user = userEvent.setup();
+ await user.click(input);
+ screen.getByText("No options available");
+ });
});
});
diff --git a/packages/react/src/components/Forms/Select/mono.stories.tsx b/packages/react/src/components/Forms/Select/mono.stories.tsx
index 8a3ba96..de072dc 100644
--- a/packages/react/src/components/Forms/Select/mono.stories.tsx
+++ b/packages/react/src/components/Forms/Select/mono.stories.tsx
@@ -211,6 +211,14 @@ export const Error = {
},
};
+export const NoOptions = {
+ render: Template,
+ args: {
+ label: "No options available",
+ options: [],
+ },
+};
+
export const FormExample = () => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
diff --git a/packages/react/src/components/Forms/Select/multi-common.tsx b/packages/react/src/components/Forms/Select/multi-common.tsx
index 3a4b666..f57c44d 100644
--- a/packages/react/src/components/Forms/Select/multi-common.tsx
+++ b/packages/react/src/components/Forms/Select/multi-common.tsx
@@ -191,25 +191,33 @@ export const SelectMultiAux = ({
{...downshiftReturn.getMenuProps()}
>
- {downshiftReturn.isOpen &&
- options.map((option, index) => {
- const isActive = index === downshiftReturn.highlightedIndex;
- return (
- -
- {option.label}
+ {downshiftReturn.isOpen && (
+ <>
+ {options.map((option, index) => {
+ const isActive = index === downshiftReturn.highlightedIndex;
+ return (
+
-
+ {option.label}
+
+ );
+ })}
+ {options.length === 0 && (
+ -
+ {t("components.forms.select.menu_empty_placeholder")}
- );
- })}
+ )}
+ >
+ )}
diff --git a/packages/react/src/components/Forms/Select/multi.spec.tsx b/packages/react/src/components/Forms/Select/multi.spec.tsx
index ffe11d4..92e2013 100644
--- a/packages/react/src/components/Forms/Select/multi.spec.tsx
+++ b/packages/react/src/components/Forms/Select/multi.spec.tsx
@@ -1232,5 +1232,55 @@ describe("", () => {
await user.type(input, "Pa");
expectMenuToBeClosed(menu);
});
+
+ it("renders menu empty placeholder when there is no more options to display", async () => {
+ render(
+
+
+ ,
+ );
+ const input = screen.getByRole("combobox", {
+ name: "Cities",
+ });
+
+ // Expect no options to be selected.
+ expectSelectedOptions([]);
+
+ const user = userEvent.setup();
+ await user.click(input);
+
+ await user.click(
+ screen.getByRole("option", {
+ name: "Paris",
+ }),
+ );
+ expectSelectedOptions(["Paris"]);
+
+ expect(
+ screen.queryByText("No options available"),
+ ).not.toBeInTheDocument();
+
+ await user.click(
+ screen.getByRole("option", {
+ name: "London",
+ }),
+ );
+ expectSelectedOptions(["Paris", "London"]);
+
+ screen.getByText("No options available");
+ });
});
});
diff --git a/packages/react/src/components/Forms/Select/multi.stories.tsx b/packages/react/src/components/Forms/Select/multi.stories.tsx
index 2570574..6c33b28 100644
--- a/packages/react/src/components/Forms/Select/multi.stories.tsx
+++ b/packages/react/src/components/Forms/Select/multi.stories.tsx
@@ -213,6 +213,14 @@ export const Error = {
},
};
+export const NoOptions = {
+ render: Template,
+ args: {
+ label: "No options available",
+ options: [],
+ },
+};
+
export const FormExample = () => {
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
diff --git a/packages/react/src/locales/en-US.json b/packages/react/src/locales/en-US.json
index 7e04503..0d474e9 100644
--- a/packages/react/src/locales/en-US.json
+++ b/packages/react/src/locales/en-US.json
@@ -22,7 +22,8 @@
"select": {
"toggle_button_aria_label": "Toggle dropdown",
"clear_button_aria_label": "Clear selection",
- "clear_all_button_aria_label": "Clear all selections"
+ "clear_all_button_aria_label": "Clear all selections",
+ "menu_empty_placeholder": "No options available"
},
"file_uploader": {
"delete_file_name": "Delete file {name}",
diff --git a/packages/react/src/locales/fr-FR.json b/packages/react/src/locales/fr-FR.json
index a4c1c67..f6aeda9 100644
--- a/packages/react/src/locales/fr-FR.json
+++ b/packages/react/src/locales/fr-FR.json
@@ -20,7 +20,8 @@
"select": {
"toggle_button_aria_label": "Ouvrir le menu",
"clear_button_aria_label": "Effacer la sélection",
- "clear_all_button_aria_label": "Effacer toutes les sélections"
+ "clear_all_button_aria_label": "Effacer toutes les sélections",
+ "menu_empty_placeholder": "Aucun choix disponible"
},
"file_uploader": {
"delete_file_name": "Supprimer le fichier {name}",