diff --git a/src/features/editor/utils/formHelpers.ts b/src/features/editor/utils/formHelpers.ts index e7fb173..11e2330 100644 --- a/src/features/editor/utils/formHelpers.ts +++ b/src/features/editor/utils/formHelpers.ts @@ -925,11 +925,15 @@ export const computeFormControllerPublicTokens = ( ); const mappedSubmitLabel = (mappedSubmitButton?.props as ButtonBlockProps | undefined)?.label ?? null; - // FormBlock 은 레이아웃/스타일을 가지지 않는 순수 컨트롤러이므로 - // formClassName 은 고정 기본값만 사용하고, formStyle 은 항상 빈 객체로 유지한다. + // FormBlock 은 레이아웃/스타일을 가지지 않는 순수 컨트롤러이지만, + // 배경색 커스텀 값을 허용해 form 요소 배경만 제어할 수 있도록 한다. const formClassNames = ["space-y-3"]; const formStyle: CSSProperties = {}; + if (props.backgroundColorCustom && props.backgroundColorCustom.trim() !== "") { + formStyle.backgroundColor = props.backgroundColorCustom.trim(); + } + return { fields, formClassName: formClassNames.join(" "), diff --git a/tests/unit/PublicPageRendererBlockBackground.spec.tsx b/tests/unit/PublicPageRendererBlockBackground.spec.tsx index 9314d22..eee9088 100644 --- a/tests/unit/PublicPageRendererBlockBackground.spec.tsx +++ b/tests/unit/PublicPageRendererBlockBackground.spec.tsx @@ -88,7 +88,7 @@ describe("PublicPageRenderer - 블록 배경색", () => { expect(listNoBg!.style.backgroundColor === "" || listNoBg!.style.backgroundColor === "transparent").toBe(true); }); - it("form 컨트롤러 블록은 프리뷰에서 별도 폼 래퍼를 렌더하지 않아야 한다", () => { + it("form 컨트롤러 블록은 backgroundColorCustom 이 설정된 경우에만 폼 요소에 배경색을 적용해야 한다", () => { const blocksWithBg: Block[] = [ { id: "form_with_bg", @@ -107,8 +107,10 @@ describe("PublicPageRenderer - 블록 배경색", () => { const { queryByTestId, rerender } = render(); - const formWithBg = queryByTestId("preview-form-controller"); - expect(formWithBg).toBeNull(); + const formWithBg = queryByTestId("preview-form-controller") as HTMLElement | null; + expect(formWithBg).not.toBeNull(); + // JSDOM 은 #111111 을 rgb(17, 17, 17) 형태로 노출한다. + expect(formWithBg!.style.backgroundColor).toBe("rgb(17, 17, 17)"); const blocksWithoutBg: Block[] = [ { @@ -126,8 +128,11 @@ describe("PublicPageRenderer - 블록 배경색", () => { rerender(); - const formNoBg = queryByTestId("preview-form-controller"); - expect(formNoBg).toBeNull(); + const formNoBg = queryByTestId("preview-form-controller") as HTMLElement | null; + expect(formNoBg).not.toBeNull(); + expect( + formNoBg!.style.backgroundColor === "" || formNoBg!.style.backgroundColor === "transparent", + ).toBe(true); }); it("section 블록은 backgroundColorCustom 이 설정된 경우에만 섹션 요소에 배경색이 적용되어야 한다", () => { diff --git a/tests/unit/PublicPageRendererFormSubmitMessages.spec.tsx b/tests/unit/PublicPageRendererFormSubmitMessages.spec.tsx index 4244974..12eb399 100644 --- a/tests/unit/PublicPageRendererFormSubmitMessages.spec.tsx +++ b/tests/unit/PublicPageRendererFormSubmitMessages.spec.tsx @@ -12,10 +12,10 @@ describe("PublicPageRenderer - 폼 제출 메시지", () => { cleanup(); // fetch 목 초기화 // eslint-disable-next-line @typescript-eslint/no-explicit-any - (global as any).fetch = undefined; + (globalThis as any).fetch = undefined; }); - it("FormBlock 이 있어도 프리뷰에서는 폼 컨트롤러를 렌더하지 않아야 한다 (성공 메시지 설정)", () => { + it("성공 응답 시 FormBlock 의 successMessage 를 성공 메시지로 렌더해야 한다", async () => { const blocks: Block[] = [ { id: "form_success", @@ -25,6 +25,9 @@ describe("PublicPageRenderer - 폼 제출 메시지", () => { submitTarget: "internal", successMessage: "폼 성공 메시지 (config)", errorMessage: "폼 에러 메시지 (config)", + fields: [ + { id: "f1", name: "name", label: "이름", type: "text", required: true }, + ], fieldIds: [], submitButtonId: null, } as any, @@ -38,16 +41,32 @@ describe("PublicPageRenderer - 폼 제출 메시지", () => { }), ); // eslint-disable-next-line @typescript-eslint/no-explicit-any - (global as any).fetch = fetchMock; + (globalThis as any).fetch = fetchMock; render(); - const form = screen.queryByTestId("preview-form-controller"); - expect(form).toBeNull(); - expect(fetchMock).not.toHaveBeenCalled(); + const form = screen.getByTestId("preview-form-controller") as HTMLFormElement; + expect(form).toBeTruthy(); + + const input = form.querySelector('input[name="name"]') as HTMLInputElement | null; + expect(input).not.toBeNull(); + + const submitButton = screen.getByRole("button", { name: "폼 전송" }); + expect(submitButton).toBeTruthy(); + + fireEvent.submit(form); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledTimes(1); + }); + + await waitFor(() => { + const msg = screen.getByText("폼 성공 메시지 (config)"); + expect(msg).toBeTruthy(); + }); }); - it("FormBlock 이 있어도 프리뷰에서는 폼 컨트롤러나 에러 메시지를 렌더하지 않아야 한다", () => { + it("실패 응답 시 FormBlock 의 errorMessage 를 에러 메시지로 렌더해야 한다", async () => { const blocks: Block[] = [ { id: "form_error", @@ -57,6 +76,9 @@ describe("PublicPageRenderer - 폼 제출 메시지", () => { submitTarget: "internal", successMessage: "폼 성공 메시지 (config)", errorMessage: "폼 에러 메시지 (config)", + fields: [ + { id: "f1", name: "name", label: "이름", type: "text", required: true }, + ], fieldIds: [], submitButtonId: null, } as any, @@ -70,13 +92,25 @@ describe("PublicPageRenderer - 폼 제출 메시지", () => { }), ); // eslint-disable-next-line @typescript-eslint/no-explicit-any - (global as any).fetch = fetchMock; + (globalThis as any).fetch = fetchMock; render(); - const form = screen.queryByTestId("preview-form-controller"); - expect(form).toBeNull(); - expect(fetchMock).not.toHaveBeenCalled(); - expect(screen.queryByText("폼 에러 메시지 (config)")).toBeNull(); + const form = screen.getByTestId("preview-form-controller") as HTMLFormElement; + expect(form).toBeTruthy(); + + const submitButton = screen.getByRole("button", { name: "폼 전송" }); + expect(submitButton).toBeTruthy(); + + fireEvent.submit(form); + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledTimes(1); + }); + + await waitFor(() => { + const errorMsg = screen.getByText("폼 에러 메시지 (config)"); + expect(errorMsg).toBeTruthy(); + }); }); }); diff --git a/tests/unit/formHelpers.spec.ts b/tests/unit/formHelpers.spec.ts index 73e68f4..2a996a8 100644 --- a/tests/unit/formHelpers.spec.ts +++ b/tests/unit/formHelpers.spec.ts @@ -467,9 +467,8 @@ describe("formHelpers.computeFormControllerPublicTokens", () => { expect(checkboxField.groupLabelImageUrl).toBe("https://example.com/group.png"); expect(tokens.formClassName).toBe("space-y-3"); - // FormBlock 은 이제 레이아웃/배경 스타일을 가지지 않는 순수 컨트롤러이므로, - // formStyle 은 항상 빈 객체여야 한다. - expect(tokens.formStyle).toEqual({}); + // FormBlock 은 레이아웃 정보는 가지지 않지만, backgroundColorCustom 으로 폼 래퍼 배경색만 제어할 수 있다. + expect(tokens.formStyle).toEqual({ backgroundColor: "#123456" }); expect(tokens.submitLabel).toBe("컨트롤러 버튼"); }); diff --git a/uploads/20b557a2-e4e8-4682-aa9b-bcb1cb23506d b/uploads/20b557a2-e4e8-4682-aa9b-bcb1cb23506d new file mode 100644 index 0000000..87ccacf --- /dev/null +++ b/uploads/20b557a2-e4e8-4682-aa9b-bcb1cb23506d @@ -0,0 +1 @@ +dummy-video \ No newline at end of file diff --git a/uploads/4205a274-0ee6-443d-963f-4714e64b9c06 b/uploads/4205a274-0ee6-443d-963f-4714e64b9c06 new file mode 100644 index 0000000..87ccacf --- /dev/null +++ b/uploads/4205a274-0ee6-443d-963f-4714e64b9c06 @@ -0,0 +1 @@ +dummy-video \ No newline at end of file diff --git a/uploads/test-image-1764642023862 b/uploads/test-image-1764642023862 new file mode 100644 index 0000000..0cc5db2 --- /dev/null +++ b/uploads/test-image-1764642023862 @@ -0,0 +1 @@ +dummy-image \ No newline at end of file diff --git a/uploads/test-image-1764642096038 b/uploads/test-image-1764642096038 new file mode 100644 index 0000000..0cc5db2 --- /dev/null +++ b/uploads/test-image-1764642096038 @@ -0,0 +1 @@ +dummy-image \ No newline at end of file diff --git a/uploads/test-section-bg-1764642023931 b/uploads/test-section-bg-1764642023931 new file mode 100644 index 0000000..0cc5db2 --- /dev/null +++ b/uploads/test-section-bg-1764642023931 @@ -0,0 +1 @@ +dummy-image \ No newline at end of file diff --git a/uploads/test-section-bg-1764642096099 b/uploads/test-section-bg-1764642096099 new file mode 100644 index 0000000..0cc5db2 --- /dev/null +++ b/uploads/test-section-bg-1764642096099 @@ -0,0 +1 @@ +dummy-image \ No newline at end of file diff --git a/uploads/test-section-bg-video-1764642023995 b/uploads/test-section-bg-video-1764642023995 new file mode 100644 index 0000000..badc796 --- /dev/null +++ b/uploads/test-section-bg-video-1764642023995 @@ -0,0 +1 @@ +dummy-section-video \ No newline at end of file diff --git a/uploads/test-section-bg-video-1764642096131 b/uploads/test-section-bg-video-1764642096131 new file mode 100644 index 0000000..badc796 --- /dev/null +++ b/uploads/test-section-bg-video-1764642096131 @@ -0,0 +1 @@ +dummy-section-video \ No newline at end of file diff --git a/uploads/test-video-1764642023985 b/uploads/test-video-1764642023985 new file mode 100644 index 0000000..87ccacf --- /dev/null +++ b/uploads/test-video-1764642023985 @@ -0,0 +1 @@ +dummy-video \ No newline at end of file diff --git a/uploads/test-video-1764642096117 b/uploads/test-video-1764642096117 new file mode 100644 index 0000000..87ccacf --- /dev/null +++ b/uploads/test-video-1764642096117 @@ -0,0 +1 @@ +dummy-video \ No newline at end of file diff --git a/uploads/test-video-poster-1764642023903 b/uploads/test-video-poster-1764642023903 new file mode 100644 index 0000000..7285c92 --- /dev/null +++ b/uploads/test-video-poster-1764642023903 @@ -0,0 +1 @@ +dummy-poster \ No newline at end of file diff --git a/uploads/test-video-poster-1764642096084 b/uploads/test-video-poster-1764642096084 new file mode 100644 index 0000000..7285c92 --- /dev/null +++ b/uploads/test-video-poster-1764642096084 @@ -0,0 +1 @@ +dummy-poster \ No newline at end of file