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"], "sandboxes": ["antd-reproduction-template-forked-jyh2k9"],
"node": "18" "node": "18"
} }

View File

@ -54,15 +54,18 @@ jobs:
permissions: permissions:
actions: read # for dawidd6/action-download-artifact to query and download artifacts actions: read # for dawidd6/action-download-artifact to query and download artifacts
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: upstream-workflow-summary needs: [upstream-workflow-summary]
steps: steps:
- name: checkout
uses: actions/checkout@v4
# We need get persist key first # We need get persist key first
- name: Download Visual Regression Ref - name: Download Visual Regression Ref
uses: dawidd6/action-download-artifact@v2 uses: dawidd6/action-download-artifact@v2
with: with:
workflow: ${{ github.event.workflow_run.workflow_id }} workflow: ${{ github.event.workflow_run.workflow_id }}
run_id: ${{ github.event.workflow_run.id }} run_id: ${{ github.event.workflow_run.id }}
name: visual-regression-persist-ref name: visual-regression-master-ref
# Save visual-regression ref to output # Save visual-regression ref to output
- name: Extra Visual Regression Ref - name: Extra Visual Regression Ref
@ -78,36 +81,20 @@ jobs:
name: image-snapshots name: image-snapshots
path: ./ path: ./
- name: Persist to Snapshot Repo - name: Persist Image Snapshot to OSS
if: github.ref_name == 'master' if: github.ref_name == 'master'
id: persist id: persist
continue-on-error: true continue-on-error: true
env: env:
GITHUB_TOKEN: ${{ secrets.ANTD_IMAGE_SNAP_REPO_TOKEN }} ALI_OSS_AK_ID: ${{ secrets.ALI_OSS_AK_ID }}
# should push to snapshot repo firstly ALI_OSS_AK_SECRET: ${{ secrets.ALI_OSS_AK_SECRET }}
# push the single folder to the repo ALI_OSS_BUCKET: ${{ secrets.ALI_OSS_BUCKET }}
run: | 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 echo "✅ Uploaded"
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"

View File

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

View File

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

View File

@ -1089,16 +1089,44 @@ describe('Form', () => {
}); });
}); });
it('legacy hideRequiredMark', () => { describe('legacy hideRequiredMark', () => {
const { container } = render( it('should work', () => {
<Form hideRequiredMark role="form"> const { container } = render(
<Form.Item name="light" label="light" required> <Form hideRequiredMark role="form">
<Input /> <Form.Item name="light" label="light" required>
</Form.Item> <Input />
</Form>, </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', () => { it('form should support disabled', () => {

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ order: 5
title: Internationalization 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 ## ConfigProvider

View File

@ -98,7 +98,8 @@
"test:update": "jest --config .jest.js --no-cache -u", "test:update": "jest --config .jest.js --no-cache -u",
"token-meta": "tsx scripts/generate-token-meta.ts", "token-meta": "tsx scripts/generate-token-meta.ts",
"tsc": "tsc --noEmit", "tsc": "tsc --noEmit",
"version": "tsx scripts/generate-version.ts" "version": "tsx scripts/generate-version.ts",
"npm-install": "npm install"
}, },
"lint-staged": { "lint-staged": {
"*.{ts,tsx,js,jsx}": "biome format --write", "*.{ts,tsx,js,jsx}": "biome format --write",
@ -183,6 +184,7 @@
"@testing-library/jest-dom": "^6.1.4", "@testing-library/jest-dom": "^6.1.4",
"@testing-library/react": "^14.1.2", "@testing-library/react": "^14.1.2",
"@testing-library/user-event": "^14.5.1", "@testing-library/user-event": "^14.5.1",
"@types/ali-oss": "^6.16.11",
"@types/fs-extra": "^11.0.4", "@types/fs-extra": "^11.0.4",
"@types/gtag.js": "^0.0.18", "@types/gtag.js": "^0.0.18",
"@types/http-server": "^0.12.4", "@types/http-server": "^0.12.4",
@ -209,6 +211,7 @@
"@types/warning": "^3.0.3", "@types/warning": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^6.12.0", "@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0", "@typescript-eslint/parser": "^6.12.0",
"ali-oss": "^6.18.1",
"antd-img-crop": "^4.17.0", "antd-img-crop": "^4.17.0",
"antd-style": "^3.5.2", "antd-style": "^3.5.2",
"antd-token-previewer": "^2.0.5", "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 // eslint-disable-next-line import/no-unresolved
import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs'; import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import path from 'path';
import { globSync } from 'glob'; import { globSync } from 'glob';
import { configureToMatchImageSnapshot } from 'jest-image-snapshot'; import { configureToMatchImageSnapshot } from 'jest-image-snapshot';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
@ -29,7 +30,11 @@ interface ImageTestOptions {
} }
// eslint-disable-next-line jest/no-export // 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) { function test(name: string, themedComponent: React.ReactElement) {
it(name, async () => { it(name, async () => {
await jestPuppeteer.resetPage(); await jestPuppeteer.resetPage();
@ -80,7 +85,9 @@ export default function imageTest(component: React.ReactElement, options: ImageT
fullPage: !options.onlyViewport, fullPage: !options.onlyViewport,
}); });
expect(image).toMatchImageSnapshot(); expect(image).toMatchImageSnapshot({
customSnapshotIdentifier: `${identifier}-${name.replace(/\s/g, '-')}`,
});
MockDate.reset(); MockDate.reset();
page.off('request', onRequestHandle); page.off('request', onRequestHandle);
@ -96,7 +103,7 @@ export default function imageTest(component: React.ReactElement, options: ImageT
</div>, </div>,
); );
test( 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 style={{ background: key === 'dark' ? '#000' : '', padding: `24px 12px` }} key={key}>
<div>CSS Var</div> <div>CSS Var</div>
<ConfigProvider theme={{ algorithm, cssVar: true }}>{component}</ConfigProvider> <ConfigProvider theme={{ algorithm, cssVar: true }}>{component}</ConfigProvider>
@ -115,7 +122,7 @@ export default function imageTest(component: React.ReactElement, options: ImageT
</>, </>,
); );
test( test(
`[CSS Var] component image screenshot should correct`, `component image screenshot should correct.css-var`,
<> <>
<div>CSS Var</div> <div>CSS Var</div>
{Object.entries(themes).map(([key, algorithm]) => ( {Object.entries(themes).map(([key, algorithm]) => (
@ -151,7 +158,7 @@ export function imageDemoTest(component: string, options: Options = {}) {
if (typeof Demo === 'function') { if (typeof Demo === 'function') {
Demo = <Demo />; Demo = <Demo />;
} }
imageTest(Demo, { imageTest(Demo, `${component}-${path.basename(file, '.tsx')}`, {
onlyViewport: onlyViewport:
options.onlyViewport === true || options.onlyViewport === true ||
(Array.isArray(options.onlyViewport) && (Array.isArray(options.onlyViewport) &&