diff --git a/jest.config.js b/jest.config.js index ded486df9..4af0d88cc 100644 --- a/jest.config.js +++ b/jest.config.js @@ -28,7 +28,7 @@ module.exports = { testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"], coverageThreshold: { global: { - statements: 85, + statements: 80, // Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90 }, }, diff --git a/src/App.vue b/src/App.vue index c934ee244..d817ff749 100644 --- a/src/App.vue +++ b/src/App.vue @@ -11,48 +11,12 @@ diff --git a/src/common-components/base-input-button/base-input-button.spec.js b/src/common-components/base-input-button/base-input-button.spec.js index 56a49363f..72e7e4843 100644 --- a/src/common-components/base-input-button/base-input-button.spec.js +++ b/src/common-components/base-input-button/base-input-button.spec.js @@ -952,13 +952,30 @@ describe.skip("baseInputButton.vue", () => { test.only("click on both => both are selected", async () => { // Arrange const { wrapper } = setupBaseInputButtonWrapper({ - isMultiSelect: true + isMultiSelect: true, + value1: "value1" }) - console.log(wrapper.html()) + // Act const buttonWrappers = wrapper.findAllComponents({name: "baseInputButtonWrapper"}) - await buttonWrappers[0].trigger("click") + + // The two together simulate a click + const inputOne = buttonWrappers.at(0); + await inputOne.trigger("mousedown.left") + await inputOne.trigger("change") + await inputOne.trigger("click") + + console.log({ + html: wrapper.html(), + buttonWrapper1: inputOne, + a: inputOne.vm.selectedValue, + c: inputOne.vm.modelValue, + d: inputOne.modelValue, + e: inputOne.vm.value, + f: inputOne.value, + }) + // Assert expect(wrapper.vm.value).toEqual(["value1"]) @@ -1015,8 +1032,8 @@ function setupBaseInputButtonWrapper({ mockData = {} }) { }; let parentComponentTemplate = "
" - parentComponentTemplate += `` - parentComponentTemplate += `` + parentComponentTemplate += `` + parentComponentTemplate += `` parentComponentTemplate += `
` const wrapper = mount({ data() { diff --git a/src/common-components/base-input-button/base-input-button.vue b/src/common-components/base-input-button/base-input-button.vue index 5c7ca21f7..162f0866f 100644 --- a/src/common-components/base-input-button/base-input-button.vue +++ b/src/common-components/base-input-button/base-input-button.vue @@ -26,40 +26,15 @@ import { useField } from "vee-validate"; import { toRef } from "vue"; import { queryStrings } from "@/constants/query-strings"; -import inputButtonWrapperMixin from "../../mixins/input-button-wrapper-mixin"; +import { handleInputFocus, handleInputBlur } from "@/helpers/input-button-focus-helper"; +import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props"; export default { name: "base-input-button", props: { - // ...inputButtonWrapperMixin.props - value: { - type: [String, Number], - required: true, - }, - modelValue: { - type: [Array, String, Number], - required: true, - }, - isMultiSelect: Boolean, - groupName: { - type: String, - required: true, - }, - validationRules: { - type: String, - default: "", - }, + ...inputButtonProps, buttonWrapperClasses: [String, Array, Object], inputClasses: [String, Array, Object], - valueToLogType: String, - selectOnKeypress: { - type: Boolean, - default: true, - }, - isRequired: Boolean, - lastValuePushedToGa: [String, Number], - setLastValuePushedToGa: Function, - shouldPushClickEventToGAOnMount: Boolean, }, data() { return { @@ -68,15 +43,15 @@ export default { }, mounted() { if (this.isChecked) { - if (this.shouldPushClickEventToGAOnMount) { - this.handleEventAction(this.eventTypes.MOUNT) - } else { - this.handleChange(this.modelValue); - } + this.handleChange(this.modelValue); } }, methods: { handleEventAction(eventType, e) { + console.log("HANDLE EVENT ACTION: ", { + eventType, + e + }) if (this.isMultiSelect) { switch (eventType) { case this.eventTypes.ENTER: @@ -92,16 +67,15 @@ export default { case this.eventTypes.CLICK: case this.eventTypes.ENTER: case this.eventTypes.SPACE: - case this.eventTypes.MOUNT: this.handleClick(e); this.handlePushClickEventToGACheck( this.eventTypes.CLICK ); break; case this.eventTypes.CHANGE: - this.selectOnKeypress - ? this.handleClick(e) - : this.handleSelectionChange(e); + this.selectingInitiatesLoad + ? this.handleSelectionChange(e) + : this.handleClick(e); break; } } @@ -130,12 +104,12 @@ export default { this.$emit("update:modelValue", this.valueToEmit); }, handleFocus() { - this.$root.handleInputFocus({ + handleInputFocus({ groupName: this.groupName, }); }, handleBlur() { - this.$root.handleInputBlur({ + handleInputBlur({ groupName: this.groupName, onFocusCallback: this.handlePushClickEventToGACheck, }); @@ -146,6 +120,7 @@ export default { this.pushClickEventToGA(); } else { // if from tabbing around if ( + this.valueToEmit !== null && !this.isValueSelectedOnClick && this.isChecked && this.lastValuePushedToGa != this.value @@ -193,7 +168,6 @@ export default { ENTER: "enter", SPACE: "space", CLICK: "click", - MOUNT: "mount" }; }, }, diff --git a/src/common-components/base-input-button/button-functionality-props.js b/src/common-components/base-input-button/button-functionality-props.js new file mode 100644 index 000000000..b9a63c925 --- /dev/null +++ b/src/common-components/base-input-button/button-functionality-props.js @@ -0,0 +1,30 @@ +export const inputButtonProps = { + value: { + type: [String, Number], + required: true, + }, + modelValue: { + type: [Array, String, Number], + required: true, + }, + isMultiSelect: Boolean, + groupName: { + type: String, + required: true, + }, + validationRules: { + type: String, + default: "", + }, + valueToLogType: String, + isRequired: { + type: Boolean, + default: true, + }, + lastValuePushedToGa: [String, Number], + setLastValuePushedToGa: Function, + selectingInitiatesLoad: { + type: Boolean, + default: false, + }, +} \ No newline at end of file diff --git a/src/common-components/button-question/button-question.vue b/src/common-components/button-question/button-question.vue index eb198c93f..56ab5abd7 100644 --- a/src/common-components/button-question/button-question.vue +++ b/src/common-components/button-question/button-question.vue @@ -49,10 +49,8 @@ :isWide="isWide" :validationRules="validationRules" :textPosition="textPosition" - :selectOnKeypress="selectOnKeypress" :lastValuePushedToGa="lastValuePushedToGa" :setLastValuePushedToGa="setLastValuePushedToGa" - :shouldPushClickEventToGAOnMount="shouldPushClickEventToGAOnMount" v-model="selectedValues" /> @@ -118,11 +116,6 @@ export default { suppressError: Boolean, useTextForValue: Boolean, valueToLogType: String, - selectOnKeypress: { - type: Boolean, - default: true, - }, - shouldPushClickEventToGAOnMount: Boolean }, data() { return { @@ -179,7 +172,6 @@ export default { } }, buttonsInfo() { - // TODO KO temporary. It should always just be an array return (Array.isArray(this.answers) ? this.answers : [])?.map( (answer) => ({ buttonLabel: answer.buttonLabel ?? answer.Text ?? answer, diff --git a/src/helpers/analytics-helper.js b/src/helpers/analytics-helper.js new file mode 100644 index 000000000..23b6557ed --- /dev/null +++ b/src/helpers/analytics-helper.js @@ -0,0 +1,35 @@ +let lastFocusedInputGroupName = ""; +let onFocusCallback = null; + +// TODO KO Add tests for this file +const handleChildFocus = (e) => { + const targetType = e.target.type; + if (targetType !== "radio" && targetType !== "checkbox") { + handleInputFocus({ + groupName: null, + }); + } +} + +const handleInputFocus = (e) => { + if ( + e && + lastFocusedInputGroupName !== e.groupName && + onFocusCallback + ) { + onFocusCallback(); + } +} + +const handleInputBlur = (e) => { + if (e) { + lastFocusedInputGroupName = e.groupName; + onFocusCallback = e.onFocusCallback; + } +} + +export { + handleChildFocus, + handleInputFocus, + handleInputBlur +} \ No newline at end of file diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue index d4da8cd1b..503c30cb8 100644 --- a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue +++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue @@ -25,7 +25,7 @@ export default ({ name: "replaceOptionsQuestion", data(){ return { - replaceOptions: [], + replaceOptions: this.isMultiSelect ? [] : "", } }, props: { @@ -46,7 +46,7 @@ export default ({ updateSelectedValues() { // UPDATE SELECTEDVALUES IF ONLY ONE ANSWER if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) { - this.selectedValues = [this.answersToDisplay[0].Name]; + this.selectedValues = this.isMultiSelect ? [this.answersToDisplay[0].Name] : this.answersToDisplay[0].Name; } }, }, @@ -91,7 +91,7 @@ export default ({ }, shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) { if (!shouldDisplayReplaceOptionsQuestion) { - this.selectedValues = []; + this.selectedValues = this.isMultiSelect ? [] : ""; } } }, diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue index 9af96c117..99f644d6f 100644 --- a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue +++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue @@ -57,7 +57,7 @@ export default ({ name: "sideDoorOptions", props: { groupName: String, - modelValue: [Array, Object], // TODO Does this take an array? + modelValue: Object, selectedDamageLocations: Array, cmsWidgetName: String, }, diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-options.vue b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue index 31b008760..8081bb2b2 100644 --- a/src/layouts/vehicle-damage/windshield-options/windshield-options.vue +++ b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue @@ -57,8 +57,8 @@ defineRule("windshield-replace-options-required", required(errorMessages.WINSHIE defineRule("check-for-repair-and-replace", (selectedWindshieldDamageType, selectedDamageLocations) => { return selectedWindshieldDamageType.toString() != damageLocationsSelected.REPAIR || - (!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) && !selectedDamageLocations[0]?.includes(damageLocationsSelected.WINDSHIELD)) || - (selectedDamageLocations.length === 1 && selectedDamageLocations[0].length === 1); + (!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) && !selectedDamageLocations[0]?.includes(damageLocationsSelected.WINDSHIELD)) || + (selectedDamageLocations[0].length === 1); }); defineRule("repair-only", (value) => { return value.toString() === damageLocationsSelected.REPAIR; @@ -83,7 +83,7 @@ export default ({ }, props: { - modelValue: [Object, String], // TODO Does this take a string? + modelValue: Object, selectedDamageLocations: Array, hasRepairReplaceConflict: Boolean, hasSplitSingleConflict: Boolean, diff --git a/src/layouts/vehicle-make/make-question/make-question.vue b/src/layouts/vehicle-make/make-question/make-question.vue index c2a7cd2d2..28246814f 100644 --- a/src/layouts/vehicle-make/make-question/make-question.vue +++ b/src/layouts/vehicle-make/make-question/make-question.vue @@ -8,7 +8,6 @@ groupName="ChooseVehicleMake" textPosition="text-start" v-model="selectedValue" - :selectOnKeypress="false" isRequired /> diff --git a/src/layouts/vehicle-model/model-question/model-question.vue b/src/layouts/vehicle-model/model-question/model-question.vue index 393df6fc9..bc20bdc6a 100644 --- a/src/layouts/vehicle-model/model-question/model-question.vue +++ b/src/layouts/vehicle-model/model-question/model-question.vue @@ -8,7 +8,6 @@ groupName="ChooseVehicleModel" textPosition="text-start" v-model="selectedValue" - :selectOnKeypress="false" isRequired /> diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue index 14f707818..d45c8cddb 100644 --- a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue @@ -27,9 +27,7 @@ isRequired :groupName="`${glassLocation}-${glassName}-${selectedTint}`" :validationRules="partValidationRules" - :shouldPushClickEventToGAOnMount=" - shouldPushClickEventToGAOnMount - " /> + /> @@ -57,7 +55,6 @@ export default { glassColorQuestion: "", glassFeatureQuestion: "", selectedTint: "", - shouldPushClickEventToGAOnMount: true, }; }, props: { @@ -224,7 +221,6 @@ export default { this.$nextTick(() => { if (this.modelValue !== undefined) { // Populate button-question model-value if parts data already exists in VueX - this.shouldPushClickEventToGAOnMount = false; this.selectedTint = this.modelValue?.color } }); diff --git a/src/layouts/vehicle-style/style-question/style-question.vue b/src/layouts/vehicle-style/style-question/style-question.vue index bf8fd3d07..806f7ac41 100644 --- a/src/layouts/vehicle-style/style-question/style-question.vue +++ b/src/layouts/vehicle-style/style-question/style-question.vue @@ -8,7 +8,6 @@ groupName="ChooseVehicleStyle" textPosition="text-start" v-model="selectedValue" - :selectOnKeypress="false" isRequired /> diff --git a/src/layouts/vehicle-year/year-question/year-question.vue b/src/layouts/vehicle-year/year-question/year-question.vue index 06af5ce64..6851f282c 100644 --- a/src/layouts/vehicle-year/year-question/year-question.vue +++ b/src/layouts/vehicle-year/year-question/year-question.vue @@ -8,7 +8,6 @@ groupName="ChooseVehicleYear" textPosition="text-start" v-model="selectedValue" - :selectOnKeypress="false" isRequired /> diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 6eb18e462..e773c6a01 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -48,6 +48,7 @@ export default { pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) { const currentPageName = getPageNameByQueryString(); const labelToLog = getValueToLog(label, valueToLogType); + const eventToBePushed = { 'event': GaEvents.GENERIC_EVENT, 'category': category, @@ -140,7 +141,7 @@ export default { noSession() { return getSessionKeyValue() === 0 || getSessionIdValue() === '00000000-0000-0000-0000-000000000000'; - } + }, }, computed: { analyticsPageEvents() { diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js index eae27c2fe..866e5d1d6 100644 --- a/src/mixins/input-button-wrapper-mixin.js +++ b/src/mixins/input-button-wrapper-mixin.js @@ -1,9 +1,8 @@ +import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props"; + export default { props: { - modelValue: [Array, String, Number], - value: [String, Number], - isMultiSelect: Boolean, - groupName: String, + ...inputButtonProps, buttonLabel: [Number, String], buttonLabelSubCopy: String, buttonImage: String, @@ -13,16 +12,7 @@ export default { }, textPosition: String, screenReaderOnlyText: String, - valueToLogType: String, - validationRules: String, isWide: Boolean, - isRequired: { - type: Boolean, - default: true, - }, - lastValuePushedToGa: [String, Number], - setLastValuePushedToGa: Function, - shouldPushClickEventToGAOnMount: Boolean, }, computed: { selectedValue: { diff --git a/src/store/index.js b/src/store/index.js index 1d6e68482..8d2f6b309 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1044,11 +1044,8 @@ export const actions = { ); const isWindshieldRepairTheSame = isWindshieldRepair === context.state.order.damage.isRepair; - const isChipCountTheSame = Array.isArray(selectedWindshieldChipCount) //TODO: fix the underlying components so this is never an array - ? selectedWindshieldChipCount[0] === - context.state.order.damage.numberOfChips - : selectedWindshieldChipCount === - context.state.order.damage.numberOfChips; + + const isChipCountTheSame = selectedWindshieldChipCount === context.state.order.damage.numberOfChips; const isDamageChanging = !isGlassToReplaceTheSame ||