diff --git a/packages/calcite-components/src/components/list-item/list-item.tsx b/packages/calcite-components/src/components/list-item/list-item.tsx index 173a51f9697..5fc5e11a8bf 100644 --- a/packages/calcite-components/src/components/list-item/list-item.tsx +++ b/packages/calcite-components/src/components/list-item/list-item.tsx @@ -317,6 +317,8 @@ export class ListItem actionsEndEl: HTMLTableCellElement; + handleGridEl: HTMLTableCellElement; + defaultSlotEl: HTMLSlotElement; // -------------------------------------------------------------------------- @@ -365,11 +367,11 @@ export class ListItem @Method() async setFocus(): Promise { await componentFocusable(this); - const { containerEl, contentEl, actionsStartEl, actionsEndEl, parentListEl } = this; + const { containerEl, parentListEl } = this; const focusIndex = focusMap.get(parentListEl); if (typeof focusIndex === "number") { - const cells = [actionsStartEl, contentEl, actionsEndEl].filter((el) => el && !el.hidden); + const cells = this.getGridCells(); if (cells[focusIndex]) { this.focusCell(cells[focusIndex]); } else { @@ -411,13 +413,22 @@ export class ListItem } renderDragHandle(): VNode { - return this.dragHandle ? ( - + const { label, dragHandle, dragDisabled, setPosition, setSize } = this; + + return dragHandle ? ( + (this.handleGridEl = el)} + > ) : null; @@ -761,6 +772,12 @@ export class ListItem this.calciteListItemSelect.emit(); }; + getGridCells(): HTMLTableCellElement[] { + return [this.handleGridEl, this.actionsStartEl, this.contentEl, this.actionsEndEl].filter( + (el) => el && !el.hidden + ); + } + handleItemKeyDown = (event: KeyboardEvent): void => { if (event.defaultPrevented) { return; @@ -768,9 +785,9 @@ export class ListItem const { key } = event; const composedPath = event.composedPath(); - const { containerEl, contentEl, actionsStartEl, actionsEndEl, open, openable } = this; + const { containerEl, actionsStartEl, actionsEndEl, open, openable } = this; - const cells = [actionsStartEl, contentEl, actionsEndEl].filter((el) => el && !el.hidden); + const cells = this.getGridCells(); const currentIndex = cells.findIndex((cell) => composedPath.includes(cell)); if ( @@ -817,7 +834,7 @@ export class ListItem }; focusCell = (focusEl: HTMLTableCellElement, saveFocusIndex = true): void => { - const { contentEl, actionsStartEl, actionsEndEl, parentListEl } = this; + const { parentListEl } = this; if (saveFocusIndex) { focusMap.set(parentListEl, null); @@ -825,17 +842,15 @@ export class ListItem const focusedEl = getFirstTabbable(focusEl); - [actionsStartEl, contentEl, actionsEndEl] - .filter((el) => el && !el.hidden) - .forEach((tableCell, cellIndex) => { - const tabIndexAttr = "tabindex"; - if (tableCell === focusEl) { - focusEl === focusedEl && tableCell.setAttribute(tabIndexAttr, "0"); - saveFocusIndex && focusMap.set(parentListEl, cellIndex); - } else { - tableCell.removeAttribute(tabIndexAttr); - } - }); + this.getGridCells().forEach((tableCell, cellIndex) => { + // Only one cell within a list-item should be focusable at a time. Ensures the active cell is focusable. + if (tableCell === focusEl) { + tableCell.tabIndex = focusEl === focusedEl ? 0 : -1; + saveFocusIndex && focusMap.set(parentListEl, cellIndex); + } else { + tableCell.tabIndex = -1; + } + }); focusedEl?.focus(); }; diff --git a/packages/calcite-components/src/components/list/list.e2e.ts b/packages/calcite-components/src/components/list/list.e2e.ts index db811d55a62..451634335b1 100755 --- a/packages/calcite-components/src/components/list/list.e2e.ts +++ b/packages/calcite-components/src/components/list/list.e2e.ts @@ -640,6 +640,78 @@ describe("calcite-list", () => { expect(await isElementFocused(page, "#one")).toBe(true); expect(await one.getProperty("open")).toBe(false); }); + + it("should navigate a draggable list via ArrowRight and ArrowLeft", async () => { + const page = await newE2EPage(); + await page.setContent(html` + + + + + + + + + + `); + await page.waitForChanges(); + const list = await page.find("calcite-list"); + await list.callMethod("setFocus"); + await page.waitForChanges(); + + const one = await page.find("#one"); + expect(await one.getProperty("open")).toBe(false); + + expect(await isElementFocused(page, "#one")).toBe(true); + + await list.press("ArrowRight"); + + expect(await isElementFocused(page, "#one")).toBe(true); + expect(await one.getProperty("open")).toBe(true); + + await list.press("ArrowRight"); + + expect(await isElementFocused(page, `calcite-handle`, { shadowed: true })).toBe(true); + + await list.press("ArrowRight"); + + expect(await isElementFocused(page, `.${CSS.contentContainer}`, { shadowed: true })).toBe(true); + + await list.press("ArrowRight"); + + expect(await isElementFocused(page, "calcite-action")).toBe(true); + + await list.press("ArrowLeft"); + + expect(await isElementFocused(page, `.${CSS.contentContainer}`, { shadowed: true })).toBe(true); + + await list.press("ArrowLeft"); + + expect(await isElementFocused(page, `calcite-handle`, { shadowed: true })).toBe(true); + + await list.press("ArrowLeft"); + + expect(await isElementFocused(page, "#one")).toBe(true); + expect(await one.getProperty("open")).toBe(true); + + await list.press("ArrowLeft"); + + expect(await isElementFocused(page, "#one")).toBe(true); + expect(await one.getProperty("open")).toBe(false); + }); }); describe("drag and drop", () => {