diff --git a/src/components/stepper-item/stepper-item.e2e.ts b/src/components/stepper-item/stepper-item.e2e.ts
index e22e2759c7d..72140be2e1d 100644
--- a/src/components/stepper-item/stepper-item.e2e.ts
+++ b/src/components/stepper-item/stepper-item.e2e.ts
@@ -1,4 +1,7 @@
+import { newE2EPage } from "@stencil/core/testing";
+import { html } from "../../../support/formatting";
import { disabled, renders, hidden } from "../../tests/commonTests";
+import { clickStepperItemContent, getSelectedItemId, itemClicker } from "../stepper/utils";
describe("calcite-stepper-item", () => {
it("renders", () => renders("calcite-stepper-item", { display: "flex" }));
@@ -6,4 +9,82 @@ describe("calcite-stepper-item", () => {
it("honors hidden attribute", async () => hidden("calcite-stepper-item"));
it("can be disabled", () => disabled("calcite-stepper-item"));
+
+ describe("should emit calciteStepperItemSelect on user interaction", () => {
+ const stepperPage = html`
+
+ Step 1 content
+
+
+ Step 2 content
+
+
+ Step 3 content
+
+
+ Step 4 content
+
+ `;
+
+ it("should emit calciteStepperItemSelect on mouse interaction", async () => {
+ const page = await newE2EPage();
+ await page.setContent(stepperPage);
+
+ const item1 = await page.find("calcite-stepper-item#step-1");
+ expect(await item1.getProperty("selected")).toBe(true);
+ expect(await getSelectedItemId(page)).toBe("step-1");
+
+ const item2 = await page.find("calcite-stepper-item#step-2");
+ const eventSpy = await item2.spyOnEvent("calciteStepperItemSelect");
+ item2.setProperty("selected", true);
+ await page.waitForChanges();
+ expect(await getSelectedItemId(page)).toBe("step-2");
+ expect(eventSpy).toHaveReceivedEventTimes(1);
+
+ await page.$eval("calcite-stepper-item#step-2", itemClicker);
+
+ expect(await getSelectedItemId(page)).toBe("step-2");
+ expect(eventSpy).toHaveReceivedEventTimes(1);
+ });
+
+ it("should emit calciteStepperItemSelect on keyboard interaction", async () => {
+ const page = await newE2EPage();
+ await page.setContent(stepperPage);
+
+ const item1 = await page.find("calcite-stepper-item#step-1");
+ expect(await item1.getProperty("selected")).toBe(true);
+ expect(await getSelectedItemId(page)).toBe("step-1");
+
+ const item4 = await page.find("calcite-stepper-item#step-4");
+ const eventSpy = await item4.spyOnEvent("calciteStepperItemSelect");
+ expect(eventSpy).toHaveReceivedEventTimes(0);
+
+ await page.keyboard.press("Tab");
+ await page.waitForChanges();
+ await page.keyboard.press("Tab");
+ await page.waitForChanges();
+ await page.keyboard.press("Tab");
+ await page.waitForChanges();
+ await page.keyboard.press("Enter");
+ await page.waitForChanges();
+ expect(eventSpy).toHaveReceivedEventTimes(1);
+ expect(await getSelectedItemId(page)).toBe("step-4");
+ expect(await item4.getProperty("selected")).toBe(true);
+ expect(await item1.getProperty("selected")).toBe(false);
+ });
+
+ it("should not emit calciteStepperItemSelect on user interaction with content", async () => {
+ const page = await newE2EPage();
+ await page.setContent(stepperPage);
+
+ const item1 = await page.find("calcite-stepper-item#step-1");
+ expect(await item1.getProperty("selected")).toBe(true);
+
+ const eventSpy = await item1.spyOnEvent("calciteStepperItemSelect");
+ expect(eventSpy).toHaveReceivedEventTimes(0);
+
+ await clickStepperItemContent(page, "#step-1");
+ expect(eventSpy).toHaveReceivedEventTimes(0);
+ });
+ });
});
diff --git a/src/components/stepper-item/stepper-item.tsx b/src/components/stepper-item/stepper-item.tsx
index 1c97cca988f..d62254e51af 100644
--- a/src/components/stepper-item/stepper-item.tsx
+++ b/src/components/stepper-item/stepper-item.tsx
@@ -32,6 +32,7 @@ import {
LoadableComponent,
componentLoaded
} from "../../utils/loadable";
+import { getItemPosition } from "../stepper/utils";
/**
* @slot - A slot for adding custom content.
@@ -142,10 +143,10 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo
calciteInternalStepperItemKeyEvent: EventEmitter;
/**
- * @internal
+ * Emits when the component's header is selected.
*/
@Event({ cancelable: false })
- calciteInternalStepperItemSelect: EventEmitter;
+ calciteStepperItemSelect: EventEmitter;
/**
* @internal
@@ -176,7 +177,7 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo
this.layout = getElementProp(this.el, "layout", false);
this.scale = getElementProp(this.el, "scale", "m");
this.parentStepperEl = this.el.parentElement as HTMLCalciteStepperElement;
- this.itemPosition = this.getItemPosition();
+ this.itemPosition = getItemPosition(this.parentStepperEl, this.el);
this.registerStepperItem();
if (this.selected) {
@@ -332,15 +333,14 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo
) {
return;
}
-
this.emitUserRequestedItem();
};
private emitUserRequestedItem = (): void => {
- this.emitRequestedItem();
if (!this.disabled) {
const position = this.itemPosition;
-
+ this.selectedPosition = getItemPosition(this.parentStepperEl, this.el);
+ this.determineSelectedItem();
this.calciteInternalUserRequestedStepperItemSelect.emit({
position
});
@@ -349,20 +349,10 @@ export class StepperItem implements InteractiveComponent, LocalizedComponent, Lo
private emitRequestedItem = (): void => {
if (!this.disabled) {
- const position = this.itemPosition;
-
- this.calciteInternalStepperItemSelect.emit({
- position
- });
+ this.calciteStepperItemSelect.emit();
}
};
- private getItemPosition(): number {
- return Array.from(this.parentStepperEl?.querySelectorAll("calcite-stepper-item")).indexOf(
- this.el
- );
- }
-
renderNumbers(): string {
numberStringFormatter.numberFormatOptions = {
locale: this.effectiveLocale,
diff --git a/src/components/stepper/stepper.e2e.ts b/src/components/stepper/stepper.e2e.ts
index 946e1f4e8a0..4f3844cce0b 100644
--- a/src/components/stepper/stepper.e2e.ts
+++ b/src/components/stepper/stepper.e2e.ts
@@ -1,6 +1,7 @@
import { E2EPage, newE2EPage } from "@stencil/core/testing";
import { renders, hidden } from "../../tests/commonTests";
import { html } from "../../../support/formatting";
+import { clickStepperItemContent, getSelectedItemId, itemClicker } from "./utils";
// todo test the automatic setting of first item to selected
describe("calcite-stepper", () => {
@@ -440,12 +441,6 @@ describe("calcite-stepper", () => {
const eventSpy = await element.spyOnEvent("calciteStepperItemChange");
const firstItem = await page.find("#step-1");
- const getSelectedItemId = async (): Promise => {
- return await page.evaluate((): string => {
- return document.querySelector("calcite-stepper")?.selectedItem?.id || "";
- });
- };
-
let expectedEvents = 0;
// non user interaction
@@ -453,23 +448,15 @@ describe("calcite-stepper", () => {
await page.waitForChanges();
expect(eventSpy).toHaveReceivedEventTimes(expectedEvents);
- // we use browser-context function to click on items to workaround `E2EElement#click` error
- async function itemClicker(item: HTMLCalciteStepperItemElement) {
- item.click();
- }
-
await page.$eval("#step-2", itemClicker);
expect(eventSpy).toHaveReceivedEventTimes(++expectedEvents);
- expect(await getSelectedItemId()).toBe("step-2");
+ expect(await getSelectedItemId(page)).toBe("step-2");
if (hasContent) {
- await page.$eval("#step-1", (item: HTMLCalciteStepperItemElement) =>
- item.shadowRoot.querySelector(".stepper-item-content").click()
- );
-
+ await clickStepperItemContent(page, "#step-1");
if (layout === "vertical") {
expect(eventSpy).toHaveReceivedEventTimes(++expectedEvents);
- expect(await getSelectedItemId()).toBe("step-1");
+ expect(await getSelectedItemId(page)).toBe("step-1");
} else {
// no events since horizontal layout moves content outside of item selection hit area
expect(eventSpy).toHaveReceivedEventTimes(expectedEvents);
@@ -482,7 +469,7 @@ describe("calcite-stepper", () => {
await page.$eval("#step-4", itemClicker);
expect(eventSpy).toHaveReceivedEventTimes(++expectedEvents);
- expect(await getSelectedItemId()).toBe("step-4");
+ expect(await getSelectedItemId(page)).toBe("step-4");
await element.callMethod("prevStep");
await page.waitForChanges();
diff --git a/src/components/stepper/stepper.tsx b/src/components/stepper/stepper.tsx
index 0b30ba3e60b..237046a7bcc 100644
--- a/src/components/stepper/stepper.tsx
+++ b/src/components/stepper/stepper.tsx
@@ -14,6 +14,7 @@ import { focusElementInGroup } from "../../utils/dom";
import { NumberingSystem } from "../../utils/locale";
import { Layout, Scale } from "../interfaces";
import { StepperItemChangeEventDetail, StepperItemKeyEventDetail } from "./interfaces";
+import { getItemPosition } from "./utils";
/**
* @slot - A slot for adding `calcite-stepper-item` elements.
@@ -154,18 +155,18 @@ export class Stepper {
event.stopPropagation();
}
- @Listen("calciteInternalStepperItemSelect")
+ @Listen("calciteStepperItemSelect")
updateItem(event: CustomEvent): void {
- const { position } = event.detail;
-
+ const stepperItemEl = event.target as HTMLCalciteStepperItemElement;
+ const position = getItemPosition(this.el, stepperItemEl);
if (typeof position === "number") {
this.currentPosition = position;
- this.selectedItem = event.target as HTMLCalciteStepperItemElement;
- }
+ this.selectedItem = stepperItemEl;
- this.calciteInternalStepperItemChange.emit({
- position
- });
+ this.calciteInternalStepperItemChange.emit({
+ position
+ });
+ }
}
@Listen("calciteInternalUserRequestedStepperItemSelect")
diff --git a/src/components/stepper/utils.ts b/src/components/stepper/utils.ts
new file mode 100644
index 00000000000..8e016ec44da
--- /dev/null
+++ b/src/components/stepper/utils.ts
@@ -0,0 +1,25 @@
+import { E2EPage } from "@stencil/core/testing";
+
+export const getItemPosition = (
+ stepperEl: HTMLCalciteStepperElement,
+ stepperItemEl: HTMLCalciteStepperItemElement
+): number => {
+ return Array.from(stepperEl.querySelectorAll("calcite-stepper-item")).indexOf(stepperItemEl);
+};
+
+export const getSelectedItemId = async (page: E2EPage): Promise => {
+ return await page.evaluate((): string => {
+ return document.querySelector("calcite-stepper")?.selectedItem?.id || "";
+ });
+};
+
+export const clickStepperItemContent = async (page: E2EPage, selector: string): Promise => {
+ await page.$eval(selector, (item: HTMLCalciteStepperItemElement) =>
+ item.shadowRoot.querySelector(".stepper-item-content").click()
+ );
+};
+
+// we use browser-context function to click on items to workaround `E2EElement#click` error
+export const itemClicker = async (item: HTMLCalciteStepperItemElement): Promise => {
+ item.click();
+};