diff --git a/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx b/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx index 935cb409ab..6cf9648522 100644 --- a/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx +++ b/frontend/__tests__/components/features/onboarding/information-request-form.test.tsx @@ -2,9 +2,11 @@ import { render, screen } from "@testing-library/react"; import userEvent from "@testing-library/user-event"; import { describe, expect, it, vi, beforeEach } from "vitest"; import { createRoutesStub } from "react-router"; +import { useState } from "react"; import { InformationRequestForm, RequestType, + FormData, } from "#/components/features/onboarding/information-request-form"; // Mock useTracking @@ -15,6 +17,12 @@ vi.mock("#/hooks/use-tracking", () => ({ }), })); +// Wrapper to manage form state (needed since component is controlled) +function StatefulForm({ requestType, onBack }: { requestType: RequestType; onBack: () => void }) { + const [formData, setFormData] = useState({ name: "", company: "", email: "", message: "" }); + return ; +} + describe("InformationRequestForm", () => { const defaultProps = { requestType: "saas" as RequestType, @@ -29,7 +37,7 @@ describe("InformationRequestForm", () => { const Stub = createRoutesStub([ { path: "/", - Component: () => , + Component: () => , }, { path: "/login", diff --git a/frontend/src/components/features/onboarding/form-input.tsx b/frontend/src/components/features/onboarding/form-input.tsx index 5bc4d6b5f9..1119e05bb6 100644 --- a/frontend/src/components/features/onboarding/form-input.tsx +++ b/frontend/src/components/features/onboarding/form-input.tsx @@ -1,5 +1,7 @@ export const isValidEmail = (email: string): boolean => - /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email); + /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/.test( + email, + ); interface FormInputProps { id: string; diff --git a/frontend/src/components/features/onboarding/information-request-form.tsx b/frontend/src/components/features/onboarding/information-request-form.tsx index f3b24c702c..22670e5fcb 100644 --- a/frontend/src/components/features/onboarding/information-request-form.tsx +++ b/frontend/src/components/features/onboarding/information-request-form.tsx @@ -12,24 +12,29 @@ import StackedIcon from "#/icons/stacked.svg?react"; export type RequestType = "saas" | "self-hosted"; +export interface FormData { + name: string; + company: string; + email: string; + message: string; +} + interface InformationRequestFormProps { requestType: RequestType; + formData: FormData; + onFormDataChange: (data: FormData) => void; onBack: () => void; } export function InformationRequestForm({ requestType, + formData, + onFormDataChange, onBack, }: InformationRequestFormProps) { const { t } = useTranslation(); const navigate = useNavigate(); const { trackEnterpriseLeadFormSubmitted } = useTracking(); - const [formData, setFormData] = useState({ - name: "", - company: "", - email: "", - message: "", - }); const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); @@ -116,9 +121,7 @@ export function InformationRequestForm({ placeholder={t(I18nKey.ENTERPRISE$FORM_NAME_PLACEHOLDER)} required showError={hasAttemptedSubmit} - onChange={(value) => - setFormData((prev) => ({ ...prev, name: value })) - } + onChange={(value) => onFormDataChange({ ...formData, name: value })} /> - setFormData((prev) => ({ ...prev, company: value })) + onFormDataChange({ ...formData, company: value }) } /> @@ -142,7 +145,7 @@ export function InformationRequestForm({ required showError={hasAttemptedSubmit} onChange={(value) => - setFormData((prev) => ({ ...prev, email: value })) + onFormDataChange({ ...formData, email: value }) } /> @@ -155,7 +158,7 @@ export function InformationRequestForm({ required showError={hasAttemptedSubmit} onChange={(value) => - setFormData((prev) => ({ ...prev, message: value })) + onFormDataChange({ ...formData, message: value }) } /> diff --git a/frontend/src/routes/information-request.tsx b/frontend/src/routes/information-request.tsx index 0a02d8a589..6a7890a64d 100644 --- a/frontend/src/routes/information-request.tsx +++ b/frontend/src/routes/information-request.tsx @@ -8,6 +8,7 @@ import { BrandButton } from "#/components/features/settings/brand-button"; import { InformationRequestForm, RequestType, + FormData, } from "#/components/features/onboarding/information-request-form"; import OpenHandsLogoWhite from "#/assets/branding/openhands-logo-white.svg?react"; import CloudIcon from "#/icons/cloud-minimal.svg?react"; @@ -74,6 +75,12 @@ export default function InformationRequest() { const navigate = useNavigate(); const [selectedRequestType, setSelectedRequestType] = useState(null); + const [formData, setFormData] = useState({ + name: "", + company: "", + email: "", + message: "", + }); const handleBack = () => { navigate("/login"); @@ -110,6 +117,8 @@ export default function InformationRequest() { >