diff --git a/src/constants/experiments.js b/src/constants/experiments.js index 4fab998af..8df02597c 100644 --- a/src/constants/experiments.js +++ b/src/constants/experiments.js @@ -58,6 +58,8 @@ const experimentSettings = { TIER_ONE: "TierOne", TIER_TWO: "TierTwo", TIER_THREE: "TierThree", + TIER_TWO_DESCRIPTION: "TierTwo_Description", + TIER_THREE_DESCRIPTION: "TierThree_Description", // Consent Management SHOW_CONSENT_MANAGEMENT: "ShowConsentManagement", diff --git a/src/experiment-components/service-package-radio-for-afterpay.vue b/src/experiment-components/service-package-radio-for-afterpay.vue index 7f7e91084..1e896b3f7 100644 --- a/src/experiment-components/service-package-radio-for-afterpay.vue +++ b/src/experiment-components/service-package-radio-for-afterpay.vue @@ -4,6 +4,7 @@ class="package-label" :class="[ this.buttonLabelSubCopy ? 'has-subheader' : '', + this.additionalButtonData?.packageDescription ? 'has-description' : '', this.additionalButtonData.isInsuranceSelected ? 'is-insurance' : '', this.hasPackageDiscount() ? 'has-package-discount' : '', ]" @@ -13,6 +14,10 @@

+

.package-specs { display: inline-flex; } } //Package container height with package discount AND "most popular" label - &.has-package-discount.has-subheader { + &.has-package-discount.has-subheader, + &.has-package-discount.has-description { min-height: 170px; } + &.has-package-discount.has-subheader.has-description { + min-height: 190px; + } //Package container height with package discount only &.has-package-discount { min-height: 136px; @@ -307,30 +318,28 @@ export default { &:checked { + .package-label { + max-height: 1000px; + border: 1px solid $blue; + .package-specs { - max-height: 1000px; - } - } - + .package-label { - .package-specs { + max-height: none; + .hide-when-closed { - display: block; + max-height: none; + overflow: visible; + opacity: 1; + transition: opacity 0.2s ease 0.15s; } } - } - + .package-label { - border: 1px solid $blue; - max-height: 500px; + .special-save-box { background-color: $green-100; } - } - + .package-label { + &:before { box-shadow: 0px 0px 0px 1px $blue; } - } - + .package-label { + &:after { background: $blue; } @@ -356,8 +365,9 @@ export default { flex-direction: column; width: 100%; transition: all 0.5s ease; + @include media-breakpoint-up(md) { - max-height: 500px; + max-height: none; } .row { @@ -378,6 +388,13 @@ export default { font-size: 0.75rem; margin-left: auto; } + &.package-description { + color: $gray-550; + font-size: 0.875rem; + font-weight: 400; + font-style: italic; + white-space: normal; + } } span { &.service-package-discount { @@ -441,10 +458,17 @@ export default { } .hide-when-closed { - display: none; + display: flex; + flex-direction: column; + max-height: 0; + overflow: hidden; + opacity: 0; + transition: opacity 0.15s ease; + @include media-breakpoint-up(md) { - display: flex; - flex-direction: column; + max-height: none; + opacity: 1; + overflow: visible; } } } diff --git a/src/experiment-components/service-package-radio.vue b/src/experiment-components/service-package-radio.vue index a116fecd8..763035ff0 100644 --- a/src/experiment-components/service-package-radio.vue +++ b/src/experiment-components/service-package-radio.vue @@ -4,6 +4,7 @@ class="package-label pricing-by-day-pkg-lbl" :class="[ this.buttonLabelSubCopy ? 'has-subheader' : '', + this.additionalButtonData?.packageDescription ? 'has-description' : '', !this.additionalButtonData.hasDiscount ? 'adjust-top' : '', ]" for="testradio"> @@ -13,6 +14,10 @@

+

+

