commit
This commit is contained in:
parent
a4ef1ace14
commit
3df7c33845
6 changed files with 351 additions and 8 deletions
|
|
@ -0,0 +1,51 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import menuModal from "./menu-modal";
|
||||
|
||||
describe("menu-modal.vue", () => {
|
||||
it("Should return text Footer Navigation", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(menuModal);
|
||||
|
||||
// Assert
|
||||
const footerModalLabel = wrapper.find("h5");
|
||||
|
||||
// Expect
|
||||
expect(footerModalLabel.text()).toContain("Footer Navigation");
|
||||
});
|
||||
|
||||
it("Should return footer text as Safelite Group", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(menuModal);
|
||||
|
||||
// Assert
|
||||
const modalFooter = wrapper.find("div.modal-footer");
|
||||
|
||||
// Expect
|
||||
expect(modalFooter.text()).toContain("Safelite Group");
|
||||
});
|
||||
|
||||
it("Should return Terms of use text link text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(menuModal);
|
||||
|
||||
// Expect
|
||||
expect(wrapper.html()).toContain("Terms of use");
|
||||
});
|
||||
|
||||
it("Should return Privacy policy text link text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(menuModal);
|
||||
|
||||
// Expect
|
||||
expect(wrapper.html()).toContain("Privacy policy");
|
||||
});
|
||||
|
||||
it("Should returnDo not sell my information text link text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(menuModal);
|
||||
|
||||
// Expect
|
||||
expect(wrapper.html()).toContain("Do not sell my information");
|
||||
});
|
||||
|
||||
});
|
||||
170
src/common-components/site-header/menu-modal/menu-modal.vue
Normal file
170
src/common-components/site-header/menu-modal/menu-modal.vue
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<div class="menu-modal-container">
|
||||
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" data-bs-toggle="modal" data-bs-target="#footerModal" aria-label="Hamburger Menu (modal window)">
|
||||
<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" v-on="{ 'show.bs.modal' : show, 'hide.bs.modal' : hide }" :style="`height: calc(100% - ${currentFooterAndHeaderHeight}px);`">
|
||||
<div class="menu-modal-container">
|
||||
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" data-bs-toggle="modal" data-bs-target="#footerModal" aria-label="Hamburger Menu (modal window)">
|
||||
<div class="bar1"></div>
|
||||
<div class="bar2"></div>
|
||||
<div class="bar3"></div>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
<div class="modal-body d-flex flex-column">
|
||||
<textLink linkType="navigation" text="Terms of use" href="https://www.safelite.com/terms-of-use" target="_blank" />
|
||||
<textLink linkType="navigation" text="Privacy policy" href="https://www.safelite.com/safelite-group-privacy-policy" target="_blank" />
|
||||
<textLink linkType="navigation" text="Do not sell my information" href="https://privacyportal-cdn.onetrust.com/dsarwebform/d3b95a93-e22e-4d4d-a806-482052406557/9e371601-eae3-4338-9430-b90b9036022b.html" target="_blank" />
|
||||
</div>
|
||||
<div class="modal-footer d-flex justify-content-start">
|
||||
© {{new Date().getFullYear()}} Safelite Group
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import textLink from "@/ux-components/text-link/text-link";
|
||||
export default {
|
||||
name: 'menuModal',
|
||||
data() {
|
||||
return {
|
||||
isActive: false,
|
||||
currentFooterAndHeaderHeight: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
this.currentFooterAndHeaderHeight = this.getFooterInfoBoxHeight() + 72;
|
||||
this.isActive = true;
|
||||
document.querySelector('.page-container-grouped-styles').scrollTo({
|
||||
top: 0, behavior: 'smooth'
|
||||
});
|
||||
},
|
||||
hide() {
|
||||
var self = this;
|
||||
self.isActive = false;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
textLink,
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.menu-modal-container {
|
||||
position: absolute;
|
||||
padding: 1.47rem 1rem 1.47rem 1.47rem;
|
||||
right: 0;
|
||||
button {
|
||||
border: none;
|
||||
&.menu-button {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 8px 0 rgba(0,0,0,0.2);
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;//Required to prevent 'squish' on iPhone
|
||||
z-index: 1050;
|
||||
.bar1,
|
||||
.bar2,
|
||||
.bar3 {
|
||||
width: 14px;
|
||||
height: 2px;
|
||||
background-color: $blue;
|
||||
margin: 1px 0;
|
||||
transition: 0.25s;
|
||||
}
|
||||
&.active .bar1 {
|
||||
transform: rotate(-45deg) translate(-3px, 3px);
|
||||
}
|
||||
&.active .bar2 {opacity: 0;}
|
||||
&.active .bar3 {
|
||||
transform: rotate(45deg) translate(-3px, -3px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.modal {
|
||||
&.menu-modal {
|
||||
max-width: 576px;
|
||||
left: auto;
|
||||
height: calc(100% - 72px);
|
||||
top: 72px;
|
||||
border-top: 1px solid $gray-300;
|
||||
overflow-x: visible;
|
||||
overflow-y: visible;
|
||||
.modal-body {
|
||||
padding: 2rem;
|
||||
}
|
||||
.modal-fullscreen {
|
||||
width: 100vw;
|
||||
max-width: 576px;
|
||||
}
|
||||
.navigation-link {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.modal-footer {
|
||||
border-top: none;
|
||||
padding: 2rem;
|
||||
}
|
||||
.menu-modal-container {
|
||||
position: absolute;
|
||||
padding: 1.47rem 1rem 1.47rem 1.47rem;
|
||||
right: 0;
|
||||
top: -4.5rem;
|
||||
button {
|
||||
border: none;
|
||||
&.menu-button {
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
border-radius: 50%;
|
||||
box-shadow: none;
|
||||
background-color: $white;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0;//Required to prevent 'squish' on iPhone
|
||||
z-index: 1056;
|
||||
.bar1,
|
||||
.bar2,
|
||||
.bar3 {
|
||||
width: 14px;
|
||||
height: 2px;
|
||||
background-color: $blue;
|
||||
margin: 1px 0;
|
||||
transition: 0.25s;
|
||||
}
|
||||
&.active .bar1 {
|
||||
transform: rotate(-45deg) translate(-3px, 3px);
|
||||
}
|
||||
&.active .bar2 {opacity: 0;}
|
||||
&.active .bar3 {
|
||||
transform: rotate(45deg) translate(-3px, -3px);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//.modal-backdrop styles are in common-styles.scss
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
<template>
|
||||
<div class="headerContainer">
|
||||
<img id="siteHeaderImage" :src="imageSrc" :alt="altText" />
|
||||
<div class="site-header d-flex justify-content-center align-items-center flex-column">
|
||||
<img id="siteHeaderImage" class="img-fluid" :src="imageSrc" :alt="altText" />
|
||||
<menuModal/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import menuModal from "@/common-components/site-header/menu-modal/menu-modal";
|
||||
export default({
|
||||
name: "siteHeader",
|
||||
props: {
|
||||
|
|
@ -19,16 +21,17 @@
|
|||
{
|
||||
return this.getCmsContent(this.cmsWidgetName, "AltText")
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
menuModal
|
||||
},
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.headerContainer{
|
||||
width: 100%;
|
||||
margin: 16px auto;
|
||||
text-align: center
|
||||
}
|
||||
.site-header {
|
||||
padding: 0.97rem 0;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
@ -19,6 +19,10 @@ export default {
|
|||
getCmsContent(widgetName, fieldName) {
|
||||
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
|
||||
},
|
||||
getFooterInfoBoxHeight() {
|
||||
const footerInfoBox = document.querySelector(".footer#infoBox");
|
||||
return footerInfoBox ? footerInfoBox.offsetHeight : 0;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
// store will be accessible globally as its id + 'Store'
|
||||
|
|
|
|||
49
src/ux-components/text-link/text-link.spec.js
Normal file
49
src/ux-components/text-link/text-link.spec.js
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import textLink from "./text-link";
|
||||
|
||||
describe("text-link.vue", () => {
|
||||
it("Should return class navigation-link", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(textLink, {
|
||||
propsData: {
|
||||
linkType: "navigation",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("a");
|
||||
|
||||
// Expect
|
||||
expect(paragraph.attributes("class")).toContain("navigation-link");
|
||||
});
|
||||
|
||||
it("Should return class footer", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(textLink, {
|
||||
propsData: {
|
||||
linkType: "footer",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("a");
|
||||
|
||||
// Expect
|
||||
expect(paragraph.attributes("class")).toContain("footer");
|
||||
});
|
||||
|
||||
it("Should return class text-small", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(textLink, {
|
||||
propsData: {
|
||||
linkType: "textSmall",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("a");
|
||||
|
||||
// Expect
|
||||
expect(paragraph.attributes("class")).toContain("small");
|
||||
});
|
||||
});
|
||||
66
src/ux-components/text-link/text-link.vue
Normal file
66
src/ux-components/text-link/text-link.vue
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<template>
|
||||
<a v-if="linkType === 'navigation'" @click="handleClick" class="navigation-link" :href="href">{{text}}</a>
|
||||
<a v-else-if="linkType === 'footer'" @click="handleClick" class="footer-link" :href="href" target="_blank">{{text}}</a>
|
||||
<a v-else-if="linkType === 'textSmall'" @click="handleClick" class="small" :href="href">{{text}}</a>
|
||||
<a v-else-if="linkType === 'dashedUnderline'" @click="handleClick" class="dashed-underline" :href="href">{{text}}</a>
|
||||
<a v-else-if="linkType === 'text'" @click="handleClick" :href="href">{{text}}</a>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "textLink",
|
||||
props: {
|
||||
linkType: String,
|
||||
text: String,
|
||||
href: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleClick(event) {
|
||||
this.$emit("click-event");
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
a {
|
||||
display: inline-flex !important;
|
||||
color: $blue;
|
||||
text-underline-offset: 0.5em;
|
||||
line-height: 26px;
|
||||
padding: 0 0 4px 0;
|
||||
font-weight: 500;
|
||||
&:hover {
|
||||
color: $blue-700;
|
||||
}
|
||||
&.small {
|
||||
line-height: 24px;
|
||||
&:hover {
|
||||
color: $blue-700;
|
||||
}
|
||||
}
|
||||
&.navigation-link {
|
||||
color: $black;
|
||||
line-height: 26px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&.footer-link {
|
||||
color: $gray-600;
|
||||
text-decoration: none;
|
||||
line-height: 20px;
|
||||
padding: 0 0 2px 0;
|
||||
font-weight: 400;
|
||||
font-size: 0.75rem;
|
||||
&:hover {
|
||||
padding: 0 0 2px 0;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
&.dashed-underline {
|
||||
text-decoration: underline dashed 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in a new issue