diff --git a/.changeset/wet-spiders-hide.md b/.changeset/wet-spiders-hide.md
new file mode 100644
index 0000000..dfbc348
--- /dev/null
+++ b/.changeset/wet-spiders-hide.md
@@ -0,0 +1,5 @@
+---
+"@openfun/cunningham-react": minor
+---
+
+add disable property to select option
diff --git a/packages/react/src/components/Forms/Select/index.mdx b/packages/react/src/components/Forms/Select/index.mdx
index a3519db..831b66d 100644
--- a/packages/react/src/components/Forms/Select/index.mdx
+++ b/packages/react/src/components/Forms/Select/index.mdx
@@ -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
+
+## Disabled options
+
+You can disable some options by using the `disabled` props on the `Option` object.
+
+
+
## 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
diff --git a/packages/react/src/components/Forms/Select/index.scss b/packages/react/src/components/Forms/Select/index.scss
index 8791ef4..9a8f30f 100644
--- a/packages/react/src/components/Forms/Select/index.scss
+++ b/packages/react/src/components/Forms/Select/index.scss
@@ -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;
+ }
}
}
diff --git a/packages/react/src/components/Forms/Select/index.spec.tsx b/packages/react/src/components/Forms/Select/index.spec.tsx
index f18f1e3..e35fdd9 100644
--- a/packages/react/src/components/Forms/Select/index.spec.tsx
+++ b/packages/react/src/components/Forms/Select/index.spec.tsx
@@ -35,6 +35,12 @@ describe("", () => {
"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("", () => {
})
).not.toBeInTheDocument();
});
+ it("is not possible to select disabled options", async () => {
+ render(
+
+
+
+ );
+ 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");
+ });
});
});
diff --git a/packages/react/src/components/Forms/Select/index.tsx b/packages/react/src/components/Forms/Select/index.tsx
index 473bfbd..55fec71 100644
--- a/packages/react/src/components/Forms/Select/index.tsx
+++ b/packages/react/src/components/Forms/Select/index.tsx
@@ -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 = ({
>
{downshiftReturn.isOpen &&
- options.map((item, index) => (
- -
- {item.label}
-
- ))}
+ options.map((item, index) => {
+ const isActive = index === downshiftReturn.highlightedIndex;
+ return (
+ -
+ {item.label}
+
+ );
+ })}
diff --git a/packages/react/src/components/Forms/Select/mono.stories.tsx b/packages/react/src/components/Forms/Select/mono.stories.tsx
index 62762e9..ba1fc69 100644
--- a/packages/react/src/components/Forms/Select/mono.stories.tsx
+++ b/packages/react/src/components/Forms/Select/mono.stories.tsx
@@ -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,
diff --git a/packages/react/src/components/Forms/Select/tokens.ts b/packages/react/src/components/Forms/Select/tokens.ts
index 58b353c..b0c62a7 100644
--- a/packages/react/src/components/Forms/Select/tokens.ts
+++ b/packages/react/src/components/Forms/Select/tokens.ts
@@ -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",
diff --git a/packages/react/src/cunningham-tokens.css b/packages/react/src/cunningham-tokens.css
index f86f82b..2290623 100644
--- a/packages/react/src/cunningham-tokens.css
+++ b/packages/react/src/cunningham-tokens.css
@@ -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;