mirror of
https://github.com/formbricks/formbricks.git
synced 2026-04-19 11:11:05 -05:00
Compare commits
2 Commits
typeerror-
...
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,42 +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";
|
||||
|
||||
/**
|
||||
* Safely calls requestSubmit on a form element with fallback for browsers
|
||||
* that don't support it (e.g., Mobile Safari 15.5)
|
||||
*/
|
||||
const safeRequestSubmit = (form: HTMLFormElement | null | undefined): void => {
|
||||
if (!form) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if requestSubmit is available
|
||||
if (typeof form.requestSubmit === "function") {
|
||||
try {
|
||||
form.requestSubmit();
|
||||
} catch (error) {
|
||||
// Fallback if requestSubmit throws an error
|
||||
console.warn("[Formbricks] form.requestSubmit() failed, using fallback:", error);
|
||||
dispatchSubmitEvent(form);
|
||||
}
|
||||
} else {
|
||||
// Fallback for browsers that don't support requestSubmit
|
||||
dispatchSubmitEvent(form);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fallback method to trigger form validation by dispatching a submit event
|
||||
*/
|
||||
const dispatchSubmitEvent = (form: HTMLFormElement): void => {
|
||||
const submitEvent = new Event("submit", {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
});
|
||||
form.dispatchEvent(submitEvent);
|
||||
};
|
||||
import { cn, safeFormRequestSubmit } from "@/lib/utils";
|
||||
|
||||
interface BlockConditionalProps {
|
||||
block: TSurveyBlock;
|
||||
@@ -176,7 +141,7 @@ export function BlockConditional({
|
||||
response.length < rankingElement.choices.length);
|
||||
|
||||
if (hasIncompleteRanking) {
|
||||
safeRequestSubmit(form);
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -209,7 +174,7 @@ export function BlockConditional({
|
||||
element.type === TSurveyElementTypeEnum.ContactInfo
|
||||
) {
|
||||
if (!form.checkValidity()) {
|
||||
safeRequestSubmit(form);
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
@@ -226,14 +191,14 @@ export function BlockConditional({
|
||||
response &&
|
||||
hasUnansweredRows(response, element)
|
||||
) {
|
||||
safeRequestSubmit(form);
|
||||
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)) {
|
||||
safeRequestSubmit(form);
|
||||
safeFormRequestSubmit(form);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -264,7 +229,9 @@ export function BlockConditional({
|
||||
// Call each form's submit method to trigger TTC calculation
|
||||
block.elements.forEach((element) => {
|
||||
const form = elementFormRefs.current.get(element.id);
|
||||
safeRequestSubmit(form);
|
||||
if (form) {
|
||||
safeFormRequestSubmit(form);
|
||||
}
|
||||
});
|
||||
|
||||
// Collect TTC from the ref (populated synchronously by form submissions)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
1
pnpm-lock.yaml
generated
1
pnpm-lock.yaml
generated
@@ -10249,7 +10249,6 @@ packages:
|
||||
tar@6.2.1:
|
||||
resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==}
|
||||
engines: {node: '>=10'}
|
||||
deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exhorbitant rates) by contacting i@izs.me
|
||||
|
||||
tarn@3.0.2:
|
||||
resolution: {integrity: sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==}
|
||||
|
||||
Reference in New Issue
Block a user