fix: unknown property warnings (#5800)

This commit is contained in:
Dhruwang Jariwala
2025-05-16 19:15:48 +05:30
committed by GitHub
parent 022d33d06f
commit 7678084061
5 changed files with 405 additions and 27 deletions

View File

@@ -7,39 +7,133 @@ export const GET = async (req: NextRequest) => {
return new ImageResponse(
(
<div tw={`flex flex-col w-full h-full items-center bg-[${brandColor}]/75 rounded-xl `}>
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
height: "100%",
alignItems: "center",
backgroundColor: brandColor ? brandColor + "BF" : "#0000BFBF", // /75 opacity is approximately BF in hex
borderRadius: "0.75rem",
}}>
<div
tw="flex flex-col w-[80%] h-[60%] bg-white rounded-xl mt-13 absolute left-12 top-3 opacity-20"
style={{
display: "flex",
flexDirection: "column",
width: "80%",
height: "60%",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "3.25rem",
position: "absolute",
left: "3rem",
top: "0.75rem",
opacity: 0.2,
transform: "rotate(356deg)",
}}></div>
<div
tw="flex flex-col w-[84%] h-[60%] bg-white rounded-xl mt-12 absolute top-5 left-13 border-2 opacity-60"
style={{
display: "flex",
flexDirection: "column",
width: "84%",
height: "60%",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "3rem",
position: "absolute",
top: "1.25rem",
left: "3.25rem",
borderWidth: "2px",
opacity: 0.6,
transform: "rotate(357deg)",
}}></div>
<div
tw="flex flex-col w-[85%] h-[67%] items-center bg-white rounded-xl mt-8 absolute top-[2.3rem] left-14"
style={{
display: "flex",
flexDirection: "column",
width: "85%",
height: "67%",
alignItems: "center",
backgroundColor: "white",
borderRadius: "0.75rem",
marginTop: "2rem",
position: "absolute",
top: "2.3rem",
left: "3.5rem",
transform: "rotate(360deg)",
}}>
<div tw="flex flex-col w-full">
<div tw="flex flex-col md:flex-row w-full md:items-center justify-between ">
<div tw="flex flex-col px-8">
<h2 tw="flex flex-col text-[8] sm:text-4xl font-bold tracking-tight text-slate-900 text-left mt-15">
<div style={{ display: "flex", flexDirection: "column", width: "100%" }}>
<div
style={{
display: "flex",
flexDirection: "column",
width: "100%",
justifyContent: "space-between",
}}>
<div
style={{
display: "flex",
flexDirection: "column",
paddingLeft: "2rem",
paddingRight: "2rem",
}}>
<h2
style={{
display: "flex",
flexDirection: "column",
fontSize: "2rem",
fontWeight: "700",
letterSpacing: "-0.025em",
color: "#0f172a",
textAlign: "left",
marginTop: "3.75rem",
}}>
{name}
</h2>
</div>
</div>
<div tw="flex justify-end mr-10 ">
<div tw="flex rounded-2xl absolute -right-2 mt-2">
<a tw={`rounded-xl border border-transparent bg-[${brandColor}] h-18 w-38 opacity-50`}></a>
<div style={{ display: "flex", justifyContent: "flex-end", marginRight: "2.5rem" }}>
<div
style={{
display: "flex",
borderRadius: "1rem",
position: "absolute",
right: "-0.5rem",
marginTop: "0.5rem",
}}>
<div
content=""
style={{
borderRadius: "0.75rem",
border: "1px solid transparent",
backgroundColor: brandColor ?? "#000",
height: "4.5rem",
width: "9.5rem",
opacity: 0.5,
}}></div>
</div>
<div tw="flex rounded-2xl shadow ">
<a
tw={`flex items-center justify-center rounded-xl border border-transparent bg-[${brandColor}] text-2xl text-white h-18 w-38`}>
<div
style={{
display: "flex",
borderRadius: "1rem",
boxShadow: "0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)",
}}>
<div
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
borderRadius: "0.75rem",
border: "1px solid transparent",
backgroundColor: brandColor ?? "#000",
fontSize: "1.5rem",
color: "white",
height: "4.5rem",
width: "9.5rem",
}}>
Begin!
</a>
</div>
</div>
</div>
</div>

View File

