Skip to content

feat(combobox): make combobox clearable #6972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
May 18, 2023
87 changes: 56 additions & 31 deletions src/components/combobox/combobox.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -652,44 +652,69 @@ describe("calcite-combobox", () => {
});

describe("clearing values", () => {
async function assertValueClearing(html: string): Promise<void> {
const testCases = [
{
selectionMode: "single",
html: html`
<calcite-combobox selection-mode="single">
<calcite-combobox-item selected id="one" value="one" text-label="one"></calcite-combobox-item>
<calcite-combobox-item id="two" value="two" text-label="two"></calcite-combobox-item>
<calcite-combobox-item id="three" value="three" text-label="three"></calcite-combobox-item>
</calcite-combobox>
`
},
{
selectionMode: "multiple",
html: html`
<calcite-combobox selection-mode="multiple">
<calcite-combobox-item selected id="one" value="one" text-label="one"></calcite-combobox-item>
<calcite-combobox-item selected id="two" value="two" text-label="two"></calcite-combobox-item>
<calcite-combobox-item selected id="three" value="three" text-label="three"></calcite-combobox-item>
</calcite-combobox>
`
},
{
selectionMode: "ancestors",
html: html`
<calcite-combobox selection-mode="ancestors">
<calcite-combobox-item value="parent" text-label="parent">
<calcite-combobox-item value="child1" text-label="child1"></calcite-combobox-item>
<calcite-combobox-item selected value="child2" text-label="child2"></calcite-combobox-item>
</calcite-combobox-item>
</calcite-combobox>
`
}
];

async function assertValueClearing(html: string, mode: "mouse" | "keyboard"): Promise<void> {
const page = await newE2EPage();
await page.setContent(html);
const combobox = await page.find("calcite-combobox");
const xButton = await page.find(`calcite-combobox >>> .${XButtonCSS.button}`);

await xButton.click();
const combobox = await page.find("calcite-combobox");
if (mode === "mouse") {
const xButton = await page.find(`calcite-combobox >>> .${XButtonCSS.button}`);
await xButton.click();
} else {
await combobox.callMethod("setFocus");
await page.keyboard.press("Escape");
}

expect(await combobox.getProperty("value")).toBe("");
}

it("clears the value in single-selection mode", async () =>
assertValueClearing(html`
<calcite-combobox selection-mode="single">
<calcite-combobox-item selected id="one" value="one" text-label="one"></calcite-combobox-item>
<calcite-combobox-item id="two" value="two" text-label="two"></calcite-combobox-item>
<calcite-combobox-item id="three" value="three" text-label="three"></calcite-combobox-item>
</calcite-combobox>
`));

it("clears the value in multiple-selection mode", async () =>
assertValueClearing(html`
<calcite-combobox selection-mode="multiple">
<calcite-combobox-item selected id="one" value="one" text-label="one"></calcite-combobox-item>
<calcite-combobox-item selected id="two" value="two" text-label="two"></calcite-combobox-item>
<calcite-combobox-item selected id="three" value="three" text-label="three"></calcite-combobox-item>
</calcite-combobox>
`));

it("clears the value in ancestors-selection mode", async () =>
assertValueClearing(html`
<calcite-combobox selection-mode="ancestors">
<calcite-combobox-item value="parent" text-label="parent">
<calcite-combobox-item value="child1" text-label="child1"></calcite-combobox-item>
<calcite-combobox-item selected value="child2" text-label="child2"></calcite-combobox-item>
</calcite-combobox-item>
</calcite-combobox>
`));
describe("via mouse", () => {
testCases.forEach((testCase) => {
it(`clears the value in ${testCase.selectionMode}-selection mode`, () =>
assertValueClearing(testCase.html, "mouse"));
});
});

describe("via keyboard", () => {
testCases.forEach((testCase) => {
it(`clears the value in ${testCase.selectionMode}-selection mode`, () =>
assertValueClearing(testCase.html, "keyboard"));
});
});
});

describe("keyboard navigation", () => {
Expand Down
10 changes: 7 additions & 3 deletions src/components/combobox/combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export class Combobox
//--------------------------------------------------------------------------

/**
* When `true`, the clear-value button will not be displayed when the component has a value.
* When `true`, the value-clearing will be disabled.
*/
@Prop({ reflect: true }) clearDisabled = false;

Expand Down Expand Up @@ -552,7 +552,7 @@ export class Combobox
);
}

keydownHandler = (event: KeyboardEvent): void => {
private keyDownHandler = (event: KeyboardEvent): void => {
const { key } = event;

switch (key) {
Expand Down Expand Up @@ -628,6 +628,10 @@ export class Combobox
}
break;
case "Escape":
if (!this.open) {
this.clearValue();
}

this.open = false;
event.preventDefault();
break;
Expand Down Expand Up @@ -1303,7 +1307,7 @@ export class Combobox
"wrapper--active": open
}}
onClick={this.clickHandler}
onKeyDown={this.keydownHandler}
onKeyDown={this.keyDownHandler}
role="combobox"
// eslint-disable-next-line react/jsx-sort-props
ref={this.setReferenceEl}
Expand Down