fix(list-box): correct button/description translations based on selection count (#2139)

The `ListBoxSelection` component now properly handles
translations for the clear button based on the selected items:

- Fix `buttonLabel` and `description` to use the same translation logic
- Add tests for custom translations in both `ComboBox` and `MultiSelect`
This commit is contained in:
Eric Liu 2025-03-23 11:28:06 -07:00 committed by GitHub
commit 1a5f2d8e67
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 75 additions and 13 deletions

View file

@ -44,13 +44,12 @@
$: if (ctx && ref) { $: if (ctx && ref) {
ctx.declareRef({ key: "selection", ref }); ctx.declareRef({ key: "selection", ref });
} }
$: translationId =
$: translationId = selectionCount selectionCount === undefined
? translationIds.clearAll ? translationIds.clearSelection
: translationIds.clearSelection; : translationIds.clearAll;
$: buttonLabel = $: buttonLabel =
translateWithId?.(translationIds.clearAll) ?? translateWithId?.(translationId) ?? defaultTranslations[translationId];
defaultTranslations[translationIds.clearAll];
$: description = $: description =
translateWithId?.(translationId) ?? defaultTranslations[translationId]; translateWithId?.(translationId) ?? defaultTranslations[translationId];
</script> </script>

View file

@ -1,13 +1,13 @@
<script lang="ts"> <script lang="ts">
import { ComboBox } from "carbon-components-svelte"; import { ComboBox } from "carbon-components-svelte";
import type { ComboBoxItem } from "carbon-components-svelte/ComboBox/ComboBox.svelte"; import type { ComponentProps } from "svelte";
export let items: ComboBoxItem[] = [ export let items: ComponentProps<ComboBox>["items"] = [
{ id: "0", text: "Slack" }, { id: "0", text: "Slack" },
{ id: "1", text: "Email" }, { id: "1", text: "Email" },
{ id: "2", text: "Fax" }, { id: "2", text: "Fax" },
]; ];
export let selectedId: string | undefined = undefined; export let selectedId: ComponentProps<ComboBox>["selectedId"] = undefined;
export let value = ""; export let value = "";
export let disabled = false; export let disabled = false;
export let invalid = false; export let invalid = false;
@ -20,8 +20,12 @@
export let warnText = ""; export let warnText = "";
export let helperText = ""; export let helperText = "";
export let size: "sm" | "xl" | undefined = undefined; export let size: "sm" | "xl" | undefined = undefined;
export let shouldFilterItem = (item: ComboBoxItem, value: string) => export let shouldFilterItem: ComponentProps<ComboBox>["shouldFilterItem"] = (
item.text.toLowerCase().includes(value.toLowerCase()); item,
value,
) => item.text.toLowerCase().includes(value.toLowerCase());
export let translateWithIdSelection: ComponentProps<ComboBox>["translateWithIdSelection"] =
undefined;
</script> </script>
<ComboBox <ComboBox
@ -40,6 +44,7 @@
{warn} {warn}
{warnText} {warnText}
{shouldFilterItem} {shouldFilterItem}
{translateWithIdSelection}
on:select={(e) => { on:select={(e) => {
console.log("select", e.detail); console.log("select", e.detail);
}} }}

View file

@ -60,13 +60,35 @@ describe("ComboBox", () => {
}, },
}); });
const clearButton = screen.getByRole("button", { name: /clear/i }); const clearButton = screen.getByRole("button", {
name: "Clear selected item",
});
await user.click(clearButton); await user.click(clearButton);
expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String)); expect(consoleLog).toHaveBeenCalledWith("clear", expect.any(String));
expect(screen.getByRole("textbox")).toHaveValue(""); expect(screen.getByRole("textbox")).toHaveValue("");
}); });
it("should use custom translations when translateWithId is provided", () => {
const customTranslations = {
clearSelection: "Remove selected item",
clearAll: "Remove all items",
} as const;
render(ComboBox, {
props: {
selectedId: "1",
value: "Email",
translateWithIdSelection: (id) => customTranslations[id],
},
});
const clearButton = screen.getByRole("button", {
name: "Remove selected item",
});
expect(clearButton).toBeInTheDocument();
});
it("should handle disabled state", () => { it("should handle disabled state", () => {
render(ComboBox, { props: { disabled: true } }); render(ComboBox, { props: { disabled: true } });

View file

@ -124,7 +124,7 @@ describe("MultiSelect", () => {
await toggleOption("C"); await toggleOption("C");
expect(nthRenderedOptionText(0)).toBe("A"); expect(nthRenderedOptionText(0)).toBe("A");
// The newly-selected item also shouldnt move after the dropdown is closed // The newly-selected item also shouldn't move after the dropdown is closed
// and reopened. // and reopened.
await closeMenu(); await closeMenu();
await openMenu(); await openMenu();
@ -179,6 +179,42 @@ describe("MultiSelect", () => {
const input = screen.getByRole("combobox"); const input = screen.getByRole("combobox");
expect(input).toHaveValue(""); expect(input).toHaveValue("");
}); });
it("should show correct clear button label regardless of selection count", async () => {
render(MultiSelect, {
items,
selectedIds: ["0"],
});
expect(
screen.getByLabelText("Clear all selected items"),
).toBeInTheDocument();
await openMenu();
await toggleOption("Email");
expect(
screen.getByLabelText("Clear all selected items"),
).toBeInTheDocument();
});
it("should use custom translations when translateWithId is provided", async () => {
const customTranslations = {
clearSelection: "Remove selected item",
clearAll: "Remove all items",
} as const;
render(MultiSelect, {
items,
selectedIds: ["0"],
translateWithIdSelection: (id) => customTranslations[id],
});
expect(screen.getByLabelText("Remove all items")).toBeInTheDocument();
await openMenu();
await toggleOption("Email");
expect(screen.getByLabelText("Remove all items")).toBeInTheDocument();
});
}); });
describe("keyboard navigation", () => { describe("keyboard navigation", () => {