Before merging from develop

This commit is contained in:
Leah Schumann 2023-03-27 09:47:30 -04:00
parent d704eee8e8
commit 2fd0ce5f21
8 changed files with 379 additions and 55 deletions

View file

@ -79,7 +79,7 @@ const endpoints = {
method: "GET",
},
GetServiceabilityDetails: {
url: "/location/serviceability-details",
url: "/location/api/v1/location/serviceability-details",
method: "GET",
},
GetSupportingItems: {

View file

@ -26,7 +26,7 @@
<slot></slot>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
<modalButtonMain
isPrimary
class="w-100"
ref="buttonMain"
@ -41,7 +41,7 @@
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
import modalButtonMain from "@/ux-components/modal-button-main/modal-button-main";
import { Modal } from "bootstrap";
import { useForm, useIsFormTouched, useIsFormDirty, useIsFormValid } from "vee-validate";
@ -60,14 +60,14 @@ export default {
setup() {
const modalId = `modal-${crypto.randomUUID()}`;
const form = useForm();
const { validate } = useForm();
const isFormTouched = useIsFormTouched();
const isFormDirty = useIsFormDirty();
const isFormValid = useIsFormValid();
return {
modalId,
form,
validate,
isFormTouched,
isFormDirty,
isFormValid,
@ -75,9 +75,9 @@ export default {
},
methods: {
async validateAndEmit() {
const validationResult = await this.form.validate();
const validationResult = await this.validate();
if (validationResult.valid) {
this.$emit("footer-button-event");
this.$emit("footer-button-event");
} else {
this.resetButtonStyle();
}
@ -110,7 +110,7 @@ export default {
},
},
components: {
buttonMain,
modalButtonMain,
},
};
</script>

View file

@ -24,7 +24,7 @@
:isGlassServiceableMobile="isGlassServiceableMobile"
:isRecalibrationServiceableMobile="isRecalibrationServiceableMobile"
ref="serviceTypeQuestion"
groupName="ServiceTypeQuestion"
groupName="serviceTypeQuestion"
cmsWidgetName="ServiceTypeQuestionWidget"
validationRules="option-required" />
<mobileLocationModalQuestions
@ -96,11 +96,7 @@ export default {
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
const getZipCodeData = baseMixin.methods.getZipCodeData(serviceZipCode);
const lineItems = store.getters.order.lineItems;
const serviceabilityDetailsPromise = getServiceabilityDetails({
serviceZipCode: serviceZipCode,
lineItems: lineItems,
});
const serviceabilityDetailsPromise = getServiceabilityDetails(serviceZipCode);
const serviceType = store.getters.damage.isRepair ? "Repair" : "Replace";
const parentAccountNumber = store.getters.payment.parentAccountNumber;
@ -256,11 +252,8 @@ export default {
});
}
const lineItems = store.getters.order.lineItems;
const serviceabilityDetails = await getServiceabilityDetails({
serviceZipCode: newValue,
lineItems: lineItems,
});
const serviceZipCode = store.getters.order.serviceLocation.zipCode;
const serviceabilityDetails = await getServiceabilityDetails(serviceZipCode);
this.setServiceabilityDetails(serviceabilityDetails.data);
},

View file

@ -55,37 +55,20 @@ export default {
return filteredAnswers;
},
serviceability() {
const mobileAvailable = this.isGlassServiceableMobile && this.isRecalibrationServiceableMobile;
const inshopAvailable = this.isGlassServiceableInshop && this.isRecalibrationServiceableInshop;
let result;
if (
this.isGlassServiceableMobile &&
this.isRecalibrationServiceableMobile &&
this.isGlassServiceableInshop &&
this.isRecalibrationServiceableInshop
) {
if (inshopAvailable && mobileAvailable) {
result = "All";
} else if (
!this.isGlassServiceableInshop &&
!this.isRecalibrationServiceableInshop &&
this.isGlassServiceableMobile &&
this.isRecalibrationServiceableMobile
) {
result = "MobileOnly";
} else if (
this.isGlassServiceableInshop &&
this.isRecalibrationServiceableInshop &&
!this.isGlassServiceableMobile &&
!this.isRecalibrationServiceableMobile
) {
} else if (inshopAvailable && !mobileAvailable) {
result = "InshopOnly";
} else if (
!this.isGlassServiceableInshop &&
!this.isRecalibrationServiceableInshop &&
!this.isGlassServiceableMobile &&
!this.isRecalibrationServiceableMobile
) {
} else if (!inshopAvailable && mobileAvailable) {
result = "MobileOnly";
} else if (!inshopAvailable && !mobileAvailable) {
result = "None";
}
return result;
},
selectedValues: {
@ -99,11 +82,9 @@ export default {
},
watch: {
serviceability: {
handler(newValue, oldValue) {
handler(newValue) {
if (newValue == "MobileOnly") {
this.selectedValues = "Mobile";
} else {
this.selectedValues = null;
}
},
},

View file

@ -19,6 +19,7 @@
@footer-button-event="setZipCode">
<serviceZipQuestion
ref="serviceZipQuestion"
customInputId="serviceZipCode"
v-model="internalModel.zipCode"
v-on="{ 'textboxQuestionEvent.inputIdAssigned': onInputIdAssigned }"
:cmsWidgetName="textboxQuestionWidgetName" />
@ -49,6 +50,7 @@ export default {
internalModel: this.copyModel(this.modelValue),
serviceZipCodeTextInputId: "",
displayInvalidZipAlert: false,
};
},
props: {

View file

@ -926,14 +926,13 @@ export const actions = {
});
},
getServiceabilityDetails(context, { serviceZipCode, lineItems }) {
return globalMethods.callMockHttpClient({
getServiceabilityDetails(context, { serviceZipCode }) {
const lineItems = context.getters.order.lineItems;
const lineItemsToSend = [...lineItems.supportingItems];
const encodedLineItems = encodeURIComponent(JSON.stringify(lineItemsToSend))
return globalMethods.callHttpClient({
method: endpoints.GetServiceabilityDetails.method,
//TODO: Remove Mocky Endpoints
//endpoint: "https://run.mocky.io/v3/59e1a644-cf16-4f08-8069-1ab2a1e38f79", // NoShopsAvailable
//endpoint: "https://run.mocky.io/v3/4fe1fb89-dd56-4e4a-9af2-96bd1ab77847", // ForcedInshop
//endpoint: "https://run.mocky.io/v3/e2eaa097-6ea5-4906-af53-901edaa94939", // ForcedMobile
endpoint: "https://run.mocky.io/v3/1811a1fe-12a7-48f3-939e-d10a9b77dd25", // All Options
endpoint: `${endpoints.GetServiceabilityDetails.url}?zip=${serviceZipCode}&lineItems=${encodedLineItems}`,
});
},
@ -1664,3 +1663,12 @@ function getLineItemQueryStringForPricing(lineItems) {
})
.join("");
}
function getLineItemQueryStringForServiceability(lineItems) {
return lineItems
.map((lineItem) => {
let queryStringSnippet = `&lineItems=${lineItem.partNumber}`;
return queryStringSnippet;
})
.join("");
}

View file

@ -0,0 +1,187 @@
import { shallowMount } from "@vue/test-utils";
import buttonMain from "./button-main";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
describe("buttonMain.vue", () => {
it("Should return btn-primary class", () => {
// Arrange/Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
isPrimary: true,
},
})
);
const button = wrapper.find("button");
// Assert
expect(button.attributes("class")).toContain("btn-primary");
});
it("Should return aria-disabled state", () => {
// Arrange/Act
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
isDisabled: true,
},
})
);
const button = wrapper.find("button");
// Assert
expect(button.attributes()["aria-disabled"]).toEqual("true");
});
it("Should return loader color", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderColor: "blue",
loaderEnabled: true,
},
})
);
// Act
wrapper.vm.clicked();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(loader.attributes("class")).toContain("blue");
});
it("Should return loader position", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
// Act
wrapper.vm.clicked();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(loader.attributes("class")).toContain("right");
});
it("Should set 'isLoaderDisplayed' to false when calling 'removeLoader'", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
wrapper.setData({
isLoaderDisplayed: true,
});
// Act
wrapper.vm.removeLoader();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(wrapper.vm.isLoaderDisplayed).toBe(false);
});
it("Should set 'isLoaderDisplayed' to false when calling 'resetButtonStyle'", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
wrapper.setData({
isLoaderDisplayed: true,
});
// Act
wrapper.vm.resetButtonStyle();
await nextTick();
// Assert
expect(wrapper.vm.isLoaderDisplayed).toBe(false);
});
it("Should emit 'click-event' event when clicking if the button is enabled", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderPosition: "right",
loaderEnabled: true,
isDisabled: false,
},
})
);
const buttonElement = wrapper.find("button");
// Act
buttonElement.trigger("click");
await nextTick();
// Assert
expect(wrapper.emitted("click-event")).toBeTruthy();
});
it("Should not emit 'click-event' event when clicking if the button is disabled", async () => {
// Arrange
const wrapper = shallowMount(
buttonMain,
setupMocks({
props: {
loaderPosition: "right",
loaderEnabled: true,
isDisabled: true,
},
})
);
const buttonElement = wrapper.find("button");
// Act
buttonElement.trigger("click");
await nextTick();
// Assert
expect(wrapper.emitted("click-event")).toBeFalsy();
});
});
function setupMocks(mountOptionsMockData = {}) {
const defaultMountOptions = { route: { query: { fmgPage: "page-name" } } };
const baseMountOptions = getMountOptions(
Object.assign(defaultMountOptions, mountOptionsMockData)
);
const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
return allMountOptions;
}

