Merge pull request #288 from Safelite/feature/CSR-319
CSR-319: rearrange form markup location / move submit fns into mixin
This commit is contained in:
commit
5bb00468f2
9 changed files with 105 additions and 68 deletions
|
|
@ -782,41 +782,6 @@ describe("vehicle-damage.vue", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("vehicle-damage.vue", () => {
|
||||
test("when onInvalidSubmit is triggered with errors focus will be put on the first element with an error", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const mockedValidationPayload = {
|
||||
values: {},
|
||||
errors: {
|
||||
driverSideOptions: 'Please select window',
|
||||
passengerSideOptions: 'Please select window'
|
||||
},
|
||||
results: {},
|
||||
}
|
||||
const newObj = document.createElement('input');
|
||||
newObj.setAttribute("id", "testInput");
|
||||
newObj.setAttribute("data-focus-target", "driverSideOptions");
|
||||
document.body.appendChild(newObj);
|
||||
const testInputElement = document.getElementById("testInput");
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-damage" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
wrapper.vm.onInvalidSubmit(mockedValidationPayload);
|
||||
await nextTick();
|
||||
const focusedEl = document.activeElement;
|
||||
|
||||
//Assert
|
||||
expect(testInputElement).toBe(focusedEl);
|
||||
});
|
||||
});
|
||||
|
||||
// THE FOLLOWING TEST IS NOT NECESSARILY REQUIRED FOR COVERAGE
|
||||
// BUT KEEP FOR AN EXAMPLE OF A VALIDATION TEST
|
||||
//
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
<template>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
class="vehicle-damage-form"
|
||||
>
|
||||
<damageLocationQuestion
|
||||
ref="damageLocation"
|
||||
v-model="selectedDamageLocations"
|
||||
|
|
@ -50,8 +49,8 @@
|
|||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
/>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -161,19 +160,6 @@ export default {
|
|||
this.$route
|
||||
);
|
||||
},
|
||||
onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
// get error names array
|
||||
const errorNames = errors ? Object.keys(errors) : [];
|
||||
const firstErrorEl = errorNames[0];
|
||||
if (firstErrorEl) {
|
||||
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
||||
const el = document.querySelector(qsString);
|
||||
el && el.focus();
|
||||
}
|
||||
},
|
||||
|
||||
getDamageLocationsFromStore() {
|
||||
var glassSelections = [];
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,19 @@ export default {
|
|||
},
|
||||
savePageDataToStore(page, data){
|
||||
store.commit(storeMutations.UPDATE_PAGE_DATA, { page: page, data: data });
|
||||
}
|
||||
},
|
||||
onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
// get error names array
|
||||
const errorNames = errors ? Object.keys(errors) : [];
|
||||
const firstErrorEl = errorNames[0];
|
||||
if (firstErrorEl) {
|
||||
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
||||
const el = document.querySelector(qsString);
|
||||
el && el.focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
storeActions() {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { storeActions } from "@/constants/store-actions.js";
|
|||
import { widgetNames } from "@/constants/widget-names.js";
|
||||
import { storeMutations } from "@/constants/store-mutations.js";
|
||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||
import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
||||
import store from "@/store";
|
||||
|
||||
describe("baseMixin.js", () => {
|
||||
|
|
@ -77,6 +78,36 @@ describe("baseMixin.js", () => {
|
|||
// Assert
|
||||
expect(widgetNamesForTest).toEqual(widgetNames);
|
||||
});
|
||||
|
||||
test("computed: vehicleCategories should be equal to import object", () => {
|
||||
// Arrange
|
||||
const mixIn = getMixInInstance({});
|
||||
|
||||
// Act
|
||||
let vehicleCategoriesForTest = mixIn.computed.vehicleCategories();
|
||||
|
||||
// Assert
|
||||
expect(vehicleCategoriesForTest).toEqual(vehicleCategories);
|
||||
});
|
||||
|
||||
test("onInvalidSubmit: puts focus on first error", () => {
|
||||
// Arrange
|
||||
const mixIn = getMixInInstance({});
|
||||
const validationData = {
|
||||
errors: {
|
||||
fieldOne: 'error message 1',
|
||||
fieldTwo: 'error message 2',
|
||||
}
|
||||
};
|
||||
|
||||
global.document.querySelector = jest.fn();
|
||||
|
||||
// Act
|
||||
mixIn.methods.onInvalidSubmit(validationData);
|
||||
|
||||
// Assert
|
||||
expect(global.document.querySelector).toBeCalledWith("[data-focus-target='fieldOne']");
|
||||
});
|
||||
});
|
||||
|
||||
function getMixInInstance({ isDispatchSuccess = true }) {
|
||||
|
|
@ -101,6 +132,7 @@ function getMixInInstance({ isDispatchSuccess = true }) {
|
|||
baseMixIn.methods.$route = route;
|
||||
baseMixIn.methods.storeActions = storeActions;
|
||||
baseMixIn.methods.widgetNames = widgetNames;
|
||||
baseMixIn.methods.vehicleCategories = vehicleCategories;
|
||||
|
||||
store.dispatch = storeDispatch;
|
||||
store.commit = jest.fn();
|
||||
|
|
|
|||
|
|
@ -109,11 +109,15 @@ export default {
|
|||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
potentialInitialValue: props.selectedValues,
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
// Set initialValue for validation setup if pre-selected
|
||||
// NOTE: props.selectedValues could be an array of strings, or an array of integers...
|
||||
if (props.selectedValues && (props.selectedValues.includes(props.value) || props.selectedValues.includes(parseInt(props.value)))) {
|
||||
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
|
||||
}
|
||||
|
||||
const {
|
||||
checked,
|
||||
handleChange,
|
||||
|
|
@ -124,6 +128,7 @@ export default {
|
|||
checked,
|
||||
handleChange,
|
||||
errors,
|
||||
fieldOptions, // only need to expose this for unit test purposes
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -166,11 +166,15 @@ describe("list-button.vue", () => {
|
|||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
buttonID: 'list-card-id'
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.vm.handleCheckChange();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean}]);
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean, buttonId: 'list-card-id'}]);
|
||||
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
|
|
@ -192,4 +196,5 @@ describe("list-button.vue", () => {
|
|||
// Assert
|
||||
expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -100,7 +100,13 @@ export default {
|
|||
handleCheckChange(newValue, oldValue){
|
||||
const isInitialization = typeof(oldValue) === 'function';
|
||||
if (!isInitialization) {
|
||||
this.$emit('isCheckedChanged', { checkValue: this.checkValue, value: this.value.toString() });
|
||||
const emitEvent = {
|
||||
checkValue: this.checkValue,
|
||||
value: this.value.toString(),
|
||||
buttonId: this.buttonID.toString(),
|
||||
};
|
||||
this.$emit('isCheckedChanged', emitEvent);
|
||||
this.$emit("update:modelValue", emitEvent);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -109,14 +115,19 @@ export default {
|
|||
},
|
||||
setup(props) {
|
||||
const inputType = props.isMultiSelect ? "checkbox" : "radio";
|
||||
|
||||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
potentialInitialValue: props.selectedValues,
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
// Set initialValue for validation setup if pre-selected
|
||||
// NOTE: props.selectedValues could be an array of strings, or an array of integers...
|
||||
if (props.selectedValues && (props.selectedValues.includes(props.value) || props.selectedValues.includes(parseInt(props.value)))) {
|
||||
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
|
||||
}
|
||||
|
||||
const {
|
||||
checked,
|
||||
handleChange,
|
||||
|
|
@ -127,6 +138,7 @@ export default {
|
|||
checked,
|
||||
handleChange,
|
||||
errors,
|
||||
fieldOptions, // only need to expose this for unit test purposes
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -232,5 +232,20 @@ describe("list-card.vue", () => {
|
|||
expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
|
||||
});
|
||||
|
||||
it("Should set an initial value for validation if selectedValues include the value", async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(listCard, {
|
||||
propsData: {
|
||||
value: "Windshield",
|
||||
groupName: "radio 1",
|
||||
modelValue: ["Windshield"],
|
||||
selectedValues: ["Windshield"],
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.fieldOptions.initialValue).toEqual([ 'Windshield' ]);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@
|
|||
|
||||
<script>
|
||||
import { useField } from "vee-validate";
|
||||
|
||||
export default {
|
||||
name: "listCard",
|
||||
props: {
|
||||
|
|
@ -135,11 +136,14 @@ export default {
|
|||
|
||||
const fieldOptions = {
|
||||
type: inputType,
|
||||
checkedValue: props.value,
|
||||
checkedValue: props.value, // EX: "Single" or "Passenger"
|
||||
potentialInitialValue: props.selectedValues,
|
||||
};
|
||||
|
||||
if (Array.isArray(props.selectedValues) && props.selectedValues.length == 1) {
|
||||
fieldOptions['initialValue'] = fieldOptions.checkedValue;
|
||||
// Set initialValue for validation setup if pre-selected
|
||||
// NOTE: props.selectedValues could be an array of strings, or an array of integers...
|
||||
if (props.selectedValues && (props.selectedValues.includes(props.value) || props.selectedValues.includes(parseInt(props.value)))) {
|
||||
fieldOptions['initialValue'] = fieldOptions.potentialInitialValue;
|
||||
}
|
||||
|
||||
const {
|
||||
|
|
@ -150,6 +154,7 @@ export default {
|
|||
return {
|
||||
handleChange,
|
||||
errors,
|
||||
fieldOptions, // only need to expose this for unit test purposes
|
||||
};
|
||||
},
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue