Merge pull request #368 from Safelite/bugfix/CSR-447

Bugfix/CSR-447
This commit is contained in:
AdamCaouetteSafelite 2022-04-26 16:41:07 -04:00 committed by GitHub
commit a16416be69
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 460 additions and 113 deletions

View file

@ -1,6 +1,9 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import buttonQuestion from "@/common-components/button-question/button-question"; import buttonQuestion from "@/common-components/button-question/button-question";
import { nextTick } from "vue"; import { nextTick } from "vue";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import store from "@/store";
jest.mock("@/store",()=>{return{};},{virtual:true});
describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => {
it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => { it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => {
@ -45,6 +48,65 @@ describe("buttonQuestion.vue", () => {
}); });
}); });
describe("buttonQuestion.vue", () => {
it("Fieldset classes should contain ui-radio if button type is radio", () => {
// Act
const wrapper = shallowMount(buttonQuestion, {
propsData: {
buttonType: "radio",
}
});
// Assert
const Div = wrapper.find('fieldset div');
expect(Div.classes()).toContain("ui-radio");
});
});
// testing a computed property
describe("buttonQuestion.vue", () => {
it("getColLength should return '12' if prop isWide is set to true", () => {
// Act
const localThis = { isWide: true }
expect(buttonQuestion.computed.getColLength.call(localThis)).toBe("12");
});
});
describe("buttonQuestion.vue", () => {
it("getColLength should return '' if prop isWide is set to false", () => {
// Act
const localThis = {
isWide: false,
answers: ['a', 'b']
}
expect(buttonQuestion.computed.getColLength.call(localThis)).toBe("");
});
});
describe("buttonQuestion.vue", () => {
it("Should return answer.Text if prop useTextForValue is true", async () => {
// Act
const localThis = { useTextForValue: true };
const answer = { 'Name': 'testName', 'Text': 'testText' };
// Assert
expect(buttonQuestion.methods.getValues.call(localThis, answer)).toBe('testText');
});
});
describe("buttonQuestion.vue", () => {
it("Should return answer.Name if prop useTextForValue is false and answer.Name exists", async () => {
// Act
const localThis = { useTextForValue: false };
const answer = { 'Name': 'testName', 'Text': 'testText' };
// Assert
expect(buttonQuestion.methods.getValues.call(localThis, answer)).toBe('testName');
});
});
describe("buttonQuestion.vue", () => { describe("buttonQuestion.vue", () => {
it("Should trigger event modelValue change to new value on when radio button selected", async () => { it("Should trigger event modelValue change to new value on when radio button selected", async () => {
// Act // Act
@ -76,3 +138,54 @@ describe("buttonQuestion.vue", () => {
}); });
}); });
describe("buttonQuestion.vue", () => {
it("Should add a value to this.selectedValues if prop isMultiSelect is true, checkValue is true and this.selectedValues already exists", () => {
// Act
const wrapper = shallowMount(buttonQuestion, {
propsData: {
isMultiSelect: true,
modelValue: [ 'a', 'b' ]
}
});
const val = { checkValue: true, value: "2021", }
wrapper.vm.handleCheckedChanged(val);
// Assert
expect(wrapper.vm.selectedValues).toEqual(["a", "b", "2021"]);
});
});
describe("buttonQuestion.vue", () => {
it("Should remove a value to this.selectedValues if prop isMultiSelect is true, checkValue is false and this.selectedValues already exists", () => {
// Act
const wrapper = shallowMount(buttonQuestion, {
propsData: {
isMultiSelect: true,
modelValue: [ 'a', 'b' ]
}
});
const val = { checkValue: false, value: "a", }
wrapper.vm.handleCheckedChanged(val);
// Assert
expect(wrapper.vm.selectedValues).toEqual(["b"]);
});
});
describe("buttonQuestion.vue", () => {
it("Should do nothing to this.selectedValues if this.selectedValues is not an array", () => {
// Act
const wrapper = shallowMount(buttonQuestion, {
propsData: {
isMultiSelect: true,
modelValue: 'a',
}
});
const val = { checkValue: true, value: "c", }
wrapper.vm.handleCheckedChanged(val);
// Assert
expect(wrapper.vm.selectedValues).toEqual("a");
});
});

View file

@ -5,7 +5,7 @@
selectingInitiatesLoad selectingInitiatesLoad
:questionText="questionText" :questionText="questionText"
:answers="makes" :answers="makes"
groupName="Choose Vehicle Make" groupName="ChooseVehicleMake"
textPosition="text-start" textPosition="text-start"
v-model="selectedValueAsArray" v-model="selectedValueAsArray"
isRequired=true isRequired=true

View file

@ -5,7 +5,7 @@
selectingInitiatesLoad selectingInitiatesLoad
:questionText="questionText" :questionText="questionText"
:answers="models" :answers="models"
groupName="Choose Vehicle Model" groupName="ChooseVehicleModel"
textPosition="text-start" textPosition="text-start"
v-model="selectedValueAsArray" v-model="selectedValueAsArray"
isRequired=true isRequired=true

View file

@ -5,7 +5,7 @@
selectingInitiatesLoad selectingInitiatesLoad
:questionText="questionText" :questionText="questionText"
:answers="styles" :answers="styles"
groupName="Choose Vehicle Style" groupName="ChooseVehicleStyle"
textPosition="text-start" textPosition="text-start"
v-model="selectedValueAsArray" v-model="selectedValueAsArray"
isRequired=true isRequired=true

View file

@ -5,7 +5,7 @@
selectingInitiatesLoad selectingInitiatesLoad
:questionText="questionText" :questionText="questionText"
:answers="years" :answers="years"
groupName="Choose Vehicle Year" groupName="ChooseVehicleYear"
textPosition="text-start" textPosition="text-start"
v-model="selectedValueAsArray" v-model="selectedValueAsArray"
isRequired=true isRequired=true

View file

@ -75,7 +75,7 @@ html {
} }
input:checked:focus { input:checked:focus {
+ label { + label {
box-shadow: 0 0 0 1px $blue; box-shadow: 0 0 0 2.5px $blue;
border-radius: .5rem; border-radius: .5rem;
} }
} }

View file

@ -100,7 +100,7 @@ describe("list-button-horizontal.vue", () => {
const label = wrapper.find("label"); const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
@ -123,7 +123,7 @@ describe("list-button-horizontal.vue", () => {
const label = wrapper.find("label"); const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
@ -146,7 +146,7 @@ describe("list-button-horizontal.vue", () => {
const label = wrapper.find("label"); const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
@ -195,4 +195,54 @@ describe("list-button-horizontal.vue", () => {
// Assert // Assert
expect(wrapper.componentVM.checkValue).toEqual("Car-Front"); expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
}); });
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
// Act
const wrapper = shallowMount(listButtonHorizontal, {
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.handleInputChange();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should do nothing if isMultiSelect is true and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listButtonHorizontal, {
propsData: {
isMultiSelect: true,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleKeyupArrow).toHaveReturned;
});
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listButtonHorizontal, {
propsData: {
selectingInitiatesLoad: false,
isMultiSelect: false,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
}); });

View file

@ -2,8 +2,11 @@
<div <div
class="list-group list-button-horizontal d-flex flex-column w-100 mb-2" class="list-group list-button-horizontal d-flex flex-column w-100 mb-2"
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']" :class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
@mouseup="handleClick(value)" @keyup.space="triggerButton()"
@keyup.space="handleClick(value)" @keyup.up="handleKeyupArrow()"
@keyup.down="handleKeyupArrow()"
@keyup.left="handleKeyupArrow()"
@keyup.right="handleKeyupArrow()"
> >
<input <input
:type="isMultiSelect ? 'checkbox' : 'radio'" :type="isMultiSelect ? 'checkbox' : 'radio'"
@ -12,13 +15,14 @@
:value="value" :value="value"
:aria-required="isRequired" :aria-required="isRequired"
v-model="checkValue" v-model="checkValue"
@change="!selectingInitiatesLoad ? handleCheckChange() : ''" @change="handleInputChange()"
/> />
<label <label
tabindex="-1" tabindex="-1"
:for="buttonID" :for="buttonID"
:aria-labelledby="buttonID" :aria-labelledby="buttonID"
class="d-flex flex-column justify-content-center py-3 px-4" class="d-flex flex-column justify-content-center py-3 px-4"
@mouseup="triggerButton()"
> >
<span <span
class="m-0" class="m-0"
@ -31,13 +35,16 @@
class="m-0 small" class="m-0 small"
:class="textPosition" :class="textPosition"
> >
{{buttonLabelSubCopy}} {{ buttonLabelSubCopy }}
</span> </span>
<span v-if="screenReaderOnlyText" class="sr-only"> <span
{{screenReaderOnlyText}} v-if="screenReaderOnlyText"
class="sr-only"
>
{{ screenReaderOnlyText }}
</span> </span>
<loader <loader
v-if="isLoaderDisplayed && !isMultiSelect" v-if="isLoaderDisplayed && selectingInitiatesLoad"
:class="[loaderColor, loaderPosition]" :class="[loaderColor, loaderPosition]"
/> />
</label> </label>
@ -77,27 +84,47 @@ export default {
checkValue: Boolean, checkValue: Boolean,
}; };
}, },
created(){ created() {
if(Array.isArray(this.selectedValues)){ if (Array.isArray(this.selectedValues)) {
this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0]; this.checkValue = this.isMultiSelect
? this.selectedValues.includes(this.value)
: this.selectedValues[0];
} }
}, },
methods: { methods: {
displayLoader() { displayLoader() {
this.isLoaderDisplayed = true; this.isLoaderDisplayed = true;
}, },
handleClick(value) { handleInputChange() {
if(!this.selectingInitiatesLoad) {
this.handleCheckChange();
}
},
handleKeyupArrow() {
if (this.isMultiSelect) {
return; // Prevent arrow keys from doing anything if element is a checkbox
}
if(!this.selectingInitiatesLoad) {
this.handleCheckChange();
}
this.handleChange(this.value);
},
triggerButton() {
if(this.selectingInitiatesLoad) { if(this.selectingInitiatesLoad) {
this.displayLoader(); this.displayLoader();
this.handleCheckChange(); this.handleCheckChange();
} }
this.handleChange(value); this.handleChange(this.value);
}, },
handleCheckChange(newValue, oldValue){ handleCheckChange() {
const isInitialization = typeof(oldValue) === 'function'; const emitEvent = {
if (!isInitialization) { checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question
this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() }); value: this.value.toString(),
} buttonId: this.buttonID && this.buttonID.toString(),
};
this.$emit("isCheckedChanged", emitEvent);
this.$emit("update:modelValue", emitEvent);
} }
}, },
components: { components: {
@ -105,6 +132,7 @@ export default {
}, },
setup(props) { setup(props) {
const inputType = props.isMultiSelect ? "checkbox" : "radio"; const inputType = props.isMultiSelect ? "checkbox" : "radio";
const fieldOptions = { const fieldOptions = {
type: inputType, type: inputType,
checkedValue: props.value, checkedValue: props.value,
@ -118,13 +146,11 @@ export default {
} }
const { const {
checked,
handleChange, handleChange,
errors, errors,
} = useField(props.groupName, props.validationRules, fieldOptions); } = useField(props.groupName, props.validationRules, fieldOptions);
return { return {
checked,
handleChange, handleChange,
errors, errors,
fieldOptions, // only need to expose this for unit test purposes fieldOptions, // only need to expose this for unit test purposes
@ -137,10 +163,11 @@ export default {
.list-button-horizontal { .list-button-horizontal {
input[type="radio"], input[type="radio"],
input[type="checkbox"] { input[type="checkbox"] {
position: absolute;
height: 0;
opacity: 0; opacity: 0;
width: 0; width: 0;
height: 0;
position: absolute;
&:focus-visible + label { &:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue; box-shadow: 0 0 0 2.5px $blue;
z-index: 2; z-index: 2;

View file

@ -96,11 +96,8 @@ describe("list-button.vue", () => {
}); });
// Assert // Assert
const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
@ -119,10 +116,8 @@ describe("list-button.vue", () => {
}); });
// Assert // Assert
const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
const loader = wrapper.find("loader-stub"); const loader = wrapper.find("loader-stub");
@ -140,11 +135,8 @@ describe("list-button.vue", () => {
}); });
// Assert // Assert
const label = wrapper.find("label");
wrapper.vm.handleCheckChange = jest.fn(); wrapper.vm.handleCheckChange = jest.fn();
wrapper.vm.handleClick(); wrapper.vm.triggerButton();
await nextTick(); await nextTick();
@ -197,4 +189,53 @@ describe("list-button.vue", () => {
expect(wrapper.componentVM.checkValue).toEqual("Car-Front"); expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
}); });
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
// Act
const wrapper = shallowMount(listButton, {
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.handleInputChange();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should do nothing if isMultiSelect is true and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listButton, {
propsData: {
isMultiSelect: true,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleKeyupArrow).toHaveReturned;
});
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listButton, {
propsData: {
selectingInitiatesLoad: false,
isMultiSelect: false,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
}); });

View file

@ -2,8 +2,11 @@
<div <div
class="list-group list-button d-flex flex-column w-100 mb-2" class="list-group list-button d-flex flex-column w-100 mb-2"
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']" :class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
@mouseup="handleClick(value)" @keyup.space="triggerButton()"
@keyup.space="handleClick(value)" @keyup.up="handleKeyupArrow()"
@keyup.down="handleKeyupArrow()"
@keyup.left="handleKeyupArrow()"
@keyup.right="handleKeyupArrow()"
> >
<input <input
:type="isMultiSelect ? 'checkbox' : 'radio'" :type="isMultiSelect ? 'checkbox' : 'radio'"
@ -12,13 +15,14 @@
:value="value" :value="value"
:aria-required="isRequired" :aria-required="isRequired"
v-model="checkValue" v-model="checkValue"
@change="!selectingInitiatesLoad ? handleCheckChange() : ''" @change="handleInputChange()"
> >
<label <label
tabindex="-1" tabindex="-1"
:for="buttonID" :for="buttonID"
:aria-labelledby="buttonID" :aria-labelledby="buttonID"
class="d-flex flex-column justify-content-center py-3 px-4" class="d-flex flex-column justify-content-center py-3 px-4"
@mouseup="triggerButton()"
> >
<span <span
class="m-0" class="m-0"
@ -40,7 +44,7 @@
{{ screenReaderOnlyText }} {{ screenReaderOnlyText }}
</span> </span>
<loader <loader
v-if="isLoaderDisplayed && !isMultiSelect" v-if="isLoaderDisplayed && selectingInitiatesLoad"
:class="[this.loaderColor, this.loaderPosition]" :class="[this.loaderColor, this.loaderPosition]"
/> />
</label> </label>
@ -80,33 +84,47 @@ export default {
checkValue: Boolean, checkValue: Boolean,
}; };
}, },
created(){ created() {
if(Array.isArray(this.selectedValues)){ if (Array.isArray(this.selectedValues)) {
this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0]; this.checkValue = this.isMultiSelect
? this.selectedValues.includes(this.value)
: this.selectedValues[0];
} }
}, },
methods: { methods: {
displayLoader() { displayLoader() {
this.isLoaderDisplayed = true; this.isLoaderDisplayed = true;
}, },
handleClick(value) { handleInputChange() {
if(!this.selectingInitiatesLoad) {
this.handleCheckChange();
}
},
handleKeyupArrow() {
if (this.isMultiSelect) {
return; // Prevent arrow keys from doing anything if element is a checkbox
}
if(!this.selectingInitiatesLoad) {
this.handleCheckChange();
}
this.handleChange(this.value);
},
triggerButton() {
if(this.selectingInitiatesLoad) { if(this.selectingInitiatesLoad) {
this.displayLoader(); this.displayLoader();
this.handleCheckChange(); this.handleCheckChange();
} }
this.handleChange(value); this.handleChange(this.value);
}, },
handleCheckChange(value, oldValue){ handleCheckChange() {
const isInitialization = typeof(oldValue) === 'function';
if (!isInitialization) {
const emitEvent = { const emitEvent = {
checkValue: this.checkValue, checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question
value: this.value.toString(), value: this.value.toString(),
buttonId: this.buttonID.toString(), buttonId: this.buttonID && this.buttonID.toString(),
}; };
this.$emit('isCheckedChanged', emitEvent); this.$emit("isCheckedChanged", emitEvent);
this.$emit("update:modelValue", emitEvent); this.$emit("update:modelValue", emitEvent);
}
}, },
}, },
components: { components: {
@ -128,13 +146,11 @@ export default {
} }
const { const {
checked,
handleChange, handleChange,
errors, errors,
} = useField(props.groupName, props.validationRules, fieldOptions); } = useField(props.groupName, props.validationRules, fieldOptions);
return { return {
checked,
handleChange, handleChange,
errors, errors,
fieldOptions, // only need to expose this for unit test purposes fieldOptions, // only need to expose this for unit test purposes
@ -154,10 +170,10 @@ export default {
opacity: 0; opacity: 0;
&:focus-visible + label { &:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue inset; box-shadow: 0 0 0 2.5px $blue;
} }
&:focus + label { &:focus + label {
box-shadow: 0 0 0 2.5px $blue inset; box-shadow: 0 0 0 2.5px $blue;
} }
&:checked + label { &:checked + label {
color: $black; color: $black;
@ -165,6 +181,9 @@ export default {
background: $blue-100; background: $blue-100;
box-shadow: 0 0 0 1px $blue; box-shadow: 0 0 0 1px $blue;
} }
&:checked:focus + label {
box-shadow: 0 0 0 2.5px $blue;
}
&:checked + label p, &:checked + label p,
&:checked + label span { &:checked + label span {
font-weight: 500; font-weight: 500;

View file

@ -1,5 +1,6 @@
import { shallowMount } from "@vue/test-utils"; import { shallowMount } from "@vue/test-utils";
import listCard from "./list-card"; import listCard from "./list-card";
import { nextTick } from "vue";
describe("list-card.vue", () => { describe("list-card.vue", () => {
it("Should return input type checkbox if isMultiSelect is true", async () => { it("Should return input type checkbox if isMultiSelect is true", async () => {
@ -18,7 +19,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const input = wrapper.find("input"); const input = wrapper.find("input");
expect(input.attributes().type).toEqual("checkbox"); expect(input.attributes().type).toEqual("checkbox");
}); });
@ -38,7 +38,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const paragraph = wrapper.find("p"); const paragraph = wrapper.find("p");
expect(paragraph.text()).toEqual("Windshield"); expect(paragraph.text()).toEqual("Windshield");
}); });
@ -59,7 +58,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const paragraph = wrapper.find("p:nth-of-type(2)"); const paragraph = wrapper.find("p:nth-of-type(2)");
expect(paragraph.text()).toEqual("Test"); expect(paragraph.text()).toEqual("Test");
}); });
@ -80,7 +78,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const label = wrapper.find("label"); const label = wrapper.find("label");
expect(label.attributes().for).toEqual("List Card Checkbox"); expect(label.attributes().for).toEqual("List Card Checkbox");
}); });
@ -101,7 +98,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const input = wrapper.find("input"); const input = wrapper.find("input");
expect(input.attributes().name).toEqual("radio 1"); expect(input.attributes().name).toEqual("radio 1");
}); });
@ -122,7 +118,6 @@ describe("list-card.vue", () => {
// Assert // Assert
const input = wrapper.find("input"); const input = wrapper.find("input");
expect(input.attributes()["aria-required"]).toEqual("true"); expect(input.attributes()["aria-required"]).toEqual("true");
}); });
@ -247,5 +242,88 @@ describe("list-card.vue", () => {
expect(wrapper.vm.fieldOptions.initialValue).toEqual([ 'Windshield' ]); expect(wrapper.vm.fieldOptions.initialValue).toEqual([ 'Windshield' ]);
}); });
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.handleInputChange();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should do nothing if isMultiSelect is true and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
isMultiSelect: true,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleKeyupArrow).toHaveReturned;
});
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleKeyupArrow is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: false,
isMultiSelect: false,
},
});
// Assert
wrapper.vm.handleKeyupArrow();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
});
it("Should run handleChange if triggerButton is triggered", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: false,
},
});
// Assert
wrapper.vm.triggerButton();
await nextTick();
expect(wrapper.vm.handleChange).toBeCalled;
expect(wrapper.vm.handleCheckChange).not.toBeCalled;
expect(wrapper.vm.displayLoader).not.toBeCalled;
});
it("Should run handleCheckChange and displayLoader if triggerButton is triggered and seletingInitiatesLoad is true", async () => {
// Act
const wrapper = shallowMount(listCard, {
propsData: {
selectingInitiatesLoad: true,
},
});
// Assert
wrapper.vm.triggerButton();
await nextTick();
expect(wrapper.vm.handleCheckChange).toBeCalled;
expect(wrapper.vm.displayLoader).toBeCalled;
});
}); });

