diff --git a/static/app/components/featureFeedback/feedbackModal.tsx b/static/app/components/featureFeedback/feedbackModal.tsx index 93e2963f327d..65044a427d41 100644 --- a/static/app/components/featureFeedback/feedbackModal.tsx +++ b/static/app/components/featureFeedback/feedbackModal.tsx @@ -1,4 +1,4 @@ -import {Fragment, useCallback, useMemo, useState} from 'react'; +import {Fragment, useMemo, useState} from 'react'; import {css, useTheme} from '@emotion/react'; import type {Event} from '@sentry/core'; import { @@ -83,6 +83,113 @@ export type FeedbackModalProps = ( useNewUserFeedback?: boolean; }; +type FeedbackModalHeaderProps = { + children: React.ReactNode; + header: ModalRenderProps['Header']; +}; + +function FeedbackModalHeader({children, header: Header}: FeedbackModalHeaderProps) { + return ( +
+

{children}

+
+ ); +} + +type FeedbackModalBodyProps = { + body: ModalRenderProps['Body']; + children: React.ReactNode; + isSelfHosted: boolean; + showSelfHostedMessage?: boolean; +}; + +function FeedbackModalBody({ + children, + body: Body, + isSelfHosted, + showSelfHostedMessage = true, +}: FeedbackModalBodyProps) { + return ( + + {children} + {isSelfHosted && showSelfHostedMessage && ( + + + {tct( + "You agree that any feedback you submit is subject to Sentry's [privacyPolicy:Privacy Policy] and Sentry may use such feedback without restriction or obligation.", + { + privacyPolicy: , + } + )} + + + )} + + ); +} + +type FeedbackModalFooterProps = { + closeModal: () => void; + footer: ModalRenderProps['Footer']; + handleSubmit: (submitEventData?: Event) => void; + hasCustomChildren: boolean; + isScreenSmall: boolean; + state: {[key: string]: any; subject?: any}; + onBack?: () => void; + onNext?: () => void; + primaryDisabledReason?: string; + secondaryAction?: React.ReactNode; + submitEventData?: Event; +}; + +function FeedbackModalFooter({ + footer: Footer, + closeModal, + handleSubmit, + isScreenSmall, + state, + hasCustomChildren, + onBack, + onNext, + submitEventData, + primaryDisabledReason, + secondaryAction, +}: FeedbackModalFooterProps) { + return ( + + ); +} + /** * A modal that allows users to submit feedback to Sentry (feedbacks project). * @@ -194,90 +301,7 @@ export function FeedbackModal({ ] ); - const ModalHeader = useCallback( - ({children: headerChildren}: {children: React.ReactNode}) => { - return ( -
-

{headerChildren}

-
- ); - }, - [Header] - ); - - const ModalFooter = useCallback( - ({ - onBack, - onNext, - submitEventData, - primaryDisabledReason, - secondaryAction, - }: Parameters['Footer']>[0]) => { - return ( -
- {secondaryAction && ( - - {secondaryAction} - - )} - {onBack && ( - - - - )} - - - - -
- ); - }, - [Footer, isScreenSmall, closeModal, handleSubmit, state, props.children] - ); - - const ModalBody = useCallback( - ({ - children: bodyChildren, - showSelfHostedMessage = true, - }: Parameters['Body']>[0]) => { - return ( - - {bodyChildren} - {isSelfHosted && showSelfHostedMessage && ( - - - {tct( - "You agree that any feedback you submit is subject to Sentry's [privacyPolicy:Privacy Policy] and Sentry may use such feedback without restriction or obligation.", - { - privacyPolicy: , - } - )} - - - )} - - ); - }, - [Body, isSelfHosted] - ); + const hasCustomChildren = props.children !== undefined; function handleFieldChange(field: Field, value: T[Field]) { const newState = cloneDeep(state); @@ -290,8 +314,8 @@ export function FeedbackModal({ return ( - {t('Submit Feedback')} - + {t('Submit Feedback')} + ({ } /> - - + + ); } + // For custom children, create bound wrappers that match the ChildrenProps API. + // These are passed as function values (not used as JSX element types here), + // so they do not trigger the static-component-definitions convention. + const boundHeader: ChildrenProps['Header'] = ({children: headerChildren}) => ( + {headerChildren} + ); + const boundBody: ChildrenProps['Body'] = ({ + children: bodyChildren, + showSelfHostedMessage, + }) => ( + + {bodyChildren} + + ); + const boundFooter: ChildrenProps['Footer'] = ({ + onBack, + onNext, + submitEventData, + primaryDisabledReason, + secondaryAction, + }) => ( + + ); + return ( {props.children({ - Header: ModalHeader, - Body: ModalBody, - Footer: ModalFooter, + Header: boundHeader, + Body: boundBody, + Footer: boundFooter, onFieldChange: handleFieldChange, state, })}