chore: auto merge branches (#50748)

chore: merge master into feature
This commit is contained in:
github-actions[bot] 2024-09-07 04:19:01 +00:00 committed by GitHub
commit 33533ff22c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 162 additions and 44 deletions

View File

@ -6,13 +6,14 @@ import { composeRef, supportRef } from 'rc-util/lib/ref';
import type { ConfigConsumerProps } from '../../config-provider'; import type { ConfigConsumerProps } from '../../config-provider';
import { ConfigContext } from '../../config-provider'; import { ConfigContext } from '../../config-provider';
import { cloneElement } from '../reactNode'; import { cloneElement } from '../reactNode';
import type { WaveComponent } from './interface';
import useStyle from './style'; import useStyle from './style';
import useWave from './useWave'; import useWave from './useWave';
export interface WaveProps { export interface WaveProps {
disabled?: boolean; disabled?: boolean;
children?: React.ReactNode; children?: React.ReactNode;
component?: 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch'; component?: WaveComponent;
} }
const Wave: React.FC<WaveProps> = (props) => { const Wave: React.FC<WaveProps> = (props) => {

View File

@ -8,10 +8,12 @@ export type ShowWaveEffect = (
info: { info: {
className: string; className: string;
token: GlobalToken; token: GlobalToken;
component?: string; component?: WaveComponent;
event: MouseEvent; event: MouseEvent;
hashId: string; hashId: string;
}, },
) => void; ) => void;
export type ShowWave = (event: MouseEvent) => void; export type ShowWave = (event: MouseEvent) => void;
export type WaveComponent = 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch';

View File

@ -5,13 +5,13 @@ import raf from 'rc-util/lib/raf';
import { ConfigContext } from '../../config-provider'; import { ConfigContext } from '../../config-provider';
import useToken from '../../theme/useToken'; import useToken from '../../theme/useToken';
import { TARGET_CLS } from './interface'; import { TARGET_CLS } from './interface';
import type { ShowWave } from './interface'; import type { ShowWave, WaveComponent } from './interface';
import showWaveEffect from './WaveEffect'; import showWaveEffect from './WaveEffect';
const useWave = ( const useWave = (
nodeRef: React.RefObject<HTMLElement>, nodeRef: React.RefObject<HTMLElement>,
className: string, className: string,
component?: 'Tag' | 'Button' | 'Checkbox' | 'Radio' | 'Switch', component?: WaveComponent,
) => { ) => {
const { wave } = React.useContext(ConfigContext); const { wave } = React.useContext(ConfigContext);
const [, token, hashId] = useToken(); const [, token, hashId] = useToken();

View File

@ -145,7 +145,6 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token) => {
iconCls, iconCls,
antCls, antCls,
badgeShadowSize, badgeShadowSize,
motionDurationSlow,
textFontSize, textFontSize,
textFontSizeSM, textFontSizeSM,
statusSize, statusSize,
@ -231,9 +230,6 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token) => {
borderRadius: '100%', borderRadius: '100%',
boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`, boxShadow: `0 0 0 ${unit(badgeShadowSize)} ${token.badgeShadowColor}`,
}, },
[`${componentCls}-dot${numberPrefixCls}`]: {
transition: `background ${motionDurationSlow}`,
},
[`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: { [`${componentCls}-count, ${componentCls}-dot, ${numberPrefixCls}-custom-component`]: {
position: 'absolute', position: 'absolute',
top: 0, top: 0,

View File

@ -227,7 +227,6 @@ const genBaseStyle: GenerateStyle<DropdownToken> = (token) => {
position: 'relative', position: 'relative',
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
whiteSpace: 'nowrap',
}, },
[`${menuCls}-item-icon`]: { [`${menuCls}-item-icon`]: {

View File

@ -686,8 +686,6 @@ const genSearchInputStyle: GenerateStyle<InputToken> = (token: InputToken) => {
paddingTop: 0, paddingTop: 0,
paddingBottom: 0, paddingBottom: 0,
borderStartStartRadius: 0, borderStartStartRadius: 0,
borderStartEndRadius: token.borderRadius,
borderEndEndRadius: token.borderRadius,
borderEndStartRadius: 0, borderEndStartRadius: 0,
boxShadow: 'none', boxShadow: 'none',
}, },

View File

@ -7,29 +7,18 @@ import { ConfigContext } from '../config-provider';
import useCSSVarCls from '../config-provider/hooks/useCSSVarCls'; import useCSSVarCls from '../config-provider/hooks/useCSSVarCls';
import useSize from '../config-provider/hooks/useSize'; import useSize from '../config-provider/hooks/useSize';
import { RadioGroupContextProvider } from './context'; import { RadioGroupContextProvider } from './context';
import type { RadioChangeEvent, RadioGroupButtonStyle, RadioGroupProps } from './interface'; import type {
RadioChangeEvent,
RadioGroupButtonStyle,
RadioGroupContextProps,
RadioGroupProps,
} from './interface';
import Radio from './radio'; import Radio from './radio';
import useStyle from './style'; import useStyle from './style';
const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref) => { const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext); const { getPrefixCls, direction } = React.useContext(ConfigContext);
const [value, setValue] = useMergedState(props.defaultValue, {
value: props.value,
});
const onRadioChange = (ev: RadioChangeEvent) => {
const lastValue = value;
const val = ev.target.value;
if (!('value' in props)) {
setValue(val);
}
const { onChange } = props;
if (onChange && val !== lastValue) {
onChange(ev);
}
};
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
className, className,
@ -41,11 +30,35 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
size: customizeSize, size: customizeSize,
style, style,
id, id,
optionType,
name,
defaultValue,
value: customizedValue,
onChange,
onMouseEnter, onMouseEnter,
onMouseLeave, onMouseLeave,
onFocus, onFocus,
onBlur, onBlur,
} = props; } = props;
const [value, setValue] = useMergedState(defaultValue, {
value: customizedValue,
});
const onRadioChange = React.useCallback(
(event: RadioChangeEvent) => {
const lastValue = value;
const val = event.target.value;
if (!('value' in props)) {
setValue(val);
}
if (val !== lastValue) {
onChange?.(event);
}
},
[value, setValue, onChange],
);
const prefixCls = getPrefixCls('radio', customizePrefixCls); const prefixCls = getPrefixCls('radio', customizePrefixCls);
const groupPrefixCls = `${prefixCls}-group`; const groupPrefixCls = `${prefixCls}-group`;
@ -105,6 +118,12 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
cssVarCls, cssVarCls,
rootCls, rootCls,
); );
const memoizedValue = React.useMemo<RadioGroupContextProps>(
() => ({ onChange: onRadioChange, value, disabled, name, optionType }),
[onRadioChange, value, disabled, name, optionType],
);
return wrapCSSVar( return wrapCSSVar(
<div <div
{...pickAttrs(props, { aria: true, data: true })} {...pickAttrs(props, { aria: true, data: true })}
@ -117,15 +136,7 @@ const RadioGroup = React.forwardRef<HTMLDivElement, RadioGroupProps>((props, ref
id={id} id={id}
ref={ref} ref={ref}
> >
<RadioGroupContextProvider <RadioGroupContextProvider value={memoizedValue}>
value={{
onChange: onRadioChange,
value,
disabled: props.disabled,
name: props.name,
optionType: props.optionType,
}}
>
{childrenToRender} {childrenToRender}
</RadioGroupContextProvider> </RadioGroupContextProvider>
</div>, </div>,

View File

@ -118,7 +118,7 @@ Common props ref[Common props](/docs/react/common-props)
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 | | status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | ReactNode | `<DownOutlined />` | | | suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | ReactNode | `<DownOutlined />` | |
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) => ReactNode | - | | | tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) => ReactNode | - | |
| labelRender | Customize selected label render | (label: ReactNode) => ReactNode | - | 5.15.0 | | labelRender | Customize selected label render (LabelInValueType definition see [LabelInValueType](https://github.com/react-component/select/blob/b39c28aa2a94e7754ebc570f200ab5fd33bd31e7/src/Select.tsx#L70)) | (props: LabelInValueType) => ReactNode | - | 5.15.0 |
| tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string\[] | - | | | tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string\[] | - | |
| value | Current selected option (considered as a immutable array) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | | | value | Current selected option (considered as a immutable array) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 | | variant | Variants of selector | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |

View File

@ -119,7 +119,7 @@ return (
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 | | status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| suffixIcon | 自定义的选择框后缀图标。以防止图标被用于其他交互,替换的图标默认不会响应展开、收缩事件,可以通过添加 `pointer-events: none` 样式透传。 | ReactNode | `<DownOutlined />` | | | suffixIcon | 自定义的选择框后缀图标。以防止图标被用于其他交互,替换的图标默认不会响应展开、收缩事件,可以通过添加 `pointer-events: none` 样式透传。 | ReactNode | `<DownOutlined />` | |
| tagRender | 自定义 tag 内容 render仅在 `mode``multiple``tags` 时生效 | (props) => ReactNode | - | | | tagRender | 自定义 tag 内容 render仅在 `mode``multiple``tags` 时生效 | (props) => ReactNode | - | |
| labelRender | 自定义当前选中的 label 内容 render | (label: ReactNode) => ReactNode | - | 5.15.0 | | labelRender | 自定义当前选中的 label 内容 render LabelInValueType的定义见 [LabelInValueType](https://github.com/react-component/select/blob/b39c28aa2a94e7754ebc570f200ab5fd33bd31e7/src/Select.tsx#L70) | (props: LabelInValueType) => ReactNode | - | 5.15.0 |
| tokenSeparators | 自动分词的分隔符,仅在 `mode="tags"` 时生效 | string\[] | - | | | tokenSeparators | 自动分词的分隔符,仅在 `mode="tags"` 时生效 | string\[] | - | |
| value | 指定当前选中的条目多选时为一个数组。value 数组引用未变化时Select 不会更新) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | | | value | 指定当前选中的条目多选时为一个数组。value 数组引用未变化时Select 不会更新) | string \| string\[] \| <br />number \| number\[] \| <br />LabeledValue \| LabeledValue\[] | - | |
| variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 | | variant | 形态变体 | `outlined` \| `borderless` \| `filled` | `outlined` | 5.13.0 |

View File

@ -43,4 +43,111 @@ describe('Table.Virtual', () => {
expect(errSpy).toHaveBeenCalledWith('Warning: `scroll.y` in virtual table must be number.'); expect(errSpy).toHaveBeenCalledWith('Warning: `scroll.y` in virtual table must be number.');
errSpy.mockRestore(); errSpy.mockRestore();
}); });
it('should work with edit cell', () => {
const EditableRow: React.FC = ({ ...props }) => <tr {...props} />;
const EditableCell: React.FC<React.PropsWithChildren<any>> = ({ children, ...restProps }) => (
<td {...restProps}>{children}</td>
);
const components = {
body: {
row: EditableRow,
cell: EditableCell,
},
};
const { container } = render(
<Table
virtual
components={components}
scroll={{ y: 100 }}
columns={[
{
dataIndex: 'key',
},
]}
dataSource={[
{
key: 'bamboo',
},
]}
/>,
);
expect(
container.querySelectorAll('.ant-table-wrapper .ant-table-tbody-virtual .ant-table-row'),
).toHaveLength(1);
expect(
container.querySelectorAll('.ant-table-tbody-virtual-holder .ant-table-cell'),
).toHaveLength(1);
expect(
container.querySelector('.ant-table-tbody-virtual-holder .ant-table-cell')?.textContent,
).toEqual('bamboo');
const styleMap = getComputedStyle(
container.querySelector<HTMLElement>(
'.ant-table-wrapper .ant-table-tbody-virtual .ant-table-row',
)!,
);
expect(styleMap.display).toEqual('flex');
});
it('should work with sub table', () => {
const expandedRowRender = () => {
const columns = [
{ title: 'Date', dataIndex: 'date', key: 'date' },
{ title: 'Name', dataIndex: 'name', key: 'name' },
{ title: 'Upgrade Status', dataIndex: 'upgradeNum', key: 'upgradeNum' },
];
const data = [];
for (let i = 0; i < 3; ++i) {
data.push({
key: i.toString(),
date: '2014-12-24 23:12:00',
name: 'This is production name',
upgradeNum: 'Upgraded: 56',
});
}
return <Table columns={columns} dataSource={data} pagination={false} />;
};
const { container } = render(
<Table
columns={[
{
dataIndex: 'key',
},
]}
expandable={{ expandedRowRender, defaultExpandedRowKeys: ['0'] }}
dataSource={[
{
key: '0',
},
]}
size="middle"
virtual
scroll={{ y: 200 }}
/>,
);
expect(
container.querySelectorAll('.ant-table-tbody-virtual-holder-inner > div > .ant-table-row'),
).toHaveLength(1);
expect(
container.querySelectorAll(
'.ant-table-tbody-virtual-holder-inner > div > .ant-table-row > .ant-table-cell',
)?.[1]?.textContent,
).toEqual('0');
expect(
container.querySelectorAll('.ant-table-tbody-virtual-holder .ant-table-expanded-row'),
).toHaveLength(1);
const styleMap = getComputedStyle(
container.querySelector<HTMLElement>(
'.ant-table-tbody-virtual-holder .ant-table-expanded-row .ant-table-row',
)!,
);
expect(styleMap.display).toEqual('table-row');
});
}); });

View File

@ -89,7 +89,6 @@ const columns: TableProps<RecordType>['columns'] = [
{ {
title: 'LastName', title: 'LastName',
dataIndex: 'lastName', dataIndex: 'lastName',
width: 120,
}, },
]; ];

View File

@ -15,11 +15,16 @@ const genVirtualStyle: GenerateStyle<TableToken, CSSObject> = (token) => {
[`${componentCls}-wrapper`]: { [`${componentCls}-wrapper`]: {
// ========================== Row ========================== // ========================== Row ==========================
[`${componentCls}-tbody-virtual`]: { [`${componentCls}-tbody-virtual`]: {
[`${componentCls}-row:not(tr)`]: { [`${componentCls}-tbody-virtual-holder-inner`]: {
[`
& > ${componentCls}-row,
& > div:not(${componentCls}-row) > ${componentCls}-row
`]: {
display: 'flex', display: 'flex',
boxSizing: 'border-box', boxSizing: 'border-box',
width: '100%', width: '100%',
}, },
},
[`${componentCls}-cell`]: { [`${componentCls}-cell`]: {
borderBottom: tableBorder, borderBottom: tableBorder,