From a9aba53da1fe96843331e68e9e350ff2fbf931d2 Mon Sep 17 00:00:00 2001 From: keiseiTi Date: Wed, 26 Nov 2025 10:52:50 +0800 Subject: [PATCH 1/5] fix: panel styles and classNames not working --- src/hooks/useItems.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index 4b937a9..e261871 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -22,7 +22,7 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { openMotion, expandIcon, classNames: collapseClassNames, - styles, + styles: collapseStyles, } = props; return items.map((item, index) => { @@ -33,6 +33,8 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { collapsible: rawCollapsible, onItemClick: rawOnItemClick, destroyOnHidden: rawDestroyOnHidden, + classNames, + styles, ...restProps } = item; @@ -60,8 +62,8 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { return ( Date: Wed, 26 Nov 2025 13:39:51 +0800 Subject: [PATCH 2/5] fix: adjust classNames and styles in the Panel --- src/hooks/useItems.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index e261871..8eeefaa 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -62,8 +62,14 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { return ( Date: Wed, 26 Nov 2025 14:02:08 +0800 Subject: [PATCH 3/5] fix: merge classNames and styles in the panel, and add test --- src/hooks/useItems.tsx | 31 +++++++++++++++++------- tests/index.spec.tsx | 54 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index 8eeefaa..114e749 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -1,7 +1,8 @@ import toArray from '@rc-component/util/lib/Children/toArray'; import React from 'react'; -import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface'; +import type { CollapsePanelProps, CollapseProps, ItemType, SemanticName } from '../interface'; import CollapsePanel from '../Panel'; +import clsx from 'clsx'; type Props = Pick< CollapsePanelProps, @@ -59,17 +60,29 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { isActive = activeKey.indexOf(key) > -1; } + const mergeClassNames: Partial> = { + ...collapseClassNames, + header: clsx(collapseClassNames?.header, classNames?.header), + body: clsx(collapseClassNames?.body, classNames?.body), + }; + + const mergeStyles: Partial> = { + ...collapseStyles, + header: { + ...collapseStyles?.header, + ...styles?.header, + }, + body: { + ...collapseStyles?.body, + ...styles?.body, + }, + }; + return ( { expect(titleElement.style.color).toBe('green'); expect(iconElement.style.color).toBe('yellow'); }); + + it('should support styles and classNames in panel', () => { + const customStyles = { + header: { color: 'red' }, + body: { color: 'blue' }, + title: { color: 'green' }, + icon: { color: 'yellow' }, + }; + const customClassnames = { + header: 'custom-header', + body: 'custom-body', + }; + + const { container } = render( + , + ); + const headerElement = container.querySelector('.rc-collapse-header') as HTMLElement; + const bodyElement = container.querySelector('.rc-collapse-body') as HTMLElement; + + // check classNames + expect(headerElement.classList).toContain('custom-header'); + expect(headerElement.classList).toContain('custom-header-panel'); + expect(bodyElement.classList).toContain('custom-body'); + expect(bodyElement.classList).toContain('custom-body-panel'); + + // check styles + expect(headerElement.style.color).toBe('blue'); + expect(headerElement.style.fontSize).toBe('20px'); + expect(bodyElement.style.color).toBe('blue'); + expect(bodyElement.style.fontSize).toBe('20px'); + }); }); }); From d57c865f2550718003af905441ca3c9a35679f53 Mon Sep 17 00:00:00 2001 From: keiseiTi Date: Mon, 22 Dec 2025 10:04:23 +0800 Subject: [PATCH 4/5] fix: adjust test and merged styles and classNames --- src/hooks/useItems.tsx | 10 ++++++++++ tests/index.spec.tsx | 16 ++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index 114e749..d01768a 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -64,6 +64,8 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { ...collapseClassNames, header: clsx(collapseClassNames?.header, classNames?.header), body: clsx(collapseClassNames?.body, classNames?.body), + title: clsx(collapseClassNames?.title, classNames?.title), + icon: clsx(collapseClassNames?.icon, classNames?.icon), }; const mergeStyles: Partial> = { @@ -76,6 +78,14 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { ...collapseStyles?.body, ...styles?.body, }, + title: { + ...collapseStyles?.title, + ...styles?.title, + }, + icon: { + ...collapseStyles?.icon, + ...styles?.icon, + }, }; return ( diff --git a/tests/index.spec.tsx b/tests/index.spec.tsx index fa9c9ca..1b2e1fc 100644 --- a/tests/index.spec.tsx +++ b/tests/index.spec.tsx @@ -927,6 +927,12 @@ describe('collapse', () => { body: { fontSize: 20, }, + title: { + color: 'red', + }, + icon: { + color: 'blue', + }, }, classNames: { header: 'custom-header-panel', @@ -939,6 +945,8 @@ describe('collapse', () => { ); const headerElement = container.querySelector('.rc-collapse-header') as HTMLElement; const bodyElement = container.querySelector('.rc-collapse-body') as HTMLElement; + const titleElement = container.querySelector('.rc-collapse-title') as HTMLElement; + const iconElement = container.querySelector('.rc-collapse-expand-icon') as HTMLElement; // check classNames expect(headerElement.classList).toContain('custom-header'); @@ -947,10 +955,10 @@ describe('collapse', () => { expect(bodyElement.classList).toContain('custom-body-panel'); // check styles - expect(headerElement.style.color).toBe('blue'); - expect(headerElement.style.fontSize).toBe('20px'); - expect(bodyElement.style.color).toBe('blue'); - expect(bodyElement.style.fontSize).toBe('20px'); + expect(headerElement).toHaveStyle({ color: 'blue', fontSize: '20px' }); + expect(bodyElement).toHaveStyle({ color: 'blue', fontSize: '20px' }); + expect(titleElement).toHaveStyle({ color: 'red' }); + expect(iconElement).toHaveStyle({ color: 'blue' }); }); }); }); From 8372d7710f649ac9f54d8fd3909ae7205057d073 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Tue, 6 Jan 2026 17:33:25 +0800 Subject: [PATCH 5/5] chore: simplify --- src/hooks/useItems.tsx | 58 ++++++++++++++++++++---------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/src/hooks/useItems.tsx b/src/hooks/useItems.tsx index d01768a..1ee76f7 100644 --- a/src/hooks/useItems.tsx +++ b/src/hooks/useItems.tsx @@ -1,6 +1,6 @@ import toArray from '@rc-component/util/lib/Children/toArray'; import React from 'react'; -import type { CollapsePanelProps, CollapseProps, ItemType, SemanticName } from '../interface'; +import type { CollapsePanelProps, CollapseProps, ItemType } from '../interface'; import CollapsePanel from '../Panel'; import clsx from 'clsx'; @@ -12,6 +12,30 @@ type Props = Pick< activeKey: React.Key[]; }; +function mergeSemantic(src: T, tgt: T, mergeFn: (a: any, b: any) => any) { + if (!src || !tgt) { + return src || tgt; + } + + const keys = Array.from(new Set([...Object.keys(src), ...Object.keys(tgt)])); + const result = {}; + keys.forEach((key) => { + result[key] = mergeFn(src[key], tgt[key]); + }); + return result; +} + +function mergeSemanticClassNames(src: T, tgt: T) { + return mergeSemantic(src, tgt, (a: string, b: string) => clsx(a, b)); +} + +function mergeSemanticStyles(src: T, tgt: T) { + return mergeSemantic(src, tgt, (a: React.CSSProperties, b: React.CSSProperties) => ({ + ...a, + ...b, + })); +} + const convertItemsToNodes = (items: ItemType[], props: Props) => { const { prefixCls, @@ -60,39 +84,11 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => { isActive = activeKey.indexOf(key) > -1; } - const mergeClassNames: Partial> = { - ...collapseClassNames, - header: clsx(collapseClassNames?.header, classNames?.header), - body: clsx(collapseClassNames?.body, classNames?.body), - title: clsx(collapseClassNames?.title, classNames?.title), - icon: clsx(collapseClassNames?.icon, classNames?.icon), - }; - - const mergeStyles: Partial> = { - ...collapseStyles, - header: { - ...collapseStyles?.header, - ...styles?.header, - }, - body: { - ...collapseStyles?.body, - ...styles?.body, - }, - title: { - ...collapseStyles?.title, - ...styles?.title, - }, - icon: { - ...collapseStyles?.icon, - ...styles?.icon, - }, - }; - return (