From d1b1e163685761ee7deaf6f8e3348f0049a18f89 Mon Sep 17 00:00:00 2001 From: shin Date: Fri, 3 Jul 2026 18:28:50 +0900 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20Checkbox=20=E3=81=8C=20className=20?= =?UTF-8?q?=E3=82=92=E5=8F=97=E3=81=91=E5=8F=96=E3=82=8C=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit {...props} の後に固定の className を当てていたため、利用側から渡した className が上書きされて消えていた。Radio と同じ clsx マージに揃える。 Closes #16 Co-Authored-By: Claude Fable 5 --- src/Checkbox/Checkbox.test.tsx | 20 ++++++++++++++++++++ src/Checkbox/index.tsx | 4 ++-- 2 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 src/Checkbox/Checkbox.test.tsx diff --git a/src/Checkbox/Checkbox.test.tsx b/src/Checkbox/Checkbox.test.tsx new file mode 100644 index 0000000..3849075 --- /dev/null +++ b/src/Checkbox/Checkbox.test.tsx @@ -0,0 +1,20 @@ +import { renderToStaticMarkup } from 'react-dom/server' +import { describe, expect, it } from 'vitest' + +import { Checkbox } from './index' + +describe('Checkbox', () => { + it('渡した className がルートの label にマージされる', () => { + const html = renderToStaticMarkup(ラベル) + + expect(html).toContain('aria-checkbox') + expect(html).toContain('my-card') + }) + + it('className を渡さない場合は既存のクラスのみが付く', () => { + const html = renderToStaticMarkup(ラベル) + + expect(html).toContain('aria-checkbox') + expect(html).not.toContain('undefined') + }) +}) diff --git a/src/Checkbox/index.tsx b/src/Checkbox/index.tsx index 43fe752..69a4354 100644 --- a/src/Checkbox/index.tsx +++ b/src/Checkbox/index.tsx @@ -60,7 +60,7 @@ const useCheckboxState = (ref: React.RefObject) => { * * @summary 複数選択用のチェックボックス */ -const Checkbox = ({ children, ...props }: CheckboxProps) => { +const Checkbox = ({ children, className, ...props }: CheckboxProps) => { const ariaRef = useRef(null) const { isSelected, isIndeterminate, isDisabled, isHovered, isFocused } = useCheckboxState(ariaRef) @@ -78,7 +78,7 @@ const Checkbox = ({ children, ...props }: CheckboxProps) => { }) return ( - +
{children && ( From db20aa6788c88a210d0f307abb3877907f9f7340 Mon Sep 17 00:00:00 2001 From: shin Date: Fri, 3 Jul 2026 19:11:38 +0900 Subject: [PATCH 2/2] =?UTF-8?q?test:=20className=20=E3=81=AE=E6=A4=9C?= =?UTF-8?q?=E8=A8=BC=E3=82=92=E3=83=AB=E3=83=BC=E3=83=88=20label=20?= =?UTF-8?q?=E3=81=AE=20class=20=E5=B1=9E=E6=80=A7=E3=81=AB=E7=B5=9E?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit HTML 全文への部分一致だと、クラスが別の要素に付いた場合や無関係な 文字列にも反応し得るため、ルートの label の class 属性を取り出して 検証する。レビュー指摘への対応。 Co-Authored-By: Claude Fable 5 --- src/Checkbox/Checkbox.test.tsx | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Checkbox/Checkbox.test.tsx b/src/Checkbox/Checkbox.test.tsx index 3849075..4adc3bb 100644 --- a/src/Checkbox/Checkbox.test.tsx +++ b/src/Checkbox/Checkbox.test.tsx @@ -3,18 +3,27 @@ import { describe, expect, it } from 'vitest' import { Checkbox } from './index' +/** SSR 出力からルートの label の class 属性をクラス名の配列として取り出す */ +const getRootLabelClasses = (html: string): string[] => { + const className = html.match(/^]*\bclass="([^"]*)"/)?.[1] + if (className === undefined) { + throw new Error(`ルートの label に class 属性が見つかりません: ${html}`) + } + return className.split(' ') +} + describe('Checkbox', () => { it('渡した className がルートの label にマージされる', () => { const html = renderToStaticMarkup(ラベル) + const classes = getRootLabelClasses(html) - expect(html).toContain('aria-checkbox') - expect(html).toContain('my-card') + expect(classes).toContain('aria-checkbox') + expect(classes).toContain('my-card') }) it('className を渡さない場合は既存のクラスのみが付く', () => { const html = renderToStaticMarkup(ラベル) - expect(html).toContain('aria-checkbox') - expect(html).not.toContain('undefined') + expect(getRootLabelClasses(html)).toEqual(['aria-checkbox']) }) })