Fix mobile location auto-complete defect and defect 1328

This commit is contained in:
Leah Schumann 2023-06-19 09:03:05 -04:00
parent 1a6db3df0f
commit dc309e1dae
4 changed files with 91 additions and 115 deletions

View file

@ -90,12 +90,12 @@ export default {
openModal() { openModal() {
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId)); const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
modal.show(); modal.show();
this.$emit("modalOpened", true); this.$emit("isModalOpened", true);
}, },
closeModal() { closeModal() {
const modal = Modal.getInstance(document.getElementById(this.modalId)); const modal = Modal.getInstance(document.getElementById(this.modalId));
modal.hide(); modal.hide();
this.$emit("modalOpened", false); this.$emit("isModalOpened", false);
}, },
}, },
computed: { computed: {

View file

@ -122,17 +122,15 @@ export default {
}, },
data() { data() {
return { return {
componentKey: 0,
displayVerificationWarning: false, displayVerificationWarning: false,
displayNoMatchWarning: false, displayNoMatchWarning: false,
alertHeadlineVerificationWarning: "", alertHeadlineVerificationWarning: "",
alertCopyVerificationWarning: "", alertCopyVerificationWarning: "",
alertHeadlineNoMatchWarning: "", alertHeadlineNoMatchWarning: "",
alertCopyNoMatchWarning: "", alertCopyNoMatchWarning: "",
scriptLoaded: null,
addressField1: null,
autocomplete: null, autocomplete: null,
autocompleteListener: null, autocompleteListener: null,
showAddressFields: false,
matchFound: null, // null = no attempted match, true = match was found, false = match was not found matchFound: null, // null = no attempted match, true = match was found, false = match was not found
}; };
}, },
@ -202,27 +200,30 @@ export default {
this.$emit("update:modelValue", newValue); this.$emit("update:modelValue", newValue);
}, },
}, },
showAddressFields: {
get: function () {
return (
this.addressModel.streetAddress &&
this.addressModel.city &&
this.addressModel.state &&
this.addressModel.zipCode
);
},
},
showApartmentNumberOrBusinessNameField: { showApartmentNumberOrBusinessNameField: {
get: function () { get: function () {
return this.captureApartmentNumberOrBusinessName; return this.captureApartmentNumberOrBusinessName;
}, },
}, },
addressField1: {
get: function () {
return document.getElementById("autocomplete");
},
},
}, },
methods: { methods: {
loadGooglePlacesAutocompleteScript() {
// Load the Google Places Autocomplete script
const apiKey = applicationConfig.GOOGLE_PLACES_API_KEY;
this.$loadScript(
`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=Function.prototype`
).then(() => {
// When loaded trigger the setup
this.initializeAutocomplete();
});
},
initializeAutocomplete() { initializeAutocomplete() {
this.addressField1 = document.getElementById("autocomplete"); // Initialize the Google Places Autocomplete
// initialize the Google Places Autocomplete
this.autocomplete = new window.google.maps.places.Autocomplete(this.addressField1, { this.autocomplete = new window.google.maps.places.Autocomplete(this.addressField1, {
componentRestrictions: { country: ["us"] }, componentRestrictions: { country: ["us"] },
fields: ["address_components"], fields: ["address_components"],
@ -236,18 +237,21 @@ export default {
this.fillInAddress this.fillInAddress
); );
// When the Street Address textbox receives focus,
// append the search results list container to the bottom of the textbox
// and disable browser autofill
this.addressField1.addEventListener("focus", (e) => { this.addressField1.addEventListener("focus", (e) => {
// Unfortunately this is the only place we can set the autocomplete attribute without the
// Google Places object resetting it to "off" which does nothing to prevent browser autofill
this.addressField1.setAttribute("autocomplete", "do-not-autofill");
// Make place results box stick to the input on scroll // Make place results box stick to the input on scroll
// Due to the different ways we're using this component it is necessary to do this here instead of ahead of time
const streetAddressField = document.getElementById("streetAddressField"); const streetAddressField = document.getElementById("streetAddressField");
const autocompleteResultsContainer = document.getElementsByClassName("pac-container")[0]; const autocompleteResultsContainer =
document.getElementsByClassName("pac-container")[0];
if (autocompleteResultsContainer) { if (autocompleteResultsContainer) {
streetAddressField.appendChild(autocompleteResultsContainer); streetAddressField.appendChild(autocompleteResultsContainer);
} }
// Unfortunately this is the only place we can set the autocomplete attribute without the
// Google Places object resetting it to "off" which does nothing to prevent browser autofill
this.addressField1.setAttribute("autocomplete", "do-not-autofill");
}); });
this.addressField1.addEventListener("keydown", (e) => { this.addressField1.addEventListener("keydown", (e) => {
@ -260,18 +264,36 @@ export default {
".pac-container .pac-item-selected" ".pac-container .pac-item-selected"
); );
// If an item was selected then fill in the address with the selected item
if (selectedItem !== null) { if (selectedItem !== null) {
// Fill-in the address using selected item in the list. // If an item was selected then fill in the address with the selected item
// by triggering the "place_changed" event of the Autocomplete object
this.autocomplete.dispatchEvent(event); this.autocomplete.dispatchEvent(event);
} else { } else {
// Fill-in the address using first item in the list. // Otherwise fill-in the address using first item from the list.
this.fillInAddressUsingFirstItem(); this.fillInAddressUsingFirstItem();
} }
} else { } else {
return; return;
} }
}); });
this.addressField1.addEventListener("change", () => {
// If a match has been previously attempted then do nothing
if (this.matchFound !== null) {
return;
}
// 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.
this.fillInAddressUsingFirstItem();
}
});
}, },
fillInAddress(place) { fillInAddress(place) {
if (!place) { if (!place) {
@ -312,14 +334,8 @@ export default {
} }
} }
// after showing the address fields, disable the address autocomplete // After filling in the address fields, disable the address autocomplete
window.google.maps.event.removeListener(this.autocompleteListener); this.unloadAutocomplete();
window.google.maps.event.clearInstanceListeners(this.autocomplete);
this.addressField1.onchange = null;
const pacContainer = document.querySelector(".pac-container");
if (pacContainer) {
pacContainer.remove();
}
}); });
} }
}, },
@ -348,39 +364,30 @@ export default {
resetAlerts() { resetAlerts() {
this.displayVerificationWarning = false; this.displayVerificationWarning = false;
}, },
forceRerender() { unloadAutocomplete() {
this.componentKey.value += 1; window.google.maps.event.removeListener(this.autocompleteListener);
window.google.maps.event.clearInstanceListeners(this.autocomplete);
this.addressField1.onchange = null;
const pacContainer = document.querySelector(".pac-container");
if (pacContainer) {
pacContainer.remove();
}
} }
}, },
mounted() { mounted() {
console.log("mounted") // If we already have a full address, show it
// Load the Google Places Autocomplete script this.showAddressFields = this.addressModel.streetAddress &&
const apiKey = applicationConfig.GOOGLE_PLACES_API_KEY; this.addressModel.city &&
this.$loadScript( this.addressModel.state &&
`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places&callback=Function.prototype` this.addressModel.zipCode;
).then(() => {
// When loaded trigger the setup this.loadGooglePlacesAutocompleteScript();
this.scriptLoaded = true;
});
}, },
unmounted() { unmounted() {
console.log("unmounted") this.unloadAutocomplete();
},
beforeUpdate() {
// Make place results box stick to the input on scroll
const streetAddressField = document.getElementById("streetAddressField");
const autocompleteResultsContainer = document.getElementsByClassName("pac-container")[0];
if (autocompleteResultsContainer) {
streetAddressField.appendChild(autocompleteResultsContainer);
}
}, },
watch: { watch: {
scriptLoaded: {
handler() {
// After script is loaded, initialize the autocomplete textbox
this.initializeAutocomplete();
},
},
matchFound: { matchFound: {
handler(newValue) { handler(newValue) {
if (newValue === null) { if (newValue === null) {
@ -398,7 +405,7 @@ export default {
this.addressModel.state = ""; this.addressModel.state = "";
this.addressModel.zipCode = ""; this.addressModel.zipCode = "";
} }
this.showAddressFields = true;
this.displayVerificationWarning = false; this.displayVerificationWarning = false;
// Only deep watch the Address Model after a failed match // Only deep watch the Address Model after a failed match
@ -421,6 +428,7 @@ export default {
alert, alert,
}, },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -95,8 +95,11 @@ export default {
</script> </script>
<style lang="scss"> <style lang="scss">
.list-card img { .appointment-type-question {
height: auto;
width: 3.417rem; .list-card img {
height: auto;
width: 3.417rem;
}
} }
</style> </style>

View file

@ -33,10 +33,10 @@
:footerButtonText="modalFooterText" :footerButtonText="modalFooterText"
:onModalOpenedCallback="onModalOpened" :onModalOpenedCallback="onModalOpened"
:onModalClosedCallback="onModalClosed" :onModalClosedCallback="onModalClosed"
@modalOpened="setModalOpened" @isModalOpened="setModalStatus"
@footer-button-event="setMobileLocation"> @footer-button-event="setMobileLocation">
<template v-if="modalOpened"> <template v-if="isModalOpened">
<addressQuestions <addressQuestions
ref="addressQuestions" ref="addressQuestions"
v-model="internalModel.addressQuestions" v-model="internalModel.addressQuestions"
captureApartmentNumberOrBusinessName="true" captureApartmentNumberOrBusinessName="true"
@ -54,7 +54,6 @@
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
</template> </template>
</modal> </modal>
</div> </div>
</transition> </transition>
@ -87,7 +86,7 @@ export default {
internalModel: deepClone(this.modelValue), internalModel: deepClone(this.modelValue),
displayInvalidZipAlert: false, displayInvalidZipAlert: false,
addressQuestionsKey: 0, addressQuestionsKey: 0,
modalOpened: false, isModalOpened: false,
}; };
}, },
setup(props) { setup(props) {
@ -191,6 +190,9 @@ export default {
modalFooterText() { modalFooterText() {
return this.getCmsContent(this.modalWidgetName, "FooterText"); return this.getCmsContent(this.modalWidgetName, "FooterText");
}, },
modal() {
return this.$refs[this.modalName];
},
addressModel: { addressModel: {
get: function () { get: function () {
return this.modelValue.addressQuestions; return this.modelValue.addressQuestions;
@ -199,52 +201,23 @@ export default {
}, },
methods: { methods: {
openModal() { openModal() {
this.$refs[this.modalName].openModal(); this.modal.openModal();
},
setModalOpened(isOpened) {
this.modalOpened = isOpened;
console.log(this.modalOpened)
},
closeModal() {
this.$refs[this.modalName].closeModal();
}, },
onModalOpened() { onModalOpened() {
this.internalModel = deepClone(this.modelValue); this.internalModel = deepClone(this.modelValue);
}, },
setModalStatus(isOpened) {
this.isModalOpened = isOpened;
},
closeModal() {
this.modal.closeModal();
},
onModalClosed() { onModalClosed() {
this.displayInvalidZipAlert = false; this.displayInvalidZipAlert = false;
this.internalModel = deepClone(this.modelValue); this.internalModel = deepClone(this.modelValue);
this.resetValidation();
},
resetComponent(updatedServiceZipCodeInfo) {
// Reset the validation form, setting the initial values
// for the state and zipCode to those that were entered
// on the service-zip-modal-question component
this.$refs[this.modalName].resetForm({
values: {
autocomplete: updatedServiceZipCodeInfo.streetAddress,
city: updatedServiceZipCodeInfo.city,
state: updatedServiceZipCodeInfo.state,
zipCode: updatedServiceZipCodeInfo.zipCode,
isVehicleProtected: updatedServiceZipCodeInfo.isVehicleProtected,
},
});
}, },
resetModalButtonStyle() { resetModalButtonStyle() {
this.$refs[this.modalName].resetButtonStyle(); this.modal.resetButtonStyle();
},
resetValidation() {
//this.$refs.addressQuestions.resetAlerts();
// this.$refs[this.modalName].resetForm({
// values: {
// autocomplete: this.internalModel.addressQuestions.streetAddress,
// city: this.internalModel.addressQuestions.city,
// state: this.internalModel.addressQuestions.state,
// zipCode: this.internalModel.addressQuestions.zipCode,
// isVehicleProtected: this.internalModel.isVehicleProtected,
// },
// });
}, },
async setMobileLocation() { async setMobileLocation() {
if ( if (
@ -294,14 +267,6 @@ export default {
this.internalModel = deepClone(newValue); this.internalModel = deepClone(newValue);
this.handleChange(newValue); this.handleChange(newValue);
this.resetComponent({
streetAddress: newValue.addressQuestions.streetAddress,
city: newValue.addressQuestions.city,
state: newValue.addressQuestions.state,
zipCode: newValue.addressQuestions.zipCode,
isVehicleProtected: newValue.isVehicleProtected,
});
}, },
deep: true, deep: true,
}, },