diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ca8a03af6..620d4fb35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ ### Features - Adds the `FeedbackButton` component that shows the Feedback Widget ([#4378](https://github.com/getsentry/sentry-react-native/pull/4378)) +- Add Feedback Widget theming ([#4677](https://github.com/getsentry/sentry-react-native/pull/4677)) ### Fixes diff --git a/packages/core/src/js/feedback/FeedbackButton.tsx b/packages/core/src/js/feedback/FeedbackButton.tsx index 04a51cc0ca..d01a8b4bc1 100644 --- a/packages/core/src/js/feedback/FeedbackButton.tsx +++ b/packages/core/src/js/feedback/FeedbackButton.tsx @@ -1,8 +1,10 @@ import * as React from 'react'; -import { Image, Text, TouchableOpacity } from 'react-native'; +import type { NativeEventSubscription} from 'react-native'; +import { Appearance, Image, Text, TouchableOpacity } from 'react-native'; import { defaultButtonConfiguration } from './defaults'; import { defaultButtonStyles } from './FeedbackWidget.styles'; +import { getTheme } from './FeedbackWidget.theme'; import type { FeedbackButtonProps, FeedbackButtonStyles, FeedbackButtonTextConfiguration } from './FeedbackWidget.types'; import { feedbackIcon } from './icons'; import { lazyLoadFeedbackIntegration } from './lazy'; @@ -18,20 +20,41 @@ const showFeedbackWidget = (): void => { * Implements a feedback button that opens the FeedbackForm. */ export class FeedbackButton extends React.Component { + private _themeListener: NativeEventSubscription; + public constructor(props: FeedbackButtonProps) { super(props); lazyLoadFeedbackIntegration(); } + /** + * Adds a listener for theme changes. + */ + public componentDidMount(): void { + this._themeListener = Appearance.addChangeListener(() => { + this.forceUpdate(); + }); + } + + /** + * Removes the theme listener. + */ + public componentWillUnmount(): void { + if (this._themeListener) { + this._themeListener.remove(); + } + } + /** * Renders the feedback button. */ public render(): React.ReactNode { + const theme = getTheme(); const text: FeedbackButtonTextConfiguration = { ...defaultButtonConfiguration, ...this.props }; const styles: FeedbackButtonStyles = { - triggerButton: { ...defaultButtonStyles.triggerButton, ...this.props.styles?.triggerButton }, - triggerText: { ...defaultButtonStyles.triggerText, ...this.props.styles?.triggerText }, - triggerIcon: { ...defaultButtonStyles.triggerIcon, ...this.props.styles?.triggerIcon }, + triggerButton: { ...defaultButtonStyles(theme).triggerButton, ...this.props.styles?.triggerButton }, + triggerText: { ...defaultButtonStyles(theme).triggerText, ...this.props.styles?.triggerText }, + triggerIcon: { ...defaultButtonStyles(theme).triggerIcon, ...this.props.styles?.triggerIcon }, }; return ( diff --git a/packages/core/src/js/feedback/FeedbackWidget.styles.ts b/packages/core/src/js/feedback/FeedbackWidget.styles.ts index 1bd58be9f4..5feeff4957 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.styles.ts +++ b/packages/core/src/js/feedback/FeedbackWidget.styles.ts @@ -1,133 +1,135 @@ import type { ViewStyle } from 'react-native'; +import type { FeedbackWidgetTheme } from './FeedbackWidget.theme'; import type { FeedbackButtonStyles, FeedbackWidgetStyles } from './FeedbackWidget.types'; -const PURPLE = 'rgba(88, 74, 192, 1)'; -const FOREGROUND_COLOR = '#2b2233'; -const BACKGROUND_COLOR = '#ffffff'; -const BORDER_COLOR = 'rgba(41, 35, 47, 0.13)'; - -const defaultStyles: FeedbackWidgetStyles = { - container: { - flex: 1, - padding: 20, - backgroundColor: BACKGROUND_COLOR, - }, - title: { - fontSize: 24, - fontWeight: 'bold', - marginBottom: 20, - textAlign: 'left', - flex: 1, - color: FOREGROUND_COLOR, - }, - label: { - marginBottom: 4, - fontSize: 16, - color: FOREGROUND_COLOR, - }, - input: { - height: 50, - borderColor: BORDER_COLOR, - borderWidth: 1, - borderRadius: 5, - paddingHorizontal: 10, - marginBottom: 15, - fontSize: 16, - color: FOREGROUND_COLOR, - }, - textArea: { - height: 100, - textAlignVertical: 'top', - color: FOREGROUND_COLOR, - }, - screenshotButton: { - backgroundColor: BACKGROUND_COLOR, - padding: 15, - borderRadius: 5, - alignItems: 'center', - flex: 1, - borderWidth: 1, - borderColor: BORDER_COLOR, - }, - screenshotContainer: { - flexDirection: 'row', - alignItems: 'center', - width: '100%', - marginBottom: 20, - }, - screenshotThumbnail: { - width: 50, - height: 50, - borderRadius: 5, - marginRight: 10, - }, - screenshotText: { - color: FOREGROUND_COLOR, - fontSize: 16, - }, - submitButton: { - backgroundColor: PURPLE, - paddingVertical: 15, - borderRadius: 5, - alignItems: 'center', - marginBottom: 10, - }, - submitText: { - color: BACKGROUND_COLOR, - fontSize: 18, - }, - cancelButton: { - backgroundColor: BACKGROUND_COLOR, - padding: 15, - borderRadius: 5, - alignItems: 'center', - borderWidth: 1, - borderColor: BORDER_COLOR, - }, - cancelText: { - color: FOREGROUND_COLOR, - fontSize: 16, - }, - titleContainer: { - flexDirection: 'row', - width: '100%', - }, - sentryLogo: { - width: 40, - height: 40, - }, +const defaultStyles = (theme: FeedbackWidgetTheme): FeedbackWidgetStyles => { + return { + container: { + flex: 1, + padding: 20, + backgroundColor: theme.background, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + marginBottom: 20, + textAlign: 'left', + flex: 1, + color: theme.foreground, + }, + label: { + marginBottom: 4, + fontSize: 16, + color: theme.foreground, + }, + input: { + height: 50, + borderColor: theme.border, + borderWidth: 1, + borderRadius: 5, + paddingHorizontal: 10, + marginBottom: 15, + fontSize: 16, + color: theme.foreground, + }, + textArea: { + height: 100, + textAlignVertical: 'top', + color: theme.foreground, + }, + screenshotButton: { + backgroundColor: theme.background, + padding: 15, + borderRadius: 5, + alignItems: 'center', + flex: 1, + borderWidth: 1, + borderColor: theme.border, + }, + screenshotContainer: { + flexDirection: 'row', + alignItems: 'center', + width: '100%', + marginBottom: 20, + }, + screenshotThumbnail: { + width: 50, + height: 50, + borderRadius: 5, + marginRight: 10, + }, + screenshotText: { + color: theme.foreground, + fontSize: 16, + }, + submitButton: { + backgroundColor: theme.accentBackground, + paddingVertical: 15, + borderRadius: 5, + alignItems: 'center', + marginBottom: 10, + }, + submitText: { + color: theme.accentForeground, + fontSize: 18, + }, + cancelButton: { + backgroundColor: theme.background, + padding: 15, + borderRadius: 5, + alignItems: 'center', + borderWidth: 1, + borderColor: theme.border, + }, + cancelText: { + color: theme.foreground, + fontSize: 16, + }, + titleContainer: { + flexDirection: 'row', + width: '100%', + }, + sentryLogo: { + width: 40, + height: 40, + tintColor: theme.sentryLogo, + }, + }; }; -export const defaultButtonStyles: FeedbackButtonStyles = { - triggerButton: { - position: 'absolute', - bottom: 30, - right: 30, - backgroundColor: BACKGROUND_COLOR, - padding: 15, - borderRadius: 40, - justifyContent: 'center', - alignItems: 'center', - elevation: 5, - shadowColor: BORDER_COLOR, - shadowOffset: { width: 1, height: 2 }, - shadowOpacity: 0.5, - shadowRadius: 3, - flexDirection: 'row', - borderWidth: 1, - borderColor: BORDER_COLOR, - }, - triggerText: { - color: FOREGROUND_COLOR, - fontSize: 18, - }, - triggerIcon: { - width: 24, - height: 24, - padding: 2, - marginEnd: 6, - }, +export const defaultButtonStyles = (theme: FeedbackWidgetTheme): FeedbackButtonStyles => { + return { + triggerButton: { + position: 'absolute', + bottom: 30, + right: 30, + backgroundColor: theme.background, + padding: 15, + borderRadius: 40, + justifyContent: 'center', + alignItems: 'center', + elevation: 5, + shadowColor: theme.border, + shadowOffset: { width: 1, height: 2 }, + shadowOpacity: 0.5, + shadowRadius: 3, + flexDirection: 'row', + borderWidth: 1, + borderColor: theme.border, + }, + triggerText: { + color: theme.foreground, + fontSize: 18, + }, + triggerIcon: { + width: 24, + height: 24, + padding: 2, + marginEnd: 6, + tintColor: theme.sentryLogo, + }, + }; }; export const modalWrapper: ViewStyle = { @@ -138,18 +140,20 @@ export const modalWrapper: ViewStyle = { bottom: 0, }; -export const modalSheetContainer: ViewStyle = { - backgroundColor: '#ffffff', - borderTopLeftRadius: 16, - borderTopRightRadius: 16, - overflow: 'hidden', - alignSelf: 'stretch', - shadowColor: '#000', - shadowOffset: { width: 0, height: -3 }, - shadowOpacity: 0.1, - shadowRadius: 4, - elevation: 5, - flex: 1, +export const modalSheetContainer = (theme: FeedbackWidgetTheme): ViewStyle => { + return { + backgroundColor: theme.background, + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + overflow: 'hidden', + alignSelf: 'stretch', + shadowColor: '#000', + shadowOffset: { width: 0, height: -3 }, + shadowOpacity: 0.1, + shadowRadius: 4, + elevation: 5, + flex: 1, + }; }; export const topSpacer: ViewStyle = { diff --git a/packages/core/src/js/feedback/FeedbackWidget.theme.ts b/packages/core/src/js/feedback/FeedbackWidget.theme.ts new file mode 100644 index 0000000000..aa8711a934 --- /dev/null +++ b/packages/core/src/js/feedback/FeedbackWidget.theme.ts @@ -0,0 +1,71 @@ +import { Appearance } from 'react-native'; + +import { getColorScheme, getFeedbackDarkTheme, getFeedbackLightTheme } from './integration'; + +/** + * Get the theme for the feedback widget based on the current color scheme + */ +export function getTheme(): FeedbackWidgetTheme { + const userTheme = getColorScheme(); + const colorScheme = userTheme === 'system' ? Appearance.getColorScheme() : userTheme; + const lightTheme = { ...LightTheme, ...getFeedbackLightTheme() }; + const darkTheme = { ...DarkTheme, ...getFeedbackDarkTheme() }; + return colorScheme === 'dark' ? darkTheme : lightTheme; +} + +export interface FeedbackWidgetTheme { + /** + * Background color for surfaces + */ + background: string; + + /** + * Foreground color (i.e. text color) + */ + foreground: string; + + /** + * Foreground color for accented elements + */ + accentForeground?: string; + + /** + * Background color for accented elements + */ + accentBackground?: string; + + /** + * Border color + */ + border?: string; + + /** + * Color for feedback icon + */ + feedbackIcon?: string; + + /** + * Color for Sentry logo + */ + sentryLogo?: string; +} + +export const LightTheme: FeedbackWidgetTheme = { + accentBackground: 'rgba(88, 74, 192, 1)', + accentForeground: '#ffffff', + foreground: '#2b2233', + background: '#ffffff', + border: 'rgba(41, 35, 47, 0.13)', + feedbackIcon: 'rgba(54, 45, 89, 1)', + sentryLogo: 'rgba(54, 45, 89, 1)', +}; + +export const DarkTheme: FeedbackWidgetTheme = { + accentBackground: 'rgba(88, 74, 192, 1)', + accentForeground: '#ffffff', + foreground: '#ebe6ef', + background: '#29232f', + border: 'rgba(235, 230, 239, 0.15)', + feedbackIcon: '#ffffff', + sentryLogo: '#ffffff', +}; diff --git a/packages/core/src/js/feedback/FeedbackWidget.tsx b/packages/core/src/js/feedback/FeedbackWidget.tsx index 7448d9d8b4..a3d7e8489e 100644 --- a/packages/core/src/js/feedback/FeedbackWidget.tsx +++ b/packages/core/src/js/feedback/FeedbackWidget.tsx @@ -1,8 +1,10 @@ import type { SendFeedbackParams } from '@sentry/core'; import { captureFeedback, getCurrentScope, lastEventId, logger } from '@sentry/core'; import * as React from 'react'; -import type { KeyboardTypeOptions } from 'react-native'; +import type { KeyboardTypeOptions , + NativeEventSubscription} from 'react-native'; import { + Appearance, Image, Keyboard, Text, @@ -17,6 +19,7 @@ import { getDataFromUri } from '../wrapper'; import { sentryLogo } from './branding'; import { defaultConfiguration } from './defaults'; import defaultStyles from './FeedbackWidget.styles'; +import { getTheme } from './FeedbackWidget.theme'; import type { FeedbackGeneralConfiguration, FeedbackTextConfiguration, FeedbackWidgetProps, FeedbackWidgetState, FeedbackWidgetStyles, ImagePickerConfiguration } from './FeedbackWidget.types'; import { lazyLoadFeedbackIntegration } from './lazy'; import { base64ToUint8Array, feedbackAlertDialog, isValidEmail } from './utils'; @@ -39,6 +42,8 @@ export class FeedbackWidget extends React.Component { + this.forceUpdate(); + }); + } + + /** + * Save the state before unmounting the component and remove the theme listener. */ public componentWillUnmount(): void { if (this._didSubmitForm) { @@ -196,18 +210,22 @@ export class FeedbackWidget extends React.Component { if (onFormClose) { onFormClose(); diff --git a/packages/core/src/js/feedback/FeedbackWidgetManager.tsx b/packages/core/src/js/feedback/FeedbackWidgetManager.tsx index 61b93a3a25..18224cab16 100644 --- a/packages/core/src/js/feedback/FeedbackWidgetManager.tsx +++ b/packages/core/src/js/feedback/FeedbackWidgetManager.tsx @@ -1,12 +1,13 @@ import { logger } from '@sentry/core'; import * as React from 'react'; -import type { NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; -import { Animated, Dimensions, Easing, Modal, PanResponder, Platform, ScrollView, View } from 'react-native'; +import type { NativeEventSubscription, NativeScrollEvent, NativeSyntheticEvent} from 'react-native'; +import { Animated, Appearance, Dimensions, Easing, Modal, PanResponder, Platform, ScrollView, View } from 'react-native'; import { notWeb } from '../utils/environment'; import { FeedbackButton } from './FeedbackButton'; import { FeedbackWidget } from './FeedbackWidget'; import { modalSheetContainer, modalWrapper, topSpacer } from './FeedbackWidget.styles'; +import { getTheme } from './FeedbackWidget.theme'; import type { FeedbackWidgetStyles } from './FeedbackWidget.types'; import { getFeedbackButtonOptions, getFeedbackOptions } from './integration'; import { lazyLoadAutoInjectFeedbackButtonIntegration,lazyLoadAutoInjectFeedbackIntegration } from './lazy'; @@ -97,6 +98,8 @@ class FeedbackWidgetProvider extends React.Component { return notWeb() && this.state.isScrollAtTop && gestureState.dy > 0; @@ -135,6 +138,24 @@ class FeedbackWidgetProvider extends React.Component { + this.forceUpdate(); + }); + } + + /** + * Clean up the theme listener. + */ + public componentWillUnmount(): void { + if (this._themeListener) { + this._themeListener.remove(); + } + } + /** * Animates the background opacity when the modal is shown. */ @@ -170,6 +191,8 @@ class FeedbackWidgetProvider extends React.Component{this.props.children}; } + const theme = getTheme(); + const { isButtonVisible, isVisible, backgroundOpacity } = this.state; const backgroundColor = backgroundOpacity.interpolate({ @@ -188,7 +211,7 @@ class FeedbackWidgetProvider extends React.Component ; buttonOptions: Partial; + colorScheme?: 'system' | 'light' | 'dark'; + themeLight: Partial; + themeDark: Partial; }; export const feedbackIntegration = ( - initOptions: FeedbackWidgetProps & { buttonOptions?: FeedbackButtonProps } = {}, + initOptions: FeedbackWidgetProps & { + buttonOptions?: FeedbackButtonProps; + colorScheme?: 'system' | 'light' | 'dark'; + themeLight?: Partial; + themeDark?: Partial; + } = {}, ): FeedbackIntegration => { - const { buttonOptions, ...widgetOptions } = initOptions; + const { buttonOptions, colorScheme, themeLight: lightTheme, themeDark: darkTheme, ...widgetOptions } = initOptions; return { name: MOBILE_FEEDBACK_INTEGRATION_NAME, options: widgetOptions, buttonOptions: buttonOptions || {}, + colorScheme: colorScheme || 'system', + themeLight: lightTheme || {}, + themeDark: darkTheme || {}, }; }; +const _getClientIntegration = (): FeedbackIntegration => { + return getClient()?.getIntegrationByName>(MOBILE_FEEDBACK_INTEGRATION_NAME); +}; + export const getFeedbackOptions = (): Partial => { - const integration = getClient()?.getIntegrationByName>( - MOBILE_FEEDBACK_INTEGRATION_NAME, - ); + const integration = _getClientIntegration(); if (!integration) { return {}; } @@ -33,12 +47,37 @@ export const getFeedbackOptions = (): Partial => { }; export const getFeedbackButtonOptions = (): Partial => { - const integration = getClient()?.getIntegrationByName>( - MOBILE_FEEDBACK_INTEGRATION_NAME, - ); + const integration = _getClientIntegration(); if (!integration) { return {}; } return integration.buttonOptions; }; + +export const getColorScheme = (): 'system' | 'light' | 'dark' => { + const integration = _getClientIntegration(); + if (!integration) { + return 'system'; + } + + return integration.colorScheme; +}; + +export const getFeedbackLightTheme = (): Partial => { + const integration = _getClientIntegration(); + if (!integration) { + return {}; + } + + return integration.themeLight; +}; + +export const getFeedbackDarkTheme = (): Partial => { + const integration = _getClientIntegration(); + if (!integration) { + return {}; + } + + return integration.themeDark; +}; diff --git a/packages/core/test/feedback/FeedbackWidgetManager.test.tsx b/packages/core/test/feedback/FeedbackWidgetManager.test.tsx index 92894320cb..8449f0e366 100644 --- a/packages/core/test/feedback/FeedbackWidgetManager.test.tsx +++ b/packages/core/test/feedback/FeedbackWidgetManager.test.tsx @@ -1,7 +1,7 @@ import { getClient, logger, setCurrentClient } from '@sentry/core'; import { render } from '@testing-library/react-native'; import * as React from 'react'; -import { Text } from 'react-native'; +import { Appearance,Text } from 'react-native'; import { defaultConfiguration } from '../../src/js/feedback/defaults'; import { FeedbackWidgetProvider, hideFeedbackButton,resetFeedbackButtonManager, resetFeedbackWidgetManager, showFeedbackButton, showFeedbackWidget } from '../../src/js/feedback/FeedbackWidgetManager'; @@ -141,6 +141,11 @@ describe('FeedbackWidgetManager', () => { }); describe('FeedbackButtonManager', () => { + let listener: (preferences: Appearance.AppearancePreferences) => void; + + afterEach(() => { + jest.resetAllMocks(); + }); beforeEach(() => { const client = new TestClient(getDefaultTestClientOptions()); @@ -148,6 +153,11 @@ describe('FeedbackButtonManager', () => { client.init(); consoleWarnSpy.mockReset(); resetFeedbackButtonManager(); + + jest.spyOn(Appearance, 'addChangeListener').mockImplementation((cb) => { + listener = cb; + return { remove: jest.fn() }; + }); }); it('showFeedbackButton displays the button when FeedbackWidgetProvider is used', () => { @@ -204,4 +214,288 @@ describe('FeedbackButtonManager', () => { expect(getClient().getIntegrationByName(AUTO_INJECT_FEEDBACK_BUTTON_INTEGRATION_NAME)).toBeDefined(); }); + + it('the Feedback Widget matches the snapshot with default configuration and system light theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with default configuration and system dark theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with default configuration and dynamically changed theme', () => { + const component = ( + + App Components + + ); + + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render(component); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackWidget(); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + listener({ colorScheme: 'dark' }); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with custom light theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'light', + themeLight: { + foreground: '#ff0000', + background: '#00ff00', + }, + }); + getClient()?.addIntegration(integration); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with custom dark theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'dark', + themeDark: { + foreground: '#00ff00', + background: '#ff0000', + }, + }); + getClient()?.addIntegration(integration); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with system light custom theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'system', + themeLight: { + foreground: '#ff0000', + background: '#00ff00', + }, + }); + getClient()?.addIntegration(integration); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Widget matches the snapshot with system dark custom theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'system', + themeDark: { + foreground: '#00ff00', + background: '#ff0000', + }, + }); + getClient()?.addIntegration(integration); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + + showFeedbackWidget(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with default configuration and system light theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with default configuration and system dark theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with default configuration and dynamically changed theme', () => { + const component = ( + + App Components + + ); + + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render(component); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackButton(); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + listener({ colorScheme: 'dark' }); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with custom light theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'light', + themeLight: { + foreground: '#ff0000', + background: '#00ff00', + }, + }); + getClient()?.addIntegration(integration); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with custom dark theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'dark', + themeDark: { + foreground: '#00ff00', + background: '#ff0000', + }, + }); + getClient()?.addIntegration(integration); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with system light custom theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'system', + themeLight: { + foreground: '#ff0000', + background: '#00ff00', + }, + }); + getClient()?.addIntegration(integration); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('light'); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); + + it('the Feedback Button matches the snapshot with system dark custom theme', () => { + mockedIsModalSupported.mockReturnValue(true); + const { toJSON } = render( + + App Components + + ); + + const integration = feedbackIntegration({ + colorScheme: 'system', + themeDark: { + foreground: '#00ff00', + background: '#ff0000', + }, + }); + getClient()?.addIntegration(integration); + + jest.spyOn(Appearance, 'getColorScheme').mockReturnValue('dark'); + + showFeedbackButton(); + + expect(toJSON()).toMatchSnapshot(); + }); }); diff --git a/packages/core/test/feedback/__snapshots__/FeedbackButton.test.tsx.snap b/packages/core/test/feedback/__snapshots__/FeedbackButton.test.tsx.snap index 178a48b2d3..5b1ab60eb4 100644 --- a/packages/core/test/feedback/__snapshots__/FeedbackButton.test.tsx.snap +++ b/packages/core/test/feedback/__snapshots__/FeedbackButton.test.tsx.snap @@ -66,6 +66,7 @@ exports[`FeedbackButton matches the snapshot with custom styles 1`] = ` "height": 24, "marginEnd": 6, "padding": 2, + "tintColor": "rgba(54, 45, 89, 1)", "width": 24, } } @@ -149,6 +150,7 @@ exports[`FeedbackButton matches the snapshot with custom texts 1`] = ` "height": 24, "marginEnd": 6, "padding": 2, + "tintColor": "rgba(54, 45, 89, 1)", "width": 24, } } @@ -232,6 +234,7 @@ exports[`FeedbackButton matches the snapshot with default configuration 1`] = ` "height": 24, "marginEnd": 6, "padding": 2, + "tintColor": "rgba(54, 45, 89, 1)", "width": 24, } } diff --git a/packages/core/test/feedback/__snapshots__/FeedbackWidget.test.tsx.snap b/packages/core/test/feedback/__snapshots__/FeedbackWidget.test.tsx.snap index 9f71d72ceb..9912460141 100644 --- a/packages/core/test/feedback/__snapshots__/FeedbackWidget.test.tsx.snap +++ b/packages/core/test/feedback/__snapshots__/FeedbackWidget.test.tsx.snap @@ -53,6 +53,7 @@ exports[`FeedbackWidget matches the snapshot with custom styles 1`] = ` style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } @@ -285,6 +286,7 @@ exports[`FeedbackWidget matches the snapshot with custom styles and screenshot b style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } @@ -580,6 +582,7 @@ exports[`FeedbackWidget matches the snapshot with custom texts 1`] = ` style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } @@ -843,6 +846,7 @@ exports[`FeedbackWidget matches the snapshot with custom texts and screenshot bu style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } @@ -1169,6 +1173,7 @@ exports[`FeedbackWidget matches the snapshot with default configuration 1`] = ` style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } @@ -1432,6 +1437,7 @@ exports[`FeedbackWidget matches the snapshot with default configuration and scre style={ { "height": 40, + "tintColor": "rgba(54, 45, 89, 1)", "width": 40, } } diff --git a/packages/core/test/feedback/__snapshots__/FeedbackWidgetManager.test.tsx.snap b/packages/core/test/feedback/__snapshots__/FeedbackWidgetManager.test.tsx.snap new file mode 100644 index 0000000000..d8400faf0c --- /dev/null +++ b/packages/core/test/feedback/__snapshots__/FeedbackWidgetManager.test.tsx.snap @@ -0,0 +1,3053 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with custom dark theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with custom light theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with default configuration and dynamically changed theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with default configuration and system dark theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with default configuration and system light theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with system dark custom theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Button matches the snapshot with system light custom theme 1`] = ` +[ + + App Components + , + + + + Report a Bug + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with custom dark theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with custom light theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with default configuration and dynamically changed theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with default configuration and system dark theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with default configuration and system light theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with system dark custom theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`; + +exports[`FeedbackButtonManager the Feedback Widget matches the snapshot with system light custom theme 1`] = ` +[ + + App Components + , + + + + + + + + + + Report a Bug + + + + + Name + + + + Email + + + + Description + (required) + + + + + Send Bug Report + + + + + Cancel + + + + + + + + , +] +`;