Skip to content

Commit 3d011c6

Browse files
committed
feat(feedback): Add onClose callback to showReportDialog (#9433)
1 parent 27686ba commit 3d011c6

File tree

5 files changed

+86
-0
lines changed

5 files changed

+86
-0
lines changed

packages/browser/src/helpers.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,6 @@ export interface ReportDialogOptions {
180180
successMessage?: string;
181181
/** Callback after reportDialog showed up */
182182
onLoad?(this: void): void;
183+
/** Callback after reportDialog closed */
184+
onClose?(this: void): void;
183185
}

packages/browser/src/sdk.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,20 @@ export function showReportDialog(options: ReportDialogOptions = {}, hub: Hub = g
165165
script.onload = options.onLoad;
166166
}
167167

168+
const { onClose } = options;
169+
if (onClose) {
170+
const reportDialogClosedMessageHandler = (event: MessageEvent): void => {
171+
if (event.data === 'reportdialog_closed') {
172+
try {
173+
onClose();
174+
} finally {
175+
WINDOW.removeEventListener('message', reportDialogClosedMessageHandler);
176+
}
177+
}
178+
};
179+
WINDOW.addEventListener('message', reportDialogClosedMessageHandler);
180+
}
181+
168182
const injectionPoint = WINDOW.document.head || WINDOW.document.body;
169183
if (injectionPoint) {
170184
injectionPoint.appendChild(script);

packages/browser/test/unit/index.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
Integrations,
1717
Scope,
1818
showReportDialog,
19+
WINDOW,
1920
wrap,
2021
} from '../../src';
2122
import { getDefaultBrowserClientOptions } from './helper/browser-client-options';
@@ -123,6 +124,65 @@ describe('SentryBrowser', () => {
123124
);
124125
});
125126
});
127+
128+
describe('onClose', () => {
129+
const dummyErrorHandler = jest.fn();
130+
beforeEach(() => {
131+
// this prevents jest-environment-jsdom from failing the test
132+
// when an error in `onClose` is thrown
133+
// it does not prevent errors thrown directly inside the test,
134+
// so we don't have to worry about tests passing that should
135+
// otherwise fail
136+
// see: https://github.com/jestjs/jest/blob/main/packages/jest-environment-jsdom/src/index.ts#L95-L115
137+
WINDOW.addEventListener('error', dummyErrorHandler);
138+
});
139+
140+
afterEach(() => {
141+
WINDOW.removeEventListener('error', dummyErrorHandler);
142+
});
143+
144+
const waitForPostMessage = async (message: string) => {
145+
WINDOW.postMessage(message, '*');
146+
await flush(10);
147+
};
148+
149+
it('should call `onClose` when receiving `reportdialog_closed` MessageEvent', async () => {
150+
const onClose = jest.fn();
151+
showReportDialog({ onClose });
152+
153+
await waitForPostMessage('reportdialog_closed');
154+
expect(onClose).toHaveBeenCalledTimes(1);
155+
156+
// ensure the event handler has been removed so onClose is not called again
157+
await waitForPostMessage('reportdialog_closed');
158+
expect(onClose).toHaveBeenCalledTimes(1);
159+
});
160+
161+
it('should call `onClose` only once even if it throws', async () => {
162+
const onClose = jest.fn(() => {
163+
throw new Error();
164+
});
165+
showReportDialog({ onClose });
166+
167+
await waitForPostMessage('reportdialog_closed');
168+
expect(onClose).toHaveBeenCalledTimes(1);
169+
170+
// ensure the event handler has been removed so onClose is not called again
171+
await waitForPostMessage('reportdialog_closed');
172+
expect(onClose).toHaveBeenCalledTimes(1);
173+
});
174+
175+
it('should not call `onClose` for other MessageEvents', async () => {
176+
const onClose = jest.fn();
177+
showReportDialog({ onClose });
178+
179+
await waitForPostMessage('some_message');
180+
expect(onClose).not.toHaveBeenCalled();
181+
182+
await waitForPostMessage('reportdialog_closed');
183+
expect(onClose).toHaveBeenCalledTimes(1);
184+
});
185+
});
126186
});
127187

128188
describe('breadcrumbs', () => {

packages/core/src/api.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ export function getReportDialogEndpoint(
7070
continue;
7171
}
7272

73+
if (key === 'onClose') {
74+
continue;
75+
}
76+
7377
if (key === 'user') {
7478
const user = dialogOptions.user;
7579
if (!user) {

packages/core/test/lib/api.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ describe('API', () => {
119119
{ user: undefined },
120120
'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://[email protected]:1234/subpath/123',
121121
],
122+
[
123+
'with Public DSN and onClose callback',
124+
dsnPublic,
125+
{ onClose: () => {} },
126+
'https://sentry.io:1234/subpath/api/embed/error-page/?dsn=https://[email protected]:1234/subpath/123',
127+
],
122128
])(
123129
'%s',
124130
(

0 commit comments

Comments
 (0)