+
- {{ questionText }}
+ {{ questionText }}
+
-
+
-
-
-
-
-
-
-
\ No newline at end of file
+ fieldset {
+ .ui-radio {
+ margin: 0;
+ }
+ }
+}
+
diff --git a/src/common-components/site-footer/site-footer.vue b/src/common-components/site-footer/site-footer.vue
index 2a611968..fc21f9ce 100644
--- a/src/common-components/site-footer/site-footer.vue
+++ b/src/common-components/site-footer/site-footer.vue
@@ -33,7 +33,7 @@ import textLink from "@/ux-components/text-link/text-link";
import buttonMain from "@/ux-components/button-main/button-main";
export default {
- name: "funnelFooter",
+ name: "siteFooter",
props: {
isForwardActionDisabled: Boolean,
isBackButtonHidden: { type: Boolean, default: false },
diff --git a/src/constants/damage-locations-cms.js b/src/constants/damage-locations-cms.js
new file mode 100644
index 00000000..82a43175
--- /dev/null
+++ b/src/constants/damage-locations-cms.js
@@ -0,0 +1,9 @@
+const damageLocationsCms = {
+ WINDSHIELD: "WINDSHIELD",
+ SIDEDOOR: "SIDEDOOR",
+ REARWINDOW: "REARWINDOW",
+ DRIVERSIDE: "DRIVERSIDE",
+ PASSENGERSIDE: "PASSENGERSIDE",
+};
+
+export { damageLocationsCms };
diff --git a/src/constants/damage-locations-selected.js b/src/constants/damage-locations-selected.js
new file mode 100644
index 00000000..7ede615c
--- /dev/null
+++ b/src/constants/damage-locations-selected.js
@@ -0,0 +1,21 @@
+const damageLocationsSelected = {
+ WINDSHIELD: "Windshield",
+ SIDEDOOR: "SideDoor",
+ REARWINDOW: "RearWindow",
+ REPAIR: "Repair",
+ REPLACE: "Replace",
+ DRIVER: "Driver",
+ PASSENGER: "Passenger",
+ FRONT: "Front",
+ REAR: "Rear",
+ BACK: "Back",
+ QUARTER: "Quarter",
+ VENT: "Vent",
+ SINGLE: "Single",
+ DRIVERSIDE: "DriverSide",
+ PASSENGERSIDE: "PassengerSide",
+ STATIONARY: "Stationary",
+ SLIDER: "Slider",
+};
+
+export { damageLocationsSelected };
diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js
index 5a80175f..3aa3ce24 100644
--- a/src/constants/endpoints.js
+++ b/src/constants/endpoints.js
@@ -27,6 +27,10 @@ const endpoints = {
url: "/vehicle/api/v1/vehicle/styles",
method: "GET",
},
+ GetDamageOptions: {
+ url: "/parts/api/v1/parts/damage-options",
+ method: "GET",
+ },
GetVehicle: {
url: "/vehicle/api/v1/vehicle/lookup",
method: "GET",
diff --git a/src/global-methods.js b/src/global-methods.js
index ce66babb..3f7e75b8 100644
--- a/src/global-methods.js
+++ b/src/global-methods.js
@@ -33,7 +33,6 @@ export default {
true
);
}
-
return resolve(response);
},
error => {
diff --git a/src/helpers/button-question-focus-helper.js b/src/helpers/button-question-focus-helper.js
new file mode 100644
index 00000000..427f2546
--- /dev/null
+++ b/src/helpers/button-question-focus-helper.js
@@ -0,0 +1,38 @@
+/**
+ * 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 if the selection 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;
+
+ 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 };
+
\ No newline at end of file
diff --git a/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue
new file mode 100644
index 00000000..b015907a
--- /dev/null
+++ b/src/layouts/vehicle-damage/damage-location-question/damage-location-question.vue
@@ -0,0 +1,86 @@
+
+
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue
new file mode 100644
index 00000000..e7884c9e
--- /dev/null
+++ b/src/layouts/vehicle-damage/replace-options-question/replace-options-question.vue
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/side-door-options/side-door-options.vue b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue
new file mode 100644
index 00000000..5a44ccfb
--- /dev/null
+++ b/src/layouts/vehicle-damage/side-door-options/side-door-options.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/vehicle-damage.vue b/src/layouts/vehicle-damage/vehicle-damage.vue
new file mode 100644
index 00000000..3d5fcfe0
--- /dev/null
+++ b/src/layouts/vehicle-damage/vehicle-damage.vue
@@ -0,0 +1,464 @@
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue
new file mode 100644
index 00000000..48b813f2
--- /dev/null
+++ b/src/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue
new file mode 100644
index 00000000..01e14608
--- /dev/null
+++ b/src/layouts/vehicle-damage/windshield-options/windshield-damage-type-question/windshield-damage-type-question.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/layouts/vehicle-damage/windshield-options/windshield-options.vue b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue
new file mode 100644
index 00000000..b878e538
--- /dev/null
+++ b/src/layouts/vehicle-damage/windshield-options/windshield-options.vue
@@ -0,0 +1,238 @@
+
+
+
+
+
diff --git a/src/layouts/vehicle-style/vehicle-style.vue b/src/layouts/vehicle-style/vehicle-style.vue
index 1ce939af..4c48f960 100644
--- a/src/layouts/vehicle-style/vehicle-style.vue
+++ b/src/layouts/vehicle-style/vehicle-style.vue
@@ -7,6 +7,7 @@
diff --git a/src/mixins/base-mixin.js b/src/mixins/base-mixin.js
index e003e989..32134711 100644
--- a/src/mixins/base-mixin.js
+++ b/src/mixins/base-mixin.js
@@ -5,6 +5,7 @@ import { navigationScenarios } from "@/router/router-constants/navigation-scenar
import { vehicleCategories } from "@/constants/vehicle-categories.js";
import { queryStrings } from "@/constants/query-strings";
import { dynamicStrings } from "@/constants/dynamic-strings";
+import { routerParams } from "@/router/router-constants/router-params";
export default {
data() {
@@ -43,5 +44,8 @@ export default {
cssClassNameForCmsWidget(){
return "widget-name-" + this.cmsWidgetName;
},
+ routerParams() {
+ return routerParams;
+ },
},
};
\ No newline at end of file
diff --git a/src/mixins/input-button-wrapper-mixin.js b/src/mixins/input-button-wrapper-mixin.js
new file mode 100644
index 00000000..1d34d652
--- /dev/null
+++ b/src/mixins/input-button-wrapper-mixin.js
@@ -0,0 +1,40 @@
+import { inputButtonProps } from "@/common-components/base-input-button/button-functionality-props";
+
+export default {
+ model: {
+ prop: "modelValue",
+ event: "change",
+ },
+ props: {
+ ...inputButtonProps,
+ buttonLabel: [Number, String],
+ buttonLabelSubCopy: String,
+ buttonBodyCopy: String,
+ buttonAuxillaryCopy: String,
+ buttonFooterCopy: String,
+ buttonImage: String,
+ buttonImageId: String,
+ altText: {
+ type: String,
+ default: "",
+ },
+ textPosition: String,
+ screenReaderOnlyText: String,
+ isWide: Boolean,
+ additionalButtonStyling: String,
+ },
+ computed: {
+ selectedValue: {
+ get() {
+ return this.modelValue;
+ },
+ set(e) {
+ if (this.preHandleAnswerChange) {
+ this.preHandleAnswerChange(e);
+ }
+
+ this.$emit("update:modelValue", e);
+ },
+ },
+ },
+};
diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js
index 4263b29c..717ef11e 100644
--- a/src/router/router-constants/routing-table.js
+++ b/src/router/router-constants/routing-table.js
@@ -52,10 +52,27 @@ const routingTable = function(store) {
},
{
scenario: navigationScenarios.SELECTED_STYLE,
- destinationIssPageValue: issPageValues.WELCOME_PAGE,
+ destinationIssPageValue: issPageValues.VEHICLE_DAMAGE,
}
]
},
+ {
+ issPageValue: issPageValues.VEHICLE_DAMAGE,
+ maps: [
+ {
+ scenario: navigationScenarios.CLICKED_BACK,
+ destinationIssPageValue: issPageValues.VEHICLE_STYLE,
+ },
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
+ destinationIssPageValue: issPageValues.WELCOME_PAGE,
+ },
+ {
+ scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
+ destinationIssPageValue: issPageValues.WELCOME_PAGE,
+ },
+ ],
+ },
{
issPageValue: issPageValues.WELCOME_PAGE,
maps: [
diff --git a/src/router/router-params.js b/src/router/router-params.js
new file mode 100644
index 00000000..a8ee17b9
--- /dev/null
+++ b/src/router/router-params.js
@@ -0,0 +1,5 @@
+const routerParams = {
+ DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert",
+};
+
+export { routerParams };
\ No newline at end of file
diff --git a/src/store/index.js b/src/store/index.js
index de4c65ac..91d82140 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -30,6 +30,14 @@ const getDefaultState = () => {
lastName: null,
},
},
+ damage: {
+ isRepair: null,
+ numberOfChips: null,
+ glassToReplace: null,
+ partQuestionAnswers: null,
+ moldingQuestionAnswers: null,
+ capabilityQuestionAnswers: null,
+ },
referralNumber: null,
referralDate: null,
accountNumber: 0,
@@ -56,6 +64,8 @@ export const useMainStore = defineStore({
id: storeId,
state: () => state,
getters: {
+ vehicle: (state) => state.order.vehicle,
+ damage: (state) => state.order.damage,
eventBusItem: (state) => ( eventCategory, eventSubCategory) => {
const matchedEvent = state.applicationUser.eventBus.find(
@@ -190,6 +200,13 @@ export const useMainStore = defineStore({
});
},
+ getDamageOptions(carId) {
+ return globalMethods.callHttpClient({
+ methods: endpoints.GetDamageOptions.method,
+ endpoint: `${endpoints.GetDamageOptions.url}/${carId}`,
+ payload: {},
+ });
+ },
setVehicle() {
return globalMethods
.callHttpClient({
@@ -204,7 +221,11 @@ export const useMainStore = defineStore({
},
updateVehicle(vehicle) {
- this.order.vehicle = { ...this.order.vehicle, ...vehicle };
+ this.order.vehicle.carId = vehicle.carId;
+ this.order.vehicle.category = vehicle.category;
+ this.order.vehicle.imageUrl = vehicle.imageUrl;
+ this.order.vehicle.imageVifNumber = vehicle.imageVifNumber;
+ this.order.vehicle.imageColor = vehicle.imageColor;
},
resetVehicleState()
diff --git a/src/store/store.spec.js b/src/store/store.spec.js
index a1e6e2f5..5bf8bbad 100644
--- a/src/store/store.spec.js
+++ b/src/store/store.spec.js
@@ -17,7 +17,7 @@ describe("Store", () => {
store = useMainStore();
store.applicationUser.eventBus = [];
jest.resetAllMocks();
- })
+ });
it("Should Store Vehicle Year", () => {
@@ -114,7 +114,7 @@ describe("Store", () => {
expect(store.order.vehicle.imageUrl).toEqual(response.data.imageUrl);
expect(store.order.vehicle.imageVifNumber).toEqual(response.data.imageVifNumber);
- expect(store.order.vehicle.imageVifColor).toEqual(response.data.imageVifColor);
+ expect(store.order.vehicle.style).toEqual(response.data.style);
});
it("setVehicle should call globalMethods.callHttpClient", () => {
diff --git a/src/styles/mixins/customMixins.scss b/src/styles/mixins/customMixins.scss
index 3576a8ac..cd3555cc 100644
--- a/src/styles/mixins/customMixins.scss
+++ b/src/styles/mixins/customMixins.scss
@@ -4,3 +4,7 @@
@mixin blue-gradient {
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
}
+
+@mixin box-shadow-hover($color) {
+ box-shadow: 0 0 0 4px $color;
+}
diff --git a/src/styles/shared-input-button-styles.scss b/src/styles/shared-input-button-styles.scss
new file mode 100644
index 00000000..dbffa5b0
--- /dev/null
+++ b/src/styles/shared-input-button-styles.scss
@@ -0,0 +1,15 @@
+.base-input-button {
+ &:not(.has-error):hover {
+ cursor: pointer;
+
+ &.list-button,
+ &.list-button-horizontal,
+ &.list-card {
+ &:not(.selected) {
+ position: relative;
+ z-index: 4;
+ @include box-shadow-hover($blue-300);
+ }
+ }
+ }
+}
diff --git a/src/styles/ux-variables.scss b/src/styles/ux-variables.scss
index 52cf8916..d9b462af 100644
--- a/src/styles/ux-variables.scss
+++ b/src/styles/ux-variables.scss
@@ -1,186 +1,189 @@
//Colors
// White/black
-$white: #FFFFFF;
-$black: #000000;
+$white: #ffffff;
+$black: #000000;
// Blues
-$blue-100: #E4F1F7;// Used in theme
-$blue-200: #C1DFEE;
-$blue-300: #9FCEE6;
-$blue-400: #69ADCF;// Used in theme
-$blue-500: #3B8FB8;
-$blue: #1574A1;// Default Blue
-$blue-700: #06577C;
-$blue-800: #003D58;
-$blue-900: #002433;// Used in theme
+$blue-100: #e4f1f7; // Used in theme
+$blue-200: #c1dfee;
+$blue-300: #9fcee6;
+$blue-400: #69adcf; // Used in theme
+$blue-500: #3b8fb8;
+$blue: #1574a1; // Default Blue
+$blue-700: #06577c;
+$blue-800: #003d58;
+$blue-900: #002433; // Used in theme
// Reds
-$red-100: #FFE6E4;
-$red-200: #FCBFBB;
-$red-300: #F89892;
-$red-400: #E65C53;
-$red: #D4281C;// Default Red
-$red-600: #AC160B;
-$red-700: #840900;
-$red-800: #5B0600;
-$red-900: #330300;
+$red-100: #ffe6e4;
+$red-200: #fcbfbb;
+$red-300: #f89892;
+$red-400: #e65c53;
+$red: #d4281c; // Default Red
+$red-600: #ac160b;
+$red-700: #840900;
+$red-800: #5b0600;
+$red-900: #330300;
// Greens
-$green-100: #E3F2EA;
-$green-200: #BCEDD4;
-$green-300: #94D7B6;
-$green-400: #5ABC8C;// Used in theme
-$green-500: #2CA168;
-$green: #0C7E47;// Default Green
-$green-700: #006A36;
-$green-800: #004F28;
-$green-900: #00331A;
+$green-100: #e3f2ea;
+$green-200: #bcedd4;
+$green-300: #94d7b6;
+$green-400: #5abc8c; // Used in theme
+$green-500: #2ca168;
+$green: #0c7e47; // Default Green
+$green-700: #006a36;
+$green-800: #004f28;
+$green-900: #00331a;
// Yellows
-$yellow-100: #FFF5EB;
-$yellow-200: #FFE1C6;
-$yellow-300: #FFCAA0;
-$yellow-400: #F5975D;
-$yellow: #E86421;// Default Yellow
-$yellow-600: #BB4B06;
-$yellow-700: #8E3C00;
-$yellow-800: #602D00;
-$yellow-900: #331A00;
+$yellow-100: #fff5eb;
+$yellow-200: #ffe1c6;
+$yellow-300: #ffcaa0;
+$yellow-400: #f5975d;
+$yellow: #e86421; // Default Yellow
+$yellow-600: #bb4b06;
+$yellow-700: #8e3c00;
+$yellow-800: #602d00;
+$yellow-900: #331a00;
// Grays
-$gray-100: #F5F5F5;// Used in theme
-$gray-200: #E3E4E4;// Used in theme
-$gray-300: #D2D4D4;
-$gray: #B0B3B3;// Default Gray
-$gray-500: #8E9292;// Used in theme
-$gray-550: #727676;// Used in theme
-$gray-600: #4D5151;// Used in theme
-$gray-700: #303333;// Used in theme
-$gray-800: #222424;// Used in theme
-$gray-900: #181A1A;// Used in theme
+$gray-100: #f5f5f5; // Used in theme
+$gray-200: #e3e4e4; // Used in theme
+$gray-300: #d2d4d4;
+$gray: #b0b3b3; // Default Gray
+$gray-500: #8e9292; // Used in theme
+$gray-550: #727676; // Used in theme
+$gray-600: #4d5151; // Used in theme
+$gray-700: #303333; // Used in theme
+$gray-800: #222424; // Used in theme
+$gray-900: #181a1a; // Used in theme
// Miscellaneous Colors
-$indigo: #6610f2;
-$purple: #6f42c1;
-$pink: #d63384;
-$orange: #fd7e14;
-$teal: #20c997;
-$cyan: #0dcaf0;
+$indigo: #6610f2;
+$purple: #6f42c1;
+$pink: #d63384;
+$orange: #fd7e14;
+$teal: #20c997;
+$cyan: #0dcaf0;
// scss-docs-start colors-map
$colors: (
-"blue": $blue,
-"indigo": $indigo,
-"purple": $purple,
-"pink": $pink,
-"red": $red,
-"orange": $orange,
-"yellow": $yellow,
-"green": $green,
-"teal": $teal,
-"cyan": $cyan,
-"white": $white,
-"gray": $gray,
-"gray-dark": $gray-500
+ "blue": $blue,
+ "indigo": $indigo,
+ "purple": $purple,
+ "pink": $pink,
+ "red": $red,
+ "orange": $orange,
+ "yellow": $yellow,
+ "green": $green,
+ "teal": $teal,
+ "cyan": $cyan,
+ "white": $white,
+ "gray": $gray,
+ "gray-dark": $gray-500,
);
// scss-docs-start theme-color-variables
-$primary: $blue;
-$secondary: $red;
-$success: $green;
-$info: $cyan;
-$warning: $yellow;
-$danger: $red;
-$light: $gray-100;
-$dark: $gray-500;
+$primary: $blue;
+$secondary: $red;
+$success: $green;
+$info: $cyan;
+$warning: $yellow;
+$danger: $red;
+$light: $gray-100;
+$dark: $gray-500;
// scss-docs-start theme-colors-map
$theme-colors: (
-"primary": $primary,
-"secondary": $secondary,
-"success": $success,
-"info": $info,
-"warning": $warning,
-"danger": $danger,
-"light": $light,
-"dark": $dark
+ "primary": $primary,
+ "secondary": $secondary,
+ "success": $success,
+ "info": $info,
+ "warning": $warning,
+ "danger": $danger,
+ "light": $light,
+ "dark": $dark,
);
//Default font color
-$body-color: $gray-600;
+$body-color: $gray-600;
//Fonts
-$font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif;
-$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
+$font-family-sans-serif: Roboto, Arial, Helvetica, sans-serif;
+$font-family-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
+ monospace;
// stylelint-enable value-keyword-case
-$font-family-base: $font-family-sans-serif;
-$font-family-code: $font-family-monospace;
-$font-size-base: 1rem; // Assumes the browser default, typically `16px`
+$font-family-base: $font-family-sans-serif;
+$font-family-code: $font-family-monospace;
+$font-size-base: 1rem; // Assumes the browser default, typically `16px`
//Custom Font size (extra small)
-$font-size-xsm: $font-size-base * .75;
+$font-size-xsm: $font-size-base * 0.75;
$font-sizes: (
- 7: $font-size-xsm
+ 7: $font-size-xsm,
);
//Font weight
-$font-weight-lighter: lighter;
-$font-weight-light: 300;
-$font-weight-normal: 400;
-$font-weight-bold: 500;
-$font-weight-bolder: bolder;
+$font-weight-lighter: lighter;
+$font-weight-light: 300;
+$font-weight-normal: 400;
+$font-weight-bold: 500;
+$font-weight-bolder: bolder;
//Headings
-$h1-font-size: $font-size-base * 3;
-$h2-font-size: $font-size-base * 2.625;
-$h3-font-size: $font-size-base * 2;
-$h4-font-size: $font-size-base * 1.625;
-$h5-font-size: $font-size-base * 1.25;
-$h6-font-size: $font-size-base * .875;
+$h1-font-size: $font-size-base * 3;
+$h2-font-size: $font-size-base * 2.625;
+$h3-font-size: $font-size-base * 2;
+$h4-font-size: $font-size-base * 1.625;
+$h5-font-size: $font-size-base * 1.25;
+$h6-font-size: $font-size-base * 0.875;
//Border Radius
// Helper classes are rounded, rounded-1, rounded-2, rounded-3
-$border-radius: .25rem;
-$border-radius-sm: .2rem;
-$border-radius-lg: .5rem;//Used for buttons. Can be used for other things, of course.
-$border-radius-pill: 50rem;
+$border-radius: 0.25rem;
+$border-radius-sm: 0.2rem;
+$border-radius-lg: 0.5rem; //Used for buttons. Can be used for other things, of course.
+$border-radius-pill: 50rem;
//Spacing
// 8 spacers available instead of the usual 5
$spacer: 1rem;
$spacers: (
- 0: 0,
- 1: $spacer * .25, /* 4px */
- 2: $spacer * .5, /* 8px */
- 3: $spacer * .75, /* 12px */
- 4: $spacer * 1, /* 16px */
- 5: $spacer * 1.5, /* 24px */
- 6: $spacer * 2, /* 32px */
- 7: $spacer * 2.5, /* 40px */
- 8: $spacer * 3, /* 48px */
+ 0: 0,
+ 1: $spacer * 0.25,
+ /* 4px */ 2: $spacer * 0.5,
+ /* 8px */ 3: $spacer * 0.75,
+ /* 12px */ 4: $spacer * 1,
+ /* 16px */ 5: $spacer * 1.5,
+ /* 24px */ 6: $spacer * 2,
+ /* 32px */ 7: $spacer * 2.5,
+ /* 40px */ 8: $spacer * 3,
+ /* 48px */
);
//Grid breakpoints
$grid-breakpoints: (
- xs: 0,
- sm: 576px,
- md: 838px,
- lg: 1074px,
- xl: 1416px
+ xs: 0,
+ sm: 576px,
+ md: 838px,
+ lg: 1074px,
+ xl: 1416px,
);
//Shadow
-$box-shadow: 0 .5rem 1rem rgba($black, .15);
-$box-shadow-sm: 0 .125rem .25rem rgba($black, .075);
-$box-shadow-lg: 0 1rem 3rem rgba($black, .25);//Safelite default
-$box-shadow-inset: inset 0 1px 2px rgba($black, .075);
+$box-shadow: 0 0.5rem 1rem rgba($black, 0.15);
+$box-shadow-sm: 0 0.125rem 0.25rem rgba($black, 0.075);
+$box-shadow-lg: 0 1rem 3rem rgba($black, 0.25); //Safelite default
+$box-shadow-inset: inset 0 1px 2px rgba($black, 0.075);
//Alerts
-$alert-bg-scale: -90%;
-$alert-border-scale: -100%;
-$alert-color-scale: 40%;
+$alert-bg-scale: -90%;
+$alert-border-scale: -100%;
+$alert-color-scale: 40%;
//Modal animation
-$modal-fade-transform: translate(0, 0);
-$modal-backdrop-opacity: 0;
+// This affects all [Bootstrap] modals
+$modal-fade-transform: translate(0, 100%);
+$modal-backdrop-opacity: 0;
diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js
index 0d51bf57..43271781 100644
--- a/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js
+++ b/src/ux-components/list-button-horizontal/list-button-horizontal.spec.js
@@ -1,264 +1,139 @@
-import { shallowMount } from "@vue/test-utils";
+import { mount } from "@vue/test-utils";
import listButtonHorizontal from "./list-button-horizontal";
-import { nextTick } from "vue";
+import inputButtonWrapperMixin from "@/mixins/input-button-wrapper-mixin";
describe("list-button-horizontal.vue", () => {
- it("Should return input type checkbox if isMultiSelect is true", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- propsData: {
- isMultiSelect: true,
- },
+ 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");
+ });
+
+ it("Should return text alignment class", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ textPosition: "text-center",
+ },
+ },
+ });
+
+ // Assert
+ const paragraph = wrapper.find("span.m-0");
+
+ expect(paragraph.attributes("class")).toContain("text-center");
+ });
+
+ it("Should return aria-required state", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ isRequired: true,
+ },
+ },
+ });
+
+ // Assert
+ const input = wrapper.find("input");
+
+ expect(input.attributes()["aria-required"]).toEqual("true");
+ });
+
+ it("is cash or insurance button => has 'strong' class", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ additionalButtonStyling: "listButtonHorizontalStrong",
+ },
+ },
+ });
+
+ // Assert
+ const label = wrapper.find("label");
+
+ expect(label.classes()).toContain("strong");
+ });
+
+ test("has buttonLabel => displays buttonLabel", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ },
+ },
+ });
+
+ // Assert
+ const content = wrapper.find(".list-button-horizontal-content");
+ expect(content.isVisible()).toBe(true);
+ expect(content.text()).toContain("Surprise!");
+ });
+
+ test("has buttonLabelSubCopy => displays buttonLabelSubCopy", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ buttonLabelSubCopy: "Super duper surprise :)",
+ },
+ },
+ });
+
+ // Assert
+ const content = wrapper.find(".list-button-horizontal-content");
+ expect(content.isVisible()).toBe(true);
+ expect(content.text()).toContain("Super duper surprise :)");
+ });
+
+ test("has screenReaderOnlyText => displays screenReaderOnlyText", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ buttonLabelSubCopy: "Super duper surprise :)",
+ screenReaderOnlyText: "Tests are fun!",
+ },
+ },
+ });
+
+ // 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 input = wrapper.find("input");
-
- 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");
- });
-
- it("Should return primary label text (buttonID)", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- propsData: {
- buttonID: "List Card Checkbox",
- },
- });
-
- // Assert
- const label = wrapper.find("label");
-
- expect(label.attributes().for).toEqual("List Card Checkbox");
- });
-
- it("Should return screen reader text", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- propsData: {
- screenReaderOnlyText: "Screen Reader Only Text",
- },
- });
-
- // Assert
- const paragraph = wrapper.find("span.sr-only");
-
- expect(paragraph.text()).toEqual("Screen Reader Only Text");
- });
-
- it("Should return text alignment class", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- propsData: {
- textPosition: "text-center",
- },
- });
-
- // Assert
- const paragraph = wrapper.find("span.m-0");
-
- expect(paragraph.attributes("class")).toContain("text-center");
- });
-
- it("Should return aria-required state", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- propsData: {
- isRequired: true,
- },
- });
-
- // Assert
- const input = wrapper.find("input");
-
- expect(input.attributes()["aria-required"]).toEqual("true");
- });
-
- it("Should return loader enabled true", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- selectingInitiatesLoad: true,
- },
- });
-
- // Assert
-
- 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: { issPage: 'page-name' } },
- }
- },
- propsData: {
- loaderColor: "blue",
- selectingInitiatesLoad: true,
- },
- });
-
- // 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("blue");
- });
-
- it("Should return loader position", async () => {
- // Act
- const wrapper = shallowMount(listButtonHorizontal, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- loaderPosition: "right",
- selectingInitiatesLoad: true,
- },
- });
-
- // 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 };
+}
diff --git a/src/ux-components/list-button-horizontal/list-button-horizontal.vue b/src/ux-components/list-button-horizontal/list-button-horizontal.vue
index 534448b7..e51d9ecd 100644
--- a/src/ux-components/list-button-horizontal/list-button-horizontal.vue
+++ b/src/ux-components/list-button-horizontal/list-button-horizontal.vue
@@ -1,206 +1,84 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+ //Cash/insurance styling
+ &:first-of-type {
+ .list-button-horizontal.strong {
+ input[type="radio"] {
+ &:checked + .list-button-horizontal-content {
+ border-bottom-right-radius: 0;
+ border-top-right-radius: 0;
+ }
+
+ &:checked:focus + .list-button-horizontal-content {
+ border-bottom-right-radius: 0.5rem;
+ border-top-right-radius: 0.5rem;
+ }
+ }
+ }
+ }
+
+ &:last-of-type {
+ .list-button-horizontal.strong {
+ input[type="radio"] {
+ &:checked + .list-button-horizontal-content {
+ border-bottom-left-radius: 0;
+ border-top-left-radius: 0;
+ }
+
+ &:checked:focus + .list-button-horizontal-content {
+ border-bottom-left-radius: 0.5rem;
+ border-top-left-radius: 0.5rem;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/ux-components/list-button/list-button.spec.js b/src/ux-components/list-button/list-button.spec.js
index e54710ed..3b7d267d 100644
--- a/src/ux-components/list-button/list-button.spec.js
+++ b/src/ux-components/list-button/list-button.spec.js
@@ -1,256 +1,304 @@
-import { shallowMount } from "@vue/test-utils";
+import { 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";
describe("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.selectedValue = "something";
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ const loader = wrapper.findComponent({ name: "loader" });
+ expect(loader.exists()).toBe(true);
+ });
+
+ it("Should return loader color", async () => {
+ // Arrange
+ const { wrapper } = setupMocks({
+ mockData: {
+ global: {
+ mocks: {
+ $route: { query: { fmgPage: "page-name" } },
+ GaActions: GaActions,
+ pushEventToGA: jest.fn(),
+ },
+ },
+ propsData: {
+ loaderColor: "blue",
+ selectingInitiatesLoad: true,
+ },
+ },
+ });
+
+ // Act
+ wrapper.vm.selectedValue = "something";
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ const loader = wrapper.findComponent({ name: "loader" });
+ expect(loader.attributes("class")).toContain("blue");
+ });
+
+ 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,
+ },
+ },
+ });
+
+ // Act
+ wrapper.vm.selectedValue = "something";
+ await wrapper.vm.$nextTick();
+
+ // Assert
+ const loader = wrapper.findComponent({ name: "loader" });
+ expect(loader.attributes("class")).toContain("right");
+ });
});
- // Assert
- const input = wrapper.find("input");
+ describe("baseInputButton checks", () => {
+ it("Should return input type checkbox if isMultiSelect is true", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ isMultiSelect: true,
+ },
+ },
+ });
- 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 = shallowMount(listButton, {
- 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 } = setupMocks({
+ mockData: {
+ propsData: {
+ isMultiSelect: false,
+ },
+ },
+ });
+
+ // Assert
+ const input = wrapper.find("input");
+ expect(input.attributes().type).toEqual("radio");
+ });
+
+ it("(Radio) Should emit button value on selectedValue change", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ value: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio1",
+ buttonImage: "windshield-damage.svg",
+ isRequired: true,
+ isWide: false,
+ modelValue: "List Card Checkbox",
+ buttonID: "list-card-id",
+ },
+ },
+ });
+
+ // Act
+ wrapper.vm.selectedValue = "test";
+
+ // Assert
+ expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual("test");
+ });
+
+ it("(Checkbox) Should emit button value on selectedValue change", 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",
+ isMultiSelect: true,
+ },
+ },
+ });
+
+ // Act
+ wrapper.vm.selectedValue = ["test"];
+
+ // Assert
+ expect(wrapper.emitted()["update:modelValue"][0][0]).toEqual(["test"]);
+ });
});
- // Assert
- const input = wrapper.find("input");
+ describe("styling/UI", () => {
+ test("has buttonLabel => displays buttonLabel", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ modelValue: "",
+ groupName: "groupName",
+ value: "myValue",
+ },
+ },
+ });
- expect(input.attributes().type).toEqual("radio");
- });
+ // Assert
+ const content = wrapper.find(".list-button-content");
+ expect(content.isVisible()).toBe(true);
+ expect(content.text()).toContain("Surprise!");
+ });
- it("Should return primary label text (buttonID)", async () => {
- // Act
- const wrapper = shallowMount(listButton, {
- propsData: {
- buttonID: "List Card Checkbox",
- },
+ test("has buttonLabelSubCopy => displays buttonLabelSubCopy", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ buttonLabelSubCopy: "Super duper surprise :)",
+ modelValue: "",
+ groupName: "groupName",
+ value: "myValue",
+ },
+ },
+ });
+
+ // Assert
+ const content = wrapper.find(".list-button-content");
+ expect(content.isVisible()).toBe(true);
+ expect(content.text()).toContain("Super duper surprise :)");
+ });
+
+ test("has screenReaderOnlyText => displays screenReaderOnlyText", () => {
+ // Arrange/Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ buttonLabel: "Surprise!",
+ buttonLabelSubCopy: "Super duper surprise :)",
+ screenReaderOnlyText: "Tests are fun!",
+ modelValue: "",
+ groupName: "groupName",
+ value: "myValue",
+ },
+ },
+ });
+
+ // 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!");
+ });
+
+ it("Should return screen reader text", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ screenReaderOnlyText: "Screen Reader Only Text",
+ modelValue: "",
+ groupName: "groupName",
+ value: "myValue",
+ },
+ },
+ });
+
+ // Assert
+ const paragraph = wrapper.find("span.sr-only");
+
+ expect(paragraph.text()).toEqual("Screen Reader Only Text");
+ });
+
+ it("Should return text alignment class", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ textPosition: "text-center",
+ modelValue: "",
+ groupName: "groupName",
+ value: "myValue",
+ },
+ },
+ });
+
+ // Assert
+ const paragraph = wrapper.find("span.m-0");
+
+ expect(paragraph.attributes("class")).toContain("text-center");
+ });
+
+ it("Should return aria-required state", async () => {
+ // Act
+ const { wrapper } = setupMocks({
+ mockData: {
+ propsData: {
+ isRequired: true,
+ groupName: "groupName",
+ modelValue: "",
+ value: "myValue",
+ },
+ },
+ });
+
+ // Assert
+ const input = wrapper.find("input");
+
+ expect(input.attributes()["aria-required"]).toEqual("true");
+ });
});
-
- // Assert
- const label = wrapper.find("label");
-
- 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 paragraph = wrapper.find("span.sr-only");
-
- expect(paragraph.text()).toEqual("Screen Reader Only Text");
- });
-
- it("Should return text alignment class", async () => {
- // Act
- const wrapper = shallowMount(listButton, {
- propsData: {
- textPosition: "text-center",
- },
- });
-
- // Assert
- const paragraph = wrapper.find("span.m-0");
-
- expect(paragraph.attributes("class")).toContain("text-center");
- });
-
- it("Should return aria-required state", async () => {
- // Act
- const wrapper = shallowMount(listButton, {
- propsData: {
- isRequired: true,
- },
- });
-
- // Assert
- const input = wrapper.find("input");
-
- expect(input.attributes()["aria-required"]).toEqual("true");
- });
-
- it("Should return loader enabled true", async () => {
- // Act
- const wrapper = shallowMount(listButton, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- selectingInitiatesLoad: true,
- },
- });
-
- // Assert
- 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(listButton, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- loaderColor: "blue",
- selectingInitiatesLoad: true,
- },
- });
-
- // Assert
- 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(listButton, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- loaderPosition: "right",
- selectingInitiatesLoad: true,
- },
- });
-
- // Assert
- 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(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'
- },
- });
-
- 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 };
+}
diff --git a/src/ux-components/list-button/list-button.vue b/src/ux-components/list-button/list-button.vue
index c789060e..d6770b1a 100644
--- a/src/ux-components/list-button/list-button.vue
+++ b/src/ux-components/list-button/list-button.vue
@@ -1,241 +1,114 @@
-
-
-
-
-
-
-
+
+
-
\ No newline at end of file
+}
+.list-button-content {
+ color: $gray-600;
+ position: relative;
+ background: $white;
+ transition: all 150ms linear;
+ border-radius: $border-radius-lg;
+ border: 1px solid $gray-500;
+ width: 100%;
+ outline: none;
+
+ span {
+ &.small {
+ font-size: 0.75rem;
+ color: $gray-550;
+ }
+ }
+}
+
diff --git a/src/ux-components/list-card/list-card.spec.js b/src/ux-components/list-card/list-card.spec.js
index 9c9e1986..25d4a353 100644
--- a/src/ux-components/list-card/list-card.spec.js
+++ b/src/ux-components/list-card/list-card.spec.js
@@ -1,329 +1,179 @@
-import { shallowMount } from "@vue/test-utils";
+import { mount } from "@vue/test-utils";
import listCard from "./list-card";
import { nextTick } from "vue";
describe("list-card.vue", () => {
- it("Should return input type checkbox if isMultiSelect is true", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isMultiSelect: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "checkbox-demo-1",
- groupName: "Checkbox 1",
- buttonImage: "windshield-damage.svg",
- },
- });
+ it("Should return input type checkbox if isMultiSelect is true", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isMultiSelect: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "checkbox-demo-1",
+ groupName: "Checkbox 1",
+ buttonImage: "windshield-damage.svg",
+ value: "test value",
+ },
+ });
- // Assert
- const input = wrapper.find("input");
- expect(input.attributes().type).toEqual("checkbox");
+ // Assert
+ const input = wrapper.find("input");
+ expect(input.attributes().type).toEqual("checkbox");
});
- it("Should return primary label text", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- },
- });
+ it("Should return primary label text", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ value: "test value",
+ },
+ });
- // Assert
- const paragraph = wrapper.find("p");
- expect(paragraph.text()).toEqual("Windshield");
+ // Assert
+ const paragraph = wrapper.find("p");
+ expect(paragraph.text()).toEqual("Windshield");
});
- it("Should return secondary (sub) label text", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- buttonLabelSubCopy: "Test",
- },
- });
+ it("Should return secondary (sub) label text", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ buttonLabelSubCopy: "Test",
+ value: "test value",
+ },
+ });
- // Assert
- const paragraph = wrapper.find("p:nth-of-type(2)");
- expect(paragraph.text()).toEqual("Test");
+ // Assert
+ const paragraph = wrapper.find("p:nth-of-type(2)");
+ expect(paragraph.text()).toEqual("Test");
});
- it("Should return value used for various text settings including the label 'for' and input id", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- buttonLabelSubCopy: "Test",
- },
- });
+ it("Should return input group name used for radio or checkbox", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ buttonLabelSubCopy: "Test",
+ value: "test value",
+ },
+ });
- // Assert
- const label = wrapper.find("label");
- expect(label.attributes().for).toEqual("List Card Checkbox");
+ // Assert
+ const input = wrapper.find("input");
+ expect(input.attributes().name).toEqual("radio 1");
});
- it("Should return input group name used for radio or checkbox", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- buttonLabelSubCopy: "Test",
- },
- });
+ it("Should return aria-required state", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ isRequired: true,
+ value: "test value",
+ },
+ });
- // Assert
- const input = wrapper.find("input");
- expect(input.attributes().name).toEqual("radio 1");
+ // Assert
+ const input = wrapper.find("input");
+ expect(input.attributes()["aria-required"]).toEqual("true");
});
- it("Should return aria-required state", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- },
- });
+ it("Should return flex row classes if isWide is true", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ isRequired: true,
+ isWide: true,
+ buttonLabelSubCopy: "",
+ value: "test value",
+ },
+ });
- // Assert
- const input = wrapper.find("input");
- expect(input.attributes()["aria-required"]).toEqual("true");
+ // Assert
+ const label = wrapper.find(".list-card-content");
+ expect(label.exists()).toBe(true);
+ const labelClasses = wrapper.vm.labelClasses;
+ expect(labelClasses).toContain("flex-row");
+ expect(label.classes()).toContain("flex-row");
});
- it("Should return flex row classes if isWide is true", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- isWide: true,
- buttonLabelSubCopy: "",
- },
- });
+ it("Should return flex row classes if isWide is true and checkboxTop if buttonLabelSubCopy is provided", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ isRequired: true,
+ isWide: true,
+ buttonLabelSubCopy: "Button Subcopy",
+ value: "test value",
+ },
+ });
- // Assert
- const label = wrapper.find("label");
- expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-2", "ps-4", "pe-4"]);
+ // Assert
+ const label = wrapper.find(".list-card-content");
+ expect(label.exists()).toBe(true);
+ const labelClasses = wrapper.vm.labelClasses;
+ expect(labelClasses).toContain("flex-row");
+ expect(label.classes()).toContain("flex-row");
+ expect(labelClasses).toContain("checkboxTop");
+ expect(label.classes()).toContain("checkboxTop");
});
- it("Should return flex row classes if isWide is true and checkboxTop if buttonLabelSubCopy is true", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- isWide: true,
- buttonLabelSubCopy: "Button Subcopy",
- },
- });
+ it("Should return flex column classes if isWide is false", () => {
+ // Act
+ const wrapper = mount(listCard, {
+ propsData: {
+ isRadioHorizontal: true,
+ buttonLabel: "Windshield",
+ buttonID: "List Card Checkbox",
+ groupID: "radio-demo-1",
+ groupName: "radio 1",
+ buttonImage: "windshield-damage.svg",
+ isRequired: true,
+ isWide: false,
+ value: "test value",
+ },
+ });
- // Assert
- const label = wrapper.find("label");
- expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-row", "py-2", "ps-4", "pe-4", "checkboxTop"]);
+ // Assert
+ const label = wrapper.find(".list-card-content");
+ expect(label.exists()).toBe(true);
+ const labelClasses = wrapper.vm.labelClasses;
+ expect(labelClasses).toContain("flex-column");
+ expect(label.classes()).toContain("flex-column");
});
-
- it("Should return flex column classes if isWide is false", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- buttonID: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- isWide: false,
- },
- });
-
- // Assert
- const label = wrapper.find("label");
- expect(label.classes()).toEqual(["d-flex", "w-100", "align-items-center", "px-2", "h-100", "flex-column", "pt-4", "pb-3"]);
- });
-
- it("Should emit button value on click", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- value: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- isWide: false,
- buttonID: 'list-card-id',
- selectedValues: "List Card Checkbox"
- },
- });
- wrapper.vm.handleCheckChange();
- // Assert
- expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: true, buttonId: 'list-card-id'}]);
- });
-
- it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isRadioHorizontal: true,
- buttonLabel: "Windshield",
- value: "List Card Checkbox",
- groupID: "radio-demo-1",
- groupName: "radio 1",
- buttonImage: "windshield-damage.svg",
- isRequired: true,
- isWide: false,
- selectedValues: "Car-Front"
- },
- });
- // Assert
- expect(wrapper.componentVM.checkValue).toEqual(false);
- });
-
- it("Should set an initial value for validation if selectedValues include the value", async () => {
- // Arrange
- const wrapper = shallowMount(listCard, {
- propsData: {
- value: "Windshield",
- groupName: "radio 1",
- selectedValues: ["Windshield"],
- },
- });
-
- // Assert
- expect(wrapper.vm.fieldOptions.initialValue).toEqual([ 'Windshield' ]);
- });
-
- it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- selectingInitiatesLoad: false,
- },
- });
-
- // Assert
- wrapper.vm.handleInputChange();
-
- await nextTick();
-
- expect(wrapper.vm.handleCheckChange).toBeCalled;
- });
-
- it("Should do nothing if isMultiSelect is true and handleKeyupArrow is triggered", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- isMultiSelect: true,
- },
- });
-
- // Assert
- wrapper.vm.handleKeyupArrow();
-
- await nextTick();
-
- expect(wrapper.vm.handleKeyupArrow).toHaveReturned;
- });
-
- it("Should run handleCheckChange if selectingInitiatesLoad is false and handleKeyupArrow is triggered", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- propsData: {
- selectingInitiatesLoad: false,
- isMultiSelect: false,
- },
- });
-
- // Assert
- wrapper.vm.handleKeyupArrow();
-
- await nextTick();
-
- expect(wrapper.vm.handleCheckChange).toBeCalled;
- });
-
- it("Should run handleChange if triggerButton is triggered", async () => {
-
- // Act
- const wrapper = shallowMount(listCard, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- selectingInitiatesLoad: false,
- },
- });
-
- // Assert
- wrapper.vm.triggerButton();
-
- await nextTick();
-
- expect(wrapper.vm.handleChange).toBeCalled;
- expect(wrapper.vm.handleCheckChange).not.toBeCalled;
- expect(wrapper.vm.displayLoader).not.toBeCalled;
- });
-
- it("Should run handleCheckChange and displayLoader if triggerButton is triggered and seletingInitiatesLoad is true", async () => {
- // Act
- const wrapper = shallowMount(listCard, {
- global: {
- mocks: {
- '$route': { query: { issPage: 'page-name' } },
- }
- },
- propsData: {
- selectingInitiatesLoad: true,
- },
- });
-
- // Assert
- wrapper.vm.triggerButton();
-
- await nextTick();
-
- expect(wrapper.vm.handleCheckChange).toBeCalled;
- expect(wrapper.vm.displayLoader).toBeCalled;
- });
-
});
diff --git a/src/ux-components/list-card/list-card.vue b/src/ux-components/list-card/list-card.vue
index eb2897b2..ed70705c 100644
--- a/src/ux-components/list-card/list-card.vue
+++ b/src/ux-components/list-card/list-card.vue
@@ -1,399 +1,243 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
+
+ input[type="radio"] {
+ + .list-card-content::before {
+ content: "";
+ display: none;
+ }
+
+ + .list-card-content::after {
+ content: "";
+ display: none;
+ }
+
+ + .list-card-content {
+ img {
+ margin-bottom: 0;
+ }
+ }
+ }
+
+ &.horizontal {
+ img {
+ margin-bottom: 0;
+ width: 5.5rem;
+ }
+
+ input[type="checkbox"],
+ input[type="radio"] {
+ + .list-card-content::before {
+ content: "";
+ position: relative;
+ margin: 0 0.5rem 0 0;
+ order: 1;
+ }
+
+ &:checked + .list-card-content::after {
+ content: "";
+ margin: -0.15rem 0 0 0;
+ left: 1.175rem;
+ }
+
+ &:checked + .list-card-content {
+ p {
+ color: $black;
+ font-weight: 500;
+ }
+
+ .sub-copy {
+ color: $gray-600;
+ font-weight: 400;
+ }
+ }
+
+ + .list-card-content {
+ outline: none;
+ min-height: 48px;
+ color: $gray-600;
+
+ img {
+ margin-bottom: 0;
+ }
+
+ p {
+ color: $gray-600;
+ text-align: left;
+
+ &.sub-copy {
+ color: $gray-550;
+ }
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/ux-components/loader/loader.vue b/src/ux-components/loader/loader.vue
index 25d7e02d..0656b44c 100644
--- a/src/ux-components/loader/loader.vue
+++ b/src/ux-components/loader/loader.vue
@@ -1,34 +1,34 @@
-
-
-
-
-
-
-
\ No newline at end of file
+ &.red:after {
+ background-color: $red;
+ }
+ &.green:after {
+ background-color: $green;
+ }
+ &.white:after {
+ background-color: $white;
+ }
+ &.black:after {
+ background-color: $black;
+ }
+}
+