View file

@ -0,0 +1,153 @@
<template>
<button
type="button"
:aria-disabled="isDisabled"
class="btn d-flex align-items-center justify-content-center py-3 px-4 delay"
:class="[
isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '',
isLoaderDisplayed ? 'has-loader' : '',
]"
@click="clicked">
<span class="m-0">{{ this.buttonText }}</span>
<loader
class="ms-2"
v-if="isLoaderDisplayed && !suppressLoader"
v-bind:class="[this.loaderColor, this.loaderPosition]" />
</button>
</template>
<script>
import loader from "@/ux-components/loader/loader";
export default {
name: "modalButtonMain",
props: {
isPrimary: Boolean,
buttonText: String,
isDisabled: Boolean,
loaderColor: String,
loaderPosition: String,
isFloat: Boolean,
suppressLoader: Boolean,
},
data() {
return {
isLoaderDisplayed: false,
};
},
methods: {
removeLoader() {
this.isLoaderDisplayed = false;
},
clicked() {
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
this.GaActions.CLICKED,
this.buttonText,
true
);
if (!this.isDisabled) {
this.isLoaderDisplayed = true;
this.$emit("click-event");
}
},
resetButtonStyle() {
this.isLoaderDisplayed = false;
},
},
components: {
loader,
},
};
</script>
<style lang="scss">
.btn {
&.btn-primary {
position: relative;
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
border: none;
border-radius: $border-radius-lg;
color: $white;
justify-content: center;
font-weight: 500;
@media (hover: hover) {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
&:focus {
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
color: $white;
background: linear-gradient(270deg, rgba(6, 87, 124, 1) 0%, rgba(6, 87, 124, 1) 100%);
}
&:disabled {
background: $gray-200 !important;
background: linear-gradient(270deg, $gray-200 0%, $gray-200 100%) !important;
color: $gray-600 !important;
font-weight: 400;
height: 48px;
border: none;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $white;
background: $blue-700;
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
&.btn-secondary {
position: relative;
background: transparent;
border: 1px solid $blue;
border-radius: $border-radius-lg;
color: $blue;
font-weight: 500;
transition: all 150ms linear;
height: 3rem;
&:hover {
color: $white;
@include blue-gradient;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700;
color: $white;
@include blue-gradient;
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
height: 48px;
border: 1px solid $gray-550;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $white;
@include blue-gradient;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
}
</style>