From 8f4f3d87285c49ba722d555ceebb25f25a0b8e1c Mon Sep 17 00:00:00 2001 From: Catherine Lee <55311782+c298lee@users.noreply.github.com> Date: Tue, 26 Mar 2024 15:40:28 -0400 Subject: [PATCH] feat(feedback): Update user feedback screenshot and cropping to align with designs (#11227) Updated some screenshot styles to align with design: - Changed cropping to more closely follow photoshop: darker background, white cropping corners + rectangle and additional black border for contrast if the screenshot is white - Changed naming to follow BES naming - Added padding so that the submit/cancel buttons are always visible without scrolling - Reduced blurriness of cropping rectangle Relates to https://github.com/getsentry/sentry/issues/63749 --------- --- packages/feedback/src/constants/index.ts | 2 + .../components/ScreenshotEditor.tsx | 80 ++++++++++--------- .../components/ScreenshotInput.css.ts | 44 +++++++--- 3 files changed, 76 insertions(+), 50 deletions(-) diff --git a/packages/feedback/src/constants/index.ts b/packages/feedback/src/constants/index.ts index 218f2e678494..fe4683b5ee17 100644 --- a/packages/feedback/src/constants/index.ts +++ b/packages/feedback/src/constants/index.ts @@ -26,3 +26,5 @@ export const FEEDBACK_WIDGET_SOURCE = 'widget'; export const FEEDBACK_API_SOURCE = 'api'; export const SUCCESS_MESSAGE_TIMEOUT = 5000; + +export const CROP_COLOR = '#ffffff'; diff --git a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx index 826d7ecb1947..f3c0e39970ce 100644 --- a/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx +++ b/packages/feedback/src/screenshot/components/ScreenshotEditor.tsx @@ -3,7 +3,7 @@ import type { ComponentType, VNode, h as hType } from 'preact'; // biome-ignore lint: needed for preact import { h } from 'preact'; // eslint-disable-line @typescript-eslint/no-unused-vars import { useCallback, useEffect, useMemo, useRef, useState } from 'preact/hooks'; -import { DOCUMENT, WINDOW } from '../../constants'; +import { CROP_COLOR, DOCUMENT, WINDOW } from '../../constants'; import type { Dialog } from '../../types'; import { createScreenshotInputStyles } from './ScreenshotInput.css'; import { useTakeScreenshot } from './useTakeScreenshot'; @@ -11,6 +11,7 @@ import { useTakeScreenshot } from './useTakeScreenshot'; const CROP_BUTTON_SIZE = 30; const CROP_BUTTON_BORDER = 3; const CROP_BUTTON_OFFSET = CROP_BUTTON_SIZE + CROP_BUTTON_BORDER; +const DPI = WINDOW.devicePixelRatio; interface FactoryParams { h: typeof hType; @@ -79,8 +80,14 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor const cropper = croppingRef.current; const imageDimensions = constructRect(getContainedSize(imageBuffer)); if (cropper) { - cropper.width = imageDimensions.width; - cropper.height = imageDimensions.height; + cropper.width = imageDimensions.width * DPI; + cropper.height = imageDimensions.height * DPI; + cropper.style.width = `${imageDimensions.width}px`; + cropper.style.height = `${imageDimensions.height}px`; + const ctx = cropper.getContext('2d'); + if (ctx) { + ctx.scale(DPI, DPI); + } } const cropButton = cropContainerRef.current; @@ -104,9 +111,9 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor if (!ctx) { return; } + const imageDimensions = constructRect(getContainedSize(imageBuffer)); const croppingBox = constructRect(croppingRect); - ctx.clearRect(0, 0, imageDimensions.width, imageDimensions.height); // draw gray overlay around the selection @@ -115,9 +122,12 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor ctx.clearRect(croppingBox.x, croppingBox.y, croppingBox.width, croppingBox.height); // draw selection border - ctx.strokeStyle = 'purple'; + ctx.strokeStyle = CROP_COLOR; ctx.lineWidth = 3; - ctx.strokeRect(croppingBox.x, croppingBox.y, croppingBox.width, croppingBox.height); + ctx.strokeRect(croppingBox.x + 1, croppingBox.y + 1, croppingBox.width - 2, croppingBox.height - 2); + ctx.strokeStyle = '#000000'; + ctx.lineWidth = 1; + ctx.strokeRect(croppingBox.x + 3, croppingBox.y + 3, croppingBox.width - 6, croppingBox.height - 6); }, [croppingRect]); function onGrabButton(e: Event, corner: string): void { @@ -143,32 +153,32 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor const mouseX = e.clientX - cropBoundingRect.x; const mouseY = e.clientY - cropBoundingRect.y; switch (corner) { - case 'topleft': + case 'top-left': setCroppingRect(prev => ({ ...prev, startX: Math.min(Math.max(0, mouseX), prev.endX - CROP_BUTTON_OFFSET), startY: Math.min(Math.max(0, mouseY), prev.endY - CROP_BUTTON_OFFSET), })); break; - case 'topright': + case 'top-right': setCroppingRect(prev => ({ ...prev, - endX: Math.max(Math.min(mouseX, cropCanvas.width), prev.startX + CROP_BUTTON_OFFSET), + endX: Math.max(Math.min(mouseX, cropCanvas.width / DPI), prev.startX + CROP_BUTTON_OFFSET), startY: Math.min(Math.max(0, mouseY), prev.endY - CROP_BUTTON_OFFSET), })); break; - case 'bottomleft': + case 'bottom-left': setCroppingRect(prev => ({ ...prev, startX: Math.min(Math.max(0, mouseX), prev.endX - CROP_BUTTON_OFFSET), - endY: Math.max(Math.min(mouseY, cropCanvas.height), prev.startY + CROP_BUTTON_OFFSET), + endY: Math.max(Math.min(mouseY, cropCanvas.height / DPI), prev.startY + CROP_BUTTON_OFFSET), })); break; - case 'bottomright': + case 'bottom-right': setCroppingRect(prev => ({ ...prev, - endX: Math.max(Math.min(mouseX, cropCanvas.width), prev.startX + CROP_BUTTON_OFFSET), - endY: Math.max(Math.min(mouseY, cropCanvas.height), prev.startY + CROP_BUTTON_OFFSET), + endX: Math.max(Math.min(mouseX, cropCanvas.width / DPI), prev.startX + CROP_BUTTON_OFFSET), + endY: Math.max(Math.min(mouseY, cropCanvas.height / DPI), prev.startY + CROP_BUTTON_OFFSET), })); break; } @@ -238,32 +248,32 @@ export function makeScreenshotEditorComponent({ h, imageBuffer, dialog }: Factor return (