From 8ee5eeff0ab0fbf4902baacd46a089c56fb1b623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 1 Jul 2025 15:24:56 +0800 Subject: [PATCH 1/9] feat: Add attributes to 'segmented' --- src/index.tsx | 9 +++++++-- tests/index.test.tsx | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index db48a62..be2e743 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -44,6 +44,7 @@ export interface SegmentedProps name?: string; classNames?: Partial>; styles?: Partial>; + itemRender?: (node: React.ReactNode) => React.ReactNode; } function getValidTitle(option: SegmentedLabeledOption) { @@ -86,6 +87,7 @@ const InternalSegmentedOption: React.FC<{ title?: string; value: SegmentedRawOption; name?: string; + itemRender?: (node: React.ReactNode) => React.ReactNode; onChange: ( e: React.ChangeEvent, value: SegmentedRawOption, @@ -107,6 +109,7 @@ const InternalSegmentedOption: React.FC<{ title, value, name, + itemRender = (node: React.ReactNode) => node, onChange, onFocus, onBlur, @@ -120,8 +123,7 @@ const InternalSegmentedOption: React.FC<{ } onChange(event, value); }; - - return ( + const ItemContent = ( ); + return itemRender(ItemContent); }; const Segmented = React.forwardRef( @@ -174,6 +177,7 @@ const Segmented = React.forwardRef( styles, classNames: segmentedClassNames, motionName = 'thumb-motion', + itemRender, ...restProps } = props; @@ -298,6 +302,7 @@ const Segmented = React.forwardRef( { expect(itemElement.style.color).toBe('yellow'); expect(labelElement.style.backgroundColor).toBe('black'); }); + describe('itemRender', () => { + it('When "itemRender" is not configured, render the original "label"', () => { + const { container } = render( + , + ); + const label = container.querySelector('.rc-segmented-item-label'); + expect(label).toHaveTextContent('iOS'); + }); + it('Configure "itemRender" to render the return value', () => { + const { container } = render( +
{node}
} + />, + ); + const labels = container.querySelectorAll('.test-title'); + expect(labels).toHaveLength(3); + }); + }); }); From 0fefb75e41c0be07ca9c079648d9894cde373b13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 1 Jul 2025 17:36:28 +0800 Subject: [PATCH 2/9] feat: add props to itemRender --- src/index.tsx | 57 +++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index be2e743..3dcbfec 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -44,7 +44,7 @@ export interface SegmentedProps name?: string; classNames?: Partial>; styles?: Partial>; - itemRender?: (node: React.ReactNode) => React.ReactNode; + itemRender?: (node: React.ReactNode, itemInfo: ItemInfo) => React.ReactNode; } function getValidTitle(option: SegmentedLabeledOption) { @@ -75,7 +75,7 @@ function normalizeOptions(options: SegmentedOptions): SegmentedLabeledOption[] { }); } -const InternalSegmentedOption: React.FC<{ +interface ItemInfo { prefixCls: string; className?: string; style?: React.CSSProperties; @@ -87,7 +87,6 @@ const InternalSegmentedOption: React.FC<{ title?: string; value: SegmentedRawOption; name?: string; - itemRender?: (node: React.ReactNode) => React.ReactNode; onChange: ( e: React.ChangeEvent, value: SegmentedRawOption, @@ -97,26 +96,33 @@ const InternalSegmentedOption: React.FC<{ onKeyDown: (e: React.KeyboardEvent) => void; onKeyUp: (e: React.KeyboardEvent) => void; onMouseDown: () => void; -}> = ({ - prefixCls, - className, - style, - styles, - classNames: segmentedClassNames, - disabled, - checked, - label, - title, - value, - name, - itemRender = (node: React.ReactNode) => node, - onChange, - onFocus, - onBlur, - onKeyDown, - onKeyUp, - onMouseDown, -}) => { +} + +const InternalSegmentedOption: React.FC< + { + itemRender?: (node: React.ReactNode, itemInfo: ItemInfo) => React.ReactNode; + } & ItemInfo +> = (props) => { + const { + prefixCls, + className, + style, + styles, + classNames: segmentedClassNames, + disabled, + checked, + label, + title, + value, + name, + itemRender = (node: React.ReactNode) => node, + onChange, + onFocus, + onBlur, + onKeyDown, + onKeyUp, + onMouseDown, + } = props; const handleChange = (event: React.ChangeEvent) => { if (disabled) { return; @@ -157,7 +163,10 @@ const InternalSegmentedOption: React.FC<{ ); - return itemRender(ItemContent); + + const renderProps = { ...props }; + delete renderProps.itemRender; + return itemRender(ItemContent, renderProps); }; const Segmented = React.forwardRef( From 89edf8b37ff5a0f856acc73fdffad0b1965da521 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Tue, 1 Jul 2025 17:36:28 +0800 Subject: [PATCH 3/9] feat:add ts to ItemContent --- src/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.tsx b/src/index.tsx index 3dcbfec..e7a96fd 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -129,7 +129,7 @@ const InternalSegmentedOption: React.FC< } onChange(event, value); }; - const ItemContent = ( + const ItemContent: React.ReactNode = ( ); - const renderProps = { ...props }; - delete renderProps.itemRender; - return itemRender(ItemContent, renderProps); + return itemRender(ItemContent, props); }; const Segmented = React.forwardRef( From 2c187bd41458ed58234668f8080f4c3b1a1da7ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 2 Jul 2025 14:01:29 +0800 Subject: [PATCH 5/9] feat: change itemInfo value --- src/index.tsx | 77 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 30 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 165e0e9..9f1b94e 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -22,6 +22,15 @@ export interface SegmentedLabeledOption { title?: string; } +interface SegementItem { + disabled?: boolean; + checked: boolean; + label: React.ReactNode; + title?: string; + value: SegmentedRawOption; + name?: string; +} + type SegmentedOptions = ( | T | SegmentedLabeledOption @@ -44,7 +53,10 @@ export interface SegmentedProps name?: string; classNames?: Partial>; styles?: Partial>; - itemRender?: (node: React.ReactNode, itemInfo: ItemInfo) => React.ReactNode; + itemRender?: ( + node: React.ReactNode, + info: { item: SegementItem }, + ) => React.ReactNode; } function getValidTitle(option: SegmentedLabeledOption) { @@ -75,7 +87,7 @@ function normalizeOptions(options: SegmentedOptions): SegmentedLabeledOption[] { }); } -interface ItemInfo { +const InternalSegmentedOption: React.FC<{ prefixCls: string; className?: string; style?: React.CSSProperties; @@ -96,32 +108,30 @@ interface ItemInfo { onKeyDown: (e: React.KeyboardEvent) => void; onKeyUp: (e: React.KeyboardEvent) => void; onMouseDown: () => void; -} - -const InternalSegmentedOption: React.FC< - { - itemRender?: (node: React.ReactNode, itemInfo: ItemInfo) => React.ReactNode; - } & ItemInfo -> = ({ itemRender = (node: React.ReactNode) => node, ...props }) => { - const { - prefixCls, - className, - style, - styles, - classNames: segmentedClassNames, - disabled, - checked, - label, - title, - value, - name, - onChange, - onFocus, - onBlur, - onKeyDown, - onKeyUp, - onMouseDown, - } = props; + itemRender?: ( + node: React.ReactNode, + info: { item: SegementItem }, + ) => React.ReactNode; +}> = ({ + prefixCls, + className, + style, + styles, + classNames: segmentedClassNames, + disabled, + checked, + label, + title, + value, + name, + onChange, + onFocus, + onBlur, + onKeyDown, + onKeyUp, + onMouseDown, + itemRender = (node: React.ReactNode) => node, +}) => { const handleChange = (event: React.ChangeEvent) => { if (disabled) { return; @@ -162,8 +172,15 @@ const InternalSegmentedOption: React.FC< ); - - return itemRender(ItemContent, props); + const itemInfo: SegementItem = { + label, + title, + value, + name, + disabled, + checked, + }; + return itemRender(ItemContent, { item: itemInfo }); }; const Segmented = React.forwardRef( From d136c0136844bb2e691f17308169b8cbf766cbd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 2 Jul 2025 14:07:47 +0800 Subject: [PATCH 6/9] feat: change ts --- src/index.tsx | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 9f1b94e..9bca2da 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -31,6 +31,11 @@ interface SegementItem { name?: string; } +type ItemRender = ( + node: React.ReactNode, + info: { item: SegementItem }, +) => React.ReactNode; + type SegmentedOptions = ( | T | SegmentedLabeledOption @@ -53,10 +58,7 @@ export interface SegmentedProps name?: string; classNames?: Partial>; styles?: Partial>; - itemRender?: ( - node: React.ReactNode, - info: { item: SegementItem }, - ) => React.ReactNode; + itemRender?: ItemRender; } function getValidTitle(option: SegmentedLabeledOption) { @@ -108,10 +110,7 @@ const InternalSegmentedOption: React.FC<{ onKeyDown: (e: React.KeyboardEvent) => void; onKeyUp: (e: React.KeyboardEvent) => void; onMouseDown: () => void; - itemRender?: ( - node: React.ReactNode, - info: { item: SegementItem }, - ) => React.ReactNode; + itemRender?: ItemRender; }> = ({ prefixCls, className, @@ -138,7 +137,7 @@ const InternalSegmentedOption: React.FC<{ } onChange(event, value); }; - const ItemContent: React.ReactNode = ( + const itemContent: React.ReactNode = ( ); - const itemInfo: SegementItem = { + const itemInfo: SegmentedLabeledOption = { label, title, value, - name, disabled, - checked, }; return itemRender(itemContent, { item: itemInfo }); }; From b8762960d1f7d4fc6771edfbb6068ee632646648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 2 Jul 2025 14:53:00 +0800 Subject: [PATCH 8/9] feat: pass optionData to SegmentedOption --- src/index.tsx | 92 +++++++++++++++++++++++++++++---------------------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/src/index.tsx b/src/index.tsx index 84b55da..65efdad 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -86,6 +86,7 @@ const InternalSegmentedOption: React.FC<{ style?: React.CSSProperties; classNames?: Partial>; styles?: Partial>; + data: SegmentedLabeledOption; disabled?: boolean; checked: boolean; label: React.ReactNode; @@ -108,6 +109,7 @@ const InternalSegmentedOption: React.FC<{ style, styles, classNames: segmentedClassNames, + data, disabled, checked, label, @@ -162,13 +164,7 @@ const InternalSegmentedOption: React.FC<{ ); - const itemInfo: SegmentedLabeledOption = { - label, - title, - value, - disabled, - }; - return itemRender(itemContent, { item: itemInfo }); + return itemRender(itemContent, { item: data }); }; const Segmented = React.forwardRef( @@ -273,6 +269,54 @@ const Segmented = React.forwardRef( break; } }; + + const renderOption = (segmentedOption: SegmentedLabeledOption) => { + const { + label, + value: optionValue, + disabled: optionDisabled, + title, + } = segmentedOption; + const optionData: SegmentedLabeledOption = { + label, + value: optionValue, + disabled: optionDisabled, + title, + }; + return ( + + ); + }; + return (
( setThumbShow(false); }} /> - {segmentedOptions.map((segmentedOption) => ( - - ))} + {segmentedOptions.map(renderOption)}
); From 50e4846cd041b66693241cf159260c8c2803afc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=AC=A2?= Date: Wed, 2 Jul 2025 16:11:45 +0800 Subject: [PATCH 9/9] test: add itemRender test --- tests/index.test.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/index.test.tsx b/tests/index.test.tsx index 916d6fb..7fff1b4 100644 --- a/tests/index.test.tsx +++ b/tests/index.test.tsx @@ -817,5 +817,29 @@ describe('Segmented keyboard navigation', () => { const labels = container.querySelectorAll('.test-title'); expect(labels).toHaveLength(3); }); + it('should pass complete params to itemRender', () => { + const mockItemRender = jest.fn((node, params) => node); + const testData = { + label: 'iOS', + value: 'iOS', + disabled: false, + title: 'iOS', + }; + render( + , + ); + expect(mockItemRender).toHaveBeenCalledTimes(3); + const callArgs = mockItemRender.mock.calls[0]; + const receivedParams = callArgs[1]; + expect(receivedParams).toEqual({ + item: { + ...testData, + }, + }); + expect(React.isValidElement(callArgs[0])).toBeTruthy(); + }); }); });