mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-03-22 05:37:20 +08:00
refactor: show confirmation modal on homepage after form submit
- Update form to navigate to '/' with state { showRequestSubmittedModal: true }
- Update HomeScreen to check location state and show modal
- Modal renders on homepage, not on form page
- Clear location state after modal closes to prevent re-showing on refresh
- Update tests for new navigation flow
Flow:
1. User fills form and clicks Submit
2. Navigate to homepage with state
3. Homepage renders modal overlay
4. User clicks Done/Close -> modal closes, state cleared
Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
@@ -180,18 +180,17 @@ describe("InformationRequestForm", () => {
|
||||
expect(messageInput).toHaveAttribute("aria-invalid", "false");
|
||||
});
|
||||
|
||||
it("should not show modal when form is submitted with empty fields", async () => {
|
||||
it("should not navigate when form is submitted with empty fields", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithRouter();
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" });
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(screen.queryByTestId("request-submitted-modal")).not.toBeInTheDocument();
|
||||
expect(mockNavigate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should show modal when form is submitted with all fields filled", async () => {
|
||||
it("should navigate to homepage with modal state when form is submitted with all fields filled", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithRouter();
|
||||
|
||||
@@ -203,27 +202,9 @@ describe("InformationRequestForm", () => {
|
||||
const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" });
|
||||
await user.click(submitButton);
|
||||
|
||||
expect(screen.getByTestId("request-submitted-modal")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("should navigate to homepage when modal Done button is clicked", async () => {
|
||||
const user = userEvent.setup();
|
||||
renderWithRouter();
|
||||
|
||||
// Fill form and submit
|
||||
await user.type(screen.getByTestId("form-input-name"), "John Doe");
|
||||
await user.type(screen.getByTestId("form-input-company"), "Acme Inc");
|
||||
await user.type(screen.getByTestId("form-input-email"), "john@example.com");
|
||||
await user.type(screen.getByTestId("form-input-message"), "Hello world");
|
||||
|
||||
const submitButton = screen.getByRole("button", { name: "ENTERPRISE$FORM_SUBMIT" });
|
||||
await user.click(submitButton);
|
||||
|
||||
// Click Done on modal
|
||||
const doneButton = screen.getByRole("button", { name: "ENTERPRISE$DONE_BUTTON" });
|
||||
await user.click(doneButton);
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith("/");
|
||||
expect(mockNavigate).toHaveBeenCalledWith("/", {
|
||||
state: { showRequestSubmittedModal: true },
|
||||
});
|
||||
});
|
||||
|
||||
it("should have valid aria-invalid state when field has value", async () => {
|
||||
|
||||
@@ -5,7 +5,6 @@ import { I18nKey } from "#/i18n/declaration";
|
||||
import { Card } from "#/ui/card";
|
||||
import { Text } from "#/ui/typography";
|
||||
import { FormInput } from "./form-input";
|
||||
import { RequestSubmittedModal } from "./request-submitted-modal";
|
||||
import OpenHandsLogoWhite from "#/assets/branding/openhands-logo-white.svg?react";
|
||||
import CloudIcon from "#/icons/cloud-minimal.svg?react";
|
||||
import StackedIcon from "#/icons/stacked.svg?react";
|
||||
@@ -30,7 +29,6 @@ export function InformationRequestForm({
|
||||
message: "",
|
||||
});
|
||||
const [hasAttemptedSubmit, setHasAttemptedSubmit] = useState(false);
|
||||
const [showModal, setShowModal] = useState(false);
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
@@ -48,12 +46,8 @@ export function InformationRequestForm({
|
||||
}
|
||||
|
||||
// TODO: Implement actual form submission API call
|
||||
setShowModal(true);
|
||||
};
|
||||
|
||||
const handleModalClose = () => {
|
||||
setShowModal(false);
|
||||
navigate("/");
|
||||
// Navigate to homepage with state to show confirmation modal
|
||||
navigate("/", { state: { showRequestSubmittedModal: true } });
|
||||
};
|
||||
|
||||
const isSaas = requestType === "saas";
|
||||
@@ -192,8 +186,6 @@ export function InformationRequestForm({
|
||||
<Text className="text-[#8C8C8C]">{cardDescription}</Text>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{showModal && <RequestSubmittedModal onClose={handleModalClose} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from "react";
|
||||
import { PrefetchPageLinks } from "react-router";
|
||||
import { PrefetchPageLinks, useLocation, useNavigate } from "react-router";
|
||||
import { HomeHeader } from "#/components/features/home/home-header/home-header";
|
||||
import { RepoConnector } from "#/components/features/home/repo-connector";
|
||||
import { TaskSuggestions } from "#/components/features/home/tasks/task-suggestions";
|
||||
@@ -7,14 +7,23 @@ import { GitRepository } from "#/types/git";
|
||||
import { NewConversation } from "#/components/features/home/new-conversation/new-conversation";
|
||||
import { RecentConversations } from "#/components/features/home/recent-conversations/recent-conversations";
|
||||
import { HomepageCTA } from "#/components/features/home/homepage-cta";
|
||||
import { RequestSubmittedModal } from "#/components/features/onboarding/request-submitted-modal";
|
||||
import { isCTADismissed } from "#/utils/local-storage";
|
||||
import { useConfig } from "#/hooks/query/use-config";
|
||||
import { ENABLE_PROJ_USER_JOURNEY } from "#/utils/feature-flags";
|
||||
|
||||
<PrefetchPageLinks page="/conversations/:conversationId" />;
|
||||
|
||||
interface LocationState {
|
||||
showRequestSubmittedModal?: boolean;
|
||||
}
|
||||
|
||||
function HomeScreen() {
|
||||
const { data: config } = useConfig();
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
const locationState = location.state as LocationState | null;
|
||||
|
||||
const [selectedRepo, setSelectedRepo] = React.useState<GitRepository | null>(
|
||||
null,
|
||||
);
|
||||
@@ -23,8 +32,18 @@ function HomeScreen() {
|
||||
() => !isCTADismissed("homepage"),
|
||||
);
|
||||
|
||||
const [showRequestModal, setShowRequestModal] = React.useState(
|
||||
() => locationState?.showRequestSubmittedModal ?? false,
|
||||
);
|
||||
|
||||
const isSaasMode = config?.app_mode === "saas";
|
||||
|
||||
const handleModalClose = () => {
|
||||
setShowRequestModal(false);
|
||||
// Clear the state from location to prevent modal showing on refresh
|
||||
navigate(location.pathname, { replace: true, state: {} });
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
data-testid="home-screen"
|
||||
@@ -57,6 +76,8 @@ function HomeScreen() {
|
||||
<HomepageCTA setShouldShowCTA={setShouldShowCTA} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showRequestModal && <RequestSubmittedModal onClose={handleModalClose} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user