diff --git a/src/packages/tour/steps.test.ts b/src/packages/tour/steps.test.ts index 24f75b35f..89f203b54 100644 --- a/src/packages/tour/steps.test.ts +++ b/src/packages/tour/steps.test.ts @@ -1,6 +1,6 @@ import { fetchSteps, nextStep, previousStep } from "./steps"; import _showElement from "./showElement"; -import { appendMockSteps, getMockSteps, getMockTour } from "./tests/mock"; +import { appendMockSteps, getMockPartialSteps, getMockSteps, getMockTour } from "./tests/mock"; import createElement from "../../util/createElement"; jest.mock("./showElement"); @@ -51,6 +51,7 @@ describe("steps", () => { const showElementMock = jest.fn(); (_showElement as jest.Mock).mockImplementation(showElementMock); const mockTour = getMockTour(); + mockTour.setSteps(getMockSteps()); // Act await nextStep(mockTour); @@ -62,6 +63,7 @@ describe("steps", () => { test("should call the onBeforeChange callback", async () => { // Arrange const mockTour = getMockTour(); + mockTour.setSteps(getMockSteps()); const fnBeforeChangeCallback = jest.fn(); mockTour.onBeforeChange(fnBeforeChangeCallback); @@ -72,7 +74,7 @@ describe("steps", () => { expect(fnBeforeChangeCallback).toHaveBeenCalledTimes(1); expect(fnBeforeChangeCallback).toHaveBeenCalledWith( undefined, - 1, + 0, "forward" ); }); @@ -85,7 +87,7 @@ describe("steps", () => { const fnBeforeChangeCallback = jest.fn(); fnBeforeChangeCallback.mockReturnValue(false); - mockTour.onbeforechange(fnBeforeChangeCallback); + mockTour.onBeforeChange(fnBeforeChangeCallback); // Act await nextStep(mockTour); @@ -98,13 +100,14 @@ describe("steps", () => { test("should wait for the onBeforeChange promise object", async () => { // Arrange const mockTour = getMockTour(); + mockTour.setSteps(getMockSteps()); const showElementMock = jest.fn(); (_showElement as jest.Mock).mockImplementation(showElementMock); const onBeforeChangeMock = jest.fn(); const sideEffect: number[] = []; - mockTour.onbeforechange(async () => { + mockTour.onBeforeChange(async () => { return new Promise((res) => { setTimeout(() => { sideEffect.push(1); @@ -127,12 +130,14 @@ describe("steps", () => { test("should call the complete callback", async () => { // Arrange const mockTour = getMockTour(); + mockTour.setSteps(getMockSteps().slice(0, 2)); const fnCompleteCallback = jest.fn(); - mockTour.oncomplete(fnCompleteCallback); + mockTour.onComplete(fnCompleteCallback); // Act await nextStep(mockTour); await nextStep(mockTour); + await nextStep(mockTour); // Assert expect(fnCompleteCallback).toBeCalledTimes(1); @@ -189,62 +194,88 @@ describe("steps", () => { const steps = fetchSteps(mockTour); // Assert - expect(steps.length).toBe(4); + expect(steps.length).toBe(5); expect(steps[0].position).toBe("floating"); + expect(steps[0].title).toBe("Floating title 1"); expect(steps[0].intro).toBe("Step One of the tour"); expect(steps[0].step).toBe(1); expect(steps[1].position).toBe("floating"); + expect(steps[1].title).toBe("Floating title 2"); expect(steps[1].intro).toBe("Step Two of the tour"); expect(steps[1].step).toBe(2); }); test("should find and add elements from options.steps to the list", () => { // Arrange + document.body.appendChild(createElement("h1")); + const mockTour = getMockTour(); - const [mockStepOneElement, mockStepTwoElement, _, __] = appendMockSteps(); + mockTour.addSteps(getMockPartialSteps()); // Act const steps = fetchSteps(mockTour); // Assert - expect(steps.length).toBe(7); + expect(steps.length).toBe(5); - expect(steps[0].element).toBe(mockStepOneElement); - expect(steps[0].position).toBe("bottom"); - expect(steps[0].intro).toBe("first"); + expect(steps[0].position).toBe("floating"); + expect(steps[0].title).toBe("Floating title 1"); + expect(steps[0].intro).toBe("Step One of the tour"); expect(steps[0].step).toBe(1); - expect(steps[1].element).toBe(mockStepTwoElement); - expect(steps[1].position).toBe("top"); - expect(steps[1].intro).toBe("second"); + expect(steps[1].position).toBe("floating"); + expect(steps[1].title).toBe("Floating title 2"); + expect(steps[1].intro).toBe("Step Two of the tour"); expect(steps[1].step).toBe(2); - expect(steps[2].position).toBe("floating"); - expect(steps[2].intro).toBe("third"); + expect(steps[2].position).toBe("top"); + expect(steps[2].title).toBe("First title"); + expect(steps[2].intro).toBe("Step Three of the tour"); expect(steps[2].step).toBe(3); + + expect(steps[3].element).toStrictEqual(getMockPartialSteps()[3].element); + expect(steps[3].position).toBe("right"); + expect(steps[3].intro).toBe("Step Four of the tour"); + expect(steps[3].step).toBe(4); + + expect(steps[4].position).toBe("floating"); + expect(steps[4].intro).toBe("Element not found"); + expect(steps[4].step).toBe(5); }); - test("should find the data-* elements from the DOM", () => { + test("should find the data-* elements from the DOM with the correct order", () => { // Arrange const targetElement = createElement("div"); - appendMockSteps(targetElement); + const [mockElementOne, mockElementTwo, mockElementThree, mockElementFour] = appendMockSteps(targetElement); const mockTour = getMockTour(targetElement); // Act const steps = fetchSteps(mockTour); // Assert - expect(steps.length).toBe(2); + expect(steps.length).toBe(4); expect(steps[0].position).toBe("bottom"); - expect(steps[0].intro).toBe("first"); + expect(steps[0].intro).toBe("Mock element"); + expect(steps[0].element).toBe(mockElementOne); expect(steps[0].step).toBe(1); expect(steps[1].position).toBe("left"); - expect(steps[1].intro).toBe("second"); + expect(steps[1].intro).toBe("Mock element left position"); + expect(steps[1].element).toBe(mockElementTwo); expect(steps[1].step).toBe(2); + + expect(steps[2].position).toBe("bottom"); + expect(steps[2].intro).toBe("Mock element second to last"); + expect(steps[2].element).toBe(mockElementThree); + expect(steps[2].step).toBe(10); + + expect(steps[3].position).toBe("bottom"); + expect(steps[3].intro).toBe("Mock element last"); + expect(steps[3].element).toBe(mockElementFour); + expect(steps[3].step).toBe(20); }); test("should respect the custom step attribute (DOM)", () => { @@ -257,13 +288,13 @@ describe("steps", () => { const steps = fetchSteps(mockTour); // Assert - expect(steps.length).toBe(2); + expect(steps.length).toBe(4); - expect(steps[0].intro).toBe("first"); - expect(steps[0].step).toBe(1); + expect(steps[2].intro).toBe("Mock element second to last"); + expect(steps[2].step).toBe(10); - expect(steps[1].intro).toBe("second"); - expect(steps[1].step).toBe(5); + expect(steps[3].intro).toBe("Mock element last"); + expect(steps[3].step).toBe(20); }); test("should ignore DOM elements when options.steps is available", () => { @@ -276,34 +307,9 @@ describe("steps", () => { const steps = fetchSteps(mockTour); // Assert - expect(steps.length).toBe(2); - expect(steps[0].intro).toBe("steps-first"); - expect(steps[1].intro).toBe("steps-second"); - }); - - it("should correctly sort based on data-step", () => { - // Arrange - const targetElement = document.createElement("div"); - appendMockSteps(targetElement); - const mockTour = getMockTour(targetElement); - - // Act - const steps = fetchSteps(mockTour); - - // Assert - expect(steps.length).toBe(4); - - expect(steps[0].intro).toBe("one"); - expect(steps[0].step).toBe(1); - - expect(steps[1].intro).toBe("two"); - expect(steps[1].step).toBe(2); - - expect(steps[2].intro).toBe("three"); - expect(steps[2].step).toBe(3); - - expect(steps[3].intro).toBe("four"); - expect(steps[3].step).toBe(5); + expect(steps.length).toBe(5); + expect(steps[0].intro).toBe("Step One of the tour"); + expect(steps[1].intro).toBe("Step Two of the tour"); }); }); }); diff --git a/src/packages/tour/steps.ts b/src/packages/tour/steps.ts index dfedec26f..49ee66d10 100644 --- a/src/packages/tour/steps.ts +++ b/src/packages/tour/steps.ts @@ -158,7 +158,9 @@ export const fetchSteps = (tour: Tour) => { } } } else { - const elements = Array.from(queryElements(`*[${dataIntroAttribute}]`)); + const elements = Array.from( + queryElements(`*[${dataIntroAttribute}]`, tour.getTargetElement()) + ); // if there's no element to intro if (elements.length < 1) { diff --git a/src/packages/tour/tests/mock.ts b/src/packages/tour/tests/mock.ts index 760b2860e..bb08e7607 100644 --- a/src/packages/tour/tests/mock.ts +++ b/src/packages/tour/tests/mock.ts @@ -15,13 +15,13 @@ export const appendMockSteps = (targetElement: HTMLElement = document.body) => { mockElementTwo.setAttribute(dataIntroAttribute, "Mock element left position"); mockElementTwo.setAttribute(dataPosition, "left"); - const mockElementThree = createElement("a"); - mockElementThree.setAttribute(dataIntroAttribute, "Mock element last"); - mockElementThree.setAttribute(dataStepAttribute, "20"); + const mockElementThree = createElement("h1"); + mockElementThree.setAttribute(dataIntroAttribute, "Mock element second to last"); + mockElementThree.setAttribute(dataStepAttribute, "10"); - const mockElementFour = createElement("h1"); - mockElementFour.setAttribute(dataIntroAttribute, "Mock element second to last"); - mockElementFour.setAttribute(dataStepAttribute, "10"); + const mockElementFour = createElement("a"); + mockElementFour.setAttribute(dataIntroAttribute, "Mock element last"); + mockElementFour.setAttribute(dataStepAttribute, "20"); targetElement.appendChild(mockElementOne); targetElement.appendChild(mockElementTwo); @@ -31,7 +31,7 @@ export const appendMockSteps = (targetElement: HTMLElement = document.body) => { return [mockElementOne, mockElementTwo, mockElementThree, mockElementFour]; }; -export const getMockSteps = (): Partial[] => { +export const getMockPartialSteps = (): Partial[] => { return [ { title: "Floating title 1", @@ -46,6 +46,7 @@ export const getMockSteps = (): Partial[] => { intro: "Step Three of the tour", position: "top", scrollTo: "tooltip", + element: "h1", }, { intro: "Step Four of the tour", @@ -60,6 +61,49 @@ export const getMockSteps = (): Partial[] => { ]; }; +export const getMockSteps = (): TourStep[] => { + return [ + { + step: 1, + scrollTo: "tooltip", + position: "bottom", + title: "Floating title 1", + intro: "Step One of the tour", + }, + { + step: 2, + scrollTo: "tooltip", + position: "bottom", + title: "Floating title 2", + intro: "Step Two of the tour", + }, + { + step: 3, + position: "top", + scrollTo: "tooltip", + title: "First title", + intro: "Step Three of the tour", + }, + { + step: 4, + position: "right", + scrollTo: "off", + title: "", + intro: "Step Four of the tour", + element: document.createElement("div"), + }, + { + step: 5, + position: "right", + scrollTo: "off", + title: "", + intro: "Element not found", + element: ".not-found", + }, + ]; +}; + + export const getMockTour = (targetElement: HTMLElement = document.body) => { return new Tour(targetElement); }; diff --git a/src/packages/tour/tour.ts b/src/packages/tour/tour.ts index 6705784e6..c12fd7a03 100644 --- a/src/packages/tour/tour.ts +++ b/src/packages/tour/tour.ts @@ -81,6 +81,11 @@ export class Tour implements Package { return this; } + /** + * Add a step to the tour options. + * This method should be used in conjunction with the `render()` method. + * @param step Partial + */ addStep(step: Partial) { if (!this._options.steps) { this._options.steps = []; @@ -91,6 +96,11 @@ export class Tour implements Package { return this; } + /** + * Add multiple steps to the tour options. + * This method should be used in conjunction with the `render()` method. + * @param steps Partial[] + */ addSteps(steps: Partial[]) { if (!steps.length) return this; @@ -101,6 +111,15 @@ export class Tour implements Package { return this; } + /** + * Set the steps of the tour + * @param steps TourStep[] + */ + setSteps(steps: TourStep[]): this { + this._steps = steps; + return this; + } + getSteps(): TourStep[] { return this._steps; } @@ -172,11 +191,6 @@ export class Tour implements Package { return this._options[key]; } - setSteps(steps: TourStep[]): this { - this._steps = steps; - return this; - } - clone(): ThisType { return new Tour(this._targetElement); }