Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions src/hooks/useItems.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -22,7 +23,7 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => {
openMotion,
expandIcon,
classNames: collapseClassNames,
styles,
styles: collapseStyles,

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Renaming styles to collapseStyles is a good step to avoid naming collisions. However, this fix for panel-specific styles and classNames appears to be incomplete because it only applies to panels defined via the items prop.

The getNewChild function (which handles Panel components passed as children, starting on line 88) has not been updated. It still passes the Collapse component's styles and classNames to each CollapsePanel, ignoring the props on the Panel itself.

To ensure consistent behavior, I recommend applying a similar fix to the getNewChild function. This would involve:

  1. Destructuring classNames and styles from child.props.
  2. Passing childClassNames || collapseClassNames and childStyles || collapseStyles to the cloned element.

This will make the fix comprehensive, even though getNewChild is deprecated.

} = props;

return items.map((item, index) => {
Expand All @@ -33,6 +34,8 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => {
collapsible: rawCollapsible,
onItemClick: rawOnItemClick,
destroyOnHidden: rawDestroyOnHidden,
classNames,
styles,
...restProps
} = item;

Expand All @@ -57,11 +60,39 @@ const convertItemsToNodes = (items: ItemType[], props: Props) => {
isActive = activeKey.indexOf(key) > -1;
}

const mergeClassNames: Partial<Record<SemanticName, string>> = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我建议是在 Collapse 顶层做一个 SemanticContext,然后传入 classNames 和 styles,然后在 panel 里读取 className 和 style 以及 contextClassName 和 contextStyle,在消费的时候做聚合。
现在合并在 useItems 这层,如果未来额外加 semantic structure 很容易忘记。

...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<Record<SemanticName, React.CSSProperties>> = {
...collapseStyles,
header: {
...collapseStyles?.header,
...styles?.header,
},
body: {
...collapseStyles?.body,
...styles?.body,
},
title: {
...collapseStyles?.title,
...styles?.title,
},
icon: {
...collapseStyles?.icon,
...styles?.icon,
},
};

return (
<CollapsePanel
{...restProps}
classNames={collapseClassNames}
styles={styles}
classNames={mergeClassNames}
styles={mergeStyles}
prefixCls={prefixCls}
key={key}
panelKey={key}
Expand Down
62 changes: 62 additions & 0 deletions tests/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -898,5 +898,67 @@ describe('collapse', () => {
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(
<Collapse
activeKey={['1']}
styles={customStyles}
classNames={customClassnames}
items={[
{
key: '1',
styles: {
header: {
color: 'blue',
fontSize: 20,
},
body: {
fontSize: 20,
},
title: {
color: 'red',
},
icon: {
color: 'blue',
},
},
classNames: {
header: 'custom-header-panel',
body: 'custom-body-panel',
},
label: 'title',
},
]}
/>,
);
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');
expect(headerElement.classList).toContain('custom-header-panel');
expect(bodyElement.classList).toContain('custom-body');
expect(bodyElement.classList).toContain('custom-body-panel');

// check styles
expect(headerElement).toHaveStyle({ color: 'blue', fontSize: '20px' });
expect(bodyElement).toHaveStyle({ color: 'blue', fontSize: '20px' });
expect(titleElement).toHaveStyle({ color: 'red' });
expect(iconElement).toHaveStyle({ color: 'blue' });
});
});
});