Skip to content

Commit 60ad48c

Browse files
feat(feedback): Add integrations to client to allow for usage tracking (#4580)
1 parent ce3b58a commit 60ad48c

File tree

6 files changed

+79
-15
lines changed

6 files changed

+79
-15
lines changed

packages/core/src/js/feedback/FeedbackWidget.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { sentryLogo } from './branding';
1818
import { defaultConfiguration } from './defaults';
1919
import defaultStyles from './FeedbackWidget.styles';
2020
import type { FeedbackGeneralConfiguration, FeedbackTextConfiguration, FeedbackWidgetProps, FeedbackWidgetState, FeedbackWidgetStyles, ImagePickerConfiguration } from './FeedbackWidget.types';
21+
import { lazyLoadFeedbackIntegration } from './lazy';
2122
import { base64ToUint8Array, feedbackAlertDialog, isValidEmail } from './utils';
2223

2324
/**
@@ -59,6 +60,8 @@ export class FeedbackWidget extends React.Component<FeedbackWidgetProps, Feedbac
5960
attachment: FeedbackWidget._savedState.attachment || undefined,
6061
attachmentUri: FeedbackWidget._savedState.attachmentUri || undefined,
6162
};
63+
64+
lazyLoadFeedbackIntegration();
6265
}
6366

6467
public handleFeedbackSubmit: () => void = () => {

packages/core/src/js/feedback/FeedbackWidgetManager.tsx

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { FeedbackWidget } from './FeedbackWidget';
88
import { modalSheetContainer, modalWrapper, topSpacer } from './FeedbackWidget.styles';
99
import type { FeedbackWidgetStyles } from './FeedbackWidget.types';
1010
import { getFeedbackOptions } from './integration';
11+
import { lazyLoadAutoInjectFeedbackIntegration } from './lazy';
1112
import { isModalSupported } from './utils';
1213

1314
const PULL_DOWN_CLOSE_THRESHOLD = 200;
@@ -224,6 +225,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
224225
}
225226

226227
const showFeedbackWidget = (): void => {
228+
lazyLoadAutoInjectFeedbackIntegration();
227229
FeedbackWidgetManager.show();
228230
};
229231

+14-9
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1-
import type { Integration } from '@sentry/core';
1+
import { type Integration, getClient } from '@sentry/core';
22

33
import type { FeedbackWidgetProps } from './FeedbackWidget.types';
44

5-
export const FEEDBACK_FORM_INTEGRATION_NAME = 'MobileFeedback';
5+
export const MOBILE_FEEDBACK_INTEGRATION_NAME = 'MobileFeedback';
66

77
type FeedbackIntegration = Integration & {
88
options: Partial<FeedbackWidgetProps>;
99
};
1010

11-
let savedOptions: Partial<FeedbackWidgetProps> = {};
12-
1311
export const feedbackIntegration = (initOptions: FeedbackWidgetProps = {}): FeedbackIntegration => {
14-
savedOptions = initOptions;
15-
1612
return {
17-
name: FEEDBACK_FORM_INTEGRATION_NAME,
18-
options: savedOptions,
13+
name: MOBILE_FEEDBACK_INTEGRATION_NAME,
14+
options: initOptions,
1915
};
2016
};
2117

22-
export const getFeedbackOptions = (): Partial<FeedbackWidgetProps> => savedOptions;
18+
export const getFeedbackOptions = (): Partial<FeedbackWidgetProps> => {
19+
const integration = getClient()?.getIntegrationByName<ReturnType<typeof feedbackIntegration>>(
20+
MOBILE_FEEDBACK_INTEGRATION_NAME,
21+
);
22+
if (!integration) {
23+
return {};
24+
}
25+
26+
return integration.options;
27+
};

packages/core/src/js/feedback/lazy.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { getClient } from '@sentry/core';
2+
3+
import { feedbackIntegration, MOBILE_FEEDBACK_INTEGRATION_NAME } from './integration';
4+
5+
/**
6+
* Lazy loads the feedback integration if it is not already loaded.
7+
*/
8+
export function lazyLoadFeedbackIntegration(): void {
9+
const integration = getClient()?.getIntegrationByName(MOBILE_FEEDBACK_INTEGRATION_NAME);
10+
if (!integration) {
11+
// Lazy load the integration to track usage
12+
getClient()?.addIntegration(feedbackIntegration());
13+
}
14+
}
15+
16+
export const AUTO_INJECT_FEEDBACK_INTEGRATION_NAME = 'AutoInjectMobileFeedback';
17+
18+
/**
19+
* Lazy loads the auto inject feedback integration if it is not already loaded.
20+
*/
21+
export function lazyLoadAutoInjectFeedbackIntegration(): void {
22+
const integration = getClient()?.getIntegrationByName(AUTO_INJECT_FEEDBACK_INTEGRATION_NAME);
23+
if (!integration) {
24+
// Lazy load the integration to track usage
25+
getClient()?.addIntegration({ name: AUTO_INJECT_FEEDBACK_INTEGRATION_NAME });
26+
}
27+
}

packages/core/test/feedback/FeedbackWidget.test.tsx

+14-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
import { captureFeedback } from '@sentry/core';
1+
import { captureFeedback, getClient, setCurrentClient } from '@sentry/core';
22
import { fireEvent, render, waitFor } from '@testing-library/react-native';
33
import * as React from 'react';
44
import { Alert } from 'react-native';
55

66
import { FeedbackWidget } from '../../src/js/feedback/FeedbackWidget';
77
import type { FeedbackWidgetProps, FeedbackWidgetStyles, ImagePicker } from '../../src/js/feedback/FeedbackWidget.types';
8+
import { MOBILE_FEEDBACK_INTEGRATION_NAME } from '../../src/js/feedback/integration';
9+
import { getDefaultTestClientOptions,TestClient } from '../mocks/client';
810

911
const mockOnFormClose = jest.fn();
1012
const mockOnAddScreenshot = jest.fn();
@@ -369,7 +371,7 @@ describe('FeedbackWidget', () => {
369371
expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('[email protected]');
370372
expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('This is a feedback message.');
371373
});
372-
374+
373375
it('onCancel the input is saved and restored when the form reopens', async () => {
374376
const { getByPlaceholderText, getByText, unmount } = render(<FeedbackWidget {...defaultProps} />);
375377

@@ -401,4 +403,14 @@ describe('FeedbackWidget', () => {
401403
expect(queryByPlaceholderText(defaultProps.emailPlaceholder).props.value).toBe('[email protected]');
402404
expect(queryByPlaceholderText(defaultProps.messagePlaceholder).props.value).toBe('');
403405
});
406+
407+
it('lazyLoadFeedbackIntegration is called when the FeedbackWidget is rendered', () => {
408+
const client = new TestClient(getDefaultTestClientOptions());
409+
setCurrentClient(client);
410+
client.init();
411+
412+
render(<FeedbackWidget {...defaultProps} />);
413+
414+
expect(getClient().getIntegrationByName(MOBILE_FEEDBACK_INTEGRATION_NAME)).toBeDefined();
415+
});
404416
});

packages/core/test/feedback/FeedbackWidgetManager.test.tsx

+19-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
import { logger } from '@sentry/core';
1+
import { getClient, logger, setCurrentClient } from '@sentry/core';
22
import { render } from '@testing-library/react-native';
33
import * as React from 'react';
44
import { Text } from 'react-native';
55

66
import { defaultConfiguration } from '../../src/js/feedback/defaults';
77
import { FeedbackWidgetProvider, resetFeedbackWidgetManager, showFeedbackWidget } from '../../src/js/feedback/FeedbackWidgetManager';
88
import { feedbackIntegration } from '../../src/js/feedback/integration';
9+
import { AUTO_INJECT_FEEDBACK_INTEGRATION_NAME } from '../../src/js/feedback/lazy';
910
import { isModalSupported } from '../../src/js/feedback/utils';
11+
import { getDefaultTestClientOptions, TestClient } from '../mocks/client';
1012

1113
jest.mock('../../src/js/feedback/utils', () => ({
1214
isModalSupported: jest.fn(),
@@ -23,6 +25,9 @@ beforeEach(() => {
2325
describe('FeedbackWidgetManager', () => {
2426

2527
beforeEach(() => {
28+
const client = new TestClient(getDefaultTestClientOptions());
29+
setCurrentClient(client);
30+
client.init();
2631
consoleWarnSpy.mockReset();
2732
resetFeedbackWidgetManager();
2833
});
@@ -72,10 +77,11 @@ describe('FeedbackWidgetManager', () => {
7277
</FeedbackWidgetProvider>
7378
);
7479

75-
feedbackIntegration({
80+
const integration = feedbackIntegration({
7681
messagePlaceholder: 'Custom Message Placeholder',
7782
submitButtonLabel: 'Custom Submit Button',
7883
});
84+
getClient()?.addIntegration(integration);
7985

8086
showFeedbackWidget();
8187

@@ -91,9 +97,10 @@ describe('FeedbackWidgetManager', () => {
9197
</FeedbackWidgetProvider>
9298
);
9399

94-
feedbackIntegration({
100+
const integration = feedbackIntegration({
95101
submitButtonLabel: 'Custom Submit Button',
96-
}),
102+
});
103+
getClient()?.addIntegration(integration);
97104

98105
showFeedbackWidget();
99106

@@ -123,4 +130,12 @@ describe('FeedbackWidgetManager', () => {
123130

124131
expect(consoleWarnSpy).not.toHaveBeenCalled();
125132
});
133+
134+
it('showFeedbackWidget adds the feedbackIntegration to the client', () => {
135+
mockedIsModalSupported.mockReturnValue(true);
136+
137+
showFeedbackWidget();
138+
139+
expect(getClient().getIntegrationByName(AUTO_INJECT_FEEDBACK_INTEGRATION_NAME)).toBeDefined();
140+
});
126141
});

0 commit comments

Comments
 (0)