Merge branch 'feature/CSR-1088' into feature/CSR-1012

This commit is contained in:
Matt Caimi 2023-01-27 10:33:42 -05:00
commit 7d87ff3240
5 changed files with 355 additions and 20 deletions

View file

@ -59,6 +59,80 @@ describe("address-questions.vue", () => {
expect(zipCode.exists()).toBe(true);
});
test("Should render apartmentNumberOrBusinessName textbox-question when captureApartmentNumberOrBusinessName = true", async () => {
// Arrange
const { wrapper } = setupMocks({
props: {
modelValue: {
streetAddress: "",
apartmentNumberOrBusinessName: "",
city: "",
state: "",
zipCode: "",
},
captureApartmentNumberOrBusinessName: true,
},
});
await wrapper.setData({
showAddressFields: true,
});
// Act
const streetAddress = wrapper.findComponent({ ref: "autocomplete" });
const apartmentNumberOrBusinessName = wrapper.findComponent({
ref: "apartmentNumberOrBusinessName",
});
const city = wrapper.findComponent({ ref: "city" });
const state = wrapper.findComponent({ ref: "state" });
const zipCode = wrapper.findComponent({ ref: "zipCode" });
// Assert
expect(streetAddress.exists()).toBe(true);
expect(apartmentNumberOrBusinessName.exists()).toBe(true);
expect(apartmentNumberOrBusinessName.isVisible()).toBe(true);
expect(city.exists()).toBe(true);
expect(state.exists()).toBe(true);
expect(zipCode.exists()).toBe(true);
});
test("Should *not* render apartmentNumberOrBusinessName textbox-question when captureApartmentNumberOrBusinessName = false", async () => {
// Arrange
const { wrapper } = setupMocks({
props: {
modelValue: {
streetAddress: "",
apartmentNumberOrBusinessName: "",
city: "",
state: "",
zipCode: "",
},
captureApartmentNumberOrBusinessName: false,
},
});
await wrapper.setData({
showAddressFields: true,
});
// Act
const streetAddress = wrapper.findComponent({ ref: "autocomplete" });
const apartmentNumberOrBusinessName = wrapper.findComponent({
ref: "apartmentNumberOrBusinessName",
});
const city = wrapper.findComponent({ ref: "city" });
const state = wrapper.findComponent({ ref: "state" });
const zipCode = wrapper.findComponent({ ref: "zipCode" });
// Assert
expect(streetAddress.exists()).toBe(true);
expect(apartmentNumberOrBusinessName.exists()).toBe(true);
expect(apartmentNumberOrBusinessName.isVisible()).toBe(false);
expect(city.exists()).toBe(true);
expect(state.exists()).toBe(true);
expect(zipCode.exists()).toBe(true);
});
test("Should set this.showAddressFields to true when the model is prepopulated", async () => {
// Arrange
// Act

View file

@ -16,6 +16,21 @@
@keydown.enter.prevent />
</div>
</div>
<transition name="fade" mode="out-in">
<div
class="row mb-4"
v-show="showAddressFields && showApartmentNumberOrBusinessNameField"
aria-live="polite">
<div class="col">
<textboxQuestion
cmsWidgetName="ApartmentNumberOrBusinessNameQuestionWidget"
v-model="addressModel.apartmentNumberOrBusinessName"
ref="apartmentNumberOrBusinessName"
inputId="639b0aaa35f64609ad6995f86a74322b"
disableAutoFill />
</div>
</div>
</transition>
<transition name="fade" mode="out-in">
<div class="row mb-4" v-show="showAddressFields" aria-live="polite">
<div class="col">
@ -78,7 +93,6 @@ import { applicationConfig } from "@/constants/application-config.js";
import { defineRule } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { fill } from "lodash";
// DEFINE VALIDATION RULES
defineRule("street-address-required", required(errorMessages.STREET_ADDRESS_REQUIRED));
@ -95,12 +109,17 @@ export default {
type: Object,
default: () => ({
streetAddress: "",
apartmentNumberOrBusinessName: "",
city: "",
state: "",
zipCode: "",
}),
},
validationRules: String,
captureApartmentNumberOrBusinessName: {
type: Boolean,
default: false,
},
},
data() {
return {
@ -180,6 +199,11 @@ export default {
this.$emit("update:modelValue", newValue);
},
},
showApartmentNumberOrBusinessNameField: {
get: function () {
return this.captureApartmentNumberOrBusinessName;
},
},
},
methods: {
setupAddressLookup() {
@ -233,13 +257,17 @@ export default {
});
addressField1.addEventListener("keydown", (e) => {
if (e.code === "Enter" || e.code === "NumpadEnter") {
const autocomplete = document.getElementById("autocomplete");
const event = new Event("place_changed");
if (e.code === "Enter" || e.code === "NumpadEnter" || e.code === "Tab") {
const selectedItem = document.querySelector(
".pac-container .pac-item-selected"
);
if (selectedItem !== null) {
// Fill-in the address using selected item in the list.
fillInAddress(selectedItem);
autocomplete.dispatchEvent(event);
//fillInAddress(selectedItem.textContent);
} else {
// Fill-in the address using first item in the list.
fillInAddressUsingFirstItem();
@ -249,23 +277,25 @@ export default {
}
});
addressField1.addEventListener("change", () => {
// If a match has been previously attempted then do nothing
if (self.matchFound !== null) {
return;
}
// addressField1.addEventListener("change", () => {
// // If a match has been previously attempted then do nothing
// if (self.matchFound !== null) {
// return;
// }
// Get the address that the user clicked on (if any)
const clickedAddress = document.querySelector(
".pac-container .pac-item:hover"
);
// // Get the address that the user clicked on (if any)
// const clickedAddress = document.querySelector(
// ".pac-container .pac-item:hover"
// );
// If the Street Address field changed without clicking (i.e. by pressing Tab, or clicking outside the field)
if (clickedAddress === null) {
// Fill-in the address using first item in the list.
fillInAddressUsingFirstItem();
}
});
// // If the Street Address field changed without clicking (i.e. by pressing Tab, or clicking outside the field)
// if (clickedAddress === null) {
// // Fill-in the address using first item in the list.
// fillInAddressUsingFirstItem();
// }
// console.log("change");
// });
function fillInAddressUsingFirstItem() {
// Fill-in the address using first item in the list.
@ -290,6 +320,7 @@ export default {
}
function fillInAddress(place) {
console.log("fillInAddress");
if (!place) {
place = autocomplete.getPlace();
}
@ -297,8 +328,11 @@ export default {
if (place && place.address_components) {
self.matchFound = true;
self.addressModel.streetAddress = "";
self.$nextTick(function () {
//self.addressModel.streetAddress = "";
//self.$nextTick();
self.showAddressFields = true;
for (const component of place.address_components) {
@ -382,6 +416,11 @@ export default {
}
},
},
"addressModel.streetAddress": {
handler(newValue, oldValue) {
console.log(`I got changed from ${oldValue} to ${newValue}`);
},
},
},
components: {
textboxQuestion,

View file

@ -0,0 +1,152 @@
<template>
<div class="text-center">
<label
:for="displayModalLink"
:aria-label="questionText"
class="form-label fw-bold w-100 ps-4 pe-4 pt-4"
:class="[questionAlignment === 'center' ? 'text-center w-100 mb-5' : '']"
v-html="questionText"></label>
<div class="update-mobile-location-text-link">
<textLink
id="displayModalLink"
linkType="text"
:text="this.mobileLocationDisplayText"
href="#!"
@click-event="openModal"
:data-bs-target="getModalId"
aria-label="Modal window" />
</div>
</div>
<modal
ref="mobileServiceModal"
:cmsWidgetName="this.modalWidgetName"
:onModalOpenedCallback="onModalOpened"
:footerButtonCallback="saveMobileLocationData">
<div class="modal-body ps-5 pe-5 pt-5 pb-4">
<addressQuestions
ref="addressQuestions"
v-model="mobileQuestionModel.addressQuestions"
captureApartmentNumberOrBusinessName="true" />
<vehicleProtectedQuestion v-model="mobileQuestionModel.selectedValue"/>
<textBlock cmsWidgetName="QuoteEmailTextBlockWidget" typeStyle="caption" />
</div>
</modal>
</template>
<script>
// Components
import textLink from "@/ux-components/text-link/text-link";
import modal from "@/digital-components/modal/modal";
import buttonQuestion from "@/digital-components/button-question/button-question";
import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions";
import vehicleProtectedQuestion from "@/layouts/service-location/mobile-location-modal-questions/vehicle-protected-question/vehicle-protected-question";
import textBlock from "@/digital-components/text-block/text-block";
// Supporting Files
import { defineRule } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js";
import TextBlock from '../../../digital-components/text-block/text-block.vue';
// Define Validation Rules
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
export default {
name: "mobile-location-modal-questions",
emits: ["update:modelValue"], // The component emits an event
data() {
return {
isVehicleProtectedAnswers: [],
};
},
props: {
modelValue: {
type: Object,
default: () => ({
addressQuestions: {
streetAddress: "",
apartmentNumberOrBusinessName: "",
city: "",
state: "",
zipCode: "",
},
isZipServiceableMobile: Boolean,
isZipServiceableInShop: Boolean,
isVehicleProtected: Boolean,
}),
},
cmsWidgetName: String,
modalWidgetName: String,
textboxQuestionWidgetName: String,
alertNonServiceableZipWidgetName: String,
alertInvalidZipWidgetName: String,
},
methods: {
loadInitialData() {
return baseMixin.methods.dispatchStoreAction(storeActions.GET_VEHICLE_YEARS, {});
},
initializeComponent(initialData) {
this.isVehicleProtectedAnswers = initialData;
},
openModal() {
this.$refs.mobileServiceModal.openModal();
},
closeModal() {
this.$refs.mobileServiceModal.closeModal();
},
focusOnZipInput() {
this.$nextTick(() => {
console.log(this.$refs);
const input = this.$refs.streetAddressField;
input.focus();
});
},
},
computed: {
getModalId() {
return "#" + this.modalWidgetName;
},
questionText() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
mobileLocationDisplayText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
mobileQuestionModel: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
mounted() {},
components: {
textLink,
modal,
addressQuestions,
vehicleProtectedQuestion,
textBlock
},
};
</script>
<style lang="scss" scoped>
.update-zip-text-link::before {
content: "";
display: inline-block;
width: 13px;
height: 16px;
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 13 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.49635 1.00142e-07C5.64734 -0.000153295 4.80722 0.175918 4.0274 0.517444C3.24757 0.858969 2.54443 1.35877 1.96099 1.98626C0.765713 3.27588 0.0999756 4.98141 0.0999756 6.75394C0.0999756 8.52646 0.765713 10.232 1.96099 11.5216L5.98324 15.777C6.04954 15.8475 6.12918 15.9036 6.21736 15.9419C6.30555 15.9802 6.40045 16 6.49635 16C6.59225 16 6.68716 15.9802 6.77534 15.9419C6.86353 15.9036 6.94317 15.8475 7.00946 15.777L11.0317 11.52C12.2391 10.2383 12.909 8.52914 12.8999 6.75394C12.9094 4.97818 12.2394 3.26832 11.0317 1.98626C10.4481 1.35899 9.74493 0.859347 8.96514 0.517839C8.18535 0.176331 7.34532 0.000130509 6.49635 1.00142e-07V1.00142e-07ZM6.49635 9.13131C6.02507 9.13131 5.56437 8.98913 5.17251 8.72275C4.78065 8.45637 4.47524 8.07776 4.29488 7.63479C4.11453 7.19181 4.06734 6.70438 4.15928 6.23412C4.25123 5.76387 4.47817 5.33191 4.81142 4.99287C5.14467 4.65384 5.56925 4.42295 6.03148 4.32941C6.49371 4.23587 6.97282 4.28388 7.40823 4.46736C7.84364 4.65085 8.21579 4.96157 8.47762 5.36023C8.73945 5.7589 8.87921 6.2276 8.87921 6.70707C8.87921 7.34974 8.62837 7.96611 8.18185 8.4207C7.73532 8.87528 7.12964 9.13088 6.49794 9.13131H6.49635Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
background-size: contain;
vertical-align: middle;
margin-right: 0.5em;
}
</style>

View file

@ -0,0 +1,53 @@
<template>
<buttonQuestion
class="radioQuestion"
:questionText="questionText"
:answers="answers"
groupName="isVehicleProtected"
textPosition="text-center"
v-model="selectedValue"
isRequired />
</template>
<script>
import buttonQuestion from "@/digital-components/button-question/button-question";
// Supporting files
import { storeActions } from "@/constants/store-actions.js";
import baseMixin from "@/mixins/base-mixin.js";
export default {
name: "vehicle-protected-question",
data() {
return {
answers: [ "Yes", "No" ],
};
},
props: {
modelValue: String,
cmsWidgetName: String,
},
components: {
buttonQuestion,
},
computed: {
questionText() {
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
},
selectedValue: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
methods: {
loadInitialData() {
return baseMixin.methods.dispatchStoreAction(storeActions.GET_VEHICLE_YEARS, {});
},
initializeComponent(initialData) {
this.years = initialData;
},
},
};
</script>

View file

@ -9,6 +9,11 @@ fmg
textboxQuestionWidgetName="ServiceZipQuestionWidget"
alertNonServiceableZipWidgetName="AlertNonServiceableZipWidget"
alertInvalidZipWidgetName="AlertInvalidZipWidget" />
<mobileLocationModalQuestions
v-model="mobileLocationQuestion"
cmsWidgetName="MobileLocationModalWidget"
modalWidgetName="LocationModalWidget"
textboxQuestionWidgetName="MobileLocationQuestionWidget" />
<funnel-footer
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ -21,6 +26,7 @@ fmg
<script>
// Components
import serviceZipModalQuestion from "@/layouts/service-location/service-zip-modal-question/service-zip-modal-question";
import mobileLocationModalQuestions from "@/layouts/service-location/mobile-location-modal-questions/mobile-location-modal-questions";
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelFooter from "@/fmg-components/funnel-footer/funnel-footer";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
@ -33,7 +39,17 @@ import { settleAllPromises } from "@/helpers/layout-helper";
export default {
name: "service-location",
data() {
return {};
return {
mobileLocationQuestion: {
addressQuestions: {
streetAddress: "",
apartmentNumberOrBusinessName: "",
city: "",
state: "",
zipCode: "",
},
},
};
},
async beforeRouteEnter(to, from, next) {
// Call APIs
@ -64,6 +80,7 @@ export default {
},
components: {
serviceZipModalQuestion,
mobileLocationModalQuestions,
funnelHeader,
funnelFooter,
funnelSubHeader,