mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-24 02:59:58 +08:00
chore: auto merge branches (#51642)
Some checks failed
Publish Any Commit / build (push) Has been cancelled
🔀 Sync mirror to Gitee / mirror (push) Has been cancelled
✅ test / lint (push) Has been cancelled
✅ test / test-react-legacy (16, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (16, 2/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 2/2) (push) Has been cancelled
✅ test / test-node (push) Has been cancelled
✅ test / test-react-latest (dom, 1/2) (push) Has been cancelled
✅ test / test-react-latest (dom, 2/2) (push) Has been cancelled
✅ test / build (push) Has been cancelled
✅ test / test lib/es module (es, 1/2) (push) Has been cancelled
✅ test / test lib/es module (es, 2/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 1/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 2/2) (push) Has been cancelled
👁️ Visual Regression Persist Start / test image (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 2/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 2/2) (push) Has been cancelled
✅ test / test-coverage (push) Has been cancelled
Some checks failed
Publish Any Commit / build (push) Has been cancelled
🔀 Sync mirror to Gitee / mirror (push) Has been cancelled
✅ test / lint (push) Has been cancelled
✅ test / test-react-legacy (16, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (16, 2/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 1/2) (push) Has been cancelled
✅ test / test-react-legacy (17, 2/2) (push) Has been cancelled
✅ test / test-node (push) Has been cancelled
✅ test / test-react-latest (dom, 1/2) (push) Has been cancelled
✅ test / test-react-latest (dom, 2/2) (push) Has been cancelled
✅ test / build (push) Has been cancelled
✅ test / test lib/es module (es, 1/2) (push) Has been cancelled
✅ test / test lib/es module (es, 2/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 1/2) (push) Has been cancelled
✅ test / test lib/es module (lib, 2/2) (push) Has been cancelled
👁️ Visual Regression Persist Start / test image (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist, 2/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 1/2) (push) Has been cancelled
✅ test / test-react-latest-dist (dist-min, 2/2) (push) Has been cancelled
✅ test / test-coverage (push) Has been cancelled
chore: merge master into feature
This commit is contained in:
commit
1b8e956a62
@ -58,7 +58,13 @@ jobs:
|
|||||||
if (comment.body.includes('VISUAL_DIFF_FAILED')) {
|
if (comment.body.includes('VISUAL_DIFF_FAILED')) {
|
||||||
hasDiffFailed = true;
|
hasDiffFailed = true;
|
||||||
}
|
}
|
||||||
if (comment.body.includes('- [x] Visual diff is acceptable')) {
|
|
||||||
|
// https://regex101.com/r/kLjudz/1
|
||||||
|
const RE = /(?<=\>\s\[!IMPORTANT\].*?- \[ \])/s;
|
||||||
|
if (
|
||||||
|
comment.body.includes('- [x] Visual diff is acceptable') &&
|
||||||
|
comment.body.match(RE) == null /** 检查 IMPORTANT 是否存在未勾选的 */
|
||||||
|
) {
|
||||||
hasMemberApprove = true;
|
hasMemberApprove = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -474,4 +474,10 @@ describe('Button', () => {
|
|||||||
'--ant-button-solid-text-color': '#000',
|
'--ant-button-solid-text-color': '#000',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('autoFocus should work', () => {
|
||||||
|
const { container } = render(<Button autoFocus>button</Button>);
|
||||||
|
|
||||||
|
expect(container.querySelector('button')).toBe(document.activeElement);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React, { Children, createRef, useContext, useEffect, useMemo, useState } from 'react';
|
import React, { Children, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import omit from 'rc-util/lib/omit';
|
import omit from 'rc-util/lib/omit';
|
||||||
import { composeRef } from 'rc-util/lib/ref';
|
import { useComposeRef } from 'rc-util/lib/ref';
|
||||||
|
|
||||||
import { devUseWarning } from '../_util/warning';
|
import { devUseWarning } from '../_util/warning';
|
||||||
import Wave from '../_util/wave';
|
import Wave from '../_util/wave';
|
||||||
@ -119,6 +119,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
classNames: customClassNames,
|
classNames: customClassNames,
|
||||||
style: customStyle = {},
|
style: customStyle = {},
|
||||||
autoInsertSpace,
|
autoInsertSpace,
|
||||||
|
autoFocus,
|
||||||
...rest
|
...rest
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -162,13 +163,15 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
|
|
||||||
const [hasTwoCNChar, setHasTwoCNChar] = useState<boolean>(false);
|
const [hasTwoCNChar, setHasTwoCNChar] = useState<boolean>(false);
|
||||||
|
|
||||||
const internalRef = createRef<HTMLButtonElement | HTMLAnchorElement>();
|
const buttonRef = useRef<HTMLButtonElement | HTMLAnchorElement>();
|
||||||
|
|
||||||
const buttonRef = composeRef(ref, internalRef);
|
const mergedRef = useComposeRef(ref, buttonRef);
|
||||||
|
|
||||||
const needInserted =
|
const needInserted =
|
||||||
Children.count(children) === 1 && !icon && !isUnBorderedButtonVariant(mergedVariant);
|
Children.count(children) === 1 && !icon && !isUnBorderedButtonVariant(mergedVariant);
|
||||||
|
|
||||||
|
// ========================= Effect =========================
|
||||||
|
// Loading
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let delayTimer: ReturnType<typeof setTimeout> | null = null;
|
let delayTimer: ReturnType<typeof setTimeout> | null = null;
|
||||||
if (loadingOrDelay.delay > 0) {
|
if (loadingOrDelay.delay > 0) {
|
||||||
@ -190,12 +193,13 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
return cleanupTimer;
|
return cleanupTimer;
|
||||||
}, [loadingOrDelay]);
|
}, [loadingOrDelay]);
|
||||||
|
|
||||||
|
// Two chinese characters check
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// FIXME: for HOC usage like <FormatMessage />
|
// FIXME: for HOC usage like <FormatMessage />
|
||||||
if (!buttonRef || !(buttonRef as any).current || !mergedInsertSpace) {
|
if (!buttonRef.current || !mergedInsertSpace) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const buttonText = (buttonRef as any).current.textContent;
|
const buttonText = buttonRef.current.textContent || '';
|
||||||
if (needInserted && isTwoCNChar(buttonText)) {
|
if (needInserted && isTwoCNChar(buttonText)) {
|
||||||
if (!hasTwoCNChar) {
|
if (!hasTwoCNChar) {
|
||||||
setHasTwoCNChar(true);
|
setHasTwoCNChar(true);
|
||||||
@ -203,8 +207,16 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
} else if (hasTwoCNChar) {
|
} else if (hasTwoCNChar) {
|
||||||
setHasTwoCNChar(false);
|
setHasTwoCNChar(false);
|
||||||
}
|
}
|
||||||
}, [buttonRef]);
|
});
|
||||||
|
|
||||||
|
// Auto focus
|
||||||
|
useEffect(() => {
|
||||||
|
if (autoFocus && buttonRef.current) {
|
||||||
|
buttonRef.current.focus();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// ========================= Events =========================
|
||||||
const handleClick = React.useCallback(
|
const handleClick = React.useCallback(
|
||||||
(e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
|
(e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
|
||||||
// FIXME: https://github.com/ant-design/ant-design/issues/30207
|
// FIXME: https://github.com/ant-design/ant-design/issues/30207
|
||||||
@ -217,6 +229,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
[props.onClick, innerLoading, mergedDisabled],
|
[props.onClick, innerLoading, mergedDisabled],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ========================== Warn ==========================
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
const warning = devUseWarning('Button');
|
const warning = devUseWarning('Button');
|
||||||
|
|
||||||
@ -233,6 +246,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ========================== Size ==========================
|
||||||
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
|
const { compactSize, compactItemClassnames } = useCompactItemContext(prefixCls, direction);
|
||||||
|
|
||||||
const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined };
|
const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined };
|
||||||
@ -245,6 +259,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
|
|
||||||
const linkButtonRestProps = omit(rest as ButtonProps & { navigate: any }, ['navigate']);
|
const linkButtonRestProps = omit(rest as ButtonProps & { navigate: any }, ['navigate']);
|
||||||
|
|
||||||
|
// ========================= Render =========================
|
||||||
const classes = classNames(
|
const classes = classNames(
|
||||||
prefixCls,
|
prefixCls,
|
||||||
hashId,
|
hashId,
|
||||||
@ -301,7 +316,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
href={mergedDisabled ? undefined : linkButtonRestProps.href}
|
href={mergedDisabled ? undefined : linkButtonRestProps.href}
|
||||||
style={fullStyle}
|
style={fullStyle}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
ref={buttonRef as React.Ref<HTMLAnchorElement>}
|
ref={mergedRef as React.Ref<HTMLAnchorElement>}
|
||||||
tabIndex={mergedDisabled ? -1 : 0}
|
tabIndex={mergedDisabled ? -1 : 0}
|
||||||
>
|
>
|
||||||
{iconNode}
|
{iconNode}
|
||||||
@ -318,7 +333,7 @@ const InternalCompoundedButton = React.forwardRef<
|
|||||||
style={fullStyle}
|
style={fullStyle}
|
||||||
onClick={handleClick}
|
onClick={handleClick}
|
||||||
disabled={mergedDisabled}
|
disabled={mergedDisabled}
|
||||||
ref={buttonRef as React.Ref<HTMLButtonElement>}
|
ref={mergedRef as React.Ref<HTMLButtonElement>}
|
||||||
>
|
>
|
||||||
{iconNode}
|
{iconNode}
|
||||||
{kids}
|
{kids}
|
||||||
|
@ -78,6 +78,17 @@ const ErrorList: React.FC<ErrorListProps> = ({
|
|||||||
];
|
];
|
||||||
}, [help, helpStatus, debounceErrors, debounceWarnings]);
|
}, [help, helpStatus, debounceErrors, debounceWarnings]);
|
||||||
|
|
||||||
|
const filledKeyFullKeyList = React.useMemo<ErrorEntity[]>(() => {
|
||||||
|
const keysCount: Record<string, number> = {};
|
||||||
|
fullKeyList.forEach(({ key }) => {
|
||||||
|
keysCount[key] = (keysCount[key] || 0) + 1;
|
||||||
|
});
|
||||||
|
return fullKeyList.map((entity, index) => ({
|
||||||
|
...entity,
|
||||||
|
key: keysCount[entity.key] > 1 ? `${entity.key}-fallback-${index}` : entity.key,
|
||||||
|
}));
|
||||||
|
}, [fullKeyList]);
|
||||||
|
|
||||||
const helpProps: { id?: string } = {};
|
const helpProps: { id?: string } = {};
|
||||||
|
|
||||||
if (fieldId) {
|
if (fieldId) {
|
||||||
@ -88,7 +99,7 @@ const ErrorList: React.FC<ErrorListProps> = ({
|
|||||||
<CSSMotion
|
<CSSMotion
|
||||||
motionDeadline={collapseMotion.motionDeadline}
|
motionDeadline={collapseMotion.motionDeadline}
|
||||||
motionName={`${prefixCls}-show-help`}
|
motionName={`${prefixCls}-show-help`}
|
||||||
visible={!!fullKeyList.length}
|
visible={!!filledKeyFullKeyList.length}
|
||||||
onVisibleChanged={onVisibleChanged}
|
onVisibleChanged={onVisibleChanged}
|
||||||
>
|
>
|
||||||
{(holderProps) => {
|
{(holderProps) => {
|
||||||
@ -109,7 +120,7 @@ const ErrorList: React.FC<ErrorListProps> = ({
|
|||||||
role="alert"
|
role="alert"
|
||||||
>
|
>
|
||||||
<CSSMotionList
|
<CSSMotionList
|
||||||
keys={fullKeyList}
|
keys={filledKeyFullKeyList}
|
||||||
{...initCollapseMotion(prefixCls)}
|
{...initCollapseMotion(prefixCls)}
|
||||||
motionName={`${prefixCls}-show-help-item`}
|
motionName={`${prefixCls}-show-help-item`}
|
||||||
component={false}
|
component={false}
|
||||||
|
@ -2459,4 +2459,60 @@ describe('Form', () => {
|
|||||||
fireEvent.click(container.querySelector('input')!);
|
fireEvent.click(container.querySelector('input')!);
|
||||||
expect(container.querySelector('input')?.checked).toBeTruthy();
|
expect(container.querySelector('input')?.checked).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('not warning for react key', async () => {
|
||||||
|
const MockInput = (props: { onChange?: (value: number[]) => void }) => (
|
||||||
|
<Input
|
||||||
|
onChange={({ target: { value } }) => {
|
||||||
|
props.onChange?.(value.split(',').map(Number));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const { container } = render(
|
||||||
|
<Form>
|
||||||
|
<Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="test"
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
type: 'array',
|
||||||
|
defaultField: {
|
||||||
|
type: 'number',
|
||||||
|
min: 10,
|
||||||
|
message: 'LESS_THAN_10',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<MockInput />
|
||||||
|
</Form.Item>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>,
|
||||||
|
);
|
||||||
|
|
||||||
|
function expectErrors(errors: string[]) {
|
||||||
|
expect(container.querySelectorAll('.ant-form-item-explain-error')).toHaveLength(
|
||||||
|
errors.length,
|
||||||
|
);
|
||||||
|
errors.forEach((error, index) => {
|
||||||
|
expect(container.querySelectorAll('.ant-form-item-explain-error')[index]).toHaveTextContent(
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// user type something and clear
|
||||||
|
await changeValue(0, '1');
|
||||||
|
expectErrors(['LESS_THAN_10']);
|
||||||
|
|
||||||
|
await changeValue(0, '1,1');
|
||||||
|
expectErrors(['LESS_THAN_10', 'LESS_THAN_10']);
|
||||||
|
|
||||||
|
await changeValue(0, '1');
|
||||||
|
expectErrors(['LESS_THAN_10']);
|
||||||
|
|
||||||
|
await changeValue(0, '100');
|
||||||
|
expectErrors([]);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -52,13 +52,13 @@ Common props ref:[Common props](/docs/react/common-props)
|
|||||||
|
|
||||||
### Timeline
|
### Timeline
|
||||||
|
|
||||||
| Property | Description | Type | Default |
|
| Property | Description | Type | Default | Version |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| mode | By sending `alternate` the timeline will distribute the nodes to the left and right | `left` \| `alternate` \| `right` | - |
|
| mode | By sending `alternate` the timeline will distribute the nodes to the left and right | `left` \| `alternate` \| `right` | - | |
|
||||||
| pending | Set the last ghost node's existence or its content | ReactNode | false |
|
| pending | Set the last ghost node's existence or its content | ReactNode | false | |
|
||||||
| pendingDot | Set the dot of the last ghost node when pending is true | ReactNode | <LoadingOutlined /> |
|
| pendingDot | Set the dot of the last ghost node when pending is true | ReactNode | <LoadingOutlined /> | |
|
||||||
| reverse | Whether reverse nodes or not | boolean | false |
|
| reverse | Whether reverse nodes or not | boolean | false | |
|
||||||
| items | Each node of timeline | [Items](#Items)[] | 5.2.0 |
|
| items | Each node of timeline | [Items](#Items)[] | - | 5.2.0 |
|
||||||
|
|
||||||
### Items
|
### Items
|
||||||
|
|
||||||
|
@ -53,13 +53,13 @@ return (
|
|||||||
|
|
||||||
### Timeline
|
### Timeline
|
||||||
|
|
||||||
| 参数 | 说明 | 类型 | 默认值 |
|
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||||
| --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| mode | 通过设置 `mode` 可以改变时间轴和内容的相对位置 | `left` \| `alternate` \| `right` | - |
|
| mode | 通过设置 `mode` 可以改变时间轴和内容的相对位置 | `left` \| `alternate` \| `right` | - | |
|
||||||
| pending | 指定最后一个幽灵节点是否存在或内容 | ReactNode | false |
|
| pending | 指定最后一个幽灵节点是否存在或内容 | ReactNode | false | |
|
||||||
| pendingDot | 当最后一个幽灵节点存在時,指定其时间图点 | ReactNode | <LoadingOutlined /> |
|
| pendingDot | 当最后一个幽灵节点存在時,指定其时间图点 | ReactNode | <LoadingOutlined /> | |
|
||||||
| reverse | 节点排序 | boolean | false |
|
| reverse | 节点排序 | boolean | false | |
|
||||||
| items | 选项配置 | [Items](#Items)[] | 5.2.0 |
|
| items | 选项配置 | [Items](#Items)[] | - | 5.2.0 |
|
||||||
|
|
||||||
### Items
|
### Items
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import pixelmatch from 'pixelmatch';
|
|||||||
import { PNG } from 'pngjs';
|
import { PNG } from 'pngjs';
|
||||||
import sharp from 'sharp';
|
import sharp from 'sharp';
|
||||||
import simpleGit from 'simple-git';
|
import simpleGit from 'simple-git';
|
||||||
|
import filter from 'lodash/filter';
|
||||||
|
|
||||||
import markdown2Html from './convert';
|
import markdown2Html from './convert';
|
||||||
|
|
||||||
@ -283,9 +284,17 @@ ${fullReport}
|
|||||||
|
|
||||||
let diffCount = 0;
|
let diffCount = 0;
|
||||||
|
|
||||||
|
// Summary
|
||||||
|
const badCount = badCases.length;
|
||||||
|
const commentReportLimit = isLocalEnv ? badCount : 8;
|
||||||
|
|
||||||
|
const changedCount = filter(badCases, { type: 'changed' }).length;
|
||||||
|
const removedCount = filter(badCases, { type: 'removed' }).length;
|
||||||
|
const addedCount = filter(badCases, { type: 'added' }).length;
|
||||||
|
|
||||||
for (const badCase of badCases) {
|
for (const badCase of badCases) {
|
||||||
diffCount += 1;
|
diffCount += 1;
|
||||||
if (diffCount <= 10) {
|
if (diffCount <= commentReportLimit) {
|
||||||
// 将图片下方增加文件名
|
// 将图片下方增加文件名
|
||||||
reportMdStr += generateLineReport(badCase, publicPath, currentRef, true);
|
reportMdStr += generateLineReport(badCase, publicPath, currentRef, true);
|
||||||
}
|
}
|
||||||
@ -293,18 +302,37 @@ ${fullReport}
|
|||||||
fullVersionMd += generateLineReport(badCase, publicPath, currentRef, false);
|
fullVersionMd += generateLineReport(badCase, publicPath, currentRef, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
reportMdStr += `\n\nCheck <a href="${htmlReportLink}" target="_blank">Full Report</a> for details`;
|
const hasMore = badCount > commentReportLimit;
|
||||||
|
|
||||||
|
if (hasMore) {
|
||||||
|
reportMdStr += [
|
||||||
|
'\r',
|
||||||
|
'> [!WARNING]',
|
||||||
|
`> There are more diffs not shown in the table. Please check the <a href="${htmlReportLink}" target="_blank">Full Report</a> for details.`,
|
||||||
|
'\r',
|
||||||
|
].join('\n');
|
||||||
|
}
|
||||||
|
|
||||||
// tips for comment `Pass Visual Diff` will pass the CI
|
// tips for comment `Pass Visual Diff` will pass the CI
|
||||||
if (!passed) {
|
if (!passed) {
|
||||||
reportMdStr += `
|
const summaryLine = [
|
||||||
|
changedCount > 0 && `🔄 **${changedCount}** changed`,
|
||||||
|
removedCount > 0 && `🛑 **${removedCount}** removed`,
|
||||||
|
addedCount > 0 && `🆕 **${addedCount}** added`,
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join(', ');
|
||||||
|
|
||||||
-----
|
reportMdStr += [
|
||||||
|
'\n---\n',
|
||||||
If you think the visual diff is acceptable, please check:
|
'> [!IMPORTANT]',
|
||||||
|
`> There are **${badCount}** diffs found in this PR: ${summaryLine}.`,
|
||||||
- [ ] Visual diff is acceptable
|
'> **Please check all items:**',
|
||||||
`;
|
hasMore && '> - [ ] Checked all diffs in the full report',
|
||||||
|
'> - [ ] Visual diff is acceptable',
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert fullVersionMd to html
|
// convert fullVersionMd to html
|
||||||
|
Loading…
Reference in New Issue
Block a user