@@ -4,8 +4,8 @@ export const NetPromoterScoreIcon: React.FC<React.SVGProps<SVGSVGElement>> = (pr
<g id="Frame">
<path
id="Vector"
fill-rule="evenodd"
clip-rule="evenodd"
fillRule="evenodd"
clipRule="evenodd"
d="M2.25 2.25C2.05109 2.25 1.86032 2.32902 1.71967 2.46967C1.57902 2.61032 1.5 2.80109 1.5 3C1.5 3.19891 1.57902 3.38968 1.71967 3.53033C1.86032 3.67098 2.05109 3.75 2.25 3.75H3V14.25C3 15.0456 3.31607 15.8087 3.87868 16.3713C4.44129 16.9339 5.20435 17.25 6 17.25H7.21L6.038 20.763C5.97514 20.9518 5.98988 21.1579 6.07896 21.3359C6.16804 21.5138 6.32417 21.6491 6.513 21.712C6.70183 21.7749 6.9079 21.7601 7.08588 21.671C7.26385 21.582 7.39914 21.4258 7.462 21.237L7.791 20.25H16.209L16.539 21.237C16.6073 21.4186 16.7433 21.5666 16.9184 21.6501C17.0935 21.7335 17.2941 21.7459 17.4782 21.6845C17.6622 21.6232 17.8153 21.4929 17.9053 21.3211C17.9954 21.1493 18.0153 20.9492 17.961 20.763L16.791 17.25H18C18.7956 17.25 19.5587 16.9339 20.1213 16.3713C20.6839 15.8087 21 15.0456 21 14.25V3.75H21.75C21.9489 3.75 22.1397 3.67098 22.2803 3.53033C22.421 3.38968 22.5 3.19891 22.5 3C22.5 2.80109 22.421 2.61032 22.2803 2.46967C22.1397 2.32902 21.9489 2.25 21.75 2.25H2.25ZM8.29 18.75L8.79 17.25H15.21L15.71 18.75H8.29ZM15.75 6.75C15.75 6.55109 15.671 6.36032 15.5303 6.21967C15.3897 6.07902 15.1989 6 15 6C14.8011 6 14.6103 6.07902 14.4697 6.21967C14.329 6.36032 14.25 6.55109 14.25 6.75V12.75C14.25 12.9489 14.329 13.1397 14.4697 13.2803C14.6103 13.421 14.8011 13.5 15 13.5C15.1989 13.5 15.3897 13.421 15.5303 13.2803C15.671 13.1397 15.75 12.9489 15.75 12.75V6.75ZM12.75 9C12.75 8.80109 12.671 8.61032 12.5303 8.46967C12.3897 8.32902 12.1989 8.25 12 8.25C11.8011 8.25 11.6103 8.32902 11.4697 8.46967C11.329 8.61032 11.25 8.80109 11.25 9V12.75C11.25 12.9489 11.329 13.1397 11.4697 13.2803C11.6103 13.421 11.8011 13.5 12 13.5C12.1989 13.5 12.3897 13.421 12.5303 13.2803C12.671 13.1397 12.75 12.9489 12.75 12.75V9ZM9.75 11.25C9.75 11.0511 9.67098 10.8603 9.53033 10.7197C9.38968 10.579 9.19891 10.5 9 10.5C8.80109 10.5 8.61032 10.579 8.46967 10.7197C8.32902 10.8603 8.25 11.0511 8.25 11.25V12.75C8.25 12.9489 8.32902 13.1397 8.46967 13.2803C8.61032 13.421 8.80109 13.5 9 13.5C9.19891 13.5 9.38968 13.421 9.53033 13.2803C9.67098 13.1397 9.75 12.9489 9.75 12.75V11.25Z"
fill="white"
/>

View File

@@ -53,7 +53,7 @@ export function QuestionMedia({ imgUrl, videoUrl, altText = "Image" }: QuestionM
setIsLoading(false);
}}
allow="accelerometer; autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"
referrerpolicy="strict-origin-when-cross-origin"
referrerPolicy="strict-origin-when-cross-origin"
/>
</div>
</div>

View File

