Skip to content

Commit 1bb4e34

Browse files
antoniskrystofwoldrichlucas-zimerman
authored
feat(feedback): Report a Bug button (#4378)
* Update the client implementation to use the new capture feedback js api * Updates SDK API * Adds new feedback button in the sample * Adds changelog * Removes unused mock * Update CHANGELOG.md Co-authored-by: Krystof Woldrich <[email protected]> * Directly use captureFeedback from sentry/core * Use import from core * Fixes imports order lint issue * Fixes build issue * Adds captureFeedback tests from sentry-javascript * Update CHANGELOG.md * Only deprecate client captureUserFeedback * Add simple form UI * Adds basic form functionality * Update imports * Update imports * Remove useState hook to avoid multiple react instances issues * Move types and styles in different files * Removes attachment button to be added back separately along with the implementation * Add basic field validation * Adds changelog * Updates changelog * Updates changelog * Trim whitespaces from the submitted feedback * Adds tests * Renames FeedbackFormScreen to FeedbackForm * Add beta label * Extract default text to constants * Moves constant to a separate file and aligns naming with JS * Adds input text labels * Close screen before sending the feedback to minimise wait time Co-authored-by: LucasZF <[email protected]> * Rename file for consistency * Flatten configuration hierarchy and clean up * Align required values with JS * Use Sentry user email and name when set * Simplifies email validation * Show success alert message * Aligns naming with JS and unmounts the form by default * Use the minimum config without props in the changelog * Adds development not for unimplemented function * Show email and name conditionally * Adds sentry branding (png logo) * Adds sentry logo resource * Add assets in module exports * Revert "Add assets in module exports" This reverts commit 5292475. * Revert "Adds sentry logo resource" This reverts commit d6e9229. * Revert "Adds sentry branding (png logo)" This reverts commit 8c56753. * Add last event id * Mock lastEventId * Adds beta note in the changelog * Autoinject feedback form * Updates changelog * Align colors with JS * Update CHANGELOG.md Co-authored-by: Krystof Woldrich <[email protected]> * Update CHANGELOG.md Co-authored-by: Krystof Woldrich <[email protected]> * Update CHANGELOG.md Co-authored-by: Krystof Woldrich <[email protected]> * Use regular fonts for both buttons * Handle keyboard properly * Adds an option on whether the email should be validated * Merge properties only once * Loads current user data on form construction * Remove unneeded extra padding * Fix background color issue * Adds feedback button * Updates the changelog * Fixes changelog typo * Updates styles background color Co-authored-by: Krystof Woldrich <[email protected]> * Use defaultProps * Correct defaultProps * Adds test to verify when getUser is called * Use smaller image Co-authored-by: LucasZF <[email protected]> * Add margin next to the icon * Adds bottom spacing in the ErrorScreen so that the feedback button does not hide the scrollview buttons * (2.2) feat: Add Feedback Form UI Branding logo (#4357) * Adds sentry branding logo as a base64 encoded png --------- Co-authored-by: LucasZF <[email protected]> * Autoinject feedback form (#4370) * Align changelog entry * Update changelog * Disable bouncing * Add modal ui appearance * Update snapshot tests * Fix bottom margin * Fix sheet height * Remove extra modal border * Do not expose modal styles * Animate background color * Avoid keyboard in modal * Update changelog * Fix changelog * Updates comment * Extract FeedbackButtonProps * Add public function description to satisfy lint check * Adds tests * Fix tests * Include in the feedback integration * Fix circular dependency * Remove unneeded line Co-authored-by: Krystof Woldrich <[email protected]> * Place widget button below the feedback widget shadow * Expose showFeedbackButton/hideFeedbackButton methods * Add dummy integration for tracking usage * Adds button border * Fixes tests * Rename FeedbackButtonProps in tests for clarity * Add missing function call in test Co-authored-by: Krystof Woldrich <[email protected]> * Adds missing semicolon in test * Adds feedback button in expo app --------- Co-authored-by: Krystof Woldrich <[email protected]> Co-authored-by: LucasZF <[email protected]>
1 parent f32b22a commit 1bb4e34

File tree

17 files changed

+617
-19
lines changed

17 files changed

+617
-19
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
> make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first.
77
<!-- prettier-ignore-end -->
88
9+
## Unreleased
10+
11+
### Features
12+
13+
- Adds the `FeedbackButton` component that shows the Feedback Widget ([#4378](https://github.com/getsentry/sentry-react-native/pull/4378))
14+
915
## 6.11.0-beta.0
1016

1117
### Features
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import * as React from 'react';
2+
import { Image, Text, TouchableOpacity } from 'react-native';
3+
4+
import { defaultButtonConfiguration } from './defaults';
5+
import { defaultButtonStyles } from './FeedbackWidget.styles';
6+
import type { FeedbackButtonProps, FeedbackButtonStyles, FeedbackButtonTextConfiguration } from './FeedbackWidget.types';
7+
import { feedbackIcon } from './icons';
8+
import { lazyLoadFeedbackIntegration } from './lazy';
9+
10+
const showFeedbackWidget = (): void => {
11+
// eslint-disable-next-line @typescript-eslint/no-var-requires
12+
const { showFeedbackWidget } = require('./FeedbackWidgetManager');
13+
showFeedbackWidget();
14+
};
15+
16+
/**
17+
* @beta
18+
* Implements a feedback button that opens the FeedbackForm.
19+
*/
20+
export class FeedbackButton extends React.Component<FeedbackButtonProps> {
21+
public constructor(props: FeedbackButtonProps) {
22+
super(props);
23+
lazyLoadFeedbackIntegration();
24+
}
25+
26+
/**
27+
* Renders the feedback button.
28+
*/
29+
public render(): React.ReactNode {
30+
const text: FeedbackButtonTextConfiguration = { ...defaultButtonConfiguration, ...this.props };
31+
const styles: FeedbackButtonStyles = {
32+
triggerButton: { ...defaultButtonStyles.triggerButton, ...this.props.styles?.triggerButton },
33+
triggerText: { ...defaultButtonStyles.triggerText, ...this.props.styles?.triggerText },
34+
triggerIcon: { ...defaultButtonStyles.triggerIcon, ...this.props.styles?.triggerIcon },
35+
};
36+
37+
return (
38+
<TouchableOpacity
39+
style={styles.triggerButton}
40+
onPress={showFeedbackWidget}
41+
accessibilityLabel={text.triggerAriaLabel}
42+
>
43+
<Image source={{ uri: feedbackIcon }} style={styles.triggerIcon}/>
44+
<Text style={styles.triggerText}>{text.triggerLabel}</Text>
45+
</TouchableOpacity>
46+
);
47+
}
48+
}

packages/core/src/js/feedback/FeedbackWidget.styles.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { ViewStyle } from 'react-native';
22

3-
import type { FeedbackWidgetStyles } from './FeedbackWidget.types';
3+
import type { FeedbackButtonStyles, FeedbackWidgetStyles } from './FeedbackWidget.types';
44

55
const PURPLE = 'rgba(88, 74, 192, 1)';
66
const FOREGROUND_COLOR = '#2b2233';
@@ -99,6 +99,37 @@ const defaultStyles: FeedbackWidgetStyles = {
9999
},
100100
};
101101

102+
export const defaultButtonStyles: FeedbackButtonStyles = {
103+
triggerButton: {
104+
position: 'absolute',
105+
bottom: 30,
106+
right: 30,
107+
backgroundColor: BACKGROUND_COLOR,
108+
padding: 15,
109+
borderRadius: 40,
110+
justifyContent: 'center',
111+
alignItems: 'center',
112+
elevation: 5,
113+
shadowColor: BORDER_COLOR,
114+
shadowOffset: { width: 1, height: 2 },
115+
shadowOpacity: 0.5,
116+
shadowRadius: 3,
117+
flexDirection: 'row',
118+
borderWidth: 1,
119+
borderColor: BORDER_COLOR,
120+
},
121+
triggerText: {
122+
color: FOREGROUND_COLOR,
123+
fontSize: 18,
124+
},
125+
triggerIcon: {
126+
width: 24,
127+
height: 24,
128+
padding: 2,
129+
marginEnd: 6,
130+
},
131+
};
132+
102133
export const modalWrapper: ViewStyle = {
103134
position: 'absolute',
104135
top: 0,

packages/core/src/js/feedback/FeedbackWidget.types.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,21 @@ export interface FeedbackTextConfiguration {
154154
genericError?: string;
155155
}
156156

157+
/**
158+
* The FeedbackButton text labels that can be customized
159+
*/
160+
export interface FeedbackButtonTextConfiguration {
161+
/**
162+
* The label for the Feedback widget button that opens the dialog
163+
*/
164+
triggerLabel?: string;
165+
166+
/**
167+
* The aria label for the Feedback widget button that opens the dialog
168+
*/
169+
triggerAriaLabel?: string;
170+
}
171+
157172
/**
158173
* The public callbacks available for the feedback integration
159174
*/
@@ -247,6 +262,22 @@ export interface FeedbackWidgetStyles {
247262
sentryLogo?: ImageStyle;
248263
}
249264

265+
/**
266+
* The props for the feedback button
267+
*/
268+
export interface FeedbackButtonProps extends FeedbackButtonTextConfiguration {
269+
styles?: FeedbackButtonStyles;
270+
}
271+
272+
/**
273+
* The styles for the feedback button
274+
*/
275+
export interface FeedbackButtonStyles {
276+
triggerButton?: ViewStyle;
277+
triggerText?: TextStyle;
278+
triggerIcon?: ImageStyle;
279+
}
280+
250281
/**
251282
* The state of the feedback form
252283
*/

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

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,25 @@ import type { NativeScrollEvent, NativeSyntheticEvent} from 'react-native';
44
import { Animated, Dimensions, Easing, Modal, PanResponder, Platform, ScrollView, View } from 'react-native';
55

66
import { notWeb } from '../utils/environment';
7+
import { FeedbackButton } from './FeedbackButton';
78
import { FeedbackWidget } from './FeedbackWidget';
89
import { modalSheetContainer, modalWrapper, topSpacer } from './FeedbackWidget.styles';
910
import type { FeedbackWidgetStyles } from './FeedbackWidget.types';
10-
import { getFeedbackOptions } from './integration';
11-
import { lazyLoadAutoInjectFeedbackIntegration } from './lazy';
11+
import { getFeedbackButtonOptions, getFeedbackOptions } from './integration';
12+
import { lazyLoadAutoInjectFeedbackButtonIntegration,lazyLoadAutoInjectFeedbackIntegration } from './lazy';
1213
import { isModalSupported } from './utils';
1314

1415
const PULL_DOWN_CLOSE_THRESHOLD = 200;
1516
const SLIDE_ANIMATION_DURATION = 200;
1617
const BACKGROUND_ANIMATION_DURATION = 200;
1718

18-
class FeedbackWidgetManager {
19-
private static _isVisible = false;
20-
private static _setVisibility: (visible: boolean) => void;
19+
abstract class FeedbackManager {
20+
protected static _isVisible = false;
21+
protected static _setVisibility: (visible: boolean) => void;
22+
23+
protected static get _feedbackComponentName(): string {
24+
throw new Error('Subclasses must override feedbackComponentName');
25+
}
2126

2227
public static initialize(setVisibility: (visible: boolean) => void): void {
2328
this._setVisibility = setVisibility;
@@ -38,7 +43,7 @@ class FeedbackWidgetManager {
3843
} else {
3944
// This message should be always shown otherwise it's not possible to use the widget.
4045
// eslint-disable-next-line no-console
41-
console.warn('[Sentry] FeedbackWidget requires `Sentry.wrap(RootComponent)` to be called before `showFeedbackWidget()`.');
46+
console.warn(`[Sentry] ${this._feedbackComponentName} requires 'Sentry.wrap(RootComponent)' to be called before 'show${this._feedbackComponentName}()'.`);
4247
}
4348
}
4449

@@ -49,7 +54,7 @@ class FeedbackWidgetManager {
4954
} else {
5055
// This message should be always shown otherwise it's not possible to use the widget.
5156
// eslint-disable-next-line no-console
52-
console.warn('[Sentry] FeedbackWidget requires `Sentry.wrap(RootComponent)` before interacting with the widget.');
57+
console.warn(`[Sentry] ${this._feedbackComponentName} requires 'Sentry.wrap(RootComponent)' before interacting with the widget.`);
5358
}
5459
}
5560

@@ -58,12 +63,25 @@ class FeedbackWidgetManager {
5863
}
5964
}
6065

66+
class FeedbackWidgetManager extends FeedbackManager {
67+
protected static get _feedbackComponentName(): string {
68+
return 'FeedbackWidget';
69+
}
70+
}
71+
72+
class FeedbackButtonManager extends FeedbackManager {
73+
protected static get _feedbackComponentName(): string {
74+
return 'FeedbackButton';
75+
}
76+
}
77+
6178
interface FeedbackWidgetProviderProps {
6279
children: React.ReactNode;
6380
styles?: FeedbackWidgetStyles;
6481
}
6582

6683
interface FeedbackWidgetProviderState {
84+
isButtonVisible: boolean;
6785
isVisible: boolean;
6886
backgroundOpacity: Animated.Value;
6987
panY: Animated.Value;
@@ -72,6 +90,7 @@ interface FeedbackWidgetProviderState {
7290

7391
class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps> {
7492
public state: FeedbackWidgetProviderState = {
93+
isButtonVisible: false,
7594
isVisible: false,
7695
backgroundOpacity: new Animated.Value(0),
7796
panY: new Animated.Value(Dimensions.get('screen').height),
@@ -112,6 +131,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
112131

113132
public constructor(props: FeedbackWidgetProviderProps) {
114133
super(props);
134+
FeedbackButtonManager.initialize(this._setButtonVisibilityFunction);
115135
FeedbackWidgetManager.initialize(this._setVisibilityFunction);
116136
}
117137

@@ -150,7 +170,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
150170
return <>{this.props.children}</>;
151171
}
152172

153-
const { isVisible, backgroundOpacity } = this.state;
173+
const { isButtonVisible, isVisible, backgroundOpacity } = this.state;
154174

155175
const backgroundColor = backgroundOpacity.interpolate({
156176
inputRange: [0, 1],
@@ -162,6 +182,7 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
162182
return (
163183
<>
164184
{this.props.children}
185+
{isButtonVisible && <FeedbackButton {...getFeedbackButtonOptions()}/>}
165186
{isVisible &&
166187
<Animated.View style={[modalWrapper, { backgroundColor }]} >
167188
<Modal visible={isVisible} transparent animationType="none" onRequestClose={this._handleClose} testID="feedback-form-modal">
@@ -219,6 +240,10 @@ class FeedbackWidgetProvider extends React.Component<FeedbackWidgetProviderProps
219240
}
220241
};
221242

243+
private _setButtonVisibilityFunction = (visible: boolean): void => {
244+
this.setState({ isButtonVisible: visible });
245+
};
246+
222247
private _handleClose = (): void => {
223248
FeedbackWidgetManager.hide();
224249
};
@@ -233,4 +258,17 @@ const resetFeedbackWidgetManager = (): void => {
233258
FeedbackWidgetManager.reset();
234259
};
235260

236-
export { showFeedbackWidget, FeedbackWidgetProvider, resetFeedbackWidgetManager };
261+
const showFeedbackButton = (): void => {
262+
lazyLoadAutoInjectFeedbackButtonIntegration();
263+
FeedbackButtonManager.show();
264+
};
265+
266+
const hideFeedbackButton = (): void => {
267+
FeedbackButtonManager.hide();
268+
};
269+
270+
const resetFeedbackButtonManager = (): void => {
271+
FeedbackButtonManager.reset();
272+
};
273+
274+
export { showFeedbackButton, hideFeedbackButton, showFeedbackWidget, FeedbackWidgetProvider, resetFeedbackButtonManager, resetFeedbackWidgetManager };

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { FeedbackWidgetProps } from './FeedbackWidget.types';
1+
import type { FeedbackButtonProps, FeedbackWidgetProps } from './FeedbackWidget.types';
22
import { feedbackAlertDialog } from './utils';
33

44
const FORM_TITLE = 'Report a Bug';
@@ -11,6 +11,7 @@ const MESSAGE_LABEL = 'Description';
1111
const IS_REQUIRED_LABEL = '(required)';
1212
const SUBMIT_BUTTON_LABEL = 'Send Bug Report';
1313
const CANCEL_BUTTON_LABEL = 'Cancel';
14+
const TRIGGER_LABEL = 'Report a Bug';
1415
const ERROR_TITLE = 'Error';
1516
const FORM_ERROR = 'Please fill out all required fields.';
1617
const EMAIL_ERROR = 'Please enter a valid email address.';
@@ -80,3 +81,8 @@ export const defaultConfiguration: Partial<FeedbackWidgetProps> = {
8081
removeScreenshotButtonLabel: REMOVE_SCREENSHOT_LABEL,
8182
genericError: GENERIC_ERROR_TEXT,
8283
};
84+
85+
export const defaultButtonConfiguration: Partial<FeedbackButtonProps> = {
86+
triggerLabel: TRIGGER_LABEL,
87+
triggerAriaLabel: '',
88+
};
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const feedbackIcon =
2+
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAMAAABrrFhUAAAAk1BMVEUAAAAqIDEqIjMmIDArIjIrIjMoHjErITMrIjMqITIqITIqITIqIjIqITIqITIhHi4qITIqITIeAConHi8rITMqITIqITIcHCopITIpIDEpIDIlGC8qITIqITIpIDIpHjAqITIqIDIqIDEqIDAqITInHjAqIDIqIDEqITIqITIqIDIiFCgpIDEqITIoHS8qIDIrIjMN1S0HAAAAMHRSTlMAhnknzMQy1zyF6/H55PYQ0rIGGPudjAmUTEQUv7p2K6lmWDZxHV9S36J+DD7bI208pRBPAAAGrUlEQVR42uzd6XLaMBSG4Y/YhuAVbAwx+xp2eu7/6tq0nfaHIAizSNbR+5vJJM8EG45lGfrm7EZRSNL5bvcAg1oM6fZaBQxp/0HlylOY0PuRyjbco/L1unRHbVS9RYPuaoxKlzY7dF9xhgpXb9Hd1VDddiHdX4SqNnHpIU1QzcYhPaY5qpjj0aNaooL9iOlhvaFynYZEnAFmAXEGWI2IOAN8BsQZIGsTcQaY+0SMAQZT+hVfgLVPX3EFSLb0J6YARUR/YwmQ5PQvjgD9DcmWJ7hUraoANwy9owIwDuCGofc2gXEANwy9/TVgHMANQ+/pAMYBpLUOSebPAeMAbhh6tzOYByA/9A4+AeMAbhh6j1YwD0B+6B3MAOMAbhh6D09QAdCvtT33ecUkWfwDeD1Ab+yTHnknKABYHEmPwjGgAKAISY/cCVQAvGvy94c7QAVAEpEWtepQA/BBOtRppngYwDaDfAMt3gCNBVAeQKzxUciC/iAN6vZwJ4BYsC0gU5uUd3wHHgAgFu0yXE39IfCjhwcBiHWmE1yJFLfpA+UBJJpOtAbI93gwgFieaQsQFcDzAShYagqwTfAUADF3oiGAvwZeBUDxUjuA6QBPBBAbDbQC8OfAawFoU9cIoJ3h5QAUz3UBCD4BBQBESz0ARisoAqCmBgDBDFAGQB/KAaYrqASgrlKAVtcB1ALQThJgXX94px4A5QD0KQfgQP/KAYQH5gDkZ8wByOMOQE3DAfJZHtF3dd7NBngDsKh9Z9BIjQf41dyjizU5AACHFl0odFgAADOfztdmAoBsROfrMwEAxh06l8cGAEVM5+qzAcDCpzMN+QBgEtCZ6nwAcAhJbMsIAHMSixNGAPggsRkngF6DhDxOAKiHJJRxAjj3JvjBCmAgngvbrACwE88DKSuARPxIfGAFgFx8JS+AungQ4AUAYUAUMQMQ79pNeAH0hZe+8wJIY+FKKS8AuMLFcmYAubBihBnAUliwwgxgLQwGmQEIpwGXGcBCWLXEDGAiXCZmBlDn/h9w4H4MKIS5KDOAmfB9mBmA8HUwZwYgLBaoMQOIhItDvAAccZEAL4Cl8NIVLwDhEBDwmgmKeyV4vADG4v0jvACO4m0hrAAKEspYAYjrZl1WV4c/SajGCWAfkdCCE0BOQhtOa4TWJNZlBLDySczhA5A0SMzjs1I0HdKZ5mwAem06U8RmtXji0bmWXACcBp3L7zEBuPQcpyWPW2ayKZ1vk3IASMcBXWjN4La5/exIlxqZf+fopBvQxeKT2QD7fvdI3zU2++7xgK40NHz/gGsFK+YABfMtNJrMd5GZQgpgE4RR47F50/FEPYDXU7qXmFsoBmglKAGg316KZQEaGcoBaLWZanmAVqZ8O72vtokiAC/RYEPFr6K+EoB2T48tNb/66L0eoKnRrrJEjcWLAYK1VvsKE3V2LwVwHd12lhZ+pzIA8toa7i1OFC9fBODW9dxdnmi4egGAP8M3dahMemwyLQUQ1BJ8l/KnrLUHzwQIatd+vEeq84unARyXCa7VJPXl+2cAxNM+JJqQBm0Ojwbwp+se5BqRDnXTBwK0xnXIV++QDrXqT7k2WJmnjRGFb88CqMgD94i8kyIAjEmP4pkiALy3SI9GmRoAoL/dkA75czUAXyUnx3HpfHPnWjmdz/nTW0iSTRMFAFc/GL+X/mVuf/J+1DcSAGn3hoGhiQDAYSM/MDQSAPv8hqfwmggArH35JwQaCYBBW35gaCQA8BnIDwyNBMBqKD8wNBIAWMbyA0MjATBx5QeGRgIgbXbkB4YmAgCLI91flQHQ+z+H4QkA9CP6E1cAJFP6HVsAYO7TrxgD/HkMEGcAYBYzB8DJYw4AvIXMAVBvMQdA2mUOABw2zAHEgWH5xpUEAAqfHtO8ogDCwLBsk6oCCAPDckWoLoAwMCxTt8oAwsDw9uJVtQHguPeeBCsOIAwMb2uEygMAiwaVbZiYAFB+YJinMAIAWAzp9loFYAoA4OxGUUjS+W73AJgEUCILYAEsgAWwABbAAlgAC2ABLIAFsAAWwAJYAAtgASyABbAAFsACWAALYAEsgAWwABbAAlgAC2ABLIAFsAAWwAKUANg2r+WZDSCRBbAAFsAC/GyfXlIYhIIgitYgGHhoiMmDSPyAgqKz2v/qxIkL0JoU9NlB3+6OABEgAkSACBABnAP0FMvwslGsgpeBYjW8FBQbYeZNqbKFmYlSDex0FEp2BwB8V8rkPwwViSJ5gaX5QYlkuf/DZ0i8rWwM///0m7bni5flqh5dxt8BPG/wrQSMUX8AAAAASUVORK5CYII=';

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

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,23 @@
11
import { type Integration, getClient } from '@sentry/core';
22

3-
import type { FeedbackWidgetProps } from './FeedbackWidget.types';
3+
import type { FeedbackButtonProps, FeedbackWidgetProps } from './FeedbackWidget.types';
44

55
export const MOBILE_FEEDBACK_INTEGRATION_NAME = 'MobileFeedback';
66

77
type FeedbackIntegration = Integration & {
88
options: Partial<FeedbackWidgetProps>;
9+
buttonOptions: Partial<FeedbackButtonProps>;
910
};
1011

11-
export const feedbackIntegration = (initOptions: FeedbackWidgetProps = {}): FeedbackIntegration => {
12+
export const feedbackIntegration = (
13+
initOptions: FeedbackWidgetProps & { buttonOptions?: FeedbackButtonProps } = {},
14+
): FeedbackIntegration => {
15+
const { buttonOptions, ...widgetOptions } = initOptions;
16+
1217
return {
1318
name: MOBILE_FEEDBACK_INTEGRATION_NAME,
14-
options: initOptions,
19+
options: widgetOptions,
20+
buttonOptions: buttonOptions || {},
1521
};
1622
};
1723

@@ -25,3 +31,14 @@ export const getFeedbackOptions = (): Partial<FeedbackWidgetProps> => {
2531

2632
return integration.options;
2733
};
34+
35+
export const getFeedbackButtonOptions = (): Partial<FeedbackButtonProps> => {
36+
const integration = getClient()?.getIntegrationByName<ReturnType<typeof feedbackIntegration>>(
37+
MOBILE_FEEDBACK_INTEGRATION_NAME,
38+
);
39+
if (!integration) {
40+
return {};
41+
}
42+
43+
return integration.buttonOptions;
44+
};

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,16 @@ export function lazyLoadAutoInjectFeedbackIntegration(): void {
2525
getClient()?.addIntegration({ name: AUTO_INJECT_FEEDBACK_INTEGRATION_NAME });
2626
}
2727
}
28+
29+
export const AUTO_INJECT_FEEDBACK_BUTTON_INTEGRATION_NAME = 'AutoInjectMobileFeedbackButton';
30+
31+
/**
32+
* Lazy loads the auto inject feedback button integration if it is not already loaded.
33+
*/
34+
export function lazyLoadAutoInjectFeedbackButtonIntegration(): void {
35+
const integration = getClient()?.getIntegrationByName(AUTO_INJECT_FEEDBACK_BUTTON_INTEGRATION_NAME);
36+
if (!integration) {
37+
// Lazy load the integration to track usage
38+
getClient()?.addIntegration({ name: AUTO_INJECT_FEEDBACK_BUTTON_INTEGRATION_NAME });
39+
}
40+
}

packages/core/src/js/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ export type { TimeToDisplayProps } from './tracing';
8787

8888
export { Mask, Unmask } from './replay/CustomMask';
8989

90+
export { FeedbackButton } from './feedback/FeedbackButton';
9091
export { FeedbackWidget } from './feedback/FeedbackWidget';
91-
export { showFeedbackWidget } from './feedback/FeedbackWidgetManager';
92+
export { showFeedbackWidget, showFeedbackButton, hideFeedbackButton } from './feedback/FeedbackWidgetManager';
9293

9394
export { getDataFromUri } from './wrapper';

0 commit comments

Comments
 (0)