chore: auto merge branches (#46338)

chore: feature merge master
This commit is contained in:
github-actions[bot] 2023-12-08 10:22:05 +00:00 committed by GitHub
commit 1aa5bc1d68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 211 additions and 59 deletions

View File

@ -1,4 +1,5 @@
{
"installCommand": "npm-install",
"sandboxes": ["antd-reproduction-template-forked-jyh2k9"],
"node": "18"
}

View File

@ -54,15 +54,18 @@ jobs:
permissions:
actions: read # for dawidd6/action-download-artifact to query and download artifacts
runs-on: ubuntu-latest
needs: upstream-workflow-summary
needs: [upstream-workflow-summary]
steps:
- name: checkout
uses: actions/checkout@v4
# We need get persist key first
- name: Download Visual Regression Ref
uses: dawidd6/action-download-artifact@v2
with:
workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }}
name: visual-regression-persist-ref
name: visual-regression-master-ref
# Save visual-regression ref to output
- name: Extra Visual Regression Ref
@ -78,36 +81,20 @@ jobs:
name: image-snapshots
path: ./
- name: Persist to Snapshot Repo
- name: Persist Image Snapshot to OSS
if: github.ref_name == 'master'
id: persist
continue-on-error: true
env:
GITHUB_TOKEN: ${{ secrets.ANTD_IMAGE_SNAP_REPO_TOKEN }}
# should push to snapshot repo firstly
# push the single folder to the repo
ALI_OSS_AK_ID: ${{ secrets.ALI_OSS_AK_ID }}
ALI_OSS_AK_SECRET: ${{ secrets.ALI_OSS_AK_SECRET }}
ALI_OSS_BUCKET: ${{ secrets.ALI_OSS_BUCKET }}
run: |
git clone https://$GITHUB_TOKEN@github.com/ant-design/antd-image-snapshots.git
rm package.json
npm i ali-oss --no-save
echo "✅ Clone Finished"
echo "🤖 Uploading"
node scripts/visual-regression-upload.js ./imageSnapshots.tar.gz --ref=${{ github.sha }}
node scripts/visual-regression-upload.js ./visual-regression-ref.txt --ref=${{ github.ref_name }}
rm antd-image-snapshots/*.txt
mv visual-regression-ref.txt antd-image-snapshots/
rm -rf antd-image-snapshots/imageSnapshots/*
tar -xzvf imageSnapshots.tar.gz -C antd-image-snapshots/imageSnapshots
echo "✅ Changes Finished"
cd antd-image-snapshots
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit -a -m 'feat: update snapshot from ${{steps.visuall-regression.outputs.id}}'
echo "✅ Commit Finished"
git push --prune https://vaugsX:$GITHUB_TOKEN@github.com/ant-design/antd-image-snapshots.git +refs/remotes/origin/*:refs/heads/*
echo "✅ Push Finished"
echo "✅ Uploaded"

View File

@ -90,5 +90,5 @@ jobs:
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: visual-regression-persist-ref
name: visual-regression-master-ref
path: ./visual-regression-ref.txt

View File

@ -88,13 +88,13 @@ const InternalForm: React.ForwardRefRenderFunction<FormInstance, FormProps> = (p
return requiredMark;
}
if (contextForm && contextForm.requiredMark !== undefined) {
return contextForm.requiredMark;
}
if (hideRequiredMark) {
return false;
}
if (contextForm && contextForm.requiredMark !== undefined) {
return contextForm.requiredMark;
}
return true;
}, [hideRequiredMark, requiredMark, contextForm]);

View File

@ -1089,16 +1089,44 @@ describe('Form', () => {
});
});
it('legacy hideRequiredMark', () => {
const { container } = render(
<Form hideRequiredMark role="form">
<Form.Item name="light" label="light" required>
<Input />
</Form.Item>
</Form>,
);
describe('legacy hideRequiredMark', () => {
it('should work', () => {
const { container } = render(
<Form hideRequiredMark role="form">
<Form.Item name="light" label="light" required>
<Input />
</Form.Item>
</Form>,
);
expect(container.querySelector('form')!).toHaveClass('ant-form-hide-required-mark');
expect(container.querySelector('form')!).toHaveClass('ant-form-hide-required-mark');
});
it('priority should be higher than CP', () => {
const { container, rerender } = render(
<ConfigProvider form={{ requiredMark: true }}>
<Form hideRequiredMark role="form">
<Form.Item name="light" label="light" required>
<Input />
</Form.Item>
</Form>
</ConfigProvider>,
);
expect(container.querySelector('form')!).toHaveClass('ant-form-hide-required-mark');
rerender(
<ConfigProvider form={{ requiredMark: undefined }}>
<Form hideRequiredMark role="form">
<Form.Item name="light" label="light" required>
<Input />
</Form.Item>
</Form>
</ConfigProvider>,
);
expect(container.querySelector('form')!).toHaveClass('ant-form-hide-required-mark');
});
});
it('form should support disabled', () => {

View File

@ -75,7 +75,6 @@ const genSpinStyle: GenerateStyle<SpinToken> = (token: SpinToken): CSSObject =>
alignItems: 'center',
flexDirection: 'column',
justifyContent: 'center',
pointerEvents: 'none',
opacity: 0,
visibility: 'hidden',
transition: `all ${token.motionDurationMid}`,

View File

@ -202,6 +202,10 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
const prefixCls = getPrefixCls('table', customizePrefixCls);
const dropdownPrefixCls = getPrefixCls('dropdown', customizeDropdownPrefixCls);
const [, token] = useToken();
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId] = useStyle(prefixCls, rootCls);
const mergedExpandable: ExpandableConfig<RecordType> = {
childrenColumnName: legacyChildrenColumnName,
expandIconColumnIndex,
@ -349,6 +353,7 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
mergedColumns,
onFilterChange,
getPopupContainer: getPopupContainer || getContextPopupContainer,
rootClassName: classNames(rootClassName, rootCls),
});
const mergedData = getFilterData(sortedData, filterStates);
@ -538,10 +543,6 @@ const InternalTable = <RecordType extends AnyObject = AnyObject>(
};
}
const [, token] = useToken();
const rootCls = useCSSVarCls(prefixCls);
const [wrapCSSVar, hashId] = useStyle(prefixCls, rootCls);
const wrapperClassNames = classNames(
rootCls,
`${prefixCls}-wrapper`,

View File

@ -133,6 +133,7 @@ export interface FilterDropdownProps<RecordType> {
locale: TableLocale;
getPopupContainer?: GetPopupContainer;
filterResetToDefaultFilteredValue?: boolean;
rootClassName?: string;
}
function wrapStringListType(keys?: FilterKey) {
@ -154,6 +155,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
locale,
children,
getPopupContainer,
rootClassName,
} = props;
const {
@ -524,6 +526,7 @@ function FilterDropdown<RecordType>(props: FilterDropdownProps<RecordType>) {
onOpenChange={onVisibleChange}
getPopupContainer={getPopupContainer}
placement={direction === 'rtl' ? 'bottomLeft' : 'bottomRight'}
rootClassName={rootClassName}
>
<span
role="button"

View File

@ -76,6 +76,7 @@ function injectFilter<RecordType>(
triggerFilter: (filterState: FilterState<RecordType>) => void,
getPopupContainer?: GetPopupContainer,
pos?: string,
rootClassName?: string,
): ColumnsType<RecordType> {
return columns.map((column, index) => {
const columnPos = getColumnPos(index, pos);
@ -103,6 +104,7 @@ function injectFilter<RecordType>(
triggerFilter={triggerFilter}
locale={locale}
getPopupContainer={getPopupContainer}
rootClassName={rootClassName}
>
{renderColumnTitle(column.title, renderProps)}
</FilterDropdown>
@ -122,6 +124,7 @@ function injectFilter<RecordType>(
triggerFilter,
getPopupContainer,
columnPos,
rootClassName,
),
};
}
@ -184,6 +187,7 @@ interface FilterConfig<RecordType> {
filterStates: FilterState<RecordType>[],
) => void;
getPopupContainer?: GetPopupContainer;
rootClassName?: string;
}
const getMergedColumns = <RecordType extends unknown>(
@ -203,6 +207,7 @@ function useFilter<RecordType>({
onFilterChange,
getPopupContainer,
locale: tableLocale,
rootClassName,
}: FilterConfig<RecordType>): [
TransformColumns<RecordType>,
FilterState<RecordType>[],
@ -282,6 +287,8 @@ function useFilter<RecordType>({
tableLocale,
triggerFilter,
getPopupContainer,
undefined,
rootClassName,
);
return [transformColumns, mergedFilterStates, filters];

View File

@ -5,7 +5,7 @@ order: 5
title: Internationalization
---
The default language of `antd@2.x` is currently English. If you wish to use other languages, follow the instructions below.
The default language of `antd` is currently English. If you wish to use other languages, follow the instructions below.
## ConfigProvider

View File

@ -98,7 +98,8 @@
"test:update": "jest --config .jest.js --no-cache -u",
"token-meta": "tsx scripts/generate-token-meta.ts",
"tsc": "tsc --noEmit",
"version": "tsx scripts/generate-version.ts"
"version": "tsx scripts/generate-version.ts",
"npm-install": "npm install"
},
"lint-staged": {
"*.{ts,tsx,js,jsx}": "biome format --write",
@ -183,6 +184,7 @@
"@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.1",
"@types/ali-oss": "^6.16.11",
"@types/fs-extra": "^11.0.4",
"@types/gtag.js": "^0.0.18",
"@types/http-server": "^0.12.4",
@ -209,6 +211,7 @@
"@types/warning": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"ali-oss": "^6.18.1",
"antd-img-crop": "^4.17.0",
"antd-style": "^3.5.2",
"antd-token-previewer": "^2.0.5",

View File

@ -1,5 +0,0 @@
| expected | actual | diff |
| --- | --- | --- |
| ![master image name](test/fixtures/4a.png) | ![pr image name](test/fixtures/4b.png) | ![diff](test/fixtures/4diff.png) |
| ![master image name](test/fixtures/3a.png) | ![pr image name](test/fixtures/3b.png) | ![diff](test/fixtures/3diff.png) |
| ![master image name](test/fixtures/6a.png) | ![pr image name](test/fixtures/6b.png) | ![diff](test/fixtures/6diff.png) |

View File

@ -0,0 +1,121 @@
/* eslint-disable no-restricted-syntax, no-console */
// Attention: use all node builtin modules except `ali-oss`
// Must keep our ak/sk safe
// eslint-disable-next-line import/no-extraneous-dependencies
const OSS = require('ali-oss');
const path = require('path');
const fs = require('fs');
const { assert } = require('console');
// node scripts/visual-regression-upload.js ./visualRegressionReport.tar.gz --ref=pr-id
// node scripts/visual-regression-upload.js ./imageSnapshots.tar.gz --ref=master-commitId
const args = process.argv.slice(2);
if (args.length < 2) {
console.error('Usage: node scripts/visual-regression-upload.js <tarFilePath> --ref=<refValue>');
process.exit(1);
}
/**
* Extract the tar file path and ref value from the cli arguments
* @param {string[]} cliArgs
*/
function parseArgs(cliArgs) {
const filepath = cliArgs[0];
let refValue = '';
for (let i = 1; i < cliArgs.length; i++) {
if (cliArgs[i].startsWith('--ref=')) {
refValue = cliArgs[i].substring(6);
break;
}
}
return [filepath, refValue];
}
async function walkDir(dirPath) {
const fileList = [];
const files = await fs.promises.readdir(dirPath);
for (const file of files) {
const filePath = path.join(dirPath, file);
const fileStat = fs.statSync(filePath);
if (fileStat.isDirectory()) {
// Recursively call this func for subdirs
// eslint-disable-next-line no-await-in-loop
fileList.push(...(await walkDir(filePath)));
} else {
fileList.push(filePath);
}
}
return fileList;
}
/**
*
* @param {import('ali-oss')} client
* @param {*} filePath
* @param {*} refValue
*/
async function uploadFile(client, filePath, refValue) {
const headers = {
// https://help.aliyun.com/zh/oss/user-guide/object-acl
'x-oss-object-acl': 'public-read',
// https://help.aliyun.com/zh/oss/developer-reference/prevent-objects-from-being-overwritten-by-objects-that-have-the-same-names-3
'x-oss-forbid-overwrite': 'false',
};
console.log('Uploading file: %s', filePath);
try {
const targetFilePath = path.relative(process.cwd(), filePath);
const r1 = await client.put(`${refValue}/${targetFilePath}`, filePath, { headers });
console.log('Uploading file successfully: %s', r1.name);
} catch (err) {
console.error('Uploading file failed: %s', err);
process.exit(1);
}
}
async function boot() {
const [filepath, refValue] = parseArgs(args);
assert(filepath, 'filepath is required');
assert(refValue, 'refValue is required');
const fileOrFolderName = filepath;
// check if exists
const filePath = path.resolve(process.cwd(), fileOrFolderName);
if (!fs.existsSync(filePath)) {
console.error('File not exists: %s', filePath);
process.exit(1);
}
const client = new OSS({
endpoint: 'oss-cn-shanghai.aliyuncs.com',
accessKeyId: process.env.ALI_OSS_AK_ID,
accessKeySecret: process.env.ALI_OSS_AK_SECRET,
bucket: process.env.ALI_OSS_BUCKET,
});
// if is a file then upload it directly
const stat = fs.statSync(filePath);
if (stat.isFile()) {
await uploadFile(client, filePath, refValue);
return;
}
if (stat.isDirectory()) {
const fileList = await walkDir(filePath);
for (const file of fileList) {
// eslint-disable-next-line no-await-in-loop
await uploadFile(client, file, refValue);
}
}
}
boot();

