diff --git a/.github/workflows/ui-upload.yml b/.github/workflows/ui-upload.yml index 632e53a1e8..107b931e94 100644 --- a/.github/workflows/ui-upload.yml +++ b/.github/workflows/ui-upload.yml @@ -1,6 +1,5 @@ # Upload 📷 UI snapshots to argos server, help visual regression testing. - -name: UI Upload +name: 📷 UI Upload on: workflow_run: @@ -21,25 +20,8 @@ jobs: if: > github.event.workflow_run.conclusion == 'success' steps: - - name: Download commit artifact - uses: dawidd6/action-download-artifact@v2 - with: - workflow: ${{ github.event.workflow_run.workflow_id }} - name: commit - - - name: Save commit id - id: commit - run: echo "::set-output name=id::$( ./commit.txt - - - name: Save commit - if: github.event_name == 'push' - run: echo ${{ github.sha }} > ./commit.txt - - - name: Upload commit - if: ${{ always() }} - uses: actions/upload-artifact@v3 - with: - name: commit - path: ./commit.txt - - - name: Save branch - if: github.event_name == 'pull_request' && github.base_ref == 'master' - run: echo pull/${{ github.event.pull_request.number }}/merge > ./branch.txt - - - name: Save branch - if: github.event_name == 'push' - run: echo ${GITHUB_REF##*/} > ./branch.txt - - - name: Upload branch - if: ${{ always() }} - uses: actions/upload-artifact@v3 - with: - name: branch - path: ./branch.txt diff --git a/README-pt_BR.md b/README-pt_BR.md index 06815ae956..3ddd63b198 100644 --- a/README-pt_BR.md +++ b/README-pt_BR.md @@ -12,7 +12,7 @@ Uma solução empresarial de design e biblioteca UI para React. [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url] -[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] +[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] [![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] @@ -22,10 +22,6 @@ Uma solução empresarial de design e biblioteca UI para React. [github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22 [codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square [codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master -[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square -[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev -[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square -[david-url]: https://david-dm.org/ant-design/ant-design [download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square [download-url]: https://npmjs.org/package/antd [lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design @@ -44,6 +40,8 @@ Uma solução empresarial de design e biblioteca UI para React. [unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css [issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square [issues-helper-url]: https://github.com/actions-cool/issues-helper +[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square +[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498 diff --git a/README-uk_UA.md b/README-uk_UA.md index 41cb91b07b..2131172494 100644 --- a/README-uk_UA.md +++ b/README-uk_UA.md @@ -12,7 +12,7 @@ [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url] -[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] +[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] [![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] @@ -22,10 +22,6 @@ [github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22 [codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square [codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master -[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square -[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev -[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square -[david-url]: https://david-dm.org/ant-design/ant-design [download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square [download-url]: https://npmjs.org/package/antd [lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design @@ -44,6 +40,8 @@ [unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css [issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square [issues-helper-url]: https://github.com/actions-cool/issues-helper +[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square +[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498 diff --git a/README-zh_CN.md b/README-zh_CN.md index fcdd4b926c..4aa26fe699 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -12,7 +12,7 @@ [![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url] -[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] +[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url] [![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url] @@ -22,10 +22,6 @@ [github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22 [codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square [codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master -[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square -[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev -[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square -[david-url]: https://david-dm.org/ant-design/ant-design [download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square [download-url]: https://npmjs.org/package/antd [lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design @@ -44,6 +40,8 @@ [unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css [issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square [issues-helper-url]: https://github.com/actions-cool/issues-helper +[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square +[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498 diff --git a/components/modal/index.en-US.md b/components/modal/index.en-US.md index 2479edbc99..85d8a8caf6 100644 --- a/components/modal/index.en-US.md +++ b/components/modal/index.en-US.md @@ -142,6 +142,10 @@ return
{contextHolder}
; ## FAQ +### Why content not update when Modal closed? + +Modal will use memo to avoid content jumping when closed. Also, if you use Form in Modal, you can reset `initialValues` by calling `resetFields` in effect. + ### Why I can not access context, redux, ConfigProvider `locale/prefixCls` in Modal.xxx? antd will dynamic create React instance by `ReactDOM.render` when call Modal methods. Whose context is different with origin code located context. diff --git a/components/modal/index.zh-CN.md b/components/modal/index.zh-CN.md index 7074a69970..0308d2aa13 100644 --- a/components/modal/index.zh-CN.md +++ b/components/modal/index.zh-CN.md @@ -145,6 +145,10 @@ return
{contextHolder}
; ## FAQ +### 为什么 Modal 关闭时,内容不会更新? + +Modal 在关闭时会将内容进行 memo 从而避免关闭过程中的内容跳跃。也因此如果你在配合使用 Form 有关闭时重置 `initialValues` 的操作,请通过在 effect 中调用 `resetFields` 来重置。 + ### 为什么 Modal 方法不能获取 context、redux、的内容和 ConfigProvider `locale/prefixCls` 配置? 直接调用 Modal 方法,antd 会通过 `ReactDOM.render` 动态创建新的 React 实体。其 context 与当前代码所在 context 并不相同,因而无法获取 context 信息。 diff --git a/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap b/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap index d2102e19fa..d9d5a2f118 100644 --- a/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap +++ b/components/segmented/__tests__/__snapshots__/demo-extend.test.ts.snap @@ -178,6 +178,106 @@ exports[`renders ./components/segmented/demo/controlled.md extend context correc `; +exports[`renders ./components/segmented/demo/controlled-two.md extend context correctly 1`] = ` +Array [ +
+ + + +
, + "  ", +
+ + + +
, +] +`; + exports[`renders ./components/segmented/demo/custom.md extend context correctly 1`] = ` Array [
`; +exports[`renders ./components/segmented/demo/controlled-two.md correctly 1`] = ` +Array [ +
+ + + +
, + "  ", +
+ + + +
, +] +`; + exports[`renders ./components/segmented/demo/custom.md correctly 1`] = ` Array [
{ .map(el => (el.getDOMNode() as HTMLInputElement).checked), ).toEqual([true, false, false]); expect( - wrapper.find(`.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`), + wrapper.find(`label.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`), ).toBeTruthy(); wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change'); @@ -164,7 +164,7 @@ describe('Segmented', () => { ); expect(wrapper.render()).toMatchSnapshot(); expect( - wrapper.find(`.${prefixCls}-item`).at(1).hasClass(`${prefixCls}-item-disabled`), + wrapper.find(`label.${prefixCls}-item`).at(1).hasClass(`${prefixCls}-item-disabled`), ).toBeTruthy(); expect(wrapper.find(`.${prefixCls}-item-input`).at(1).prop('disabled')).toBeTruthy(); @@ -304,7 +304,7 @@ describe('Segmented', () => { .map(el => (el.getDOMNode() as HTMLInputElement).checked), ).toEqual([true, false, false]); expect( - wrapper.find(`.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`), + wrapper.find(`label.${prefixCls}-item`).at(0).hasClass(`${prefixCls}-item-selected`), ).toBeTruthy(); wrapper.find(`.${prefixCls}-item-input`).at(2).simulate('change'); diff --git a/components/segmented/demo/controlled-two.md b/components/segmented/demo/controlled-two.md new file mode 100644 index 0000000000..16b0a1569e --- /dev/null +++ b/components/segmented/demo/controlled-two.md @@ -0,0 +1,41 @@ +--- +order: 99 +title: + zh-CN: 受控同步模式 + en-US: Controlled Synced mode +debug: true +--- + +## zh-CN + +测试受控模式下两个 Segmented 同步 state。 + +## en-US + +Tests two Segmented synchronized states in controlled mode. + +```jsx +import { useState } from 'react'; +import { Segmented } from 'antd'; + +const Demo = () => { + const [foo, setFoo] = useState('AND'); + return ( + <> + setFoo(e.target.value)} + /> +    + setFoo(e.target.value)} + /> + + ); +}; + +export default Demo; +``` diff --git a/components/segmented/style/index.less b/components/segmented/style/index.less index 6f53806014..c3d83b726a 100644 --- a/components/segmented/style/index.less +++ b/components/segmented/style/index.less @@ -114,3 +114,5 @@ will-change: transform, width; } } + +@import './rtl'; diff --git a/components/segmented/style/rtl.less b/components/segmented/style/rtl.less new file mode 100644 index 0000000000..c459bf035e --- /dev/null +++ b/components/segmented/style/rtl.less @@ -0,0 +1,15 @@ +@import '../../style/themes/index'; +@import '../../style/mixins/index'; + +@segmented-prefix-cls: ~'@{ant-prefix}-segmented'; + +.@{segmented-prefix-cls} { + &&-rtl { + direction: rtl; + } + + &&-rtl &-item-icon { + margin-right: 0; + margin-left: 6px; + } +} diff --git a/components/style/themes/variable.less b/components/style/themes/variable.less index dd2c52dbb9..55f4a0836d 100644 --- a/components/style/themes/variable.less +++ b/components/style/themes/variable.less @@ -9,7 +9,7 @@ // An override for the html selector for theme prefixes @html-selector: html; -html { +@{html-selector} { @base-primary: @blue-6; // ========= Primary Color ========= diff --git a/components/table/__tests__/Table.filter.test.js b/components/table/__tests__/Table.filter.test.js index 310abaec5f..cf74e8d1cd 100644 --- a/components/table/__tests__/Table.filter.test.js +++ b/components/table/__tests__/Table.filter.test.js @@ -2252,4 +2252,57 @@ describe('Table.filter', () => { expect(wrapper.find('.ant-tree-checkbox-checked').length).toBe(1); expect(wrapper.find('.ant-tree-checkbox-checked+span').text()).toBe('Girl'); }); + + it('filteredKeys should all be controlled or not controlled', () => { + const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); + errorSpy.mockReset(); + const tableData = [ + { + key: '1', + name: 'John Brown', + age: 32, + }, + ]; + const columns = [ + { + title: 'name', + dataIndex: 'name', + key: 'name', + filters: [], + }, + { + title: 'age', + dataIndex: 'age', + key: 'age', + filters: [], + }, + ]; + render( + createTable({ + columns, + data: tableData, + }), + ); + expect(errorSpy).not.toBeCalled(); + errorSpy.mockReset(); + columns[0].filteredValue = []; + render( + createTable({ + columns, + data: tableData, + }), + ); + expect(errorSpy).toBeCalledWith( + 'Warning: [antd: Table] Columns should all contain `filteredValue` or not contain `filteredValue`.', + ); + errorSpy.mockReset(); + columns[1].filteredValue = []; + render( + createTable({ + columns, + data: tableData, + }), + ); + expect(errorSpy).not.toBeCalled(); + }); }); diff --git a/components/table/hooks/useFilter/index.tsx b/components/table/hooks/useFilter/index.tsx index 18b7593dab..e222e75713 100644 --- a/components/table/hooks/useFilter/index.tsx +++ b/components/table/hooks/useFilter/index.tsx @@ -211,24 +211,25 @@ function useFilter({ const mergedFilterStates = React.useMemo(() => { const collectedStates = collectFilterStates(mergedColumns, false); - - const filteredKeysIsNotControlled = collectedStates.every( - ({ filteredKeys }) => filteredKeys === undefined, - ); + let filteredKeysIsAllNotControlled = true; + let filteredKeysIsAllControlled = true; + collectedStates.forEach(({ filteredKeys }) => { + if (filteredKeys !== undefined) { + filteredKeysIsAllNotControlled = false; + } else { + filteredKeysIsAllControlled = false; + } + }); // Return if not controlled - if (filteredKeysIsNotControlled) { + if (filteredKeysIsAllNotControlled) { return filterStates; } - const filteredKeysIsAllControlled = collectedStates.every( - ({ filteredKeys }) => filteredKeys !== undefined, - ); - devWarning( - filteredKeysIsNotControlled || filteredKeysIsAllControlled, + filteredKeysIsAllControlled, 'Table', - '`FilteredKeys` should all be controlled or not controlled.', + 'Columns should all contain `filteredValue` or not contain `filteredValue`.', ); return collectedStates; diff --git a/components/upload/__tests__/type.test.tsx b/components/upload/__tests__/type.test.tsx index b48e267836..d0d75a11cd 100644 --- a/components/upload/__tests__/type.test.tsx +++ b/components/upload/__tests__/type.test.tsx @@ -35,6 +35,20 @@ describe('Upload.typescript', () => { expect(upload).toBeTruthy(); }); + it('onChange fileList', () => { + type IFile = { + customFile: File; + }; + + const upload = ( + onChange={({ fileList }) => fileList.map(file => file.response?.customFile)}> + click to upload + + ); + + expect(upload).toBeTruthy(); + }); + it('onChange in UploadProps', () => { const uploadProps: UploadProps = { onChange: ({ file }) => file, diff --git a/components/upload/interface.tsx b/components/upload/interface.tsx index 9e5d20705e..9db210ade6 100755 --- a/components/upload/interface.tsx +++ b/components/upload/interface.tsx @@ -44,7 +44,7 @@ export interface InternalUploadFile extends UploadFile { export interface UploadChangeParam { // https://github.com/ant-design/ant-design/issues/14420 file: T; - fileList: UploadFile[]; + fileList: T[]; event?: { percent: number }; } diff --git a/package.json b/package.json index 0c50447e5d..512ac41a1e 100644 --- a/package.json +++ b/package.json @@ -99,11 +99,11 @@ "tsc": "tsc --noEmit", "site:test": "jest --config .jest.site.js --cache=false --force-exit", "test-image": "npm run dist && docker-compose run tests", + "argos": "node ./scripts/argos-upload.js", "version": "node ./scripts/generate-version", "install-react-16": "npm i --no-save --legacy-peer-deps react@16 react-dom@16 enzyme-adapter-react-16", "install-react-17": "npm i --no-save --legacy-peer-deps react@17 react-dom@17", - "install-react-18": "npm i --no-save --legacy-peer-deps react@18 react-dom@18 @testing-library/react@13", - "argos": "argos upload imageSnapshots" + "install-react-18": "npm i --no-save --legacy-peer-deps react@18 react-dom@18 @testing-library/react@13" }, "browserslist": [ "> 0.5%", @@ -189,7 +189,6 @@ "@typescript-eslint/parser": "^5.0.0", "@wojtekmaj/enzyme-adapter-react-17": "^0.6.0", "antd-img-crop": "^4.0.0", - "argos-cli": "^0.3.0", "array-move": "^4.0.0", "babel-plugin-add-react-displayname": "^0.0.5", "bisheng": "^3.2.1", @@ -222,6 +221,7 @@ "eslint-plugin-react": "^7.28.0", "eslint-plugin-react-hooks": "^4.1.2", "eslint-plugin-unicorn": "^42.0.0", + "fast-glob": "^3.2.11", "fetch-jsonp": "^1.1.3", "fs-extra": "^10.0.0", "full-icu": "^1.3.0", diff --git a/scripts/argos-upload.js b/scripts/argos-upload.js new file mode 100644 index 0000000000..f81158d795 --- /dev/null +++ b/scripts/argos-upload.js @@ -0,0 +1,65 @@ +// Thanks to material-ui ❤️ +// Create chunks for Argos: https://github.com/mui/material-ui/pull/23518 +// https://github.com/mui/material-ui/blob/af81aae3b292ed180e7652a665fad1be2b38a7b3/scripts/pushArgos.js +const util = require('util'); +const glob = require('fast-glob'); +const lodashChunk = require('lodash/chunk'); +const childProcess = require('child_process'); + +const execFileNode = util.promisify(childProcess.execFile); + +function execFile(command, args) { + return execFileNode(command, args, { + cwd: process.cwd(), + env: process.env, + encoding: 'utf-8', + }); +} + +const screenshotsBase = 'imageSnapshots'; +const screenshotsTmp = `${screenshotsBase}/temp`; +const BATCH_SIZE = 200; + +async function move2Temp(screenshot, target) { + await execFile('mkdir', ['-p', target]); + await execFile('mv', [screenshot, target]); +} + +async function run() { + const screenshots = await glob(`${screenshotsBase}/**/*`); + const chunks = lodashChunk(screenshots, BATCH_SIZE); + + await Promise.all( + chunks.map((chunk, chunkIndex) => + Promise.all( + chunk.map(screenshot => move2Temp(screenshot, `${screenshotsTmp}/${chunkIndex}`)), + ), + ), + ); + + for (let i = 0; i < chunks.length; i += 1) { + // eslint-disable-next-line no-await-in-loop + const argosResults = await execFile('argos', [ + 'upload', + `${screenshotsTmp}/${i}`, + '--token', + process.env.ARGOS_TOKEN, + '--batchCount', + chunks.length, + '--branch', + process.env.GITHUB_REF_NAME, + '--commit', + process.env.GITHUB_SHA, + '--external-build-id', + process.env.GITHUB_SHA, + ]); + // eslint-disable-next-line no-console -- pipe stdout + console.log(argosResults.stdout); + } +} + +run().catch(error => { + // eslint-disable-next-line no-console + console.error(error); + process.exit(1); +});