import React, { useState, useRef, useEffect } from 'react' import cx from 'classnames' import { useRouter } from 'next/router' import { ThumbsdownIcon, ThumbsupIcon } from '@primer/octicons-react' import { useTranslation } from '@/languages/components/useTranslation' import { Link } from '@/frame/components/Link' import { sendEvent } from '@/events/components/events' import { EventType } from '../types' import styles from './Survey.module.scss' enum ViewState { START = 'START', END = 'END', } enum VoteState { YES = 'YES', NO = 'NO', } type EventData = { vote: boolean token?: string comment?: string email?: string } export const Survey = () => { const { asPath, locale } = useRouter() const { t } = useTranslation('survey') const [state, setState] = useState(ViewState.START) const [voteState, setVoteState] = useState(null) const [isEmailError, setIsEmailError] = useState(false) const formRef = useRef(null) const [comment, setComment] = useState('') const [email, setEmail] = useState('') const [token, setToken] = useState('') useEffect(() => { // Always reset the form if navigating to a new page because what // you might have said or started to say belongs exclusively to // to the page you started on. setState(ViewState.START) setVoteState(null) }, [asPath]) useEffect(() => { // After the form is submitted we need to manually set the focus since we // remove the form inputs after submit. The privacy policy link is the // next focusable element in the footer so we focus that. if (state === ViewState.END) { document .querySelector( `footer a[href="/${locale}/site-policy/privacy-policies/github-privacy-statement"]`, ) ?.focus() } }, [state]) function vote(userVote: VoteState) { return () => { trackEvent(getEventData(userVote === VoteState.YES)) setVoteState(userVote) } } // Though we set `type="email"` on the email address input which gives us browser // validation of the field, that has accessibility issues (e.g. some screen // readers won't read the error message) so we need to do manual validation // ourselves. useEffect(() => { const emailRegex = /[^@\s.][^@\s]*@\[?[a-z0-9.-]+\]?\.\[?[a-z0-9.-]+\]?/i if (!email.trim() || emailRegex.test(email)) { setIsEmailError(false) } else { setIsEmailError(true) } }, [email]) function submit(evt: React.FormEvent) { evt.preventDefault() if (voteState === null) return trackEvent(getEventData(voteState === VoteState.YES)) completeSurvey() } function completeSurvey() { setState(ViewState.END) setIsEmailError(false) setComment('') } function getEventData(voteValue: boolean): EventData { return { vote: voteValue, comment, email, token, } } return (

{t`able_to_find`}

{/* Honeypot: token isn't a real field */} setToken(event.target.value)} /> {state !== ViewState.END && (
)} {state === ViewState.START && voteState && (

{t`feedback`}

)} {state === ViewState.START && voteState && ( <>

3 ? 'warn' : '')}> setEmail(event.target.value)} aria-invalid={isEmailError} {...(isEmailError ? { 'aria-describedby': 'email-input-validation' } : {})} /> {isEmailError && email.trim().length > 3 && (

{t`email_validation`}

)}
)} {state === ViewState.END && (

{t`feedback`}

)} {t`privacy_policy`}
) } function trackEvent(eventData: EventData) { // Nota bene: convert empty strings to undefined return sendEvent({ type: EventType.survey, survey_token: eventData.token || undefined, // Honeypot survey_vote: eventData.vote, survey_comment: eventData.comment || undefined, survey_email: eventData.email || undefined, }) }