Merged from feature/CSR-1012

This commit is contained in:
Leah Schumann 2023-02-08 13:40:45 -05:00
commit 0517b7072a
11 changed files with 174 additions and 92 deletions

View file

@ -1,5 +1,7 @@
<template>
<div class="dropdown-question" :class="errorMessage || hasError ? 'has-error' : ''">
<div
class="dropdown-question"
:class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label
:for="inputId"
:aria-label="questionText"
@ -13,7 +15,9 @@
:aria-disabled="isDisabled"
:disabled="isDisabled"
:aria-required="isRequired"
:validationRules="validationRules">
:validationRules="validationRules"
:placeHolderText="placeHolderText">
<option v-if="placeHolderText" value="" selected>{{ placeHolderText }}</option>
<option v-for="(value, name, index) in options" :value="name" :key="index">
{{ value }}
</option>
@ -38,9 +42,11 @@ export default {
},
isDisabled: Boolean,
isRequired: Boolean,
disableAutoFill: Boolean,
validationRules: String,
cmsWidgetName: String,
hasError: Boolean,
placeHolderText: String,
},
setup(props) {
const propsClone = Object.assign({}, props);
@ -62,7 +68,7 @@ export default {
initialValue: initialValue,
};
const { errorMessage, handleBlur, handleChange, meta } = useField(
const { errorMessage, handleBlur, handleChange, meta, errors } = useField(
props.inputId,
props.validationRules,
fieldOptions
@ -73,6 +79,7 @@ export default {
handleBlur,
handleChange,
meta,
errors,
};
},
computed: {
@ -81,7 +88,7 @@ export default {
},
selectedOption: {
get: function () {
return this.modelValue;
return !this.modelValue ? "" : this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);

View file

@ -1,68 +1,31 @@
import { shallowMount } from "@vue/test-utils";
import Modal from "./modal";
const modalId = "modal-one";
const footerButtonText = "Sample footer text here.";
const headerText = "Sample header text here.";
describe("modal.vue", () => {
it("Should display header text when HeaderText is defined in the CMS", async () => {
// Act
it("Should display footer button text when footerButtonText is defined", async () => {
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
modalId: modalId,
footerButtonText: footerButtonText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
expect(wrapper.html()).toEqual(expect.stringContaining(footerButtonText));
});
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
// Act
it("Should display modal header text when headerText is defined", async () => {
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
modalId: modalId,
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
});
it("Should insert image url when Image is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
});
it("Should display body text when BodyText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
expect(wrapper.html()).toEqual(expect.stringContaining(headerText));
});
});
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
}),
},
};
const mockCmsContent = {
HeaderText: "Sample header text here.",
SubheaderText: "Sample subheader text here.",
Image: "https://www.sampleImage.sample",
BodyText: "Sample body text here.",
FooterText: "Sample footer text here.",
};

View file

