Merge branch 'develop' into feature/digital/SSR-390

This commit is contained in:
Kroell 2023-04-12 13:59:07 -04:00
commit 1017892f85
21 changed files with 1051 additions and 526 deletions

View file

@ -1,68 +1,255 @@
jest.mock("vee-validate", () => ({
useForm: jest.fn(),
}));
const mockValidate = (returnValue) => jest.fn(async () => Promise.resolve({ valid: returnValue }));
const mockMeta = (returnValue) => jest.fn(async () => Promise.resolve(returnValue));
import { shallowMount } from "@vue/test-utils";
import Modal from "./modal";
import modal from "./modal";
import crypto from "crypto";
import { useForm } from "vee-validate";
import { Modal } from "bootstrap";
const footerButtonText = "Sample footer text here.";
const headerText = "Sample header text here.";
global.crypto = crypto;
describe("modal.vue", () => {
it("Should display header text when HeaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
it("Should display modal header text when headerText is defined", async () => {
// Arrange / Act
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const wrapper = shallowMount(modal, {
props: {
cmsWidgetName: "test",
headerText: headerText,
footerButtonText: footerButtonText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
// Assert
expect(wrapper.html()).toEqual(expect.stringContaining(headerText));
});
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
it("Should display footer button text when footerButtonText is defined", async () => {
// Arrange / Act
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const wrapper = shallowMount(modal, {
props: {
cmsWidgetName: "test",
footerButtonText: footerButtonText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
// Assert
expect(wrapper.html()).toEqual(expect.stringContaining(footerButtonText));
});
it("Should insert image url when Image is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
it("Should emit 'footer-button-event' if the form is valid", async () => {
// Arrange
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
cmsWidgetName: "test",
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Act
const buttonMain = wrapper.findComponent({ ref: "modalButtonMain" });
await buttonMain.trigger("click-event");
// Assert
expect(wrapper.emitted("footer-button-event")).toBeTruthy();
});
it("Should display body text when BodyText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
it("Should not emit 'footer-button-event' if the form is invalid", async () => {
// Arrange
const fakeMeta = {
touched: true,
dirty: true,
valid: false,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(false),
});
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
cmsWidgetName: "test",
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Act
const buttonMain = wrapper.findComponent({ ref: "modalButtonMain" });
await buttonMain.trigger("click-event");
// Assert
expect(wrapper.emitted("footer-button-event")).toBeFalsy();
});
it("Should have a disabled footer button when the form has not been touched", async () => {
// Arrange / Act
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Assert
expect(wrapper.vm.isFooterButtonDisabled).toBe(true);
});
it("Should have a disabled footer button when the form is invalid", async () => {
// Arrange / Act
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Assert
expect(wrapper.vm.isFooterButtonDisabled).toBe(true);
});
it("Should call bootstrap Modal method 'show' when calling 'openModal'", async () => {
// Arrange
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const showMock = jest.spyOn(Modal.prototype, "show");
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Act
wrapper.vm.openModal();
// Assert
expect(showMock).toHaveBeenCalled();
});
it("Should call bootstrap Modal method 'hide' when calling 'closeModal'", async () => {
// Arrange
const fakeMeta = {
touched: true,
dirty: true,
valid: true,
validated: true,
};
useForm.mockReturnValue({
meta: mockMeta(fakeMeta),
validate: mockValidate(true),
});
const hideMock = jest.spyOn(Modal.prototype, "hide");
const resetButtonStyle = jest.fn();
const wrapper = shallowMount(modal, {
props: {
footerButtonText: footerButtonText,
headerText: headerText,
},
attachTo: document.body,
});
wrapper.vm.resetButtonStyle = resetButtonStyle;
// Act
wrapper.vm.openModal();
wrapper.vm.closeModal();
// Assert
expect(hideMock).toHaveBeenCalled();
});
});
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

@ -2,39 +2,38 @@
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="cmsWidgetName"
v-on="{ 'hidden.bs.modal': onModalClosed, 'shown.bs.modal': onModalOpened }"
:id="modalId"
tabindex="-1"
aria-labelledby="ModalComponentLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<div class="modal-header mb-2 mt-2">
<label
v-if="headerText"
class="modal-title d-flex justify-content-center pb-0 w-100">
{{ headerText }}
</label>
<button
ref="closeButton"
type="button"
class="btn-close"
data-bs-dismiss="modal"
@mousedown="closeModal"
aria-label="Close"></button>
</div>
<div class="modal-body ps-4 pe-4 pt-5 pb-4">
<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"
v-html="ModalSubBodyText"></p>
<div class="modal-body ps-5 pe-5 pb-4 pt-0">
<slot></slot>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
<modalButtonMain
isPrimary
class="w-100"
ref="buttonMain"
suppressLoader
:buttonText="ModalCloseButtonText"
@click-event="buttonClick"
data-bs-dismiss="modal" />
ref="modalButtonMain"
loaderColor="white"
:buttonText="footerButtonText"
@click-event="validateAndEmit"
:class="isFooterButtonDisabled && 'form-test-invalid'" />
</div>
</div>
</div>
@ -42,46 +41,80 @@
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
import modalButtonMain from "@/ux-components/modal-button-main/modal-button-main";
import { Modal } from "bootstrap";
import { useForm } from "vee-validate";
export default {
name: "modal",
props: {
cmsWidgetName: String,
modalId: String,
headerText: String,
footerButtonText: String,
onModalOpenedCallback: {
type: Function,
},
onModalClosedCallback: {
type: Function,
},
},
computed: {
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
ModalSubheadertext() {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalSubBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText2");
},
ModalImage() {
return this.getCmsContent(this.cmsWidgetName, "Image");
},
ModalCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
setup(props) {
const modalId = props.modalId ? props.modalId : `modal-${crypto.randomUUID()}`;
const { meta, validate, resetForm } = useForm();
return {
modalId,
meta,
validate,
resetForm,
};
},
methods: {
async validateAndEmit() {
const validationResult = await this.validate();
if (validationResult.valid) {
this.$emit("footer-button-event");
} else {
this.resetButtonStyle();
}
},
resetButtonStyle() {
this.$refs.buttonMain.resetButtonStyle();
this.$refs.modalButtonMain.resetButtonStyle();
},
onModalOpened() {
this.onModalOpenedCallback?.();
},
onModalClosed() {
this.resetButtonStyle();
this.onModalClosedCallback?.();
},
openModal() {
const modal = Modal.getOrCreateInstance(document.getElementById(this.modalId));
modal?.show();
},
closeModal() {
const modal = Modal.getInstance(document.getElementById(this.modalId));
modal?.hide();
},
},
computed: {
isFooterButtonDisabled() {
if (!this.meta.touched) {
return !this.meta.valid;
}
return !this.meta.dirty || !this.meta.valid;
},
},
components: {
buttonMain,
modalButtonMain,
},
};
</script>
<style lang="scss">
.modal {
overflow: hidden;
top: auto;
bottom: 0;
h5,
@ -92,9 +125,17 @@ export default {
.modal-header {
border-bottom: none;
.btn-close {
position: absolute;
right: 1rem;
top: 1rem;
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.6874 1.82179L9.50889 8L15.6874 14.1782C16.1042 14.595 16.1042 15.2707 15.6874 15.6875C15.4843 15.8907 15.2146 16 14.9328 16C14.6514 16 14.3818 15.8906 14.1787 15.6875L8.00017 9.50934L1.82171 15.6875C1.6182 15.8907 1.34876 16 1.06708 16C0.785733 16 0.516056 15.8906 0.312965 15.6875C-0.10429 15.2706 -0.10429 14.5952 0.312797 14.1784L6.03276 8.45867L6.49146 8L0.312945 1.82177C-0.10429 1.40483 -0.10429 0.729418 0.312797 0.31262C0.728936 -0.104145 1.40489 -0.104051 1.82185 0.31262L7.54152 6.03202L8.00017 6.49065L14.1786 0.312472C14.5955 -0.104107 15.2707 -0.104201 15.6874 0.312452C16.1042 0.729289 16.1042 1.40496 15.6874 1.82179Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
opacity: 1;
}
.modal-title {
font-weight: 500;
color: $black;
}
}
&.modal-component {
.modal-dialog {
@ -141,4 +182,4 @@ body {
}
}
}
</style>
</style>

View file

@ -173,9 +173,14 @@ function mapStringToModal(str) {
let params = linkToReplace.substring((dynamicStrings.MODAL_LINK).length + 2, linkToReplace.length -1)
let splitParams = params.split(",");
let bodyText = '<a href="#!" data-bs-toggle="modal" data-bs-target="#' + splitParams[0] + '" aria-label="Modal window">' + splitParams[1] +'</a>'
return str.replace(linkToReplace, bodyText);
let bodyText = '<a modalTarget="' + splitParams[0] + '" class="modal-text" aria-label="Modal window">' + splitParams[1] + '</a>'
let returnVal = str.replace(linkToReplace, bodyText)
if (returnVal.includes(dynamicStrings.MODAL_LINK)) {
returnVal = mapStringToModal(returnVal);
}
return returnVal;
}
// Function to convert a string, into a matching global state item.
@ -214,7 +219,20 @@ function mapStringToState(str) {
}
return stringBuilder.trimStart();
}
}
export function setupModalLinks(context) {
context.$nextTick(() => {
const elements = document.getElementsByClassName("modal-text")
for(let element of elements){
const target = element.getAttribute("modalTarget");
if(target)
{
element.addEventListener("click", () => context.$refs[target].openModal() );
}
};
});
}
export function doesCopyContainRouterLink(copy) {
return copy.includes(this.dynamicStrings.ROUTER_LINK);

View file

@ -48,6 +48,8 @@ export function getMountOptions(mockData) {
mocks.queryStrings = queryStrings;
mocks.$router = mockData?.router;
mocks.$route = mockData?.route;
mocks.GaActions = GaActions;
mocks.pushEventToGA = jest.fn();
mocks.$loadScript = mockData?.loadScript;
mocks.prependActionToMethod = jest.fn();

View file

@ -0,0 +1,67 @@
import { mount } from "@vue/test-utils";
import contentGroupModal from "./content-group-modal";
import crypto from "crypto";
global.crypto = crypto;
describe("content-group-modal.vue", () => {
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

@ -0,0 +1,86 @@
<template>
<modal
:ref="ModalName"
: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>
<p
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText"
v-html="ModalSubBodyText"></p>
</modal>
</template>
<script>
import modal from "@/digital-components/modal/modal";
export default {
name: "content-group-modal",
props: {
cmsWidgetName: String,
},
computed: {
ModalName() {
return this.cmsWidgetName;
},
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
ModalSubheadertext() {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalSubBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText2");
},
ModalImage() {
return this.getCmsContent(this.cmsWidgetName, "Image");
},
ModalCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
},
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
footerButtonClick() {
this.$refs[this.ModalName].closeModal();
},
},
components: {
modal,
},
};
</script>
<style lang="scss">
.modal {
&.modal-component {
.modal-dialog {
.modal-content {
.modal-body {
.modal-sub-body {
color: $gray-600;
}
ul {
margin-bottom: 0;
}
p {
&:last-child {
margin-bottom: 0;
}
}
}
}
}
}
}
</style>

View file

@ -24,12 +24,12 @@ describe("menu-modal.vue", () => {
expect(modalFooter.text()).toContain("Safelite Group");
});
it("Should return Terms of use text link text", async () => {
it("Should return Terms of service text link text", async () => {
// Act
const wrapper = shallowMount(menuModal);
// Expect
expect(wrapper.html()).toContain("Terms of use");
expect(wrapper.html()).toContain("Terms of service");
});
it('Should return "Your privacy choices" text link text', async () => {

View file

@ -16,7 +16,7 @@
<div class="modal-body d-flex flex-column">
<textLink
linkType="navigation"
text="Terms of use"
text="Terms of service"
href="//www.safelite.com/terms-of-use"
target="_blank" />
<textLink

View file

@ -8,7 +8,7 @@ import { useMainStore } from "@/store";
import baseMixin from "@/mixins/base-mixin";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import { applicationConfig } from "@/constants/application-config";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { fetchCmsContentForPage, setupModalLinks } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper.js";
jest.mock("@/helpers/damage-helper", () => ({
@ -20,9 +20,10 @@ jest.mock("@/helpers/layout-helper.js", () => ({
settleAllPromises: jest.fn(),
}));
// Mock fetchCmsContentForPage
// Mock fetchCmsContentForPage, setupModalLinks
jest.mock("@/helpers/cms-content-helper", () => ({
fetchCmsContentForPage: jest.fn(),
setupModalLinks: jest.fn(),
}));
describe("coverage-statement.vue...", () => {

View file

@ -1,48 +1,49 @@
<template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" >
<div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="select-car">
<div class="container-fluid pb-2">
<div class="row px-3">
<div class="col">
<div class="select-car-form rounded">
<textBlock
cmsWidgetName="verifyingCoverageStatement"
typeStyle="h5"
justifyText="center"
class="mt-0 mb-4"
id="coverage-statement-text-block"
/>
<div>
<p v-html="continueWithSchedulingBodyText" class="mt-0 small" ></p>
</div>
<textBlock
cmsWidgetName="whatHappensNextCopy"
class="mt-4 mb-2 fw-bold"
id="coverage-statement-text-block"
/>
<div>
<p class="small" v-html="bodyText" ref="coverageStatementBodyText"></p>
</div>
<recalModal cmsWidgetName="RecalModal" />
<steeringText cmsWidgetName="MASteeringText" ></steeringText>
<siteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction"
ref="siteFooter"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</Form>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" >
<div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="select-car">
<div class="container-fluid pb-2">
<div class="row px-3">
<div class="col">
<div class="select-car-form rounded">
<textBlock
cmsWidgetName="verifyingCoverageStatement"
typeStyle="h5"
justifyText="center"
class="mt-0 mb-4"
id="coverage-statement-text-block"
/>
<div>
<p v-html="continueWithSchedulingBodyText" class="mt-0 small" ></p>
</div>
<textBlock
cmsWidgetName="whatHappensNextCopy"
class="mt-4 mb-2 fw-bold"
id="coverage-statement-text-block"
/>
<div>
<p class="small" v-html="bodyText" ref="coverageStatementBodyText"></p>
</div>
<steeringText cmsWidgetName="MASteeringText" ></steeringText>
<siteFooter
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="navigateBack"
@forwardClicked="forwardButtonAction"
ref="siteFooter"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<recalModal ref="RecalModal" cmsWidgetName="RecalModal" />
</Form>
</template>
<script>
@ -56,116 +57,119 @@ import textBlock from "@/digital-components/text-block/text-block";
import recalModal from '@/layouts/coverage-statement/recal-modal/recal-modal.vue';
import steeringText from "@/iss-components/steering-text/steering-text.vue";
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { fetchCmsContentForPage, setupModalLinks } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { getDamageString } from '@/helpers/damage-helper.js';
import { useMainStore } from "@/store";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
export default {
name: 'coverage-statement',
mixins: [baseFormMixin, vehicleQuestionsMixin],
components: {
siteFooter,
siteHeader,
siteSubHeader,
Form,
textBlock,
recalModal,
steeringText
},
computed: {
bodyText() {
if (useMainStore().order.damage.isRepair) {
return this.unverifiedNonADASRepairBodyText;
}
else {
let parts = useMainStore().order.lineItems.glassParts;
name: 'coverage-statement',
mixins: [baseFormMixin, vehicleQuestionsMixin],
components: {
siteFooter,
siteHeader,
siteSubHeader,
Form,
textBlock,
recalModal,
steeringText
},
mounted() {
setupModalLinks(this);
},
computed: {
bodyText() {
if (useMainStore().order.damage.isRepair) {
return this.unverifiedNonADASRepairBodyText;
}
else {
let parts = useMainStore().order.lineItems.glassParts;
// if ADAS, display ADASNextSteps
if (parts.filter(part => part.requiresRecalibration).length > 0) {
return this.unverifiedADASNextStepsBodyText;
}
// if non-ADAS, display NonADASNextSteps
else {
return this.unverifiedNonADASNextStepsBodyText;
}
}
},
continueWithSchedulingBodyText() {
return this.getCmsContent("continueWithSchedulingCopy", "BodyText");
},
unverifiedADASNextStepsBodyText() {
return this.getCmsContent("UnverifiedADASNextStepsWidget", "BodyText").replaceAll("{custom:damage}", this.damageText);
},
unverifiedNonADASNextStepsBodyText() {
return this.getCmsContent("UnverifiedNonADASNextStepsWidget", "BodyText").replaceAll("{custom:damage}", this.damageText);
},
unverifiedNonADASRepairBodyText() {
return this.getCmsContent("UnverifiedNonADASRepairWidget", "BodyText");
},
damageText() {
var damageString = getDamageString();
return damageString == "match" ? "" : damageString;
},
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// if ADAS, display ADASNextSteps
if (parts.filter(part => part.requiresRecalibration).length > 0) {
return this.unverifiedADASNextStepsBodyText;
}
// if non-ADAS, display NonADASNextSteps
else {
return this.unverifiedNonADASNextStepsBodyText;
}
}
},
continueWithSchedulingBodyText() {
return this.getCmsContent("continueWithSchedulingCopy", "BodyText");
},
unverifiedADASNextStepsBodyText() {
return this.getCmsContent("UnverifiedADASNextStepsWidget", "BodyText").replaceAll("{custom:damage}", this.damageText);
},
unverifiedNonADASNextStepsBodyText() {
return this.getCmsContent("UnverifiedNonADASNextStepsWidget", "BodyText").replaceAll("{custom:damage}", this.damageText);
},
unverifiedNonADASRepairBodyText() {
return this.getCmsContent("UnverifiedNonADASRepairWidget", "BodyText");
},
damageText() {
var damageString = getDamageString();
return damageString == "match" ? "" : damageString;
},
},
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise,
},
];
// Settle promises and get results
const promiseResultMap = [
{
resultKey: 'cmsContent',
promise: cmsContentPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
methods: {
arePagePrerequisitesValid() {
if (useMainStore().order.vehicle.vin) {
return true;
}
return false;
},
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
methods: {
arePagePrerequisitesValid() {
if (useMainStore().order.vehicle.vin) {
return true;
}
return false;
},
async forwardButtonAction() {
return this.navigateForward();
},
async forwardButtonAction() {
return this.navigateForward();
},
navigateForward() {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_COVERAGE_STATEMENT,
this.$route
);
},
},
navigateForward() {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_COVERAGE_STATEMENT,
this.$route
);
},
},
};
</script>
<style lang="scss">
ol {
margin-left: -1rem;
li {
margin-bottom: .5rem;
line-height: 1.5rem;
a {
line-height: 1.5rem;
padding: 0;
}
}
margin-left: -1rem;
li {
margin-bottom: .5rem;
line-height: 1.5rem;
a {
line-height: 1.5rem;
padding: 0;
}
}
}
#coverage-statement-text-block {
color: #000000;
color: $black;
}
p strong {
color: #000000;
font-weight: 500;
color: $black;
font-weight: 500;
}
</style>
</style>

View file

@ -1,10 +1,10 @@
import { shallowMount } from "@vue/test-utils";
import { shallowMount, mount } from "@vue/test-utils";
import recalModal from "@/layouts/coverage-statement/recal-modal/recal-modal.vue";
describe("modal.vue", () => {
it("Should display header text when HeaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(recalModal, {
const wrapper = mount(recalModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
@ -16,7 +16,7 @@ describe("modal.vue", () => {
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(recalModal, {
const wrapper = mount(recalModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
@ -28,7 +28,7 @@ describe("modal.vue", () => {
it("Should insert image url when Image is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(recalModal, {
const wrapper = mount(recalModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
@ -40,7 +40,7 @@ describe("modal.vue", () => {
it("Should display body text when BodyText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(recalModal, {
const wrapper = mount(recalModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",

View file

@ -1,55 +1,45 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="cmsWidgetName"
tabindex="-1"
aria-labelledby="ModalComponentLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body ps-4 pe-4 pt-0 pb-5">
<h5 class="mb-4 text-center" v-html="ModalHeadline"></h5>
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p>
<p class="mb-0 small" v-html="ModalBodyText"></p>
<p
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText"
v-html="ModalSubBodyText"></p>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
isPrimary
class="w-100"
ref="buttonMain"
suppressLoader
:buttonText="ModalCloseButtonText"
@click-event="buttonClick"
data-bs-dismiss="modal" />
</div>
</div>
<modal
:ref="ModalName"
:modalId="ModalName"
:footerButtonText="ModalCloseButtonText"
@footer-button-event="closeModal"
>
<div class="recal-modal-body ps-4 pe-4 pt-0 pb-5">
<h5 class="mb-4 text-center" v-html="ModalHeadline"></h5>
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p>
<p class="mb-0 small" v-html="ModalBodyText"></p>
<p
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText"
v-html="ModalSubBodyText">
</p>
</div>
</div>
</modal>
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
import modal from "@/digital-components/modal/modal";
export default {
name: "modal",
name: "recal-modal",
props: {
cmsWidgetName: String,
},
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
closeModal() {
this.$refs[this.ModalName].closeModal();
},
},
computed: {
ModalName() {
return this.cmsWidgetName;
},
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
@ -69,77 +59,30 @@ export default {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
},
methods: {
resetButtonStyle() {
this.$refs.buttonMain.resetButtonStyle();
},
},
components: {
buttonMain,
modal,
},
};
</script>
<style lang="scss">
.modal {
top: auto;
bottom: 0;
overflow: hidden;
h5,
strong,
.subheader-text {
color: $black;
}
.modal-header {
border-bottom: none;
.btn-close {
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.6874 1.82179L9.50889 8L15.6874 14.1782C16.1042 14.595 16.1042 15.2707 15.6874 15.6875C15.4843 15.8907 15.2146 16 14.9328 16C14.6514 16 14.3818 15.8906 14.1787 15.6875L8.00017 9.50934L1.82171 15.6875C1.6182 15.8907 1.34876 16 1.06708 16C0.785733 16 0.516056 15.8906 0.312965 15.6875C-0.10429 15.2706 -0.10429 14.5952 0.312797 14.1784L6.03276 8.45867L6.49146 8L0.312945 1.82177C-0.10429 1.40483 -0.10429 0.729418 0.312797 0.31262C0.728936 -0.104145 1.40489 -0.104051 1.82185 0.31262L7.54152 6.03202L8.00017 6.49065L14.1786 0.312472C14.5955 -0.104107 15.2707 -0.104201 15.6874 0.312452C16.1042 0.729289 16.1042 1.40496 15.6874 1.82179Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
opacity: 1;
.recal-modal-body {
.modal-sub-body {
color: $gray-600;
}
}
&.modal-component {
.modal-dialog {
max-width: 576px;
margin: 0 auto;
.modal-content {
margin: 0 auto;
box-shadow: 0px 16px 48px -16px rgba(0, 0, 0, 0.25);
border-radius: 1.5rem 1.5rem 0 0;
.modal-body {
.modal-sub-body {
color: $gray-600;
}
ul {
margin-bottom: 0;
}
p {
&:last-child {
margin-bottom: 0;
}
}
}
}
&.modal-dialog-centered {
align-items: flex-end;
min-height: 100%;
ul {
margin-bottom: 0;
}
p {
&:last-child {
margin-bottom: 0;
}
}
.modal-footer {
border-top: none;
background-color: $gray-100;
box-shadow: 0px -1px 0px rgba(179, 180, 181, 0.3);
button {
margin: 0;
}
.subheader-text {
color: $black;
}
}
}
body {
.modal-backdrop {
height: 100%;
&.show {
opacity: 0.4;
}
}
}
</style>

View file

@ -146,11 +146,11 @@
break;
case "returnurl":
this.mainStore.iisConfig.returnURL = value;
this.mainStore.issConfig.returnURL = value;
break;
case "returnurl2":
this.mainStore.iisConfig.returnURL2 = value;
this.mainStore.issConfig.returnURL2 = value;
break;
// Not stored

View file

@ -350,7 +350,7 @@ function setupMocks({
],
route: {
query: {
fmgPage: "part-questions",
issPage: "part-questions",
},
},
data() {

View file

@ -14,7 +14,7 @@
v-html="ProviderPreferenceBodyText"
class="mt-0 body-text"
></div>
<shoppreferenceModal cmsWidgetName="ShopPreferenceDrawer" />
<buttonQuestion
cmsWidgetName="ServiceLocationQuestion"
:questionText="questionText"
@ -38,15 +38,19 @@
</div>
</div>
</div>
<contentGroupModal cmsWidgetName="ShopPreferenceDrawer"
ref="ShopPreferenceDrawer"
/>
</Form>
</template>
<script>
// Import Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { fetchCmsContentForPage, setupModalLinks } from '@/helpers/cms-content-helper';
import { settleAllPromises } from "@/helpers/layout-helper";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import buttonQuestion from "@/digital-components/button-question/button-question";
import contentGroupModal from "@/iss-components/content-group-modal/content-group-modal"
// Import Component
import baseFormMixin from "@/mixins/base-form-mixin";
@ -54,7 +58,6 @@ import { Form,defineRule } from "vee-validate";
import siteFooter from "@/iss-components/site-footer/site-footer.vue";
import siteHeader from "@/iss-components/site-header/site-header.vue";
import siteSubHeader from "@/iss-components/site-sub-header/site-sub-header.vue";
import shoppreferenceModal from '@/layouts/provider-preference/shoppreference-modal/shoppreference-modal.vue';
// DEFINE VALIDATION RULES
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
@ -67,8 +70,8 @@ export default {
siteHeader,
siteSubHeader,
Form,
shoppreferenceModal,
buttonQuestion
buttonQuestion,
contentGroupModal
},
data() {
return {
@ -124,6 +127,9 @@ export default {
},
resetDependentState() {},
},
mounted() {
setupModalLinks(this);
}
};
</script>
<style lang="scss">

View file

@ -1,41 +0,0 @@
import { shallowMount } from "@vue/test-utils";
import shoppreferenceModal from '@/layouts/provider-preference/shoppreference-modal/shoppreference-modal.vue';
describe("modal.vue", () => {
it("Should display header text when HeaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(shoppreferenceModal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
});
it("Should display body text when BodyText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(shoppreferenceModal, {
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.",
BodyText: "Sample body text here.",
FooterText: "Sample footer text here.",
};

View file

@ -1,137 +0,0 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="cmsWidgetName"
tabindex="-1"
aria-labelledby="ModalComponentLabel"
aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<button
type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body ps-4 pe-4 pt-4 pb-4">
<h5 class="mb-2 text-center header-text" v-html="ModalHeadline"></h5>
<p class="mb-0 small body-text" v-html="ModalBodyText"></p>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
isPrimary
class="w-100"
ref="buttonMain"
suppressLoader
:buttonText="ModalCloseButtonText"
@click-event="buttonClick"
data-bs-dismiss="modal" />
</div>
</div>
</div>
</div>
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
export default {
name: "modal",
props: {
cmsWidgetName: String,
},
computed: {
ModalHeadline() {
return this.getCmsContent(this.cmsWidgetName, "HeaderText");
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalCloseButtonText() {
return this.getCmsContent(this.cmsWidgetName, "FooterText");
},
},
methods: {
resetButtonStyle() {
this.$refs.buttonMain.resetButtonStyle();
},
},
components: {
buttonMain,
},
};
</script>
<style lang="scss">
.modal {
top: auto;
bottom: 0;
h5,
strong,
.header-text{
color: #000000;
font-weight: 500;
font-size: 16px;
line-height: 26px;
}
.body-text{
color:#525656;
font-size: 16px;
line-height: 26px;
}
.modal-header {
border-bottom: none;
.btn-close {
background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.6874 1.82179L9.50889 8L15.6874 14.1782C16.1042 14.595 16.1042 15.2707 15.6874 15.6875C15.4843 15.8907 15.2146 16 14.9328 16C14.6514 16 14.3818 15.8906 14.1787 15.6875L8.00017 9.50934L1.82171 15.6875C1.6182 15.8907 1.34876 16 1.06708 16C0.785733 16 0.516056 15.8906 0.312965 15.6875C-0.10429 15.2706 -0.10429 14.5952 0.312797 14.1784L6.03276 8.45867L6.49146 8L0.312945 1.82177C-0.10429 1.40483 -0.10429 0.729418 0.312797 0.31262C0.728936 -0.104145 1.40489 -0.104051 1.82185 0.31262L7.54152 6.03202L8.00017 6.49065L14.1786 0.312472C14.5955 -0.104107 15.2707 -0.104201 15.6874 0.312452C16.1042 0.729289 16.1042 1.40496 15.6874 1.82179Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
opacity: 1;
}
}
&.modal-component {
.modal-dialog {
max-width: 576px;
margin: 0 auto;
.modal-content {
margin: 0 auto;
box-shadow: 0px 16px 48px -16px rgba(0, 0, 0, 0.25);
border-radius: 1.5rem 1.5rem 0 0;
.modal-body {
.modal-sub-body {
color: $gray-600;
}
ul {
margin-bottom: 0;
}
p {
&:last-child {
margin-bottom: 0;
}
}
}
}
&.modal-dialog-centered {
align-items: flex-end;
min-height: 100%;
}
}
.modal-footer {
border-top: none;
background-color: $gray-100;
box-shadow: 0px -1px 0px rgba(179, 180, 181, 0.3);
button {
margin: 0;
}
}
}
}
body {
.modal-backdrop {
height: 100%;
&.show {
opacity: 0.4;
}
}
}
</style>

View file

@ -63,4 +63,11 @@ body {
overflow: hidden;
}
}
}
.modal-text {
display: inline;
color: $blue;
cursor: pointer;
text-decoration: underline;
}
}

View file

@ -11,7 +11,7 @@ describe("list-button.vue", () => {
mockData: {
global: {
mocks: {
$route: { query: { fmgPage: "page-name" } },
$route: { query: { issPage: "page-name" } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
},
@ -37,7 +37,7 @@ describe("list-button.vue", () => {
mockData: {
global: {
mocks: {
$route: { query: { fmgPage: "page-name" } },
$route: { query: { issPage: "page-name" } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
},
@ -64,7 +64,7 @@ describe("list-button.vue", () => {
mockData: {
global: {
mocks: {
$route: { query: { fmgPage: "page-name" } },
$route: { query: { issPage: "page-name" } },
GaActions: GaActions,
pushEventToGA: jest.fn(),
},

View file

@ -0,0 +1,188 @@
import { shallowMount } from "@vue/test-utils";
import modalButtonMain from "./modal-button-main";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
describe("modal-button-main.vue", () => {
it("Should return btn-primary class", () => {
// Arrange/Act
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
isPrimary: true,
},
})
);
const button = wrapper.find("button");
// Assert
expect(button.attributes("class")).toContain("btn-primary");
});
it("Should return aria-disabled state", () => {
// Arrange/Act
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
isDisabled: true,
},
})
);
const button = wrapper.find("button");
// Assert
expect(button.attributes()["aria-disabled"]).toEqual("true");
});
it("Should return loader color", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderColor: "blue",
loaderEnabled: true,
},
})
);
// Act
wrapper.vm.clicked();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(loader.attributes("class")).toContain("blue");
});
it("Should return loader position", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
// Act
wrapper.vm.clicked();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(loader.attributes("class")).toContain("right");
});
it("Should set 'isLoaderDisplayed' to false when calling 'removeLoader'", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
wrapper.setData({
isLoaderDisplayed: true,
});
// Act
wrapper.vm.removeLoader();
await nextTick();
// Assert
const loader = wrapper.find("loader-stub");
expect(wrapper.vm.isLoaderDisplayed).toBe(false);
});
it("Should set 'isLoaderDisplayed' to false when calling 'resetButtonStyle'", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: "right",
loaderEnabled: true,
},
})
);
wrapper.setData({
isLoaderDisplayed: true,
});
// Act
wrapper.vm.resetButtonStyle();
await nextTick();
// Assert
expect(wrapper.vm.isLoaderDisplayed).toBe(false);
});
it("Should emit 'click-event' event when clicking if the button is enabled", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: "right",
loaderEnabled: true,
isDisabled: false,
},
})
);
const buttonElement = wrapper.find("button");
// Act
buttonElement.trigger("click");
await nextTick();
// Assert
expect(wrapper.emitted("click-event")).toBeTruthy();
});
it("Should not emit 'click-event' event when clicking if the button is disabled", async () => {
// Arrange
const wrapper = shallowMount(
modalButtonMain,
setupMocks({
propsData: {
loaderPosition: "right",
loaderEnabled: true,
isDisabled: true,
},
})
);
const buttonElement = wrapper.find("button");
// Act
buttonElement.trigger("click");
await nextTick();
// Assert
expect(wrapper.emitted("click-event")).toBeFalsy();
});
});
function setupMocks(mountOptionsMockData = {}) {
const defaultMountOptions = { route: { query: { issPage: "page-name" } } };
const baseMountOptions = getMountOptions(
Object.assign(defaultMountOptions, mountOptionsMockData)
);
const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
return allMountOptions;
}

View file

@ -0,0 +1,153 @@
<template>
<button
type="button"
:aria-disabled="isDisabled"
class="btn d-flex align-items-center justify-content-center py-3 px-4 delay"
:class="[
isPrimary ? 'btn-primary' : 'btn-secondary',
isFloat ? 'float-end' : '',
isLoaderDisplayed ? 'has-loader' : '',
]"
@click="clicked">
<span class="m-0">{{ this.buttonText }}</span>
<loader
class="ms-2"
v-if="isLoaderDisplayed && !suppressLoader"
v-bind:class="[this.loaderColor, this.loaderPosition]" />
</button>
</template>
<script>
import loader from "@/ux-components/loader/loader";
export default {
name: "modalButtonMain",
props: {
isPrimary: Boolean,
buttonText: String,
isDisabled: Boolean,
loaderColor: String,
loaderPosition: String,
isFloat: Boolean,
suppressLoader: Boolean,
},
data() {
return {
isLoaderDisplayed: false,
};
},
methods: {
removeLoader() {
this.isLoaderDisplayed = false;
},
clicked() {
this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.CLICKED,
this.buttonText,
true
);
if (!this.isDisabled) {
this.isLoaderDisplayed = true;
this.$emit("click-event");
}
},
resetButtonStyle() {
this.isLoaderDisplayed = false;
},
},
components: {
loader,
},
};
</script>
<style lang="scss">
.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: 500;
@media (hover: hover) {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
&:focus {
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
color: $white;
background: linear-gradient(270deg, rgba(6, 87, 124, 1) 0%, rgba(6, 87, 124, 1) 100%);
}
&:disabled {
background: $gray-200 !important;
background: linear-gradient(270deg, $gray-200 0%, $gray-200 100%) !important;
color: $gray-600 !important;
font-weight: 400;
height: 48px;
border: none;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $white;
background: $blue-700;
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
&.btn-secondary {
position: relative;
background: transparent;
border: 1px solid $blue;
border-radius: $border-radius-lg;
color: $blue;
font-weight: 500;
transition: all 150ms linear;
height: 3rem;
&:hover {
color: $white;
@include blue-gradient;
}
&:focus, // Mouse, touch, stylus focus
&:focus-visible {
// Keyboard focus for accessibility
outline: none;
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700;
color: $white;
@include blue-gradient;
}
&:disabled {
background: transparent;
color: $gray-550 !important;
font-weight: 400;
height: 48px;
border: 1px solid $gray-550;
border-radius: $border-radius-lg;
cursor: pointer;
pointer-events: all;
}
&.has-loader {
color: $white;
@include blue-gradient;
pointer-events: none;
}
&.delay {
// fixes flicker while transitioning between states
transition: background 0s 0s ease-in-out;
}
}
}
</style>