diff --git a/src/constants/dynamic-strings.js b/src/constants/dynamic-strings.js index 1c3b8ade7..c526e45a9 100644 --- a/src/constants/dynamic-strings.js +++ b/src/constants/dynamic-strings.js @@ -1,7 +1,7 @@ const dynamicStrings = { GLOBAL_STATE: "globalState", CUSTOM: "custom", - ROUTER_LINK: "routerLink" + ROUTER_LINK: "routerLink:" }; export { dynamicStrings }; \ No newline at end of file diff --git a/src/helpers/cms-content-helper.js b/src/helpers/cms-content-helper.js index 642f0d014..73912835a 100644 --- a/src/helpers/cms-content-helper.js +++ b/src/helpers/cms-content-helper.js @@ -120,4 +120,37 @@ function processWidgetItemForReplacement(widgetModel, key) { // If we have something else like a number, boolean, etc. just return it return widgetModel[key]; -} \ No newline at end of file +} + + +export function doesCopyContainRouterLink(copy) { + return copy.includes(this.dynamicStrings.ROUTER_LINK); +} + +export function splitCopyOnCMSPlaceHolder(copy){ + // splits copy on { ... } such as {routerlink: ...} + return copy.split(/{(.*?)}/g); +} + +export function getRouterLinkRouteFromCopy(copy){ + // sample input: {routerLink:estimate,provide your VIN} + // first split would return 'estimate,provide your VIN' + // second split would return 'estimate' + return copy.split(':')[1].split(',')[0]; +} + +export function getRouterLinkDisplayTextFromCopy(copy){ + // sample input: {routerLink:estimate,provide your VIN} + // first split would return 'estimate,provide your VIN' + // second split would return 'provide your VIN' + return copy.split(':')[1].split(',')[1]; +} + +// Copy returned from the CMS that has newlines will return blocks wrapped in +//
...
+// This function returns an array of each paragraph, works with or without html +// attributes present +export function splitCMSCopyOnParagraphTag(copy) { + // filter removes empty strings that are a result of string.split with regex + return copy.split(/(?:)|(?:<\/p>)/g).filter(paragraph => paragraph !== ""); +} diff --git a/src/layouts/address-vehicles/address-vehicles.spec.js b/src/layouts/address-vehicles/address-vehicles.spec.js index 2fdd10276..90eeaecbe 100644 --- a/src/layouts/address-vehicles/address-vehicles.spec.js +++ b/src/layouts/address-vehicles/address-vehicles.spec.js @@ -331,6 +331,11 @@ function setupMocks({ // lookupVin: jest.fn(() => Promise.resolve(lookupVinResponse)), }, + computed: { + dynamicStrings() { + return {ROUTER_LINK: "routerLink:"} + } + } } // mountOptions.propsData = { // modelValue: modelValueProp, diff --git a/src/layouts/address-vehicles/address-vehicles.vue b/src/layouts/address-vehicles/address-vehicles.vue index dc2e53484..faf71e1fb 100644 --- a/src/layouts/address-vehicles/address-vehicles.vue +++ b/src/layouts/address-vehicles/address-vehicles.vue @@ -28,8 +28,8 @@ />
tags if the body copy has 'n'
tags", () =>{ + // Arrange & Act + const wrapper = shallowMount(alert, setupMocks({ + propsData: { + manualHeadline: 'testHeader', + manualCopy: '
testCopy with a {routerLink: testName, testLink} inside of it
and two paragraphs
' + }, + stubs: ['router-link'], + })); + // Assert + expect(wrapper.findAll('p').length === 3).toBe(true); + }); + + it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (out of view top)", () => { + // Arrange + var viewPortHeight = 200; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(()=> { + return {top: -100, bottom: 200} + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).toHaveBeenCalled(); + }); + + it("Should call scrollIntoView() when the clientBoundingRect is not entirely in the viewport (bottom is hidden behind footer)", () => { + // Arrange + var viewPortHeight = 240; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(()=> { + return {top: 100, bottom: 200} + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).toHaveBeenCalled(); + }); + + it("Should not call scrollIntoView() when the clientBoundingRect is not entirely in the viewport but 'shouldScrollToOnMount' is false", () => { + // Arrange + var viewPortHeight = 200; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(()=> { + return {top: -100, bottom: 200} + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({ + propsData: { + shouldScrollToOnMount: false, + manualHeadline: 'testHeader', + manualCopy: 'testCopy' + }, + })); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).not.toHaveBeenCalled(); + + }); + + it("Should not call scrollIntoView() when the clientBoundingRect is entirely in the viewport", () => { + // Arrange + var viewPortHeight = 500; + setUpViewPort(viewPortHeight); + + Element.prototype.getBoundingClientRect = jest.fn(()=> { + return {top: 100, bottom: 200} + }); + + var mockScrollIntoView = jest.fn(); + Element.prototype.scrollIntoView = mockScrollIntoView; + + // Act + const wrapper = shallowMount(alert, setupMocks({})); + + // Assert + // This is an implementation detail - we just need to test that the final step of snapping + // the window to the alert is working. If using a different function to accomplish that + // just swap this out with the new function + expect(mockScrollIntoView).not.toHaveBeenCalled(); + + }); + }); const mockMixin = { methods: { getCmsContent: jest.fn(), - getFooterInfoBoxHeight: jest.fn(()=> 80), + getFooterInfoBoxHeight: jest.fn(()=> 50), + }, + computed: { + dynamicStrings: jest.fn(()=> { + return {ROUTER_LINK: 'routerLink:'} + }) } +} + +function setUpViewPort(height) { + Object.defineProperty(global.window, 'innerHeight', { + writable: true, + configurable: true, + value: height, + }); + + Object.defineProperty(window.document.documentElement, 'clientHeight', { + writable: true, + configurable: true, + value: height + }); +} + +function setupMocks(mountOptionsMockData = {}) { + const defaultMountOptions = { + propsData: { + manualHeadline: 'testHeader', + manualCopy: 'testCopy' + }, + mixins: [mockMixin] + }; + const baseMountOptions = getMountOptions(Object.assign(defaultMountOptions, mountOptionsMockData)); + const allMountOptions = Object.assign(defaultMountOptions, baseMountOptions); + return allMountOptions; } \ No newline at end of file diff --git a/src/ux-components/alert/alert.vue b/src/ux-components/alert/alert.vue index 5c0d7a270..d356d0569 100644 --- a/src/ux-components/alert/alert.vue +++ b/src/ux-components/alert/alert.vue @@ -8,7 +8,7 @@
-
+