.package-specs { + display: inline-flex; + } } &:before { @@ -288,12 +302,6 @@ export default { height: 10px; min-width: 10px; } - - &.has-subheader { - & > .package-specs { - display: inline-flex; - } - } } &:hover { @@ -309,30 +317,28 @@ export default { &:checked { + .package-label { + max-height: 1000px; + border: 1px solid $blue; + .package-specs { - max-height: 1000px; - } - } - + .package-label { - .package-specs { + max-height: none; + .hide-when-closed { - display: block; + max-height: none; + overflow: visible; + opacity: 1; + transition: opacity 0.2s ease 0.15s; } } - } - + .package-label { - border: 1px solid $blue; - max-height: 500px; + .special-save-box { background-color: $green-200; } - } - + .package-label { + &:before { box-shadow: 0px 0px 0px 1px $blue; } - } - + .package-label { + &:after { background: $blue; } @@ -358,8 +364,9 @@ export default { flex-direction: column; width: 100%; transition: all 0.5s ease; + @include media-breakpoint-up(md) { - max-height: 500px; + max-height: none; } span.pricing-by-day { @@ -398,6 +405,13 @@ export default { text-transform: uppercase; font-size: 0.75rem; } + &.package-description { + color: $gray-550; + font-size: 0.875rem; + font-weight: 400; + font-style: italic; + white-space: normal; + } } span { &.service-package-discount-pricing-by-day { @@ -458,10 +472,17 @@ export default { } .hide-when-closed { - display: none; + display: flex; + flex-direction: column; + max-height: 0; + overflow: hidden; + opacity: 0; + transition: opacity 0.15s ease; + @include media-breakpoint-up(md) { - display: flex; - flex-direction: column; + max-height: none; + opacity: 1; + overflow: visible; } } } diff --git a/src/helpers/service-package-helper.js b/src/helpers/service-package-helper.js index f43680dd2..b790385fb 100644 --- a/src/helpers/service-package-helper.js +++ b/src/helpers/service-package-helper.js @@ -16,6 +16,11 @@ const tierExperimentSettingKeys = { [packageNames.TIER_THREE]: experimentSettings.TIER_THREE, }; +const tierDescriptionExperimentSettingKeys = { + [packageNames.TIER_TWO]: experimentSettings.TIER_TWO_DESCRIPTION, + [packageNames.TIER_THREE]: experimentSettings.TIER_THREE_DESCRIPTION, +}; + export function containsLineItemWithPartType(typeToFind, itemsToSearch) { const partTypeMatches = findLineItemsWithPartType(typeToFind, itemsToSearch); return !!partTypeMatches?.length; @@ -284,16 +289,24 @@ export function isServicePackageNameTestActive(experimentSettingsMap) { } export function getExperimentPackageDisplayName(tierName, experimentSettingsMap) { + return getExperimentPackageSetting(tierName, tierExperimentSettingKeys, experimentSettingsMap); +} + +export function getExperimentPackageDescription(tierName, experimentSettingsMap) { + return getExperimentPackageSetting( + tierName, + tierDescriptionExperimentSettingKeys, + experimentSettingsMap + ); +} + +function getExperimentPackageSetting(tierName, settingKeys, experimentSettingsMap) { if (!isServicePackageNameTestActive(experimentSettingsMap)) { return null; } - const settingKey = tierExperimentSettingKeys[tierName]; - if (!settingKey) { - return null; - } - - return experimentHelper.getSettingValue(settingKey, experimentSettingsMap); + const settingKey = settingKeys[tierName]; + return settingKey ? experimentHelper.getSettingValue(settingKey, experimentSettingsMap) : null; } function maxTier(tierA, tierB) { diff --git a/src/helpers/service-package-helper.spec.js b/src/helpers/service-package-helper.spec.js index 495436c8d..2e4f12e76 100644 --- a/src/helpers/service-package-helper.spec.js +++ b/src/helpers/service-package-helper.spec.js @@ -969,34 +969,29 @@ describe("service-package-helper.js", () => { describe("getExperimentPackageDisplayName", () => { const { experimentSettings } = require("@/constants/experiments"); + const activeExperimentSettingsMap = { + [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "true", + [experimentSettings.TIER_ONE]: "Essential", + [experimentSettings.TIER_TWO]: "Plus", + [experimentSettings.TIER_THREE]: "Full Service", + [experimentSettings.TIER_TWO_DESCRIPTION]: "Most popular choice", + [experimentSettings.TIER_THREE_DESCRIPTION]: "Best value", + }; it("Returns null when the experiment is not active", () => { - const result = servicePackageHelper.getExperimentPackageDisplayName( - packageNames.TIER_ONE, - {} - ); - - expect(result).toBeNull(); + expect( + servicePackageHelper.getExperimentPackageDisplayName(packageNames.TIER_ONE, {}) + ).toBeNull(); + expect( + servicePackageHelper.getExperimentPackageDescription(packageNames.TIER_TWO, {}) + ).toBeNull(); }); it("Returns null when the test flag is not true", () => { - const result = servicePackageHelper.getExperimentPackageDisplayName( - packageNames.TIER_ONE, - { - [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "false", - [experimentSettings.TIER_ONE]: "Essential", - } - ); - - expect(result).toBeNull(); - }); - - it("Returns the experiment display name for each tier when active", () => { const experimentSettingsMap = { - [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "true", + [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "false", [experimentSettings.TIER_ONE]: "Essential", - [experimentSettings.TIER_TWO]: "Plus", - [experimentSettings.TIER_THREE]: "Full Service", + [experimentSettings.TIER_TWO_DESCRIPTION]: "Most popular choice", }; expect( @@ -1004,28 +999,70 @@ describe("service-package-helper.js", () => { packageNames.TIER_ONE, experimentSettingsMap ) + ).toBeNull(); + expect( + servicePackageHelper.getExperimentPackageDescription( + packageNames.TIER_TWO, + experimentSettingsMap + ) + ).toBeNull(); + }); + + it("Returns the experiment display name for each tier when active", () => { + expect( + servicePackageHelper.getExperimentPackageDisplayName( + packageNames.TIER_ONE, + activeExperimentSettingsMap + ) ).toBe("Essential"); expect( servicePackageHelper.getExperimentPackageDisplayName( packageNames.TIER_TWO, - experimentSettingsMap + activeExperimentSettingsMap ) ).toBe("Plus"); expect( servicePackageHelper.getExperimentPackageDisplayName( packageNames.TIER_THREE, - experimentSettingsMap + activeExperimentSettingsMap ) ).toBe("Full Service"); }); - it("Returns null for an unknown tier", () => { - const result = servicePackageHelper.getExperimentPackageDisplayName("UnknownTier", { - [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "true", - [experimentSettings.TIER_ONE]: "Essential", - }); + it("Returns the experiment description for tier two and three when active", () => { + expect( + servicePackageHelper.getExperimentPackageDescription( + packageNames.TIER_TWO, + activeExperimentSettingsMap + ) + ).toBe("Most popular choice"); + expect( + servicePackageHelper.getExperimentPackageDescription( + packageNames.TIER_THREE, + activeExperimentSettingsMap + ) + ).toBe("Best value"); + }); - expect(result).toBeNull(); + it("Returns null for unknown tiers and tiers without configured settings", () => { + expect( + servicePackageHelper.getExperimentPackageDisplayName("UnknownTier", { + [experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST]: "true", + [experimentSettings.TIER_ONE]: "Essential", + }) + ).toBeNull(); + expect( + servicePackageHelper.getExperimentPackageDescription( + packageNames.TIER_ONE, + activeExperimentSettingsMap + ) + ).toBeNull(); + expect( + servicePackageHelper.getExperimentPackageDescription( + "UnknownTier", + activeExperimentSettingsMap + ) + ).toBeNull(); }); }); }); diff --git a/src/layouts/quote/service-package-question/service-package-question.spec.js b/src/layouts/quote/service-package-question/service-package-question.spec.js index 1d0f78c1a..46b084d02 100644 --- a/src/layouts/quote/service-package-question/service-package-question.spec.js +++ b/src/layouts/quote/service-package-question/service-package-question.spec.js @@ -856,6 +856,52 @@ describe("service-package-question.vue, matching business rules for package disp getExperimentPackageLabelSpy.mockRestore(); }); + it("should use experiment package descriptions when ServicePackageNameTest is active", () => { + const getExperimentPackageDescriptionSpy = jest + .spyOn(servicePackageQuestion.methods, "getExperimentPackageDescription") + .mockImplementation((tierName) => { + const experimentPackageDescriptions = { + [packageNames.TIER_TWO]: "Most popular choice", + [packageNames.TIER_THREE]: "Best value", + }; + + return experimentPackageDescriptions[tierName] ?? null; + }); + + const packageNameKey = "05_01_CSR_Quote_Standard_Repair"; + Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]); + mockProps.availableLineItems.push(driverFrontWiperLineItem); + mockProps.availableLineItems.push(passengerFrontWiperLineItem); + const wrapper = setupMocks({ + mountOptionsMockData: { + store: { + getters: { + order: { + damage: { + isRepair: true, + glassToReplace: [], + }, + }, + hasAnyNonWindshieldGlassParts: false, + }, + }, + }, + }); + + expect( + wrapper.vm.servicePackageAnswers[0].additionalButtonData.packageDescription + ).toBeNull(); + expect(wrapper.vm.servicePackageAnswers[1].additionalButtonData.packageDescription).toBe( + "Most popular choice" + ); + expect(wrapper.vm.servicePackageAnswers[2].additionalButtonData.packageDescription).toBe( + "Best value" + ); + expect(wrapper.vm.servicePackageAnswers[1].buttonLabelSubCopy).toBe("MOST POPULAR"); + expect(wrapper.vm.servicePackageAnswers[2].buttonLabelSubCopy).toBe(""); + + getExperimentPackageDescriptionSpy.mockRestore(); + }); it("should use CMS package names when ServicePackageNameTest is not active", () => { const packageNameKey = "05_01_CSR_Quote_Standard_Repair"; Object.assign(processedCmsContent, mockProcessedCmsContent[packageNameKey]); diff --git a/src/layouts/quote/service-package-question/service-package-question.vue b/src/layouts/quote/service-package-question/service-package-question.vue index 4a54ee3f5..905898481 100644 --- a/src/layouts/quote/service-package-question/service-package-question.vue +++ b/src/layouts/quote/service-package-question/service-package-question.vue @@ -159,6 +159,7 @@ export default { ? false : "Special: Save $" + this.getDiscountPrice(answer.Name), isInsuranceSelected: this.isInsuranceSelected, + packageDescription: this.getExperimentPackageDescription(answer.Name), }, })); @@ -265,6 +266,19 @@ export default { ); }, getExperimentPackageLabel(tierName) { + return this.getExperimentPackageSetting(tierName, { + [packageNames.TIER_ONE]: experimentSettings.TIER_ONE, + [packageNames.TIER_TWO]: experimentSettings.TIER_TWO, + [packageNames.TIER_THREE]: experimentSettings.TIER_THREE, + }); + }, + getExperimentPackageDescription(tierName) { + return this.getExperimentPackageSetting(tierName, { + [packageNames.TIER_TWO]: experimentSettings.TIER_TWO_DESCRIPTION, + [packageNames.TIER_THREE]: experimentSettings.TIER_THREE_DESCRIPTION, + }); + }, + getExperimentPackageSetting(tierName, tierSettingKeys) { if ( !experimentMixin.methods.hasSettingEqualTo( experimentSettings.SHOW_SERVICE_PACKAGE_NAME_TEST, @@ -274,13 +288,8 @@ export default { return null; } - const tierSettingKey = { - [packageNames.TIER_ONE]: experimentSettings.TIER_ONE, - [packageNames.TIER_TWO]: experimentSettings.TIER_TWO, - [packageNames.TIER_THREE]: experimentSettings.TIER_THREE, - }[tierName]; - - return tierSettingKey ? experimentMixin.methods.getSettingValue(tierSettingKey) : null; + const settingKey = tierSettingKeys[tierName]; + return settingKey ? experimentMixin.methods.getSettingValue(settingKey) : null; }, getHeaderTextFromCms(cmsWidgetName) { return this.getCmsContent(cmsWidgetName, "HeaderText"); diff --git a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue index 959c7345e..bae95af8f 100644 --- a/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue +++ b/src/layouts/quote/service-package-question/service-package-radio/service-package-radio.vue @@ -2,7 +2,10 @@

@@ -10,6 +13,10 @@

+

+