Skip to content

Commit 0e3d27e

Browse files
committed
Prevent page flicker after burn
1 parent 0e27c44 commit 0e3d27e

3 files changed

Lines changed: 57 additions & 9 deletions

File tree

src/app/page.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {
2828
ZENDESK_SEND_BUTTON_IDENTIFIER,
2929
ZENDESK_YES_BUTTON_IDENTIFIER,
3030
ZENDESK_NO_BUTTON_IDENTIFIER,
31+
ZENDESK_SCRIPT_TAG_ID,
32+
ZENDESK_HIDDEN_IFRAME_SELECTOR,
3133
} from '@/constants/zendesk-selectors';
3234
import { ZENDESK_IFRAME_STYLES } from '@/constants/zendesk-styles';
3335
import { getCSSVariable } from '@/utils/get-css-variable';
@@ -140,7 +142,43 @@ export default function Home() {
140142
behavior: prefersReducedMotion ? 'auto' : 'smooth',
141143
});
142144

143-
window.location.reload();
145+
// Clear contents of messaging-container div (Zendesk iframe, etc.)
146+
const messagingContainer = document.getElementById(EMBEDDED_TARGET_ELEMENT);
147+
if (messagingContainer) {
148+
messagingContainer.innerHTML = '';
149+
}
150+
151+
// Remove hidden Zendesk iframe (data-product="web_widget")
152+
const hiddenIframe = document.querySelector(ZENDESK_HIDDEN_IFRAME_SELECTOR);
153+
if (hiddenIframe) {
154+
hiddenIframe.remove();
155+
}
156+
157+
// Remove ze-snippet script element
158+
const zeSnippet = document.getElementById(ZENDESK_SCRIPT_TAG_ID);
159+
if (zeSnippet) {
160+
zeSnippet.remove();
161+
}
162+
163+
// Clean up Zendesk global objects
164+
if (typeof window !== 'undefined') {
165+
// Remove zE function if it exists
166+
try {
167+
delete (window as unknown as { zE?: unknown }).zE;
168+
} catch {
169+
// Ignore errors if zE is not configurable
170+
}
171+
172+
// Remove zEMessenger if it exists
173+
try {
174+
delete (window as unknown as { zEMessenger?: unknown }).zEMessenger;
175+
} catch {
176+
// Ignore errors if zEMessenger is not configurable
177+
}
178+
}
179+
180+
// Stop burn animation
181+
setIsBurning(false);
144182
}, []);
145183

146184
const handleOnError = useCallback((e: Error) => {
@@ -236,7 +274,7 @@ export default function Home() {
236274
</main>
237275
{loadWidget && (
238276
<Script
239-
id="ze-snippet"
277+
id={ZENDESK_SCRIPT_TAG_ID}
240278
src={ZENDESK_SCRIPT_URL}
241279
onLoad={handleOnLoad}
242280
onError={handleOnError}

src/constants/zendesk-selectors.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,14 @@ export const ZENDESK_BUTTON_DATA_ATTR = 'buttons.button';
4949
* Data attribute value for icon button elements
5050
*/
5151
export const ZENDESK_ICON_BUTTON_DATA_ATTR = 'buttons.icon_button';
52+
53+
/**
54+
* Zendesk script tag
55+
*/
56+
export const ZENDESK_SCRIPT_TAG_ID = 'ze-snippet';
57+
58+
/**
59+
* Selector for the hidden iframe. Used when resetting the chat
60+
*/
61+
export const ZENDESK_HIDDEN_IFRAME_SELECTOR =
62+
'iframe[data-product="web_widget"][aria-hidden="true"]';

src/tests/integration/complete-flow.test.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -462,8 +462,8 @@ test.describe('Complete Zendesk Widget Flow', () => {
462462
await expect(confirmButton).toBeVisible();
463463
await confirmButton.click();
464464

465-
// Wait for page reload (burn animation completes)
466-
// The reload happens after ZENDESK_RESET_DELAY_MS (1000ms) + burn animation
465+
// Wait for page reset (burn animation completes)
466+
// The reset happens after ZENDESK_RESET_DELAY_MS (1000ms) + burn animation
467467
await page.waitForLoadState('networkidle');
468468

469469
// Verify we're back at the chat screen (consent is persisted, so "New Chat"
@@ -474,8 +474,8 @@ test.describe('Complete Zendesk Widget Flow', () => {
474474
await expect(page.getByTestId(LOAD_ZD_BUTTON_TEST_ID)).toBeVisible();
475475

476476
// Verify storage is cleared (except consent which persists)
477-
// @note: Storage clearing happens in resetWidget callback before reload.
478-
// After reload, localStorage should only contain ddg_consent. All other
477+
// @note: Storage clearing happens in resetWidget callback before reset.
478+
// After reset, localStorage should only contain ddg_consent. All other
479479
// keys should be removed. We verify that our test keys are gone and only
480480
// consent remains, which confirms the clear() was called.
481481
const storageState = await page.evaluate(() => {
@@ -533,7 +533,7 @@ test.describe('Complete Zendesk Widget Flow', () => {
533533
// Verify only ddg_consent remains
534534
expect(storageStateRetry.localStorage.count).toBe(1);
535535
} else {
536-
// Storage was cleared before reload - verify it's still cleared
536+
// Storage was cleared before reset - verify it's still cleared
537537
expect(storageState.localStorage.hasTestKey).toBe(false);
538538
expect(storageState.sessionStorage.hasTestKey).toBe(false);
539539
// SessionStorage should be completely empty
@@ -546,8 +546,7 @@ test.describe('Complete Zendesk Widget Flow', () => {
546546
expect(storageState.localStorage.count).toBe(1);
547547
}
548548

549-
// After reload and state reset, loadWidget should be false, so no widget
550-
// should render
549+
// After state reset, loadWidget should be false, so no widget should render
551550
const widgetRendered = await page.evaluate(() => {
552551
const container = document.querySelector('#messaging-container');
553552

0 commit comments

Comments
 (0)