@ -30,7 +30,7 @@
isPrimary
class="w-100"
ref="buttonMain"
suppressLoader
loaderColor="white"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="isFooterButtonDisabled && 'form-test-invalid'" />
@ -44,11 +44,11 @@
import buttonMain from "@/ux-components/button-main/button-main";
import { Modal } from "bootstrap";
import { useIsFormTouched, useIsFormDirty, useIsFormValid } from "vee-validate";
import { checkPropertyChange } from "json-schema";
export default {
name: "modal",
props: {
modalId: String,
headerText: String,
footerButtonText: String,
onModalOpenedCallback: {
@ -56,11 +56,14 @@ export default {
},
},
setup() {
const modalId = `modal-${crypto.randomUUID()}`;
const isTouched = useIsFormTouched();
const isDirty = useIsFormDirty();
const isValid = useIsFormValid();
return {
modalId,
isTouched,
isDirty,
isValid,

View file

@ -1,3 +1,64 @@
import { mount } from "@vue/test-utils";
import contentGroupModal from "./content-group-modal";
describe("content-group-modal.vue", () => {
test.todo("test this");
it("Should display header text when HeaderText is defined in the CMS", async () => {
const wrapper = mount(contentGroupModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
});
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
const wrapper = mount(contentGroupModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
});
it("Should insert image url when Image is defined in the CMS", async () => {
const wrapper = mount(contentGroupModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
});
it("Should display body text when BodyText is defined in the CMS", async () => {
const wrapper = mount(contentGroupModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
});
});
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
}),
},
};
const mockCmsContent = {
HeaderText: "Sample header text here.",
SubheaderText: "Sample subheader text here.",
Image: "https://www.sampleImage.sample",
BodyText: "Sample body text here.",
FooterText: "Sample footer text here.",
};

View file

@ -277,25 +277,23 @@ 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();
// }
// console.log("change");
// });
// 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();
}
});
function fillInAddressUsingFirstItem() {
// Fill-in the address using first item in the list.
@ -328,10 +326,6 @@ export default {
self.matchFound = true;
self.$nextTick(function () {
//self.addressModel.streetAddress = "";
//self.$nextTick();
self.showAddressFields = true;
for (const component of place.address_components) {

View file

@ -247,7 +247,7 @@ export default {
line-height: 1.4;
a {
text-underline-offset: 0.2em;
text-underline-offset: 4px; //Per Devyn. This can't be documented in Figma so there is a comment with the Prototype mocks on the Quote page in Figma
line-height: inherit;
}
}

View file

@ -55,7 +55,6 @@ import cashOrInsuranceQuestion from "./cash-or-insurance-question/cash-or-insura
import servicePackageQuestion from "./service-package-question/service-package-question";
import textBlock from "@/digital-components/text-block/text-block";
import contentGroupModal from "@/fmg-components/content-group-modal/content-group-modal";
//import modal from "@/digital-components/modal/modal";
import loadingModal from "@/fmg-components/loading-modal/loading-modal.vue";
import baseMixin from "@/mixins/base-mixin.js";
import vehicleQuestionsMixin from "../../mixins/vehicle-questions-mixin";

View file

@ -245,7 +245,6 @@ export default {
a {
line-height: 1.714;
padding: 0;
text-underline-offset: 0.1em;
}
}
}

View file

@ -5,6 +5,7 @@ fmg
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<serviceZipModalQuestion
v-model="serviceZipQuestion"
cmsWidgetName="ServiceZipModalWidget"
textboxQuestionWidgetName="ServiceZipQuestionWidget"
alertNonServiceableZipWidgetName="AlertNonServiceableZipWidget"
@ -41,6 +42,10 @@ export default {
name: "service-location",
data() {
return {
serviceZipQuestion: {
serviceState: "",
serviceZipCode: "",
},
mobileLocationQuestions: {
addressQuestions: {
streetAddress: "",

View file

@ -1,16 +1,16 @@
<template>
<div class="text-center">
<label
:for="displayModalLink"
:aria-label="questionText"
:for="serviceLocationLinkPromptId"
:aria-label="serviceLocationLinkPromptText"
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>
v-html="serviceLocationLinkPromptText"></label>
<div class="update-zip-text-link">
<textLink
id="displayModalLink"
id="serviceLocationLinkPromptId"
linkType="text"
:text="this.serviceZipDisplayText"
:text="this.serviceZipLinkText"
href="#!"
@click-event="openZipCodeModal"
aria-label="Modal window" />
@ -22,13 +22,13 @@
:onModalOpenedCallback="onModalOpened"
:footerButtonText="footerButtonText"
:headerText="textboxQuestionPrompt"
@footer-button-event="setZip"
@footer-button-event="setZipCode"
:modalForm="modalForm">
<textboxQuestion
ref="zipInputTextQuestion"
:cmsWidgetName="this.textboxQuestionWidgetName"
v-model="serviceZipCodeInput"
inputId="serviceZipCodeInput"
v-model="serviceZipCode"
inputId="serviceZipCode"
questionAlignment="center"
mask="#####"
:displayQuestionText="false"
@ -81,6 +81,15 @@ export default {
};
},
props: {
modelValue: {
type: Object,
default: () => ({
serviceZipQuestion: {
serviceState: "",
serviceZipCode: "",
},
}),
},
cmsWidgetName: String,
textboxQuestionWidgetName: String,
alertNonServiceableZipWidgetName: String,
@ -94,6 +103,10 @@ export default {
};
},
methods: {
mobileLocationLinkPromptText() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
openZipCodeModal() {
this.$refs[this.modalName].openModal();
},
@ -128,7 +141,11 @@ export default {
this.focusOnZipInput();
},
async setZip() {
closeModal() {
this.$refs[this.modalName].closeModal();
},
async setZipCode() {
this.resetAlerts();
const zipCodeData = await this.getZipCodeData(this.serviceZipCodeInput);
@ -141,11 +158,30 @@ export default {
this.serviceZip = this.serviceZipCodeInput;
this.serviceZipState = zipCodeData.state;
this.$refs[this.modalName].closeModal();
this.closeModal();
}
},
},
computed: {
serviceZipLinkPromptText() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
serviceZipLinkText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
modalHeaderText() {
return this.getCmsContent(this.textboxQuestionWidgetName, "QuestionText");
},
modalFooterText() {
return this.getCmsContent(this.modalWidgetName, "FooterText");
},
textboxQuestionPrompt() {
return this.getCmsContent(this.textboxQuestionWidgetName, "QuestionText");
},
@ -174,6 +210,21 @@ export default {
).replaceAll("{custom:serviceZipCodeInput}", zipCode);
return text;
},
serviceZipModel: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
watch: {
serviceZipModel: {
handler() {
this.displayNonServiceableZipAlert = false;
},
},
},
mounted() {
this.$watch(

View file

@ -45,7 +45,7 @@ export default {
<style lang="scss">
a {
color: $blue;
text-underline-offset: 5px;
text-underline-offset: 4px; //Per Devyn. This can't be documented in Figma so there is a comment with the Prototype mocks on the Quote page in Figma
line-height: 2;
padding: 0 0 4px 0;
font-weight: 500;