View file

@ -6,8 +6,11 @@
isWide ? 'horizontal' : '', isWide ? 'horizontal' : '',
(errors.length > 0 || hasError) ? 'has-error' : '', (errors.length > 0 || hasError) ? 'has-error' : '',
]" ]"
@mouseup="handleChange(value)" @keyup.space="triggerButton()"
@keyup.space="handleChange(value)" @keyup.up="handleKeyupArrow()"
@keyup.down="handleKeyupArrow()"
@keyup.left="handleKeyupArrow()"
@keyup.right="handleKeyupArrow()"
> >
<input <input
:type="isMultiSelect ? 'checkbox' : 'radio'" :type="isMultiSelect ? 'checkbox' : 'radio'"
@ -16,13 +19,15 @@
:value="value" :value="value"
:aria-required="isRequired" :aria-required="isRequired"
v-model="checkValue" v-model="checkValue"
@change="handleCheckChange(value)" @change="handleInputChange()"
/> />
<label <label
tabindex="-1"
:for="buttonID" :for="buttonID"
:aria-labelledby="buttonID"
class="d-flex w-100 align-items-center px-2 h-100" class="d-flex w-100 align-items-center px-2 h-100"
:class="getLabelClasses" :class="getLabelClasses"
tabindex="-1" @mouseup="triggerButton()"
> >
<img <img
:id="buttonImageId" :id="buttonImageId"
@ -93,15 +98,6 @@ export default {
: this.selectedValues[0]; : this.selectedValues[0];
} }
}, },
watch: {
// Changing this will impact pre-selection data loads on vehicle-parts.
// If changed, please regression test that vehicle-parts data still loads correctly with previous selections.
modelValue(newVal) {
if (newVal !== undefined) {
this.checkValue = newVal.value;
}
},
},
computed: { computed: {
getLabelClasses() { getLabelClasses() {
if (this.isWide) { if (this.isWide) {
@ -116,16 +112,44 @@ export default {
}, },
}, },
methods: { methods: {
handleCheckChange(newValue, oldValue) { handleInputChange() {
const isInitialization = typeof oldValue === "function"; if(!this.selectingInitiatesLoad) {
if (!isInitialization) { this.handleCheckChange();
}
},
handleKeyupArrow() {
if (this.isMultiSelect) {
return; // Prevent arrow keys from doing anything if element is a checkbox
}
if(!this.selectingInitiatesLoad) {
this.handleCheckChange();
}
this.handleChange(this.value);
},
triggerButton() {
if(this.selectingInitiatesLoad) {
this.displayLoader();
this.handleCheckChange();
}
this.handleChange(this.value);
},
handleCheckChange() {
const emitEvent = { const emitEvent = {
checkValue: this.checkValue, checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question
value: this.value.toString(), value: this.value.toString(),
buttonId: this.buttonID.toString(), buttonId: this.buttonID && this.buttonID.toString(),
}; };
this.$emit("isCheckedChanged", emitEvent); this.$emit("isCheckedChanged", emitEvent);
this.$emit("update:modelValue", emitEvent); this.$emit("update:modelValue", emitEvent);
},
},
watch: {
// Changing this will impact pre-selection data loads on vehicle-parts.
// If changed, please regression test that vehicle-parts data still loads correctly with previous selections.
modelValue(newVal) {
if (newVal !== undefined) {
this.checkValue = newVal.value;
} }
}, },
}, },
@ -213,18 +237,17 @@ export default {
box-shadow: 0 0 0 1px $blue; box-shadow: 0 0 0 1px $blue;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
&:focus-visible + label {
box-shadow: 0 0 0 2.5px $blue;
border-radius: 0.5rem;
}
&:focus + label { &:focus + label {
box-shadow: 0 0 0 2.5px $blue; box-shadow: 0 0 0 2.5px $blue;
border-radius: 0.5rem; border-radius: 0.5rem;
} }
&:checked:focus + label {
&:checked {
&:focus + label {
box-shadow: 0 0 0 2.5px $blue; box-shadow: 0 0 0 2.5px $blue;
} }
}
&:checked + label { &:checked + label {
p { p {
color: $black; color: $black;

View file

@ -56,19 +56,15 @@ export default {
handleClick(value) { handleClick(value) {
this.handleChange(value); this.handleChange(value);
}, },
handleCheckChange(newValue, oldValue) { handleCheckChange() {
const isInitialization = typeof oldValue === "function";
if (!isInitialization) {
const emitEvent = { const emitEvent = {
checkValue: this.checkValue, checkValue: this.checkValue,
value: this.value.toString(), value: this.value.toString(),
buttonID: this.buttonID.toString(), buttonID: this.buttonID && this.buttonID.toString(),
}; };
this.$emit("isCheckedChanged", emitEvent); this.$emit("isCheckedChanged", emitEvent);
this.$emit("update:modelValue", emitEvent); this.$emit("update:modelValue", emitEvent);
}
}, },
}, },
setup(props) { setup(props) {