View File

@ -3,6 +3,7 @@ import React from 'react';
// eslint-disable-next-line import/no-unresolved
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import dayjs from 'dayjs';
import path from 'path';
import { globSync } from 'glob';
import { configureToMatchImageSnapshot } from 'jest-image-snapshot';
import MockDate from 'mockdate';
@ -29,7 +30,11 @@ interface ImageTestOptions {
}
// eslint-disable-next-line jest/no-export
export default function imageTest(component: React.ReactElement, options: ImageTestOptions) {
export default function imageTest(
component: React.ReactElement,
identifier: string,
options: ImageTestOptions,
) {
function test(name: string, themedComponent: React.ReactElement) {
it(name, async () => {
await jestPuppeteer.resetPage();
@ -80,7 +85,9 @@ export default function imageTest(component: React.ReactElement, options: ImageT
fullPage: !options.onlyViewport,
});
expect(image).toMatchImageSnapshot();
expect(image).toMatchImageSnapshot({
customSnapshotIdentifier: `${identifier}-${name.replace(/\s/g, '-')}`,
});
MockDate.reset();
page.off('request', onRequestHandle);
@ -96,7 +103,7 @@ export default function imageTest(component: React.ReactElement, options: ImageT
</div>,
);
test(
`[CSS Var] component image screenshot should correct ${key}`,
`component image screenshot should correct ${key}.css-var`,
<div style={{ background: key === 'dark' ? '#000' : '', padding: `24px 12px` }} key={key}>
<div>CSS Var</div>
<ConfigProvider theme={{ algorithm, cssVar: true }}>{component}</ConfigProvider>
@ -115,7 +122,7 @@ export default function imageTest(component: React.ReactElement, options: ImageT
</>,
);
test(
`[CSS Var] component image screenshot should correct`,
`component image screenshot should correct.css-var`,
<>
<div>CSS Var</div>
{Object.entries(themes).map(([key, algorithm]) => (
@ -151,7 +158,7 @@ export function imageDemoTest(component: string, options: Options = {}) {
if (typeof Demo === 'function') {
Demo = <Demo />;
}
imageTest(Demo, {
imageTest(Demo, `${component}-${path.basename(file, '.tsx')}`, {
onlyViewport:
options.onlyViewport === true ||
(Array.isArray(options.onlyViewport) &&