From 9a189b17fdc1b8a508f6ae8267e9eb44afd4a5bb Mon Sep 17 00:00:00 2001 From: Ryan Albrecht Date: Thu, 20 Nov 2025 16:07:17 -0800 Subject: [PATCH 1/3] chore: Refactor FeedbackButton to accept all the feedback options Also I have made some extra layers and older systems deprecated. I will try to followup and remove them fully afterwards --- .../featureFeedback/feedbackModal.tsx | 5 ++ .../app/components/featureFeedback/index.tsx | 8 +- .../feedback/widget/feedbackWidgetButton.tsx | 27 ------- .../widget/floatingFeedbackWidget.tsx | 27 ------- .../feedback/widget/useFeedbackWidget.tsx | 54 ------------- .../feedbackButton/feedbackButton.tsx | 79 +++++++++++++++++++ .../feedbackButton/floatingFeedbackButton.tsx | 43 ++++++++++ .../useFeedbackSDKIntegration.tsx} | 21 +++-- .../feedbackButton/useFeedbackWidget.tsx | 36 +++++++++ .../profiling/continuousProfileHeader.tsx | 4 +- .../components/profiling/profileHeader.tsx | 4 +- .../breadcrumbs/replayComparisonModal.tsx | 6 +- .../replays/header/feedbackButton.tsx | 7 +- static/app/utils/useFeedbackForm.spec.tsx | 6 +- static/app/utils/useFeedbackForm.tsx | 8 +- static/app/views/alerts/list/header.tsx | 4 +- static/app/views/dashboards/manage/index.tsx | 4 +- .../views/explore/multiQueryMode/index.tsx | 4 +- .../app/views/explore/savedQueries/index.tsx | 4 +- static/app/views/explore/spans/content.tsx | 4 +- .../crons/components/monitorHeaderActions.tsx | 4 +- .../views/insights/crons/views/overview.tsx | 4 +- .../views/insights/pages/domainViewHeader.tsx | 4 +- .../views/insights/uptime/views/overview.tsx | 4 +- .../app/views/issueDetails/groupDetails.tsx | 4 +- .../newTraceDetails/traceWaterfallState.tsx | 2 +- .../performance/transactionSummary/header.tsx | 4 +- .../header/buildCompareHeaderContent.tsx | 6 +- .../header/buildDetailsHeaderContent.tsx | 6 +- .../app/views/prevent/tests/testsWrapper.tsx | 6 +- static/app/views/profiling/content.tsx | 4 +- .../views/profiling/profileSummary/index.tsx | 4 +- .../app/views/projectDetail/projectDetail.tsx | 4 +- static/app/views/releases/list/index.tsx | 4 +- .../header/replayDetailsHeaderActions.tsx | 6 +- .../organizationMembers/inviteBanner.tsx | 4 +- .../views/settings/project/tempest/index.tsx | 4 +- 37 files changed, 245 insertions(+), 184 deletions(-) delete mode 100644 static/app/components/feedback/widget/feedbackWidgetButton.tsx delete mode 100644 static/app/components/feedback/widget/floatingFeedbackWidget.tsx delete mode 100644 static/app/components/feedback/widget/useFeedbackWidget.tsx create mode 100644 static/app/components/feedbackButton/feedbackButton.tsx create mode 100644 static/app/components/feedbackButton/floatingFeedbackButton.tsx rename static/app/components/{feedback/widget/useFeedback.tsx => feedbackButton/useFeedbackSDKIntegration.tsx} (70%) create mode 100644 static/app/components/feedbackButton/useFeedbackWidget.tsx diff --git a/static/app/components/featureFeedback/feedbackModal.tsx b/static/app/components/featureFeedback/feedbackModal.tsx index 3b96a1fba1c082..269272a056a2ab 100644 --- a/static/app/components/featureFeedback/feedbackModal.tsx +++ b/static/app/components/featureFeedback/feedbackModal.tsx @@ -84,6 +84,11 @@ export type FeedbackModalProps = ( useNewUserFeedback?: boolean; }; +/** + * A modal that allows users to submit feedback to Sentry (feedbacks project). + * + * @deprecated Use `` instead. + */ export function FeedbackModal({ Header, Body, diff --git a/static/app/components/featureFeedback/index.tsx b/static/app/components/featureFeedback/index.tsx index 6907b12a4c8b5c..9ab202a4bfd129 100644 --- a/static/app/components/featureFeedback/index.tsx +++ b/static/app/components/featureFeedback/index.tsx @@ -12,8 +12,12 @@ type FeatureFeedbackProps = FeedbackModalProps & { secondaryAction?: React.ReactNode; }; -// Provides a button that, when clicked, opens a modal with a form that, -// when filled and submitted, will send feedback to Sentry (feedbacks project). +/** + * Provides a button that, when clicked, opens a modal with a form that, + * when filled and submitted, will send feedback to Sentry (feedbacks project). + * + * @deprecated Use `` instead. + */ export function FeatureFeedback({ buttonProps = {}, ...props diff --git a/static/app/components/feedback/widget/feedbackWidgetButton.tsx b/static/app/components/feedback/widget/feedbackWidgetButton.tsx deleted file mode 100644 index 417b5c2a87546a..00000000000000 --- a/static/app/components/feedback/widget/feedbackWidgetButton.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {useRef} from 'react'; - -import {Button} from 'sentry/components/core/button'; -import type {UseFeedbackOptions} from 'sentry/components/feedback/widget/useFeedback'; -import useFeedbackWidget from 'sentry/components/feedback/widget/useFeedbackWidget'; -import {IconMegaphone} from 'sentry/icons/iconMegaphone'; -import {t} from 'sentry/locale'; - -export default function FeedbackWidgetButton({ - optionOverrides, -}: { - optionOverrides?: UseFeedbackOptions; -}) { - const buttonRef = useRef(null); - const feedback = useFeedbackWidget({buttonRef, optionOverrides}); - - // Do not show button if Feedback integration is not enabled - if (!feedback) { - return null; - } - - return ( - - ); -} diff --git a/static/app/components/feedback/widget/floatingFeedbackWidget.tsx b/static/app/components/feedback/widget/floatingFeedbackWidget.tsx deleted file mode 100644 index b4ff6b20de6b97..00000000000000 --- a/static/app/components/feedback/widget/floatingFeedbackWidget.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import {css, Global, useTheme} from '@emotion/react'; - -import useFeedbackWidget from 'sentry/components/feedback/widget/useFeedbackWidget'; - -/** - * Use this to display the Feedback widget in certain routes/components - */ -export default function FloatingFeedbackWidget() { - const feedback = useFeedbackWidget({}); - const theme = useTheme(); - - // No need for global styles if Feedback integration is not enabled - if (!feedback) { - return null; - } - - // z-index needs to be below our indicators which is 10001 - return ( - - ); -} diff --git a/static/app/components/feedback/widget/useFeedbackWidget.tsx b/static/app/components/feedback/widget/useFeedbackWidget.tsx deleted file mode 100644 index 201a4be3bfce15..00000000000000 --- a/static/app/components/feedback/widget/useFeedbackWidget.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import type {RefObject} from 'react'; -import {useEffect} from 'react'; - -import type {UseFeedbackOptions} from 'sentry/components/feedback/widget/useFeedback'; -import {useFeedback} from 'sentry/components/feedback/widget/useFeedback'; - -interface Props { - buttonRef?: RefObject | RefObject; - formTitle?: string; - messagePlaceholder?: string; - optionOverrides?: UseFeedbackOptions; -} - -export default function useFeedbackWidget({ - buttonRef, - formTitle, - messagePlaceholder, - optionOverrides, -}: Props) { - const {feedback, options: defaultOptions} = useFeedback({ - formTitle, - messagePlaceholder, - }); - - useEffect(() => { - if (!feedback) { - return undefined; - } - - const options = { - ...defaultOptions, - ...optionOverrides, - tags: { - ...defaultOptions.tags, - ...optionOverrides?.tags, - }, - }; - - if (buttonRef) { - if (buttonRef.current) { - return feedback.attachTo(buttonRef.current, options); - } - } else { - const widget = feedback.createWidget(options); - return () => { - widget.removeFromDom(); - }; - } - - return undefined; - }, [buttonRef, feedback, defaultOptions, optionOverrides]); - - return feedback; -} diff --git a/static/app/components/feedbackButton/feedbackButton.tsx b/static/app/components/feedbackButton/feedbackButton.tsx new file mode 100644 index 00000000000000..98d66a989c3ea9 --- /dev/null +++ b/static/app/components/feedbackButton/feedbackButton.tsx @@ -0,0 +1,79 @@ +import {useEffect, useRef} from 'react'; + +import {Button, type ButtonProps} from 'sentry/components/core/button'; +import { + useFeedbackSDKIntegration, + type UseFeedbackOptions, +} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; +import {IconMegaphone} from 'sentry/icons/iconMegaphone'; +import {t} from 'sentry/locale'; + +interface Props extends Omit { + feedbackOptions?: UseFeedbackOptions; +} + +/** + * A button component that opens the Sentry feedback widget when clicked. + * + * Use this component to embed a feedback collection button anywhere in the UI where users + * might want to submit feedback, report issues, or share suggestions with your team. + * + * The component will return null when the Feedback SDK integration is not enabled, + * like in self-hosted environments. + * + * It's strongly recommended to add the tags: `feedback.source` and `feedback.owner` + * and then setup an alert rule to notify you when feedback is submitted. + * + * @example + * // Mix of Button and Feedback props + * + * + * @param feedbackOptions - Optional configuration to customize the feedback widget behavior, + * such as form labels, tags, or user metadata + * + * @param children - The content to display inside the button. If not provided, the default label 'Give Feedback' will be used. + * + * @param * - All standard Button props except `icon` (icon is fixed to megaphone). + * Includes size, priority, disabled, onClick handlers, etc. + * + * @returns A Button that opens the feedback widget on click, or null if feedback is not enabled + */ +export default function FeedbackButton({ + feedbackOptions, + children, + ...buttonProps +}: Props) { + const buttonRef = useRef(null); + const {feedback, options} = useFeedbackSDKIntegration({ + optionOverrides: feedbackOptions, + }); + + useEffect(() => { + if (feedback && buttonRef.current) { + return feedback.attachTo(buttonRef.current, options); + } + + return undefined; + }, [buttonRef, feedback, options]); + + // Do not show button if Feedback integration is not enabled + if (!feedback) { + return null; + } + + return ( + + ); +} diff --git a/static/app/components/feedbackButton/floatingFeedbackButton.tsx b/static/app/components/feedbackButton/floatingFeedbackButton.tsx new file mode 100644 index 00000000000000..8e2859c87a0ad9 --- /dev/null +++ b/static/app/components/feedbackButton/floatingFeedbackButton.tsx @@ -0,0 +1,43 @@ +import {useEffect} from 'react'; +import {css, Global, useTheme} from '@emotion/react'; + +import {useFeedbackSDKIntegration} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; + +/** + * ` renders a 'Give Feedback' button that floats in + * the bottom right corner of the page. + * + * This button can float overtop of content, but can also be helpful because it + * allows users to scroll anywhere and still be able to trigger the Feedback form + * which allows taking screenshots of what's visible on the page. + */ +export default function FloatingFeedbackButton() { + const theme = useTheme(); + const {feedback, options} = useFeedbackSDKIntegration(); + + useEffect(() => { + if (!feedback) { + return undefined; + } + + const widget = feedback.createWidget(options); + return () => { + widget.removeFromDom(); + }; + }, [feedback, options]); + + if (!feedback) { + return null; + } + + // z-index needs to be below our indicators which is 10001 + return ( + + ); +} diff --git a/static/app/components/feedback/widget/useFeedback.tsx b/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx similarity index 70% rename from static/app/components/feedback/widget/useFeedback.tsx rename to static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx index c186620af3b85a..9f42e03bf04a8f 100644 --- a/static/app/components/feedback/widget/useFeedback.tsx +++ b/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx @@ -10,11 +10,11 @@ export type FeedbackIntegration = NonNullable[0]; -export function useFeedback({ - formTitle, - messagePlaceholder, - tags, -}: NonNullable): { +interface Props { + optionOverrides?: UseFeedbackOptions; +} + +export function useFeedbackSDKIntegration({optionOverrides = {}}: Props = {}): { feedback: FeedbackIntegration | undefined; options: NonNullable; } { @@ -23,16 +23,15 @@ export function useFeedback({ const feedback = state.Feedback as FeedbackIntegration | undefined; - const options = useMemo(() => { + const options = useMemo((): NonNullable => { return { colorScheme: config.theme === 'dark' ? ('dark' as const) : ('light' as const), - buttonLabel: t('Give Feedback'), submitButtonLabel: t('Send Feedback'), - messagePlaceholder: messagePlaceholder ?? t('What did you expect?'), - formTitle: formTitle ?? t('Give Feedback'), - tags, + messagePlaceholder: t('What did you expect?'), + formTitle: t('Give Feedback'), + ...optionOverrides, }; - }, [config.theme, formTitle, messagePlaceholder, tags]); + }, [config.theme, optionOverrides]); return {feedback, options}; } diff --git a/static/app/components/feedbackButton/useFeedbackWidget.tsx b/static/app/components/feedbackButton/useFeedbackWidget.tsx new file mode 100644 index 00000000000000..e377b58ba64669 --- /dev/null +++ b/static/app/components/feedbackButton/useFeedbackWidget.tsx @@ -0,0 +1,36 @@ +import type {RefObject} from 'react'; +import {useEffect} from 'react'; + +import {useFeedbackSDKIntegration} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; + +interface Props { + buttonRef?: RefObject | RefObject; +} + +/** + * @deprecated This layer isn't needed. Call `useFeedbackSDKIntegration` or use `` or `` + */ +export default function useFeedbackWidget({buttonRef}: Props) { + const {feedback, options} = useFeedbackSDKIntegration(); + + useEffect(() => { + if (!feedback) { + return undefined; + } + + if (buttonRef) { + if (buttonRef.current) { + return feedback.attachTo(buttonRef.current, options); + } + } else { + const widget = feedback.createWidget(options); + return () => { + widget.removeFromDom(); + }; + } + + return undefined; + }, [buttonRef, feedback, options]); + + return feedback; +} diff --git a/static/app/components/profiling/continuousProfileHeader.tsx b/static/app/components/profiling/continuousProfileHeader.tsx index c8fcf9aa8a8201..67b3b2864eecf6 100644 --- a/static/app/components/profiling/continuousProfileHeader.tsx +++ b/static/app/components/profiling/continuousProfileHeader.tsx @@ -2,7 +2,7 @@ import {useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import {LinkButton} from 'sentry/components/core/button/linkButton'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import * as Layout from 'sentry/components/layouts/thirds'; import type {ProfilingBreadcrumbsProps} from 'sentry/components/profiling/profilingBreadcrumbs'; import {ProfilingBreadcrumbs} from 'sentry/components/profiling/profilingBreadcrumbs'; @@ -51,7 +51,7 @@ export function ContinuousProfileHeader({transaction}: ContinuousProfileHeader) - + {transactionTarget && ( {t('Go to Trace')} diff --git a/static/app/components/profiling/profileHeader.tsx b/static/app/components/profiling/profileHeader.tsx index 9d105df8273412..52fdb0740583ae 100644 --- a/static/app/components/profiling/profileHeader.tsx +++ b/static/app/components/profiling/profileHeader.tsx @@ -2,7 +2,7 @@ import {useCallback, useMemo} from 'react'; import styled from '@emotion/styled'; import {LinkButton} from 'sentry/components/core/button/linkButton'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import * as Layout from 'sentry/components/layouts/thirds'; import type {ProfilingBreadcrumbsProps} from 'sentry/components/profiling/profilingBreadcrumbs'; import {ProfilingBreadcrumbs} from 'sentry/components/profiling/profilingBreadcrumbs'; @@ -90,7 +90,7 @@ function ProfileHeader({transaction, projectId, eventId}: ProfileHeaderProps) { - + {transactionTarget && ( {t('Go to Trace')} diff --git a/static/app/components/replays/breadcrumbs/replayComparisonModal.tsx b/static/app/components/replays/breadcrumbs/replayComparisonModal.tsx index 025bbd3bba27b4..bd5e917b653659 100644 --- a/static/app/components/replays/breadcrumbs/replayComparisonModal.tsx +++ b/static/app/components/replays/breadcrumbs/replayComparisonModal.tsx @@ -7,7 +7,7 @@ import {Button} from 'sentry/components/core/button'; import {Flex} from 'sentry/components/core/layout'; import {ExternalLink} from 'sentry/components/core/link'; import {Tooltip} from 'sentry/components/core/tooltip'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import {useGlobalModal} from 'sentry/components/globalModal/useGlobalModal'; import {Hovercard} from 'sentry/components/hovercard'; import {DiffCompareContextProvider} from 'sentry/components/replays/diff/diffCompareContext'; @@ -88,8 +88,8 @@ export default function ReplayComparisonModal({ ) : null} {focusTrap ? ( - { focusTrap.pause(); }, diff --git a/static/app/components/replays/header/feedbackButton.tsx b/static/app/components/replays/header/feedbackButton.tsx index 916432caf76caf..51b35cfa7c9112 100644 --- a/static/app/components/replays/header/feedbackButton.tsx +++ b/static/app/components/replays/header/feedbackButton.tsx @@ -8,12 +8,13 @@ const FeedbackButtonHook = HookOrDefault({ defaultComponent: ({children}) => {children}, }); -function FeedbackButton() { +/** + * @deprecated Use `` instead. + */ +export default function FeedbackButton() { return ( ); } - -export default FeedbackButton; diff --git a/static/app/utils/useFeedbackForm.spec.tsx b/static/app/utils/useFeedbackForm.spec.tsx index a88c8b7c68f042..6bb9f4a2ec77ee 100644 --- a/static/app/utils/useFeedbackForm.spec.tsx +++ b/static/app/utils/useFeedbackForm.spec.tsx @@ -1,7 +1,7 @@ import {renderHook, waitFor} from 'sentry-test/reactTestingLibrary'; -import type {FeedbackIntegration} from 'sentry/components/feedback/widget/useFeedback'; -import * as useFeedback from 'sentry/components/feedback/widget/useFeedback'; +import type {FeedbackIntegration} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; +import * as useFeedbackSDKIntegration from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; import {GlobalFeedbackForm, useFeedbackForm} from 'sentry/utils/useFeedbackForm'; const mockForm = { @@ -27,7 +27,7 @@ const defaultOptions = { describe('useFeedbackForm', () => { beforeEach(() => { jest - .spyOn(useFeedback, 'useFeedback') + .spyOn(useFeedbackSDKIntegration, 'useFeedbackSDKIntegration') .mockReturnValue({feedback: mockFeedback, options: defaultOptions}); jest.clearAllMocks(); }); diff --git a/static/app/utils/useFeedbackForm.tsx b/static/app/utils/useFeedbackForm.tsx index 758c4178d3f158..7ce9389a9759b6 100644 --- a/static/app/utils/useFeedbackForm.tsx +++ b/static/app/utils/useFeedbackForm.tsx @@ -10,9 +10,9 @@ import type {FeedbackModalIntegration} from '@sentry/core'; import isEqual from 'lodash/isEqual'; import { - useFeedback, + useFeedbackSDKIntegration, type UseFeedbackOptions, -} from 'sentry/components/feedback/widget/useFeedback'; +} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; /** * A function that opens the feedback form. It accepts some option overrides @@ -42,6 +42,8 @@ const GlobalFeedbackFormContext = createContext(null); * * return ; * ``` + * + * @deprecated This hook is too low level. Use `` or `` instead. */ export function useFeedbackForm() { return useContext(GlobalFeedbackFormContext); @@ -53,7 +55,7 @@ function useOpenForm() { // This is used to determine if we should reuse the existing form instance const formOptionsOverrideRef = useRef(null); - const {feedback, options} = useFeedback({}); + const {feedback, options} = useFeedbackSDKIntegration(); const close = useCallback(() => { if (formRef.current) { diff --git a/static/app/views/alerts/list/header.tsx b/static/app/views/alerts/list/header.tsx index 5c74dfb723f066..c7888aa1a0b2d6 100644 --- a/static/app/views/alerts/list/header.tsx +++ b/static/app/views/alerts/list/header.tsx @@ -3,7 +3,7 @@ import {ButtonBar} from 'sentry/components/core/button/buttonBar'; import {LinkButton} from 'sentry/components/core/button/linkButton'; import {TabList} from 'sentry/components/core/tabs'; import CreateAlertButton from 'sentry/components/createAlertButton'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import * as Layout from 'sentry/components/layouts/thirds'; import {PageHeadingQuestionTooltip} from 'sentry/components/pageHeadingQuestionTooltip'; import {IconSettings} from 'sentry/icons'; @@ -73,7 +73,7 @@ function AlertHeader({activeTab}: Props) { > {t('Create Alert')} - + - + {({ hasReachedDashboardLimit, diff --git a/static/app/views/explore/multiQueryMode/index.tsx b/static/app/views/explore/multiQueryMode/index.tsx index c1a929e60cede4..12f842667f863b 100644 --- a/static/app/views/explore/multiQueryMode/index.tsx +++ b/static/app/views/explore/multiQueryMode/index.tsx @@ -1,7 +1,7 @@ import Feature from 'sentry/components/acl/feature'; import {Breadcrumbs} from 'sentry/components/breadcrumbs'; import {ButtonBar} from 'sentry/components/core/button/buttonBar'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import * as Layout from 'sentry/components/layouts/thirds'; import {NoAccess} from 'sentry/components/noAccess'; import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container'; @@ -58,7 +58,7 @@ export default function MultiQueryMode() { {defined(id) && savedQuery?.isPrebuilt === false && } - + diff --git a/static/app/views/explore/savedQueries/index.tsx b/static/app/views/explore/savedQueries/index.tsx index dc9275121bca6e..907cdad5b26779 100644 --- a/static/app/views/explore/savedQueries/index.tsx +++ b/static/app/views/explore/savedQueries/index.tsx @@ -4,7 +4,7 @@ import {Button} from 'sentry/components/core/button'; import {ButtonBar} from 'sentry/components/core/button/buttonBar'; import {LinkButton} from 'sentry/components/core/button/linkButton'; import {DropdownMenu} from 'sentry/components/dropdownMenu'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import * as Layout from 'sentry/components/layouts/thirds'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {IconAdd} from 'sentry/icons/iconAdd'; @@ -48,7 +48,7 @@ export default function SavedQueriesView() { - + {hasLogsFeature ? ( {defined(id) && savedQuery?.isPrebuilt === false && } - + diff --git a/static/app/views/insights/crons/components/monitorHeaderActions.tsx b/static/app/views/insights/crons/components/monitorHeaderActions.tsx index e870df97278563..1bc9a0c6a890b3 100644 --- a/static/app/views/insights/crons/components/monitorHeaderActions.tsx +++ b/static/app/views/insights/crons/components/monitorHeaderActions.tsx @@ -5,7 +5,7 @@ import {Button, type ButtonProps} from 'sentry/components/core/button'; import {ButtonBar} from 'sentry/components/core/button/buttonBar'; import {LinkButton} from 'sentry/components/core/button/linkButton'; import {Link} from 'sentry/components/core/link'; -import FeedbackWidgetButton from 'sentry/components/feedback/widget/feedbackWidgetButton'; +import FeedbackButton from 'sentry/components/feedbackButton/feedbackButton'; import {IconDelete, IconEdit, IconSubscribed, IconUnsubscribed} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {browserHistory} from 'sentry/utils/browserHistory'; @@ -83,7 +83,7 @@ function MonitorHeaderActions({monitor, orgSlug, onUpdate}: Props) { return ( - + ); diff --git a/static/app/components/feedbackButton/floatingFeedbackButton.tsx b/static/app/components/feedbackButton/floatingFeedbackButton.tsx index 8e2859c87a0ad9..30df0116aaeb36 100644 --- a/static/app/components/feedbackButton/floatingFeedbackButton.tsx +++ b/static/app/components/feedbackButton/floatingFeedbackButton.tsx @@ -13,18 +13,18 @@ import {useFeedbackSDKIntegration} from 'sentry/components/feedbackButton/useFee */ export default function FloatingFeedbackButton() { const theme = useTheme(); - const {feedback, options} = useFeedbackSDKIntegration(); + const {feedback, defaultOptions} = useFeedbackSDKIntegration(); useEffect(() => { if (!feedback) { return undefined; } - const widget = feedback.createWidget(options); + const widget = feedback.createWidget(defaultOptions); return () => { widget.removeFromDom(); }; - }, [feedback, options]); + }, [feedback, defaultOptions]); if (!feedback) { return null; diff --git a/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx b/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx index 9f42e03bf04a8f..26acd796b45fd7 100644 --- a/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx +++ b/static/app/components/feedbackButton/useFeedbackSDKIntegration.tsx @@ -10,28 +10,23 @@ export type FeedbackIntegration = NonNullable[0]; -interface Props { - optionOverrides?: UseFeedbackOptions; -} - -export function useFeedbackSDKIntegration({optionOverrides = {}}: Props = {}): { +export function useFeedbackSDKIntegration(): { + defaultOptions: NonNullable; feedback: FeedbackIntegration | undefined; - options: NonNullable; } { const config = useLegacyStore(ConfigStore); const {state} = useAsyncSDKIntegrationStore(); const feedback = state.Feedback as FeedbackIntegration | undefined; - const options = useMemo((): NonNullable => { + const defaultOptions = useMemo((): NonNullable => { return { colorScheme: config.theme === 'dark' ? ('dark' as const) : ('light' as const), submitButtonLabel: t('Send Feedback'), messagePlaceholder: t('What did you expect?'), formTitle: t('Give Feedback'), - ...optionOverrides, }; - }, [config.theme, optionOverrides]); + }, [config.theme]); - return {feedback, options}; + return {feedback, defaultOptions}; } diff --git a/static/app/components/feedbackButton/useFeedbackWidget.tsx b/static/app/components/feedbackButton/useFeedbackWidget.tsx index e377b58ba64669..2257e403865c48 100644 --- a/static/app/components/feedbackButton/useFeedbackWidget.tsx +++ b/static/app/components/feedbackButton/useFeedbackWidget.tsx @@ -11,7 +11,7 @@ interface Props { * @deprecated This layer isn't needed. Call `useFeedbackSDKIntegration` or use `` or `` */ export default function useFeedbackWidget({buttonRef}: Props) { - const {feedback, options} = useFeedbackSDKIntegration(); + const {feedback, defaultOptions} = useFeedbackSDKIntegration(); useEffect(() => { if (!feedback) { @@ -20,17 +20,17 @@ export default function useFeedbackWidget({buttonRef}: Props) { if (buttonRef) { if (buttonRef.current) { - return feedback.attachTo(buttonRef.current, options); + return feedback.attachTo(buttonRef.current, defaultOptions); } } else { - const widget = feedback.createWidget(options); + const widget = feedback.createWidget(defaultOptions); return () => { widget.removeFromDom(); }; } return undefined; - }, [buttonRef, feedback, options]); + }, [buttonRef, feedback, defaultOptions]); return feedback; } diff --git a/static/app/utils/useFeedbackForm.spec.tsx b/static/app/utils/useFeedbackForm.spec.tsx index 6bb9f4a2ec77ee..0190bf21f15f20 100644 --- a/static/app/utils/useFeedbackForm.spec.tsx +++ b/static/app/utils/useFeedbackForm.spec.tsx @@ -1,6 +1,9 @@ import {renderHook, waitFor} from 'sentry-test/reactTestingLibrary'; -import type {FeedbackIntegration} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; +import type { + FeedbackIntegration, + UseFeedbackOptions, +} from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; import * as useFeedbackSDKIntegration from 'sentry/components/feedbackButton/useFeedbackSDKIntegration'; import {GlobalFeedbackForm, useFeedbackForm} from 'sentry/utils/useFeedbackForm'; @@ -15,20 +18,18 @@ const mockFeedback = { createForm: jest.fn().mockResolvedValue(mockForm), } as unknown as FeedbackIntegration; -const defaultOptions = { +const defaultOptions: NonNullable = { colorScheme: 'light' as const, - buttonLabel: '', submitButtonLabel: '', messagePlaceholder: '', formTitle: '', - tags: {}, }; describe('useFeedbackForm', () => { beforeEach(() => { jest .spyOn(useFeedbackSDKIntegration, 'useFeedbackSDKIntegration') - .mockReturnValue({feedback: mockFeedback, options: defaultOptions}); + .mockReturnValue({feedback: mockFeedback, defaultOptions}); jest.clearAllMocks(); }); diff --git a/static/app/utils/useFeedbackForm.tsx b/static/app/utils/useFeedbackForm.tsx index 7ce9389a9759b6..3fc88c6a9362df 100644 --- a/static/app/utils/useFeedbackForm.tsx +++ b/static/app/utils/useFeedbackForm.tsx @@ -55,7 +55,7 @@ function useOpenForm() { // This is used to determine if we should reuse the existing form instance const formOptionsOverrideRef = useRef(null); - const {feedback, options} = useFeedbackSDKIntegration(); + const {feedback, defaultOptions} = useFeedbackSDKIntegration(); const close = useCallback(() => { if (formRef.current) { @@ -91,7 +91,7 @@ function useOpenForm() { cleanup(); formRef.current = await feedback.createForm({ - ...options, + ...defaultOptions, ...optionOverrides, onFormClose: close, onFormSubmitted: cleanup, @@ -102,7 +102,7 @@ function useOpenForm() { formRef.current.open(); } }, - [cleanup, feedback, options, close] + [cleanup, feedback, defaultOptions, close] ); useEffect(() => {