From f25946fd4b699f339a7a644af22006daa42da45e Mon Sep 17 00:00:00 2001 From: Kai by Gleap <290069030+kai-by-gleap[bot]@users.noreply.github.com> Date: Fri, 9 Oct 2026 18:44:02 +0000 Subject: [PATCH] Kai Code: Only allow the expanded widget on viewports with room for it The expanded messenger (max-width 690px, no height cap) was allowed on any viewport of 451px or more. On a phone with the page zoomed out just past the mobile breakpoint (e.g. a 571px-wide CSS viewport), the widget left the full-screen mobile layout, and a stored "expanded" choice made it `calc(100% - 40px)` wide and nearly full height, covering almost the whole page. Expansion now needs a viewport of at least 768px (`widgetExpandMinWidth` in UI.js), used by both the expanded CSS media query and `GleapFrameManager.canExpandWidget()`, so they stay in sync. Narrower viewports show the default 410px widget, hide the expand button, and keep the stored preference for when the window gets wider again. Added tests for 571px, a fractional 767.5px, and the 768px boundary. Gleap ticket #185052. --- src/GleapFrameManager.js | 17 +++++++++-------- src/GleapFrameManager.test.js | 23 ++++++++++++++++++++++- src/UI.js | 11 ++++++++--- 3 files changed, 39 insertions(+), 12 deletions(-) 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; }