mirror of
https://github.com/formbricks/formbricks.git
synced 2026-01-31 12:23:33 -06:00
Compare commits
2 Commits
4.6.0-rc.1
...
typeerror-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
520c337748 | ||
|
|
7f5c93b629 |
@@ -19,6 +19,7 @@ import { TwoFactorBackup } from "@/modules/ee/two-factor-auth/components/two-fac
|
||||
import { Button } from "@/modules/ui/components/button";
|
||||
import { FormControl, FormError, FormField, FormItem } from "@/modules/ui/components/form";
|
||||
import { PasswordInput } from "@/modules/ui/components/password-input";
|
||||
import { safeFormRequestSubmit } from "@/modules/ui/lib/utils";
|
||||
|
||||
const ZLoginForm = z.object({
|
||||
email: z.string().email(),
|
||||
@@ -236,7 +237,7 @@ export const LoginForm = ({
|
||||
// Add a slight delay before focusing the input field to ensure it's visible
|
||||
setTimeout(() => emailRef.current?.focus(), 100);
|
||||
} else if (formRef.current) {
|
||||
formRef.current.requestSubmit();
|
||||
safeFormRequestSubmit(formRef.current);
|
||||
}
|
||||
}}
|
||||
className="relative w-full justify-center"
|
||||
|
||||
@@ -4,3 +4,27 @@ import { twMerge } from "tailwind-merge";
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely requests form submission with validation.
|
||||
* Provides a fallback for browsers that don't support requestSubmit() (iOS Safari < 16.0).
|
||||
* @param form The form element to submit
|
||||
*/
|
||||
export function safeFormRequestSubmit(form: HTMLFormElement): void {
|
||||
// Check if requestSubmit is supported (iOS Safari 16.0+, all modern browsers)
|
||||
if (typeof form.requestSubmit === "function") {
|
||||
form.requestSubmit();
|
||||
} else {
|
||||
// Fallback for older browsers (iOS Safari < 16.0)
|
||||
// reportValidity() triggers native validation UI
|
||||
if (!form.reportValidity()) {
|
||||
return;
|
||||
}
|
||||
// Dispatch submit event manually to trigger form submission handlers
|
||||
const submitEvent = new Event("submit", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
form.dispatchEvent(submitEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import { SubmitButton } from "@/components/buttons/submit-button";
|
||||
import { ElementConditional } from "@/components/general/element-conditional";
|
||||
import { ScrollableContainer } from "@/components/wrappers/scrollable-container";
|
||||
import { getLocalizedValue } from "@/lib/i18n";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { cn, safeFormRequestSubmit } from "@/lib/utils";
|
||||
|
||||
interface BlockConditionalProps {
|
||||
block: TSurveyBlock;
|
||||
@@ -141,7 +141,7 @@ export function BlockConditional({
|
||||
response.length < rankingElement.choices.length);
|
||||
|
||||
if (hasIncompleteRanking) {
|
||||
form.requestSubmit();
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -174,7 +174,7 @@ export function BlockConditional({
|
||||
element.type === TSurveyElementTypeEnum.ContactInfo
|
||||
) {
|
||||
if (!form.checkValidity()) {
|
||||
form.requestSubmit();
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -191,14 +191,14 @@ export function BlockConditional({
|
||||
response &&
|
||||
hasUnansweredRows(response, element)
|
||||
) {
|
||||
form.requestSubmit();
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
|
||||
// For other element types, check if required fields are empty
|
||||
// CTA elements should not block navigation even if marked required (as they are informational)
|
||||
if (element.type !== TSurveyElementTypeEnum.CTA && element.required && isEmptyResponse(response)) {
|
||||
form.requestSubmit();
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -230,7 +230,7 @@ export function BlockConditional({
|
||||
block.elements.forEach((element) => {
|
||||
const form = elementFormRefs.current.get(element.id);
|
||||
if (form) {
|
||||
form.requestSubmit();
|
||||
safeFormRequestSubmit(form);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
getMimeType,
|
||||
getShuffledChoicesIds,
|
||||
getShuffledRowIndices,
|
||||
safeFormRequestSubmit,
|
||||
} from "./utils";
|
||||
|
||||
// Mock crypto.getRandomValues for deterministic shuffle tests
|
||||
@@ -327,3 +328,54 @@ describe("findBlockByElementId", () => {
|
||||
expect(block).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe("safeFormRequestSubmit", () => {
|
||||
let mockForm: HTMLFormElement;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create a mock form element
|
||||
mockForm = document.createElement("form");
|
||||
});
|
||||
|
||||
test("should call requestSubmit when it's supported", () => {
|
||||
// Mock requestSubmit as a function
|
||||
const requestSubmitSpy = vi.fn();
|
||||
mockForm.requestSubmit = requestSubmitSpy;
|
||||
|
||||
safeFormRequestSubmit(mockForm);
|
||||
|
||||
expect(requestSubmitSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("should use fallback when requestSubmit is not supported", () => {
|
||||
// Remove requestSubmit to simulate iOS Safari 15.5
|
||||
mockForm.requestSubmit = undefined as unknown as typeof mockForm.requestSubmit;
|
||||
|
||||
const reportValiditySpy = vi.spyOn(mockForm, "reportValidity").mockReturnValue(true);
|
||||
const dispatchEventSpy = vi.spyOn(mockForm, "dispatchEvent");
|
||||
|
||||
safeFormRequestSubmit(mockForm);
|
||||
|
||||
expect(reportValiditySpy).toHaveBeenCalled();
|
||||
expect(dispatchEventSpy).toHaveBeenCalled();
|
||||
|
||||
// Verify the submit event was dispatched with correct properties
|
||||
const dispatchedEvent = dispatchEventSpy.mock.calls[0][0];
|
||||
expect(dispatchedEvent.type).toBe("submit");
|
||||
expect(dispatchedEvent.bubbles).toBe(true);
|
||||
expect(dispatchedEvent.cancelable).toBe(true);
|
||||
});
|
||||
|
||||
test("should not dispatch event when reportValidity returns false", () => {
|
||||
// Remove requestSubmit to simulate iOS Safari 15.5
|
||||
mockForm.requestSubmit = undefined as unknown as typeof mockForm.requestSubmit;
|
||||
|
||||
const reportValiditySpy = vi.spyOn(mockForm, "reportValidity").mockReturnValue(false);
|
||||
const dispatchEventSpy = vi.spyOn(mockForm, "dispatchEvent");
|
||||
|
||||
safeFormRequestSubmit(mockForm);
|
||||
|
||||
expect(reportValiditySpy).toHaveBeenCalled();
|
||||
expect(dispatchEventSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -275,3 +275,27 @@ export const getFirstElementIdInBlock = (
|
||||
const block = survey.blocks.find((b) => b.id === blockId);
|
||||
return block?.elements[0]?.id;
|
||||
};
|
||||
|
||||
/**
|
||||
* Safely requests form submission with validation.
|
||||
* Provides a fallback for browsers that don't support requestSubmit() (iOS Safari < 16.0).
|
||||
* @param form The form element to submit
|
||||
*/
|
||||
export const safeFormRequestSubmit = (form: HTMLFormElement): void => {
|
||||
// Check if requestSubmit is supported (iOS Safari 16.0+, all modern browsers)
|
||||
if (typeof form.requestSubmit === "function") {
|
||||
form.requestSubmit();
|
||||
} else {
|
||||
// Fallback for older browsers (iOS Safari < 16.0)
|
||||
// reportValidity() triggers native validation UI
|
||||
if (!form.reportValidity()) {
|
||||
return;
|
||||
}
|
||||
// Dispatch submit event manually to trigger form submission handlers
|
||||
const submitEvent = new Event("submit", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
form.dispatchEvent(submitEvent);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user