mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-14 09:51:36 +00:00
parent
8c60dbbbbb
commit
28a895e3cc
2 changed files with 141 additions and 135 deletions
|
@ -347,63 +347,7 @@
|
|||
class="bx--list-box__invalid-icon bx--list-box__invalid-icon--warning"
|
||||
/>
|
||||
{/if}
|
||||
<ListBoxField
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-expanded={open}
|
||||
on:click={() => {
|
||||
if (disabled) return;
|
||||
if (filterable) {
|
||||
open = true;
|
||||
inputRef.focus();
|
||||
} else {
|
||||
open = !open;
|
||||
}
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (filterable) {
|
||||
return;
|
||||
}
|
||||
const key = e.key;
|
||||
if ([" ", "ArrowUp", "ArrowDown"].includes(key)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (key === " ") {
|
||||
open = !open;
|
||||
} else if (key === "Tab") {
|
||||
if (selectionRef && checked.length > 0) {
|
||||
selectionRef.focus();
|
||||
} else {
|
||||
open = false;
|
||||
}
|
||||
} else if (key === "ArrowDown") {
|
||||
change(1);
|
||||
} else if (key === "ArrowUp") {
|
||||
change(-1);
|
||||
} else if (key === "Enter") {
|
||||
if (highlightedIndex > -1) {
|
||||
sortedItems = sortedItems.map((item, i) => {
|
||||
if (i !== highlightedIndex) return item;
|
||||
return { ...item, checked: !item.checked };
|
||||
});
|
||||
}
|
||||
} else if (key === "Escape") {
|
||||
open = false;
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
if (filterable) {
|
||||
open = true;
|
||||
if (inputRef) inputRef.focus();
|
||||
}
|
||||
}}
|
||||
on:blur={(e) => {
|
||||
if (!filterable) dispatch("blur", e);
|
||||
}}
|
||||
{id}
|
||||
{disabled}
|
||||
{translateWithId}
|
||||
>
|
||||
<div class:bx--list-box__field--wrapper={true}>
|
||||
{#if checked.length > 0}
|
||||
<ListBoxSelection
|
||||
selectionCount={checked.length}
|
||||
|
@ -419,85 +363,143 @@
|
|||
{disabled}
|
||||
/>
|
||||
{/if}
|
||||
{#if filterable}
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:value
|
||||
{...$$restProps}
|
||||
role="combobox"
|
||||
tabindex="0"
|
||||
autocomplete="off"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded={open}
|
||||
aria-activedescendant={highlightedId}
|
||||
aria-disabled={disabled}
|
||||
aria-controls={menuId}
|
||||
class:bx--text-input={true}
|
||||
class:bx--text-input--empty={value === ""}
|
||||
class:bx--text-input--light={light}
|
||||
on:keydown
|
||||
on:keydown|stopPropagation={({ key }) => {
|
||||
if (key === "Enter") {
|
||||
if (highlightedId) {
|
||||
const filteredItemIndex = sortedItems.findIndex(
|
||||
(item) => item.id === highlightedId,
|
||||
);
|
||||
sortedItems = sortedItems.map((item, i) => {
|
||||
if (i !== filteredItemIndex) return item;
|
||||
return { ...item, checked: !item.checked };
|
||||
});
|
||||
}
|
||||
} else if (key === "Tab") {
|
||||
<ListBoxField
|
||||
role="button"
|
||||
tabindex="0"
|
||||
aria-expanded={open}
|
||||
on:click={() => {
|
||||
if (disabled) return;
|
||||
if (filterable) {
|
||||
open = true;
|
||||
inputRef.focus();
|
||||
} else {
|
||||
open = !open;
|
||||
}
|
||||
}}
|
||||
on:keydown={(e) => {
|
||||
if (filterable) {
|
||||
return;
|
||||
}
|
||||
const key = e.key;
|
||||
if ([" ", "ArrowUp", "ArrowDown"].includes(key)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
if (key === " ") {
|
||||
open = !open;
|
||||
} else if (key === "Tab") {
|
||||
if (selectionRef && checked.length > 0) {
|
||||
selectionRef.focus();
|
||||
} else {
|
||||
open = false;
|
||||
} else if (key === "ArrowDown") {
|
||||
change(1);
|
||||
} else if (key === "ArrowUp") {
|
||||
change(-1);
|
||||
} else if (key === "Escape") {
|
||||
open = false;
|
||||
} else if (key === " ") {
|
||||
if (!open) open = true;
|
||||
}
|
||||
}}
|
||||
on:input
|
||||
on:keyup
|
||||
on:focus
|
||||
on:blur
|
||||
on:paste
|
||||
{disabled}
|
||||
{placeholder}
|
||||
{id}
|
||||
{name}
|
||||
/>
|
||||
{#if invalid}
|
||||
<WarningFilled class="bx--list-box__invalid-icon" />
|
||||
{/if}
|
||||
{#if value}
|
||||
<ListBoxSelection
|
||||
on:clear={() => {
|
||||
value = "";
|
||||
open = false;
|
||||
} else if (key === "ArrowDown") {
|
||||
change(1);
|
||||
} else if (key === "ArrowUp") {
|
||||
change(-1);
|
||||
} else if (key === "Enter") {
|
||||
if (highlightedIndex > -1) {
|
||||
sortedItems = sortedItems.map((item, i) => {
|
||||
if (i !== highlightedIndex) return item;
|
||||
return { ...item, checked: !item.checked };
|
||||
});
|
||||
}
|
||||
} else if (key === "Escape") {
|
||||
open = false;
|
||||
}
|
||||
}}
|
||||
on:focus={() => {
|
||||
if (filterable) {
|
||||
open = true;
|
||||
if (inputRef) inputRef.focus();
|
||||
}
|
||||
}}
|
||||
on:blur={(e) => {
|
||||
if (!filterable) dispatch("blur", e);
|
||||
}}
|
||||
{id}
|
||||
{disabled}
|
||||
{translateWithId}
|
||||
>
|
||||
{#if filterable}
|
||||
<input
|
||||
bind:this={inputRef}
|
||||
bind:value
|
||||
{...$$restProps}
|
||||
role="combobox"
|
||||
tabindex="0"
|
||||
autocomplete="off"
|
||||
aria-autocomplete="list"
|
||||
aria-expanded={open}
|
||||
aria-activedescendant={highlightedId}
|
||||
aria-disabled={disabled}
|
||||
aria-controls={menuId}
|
||||
class:bx--text-input={true}
|
||||
class:bx--text-input--empty={value === ""}
|
||||
class:bx--text-input--light={light}
|
||||
on:keydown
|
||||
on:keydown|stopPropagation={({ key }) => {
|
||||
if (key === "Enter") {
|
||||
if (highlightedId) {
|
||||
const filteredItemIndex = sortedItems.findIndex(
|
||||
(item) => item.id === highlightedId,
|
||||
);
|
||||
sortedItems = sortedItems.map((item, i) => {
|
||||
if (i !== filteredItemIndex) return item;
|
||||
return { ...item, checked: !item.checked };
|
||||
});
|
||||
}
|
||||
} else if (key === "Tab") {
|
||||
open = false;
|
||||
} else if (key === "ArrowDown") {
|
||||
change(1);
|
||||
} else if (key === "ArrowUp") {
|
||||
change(-1);
|
||||
} else if (key === "Escape") {
|
||||
open = false;
|
||||
} else if (key === " ") {
|
||||
if (!open) open = true;
|
||||
}
|
||||
}}
|
||||
translateWithId={translateWithIdSelection}
|
||||
on:input
|
||||
on:keyup
|
||||
on:focus
|
||||
on:blur
|
||||
on:paste
|
||||
{disabled}
|
||||
{placeholder}
|
||||
{id}
|
||||
{name}
|
||||
/>
|
||||
{#if invalid}
|
||||
<WarningFilled class="bx--list-box__invalid-icon" />
|
||||
{/if}
|
||||
{#if value}
|
||||
<ListBoxSelection
|
||||
on:clear={() => {
|
||||
value = "";
|
||||
open = false;
|
||||
}}
|
||||
translateWithId={translateWithIdSelection}
|
||||
{disabled}
|
||||
{open}
|
||||
/>
|
||||
{/if}
|
||||
<ListBoxMenuIcon
|
||||
style="pointer-events: {open ? 'auto' : 'none'}"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
open = !open;
|
||||
}}
|
||||
{translateWithId}
|
||||
{open}
|
||||
/>
|
||||
{/if}
|
||||
<ListBoxMenuIcon
|
||||
style="pointer-events: {open ? 'auto' : 'none'}"
|
||||
on:click={(e) => {
|
||||
e.stopPropagation();
|
||||
open = !open;
|
||||
}}
|
||||
{translateWithId}
|
||||
{open}
|
||||
/>
|
||||
{/if}
|
||||
{#if !filterable}
|
||||
<span class:bx--list-box__label={true}>{label}</span>
|
||||
<ListBoxMenuIcon {open} {translateWithId} />
|
||||
{/if}
|
||||
</ListBoxField>
|
||||
{#if !filterable}
|
||||
<span class:bx--list-box__label={true}>{label}</span>
|
||||
<ListBoxMenuIcon {open} {translateWithId} />
|
||||
{/if}
|
||||
</ListBoxField>
|
||||
</div>
|
||||
<div style:display={open ? "block" : "none"}>
|
||||
<ListBoxMenu aria-label={ariaLabel} {id} aria-multiselectable="true">
|
||||
{#each filterable ? filteredItems : sortedItems as item, i (item.id)}
|
||||
|
|
|
@ -465,20 +465,24 @@ describe("MultiSelect", () => {
|
|||
selectedIds: ["0", "1"],
|
||||
},
|
||||
});
|
||||
await user.click(screen.getAllByRole("button")[0]);
|
||||
|
||||
await openMenu();
|
||||
const options = screen.getAllByRole("option");
|
||||
expect(options[0]).toHaveAttribute("aria-selected", "true");
|
||||
expect(options[1]).toHaveAttribute("aria-selected", "true");
|
||||
expect(options[2]).toHaveAttribute("aria-selected", "false");
|
||||
|
||||
const clearButton = screen.getByRole("button", { name: /clear/i });
|
||||
await closeMenu();
|
||||
const clearButton = screen.getByRole("button", {
|
||||
name: /clear all selected items/i,
|
||||
});
|
||||
await user.click(clearButton);
|
||||
await user.click(screen.getByRole("button"));
|
||||
|
||||
expect(options[0]).toHaveAttribute("aria-selected", "false");
|
||||
expect(options[1]).toHaveAttribute("aria-selected", "false");
|
||||
expect(options[2]).toHaveAttribute("aria-selected", "false");
|
||||
await openMenu();
|
||||
const updatedOptions = screen.getAllByRole("option");
|
||||
expect(updatedOptions[0]).toHaveAttribute("aria-selected", "false");
|
||||
expect(updatedOptions[1]).toHaveAttribute("aria-selected", "false");
|
||||
expect(updatedOptions[2]).toHaveAttribute("aria-selected", "false");
|
||||
});
|
||||
|
||||
it("skips disabled items during keyboard navigation", async () => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue