Before merging from develop
This commit is contained in:
parent
d704eee8e8
commit
2fd0ce5f21
8 changed files with 379 additions and 55 deletions
|
|
@ -79,7 +79,7 @@ const endpoints = {
|
|||
method: "GET",
|
||||
},
|
||||
GetServiceabilityDetails: {
|
||||
url: "/location/serviceability-details",
|
||||
url: "/location/api/v1/location/serviceability-details",
|
||||
method: "GET",
|
||||
},
|
||||
GetSupportingItems: {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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: {
|
||||
|
|
|
|||
|
|
@ -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("");
|
||||
}
|
||||
|
|
|
|||
187
src/ux-components/modal-button-main/modal-button-main.spec.js
Normal file
187
src/ux-components/modal-button-main/modal-button-main.spec.js
Normal 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;
|
||||
}
|
||||
153
src/ux-components/modal-button-main/modal-button-main.vue
Normal file
153
src/ux-components/modal-button-main/modal-button-main.vue
Normal 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>
|
||||
Loading…
Reference in a new issue