@@ -1,5 +1,5 @@
import "@testing-library/jest-dom/vitest";
import { cleanup, render, screen } from "@testing-library/preact";
import { cleanup, fireEvent, render, screen } from "@testing-library/preact";
import userEvent from "@testing-library/user-event";
import { afterEach, describe, expect, test, vi } from "vitest";
import { type TSurveyOpenTextQuestion, TSurveyQuestionTypeEnum } from "@formbricks/types/surveys/types";
@@ -76,10 +76,7 @@ describe("OpenTextQuestion", () => {
render(<OpenTextQuestion {...defaultProps} onChange={onChange} />);
const input = screen.getByPlaceholderText("Type here...");
// Directly set the input value and trigger the input event
Object.defineProperty(input, "value", { value: "Hello" });
input.dispatchEvent(new Event("input", { bubbles: true }));
fireEvent.input(input, { target: { value: "Hello" } });
expect(onChange).toHaveBeenCalledWith({ q1: "Hello" });
});
@@ -163,4 +160,291 @@ describe("OpenTextQuestion", () => {
expect(focusMock).toHaveBeenCalled();
});
test("handles input change for textarea with resize functionality", async () => {
// Create a spy on the Element.prototype to monitor style changes
const styleSpy = vi.spyOn(HTMLElement.prototype, "style", "get").mockImplementation(
() =>
({
height: "",
overflow: "",
}) as CSSStyleDeclaration
);
const onChange = vi.fn();
render(
<OpenTextQuestion
{...defaultProps}
onChange={onChange}
question={{ ...defaultQuestion, longAnswer: true }}
/>
);
const textarea = screen.getByRole("textbox");
// Only trigger a regular input event without trying to modify scrollHeight
fireEvent.input(textarea, { target: { value: "Test value for textarea" } });
// Check that onChange was called with the correct value
expect(onChange).toHaveBeenCalledWith({ q1: "Test value for textarea" });
// Clean up the spy
styleSpy.mockRestore();
});
test("handles textarea resize with different heights", async () => {
// Mock styles and scrollHeight for handleInputResize testing
let heightValue = "";
let overflowValue = "";
// Mock style setter to capture values
const originalSetProperty = CSSStyleDeclaration.prototype.setProperty;
CSSStyleDeclaration.prototype.setProperty = vi.fn();
// Mock to capture style changes
Object.defineProperty(HTMLElement.prototype, "style", {
get: vi.fn(() => ({
height: heightValue,
overflow: overflowValue,
setProperty: (prop: string, value: string) => {
if (prop === "height") heightValue = value;
if (prop === "overflow") overflowValue = value;
},
})),
});
const onChange = vi.fn();
render(
<OpenTextQuestion
{...defaultProps}
onChange={onChange}
question={{ ...defaultQuestion, longAnswer: true }}
/>
);
const textarea = screen.getByRole("textbox");
// Simulate normal height (less than max)
const mockNormalEvent = {
target: {
style: { height: "", overflow: "" },
scrollHeight: 100, // Less than max 160px
},
};
// Get the event handler
const inputHandler = textarea.oninput as EventListener;
if (inputHandler) {
inputHandler(mockNormalEvent as unknown as Event);
}
// Now simulate text that exceeds max height
const mockOverflowEvent = {
target: {
style: { height: "", overflow: "" },
scrollHeight: 200, // More than max 160px
},
};
if (inputHandler) {
inputHandler(mockOverflowEvent as unknown as Event);
}
// Restore the original method
CSSStyleDeclaration.prototype.setProperty = originalSetProperty;
});
test("handles form submission by enter key", async () => {
const onSubmit = vi.fn();
const setTtc = vi.fn();
const { container } = render(
<OpenTextQuestion {...defaultProps} value="Test submission" onSubmit={onSubmit} setTtc={setTtc} />
);
// Get the form element using container query
const form = container.querySelector("form");
expect(form).toBeInTheDocument();
// Simulate form submission
fireEvent.submit(form!);
expect(onSubmit).toHaveBeenCalledWith({ q1: "Test submission" }, {});
expect(setTtc).toHaveBeenCalled();
});
test("applies minLength constraint when configured", () => {
render(
<OpenTextQuestion
{...defaultProps}
question={{ ...defaultQuestion, charLimit: { min: 5, max: 100 } }}
/>
);
const input = screen.getByPlaceholderText("Type here...");
expect(input).toHaveAttribute("minLength", "5");
expect(input).toHaveAttribute("maxLength", "100");
});
test("handles video URL in media", () => {
render(
<OpenTextQuestion
{...defaultProps}
question={{ ...defaultQuestion, videoUrl: "https://example.com/video.mp4" }}
/>
);
expect(screen.getByTestId("question-media")).toBeInTheDocument();
});
test("doesn't autofocus when not current question", () => {
const focusMock = vi.fn();
window.HTMLElement.prototype.focus = focusMock;
render(
<OpenTextQuestion
{...defaultProps}
autoFocusEnabled={true}
currentQuestionId="q2" // Different from question id (q1)
/>
);
expect(focusMock).not.toHaveBeenCalled();
});
test("handles input change for textarea", async () => {
const onChange = vi.fn();
render(
<OpenTextQuestion
{...defaultProps}
onChange={onChange}
question={{ ...defaultQuestion, longAnswer: true }}
/>
);
const textarea = screen.getByRole("textbox");
fireEvent.input(textarea, { target: { value: "Long text response" } });
expect(onChange).toHaveBeenCalledWith({ q1: "Long text response" });
});
test("applies phone number maxLength constraint", () => {
render(<OpenTextQuestion {...defaultProps} question={{ ...defaultQuestion, inputType: "phone" }} />);
const input = screen.getByPlaceholderText("Type here...");
expect(input).toHaveAttribute("maxLength", "30");
});
test("renders without subheader when not provided", () => {
const questionWithoutSubheader = {
...defaultQuestion,
subheader: undefined,
};
render(<OpenTextQuestion {...defaultProps} question={questionWithoutSubheader} />);
expect(screen.getByTestId("mock-subheader")).toHaveTextContent("");
});
test("sets correct tabIndex based on current question status", () => {
// When it's the current question
render(<OpenTextQuestion {...defaultProps} currentQuestionId="q1" />);
const inputCurrent = screen.getByPlaceholderText("Type here...");
const submitCurrent = screen.getByRole("button", { name: "Submit" });
expect(inputCurrent).toHaveAttribute("tabIndex", "0");
expect(submitCurrent).toHaveAttribute("tabIndex", "0");
// When it's not the current question
cleanup();
render(<OpenTextQuestion {...defaultProps} currentQuestionId="q2" />);
const inputNotCurrent = screen.getByPlaceholderText("Type here...");
const submitNotCurrent = screen.getByRole("button", { name: "Submit" });
expect(inputNotCurrent).toHaveAttribute("tabIndex", "-1");
expect(submitNotCurrent).toHaveAttribute("tabIndex", "-1");
});
test("applies title attribute for phone input in textarea", () => {
render(
<OpenTextQuestion
{...defaultProps}
question={{
...defaultQuestion,
longAnswer: true,
inputType: "phone",
}}
/>
);
const textarea = screen.getByRole("textbox");
expect(textarea).toHaveAttribute("title", "Please enter a valid phone number");
});
test("applies character limits for textarea", () => {
render(
<OpenTextQuestion
{...defaultProps}
question={{
...defaultQuestion,
longAnswer: true,
inputType: "text",
charLimit: { min: 10, max: 200 },
}}
/>
);
const textarea = screen.getByRole("textbox");
expect(textarea).toHaveAttribute("minLength", "10");
expect(textarea).toHaveAttribute("maxLength", "200");
});
test("renders input with no maxLength for other input types", () => {
render(
<OpenTextQuestion
{...defaultProps}
question={{
...defaultQuestion,
inputType: "email",
}}
/>
);
const input = screen.getByPlaceholderText("Type here...");
// Should be undefined for non-text, non-phone types
expect(input).not.toHaveAttribute("maxLength");
});
test("applies autofocus attribute to textarea when enabled", () => {
render(
<OpenTextQuestion
{...defaultProps}
autoFocusEnabled={true}
question={{
...defaultQuestion,
longAnswer: true,
}}
/>
);
const textarea = screen.getByRole("textbox");
expect(textarea).toHaveAttribute("autoFocus");
});
test("does not apply autofocus attribute to textarea when not current question", () => {
render(
<OpenTextQuestion
{...defaultProps}
autoFocusEnabled={true}
currentQuestionId="q2" // different from question.id (q1)
question={{
...defaultQuestion,
longAnswer: true,
}}
/>
);
const textarea = screen.getByRole("textbox");
expect(textarea).not.toHaveAttribute("autoFocus");
});
});

