From 31656361667c5ec352e5f9dd5a9e497770d8d8d2 Mon Sep 17 00:00:00 2001 From: Arthur Knaus Date: Mon, 21 Aug 2023 12:16:49 +0200 Subject: [PATCH 01/26] Add initial dialog --- gatsby-browser.tsx | 10 ++- src/components/feedbackButton.tsx | 24 ++++++ src/components/feedbackModal.tsx | 118 +++++++++++++++++++++++++++ src/components/feedbackWidget.tsx | 20 +++++ src/components/hooks/useFocusTrap.ts | 48 +++++++++++ src/components/hooks/useShortcut.ts | 35 ++++++++ 6 files changed, 254 insertions(+), 1 deletion(-) create mode 100644 src/components/feedbackButton.tsx create mode 100644 src/components/feedbackModal.tsx create mode 100644 src/components/feedbackWidget.tsx create mode 100644 src/components/hooks/useFocusTrap.ts create mode 100644 src/components/hooks/useShortcut.ts diff --git a/gatsby-browser.tsx b/gatsby-browser.tsx index 7a9b945f6e4c2..e788d0a5e737a 100644 --- a/gatsby-browser.tsx +++ b/gatsby-browser.tsx @@ -1,12 +1,20 @@ import React from 'react'; import {GatsbyBrowser} from 'gatsby'; +import {FeebdackWidget} from 'sentry-docs/components/feedbackWidget'; import PageContext from 'sentry-docs/components/pageContext'; export const wrapPageElement: GatsbyBrowser['wrapPageElement'] = ({ element, props: {pageContext}, -}) => {element}; +}) => { + return ( + + + {element} + + ); +}; // Disable prefetching altogether so our bw is not destroyed. // If this turns out to hurt performance significantly, we can diff --git a/src/components/feedbackButton.tsx b/src/components/feedbackButton.tsx new file mode 100644 index 0000000000000..f0821815156bd --- /dev/null +++ b/src/components/feedbackButton.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import styled from '@emotion/styled'; + +const Button = styled.button` + position: fixed; + right: 0px; + bottom: 50%; + transform: translate(25%, 50%) rotate(-90deg); + background-color: #fff; + border: 1px solid #ccc; + border-radius: 4px; + color: #333; + cursor: pointer; + font-size: 14px; + font-weight: 600; + padding: 6px 16px; + text-align: center; + text-decoration: none; + z-index: 9000; +`; + +export function FeebdackButton(props) { + return ; +} diff --git a/src/components/feedbackModal.tsx b/src/components/feedbackModal.tsx new file mode 100644 index 0000000000000..230ac9d0cd341 --- /dev/null +++ b/src/components/feedbackModal.tsx @@ -0,0 +1,118 @@ +import React, {FormEvent} from 'react'; +import styled from '@emotion/styled'; + +import {useFocusTrap} from './hooks/useFocusTrap'; +import {useShortcut} from './hooks/useShortcut'; + +const Dialog = styled.dialog` + background-color: rgba(0, 0, 0, 0.05); + border: none; + position: fixed; + inset: 0; + z-index: 10000; + width: 100vw; + height: 100vh; + display: flex; + align-items: center; + justify-content: center; + &:not([open]) { + opacity: 0; + pointer-events: none; + } +`; + +const Content = styled.div` + border-radius: 4px; + background-color: #fff; + padding: 24px; + width: 500px; + max-width: 100%; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 0 4px 16px rgba(0, 0, 0, 0.2); +`; + +const Form = styled.form` + display: flex; + flex-direction: column; + gap: 16px; +`; + +const Label = styled.label` + display: flex; + flex-direction: column; + gap: 4px; +`; + +const SubmitButton = styled.button` + background-color: #79628c; + border: 1px solid #ccc; + border-radius: 4px; + color: #fff; + cursor: pointer; + font-size: 14px; + font-weight: 600; + padding: 6px 16px; + align-self: flex-end; +`; + +interface FeedbackModalProps { + onClose: () => void; + onSubmit: (data: {comment: string; title: string}) => void; + open: boolean; +} + +function stopPropagation(e: React.MouseEvent) { + e.stopPropagation(); +} + +const retrieveStringValue = (formData: FormData, key: string) => { + const value = formData.get(key); + if (typeof value === 'string') { + return value.trim(); + } + return ''; +}; + +export function FeedbackModal({open, onClose, onSubmit}: FeedbackModalProps) { + const dialogRef = React.useRef(null); + const formRef = React.useRef(null); + + useFocusTrap(dialogRef, open); + + useShortcut('Escape', onClose); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + const formData = new FormData(e.target as HTMLFormElement); + onSubmit({ + comment: retrieveStringValue(formData, 'comment'), + title: retrieveStringValue(formData, 'title'), + }); + formRef.current.reset(); + }; + + return ( + + +
+ +