diff --git a/packages/feedback/src/constants/index.ts b/packages/feedback/src/constants/index.ts index fe4683b5ee17..a8794ed663b0 100644 --- a/packages/feedback/src/constants/index.ts +++ b/packages/feedback/src/constants/index.ts @@ -9,9 +9,10 @@ export const WINDOW = GLOBAL_OBJ as typeof GLOBAL_OBJ & Window; export const DOCUMENT = WINDOW.document; export const NAVIGATOR = WINDOW.navigator; -export const ACTOR_LABEL = 'Report a Bug'; +export const TRIGGER_LABEL = 'Report a Bug'; export const CANCEL_BUTTON_LABEL = 'Cancel'; export const SUBMIT_BUTTON_LABEL = 'Send Bug Report'; +export const CONFIRM_BUTTON_LABEL = 'Confirm'; export const FORM_TITLE = 'Report a Bug'; export const EMAIL_PLACEHOLDER = 'your.email@example.org'; export const EMAIL_LABEL = 'Email'; @@ -20,7 +21,9 @@ export const MESSAGE_LABEL = 'Description'; export const NAME_PLACEHOLDER = 'Your Name'; export const NAME_LABEL = 'Name'; export const SUCCESS_MESSAGE_TEXT = 'Thank you for your report!'; -export const IS_REQUIRED_TEXT = '(required)'; +export const IS_REQUIRED_LABEL = '(required)'; +export const ADD_SCREENSHOT_LABEL = 'Add a screenshot'; +export const REMOVE_SCREENSHOT_LABEL = 'Remove screenshot'; export const FEEDBACK_WIDGET_SOURCE = 'widget'; export const FEEDBACK_API_SOURCE = 'api'; diff --git a/packages/feedback/src/core/components/Actor.ts b/packages/feedback/src/core/components/Actor.ts index ac62fad4957e..6b2469e0313c 100644 --- a/packages/feedback/src/core/components/Actor.ts +++ b/packages/feedback/src/core/components/Actor.ts @@ -3,7 +3,7 @@ import { createActorStyles } from './Actor.css'; import { FeedbackIcon } from './FeedbackIcon'; export interface ActorProps { - buttonLabel: string; + triggerLabel: string; shadow: ShadowRoot; } @@ -22,16 +22,16 @@ export interface ActorComponent { /** * The sentry-provided button to open the feedback modal */ -export function Actor({ buttonLabel, shadow }: ActorProps): ActorComponent { +export function Actor({ triggerLabel, shadow }: ActorProps): ActorComponent { const el = DOCUMENT.createElement('button'); el.type = 'button'; el.className = 'widget__actor'; el.ariaHidden = 'false'; - el.ariaLabel = buttonLabel; + el.ariaLabel = triggerLabel; el.appendChild(FeedbackIcon()); - if (buttonLabel) { + if (triggerLabel) { const label = DOCUMENT.createElement('span'); - label.appendChild(DOCUMENT.createTextNode(buttonLabel)); + label.appendChild(DOCUMENT.createTextNode(triggerLabel)); el.appendChild(label); } diff --git a/packages/feedback/src/core/integration.ts b/packages/feedback/src/core/integration.ts index 32cce8b83fe7..1dace11ced6e 100644 --- a/packages/feedback/src/core/integration.ts +++ b/packages/feedback/src/core/integration.ts @@ -9,20 +9,23 @@ import type { } from '@sentry/types'; import { isBrowser, logger } from '@sentry/utils'; import { - ACTOR_LABEL, + ADD_SCREENSHOT_LABEL, CANCEL_BUTTON_LABEL, + CONFIRM_BUTTON_LABEL, DEFAULT_THEME, DOCUMENT, EMAIL_LABEL, EMAIL_PLACEHOLDER, FORM_TITLE, - IS_REQUIRED_TEXT, + IS_REQUIRED_LABEL, MESSAGE_LABEL, MESSAGE_PLACEHOLDER, NAME_LABEL, NAME_PLACEHOLDER, + REMOVE_SCREENSHOT_LABEL, SUBMIT_BUTTON_LABEL, SUCCESS_MESSAGE_TEXT, + TRIGGER_LABEL, } from '../constants'; import { DEBUG_BUILD } from '../util/debug-build'; import { isScreenshotSupported } from '../util/isScreenshotSupported'; @@ -80,18 +83,21 @@ export const buildFeedbackIntegration = ({ themeDark, // FeedbackTextConfiguration - buttonLabel = ACTOR_LABEL, + addScreenshotButtonLabel = ADD_SCREENSHOT_LABEL, + triggerLabel = TRIGGER_LABEL, cancelButtonLabel = CANCEL_BUTTON_LABEL, - submitButtonLabel = SUBMIT_BUTTON_LABEL, - formTitle = FORM_TITLE, + confirmButtonLabel = CONFIRM_BUTTON_LABEL, emailLabel = EMAIL_LABEL, emailPlaceholder = EMAIL_PLACEHOLDER, + formTitle = FORM_TITLE, + isRequiredLabel = IS_REQUIRED_LABEL, messageLabel = MESSAGE_LABEL, messagePlaceholder = MESSAGE_PLACEHOLDER, nameLabel = NAME_LABEL, namePlaceholder = NAME_PLACEHOLDER, + removeScreenshotButtonLabel = REMOVE_SCREENSHOT_LABEL, + submitButtonLabel = SUBMIT_BUTTON_LABEL, successMessageText = SUCCESS_MESSAGE_TEXT, - isRequiredText = IS_REQUIRED_TEXT, // FeedbackCallbacks onFormOpen, @@ -121,9 +127,10 @@ export const buildFeedbackIntegration = ({ ...themeLight, }, - buttonLabel, + triggerLabel, cancelButtonLabel, submitButtonLabel, + confirmButtonLabel, formTitle, emailLabel, emailPlaceholder, @@ -132,7 +139,9 @@ export const buildFeedbackIntegration = ({ nameLabel, namePlaceholder, successMessageText, - isRequiredText, + isRequiredLabel, + addScreenshotButtonLabel, + removeScreenshotButtonLabel, onFormClose, onFormOpen, @@ -250,7 +259,7 @@ export const buildFeedbackIntegration = ({ const _createActor = (optionOverrides: OverrideFeedbackConfiguration = {}): ActorComponent => { const shadow = _createShadow(_options); - const actor = Actor({ buttonLabel: _options.buttonLabel, shadow }); + const actor = Actor({ triggerLabel: _options.triggerLabel, shadow }); const mergedOptions = mergeOptions(_options, { ...optionOverrides, onFormOpen() { diff --git a/packages/feedback/src/modal/components/Form.tsx b/packages/feedback/src/modal/components/Form.tsx index 15d3bcb3ef91..7e2b645c45bf 100644 --- a/packages/feedback/src/modal/components/Form.tsx +++ b/packages/feedback/src/modal/components/Form.tsx @@ -16,6 +16,8 @@ import { getMissingFields } from '../../util/validate'; export interface Props extends Pick< FeedbackInternalOptions, + | 'addScreenshotButtonLabel' + | 'removeScreenshotButtonLabel' | 'cancelButtonLabel' | 'emailLabel' | 'emailPlaceholder' @@ -28,7 +30,7 @@ export interface Props | 'showEmail' | 'showName' | 'submitButtonLabel' - | 'isRequiredText' + | 'isRequiredLabel' > { defaultEmail: string; defaultName: string; @@ -48,6 +50,8 @@ function retrieveStringValue(formData: FormData, key: string): string { } export function Form({ + addScreenshotButtonLabel, + removeScreenshotButtonLabel, cancelButtonLabel, defaultEmail, defaultName, @@ -66,7 +70,7 @@ export function Form({ showEmail, showName, submitButtonLabel, - isRequiredText, + isRequiredLabel, screenshotInput, }: Props): VNode { // TODO: set a ref on the form, and whenever an input changes call proceessForm() and setError() @@ -159,7 +163,7 @@ export function Form({ {showName ? ( - + - + - + - Screenshot - !prev); }} > - {showScreenshotInput ? 'Remove' : 'Add'} + {showScreenshotInput ? removeScreenshotButtonLabel : addScreenshotButtonLabel} {screenshotError ? {screenshotError.message} : null} @@ -238,12 +240,12 @@ export function Form({ function LabelText({ label, isRequired, - isRequiredText, -}: { label: string; isRequired: boolean; isRequiredText: string }): VNode { + isRequiredLabel, +}: { label: string; isRequired: boolean; isRequiredLabel: string }): VNode { return ( {label} - {isRequired && {isRequiredText}} + {isRequired && {isRequiredLabel}} ); } diff --git a/packages/feedback/src/modal/integration.tsx b/packages/feedback/src/modal/integration.tsx index cf5048c53619..2cf18481319b 100644 --- a/packages/feedback/src/modal/integration.tsx +++ b/packages/feedback/src/modal/integration.tsx @@ -45,7 +45,7 @@ export const feedbackModalIntegration = ((): FeedbackModalIntegration => { }, }; - const screenshotInput = screenshotIntegration && screenshotIntegration.createInput(h, dialog); + const screenshotInput = screenshotIntegration && screenshotIntegration.createInput(h, dialog, options); const renderContent = (open: boolean): void => { render( @@ -68,7 +68,9 @@ export const feedbackModalIntegration = ((): FeedbackModalIntegration => { defaultName={(userKey && user && user[userKey.name]) || ''} defaultEmail={(userKey && user && user[userKey.email]) || ''} successMessageText={options.successMessageText} - isRequiredText={options.isRequiredText} + isRequiredLabel={options.isRequiredLabel} + addScreenshotButtonLabel={options.addScreenshotButtonLabel} + removeScreenshotButtonLabel={options.removeScreenshotButtonLabel} onFormClose={() => { renderContent(false); options.onFormClose && options.onFormClose(); diff --git a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx index 0287b647ece0..c06fc0b9e58a 100644 --- a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx +++ b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx @@ -1,4 +1,4 @@ -import type { FeedbackDialog } from '@sentry/types'; +import type { FeedbackDialog, FeedbackInternalOptions } from '@sentry/types'; /* eslint-disable max-lines */ import type { ComponentType, VNode, h as hType } from 'preact'; // biome-ignore lint: needed for preact @@ -17,6 +17,7 @@ interface FactoryParams { h: typeof hType; imageBuffer: HTMLCanvasElement; dialog: FeedbackDialog; + options: FeedbackInternalOptions; } interface Props { @@ -61,8 +62,7 @@ const getContainedSize = (img: HTMLCanvasElement): Box => { return { startX: x, startY: y, endX: width + x, endY: height + y }; }; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: FactoryParams): ComponentType { +export function makeScreenshotEditorComponent({ imageBuffer, dialog, options }: FactoryParams): ComponentType { return function ScreenshotEditor({ onError }: Props): VNode { const styles = useMemo(() => ({ __html: createScreenshotInputStyles().innerText }), []); @@ -298,7 +298,7 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor }} class="btn btn--default" > - Cancel + {options.cancelButtonLabel} { @@ -308,7 +308,7 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor }} class="btn btn--primary" > - Confirm + {options.confirmButtonLabel} diff --git a/packages/feedback/src/screenshot/integration.ts b/packages/feedback/src/screenshot/integration.ts index f4d8a287ad96..6edb1ce6a246 100644 --- a/packages/feedback/src/screenshot/integration.ts +++ b/packages/feedback/src/screenshot/integration.ts @@ -1,4 +1,9 @@ -import type { FeedbackDialog, FeedbackScreenshotIntegration, IntegrationFn } from '@sentry/types'; +import type { + FeedbackDialog, + FeedbackInternalOptions, + FeedbackScreenshotIntegration, + IntegrationFn, +} from '@sentry/types'; import type { Attachment } from '@sentry/types'; import type { h as hType } from 'preact'; import { DOCUMENT } from '../constants'; @@ -9,12 +14,12 @@ export const feedbackScreenshotIntegration = ((): FeedbackScreenshotIntegration name: 'FeedbackScreenshot', // eslint-disable-next-line @typescript-eslint/no-empty-function setupOnce() {}, - createInput: (h: unknown, dialog: FeedbackDialog) => { + createInput: (h: unknown, dialog: FeedbackDialog, options: FeedbackInternalOptions) => { const imageBuffer = DOCUMENT.createElement('canvas'); return { // eslint-disable-next-line @typescript-eslint/no-explicit-any - input: makeScreenshotEditorComponent({ h: h as typeof hType, imageBuffer, dialog }) as any, + input: makeScreenshotEditorComponent({ h: h as typeof hType, imageBuffer, dialog, options }) as any, value: async () => { const blob = await new Promise[0]>(resolve => { diff --git a/packages/types/src/feedback/config.ts b/packages/types/src/feedback/config.ts index 97c0465b7392..2c98376939a2 100644 --- a/packages/types/src/feedback/config.ts +++ b/packages/types/src/feedback/config.ts @@ -84,7 +84,7 @@ export interface FeedbackTextConfiguration { /** * The label for the Feedback widget button that opens the dialog */ - buttonLabel: string; + triggerLabel: string; /** * The label for the Feedback form cancel button that closes dialog @@ -96,6 +96,11 @@ export interface FeedbackTextConfiguration { */ submitButtonLabel: string; + /** + * The label for the Screenshot editor cancel buttons + */ + confirmButtonLabel: string; + /** * The title of the Feedback form */ @@ -139,7 +144,17 @@ export interface FeedbackTextConfiguration { /** * Text which indicates that a field is required */ - isRequiredText: string; + isRequiredLabel: string; + + /** + * The label for the button that adds a screeshot and renders the image editor + */ + addScreenshotButtonLabel: string; + + /** + * The label for the button that removes a screenshot and hides the image editor + */ + removeScreenshotButtonLabel: string; } /** diff --git a/packages/types/src/feedback/index.ts b/packages/types/src/feedback/index.ts index 477ae46d8f92..aa34d1a574b7 100644 --- a/packages/types/src/feedback/index.ts +++ b/packages/types/src/feedback/index.ts @@ -69,6 +69,7 @@ export interface FeedbackScreenshotIntegration extends Integration { createInput: ( h: HType, dialog: FeedbackDialog, + options: FeedbackInternalOptions, ) => { /** * The preact component