Merge branch 'develop' into feature/CSR-254

This commit is contained in:
Adam Caouette 2022-02-10 09:15:45 -05:00
commit 7deaffe68f
17 changed files with 410 additions and 331 deletions

View file

@ -9,29 +9,22 @@ describe("funnel-footer.vue", () => {
const r = {
test:"testing",
clientHeight: 10,
classList: {
add: jest.fn(() => ''),
remove: jest.fn()
},
offsetHeight: 24,
};
global.document.getElementById = jest.fn().mockImplementation(()=> {
global.document.querySelector = jest.fn().mockImplementation(()=> {
return r;
});
const wrapper = mount(funnelFooter, {
propsData: {
footer: true
},
});
// Assert
const link = wrapper.find("a");
console.log(wrapper.html());
// Expect
expect(link.attributes('class')).toContain("footer-link");
expect(global.document.getElementById).toBeCalled();
expect(r.classList.add).toBeCalledWith("justify-content-end");
expect(r.classList.remove).toBeCalledWith("justify-content-start");
expect(link.attributes('class')).toContain("footer");
});
@ -64,32 +57,4 @@ describe("funnel-footer.vue", () => {
expect(input.attributes("class")).toContain("btn-primary");
});
it("Should return class justify-content-end if paddingHeight < 80px", async () => {
global.document.getElementById = jest.fn().mockImplementation(()=> {
return {
test:"testing",
clientHeight: 10,
classList: {
add: jest.fn(),
remove: jest.fn()
}
}
});
// Act
const wrapper = mount(funnelFooter, {
propsData: {
footer: true
},
});
const infoBox = document.getElementById('infoBox');
// Assert
const stacked = wrapper.find("#stacked");
wrapper.vm.paddingHeight = 100;
// Expect
expect(stacked.attributes('class')).toContain("justify-content-end");
});
});

View file

