mirror of
https://github.com/carbon-design-system/carbon-components-svelte.git
synced 2025-09-15 18:31:06 +00:00
Merge branch 'master' into new-tree-flatarray-prop
This commit is contained in:
commit
d1c00813fd
11 changed files with 182 additions and 30 deletions
3
.github/workflows/checks.yml
vendored
3
.github/workflows/checks.yml
vendored
|
@ -17,6 +17,9 @@ jobs:
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install
|
run: npm install
|
||||||
|
|
||||||
|
- name: Run lint
|
||||||
|
run: npm run lint
|
||||||
|
|
||||||
- name: Unit tests
|
- name: Unit tests
|
||||||
run: npm run test
|
run: npm run test
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||||
|
|
||||||
|
### [0.86.2](https://github.com/carbon-design-system/carbon-components-svelte/compare/v0.86.1...v0.86.2) (2024-11-30)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
- **multi-select:** fix sorting behavior ([c3a390f](https://github.com/carbon-design-system/carbon-components-svelte/commit/c3a390f3fef072c6b736e33a85a2ae772df12e52)), closes [#2066](https://github.com/carbon-design-system/carbon-components-svelte/issues/2066)
|
||||||
|
|
||||||
## [0.86.1](https://github.com/carbon-design-system/carbon-components-svelte/compare/v0.86.0...v0.86.1) (2024-11-22)
|
## [0.86.1](https://github.com/carbon-design-system/carbon-components-svelte/compare/v0.86.0...v0.86.1) (2024-11-22)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# Component Index
|
# Component Index
|
||||||
|
|
||||||
> 165 components exported from carbon-components-svelte@0.86.1.
|
> 165 components exported from carbon-components-svelte@0.86.2.
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
|
|
|
@ -3235,10 +3235,7 @@
|
||||||
"ts": "interface DataTableCell<\n Row = DataTableRow,\n> {\n key:\n | DataTableKey<Row>\n | (string & {});\n value: DataTableValue;\n display?: (\n item: DataTableValue,\n row: DataTableRow,\n ) => DataTableValue;\n}\n"
|
"ts": "interface DataTableCell<\n Row = DataTableRow,\n> {\n key:\n | DataTableKey<Row>\n | (string & {});\n value: DataTableValue;\n display?: (\n item: DataTableValue,\n row: DataTableRow,\n ) => DataTableValue;\n}\n"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"generics": [
|
"generics": ["Row", "Row extends DataTableRow = DataTableRow"],
|
||||||
"Row",
|
|
||||||
"Row extends DataTableRow = DataTableRow"
|
|
||||||
],
|
|
||||||
"rest_props": {
|
"rest_props": {
|
||||||
"type": "Element",
|
"type": "Element",
|
||||||
"name": "div"
|
"name": "div"
|
||||||
|
|
|
@ -39,10 +39,10 @@
|
||||||
|
|
||||||
$: source = `https://github.com/carbon-design-system/carbon-components-svelte/tree/master/${component.filePath}`;
|
$: source = `https://github.com/carbon-design-system/carbon-components-svelte/tree/master/${component.filePath}`;
|
||||||
$: forwarded_events = component.events.filter(
|
$: forwarded_events = component.events.filter(
|
||||||
(event) => event.type === "forwarded"
|
(event) => event.type === "forwarded",
|
||||||
);
|
);
|
||||||
$: dispatched_events = component.events.filter(
|
$: dispatched_events = component.events.filter(
|
||||||
(event) => event.type === "dispatched"
|
(event) => event.type === "dispatched",
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -145,10 +145,16 @@
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
<div style:margin-top="var(--cds-layout-02)" style:margin-bottom="var(--cds-spacing-03)">
|
<div
|
||||||
|
style:margin-top="var(--cds-layout-02)"
|
||||||
|
style:margin-bottom="var(--cds-spacing-03)"
|
||||||
|
>
|
||||||
<strong>Default value</strong>
|
<strong>Default value</strong>
|
||||||
</div>
|
</div>
|
||||||
<div style:margin-bottom="var(--cds-layout-01)" style:max-width="85%">
|
<div
|
||||||
|
style:margin-bottom="var(--cds-layout-01)"
|
||||||
|
style:max-width="85%"
|
||||||
|
>
|
||||||
{#if prop.value === undefined}
|
{#if prop.value === undefined}
|
||||||
<em>undefined</em>
|
<em>undefined</em>
|
||||||
{:else}
|
{:else}
|
||||||
|
|
4
package-lock.json
generated
4
package-lock.json
generated
|
@ -1,12 +1,12 @@
|
||||||
{
|
{
|
||||||
"name": "carbon-components-svelte",
|
"name": "carbon-components-svelte",
|
||||||
"version": "0.86.1",
|
"version": "0.86.2",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "carbon-components-svelte",
|
"name": "carbon-components-svelte",
|
||||||
"version": "0.86.1",
|
"version": "0.86.2",
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "carbon-components-svelte",
|
"name": "carbon-components-svelte",
|
||||||
"version": "0.86.1",
|
"version": "0.86.2",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"description": "Svelte implementation of the Carbon Design System",
|
"description": "Svelte implementation of the Carbon Design System",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
|
|
|
@ -9,7 +9,7 @@ const formatTypeScript = async (value) => {
|
||||||
parser: "typescript",
|
parser: "typescript",
|
||||||
plugins: [plugin],
|
plugins: [plugin],
|
||||||
printWidth: 40, // Force breaking onto new lines
|
printWidth: 40, // Force breaking onto new lines
|
||||||
bracketSameLine: false
|
bracketSameLine: false,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -243,11 +243,11 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$: menuId = `menu-${id}`;
|
function sort() {
|
||||||
$: inline = type === "inline";
|
if (
|
||||||
$: ariaLabel = $$props["aria-label"] || "Choose an item";
|
selectionFeedback === "top" ||
|
||||||
$: sortedItems = (() => {
|
selectionFeedback === "top-after-reopen"
|
||||||
if (selectionFeedback === "top" && selectedIds.length > 0) {
|
) {
|
||||||
const checkedItems = items
|
const checkedItems = items
|
||||||
.filter((item) => selectedIds.includes(item.id))
|
.filter((item) => selectedIds.includes(item.id))
|
||||||
.map((item) => ({ ...item, checked: true }));
|
.map((item) => ({ ...item, checked: true }));
|
||||||
|
@ -269,7 +269,20 @@
|
||||||
checked: selectedIds.includes(item.id),
|
checked: selectedIds.includes(item.id),
|
||||||
}))
|
}))
|
||||||
.sort(sortItem);
|
.sort(sortItem);
|
||||||
})();
|
}
|
||||||
|
|
||||||
|
let sortedItems = sort();
|
||||||
|
|
||||||
|
$: menuId = `menu-${id}`;
|
||||||
|
$: inline = type === "inline";
|
||||||
|
$: ariaLabel = $$props["aria-label"] || "Choose an item";
|
||||||
|
$: if (
|
||||||
|
selectedIds &&
|
||||||
|
(selectionFeedback === "top" ||
|
||||||
|
(selectionFeedback === "top-after-reopen" && open === false))
|
||||||
|
) {
|
||||||
|
sortedItems = sort();
|
||||||
|
}
|
||||||
$: checked = sortedItems.filter(({ checked }) => checked);
|
$: checked = sortedItems.filter(({ checked }) => checked);
|
||||||
$: unchecked = sortedItems.filter(({ checked }) => !checked);
|
$: unchecked = sortedItems.filter(({ checked }) => !checked);
|
||||||
$: filteredItems = sortedItems.filter((item) => filterItem(item, value));
|
$: filteredItems = sortedItems.filter((item) => filterItem(item, value));
|
||||||
|
|
124
tests/MultiSelect/MultiSelect.test.ts
Normal file
124
tests/MultiSelect/MultiSelect.test.ts
Normal file
|
@ -0,0 +1,124 @@
|
||||||
|
import { render, screen } from "@testing-library/svelte";
|
||||||
|
import { MultiSelect } from "carbon-components-svelte";
|
||||||
|
import { user } from "../setup-tests";
|
||||||
|
|
||||||
|
describe("MultiSelect sorts items correctly", () => {
|
||||||
|
/** Opens the dropdown. */
|
||||||
|
const openMenu = async () =>
|
||||||
|
await user.click(
|
||||||
|
await screen.findByLabelText("Open menu", {
|
||||||
|
selector: `[role="button"]`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Closes the dropdown. */
|
||||||
|
const closeMenu = async () =>
|
||||||
|
await user.click(
|
||||||
|
await screen.findByLabelText("Close menu", {
|
||||||
|
selector: `[role="button"]`,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Toggles an option, identifying it by its `text` value. */
|
||||||
|
const toggleOption = async (optionText: string) =>
|
||||||
|
await user.click(
|
||||||
|
await screen.findByText((text) => text.trim() === optionText),
|
||||||
|
);
|
||||||
|
|
||||||
|
/** Fetches the `text` value of the nth option in the MultiSelect component. */
|
||||||
|
const nthRenderedOptionText = (index: number) =>
|
||||||
|
screen.queryAllByRole("option").at(index)?.textContent?.trim();
|
||||||
|
|
||||||
|
it("initially sorts items alphabetically", async () => {
|
||||||
|
render(MultiSelect, {
|
||||||
|
items: [
|
||||||
|
{ id: "1", text: "C" },
|
||||||
|
{ id: "3", text: "A" },
|
||||||
|
{ id: "2", text: "B" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
expect(nthRenderedOptionText(1)).toBe("B");
|
||||||
|
expect(nthRenderedOptionText(2)).toBe("C");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("immediately moves selected items to the top (with selectionFeedback: top)", async () => {
|
||||||
|
render(MultiSelect, {
|
||||||
|
items: [
|
||||||
|
{ id: "3", text: "C" },
|
||||||
|
{ id: "1", text: "A" },
|
||||||
|
{ id: "2", text: "B" },
|
||||||
|
],
|
||||||
|
selectionFeedback: "top",
|
||||||
|
});
|
||||||
|
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
|
||||||
|
await toggleOption("C");
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("C");
|
||||||
|
|
||||||
|
await toggleOption("C");
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("sorts newly-toggled items only after the dropdown is reoponed (with selectionFeedback: top-after-reopen)", async () => {
|
||||||
|
render(MultiSelect, {
|
||||||
|
items: [
|
||||||
|
{ id: "3", text: "C" },
|
||||||
|
{ id: "1", text: "A" },
|
||||||
|
{ id: "2", text: "B" },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initially, items should be sorted alphabetically.
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
|
||||||
|
// While the menu is still open, a newly-selected item should not move.
|
||||||
|
await toggleOption("C");
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
|
||||||
|
// The newly-selected item should move after the menu is closed and
|
||||||
|
// re-opened.
|
||||||
|
await closeMenu();
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("C");
|
||||||
|
|
||||||
|
// A deselected item should not move while the dropdown is still open.
|
||||||
|
await toggleOption("C");
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("C");
|
||||||
|
|
||||||
|
// The deselected item should move after closing and re-opening the dropdown.
|
||||||
|
await closeMenu();
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("never moves selected items to the top (with selectionFeedback: fixed)", async () => {
|
||||||
|
render(MultiSelect, {
|
||||||
|
items: [
|
||||||
|
{ id: "3", text: "C" },
|
||||||
|
{ id: "1", text: "A" },
|
||||||
|
{ id: "2", text: "B" },
|
||||||
|
],
|
||||||
|
selectionFeedback: "fixed",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Items should be sorted alphabetically.
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
|
||||||
|
// A newly-selected item should not move after the selection is made.
|
||||||
|
await toggleOption("C");
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
|
||||||
|
// The newly-selected item also shouldn’t move after the dropdown is closed
|
||||||
|
// and reopened.
|
||||||
|
await closeMenu();
|
||||||
|
await openMenu();
|
||||||
|
expect(nthRenderedOptionText(0)).toBe("A");
|
||||||
|
});
|
||||||
|
});
|
|
@ -3,4 +3,7 @@ import "@testing-library/jest-dom/vitest";
|
||||||
import { userEvent } from "@testing-library/user-event";
|
import { userEvent } from "@testing-library/user-event";
|
||||||
import "../css/all.css";
|
import "../css/all.css";
|
||||||
|
|
||||||
|
// Mock scrollIntoView since it's not implemented in JSDOM
|
||||||
|
Element.prototype.scrollIntoView = vi.fn();
|
||||||
|
|
||||||
export const user = userEvent.setup();
|
export const user = userEvent.setup();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue