✨(react) add select menu empty placeholder
Previously if the menu was opened and no there were no options to be displayed it was just showing a tiny empty menu, this commit adds an empty placeholder in order to make it clearer that the list is empty.
This commit is contained in:
5
.changeset/nasty-pianos-bake.md
Normal file
5
.changeset/nasty-pianos-bake.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-react": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add select menu empty placeholder
|
||||||
@@ -141,6 +141,11 @@
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__empty-placeholder {
|
||||||
|
color: var(--c--theme--colors--greyscale-600);
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Modifiers */
|
/** Modifiers */
|
||||||
|
|||||||
@@ -174,8 +174,9 @@ export const SelectMonoAux = ({
|
|||||||
{...downshiftReturn.getMenuProps()}
|
{...downshiftReturn.getMenuProps()}
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
{downshiftReturn.isOpen &&
|
{downshiftReturn.isOpen && (
|
||||||
options.map((item, index) => {
|
<>
|
||||||
|
{options.map((item, index) => {
|
||||||
const isActive = index === downshiftReturn.highlightedIndex;
|
const isActive = index === downshiftReturn.highlightedIndex;
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
@@ -185,7 +186,7 @@ export const SelectMonoAux = ({
|
|||||||
downshiftReturn.selectedItem === item,
|
downshiftReturn.selectedItem === item,
|
||||||
"c__select__menu__item--disabled": item.disabled,
|
"c__select__menu__item--disabled": item.disabled,
|
||||||
})}
|
})}
|
||||||
key={`${item.value}${index}`}
|
key={`${item.value}${index.toString()}`}
|
||||||
{...downshiftReturn.getItemProps({
|
{...downshiftReturn.getItemProps({
|
||||||
item,
|
item,
|
||||||
index,
|
index,
|
||||||
@@ -195,6 +196,13 @@ export const SelectMonoAux = ({
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{options.length === 0 && (
|
||||||
|
<li className="c__select__menu__item c__select__menu__empty-placeholder">
|
||||||
|
{t("components.forms.select.menu_empty_placeholder")}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1138,5 +1138,19 @@ describe("<Select/>", () => {
|
|||||||
const label = screen.getByText("City");
|
const label = screen.getByText("City");
|
||||||
expect(Array.from(label.classList)).toContain("offscreen");
|
expect(Array.from(label.classList)).toContain("offscreen");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders menu empty placeholder when there are no options to display", async () => {
|
||||||
|
render(
|
||||||
|
<CunninghamProvider>
|
||||||
|
<Select label="City" options={[]} hideLabel={true} />
|
||||||
|
</CunninghamProvider>,
|
||||||
|
);
|
||||||
|
const input = screen.getByRole("combobox", {
|
||||||
|
name: "City",
|
||||||
|
});
|
||||||
|
const user = userEvent.setup();
|
||||||
|
await user.click(input);
|
||||||
|
screen.getByText("No options available");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -211,6 +211,14 @@ export const Error = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NoOptions = {
|
||||||
|
render: Template,
|
||||||
|
args: {
|
||||||
|
label: "No options available",
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const FormExample = () => {
|
export const FormExample = () => {
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -191,8 +191,9 @@ export const SelectMultiAux = ({
|
|||||||
{...downshiftReturn.getMenuProps()}
|
{...downshiftReturn.getMenuProps()}
|
||||||
>
|
>
|
||||||
<ul>
|
<ul>
|
||||||
{downshiftReturn.isOpen &&
|
{downshiftReturn.isOpen && (
|
||||||
options.map((option, index) => {
|
<>
|
||||||
|
{options.map((option, index) => {
|
||||||
const isActive = index === downshiftReturn.highlightedIndex;
|
const isActive = index === downshiftReturn.highlightedIndex;
|
||||||
return (
|
return (
|
||||||
<li
|
<li
|
||||||
@@ -200,7 +201,7 @@ export const SelectMultiAux = ({
|
|||||||
"c__select__menu__item--highlight": isActive,
|
"c__select__menu__item--highlight": isActive,
|
||||||
"c__select__menu__item--disabled": option.disabled,
|
"c__select__menu__item--disabled": option.disabled,
|
||||||
})}
|
})}
|
||||||
key={`${option.value}${index}`}
|
key={`${option.value}${index.toString()}`}
|
||||||
{...downshiftReturn.getItemProps({
|
{...downshiftReturn.getItemProps({
|
||||||
item: option,
|
item: option,
|
||||||
index,
|
index,
|
||||||
@@ -210,6 +211,13 @@ export const SelectMultiAux = ({
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
{options.length === 0 && (
|
||||||
|
<li className="c__select__menu__item c__select__menu__empty-placeholder">
|
||||||
|
{t("components.forms.select.menu_empty_placeholder")}
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1232,5 +1232,55 @@ describe("<Select multi={true} />", () => {
|
|||||||
await user.type(input, "Pa");
|
await user.type(input, "Pa");
|
||||||
expectMenuToBeClosed(menu);
|
expectMenuToBeClosed(menu);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("renders menu empty placeholder when there is no more options to display", async () => {
|
||||||
|
render(
|
||||||
|
<CunninghamProvider>
|
||||||
|
<Select
|
||||||
|
label="Cities"
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
label: "Paris",
|
||||||
|
value: "paris",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "London",
|
||||||
|
value: "london",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
multi={true}
|
||||||
|
/>
|
||||||
|
</CunninghamProvider>,
|
||||||
|
);
|
||||||
|
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");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -213,6 +213,14 @@ export const Error = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const NoOptions = {
|
||||||
|
render: Template,
|
||||||
|
args: {
|
||||||
|
label: "No options available",
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
export const FormExample = () => {
|
export const FormExample = () => {
|
||||||
const handleSubmit = (e: React.FormEvent) => {
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|||||||
@@ -22,7 +22,8 @@
|
|||||||
"select": {
|
"select": {
|
||||||
"toggle_button_aria_label": "Toggle dropdown",
|
"toggle_button_aria_label": "Toggle dropdown",
|
||||||
"clear_button_aria_label": "Clear selection",
|
"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": {
|
"file_uploader": {
|
||||||
"delete_file_name": "Delete file {name}",
|
"delete_file_name": "Delete file {name}",
|
||||||
|
|||||||
@@ -20,7 +20,8 @@
|
|||||||
"select": {
|
"select": {
|
||||||
"toggle_button_aria_label": "Ouvrir le menu",
|
"toggle_button_aria_label": "Ouvrir le menu",
|
||||||
"clear_button_aria_label": "Effacer la sélection",
|
"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": {
|
"file_uploader": {
|
||||||
"delete_file_name": "Supprimer le fichier {name}",
|
"delete_file_name": "Supprimer le fichier {name}",
|
||||||
|
|||||||
Reference in New Issue
Block a user