Skip to content

Commit

Permalink
fix(dropdown): open dropdown on ArrowDown & ArrowUp keys (#10264)
Browse files Browse the repository at this point in the history
**Related Issue:** #8205 

## Summary

Open dropdown and focus first item on `ArrowDown`
Open dropdown and focus last item on `ArrowUp`

If any of the items is `selected` , both `ArrowDown` & `ArrowUp` keys
focus the selected item.
  • Loading branch information
anveshmekala authored Sep 25, 2024
1 parent 97d8bb8 commit 98548e4
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 30 deletions.
128 changes: 128 additions & 0 deletions packages/calcite-components/src/components/dropdown/dropdown.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1308,5 +1308,133 @@ describe("calcite-dropdown", () => {

expect(await isElementFocused(page, "#item-3")).toBe(true);
});

it("should open the dropdown and focus the first item with ArrowDown", async () => {
const page = await newE2EPage();
await page.setContent(html`
<calcite-dropdown>
<calcite-button slot="trigger">Open</calcite-button>
<calcite-dropdown-group selection-mode="single">
<calcite-dropdown-item id="item-1">1</calcite-dropdown-item>
<calcite-dropdown-item id="item-2">2</calcite-dropdown-item>
<calcite-dropdown-item id="item-3">3</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-dropdown>
`);
await skipAnimations(page);

const dropdown = await page.find("calcite-dropdown");
await dropdown.callMethod("setFocus");
await page.waitForChanges();

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await dropdown.getProperty("open")).toBe(true);
expect(await isElementFocused(page, "#item-1")).toBe(true);

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-2")).toBe(true);

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-1")).toBe(true);
});

it("should open the dropdown and focus the last item with ArrowUp", async () => {
const page = await newE2EPage();
await page.setContent(html`
<calcite-dropdown>
<calcite-button slot="trigger">Open</calcite-button>
<calcite-dropdown-group selection-mode="single">
<calcite-dropdown-item id="item-1">1</calcite-dropdown-item>
<calcite-dropdown-item id="item-2">2</calcite-dropdown-item>
<calcite-dropdown-item id="item-3">3</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-dropdown>
`);
await skipAnimations(page);

const dropdown = await page.find("calcite-dropdown");
await dropdown.callMethod("setFocus");
await page.waitForChanges();

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await dropdown.getProperty("open")).toBe(true);
expect(await isElementFocused(page, "#item-3")).toBe(true);

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-1")).toBe(true);

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-3")).toBe(true);
});

it("should open the dropdown and focus the selected item with ArrowDown", async () => {
const page = await newE2EPage();
await page.setContent(html`
<calcite-dropdown>
<calcite-button slot="trigger">Open</calcite-button>
<calcite-dropdown-group selection-mode="single">
<calcite-dropdown-item id="item-1">1</calcite-dropdown-item>
<calcite-dropdown-item id="item-2" selected>2</calcite-dropdown-item>
<calcite-dropdown-item id="item-3">3</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-dropdown>
`);
await skipAnimations(page);

const dropdown = await page.find("calcite-dropdown");
await dropdown.callMethod("setFocus");
await page.waitForChanges();

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await dropdown.getProperty("open")).toBe(true);
expect(await isElementFocused(page, "#item-2")).toBe(true);

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-3")).toBe(true);

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-2")).toBe(true);
});

it("should open the dropdown and focus the selected item with ArrowUp", async () => {
const page = await newE2EPage();
await page.setContent(html`
<calcite-dropdown>
<calcite-button slot="trigger">Open</calcite-button>
<calcite-dropdown-group selection-mode="single">
<calcite-dropdown-item id="item-1">1</calcite-dropdown-item>
<calcite-dropdown-item id="item-2" selected>2</calcite-dropdown-item>
<calcite-dropdown-item id="item-3">3</calcite-dropdown-item>
</calcite-dropdown-group>
</calcite-dropdown>
`);
await skipAnimations(page);

const dropdown = await page.find("calcite-dropdown");
await dropdown.callMethod("setFocus");
await page.waitForChanges();

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await dropdown.getProperty("open")).toBe(true);
expect(await isElementFocused(page, "#item-2")).toBe(true);

await page.keyboard.press("ArrowUp");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-1")).toBe(true);

await page.keyboard.press("ArrowDown");
await page.waitForChanges();
expect(await isElementFocused(page, "#item-2")).toBe(true);
});
});
});
64 changes: 34 additions & 30 deletions packages/calcite-components/src/components/dropdown/dropdown.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ export class Dropdown
<div
class="calcite-trigger-container"
id={`${guid}-menubutton`}
onClick={this.openCalciteDropdown}
onClick={this.toggleDropdown}
onKeyDown={this.keyDownHandler}
ref={this.setReferenceEl}
>
Expand Down Expand Up @@ -354,7 +354,7 @@ export class Dropdown
return;
}

this.openCalciteDropdown();
this.toggleDropdown();
}

@Listen("pointerleave")
Expand Down Expand Up @@ -445,6 +445,8 @@ export class Dropdown

guid = `calcite-dropdown-${guid()}`;

private focusLastDropdownItem = false;

//--------------------------------------------------------------------------
//
// Private Methods
Expand Down Expand Up @@ -573,24 +575,25 @@ export class Dropdown
return;
}

if (this.open) {
if (key === "Escape") {
this.closeCalciteDropdown();
event.preventDefault();
return;
} else if (event.shiftKey && key === "Tab") {
this.closeCalciteDropdown();
event.preventDefault();
return;
}
if (key === "Escape") {
this.closeCalciteDropdown();
event.preventDefault();
return;
}

if (isActivationKey(key)) {
this.openCalciteDropdown();
event.preventDefault();
} else if (key === "Escape") {
if (this.open && event.shiftKey && key === "Tab") {
this.closeCalciteDropdown();
event.preventDefault();
return;
}

if (isActivationKey(key)) {
this.toggleDropdown();
event.preventDefault();
} else if (key === "ArrowDown" || key === "ArrowUp") {
this.focusLastDropdownItem = key === "ArrowUp";
this.open = true;
this.el.addEventListener("calciteDropdownOpen", this.onOpenEnd);
}
};

Expand Down Expand Up @@ -634,29 +637,30 @@ export class Dropdown
}
}

private focusOnFirstActiveOrFirstItem = (): void => {
this.getFocusableElement(
this.getTraversableItems().find((item) => item.selected) || this.items[0],
);
};
private focusOnFirstActiveOrDefaultItem = (): void => {
const selectedItem = this.getTraversableItems().find((item) => item.selected);
const target: HTMLCalciteDropdownItemElement =
selectedItem ||
(this.focusLastDropdownItem ? this.items[this.items.length - 1] : this.items[0]);

this.focusLastDropdownItem = false;

private getFocusableElement(item: HTMLCalciteDropdownItemElement): void {
if (!item) {
if (!target) {
return;
}

focusElement(item);
}
focusElement(target);
};

private toggleOpenEnd = (): void => {
this.focusOnFirstActiveOrFirstItem();
this.el.removeEventListener("calciteDropdownOpen", this.toggleOpenEnd);
private onOpenEnd = (): void => {
this.focusOnFirstActiveOrDefaultItem();
this.el.removeEventListener("calciteDropdownOpen", this.onOpenEnd);
};

private openCalciteDropdown = () => {
private toggleDropdown = () => {
this.open = !this.open;
if (this.open) {
this.el.addEventListener("calciteDropdownOpen", this.toggleOpenEnd);
this.el.addEventListener("calciteDropdownOpen", this.onOpenEnd);
}
};

Expand Down

0 comments on commit 98548e4

Please sign in to comment.