feat: ConfigProvider support classNames and styles for radio (#52780)

* feat: ConfigProvider support classNames and styles for radio

* Update components/radio/__tests__/radio.test.tsx

Signed-off-by: thinkasany <480968828@qq.com>

* update input to icon

* Update components/radio/demo/_semantic.tsx

Signed-off-by: thinkasany <480968828@qq.com>

---------

Signed-off-by: thinkasany <480968828@qq.com>
This commit is contained in:
thinkasany
2025-02-14 11:35:16 +08:00
committed by GitHub
parent bbb381048c
commit 3cf11fc36f
10 changed files with 121 additions and 16 deletions

View File

@@ -47,6 +47,7 @@ import type { TransferProps } from '../transfer';
import type { TreeSelectProps } from '../tree-select';
import type { RenderEmptyHandler } from './defaultRenderEmpty';
import type { CheckboxProps } from '../checkbox';
import type { RadioProps } from '../radio';
export const defaultPrefixCls = 'ant';
export const defaultIconPrefixCls = 'anticon';
@@ -230,6 +231,8 @@ export type StatisticConfig = ComponentStyleConfig & Pick<StatisticProps, 'class
export type ResultConfig = ComponentStyleConfig & Pick<ResultProps, 'classNames' | 'styles'>;
export type RadioConfig = ComponentStyleConfig & Pick<RadioProps, 'classNames' | 'styles'>;
export type InputNumberConfig = ComponentStyleConfig & Pick<InputNumberProps, 'variant'>;
export type CascaderConfig = ComponentStyleConfig & Pick<CascaderProps, 'variant'>;
@@ -311,7 +314,7 @@ export interface ConfigComponentProps {
descriptions?: DescriptionsConfig;
empty?: EmptyConfig;
badge?: BadgeConfig;
radio?: ComponentStyleConfig;
radio?: RadioConfig;
rate?: ComponentStyleConfig;
switch?: ComponentStyleConfig;
transfer?: TransferConfig;

View File

@@ -145,7 +145,7 @@ const {
| notification | Set Notification common props | { className?: string, style?: React.CSSProperties, closeIcon?: React.ReactNode, classNames?: [NotificationConfig\["classNames"\]](/components/notification#api), styles?: [NotificationConfig\["styles"\]](/components/notification#api) } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames` and `styles`: 6.0.0 |
| pagination | Set Pagination common props | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | Set Progress common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | Set Radio common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | Set Radio common props | { className?: string, style?: React.CSSProperties, classNames?: [RadioConfig\["classNames"\]](/components/radio#api), styles?: [RadioConfig\["styles"\]](/components/radio#api) } | - | 5.7.0, `classNames` and `styles`: 6.0.0 |
| rate | Set Rate common props | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| result | Set Result common props | { className?: string, style?: React.CSSProperties , classNames?: [ResultProps\["classNames"\]](/components/result#api), styles?: [ResultProps\["styles"\]](/components/result#api)} | - | 5.7.0, `classNames` and `styles`: 6.0.0 |
| ribbon | Set Ribbon common props | { className?: string, style?: React.CSSProperties, , classNames?: [RibbonProps\["classNames"\]](/components/badge#api), styles?: [RibbonProps\["styles"\]](/components/badge#api) } | - | 6.0.0 |

View File

@@ -45,6 +45,7 @@ import type {
PopconfirmConfig,
PopoverConfig,
PopupOverflow,
RadioConfig,
RangePickerConfig,
RibbonConfig,
SelectConfig,
@@ -219,7 +220,7 @@ export interface ConfigProviderProps {
descriptions?: ComponentStyleConfig;
empty?: EmptyConfig;
badge?: BadgeConfig;
radio?: ComponentStyleConfig;
radio?: RadioConfig;
rate?: ComponentStyleConfig;
ribbon?: RibbonConfig;
switch?: ComponentStyleConfig;

View File

@@ -147,7 +147,7 @@ const {
| notification | 设置 Notification 组件的通用属性 | { className?: string, style?: React.CSSProperties, closeIcon?: React.ReactNode, classNames?: [NotificationConfig\["classNames"\]](/components/notification-cn#api), styles?: [NotificationConfig\["styles"\]](/components/notification-cn#api) } | - | 5.7.0, `closeIcon`: 5.14.0, `classNames``styles`: 6.0.0 |
| pagination | 设置 Pagination 组件的通用属性 | { showSizeChanger?: boolean, className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| progress | 设置 Progress 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | 设置 Radio 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| radio | 设置 Radio 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [RadioConfig\["classNames"\]](/components/radio-cn#api), styles?: [RadioConfig\["styles"\]](/components/radio-cn#api) } | - | 5.7.0, `classNames``styles`: 6.0.0 |
| rate | 设置 Rate 组件的通用属性 | { className?: string, style?: React.CSSProperties } | - | 5.7.0 |
| result | 设置 Result 组件的通用属性 | { className?: string, style?: React.CSSProperties, classNames?: [ResultProps\["classNames"\]](/components/result-cn#api), styles?: [ResultProps\["styles"\]](/components/result-cn#api) } | - | 5.7.0, `classNames``styles`: 6.0.0 |
| ribbon | 设置 Ribbon 组件的通用属性 | { className?: string, style?: React.CSSProperties, , classNames?: [RibbonProps\["classNames"\]](/components/badge-cn#api), styles?: [RibbonProps\["styles"\]](/components/badge-cn#api) } | - | 6.0.0 |

View File

@@ -98,4 +98,34 @@ describe('Radio', () => {
expect(onClick).toHaveBeenCalledTimes(3);
expect(onRootClick).toHaveBeenCalledTimes(3);
});
it('should support custom styles', () => {
const customClassNames = {
root: 'custom-root',
icon: 'custom-icon',
label: 'custom-label',
};
const customStyles = {
root: { backgroundColor: 'red' },
icon: { backgroundColor: 'black' },
label: { backgroundColor: 'gray' },
};
const { container } = render(
<Radio classNames={customClassNames} styles={customStyles}>
Test
</Radio>,
);
const rootElement = container.querySelector('.ant-radio-wrapper') as HTMLElement;
const iconElement = container.querySelector('.ant-radio') as HTMLElement;
const labelElement = container.querySelector('.ant-radio-label') as HTMLElement;
expect(rootElement.classList).toContain('custom-root');
expect(iconElement.classList).toContain('custom-icon');
expect(labelElement.classList).toContain('custom-label');
expect(rootElement.style.backgroundColor).toBe('red');
expect(iconElement.style.backgroundColor).toBe('black');
expect(labelElement.style.backgroundColor).toBe('gray');
});
});

View File

@@ -0,0 +1,35 @@
import React from 'react';
import { Radio } from 'antd';
import SemanticPreview from '../../../.dumi/components/SemanticPreview';
import useLocale from '../../../.dumi/hooks/useLocale';
const locales = {
cn: {
root: '根元素',
icon: '选中框元素',
label: '文本元素',
},
en: {
root: 'Root element',
icon: 'Icon element',
label: 'Label element',
},
};
const App: React.FC = () => {
const [locale] = useLocale(locales);
return (
<SemanticPreview
semantics={[
{ name: 'root', desc: locale.root, version: '6.0.0' },
{ name: 'icon', desc: locale.icon, version: '6.0.0' },
{ name: 'label', desc: locale.label, version: '6.0.0' },
]}
>
<Radio>Radio</Radio>
</SemanticPreview>
);
};
export default App;

View File

@@ -20,9 +20,9 @@ return (
<Radio.Group
value={value}
options={[
{ value: 1, label: "A" },
{ value: 2, label: "B"},
{ value: 3, label: "C" },
{ value: 1, label: 'A' },
{ value: 2, label: 'B' },
{ value: 3, label: 'C' },
]}
/>
);
@@ -109,6 +109,10 @@ Radio group can wrap a group of `Radio`。
| blur() | Remove focus |
| focus() | Get focus |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## Design Token
<ComponentTokenTable component="Radio"></ComponentTokenTable>

View File

@@ -21,9 +21,9 @@ return (
<Radio.Group
value={value}
options={[
{ value: 1, label: "A" },
{ value: 2, label: "B"},
{ value: 3, label: "C" },
{ value: 1, label: 'A' },
{ value: 2, label: 'B' },
{ value: 3, label: 'C' },
]}
/>
);
@@ -112,6 +112,10 @@ return (
| blur() | 移除焦点 |
| focus() | 获取焦点 |
## Semantic DOM
<code src="./demo/_semantic.tsx" simplify="true"></code>
## 主题变量Design Token
<ComponentTokenTable component="Radio"></ComponentTokenTable>

View File

@@ -41,6 +41,7 @@ export interface RadioGroupContextProps {
block?: boolean;
}
type SemanticName = 'root' | 'icon' | 'label';
export interface RadioProps extends AbstractCheckboxProps<RadioChangeEvent> {
/**
* Control the appearance for Radio to display as button or not
@@ -49,6 +50,8 @@ export interface RadioProps extends AbstractCheckboxProps<RadioChangeEvent> {
* @internal
*/
optionType?: RadioGroupOptionType;
classNames?: Partial<Record<SemanticName, string>>;
styles?: Partial<Record<SemanticName, React.CSSProperties>>;
}
export interface RadioChangeEventTarget extends RadioProps {

View File

@@ -7,7 +7,7 @@ import { devUseWarning } from '../_util/warning';
import Wave from '../_util/wave';
import { TARGET_CLS } from '../_util/wave/interface';
import useBubbleLock from '../checkbox/useBubbleLock';
import { ConfigContext } from '../config-provider';
import { useComponentConfig } from '../config-provider/context';
import DisabledContext from '../config-provider/DisabledContext';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import { FormItemInputContext } from '../form/context';
@@ -19,7 +19,14 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
const groupContext = React.useContext(RadioGroupContext);
const radioOptionTypeContext = React.useContext(RadioOptionTypeContext);
const { getPrefixCls, direction, radio } = React.useContext(ConfigContext);
const {
getPrefixCls,
direction,
className: contextClassName,
style: contextStyle,
classNames: contextClassNames,
styles: contextStyles,
} = useComponentConfig('radio');
const innerRef = React.useRef<RadioRef>(null);
const mergedRef = composeRef(ref, innerRef);
const { isFormItemInput } = React.useContext(FormItemInputContext);
@@ -42,6 +49,8 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
children,
style,
title,
classNames: radioClassNames,
styles,
...restProps
} = props;
const radioPrefixCls = getPrefixCls('radio', customizePrefixCls);
@@ -75,9 +84,11 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
[`${prefixCls}-wrapper-in-form-item`]: isFormItemInput,
[`${prefixCls}-wrapper-block`]: !!groupContext?.block,
},
radio?.className,
contextClassName,
className,
rootClassName,
contextClassNames.root,
radioClassNames?.root,
hashId,
cssVarCls,
rootCls,
@@ -91,7 +102,7 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
<Wave component="Radio" disabled={radioProps.disabled}>
<label
className={wrapperClassString}
style={{ ...radio?.style, ...style }}
style={{ ...contextStyles.root, ...styles?.root, ...contextStyle, ...style }}
onMouseEnter={props.onMouseEnter}
onMouseLeave={props.onMouseLeave}
title={title}
@@ -100,13 +111,27 @@ const InternalRadio: React.ForwardRefRenderFunction<RadioRef, RadioProps> = (pro
{/* @ts-ignore */}
<RcCheckbox
{...radioProps}
className={classNames(radioProps.className, { [TARGET_CLS]: !isButtonType })}
className={classNames(radioClassNames?.icon, contextClassNames.icon, {
[TARGET_CLS]: !isButtonType,
})}
style={{ ...contextStyles.icon, ...styles?.icon }}
type="radio"
prefixCls={prefixCls}
ref={mergedRef}
onClick={onInputClick}
/>
{children !== undefined ? <span className={`${prefixCls}-label`}>{children}</span> : null}
{children !== undefined ? (
<span
className={classNames(
`${prefixCls}-label`,
contextClassNames.label,
radioClassNames?.label,
)}
style={{ ...contextStyles.label, ...styles?.label }}
>
{children}
</span>
) : null}
</label>
</Wave>,
);