mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 18:01:06 +00:00
parent
211885bad7
commit
2fc884caca
2 changed files with 49 additions and 57 deletions
|
@ -116,7 +116,6 @@
|
||||||
import WarningFilled from "../icons/WarningFilled.svelte";
|
import WarningFilled from "../icons/WarningFilled.svelte";
|
||||||
import WarningAltFilled from "../icons/WarningAltFilled.svelte";
|
import WarningAltFilled from "../icons/WarningAltFilled.svelte";
|
||||||
import ListBox from "../ListBox/ListBox.svelte";
|
import ListBox from "../ListBox/ListBox.svelte";
|
||||||
import ListBoxField from "../ListBox/ListBoxField.svelte";
|
|
||||||
import ListBoxMenu from "../ListBox/ListBoxMenu.svelte";
|
import ListBoxMenu from "../ListBox/ListBoxMenu.svelte";
|
||||||
import ListBoxMenuIcon from "../ListBox/ListBoxMenuIcon.svelte";
|
import ListBoxMenuIcon from "../ListBox/ListBoxMenuIcon.svelte";
|
||||||
import ListBoxMenuItem from "../ListBox/ListBoxMenuItem.svelte";
|
import ListBoxMenuItem from "../ListBox/ListBoxMenuItem.svelte";
|
||||||
|
@ -254,22 +253,12 @@
|
||||||
{warn}
|
{warn}
|
||||||
{warnText}
|
{warnText}
|
||||||
>
|
>
|
||||||
<ListBoxField
|
<div class:bx--list-box__field={true}>
|
||||||
role="button"
|
|
||||||
aria-expanded={open}
|
|
||||||
on:click={async () => {
|
|
||||||
if (disabled) return;
|
|
||||||
open = true;
|
|
||||||
await tick();
|
|
||||||
ref.focus();
|
|
||||||
}}
|
|
||||||
{id}
|
|
||||||
{disabled}
|
|
||||||
{translateWithId}
|
|
||||||
>
|
|
||||||
<input
|
<input
|
||||||
bind:this={ref}
|
bind:this={ref}
|
||||||
bind:value
|
bind:value
|
||||||
|
type="text"
|
||||||
|
role="combobox"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
aria-autocomplete="list"
|
aria-autocomplete="list"
|
||||||
|
@ -287,6 +276,10 @@
|
||||||
class:bx--text-input={true}
|
class:bx--text-input={true}
|
||||||
class:bx--text-input--light={light}
|
class:bx--text-input--light={light}
|
||||||
class:bx--text-input--empty={value === ""}
|
class:bx--text-input--empty={value === ""}
|
||||||
|
on:click={() => {
|
||||||
|
if (disabled) return;
|
||||||
|
open = true;
|
||||||
|
}}
|
||||||
on:input={({ target }) => {
|
on:input={({ target }) => {
|
||||||
if (!open && target.value.length > 0) {
|
if (!open && target.value.length > 0) {
|
||||||
open = true;
|
open = true;
|
||||||
|
@ -384,7 +377,7 @@
|
||||||
{translateWithId}
|
{translateWithId}
|
||||||
{open}
|
{open}
|
||||||
/>
|
/>
|
||||||
</ListBoxField>
|
</div>
|
||||||
{#if open}
|
{#if open}
|
||||||
<ListBoxMenu aria-label={ariaLabel} {id} on:scroll bind:ref={listRef}>
|
<ListBoxMenu aria-label={ariaLabel} {id} on:scroll bind:ref={listRef}>
|
||||||
{#each filteredItems as item, i (item.id)}
|
{#each filteredItems as item, i (item.id)}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import ComboBox from "./ComboBox.test.svelte";
|
||||||
import ComboBoxCustom from "./ComboBoxCustom.test.svelte";
|
import ComboBoxCustom from "./ComboBoxCustom.test.svelte";
|
||||||
|
|
||||||
describe("ComboBox", () => {
|
describe("ComboBox", () => {
|
||||||
|
const getInput = () => screen.getByRole("combobox");
|
||||||
const getClearButton = () =>
|
const getClearButton = () =>
|
||||||
screen.getByRole("button", { name: "Clear selected item" });
|
screen.getByRole("button", { name: "Clear selected item" });
|
||||||
|
|
||||||
|
@ -15,15 +16,13 @@ describe("ComboBox", () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
expect(screen.getByText("Contact")).toBeInTheDocument();
|
expect(screen.getByText("Contact")).toBeInTheDocument();
|
||||||
const input = screen.getByRole("textbox");
|
expect(getInput()).toHaveAttribute("placeholder", "Select contact method");
|
||||||
expect(input).toHaveAttribute("placeholder", "Select contact method");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should open menu on click", async () => {
|
it("should open menu on click", async () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox");
|
await user.click(getInput());
|
||||||
await user.click(input);
|
|
||||||
|
|
||||||
const dropdown = screen.getAllByRole("listbox")[1];
|
const dropdown = screen.getAllByRole("listbox")[1];
|
||||||
expect(dropdown).toBeVisible();
|
expect(dropdown).toBeVisible();
|
||||||
|
@ -33,20 +32,20 @@ describe("ComboBox", () => {
|
||||||
const consoleLog = vi.spyOn(console, "log");
|
const consoleLog = vi.spyOn(console, "log");
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
await user.click(screen.getByRole("textbox"));
|
await user.click(getInput());
|
||||||
await user.click(screen.getByText("Email"));
|
await user.click(screen.getByText("Email"));
|
||||||
|
|
||||||
expect(consoleLog).toHaveBeenCalledWith("select", {
|
expect(consoleLog).toHaveBeenCalledWith("select", {
|
||||||
selectedId: "1",
|
selectedId: "1",
|
||||||
selectedItem: { id: "1", text: "Email" },
|
selectedItem: { id: "1", text: "Email" },
|
||||||
});
|
});
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("Email");
|
expect(getInput()).toHaveValue("Email");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle keyboard navigation", async () => {
|
it("should handle keyboard navigation", async () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
await user.keyboard("{ArrowDown}");
|
await user.keyboard("{ArrowDown}");
|
||||||
await user.keyboard("{Enter}");
|
await user.keyboard("{Enter}");
|
||||||
|
@ -66,7 +65,7 @@ describe("ComboBox", () => {
|
||||||
await user.click(getClearButton());
|
await user.click(getClearButton());
|
||||||
|
|
||||||
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle clear selection via keyboard navigation (Enter)", async () => {
|
it("should handle clear selection via keyboard navigation (Enter)", async () => {
|
||||||
|
@ -79,7 +78,7 @@ describe("ComboBox", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(consoleLog).not.toHaveBeenCalled();
|
expect(consoleLog).not.toHaveBeenCalled();
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("Email");
|
expect(getInput()).toHaveValue("Email");
|
||||||
|
|
||||||
const clearButton = getClearButton();
|
const clearButton = getClearButton();
|
||||||
clearButton.focus();
|
clearButton.focus();
|
||||||
|
@ -87,7 +86,7 @@ describe("ComboBox", () => {
|
||||||
await user.keyboard("{Enter}");
|
await user.keyboard("{Enter}");
|
||||||
|
|
||||||
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle clear selection via keyboard navigation (Space)", async () => {
|
it("should handle clear selection via keyboard navigation (Space)", async () => {
|
||||||
|
@ -100,7 +99,7 @@ describe("ComboBox", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(consoleLog).not.toHaveBeenCalled();
|
expect(consoleLog).not.toHaveBeenCalled();
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("Email");
|
expect(getInput()).toHaveValue("Email");
|
||||||
|
|
||||||
const clearButton = getClearButton();
|
const clearButton = getClearButton();
|
||||||
clearButton.focus();
|
clearButton.focus();
|
||||||
|
@ -108,7 +107,7 @@ describe("ComboBox", () => {
|
||||||
await user.keyboard(" ");
|
await user.keyboard(" ");
|
||||||
|
|
||||||
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use custom translations when translateWithId is provided", () => {
|
it("should use custom translations when translateWithId is provided", () => {
|
||||||
|
@ -134,7 +133,7 @@ describe("ComboBox", () => {
|
||||||
it("should handle disabled state", () => {
|
it("should handle disabled state", () => {
|
||||||
render(ComboBox, { props: { disabled: true } });
|
render(ComboBox, { props: { disabled: true } });
|
||||||
|
|
||||||
expect(screen.getByRole("textbox")).toBeDisabled();
|
expect(getInput()).toBeDisabled();
|
||||||
expect(screen.getByText("Contact")).toHaveClass("bx--label--disabled");
|
expect(screen.getByText("Contact")).toHaveClass("bx--label--disabled");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,7 +180,7 @@ describe("ComboBox", () => {
|
||||||
it("should handle light variant", () => {
|
it("should handle light variant", () => {
|
||||||
render(ComboBox, { props: { light: true } });
|
render(ComboBox, { props: { light: true } });
|
||||||
|
|
||||||
expect(screen.getByRole("textbox")).toHaveClass("bx--text-input--light");
|
expect(getInput()).toHaveClass("bx--text-input--light");
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
|
@ -195,7 +194,7 @@ describe("ComboBox", () => {
|
||||||
it("should handle filtering items", async () => {
|
it("should handle filtering items", async () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
await user.type(input, "em");
|
await user.type(input, "em");
|
||||||
|
|
||||||
|
@ -229,21 +228,21 @@ describe("ComboBox", () => {
|
||||||
expect(consoleLog).not.toBeCalled();
|
expect(consoleLog).not.toBeCalled();
|
||||||
await user.click(getClearButton());
|
await user.click(getClearButton());
|
||||||
|
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
expect(consoleLog).toHaveBeenCalledWith("clear", "clear");
|
expect(consoleLog).toHaveBeenCalledWith("clear", "clear");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle disabled items", async () => {
|
it("should handle disabled items", async () => {
|
||||||
render(ComboBoxCustom);
|
render(ComboBoxCustom);
|
||||||
|
|
||||||
await user.click(screen.getByRole("textbox"));
|
await user.click(getInput());
|
||||||
const disabledOption = screen.getByText(/Fax/).closest('[role="option"]');
|
const disabledOption = screen.getByText(/Fax/).closest('[role="option"]');
|
||||||
assert(disabledOption);
|
assert(disabledOption);
|
||||||
expect(disabledOption).toHaveAttribute("disabled", "true");
|
expect(disabledOption).toHaveAttribute("disabled", "true");
|
||||||
expect(disabledOption).toHaveAttribute("aria-disabled", "true");
|
expect(disabledOption).toHaveAttribute("aria-disabled", "true");
|
||||||
|
|
||||||
await user.click(disabledOption);
|
await user.click(disabledOption);
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
|
|
||||||
// Dropdown remains open
|
// Dropdown remains open
|
||||||
const dropdown = screen.getAllByRole("listbox")[1];
|
const dropdown = screen.getAllByRole("listbox")[1];
|
||||||
|
@ -253,7 +252,7 @@ describe("ComboBox", () => {
|
||||||
it("should handle custom item display", async () => {
|
it("should handle custom item display", async () => {
|
||||||
render(ComboBoxCustom);
|
render(ComboBoxCustom);
|
||||||
|
|
||||||
await user.click(screen.getByRole("textbox"));
|
await user.click(getInput());
|
||||||
const options = screen.getAllByRole("option");
|
const options = screen.getAllByRole("option");
|
||||||
|
|
||||||
expect(options[0]).toHaveTextContent("Item Slack");
|
expect(options[0]).toHaveTextContent("Item Slack");
|
||||||
|
@ -272,19 +271,18 @@ describe("ComboBox", () => {
|
||||||
render(ComboBoxCustom, { props: { selectedId: "1" } });
|
render(ComboBoxCustom, { props: { selectedId: "1" } });
|
||||||
|
|
||||||
await user.click(getClearButton());
|
await user.click(getClearButton());
|
||||||
|
expect(getInput()).toHaveValue("");
|
||||||
const input = screen.getByRole("textbox");
|
|
||||||
expect(input).toHaveValue("");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should programmatically clear selection", async () => {
|
it("should programmatically clear selection", async () => {
|
||||||
render(ComboBoxCustom, { props: { selectedId: "1" } });
|
render(ComboBoxCustom, { props: { selectedId: "1" } });
|
||||||
|
|
||||||
const textbox = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
expect(textbox).toHaveValue("Email");
|
expect(input).toHaveValue("Email");
|
||||||
|
|
||||||
await user.click(screen.getByText("Clear"));
|
await user.click(screen.getByText("Clear"));
|
||||||
expect(textbox).toHaveValue("");
|
expect(input).toHaveValue("");
|
||||||
expect(textbox).toHaveFocus();
|
expect(input).toHaveFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not re-focus textbox if clearOptions.focus is false", async () => {
|
it("should not re-focus textbox if clearOptions.focus is false", async () => {
|
||||||
|
@ -295,19 +293,20 @@ describe("ComboBox", () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const textbox = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
expect(textbox).toHaveValue("Email");
|
expect(input).toHaveValue("Email");
|
||||||
|
|
||||||
await user.click(screen.getByText("Clear"));
|
await user.click(screen.getByText("Clear"));
|
||||||
expect(textbox).toHaveValue("");
|
expect(input).toHaveValue("");
|
||||||
expect(textbox).not.toHaveFocus();
|
expect(input).not.toHaveFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close menu on Escape key", async () => {
|
it("should close menu on Escape key", async () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
|
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
|
|
||||||
const dropdown = screen.getAllByRole("listbox")[1];
|
const dropdown = screen.getAllByRole("listbox")[1];
|
||||||
|
@ -315,8 +314,8 @@ describe("ComboBox", () => {
|
||||||
|
|
||||||
await user.keyboard("{Escape}");
|
await user.keyboard("{Escape}");
|
||||||
expect(dropdown).not.toBeVisible();
|
expect(dropdown).not.toBeVisible();
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
expect(screen.getByRole("textbox")).toHaveFocus();
|
expect(getInput()).toHaveFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should close menu and clear selection on Escape key", async () => {
|
it("should close menu and clear selection on Escape key", async () => {
|
||||||
|
@ -327,9 +326,9 @@ describe("ComboBox", () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("Email");
|
expect(getInput()).toHaveValue("Email");
|
||||||
|
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
|
|
||||||
const dropdown = screen.getAllByRole("listbox")[1];
|
const dropdown = screen.getAllByRole("listbox")[1];
|
||||||
|
@ -337,8 +336,8 @@ describe("ComboBox", () => {
|
||||||
|
|
||||||
await user.keyboard("{Escape}");
|
await user.keyboard("{Escape}");
|
||||||
expect(dropdown).not.toBeVisible();
|
expect(dropdown).not.toBeVisible();
|
||||||
expect(screen.getByRole("textbox")).toHaveValue("");
|
expect(getInput()).toHaveValue("");
|
||||||
expect(screen.getByRole("textbox")).toHaveFocus();
|
expect(getInput()).toHaveFocus();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use custom shouldFilterItem function", async () => {
|
it("should use custom shouldFilterItem function", async () => {
|
||||||
|
@ -353,7 +352,7 @@ describe("ComboBox", () => {
|
||||||
item.text.startsWith(value),
|
item.text.startsWith(value),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
await user.type(input, "B");
|
await user.type(input, "B");
|
||||||
const options = screen.getAllByRole("option");
|
const options = screen.getAllByRole("option");
|
||||||
|
@ -371,7 +370,7 @@ describe("ComboBox", () => {
|
||||||
itemToString: (item: { text: string }) => `Item ${item.text}`,
|
itemToString: (item: { text: string }) => `Item ${item.text}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
const options = screen.getAllByRole("option");
|
const options = screen.getAllByRole("option");
|
||||||
expect(options[0]).toHaveTextContent("Item Apple");
|
expect(options[0]).toHaveTextContent("Item Apple");
|
||||||
|
@ -395,7 +394,7 @@ describe("ComboBox", () => {
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const input = screen.getByRole("textbox");
|
const input = getInput();
|
||||||
await user.click(input);
|
await user.click(input);
|
||||||
await user.keyboard("{ArrowDown}"); // should highlight A
|
await user.keyboard("{ArrowDown}"); // should highlight A
|
||||||
await user.keyboard("{ArrowDown}"); // should skip B and highlight C
|
await user.keyboard("{ArrowDown}"); // should skip B and highlight C
|
||||||
|
@ -417,7 +416,7 @@ describe("ComboBox", () => {
|
||||||
render(ComboBox);
|
render(ComboBox);
|
||||||
|
|
||||||
await user.keyboard("{Tab}");
|
await user.keyboard("{Tab}");
|
||||||
expect(screen.getByRole("textbox")).toHaveFocus();
|
expect(getInput()).toHaveFocus();
|
||||||
|
|
||||||
const dropdown = screen.queryAllByRole("listbox")[1];
|
const dropdown = screen.queryAllByRole("listbox")[1];
|
||||||
expect(dropdown).toBeUndefined();
|
expect(dropdown).toBeUndefined();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue