(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:
Nathan Vasse
2023-09-12 16:42:23 +02:00
committed by NathanVss
parent be5559d9fe
commit 4616ad9ffb
10 changed files with 148 additions and 40 deletions

View File

@@ -0,0 +1,5 @@
---
"@openfun/cunningham-react": minor
---
add select menu empty placeholder

View File

@@ -141,6 +141,11 @@
cursor: default; cursor: default;
} }
} }
&__empty-placeholder {
color: var(--c--theme--colors--greyscale-600);
font-style: italic;
}
} }
/** Modifiers */ /** Modifiers */

View File

@@ -174,27 +174,35 @@ export const SelectMonoAux = ({
{...downshiftReturn.getMenuProps()} {...downshiftReturn.getMenuProps()}
> >
<ul> <ul>
{downshiftReturn.isOpen && {downshiftReturn.isOpen && (
options.map((item, index) => { <>
const isActive = index === downshiftReturn.highlightedIndex; {options.map((item, index) => {
return ( const isActive = index === downshiftReturn.highlightedIndex;
<li return (
className={classNames("c__select__menu__item", { <li
"c__select__menu__item--highlight": isActive, className={classNames("c__select__menu__item", {
"c__select__menu__item--selected": "c__select__menu__item--highlight": isActive,
downshiftReturn.selectedItem === item, "c__select__menu__item--selected":
"c__select__menu__item--disabled": item.disabled, downshiftReturn.selectedItem === item,
})} "c__select__menu__item--disabled": item.disabled,
key={`${item.value}${index}`} })}
{...downshiftReturn.getItemProps({ key={`${item.value}${index.toString()}`}
item, {...downshiftReturn.getItemProps({
index, item,
})} index,
> })}
<span>{item.label}</span> >
<span>{item.label}</span>
</li>
);
})}
{options.length === 0 && (
<li className="c__select__menu__item c__select__menu__empty-placeholder">
{t("components.forms.select.menu_empty_placeholder")}
</li> </li>
); )}
})} </>
)}
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -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");
});
}); });
}); });

View File

@@ -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();

View File

@@ -191,25 +191,33 @@ export const SelectMultiAux = ({
{...downshiftReturn.getMenuProps()} {...downshiftReturn.getMenuProps()}
> >
<ul> <ul>
{downshiftReturn.isOpen && {downshiftReturn.isOpen && (
options.map((option, index) => { <>
const isActive = index === downshiftReturn.highlightedIndex; {options.map((option, index) => {
return ( const isActive = index === downshiftReturn.highlightedIndex;
<li return (
className={classNames("c__select__menu__item", { <li
"c__select__menu__item--highlight": isActive, className={classNames("c__select__menu__item", {
"c__select__menu__item--disabled": option.disabled, "c__select__menu__item--highlight": isActive,
})} "c__select__menu__item--disabled": option.disabled,
key={`${option.value}${index}`} })}
{...downshiftReturn.getItemProps({ key={`${option.value}${index.toString()}`}
item: option, {...downshiftReturn.getItemProps({
index, item: option,
})} index,
> })}
<span>{option.label}</span> >
<span>{option.label}</span>
</li>
);
})}
{options.length === 0 && (
<li className="c__select__menu__item c__select__menu__empty-placeholder">
{t("components.forms.select.menu_empty_placeholder")}
</li> </li>
); )}
})} </>
)}
</ul> </ul>
</div> </div>
</div> </div>

View File

@@ -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");
});
}); });
}); });

View File

@@ -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();

View File

@@ -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}",

View File

@@ -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}",