diff --git a/src/common-components/button-question/button-question.spec.js b/src/common-components/button-question/button-question.spec.js index d36b73012..591b34317 100644 --- a/src/common-components/button-question/button-question.spec.js +++ b/src/common-components/button-question/button-question.spec.js @@ -1,6 +1,9 @@ import { shallowMount } from "@vue/test-utils"; import buttonQuestion from "@/common-components/button-question/button-question"; 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", () => { 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", () => { it("Should trigger event modelValue change to new value on when radio button selected", async () => { // 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"); + }); +}); diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index 559568236..e1493c536 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -5,7 +5,7 @@ selectingInitiatesLoad :questionText="questionText" :answers="makes" - groupName="Choose Vehicle Make" + groupName="ChooseVehicleMake" textPosition="text-start" v-model="selectedValueAsArray" isRequired=true diff --git a/src/layouts/vehicle-model/model-question/model-question.vue b/src/layouts/vehicle-model/model-question/model-question.vue index c05732352..e3a56e8c7 100644 --- a/src/layouts/vehicle-model/model-question/model-question.vue +++ b/src/layouts/vehicle-model/model-question/model-question.vue @@ -5,7 +5,7 @@ selectingInitiatesLoad :questionText="questionText" :answers="models" - groupName="Choose Vehicle Model" + groupName="ChooseVehicleModel" textPosition="text-start" v-model="selectedValueAsArray" isRequired=true diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue index 0d3dd54ac..e7a289bfd 100644 --- a/src/layouts/vehicle-style/style-question/style-question.vue +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -5,7 +5,7 @@ selectingInitiatesLoad :questionText="questionText" :answers="styles" - groupName="Choose Vehicle Style" + groupName="ChooseVehicleStyle" textPosition="text-start" v-model="selectedValueAsArray" isRequired=true diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue index 8a53e83db..16bac5442 100644 --- a/src/layouts/vehicle-year/year-question/year-question.vue +++ b/src/layouts/vehicle-year/year-question/year-question.vue @@ -5,7 +5,7 @@ selectingInitiatesLoad :questionText="questionText" :answers="years" - groupName="Choose Vehicle Year" + groupName="ChooseVehicleYear" textPosition="text-start" v-model="selectedValueAsArray" isRequired=true diff --git a/src/styles/common-error-styles.scss b/src/styles/common-error-styles.scss index 165101177..67bd9473b 100644 --- a/src/styles/common-error-styles.scss +++ b/src/styles/common-error-styles.scss @@ -75,7 +75,7 @@ html { } input:checked:focus { + label { - box-shadow: 0 0 0 1px $blue; + box-shadow: 0 0 0 2.5px $blue; border-radius: .5rem; } } diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js index f95b378d7..cbd757a88 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js @@ -100,7 +100,7 @@ describe("list-button-horizontal.vue", () => { const label = wrapper.find("label"); wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); @@ -123,7 +123,7 @@ describe("list-button-horizontal.vue", () => { const label = wrapper.find("label"); wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); @@ -146,7 +146,7 @@ describe("list-button-horizontal.vue", () => { const label = wrapper.find("label"); wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); @@ -195,4 +195,54 @@ describe("list-button-horizontal.vue", () => { // Assert 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; + }); + }); diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue index bd1977ba3..d128982eb 100644 --- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue +++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue @@ -2,8 +2,11 @@
@@ -77,27 +84,47 @@ export default { checkValue: Boolean, }; }, - created(){ - if(Array.isArray(this.selectedValues)){ - this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0]; + created() { + if (Array.isArray(this.selectedValues)) { + this.checkValue = this.isMultiSelect + ? this.selectedValues.includes(this.value) + : this.selectedValues[0]; } }, methods: { displayLoader() { this.isLoaderDisplayed = true; }, - handleClick(value) { - if(this.selectingInitiatesLoad) { - this.displayLoader(); + 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(value); + this.handleChange(this.value); }, - handleCheckChange(newValue, oldValue){ - const isInitialization = typeof(oldValue) === 'function'; - if (!isInitialization) { - this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() }); + triggerButton() { + if(this.selectingInitiatesLoad) { + this.displayLoader(); + this.handleCheckChange(); } + this.handleChange(this.value); + }, + handleCheckChange() { + const emitEvent = { + checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question + value: this.value.toString(), + buttonId: this.buttonID && this.buttonID.toString(), + }; + this.$emit("isCheckedChanged", emitEvent); + this.$emit("update:modelValue", emitEvent); } }, components: { @@ -105,6 +132,7 @@ export default { }, setup(props) { const inputType = props.isMultiSelect ? "checkbox" : "radio"; + const fieldOptions = { type: inputType, checkedValue: props.value, @@ -118,13 +146,11 @@ export default { } const { - checked, handleChange, errors, } = useField(props.groupName, props.validationRules, fieldOptions); return { - checked, handleChange, errors, fieldOptions, // only need to expose this for unit test purposes @@ -137,10 +163,11 @@ export default { .list-button-horizontal { input[type="radio"], input[type="checkbox"] { + position: absolute; + height: 0; opacity: 0; width: 0; - height: 0; - position: absolute; + &:focus-visible + label { box-shadow: 0 0 0 2.5px $blue; z-index: 2; diff --git a/src/ux-components/list-button/list-button.spec.js b/src/ux-components/list-button/list-button.spec.js index 7cc885b49..7a244a5bf 100644 --- a/src/ux-components/list-button/list-button.spec.js +++ b/src/ux-components/list-button/list-button.spec.js @@ -96,11 +96,8 @@ describe("list-button.vue", () => { }); // Assert - - const label = wrapper.find("label"); - wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); @@ -119,10 +116,8 @@ describe("list-button.vue", () => { }); // Assert - - const label = wrapper.find("label"); wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); const loader = wrapper.find("loader-stub"); @@ -140,11 +135,8 @@ describe("list-button.vue", () => { }); // Assert - - const label = wrapper.find("label"); - wrapper.vm.handleCheckChange = jest.fn(); - wrapper.vm.handleClick(); + wrapper.vm.triggerButton(); await nextTick(); @@ -197,4 +189,53 @@ describe("list-button.vue", () => { 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; + }); + }); diff --git a/src/ux-components/list-button/list-button.vue b/src/ux-components/list-button/list-button.vue index d48b2407d..aba30934d 100644 --- a/src/ux-components/list-button/list-button.vue +++ b/src/ux-components/list-button/list-button.vue @@ -2,8 +2,11 @@
@@ -80,33 +84,47 @@ export default { checkValue: Boolean, }; }, - created(){ - if(Array.isArray(this.selectedValues)){ - this.checkValue = this.isMultiSelect ? this.selectedValues.includes(this.value) : this.selectedValues[0]; + created() { + if (Array.isArray(this.selectedValues)) { + this.checkValue = this.isMultiSelect + ? this.selectedValues.includes(this.value) + : this.selectedValues[0]; } }, methods: { displayLoader() { 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) { this.displayLoader(); this.handleCheckChange(); } - this.handleChange(value); + this.handleChange(this.value); }, - handleCheckChange(value, oldValue){ - const isInitialization = typeof(oldValue) === 'function'; - if (!isInitialization) { - const emitEvent = { - checkValue: this.checkValue, - value: this.value.toString(), - buttonId: this.buttonID.toString(), - }; - this.$emit('isCheckedChanged', emitEvent); - this.$emit("update:modelValue", emitEvent); - } + handleCheckChange() { + const emitEvent = { + checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question + value: this.value.toString(), + buttonId: this.buttonID && this.buttonID.toString(), + }; + this.$emit("isCheckedChanged", emitEvent); + this.$emit("update:modelValue", emitEvent); }, }, components: { @@ -128,13 +146,11 @@ export default { } const { - checked, handleChange, errors, } = useField(props.groupName, props.validationRules, fieldOptions); return { - checked, handleChange, errors, fieldOptions, // only need to expose this for unit test purposes @@ -154,10 +170,10 @@ export default { opacity: 0; &:focus-visible + label { - box-shadow: 0 0 0 2.5px $blue inset; + box-shadow: 0 0 0 2.5px $blue; } &:focus + label { - box-shadow: 0 0 0 2.5px $blue inset; + box-shadow: 0 0 0 2.5px $blue; } &:checked + label { color: $black; @@ -165,6 +181,9 @@ export default { background: $blue-100; box-shadow: 0 0 0 1px $blue; } + &:checked:focus + label { + box-shadow: 0 0 0 2.5px $blue; + } &:checked + label p, &:checked + label span { font-weight: 500; diff --git a/src/ux-components/list-card/list-card.spec.js b/src/ux-components/list-card/list-card.spec.js index 3aa803cc1..d810909e8 100644 --- a/src/ux-components/list-card/list-card.spec.js +++ b/src/ux-components/list-card/list-card.spec.js @@ -1,5 +1,6 @@ import { shallowMount } from "@vue/test-utils"; import listCard from "./list-card"; +import { nextTick } from "vue"; describe("list-card.vue", () => { it("Should return input type checkbox if isMultiSelect is true", async () => { @@ -18,7 +19,6 @@ describe("list-card.vue", () => { // Assert const input = wrapper.find("input"); - expect(input.attributes().type).toEqual("checkbox"); }); @@ -38,7 +38,6 @@ describe("list-card.vue", () => { // Assert const paragraph = wrapper.find("p"); - expect(paragraph.text()).toEqual("Windshield"); }); @@ -59,7 +58,6 @@ describe("list-card.vue", () => { // Assert const paragraph = wrapper.find("p:nth-of-type(2)"); - expect(paragraph.text()).toEqual("Test"); }); @@ -80,7 +78,6 @@ describe("list-card.vue", () => { // Assert const label = wrapper.find("label"); - expect(label.attributes().for).toEqual("List Card Checkbox"); }); @@ -101,7 +98,6 @@ describe("list-card.vue", () => { // Assert const input = wrapper.find("input"); - expect(input.attributes().name).toEqual("radio 1"); }); @@ -122,7 +118,6 @@ describe("list-card.vue", () => { // Assert const input = wrapper.find("input"); - expect(input.attributes()["aria-required"]).toEqual("true"); }); @@ -247,5 +242,88 @@ describe("list-card.vue", () => { 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; + }); }); diff --git a/src/ux-components/list-card/list-card.vue b/src/ux-components/list-card/list-card.vue index a178895fb..742ec4584 100644 --- a/src/ux-components/list-card/list-card.vue +++ b/src/ux-components/list-card/list-card.vue @@ -6,8 +6,11 @@ isWide ? 'horizontal' : '', (errors.length > 0 || hasError) ? 'has-error' : '', ]" - @mouseup="handleChange(value)" - @keyup.space="handleChange(value)" + @keyup.space="triggerButton()" + @keyup.up="handleKeyupArrow()" + @keyup.down="handleKeyupArrow()" + @keyup.left="handleKeyupArrow()" + @keyup.right="handleKeyupArrow()" >