Merge pull request #838 from Safelite/feature/richardson/SSR-1465

Updates for ITAC Coverage Statement redesign
This commit is contained in:
brich1212safe 2024-09-09 13:38:28 -04:00 committed by GitHub
commit 7b40a8fcb2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 268 additions and 14 deletions

View file

@ -35,17 +35,30 @@
</div>
<div class="modal-footer px-5 py-4">
<modalButtonMain
v-if="useDefaultButton"
id="modalbtn"
ref="modalButtonMain"
isPrimary
class="w-100"
loaderColor="white"
:buttonText="footerButtonText"
:class="
:class="[
(isButtonDisabled || isFooterButtonDisabled) &&
'form-test-invalid'
"
'form-test-invalid',
additionalClassesString
]"
@clickEvent="validateAndEmit" />
<button
v-if="!useDefaultButton"
class="w-100 btn d-flex align-items-center justify-content-center delay"
:class="[
(isButtonDisabled || isFooterButtonDisabled) &&
'form-test-invalid',
additionalClassesString
]"
@click="validateAndEmit">
<span class="m-0">{{ footerButtonText }}</span>
</button>
</div>
</div>
</div>
@ -73,7 +86,15 @@ export default {
onModalClosedCallback: {
type: Function
},
isButtonDisabled: Boolean
isButtonDisabled: Boolean,
useDefaultButton: {
type: Boolean,
default: true
},
additionalClassesString: {
type: String,
default: null
}
},
emits: ['footer-button-event', 'isModalOpened'],
setup(props) {
@ -181,6 +202,16 @@ export default {
z-index: 5;
border-radius: 0;
background-color: $gray-100;
> .navigation-link {
color: $black;
font-weight: $font-weight-bold;
line-height: 1.625rem;
white-space: nowrap;
align-items: center;
text-decoration: underline;
text-underline-offset: .3125rem;
}
}
&.modal-component {
.modal-dialog {

View file

@ -2,6 +2,7 @@
exports[`coverageStatement.vue returns the initial data 1`] = `
Object {
"CANCEL_CLAIM_REF_NAME": "CancelClaimModal",
"DEDUCTIBLE_MODAL_REF_NAME": "DeductibleModal",
"RECAL_MODAL_REF_NAME": "RecalModal",
"SITE_FOOTER_REF_NAME": "siteFooter",
@ -12,6 +13,7 @@ Object {
},
"selectedProvider": "",
"widget": Object {
"disclaimerText": "DisclaimerWidget",
"explanatoryText": "ExplanatoryTextWidget",
"nextStep": "NextStepsWidget",
"serviceProviderQuestion": "ServiceProviderQuestion",

View file

@ -0,0 +1,8 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`returns the initial data 1`] = `
Object {
"isLoaderDisplayed": false,
"isModalOpened": false,
}
`;

View file

@ -0,0 +1,88 @@
import { shallowMount } from '@vue/test-utils';
import cancelClaimModal from '@/layouts/coverage-statement/cancel-claim-modal/cancel-claim-modal.vue';
const mockDataStrings = {
cmsBodyText: 'Some Body Text from CMS',
cmsBodyText2: 'Some Text for Button in Body from CMS',
cmsFooterText: 'Some Footer Text from CMS',
cmsHeaderText: 'Some Header Text from CMS'
};
const mockCmsContent = {
BodyText: mockDataStrings.cmsBodyText,
BodyText2: mockDataStrings.cmsBodyText2,
FooterText: mockDataStrings.cmsFooterText,
HeaderText: mockDataStrings.cmsHeaderText
};
const mockMixin = {
methods: {
getCmsContent: jest.fn((_, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};
function setupMocks() {
const wrapper = shallowMount(cancelClaimModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: 'test'
},
attachTo: document.body
});
return { wrapper };
}
test('returns the initial data', () => {
// Arrange
const { wrapper } = setupMocks();
// Assert
expect(wrapper.vm.$data).toMatchSnapshot();
});
describe('Computed', () => {
test('Should get modal body text from Widget CMS', () => {
// Arrange
const { wrapper } = setupMocks();
// Act
const modalHeader = wrapper.vm.modalBodyText;
// Assert
expect(modalHeader).toBe(mockDataStrings.cmsBodyText);
});
test('Should get modal body text2 from Widget CMS', () => {
// Arrange
const { wrapper } = setupMocks();
// Act
const modalHeader = wrapper.vm.modalBodyText2;
// Assert
expect(modalHeader).toBe(mockDataStrings.cmsBodyText2);
});
test('Should get modal footer text from Widget CMS', () => {
// Arrange
const { wrapper } = setupMocks();
// Act
const modalHeader = wrapper.vm.modalFooterText;
// Assert
expect(modalHeader).toBe(mockDataStrings.cmsFooterText);
});
test('Should get modal header text from Widget CMS', () => {
// Arrange
const { wrapper } = setupMocks();
// Act
const modalHeader = wrapper.vm.modalHeaderText;
// Assert
expect(modalHeader).toBe(mockDataStrings.cmsHeaderText);
});
});

View file

@ -0,0 +1,102 @@
<!-- eslint-disable vue/no-v-html -->
<template>
<transition
name="fade"
mode="out-in">
<div class="cancel-claim-modal-container">
<modal
ref="cancelClaimModal"
:headerText="modalHeaderText"
:footerButtonText="modalFooterText"
additionalClassesString="navigation-link"
:useDefaultButton="false"
@isModalOpened="setModalStatus"
@footerButtonEvent="cancelClaimConfirmation">
<div v-html="modalBodyText"></div>
<button
id="btnContinueReferral"
type="button"
:aria-disabled="false"
class="btn btn-primary btn-override
align-items-center justify-content-center d-inline-flex mt-4 py-3 px-4 delay w-100"
@click="returnToClaim">
{{ modalBodyText2 }}
<loader
v-if="isLoaderDisplayed"
class="ms-2 button-loader" />
</button>
</modal>
</div>
</transition>
</template>
<script>
import loader from '@/ux-components/loader/loader.vue';
import modal from '@/digital-components/modal/modal.vue';
export default {
name: 'cancel-claim-modal',
components: {
loader,
modal
},
emits: ['cancel-claim-confirmation', 'return-to-claim'],
data() {
return {
isLoaderDisplayed: false,
isModalOpened: false
};
},
computed: {
modalBodyText() {
return this.getCmsContent('CancelClaimModal', 'BodyText');
},
modalBodyText2() {
return this.getCmsContent('CancelClaimModal', 'BodyText2');
},
modalHeaderText() {
return this.getCmsContent('CancelClaimModal', 'HeaderText');
},
modalFooterText() {
return this.getCmsContent('CancelClaimModal', 'FooterText');
}
},
methods: {
openModal() {
this.$refs.cancelClaimModal.openModal();
},
removeLoader() {
this.isLoaderDisplayed = false;
},
setModalStatus(isOpened) {
this.isModalOpened = isOpened;
},
closeModal() {
this.$refs.cancelClaimModal.closeModal();
},
cancelClaimConfirmation() {
this.$emit('cancel-claim-confirmation');
},
returnToClaim() {
this.$emit('return-to-claim');
}
}
};
</script>
<style lang="scss" scoped>
.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: $font-weight-bold;
@media (hover: hover) {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
}
}
</style>

View file

@ -27,6 +27,7 @@ jest.mock('@/helpers/cms-content-helper', () => ({
}));
const SAFELITE_PROVIDER = 'Safelite';
const CANCEL_CLAIM_REF_NAME = 'CancelClaimModal';
const mockMixin = {
methods: {
@ -91,6 +92,7 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
const wrapper = shallowMount(coverageStatement, mountOptions);
wrapper.vm.$refs[CANCEL_CLAIM_REF_NAME].openModal = jest.fn();
return { wrapper };
}

View file

@ -71,9 +71,9 @@
isRequired
:validationRules="rules.selectionRequired">
</buttonQuestion>
<text-block
<textBlock
v-if="isQuoteDisplayed"
cmsWidgetName="DisclaimerWidget"
:customText="disclaimerText"
typeStyle="caption" />
<siteFooter
:ref="SITE_FOOTER_REF_NAME"
@ -93,6 +93,10 @@
:ref="DEDUCTIBLE_MODAL_REF_NAME"
cmsWidgetName="DeductibleModal"
class="deductible-modal" />
<cancelClaimModal
:ref="CANCEL_CLAIM_REF_NAME"
@cancelClaimConfirmation="cancelClaim"
@returnToClaim="continueClaim" />
</Form>
</template>
<script>
@ -101,6 +105,7 @@ import { Form } from 'vee-validate';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import siteHeader from '@/iss-components/site-header/site-header.vue';
import alert from '@/ux-components/alert/alert.vue';
import cancelClaimModal from '@/layouts/coverage-statement/cancel-claim-modal/cancel-claim-modal.vue';
import contentGroupModal from '@/iss-components/content-group-modal/content-group-modal.vue';
import buttonQuestion from '@/digital-components/button-question/button-question.vue';
import textBlock from '@/digital-components/text-block/text-block.vue';
@ -127,6 +132,7 @@ import { getPriceOfLineItems } from '@/helpers/price-calculator';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
const CANCEL_CLAIM_REF_NAME = 'CancelClaimModal';
const SAFELITE_PROVIDER = 'Safelite';
const RECAL_MODAL_REF_NAME = 'RecalModal';
const DEDUCTIBLE_MODAL_REF_NAME = 'DeductibleModal';
@ -140,6 +146,7 @@ export default {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
alert,
cancelClaimModal,
contentGroupModal,
buttonQuestion,
textBlock
@ -164,7 +171,7 @@ export default {
}
];
const { vehicle, isITAC, isDeductible, serviceLocation } = store;
const { isITAC, isDeductible, serviceLocation } = store;
if (isITAC || isDeductible) {
promiseResultMap.push({
resultKey: 'getFinalDeductible',
@ -205,12 +212,14 @@ export default {
selectionRequired: globalRules.OPTION_REQUIRED
},
widget: {
disclaimerText: 'DisclaimerWidget',
subheader: 'SiteSubHeaderWidget',
verifiedItacAlert: 'VerifiedITACAlert',
explanatoryText: 'ExplanatoryTextWidget',
nextStep: 'NextStepsWidget',
serviceProviderQuestion: 'ServiceProviderQuestion'
},
CANCEL_CLAIM_REF_NAME,
RECAL_MODAL_REF_NAME,
DEDUCTIBLE_MODAL_REF_NAME,
SITE_FOOTER_REF_NAME
@ -237,7 +246,6 @@ export default {
widgetFields.ALERT_WIDGET.BODY_TEXT
)?.replaceAll(
'{custom:costSavings}',
formatAmountInDollars(itacCostSavings)
);
},
@ -247,6 +255,12 @@ export default {
widgetFields.SUB_HEADER_WIDGET.SECONDARY_TEXT
);
},
disclaimerText() {
return this.getCmsContent(this.widget.disclaimerText, widgetFields.TEXT_BLOCK_WIDGET.TEXT)?.replaceAll(
'{custom:disclaimerAmount}',
formatAmountInDollars(this.totalServicePrice)
);
},
explanatoryText() {
return this.getTextFromCmsWithCustomIfStatements(
this.widget.explanatoryText,
@ -331,11 +345,9 @@ export default {
},
watch: {
selectedProvider() {
const buttonText =
this.selectedProvider === SAFELITE_PROVIDER
? 'Continue with Safelite'
: 'Continue';
this.$refs[SITE_FOOTER_REF_NAME].updateButtonText(buttonText);
if (this.selectedProvider && this.selectedProvider !== SAFELITE_PROVIDER) {
this.$refs[this.CANCEL_CLAIM_REF_NAME].openModal();
}
},
nextStepsBody(newValue, oldValue) {
if (newValue !== oldValue) {
@ -462,7 +474,16 @@ export default {
setBaseServiceLineItems(lineItems) {
this.baseServiceLineItems = lineItems;
},
formatAmountInDollars
formatAmountInDollars,
cancelClaim() {
useMainStore().updateIsSafeliteProvider(false);
useMainStore().setBailout(bailoutMessage.RequestCallback());
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP);
},
continueClaim() {
useMainStore().updateIsSafeliteProvider(true);
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE);
}
}
};
</script>