@ -1,54 +1,41 @@
<template>
<div class="container-fluid g-2 footer">
<div class="row">
<div class="col-12 d-flex justify-content-center pb-2 fs-7">
&copy;&nbsp;<span id="years"></span>&nbsp;Safelite Group
<div class="footer pt-4">
<div class="container-fluid g-2">
<div class="row">
<div class="col-12 d-flex justify-content-center pb-2 fs-7">
&copy;&nbsp;{{new Date().getFullYear()}}&nbsp;Safelite Group
</div>
</div>
<div class="row" :style="`padding-bottom: ${paddingHeight}px`">
<div class="col d-flex justify-content-center justify-content-around text-center">
<textLink linkType="footer" text="Terms of use" href="https://www.safelite.com/terms-of-use" />
<textLink linkType="footer" text="Privacy policy" href="https://www.safelite.com/safelite-group-privacy-policy" />
<textLink linkType="footer" text="Do not sell my information" href="https://privacyportal-cdn.onetrust.com/dsarwebform/d3b95a93-e22e-4d4d-a806-482052406557/9e371601-eae3-4338-9430-b90b9036022b.html" />
</div>
</div>
</div>
<div class="row" :style="`padding-bottom: ${paddingHeight}px`">
<div class="col d-flex justify-content-center justify-content-around text-center">
<textLink
footer=true
text="Terms of use"
href="https://www.safelite.com/terms-of-use"
/>
<textLink
footer=true
text="Privacy policy"
href="https://www.safelite.com/safelite-group-privacy-policy"
/>
<textLink
footer=true
text="Do not sell my information"
href="https://privacyportal-cdn.onetrust.com/dsarwebform/d3b95a93-e22e-4d4d-a806-482052406557/9e371601-eae3-4338-9430-b90b9036022b.html"
/>
</div>
</div>
</div>
<div class="container-fluid fixed-bottom g-4 bg-light py-4" id="infoBox">
<div class="row d-flex flex-row-reverse align-items-center">
<div class="col d-flex justify-content-end" id="stacked">
<buttonMain
isPrimary
buttonText="Button test"
loaderColor="white"
sizeInRem="1"
:class="isDisabled && 'form-test-invalid'"
:aria-disabled="isDisabled"
/>
</div>
<div class="col">
<textLink
navigation=true
text="back"
/>
<div class="container-fluid fixed-bottom g-4 bg-light py-4" id="infoBox">
<div class="row d-flex flex-row-reverse align-items-center">
<div class="col-12 button-col d-flex justify-content-end" id="stacked">
<buttonMain
isPrimary
:buttonText="buttonText"
loaderColor="white"
sizeInRem="1"
:class="isDisabled && 'form-test-invalid'"
:aria-disabled="isDisabled"
@click-event="buttonClick"
/>
</div>
<div class="col-12 link-col py-1">
<textLink linkType="navigation" :text="backLink" @click-event="linkClick"/>
</div>
</div>
</div>
</div>
</template>
<script>
import textLink from "@/ux-components/text-link/text-link";
import buttonMain from "@/ux-components/button-main/button-main";
@ -63,47 +50,55 @@ export default {
},
data() {
return {
paddingHeight: 0
paddingHeight: 0,
backLink: "",
buttonText: "",
}
},
mounted() {
this.paddingHeight = document.getElementById("infoBox").offsetHeight;
this.paddingHeight = document.querySelector(".footer #infoBox").offsetHeight + 24;
this.$nextTick(() => {
window.addEventListener('resize', this.onResize);
})
setTimeout(function(){ // Give it a moment to set the date
document.getElementById('years').innerHTML += new Date().getFullYear();
}, 100);
this.checkHeight();
},
beforeUnmount() {
window.removeEventListener('resize', this.onResize);
},
methods: {
onResize() {
this.paddingHeight = document.getElementById("infoBox").offsetHeight;
this.checkHeight();
this.paddingHeight = document.querySelector(".footer #infoBox").offsetHeight;
},
initializeComponent(cmsContent) {
this.backLink = cmsContent.BackButtonText;
this.buttonText = cmsContent.ForwardButtonText;
},
buttonClick() {
this.$emit("forwardClicked");
},
linkClick() {
this.$emit("backClicked");
},
checkHeight() {
const parentHeight = document.getElementById('infoBox').clientHeight;
const wrapper2 = document.getElementById('stacked');
console.log(parentHeight);
console.log(wrapper2);
if (parentHeight > 80) {
wrapper2.classList.add("justify-content-start");
wrapper2.classList.remove("justify-content-end");
} else {
wrapper2.classList.add("justify-content-end");
wrapper2.classList.remove("justify-content-start");
}
}
}
};
</script>
<style lang="scss" scoped>
button {
min-width: 200px;
.footer {
display: flex;
margin-top: auto;
.button-col,
.link-col,
.btn-primary {
width: 100%;
}
@media only screen and (min-width: 340px) {
.button-col,
.link-col {
width: 50% !important;
}
.btn-primary {
width: auto;
}
}
}
</style>

View file

@ -35,12 +35,16 @@ export default {
},
props: {
hasBackButton: Boolean,
backButtonAccessibleText: String,
hasSubText: Boolean
backButtonAccessibleText: String
},
components: {
buttonBack,
},
computed: {
hasSubText(){
return this.subText.length > 0;
}
},
methods: {
clickEvent() {
this.$emit("click-event");

View file

@ -19,7 +19,6 @@ export default {
},
data() {
return {
vehicleImageSrc: '',
genericVehicleImage: '',
carUnmatchedVehicleIcon: '',
truckUnmatchedVehicleIcon: '',

View file

@ -5,20 +5,93 @@
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
</div>
</div>
<div class="row my-4">
<form>
<div class="row my-4">
<div class="col">
<label for="autocomplete">Street address</label>
<input ref="autocomplete"
id="autocomplete"
placeholder="Search"
class="form-control search-location"
type="text"
autocomplete="off" />
</div>
</div>
</form>
<form>
<div v-show="showAddressFields" class="row my-4">
<div class="col">
<label for="city">City</label>
<input ref="city"
id="city"
class="form-control"
type="text"
autocomplete="off" />
</div>
</div>
</form>
<form>
<div v-show="showAddressFields" class="row my-4">
<div class="col-6">
<label for="state">State</label>
<input ref="state"
id="state"
class="form-control"
type="text"
autocomplete="off" />
</div>
<div class="col-6">
<label for="zip">Zip</label>
<input ref="zip"
id="zip"
class="form-control"
type="text"
autocomplete="off" />
</div>
</div>
</form>
<div v-show="displayVerificationWarning" class="row my-4">
<div class="col">
<label for="autocomplete">Home address</label>
<input
ref="autocomplete"
id="autocomplete"
placeholder="Search"
class="form-control search-location"
onfocus="value = ''"
type="text"
/>
<span style="color: red;">An accurate match may not have have been located. Please verify your address before continuing</span>
</div>
</div>
<div v-show="displayNoMatchWarning" class="row my-4">
<div class="col">
<span style="color: red;">An accurate match could not be located. Please re-enter your street address before continuing</span>
</div>
</div>
<div class="row my-4">
<div class="col">
<label for="firstName">First name</label>
<input ref="firstName"
id="firstName"
class="form-control"
type="text"
autocomplete="off" />
</div>
</div>
<div class="row my-4">
<div class="col">
<label for="lastName">Last name</label>
<input ref="lastName"
id="lastName"
class="form-control"
type="text"
autocomplete="off" />
</div>
</div>
<div class="row my-4">
<div class="col">
<label for="email">Email address</label>
<input ref="email"
id="email"
class="form-control"
type="text"
autocomplete="off" />
</div>
</div>
</div>
</template>
<script>
@ -28,21 +101,29 @@ export default {
components: {
vehicleBanner,
},
data() {
return {
showAddressFields: false,
displayVerificationWarning: false,
displayNoMatchWarning: false
};
},
mounted() {
this.$refs.vehicleBanner.initializeComponent({
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
});
let addressField1 = document.getElementById("autocomplete");
let self = this;
//AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo - localhost:8080
//AIzaSyBji_2ApAj2vE_XNGyV-LaUEKrmuElpJ1w
this.$loadScript(
"https://maps.googleapis.com/maps/api/js?key=AIzaSyBji_2ApAj2vE_XNGyV-LaUEKrmuElpJ1w&libraries=places"
)
//let apiKey = "AIzaSyDptGCkOPgN2uWJOy4ou4M33phRD4MAoJo"; // localhost:8080
let apiKey = "AIzaSyBji_2ApAj2vE_XNGyV-LaUEKrmuElpJ1w"; // Dev and above
this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`)
.then(() => {
// Script is loaded, do something
// Script is loaded, initialize the autocomplete textbox
self.autocomplete = new window.google.maps.places.Autocomplete(
self.$refs.autocomplete,
{
@ -52,53 +133,101 @@ export default {
}
);
self.autocomplete.addListener("place_changed", () => {
let place = self.autocomplete.getPlace();
let address1 = "";
let postcode = "";
self.autocomplete.addListener('place_changed', fillInAddress);
addressField1.onblur = function() {
var hover = document.querySelector(".pac-container .pac-item:hover");
let input = document.getElementById("autocomplete");
// if an item has been clicked, do nothing, otherwise get first solution and use Geocoder to get the place
if (hover === null) {
var item = document.querySelector(".pac-container .pac-item");
if (item != null) {
var firstResult = item.textContent;
var geocoder = new window.google.maps.Geocoder();
geocoder.geocode({
address: firstResult
}, function (results, status) {
if (status === window.google.maps.GeocoderStatus.OK) {
fillInAddress(results[0]);
self.displayVerificationWarning = true;
self.displayNoMatchWarning = false;
}
});
}
else {
let city = document.querySelector("#city");
let state = document.querySelector("#state");
let zip = document.querySelector("#zip");
for (const component of place.address_components) {
const componentType = component.types[0];
city.value = "";
state.value = "";
zip.value = "";
switch (componentType) {
case "street_number": {
address1 = `${component.long_name} ${address1}`;
break;
}
self.displayVerificationWarning = false;
self.displayNoMatchWarning = true;
}
case "route": {
address1 += component.short_name;
break;
}
case "locality":
address1 += ", " + component.long_name;
break;
case "administrative_area_level_1": {
address1 += ", " + component.short_name;
break;
}
case "postal_code": {
postcode = `${component.long_name}${postcode}`;
break;
}
case "postal_code_suffix": {
postcode = `${postcode}-${component.long_name}`;
break;
}
}
};
let addressField1 = document.querySelector("#autocomplete");
addressField1.value = address1 + " " + postcode;
function fillInAddress(place) {
if (!place) {
place = self.autocomplete.getPlace();
}
});
if (place && place.address_components) {
let address1 = "";
let city = document.querySelector("#city");
let state = document.querySelector("#state");
let zip = document.querySelector("#zip");
self.showAddressFields = true;
for (const component of place.address_components) {
const componentType = component.types[0];
switch (componentType) {
case "street_number": {
address1 = `${component.long_name} ${address1}`;
break;
}
case "route": {
address1 += component.short_name;
break;
}
case "locality": {
city.value = component.long_name;
break;
}
case "administrative_area_level_1": {
state.value = component.short_name;
break;
}
case "postal_code": {
zip.value = component.long_name;
break;
}
}
addressField1.value = address1;
self.displayVerificationWarning = false;
self.displayNoMatchWarning = false;
}
}
else {
self.displayVerificationWarning = true;
self.displayNoMatchWarning = false;
}
}
})
.catch(() => {
// Failed to fetch script
console.log("Unable to load Google Places API script");
});
},
};
</script>

View file

@ -48,18 +48,19 @@
<div class="row">
<div class="col my-3">
<textLink
navigation=true
linkType=navigation
text="Navigation Link"
/><br><br>
<textLink
linkType="text"
text="Default Link"
/><br><br>
<textLink
textSmall=true
linkType=textSmall
text="Small Link"
/><br><br>
<textLink
footer=true
linkType=footer
text="Footer Link"
href="https://google.com"
/>
@ -673,19 +674,6 @@
/>
</div>
</div>
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Links</h4>
</div>
</div>
<div class="row">
<div class="col my-3">
<textLink navigation="true" text="Navigation Link" /><br /><br />
<textLink text="Default Link" /><br /><br />
<textLink textSmall="true" text="Small Link" /><br /><br />
<textLink footer="true" text="Footer Link" href="https://google.com" />
</div>
</div>
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Typography</h4>
@ -925,16 +913,6 @@
/>
</div>
</div>
<div class="row my-4">
<div class="col">
<h4 class="m-0 p-2 bg-light rounded">Funnel Footer</h4>
</div>
</div>
<div class="row my-2">
<div class="col">
<funnelFooter/>
</div>
</div>
</div>
</template>
@ -948,9 +926,7 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
import checkbox from "@/ux-components/checkbox/checkbox";
import buttonQuestion from "@/common-components/button-question/button-question";
import textLink from "@/ux-components/text-link/text-link";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
export default {
name: "App",
components: {
@ -963,9 +939,7 @@ export default {
listButtonHorizontal,
checkbox,
funnelHeader,
buttonQuestion,
textLink,
funnelFooter
textLink
},
data() {
return {

View file

@ -1,6 +1,7 @@
// Components
import vehicleDamage from "@/layouts/vehicle-damage/vehicle-damage.vue";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
@ -144,7 +145,7 @@ describe("vehicle-damage.vue", () => {
describe("vehicle-damage.vue", () => {
test("Call invalidation, ResetPartsAndDeps should be called", async () => {
//Arrange
const { wrapper } = setupMocks({});
@ -221,6 +222,10 @@ function setupMocks({
damageLocationQuestion.methods = {
initializeComponent: jest.fn(),
};
funnelFooter.methods = {
initializeComponent: jest.fn(),
}
const mountOptions = getMountOptions(mountOptionsMockData);
@ -252,5 +257,11 @@ function setupMocks({
damageLocationQuestionWrapper.vm.initializeComponent =
damageLocationQuestion.methods.initializeComponent;
const funnelFooterWrapper = wrapper.findComponent({
name: "funnelFooter",
});
funnelFooterWrapper.vm.initializeComponent = funnelFooter.methods.initializeComponent;
return { wrapper, apiPromise };
}

View file

@ -1,11 +1,8 @@
<template>
<div class="container-fluid shadow rounded-3 p-2 position-relative">
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader
ref="funnelSubHeader"
:hasSubText=true
/>
<funnelSubHeader ref="funnelSubHeader" />
<Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
@ -36,6 +33,7 @@
groupName="DriverSideReplaceOptionsQuestion"
/>
<funnel-footer
ref="funnelFooter"
:isDisabled="!meta.valid"
/>
@ -56,6 +54,7 @@
<script>
// Components
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
@ -63,8 +62,6 @@ import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-que
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
import alert from "@/ux-components/alert/alert";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import { Form } from 'vee-validate';
@ -118,6 +115,9 @@ export default {
vm.$refs.damageLocation.initializeComponent(
resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions
);
vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget
);
});
},
data(){

View file

@ -1,10 +1,10 @@
<template>
<div class="container-fluid shadow rounded-3 p-0 position-relative">
<div class="container-fluid shadow rounded-3 p-0 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<div class="select-car">
<div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
<funnelSubHeader
<funnelSubHeader
ref="funnelSubHeader"
:hasBackButton="true"
backButtonAccessibleText="Change Vehicle Year"

View file

@ -73,7 +73,7 @@ export default {
watch: {
selectedYear(year) {
this.$store.commit(this.storeMutations.UPDATE_YEAR, year);
this.$router.navigateAfterSave(
this.navigationScenarios.SELECTED_YEAR,

View file

@ -9,6 +9,11 @@ body {
&.container-shadow {
box-shadow: 0px 0px 6px 0px rgba(0,0,0,0.15); //Use instead of Bootstrap's helper
}
&.make-tall {
height: 100vh;
display: flex;
flex-direction: column;
}
}
.pointer {
cursor: pointer;

View file

@ -2,7 +2,7 @@
<button
:disabled="isDisabled"
:aria-disabled="isDisabled"
class="btn d-flex align-items-center py-3 px-4"
class="btn d-flex align-items-center py-3 px-2"
:class="[isPrimary ? 'btn-primary' : 'btn-secondary',isFloat ? 'float-end' : '']"
@click="clicked()"
>

View file

@ -1,34 +1,34 @@
<template>
<div
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
@mouseup="handleClick(value)"
<div
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
@mouseup="handleClick(value)"
@keyup.space="handleClick(value)"
>
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
v-model="checkValue"
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
v-model="checkValue"
@change="handleCheckChange"
/>
<label
tabindex="-1"
:for="buttonID"
:aria-labelledby="buttonID"
<label
tabindex="-1"
:for="buttonID"
:aria-labelledby="buttonID"
class="d-flex flex-column justify-content-center py-3 px-4"
>
<span
class="m-0"
<span
class="m-0"
:class="textPosition"
>
{{buttonLabel}}
</span>
<span
v-if="buttonLabelSubCopy"
class="m-0 small"
<span
v-if="buttonLabelSubCopy"
class="m-0 small"
:class="textPosition"
>
{{buttonLabelSubCopy}}
@ -36,10 +36,10 @@
<span v-if="screenReaderOnlyText" class="sr-only">
{{screenReaderOnlyText}}
</span>
<loader
v-if="isLoaderDisplayed && !isMultiSelect"
:style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}"
:class="[loaderColor, loaderPosition]"
<loader
v-if="isLoaderDisplayed && !isMultiSelect"
:style="{width: `${sizeInRem}rem`, height: `${sizeInRem}rem`}"
:class="[loaderColor, loaderPosition]"
/>
</label>
</div>
@ -151,8 +151,9 @@ export default {
border: 1px solid $gray-500;
border-radius: 0;
width: 100%;
color: $gray-600;
&:hover {
box-shadow: 0 0 0 4px $blue-100;
box-shadow: 0 0 0 4px $blue-300;
cursor: pointer;
z-index: 2;
}

View file

@ -1,40 +1,40 @@
<template>
<div
class="list-group list-button d-flex flex-column w-100 mb-2"
@mouseup="handleClick(value)"
<div
class="list-group list-button d-flex flex-column w-100 mb-2"
@mouseup="handleClick(value)"
@keyup.space="handleClick(value)"
>
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
v-model="checkValue"
@change="handleCheckChange"
>
<label
tabindex="-1"
:for="buttonID"
:aria-labelledby="buttonID"
<label
tabindex="-1"
:for="buttonID"
:aria-labelledby="buttonID"
class="d-flex flex-column justify-content-center py-3 px-4"
>
<span
class="m-0"
<span
class="m-0"
:class="textPosition"
>
{{ buttonLabel }}
</span>
<span
v-if="buttonLabelSubCopy"
class="m-0 small"
<span
v-if="buttonLabelSubCopy"
class="m-0 small"
:class="textPosition"
>
{{ buttonLabelSubCopy }}
</span>
<span
v-if="screenReaderOnlyText"
<span
v-if="screenReaderOnlyText"
class="sr-only"
>
{{ screenReaderOnlyText }}
@ -67,7 +67,7 @@ export default {
loaderColor: String,
loaderPosition: String,
sizeInRem: [Number,String],
value: {
value: {
// Field initial value
type: [String, Number],
default: "",
@ -132,21 +132,23 @@ export default {
input[type="checkbox"] {
position: static; //override bootstrap
height: 0;
}
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue inset;
}
&:focus + label {
box-shadow: 0 0 0 2.5px $blue inset;
}
&:checked + label {
color: $black;
font-weight: 500;
background: $blue-100;
box-shadow: 0 0 0 1px $blue;
}
&:checked + label p:first-child {
font-weight: 500;
opacity: 0;
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue inset;
}
&:focus + label {
box-shadow: 0 0 0 2.5px $blue inset;
}
&:checked + label {
color: $black;
font-weight: 500;
background: $blue-100;
box-shadow: 0 0 0 1px $blue;
}
&:checked + label p:first-child {
font-weight: 500;
}
}
}
label {
@ -160,7 +162,7 @@ export default {
&:hover {
@include media-breakpoint-up(sm) {
box-shadow: 0 0 0 4px $blue-100;
box-shadow: 0 0 0 4px $blue-300;
}
cursor: pointer;
}

View file

@ -4,45 +4,45 @@
class="list-card w-100 rounded-3 d-flex align-items-center h-100"
:class="isWide ? 'horizontal' : ''"
>
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
@click="handleChange(value)"
v-model="checkValue"
@change="handleCheckChange"
<input
:type="isMultiSelect ? 'checkbox' : 'radio'"
:id="buttonID"
:name="groupName"
:value="buttonID"
:aria-required="isRequired"
:data-focus-target="groupName"
@click="handleChange(value)"
v-model="checkValue"
@change="handleCheckChange"
/>
<label
:for="buttonID"
class="d-flex w-100 align-items-center px-2 h-100"
:class="getLabelClasses" tabindex="-1"
>
<img
:id="buttonImageId"
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
:src="buttonImage"
:alt="altText"
/>
<label
:for="buttonID"
class="d-flex w-100 align-items-center px-2 h-100"
:class="getLabelClasses" tabindex="-1"
<p
v-if="!isWide"
class="small order-3"
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
>
<img
:id="buttonImageId"
:class="!isWide ? 'order-1' : 'ms-auto order-3'"
:src="buttonImage"
:alt="altText"
/>
<p
v-if="!isWide"
class="small order-3"
:class="isMultiSelect ? 'm-0' : 'mt-2 mb-0'"
>
{{buttonLabel}}
{{buttonLabel}}
</p>
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4 sub-copy">
{{buttonLabelSubCopy}}
</p>
<div v-if="isWide" class="order-2">
<p class="m-0 small">{{ buttonLabel }}</p>
<p v-if="buttonLabelSubCopy" class="m-0 fs-7 sub-copy">
{{ buttonLabelSubCopy }}
</p>
<p v-if="buttonLabelSubCopy && !isWide" class="fs-7 m-0 order-4">
{{buttonLabelSubCopy}}
</p>
<div v-if="isWide" class="order-2">
<p class="m-0 small">{{ buttonLabel }}</p>
<p v-if="buttonLabelSubCopy" class="m-0 fs-7">
{{ buttonLabelSubCopy }}
</p>
</div>
</label>
</div>
</label>
</div>
</div>
</template>
@ -137,9 +137,10 @@ export default {
height: auto;
width: 6.5rem;
margin-bottom: 3rem;
max-width: 100%;
}
&:hover {
box-shadow: 0px 0px 0px 6px $blue-100;
box-shadow: 0px 0px 0px 4px $blue-300;
border: 1px solid transparent;
}
input[type="checkbox"],
@ -155,6 +156,9 @@ export default {
}
p {
text-align: center;
&.sub-copy {
color: $gray-550;
}
}
}
&:focus + label {
@ -179,6 +183,7 @@ export default {
border-radius: 2px;
order: 2;
flex-shrink: 0;
color: $gray-600;
}
+ label.checkboxTop::before {
margin: -1.25rem 0.5rem 0 0 !important;
@ -235,11 +240,15 @@ export default {
}
+ label {
min-height: 48px;
color: $gray-600;
img {
margin-bottom: 0;
}
p {
text-align: left;
&.sub-copy {
color: $gray-550;
}
}
}
}

View file

@ -3,11 +3,12 @@ import textLink from "./text-link";
import { nextTick } from "vue";
describe("text-link.vue", () => {
it("Should return text-link type is navigation if prop navigation is true", async () => {
it("Should return class navigation-link", async () => {
// Act
const wrapper = shallowMount(textLink, {
propsData: {
navigation: true,
linkType: "navigation",
},
});
@ -18,11 +19,11 @@ describe("text-link.vue", () => {
expect(paragraph.attributes("class")).toContain("navigation-link");
});
it("Should return text-link type is footer if prop footer is true", async () => {
it("Should return class footer", async () => {
// Act
const wrapper = shallowMount(textLink, {
propsData: {
footer: true,
linkType: "footer",
},
});
@ -30,14 +31,14 @@ describe("text-link.vue", () => {
const paragraph = wrapper.find("a");
// Expect
expect(paragraph.attributes("class")).toContain("footer-link");
expect(paragraph.attributes("class")).toContain("footer");
});
it("Should return text-link type is textSmall if prop textSmall is true", async () => {
it("Should return class text-small", async () => {
// Act
const wrapper = shallowMount(textLink, {
propsData: {
textSmall: true,
linkType: "textSmall",
},
});
@ -48,18 +49,4 @@ describe("text-link.vue", () => {
expect(paragraph.attributes("class")).toContain("small");
});
it("Should return text for href", async () => {
// Act
const wrapper = shallowMount(textLink, {
propsData: {
text: "This is link text",
},
});
// Assert
const paragraph = wrapper.find("a");
// Expect
expect(paragraph.text()).toEqual("This is link text");
});
});

View file

@ -1,17 +1,15 @@
<template>
<a v-if="navigation" @click="handleClick" class="navigation-link" :href="href">{{text}}</a>
<a v-else-if="footer" @click="handleClick" class="footer-link" :href="href" target="_blank">{{text}}</a>
<a v-else-if="textSmall" @click="handleClick" class="small" :href="href">{{text}}</a>
<a v-else @click="handleClick" :href="href">{{text}}</a>
<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 === 'text'" @click="handleClick" :href="href">{{text}}</a>
</template>
<script>
export default {
name: "textLink",
props: {
navigation: Boolean,
footer: Boolean,
textSmall: Boolean,
linkType: String,
text: String,
href: {
type: String,