diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index 48998a14b73..65a2975be90 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -6,6 +6,7 @@ import { debounceTimeout } from "./resources"; import { CSS } from "../list-item/resources"; import { DEBOUNCE_TIMEOUT as FILTER_DEBOUNCE_TIMEOUT } from "../filter/resources"; import { GlobalTestProps, dragAndDrop, isElementFocused } from "../../tests/utils"; +import { DragDetail } from "../../utils/sortableComponent"; const placeholder = placeholderImage({ width: 140, @@ -474,6 +475,8 @@ describe("calcite-list", () => { type TestWindow = GlobalTestProps<{ calledTimes: number; + newIndex: number; + oldIndex: number; }>; it("works using a mouse", async () => { @@ -481,9 +484,14 @@ describe("calcite-list", () => { // Workaround for page.spyOnEvent() failing due to drag event payload being serialized and there being circular JSON structures from the payload elements. See: https://github.com/Esri/calcite-design-system/issues/7643 await page.$eval("calcite-list", (list: HTMLCalciteListElement) => { - (window as TestWindow).calledTimes = 0; - list.addEventListener("calciteListOrderChange", () => { - (window as TestWindow).calledTimes++; + const testWindow = window as TestWindow; + testWindow.calledTimes = 0; + testWindow.newIndex = -1; + testWindow.oldIndex = -1; + list.addEventListener("calciteListOrderChange", (event: CustomEvent) => { + testWindow.calledTimes++; + testWindow.newIndex = event?.detail?.newIndex; + testWindow.oldIndex = event?.detail?.oldIndex; }); }); @@ -504,7 +512,14 @@ describe("calcite-list", () => { expect(await second.getProperty("value")).toBe("one"); await page.waitForChanges(); - expect(await page.evaluate(() => (window as TestWindow).calledTimes)).toBe(1); + const results = await page.evaluate(() => { + const testWindow = window as TestWindow; + return { calledTimes: testWindow.calledTimes, oldIndex: testWindow.oldIndex, newIndex: testWindow.newIndex }; + }); + + expect(results.calledTimes).toBe(1); + expect(results.oldIndex).toBe(0); + expect(results.newIndex).toBe(1); }); it("supports dragging items between lists", async () => { @@ -536,11 +551,12 @@ describe("calcite-list", () => { // Workaround for page.spyOnEvent() failing due to drag event payload being serialized and there being circular JSON structures from the payload elements. See: https://github.com/Esri/calcite-design-system/issues/7643 await page.evaluate(() => { - (window as TestWindow).calledTimes = 0; + const testWindow = window as TestWindow; + testWindow.calledTimes = 0; const lists = document.querySelectorAll("calcite-list"); lists.forEach((list) => list.addEventListener("calciteListOrderChange", () => { - (window as TestWindow).calledTimes++; + testWindow.calledTimes++; }) ); }); @@ -616,16 +632,21 @@ describe("calcite-list", () => { let totalMoves = 0; - const eventSpy = await page.spyOnEvent("calciteListOrderChange"); + // Workaround for page.spyOnEvent() failing due to drag event payload being serialized and there being circular JSON structures from the payload elements. See: https://github.com/Esri/calcite-design-system/issues/7643 + await page.$eval("calcite-list", (list: HTMLCalciteListElement) => { + const testWindow = window as TestWindow; + testWindow.calledTimes = 0; + list.addEventListener("calciteListOrderChange", () => { + testWindow.calledTimes++; + }); + }); async function assertKeyboardMove( arrowKey: "ArrowDown" | "ArrowUp", expectedValueOrder: string[] ): Promise { - const calciteListOrderChangeEvent = page.waitForEvent("calciteListOrderChange"); await page.waitForChanges(); await page.keyboard.press(arrowKey); - await calciteListOrderChangeEvent; const itemsAfter = await page.findAll("calcite-list-item"); expect(itemsAfter.length).toBe(3); @@ -633,7 +654,9 @@ describe("calcite-list", () => { expect(await itemsAfter[i].getProperty("value")).toBe(expectedValueOrder[i]); } - expect(eventSpy).toHaveReceivedEventTimes(++totalMoves); + const calledTimes = await page.evaluate(() => (window as TestWindow).calledTimes); + + expect(calledTimes).toBe(++totalMoves); } await assertKeyboardMove("ArrowDown", ["two", "one", "three"]); diff --git a/packages/calcite-components/src/components/list/list.tsx b/packages/calcite-components/src/components/list/list.tsx index ea20806d094..92d04130dd5 100755 --- a/packages/calcite-components/src/components/list/list.tsx +++ b/packages/calcite-components/src/components/list/list.tsx @@ -766,23 +766,25 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo const sameParentItems = enabledListItems.filter((item) => item.parentElement === parentEl); const lastIndex = sameParentItems.length - 1; - const startingIndex = sameParentItems.indexOf(sortItem); + const oldIndex = sameParentItems.indexOf(sortItem); let appendInstead = false; - let buddyIndex: number; + let newIndex: number; if (direction === "up") { - if (startingIndex === 0) { + if (oldIndex === 0) { appendInstead = true; + newIndex = lastIndex; } else { - buddyIndex = startingIndex - 1; + newIndex = oldIndex - 1; } } else { - if (startingIndex === lastIndex) { - buddyIndex = 0; - } else if (startingIndex === lastIndex - 1) { + if (oldIndex === lastIndex) { + newIndex = 0; + } else if (oldIndex === lastIndex - 1) { appendInstead = true; + newIndex = lastIndex; } else { - buddyIndex = startingIndex + 2; + newIndex = oldIndex + 2; } } @@ -791,7 +793,7 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo if (appendInstead) { parentEl.appendChild(sortItem); } else { - parentEl.insertBefore(sortItem, sameParentItems[buddyIndex]); + parentEl.insertBefore(sortItem, sameParentItems[newIndex]); } this.updateListItems(); @@ -799,8 +801,10 @@ export class List implements InteractiveComponent, LoadableComponent, SortableCo this.calciteListOrderChange.emit({ dragEl: sortItem, - fromEl: null, - toEl: null, + fromEl: parentEl, + toEl: parentEl, + newIndex, + oldIndex, }); handle.setFocus().then(() => { diff --git a/packages/calcite-components/src/utils/sortableComponent.ts b/packages/calcite-components/src/utils/sortableComponent.ts index 071d00797bf..83fe5053bfb 100644 --- a/packages/calcite-components/src/utils/sortableComponent.ts +++ b/packages/calcite-components/src/utils/sortableComponent.ts @@ -5,6 +5,8 @@ export interface DragDetail { toEl: HTMLElement; fromEl: HTMLElement; dragEl: HTMLElement; + newIndex: number; + oldIndex: number; } export const CSS = { @@ -93,10 +95,12 @@ export function connectSortableComponent(component: SortableComponent): void { group: { name: group, ...(!!component.canPull && { - pull: (to, from, dragEl) => component.canPull({ toEl: to.el, fromEl: from.el, dragEl }), + pull: (to, from, dragEl, { newIndex, oldIndex }) => + component.canPull({ toEl: to.el, fromEl: from.el, dragEl, newIndex, oldIndex }), }), ...(!!component.canPut && { - put: (to, from, dragEl) => component.canPut({ toEl: to.el, fromEl: from.el, dragEl }), + put: (to, from, dragEl, { newIndex, oldIndex }) => + component.canPut({ toEl: to.el, fromEl: from.el, dragEl, newIndex, oldIndex }), }), }, }), @@ -109,8 +113,8 @@ export function connectSortableComponent(component: SortableComponent): void { dragState.active = false; onDragEnd(); }, - onSort: ({ from: fromEl, item: dragEl, to: toEl }) => { - component.onDragSort({ fromEl, dragEl, toEl }); + onSort: ({ from: fromEl, item: dragEl, to: toEl, newIndex, oldIndex }) => { + component.onDragSort({ fromEl, dragEl, toEl, newIndex, oldIndex }); }, }); }