View File

@@ -115,8 +115,8 @@ export function OpenTextQuestion({
className="fb-border-border placeholder:fb-text-placeholder fb-text-subheading focus:fb-border-brand fb-bg-input-bg fb-rounded-custom fb-block fb-w-full fb-border fb-p-2 fb-shadow-sm focus:fb-outline-none focus:fb-ring-0 sm:fb-text-sm"
pattern={question.inputType === "phone" ? "^[0-9+][0-9+\\- ]*[0-9]$" : ".*"}
title={question.inputType === "phone" ? "Enter a valid phone number" : undefined}
minlength={question.inputType === "text" ? question.charLimit?.min : undefined}
maxlength={
minLength={question.inputType === "text" ? question.charLimit?.min : undefined}
maxLength={
question.inputType === "text"
? question.charLimit?.max
: question.inputType === "phone"
@@ -143,8 +143,8 @@ export function OpenTextQuestion({
}}
className="fb-border-border placeholder:fb-text-placeholder fb-bg-input-bg fb-text-subheading focus:fb-border-brand fb-rounded-custom fb-block fb-w-full fb-border fb-p-2 fb-shadow-sm focus:fb-ring-0 sm:fb-text-sm"
title={question.inputType === "phone" ? "Please enter a valid phone number" : undefined}
minlength={question.inputType === "text" ? question.charLimit?.min : undefined}
maxlength={question.inputType === "text" ? question.charLimit?.max : undefined}
minLength={question.inputType === "text" ? question.charLimit?.min : undefined}
maxLength={question.inputType === "text" ? question.charLimit?.max : undefined}
/>
)}
{question.inputType === "text" && question.charLimit?.max !== undefined && (