diff --git a/src/GleapFrameManager.js b/src/GleapFrameManager.js index c2275543..cbf6b6a9 100644 --- a/src/GleapFrameManager.js +++ b/src/GleapFrameManager.js @@ -22,7 +22,7 @@ import GleapAgentToolManager from './GleapAgentToolManager'; import { getCaptureCapabilities } from './GleapCaptureSettings'; import { forwardSurveyAnalyticsEvent } from './GleapSurveyAnalyticsForwarder'; import { bootstrapGleapFrame, loadFromGleapCache, runFunctionWhenDomIsReady, saveToGleapCache } from './GleapHelper'; -import { widgetLoaderMarkup, widgetMaxHeight } from './UI'; +import { widgetExpandMinWidth, widgetLoaderMarkup, widgetMaxHeight } from './UI'; export default class GleapFrameManager { frameUrl = 'https://messenger-app.gleap.io'; @@ -429,14 +429,15 @@ export default class GleapFrameManager { } } - // The widget is full screen at <= 450px and the expanded CSS only applies from - // 451px (see UI.js). Test the same query as that CSS so a fractional (zoomed) - // viewport between the two never counts as expandable. - isMobileViewport() { + // The expanded CSS only applies from widgetExpandMinWidth (see UI.js); narrower + // viewports (mobile full screen, or a zoomed-out phone just past it) keep the + // default size. Test the same query as that CSS so a fractional (zoomed) + // viewport never counts as expandable while the CSS does not apply. + hasRoomToExpand() { try { - return typeof window.matchMedia === 'function' && !window.matchMedia('(min-width: 451px)').matches; + return typeof window.matchMedia !== 'function' || window.matchMedia(`(min-width: ${widgetExpandMinWidth}px)`).matches; } catch (e) { - return false; + return true; } } @@ -445,7 +446,7 @@ export default class GleapFrameManager { if (flowConfig && flowConfig.hideExpandButton) { return false; } - return !this.isSurvey() && !this.isMobileViewport(); + return !this.isSurvey() && this.hasRoomToExpand(); } getWidgetExpandedPreference() { diff --git a/src/GleapFrameManager.test.js b/src/GleapFrameManager.test.js index c012c8e1..dc332727 100644 --- a/src/GleapFrameManager.test.js +++ b/src/GleapFrameManager.test.js @@ -45,6 +45,7 @@ jest.mock('./GleapHelper', () => ({ jest.mock('./UI', () => ({ widgetLoaderMarkup: jest.fn(() => ''), widgetMaxHeight: 700, + widgetExpandMinWidth: 768, })); const EXPANDED_CLASS = 'gleap-frame-container--expanded'; @@ -178,6 +179,26 @@ describe('GleapFrameManager expand/collapse window', () => { expect(fm.gleapFrameContainer.classList.contains(EXPANDED_CLASS)).toBe(false); }); + it.each([571, 767.5])('is not expandable on a %spx viewport too narrow for the expanded widget', (width) => { + // E.g. a phone with the page zoomed out: past the mobile breakpoint, so the + // floating widget shows, but an expanded one would cover nearly the whole page. + loadFromGleapCache.mockImplementation((key) => (key === 'widget-expanded' ? true : null)); + viewportWidth = width; + const fm = setup(); + fm.updateFrameStyle(); + + expect(fm.canExpandWidget()).toBe(false); + expect(fm.isWidgetExpanded()).toBe(false); + expect(fm.gleapFrameContainer.classList.contains(EXPANDED_CLASS)).toBe(false); + }); + + it('is expandable from the expand breakpoint on', () => { + viewportWidth = 768; + const fm = setup(); + + expect(fm.canExpandWidget()).toBe(true); + }); + it('collapses and hides the button when a config update turns the setting off', () => { const fm = setup(); fm.setWidgetExpanded(true); @@ -224,7 +245,7 @@ describe('GleapFrameManager expand/collapse window', () => { expect(sentSizeUpdates().pop().data).toEqual({ expandable: true, expanded: false }); }); - it('applies a stored expanded choice when the window widens past the mobile breakpoint', () => { + it('applies a stored expanded choice when the window widens past the expand breakpoint', () => { loadFromGleapCache.mockImplementation((key) => (key === 'widget-expanded' ? true : null)); isMobile = true; const fm = setup(); diff --git a/src/UI.js b/src/UI.js index ea01b9b2..74df2b1e 100644 --- a/src/UI.js +++ b/src/UI.js @@ -29,6 +29,11 @@ export const calculateContrast = (hex) => { export const widgetMaxHeight = 700; +// Narrowest viewport that leaves room around the 690px expanded widget. Below it the +// expanded widget would cover nearly the whole page (e.g. a phone with the page zoomed +// out just past the 450px mobile breakpoint), so the widget keeps its default size. +export const widgetExpandMinWidth = 768; + // Builds the loading placeholder shown while the messenger iframe boots. // Mirrors the Messenger-App home backgrounds (BG.svg / BGclassic.svg / // BGclassicnofade.svg) so the hand-off from loader to app is seamless. @@ -806,9 +811,9 @@ export const injectStyledCSS = ( } /* Expanded by the end user (expand/collapse button in the messenger header). - Below 451px the widget is full screen anyway. The inner max-height must beat - the inline 700px set by GleapFrameManager.setAppMode. */ - @media only screen and (min-width: 451px) { + Only on viewports with room for it (see widgetExpandMinWidth). The inner + max-height must beat the inline 700px set by GleapFrameManager.setAppMode. */ + @media only screen and (min-width: ${widgetExpandMinWidth}px) { .gleap-frame-container--expanded { max-width: 690px !important; }