Merge pull request #75 from Safelite/feature/SSR-169

Feature/ssr 169
This commit is contained in:
Kulbhushan Kaushik 2022-12-16 09:40:30 -05:00 committed by GitHub
commit a4da7fad51
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 405 additions and 11 deletions

View file

@ -0,0 +1,153 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="this.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">
<label
:aria-label="this.ModalQuestionText"
class="form-label text-center w-100 mb-5 fw-bold"
v-html="this.ModalQuestionText"></label>
</div>
<buttonQuestion
class="radioQuestion"
:questionText="questionText"
:answers="ModalSelectOptionAnswers"
groupName="ChooseVehicleYear"
v-model="selectedValue"
isRequired />
<div class="modal-footer px-5 py-4">
<buttonMain
class="w-100"
isPrimary
ref="buttonMain"
suppressLoader
:buttonText="ModalSelectButtonText"
@click-event="buttonClick"
data-bs-dismiss="modal" />
</div>
</div>
</div>
</div>
</template>
<script>
import buttonMain from "@/ux-components/button-main/button-main";
import buttonQuestion from "@/common-components/button-question/button-question";
import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
export default {
name: "buttonQuestionModal",
props: {
cmsWidgetName: String,
},
data() {
return {
answers: [],
};
},
computed: {
ModalQuestionText() {
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
},
ModalSelectButtonText() {
return this.getCmsContent(this.cmsWidgetName, "ButtonText");
},
ModalSelectOptionAnswers() {
return this.getCmsContent(this.cmsWidgetName, "Answers");
},
},
methods: {
resetButtonStyle() {
this.$refs.buttonMain.resetButtonStyle();
},
},
components: {
buttonMain,
buttonQuestion,
},
};
</script>
<style lang="scss">
.modal {
top: auto;
bottom: 0;
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;
}
}
&.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 {
padding-bottom: 0px;
padding-top:0px;
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;
}
}
.radioQuestion
{
padding-bottom: 20px;
padding-right: 24px;
padding-left: 24px;
}
}
}
body {
.modal-backdrop {
height: 100%;
&.show {
opacity: 0.4;
}
}
}
</style>

View file

