Merge pull request #379 from Safelite/refactor/linting4
Linting of the iss-components
This commit is contained in:
commit
c4b06b6a69
18 changed files with 421 additions and 351 deletions
|
|
@ -143,13 +143,11 @@ describe('address-questions.vue', () => {
|
|||
};
|
||||
|
||||
// Act
|
||||
autocompleteElement.dispatchEvent(
|
||||
new CustomEvent('place_changed', { detail: selectedPlace })
|
||||
);
|
||||
autocompleteElement.dispatchEvent(new CustomEvent('place_changed', { detail: selectedPlace }));
|
||||
|
||||
// Assert
|
||||
wrapper.vm.$nextTick(function () {
|
||||
const addressModel = wrapper.vm.addressModel;
|
||||
wrapper.vm.$nextTick(() => {
|
||||
const { addressModel } = wrapper.vm;
|
||||
expect(addressModel.streetAddress).toEqual('1234 Test Road');
|
||||
expect(addressModel.city).toEqual('Columbus');
|
||||
expect(addressModel.state).toEqual('OH');
|
||||
|
|
@ -169,9 +167,9 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
querySelectorFunction: function (query) {
|
||||
querySelectorFunction(query) {
|
||||
if (query == '.pac-container .pac-item') {
|
||||
let element = document.createElement('div');
|
||||
const element = document.createElement('div');
|
||||
element.textContent = '123 Test Street';
|
||||
return element;
|
||||
}
|
||||
|
|
@ -222,8 +220,8 @@ describe('address-questions.vue', () => {
|
|||
}
|
||||
});
|
||||
|
||||
let noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
let verificationAlert = wrapper.findComponent({ ref: 'alertVerificationWarning' });
|
||||
const noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
const verificationAlert = wrapper.findComponent({ ref: 'alertVerificationWarning' });
|
||||
expect(noMatchAlert.exists()).toBeFalsy();
|
||||
expect(verificationAlert.exists()).toBeFalsy();
|
||||
|
||||
|
|
@ -235,7 +233,7 @@ describe('address-questions.vue', () => {
|
|||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
const addressModel = wrapper.vm.addressModel;
|
||||
const { addressModel } = wrapper.vm;
|
||||
expect(addressModel.streetAddress).toEqual('1234 Test Road');
|
||||
expect(addressModel.city).toEqual('Columbus');
|
||||
expect(addressModel.state).toEqual('OH');
|
||||
|
|
@ -245,8 +243,7 @@ describe('address-questions.vue', () => {
|
|||
|
||||
describe('alerts', () => {
|
||||
const places = [null, { address_components: null }, undefined, {}];
|
||||
test.each(places)(
|
||||
'selected place/place properties is null => display verification alert',
|
||||
test.each(places)('selected place/place properties is null => display verification alert',
|
||||
async (place) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -259,9 +256,7 @@ describe('address-questions.vue', () => {
|
|||
const selectedPlace = place;
|
||||
|
||||
// Act
|
||||
autocompleteElement.dispatchEvent(
|
||||
new CustomEvent('place_changed', { detail: selectedPlace })
|
||||
);
|
||||
autocompleteElement.dispatchEvent(new CustomEvent('place_changed', { detail: selectedPlace }));
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
|
|
@ -273,8 +268,7 @@ describe('address-questions.vue', () => {
|
|||
|
||||
const noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
expect(noMatchAlert.exists()).toBe(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test('user enters address that yields no autocomplete results => show noMatch alert', async () => {
|
||||
// Arrange
|
||||
|
|
@ -318,9 +312,9 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
querySelectorFunction: function (query) {
|
||||
querySelectorFunction(query) {
|
||||
if (query == '.pac-container .pac-item') {
|
||||
let element = document.createElement('div');
|
||||
const element = document.createElement('div');
|
||||
element.textContent = '123 Test Street';
|
||||
return element;
|
||||
}
|
||||
|
|
@ -369,7 +363,7 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.$nextTick(function () {
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.displayNoMatchWarning).toBeFalsy();
|
||||
|
||||
noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
|
|
@ -396,7 +390,7 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.$nextTick(function () {
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.displayNoMatchWarning).toBeFalsy();
|
||||
|
||||
noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
|
|
@ -423,7 +417,7 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.$nextTick(function () {
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.displayNoMatchWarning).toBeFalsy();
|
||||
|
||||
noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
|
|
@ -450,7 +444,7 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.$nextTick(function () {
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.displayNoMatchWarning).toBeFalsy();
|
||||
|
||||
noMatchAlert = wrapper.findComponent({ ref: 'alertNoMatchWarning' });
|
||||
|
|
@ -461,6 +455,15 @@ describe('address-questions.vue', () => {
|
|||
});
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param root0
|
||||
* @param root0.mountOptions
|
||||
* @param root0.props
|
||||
* @param root0.isShallowMount
|
||||
* @param root0.querySelectorFunction
|
||||
* @param root0.geocoderResult
|
||||
*/
|
||||
function setupMocks({
|
||||
mountOptions,
|
||||
props,
|
||||
|
|
@ -483,6 +486,10 @@ function setupMocks({
|
|||
addListener: jest
|
||||
.fn()
|
||||
.mockImplementation((element, eventName, callbackFunction) => {
|
||||
/**
|
||||
*
|
||||
* @param e
|
||||
*/
|
||||
function interceptedCallbackFunction(e) {
|
||||
callbackFunction(e.detail);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,26 @@
|
|||
<template>
|
||||
<div role="application">
|
||||
<alert
|
||||
ref="alertVerificationWarning"
|
||||
v-if="displayVerificationWarning"
|
||||
ref="alertVerificationWarning"
|
||||
class="mb-4"
|
||||
cmsWidgetName="AlertVerificationWarningWidget"
|
||||
alertClass="alert-warning"
|
||||
v-bind:isDismissible="false" />
|
||||
:isDismissible="false" />
|
||||
<alert
|
||||
ref="alertNoMatchWarning"
|
||||
v-if="displayNoMatchWarning"
|
||||
ref="alertNoMatchWarning"
|
||||
class="mb-4 mt-4"
|
||||
cmsWidgetName="AlertNoMatchWarningWidget"
|
||||
alertClass="alert-warning"
|
||||
v-bind:isDismissible="false" />
|
||||
:isDismissible="false" />
|
||||
<div class="row mt-2 mb-4">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
id="streetAddressField"
|
||||
cmsWidgetName="StreetAddressQuestionWidget"
|
||||
v-model="addressModel.streetAddress"
|
||||
ref="autocomplete"
|
||||
v-model="addressModel.streetAddress"
|
||||
cmsWidgetName="StreetAddressQuestionWidget"
|
||||
inputId="autocomplete"
|
||||
placeholderText="Search"
|
||||
aria-haspopup=""
|
||||
|
|
@ -30,36 +30,43 @@
|
|||
@keydown.enter.prevent />
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade" mode="out-in">
|
||||
<transition
|
||||
name="fade"
|
||||
mode="out-in">
|
||||
<div v-if="showAllFields">
|
||||
<div class="row mt-2 mb-4" v-if="includeStreetAddress2">
|
||||
<div
|
||||
v-if="includeStreetAddress2"
|
||||
class="row mt-2 mb-4">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
ref="streetAddress2"
|
||||
cmsWidgetName="StreetAddress2QuestionWidget"
|
||||
v-model="addressModel.streetAddress2"
|
||||
cmsWidgetName="StreetAddress2QuestionWidget"
|
||||
aria-haspopup=""
|
||||
inputId="streetAddress2Field"
|
||||
/>
|
||||
inputId="streetAddress2Field" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4" aria-live="polite">
|
||||
<div
|
||||
class="row mb-4"
|
||||
aria-live="polite">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="CityQuestionWidget"
|
||||
v-model="addressModel.city"
|
||||
ref="city"
|
||||
v-model="addressModel.city"
|
||||
cmsWidgetName="CityQuestionWidget"
|
||||
inputId="cbf28188fdf2436688fd735915f7ee56"
|
||||
disableAutoFill
|
||||
validationRules="city-required" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4" aria-live="polite">
|
||||
<div
|
||||
class="row mb-4"
|
||||
aria-live="polite">
|
||||
<div class="col">
|
||||
<dropdownQuestion
|
||||
cmsWidgetName="StateQuestionWidget"
|
||||
v-model="addressModel.state"
|
||||
ref="state"
|
||||
v-model="addressModel.state"
|
||||
cmsWidgetName="StateQuestionWidget"
|
||||
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
|
||||
:options="stateOptions"
|
||||
disableAutoFill
|
||||
|
|
@ -67,9 +74,9 @@
|
|||
</div>
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="ZipQuestionWidget"
|
||||
v-model="addressModel.zipCode"
|
||||
ref="zipCode"
|
||||
v-model="addressModel.zipCode"
|
||||
cmsWidgetName="ZipQuestionWidget"
|
||||
inputId="01a9a1c2de0b4c9da8e023c9ae3be498"
|
||||
mask="#####"
|
||||
disableAutoFill
|
||||
|
|
@ -101,7 +108,11 @@ defineRule('zip-code-format', regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.Z
|
|||
|
||||
export default {
|
||||
name: 'address-questions',
|
||||
emits: ['update:modelValue'], // The component emits an event
|
||||
components: {
|
||||
textboxQuestion,
|
||||
dropdownQuestion,
|
||||
alert
|
||||
}, // The component emits an event
|
||||
props: {
|
||||
modelValue: {
|
||||
type: Object,
|
||||
|
|
@ -116,6 +127,7 @@ export default {
|
|||
validationRules: String,
|
||||
includeStreetAddress2: false
|
||||
},
|
||||
emits: ['update:modelValue'],
|
||||
data() {
|
||||
return {
|
||||
displayVerificationWarning: false,
|
||||
|
|
@ -133,29 +145,59 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
stateOptions: {
|
||||
get: function () {
|
||||
get() {
|
||||
return states;
|
||||
}
|
||||
},
|
||||
addressModel: {
|
||||
get: function () {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function (newValue) {
|
||||
set(newValue) {
|
||||
this.$emit('update:modelValue', newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
matchFound: {
|
||||
handler(newValue) {
|
||||
if (!newValue) {
|
||||
this.displayNoMatchWarning = true;
|
||||
|
||||
this.addressModel.city = '';
|
||||
this.addressModel.state = '';
|
||||
this.addressModel.zipCode = '';
|
||||
this.displayVerificationWarning = false;
|
||||
|
||||
this.$nextTick(() => {
|
||||
// Only deep watch the Address Model after a failed match
|
||||
this.isAddressWatchActive = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
addressModel: {
|
||||
handler() {
|
||||
if (this.isAddressWatchActive) {
|
||||
this.displayNoMatchWarning = false;
|
||||
this.isAddressWatchActive = false;
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setupAddressLookup();
|
||||
this.showAllFields = !!this.addressModel.streetAddress;
|
||||
},
|
||||
methods: {
|
||||
setupAddressLookup() {
|
||||
const addressField1 = document.getElementById('autocomplete');
|
||||
const self = this;
|
||||
|
||||
const url = endpoints.GooglePlaces.url.replace('{apiKey}', applicationConfig.GOOGLE_PLACES_API_KEY)
|
||||
const url = endpoints.GooglePlaces.url.replace('{apiKey}', applicationConfig.GOOGLE_PLACES_API_KEY);
|
||||
|
||||
this.$loadScript(
|
||||
url
|
||||
)
|
||||
this.$loadScript(url)
|
||||
.then(() => {
|
||||
// Script is loaded, initialize the autocomplete textbox
|
||||
const autocomplete = new window.google.maps.places.Autocomplete(addressField1, {
|
||||
|
|
@ -165,11 +207,9 @@ export default {
|
|||
});
|
||||
|
||||
// Standard place_changed event handling
|
||||
const autocompleteListener = window.google.maps.event.addListener(
|
||||
autocomplete,
|
||||
const autocompleteListener = window.google.maps.event.addListener(autocomplete,
|
||||
'place_changed',
|
||||
fillInAddress
|
||||
);
|
||||
fillInAddress);
|
||||
|
||||
addressField1.addEventListener('focus', () => {
|
||||
// Wrapping the addressField1 element in the Google Address Autocomplete object
|
||||
|
|
@ -196,13 +236,12 @@ export default {
|
|||
}
|
||||
|
||||
addressField1.blur();
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
addressField1.addEventListener('change', () => {
|
||||
// NOTE: The "place_changed" event of the autocomplete fires after this and will use either the address the user had chosen
|
||||
// NOTE: The "place_changed" event of the autocomplete fires
|
||||
// after this and will use either the address the user had chosen
|
||||
// using either the down / up arrows or the address the user was hovering over when they pressed "Enter."
|
||||
|
||||
// If a match has been previously found then do nothing
|
||||
|
|
@ -215,9 +254,7 @@ export default {
|
|||
}
|
||||
|
||||
// Get the address that the user clicked on (if any)
|
||||
const clickedAddress = document.querySelector(
|
||||
'.pac-container .pac-item:hover'
|
||||
);
|
||||
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) {
|
||||
|
|
@ -228,16 +265,14 @@ export default {
|
|||
|
||||
const firstResult = item.textContent;
|
||||
const geocoder = new window.google.maps.Geocoder();
|
||||
geocoder.geocode(
|
||||
{
|
||||
address: firstResult
|
||||
},
|
||||
function (results, status) {
|
||||
if (status === window.google.maps.GeocoderStatus.OK) {
|
||||
fillInAddress(results[0]);
|
||||
}
|
||||
geocoder.geocode({
|
||||
address: firstResult
|
||||
},
|
||||
(results, status) => {
|
||||
if (status === window.google.maps.GeocoderStatus.OK) {
|
||||
fillInAddress(results[0]);
|
||||
}
|
||||
);
|
||||
});
|
||||
} else {
|
||||
// No addresses found for the input
|
||||
self.matchFound = false;
|
||||
|
|
@ -245,6 +280,10 @@ export default {
|
|||
}
|
||||
});
|
||||
|
||||
/**
|
||||
*
|
||||
* @param place
|
||||
*/
|
||||
function fillInAddress(place) {
|
||||
if (!place) {
|
||||
place = autocomplete.getPlace();
|
||||
|
|
@ -253,7 +292,7 @@ export default {
|
|||
if (place && place.address_components) {
|
||||
self.matchFound = true;
|
||||
self.addressModel.streetAddress = '';
|
||||
self.$nextTick(function () {
|
||||
self.$nextTick(() => {
|
||||
for (const component of place.address_components) {
|
||||
const componentType = component.types[0];
|
||||
|
||||
|
|
@ -264,7 +303,7 @@ export default {
|
|||
}
|
||||
case 'route': {
|
||||
self.addressModel.streetAddress
|
||||
+= ' ' + component.short_name;
|
||||
+= ` ${component.short_name}`;
|
||||
break;
|
||||
}
|
||||
case 'locality': {
|
||||
|
|
@ -279,6 +318,7 @@ export default {
|
|||
self.addressModel.zipCode = component.long_name;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -303,48 +343,11 @@ export default {
|
|||
console.log('Unable to load Google Places API script');
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.setupAddressLookup();
|
||||
this.showAllFields = !!this.addressModel.streetAddress;
|
||||
},
|
||||
watch: {
|
||||
matchFound: {
|
||||
handler(newValue) {
|
||||
if (!newValue) {
|
||||
this.displayNoMatchWarning = true;
|
||||
|
||||
this.addressModel.city = '';
|
||||
this.addressModel.state = '';
|
||||
this.addressModel.zipCode = '';
|
||||
this.displayVerificationWarning = false;
|
||||
|
||||
this.$nextTick(function () {
|
||||
// Only deep watch the Address Model after a failed match
|
||||
this.isAddressWatchActive = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
addressModel: {
|
||||
handler() {
|
||||
if (this.isAddressWatchActive) {
|
||||
this.displayNoMatchWarning = false;
|
||||
this.isAddressWatchActive = false;
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
components: {
|
||||
textboxQuestion,
|
||||
dropdownQuestion,
|
||||
alert
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
#streetAddressField {
|
||||
position: relative;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<!-- Modal -->
|
||||
<div
|
||||
:id="cmsWidgetName"
|
||||
class="modal fade modal-component"
|
||||
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
|
||||
:id="this.cmsWidgetName"
|
||||
tabindex="-1"
|
||||
aria-labelledby="ModalComponentLabel"
|
||||
aria-hidden="true">
|
||||
aria-hidden="true"
|
||||
v-on="{ 'hidden.bs.modal': resetButtonStyle }">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
|
|
@ -18,27 +18,27 @@
|
|||
</div>
|
||||
<div class="modal-body">
|
||||
<label
|
||||
:aria-label="this.ModalQuestionText"
|
||||
:aria-label="ModalQuestionText"
|
||||
class="form-label text-center w-100 mb-5 fw-bold"
|
||||
v-html="this.ModalQuestionText"></label>
|
||||
v-html="ModalQuestionText"></label>
|
||||
</div>
|
||||
<buttonQuestion
|
||||
v-model="selectedValue"
|
||||
class="radioQuestion"
|
||||
:questionText="questionText"
|
||||
:answers="ModalSelectOptionAnswers"
|
||||
groupName="ChooseVehicleYear"
|
||||
v-model="selectedValue"
|
||||
:validationRules="validationRules"
|
||||
isRequired />
|
||||
<div class="modal-footer px-5 py-4">
|
||||
<buttonMain
|
||||
ref="buttonMain"
|
||||
class="w-100"
|
||||
isPrimary
|
||||
ref="buttonMain"
|
||||
suppressLoader
|
||||
:buttonText="ModalSelectButtonText"
|
||||
@click-event="buttonClick"
|
||||
data-bs-dismiss="modal" />
|
||||
data-bs-dismiss="modal"
|
||||
@click-event="buttonClick" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -50,7 +50,11 @@ import buttonMain from '@/ux-components/button-main/button-main';
|
|||
import buttonQuestion from '@/digital-components/button-question/button-question';
|
||||
|
||||
export default {
|
||||
name: 'buttonQuestionModal',
|
||||
name: 'button-question-modal',
|
||||
components: {
|
||||
buttonMain,
|
||||
buttonQuestion
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String
|
||||
},
|
||||
|
|
@ -69,15 +73,11 @@ export default {
|
|||
resetButtonStyle() {
|
||||
this.$refs.buttonMain.resetButtonStyle();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonMain,
|
||||
buttonQuestion
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.modal {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,22 @@
|
|||
:modalId="ModalName"
|
||||
:footerButtonText="ModalCloseButtonText"
|
||||
@footer-button-event="footerButtonClick">
|
||||
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
|
||||
<h5 class="mb-4" v-html="ModalHeadline"></h5>
|
||||
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p>
|
||||
<p class="mb-0" v-html="ModalBodyText"></p>
|
||||
<img
|
||||
:src="ModalImage"
|
||||
class="mw-100 d-flex mx-auto mb-4"
|
||||
alt="" />
|
||||
<h5
|
||||
class="mb-4"
|
||||
v-html="ModalHeadline"></h5>
|
||||
<p
|
||||
class="fw-bold mb-2 subheader-text"
|
||||
v-html="ModalSubheadertext"></p>
|
||||
<p
|
||||
class="mb-0"
|
||||
v-html="ModalBodyText"></p>
|
||||
<p
|
||||
class="my-4 caption modal-sub-body"
|
||||
v-if="ModalSubBodyText"
|
||||
class="my-4 caption modal-sub-body"
|
||||
v-html="ModalSubBodyText"></p>
|
||||
</modal>
|
||||
</template>
|
||||
|
|
@ -20,6 +29,9 @@ import modal from '@/digital-components/modal/modal';
|
|||
|
||||
export default {
|
||||
name: 'content-group-modal',
|
||||
components: {
|
||||
modal
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String
|
||||
},
|
||||
|
|
@ -54,14 +66,11 @@ export default {
|
|||
footerButtonClick() {
|
||||
this.$refs[this.ModalName].closeModal();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
modal
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.modal {
|
||||
&.modal-component {
|
||||
.modal-dialog {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
<template>
|
||||
<div v-if="isModalVisible" class="container-fluid modal-loader">
|
||||
<div
|
||||
v-if="isModalVisible"
|
||||
class="container-fluid modal-loader">
|
||||
<div class="row g-2 h-100 d-flex align-items-center">
|
||||
<div class="container-fluid overflow-hidden">
|
||||
<div class="row h-100">
|
||||
<div class="col text-center tagbg mb-4">
|
||||
<div class="spinner-border text-danger" role="status">
|
||||
<div
|
||||
class="spinner-border text-danger"
|
||||
role="status">
|
||||
<span class="visually-hidden">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -52,7 +56,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Modal',
|
||||
name: 'modal',
|
||||
data() {
|
||||
return {
|
||||
isModalVisible: false
|
||||
|
|
@ -63,23 +67,21 @@ export default {
|
|||
// Display modal
|
||||
this.isModalVisible = true;
|
||||
// Force page reload on back button
|
||||
window.addEventListener(
|
||||
'pageshow',
|
||||
function (evt) {
|
||||
window.addEventListener('pageshow',
|
||||
(evt) => {
|
||||
if (evt.persisted) {
|
||||
setTimeout(function () {
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 10);
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
false);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.modal-loader {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
|
|
|||
|
|
@ -1,23 +1,34 @@
|
|||
<template>
|
||||
<input v-if="type == 'button'" type="button" class="nav-button" v-bind:value="text" v-on:click="navTo"/>
|
||||
<p v-else type="" class="nav-text" v-on:click="navTo">{{text}}</p>
|
||||
<input
|
||||
v-if="type == 'button'"
|
||||
type="button"
|
||||
class="nav-button"
|
||||
:value="text"
|
||||
@click="navTo" />
|
||||
<p
|
||||
v-else
|
||||
type=""
|
||||
class="nav-text"
|
||||
@click="navTo">
|
||||
{{ text }}
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
export default ({
|
||||
name: 'navButton',
|
||||
name: 'nav-button',
|
||||
props: {
|
||||
text: String,
|
||||
scenario: String,
|
||||
type: String
|
||||
},
|
||||
methods: {
|
||||
navTo(){
|
||||
this.$router.navigate(this.scenario, this.$route)
|
||||
navTo() {
|
||||
this.$router.navigate(this.scenario, this.$route);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -202,15 +202,15 @@ function setupMocks() {
|
|||
answerResult: '',
|
||||
answerText: 'No',
|
||||
nextQuestionSequence: 3
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
],
|
||||
glassLocation: 'Windshield',
|
||||
glassName: 'Single',
|
||||
answerKey: 'Windshield-Single',
|
||||
answerData: null
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
|
@ -233,16 +233,16 @@ function setupMocks() {
|
|||
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
|
||||
selectedAnswerText: 'Yes',
|
||||
questionNum: 2
|
||||
},
|
||||
}
|
||||
]
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
const mountOptions = getMountOptions({
|
||||
mixins: [baseMixin, vehicleQuestionsMixin]
|
||||
});
|
||||
mountOptions['attachTo'] = document.body;
|
||||
mountOptions.attachTo = document.body;
|
||||
const wrapper = shallowMount(questionsPageLayout, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@
|
|||
<div class="row px-3">
|
||||
<div class="col">
|
||||
<div class="select-car-form rounded">
|
||||
<vehicleBanner class="mt-2 mb-4" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
||||
<vehicleBanner
|
||||
class="mt-2 mb-4"
|
||||
cmsWidgetName="VehicleBannerWidget"
|
||||
:displayGenericVehicleImage="false" />
|
||||
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
|
||||
<alert
|
||||
ref="alertFewMoreQuestions"
|
||||
|
|
@ -16,25 +19,27 @@
|
|||
alertClass="alert-warning"
|
||||
:manualHeadline="alertFewMoreQuestionsHeader"
|
||||
:manualCopy="alertFewMoreQuestionsCopy"
|
||||
v-bind:isDismissible="false"
|
||||
class="mt-5"/>
|
||||
<div v-for="(questionsDatum, i) in questionsData" :key="questionsDatum.key">
|
||||
:isDismissible="false"
|
||||
class="mt-5" />
|
||||
<div
|
||||
v-for="(questionsDatum, i) in questionsData"
|
||||
:key="questionsDatum.key">
|
||||
<questionChain
|
||||
ref="questionChain"
|
||||
v-model="selectedAnswers[questionsDatum.answerKey]"
|
||||
:questionData="questionsDatum.questions"
|
||||
:index="i"
|
||||
v-if="showThisQuestionChain(questionsDatum, i)"
|
||||
:answerKey="questionsDatum.answerKey"
|
||||
:validationRules="validationRules" />
|
||||
v-if="showThisQuestionChain(questionsDatum, i)"
|
||||
ref="questionChain"
|
||||
v-model="selectedAnswers[questionsDatum.answerKey]"
|
||||
:questionData="questionsDatum.questions"
|
||||
:index="i"
|
||||
:answerKey="questionsDatum.answerKey"
|
||||
:validationRules="validationRules" />
|
||||
</div>
|
||||
<siteFooter
|
||||
class="mt-5"
|
||||
ref="siteFooter"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!isMetaValid"
|
||||
@back-clicked="handleBackButtonAction"
|
||||
@ForwardClicked="handleForwardButtonAction" />
|
||||
ref="siteFooter"
|
||||
class="mt-5"
|
||||
cmsWidgetName="SiteFooterWidget"
|
||||
:isForwardActionDisabled="!isMetaValid"
|
||||
@back-clicked="handleBackButtonAction"
|
||||
@ForwardClicked="handleForwardButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -56,6 +61,15 @@ import loadingModal from '@/iss-components/loading-modal/loading-modal.vue';
|
|||
|
||||
export default {
|
||||
name: 'questions-page',
|
||||
components: {
|
||||
siteHeader,
|
||||
vehicleBanner,
|
||||
alert,
|
||||
questionChain,
|
||||
siteSubHeader,
|
||||
siteFooter,
|
||||
loadingModal
|
||||
},
|
||||
props: {
|
||||
isMetaValid: Boolean,
|
||||
alertFewMoreQuestionsHeader: String,
|
||||
|
|
@ -65,6 +79,7 @@ export default {
|
|||
modelValue: Object,
|
||||
index: Number
|
||||
},
|
||||
emits: ['update:modelValue', 'forwardButtonAction', 'back-click'],
|
||||
computed: {
|
||||
selectedAnswers: {
|
||||
get() {
|
||||
|
|
@ -92,20 +107,11 @@ export default {
|
|||
showLoadingModal() {
|
||||
this.$refs.loadingModal.showModal();
|
||||
}
|
||||
},
|
||||
components: {
|
||||
siteHeader,
|
||||
vehicleBanner,
|
||||
alert,
|
||||
questionChain,
|
||||
siteSubHeader,
|
||||
siteFooter,
|
||||
loadingModal
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.questions-page {
|
||||
.question-text {
|
||||
margin-bottom: 0.5rem;
|
||||
|
|
|
|||
|
|
@ -1,36 +1,47 @@
|
|||
<template>
|
||||
<div>
|
||||
<footer class="footer container-fluid g-5 px-0" id="infoBox">
|
||||
<footer
|
||||
id="infoBox"
|
||||
class="footer container-fluid g-5 px-0">
|
||||
<div class="row d-flex flex-row-reverse align-items-center vw-100 mx-0">
|
||||
<div class="col button-col d-flex px-0" id="stacked" >
|
||||
<div
|
||||
id="stacked"
|
||||
class="col button-col d-flex px-0">
|
||||
<buttonMain
|
||||
v-if="!isForwardButtonHidden"
|
||||
ref="buttonMain"
|
||||
isPrimary
|
||||
:buttonText="buttonText"
|
||||
loaderColor="white"
|
||||
:class="(disableForwardAction || isForwardActionDisabled) && 'form-test-invalid'"
|
||||
:aria-disabled="isForwardActionDisabled"
|
||||
:isDisabled="isForwardActionDisabled"
|
||||
@click-event="buttonClick"
|
||||
data-bs-target="#footerModal"
|
||||
data-bs-dismiss="modal"
|
||||
data-test-id="site-footer-main-button" />
|
||||
v-if="!isForwardButtonHidden"
|
||||
ref="buttonMain"
|
||||
isPrimary
|
||||
:buttonText="buttonText"
|
||||
loaderColor="white"
|
||||
:class="(disableForwardAction || isForwardActionDisabled) && 'form-test-invalid'"
|
||||
:aria-disabled="isForwardActionDisabled"
|
||||
:isDisabled="isForwardActionDisabled"
|
||||
data-bs-target="#footerModal"
|
||||
data-bs-dismiss="modal"
|
||||
data-test-id="site-footer-main-button"
|
||||
@click-event="buttonClick" />
|
||||
</div>
|
||||
<div v-if="!isBackButtonHidden" class="col-auto link-col py-1 px-0 text-break">
|
||||
<div
|
||||
v-if="!isBackButtonHidden"
|
||||
class="col-auto link-col py-1 px-0 text-break">
|
||||
<textLink
|
||||
linkType="navigation"
|
||||
:text="backLink"
|
||||
@click-event="linkClick"
|
||||
href="javascript:void(0)"
|
||||
data-bs-target="#footerModal"
|
||||
data-bs-dismiss="modal"
|
||||
data-test-id="site-footer-back-button" />
|
||||
linkType="navigation"
|
||||
:text="backLink"
|
||||
href="javascript:void(0)"
|
||||
data-bs-target="#footerModal"
|
||||
data-bs-dismiss="modal"
|
||||
data-test-id="site-footer-back-button"
|
||||
@click-event="linkClick" />
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<div class="footerImage" id="imgCity" v-if="footerImageURL" >
|
||||
<img id="siteFooterImage" :src="footerImageURL" />
|
||||
<div
|
||||
v-if="footerImageURL"
|
||||
id="imgCity"
|
||||
class="footerImage">
|
||||
<img
|
||||
id="siteFooterImage"
|
||||
:src="footerImageURL" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -38,21 +49,21 @@
|
|||
<script>
|
||||
import textLink from '@/ux-components/text-link/text-link';
|
||||
import buttonMain from '@/ux-components/button-main/button-main';
|
||||
import { issPageValues } from '@/router/router-constants/issPage-values'
|
||||
import { issPageValues } from '@/router/router-constants/issPage-values';
|
||||
|
||||
export default {
|
||||
name: 'siteFooter',
|
||||
emits: ['backClicked', 'forwardClicked'],
|
||||
name: 'site-footer',
|
||||
components: {
|
||||
textLink,
|
||||
buttonMain
|
||||
},
|
||||
props: {
|
||||
isForwardActionDisabled: Boolean,
|
||||
isBackButtonHidden: { type: Boolean, default: false },
|
||||
isForwardButtonHidden: { type: Boolean, default: false },
|
||||
cmsWidgetName: String
|
||||
},
|
||||
components: {
|
||||
textLink,
|
||||
buttonMain
|
||||
},
|
||||
emits: ['backClicked', 'forwardClicked'],
|
||||
data() {
|
||||
return {
|
||||
paddingHeight: 0,
|
||||
|
|
@ -60,18 +71,6 @@ export default {
|
|||
disableForwardAction: false
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.paddingHeight = this.includeFooterImage ? this.getFooterInfoBoxHeight() + 16 : this.getFooterInfoBoxHeight() + 24;
|
||||
this.$nextTick(() => {
|
||||
window.addEventListener('resize', this.onResize);
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
},
|
||||
unmounted() {
|
||||
document.onkeydown = null;
|
||||
},
|
||||
computed: {
|
||||
backLink() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'BackButtonText');
|
||||
|
|
@ -86,9 +85,21 @@ export default {
|
|||
},
|
||||
includeFooterImage() {
|
||||
const currentPageName = this.getPageNameByQueryString();
|
||||
return (currentPageName.toLowerCase() == issPageValues.WELCOME_PAGE);
|
||||
return (currentPageName.toLowerCase() === issPageValues.WELCOME_PAGE);
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.paddingHeight = this.includeFooterImage ? this.getFooterInfoBoxHeight() + 16 : this.getFooterInfoBoxHeight() + 24;
|
||||
this.$nextTick(() => {
|
||||
window.addEventListener('resize', this.onResize);
|
||||
});
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.onResize);
|
||||
},
|
||||
unmounted() {
|
||||
document.onkeydown = null;
|
||||
},
|
||||
methods: {
|
||||
onResize() {
|
||||
this.paddingHeight = this.getFooterInfoBoxHeight();
|
||||
|
|
@ -98,15 +109,11 @@ export default {
|
|||
},
|
||||
removeLoader() {
|
||||
this.$refs.buttonMain.removeLoader();
|
||||
document.onkeydown = function (e) {
|
||||
return true;
|
||||
};
|
||||
document.onkeydown = () => true;
|
||||
},
|
||||
buttonClick() {
|
||||
// prevent keyboard input after button click
|
||||
document.onkeydown = function (e) {
|
||||
return false;
|
||||
};
|
||||
document.onkeydown = () => false;
|
||||
this.$emit('forwardClicked');
|
||||
},
|
||||
linkClick() {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,31 @@
|
|||
<template>
|
||||
<div class="menu-modal-container">
|
||||
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" aria-label="Hamburger Menu (modal window)" @click="toggle()">
|
||||
<button
|
||||
class="menu-button"
|
||||
type="button"
|
||||
:class="[isActive ? 'active' : '']"
|
||||
aria-label="Hamburger Menu (modal window)"
|
||||
@click="toggle()">
|
||||
<div class="bar1"></div>
|
||||
<div class="bar2"></div>
|
||||
<div class="bar3"></div>
|
||||
</button>
|
||||
</div>
|
||||
<!-- Modal -->
|
||||
<div class="modal menu-modal fade" data-bs-backdrop="false" id="footerModal" tabindex="-1" aria-labelledby="footerModalLabel" aria-hidden="true">
|
||||
<div
|
||||
id="footerModal"
|
||||
class="modal menu-modal fade"
|
||||
data-bs-backdrop="false"
|
||||
tabindex="-1"
|
||||
aria-labelledby="footerModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="menu-modal-container">
|
||||
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" aria-label="Hamburger Menu (modal window)" @click="toggle()">
|
||||
<button
|
||||
class="menu-button"
|
||||
type="button"
|
||||
:class="[isActive ? 'active' : '']"
|
||||
aria-label="Hamburger Menu (modal window)"
|
||||
@click="toggle()">
|
||||
<div class="bar1"></div>
|
||||
<div class="bar2"></div>
|
||||
<div class="bar3"></div>
|
||||
|
|
@ -18,40 +34,44 @@
|
|||
<div class="modal-dialog modal-fullscreen">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header visually-hidden">
|
||||
<h5 class="modal-title" id="footerModalLabel">Footer Navigation</h5>
|
||||
<h5
|
||||
id="footerModalLabel"
|
||||
class="modal-title">
|
||||
Footer Navigation
|
||||
</h5>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<textLink
|
||||
linkType="navigation"
|
||||
text="Terms of service"
|
||||
href="//www.safelite.com/terms-of-use"
|
||||
target="_blank" />
|
||||
linkType="navigation"
|
||||
text="Terms of service"
|
||||
href="//www.safelite.com/terms-of-use"
|
||||
target="_blank" />
|
||||
<textLink
|
||||
linkType="navigation"
|
||||
text="Your privacy choices"
|
||||
href="//www.safelite.com/privacy-center"
|
||||
target="_blank">
|
||||
<template v-slot:after-text>
|
||||
linkType="navigation"
|
||||
text="Your privacy choices"
|
||||
href="//www.safelite.com/privacy-center"
|
||||
target="_blank">
|
||||
<template #after-text>
|
||||
<img
|
||||
class="ccpa-icon"
|
||||
data-id="ccpa-icon"
|
||||
src="~@/assets/img/icons/ccpa-icon.svg"
|
||||
alt="Your privacy choices" />
|
||||
class="ccpa-icon"
|
||||
data-id="ccpa-icon"
|
||||
src="~@/assets/img/icons/ccpa-icon.svg"
|
||||
alt="Your privacy choices" />
|
||||
</template>
|
||||
</textLink>
|
||||
<textLink
|
||||
linkType="navigation"
|
||||
text="Warranty"
|
||||
href="//www.safelite.com/national-lifetime-warranty"
|
||||
target="_blank" />
|
||||
linkType="navigation"
|
||||
text="Warranty"
|
||||
href="//www.safelite.com/national-lifetime-warranty"
|
||||
target="_blank" />
|
||||
<textLink
|
||||
linkType="navigation"
|
||||
text="Notice at collection"
|
||||
href="https://www.safelite.com/ccpa-privacy-policy"
|
||||
target="_blank" />
|
||||
linkType="navigation"
|
||||
text="Notice at collection"
|
||||
href="https://www.safelite.com/ccpa-privacy-policy"
|
||||
target="_blank" />
|
||||
</div>
|
||||
<div class="modal-footer d-flex justify-content-start">
|
||||
© {{new Date().getFullYear()}} Safelite Group
|
||||
© {{ new Date().getFullYear() }} Safelite Group
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -63,7 +83,10 @@ import textLink from '@/ux-components/text-link/text-link';
|
|||
import { Modal } from 'bootstrap';
|
||||
|
||||
export default {
|
||||
name: 'menuModal',
|
||||
name: 'menu-modal',
|
||||
components: {
|
||||
textLink
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isActive: false,
|
||||
|
|
@ -88,11 +111,8 @@ export default {
|
|||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
textLink
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import siteHeader from '@/iss-components/site-header/site-header';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import { getMountOptions } from '@/helpers/unit-test-helper.js';
|
||||
import { useMainStore } from '@/store/';
|
||||
|
||||
describe('site-header', () => {
|
||||
test('renders the logo image', () => {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="site-header d-flex justify-content-center align-items-center flex-column py-2" :style="logoBackgroundStyle">
|
||||
<img id="siteHeaderImage" class="img-fluid" :src="imageSrc" :alt="altText" />
|
||||
<menuModal/>
|
||||
<div
|
||||
class="site-header d-flex justify-content-center align-items-center flex-column py-2"
|
||||
:style="logoBackgroundStyle">
|
||||
<img
|
||||
id="siteHeaderImage"
|
||||
class="img-fluid"
|
||||
:src="imageSrc"
|
||||
:alt="altText" />
|
||||
<menuModal />
|
||||
</div>
|
||||
<alert
|
||||
v-if="displayGlobalAlert"
|
||||
class="position-absolute rounded-0 w-100 border-0 shadow-sm start-0"
|
||||
cmsWidgetName="GlobalAlert"
|
||||
:manualHeadline="globalAlertMessage.messageHeadline"
|
||||
:manualCopy="globalAlertMessage.messageCopy"
|
||||
:alertClass="globalAlertMessage.type"
|
||||
v-bind:isDismissible="globalAlertMessage.isDismissible" />
|
||||
v-if="displayGlobalAlert"
|
||||
class="position-absolute rounded-0 w-100 border-0 shadow-sm start-0"
|
||||
cmsWidgetName="GlobalAlert"
|
||||
:manualHeadline="globalAlertMessage.messageHeadline"
|
||||
:manualCopy="globalAlertMessage.messageCopy"
|
||||
:alertClass="globalAlertMessage.type"
|
||||
:isDismissible="globalAlertMessage.isDismissible" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -22,7 +28,14 @@ import eventBus from '@/helpers/event-bus/event-bus';
|
|||
import { globalEvents } from '@/constants/events';
|
||||
|
||||
export default ({
|
||||
name: 'siteHeader',
|
||||
name: 'site-header',
|
||||
components: {
|
||||
menuModal,
|
||||
alert
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
displayGlobalAlert: false,
|
||||
|
|
@ -35,9 +48,6 @@ export default ({
|
|||
headerAnswers: null
|
||||
};
|
||||
},
|
||||
props: {
|
||||
cmsWidgetName: String
|
||||
},
|
||||
computed: {
|
||||
imageSrc() {
|
||||
return this.headerAnswers ? this.headerAnswers.AnswerImageUrl : '';
|
||||
|
|
@ -51,23 +61,12 @@ export default ({
|
|||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setupHeader() {
|
||||
const answers = this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
if(Array.isArray(answers)) {
|
||||
this.headerAnswers = answers.filter(x => x.AccountNumber == this.mainStore.issConfig.accountNumber)[0]
|
||||
this.headerAnswers = !this.headerAnswers ? answers.filter(x => x.AccountNumber === '0')[0] : this.headerAnswers;
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$nextTick(this.setupHeader);
|
||||
|
||||
// Check if alert event is on the bus
|
||||
const alertEvent = eventBus.readAndPopEventFromBus(
|
||||
globalEvents.Categories.GLOBAL_ALERT,
|
||||
globalEvents.SubCategories.PAGE_NOT_FOUND
|
||||
);
|
||||
const alertEvent = eventBus.readAndPopEventFromBus(globalEvents.Categories.GLOBAL_ALERT,
|
||||
globalEvents.SubCategories.PAGE_NOT_FOUND);
|
||||
// If alert event is on the bus, then display the alert
|
||||
if (alertEvent !== undefined) {
|
||||
this.displayGlobalAlert = true;
|
||||
|
|
@ -77,17 +76,21 @@ export default ({
|
|||
// load client customization overrides
|
||||
const clientOverrideClass = this.mainStore.issConfig.styleSheet.trim();
|
||||
if (clientOverrideClass
|
||||
&& clientOverrideClass != ''
|
||||
&& !document.getElementsByTagName('body')[0].className.split(' ').includes(clientOverrideClass))
|
||||
{
|
||||
document.getElementsByTagName('body')[0].className += ' ' + clientOverrideClass;
|
||||
&& clientOverrideClass !== ''
|
||||
&& !document.getElementsByTagName('body')[0].className.split(' ').includes(clientOverrideClass)) {
|
||||
document.getElementsByTagName('body')[0].className += ` ${clientOverrideClass}`;
|
||||
}
|
||||
},
|
||||
components: {
|
||||
menuModal,
|
||||
alert
|
||||
methods: {
|
||||
setupHeader() {
|
||||
const answers = this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
if (Array.isArray(answers)) {
|
||||
this.headerAnswers = answers.filter((x) => x.AccountNumber == this.mainStore.issConfig.accountNumber)[0];
|
||||
this.headerAnswers = !this.headerAnswers ? answers.filter((x) => x.AccountNumber === '0')[0] : this.headerAnswers;
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,23 +1,20 @@
|
|||
<template>
|
||||
<button
|
||||
@click="handleClick"
|
||||
class="back-button-wrapper"
|
||||
:aria-label="backButtonAccessibleText"
|
||||
>
|
||||
@click="handleClick">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="34px"
|
||||
height="30px"
|
||||
viewBox="-2.2 -6 30 30"
|
||||
>
|
||||
viewBox="-2.2 -6 30 30">
|
||||
<g id="Layer_3">
|
||||
<path
|
||||
class="outer"
|
||||
fill="#FFFFFF"
|
||||
d="M19.934-1.434h-8.486c-1.683,0-2.999,0.528-4.021,1.614L1.685,6.146C0.78,7.079,0.338,8.027,0.338,9.041
|
||||
c0,1.028,0.444,1.979,1.359,2.908l5.753,5.938c1.054,1.093,2.403,1.647,4.007,1.647l8.475,0.01c2.861,0,4.568-1.692,4.568-4.528
|
||||
V3.094C24.5,0.259,22.793-1.434,19.934-1.434L19.934-1.434z"
|
||||
/>
|
||||
V3.094C24.5,0.259,22.793-1.434,19.934-1.434L19.934-1.434z" />
|
||||
</g>
|
||||
<g id="Layer_2">
|
||||
<path
|
||||
|
|
@ -29,8 +26,7 @@
|
|||
c0-0.215,0.078-0.42,0.235-0.566l2.861-2.861l-2.861-2.862c-0.156-0.156-0.234-0.352-0.234-0.566c0-0.458,0.362-0.801,0.801-0.801
|
||||
c0.224,0,0.41,0.069,0.556,0.225l2.881,2.872l2.891-2.881c0.166-0.166,0.353-0.234,0.566-0.234c0.439,0,0.801,0.352,0.801,0.801
|
||||
c0,0.225-0.078,0.4-0.234,0.576l-2.871,2.871l2.861,2.852c0.146,0.156,0.234,0.352,0.234,0.576c0,0.449-0.361,0.82-0.811,0.82
|
||||
c-0.225,0-0.43-0.098-0.586-0.244l-2.852-2.871l-2.852,2.871C11.203,13.165,10.999,13.253,10.773,13.253z"
|
||||
/>
|
||||
c-0.225,0-0.43-0.098-0.586-0.244l-2.852-2.871l-2.852,2.871C11.203,13.165,10.999,13.253,10.773,13.253z" />
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
|
|
@ -38,7 +34,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
name: 'buttonBack',
|
||||
name: 'button-back',
|
||||
props: {
|
||||
backButtonAccessibleText: {
|
||||
type: String,
|
||||
|
|
@ -46,6 +42,7 @@ export default {
|
|||
default: ''
|
||||
}
|
||||
},
|
||||
emits: ['click-event'],
|
||||
methods: {
|
||||
handleClick() {
|
||||
this.$emit('click-event');
|
||||
|
|
@ -54,7 +51,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.back-button-wrapper {
|
||||
padding: 0;
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -1,19 +1,24 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="d-flex align-items-center justify-content-center container-fluid overflow-hidden">
|
||||
<h5 class="text-center fw-normal mb-0 subheader-primary" :class="headerColor">
|
||||
<h5
|
||||
class="text-center fw-normal mb-0 subheader-primary"
|
||||
:class="headerColor">
|
||||
<span>
|
||||
{{ content }}
|
||||
</span>
|
||||
<buttonBack
|
||||
v-if="hasBackButton"
|
||||
:backButtonAccessibleText="backButtonAccessibleText"
|
||||
@click-event="clickEvent"
|
||||
/>
|
||||
@click-event="clickEvent" />
|
||||
</h5>
|
||||
</div>
|
||||
<div class="subheader-secondary d-flex align-items-center container-fluid overflow-hidden" :class="justifySubheader">
|
||||
<p class="fw-normal mb-0" :class="alternateFormatting">
|
||||
<div
|
||||
class="subheader-secondary d-flex align-items-center container-fluid overflow-hidden"
|
||||
:class="justifySubheader">
|
||||
<p
|
||||
class="fw-normal mb-0"
|
||||
:class="alternateFormatting">
|
||||
<span v-html="subText">
|
||||
</span>
|
||||
</p>
|
||||
|
|
@ -25,7 +30,8 @@
|
|||
import buttonBack from '@/iss-components/site-sub-header/button-back/button-back';
|
||||
|
||||
export default ({
|
||||
name: 'siteSubHeader',
|
||||
name: 'site-sub-header',
|
||||
components: { buttonBack },
|
||||
props: {
|
||||
cmsWidgetName: String,
|
||||
hasBackButton: Boolean,
|
||||
|
|
@ -33,41 +39,40 @@ export default ({
|
|||
issContainingPage: String,
|
||||
darkGraySubText: Boolean
|
||||
},
|
||||
emits: ['click-event'],
|
||||
computed: {
|
||||
content() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'SubHeaderText')
|
||||
return this.getCmsContent(this.cmsWidgetName, 'SubHeaderText');
|
||||
},
|
||||
subText() {
|
||||
const subText = this.getCmsContent(this.cmsWidgetName, 'SecondaryText');
|
||||
return subText ?? '';
|
||||
},
|
||||
backButtonAccessibleText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'BackButtonAccessibleText')
|
||||
return this.getCmsContent(this.cmsWidgetName, 'BackButtonAccessibleText');
|
||||
},
|
||||
headerColor() {
|
||||
return this.subText ? 'dark-header' : 'light-header';
|
||||
},
|
||||
justifySubheader() {
|
||||
return (this.justification?.toLowerCase() === 'left') ?
|
||||
'justify-content-left' :
|
||||
'justify-content-center';
|
||||
return (this.justification?.toLowerCase() === 'left')
|
||||
? 'justify-content-left'
|
||||
: 'justify-content-center';
|
||||
},
|
||||
alternateFormatting() {
|
||||
// override for service-packages unique style
|
||||
if (this.issContainingPage?.toLowerCase() === 'service-packages') {
|
||||
return 'service-packages-subtext mt-4 mb-2 px-5';
|
||||
} else {
|
||||
return this.darkGraySubText ? 'dark-gray' : 'light-gray';
|
||||
}
|
||||
return this.darkGraySubText ? 'dark-gray' : 'light-gray';
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clickEvent() {
|
||||
this.$emit('click-event');
|
||||
}
|
||||
},
|
||||
components: { buttonBack }
|
||||
})
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import steeringTextModal from './steering-text';
|
||||
import { createApp } from 'vue';
|
||||
import App from '@/App';
|
||||
import { createPinia } from 'pinia';
|
||||
import App from '@/App.vue';
|
||||
import { createApp } from 'vue';
|
||||
import steeringTextModal from './steering-text';
|
||||
|
||||
describe('steering-text.vue', () => {
|
||||
test('Should display steering text from CMS for state MA', async () => {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
<template>
|
||||
<div>
|
||||
<textBlock
|
||||
cmsWidgetName="MASteeringText"
|
||||
id="SteeringTextContent"
|
||||
/>
|
||||
cmsWidgetName="MASteeringText" />
|
||||
<div>
|
||||
<p class="small" v-if="isStateMA" v-html="MASteeringText" ></p>
|
||||
<p
|
||||
v-if="isStateMA"
|
||||
class="small"
|
||||
v-html="MASteeringText"></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -14,20 +16,21 @@
|
|||
import { useMainStore } from '@/store';
|
||||
|
||||
export default {
|
||||
name: 'steeringTextModal',
|
||||
name: 'steering-text-modal',
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
return { mainStore };
|
||||
},
|
||||
computed: {
|
||||
MASteeringText() {
|
||||
return this.getCmsContent('MASteeringText', 'BodyText');
|
||||
},
|
||||
isStateMA() {
|
||||
if (this.mainStore.order.customer.address.state=='MA') {
|
||||
return true
|
||||
if (this.mainStore.order.customer.address.state === 'MA') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const mainStore = useMainStore();
|
||||
return { mainStore };
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import { shallowMount } from '@vue/test-utils';
|
||||
import { vehicleCategories } from '@/constants/vehicle-categories.js';
|
||||
import vehicleBanner from './vehicle-banner';
|
||||
import { useMainStore } from '@/store';
|
||||
import { createApp } from 'vue';
|
||||
import { createPinia } from 'pinia';
|
||||
import { mapStores } from 'pinia';
|
||||
import { createPinia, mapStores } from 'pinia';
|
||||
|
||||
import App from '@/App.vue';
|
||||
import vehicleBanner from './vehicle-banner';
|
||||
|
||||
const cmsData = { VehicleBannerWidget:
|
||||
{
|
||||
|
|
@ -16,8 +16,7 @@ const cmsData = { VehicleBannerWidget:
|
|||
VanUnmatchedVehicleIcon: 'van-placeholder.jpg',
|
||||
CommercialVanUnmatchedVehicleIcon: 'commercial-placeholder.jpg',
|
||||
SuvUnmatchedVehicleIcon: 'suv-placeholder.jpg'
|
||||
}
|
||||
}
|
||||
} };
|
||||
|
||||
const vueApp = createApp(App);
|
||||
const pinia = createPinia();
|
||||
|
|
@ -37,7 +36,7 @@ const mockMixin = {
|
|||
return vehicleCategories;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('vehicleBanner', () => {
|
||||
test('renders the blurrycar image when displayGenericVehicleImage is true', async () => {
|
||||
|
|
@ -81,7 +80,7 @@ describe('vehicleBanner', () => {
|
|||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
var iconUrl = wrapper.vm.vehicleImageToDisplay;
|
||||
const iconUrl = wrapper.vm.vehicleImageToDisplay;
|
||||
|
||||
expect(iconUrl).toEqual('car-placeholder.jpg');
|
||||
wrapper.unmount();
|
||||
|
|
@ -106,7 +105,7 @@ describe('vehicleBanner', () => {
|
|||
mixins: [mockMixin]
|
||||
});
|
||||
|
||||
var vehicleIcon = wrapper.vm.getUnmatchedVehicleIcon();
|
||||
const vehicleIcon = wrapper.vm.getUnmatchedVehicleIcon();
|
||||
|
||||
expect(vehicleIcon).toEqual(expectedIcon);
|
||||
expect(wrapper.find('img').attributes('class')).toContain('vehicle-image');
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@
|
|||
<img
|
||||
class="vehicle-image img-fluid"
|
||||
:src="vehicleImageToDisplay"
|
||||
alt=""
|
||||
/>
|
||||
alt="" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
@ -24,7 +23,7 @@ export default {
|
|||
return this.genericVehicleImage;
|
||||
}
|
||||
|
||||
const imageUrl = this.mainStore.order.vehicle.imageUrl;
|
||||
const { imageUrl } = this.mainStore.order.vehicle;
|
||||
if (!imageUrl || imageUrl === 'NULL') {
|
||||
return this.getUnmatchedVehicleIcon();
|
||||
}
|
||||
|
|
@ -52,7 +51,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
getUnmatchedVehicleIcon() {
|
||||
switch (this.mainStore.order.vehicle.category){
|
||||
switch (this.mainStore.order.vehicle.category) {
|
||||
case this.vehicleCategories.CAR:
|
||||
return this.carUnmatchedVehicleIcon;
|
||||
case this.vehicleCategories.SUV:
|
||||
|
|
|
|||
Loading…
Reference in a new issue