From 4146c49f5973c8fb63667d524fbe97c5a92c37c0 Mon Sep 17 00:00:00 2001 From: thxforall <113906780+thxforall@users.noreply.github.com> Date: Thu, 28 May 2026 19:03:59 +0900 Subject: [PATCH] fix(vton): before/after slider initial render shows both via clip-path (#602) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 기존 구현은 containerRef.current.offsetWidth로 inner image width를 계산하여 첫 렌더 시 ref=null → 0px → before가 보이지 않고 after만 표시되는 race condition이 있었음. 두 이미지를 absolute로 겹치고 위 레이어에 clip-path: inset(0 X% 0 0)을 적용하여 측정 의존 제거. Refs #602 (sub-PR #2) Co-Authored-By: Claude Sonnet 4.6 --- .../lib/components/vton/BeforeAfterSlider.tsx | 27 +++++------- .../vton/__tests__/BeforeAfterSlider.test.tsx | 44 +++++++++++++++++++ 2 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 packages/web/lib/components/vton/__tests__/BeforeAfterSlider.test.tsx diff --git a/packages/web/lib/components/vton/BeforeAfterSlider.tsx b/packages/web/lib/components/vton/BeforeAfterSlider.tsx index ca791f3c..8c0a7d1e 100644 --- a/packages/web/lib/components/vton/BeforeAfterSlider.tsx +++ b/packages/web/lib/components/vton/BeforeAfterSlider.tsx @@ -36,29 +36,24 @@ export function BeforeAfterSlider({ }} onPointerMove={(e) => handleMove(e.clientX)} > + {/* After (밑층, 항상 100% 표시) */} Result + {/* Before (위층, clip-path로 좌측만 표시) */} + Original + {/* Divider line + handle */}
- Original -
-
diff --git a/packages/web/lib/components/vton/__tests__/BeforeAfterSlider.test.tsx b/packages/web/lib/components/vton/__tests__/BeforeAfterSlider.test.tsx new file mode 100644 index 00000000..dd472536 --- /dev/null +++ b/packages/web/lib/components/vton/__tests__/BeforeAfterSlider.test.tsx @@ -0,0 +1,44 @@ +/* @vitest-environment jsdom */ + +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { BeforeAfterSlider } from "../BeforeAfterSlider"; + +describe("BeforeAfterSlider", () => { + it("renders both before and after images on first mount", () => { + render( + + ); + + expect(screen.getByAltText("Original")).toBeInTheDocument(); + expect(screen.getByAltText("Result")).toBeInTheDocument(); + }); + + it("applies clip-path inset(0 50% 0 0) to before image at default sliderPos=50", () => { + render( + + ); + + const beforeImg = screen.getByAltText("Original"); + expect(beforeImg.style.clipPath).toContain("inset(0 50% 0 0)"); + }); + + it("does not use offsetWidth measurement on before image", () => { + render( + + ); + + const beforeImg = screen.getByAltText("Original"); + // width should not be set as an inline px value (old race-condition approach) + expect(beforeImg.style.width).toBe(""); + }); +});