Merge remote-tracking branch 'origin/feature/CSR-762' into feature/CSR-731
This commit is contained in:
commit
65cf2d3b84
52 changed files with 2559 additions and 1649 deletions
|
|
@ -28,8 +28,7 @@ module.exports = {
|
|||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
// TODO KO
|
||||
statements: 55,
|
||||
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
|
||||
},
|
||||
},
|
||||
|
|
|
|||
35
src/App.vue
35
src/App.vue
|
|
@ -1,16 +1,29 @@
|
|||
<template>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition :duration="{ enter: 200, leave: 200 }" name="route-fade" mode="out-in">
|
||||
<!-- The above durations should be kept in sync with the global css class "fade-on-route-transition" -->
|
||||
<component :is="Component" />
|
||||
</transition>
|
||||
</router-view>
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition
|
||||
:duration="{ enter: 200, leave: 200 }"
|
||||
name="route-fade"
|
||||
mode="out-in">
|
||||
<!-- The above durations should be kept in sync with the global css class "fade-on-route-transition" -->
|
||||
<component :is="Component" @focusin="handleAnyComponentFocus" />
|
||||
</transition>
|
||||
</router-view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { handleAnyComponentFocus } from "@/helpers/button-question-focus-helper"
|
||||
export default {
|
||||
name: "app",
|
||||
methods: {
|
||||
handleAnyComponentFocus: handleAnyComponentFocus
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./node_modules/bootstrap/scss/bootstrap";
|
||||
@import "@/styles/common-styles.scss";
|
||||
@import "@/styles/common-typography-styles.scss";
|
||||
@import "@/styles/common-error-styles.scss";
|
||||
@import "@/styles/common-animations.scss";
|
||||
@import "./node_modules/bootstrap/scss/bootstrap";
|
||||
@import "@/styles/common-styles.scss";
|
||||
@import "@/styles/common-typography-styles.scss";
|
||||
@import "@/styles/common-error-styles.scss";
|
||||
@import "@/styles/common-animations.scss";
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,736 @@
|
|||
import { shallowMount, mount } from "@vue/test-utils";
|
||||
import { EXPECTATION_FAILED } from "http-status-codes";
|
||||
import inputButtonWrapperMixin from "../../mixins/input-button-wrapper-mixin";
|
||||
import baseInputButton from "./base-input-button";
|
||||
|
||||
// TODO KO
|
||||
describe("baseInputButton.vue", () => {
|
||||
describe("general", () => {
|
||||
describe("checkbox", () => {
|
||||
test("isMultiSelect => baseInputButton is a checkbox", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(inputElement.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test("!isMultiSelect => baseInputButton is a radio button", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(inputElement.attributes().type).toEqual("radio");
|
||||
});
|
||||
});
|
||||
|
||||
// tests in here should be test.each
|
||||
describe("shared", () => {
|
||||
const isMultiSelectOptions = [true, false];
|
||||
test.each(isMultiSelectOptions)("groupName", (isMultiSelect) => {
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
groupName: "boogly",
|
||||
isMultiSelect: isMultiSelect,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
const inputElementAttributes = inputElement.attributes();
|
||||
expect(inputElementAttributes.name).toEqual("boogly");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("mouse clicks", () => {
|
||||
describe("checkbox", () => {
|
||||
test("clicked => correct event and value are emitted", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
value: "X",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
// await wrapper.trigger("mousedown.left");
|
||||
await wrapper.trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual([
|
||||
"X",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test("clicked => correct event and value are emitted", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
value: "X",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.trigger("mousedown.left");
|
||||
await wrapper.trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(
|
||||
"X"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("keyboard navigation and events", () => {
|
||||
describe("checkbox", () => {
|
||||
test.todo(
|
||||
"focus on a checkbox => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"blur from a checkbox => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test("change event fired from checkbox => inputButtonClicked is emitted with correct value", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
value: "Hi",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Act
|
||||
await input.trigger("change");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual([
|
||||
"Hi",
|
||||
]);
|
||||
});
|
||||
|
||||
test("focus and click space on a checkbox => inputButtonClicked is not emitted", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
value: "Hi",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Act
|
||||
await input.trigger("keypress", { key: "space" });
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()).not.toHaveProperty(
|
||||
"update:modelValue"
|
||||
);
|
||||
});
|
||||
|
||||
test("focus and click enter on a checkbox => inputButtonClicked is emitted with correct value", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
value: "Hi",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Act
|
||||
await input.trigger("keypress", { key: "enter" });
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual([
|
||||
"Hi",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test.todo(
|
||||
"focus on a radio button => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"blur from a radio button => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test("focus and click space on a radio button => inputButtonClicked is emitted with correct value", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
value: "Hi",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Act
|
||||
await input.trigger("keypress", { key: "space" });
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()).toHaveProperty("update:modelValue");
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(
|
||||
"Hi"
|
||||
);
|
||||
});
|
||||
|
||||
test("focus and click enter on a radio button => inputButtonClicked is emitted with correct value", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
value: "Hi",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Act
|
||||
await input.trigger("keypress", { key: "enter" });
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()).toHaveProperty("update:modelValue");
|
||||
expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(
|
||||
"Hi"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("methods", () => {
|
||||
describe("handleEventAction", () => {});
|
||||
|
||||
describe("handleSelectionChange", () => {
|
||||
test.todo("handleChange is called with valueToEmit");
|
||||
|
||||
describe("checkbox", () => {
|
||||
test.todo("modelValue is null => valueToEmit is correct value");
|
||||
|
||||
test.todo(
|
||||
"modelValue is undefined => valueToEmit is correct value"
|
||||
);
|
||||
test.todo(
|
||||
"modelValue is empty => valueToEmit is correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"modelValue is not empty and does not contain this button's value => valueToEmit is correct value"
|
||||
);
|
||||
test.todo(
|
||||
"modelValue is not empty and does contain this button's value => valueToEmit is correct value"
|
||||
);
|
||||
});
|
||||
|
||||
describe("radio", () => {});
|
||||
});
|
||||
|
||||
describe("handleClick", () => {
|
||||
test.todo("handleSelectChange is also called");
|
||||
|
||||
test.todo("inputButtonClicked is emitted with valueToEmit");
|
||||
});
|
||||
});
|
||||
|
||||
describe("computed", () => {
|
||||
describe("isChecked", () => {
|
||||
describe("checkbox", () => {
|
||||
const falsyModelValues = [[], null, undefined];
|
||||
test.each(falsyModelValues)(
|
||||
"modelValue is falsy/empty => checkbox isn't checked",
|
||||
(modelValue) => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
modelValue,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
expect(inputElement.element.checked).toBe(false);
|
||||
}
|
||||
);
|
||||
|
||||
test("modelValue doesn't contain this button's value => checkbox isn't checked", async () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
modelValue: ["Aaa", "Bbb", "Ccc"],
|
||||
value: "Ddd",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
expect(inputElement.element.checked).toBe(false);
|
||||
});
|
||||
|
||||
test("modelValue contains this button's value => checkbox is checked", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
modelValue: ["Aaa", "Bbb", "Ddd", "Ccc"],
|
||||
value: "Ddd",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(true);
|
||||
expect(inputElement.element.checked).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
const falsyModelValues = ["", null, undefined, []];
|
||||
test.each(falsyModelValues)(
|
||||
"modelValue is falsy/empty => radio button isn't checked",
|
||||
(modelValue) => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
modelValue,
|
||||
value: "Aaa",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
expect(inputElement.element.checked).toBe(false);
|
||||
}
|
||||
);
|
||||
|
||||
test("modelValue equals this button's value => radio button is checked", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
modelValue: "Ddd",
|
||||
value: "Ddd",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(true);
|
||||
expect(inputElement.element.checked).toBe(true);
|
||||
});
|
||||
|
||||
test("modelValue doesn't equal this button's value => radio button isn't checked", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
modelValue: "Aaa",
|
||||
value: "Ddd",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.isChecked).toEqual(false);
|
||||
expect(inputElement.element.checked).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("buttonId", () => {
|
||||
test("groupName and value combo yield correct id for input button with string value", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
groupName: "my test-name",
|
||||
value: "Aaa-BBB CcC",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.buttonId).toBe("my-test-name-Aaa-BBB-CcC");
|
||||
expect(inputElement.attributes().id).toBe(
|
||||
"my-test-name-Aaa-BBB-CcC"
|
||||
);
|
||||
});
|
||||
|
||||
test("groupName and value combo yield correct id for input button with number value", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
groupName: "my test-name",
|
||||
value: 2,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.buttonId).toBe("my-test-name-2");
|
||||
expect(inputElement.attributes().id).toBe("my-test-name-2");
|
||||
});
|
||||
});
|
||||
|
||||
describe("inputType", () => {
|
||||
test("isMultiSelect is true => inputType is 'checkbox'", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.inputType).toEqual("checkbox");
|
||||
expect(inputElement.attributes().type).toBe("checkbox");
|
||||
});
|
||||
|
||||
test("isMultiSelect is false => inputType is 'radio'", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const inputElement = wrapper.find("input");
|
||||
expect(wrapper.vm.inputType).toEqual("radio");
|
||||
expect(inputElement.attributes().type).toBe("radio");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("integration testing", () => {
|
||||
describe("checkbox", () => {
|
||||
test("click on both => both are selected", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(true);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
|
||||
expect(wrapper.vm.value).toEqual([]);
|
||||
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
await inputButtonTwo.find("input").trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.value).toEqual(["value1", "value2"]);
|
||||
});
|
||||
|
||||
test("click input 1, 2, 1 => only input 2 selected", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(true);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
|
||||
expect(wrapper.vm.value).toEqual([]);
|
||||
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
await inputButtonTwo.find("input").trigger("click");
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.value).toEqual(["value2"]);
|
||||
});
|
||||
|
||||
const defaultCheckedCases = [
|
||||
[["value2"], false, true],
|
||||
[["value1", "value2"], true, true],
|
||||
[["value1"], true, false],
|
||||
[[], false, false]
|
||||
]
|
||||
test.each(defaultCheckedCases)("initial value is %s => correct input buttons are selected", async (modelValue, isInputButtonOneChecked, isInputButtonTwoChecked) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: true,
|
||||
initialValue: modelValue
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputs = wrapper.findAll("input");
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
|
||||
// Assert
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(true);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(true);
|
||||
expect(inputs[0].element.checked).toBe(isInputButtonOneChecked);
|
||||
expect(inputs[1].element.checked).toBe(isInputButtonTwoChecked);
|
||||
expect(wrapper.vm.value).toEqual(modelValue);
|
||||
});
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test("click on both => last clicked is selected", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(false);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
|
||||
expect(wrapper.vm.value).toEqual("");
|
||||
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
await inputButtonTwo.find("input").trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.value).toEqual("value2");
|
||||
});
|
||||
|
||||
test("click input 1, 2, 1 => input 1 is selected", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(false);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
|
||||
expect(wrapper.vm.value).toEqual("");
|
||||
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
await inputButtonTwo.find("input").trigger("click");
|
||||
await inputButtonOne.find("input").trigger("click");
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.value).toEqual("value1");
|
||||
});
|
||||
|
||||
const defaultCheckedCases = [
|
||||
["", false, false],
|
||||
["value1", true, false],
|
||||
["value2", false, true],
|
||||
[[], false, false]
|
||||
]
|
||||
test.each(defaultCheckedCases)("initial value is %s => correct input buttons are selected", async (modelValue, isInputButtonOneChecked, isInputButtonTwoChecked) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupBaseInputButtonWrapper({
|
||||
mockData: {
|
||||
isMultiSelect: false,
|
||||
initialValue: modelValue
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
const buttonWrappers = wrapper.findAllComponents({
|
||||
name: "baseInputButtonWrapper",
|
||||
});
|
||||
const inputs = wrapper.findAll("input");
|
||||
const inputButtonOne = buttonWrappers.at(0);
|
||||
const inputButtonTwo = buttonWrappers.at(1);
|
||||
|
||||
// Assert
|
||||
expect(inputButtonOne.vm.isMultiSelect).toBe(false);
|
||||
expect(inputButtonTwo.vm.isMultiSelect).toBe(false);
|
||||
expect(inputs[0].element.checked).toBe(isInputButtonOneChecked);
|
||||
expect(inputs[1].element.checked).toBe(isInputButtonTwoChecked);
|
||||
expect(wrapper.vm.value).toEqual(modelValue);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ mockData = {}, shouldShallowMount = true }) {
|
||||
const baseInputButtonWrapper = {
|
||||
components: { baseInputButton },
|
||||
template: '<baseInputButton v-model="myValue" />',
|
||||
data() {
|
||||
return {
|
||||
myValue: "",
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const mountMockData = {
|
||||
...mockData,
|
||||
propsData: {
|
||||
// to get rid of some annoying warnings
|
||||
groupName: "groupName",
|
||||
modelValue: mockData.propsData?.isMultiSelect ? [] : "",
|
||||
value: "5",
|
||||
setLastValuePushedToGa: () => {},
|
||||
// should override the above if they exist
|
||||
...mockData.propsData,
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shouldShallowMount
|
||||
? shallowMount(baseInputButton, mountMockData)
|
||||
: mount(baseInputButton, mountMockData);
|
||||
|
||||
wrapper.vm.pushEventToGA = jest.fn();
|
||||
wrapper.vm.$route = {
|
||||
query: {},
|
||||
};
|
||||
wrapper.vm.GaActions = {};
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
function setupBaseInputButtonWrapper({ mockData = {} }) {
|
||||
const originalData = baseInputButton.data();
|
||||
baseInputButton.data = () => {
|
||||
return {
|
||||
...originalData,
|
||||
// $route: {
|
||||
// query: {
|
||||
// fmgPage: "myPage",
|
||||
// },
|
||||
// },
|
||||
};
|
||||
};
|
||||
|
||||
// console.log({
|
||||
// test: baseInputButton.vm.$route
|
||||
// })
|
||||
baseInputButton.methods.pushClickEventToGA = jest.fn();
|
||||
|
||||
const baseInputButtonWrapper = {
|
||||
name: "baseInputButtonWrapper",
|
||||
components: { baseInputButton },
|
||||
template:
|
||||
'<div><baseInputButton v-bind="$props" v-model="selectedValue" /></div>',
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
};
|
||||
|
||||
let parentComponentTemplate = `<div>`;
|
||||
parentComponentTemplate += `<baseInputButtonWrapper v-model="value" groupName="myGroupName" :isMultiSelect="${mockData.isMultiSelect}" value="value1" />`;
|
||||
parentComponentTemplate += `<baseInputButtonWrapper v-model="value" groupName="myGroupName" :isMultiSelect="${mockData.isMultiSelect}" value="value2" />`;
|
||||
parentComponentTemplate += `</div>`;
|
||||
const wrapper = mount(
|
||||
{
|
||||
data() {
|
||||
return {
|
||||
value:
|
||||
mockData.initialValue ??
|
||||
(mockData.isMultiSelect ? [] : ""),
|
||||
$route: {
|
||||
query: {
|
||||
fmgPage: "myPage",
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
template: parentComponentTemplate,
|
||||
components: { baseInputButtonWrapper },
|
||||
},
|
||||
{}
|
||||
);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
@ -2,8 +2,9 @@
|
|||
<label
|
||||
:class="[buttonWrapperClasses, { 'has-error': errors.length > 0 }]"
|
||||
:for="buttonId"
|
||||
@mousedown.left="handleEventAction('click', $event)"
|
||||
@keyup="handleKeyboardNavigation">
|
||||
@focusin="handleFocus"
|
||||
@focusout="handleBlur"
|
||||
@mousedown.left="handleEventAction('click', $event)">
|
||||
<input
|
||||
:type="inputType"
|
||||
:id="buttonId"
|
||||
|
|
@ -13,8 +14,8 @@
|
|||
:aria-required="isRequired"
|
||||
:value="value"
|
||||
:checked="isChecked"
|
||||
@blur="handleBlur"
|
||||
@keypress.enter="handleEventAction('keypressSubmit', $event)"
|
||||
@keypress.space="handleEventAction('space', $event)"
|
||||
@keypress.enter="handleEventAction('enter', $event)"
|
||||
@change="handleEventAction('change', $event)" />
|
||||
|
||||
<slot></slot>
|
||||
|
|
@ -22,43 +23,18 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { useField } from "vee-validate";
|
||||
import { toRef } from "vue";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { handleButtonComponentFocus, handleInputComponentBlur } from "@/helpers/button-question-focus-helper";
|
||||
import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props";
|
||||
|
||||
export default {
|
||||
name: "base-input-button",
|
||||
emits: ["change"],
|
||||
model: {
|
||||
prop: "modelValue",
|
||||
event: "change",
|
||||
},
|
||||
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,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -72,32 +48,34 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
handleEventAction(eventType, e) {
|
||||
const eventTypes = {
|
||||
CHANGE: "change",
|
||||
KEYPRESS_SUBMIT: "keypressSubmit",
|
||||
CLICK: "click",
|
||||
};
|
||||
|
||||
// console.log({
|
||||
// eventType,
|
||||
// e
|
||||
// })
|
||||
if (this.isMultiSelect) {
|
||||
switch (eventType) {
|
||||
case eventTypes.KEYPRESS_SUBMIT:
|
||||
case eventTypes.CHANGE:
|
||||
case this.eventTypes.ENTER:
|
||||
case this.eventTypes.CHANGE:
|
||||
this.handleClick(e);
|
||||
window.lastFocusedInputGroup = this.groupName;
|
||||
this.pushClickEventToGA();
|
||||
this.handlePushClickEventToGACheck(
|
||||
this.eventTypes.CLICK
|
||||
);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (eventType) {
|
||||
case eventTypes.CLICK:
|
||||
case eventTypes.KEYPRESS_SUBMIT:
|
||||
case this.eventTypes.CLICK:
|
||||
case this.eventTypes.ENTER:
|
||||
case this.eventTypes.SPACE:
|
||||
this.handleClick(e);
|
||||
this.test();
|
||||
this.handlePushClickEventToGACheck(
|
||||
this.eventTypes.CLICK
|
||||
);
|
||||
break;
|
||||
case eventTypes.CHANGE:
|
||||
this.selectOnKeypress
|
||||
? this.handleClick(e)
|
||||
: this.handleSelectionChange(e);
|
||||
case this.eventTypes.CHANGE:
|
||||
this.selectingInitiatesLoad
|
||||
? this.handleSelectionChange(e)
|
||||
: this.handleClick(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -119,21 +97,40 @@ export default {
|
|||
this.valueToEmit = this.value;
|
||||
}
|
||||
|
||||
window.currentlySelectedValues =
|
||||
window.currentlySelectedValues ?? {};
|
||||
window.currentlySelectedValues[this.groupName] = this.value;
|
||||
this.handleChange(this.valueToEmit);
|
||||
},
|
||||
handleClick(e) {
|
||||
this.handleSelectionChange(e);
|
||||
this.$emit("buttonClicked", this.valueToEmit);
|
||||
this.$emit("update:modelValue", this.valueToEmit);
|
||||
},
|
||||
handleFocus() {
|
||||
handleButtonComponentFocus({
|
||||
groupName: this.groupName,
|
||||
});
|
||||
},
|
||||
handleBlur() {
|
||||
handleInputComponentBlur({
|
||||
groupName: this.groupName,
|
||||
onButtonQuestionLostFocusCallback: this.handlePushClickEventToGACheck,
|
||||
});
|
||||
},
|
||||
handlePushClickEventToGACheck(source) {
|
||||
if (source === this.eventTypes.CLICK) {
|
||||
this.pushClickEventToGA();
|
||||
} else {
|
||||
if (
|
||||
this.valueToEmit !== null &&
|
||||
!this.isValueSelectedOnClick &&
|
||||
this.isChecked &&
|
||||
this.lastValuePushedToGa != this.value
|
||||
) {
|
||||
this.pushClickEventToGA();
|
||||
}
|
||||
}
|
||||
},
|
||||
pushClickEventToGA(value) {
|
||||
window.firedGaClickEventValues =
|
||||
window.firedGaClickEventValues ?? {};
|
||||
window.firedGaClickEventValues[window.lastFocusedInputGroup] =
|
||||
value ?? this.value;
|
||||
|
||||
console.log("PUSHHH")
|
||||
console.log(this.$route)
|
||||
this.pushEventToGA(
|
||||
this.$route.query[queryStrings.FMG_PAGE],
|
||||
this.GaActions.CLICKED,
|
||||
|
|
@ -141,88 +138,38 @@ export default {
|
|||
true,
|
||||
this.valueToLogType
|
||||
);
|
||||
},
|
||||
handleBlur() {
|
||||
window.lastFocusedInputGroup = this.groupName;
|
||||
window.wasLastFocusedInputMultiselect = this.isMultiSelect;
|
||||
},
|
||||
handleKeyboardNavigation(key) {
|
||||
this.test(key);
|
||||
},
|
||||
test(event) {
|
||||
window.firedGaClickEventValues =
|
||||
window.firedGaClickEventValues ?? {};
|
||||
window.currentlySelectedValues =
|
||||
window.currentlySelectedValues ?? {};
|
||||
|
||||
const keyCode = event?.code;
|
||||
// Keyboard navigation
|
||||
if (keyCode) {
|
||||
if (
|
||||
window.currentlySelectedValues &&
|
||||
window.firedGaClickEventValues &&
|
||||
window.lastFocusedInputGroup &&
|
||||
!window.wasLastFocusedInputMultiselect &&
|
||||
window.currentlySelectedValues[
|
||||
window.lastFocusedInputGroup
|
||||
] &&
|
||||
window.firedGaClickEventValues[
|
||||
window.lastFocusedInputGroup
|
||||
] !=
|
||||
window.currentlySelectedValues[
|
||||
window.lastFocusedInputGroup
|
||||
]
|
||||
) {
|
||||
if (keyCode === "Tab") {
|
||||
this.pushClickEventToGA(
|
||||
window.currentlySelectedValues[
|
||||
window.lastFocusedInputGroup
|
||||
]
|
||||
);
|
||||
} else if (keyCode?.includes("Arrow")) {
|
||||
if (window.lastFocusedInputGroup != this.groupName) {
|
||||
this.pushClickEventToGA(
|
||||
window.currentlySelectedValues[
|
||||
window.lastFocusedInputGroup
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Radio click
|
||||
window.lastFocusedInputGroup = this.groupName;
|
||||
if (
|
||||
window.firedGaClickEventValues[
|
||||
window.lastFocusedInputGroup
|
||||
] !=
|
||||
window.currentlySelectedValues[window.lastFocusedInputGroup]
|
||||
) {
|
||||
this.pushClickEventToGA(
|
||||
window.currentlySelectedValues[
|
||||
window.lastFocusedInputGroup
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
this.setLastValuePushedToGa(this.value);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isChecked() {
|
||||
if (this.isMultiSelect && this.modelValue instanceof Array) {
|
||||
return this.modelValue.includes(this.value);
|
||||
} else if (!this.isMultiSelect) {
|
||||
return this.modelValue == this.value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.modelValue == this.value;
|
||||
},
|
||||
inputType() {
|
||||
return this.isMultiSelect ? "checkbox" : "radio";
|
||||
},
|
||||
buttonId() {
|
||||
return `${this.groupName}-${JSON.stringify(this.value)?.replace(
|
||||
" ",
|
||||
"-"
|
||||
)}`;
|
||||
return `${this.groupName?.replace(" ", "-")}-${this.value
|
||||
?.toString()
|
||||
?.replace(" ", "-")}`;
|
||||
},
|
||||
isValueSelectedOnClick() {
|
||||
return this.isMultiSelect || this.selectingInitiatesLoad;
|
||||
},
|
||||
eventTypes() {
|
||||
return {
|
||||
CHANGE: "change",
|
||||
ENTER: "enter",
|
||||
SPACE: "space",
|
||||
CLICK: "click",
|
||||
};
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
@ -2,189 +2,171 @@ import { shallowMount } from "@vue/test-utils";
|
|||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
|
||||
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
||||
jest.mock(
|
||||
"@/store",
|
||||
() => {
|
||||
return {};
|
||||
},
|
||||
{ virtual: true }
|
||||
);
|
||||
|
||||
// TODO KO
|
||||
describe.skip("buttonQuestion.vue", () => {
|
||||
describe("buttonQuestion.vue", () => {
|
||||
it("Should show overflow classes on fieldset if isOverflowScrollable is true", () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(buttonQuestion, {
|
||||
propsData: {
|
||||
isOverflowScrollable: true,
|
||||
groupName: "group-name"
|
||||
}
|
||||
groupName: "group-name",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const fieldSet = wrapper.find('fieldset');
|
||||
const fieldSet = wrapper.find("fieldset");
|
||||
expect(fieldSet.classes()).toContain("overflow-scroll");
|
||||
});
|
||||
});
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("Fieldset classes should contain row if button type is listCard", () => {
|
||||
// // Act
|
||||
// const wrapper = shallowMount(buttonQuestion, {
|
||||
// propsData: {
|
||||
// buttonType: "listCard",
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// });
|
||||
// // Assert
|
||||
// const Div = wrapper.find('fieldset div');
|
||||
// expect(Div.classes()).toContain("row");
|
||||
// });
|
||||
// });
|
||||
describe("buttonQuestion.vue", () => {
|
||||
it("Fieldset classes should contain row if button type is listCard", () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(buttonQuestion, {
|
||||
propsData: {
|
||||
buttonType: "listCard",
|
||||
groupName: "group-name",
|
||||
},
|
||||
});
|
||||
// Assert
|
||||
const Div = wrapper.find("fieldset div");
|
||||
expect(Div.classes()).toContain("row");
|
||||
});
|
||||
});
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("Fieldset classes should contain d-flex if button type is listButtonHorizontal", () => {
|
||||
// // Act
|
||||
// const wrapper = shallowMount(buttonQuestion, {
|
||||
// propsData: {
|
||||
// buttonType: "listButtonHorizontal",
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// });
|
||||
// // Assert
|
||||
// const Div = wrapper.find('fieldset div');
|
||||
// expect(Div.classes()).toContain("d-flex");
|
||||
// });
|
||||
// });
|
||||
describe("buttonQuestion.vue", () => {
|
||||
it("Fieldset classes should contain d-flex if button type is listButtonHorizontal", () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(buttonQuestion, {
|
||||
propsData: {
|
||||
buttonType: "listButtonHorizontal",
|
||||
groupName: "group-name",
|
||||
},
|
||||
});
|
||||
// Assert
|
||||
const Div = wrapper.find("fieldset div");
|
||||
expect(Div.classes()).toContain("d-flex");
|
||||
});
|
||||
});
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("Fieldset classes should contain ui-radio if button type is radio", () => {
|
||||
// // Act
|
||||
// const wrapper = shallowMount(buttonQuestion, {
|
||||
// propsData: {
|
||||
// buttonType: "radio",
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// });
|
||||
// // Assert
|
||||
// const Div = wrapper.find('fieldset div');
|
||||
// expect(Div.classes()).toContain("ui-radio");
|
||||
// });
|
||||
// });
|
||||
describe("buttonQuestion.vue", () => {
|
||||
it("Fieldset classes should contain ui-radio if button type is radio", () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(buttonQuestion, {
|
||||
propsData: {
|
||||
buttonType: "radio",
|
||||
groupName: "group-name",
|
||||
},
|
||||
});
|
||||
// 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 };
|
||||
|
||||
// // 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");
|
||||
});
|
||||
});
|
||||
|
||||
// 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"],
|
||||
groupName: "group-name",
|
||||
};
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("getColLength should return '' if prop isWide is set to false", () => {
|
||||
// // Act
|
||||
// const localThis = {
|
||||
// isWide: false,
|
||||
// answers: ['a', 'b'],
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
expect(buttonQuestion.computed.getColLength.call(localThis)).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
// 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" };
|
||||
|
||||
// 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.getValue.call(localThis, answer)).toBe(
|
||||
"testText"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// // Assert
|
||||
// expect(buttonQuestion.methods.getValue.call(localThis, answer)).toBe('testText');
|
||||
// });
|
||||
// });
|
||||
describe("buttonQuestion.vue", () => {
|
||||
it("Should return answer.Name if prop useTextForValue is false and there's not buttonLabel and answer.Name exists", async () => {
|
||||
// Act
|
||||
const localThis = { useTextForValue: false };
|
||||
const answer = { Name: "testName", Text: "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.getValue.call(localThis, answer)).toBe(
|
||||
"testName"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// // Assert
|
||||
// expect(buttonQuestion.methods.getValue.call(localThis, answer)).toBe('testName');
|
||||
// });
|
||||
// });
|
||||
// TODO KO
|
||||
describe("buttonQuestion.vue", () => {
|
||||
describe.skip("handleAnswerChange", () => {
|
||||
test("is radio => should emit captured value", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(
|
||||
buttonQuestion,
|
||||
setupMocks({ propsData: { groupName: "group-name" } })
|
||||
);
|
||||
await wrapper.setProps({
|
||||
answers: ["2022", "2021", "2020"],
|
||||
isMultiSelect: false,
|
||||
modelValue: "",
|
||||
});
|
||||
const val = "2021";
|
||||
wrapper.vm.handleAnswerChange(val);
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("Should trigger event modelValue change to new value on when radio button selected", async () => {
|
||||
// // Act
|
||||
// const wrapper = shallowMount(buttonQuestion, setupMocks({propsData: {groupName: "group-name"}}));
|
||||
// await wrapper.setProps({
|
||||
// answers: ["2022", "2021", "2020"],
|
||||
// isMultiSelect: false,
|
||||
// modelValue: []
|
||||
// });
|
||||
// const val = { checkValue: true, value: "2021", }
|
||||
// wrapper.vm.handleCheckedChanged(val);
|
||||
// // Assert
|
||||
// expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2021"]]);
|
||||
// });
|
||||
// });
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual(["2021"]);
|
||||
});
|
||||
|
||||
// describe("buttonQuestion.vue", () => {
|
||||
// it("Should add values to array on checkbox click", () => {
|
||||
// // Act
|
||||
// const wrapper = shallowMount(buttonQuestion, setupMocks({
|
||||
// propsData: {
|
||||
// modelValue: ["2022", "2021", "2020"],
|
||||
// isMultiSelect: true,
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// }));
|
||||
// const val = { checkValue: true, value: "2019", }
|
||||
// wrapper.vm.handleCheckedChanged(val);
|
||||
// // Assert
|
||||
// expect(wrapper.emitted()["update:modelValue"][0]).toEqual([["2022", "2021", "2020", "2019"]]);
|
||||
// });
|
||||
// });
|
||||
test("is checkbox => should emit captured value", async () => {
|
||||
const wrapper = shallowMount(
|
||||
buttonQuestion,
|
||||
setupMocks({ propsData: { groupName: "group-name" } })
|
||||
);
|
||||
await wrapper.setProps({
|
||||
answers: ["2022", "2021", "2020"],
|
||||
isMultiSelect: false,
|
||||
modelValue: "",
|
||||
});
|
||||
const val = ["2022", "2021", , "2019", "2020"];
|
||||
wrapper.vm.handleAnswerChange(val);
|
||||
|
||||
// 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, setupMocks({
|
||||
// propsData: {
|
||||
// isMultiSelect: true,
|
||||
// modelValue: ['a', 'b'],
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// }));
|
||||
// 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, setupMocks({
|
||||
// propsData: {
|
||||
// isMultiSelect: true,
|
||||
// modelValue: ['a', 'b'],
|
||||
// groupName: "group-name"
|
||||
// }
|
||||
// }));
|
||||
|
||||
// const val = { checkValue: false, value: "a", }
|
||||
// wrapper.vm.handleCheckedChanged(val);
|
||||
|
||||
// // Assert
|
||||
// expect(wrapper.vm.selectedValues).toEqual(["b"]);
|
||||
// });
|
||||
// });
|
||||
// Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([
|
||||
["2022", "2021", , "2019", "2020"],
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks(mountOptionsMockData = {}) {
|
||||
const defaultMountOptions = { route: { query: { fmgPage: 'page-name' } } };
|
||||
const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData));
|
||||
const defaultMountOptions = { route: { query: { fmgPage: "page-name" } } };
|
||||
const baseMountOptions = getMountOptions(
|
||||
Object.assign(defaultMountOptions, mountOptionsMockData)
|
||||
);
|
||||
const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions);
|
||||
|
||||
return allMountOptions;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
<legend
|
||||
class="sr-only"
|
||||
:data-focus-target="formatString(groupName)"
|
||||
tabindex="-1"
|
||||
:id="formatString(groupName)">
|
||||
{{ questionText }}
|
||||
{{
|
||||
|
|
@ -44,22 +45,21 @@
|
|||
:groupName="answer.groupName"
|
||||
:isMultiSelect="isMultiSelect"
|
||||
:value="answer.value"
|
||||
:modelValue="modelValue"
|
||||
:selectingInitiatesLoad="selectingInitiatesLoad"
|
||||
:isWide="isWide"
|
||||
:validationRules="validationRules"
|
||||
:textPosition="textPosition"
|
||||
:selectOnKeypress="selectOnKeypress"
|
||||
@blur="handleBlur"
|
||||
@focus="handleFocus"
|
||||
@buttonClicked="handleAnswerChange"
|
||||
:additionalData="additionalData" />
|
||||
:lastValuePushedToGa="lastValuePushedToGa"
|
||||
:setLastValuePushedToGa="setLastValuePushedToGa"
|
||||
v-model="selectedValues"
|
||||
/>
|
||||
<!-- For nested questions -->
|
||||
<transition name="fade" mode="out-in">
|
||||
<div
|
||||
v-if="
|
||||
typeof primaryValue == 'string' &&
|
||||
primaryValue == answer.value
|
||||
typeof selectedValues == 'string' &&
|
||||
selectedValues == answer.value
|
||||
">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
|
@ -80,9 +80,9 @@
|
|||
import listButton from "@/ux-components/list-button/list-button";
|
||||
import listButtonHorizontal from "@/ux-components/list-button-horizontal/list-button-horizontal";
|
||||
import listCard from "@/ux-components/list-card/list-card";
|
||||
import { ErrorMessage } from "vee-validate";
|
||||
import radio from "@/ux-components/radio/radio";
|
||||
import servicePackageRadio from "@/layouts/quote/service-package-question/service-package-radio/service-package-radio";
|
||||
import { ErrorMessage } from "vee-validate";
|
||||
|
||||
export default {
|
||||
name: "buttonQuestion",
|
||||
|
|
@ -118,16 +118,11 @@ export default {
|
|||
suppressError: Boolean,
|
||||
useTextForValue: Boolean,
|
||||
valueToLogType: String,
|
||||
selectOnKeypress: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
additionalData: null
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
primaryValue: "",
|
||||
lastFocusedInputGroup: "",
|
||||
lastValuePushedToGa: null,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -185,7 +180,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,
|
||||
|
|
@ -204,6 +198,14 @@ export default {
|
|||
})
|
||||
);
|
||||
},
|
||||
selectedValues: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(selectedAnswers) {
|
||||
this.$emit("update:modelValue", selectedAnswers);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatString(str) {
|
||||
|
|
@ -227,21 +229,8 @@ export default {
|
|||
: this.formatString(answer.toString());
|
||||
}
|
||||
},
|
||||
handleAnswerChange(primaryAnswerValue) {
|
||||
this.primaryValue = primaryAnswerValue;
|
||||
this.$emit("buttonQuestionChange", primaryAnswerValue);
|
||||
this.$emit("update:modelValue", primaryAnswerValue);
|
||||
},
|
||||
handleBlur() {
|
||||
// this.lastFocusedInputGroup = this.groupName;
|
||||
// window.lastFocusedInputGroup = this.groupName
|
||||
// console.log("blur: ", this.groupName);
|
||||
},
|
||||
handleFocus() {
|
||||
// console.log("focused: ", {
|
||||
// groupName: this.groupName,
|
||||
// lastFocusedInputGroup: this.lastFocusedInputGroup,
|
||||
// });
|
||||
setLastValuePushedToGa(lastValuePushedToGa) {
|
||||
this.lastValuePushedToGa = lastValuePushedToGa;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,164 +1,199 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<fieldset>
|
||||
<legend class="d-flex justify-content-center">Date Picker</legend>
|
||||
<div class="grid-container">
|
||||
<legend class=" sr-only">Date Picker</legend>
|
||||
<div class="calendar-grid-container">
|
||||
<div class="month-year body-small d-flex align-items-center ps-3 small">{{currentMonth}} {{currentYear}}</div>
|
||||
<div class="legend caption d-flex align-items-center justify-content-end"><span class="legend-circle me-1"></span> = Available</div>
|
||||
<div class="nav-back ps-3"><button></button></div>
|
||||
<div class="nav-forward pe-3"><button></button></div>
|
||||
<!-- Do the days of the weeek need to be read? -->
|
||||
<div class="grid-item"><span class="sr-only">Sunday</span>S</div>
|
||||
<div class="grid-item"><span class="sr-only">Monday</span>M</div>
|
||||
<div class="grid-item"><span class="sr-only">Tuesday</span>T</div>
|
||||
<div class="grid-item"><span class="sr-only">Wednesday</span>W</div>
|
||||
<div class="grid-item"><span class="sr-only">Thursday</span>T</div>
|
||||
<div class="grid-item"><span class="sr-only">Friday</span>F</div>
|
||||
<div class="grid-item"><span class="sr-only">Saturday</span>S</div>
|
||||
<div class="grid-item radio-wrapper past-day">
|
||||
<input type="radio" name="day-of-month" id="thirtypast" />
|
||||
<label for="thirtypast"><span>30</span></label>
|
||||
<div class="grid-item caption"><span class="sr-only">Sunday</span>S</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Monday</span>M</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Tuesday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Wednesday</span>W</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Thursday</span>T</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Friday</span>F</div>
|
||||
<div class="grid-item caption"><span class="sr-only">Saturday</span>S</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-25" />
|
||||
<label class="past-day" for="sep-25"><span>25</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper past-day">
|
||||
<input type="radio" name="day-of-month" id="thirtyonepast" />
|
||||
<label for="thirtyonepast"><span>31</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-26" />
|
||||
<label class="past-day" for="sep-26"><span>26</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day first-of-month">
|
||||
<div v-if="isFirstDayOfMonth" class="current-month">OCT</div>
|
||||
<input type="radio" name="day-of-month" id="one" />
|
||||
<label for="one"><span class="sr-only">{{dayOfWeek}} {{pastMonth}} {{dayOfMonth}}</span><span>1</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-27" />
|
||||
<label class="past-day" for="sep-27"><span>27</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="two" />
|
||||
<label for="two"><span>2</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-28" />
|
||||
<label class="past-day" for="sep-28"><span>28</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="three" />
|
||||
<label for="three"><span>3</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-29" />
|
||||
<label class="past-day" for="sep-29"><span>29</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day" :class="[isCurrentDay ? 'current-day' : '']">
|
||||
<div v-if="isCurrentDayDot" class="current-day-dot"></div>
|
||||
<input type="radio" name="day-of-month" id="four" />
|
||||
<label for="four"><span>4</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input disabled type="radio" name="day-of-month" id="sep-30" />
|
||||
<label class="past-day" for="sep-30"><span>30</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="five" />
|
||||
<label for="five"><span>5</span></label>
|
||||
<!-- Use this as the base calendar item -->
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-1" />
|
||||
<label class="highlighted-day selectable-day" :class="[isCurrentDay ? 'current-day' : '']" for="oct-1"><span>1</span>
|
||||
<span class="sr-only">October 1st</span>
|
||||
<div v-if="isFirstDayOfMonth" class="current-month first-day">OCT</div>
|
||||
</label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="six" />
|
||||
<label for="six"><span>6</span></label>
|
||||
<!-- END -->
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-2" />
|
||||
<label class="selectable-day" for="oct-2"><span>2</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="seven" />
|
||||
<label for="seven"><span>7</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-3" />
|
||||
<label class="selectable-day" for="oct-3"><span>3</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="eight" />
|
||||
<label for="eight"><span>8</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-4" />
|
||||
<label class="selectable-day" for="oct-4"><span>4</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="nine" />
|
||||
<label for="nine"><span>9</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-5" />
|
||||
<label class="selectable-day" for="oct-5"><span>5</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="ten" />
|
||||
<label for="ten"><span>10</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-6" />
|
||||
<label class="selectable-day" for="oct-6"><span>6</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="eleven" />
|
||||
<label for="eleven"><span>11</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-7" />
|
||||
<label class="selectable-day" for="oct-7"><span>7</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twelve" />
|
||||
<label for="twelve"><span>12</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-8" />
|
||||
<label class="selectable-day" for="oct-8"><span>8</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="thirteen" />
|
||||
<label for="thirteen"><span>13</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-9" />
|
||||
<label class="selectable-day" for="oct-9"><span>9</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="fourteen" />
|
||||
<label for="fourteen"><span>17</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-10" />
|
||||
<label class="selectable-day" for="oct-10"><span>10</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="fifteen" />
|
||||
<label for="fifteen"><span>15</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-11" />
|
||||
<label class="selectable-day" for="oct-11"><span>11</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="sixteen" />
|
||||
<label for="sixteen"><span>16</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-12" />
|
||||
<label class="selectable-day" for="oct-12"><span>12</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="seventeen" />
|
||||
<label for="seventeen"><span>17</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-13" />
|
||||
<label class="selectable-day" for="oct-13"><span>13</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="eighteen" />
|
||||
<label for="eighteen"><span>18</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-14" />
|
||||
<label class="selectable-day" for="oct-14"><span>14</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="nineteen" />
|
||||
<label for="nineteen"><span>19</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-15" />
|
||||
<label class="selectable-day" for="oct-15"><span>15</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twenty" />
|
||||
<label for="twenty"><span>20</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-16" />
|
||||
<label class="selectable-day" for="oct-16"><span>16</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentyone" />
|
||||
<label for="twentyone"><span>21</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-17" />
|
||||
<label class="selectable-day" for="oct-17"><span>17</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentytwo" />
|
||||
<label for="twentytwo"><span>22</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-18" />
|
||||
<label class="selectable-day" for="oct-18"><span>18</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentythree" />
|
||||
<label for="twentythree"><span>23</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-19" />
|
||||
<label class="selectable-day" for="oct-19"><span>19</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentyfour" />
|
||||
<label for="twentyfour"><span>24</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-20" />
|
||||
<label class="selectable-day" for="oct-20"><span>20</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentyfive" />
|
||||
<label for="twentyfive"><span>25</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-21" />
|
||||
<label class="selectable-day" for="oct-21"><span>21</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentysix" />
|
||||
<label for="twentysix"><span>26</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-22" />
|
||||
<label class="selectable-day" for="oct-22"><span>22</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentyseven" />
|
||||
<label for="twentyseven"><span>27</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-23" />
|
||||
<label class="selectable-day" for="oct-23"><span>23</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentyeight" />
|
||||
<label for="twentyeight"><span>28</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-24" />
|
||||
<label class="selectable-day" for="oct-24"><span>24</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="twentynine" />
|
||||
<label for="twentynine"><span>29</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-25" />
|
||||
<label class="selectable-day" for="oct-25"><span>25</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="thirty" />
|
||||
<label for="thirty"><span>30</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-26" />
|
||||
<label class="selectable-day" for="oct-26"><span>26</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper highlighted-day">
|
||||
<input type="radio" name="day-of-month" id="thirtyone" />
|
||||
<label for="thirtyone"><span>31</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-27" />
|
||||
<label class="selectable-day" for="oct-27"><span>27</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper future-day">
|
||||
<input type="radio" name="day-of-month" id="onefuture" />
|
||||
<label for="onefuture"><span>1</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-28" />
|
||||
<label class="selectable-day" for="oct-28"><span>28</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper future-day">
|
||||
<input type="radio" name="day-of-month" id="twofuture" />
|
||||
<label for="twofuture"><span>2</span></label>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-29" />
|
||||
<label class="selectable-day" for="oct-29"><span>29</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-30" />
|
||||
<label class="selectable-day" for="nov-30"><span>30</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="oct-31" />
|
||||
<label class="selectable-day" for="oct-31"><span>31</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-1" />
|
||||
<label class="future-day selectable-day" for="nov-1"><span>1</span>
|
||||
<div v-if="isFirstDayOfMonth" class="current-month first-day">OCT</div>
|
||||
</label>
|
||||
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-2" />
|
||||
<label class="future-day selectable-day" for="nov-2"><span>2</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-3" />
|
||||
<label class="future-day selectable-day" for="nov-3"><span>3</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-4" />
|
||||
<label class="future-day selectable-day" for="nov-4"><span>4</span></label>
|
||||
</div>
|
||||
<div class="grid-item radio-wrapper">
|
||||
<input type="radio" name="day-of-month" id="nov-5" />
|
||||
<label class="future-day selectable-day" for="nov-5"><span>5</span></label>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
<form class="w-100 d-flex justify-content-center mt-5">
|
||||
<input type="date">
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -166,41 +201,94 @@ export default {
|
|||
name: "datePicker",
|
||||
data() {
|
||||
return {
|
||||
dayOfWeek: "Tuesday",
|
||||
dayOfMonth: "1st",
|
||||
pastMonth: "October",
|
||||
currentMonth: "October",
|
||||
currentYear: "2022",
|
||||
isFirstDayOfMonth: true,
|
||||
isCurrentDay: true,
|
||||
isCurrentDayDot: true,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.container {
|
||||
//max-width: 341px;
|
||||
label {
|
||||
display: initial; //Override Bootstrap default
|
||||
}
|
||||
#availableDates label:nth-child(14n):after {
|
||||
content: "\a";
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
.calendar-grid-container {
|
||||
margin: 0 auto;
|
||||
max-width: 341px;
|
||||
max-width: 414px;
|
||||
display: grid;
|
||||
grid-gap: 4px;
|
||||
grid-template-columns: auto auto auto auto auto auto auto;
|
||||
grid-template-columns: repeat(7, 1fr);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 4px;
|
||||
|
||||
.grid-item {
|
||||
text-align: center;
|
||||
padding: 4px;
|
||||
margin: 10%;
|
||||
}
|
||||
|
||||
.month-year {
|
||||
grid-area: 1 / 1 / 2 / 4;
|
||||
text-transform: uppercase;
|
||||
font-weight: 300;
|
||||
letter-spacing: .75px;
|
||||
}
|
||||
.legend {
|
||||
grid-area: 1 / 4 / 2 / 6;
|
||||
.legend-circle {
|
||||
border-radius: 50%;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
}
|
||||
}
|
||||
.nav-forward {
|
||||
grid-area: 1 / 7 / 2 / 8;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M6.66831 5.99879C6.66955 6.17554 6.60074 6.34558 6.47695 6.47168L1.15501 11.8017C1.02812 11.9287 0.85603 12 0.676587 12C0.497145 12 0.325053 11.9287 0.198168 11.8017C0.0712831 11.6748 7.268e-09 11.5026 8.07183e-09 11.3231C8.87567e-09 11.1436 0.0712831 10.9714 0.198168 10.8445L5.05126 5.99879L0.198168 1.14736C0.0725521 1.02042 0.00248585 0.848755 0.00338306 0.670131C0.00428028 0.491506 0.0760674 0.320554 0.202952 0.194881C0.329837 0.0692091 0.501426 -0.000888818 0.679971 9.54485e-06C0.858515 0.000906955 1.02939 0.0727263 1.15501 0.199668L6.47312 5.52399C6.59843 5.65017 6.66862 5.82092 6.66831 5.99879Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back {
|
||||
grid-area: 1 / 6 / 2 / 7;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
button {
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 7px;
|
||||
height: 12px;
|
||||
background-image: url("data:image/svg+xml,%3Csvg width='7' height='12' viewBox='0 0 7 12' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0.331685 6.00121C0.330445 5.82446 0.399256 5.65442 0.523053 5.52832L5.84499 0.198256C5.97188 0.0713149 6.14397 -8.63821e-08 6.32341 -6.72174e-08C6.50285 -4.80527e-08 6.67495 0.071315 6.80183 0.198256C6.92872 0.325198 7 0.497368 7 0.67689C7 0.856412 6.92872 1.02858 6.80183 1.15552L1.94874 6.00121L6.80183 10.8526C6.92745 10.9796 6.99751 11.1512 6.99662 11.3299C6.99572 11.5085 6.92393 11.6794 6.79705 11.8051C6.67016 11.9308 6.49857 12.0009 6.32003 12C6.14148 11.9991 5.97061 11.9273 5.84499 11.8003L0.526881 6.47601C0.401568 6.34983 0.331375 6.17908 0.331685 6.00121Z' fill='%231574A1'/%3E%3C/svg%3E%0A");
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav-back,
|
||||
.nav-forward {
|
||||
button {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
border: 2.5px solid $blue;
|
||||
box-shadow: none;
|
||||
background-color: $blue-100;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.radio-wrapper {
|
||||
|
|
@ -208,252 +296,136 @@ export default {
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0;
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
outline: none;
|
||||
|
||||
.current-day-dot {
|
||||
position: absolute;
|
||||
input[type="radio"] {
|
||||
position: absolute; //override bootstrap
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
|
||||
}
|
||||
|
||||
&.first-of-month {
|
||||
.current-month {
|
||||
position: absolute;
|
||||
font-size: 9px;
|
||||
top: 4px;
|
||||
color: #1574a1;
|
||||
&:focus-visible + label {
|
||||
box-shadow: 0 0 0 2.5px $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.highlighted-day {
|
||||
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
// opacity: 0;
|
||||
height: 0.1px;
|
||||
width: 0.1px;
|
||||
|
||||
+ label {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
color: #1574a1;
|
||||
|
||||
span {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #1574a1;
|
||||
background-color: #e4f1f7;
|
||||
color: #1574a1;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
&:focus + label,
|
||||
&:checked:focus + label {
|
||||
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
|
||||
background-color: $blue;
|
||||
color: $white;
|
||||
&.past-day {
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
color: $gray-500;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
&:checked {
|
||||
+ label::after {
|
||||
background: #1574a1;
|
||||
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
|
||||
color: #fff;
|
||||
}
|
||||
+ label span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
+ label::before {
|
||||
background-color: #1574a1;
|
||||
}
|
||||
+ label span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.past-day {
|
||||
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
height: 0.1px;
|
||||
width: 0.1px;
|
||||
|
||||
+ label {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
color: #8E9292;
|
||||
|
||||
span {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
color: #1574a1;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
&:checked {
|
||||
+ label::after {
|
||||
background: #1574a1;
|
||||
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
|
||||
color: #fff;
|
||||
}
|
||||
+ label span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
&:focus {
|
||||
+ label::before {
|
||||
background-color: transparent;
|
||||
}
|
||||
+ label span {
|
||||
color: #1574a1;
|
||||
}
|
||||
}
|
||||
|
||||
&:active {
|
||||
+ label::before {
|
||||
background-color: #1574a1;
|
||||
}
|
||||
+ label span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.future-day {
|
||||
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
height: 0.1px;
|
||||
width: 0.1px;
|
||||
|
||||
+ label {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 50%;
|
||||
color: #4D5151;
|
||||
|
||||
span {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1px solid transparent;
|
||||
background-color: transparent;
|
||||
color: #1574a1;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.current-day {
|
||||
|
||||
input[type="radio"] {
|
||||
position: absolute;
|
||||
// opacity: 0;
|
||||
height: 0.1px;
|
||||
width: 0.1px;
|
||||
|
||||
&:after {
|
||||
content: "\A";
|
||||
width:4px;
|
||||
height:4px;
|
||||
border-radius:50%;
|
||||
background: #1574a1;
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: -1px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
&:checked {
|
||||
+ label::after {
|
||||
background: #1574a1;
|
||||
box-shadow: 0 0 0 3px #fff, 0 0 0 5.5px #1574a1;
|
||||
color: #fff;
|
||||
}
|
||||
+ label span {
|
||||
color: #fff;
|
||||
}
|
||||
&.current-day {
|
||||
&:after {
|
||||
background: #fff;
|
||||
background-color: $white;
|
||||
}
|
||||
.first-day {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:checked + label {
|
||||
color: $white;
|
||||
background: $blue;
|
||||
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue;
|
||||
&:after {
|
||||
background-color: $white;
|
||||
}
|
||||
.first-day {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.first-day {
|
||||
position: absolute;
|
||||
font-size: 9px;
|
||||
font-weight: 500;
|
||||
color: $gray-600;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
label {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
min-width: 40px;
|
||||
border-radius: 50%;
|
||||
|
||||
&.past-day {
|
||||
color: $gray-500;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
&.selectable-day {
|
||||
color: $blue;
|
||||
background-color: $blue-100;
|
||||
border: 1px solid $blue;
|
||||
}
|
||||
|
||||
&.current-day {
|
||||
&:after {
|
||||
content: '';
|
||||
width: .25rem;
|
||||
height: .25rem;
|
||||
border-radius: 50%;
|
||||
background-color: $blue;
|
||||
position: absolute;
|
||||
top: 28px;
|
||||
}
|
||||
.first-day {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
|
||||
&.future-day {
|
||||
color: $gray-600;
|
||||
background-color: transparent;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
span {
|
||||
position: relative;
|
||||
top: -.25rem;
|
||||
&.small {
|
||||
font-size: .75rem;
|
||||
color: $gray-550;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:checked {
|
||||
@include media-breakpoint-up(sm) {
|
||||
box-shadow: 0 0 0 4px transparent;
|
||||
background-color: $blue;
|
||||
color: $white;
|
||||
&:after {
|
||||
background-color: $white;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
&.current-day {
|
||||
+ .first-day {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
.first-day {
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
+ p {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,6 @@
|
|||
<template>
|
||||
<div v-for="(q, i) in questions" :key="i">
|
||||
<transition appear name="fade" mode="out-in">
|
||||
<!-- <buttonQuestion
|
||||
v-if="q.answerSelected || (q.questionSequence === currentQuestionNum)"
|
||||
class="radioQuestion"
|
||||
:class="(q.questionSequence === currentQuestionNum) && 'current-question'"
|
||||
:questionText="q.questionText"
|
||||
:answers="q.answers"
|
||||
:groupName="`question-${glassIndex}-${q.questionSequence}`"
|
||||
v-model="q.answerSelected"
|
||||
isRequired
|
||||
:validationRules="validationRules"
|
||||
/> -->
|
||||
<buttonQuestion
|
||||
v-if="q.answerSelected || (q.questionSequence === currentQuestionNum)"
|
||||
class="radioQuestion"
|
||||
|
|
|
|||
|
|
@ -18,11 +18,11 @@ const storeActions = {
|
|||
LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin",
|
||||
LOOKUP_VIN_BY_PLATE: "lookupVinByPlate",
|
||||
LOOKUP_VIN_BY_ADDRESS: "lookupVinByAddress",
|
||||
|
||||
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
||||
GET_PARTS: "getParts",
|
||||
GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions",
|
||||
GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer",
|
||||
GET_PART_FROM_CAPABILITY_QUESTION_ANSWER:
|
||||
"getPartFromCapabilityQuestionAnswer",
|
||||
GET_MOLDING_QUESTIONS: "getMoldingQuestions",
|
||||
SAVE_SESSION: "saveSession",
|
||||
LOAD_SESSION: "loadSession",
|
||||
|
|
@ -45,22 +45,23 @@ const storeActions = {
|
|||
RESET_STATE: "resetState",
|
||||
|
||||
// SAVE COMPONENT STATE
|
||||
SAVE_VEHICLE_YEAR: "saveVehicleYear",
|
||||
SAVE_VEHICLE_MAKE:"saveVehicleMake",
|
||||
SAVE_VEHICLE_MODEL:"saveVehicleModel",
|
||||
SAVE_VEHICLE_YEAR: "saveVehicleYear",
|
||||
SAVE_VEHICLE_MAKE: "saveVehicleMake",
|
||||
SAVE_VEHICLE_MODEL: "saveVehicleModel",
|
||||
SAVE_VEHICLE_STYLE: "saveVehicleStyle",
|
||||
SAVE_VEHICLE_DAMAGE: "saveVehicleDamage",
|
||||
SAVE_VIN_LOOKUP: "saveVinLookup",
|
||||
SAVE_SERVICE_LOCATION: "saveServiceLocation",
|
||||
SAVE_EMAIL: "saveEmail",
|
||||
SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup",
|
||||
SAVE_VIN: "saveVin",
|
||||
SAVE_VEHICLE_DAMAGE: "saveVehicleDamage",
|
||||
SAVE_VIN_LOOKUP: "saveVinLookup",
|
||||
SAVE_SERVICE_LOCATION: "saveServiceLocation",
|
||||
SAVE_EMAIL: "saveEmail",
|
||||
SAVE_REGISTRATION_LICENSE_PLATE_LOOKUP: "saveRegistrationLicensePlateLookup",
|
||||
SAVE_VIN: "saveVin",
|
||||
SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup",
|
||||
SAVE_GLASS_PARTS: "saveGlassParts",
|
||||
SAVE_PART_QUESTION_ANSWERS: "savePartQuestionAnswers",
|
||||
RESET_MOLDING_AND_CAPABILITY_QUESTIONS_IF_NEEDED: "resetMoldingAndCapabilityQuestionAnswersIfNeeded",
|
||||
RESET_MOLDING_AND_CAPABILITY_QUESTIONS_IF_NEEDED:
|
||||
"resetMoldingAndCapabilityQuestionAnswersIfNeeded",
|
||||
SAVE_MOLDING_QUESTION_ANSWERS: "saveMoldingQuestionAnswers",
|
||||
SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers"
|
||||
SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers",
|
||||
};
|
||||
|
||||
export { storeActions };
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
const storeMutations = {
|
||||
|
||||
// VEHICLE MUTATIONS
|
||||
UPDATE_YEAR: "updateYear",
|
||||
UPDATE_MAKE: "updateMake",
|
||||
|
|
@ -22,7 +21,7 @@ const storeMutations = {
|
|||
UPDATE_GLASS_PARTS: "updateGlassParts",
|
||||
UPDATE_OTHER_PARTS: "updateOtherParts",
|
||||
|
||||
UPDATE_REGISTRATION_LICENSE_PLATE : "updateRegistrationLicensePlate",
|
||||
UPDATE_REGISTRATION_LICENSE_PLATE: "updateRegistrationLicensePlate",
|
||||
UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress",
|
||||
UPDATE_REGISTRATION_CITY: "updateRegistrationCity",
|
||||
UPDATE_REGISTRATION_STATE: "updateRegistrationState",
|
||||
|
|
@ -69,4 +68,4 @@ const storeMutations = {
|
|||
UPDATE_TRIGGERED_SITE_ENTRY: "updateTriggeredSiteEntry",
|
||||
};
|
||||
|
||||
export { storeMutations };
|
||||
export { storeMutations };
|
||||
42
src/helpers/button-question-focus-helper.js
Normal file
42
src/helpers/button-question-focus-helper.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* Helper for GA click event. When the user mouse clicks on a `base-input-button`, we
|
||||
* push the click event. When the user tabs through a list of radio buttons via a
|
||||
* keyboard, we only want to push the GA click event iff the select was deliberate
|
||||
* (space/enter key) or if there is a selection and the user tabs off of the radio group.
|
||||
*/
|
||||
|
||||
let lastFocusedInputGroupName = "";
|
||||
let onButtonQuestionLostFocusCallback = null;
|
||||
|
||||
// TODO KO add tests
|
||||
const handleAnyComponentFocus = (e) => {
|
||||
const targetType = e.target.type;
|
||||
if (targetType !== "radio" && targetType !== "checkbox") {
|
||||
invokeButtonQuestionLostFocusCallback();
|
||||
}
|
||||
};
|
||||
|
||||
const handleButtonComponentFocus = (e) => {
|
||||
if (e && lastFocusedInputGroupName !== e.groupName) {
|
||||
invokeButtonQuestionLostFocusCallback();
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputComponentBlur = (e) => {
|
||||
if (e) {
|
||||
lastFocusedInputGroupName = e.groupName;
|
||||
onButtonQuestionLostFocusCallback = e.onButtonQuestionLostFocusCallback;
|
||||
}
|
||||
};
|
||||
|
||||
const invokeButtonQuestionLostFocusCallback = () => {
|
||||
if (onButtonQuestionLostFocusCallback) {
|
||||
onButtonQuestionLostFocusCallback();
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
handleAnyComponentFocus,
|
||||
handleButtonComponentFocus,
|
||||
handleInputComponentBlur,
|
||||
};
|
||||
|
|
@ -18,7 +18,6 @@ export async function loadSessionIfPresent() {
|
|||
return null;
|
||||
}
|
||||
|
||||
// TODO KO isOrderDifferent and isOrderSubmitted
|
||||
// Reset state if cookie says to.
|
||||
if (funnelCookie.ShouldResetState) {
|
||||
baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||
|
|
|
|||
|
|
@ -34,14 +34,14 @@ describe("addressVehiclesQuestion.vue", () => {
|
|||
const wrapper = shallowMount(addressVehiclesQuestion, {
|
||||
mixins: [mockMixin],
|
||||
propsData: {
|
||||
vehicles: ["1", "2"],
|
||||
modelValue: ["1", "2"],
|
||||
vehicles: ["1", "2", "newValue"],
|
||||
modelValue: "2",
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
const localThis = { $emit: jest.fn() }
|
||||
addressVehiclesQuestion.computed.selectedVehicleVinAsArray.set.call(localThis, ['newValue']);
|
||||
addressVehiclesQuestion.computed.selectedVehicleVin.set.call(localThis, 'newValue');
|
||||
|
||||
// Assert
|
||||
expect(localThis.$emit).toBeCalledWith("update:modelValue", "newValue");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
groupName="ChooseAddressVehicle"
|
||||
:questionText="questionText"
|
||||
:answers="vehicles"
|
||||
v-model="selectedVehicleVinAsArray"
|
||||
v-model="selectedVehicleVin"
|
||||
isRequired
|
||||
:validation-rules="validationRules"
|
||||
:valueToLogType="ValueToLogTypes.LAST_5"
|
||||
|
|
@ -63,18 +63,16 @@ export default {
|
|||
questionText() {
|
||||
return this.getCmsContent("VehicleConfirmationQuestion", "QuestionText");
|
||||
},
|
||||
selectedVehicleVinAsArray: {
|
||||
selectedVehicleVin: {
|
||||
get: function() {
|
||||
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
|
||||
return modelValueAsArray;
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
const newValueAsScalar = newValue && newValue.length > 0 ? newValue[newValue.length-1] : null;
|
||||
this.$emit("update:modelValue", newValueAsScalar);
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
selectedVehicle() { // this computed is only needed for the computed differentVehicleAlertBody text above
|
||||
return this.vehicles.find( ({ vin }) => vin === this.selectedVehicleVinAsArray[this.selectedVehicleVinAsArray.length-1] );
|
||||
return this.vehicles.find( ({ vin }) => vin === this.selectedVehicleVin[this.selectedVehicleVin.length-1] );
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ export default {
|
|||
selectedVehicleVin: {
|
||||
handler() {
|
||||
// does this vehicle match the previously selected carId?
|
||||
this.isCarIdDifferent = this.selectedVehicle.vehicle.carId !== store.getters.vehicle.carId;
|
||||
this.isCarIdDifferent = this.selectedVehicle?.vehicle.carId !== store.getters.vehicle.carId;
|
||||
if (this.isCarIdDifferent) {
|
||||
this.$refs.funnelFooter.updateButtonText(`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model}`);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@
|
|||
:answers="answersToDisplay"
|
||||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedDamageLocations"
|
||||
isRequired
|
||||
v-model="selectedValues"
|
||||
validationRules="damage-location-required"
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -26,12 +27,11 @@ export default ({
|
|||
name: "damageLocationQuestion",
|
||||
data(){
|
||||
return {
|
||||
damageOptions: {},
|
||||
selectedDamageLocations: this.modelValue
|
||||
damageOptions: Object,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modelValue: [Array, String, Number],
|
||||
props: {
|
||||
modelValue: Array,
|
||||
groupName: String,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
|
|
@ -47,6 +47,14 @@ export default ({
|
|||
answersFromCms(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
},
|
||||
selectedValues: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
damageOptionsMap(){
|
||||
return {
|
||||
Windshield: true,
|
||||
|
|
@ -69,11 +77,6 @@ export default ({
|
|||
});
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedDamageLocations(selectedDamageLocations) {
|
||||
this.$emit("update:modelValue", selectedDamageLocations);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -36,8 +36,7 @@ describe("replace-options-question.vue", () => {
|
|||
});
|
||||
|
||||
describe("replace-options-question.vue", () => {
|
||||
// TODO KO
|
||||
test.skip("when updateSelectedValues method is called with a single answerToDisplay it will call to update this.selectedValues", async () => {
|
||||
test("when updateSelectedValues method is called with a single answerToDisplay it will call to update this.selectedReplaceOptions", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper, cmsContent, replaceOptions
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
:answers="answersToDisplay"
|
||||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedReplaceOptions"
|
||||
v-model="selectedValues"
|
||||
:validationRules="validationRules"
|
||||
:suppressError="suppressError"
|
||||
:isRequired="isRequired"
|
||||
|
|
@ -20,22 +20,19 @@
|
|||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import store from "@/store";
|
||||
// import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default ({
|
||||
name: "replaceOptionsQuestion",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
data(){
|
||||
return {
|
||||
replaceOptions: [],
|
||||
selectedReplaceOptions: this.modelValue
|
||||
replaceOptions: this.isMultiSelect ? [] : "",
|
||||
}
|
||||
},
|
||||
props: {
|
||||
modelValue: [Array, String, Number],
|
||||
isAvailable: Boolean,
|
||||
filterByVehicleCategory: Boolean,
|
||||
groupName: String,
|
||||
modelValue: [Array, String, Number],
|
||||
isMultiSelect: Boolean,
|
||||
validationRules: String,
|
||||
suppressError: Boolean,
|
||||
|
|
@ -43,15 +40,13 @@ export default ({
|
|||
isRequired: Boolean,
|
||||
},
|
||||
methods: {
|
||||
initializeComponent(replaceOptions) {
|
||||
initializeComponent(replaceOptions){
|
||||
this.replaceOptions = replaceOptions;
|
||||
},
|
||||
updateSelectedValues() {
|
||||
// UPDATE SELECTEDVALUES IF ONLY ONE ANSWER
|
||||
// ex: BackGlass stationary
|
||||
if (Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) {
|
||||
const selectedAnswer = this.answersToDisplay[0].Name;
|
||||
this.selectedReplaceOptions = this.isMultiSelect ? [selectedAnswer] : selectedAnswer;
|
||||
if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1) {
|
||||
this.selectedValues = this.isMultiSelect ? [this.answersToDisplay[0].Name] : this.answersToDisplay[0].Name;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -62,6 +57,14 @@ export default ({
|
|||
answersFromCms(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
},
|
||||
selectedValues: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
answersToDisplay(){
|
||||
const filteredAnswers = Array.isArray(this.answersFromCms)
|
||||
? this.answersFromCms.filter(ans =>
|
||||
|
|
@ -88,11 +91,8 @@ export default ({
|
|||
},
|
||||
shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) {
|
||||
if (!shouldDisplayReplaceOptionsQuestion) {
|
||||
this.selectedReplaceOptions = [];
|
||||
this.selectedValues = this.isMultiSelect ? [] : "";
|
||||
}
|
||||
},
|
||||
selectedReplaceOptions(selectedReplaceOptions) {
|
||||
this.$emit("update:modelValue", selectedReplaceOptions);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export default ({
|
|||
name: "sideDoorOptions",
|
||||
props: {
|
||||
groupName: String,
|
||||
modelValue: Array,
|
||||
modelValue: Object,
|
||||
selectedDamageLocations: Array,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
|
|
|
|||
|
|
@ -55,8 +55,7 @@ jest.mock("@/store", () => ({
|
|||
},
|
||||
}));
|
||||
|
||||
// TODO KO
|
||||
describe.skip("vehicle-damage.vue", () => {
|
||||
describe("vehicle-damage.vue", () => {
|
||||
describe("navigation", () => {
|
||||
test("BackButtonAction triggers a router.navigateWithoutSaving change", async () => {
|
||||
|
||||
|
|
@ -130,20 +129,20 @@ describe.skip("vehicle-damage.vue", () => {
|
|||
},
|
||||
});
|
||||
|
||||
wrapper.vm.selectedDamageLocations = ["Windshield", "SideDoor", "RearWindow"];
|
||||
wrapper.vm.selectedWindshieldOptions = {
|
||||
selectedWindshieldChipCount: null,
|
||||
selectedWindshieldReplaceOptions: ["Single"],
|
||||
selectedWindshieldDamageType: "Replace"
|
||||
};
|
||||
|
||||
wrapper.vm.sideDoorOptionsData = {
|
||||
selectedDoorSides: ["DriverSide", "PassengerSide"],
|
||||
selectedDriverSideReplaceOptions: ["Back"],
|
||||
selectedPassengerSideReplaceOptions: ["Quarter"]
|
||||
};
|
||||
|
||||
wrapper.vm.selectedRearReplaceOptions = ["Stationary"];
|
||||
wrapper.setData({
|
||||
selectedDamageLocations: ["Windshield", "SideDoor", "RearWindow"],
|
||||
selectedWindshieldOptions: {
|
||||
selectedWindshieldChipCount: null,
|
||||
selectedWindshieldReplaceOptions: ["Single"],
|
||||
selectedWindshieldDamageType: "Replace"
|
||||
},
|
||||
sideDoorOptionsData: {
|
||||
selectedDoorSides: ["DriverSide", "PassengerSide"],
|
||||
selectedDriverSideReplaceOptions: ["Back"],
|
||||
selectedPassengerSideReplaceOptions: ["Quarter"]
|
||||
},
|
||||
selectedRearReplaceOptions: "Stationary",
|
||||
})
|
||||
|
||||
const expectedGlassToReplace = [{ glassLocation: "Windshield", glassName: "Single" }, { glassLocation: "Driver", glassName: "Back" },
|
||||
{ glassLocation: "Passenger", glassName: "Quarter" }, { glassLocation: "Rear", glassName: "Stationary" }];
|
||||
|
|
@ -520,19 +519,19 @@ describe.skip("vehicle-damage.vue", () => {
|
|||
|
||||
const storeWindshieldOptions = [[1, false, "Windshield", "Single", {
|
||||
selectedWindshieldDamageType: damageLocationsSelected.REPLACE,
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.SINGLE]
|
||||
selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.SINGLE]
|
||||
}],
|
||||
[2, false, "Windshield", "Driver", {
|
||||
selectedWindshieldDamageType: damageLocationsSelected.REPLACE,
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.DRIVER]
|
||||
selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.DRIVER]
|
||||
}],
|
||||
[3, false, "Windshield", "Passenger", {
|
||||
selectedWindshieldDamageType: damageLocationsSelected.REPLACE,
|
||||
selectedWindshieldChipCount: [], selectedWindshieldReplaceOptions: [damageLocationsSelected.PASSENGER]
|
||||
selectedWindshieldChipCount: null, selectedWindshieldReplaceOptions: [damageLocationsSelected.PASSENGER]
|
||||
}],
|
||||
[4, true, "", "", {
|
||||
selectedWindshieldDamageType: damageLocationsSelected.REPAIR,
|
||||
selectedWindshieldChipCount: [2], selectedWindshieldReplaceOptions: []
|
||||
selectedWindshieldChipCount: 2, selectedWindshieldReplaceOptions: []
|
||||
}]
|
||||
];
|
||||
test.each(storeWindshieldOptions)("getWindshieldOptionsFromStore test #%s", async (testNum, isRepair, damageLocation, damageName, expectedWindshieldOptions) => {
|
||||
|
|
@ -619,8 +618,8 @@ describe.skip("vehicle-damage.vue", () => {
|
|||
expect(glassSelections).toEqual(expectedGlass);
|
||||
});
|
||||
|
||||
const rearReplaceOptions = [["Rear", "Stationary", [damageLocationsSelected.STATIONARY]],
|
||||
["Rear", "Slider", [damageLocationsSelected.SLIDER]]
|
||||
const rearReplaceOptions = [["Rear", "Stationary", damageLocationsSelected.STATIONARY],
|
||||
["Rear", "Slider", damageLocationsSelected.SLIDER]
|
||||
];
|
||||
test.each(rearReplaceOptions)("getRearReplaceOptionsFromStore for %s-%s returns expected %s", async (damageLocation, damageName, expectedGlass) => {
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ describe("windshield-chip-count-question.vue", () => {
|
|||
const { wrapper } = setupMocks({ modelValueProp: 1 });
|
||||
|
||||
//Act
|
||||
await wrapper.setData({ numberOfChips: 2 });
|
||||
wrapper.vm.selectedValue = "2";
|
||||
|
||||
//Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([2]);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
:groupName="groupName"
|
||||
buttonType="listButtonHorizontal"
|
||||
useTextForValue
|
||||
v-model="numberOfChips"
|
||||
v-model="selectedValue"
|
||||
:validationRules="validationRules"
|
||||
isRequired
|
||||
/>
|
||||
|
|
@ -17,16 +17,9 @@
|
|||
|
||||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
// import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default ({
|
||||
name: "windshieldChipCountQuestion",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
numberOfChips: this.modelValue
|
||||
}
|
||||
},
|
||||
name: "windshieldOptions",
|
||||
props: {
|
||||
modelValue: [String, Number],
|
||||
groupName: String,
|
||||
|
|
@ -41,14 +34,18 @@ export default ({
|
|||
answersFromCms(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
},
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
const numberValue = Number(newValue);
|
||||
this.$emit("update:modelValue", numberValue);
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
watch: {
|
||||
numberOfChips(numberOfChips) {
|
||||
this.$emit("update:modelValue", numberOfChips);
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -15,7 +15,7 @@ describe("windshield-damage-type-question.vue", () => {
|
|||
await wrapper.vm.$nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.selectedWindshieldDamageType).toEqual("Repair");
|
||||
expect(wrapper.vm.selectedValues).toEqual("Repair");
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: "Replace" }]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,34 +1,25 @@
|
|||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div
|
||||
class="windshield-damage-type-question"
|
||||
v-if="isAvailable"
|
||||
aria-live="polite">
|
||||
<div class="windshield-damage-type-question" v-if="isAvailable" aria-live="polite">
|
||||
<buttonQuestion
|
||||
:questionText="questionText"
|
||||
:answers="answersFromCms"
|
||||
:groupName="groupName"
|
||||
buttonType="listCard"
|
||||
v-model="selectedWindshieldDamageType"
|
||||
v-model="selectedValues"
|
||||
:suppressError="suppressError"
|
||||
:validationRules="validationRules"
|
||||
isRequired />
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
// import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
export default ({
|
||||
name: "windshieldDamageTypeQuestion",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
selectedWindshieldDamageType: this.modelValue,
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
groupName: String,
|
||||
|
|
@ -38,20 +29,23 @@ export default {
|
|||
cmsWidgetName: String,
|
||||
},
|
||||
computed: {
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
questionText(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
answersFromCms() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "Answers");
|
||||
answersFromCms(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'Answers');
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedWindshieldDamageType(selectedWindshieldDamageType) {
|
||||
this.$emit("update:modelValue", selectedWindshieldDamageType);
|
||||
selectedValues: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
},
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -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.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: String,
|
||||
modelValue: Object,
|
||||
selectedDamageLocations: Array,
|
||||
hasRepairReplaceConflict: Boolean,
|
||||
hasSplitSingleConflict: Boolean,
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<template>
|
||||
<buttonQuestion
|
||||
class="radioQuestion"
|
||||
isOverflowScrollable
|
||||
selectingInitiatesLoad
|
||||
:questionText="questionText"
|
||||
:answers="makes"
|
||||
groupName="ChooseVehicleMake"
|
||||
textPosition="text-start"
|
||||
v-model="selectedMake"
|
||||
:selectOnKeypress="false"
|
||||
isRequired />
|
||||
<buttonQuestion
|
||||
class="radioQuestion"
|
||||
isOverflowScrollable
|
||||
selectingInitiatesLoad
|
||||
:questionText="questionText"
|
||||
:answers="makes"
|
||||
groupName="ChooseVehicleMake"
|
||||
textPosition="text-start"
|
||||
v-model="selectedValue"
|
||||
isRequired
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -18,44 +18,44 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
// import buttonQuestionWrapperMixin from "../../../mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "make-question",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
makes: [],
|
||||
selectedMake: ""
|
||||
};
|
||||
name: "make-question",
|
||||
data() {
|
||||
return {
|
||||
makes: [],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
computed: {
|
||||
questionText(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
cmsWidgetName: String,
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
methods: {
|
||||
loadInitialData() {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_VEHICLE_MAKES,
|
||||
{ year: store.getters.vehicle.year }
|
||||
);
|
||||
},
|
||||
computed: {
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
},
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
methods: {
|
||||
loadInitialData() {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_VEHICLE_MAKES,
|
||||
{ year: store.getters.vehicle.year }
|
||||
);
|
||||
},
|
||||
initializeComponent(initialData) {
|
||||
this.makes = initialData;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedMake(selectedMake) {
|
||||
this.$emit("update:modelValue", selectedMake);
|
||||
},
|
||||
initializeComponent(initialData) {
|
||||
this.makes = initialData;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
:answers="models"
|
||||
groupName="ChooseVehicleModel"
|
||||
textPosition="text-start"
|
||||
v-model="selectedModel"
|
||||
:selectOnKeypress="false"
|
||||
v-model="selectedValue"
|
||||
isRequired
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -19,15 +18,12 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "model-question",
|
||||
mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
models: [],
|
||||
selectedModel: ""
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
|
@ -38,6 +34,14 @@ export default {
|
|||
questionText(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
|
|
@ -53,10 +57,5 @@ export default {
|
|||
this.models = initialData;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedModel(selectedModel) {
|
||||
this.$emit("update:modelValue", selectedModel);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -18,9 +18,7 @@ const featureListData = {
|
|||
modelValueProp: {}
|
||||
}
|
||||
|
||||
// TODO KO
|
||||
describe.skip("glass-part-question.vue", () => {
|
||||
|
||||
describe("glass-part-question.vue", () => {
|
||||
test("Part data passed in, should map data for ButtonQuestion (radio type)", async () => {
|
||||
|
||||
//Arrange
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@
|
|||
altText=""
|
||||
isRequired
|
||||
:groupName="`${glassLocation}-${glassName}`"
|
||||
:validationRules="tintValidationRules"
|
||||
>
|
||||
:validationRules="tintValidationRules">
|
||||
<div class="row my-2" aria-live="polite">
|
||||
<div class="col">
|
||||
<buttonQuestion
|
||||
|
|
@ -28,7 +27,7 @@
|
|||
isRequired
|
||||
:groupName="`${glassLocation}-${glassName}-${selectedTint}`"
|
||||
:validationRules="partValidationRules"
|
||||
/>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</buttonQuestion>
|
||||
|
|
@ -63,7 +62,10 @@ export default {
|
|||
glassLocation: String,
|
||||
colorAnswers: Array,
|
||||
modelValue: Object,
|
||||
alreadyPopulatedPartsData: Array
|
||||
alreadyPopulatedPartsData: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.LoadPreselectedValues();
|
||||
|
|
@ -74,12 +76,18 @@ export default {
|
|||
computed: {
|
||||
tintValidationRules() {
|
||||
const validationRuleName = `${this.glassLocation}-${this.glassName}-tint-required`;
|
||||
defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED));
|
||||
defineRule(
|
||||
validationRuleName,
|
||||
required(errorMessages.OPTION_REQUIRED)
|
||||
);
|
||||
return validationRuleName;
|
||||
},
|
||||
partValidationRules() {
|
||||
const validationRuleName = `${this.glassLocation}-${this.glassName}-part-required`;
|
||||
defineRule(validationRuleName, required(errorMessages.OPTION_REQUIRED));
|
||||
defineRule(
|
||||
validationRuleName,
|
||||
required(errorMessages.OPTION_REQUIRED)
|
||||
);
|
||||
return validationRuleName;
|
||||
},
|
||||
colorQuestionText() {
|
||||
|
|
@ -110,16 +118,29 @@ export default {
|
|||
return this.modelValue?.partNumber;
|
||||
},
|
||||
set(newValue) {
|
||||
this.$emit("update:modelValue", this.partsForSelectedTint.filter(part => part.partNumber == newValue)[0]);
|
||||
this.$emit(
|
||||
"update:modelValue",
|
||||
this.partsForSelectedTint.filter(
|
||||
(part) => part.partNumber == newValue
|
||||
)[0]
|
||||
);
|
||||
},
|
||||
},
|
||||
|
||||
partsForSelectedTint() {
|
||||
const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter(dataForGlassLocationAndName =>
|
||||
dataForGlassLocationAndName.glassName == this.glassName &&
|
||||
dataForGlassLocationAndName.glassLocation == this.glassLocation);
|
||||
const matchingGlassParts = matchingGlass?.length == 1 ? matchingGlass[0].parts : [];
|
||||
return matchingGlassParts.filter(part => part.color == this.selectedTint) ?? [];
|
||||
const matchingGlass = this.PartDataFromApi.partsOrQuestions?.filter(
|
||||
(dataForGlassLocationAndName) =>
|
||||
dataForGlassLocationAndName.glassName == this.glassName &&
|
||||
dataForGlassLocationAndName.glassLocation ==
|
||||
this.glassLocation
|
||||
);
|
||||
const matchingGlassParts =
|
||||
matchingGlass?.length == 1 ? matchingGlass[0].parts : [];
|
||||
return (
|
||||
matchingGlassParts.filter(
|
||||
(part) => part.color == this.selectedTint
|
||||
) ?? []
|
||||
);
|
||||
},
|
||||
|
||||
// Creates a map of the feature list data in the correct Name/Value
|
||||
|
|
@ -151,7 +172,9 @@ export default {
|
|||
},
|
||||
|
||||
PartDataFromApi() {
|
||||
return this.$store.getters.pageData(this.$route.query.fmgPage) ?? {};
|
||||
return (
|
||||
this.$store.getters.pageData(this.$route.query.fmgPage) ?? {}
|
||||
);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -188,7 +211,8 @@ export default {
|
|||
// Check if only a single part is present for the tint and set the v-model if it is.
|
||||
AutoSelectIfSinglePart() {
|
||||
if (this.partsForSelectedTint?.length == 1) {
|
||||
this.selectedPartNumber = this.partsForSelectedTint[0].partNumber;
|
||||
this.selectedPartNumber =
|
||||
this.partsForSelectedTint[0].partNumber;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -197,7 +221,7 @@ export default {
|
|||
this.$nextTick(() => {
|
||||
if (this.modelValue !== undefined) {
|
||||
// Populate button-question model-value if parts data already exists in VueX
|
||||
this.selectedTint = this.alreadyPopulatedPartsData.filter(part => part.partNumber === this.selectedPartNumber)[0]?.color;
|
||||
this.selectedTint = this.modelValue?.color
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
@ -205,8 +229,8 @@ export default {
|
|||
watch: {
|
||||
selectedTint() {
|
||||
this.AutoSelectIfSinglePart();
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ describe("vehicle-parts.vue", () => {
|
|||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
|
||||
test("Initial data, should populate this.selectedGlassParts", async () => {
|
||||
test.only("Initial data, should populate this.selectedGlassParts", async () => {
|
||||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValue(basePartResponse);
|
||||
|
|
@ -183,7 +183,16 @@ describe("vehicle-parts.vue", () => {
|
|||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.selectedGlassParts).toEqual({ "Rear-Stationary": { "Rear": ['DB12209YPYNOEM'] } });
|
||||
expect(wrapper.vm.selectedGlassParts).toEqual({
|
||||
"Rear-Stationary": {
|
||||
partNumber: "DB12209YPYNOEM",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Gray Tint Privacy",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test("User had part questions > BackButtonAction triggers a router.navigateWithoutSaving change with correct scenario", async () => {
|
||||
|
|
@ -509,6 +518,8 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
|
|||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||
// wrapper.vm.$refs.onSubmit = jest.fn();
|
||||
// wrapper.vm.$refs.onInvalidSubmit = jest.fn();
|
||||
|
||||
return { wrapper, apiPromise };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -204,9 +204,7 @@ export default {
|
|||
const partNumber = this.alreadyPopulatedPartsData[key].partNumber;
|
||||
g.parts.forEach((p) => {
|
||||
if (p.partNumber === partNumber) {
|
||||
this.selectedGlassParts[g.glassLocation + "-" + g.glassName] = {
|
||||
[g.glassLocation]: [partNumber],
|
||||
};
|
||||
this.selectedGlassParts[g.glassLocation + "-" + g.glassName] = p;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@
|
|||
:answers="styles"
|
||||
groupName="ChooseVehicleStyle"
|
||||
textPosition="text-start"
|
||||
v-model="selectedStyle"
|
||||
:selectOnKeypress="false"
|
||||
v-model="selectedValue"
|
||||
isRequired
|
||||
/>
|
||||
</template>
|
||||
|
|
@ -19,24 +18,30 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
// import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "style-question",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
styles: [],
|
||||
selectedStyle: ""
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
computed: {
|
||||
questionText(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
|
|
@ -56,10 +61,5 @@ export default {
|
|||
this.styles = initialData;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedStyle(selectedStyle) {
|
||||
this.$emit("update:modelValue", selectedStyle);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -1,18 +1,15 @@
|
|||
<template>
|
||||
<div>
|
||||
<buttonQuestion
|
||||
class="radioQuestion"
|
||||
isOverflowScrollable
|
||||
selectingInitiatesLoad
|
||||
:questionText="questionText"
|
||||
:answers="years"
|
||||
groupName="ChooseVehicleYear"
|
||||
textPosition="text-start"
|
||||
v-model="selectedYear"
|
||||
:selectOnKeypress="false"
|
||||
isRequired
|
||||
/>
|
||||
</div>
|
||||
<buttonQuestion
|
||||
class="radioQuestion"
|
||||
isOverflowScrollable
|
||||
selectingInitiatesLoad
|
||||
:questionText="questionText"
|
||||
:answers="years"
|
||||
groupName="ChooseVehicleYear"
|
||||
textPosition="text-start"
|
||||
v-model="selectedValue"
|
||||
isRequired
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -20,27 +17,32 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
// Supporting files
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
// import buttonQuestionWrapperMixin from "@/mixins/button-question-wrapper-mixin";
|
||||
|
||||
export default {
|
||||
name: "year-question",
|
||||
// mixins: [buttonQuestionWrapperMixin],
|
||||
data() {
|
||||
return {
|
||||
years: [],
|
||||
selectedYear: ""
|
||||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
components: {
|
||||
buttonQuestion
|
||||
buttonQuestion,
|
||||
},
|
||||
computed: {
|
||||
questionText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, "QuestionText");
|
||||
questionText(){
|
||||
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
|
||||
},
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadInitialData() {
|
||||
|
|
@ -53,10 +55,5 @@ export default {
|
|||
this.years = initialData;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedYear(selectedYear) {
|
||||
this.$emit("update:modelValue", selectedYear);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -29,8 +29,6 @@ export default {
|
|||
logCustomEvent(category, action, label, value) {
|
||||
const currentPageName = getPageNameByQueryString();
|
||||
|
||||
console.log("PUSHING: ", label)
|
||||
|
||||
var payload = {
|
||||
userId: getDeviceIdValue(),
|
||||
sessionKey: getSessionKeyValue(),
|
||||
|
|
@ -50,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,
|
||||
|
|
@ -142,7 +141,7 @@ export default {
|
|||
|
||||
noSession() {
|
||||
return getSessionKeyValue() === 0 || getSessionIdValue() === '00000000-0000-0000-0000-000000000000';
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
analyticsPageEvents() {
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
|||
import { routerParams } from "@/router/router-constants/router-params";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { dynamicStrings } from "@/constants/dynamic-strings";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
cmsContentByWidget: {}
|
||||
cmsContentByWidget: {},
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -19,7 +18,9 @@ export default {
|
|||
this.$root.cmsContentByWidget = cmsContent;
|
||||
},
|
||||
getCmsContent(widgetName, fieldName) {
|
||||
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
|
||||
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName]
|
||||
? this.$root.cmsContentByWidget[widgetName][fieldName]
|
||||
: "";
|
||||
},
|
||||
dispatchStoreAction(type, payload, encodePayload = true) {
|
||||
// Encode the payload if required
|
||||
|
|
@ -32,7 +33,7 @@ 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. Cannot use .prevent modifier for vee-validate Form
|
||||
onSubmit() {}, // DO NOT REMOVE; needed to prevent default form submit behavior. Cannot use .prevent modifier for vee-validate Form
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
// get error names array
|
||||
|
|
@ -49,12 +50,15 @@ export default {
|
|||
return footerInfoBox ? footerInfoBox.offsetHeight : 0;
|
||||
},
|
||||
async getZipCodeData(zipCode) {
|
||||
const serviceZipValidationResponse = await this.dispatchStoreAction(storeActions.VALIDATE_ZIP, { zip: zipCode });
|
||||
|
||||
return {
|
||||
isValid: serviceZipValidationResponse.data.isValid,
|
||||
const serviceZipValidationResponse = await this.dispatchStoreAction(
|
||||
storeActions.VALIDATE_ZIP,
|
||||
{ zip: zipCode }
|
||||
);
|
||||
|
||||
return {
|
||||
isValid: serviceZipValidationResponse.data.isValid,
|
||||
isServiceable: serviceZipValidationResponse.data.isServiceable,
|
||||
state: serviceZipValidationResponse.data.state
|
||||
state: serviceZipValidationResponse.data.state,
|
||||
};
|
||||
},
|
||||
getEconomyPackagePrice(lineItems) {
|
||||
|
|
@ -82,13 +86,13 @@ export default {
|
|||
routerParams() {
|
||||
return routerParams;
|
||||
},
|
||||
queryStrings(){
|
||||
queryStrings() {
|
||||
return queryStrings;
|
||||
},
|
||||
dynamicStrings(){
|
||||
dynamicStrings() {
|
||||
return dynamicStrings;
|
||||
},
|
||||
cssClassNameForCmsWidget(){
|
||||
cssClassNameForCmsWidget() {
|
||||
return "widget-name-" + this.cmsWidgetName;
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,35 +0,0 @@
|
|||
import { ref, isRef } from "vue";
|
||||
|
||||
export default {
|
||||
props: {
|
||||
modelValue: [Array, String, Number],
|
||||
// modelValueName: {
|
||||
// type: String,
|
||||
// required: true,
|
||||
// },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedValue: null,
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.selectedValue = this.modelValue;
|
||||
|
||||
console.log("Created: ", {
|
||||
selectedValue: this.selectedValue,
|
||||
// modelValue: this.modelValue,
|
||||
// modelValueName: this.modelValueName
|
||||
})
|
||||
if (this.modelValueName) {
|
||||
this[this.modelValueName] = this.selectedValue;
|
||||
// this.$on("update:modelValue", (dynamicModelValue) => {
|
||||
// console.log("ON HIT", {
|
||||
// dynamicModelValue: dynamicModelValue
|
||||
// })
|
||||
// this.selectedValue = dynamicModelValue;
|
||||
// this.$emit("update:modelValue", dynamicModelValue)
|
||||
// })
|
||||
}
|
||||
},
|
||||
};
|
||||
|
|
@ -1,43 +1,36 @@
|
|||
import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props";
|
||||
|
||||
export default {
|
||||
model: {
|
||||
prop: "modelValue",
|
||||
event: "change",
|
||||
},
|
||||
props: {
|
||||
modelValue: [Array, String, Number],
|
||||
value: [String, Number],
|
||||
isMultiSelect: Boolean,
|
||||
groupName: String,
|
||||
...inputButtonProps,
|
||||
buttonLabel: [Number, String],
|
||||
buttonLabelSubCopy: String,
|
||||
buttonImage: String,
|
||||
altText: {
|
||||
type: String,
|
||||
default: ""
|
||||
default: "",
|
||||
},
|
||||
textPosition: String,
|
||||
screenReaderOnlyText: String,
|
||||
valueToLogType: String,
|
||||
validationRules: String,
|
||||
isWide: Boolean,
|
||||
isRequired: Boolean,
|
||||
additionalData: null,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedValue: null,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.selectedValue = this.modelValue;
|
||||
},
|
||||
methods: {
|
||||
handleAnswerChange(e) {
|
||||
if (this.preHandleAnswerChange) {
|
||||
this.preHandleAnswerChange(e)
|
||||
}
|
||||
this.$emit("change", e);
|
||||
computed: {
|
||||
selectedValue: {
|
||||
get() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set(e) {
|
||||
if (this.preHandleAnswerChange) {
|
||||
this.preHandleAnswerChange(e);
|
||||
}
|
||||
|
||||
this.$emit("update:modelValue", e);
|
||||
},
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedValue(selectedValue) {
|
||||
this.$emit("update:modelValue", selectedValue);
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
81
src/mixins/input-button-wrapper-mixin.spec.js
Normal file
81
src/mixins/input-button-wrapper-mixin.spec.js
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
describe("input-button-wrapper-mixin", () => {
|
||||
describe("mouse clicks", () => {
|
||||
describe("checkbox", () => {
|
||||
test.todo("clicking once checks the baseInputButton");
|
||||
|
||||
test.todo("clicking twice unchecks the baseInputButton");
|
||||
|
||||
test.todo(
|
||||
"is initially checked => checking unchecks the baseInputButton"
|
||||
);
|
||||
|
||||
test.todo("clicked => correct event and value are emitted");
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test.todo("clicking once selects the baseInputButton");
|
||||
|
||||
test.todo("clicking twice keeps the baseInputButton selected");
|
||||
|
||||
test.todo(
|
||||
"is initially selected => click keeps the baseInputButton selected"
|
||||
);
|
||||
|
||||
test.todo("clicked => correct event and value are emitted");
|
||||
});
|
||||
});
|
||||
|
||||
describe("keyboard navigation and", () => {
|
||||
describe("checkbox", () => {
|
||||
test.todo(
|
||||
"focus on a checkbox => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"blur from a checkbox => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click space on a checkbox => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click space on a checkbox that is already checked => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click enter on a checkbox => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click enter on a checkbox that is already checked => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
});
|
||||
|
||||
describe("radio", () => {
|
||||
test.todo(
|
||||
"focus on a radio button => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"blur from a radio button => inputButtonClicked is not emitted"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click space on a radio button => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click space on a radio button that is already selected => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click enter on a radio button => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
|
||||
test.todo(
|
||||
"focus and click enter on a radio button that is already selected => inputButtonClicked is emitted with correct value"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -136,9 +136,6 @@ export default {
|
|||
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
|
||||
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
|
||||
|
||||
console.log({
|
||||
hasGlassLocationWithMultipleParts: hasGlassLocationWithMultipleParts
|
||||
})
|
||||
if (hasPartQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.PART_QUESTIONS)) {
|
||||
self.$router.navigateWithSaving(self.navigationScenarios.HAS_PART_QUESTIONS, self.$route, {}, {}, { partsOrQuestions: partsOrQuestions });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -239,8 +239,6 @@ function navigateToUrl(url, optionalQuery = {}) {
|
|||
externalUrl.searchParams.append(queryKey, optionalQuery[queryKey]);
|
||||
}
|
||||
|
||||
externalUrl.searchParams.append("experiments", "ConceptFunnel=ConceptFunnel_V1=ConceptFunnel_TEST=true");
|
||||
|
||||
window.location.assign(externalUrl);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { createStore } from "vuex";
|
||||
import { createStore, Store } from "vuex";
|
||||
import { endpoints } from "@/constants/endpoints.js";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
|
||||
|
|
@ -53,19 +53,19 @@ const getDefaultState = () => {
|
|||
capabilityQuestionAnswers: null,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: null
|
||||
glassParts: null,
|
||||
},
|
||||
payment: {
|
||||
isInsurance: null,
|
||||
insuranceCoverage: {
|
||||
isVerified: null
|
||||
}
|
||||
isVerified: null,
|
||||
},
|
||||
},
|
||||
referralNumber: null,
|
||||
referralDate: null,
|
||||
referralCorrelationId: null,
|
||||
accountNumber: 0,
|
||||
eon: null
|
||||
eon: null,
|
||||
},
|
||||
applicationUser: {
|
||||
eventBus: [],
|
||||
|
|
@ -76,9 +76,15 @@ const getDefaultState = () => {
|
|||
crmCustomerId: null,
|
||||
lastPageVisited: null,
|
||||
experiments: [],
|
||||
triggeredSiteEntry: false
|
||||
triggeredSiteEntry: false,
|
||||
},
|
||||
}
|
||||
// gaClickInformation: {
|
||||
// currentlySelectedValues: {},
|
||||
// firedGaClickEventValues: {},
|
||||
// lastFocusedInputGroup: "",
|
||||
// wasLastFocusedInputMultiselect: undefined,
|
||||
// },
|
||||
};
|
||||
};
|
||||
|
||||
export const state = getDefaultState();
|
||||
|
|
@ -195,7 +201,6 @@ export const mutations = {
|
|||
state.order.customer.emailAddress = customerEmailAddress;
|
||||
},
|
||||
updateVehicle(state, vehicleInfo) {
|
||||
|
||||
state.order.vehicle.year = vehicleInfo.year;
|
||||
state.order.vehicle.make = vehicleInfo.make;
|
||||
state.order.vehicle.model = vehicleInfo.model;
|
||||
|
|
@ -209,7 +214,8 @@ export const mutations = {
|
|||
state.order.vehicle.imageColor = vehicleInfo.imageVifColor;
|
||||
},
|
||||
updateRegistration(state, registrationInfo) {
|
||||
state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
|
||||
state.order.vehicle.registration.licensePlate =
|
||||
registrationInfo?.licensePlate;
|
||||
state.order.vehicle.registration.address = registrationInfo?.address;
|
||||
state.order.vehicle.registration.city = registrationInfo?.city;
|
||||
state.order.vehicle.registration.state = registrationInfo?.state;
|
||||
|
|
@ -235,7 +241,7 @@ export const mutations = {
|
|||
state.applicationUser.crmCustomerId = crmCustomerId;
|
||||
},
|
||||
updateLastPageVisited(state, lastPageVisited) {
|
||||
state.applicationUser.lastPageVisited = lastPageVisited
|
||||
state.applicationUser.lastPageVisited = lastPageVisited;
|
||||
},
|
||||
// EVENT BUS MUTATIONS
|
||||
addEventToBus(state, event) {
|
||||
|
|
@ -244,8 +250,7 @@ export const mutations = {
|
|||
removeEventFromBus(state, eventData) {
|
||||
const matchedEvent = state.applicationUser.eventBus.find(
|
||||
({ category, subCategory }) =>
|
||||
category === eventData.category &&
|
||||
subCategory === eventData.subCategory
|
||||
category === eventData.category && subCategory === eventData.subCategory
|
||||
);
|
||||
const itemIndex = state.applicationUser.eventBus.indexOf(matchedEvent);
|
||||
|
||||
|
|
@ -331,7 +336,7 @@ export const mutations = {
|
|||
state: orderInformation.vehicle.registration.state,
|
||||
zipCode: orderInformation.vehicle.registration.zipCode,
|
||||
licensePlate: orderInformation.vehicle.registration.licensePlateNumber,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
state.order.damage.glassToReplace = orderInformation.damage.glassToReplace;
|
||||
|
|
@ -340,13 +345,18 @@ export const mutations = {
|
|||
|
||||
state.order.lineItems.glassParts = orderInformation.parts;
|
||||
state.order.accountNumber = orderInformation.accountNumber;
|
||||
state.order.serviceLocation.address = orderInformation.serviceLocation.streetAddress,
|
||||
state.order.serviceLocation.city = orderInformation.serviceLocation.city,
|
||||
state.order.serviceLocation.state = orderInformation.serviceLocation.state,
|
||||
state.order.serviceLocation.zipCode = orderInformation.serviceLocation.zipCode;
|
||||
(state.order.serviceLocation.address =
|
||||
orderInformation.serviceLocation.streetAddress),
|
||||
(state.order.serviceLocation.city =
|
||||
orderInformation.serviceLocation.city),
|
||||
(state.order.serviceLocation.state =
|
||||
orderInformation.serviceLocation.state),
|
||||
(state.order.serviceLocation.zipCode =
|
||||
orderInformation.serviceLocation.zipCode);
|
||||
|
||||
state.order.payment.isInsurance = orderInformation.IsInsuranceOrder;
|
||||
state.order.payment.insuranceCoverage.isVerified = orderInformation?.insuranceInfo.coverageVerified;
|
||||
state.order.payment.insuranceCoverage.isVerified =
|
||||
orderInformation?.insuranceInfo.coverageVerified;
|
||||
|
||||
state.order.customer.emailAddress = orderInformation.customer.emailAddress;
|
||||
state.applicationUser.experiments = orderInformation.experiments;
|
||||
|
|
@ -356,8 +366,23 @@ export const mutations = {
|
|||
},
|
||||
updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
|
||||
state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
|
||||
}
|
||||
}
|
||||
},
|
||||
// START GA click event mutations
|
||||
// updateCurrentlySelectedValues(state, groupName, value) {
|
||||
// state.gaClickInformation.currentlySelectedValues[groupName] = value;
|
||||
// },
|
||||
// updateFiredGaClickEventValues(state, groupName, value) {
|
||||
// state.gaClickInformation.firedGaClickEventValues[groupName] = value;
|
||||
// },
|
||||
// updateLastFocusedInputGroup(state, groupName) {
|
||||
// state.gaClickInformation.lastFocusedInputGroup = groupName;
|
||||
// },
|
||||
// updateWasLastFocusedInputMultiselect(state, wasLastFocusedInputMultiselect) {
|
||||
// state.gaClickInformation.wasLastFocusedInputMultiselect =
|
||||
// wasLastFocusedInputMultiselect;
|
||||
// },
|
||||
// END GA click event mutations
|
||||
};
|
||||
|
||||
// Export Getters
|
||||
export const getters = {
|
||||
|
|
@ -377,7 +402,9 @@ export const getters = {
|
|||
return !!nonWindshieldItems.length;
|
||||
},
|
||||
lineItems: (state) => state.order.lineItems,
|
||||
pageData: (state) => (page) => { return state.applicationUser.pageData[page]; },
|
||||
pageData: (state) => (page) => {
|
||||
return state.applicationUser.pageData[page];
|
||||
},
|
||||
applicationUser: (state) => state.applicationUser,
|
||||
order: (state) => state.order,
|
||||
payment: (state) => state.order.payment,
|
||||
|
|
@ -394,7 +421,8 @@ export const getters = {
|
|||
funnelServiceState: state.order.serviceLocation.state,
|
||||
funnelServiceZipCode: state.order.serviceLocation.zipCode,
|
||||
funnelParentAccountNumber: state.order.accountNumber,
|
||||
funnelIsCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
|
||||
funnelIsCoverageVerified:
|
||||
state.order.payment.insuranceCoverage.isVerified,
|
||||
funnelHasRecalibrationPart: getHasRecalibrationPart(state),
|
||||
funnelSelectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
|
||||
funnelSelectedWindshieldGlass: getNonFalseValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.WINDSHIELD),
|
||||
|
|
@ -407,8 +435,12 @@ export const getters = {
|
|||
funnelOrderPartTypes: [...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType"), ...getNonFalseValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "recalibrationType")],
|
||||
}
|
||||
},
|
||||
experimentSettings: (state) => state.applicationUser.experiments.map(x => x.settings).reduce((r, c) => Object.assign(r, c), {}) ?? {}
|
||||
}
|
||||
experimentSettings: (state) =>
|
||||
state.applicationUser.experiments
|
||||
.map((x) => x.settings)
|
||||
.reduce((r, c) => Object.assign(r, c), {}) ?? {},
|
||||
// gaClickInformation: (state) => state.gaClickInformation,
|
||||
};
|
||||
|
||||
function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
|
||||
return (array ?? []).map(x => x[propertyName]).filter(x => x);
|
||||
|
|
@ -416,7 +448,6 @@ function getNonFalseValuesOfPropertyInArrayOfObjects(array, propertyName) {
|
|||
|
||||
// Export Actions
|
||||
export const actions = {
|
||||
|
||||
// Vehicle API Actions
|
||||
getVehicleYears(context) {
|
||||
return globalMethods.callHttpClient({
|
||||
|
|
@ -447,11 +478,14 @@ export const actions = {
|
|||
endpoint: endpoints.LookupVinByPlate.url,
|
||||
payload: {
|
||||
licensePlate: licensePlate,
|
||||
licenseState: licenseState
|
||||
licenseState: licenseState,
|
||||
},
|
||||
});
|
||||
},
|
||||
lookupVinByAddress(context, { licenseLastName, licenseStreetAddress, licenseZip, licenseState }) {
|
||||
lookupVinByAddress(
|
||||
context,
|
||||
{ licenseLastName, licenseStreetAddress, licenseZip, licenseState }
|
||||
) {
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.LookupVinByAddress.method,
|
||||
endpoint: endpoints.LookupVinByAddress.url,
|
||||
|
|
@ -459,7 +493,7 @@ export const actions = {
|
|||
licenseLastName: licenseLastName,
|
||||
licenseStreetAddress: licenseStreetAddress,
|
||||
licenseZip: licenseZip,
|
||||
licenseState: licenseState
|
||||
licenseState: licenseState,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
@ -493,10 +527,22 @@ export const actions = {
|
|||
})
|
||||
.then((response) => {
|
||||
context.commit(storeMutations.UPDATE_CAR_ID, response.data.carId);
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, response.data.category);
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, response.data.imageUrl);
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, response.data.imageVifNumber);
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, response.data.imageVifColor);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_VEHICLE_CATEGORY,
|
||||
response.data.category
|
||||
);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_VEHICLE_IMAGE_URL,
|
||||
response.data.imageUrl
|
||||
);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER,
|
||||
response.data.imageVifNumber
|
||||
);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_VEHICLE_IMAGE_COLOR,
|
||||
response.data.imageVifColor
|
||||
);
|
||||
return response;
|
||||
});
|
||||
},
|
||||
|
|
@ -510,8 +556,8 @@ export const actions = {
|
|||
validateZip(context, { zip }) {
|
||||
return globalMethods.callHttpClient({
|
||||
methods: endpoints.ValidateZip.method,
|
||||
endpoint: `${endpoints.ValidateZip.url}/${zip}`
|
||||
})
|
||||
endpoint: `${endpoints.ValidateZip.url}/${zip}`,
|
||||
});
|
||||
},
|
||||
|
||||
// Dependency Actions
|
||||
|
|
@ -526,7 +572,7 @@ export const actions = {
|
|||
},
|
||||
resetRegistrationAndDependencies(context) {
|
||||
context.commit(storeMutations.RESET_REGISTRATION_STATE);
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE)
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
},
|
||||
resetPartsAndDependencies(context) {
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
|
|
@ -582,8 +628,8 @@ export const actions = {
|
|||
assignmentId: experiment.assignmentId,
|
||||
sessionKey: sessionKey,
|
||||
pageName: pageName,
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -591,13 +637,28 @@ export const actions = {
|
|||
updateStoreWithSaveSessionResponse(context, { referralNumber, referralDate, referralCorrelationId, eon, accountNumber, savedSessionId, crmCustomerId }) {
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_REFERRAL_CORRELATION_ID,
|
||||
referralCorrelationId
|
||||
);
|
||||
context.commit(storeMutations.UPDATE_EON, eon);
|
||||
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, accountNumber);
|
||||
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
|
||||
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
||||
},
|
||||
logPageView(context, { userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser }) {
|
||||
logPageView(
|
||||
context,
|
||||
{
|
||||
userId,
|
||||
sessionKey,
|
||||
pageName,
|
||||
sessionId,
|
||||
action,
|
||||
event,
|
||||
shouldUseSessionId,
|
||||
experimentsForUser,
|
||||
}
|
||||
) {
|
||||
var payload = {
|
||||
userId: userId,
|
||||
sessionKey: sessionKey,
|
||||
|
|
@ -607,17 +668,31 @@ export const actions = {
|
|||
action: action,
|
||||
event: event,
|
||||
shouldUseSessionId: shouldUseSessionId,
|
||||
experimentsForUser: experimentsForUser
|
||||
experimentsForUser: experimentsForUser,
|
||||
};
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.LogPageView.method,
|
||||
endpoint: endpoints.LogPageView.url,
|
||||
payload: payload,
|
||||
logApiCall: false
|
||||
logApiCall: false,
|
||||
});
|
||||
},
|
||||
logCustomEvent(context, { userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) {
|
||||
logCustomEvent(
|
||||
context,
|
||||
{
|
||||
userId,
|
||||
sessionKey,
|
||||
pageName,
|
||||
sessionId,
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
value,
|
||||
shouldUseSessionId,
|
||||
experimentsForUser,
|
||||
}
|
||||
) {
|
||||
var payload = {
|
||||
userId: userId,
|
||||
sessionKey: sessionKey,
|
||||
|
|
@ -629,14 +704,14 @@ export const actions = {
|
|||
label: label,
|
||||
value: value,
|
||||
shouldUseSessionId: shouldUseSessionId,
|
||||
experimentsForUser: experimentsForUser
|
||||
experimentsForUser: experimentsForUser,
|
||||
};
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.LogCustomEvent.method,
|
||||
endpoint: endpoints.LogCustomEvent.url,
|
||||
payload: payload,
|
||||
logApiCall: false
|
||||
logApiCall: false,
|
||||
});
|
||||
},
|
||||
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
|
||||
|
|
@ -648,22 +723,28 @@ export const actions = {
|
|||
userAgent: userAgent,
|
||||
operatorId: "WEB",
|
||||
userName: "SafeliteConceptFunnel",
|
||||
referrer: referrer
|
||||
referrer: referrer,
|
||||
};
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.InitializeSession.method,
|
||||
endpoint: endpoints.InitializeSession.url,
|
||||
payload: payload,
|
||||
logApiCall: false
|
||||
logApiCall: false,
|
||||
});
|
||||
},
|
||||
|
||||
// Misc Actions
|
||||
setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) {
|
||||
setReferralInformation(
|
||||
context,
|
||||
{ referralNumber, referralDate, referralCorrelationId, eon }
|
||||
) {
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_REFERRAL_CORRELATION_ID,
|
||||
referralCorrelationId
|
||||
);
|
||||
context.commit(storeMutations.UPDATE_EON, eon);
|
||||
},
|
||||
|
||||
|
|
@ -671,11 +752,14 @@ export const actions = {
|
|||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetExperimentsByUser.method,
|
||||
endpoint: `${endpoints.GetExperimentsByUser.url}/${userId}`,
|
||||
payload: {}
|
||||
payload: {},
|
||||
});
|
||||
},
|
||||
|
||||
async runExperimentsForTrigger(context, { userId, triggerEvent, triggerValue }) {
|
||||
async runExperimentsForTrigger(
|
||||
context,
|
||||
{ userId, triggerEvent, triggerValue }
|
||||
) {
|
||||
if (triggerEvent == experimentTriggers.SITE_ENTRY) {
|
||||
context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
|
||||
}
|
||||
|
|
@ -685,7 +769,7 @@ export const actions = {
|
|||
userId: userId,
|
||||
triggerEvent: triggerEvent,
|
||||
triggerValue: triggerValue,
|
||||
experimentOrder: context.getters.experimentOrder
|
||||
experimentOrder: context.getters.experimentOrder,
|
||||
};
|
||||
|
||||
const response = await globalMethods.callHttpClient({
|
||||
|
|
@ -694,7 +778,10 @@ export const actions = {
|
|||
payload: payload,
|
||||
});
|
||||
|
||||
context.commit(storeMutations.UPDATE_EXPERIMENTS, response.data.experiments);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_EXPERIMENTS,
|
||||
response.data.experiments
|
||||
);
|
||||
},
|
||||
|
||||
getEvoxImage(context, { relativeUrl }) {
|
||||
|
|
@ -723,7 +810,7 @@ export const actions = {
|
|||
carId: carId,
|
||||
glass: glassArray ?? [],
|
||||
zip: zipCode,
|
||||
vin: vin
|
||||
vin: vin,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
@ -748,7 +835,7 @@ export const actions = {
|
|||
glass: glassArray,
|
||||
answerResults: resultsArray,
|
||||
zip: zipCode,
|
||||
vin: vin
|
||||
vin: vin,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
@ -757,24 +844,31 @@ export const actions = {
|
|||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetCapabilityQuestions.method,
|
||||
endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
|
||||
})
|
||||
});
|
||||
},
|
||||
|
||||
getPartFromCapabilityQuestionAnswer(context, glassLocation) {
|
||||
const pageData = context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||
const pageData = context.getters.pageData(
|
||||
fmgPageValues.CAPABILITY_QUESTIONS
|
||||
);
|
||||
|
||||
const part = pageData.partsOrQuestions.find(x => x.glassLocation === glassLocation).parts[0];
|
||||
const capabilityQuestionAnswers = context.getters.damage.capabilityQuestionAnswers;
|
||||
const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find(x => x.glassLocation === glassLocation);
|
||||
const part = pageData.partsOrQuestions.find(
|
||||
(x) => x.glassLocation === glassLocation
|
||||
).parts[0];
|
||||
const capabilityQuestionAnswers =
|
||||
context.getters.damage.capabilityQuestionAnswers;
|
||||
const capabilityQuestionAnswersForPart = capabilityQuestionAnswers.find(
|
||||
(x) => x.glassLocation === glassLocation
|
||||
);
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetPartFromCapabilityAnswer.method,
|
||||
endpoint: endpoints.GetPartFromCapabilityAnswer.url,
|
||||
payload: {
|
||||
part,
|
||||
capabilityAnswerResults: capabilityQuestionAnswersForPart
|
||||
}
|
||||
})
|
||||
capabilityAnswerResults: capabilityQuestionAnswersForPart,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
// Session API Actions
|
||||
|
|
@ -809,21 +903,21 @@ export const actions = {
|
|||
damage: {
|
||||
numberOfChips: damage.numberOfChips,
|
||||
glassToReplace: damage.glassToReplace,
|
||||
isRepair: damage.isRepair
|
||||
isRepair: damage.isRepair,
|
||||
},
|
||||
customer: {
|
||||
emailAddress: order.customer.emailAddress,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: lineItems.glassParts
|
||||
glassParts: lineItems.glassParts,
|
||||
},
|
||||
serviceLocation: {
|
||||
streetAddress: order.serviceLocation.address,
|
||||
city: order.serviceLocation.city,
|
||||
state: order.serviceLocation.state,
|
||||
zipCode: order.serviceLocation.zipCode
|
||||
zipCode: order.serviceLocation.zipCode,
|
||||
},
|
||||
referralNumber: order.referralNumber?.toString(), // TODO It'd be nice to save these as strings in the first place
|
||||
referralNumber: order.referralNumber?.toString(), // TODO It'd be nice to save these as strings in the first place
|
||||
referralDate: order.referralDate,
|
||||
accountNumber: order.accountNumber?.toString(),
|
||||
existingPromoCode: null,
|
||||
|
|
@ -858,8 +952,7 @@ export const actions = {
|
|||
|
||||
// Vehicle
|
||||
saveVehicleYear(context, year) {
|
||||
|
||||
//Reset dependent state when changing
|
||||
//Reset dependent state when changing
|
||||
if (context.state.order.vehicle.year !== year) {
|
||||
context.commit(storeMutations.UPDATE_MAKE, null);
|
||||
context.commit(storeMutations.UPDATE_MODEL, null);
|
||||
|
|
@ -880,7 +973,6 @@ export const actions = {
|
|||
}
|
||||
},
|
||||
saveVehicleMake(context, make) {
|
||||
|
||||
//Reset dependent state when changing
|
||||
if (context.state.order.vehicle.make !== make) {
|
||||
context.commit(storeMutations.UPDATE_MODEL, null);
|
||||
|
|
@ -901,8 +993,7 @@ export const actions = {
|
|||
}
|
||||
},
|
||||
saveVehicleModel(context, model) {
|
||||
|
||||
//Reset dependent state when changing
|
||||
//Reset dependent state when changing
|
||||
if (context.state.order.vehicle.model !== model) {
|
||||
context.commit(storeMutations.UPDATE_STYLE, null);
|
||||
context.commit(storeMutations.UPDATE_CAR_ID, null);
|
||||
|
|
@ -921,7 +1012,7 @@ export const actions = {
|
|||
}
|
||||
},
|
||||
saveVehicleStyle(context, style) {
|
||||
//Reset dependent state when changing
|
||||
//Reset dependent state when changing
|
||||
if (context.state.order.vehicle.style !== style) {
|
||||
context.commit(storeMutations.UPDATE_CAR_ID, null);
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
|
||||
|
|
@ -938,20 +1029,32 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_STYLE, style);
|
||||
}
|
||||
},
|
||||
saveVehicleDamage(context, { isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount }) {
|
||||
|
||||
saveVehicleDamage(
|
||||
context,
|
||||
{ isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount }
|
||||
) {
|
||||
const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort();
|
||||
const isGlassToReplaceTheSame = (context.state.order.damage.glassToReplace?.length === selectedGlassToReplace.length)
|
||||
&& context.state.order.damage.glassToReplace
|
||||
const isGlassToReplaceTheSame =
|
||||
context.state.order.damage.glassToReplace?.length ===
|
||||
selectedGlassToReplace.length &&
|
||||
context.state.order.damage.glassToReplace
|
||||
.slice()
|
||||
.sort()
|
||||
.every((obj, index) => obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation && obj.glassName === selectedGlassPassedInSorted[index].glassName);
|
||||
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;
|
||||
.every(
|
||||
(obj, index) =>
|
||||
obj.glassLocation ===
|
||||
selectedGlassPassedInSorted[index].glassLocation &&
|
||||
obj.glassName === selectedGlassPassedInSorted[index].glassName
|
||||
);
|
||||
const isWindshieldRepairTheSame =
|
||||
isWindshieldRepair === context.state.order.damage.isRepair;
|
||||
|
||||
const isDamageChanging = !isGlassToReplaceTheSame || !isWindshieldRepairTheSame || (isWindshieldRepair && !isChipCountTheSame);
|
||||
const isChipCountTheSame = selectedWindshieldChipCount === context.state.order.damage.numberOfChips;
|
||||
|
||||
const isDamageChanging =
|
||||
!isGlassToReplaceTheSame ||
|
||||
!isWindshieldRepairTheSame ||
|
||||
(isWindshieldRepair && !isChipCountTheSame);
|
||||
|
||||
if (isDamageChanging) {
|
||||
//Reset dependent state when changing
|
||||
|
|
@ -959,14 +1062,23 @@ export const actions = {
|
|||
|
||||
// Save new values
|
||||
context.commit(storeMutations.UPDATE_IS_REPAIR, isWindshieldRepair);
|
||||
context.commit(storeMutations.UPDATE_NUMBER_OF_CHIPS, isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null);
|
||||
context.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, selectedGlassToReplace);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_NUMBER_OF_CHIPS,
|
||||
isWindshieldRepair ? parseInt(selectedWindshieldChipCount) : null
|
||||
);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_GLASS_TO_REPLACE,
|
||||
selectedGlassToReplace
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
// Vin lookup
|
||||
saveVinLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
|
||||
//Reset dependent state when changing
|
||||
saveVinLookup(
|
||||
context,
|
||||
{ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
|
||||
) {
|
||||
//Reset dependent state when changing
|
||||
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
|
@ -980,10 +1092,15 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
|
||||
}
|
||||
},
|
||||
saveRegistrationLicensePlateLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
|
||||
//Reset dependent state when changing
|
||||
if (registrationInfo?.licensePlate !== context.state.order.vehicle.registration?.licensePlate) {
|
||||
|
||||
saveRegistrationLicensePlateLookup(
|
||||
context,
|
||||
{ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
|
||||
) {
|
||||
//Reset dependent state when changing
|
||||
if (
|
||||
registrationInfo?.licensePlate !==
|
||||
context.state.order.vehicle.registration?.licensePlate
|
||||
) {
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
|
|
@ -996,10 +1113,25 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_REGISTRATION, registrationInfo);
|
||||
}
|
||||
},
|
||||
saveRegistrationAddressLookup(context, { isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }) {
|
||||
//Reset dependent state when changing
|
||||
if (registrationInfo?.address !== context.state.order.vehicle.registration?.address || registrationInfo?.city !== context.state.order.vehicle.registration?.city || registrationInfo?.state !== context.state.order.vehicle.registration?.state || registrationInfo?.zipCode !== context.state.order.vehicle.registration?.zipCode || registrationInfo?.firstName !== context.state.order.vehicle.registration?.firstName || registrationInfo?.lastName !== context.state.order.vehicle.registration?.lastName) {
|
||||
|
||||
saveRegistrationAddressLookup(
|
||||
context,
|
||||
{ isSelectedGlassAvailableForVehicle, vehicleInfo, registrationInfo }
|
||||
) {
|
||||
//Reset dependent state when changing
|
||||
if (
|
||||
registrationInfo?.address !==
|
||||
context.state.order.vehicle.registration?.address ||
|
||||
registrationInfo?.city !==
|
||||
context.state.order.vehicle.registration?.city ||
|
||||
registrationInfo?.state !==
|
||||
context.state.order.vehicle.registration?.state ||
|
||||
registrationInfo?.zipCode !==
|
||||
context.state.order.vehicle.registration?.zipCode ||
|
||||
registrationInfo?.firstName !==
|
||||
context.state.order.vehicle.registration?.firstName ||
|
||||
registrationInfo?.lastName !==
|
||||
context.state.order.vehicle.registration?.lastName
|
||||
) {
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
|
|
@ -1014,72 +1146,141 @@ export const actions = {
|
|||
},
|
||||
savePartQuestionAnswers(context, partQuestionAnswersArray) {
|
||||
// if part question answers have changed, reset subsequent question answers
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.partQuestionAnswers, "result");
|
||||
const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(partQuestionAnswersArray, "result")
|
||||
const havePartQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.result === sortedPartQuestionAnswersArray[i].result);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
|
||||
context.getters.damage.partQuestionAnswers,
|
||||
"result"
|
||||
);
|
||||
const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
|
||||
partQuestionAnswersArray,
|
||||
"result"
|
||||
);
|
||||
const havePartQuestionAnswersChanged =
|
||||
sortedPreviousResultsArray?.length !==
|
||||
sortedPartQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every(
|
||||
(x, i) => x.result === sortedPartQuestionAnswersArray[i].result
|
||||
);
|
||||
|
||||
if (havePartQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.VEHICLE_PARTS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.VEHICLE_PARTS,
|
||||
data: null,
|
||||
});
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: null,
|
||||
});
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_PART_QUESTION_ANSWERS,
|
||||
partQuestionAnswersArray
|
||||
);
|
||||
},
|
||||
resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) {
|
||||
const partsOrQuestionsDataToCompareWith = context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ?? context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ?? [];
|
||||
const partsOrQuestionsDataToCompareWith =
|
||||
context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)
|
||||
?.partsOrQuestions ??
|
||||
context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)
|
||||
?.partsOrQuestions ??
|
||||
[];
|
||||
|
||||
function getAllPartNumbers(partsOrQuestions) {
|
||||
return partsOrQuestions[0]?.parts
|
||||
? [...partsOrQuestions].map(glass => glass.parts).flat().map(part => part.partNumber).filter(partNumber => !partNumber.toUpperCase().includes("FEE")).sort().join(",")
|
||||
: []
|
||||
? [...partsOrQuestions]
|
||||
.map((glass) => glass.parts)
|
||||
.flat()
|
||||
.map((part) => part.partNumber)
|
||||
.filter((partNumber) => !partNumber.toUpperCase().includes("FEE"))
|
||||
.sort()
|
||||
.join(",")
|
||||
: [];
|
||||
}
|
||||
|
||||
const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith);
|
||||
const previouslySelectedPartNumbers = getAllPartNumbers(
|
||||
partsOrQuestionsDataToCompareWith
|
||||
);
|
||||
const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts);
|
||||
|
||||
const haveSelectedVehiclePartsChanged = previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
|
||||
const haveSelectedVehiclePartsChanged =
|
||||
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
|
||||
|
||||
if (haveSelectedVehiclePartsChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: null,
|
||||
});
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
},
|
||||
saveMoldingQuestionAnswers(context, moldingQuestionAnswers) {
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.moldingQuestionAnswers, "partNum");
|
||||
const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(moldingQuestionAnswers, "partNum");
|
||||
const haveMoldingQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
|
||||
context.getters.damage.moldingQuestionAnswers,
|
||||
"partNum"
|
||||
);
|
||||
const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(
|
||||
moldingQuestionAnswers,
|
||||
"partNum"
|
||||
);
|
||||
const haveMoldingQuestionAnswersChanged =
|
||||
sortedPreviousResultsArray?.length !==
|
||||
sortedMoldingQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every(
|
||||
(x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum
|
||||
);
|
||||
|
||||
if (haveMoldingQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
context.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: null,
|
||||
});
|
||||
}
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS,
|
||||
moldingQuestionAnswers
|
||||
);
|
||||
},
|
||||
saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) {
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.capabilityQuestionAnswers, "result");
|
||||
const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswers, "result");
|
||||
const haveCapabilityQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(
|
||||
context.getters.damage.capabilityQuestionAnswers,
|
||||
"result"
|
||||
);
|
||||
const sortedCapabilityQuestionAnswersArray =
|
||||
sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswers, "result");
|
||||
const haveCapabilityQuestionAnswersChanged =
|
||||
sortedPreviousResultsArray?.length !==
|
||||
sortedCapabilityQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every(
|
||||
(x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result
|
||||
);
|
||||
|
||||
if (haveCapabilityQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
}
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, capabilityQuestionAnswers);
|
||||
context.commit(
|
||||
storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS,
|
||||
capabilityQuestionAnswers
|
||||
);
|
||||
},
|
||||
// Misc order actions
|
||||
saveServiceLocation(context, serviceLocationInfo) {
|
||||
|
|
@ -1091,7 +1292,6 @@ export const actions = {
|
|||
saveVin(context, { isSelectedGlassAvailableForVehicle, vehicleInfo }) {
|
||||
//Reset dependent state when changing
|
||||
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
||||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
|
@ -1106,12 +1306,11 @@ export const actions = {
|
|||
},
|
||||
clearVin(context) {
|
||||
context.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default createStore({
|
||||
plugins: [createPersistedState()],
|
||||
|
||||
// IMPORTANT: Be VERY careful when modifying these fields for at least a few reasons:
|
||||
// * The CMS can reference the fields by name
|
||||
// * Return users may have a previous "version" of the model, and we don't want
|
||||
|
|
@ -1134,7 +1333,8 @@ function getHasRecalibrationPart(state) {
|
|||
} else { // Has 'requiresRecalibration' but no 'recalibrationType' at all
|
||||
return true;
|
||||
}
|
||||
} else { // Does not have 'requiresRecalibration'
|
||||
} else {
|
||||
// Does not have 'requiresRecalibration'
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1143,11 +1343,8 @@ function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
|
|||
if (!arrayOfObjects) return null;
|
||||
|
||||
return arrayOfObjects.sort((a, b) => {
|
||||
if (a[propertyName] < b[propertyName])
|
||||
return -1;
|
||||
else if (a[propertyName] > b[propertyName])
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
})
|
||||
}
|
||||
if (a[propertyName] < b[propertyName]) return -1;
|
||||
else if (a[propertyName] > b[propertyName]) return 1;
|
||||
else return 0;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ html {
|
|||
&.list-button,
|
||||
&.list-card,
|
||||
&.list-card.list-button {
|
||||
// border: none;
|
||||
color: $red;
|
||||
input[type=checkbox]:focus + label,
|
||||
input[type=radio]:focus + label {
|
||||
|
|
@ -39,6 +38,17 @@ html {
|
|||
box-shadow: 0 0 1px $red;
|
||||
}
|
||||
}
|
||||
&.grid-item {
|
||||
input[type="radio"] {
|
||||
+ label {
|
||||
border: 1px solid $red;
|
||||
&:hover {
|
||||
background-color: $blue-100;
|
||||
box-shadow: 0px 0px 0px 4px $red-200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&.ui-radio,
|
||||
&.ui-checkbox {
|
||||
input[type=checkbox],
|
||||
|
|
|
|||
|
|
@ -2,15 +2,17 @@
|
|||
<button
|
||||
:aria-disabled="isDisabled"
|
||||
class="btn d-flex align-items-center py-3 px-4 delay"
|
||||
:class="[isPrimary ? 'btn-primary' : 'btn-secondary',isFloat ? 'float-end' : '', isLoaderDisplayed ? 'has-loader' : '']"
|
||||
@click="clicked"
|
||||
>
|
||||
:class="[
|
||||
isPrimary ? 'btn-primary' : 'btn-secondary',
|
||||
isFloat ? 'float-end' : '',
|
||||
isLoaderDisplayed ? 'has-loader' : '',
|
||||
]"
|
||||
@click="clicked">
|
||||
<span class="m-0">{{ this.buttonText }}</span>
|
||||
<loader
|
||||
class="ms-2"
|
||||
v-if="isLoaderDisplayed"
|
||||
v-bind:class="[this.loaderColor, this.loaderPosition]"
|
||||
/>
|
||||
v-bind:class="[this.loaderColor, this.loaderPosition]" />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
|
@ -33,11 +35,16 @@ export default {
|
|||
};
|
||||
},
|
||||
methods: {
|
||||
removeLoader(){
|
||||
removeLoader() {
|
||||
this.isLoaderDisplayed = false;
|
||||
},
|
||||
clicked() {
|
||||
this.pushEventToGA(this.$route.query[this.queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.buttonText, true);
|
||||
this.pushEventToGA(
|
||||
this.$route.query[this.queryStrings.FMG_PAGE],
|
||||
this.GaActions.CLICKED,
|
||||
this.buttonText,
|
||||
true
|
||||
);
|
||||
if (!this.isDisabled) {
|
||||
this.isLoaderDisplayed = true;
|
||||
this.$emit("click-event");
|
||||
|
|
@ -98,7 +105,8 @@ export default {
|
|||
background: $blue-700;
|
||||
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
||||
}
|
||||
&.delay {// fixes flicker while transitioning between states
|
||||
&.delay {
|
||||
// fixes flicker while transitioning between states
|
||||
transition: background 0s 0s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
|
@ -137,7 +145,8 @@ export default {
|
|||
color: $white;
|
||||
@include blue-gradient;
|
||||
}
|
||||
&.delay {// fixes flicker while transitioning between states
|
||||
&.delay {
|
||||
// fixes flicker while transitioning between states
|
||||
transition: background 0s 0s ease-in-out;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,272 +1,202 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { shallowMount, mount } from "@vue/test-utils";
|
||||
import listButtonHorizontal from "./list-button-horizontal";
|
||||
import { nextTick } from "vue";
|
||||
import { GaActions } from "@/constants/analytics";
|
||||
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||
|
||||
// TODO KO
|
||||
describe.skip("list-button-horizontal.vue", () => {
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
describe("list-button-horizontal.vue", () => {
|
||||
// TODO KO Have this moved out to somewhere shared
|
||||
describe("Shared baseInputButton checks", () => {
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
it("Should return input type radio if isMultiSelect is false or not specified", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
|
||||
it("Should return input type radio if isMultiSelect is false or not specified", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
// TODO KO
|
||||
it.skip("Should emit button value on click", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
value: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
buttonID: "list-card-id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
// Act
|
||||
wrapper.vm.handleAnswerChange("test");
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["change"][0]).toEqual(["test"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should return primary label text (buttonID)", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
buttonID: "List Card Checkbox",
|
||||
},
|
||||
describe("styling/UI", () => {
|
||||
it("Should return screen reader text", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
screenReaderOnlyText: "Screen Reader Only Text",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.sr-only");
|
||||
|
||||
expect(paragraph.text()).toEqual("Screen Reader Only Text");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const label = wrapper.find("label");
|
||||
it("Should return text alignment class", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
textPosition: "text-center",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(label.attributes().for).toEqual("List Card Checkbox");
|
||||
});
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.m-0");
|
||||
|
||||
it("Should return screen reader text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
screenReaderOnlyText: "Screen Reader Only Text",
|
||||
},
|
||||
expect(paragraph.attributes("class")).toContain("text-center");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.sr-only");
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(paragraph.text()).toEqual("Screen Reader Only Text");
|
||||
});
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
it("Should return text alignment class", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
textPosition: "text-center",
|
||||
},
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.m-0");
|
||||
it("is cash or insurance button => has 'radio-fancy' class", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isCashOrInsurance: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(paragraph.attributes("class")).toContain("text-center");
|
||||
});
|
||||
// Assert
|
||||
const label = wrapper.find("label");
|
||||
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isRequired: true,
|
||||
},
|
||||
expect(label.classes()).toContain("radio-fancy");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
test("has buttonLabel => displays buttonLabel", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
});
|
||||
|
||||
it("Should return loader enabled true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-horizontal-content");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(content.text()).toContain("Surprise!");
|
||||
});
|
||||
|
||||
// Assert
|
||||
test("has buttonLabelSubCopy => displays buttonLabelSubCopy", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
buttonLabelSubCopy: "Super duper surprise :)",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("Should return loader color", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
loaderColor: "blue",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-horizontal-content");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(content.text()).toContain("Super duper surprise :)");
|
||||
});
|
||||
|
||||
// Assert
|
||||
test("has screenReaderOnlyText => displays screenReaderOnlyText", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
buttonLabelSubCopy: "Super duper surprise :)",
|
||||
screenReaderOnlyText: "Tests are fun!"
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes("class")).toContain("blue");
|
||||
});
|
||||
|
||||
it("Should return loader position", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-horizontal-content");
|
||||
const screenReaderOnlyText = wrapper.find(".sr-only");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(screenReaderOnlyText.exists()).toBe(true);
|
||||
expect(screenReaderOnlyText.text()).toContain("Tests are fun!");
|
||||
});
|
||||
|
||||
// Assert
|
||||
|
||||
const label = wrapper.find("label");
|
||||
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
|
||||
await nextTick();
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes("class")).toContain("right");
|
||||
});
|
||||
|
||||
it("Should emit button value on click", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
value: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
},
|
||||
});
|
||||
wrapper.vm.handleCheckChange();
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean, buttonId: undefined, checkValue: false}]);
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButtonHorizontal, {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
buttonID: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
isMultiSelect: false,
|
||||
value: "Car-Front",
|
||||
selectedValues: ["Car-Front"]
|
||||
},
|
||||
});
|
||||
// Assert
|
||||
expect(wrapper.vm.checkValue).toEqual(true);
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setupMocks({ mockData }) {
|
||||
const wrapper = mount(listButtonHorizontal, {
|
||||
...mockData,
|
||||
propsData: {
|
||||
...mockData.propsData,
|
||||
groupName: "my-group",
|
||||
modelValue: mockData.propsData?.isMultiSelect ? ["5"] : "5",
|
||||
value: mockData.propsData?.isMultiSelect ? ["4"] : "4"
|
||||
},
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
});
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
'list-group list-button-horizontal d-flex flex-column w-100',
|
||||
{ 'radio-fancy': additionalData?.isCashOrInsurance },
|
||||
]"
|
||||
@buttonClicked="handleAnswerChange">
|
||||
v-model="selectedValue">
|
||||
<div
|
||||
class="list-button-horizontal-content d-flex flex-column justify-content-center p-3">
|
||||
<span class="m-0" :class="textPosition">
|
||||
|
|
|
|||
|
|
@ -1,264 +1,262 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { shallowMount, mount } from "@vue/test-utils";
|
||||
import listButton from "./list-button";
|
||||
import { nextTick } from "vue";
|
||||
import { GaActions } from "@/constants/analytics";
|
||||
import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
||||
|
||||
// TODO KO
|
||||
describe.skip("list-button.vue", () => {
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
describe("loader", () => {
|
||||
it("selectingInitiatesLoad is true and answer is changed => show the loader", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
global: {
|
||||
mocks: {
|
||||
$route: { query: { fmgPage: "page-name" } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
},
|
||||
},
|
||||
propsData: {
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.handleAnswerChange("something");
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
const loader = wrapper.findComponent({ name: "loader" });
|
||||
expect(loader.exists()).toBe(true);
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
it("Should return loader color", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
global: {
|
||||
mocks: {
|
||||
$route: { query: { fmgPage: "page-name" } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
},
|
||||
},
|
||||
propsData: {
|
||||
loaderColor: "blue",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
// Act
|
||||
wrapper.vm.handleAnswerChange("something");
|
||||
await nextTick();
|
||||
|
||||
it("Should return input type radio if isMultiSelect is false or not specified", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
// Assert
|
||||
const loader = wrapper.findComponent({ name: "loader" });
|
||||
expect(loader.attributes("class")).toContain("blue");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
it("Should return loader position", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
global: {
|
||||
mocks: {
|
||||
$route: { query: { fmgPage: "page-name" } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
},
|
||||
},
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
// Act
|
||||
wrapper.vm.handleAnswerChange("test");
|
||||
await nextTick();
|
||||
|
||||
// Assert
|
||||
const loader = wrapper.findComponent({ name: "loader" });
|
||||
expect(loader.attributes("class")).toContain("right");
|
||||
});
|
||||
});
|
||||
|
||||
it("Should return primary label text (buttonID)", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
buttonID: "List Card Checkbox",
|
||||
},
|
||||
describe("baseInputButton checks", () => {
|
||||
it("Should return input type checkbox if isMultiSelect is true", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes().type).toEqual("checkbox");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const label = wrapper.find("label");
|
||||
it("Should return input type radio if isMultiSelect is false or not specified", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isMultiSelect: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(label.attributes().for).toEqual("List Card Checkbox");
|
||||
});
|
||||
|
||||
it("Should return screen reader text", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
screenReaderOnlyText: "Screen Reader Only Text",
|
||||
},
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
expect(input.attributes().type).toEqual("radio");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.sr-only");
|
||||
it("Should emit button value on click", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
value: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
buttonID: "list-card-id",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(paragraph.text()).toEqual("Screen Reader Only Text");
|
||||
// Act
|
||||
wrapper.vm.handleAnswerChange("test");
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["change"][0]).toEqual(["test"]);
|
||||
});
|
||||
});
|
||||
|
||||
it("Should return text alignment class", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
textPosition: "text-center",
|
||||
},
|
||||
describe("styling/UI", () => {
|
||||
test("has buttonLabel => displays buttonLabel", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-content");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(content.text()).toContain("Surprise!");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.m-0");
|
||||
test("has buttonLabelSubCopy => displays buttonLabelSubCopy", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
buttonLabelSubCopy: "Super duper surprise :)",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(paragraph.attributes("class")).toContain("text-center");
|
||||
});
|
||||
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
isRequired: true,
|
||||
},
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-content");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(content.text()).toContain("Super duper surprise :)");
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
test("has screenReaderOnlyText => displays screenReaderOnlyText", () => {
|
||||
// Arrange/Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
buttonLabel: "Surprise!",
|
||||
buttonLabelSubCopy: "Super duper surprise :)",
|
||||
screenReaderOnlyText: "Tests are fun!",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
});
|
||||
|
||||
it("Should return loader enabled true", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
// Assert
|
||||
const content = wrapper.find(".list-button-content");
|
||||
const screenReaderOnlyText = wrapper.find(".sr-only");
|
||||
expect(content.isVisible()).toBe(true);
|
||||
expect(screenReaderOnlyText.exists()).toBe(true);
|
||||
expect(screenReaderOnlyText.text()).toContain("Tests are fun!");
|
||||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
it("Should return screen reader text", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
screenReaderOnlyText: "Screen Reader Only Text",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.sr-only");
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("Should return loader color", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
loaderColor: "blue",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
expect(paragraph.text()).toEqual("Screen Reader Only Text");
|
||||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
await nextTick();
|
||||
it("Should return text alignment class", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
textPosition: "text-center",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
// Assert
|
||||
const paragraph = wrapper.find("span.m-0");
|
||||
|
||||
expect(loader.attributes("class")).toContain("blue");
|
||||
});
|
||||
|
||||
it("Should return loader position", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
global: {
|
||||
mocks: {
|
||||
'$route': { query: { fmgPage: 'page-name' } },
|
||||
GaActions: GaActions,
|
||||
pushEventToGA: jest.fn(),
|
||||
}
|
||||
},
|
||||
propsData: {
|
||||
loaderPosition: "right",
|
||||
selectingInitiatesLoad: true,
|
||||
},
|
||||
expect(paragraph.attributes("class")).toContain("text-center");
|
||||
});
|
||||
|
||||
// Assert
|
||||
wrapper.vm.handleCheckChange = jest.fn();
|
||||
wrapper.vm.triggerButton();
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const { wrapper } = setupMocks({
|
||||
mockData: {
|
||||
propsData: {
|
||||
isRequired: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
const loader = wrapper.find("loader-stub");
|
||||
|
||||
expect(loader.attributes("class")).toContain("right");
|
||||
});
|
||||
|
||||
it("Should emit button value on click", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
value: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
buttonID: 'list-card-id'
|
||||
},
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
});
|
||||
|
||||
wrapper.vm.handleCheckChange();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: false, buttonId: 'list-card-id'}]);
|
||||
|
||||
});
|
||||
|
||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(listButton, {
|
||||
propsData: {
|
||||
isRadioHorizontal: true,
|
||||
buttonLabel: "Windshield",
|
||||
buttonID: "List Card Checkbox",
|
||||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
selectedValues: "Car-Front"
|
||||
},
|
||||
});
|
||||
// Assert
|
||||
expect(wrapper.componentVM.checkValue).toEqual(false);
|
||||
});
|
||||
|
||||
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;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setupMocks({ mockData }) {
|
||||
const wrapper = mount(listButton, {
|
||||
...mockData,
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
});
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<baseInputButton
|
||||
v-bind="$props"
|
||||
buttonWrapperClasses="list-group list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||
@buttonClicked="handleAnswerChange">
|
||||
v-model="selectedValue">
|
||||
<div
|
||||
:aria-label="buttonLabel"
|
||||
class="list-button-content d-flex flex-column justify-content-center py-3 px-4">
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ describe("list-card.vue", () => {
|
|||
groupID: "checkbox-demo-1",
|
||||
groupName: "Checkbox 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ describe("list-card.vue", () => {
|
|||
groupID: "radio-demo-1",
|
||||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -51,6 +53,7 @@ describe("list-card.vue", () => {
|
|||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
buttonLabelSubCopy: "Test",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -70,6 +73,7 @@ describe("list-card.vue", () => {
|
|||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
buttonLabelSubCopy: "Test",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -89,6 +93,7 @@ describe("list-card.vue", () => {
|
|||
groupName: "radio 1",
|
||||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -110,6 +115,7 @@ describe("list-card.vue", () => {
|
|||
isRequired: true,
|
||||
isWide: true,
|
||||
buttonLabelSubCopy: "",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -134,6 +140,7 @@ describe("list-card.vue", () => {
|
|||
isRequired: true,
|
||||
isWide: true,
|
||||
buttonLabelSubCopy: "Button Subcopy",
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -159,6 +166,7 @@ describe("list-card.vue", () => {
|
|||
buttonImage: "windshield-damage.svg",
|
||||
isRequired: true,
|
||||
isWide: false,
|
||||
value: "test value",
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
'list-card w-100 rounded-3 d-flex align-items-center h-100',
|
||||
{ horizontal: isWide },
|
||||
]"
|
||||
@buttonClicked="handleAnswerChange">
|
||||
v-model="selectedValue">
|
||||
<div
|
||||
class="d-flex w-100 align-items-center px-2 h-100 list-card-content"
|
||||
:class="labelClasses">
|
||||
|
|
|
|||
|
|
@ -6,12 +6,16 @@ import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
|
|||
describe("radio.vue", () => {
|
||||
it("Should have correct group name", async () => {
|
||||
// Arrange
|
||||
let { wrapper } = setupMocks({});
|
||||
let { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
propsData: {
|
||||
groupName: "radio-button-test",
|
||||
value: "test value",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.setProps({
|
||||
groupName: "radio-button-test",
|
||||
});
|
||||
const input = wrapper.find("input");
|
||||
|
||||
// Assert
|
||||
|
|
@ -20,12 +24,16 @@ describe("radio.vue", () => {
|
|||
|
||||
it("Should have correct label text", async () => {
|
||||
// Act
|
||||
let { wrapper } = setupMocks({});
|
||||
let { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
propsData: {
|
||||
buttonLabel: "label text",
|
||||
value: "test value",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Arrange
|
||||
await wrapper.setProps({
|
||||
buttonLabel: "label text",
|
||||
});
|
||||
const paragraph = wrapper.find("p");
|
||||
|
||||
// Assert
|
||||
|
|
@ -39,6 +47,7 @@ describe("radio.vue", () => {
|
|||
// Arrange
|
||||
await wrapper.setProps({
|
||||
screenReaderOnlyText: "screenreader text",
|
||||
value: "test value",
|
||||
});
|
||||
const paragraph = wrapper.find(".sr-only");
|
||||
|
||||
|
|
@ -48,13 +57,10 @@ describe("radio.vue", () => {
|
|||
});
|
||||
|
||||
function setupMocks({ mountOptionsMockData = {} }) {
|
||||
const wrapper = mount(
|
||||
radio,
|
||||
getMountOptions({
|
||||
...mountOptionsMockData,
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
})
|
||||
);
|
||||
const wrapper = mount(radio, {
|
||||
...mountOptionsMockData,
|
||||
mixins: [inputButtonWrapperMixin],
|
||||
});
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
v-bind="$props"
|
||||
buttonWrapperClasses="ui-radio form-check"
|
||||
inputClasses="form-check-input"
|
||||
@buttonClicked="handleAnswerChange">
|
||||
v-model="selectedValue">
|
||||
<div class="d-flex align-items-start form-check-label">
|
||||
<p v-if="buttonLabel" class="m-0">{{ buttonLabel }}</p>
|
||||
<span v-if="screenReaderOnlyText" class="sr-only">{{
|
||||
|
|
|
|||
Loading…
Reference in a new issue