+ :isRequired="isRequired"
+ :logDisplayedValuesEvent="true" />
diff --git a/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.spec.js b/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.spec.js
new file mode 100644
index 000000000..b9eb6299b
--- /dev/null
+++ b/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.spec.js
@@ -0,0 +1,70 @@
+import { mount } from "@vue/test-utils";
+import shopListButton from "./shop-list-button";
+import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
+
+describe("service-package-radio.vue", () => {
+ it("Should include buttonLabel in html", async () => {
+ // Arrange
+ let { wrapper } = setupMocks({
+ mountOptionsMockData: {
+ propsData: mockProps,
+ },
+ });
+
+ // Act
+ const outputHtml = wrapper.html();
+
+ // Assert
+ expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabel"]));
+ });
+
+ it("Should include buttonLabelSubCopy in html", async () => {
+ // Arrange
+ let { wrapper } = setupMocks({
+ mountOptionsMockData: {
+ propsData: mockProps,
+ },
+ });
+
+ // Act
+ const outputHtml = wrapper.html();
+
+ // Assert
+ expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelSubCopy"]));
+ });
+
+ it("Should include buttonLabelAuxillaryCopy in html", async () => {
+ // Arrange
+ let { wrapper } = setupMocks({
+ mountOptionsMockData: {
+ propsData: mockProps,
+ },
+ });
+
+ // Act
+ const outputHtml = wrapper.html();
+
+ // Assert
+ expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelAuxillaryCopy"]));
+ });
+});
+
+const mockProps = {
+ buttonLabel: "buttonLabel test copy",
+ buttonLabelSubCopy: "buttonLabelSubCopy test copy",
+ buttonBodyCopy:
+ "
- buttonBodyCopy test copy
- 2
- 3
- 4
- 5
",
+ buttonLabelAuxillaryCopy: "buttonLabelAuxillaryCopy test copy",
+ value: 0,
+ modelValue: 0,
+ groupName: "mockGroup",
+};
+
+function setupMocks({ mountOptionsMockData = {} }) {
+ const wrapper = mount(shopListButton, {
+ ...mountOptionsMockData,
+ mixins: [inputButtonWrapperMixin],
+ });
+
+ return { wrapper };
+}
diff --git a/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.vue b/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.vue
new file mode 100644
index 000000000..b8003148f
--- /dev/null
+++ b/src/layouts/service-location/shop-question/shop-list-button/shop-list-button.vue
@@ -0,0 +1,179 @@
+
+
+
+
+
+
{{
+ buttonLabel
+ }}
+
{{
+ buttonLabelSubCopy
+ }}
+
+ {{ buttonAuxillaryCopy }}
+
+
+
+
+ {{ screenReaderOnlyText }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/service-location/shop-question/shop-question.spec.js b/src/layouts/service-location/shop-question/shop-question.spec.js
new file mode 100644
index 000000000..8b26dfee6
--- /dev/null
+++ b/src/layouts/service-location/shop-question/shop-question.spec.js
@@ -0,0 +1,555 @@
+import { shallowMount } from "@vue/test-utils";
+import { storeActions } from "@/constants/store-actions";
+import { getMountOptions } from "@/helpers/unit-test-helper.js";
+import shopQuestion from "./shop-question";
+
+jest.mock("@/mixins/base-mixin", () => ({
+ methods: {
+ dispatchStoreAction(action, items, encode) {
+ if (action === mockGetProviderLocationsStoreAction) {
+ return new Promise((resolve) => {
+ resolve(mockNewShopList);
+ });
+ }
+ },
+ scrollToPageBottom() {},
+ },
+}));
+
+const mockGetProviderLocationsStoreAction = storeActions.GET_PROVIDER_LOCATIONS;
+const mockNewShopList = {
+ data: [
+ {
+ city: "Far",
+ country: "United States",
+ distance: 100,
+ providerNumber: "129",
+ state: "OH",
+ streetAddress: "555 First Capital Ln",
+ zipCode: "45601",
+ },
+ {
+ city: "Farther",
+ country: "United States",
+ distance: 200,
+ providerNumber: "130",
+ state: "OH",
+ streetAddress: "5486 N Grove Rd",
+ zipCode: "43215",
+ },
+ {
+ city: "Farthest (Ever)",
+ country: "United States",
+ distance: 380.5,
+ providerNumber: "131",
+ state: "OH",
+ streetAddress: "1670 Bongo Ave D",
+ zipCode: "43223",
+ },
+ ],
+};
+
+const mockCmsContent = {
+ QuestionText: "Select a shop:",
+};
+
+const cmsWidgetName = "AppointmentTypeQuestionWidget";
+const shopQuestionInitialData = [
+ {
+ city: "Worthington",
+ country: "United States",
+ distance: 1.5,
+ providerNumber: "123",
+ state: "OH",
+ streetAddress: "760 Dearborn Park Ln",
+ zipCode: "43085",
+ },
+ {
+ city: "Columbus",
+ country: "United States",
+ distance: 4.5,
+ providerNumber: "124",
+ state: "OH",
+ streetAddress: "5486 N Hamilton Rd",
+ zipCode: "43230",
+ },
+ {
+ city: "Powell",
+ country: "United States",
+ distance: 7,
+ providerNumber: "125",
+ state: "OH",
+ streetAddress: "1670 Harmon Ave C",
+ zipCode: "43223",
+ },
+ {
+ city: "Chillicothe",
+ country: "United States",
+ distance: 41.5,
+ providerNumber: "126",
+ state: "OH",
+ streetAddress: "555 First Capital Ln",
+ zipCode: "45601",
+ },
+ {
+ city: "Grove City",
+ country: "United States",
+ distance: 4.5,
+ providerNumber: "127",
+ state: "OH",
+ streetAddress: "5486 N Grove Rd",
+ zipCode: "43215",
+ },
+ {
+ city: "Dayton",
+ country: "United States",
+ distance: 80.5,
+ providerNumber: "128",
+ state: "OH",
+ streetAddress: "1670 Bongo Ave D",
+ zipCode: "43223",
+ },
+ {
+ city: "Far",
+ country: "United States",
+ distance: 100,
+ providerNumber: "129",
+ state: "OH",
+ streetAddress: "555 First Capital Ln",
+ zipCode: "45601",
+ },
+ {
+ city: "Farther",
+ country: "United States",
+ distance: 200,
+ providerNumber: "130",
+ state: "OH",
+ streetAddress: "5486 N Grove Rd",
+ zipCode: "43215",
+ },
+ {
+ city: "Farthest (Ever)",
+ country: "United States",
+ distance: 380.5,
+ providerNumber: "131",
+ state: "OH",
+ streetAddress: "1670 Bongo Ave D",
+ zipCode: "43223",
+ },
+];
+
+const mockMixin = {
+ methods: {
+ getCmsContent: jest.fn((widgetName, cmsFieldName) => {
+ if (widgetName === cmsWidgetName) {
+ return mockCmsContent[cmsFieldName];
+ }
+
+ return null;
+ }),
+ },
+};
+
+describe("shop-question.vue", () => {
+ it("Should display first three shops when an appointment type has already been selected", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: null,
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(shopQuestionInitialData);
+
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.answers).toEqual([
+ {
+ Name: "123",
+ buttonLabel: "Worthington",
+ buttonLabelSubCopy: "1.5 mi",
+ buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
+ },
+ {
+ Name: "124",
+ buttonLabel: "Columbus",
+ buttonLabelSubCopy: "4.5 mi",
+ buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
+ },
+ {
+ Name: "125",
+ buttonLabel: "Powell",
+ buttonLabelSubCopy: "7 mi",
+ buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
+ },
+ ]);
+ });
+
+ it("Should display the 'Show more locations' link when there are more than three locations to chose from", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: null,
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(shopQuestionInitialData);
+
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
+
+ // Assert
+ expect(showMoreShopsLink.exists()).toBe(true);
+ });
+
+ it("Should not display the 'Show more locations' link when there are fewer than three locations to chose from", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const alsoShopQuestionInitialData = [
+ {
+ city: "Worthington",
+ country: "United States",
+ distance: 1.5,
+ providerNumber: "123",
+ state: "OH",
+ streetAddress: "760 Dearborn Park Ln",
+ zipCode: "43085",
+ },
+ {
+ city: "Columbus",
+ country: "United States",
+ distance: 4.5,
+ providerNumber: "124",
+ state: "OH",
+ streetAddress: "5486 N Hamilton Rd",
+ zipCode: "43230",
+ },
+ {
+ city: "Powell",
+ country: "United States",
+ distance: 7,
+ providerNumber: "125",
+ state: "OH",
+ streetAddress: "1670 Harmon Ave C",
+ zipCode: "43223",
+ },
+ ];
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: null,
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(alsoShopQuestionInitialData);
+
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
+
+ // Assert
+ expect(showMoreShopsLink.exists()).toBe(false);
+ });
+
+ it("Should display the next three shops when the 'Show more location' link is clicked", async () => {
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const displayedAnswers = [
+ {
+ Name: "123",
+ buttonLabel: "Worthington",
+ buttonLabelSubCopy: "1.5 mi",
+ buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
+ },
+ {
+ Name: "124",
+ buttonLabel: "Columbus",
+ buttonLabelSubCopy: "4.5 mi",
+ buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
+ },
+ {
+ Name: "125",
+ buttonLabel: "Powell",
+ buttonLabelSubCopy: "7 mi",
+ buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
+ },
+ ];
+
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: null,
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+
+ wrapper.vm.shops = shopQuestionInitialData;
+ wrapper.vm.answers = displayedAnswers;
+ wrapper.vm.shopIndex = 3;
+
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" });
+
+ showMoreShopsLink.trigger("click");
+
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.answers.length).toBe(6);
+ expect(wrapper.vm.answers).toEqual([
+ {
+ Name: "123",
+ buttonLabel: "Worthington",
+ buttonLabelSubCopy: "1.5 mi",
+ buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085",
+ },
+ {
+ Name: "124",
+ buttonLabel: "Columbus",
+ buttonLabelSubCopy: "4.5 mi",
+ buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230",
+ },
+ {
+ Name: "125",
+ buttonLabel: "Powell",
+ buttonLabelSubCopy: "7 mi",
+ buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223",
+ },
+ {
+ Name: "126",
+ buttonLabel: "Chillicothe",
+ buttonLabelSubCopy: "41.5 mi",
+ buttonBodyCopy: "555 First Capital Ln, Chillicothe, OH 45601",
+ },
+ {
+ Name: "127",
+ buttonLabel: "Grove City",
+ buttonLabelSubCopy: "4.5 mi",
+ buttonBodyCopy: "5486 N Grove Rd, Grove City, OH 43215",
+ },
+ {
+ Name: "128",
+ buttonLabel: "Dayton",
+ buttonLabelSubCopy: "80.5 mi",
+ buttonBodyCopy: "1670 Bongo Ave D, Dayton, OH 43223",
+ },
+ ]);
+ });
+
+ it("Should display the number of shops necessary to show a previously selected shop", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: "127",
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(shopQuestionInitialData);
+
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.answers.length).toEqual(5);
+ });
+
+ it("Should reset the answers when the selected appointment type changes", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: "127",
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(shopQuestionInitialData);
+
+ wrapper.setProps({
+ selectedAppointmentType: "Inshop",
+ });
+
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.answers.length).toEqual(3);
+ });
+
+ it("Should reload the shops when the service zip code changes", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: "127",
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.shops = shopQuestionInitialData;
+
+ await wrapper.vm.$nextTick();
+
+ wrapper.vm.$options.methods.loadInitialData = jest.fn().mockImplementation(() => {
+ return new Promise((resolve) => {
+ resolve(newShopList);
+ });
+ });
+ await wrapper.vm.$options.watch.serviceZipCode.handler.call(wrapper.vm, "43054");
+
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.shops.length).toEqual(3);
+ expect(wrapper.vm.shops).toEqual(mockNewShopList.data);
+ });
+
+ it("Should clear the existing answers when the service zip code changes", async () => {
+ // Arrange/Act
+ const container = document.createElement("div");
+ container.scrollTo = jest.fn();
+
+ container.classList.add("page-container-grouped-styles");
+ document.body.appendChild(container);
+
+ const { wrapper } = setupMocks({
+ mixins: [mockMixin],
+ props: {
+ modelValue: "127",
+ serviceZipCode: "43081",
+ selectedAppointmentType: "Dropoff",
+ cmsWidgetName: cmsWidgetName,
+ },
+ mountOptions: {
+ attachTo: document.body,
+ },
+ });
+
+ wrapper.vm.$refs.buttonQuestion.resetField = jest.fn();
+ wrapper.vm.initializeComponent(shopQuestionInitialData);
+
+ wrapper.setProps({
+ selectedAppointmentType: "Inshop",
+ });
+
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ expect(wrapper.vm.answers.length).toEqual(3);
+ });
+});
+
+function setupMocks({ mountOptions, mixins, props, isShallowMount = true }) {
+ const resultingMountOptions = getMountOptions({
+ ...mountOptions,
+ mixins,
+ });
+
+ if (props) resultingMountOptions.propsData = props;
+
+ const wrapper = isShallowMount
+ ? shallowMount(shopQuestion, resultingMountOptions)
+ : mount(shopQuestion, resultingMountOptions);
+
+ return { wrapper };
+}
diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue
new file mode 100644
index 000000000..cbe022d5e
--- /dev/null
+++ b/src/layouts/service-location/shop-question/shop-question.vue
@@ -0,0 +1,222 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/vin-lookup/vin-information/vin-information.vue b/src/layouts/vin-lookup/vin-information/vin-information.vue
index 9571cb5eb..a0eaf9536 100644
--- a/src/layouts/vin-lookup/vin-information/vin-information.vue
+++ b/src/layouts/vin-lookup/vin-information/vin-information.vue
@@ -1,6 +1,6 @@