(react) add disable property to select option

Based on recent feedbacks, this feature was needed for consumer apps.

Resolve #60
This commit is contained in:
Nathan Vasse
2023-05-19 12:09:14 +02:00
committed by NathanVss
parent c11727976c
commit c93c8d2a2f
8 changed files with 119 additions and 14 deletions

View File

@@ -0,0 +1,5 @@
---
"@openfun/cunningham-react": minor
---
add disable property to select option

View File

@@ -26,6 +26,7 @@ The available options must be given via the `options` props. It is an array of o
code={`{
label: string
value?: string
disabled?: boolean;
}`}
/>
@@ -83,6 +84,15 @@ By default, the select is clearable ( the cross icon on the right is shown ). Yo
<Story id="components-forms-select-mono--not-clearable"/>
</Canvas>
## Disabled options
You can disable some options by using the `disabled` props on the `Option` object.
<Canvas sourceState="shown">
<Story id="components-forms-select-mono--disabled-options"/>
</Canvas>
## Controlled / Non Controlled
Like a native select, you can use the Select component in a controlled or non controlled way. You can see the example below

View File

@@ -121,6 +121,11 @@
&--selected {
background-color: var(--c--components--forms-select--item-background-color--selected);
}
&--disabled {
color: var(--c--components--forms-select--item-color--disabled);
cursor: default;
}
}
}

View File

@@ -35,6 +35,12 @@ describe("<Select/>", () => {
"c__select__menu__item--selected"
);
};
const expectOptionToBeDisabled = (option: HTMLLIElement) => {
expect(option).toHaveAttribute("disabled");
expect(Array.from(option.classList)).contains(
"c__select__menu__item--disabled"
);
};
describe("Searchable", () => {
it("shows all options when clicking on the input", async () => {
@@ -1067,5 +1073,65 @@ describe("<Select/>", () => {
})
).not.toBeInTheDocument();
});
it("is not possible to select disabled options", async () => {
render(
<CunninghamProvider>
<Select
label="City"
options={[
{
label: "Paris",
},
{
label: "London",
},
{
label: "New York",
disabled: true,
},
{
label: "Tokyo",
},
]}
/>
</CunninghamProvider>
);
const input = screen.getByRole("combobox", {
name: "City",
});
const menu: HTMLDivElement = screen.getByRole("listbox", {
name: "City",
});
const valueRendered = document.querySelector(".c__select__inner__value");
// Make sure the select is empty.
expect(valueRendered).toHaveTextContent("");
const user = userEvent.setup();
await user.click(input);
expectMenuToBeOpen(menu);
// Make sure the disabled option is not selectable.
let option: HTMLLIElement = screen.getByRole("option", {
name: "New York",
});
expectOptionToBeDisabled(option);
// Try to click on the disabled option.
await user.click(option);
expectMenuToBeOpen(menu);
// Make sure the select is still empty.
expect(valueRendered).toHaveTextContent("");
// Select a not disabled option.
option = screen.getByRole("option", {
name: "Tokyo",
});
await user.click(option);
expectMenuToBeClosed(menu);
expect(valueRendered).toHaveTextContent("Tokyo");
});
});
});

View File

@@ -20,6 +20,7 @@ import { Button } from ":/components/Button";
interface Option {
value?: string;
label: string;
disabled?: boolean;
}
type Props = PropsWithChildren &
@@ -197,20 +198,28 @@ const SelectAux = ({
>
<ul>
{downshiftReturn.isOpen &&
options.map((item, index) => (
<li
className={classNames("c__select__menu__item", {
"c__select__menu__item--highlight":
downshiftReturn.highlightedIndex === index,
"c__select__menu__item--selected":
downshiftReturn.selectedItem === item,
})}
key={`${item.value}${index}`}
{...downshiftReturn.getItemProps({ item, index })}
>
<span>{item.label}</span>
</li>
))}
options.map((item, index) => {
const isActive = index === downshiftReturn.highlightedIndex;
return (
<li
className={classNames("c__select__menu__item", {
"c__select__menu__item--highlight": isActive,
"c__select__menu__item--selected":
downshiftReturn.selectedItem === item,
"c__select__menu__item--disabled": item.disabled,
})}
key={`${item.value}${index}`}
{...downshiftReturn.getItemProps({
item,
index,
isActive,
disabled: item.disabled,
})}
>
<span>{item.label}</span>
</li>
);
})}
</ul>
</div>
</div>

View File

@@ -165,6 +165,14 @@ export const NotClearable = {
},
};
export const DisabledOptions = {
render: Template,
args: {
label: "Select a city",
options: OPTIONS.map((option, i) => ({ ...option, disabled: i % 3 === 0 })),
},
};
export const Success = {
render: Template,

View File

@@ -15,6 +15,7 @@ export const tokens = (defaults: DefaultTokens) => ({
"item-background-color--hover": defaults.theme.colors["greyscale-200"],
"item-background-color--selected": defaults.theme.colors["primary-100"],
"item-color": defaults.theme.colors["greyscale-800"],
"item-color--disabled": defaults.theme.colors["greyscale-500"],
"item-font-size": defaults.theme.font.sizes.l,
"background-color": "white",
"menu-background-color": "white",

View File

@@ -117,6 +117,7 @@
--c--components--forms-select--item-background-color--hover: #F3F4F4;
--c--components--forms-select--item-background-color--selected: #EBF2FC;
--c--components--forms-select--item-color: #303C4B;
--c--components--forms-select--item-color--disabled: #9EA3AA;
--c--components--forms-select--item-font-size: 1rem;
--c--components--forms-select--background-color: white;
--c--components--forms-select--menu-background-color: white;