@ -0,0 +1,68 @@
import { shallowMount } from "@vue/test-utils";
import Modal from "./modal";
describe("modal.vue", () => {
it("Should display header text when HeaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["HeaderText"]));
});
it("Should display subheader text when SubheaderText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["SubheaderText"]));
});
it("Should insert image url when Image is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["Image"]));
});
it("Should display body text when BodyText is defined in the CMS", async () => {
// Act
const wrapper = shallowMount(Modal, {
mixins: [mockMixin],
props: {
cmsWidgetName: "test",
},
attachTo: document.body,
});
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent["BodyText"]));
});
});
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,133 @@
<template>
<!-- Modal -->
<div
class="modal fade modal-component"
v-on="{ 'hidden.bs.modal': resetButtonStyle }"
:id="this.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-5 pb-4">
<img :src="this.ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" />
<h5 class="mb-4" v-html="this.ModalHeadline"></h5>
<p class="fw-bold mb-2 subheader-text" v-html="this.ModalSubheadertext"></p>
<p class="mb-0" v-html="this.ModalBodyText"></p>
</div>
<div class="modal-footer px-5 py-4">
<buttonMain
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");
},
ModalSubheadertext() {
return this.getCmsContent(this.cmsWidgetName, "SubheaderText");
},
ModalBodyText() {
return this.getCmsContent(this.cmsWidgetName, "BodyText");
},
ModalImage() {
return this.getCmsContent(this.cmsWidgetName, "Image");
},
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,
.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;
}
}
&.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 {
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

@ -10,8 +10,9 @@
class="input-wrapper"
:class="[
includeSearchIcon ? 'has-search-icon' : '',
includeSelectIcon ? 'has-select-icon' : '',
]">
<input
<input
class="form-control"
v-model.trim="value"
v-maska="mask"
@ -36,8 +37,12 @@
@change="validationRules ? handleChange : () => {}"
@blur="validationRules ? handleChange : () => {}"
:maxlength="maxLength ? maxLength : '999'"
@focus="$emit('focus', $event.target.value)" />
@focus="$emit('focus', $event.target.value)"
:data-bs-toggle="includeSelectIcon ? 'modal' : ''"
:data-bs-target="'#' + this.cmsWidgetName" />
<button v-if="includeSearchIcon" type="submit" aria-label="Search button" />
<button v-if="includeSelectIcon" type="submit" data-bs-toggle="modal"
:data-bs-target="'#' + this.cmsWidgetName" aria-label="Select button" />
</div>
<div v-show="errorMessage" class="row my-2 form-test-error">
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
@ -76,6 +81,7 @@ export default {
questionAlignment: String, // Left or center. Left is default.
cornerStyle: String, // Rounded or square. Square is default.
includeSearchIcon: Boolean,
includeSelectIcon: Boolean,
min: String,
max: String,
disableAutoFill: Boolean
@ -229,7 +235,23 @@ input[type="date"]::-webkit-calendar-picker-indicator {
display: flex;
}
}
&.has-select-icon {
button[type="submit"] {
position: absolute;
top: 50%;
transform: translateY(-50%);
right: 1rem;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 8.89' xml:space='preserve'%3e%3cpath d='M8 8.89c-.24 0-.46-.09-.63-.26L.26 1.53a.901.901 0 0 1 0-1.27C.43.1.66 0 .9 0s.47.1.64.26L8 6.74 14.47.27c.17-.17.4-.27.64-.27s.47.1.63.27c.17.17.26.4.26.64s-.1.47-.27.63l-7.1 7.09a.86.86 0 0 1-.63.26z' fill='%231474a2'/%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: center;
background-color: transparent;
width: 1rem;
height: 100%;
border: 0px;
border-left: none;
display: flex;
}
}
}
input {
&.has-icon {

View file

@ -3,9 +3,6 @@
<div class="fade-on-route-transition">
<siteHeader cmsWidgetName="Header"/>
<siteSubHeader cmsWidgetName="SubHeader"/>
<p>Welcome Page Placeholder</p>
<br />
<div class="row mt-2">
<textboxQuestion
type="date"
@ -18,6 +15,19 @@
:min="'1972-12-01'"
/>
</div>
<div class="row mt-2 mb-4">
<div class="col">
<textboxQuestion
id="damageCauseField"
cmsWidgetName="DamageCauseQuestion"
v-model="damageCause"
placeholderText="Select an option"
includeSelectIcon
disableAutoFill
validationRules="damage-cause-required" />
</div>
</div>
<buttonQuestionModal cmsWidgetName="DamageCauseQuestion" />
</div>
</div>
</template>
@ -30,12 +40,14 @@
import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import buttonQuestionModal from "@/common-components/button-question-modal/button-question-modal";
//define validation rules
defineRule("loss-date-required", required(errorMessages.LOSS_DATE_REQUIRED));
defineRule("damage-cause-required", required(errorMessages.LOSS_CAUSE_REQUIRED));
export default ({
export default {
name: "welcome-page",
emits: ['update:modelValue'], // The component emits an event
props: {
@ -47,7 +59,6 @@
},
validationRules: String,
},
components: { siteHeader, siteSubHeader, textboxQuestion },
async beforeRouteEnter(to, from, next)
{
// Call APIs
@ -68,6 +79,12 @@
vm.setCmsContent(resultMap.cmsContent);
});
},
})
components: {
siteHeader,
siteSubHeader,
buttonQuestionModal,
textboxQuestion,
},
}
</script>

View file

@ -11,7 +11,7 @@
<span class="m-0">{{ this.buttonText }}</span>
<loader
class="ms-2"
v-if="isLoaderDisplayed"
v-if="isLoaderDisplayed && !suppressLoader"
v-bind:class="[this.loaderColor, this.loaderPosition]" />
</button>
</template>
@ -28,6 +28,7 @@ export default {
loaderColor: String,
loaderPosition: String,
isFloat: Boolean,
suppressLoader: Boolean,
},
data() {
return {