Browse Source

Merge branch 'next' into v5-site-upgrade

pull/38361/head
MadCcc 2 years ago
parent
commit
73a1555637
  1. 1
      .eslintignore
  2. 3
      .eslintrc.js
  3. 1
      .gitignore
  4. 106
      AUTHORS.txt
  5. 2
      components/_util/reactNode.ts
  6. 4
      components/_util/wave/index.tsx
  7. 28
      components/alert/index.en-US.md
  8. 36
      components/alert/index.zh-CN.md
  9. 16
      components/alert/style/index.tsx
  10. 9
      components/back-top/__tests__/index.test.tsx
  11. 2
      components/badge/utils.tsx
  12. 8
      components/button/button.tsx
  13. 7
      components/button/style/index.tsx
  14. 26
      components/card/style/index.tsx
  15. 22
      components/carousel/__tests__/index.test.tsx
  16. 2
      components/checkbox/Checkbox.tsx
  17. 4
      components/checkbox/Group.tsx
  18. 6
      components/collapse/__tests__/index.test.tsx
  19. 11
      components/collapse/style/index.tsx
  20. 30
      components/config-provider/__tests__/theme.test.tsx
  21. 38
      components/config-provider/index.en-US.md
  22. 2
      components/config-provider/index.tsx
  23. 38
      components/config-provider/index.zh-CN.md
  24. 194
      components/date-picker/index.en-US.md
  25. 194
      components/date-picker/index.zh-CN.md
  26. 6
      components/dropdown/__tests__/index.test.tsx
  27. 2
      components/form/FormItemLabel.tsx
  28. 117
      components/form/__tests__/list.test.tsx
  29. 2
      components/form/util.ts
  30. 4
      components/input/__tests__/__snapshots__/Password.test.tsx.snap
  31. 3
      components/input/__tests__/index.test.tsx
  32. 6
      components/list/style/index.tsx
  33. 2
      components/locale-provider/LocaleReceiver.tsx
  34. 3
      components/locale-provider/default.tsx
  35. 1
      components/locale/ar_EG.tsx
  36. 1
      components/locale/bg_BG.tsx
  37. 1
      components/locale/da_DK.tsx
  38. 136
      components/locale/default.tsx
  39. 1
      components/locale/el_GR.tsx
  40. 137
      components/locale/en_US.tsx
  41. 1
      components/locale/fi_FI.tsx
  42. 1
      components/locale/fr_BE.tsx
  43. 1
      components/locale/fr_CA.tsx
  44. 1
      components/locale/gl_ES.tsx
  45. 1
      components/locale/he_IL.tsx
  46. 1
      components/locale/hu_HU.tsx
  47. 1
      components/locale/is_IS.tsx
  48. 1
      components/locale/ja_JP.tsx
  49. 1
      components/locale/km_KH.tsx
  50. 1
      components/locale/kmr_IQ.tsx
  51. 1
      components/locale/ko_KR.tsx
  52. 3
      components/locale/ku_IQ.tsx
  53. 1
      components/locale/lv_LV.tsx
  54. 1
      components/locale/mk_MK.tsx
  55. 1
      components/locale/mn_MN.tsx
  56. 1
      components/locale/ms_MY.tsx
  57. 1
      components/locale/sl_SI.tsx
  58. 1
      components/locale/vi_VN.tsx
  59. 1
      components/locale/zh_CN.tsx
  60. 1
      components/locale/zh_HK.tsx
  61. 1
      components/locale/zh_TW.tsx
  62. 2
      components/message/style/index.tsx
  63. 2
      components/modal/locale.tsx
  64. 2
      components/modal/style/index.tsx
  65. 2
      components/modal/useModal/HookModal.tsx
  66. 4
      components/notification/style/index.tsx
  67. 2
      components/popconfirm/PurePanel.tsx
  68. 6
      components/popover/style/index.tsx
  69. 4
      components/progress/progress.tsx
  70. 6
      components/radio/style/index.tsx
  71. 7
      components/skeleton/Node.tsx
  72. 8
      components/slider/style/index.tsx
  73. 2
      components/statistic/utils.tsx
  74. 4
      components/steps/style/index.tsx
  75. 2
      components/steps/style/small.ts
  76. 4
      components/steps/style/vertical.ts
  77. 14
      components/switch/style/index.tsx
  78. 4
      components/table/Table.tsx
  79. 26
      components/table/__tests__/Table.sorter.test.tsx
  80. 220
      components/table/index.en-US.md
  81. 2
      components/table/style/expand.tsx
  82. 3
      components/table/style/index.tsx
  83. 23
      components/table/util.ts
  84. 2
      components/tabs/style/index.tsx
  85. 8
      components/tag/style/index.tsx
  86. 2
      components/theme/export.ts
  87. 91
      components/theme/interface.ts
  88. 17
      components/theme/themes/compact/genCompactSizeMapToken.ts
  89. 15
      components/theme/themes/compact/index.ts
  90. 9
      components/theme/themes/dark/index.ts
  91. 5
      components/theme/themes/default/index.ts
  92. 4
      components/theme/themes/seed.ts
  93. 28
      components/theme/themes/shared/genCommonMapToken.ts
  94. 17
      components/theme/themes/shared/genSizeMapToken.ts
  95. 40
      components/theme/util/alias.ts
  96. 8
      components/tooltip/index.tsx
  97. 4
      components/transfer/ListBody.tsx
  98. 2
      components/transfer/ListItem.tsx
  99. 3
      components/transfer/__tests__/customize.test.tsx
  100. 10
      components/transfer/__tests__/list.test.tsx

1
.eslintignore

@ -25,6 +25,7 @@ site/**/*.tsx
typings
es/**/*
lib/**/*
locale
node_modules
_site
dist

3
.eslintrc.js

@ -20,6 +20,9 @@ module.exports = {
version: 'detect',
},
polyfills: ['Promise', 'URL'],
'import/resolver': {
typescript: {},
},
},
parser: '@typescript-eslint/parser',
plugins: ['react', 'babel', 'jest', '@typescript-eslint', 'react-hooks', 'unicorn', 'markdown'],

1
.gitignore

@ -28,6 +28,7 @@ dist
report.html
/lib
/es
/locale
elasticsearch-*
config/base.yaml
/.vscode/

106
AUTHORS.txt

@ -6,6 +6,7 @@
778758944 <778758944@qq.com>
Aaron Cawte <aaron@bbncreative.co>
Aaron Planell López <aaronplanell@gmail.com>
Aaron674092290 <y1922462644@163.com>
Adam Stankiewicz <sheerun@sher.pl>
Aditya Padhi <aditya.padhi@outlook.com>
Adrian Dimitrov <dimitrov.adrian@gmail.com>
@ -19,6 +20,7 @@ Aiello <770540123@qq.com>
AkiJoey <akijoey1010635951@gmail.com>
Akshat Mittal <itsreallyakshat@gmail.com>
Alan Braithwaite <asbraithwaite@gmail.com>
Alan Deng <alanhaledc@gmail.com>
Albert Mañosa <albertmasa2@gmail.com>
Albert Zhang <ziyuximing@163.com>
Albert Zheng <lisong.zheng@gmail.com>
@ -40,6 +42,7 @@ Alexey Vinogradov <vinogradov.a.i.93@gmail.com>
Alexey Yakovlev <yallie@yandex.ru>
Alfred Qiu <sc941203@gmail.com>
Ali Zhdanov <makedonec88@gmail.com>
AliRezaBeigy <AliRezaBeigyKhu@gmail.com>
Aliaksandr <puskin1914@gmail.com>
Alireza <alireza.mh@gmail.com>
Alvin Abia <alvin.abia@icloud.com>
@ -52,6 +55,7 @@ Anas Tawfeek <anas.tawfeek@outlook.com>
Andre Perunicic <andre@intoli.com>
Andre Zyczkowski <andre@oakslab.com>
Andrea Blanco <estefania_8_3@hotmail.com>
Andrew Horn <arhorn@smcm.edu>
Andrew Murray <radarhere@gmail.com>
Andrew Shearer <andrew@ashearer.com>
Andrey G <plandem@gmail.com>
@ -107,6 +111,7 @@ Bruno Maia <bruno.mm.maia@gmail.com>
Bryan Berger <bb@ga.co>
BugHiding <609228186@qq.com>
C <4019980@qq.com>
C. T. Lin <chentsulin@gmail.com>
C.J. Winslow <whoaa512@gmail.com>
CORP\lianyufeng <15275222711@163.com>
Calin Vlad <vlad.s.calin@gmail.com>
@ -124,6 +129,7 @@ Cemre Mengu <cemremengu@gmail.com>
Chalk <chalkpe@gmail.com>
Chandler Moisen <chandlermoisen@gmail.com>
Chang Wang <cheapsteak@gmail.com>
Chang Wei <changwei1006@gmail.com>
Chang Wei <867597730@qq.com>
Charles Covey-Brandt <chazcb@gmail.com>
Charlie Jonas <charlie@callawaycloudconsulting.com>
@ -151,11 +157,13 @@ Cong Yoo <roopher@163.com>
Cong Zhang <dancerphil1994@gmail.com>
Connor White <connor.bcw@gmail.com>
Conway Anderson <hello@conwayanderson.com>
Cooper Veysey <cwveysey@gmail.com>
Cordaro <elvis07@163.com>
CornerSkyless <573196853@qq.com>
Curly.Water <water.curly@gmail.com>
D & R <jdz321@qq.com>
Daewoong Moon <wiziple@gmail.com>
Dalton Craven <daltoncraven@proton.me>
Damian Green <damian@gcoders.com>
Damian Green <damian.green@microlease.com>
Dan Minshew <ofenixculpa@gmail.com>
@ -183,7 +191,7 @@ Dean van Niekerk <deanvniekerk@gmail.com>
Debiancc <never.be.evil.debian@gmail.com>
DengYun <tdzl2003@gmail.com>
Denis <shumkovdenis@gmail.com>
Dennis273 <dennisc695@icloud.com>
Dennis Chen <dennisc695@icloud.com>
Derrick <derricktel@foxmail.com>
Di Wu <di@gridx.cn>
DiamondYuan <admin@diamondyuan.com>
@ -205,6 +213,8 @@ Dorian <dorian@doma.io>
DosLin <doslino@gmail.com>
Douglas Mason <Demasonjr@gmail.com>
Dreamcreative <m543438924@163.com>
Dunqing <dengqing0821@gmail.com>
Dunqing <1247748612@qq.com>
Dzmitry Yarmoshkin <spanb4@gmail.com>
Eager <1226393396@qq.com>
Eber Rodrigues <eberjoe@gmail.com>
@ -233,6 +243,7 @@ Eric Lee <ericfly33@gmail.com>
Eric Turriff <eric.turriff@gmail.com>
Eric Wang <eric@canva.com>
Eric Wang <wjun0912@gmail.com>
Ernest Folch <ernest.folch@gmail.com>
Erwann Mest <m+github@kud.io>
Eslam Yahya <eslamyahya.dev@gmail.com>
Eugene Matvejev <eugene.matvejev@gmail.com>
@ -261,6 +272,7 @@ Fullstop000 <fullstop1005@gmail.com>
GJ Wang <itellboy@foxmail.com>
GSBL <2602140596@qq.com>
Gabe Medrash <gabeme@alleninstitute.org>
Gabriel Haruki <gabrielharukisatoh@gmail.com>
Gabriel Henrique <bielrockx@gmail.com>
Gabriel Le Breton <lebreton.gabriel@gmail.com>
Gabriel Mendez Reyes <gabriel.mendez0@hotmail.com>
@ -295,10 +307,10 @@ Guru Mahendran <me@gurubavan.com>
HJin.me <hjin.me@gmail.com>
Hai Phan Nguyen <pnghai@gmail.com>
Haibin Yu <haibin.yu@oceanwing.com>
Hal-pan <hms181231@gmail.com>
Hale Deng <haledeng88@gmail.com>
Han Han <haannn@qq.com>
Hanai <ihanai1991@gmail.com>
Haniel Cui <coldice945@hotmail.com>
Hanjun Kim <hallazzang@gmail.com>
Hanz Luo <lhz0516@gmail.com>
HarlanLuo <luoxwen@gmail.com>
@ -326,6 +338,8 @@ Hsuan Lee <hsuangm@gmail.com>
Hubert Argasinski <argasinski.hubert@gmail.com>
Hughen <446370503@163.com>
Hugo LEHMANN <shogi31@gmail.com>
Humble <745653239@qq.com>
Hyunseok.Kim <dragmove@gmail.com>
ILdar Nogmanov <nogmanov@gmail.com>
Igor <nemytyshew@yandex.ru>
Igor Andriushchenko <igor.andriushchenko@snowsoftware.com>
@ -363,6 +377,7 @@ James <james@schoolshape.com>
James Tsang <wentao_zeng1@163.com>
James Yeung <shunjiey@hotmail.com>
JamesYin <elantion@gmail.com>
Jamki <13414367591@163.com>
Jan Václavík <jvaclavik@gmail.com>
Janry <wangzhili56@126.com>
Jaroslav Bereza <github.com@bereza.cz>
@ -377,6 +392,7 @@ Jay Fong <fjc0kb@gmail.com>
Jean-Luc Sorak <jlsorak@icloud.com>
Jean-Philippe Roy <ohnoesmyoreos@gmail.com>
Jeff Wen <sinchangwen@gmail.com>
Jefferson Rafael Kozerski <jeff.drumgod@gmail.com>
Jeffrey Carl Faden <jeffreyatw@gmail.com>
Jehu <z_zhihao@foxmail.com>
Jelle de Jong <jelle@hoest.nl>
@ -413,6 +429,7 @@ John Carlo <johncarloaustria@pm.me>
John Johnson III <john@johnjohnson.cc>
John Nguyen <jtnguyen236@gmail.com>
Johnny Lim <izeye@naver.com>
Johnny-young <1571779481@qq.com>
Johnsen <jiajunzhou1994@163.com>
Jonatas Walker <jonataswalker@gmail.com>
Jonathan Gabaut <possimpible@gmail.com>
@ -426,6 +443,7 @@ Josué <ujosuegt@outlook.com>
JounQin <admin@1stg.me>
JribiBelhassen <belha9inzaghi@gmail.com>
Jtree03 <wowns0903@gmail.com>
Ju/Smwhr <julienzamor@gmail.com>
JuFeng Zhang <zjffun@gmail.com>
Juan Carlos Lloret H <juan.carlos.lloret@lansweeper.com>
Juan Rodrigo Venegas Boesch <jrvboesch@gmail.com>
@ -442,7 +460,7 @@ Jógvan Olsen <jogvanolsen@hotmail.com>
Kaan KÜÇÜK <kaankck@gmail.com>
Kaien Liao <liaokaien@gmail.com>
Kamal Mahmudi <kamalmahmudi@yahoo.co.id>
Karott Schu <karott7@gmail.com>
Karott <karott7@gmail.com>
Kasra Bigdeli <kasra85@gmail.com>
Kayson Wu <772663139@qq.com>
Kelvin Chu <chubillkelvin@gmail.com>
@ -478,6 +496,7 @@ Kuitos <kuitos.lau@gmail.com>
Kurt Furbush <kurtfurbush@gmail.com>
Kyle Kelley <rgbkrk@gmail.com>
Kyle Rosenberg <kyle.rosenberg@gmail.com>
Kyrielin <a154571896@gmail.com>
LLinFan- <catfoursi@qq.com>
LT246-VINHNPH\vinhnph <vinhnph@blockpass.org>
Laith <laith24@gmail.com>
@ -485,6 +504,7 @@ Larry Laski <larry.laski@gmail.com>
LaySent <laysent@gmail.com>
LeeHarlan <709886167@qq.com>
LeezQ <lizhenq2009@gmail.com>
Len <wangl_msg@163.com>
LeoYang <LeoY.Dev@gmail.com>
Leon Koole <leon@koole.io>
Leon Shi <superRaytin@163.com>
@ -508,6 +528,7 @@ Lu Yu <brantscube@hotmail.com>
Lucien Lee <lkiral7903@gmail.com>
Ludwig Bäcklund <ludli839@student.liu.se>
Luke Vella <me@lukevella.com>
Luobo Zhang <zhang.pc3@gmail.com>
Lyndon001 <lld207@126.com>
M Mitchell <mail@megmitchell.ca>
M. Burak Kalkan <mburakkalkan@gmail.com>
@ -528,8 +549,10 @@ MaoYiWei <137308365@qq.com>
Map1en_ <maplenagisa@gmail.com>
Marcel Jackwerth <marceljackwerth@gmail.com>
Marcela Bomfim <mbomfim@live.com>
Marcia Sun <strear@qq.com>
Marcio Pamplona <marciopamplona79@gmail.com>
Marco Afonso <mafonso333@gmail.com>
Marco Heinrich <mheinrich86@gmail.com>
Marcus Bransbury <marcus.bransbury@gmail.com>
Marcus Stenbeck <marcus.stenbeck@gmail.com>
Marius Ileana <visvadw@gmail.com>
@ -547,6 +570,7 @@ Matt Wilkinson <mattwilki17@gmail.com>
Max <maksym.mosyura@kruschecompany.com>
Maximilian Meyer <Maximilian.Meyer@br.de>
Md_ZubairAhmed <m-zubairahmed@protonmail.com>
Mehdi Salem Naraghi <momesana@gmail.com>
MeiLin <postget.me@gmail.com>
MengZhaoFly <1424254461@qq.com>
Meow-z <372086270@qq.com>
@ -596,6 +620,7 @@ Mr.jiangzhiguo <jiangzhiguo2010@qq.com>
Ms. Wang <767074895@qq.com>
MuYu <mr.muzea@gmail.com>
Muhammad Sameer <contact@mdsameer.com.np>
Mykyta Velykanov <nikitavelykanov@gmail.com>
Mário Gonçalves <mario.mc.goncalves@gmail.com>
Nariman Movaffaghi <nariman.movaffaghi@gmail.com>
Nathan Broadbent <git@ndbroadbent.com>
@ -605,6 +630,7 @@ Nathan Tavares Nascimento <nathan.tnascimento@gmail.com>
Nathan Wells <nwwells@gmail.com>
Naveen <mailtomassnaveen@gmail.com>
Neekey <ni184775761@gmail.com>
Neil <100huming@gmail.com>
Nekron <nekron.hyt@gmail.com>
Neo Tan <neotan12@hotmail.com>
Neto Braghetto <netow93@gmail.com>
@ -621,6 +647,7 @@ Nikolay <veseliy07@gmail.com>
Nikolay Solovyov <i@mr-ozio.ru>
Nima Dehnashi <nima@getaround.com>
Nimo <nimo.jser@gmail.com>
NinJa <386805508@qq.com>
Nishant Arora <na.nishantarora@gmail.com>
Niyaz Akhmetov <axmet180@gmail.com>
Nokecy <Nokecy@163.com>
@ -637,7 +664,6 @@ Oren Kosto <orenkosto86@gmail.com>
Oren Kosto <oren@panda-os.com>
Orkhan Huseynli <orkhan.huseyn@outlook.com>
OuYancey <ou.yancey@gmail.com>
PCCCCCCC <zpc.excel@foxmail.com>
Pablo Recalde <me@pablorecalde.com>
Panjie Setiawan Wicaksono <panjie@panjiesw.com>
Patrick Gidich <patrick.gidich@simnova.com>
@ -647,6 +673,7 @@ Paul Julien <paul.julien@polyconseil.fr>
Paul Julien <paul.julien.dev@gmail.com>
Peach <scdzwyxst@gmail.com>
Pengsha Ying <810998652@qq.com>
Peritot Chan <peritotchan@gmail.com>
Peter <usstpeter@gmail.com>
Peter Berg <atticusberg@gmail.com>
Phanupong Janthapoon <panupong.jtp@gmail.com>
@ -689,6 +716,7 @@ Regan Langford <regan.reihana@gmail.com>
Renny Ren <rennyallen@hotmail.com>
Renovate Bot <bot@renovateapp.com>
Rex <zhangzilong.zzl@163.com>
Rex Zeng <rex@rexskz.info>
Ricardo Morais <moraispgsi@gmail.com>
Ricardo Raphael Joson <rrjoson08@gmail.com>
Richard D. Worth <rdworth@gmail.com>
@ -698,6 +726,7 @@ Robin Pokorny <me@robinpokorny.com>
Rodrigo Ehlers <rodrigoehlers@outlook.com>
Rohan Bagchi <rohan.bagchi01@gmail.com>
Rohan Malhotra <rohan.root@gmail.com>
Ron Šmeral <ron.smeral@gmail.com>
Rongjian Zhang <pd4d10@gmail.com>
Rrrandom <emanonhere@gmail.com>
RunningCoderLee <sprint_l@aliyun.com>
@ -748,6 +777,7 @@ Shun <polytechnics.shun@gmail.com>
Shuvalov Anton <anton@shuvalov.info>
SimaQ <sima.zhang1990@gmail.com>
Simo Aleksandrov <simo3003@me.com>
Simon <wsj_simon@163.com>
Simon Altschuler <simon@altschuler.dk>
Simon Knott <simoknott@gmail.com>
Siou <abz53378@gmail.com>
@ -767,12 +797,15 @@ Stevche Radevski <sradevski@live.com>
Steven.zhong <953665604@qq.com>
Subroto <shub1493biswas@gmail.com>
Suki小火车 <463355954@qq.com>
Sukka <isukkaw@gmail.com>
Sumit Vekariya <sumitvekariya7@gmail.com>
Sunny Luo <sunnylqm@gmail.com>
Sven Efftinge <sven.efftinge@typefox.io>
Svyatoslav <s.solopchenko@gmail.com>
SyMind <dacongsama@live.com>
SylvanasGone <397009765@qq.com>
TTC <345866517@qq.com>
TangLL <ioy102@126.com>
Tanmoy Bhowmik <tanmoy.openroot@gmail.com>
Tannmay S Gupta <tanmaygup123@gmail.com>
Tao <magicdawn@qq.com>
@ -792,7 +825,6 @@ Thiebaud Thomas <thiebaud.tom@gmail.com>
Thomas <tom@axisj.com>
Thomas Ladd <thomas.ladd@stackpath.com>
Thomas Zipner <thomas.zipner@gmail.com>
Tianyuan Zhang <tianyuan233.zhang@gmail.com>
Tino D <ginodeis@gmail.com>
Tmk <i@tmk.im>
Tom Gao <tom@zoomsoft.cc>
@ -809,7 +841,9 @@ TsesamLi <tsesamli17@gmail.com>
Ty Mick <ty@tymick.me>
Tyler <chaotyler@gmail.com>
Ubaldo Quintana <blkdr@hotmail.com>
Uladzimir Atroshchanka <uladzimir.atroshchanka@gmail.com>
Uladzimir Atroshchanka <Uladzimir.Atroshchanka@vertexinc.com>
Umberto Gariggio <gariggio@gmail.com>
Vadim Macagon <vadim.macagon@gmail.com>
Valentin Vichnal <valentin@vichnal.com>
Van Nguyen <vnguyen94@gmail.com>
@ -823,6 +857,7 @@ Vijay Thirugnanam <vijayst@gmail.com>
Vincent Zhang <vxzhong@qq.com>
Vineet Srivastav <vineetvk01@gmail.com>
Viorel Cojocaru <vio@beanon.com>
Vishal Jagtap <jagtap.vishal30@gmail.com>
Vitaliy Mazurenko <vitaliymazurenko@gmail.com>
Vitaly Budovski <vbudovski@gmail.com>
ViviaRui <zr1450995198@163.com>
@ -838,22 +873,26 @@ Wang yb <wangyibu123@gmail.com>
Warren Seymour <warren@fountainhead.tech>
Webber Takken <webber@takken.io>
Wei Zhu <yesmeck@gmail.com>
WeijieChen <cwjTerrace@163.com>
Wenchao Hu <zjuhwc@gmail.com>
Wendell <wendellhu95@outlook.com>
Wendell <wendzhue@gmail.com>
Wenqi Chen <1264578441@qq.com>
Wensheng Xu <xws@superid.cn>
Wesley <pengyw97@gmail.com>
Will <will_workhard@163.com>
Will Chen <willchen90@gmail.com>
Will Soares <willamesoares65@gmail.com>
William Bergeron-Drouin <william@inter-net.ca>
William Cai <williamcai@easyops.cn>
William Stein <wstein@gmail.com>
WingGao <wing.gao@live.com>
Wu Haotian <whtsky@gmail.com>
WuJiali <18767152447@163.com>
Wuxh <wxh1220@gmail.com>
Wuxh <wxh16144@qq.com>
X-Jagger <xl.jagger@gmail.com>
XBTop1! <xbtop1@gmail.com>
XIN HU <hoosin.git@gmail.com>
XTY <^@xty.dev>
Xiaoming <yokiming1994@gmail.com>
Xie Guanglei <xieguanglei@hotmail.com>
@ -869,6 +908,8 @@ Yaindrop <yuan_dian@live.com>
YanYuan <1025138284@qq.com>
Yang Bin <yangkghjh@gmail.com>
Yangzhedi <uiryzd@163.com>
Yanlin Jiang <cncolder@gmail.com>
Yanming Deng <cisolarix@gmail.com>
Yann Normand <yann.normand@gmail.com>
Yann Pringault <yann.pringault@gmail.com>
Yash Joshi <jyash97@gmail.com>
@ -883,6 +924,7 @@ YuChao Liang <l.yuch@foxmail.com>
YuTao <yutao0818@vip.qq.com>
Yuan <1076849402@qq.com>
Yuhang Liu <644186735@qq.com>
Yuki Zhang <foryuki@outlook.com>
Yulia Maximova <juliam2007@mail.ru>
Yunfly <120562638@qq.com>
Yunus EŞ <yunus@yunuses.com>
@ -892,6 +934,7 @@ Yury Kozyrev <urakozz@me.com>
Yusuke Ito <novi.mad@gmail.com>
Yuwei Ba <i@xiaoba.me>
Yuxuan Huo <yuxuan.huo2011@gmail.com>
Yuyao Nie <nieyuyao0826@hotmail.com>
YuyingWu <wuyuying1128@gmail.com>
ZHANGYU <723156735@qq.com>
ZYSzys <zyszys98@gmail.com>
@ -902,7 +945,9 @@ Zap <a124116186@qq.com>
ZeroToOne <igeeke@163.com>
Zester Quinn Albano <zesterquinn.albano@gmail.com>
Zhang Zhi <fytriht@gmail.com>
Zheeeng <air.island.e@foxmail.com>
Zheeeng <hi@zheeeng.me>
ZhiHao Li <940166841@qq.com>
Zhiqiang Gong <elory0513@hotmail.com>
ZhouZhen <503633021@qq.com>
Zhuo Chen <chenzhuo@caicloud.io>
@ -915,6 +960,7 @@ aashutoshrathi <aashutoshrathi@gmail.com>
acfasj <acfasj@gmail.com>
adam <adamwu1992@163.com>
afc163 <afc163@gmail.com>
agarciaguillo <albertogarciaelx@gmail.com>
agent-z <1607291079@qq.com>
aghArdeshir <ardeshireo@gmail.com>
ahalimkara <ahalimkara@gmail.com>
@ -943,6 +989,7 @@ benben <jinwentao0914@dingtalk.com>
bigbigbo <zxb141242@163.com>
binyellow <571704908@qq.com>
blankzust <450811238@qq.com>
bobo <625391250@qq.com>
btea <2356281422@qq.com>
bukas <yhz1219@gmail.com>
byuanama <byuan@ama.com.au>
@ -960,6 +1007,8 @@ chen-jingjie <2383844893@qq.com>
chencheng (云谦) <sorrycc@gmail.com>
chenlei <745512023@qq.com>
chenlong <long.chen@abssqr.com>
chensw <swchenforgetful@hotmail.com>
chenxiang <597219320@qq.com>
chenxiaochun <sjzchenxiaochun@gmail.com>
chequerNoel <noel@chequer.io>
chisus <chisus@smartstudy.co.kr>
@ -968,10 +1017,12 @@ cieldon32 <cieldon32@gmail.com>
cjahv <cjahv@qq.com>
cjmafei <1135328499@qq.com>
cjmafei <cjmafei@guandata.com>
clean99 <xff9924@gmail.com>
clinyong <clinyong@gmail.com>
cnjs <1269075501@qq.com>
codesign <zuishiguang@126.com>
corneyl <cornieljoosse@gmail.com>
csr632 <632882184@qq.com>
daczczcz1 <daczosnek@gmail.com>
damon.chen <chj_0507_dz@sina.com>
david.lv <lvdawei1970@gmail.com>
@ -980,7 +1031,6 @@ davidhatten <david.r.hatten@gmail.com>
ddcat1115 <ddcat1115@gmail.com>
decade <decadef20@gmail.com>
delesseps <andrewlessels@gmail.com>
dengqing <1247748612@qq.com>
denzw <denzw@21cn.com>
dependabot[bot] <support@dependabot.com>
desperado <yuwei_149@163.com>
@ -988,18 +1038,21 @@ detailyang <detailyang@gmail.com>
devqin <devqin@gmail.com>
dian.li <dian.li@yunzhanghu.com>
digz6666 <digz6666@gmail.com>
dingkang <dingkang0458@gmail.com>
djorkaeff <djorkae55@gmail.com>
dolfje <nikosverschore@gmail.com>
douxc <douxc512@gmail.com>
dpyzo0o <yang.xu940121@outlook.com>
dujun <emolingzhu@126.com>
duzliang <duzliang@gmail.com>
edc-hui <edc-hui@outlook.com>
edgji <j.edgji@gmail.com>
eidonjoe <806488716@qq.com>
elios <elios264@hotmail.com>
elrrrrrrr <elrrrrrrr@gmail.com>
eruca <nickwill1984@126.com>
ezpub <ez.foro@gmail.com>
fairyland <cwjTerrace@163.com>
feeng <feengqi@gmail.com>
feng zhi hao <fzhihao@outlook.com>
fengmk2 <m@fengmk2.com>
@ -1008,6 +1061,7 @@ fkysly <fkysly@gmail.com>
flashback313 <windmark2012@gmail.com>
flyerH <hzw758@qq.com>
frezc <504021398@qq.com>
gaokaifeis <gaokf0103@163.com>
gaoryrt <gaoryrt@gmail.com>
gaozhenqian <837856276@qq.com>
genie <genie88@163.com>
@ -1019,6 +1073,7 @@ gyh9457 <gyh9457@163.com>
gzq <zguoby@gmail.com>
haianweifeng <1531297152@qq.com>
haimrait <haimrait@gmail.com>
haipeng <firemmet@gmail.com>
handy <lihandi@gmail.com>
hank <stonehank310@gmail.com>
hanpei <75189218@qq.com>
@ -1034,6 +1089,7 @@ hello-chinese <841030329@qq.com>
henryv0 <henryvo94@gmail.com>
hi-caicai <hi@cai-cai.me>
hicrystal <295247343@qq.com>
hms181231 <hms181231@gmail.com>
hongxuWei <hongxu.wei@outlook.com>
howard <geograous@126.com>
huangyan.py <huangyan.py@bytedance.com>
@ -1063,13 +1119,16 @@ jiang.an <jarancn@gmail.com>
jiang.he <573748150jh@163.com>
jieniu$ <jienius@outlook.com>
jinouwuque <ee2win@gmail.com>
jinrui <jerrykingxyz@gmail.com>
jinyaqiao1102 <405782493@QQ.com>
jojoLockLock <miffyschou@sina.com>
joson <373693643@qq.com>
jueinin <1014397160@qq.com>
junjing.zhang <zhangjunjing@gmail.com>
kacjay <45483388@qq.com>
kaifei <150641329@qq.com>
kailunyao <kailunyao@163.com>
kalykun <984757534@qq.com>
kanweiwei <475801900@qq.com>
kaoding <41830859@qq.com>
kasinooya <kasinooya@gmail.com>
@ -1081,6 +1140,7 @@ keng <keng@renderinghouse.com>
kenve <zwei.xie@gmail.com>
kermolaev <kermolaev@cloudally.com>
kily zhou <keeliizhou@gmail.com>
kiner-tang(文辉) <1127031143@qq.com>
klouskingsley <harry_tse@163.com>
ko <git@yaksok.net>
konakona <lovekonakona@gmail.com>
@ -1088,6 +1148,7 @@ kossel <lis.yichao@gmail.com>
kristof0425 <dombi.kristof@gmail.com>
kuang <p2227@hotmail.com>
kun sam <kunsam624@icloud.com>
lalalazero <zzzero520@hotmail.com>
leadream <857098475@qq.com>
lehug <zcszuo5811@126.com>
leijingdao <leijingdao@163.com>
@ -1100,19 +1161,25 @@ lhyt <515593899@qq.com>
liangfei <njliangfei@gmail.com>
lich-yoo <lich95for@163.com>
liekkas <zjq0717@163.com>
lihao <dahao@qq.com>
lihqi <455711093@qq.com>
lijianan <574980606@qq.com>
lilun <lilun_cd@keruyun.com>
limingxin <906529775@qq.com>
linqiqi077 <865530219@qq.com>
lisenenkov <lisenenkov@mail.ru>
littleLane <857183384@qq.com>
littledian <1197434548@qq.com>
liuchuzhang <liuweiminer@hotmail.com>
liuchuzhang <liuweiminer@126.com>
liuycy <liuycy@yeah.net>
lixiaochou077 <qi.liqi07@gmail.com>
lixiaoyang1992 <lixiaoyang2345@gmail.com>
liyuanqiu <yuanqiulee@gmail.com>
lizhen <lizhen@youzan.com>
llwslc <llwslc@gmail.com>
loganpowell <loganp@tepper.cmu.edu>
losgif <losgif@gmail.com>
luyiming <luyimingchn@gmail.com>
lvren <luren6049@qq.com>
lxnxbnq <yuanddmail@163.com>
@ -1135,6 +1202,7 @@ mraiguo <810158465@qq.com>
mraiguo <mraiguo@gmail.com>
mumiao <mumiao@dtstack.com>
mushan0x0 <mushan0x0@gmail.com>
muxin <a2944938071@163.com>
muzuiget <muzuiget@gmail.com>
natergj <nater_nater@me.com>
netcon <netcon@live.com>
@ -1143,15 +1211,18 @@ nick-ChenZe <chenze2168@gmail.com>
niko <644506165@qq.com>
nitinknolder <nitin.arora@knoldus.com>
nnecec <nnecec@outlook.com>
nuintun <nuintun@gmail.com>
nuintun <nuintun@qq.com>
oldchicken <www.chao3208525@qq.com>
paleface001 <liuye10@yahoo.com>
parabolazz <parebolayh@outlook.com>
paranoidjk <hust2012jiangkai@gmail.com>
parlop <parlop@gmail.com>
paul <34345790@qq.com>
pbrink231 <pbrink231@gmail.com>
peiming <hyrijk@gmail.com>
pengtikui <949828390@qq.com>
pfsu <wellssu0@gmail.com>
picodoth <pikaleize@gmail.com>
picodoth <picodoth@gmail.com>
pinggod <pinggodstudio@gmail.com>
@ -1168,6 +1239,7 @@ qramilq <ramirez99@mail.ru>
qubaoming <qubaoming@didichuxing.com>
ravirambles <ravirambles@gmail.com>
realEago <774855001@qq.com>
rendaoer <rendaoer@outlook.com>
renzhao1113 <547249523@qq.com>
richardison <richard.ison@carleton.ca>
ryangun <ryangun@foxmail.com>
@ -1186,6 +1258,7 @@ shelwin <wxfans@gmail.com>
shlice <licesh@gmail.com>
shmilyKang <953333436@qq.com>
shouyong <enlangs@163.com>
simplejason <simplejason.coder@gmail.com>
siyu77 <xwzhang1986@gmail.com>
slientcloud <rjmuqiang@gmail.com>
sliwey <qlw1009@gmail.com>
@ -1215,6 +1288,7 @@ tom <gaoqiang19514@163.com>
tom <caolvchong@gmail.com>
toshi1127 <toshi.matsumoto.2n@stu.hosei.ac.jp>
twobin <twobin@live.com>
ty888 <1506125048@qq.com>
u3u <qwq@qwq.cat>
ubuntugod <adarshron@gmail.com>
uchanlee <uchanlee.dev@gmail.com>
@ -1268,7 +1342,6 @@ xiejiahe <xjh22222228@gmail.com>
xilihuasi <2857818553@qq.com>
xinhui.zxh <xinhui.zxh@antfin.com>
xrkffgg <xrkffgg@gmail.com>
xrkffgg <xrkffgg@vip.qq.com>
xuqiang <xuqiang@xiaoshouyi.com>
xyb <576420147@qq.com>
xz <limxz97@gmail.com>
@ -1284,6 +1357,7 @@ yehq <yedi728@qq.com>
yeliex <yeliex@yeliex.com>
yeshan333 <1329441308@qq.com>
yibu.wang <yibu.wang@orion.co.com>
yifanwww <yifanw1101@gmail.com>
yiminanci <yiminanci@gmail.com>
yiminghe <yiminghe@gmail.com>
yingxirz <inseeing@gmail.com>
@ -1292,6 +1366,7 @@ youngz <chinayangzhan@126.com>
yuche <i@yuche.me>
yuezk <yuezk001@gmail.com>
yui <1151815317@qq.com>
yykoypj <601924094@qq.com>
z <haig8@msn.com>
zack <zxyah@126.com>
zefeng <zefengbao@outlook.com>
@ -1308,6 +1383,7 @@ zhao-huo-long <lijiuyi1995@outlook.com>
zhaocai <lzc09008@gmail.com>
zhaopeidong <lwindscar@gmail.com>
zhenfan.yu <fanerge@qq.com>
zhengjitf <zhengjitf@gmail.com>
zhuguibiao <505418722@qq.com>
zhujun24 <zhujun87654321@gmail.com>
zhyupe <zyp108421@gmail.com>
@ -1317,16 +1393,19 @@ zj9495 <zj9495@gmail.com>
zkwolf <chenhao5866@gmail.com>
zlljqn <zlljqn@gmail.com>
zollero <corona7@163.com>
zombiej <smith3816@gmail.com>
zongzi531 <zongzi.xy@gmail.com>
zoomdong <1344492820@qq.com>
zpc7 <zpc.excel@foxmail.com>
zqran <uuxnet@gmail.com>
ztplz <mysticzt@gmail.com>
zty <tianyuan233.zhang@gmail.com>
zuiidea <zuiiidea@gmail.com>
zx6658 <zx6658@naver.com>
zxyao <zxyao145@gmail.com>
zytjs <yitongzhao@163.com>
zz <2418184580@qq.com>
°))))彡 <fisherspy@live.com>
Ömer Faruk APLAK <omer@pankod.com>
Ștefan Filip <stefy.filip@gmail.com>
रोहन मल्होत्रा <rohan.malhotra@adwyze.com>
一喵呜 <hyb628@gmail.com>
@ -1337,6 +1416,7 @@ zytjs <yitongzhao@163.com>
九思⚡⚡⚡ <2228429150@qq.com>
二哲 <kodo@forchange.cn>
二手掉包工程师 <rustin.liu@gmail.com>
二货爱吃白萝卜 <smith3816@gmail.com>
云剪者 <584518260@qq.com>
付引 <xxxquotes@gmail.com>
何乐 <work@imhele.com>
@ -1344,6 +1424,7 @@ zytjs <yitongzhao@163.com>
何锦余 <cadenho@hotmail.com>
佛门耶稣 <fomenyesu@gmail.com>
信鑫-King <chaolinjin@gmail.com>
元凛 <xrkffgg@vip.qq.com>
兼续 <rdmclin2@163.com>
冷方冰 <664930912@qq.com>
刘红 <liuhong1.happy@163.com>
@ -1360,6 +1441,7 @@ zytjs <yitongzhao@163.com>
小哈husky <951565664@qq.com>
小菜 <645801890@qq.com>
小鹅鹅鹅 <littleee.lau@gmail.com>
尾宿君 <lzm0x219@gmail.com>
山客 <zeakhold@gmail.com>
崔宏森 <948346354@qq.com>
广彬-梁 <326741518@qq.com>
@ -1384,6 +1466,7 @@ zytjs <yitongzhao@163.com>
柚子男 <yozman@sina.com>
沐霖 <304647173@qq.com>
爱but的苍蝇 <354788473@qq.com>
王小王 <wsp971@163.com>
王林涛 <hzwanglintao@corp.netease.com>
王浩 <boomler@hotmail.com>
王集鹄 <wjhu111@21cn.com>
@ -1391,6 +1474,7 @@ zytjs <yitongzhao@163.com>
白羊座小葛 <abeyuhang@gmail.com>
砖家 <brickspert.fjl@antfin.com>
砖家 <576679268@qq.com>
社长长 <ischenkan@outlook.com>
章鱼 <ryker.zy@gmail.com>
竹尔 <Juelchiang@gmail.com>
米老朱 <laozhu.me@gmail.com>
@ -1398,6 +1482,7 @@ zytjs <yitongzhao@163.com>
约修亚 <510448079@qq.com>
翁润雨 <593110501@qq.com>
臧甲彬 fadeaway <498497303@qq.com>
舜岳 <1277952981@qq.com>
苏秦 <646382806@qq.com>
英布 <chaoren1641@gmail.com>
萧琚 <yizhi.lyz@antfin.com>
@ -1406,6 +1491,7 @@ zytjs <yitongzhao@163.com>
蔡伦 <sliuqin@gmail.com>
薛定谔的猫 <weiran.zsd@outlook.com>
薛定谔的猫 <hh_2013@foxmail.com>
行冥 <571748236@qq.com>
诸岳 <fuping.dfp@antfin.com>
诸岳 <fuping.dfp@antgroup.com>
诸岳 <dengfuping_private@163.com>
@ -1414,6 +1500,7 @@ zytjs <yitongzhao@163.com>
谭真 <736420282@qq.com>
超能刚哥 <margox@foxmail.com>
迷渡 <justjavac@gmail.com>
郑国庆 <zhengshuai1993816@163.com>
郑旭 <332171564@qq.com>
闲耘™ <hotoo.cn@gmail.com>
陆离 <surgesoft@gmail.com>
@ -1434,4 +1521,5 @@ zytjs <yitongzhao@163.com>
黄文鉴 <concefly@foxmail.com>
黄斌 <bin.huang02@hand-china.com>
黑雨 <wangning4567@163.com>
龙风 <455947455@qq.com>
龚方闻 <fangwen.gong@baishancloud.com>

2
components/_util/reactNode.ts

@ -2,7 +2,7 @@ import * as React from 'react';
export const { isValidElement } = React;
export function isFragment(child: React.ReactElement): boolean {
export function isFragment(child: any): boolean {
return child && isValidElement(child) && child.type === React.Fragment;
}

4
components/_util/wave/index.tsx

@ -114,7 +114,7 @@ class InternalWave extends React.Component<WaveProps> {
onClick = (node: HTMLElement, waveColor: string) => {
const { insertExtraNode, disabled } = this.props;
if (disabled || !node || isHidden(node) || node.className.indexOf('-leave') >= 0) {
if (disabled || !node || isHidden(node) || node.className.includes('-leave')) {
return;
}
@ -183,7 +183,7 @@ class InternalWave extends React.Component<WaveProps> {
!node ||
!node.getAttribute ||
node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0
node.className.includes('disabled')
) {
return;
}

28
components/alert/index.en-US.md

@ -33,20 +33,20 @@ Alert component for feedback.
## API
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| action | The action of Alert | ReactNode | - | 4.9.0 |
| afterClose | Called when close animation is finished | () => void | - | |
| banner | Whether to show as banner | boolean | false | |
| closable | Whether Alert can be closed | boolean | - | |
| closeText | Close text to show | ReactNode | - | |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| description | Additional content of Alert | ReactNode | - | |
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
| message | Content of Alert | ReactNode | - | |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true | |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` | |
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - | |
| Property | Description | Type | Default | Version |
| ----------- | -------------------------------------------------------------------- | ----------------------- | --------------------------------------------- | ------- |
| action | The action of Alert | ReactNode | - | 4.9.0 |
| afterClose | Called when close animation is finished | () => void | - | |
| banner | Whether to show as banner | boolean | false | |
| closable | Whether Alert can be closed | boolean | - | |
| closeText | Close text to show | ReactNode | - | |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | Additional content of Alert | ReactNode | - | |
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
| message | Content of Alert | ReactNode | - | |
| showIcon | Whether to show icon | boolean | false, in `banner` mode default is true | |
| type | Type of Alert styles, options: `success`, `info`, `warning`, `error` | string | `info`, in `banner` mode default is `warning` | |
| onClose | Callback when Alert is closed | (e: MouseEvent) => void | - | |
### Alert.ErrorBoundary

36
components/alert/index.zh-CN.md

@ -34,24 +34,24 @@ group:
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| action | 自定义操作项 | ReactNode | - | 4.9.0 |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | |
| banner | 是否用作顶部公告 | boolean | false | |
| closable | 默认不显示关闭按钮 | boolean | - | |
| closeText | 自定义关闭按钮 | ReactNode | - | |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
| message | 警告提示内容 | ReactNode | - | |
| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true | |
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` | |
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------- | -------------------------------------------------------------------- | ----------------------- | ----------------------------------------- | ------ |
| action | 自定义操作项 | ReactNode | - | 4.9.0 |
| afterClose | 关闭动画结束后触发的回调函数 | () => void | - | |
| banner | 是否用作顶部公告 | boolean | false | |
| closable | 默认不显示关闭按钮 | boolean | - | |
| closeText | 自定义关闭按钮 | ReactNode | - | |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
| message | 警告提示内容 | ReactNode | - | |
| showIcon | 是否显示辅助图标 | boolean | false,`banner` 模式下默认值为 true | |
| type | 指定警告提示的样式,有四种选择 `success`、`info`、`warning`、`error` | string | `info`,`banner` 模式下默认值为 `warning` | |
| onClose | 关闭时触发的回调函数 | (e: MouseEvent) => void | - | |
### Alert.ErrorBoundary
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | {{ error stack }} | |
| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | {{ error }} | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------- | -------------------------------------------- | --------- | ----------------- | ---- |
| description | 自定义错误内容,如果未指定会展示报错堆栈 | ReactNode | {{ error stack }} | |
| message | 自定义错误标题,如果未指定会展示原生报错信息 | ReactNode | {{ error }} | |

16
components/alert/style/index.tsx

@ -7,6 +7,7 @@ export interface ComponentToken {}
type AlertToken = FullToken<'Alert'> & {
alertIconSizeLG: number;
alertPaddingHorizontal: number;
};
const genAlertTypeStyle = (
@ -36,10 +37,10 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
motionEaseInOutCirc,
alertIconSizeLG,
colorText,
paddingSM,
paddingXS,
paddingTmp,
paddingLG,
paddingContentVerticalSM,
alertPaddingHorizontal,
paddingMD,
paddingContentHorizontalLG,
} = token;
return {
@ -48,7 +49,7 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
position: 'relative',
display: 'flex',
alignItems: 'center',
padding: `${paddingXS}px ${paddingSM}px`,
padding: `${paddingContentVerticalSM}px ${alertPaddingHorizontal}px`, // Fixed horizontal padding here.
wordWrap: 'break-word',
borderRadius,
@ -95,8 +96,8 @@ export const genBaseStyle: GenerateStyle<AlertToken> = (token: AlertToken): CSSO
[`${componentCls}-with-description`]: {
alignItems: 'flex-start',
paddingInline: paddingLG,
paddingBlock: paddingTmp,
paddingInline: paddingContentHorizontalLG,
paddingBlock: paddingMD,
[`${componentCls}-icon`]: {
marginInlineEnd: marginSM,
@ -232,6 +233,7 @@ export default genComponentStyleHook('Alert', token => {
const alertToken = mergeToken<AlertToken>(token, {
alertIconSizeLG: fontSizeHeading3,
alertPaddingHorizontal: 12, // Fixed value here.
});
return [genAlertStyle(alertToken)];

9
components/back-top/__tests__/index.test.tsx

@ -2,13 +2,15 @@ import React from 'react';
import BackTop from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { fireEvent, render, sleep } from '../../../tests/utils';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
describe('BackTop', () => {
mountTest(BackTop);
rtlTest(BackTop);
it('should scroll to top after click it', async () => {
jest.useFakeTimers();
const { container } = render(<BackTop visibilityHeight={-1} />);
const scrollToSpy = jest.spyOn(window, 'scrollTo').mockImplementation((_, y) => {
window.scrollY = y;
@ -18,9 +20,12 @@ describe('BackTop', () => {
window.scrollTo(0, 400);
expect(document.documentElement.scrollTop).toBe(400);
fireEvent.click(container.querySelector('.ant-back-top')!);
await sleep(500);
await waitFakeTimer();
expect(document.documentElement.scrollTop).toBe(0);
scrollToSpy.mockRestore();
jest.clearAllTimers();
jest.useRealTimers();
});
it('support onClick', async () => {

2
components/badge/utils.tsx

@ -2,5 +2,5 @@ import { PresetColorTypes } from '../_util/colors';
// eslint-disable-next-line import/prefer-default-export
export function isPresetColor(color?: string): boolean {
return (PresetColorTypes as any[]).indexOf(color) !== -1;
return (PresetColorTypes as any[]).includes(color);
}

8
components/button/button.tsx

@ -8,7 +8,7 @@ import DisabledContext from '../config-provider/DisabledContext';
import type { SizeType } from '../config-provider/SizeContext';
import SizeContext from '../config-provider/SizeContext';
import { useCompactItemContext } from '../space/Compact';
import { cloneElement } from '../_util/reactNode';
import { cloneElement, isFragment } from '../_util/reactNode';
import { tuple } from '../_util/type';
import warning from '../_util/warning';
import Wave from '../_util/wave';
@ -28,10 +28,6 @@ function isUnBorderedButtonType(type: ButtonType | undefined) {
return type === 'text' || type === 'link';
}
function isReactFragment(node: React.ReactNode) {
return React.isValidElement(node) && node.type === React.Fragment;
}
// Insert one space between two chinese characters automatically.
function insertSpace(child: React.ReactElement | string | number, needInserted: boolean) {
// Check the child if is undefined or null.
@ -53,7 +49,7 @@ function insertSpace(child: React.ReactElement | string | number, needInserted:
if (typeof child === 'string') {
return isTwoCNChar(child) ? <span>{child.split('').join(SPACE)}</span> : <span>{child}</span>;
}
if (isReactFragment(child)) {
if (isFragment(child)) {
return <span>{child}</span>;
}
return child;

7
components/button/style/index.tsx

@ -10,6 +10,7 @@ export interface ComponentToken {}
export interface ButtonToken extends FullToken<'Button'> {
// FIXME: should be removed
colorOutlineDefault: string;
buttonPaddingHorizontal: number;
}
// ============================== Shared ==============================
@ -344,7 +345,7 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
0,
(token.controlHeight - token.fontSize * token.lineHeight) / 2 - token.controlLineWidth,
);
const paddingHorizontal = token.padding - token.controlLineWidth;
const paddingHorizontal = token.buttonPaddingHorizontal - token.controlLineWidth;
const iconOnlyCls = `${componentCls}-icon-only`;
@ -399,6 +400,7 @@ const genSizeSmallButtonStyle: GenerateStyle<ButtonToken> = token => {
const smallToken = mergeToken<ButtonToken>(token, {
controlHeight: token.controlHeightSM,
padding: token.paddingXS,
buttonPaddingHorizontal: 8, // Fixed padding
controlRadius: token.controlRadiusSM,
});
@ -417,10 +419,11 @@ const genSizeLargeButtonStyle: GenerateStyle<ButtonToken> = token => {
// ============================== Export ==============================
export default genComponentStyleHook('Button', token => {
const { controlTmpOutline } = token;
const { controlTmpOutline, paddingContentHorizontal } = token;
const buttonToken = mergeToken<ButtonToken>(token, {
colorOutlineDefault: controlTmpOutline,
buttonPaddingHorizontal: paddingContentHorizontal,
});
return [

26
components/card/style/index.tsx

@ -4,10 +4,15 @@ import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { clearFix, resetComponent } from '../../style';
export interface ComponentToken {}
interface CardToken extends FullToken<'Card'> {
cardHeaderHeight: number;
cardHeaderHeightSM: number;
cardShadow: string;
cardHeadHeight: number;
cardHeadPadding: number;
cardPaddingSM: number;
cardPaddingBase: number;
cardHeadTabsMarginBottom: number;
cardInnerHeadPadding: number;
@ -236,9 +241,9 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
cardShadow,
cardHeadHeight,
cardHeadPadding,
cardPaddingBase,
colorBorderSecondary,
boxShadow,
cardPaddingBase,
} = token;
return {
@ -345,15 +350,14 @@ const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
// ============================== Size ==============================
const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, cardPaddingBase, cardHeadPadding } = token;
const cardPaddingBaseSM = cardPaddingBase / 2;
const cardHeadPaddingSM = cardHeadPadding / 2;
const { componentCls, cardPaddingSM, fontSize, lineHeight, cardHeaderHeightSM } = token;
const cardHeadPaddingSM = (cardHeaderHeightSM - fontSize * lineHeight) / 2;
return {
[`${componentCls}-small`]: {
[`> ${componentCls}-head`]: {
minHeight: cardHeadPaddingSM * 2 + token.fontSize,
padding: `0 ${cardPaddingBaseSM}px`,
minHeight: cardHeaderHeightSM,
padding: `0 ${cardPaddingSM}px`,
fontSize: token.fontSize,
[`> ${componentCls}-head-wrapper`]: {
@ -369,7 +373,7 @@ const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
},
[`> ${componentCls}-body`]: {
padding: cardPaddingBaseSM,
padding: cardPaddingSM,
},
},
};
@ -377,17 +381,17 @@ const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
// ============================== Export ==============================
export default genComponentStyleHook('Card', token => {
const cardHeadPadding = token.padding;
const cardToken = mergeToken<CardToken>(token, {
cardShadow: token.boxShadowCard,
cardHeadHeight: token.fontSizeLG + cardHeadPadding * 2,
cardHeadPadding,
cardHeaderHeight: token.fontSizeLG * token.lineHeightLG + token.padding * 2,
cardHeaderHeightSM: token.fontSize * token.lineHeight + token.paddingXS * 2,
cardHeadPadding: token.padding,
cardPaddingBase: token.paddingLG,
cardHeadTabsMarginBottom: -token.padding - token.lineWidth,
cardInnerHeadPadding: token.paddingSM,
cardActionsLiMargin: `${token.paddingSM}px 0`,
cardActionsIconSize: token.fontSize,
cardPaddingSM: 12, // Fixed padding.
});
return [

22
components/carousel/__tests__/index.test.tsx

@ -3,7 +3,7 @@ import type { CarouselRef } from '..';
import Carousel from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep, render, act } from '../../../tests/utils';
import { waitFakeTimer, render } from '../../../tests/utils';
describe('Carousel', () => {
mountTest(Carousel);
@ -17,14 +17,6 @@ describe('Carousel', () => {
jest.useRealTimers();
});
function runAllTimersWithAct(times = 1) {
for (let i = 0; i < times; i++) {
act(() => {
jest.runAllTimers();
});
}
}
it('should has innerSlider', () => {
const ref = React.createRef<CarouselRef>();
render(
@ -51,16 +43,16 @@ describe('Carousel', () => {
expect(typeof goTo).toBe('function');
expect(ref.current?.innerSlider.state.currentSlide).toBe(0);
ref.current?.goTo(2);
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(2);
// wait for animation to be finished
runAllTimersWithAct(2);
await waitFakeTimer();
ref.current?.prev();
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(1);
runAllTimersWithAct(2);
await waitFakeTimer();
ref.current?.next();
runAllTimersWithAct(1);
await waitFakeTimer();
expect(ref.current?.innerSlider.state.currentSlide).toBe(2);
});
@ -77,7 +69,7 @@ describe('Carousel', () => {
const spy = jest.spyOn(ref.current?.innerSlider, 'autoPlay');
window.resizeTo(1000, window.outerHeight);
expect(spy).not.toHaveBeenCalled();
await sleep(500);
await waitFakeTimer();
expect(spy).toHaveBeenCalled();
});

2
components/checkbox/Checkbox.tsx

@ -106,7 +106,7 @@ const InternalCheckbox: React.ForwardRefRenderFunction<HTMLInputElement, Checkbo
}
};
checkboxProps.name = checkboxGroup.name;
checkboxProps.checked = checkboxGroup.value.indexOf(restProps.value) !== -1;
checkboxProps.checked = checkboxGroup.value.includes(restProps.value);
}
const classString = classNames(
{

4
components/checkbox/Group.tsx

@ -103,7 +103,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
const opts = getOptions();
onChange?.(
newValue
.filter(val => registeredValues.indexOf(val) !== -1)
.filter(val => registeredValues.includes(val))
.sort((a, b) => {
const indexA = opts.findIndex(opt => opt.value === a);
const indexB = opts.findIndex(opt => opt.value === b);
@ -126,7 +126,7 @@ const InternalCheckboxGroup: React.ForwardRefRenderFunction<HTMLDivElement, Chec
key={option.value.toString()}
disabled={'disabled' in option ? option.disabled : restProps.disabled}
value={option.value}
checked={value.indexOf(option.value) !== -1}
checked={value.includes(option.value)}
onChange={option.onChange}
className={`${groupPrefixCls}-item`}
style={option.style}

6
components/collapse/__tests__/index.test.tsx

@ -1,6 +1,6 @@
import React from 'react';
import { act } from 'react-dom/test-utils';
import { sleep, render, fireEvent } from '../../../tests/utils';
import { waitFakeTimer, render, fireEvent } from '../../../tests/utils';
import { resetWarned } from '../../_util/warning';
describe('Collapse', () => {
@ -66,6 +66,7 @@ describe('Collapse', () => {
});
it('could be expand and collapse', async () => {
jest.useFakeTimers();
const { container } = render(
<Collapse>
<Collapse.Panel header="This is panel header 1" key="1">
@ -77,10 +78,11 @@ describe('Collapse', () => {
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(false);
fireEvent.click(container.querySelector('.ant-collapse-header')!);
await sleep(400);
await waitFakeTimer();
expect(
container.querySelector('.ant-collapse-item')?.classList.contains('ant-collapse-item-active'),
).toBe(true);
jest.useRealTimers();
});
it('could override default openMotion', () => {

11
components/collapse/style/index.tsx

@ -3,19 +3,22 @@ import type { FullToken, GenerateStyle } from '../../theme';
import { genComponentStyleHook, mergeToken } from '../../theme';
import { resetComponent, resetIcon } from '../../style';
export interface ComponentToken {}
type CollapseToken = FullToken<'Collapse'> & {
collapseContentBg: string;
collapseContentPadding: number;
collapseHeaderBg: string;
collapseHeaderPadding: string;
collapsePanelBorderRadius: number;
collapseContentPaddingHorizontal: number;
};
export const genBaseStyle: GenerateStyle<CollapseToken> = token => {
const {
componentCls,
collapseContentBg,
collapseContentPadding,
padding,
collapseContentPaddingHorizontal,
collapseHeaderBg,
collapseHeaderPadding,
collapsePanelBorderRadius,
@ -130,7 +133,7 @@ export const genBaseStyle: GenerateStyle<CollapseToken> = token => {
borderTop: borderBase,
[`& > ${componentCls}-content-box`]: {
padding: collapseContentPadding,
padding: `${padding}px ${collapseContentPaddingHorizontal}px`,
},
[`&-hidden`]: {
@ -255,10 +258,10 @@ const genGhostStyle: GenerateStyle<CollapseToken> = token => {
export default genComponentStyleHook('Collapse', token => {
const collapseToken = mergeToken<CollapseToken>(token, {
collapseContentBg: token.colorBgContainer,
collapseContentPadding: token.padding,
collapseHeaderBg: token.colorFillAlter,
collapseHeaderPadding: `${token.paddingSM}px ${token.padding}px`,
collapsePanelBorderRadius: token.radiusLG,
collapseContentPaddingHorizontal: 16, // Fixed value
});
return [

30
components/config-provider/__tests__/theme.test.tsx

@ -8,7 +8,8 @@ import { useToken } from '../../theme';
import theme from '../../theme/export';
import { resetWarned } from '../../_util/warning';
const { defaultAlgorithm, darkAlgorithm, defaultAlgorithmV4, darkAlgorithmV4 } = theme;
const { defaultAlgorithm, darkAlgorithm, defaultAlgorithmV4, darkAlgorithmV4, compactAlgorithm } =
theme;
let mockCanUseDom = true;
@ -102,6 +103,33 @@ describe('ConfigProvider.Theme', () => {
expect(tokenRef?.colorPrimaryText).toBe('#177ddc');
});
it('compactAlgorithm should work', () => {
let tokenRef: any;
const Demo = () => {
const [, token] = useToken();
tokenRef = token;
return null;
};
render(
<ConfigProvider theme={{ token: { sizeBaseStep: 2 }, algorithm: compactAlgorithm }}>
<Demo />
</ConfigProvider>,
);
expect(tokenRef).toEqual(
expect.objectContaining({
sizeXXL: 48,
sizeXL: 32,
sizeLG: 16,
sizeMD: 16,
sizeMS: 12,
size: 8,
sizeSM: 8,
sizeXS: 4,
sizeXXS: 4,
}),
);
});
it('should support algorithm array', () => {
let tokenRef: any;
const Demo = () => {

38
components/config-provider/index.en-US.md

@ -45,25 +45,25 @@ Some components use dynamic style to support wave effect. You can config `csp` p
## API
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autoInsertSpaceInButton | Set false to remove space between 2 chinese characters on Button | boolean | true | |
| componentDisabled | Config antd component `disabled` | boolean | - | 4.21.0 |
| componentSize | Config antd component size | `small` \| `middle` \| `large` | - | |
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
| direction | Set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 4.3.0 |
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional` } | - | requiredMark: 4.8.0 |
| getPopupContainer | To set the container of the popup element. The default is to create a `div` element in `body` | function(triggerNode) | () => document.body | |
| getTargetContainer | Config Affix, Anchor scroll target container | () => HTMLElement | () => window | 4.2.0 |
| iconPrefixCls | Set icon prefix className (cooperated with [@iconfont-css-prefix](https://github.com/ant-design/ant-design/blob/d943b85a523bdf181dabc12c928226f3b4b893de/components/style/themes/default.less#L106)) | string | `anticon` | 4.11.0 |
| input | Set Input common props | { autoComplete?: string } | - | 4.2.0 |
| locale | Language package setting, you can find the packages in [antd/es/locale](http://unpkg.com/antd/es/locale/) | object | - | |
| pageHeader | Unify the ghost of PageHeader, ref [PageHeader](/components/page-header) | { ghost: boolean } | true | |
| prefixCls | Set prefix className (cooperated with [@ant-prefix](https://github.com/ant-design/ant-design/blob/2c6c789e3a9356f96c47aea0083f5a15538315cf/components/style/themes/default.less#L7)) | string | `ant` | |
| renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | |
| space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
| Property | Description | Type | Default | Version |
| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------- |
| autoInsertSpaceInButton | Set false to remove space between 2 chinese characters on Button | boolean | true | |
| componentDisabled | Config antd component `disabled` | boolean | - | 4.21.0 |
| componentSize | Config antd component size | `small` \| `middle` \| `large` | - | |
| csp | Set [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) config | { nonce: string } | - | |
| direction | Set direction of layout. See [demo](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | - | 4.3.0 |
| form | Set Form common props | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional` } | - | requiredMark: 4.8.0 |
| getPopupContainer | To set the container of the popup element. The default is to create a `div` element in `body` | function(triggerNode) | () => document.body | |
| getTargetContainer | Config Affix, Anchor scroll target container | () => HTMLElement | () => window | 4.2.0 |
| iconPrefixCls | Set icon prefix className (cooperated with [@iconfont-css-prefix](https://github.com/ant-design/ant-design/blob/d943b85a523bdf181dabc12c928226f3b4b893de/components/style/themes/default.less#L106)) | string | `anticon` | 4.11.0 |
| input | Set Input common props | { autoComplete?: string } | - | 4.2.0 |
| locale | Language package setting, you can find the packages in [antd/locale](http://unpkg.com/antd/locale/) | object | - | |
| pageHeader | Unify the ghost of PageHeader, ref [PageHeader](/components/page-header) | { ghost: boolean } | true | |
| prefixCls | Set prefix className (cooperated with [@ant-prefix](https://github.com/ant-design/ant-design/blob/2c6c789e3a9356f96c47aea0083f5a15538315cf/components/style/themes/default.less#L7)) | string | `ant` | |
| renderEmpty | Set empty content of components. Ref [Empty](/components/empty/) | function(componentName: string): ReactNode | - | |
| space | Set Space `size`, ref [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
### ConfigProvider.config() `4.13.0+`

2
components/config-provider/index.tsx

@ -8,7 +8,7 @@ import type { RequiredMark } from '../form/Form';
import type { Locale } from '../locale-provider';
import LocaleProvider, { ANT_MARK } from '../locale-provider';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import defaultLocale from '../locale/en_US';
import { DesignTokenContext } from '../theme';
import defaultSeedToken from '../theme/themes/seed';
import type { ConfigConsumerProps, CSPConfig, DirectionType, Theme, ThemeConfig } from './context';

38
components/config-provider/index.zh-CN.md

@ -46,25 +46,25 @@ export default () => (
## API
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| autoInsertSpaceInButton | 设置为 `false` 时,移除按钮中 2 个汉字之间的空格 | boolean | true | |
| componentDisabled | 设置 antd 组件禁用状态 | boolean | - | 4.21.0 |
| componentSize | 设置 antd 组件大小 | `small` \| `middle` \| `large` | - | |
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 4.3.0 |
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional`, colon?: boolean} | - | requiredMark: 4.8.0; colon: 4.18.0 |
| getPopupContainer | 弹出框(Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | function(triggerNode) | () => document.body | |
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 4.2.0 |
| iconPrefixCls | 设置图标统一样式前缀。注意:需要配合 `less` 变量 [@iconfont-css-prefix](https://github.com/ant-design/ant-design/blob/d943b85a523bdf181dabc12c928226f3b4b893de/components/style/themes/default.less#L106) 使用 | string | `anticon` | 4.11.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string } | - | 4.2.0 |
| locale | 语言包配置,语言包可到 [antd/es/locale](http://unpkg.com/antd/es/locale/) 目录下寻找 | object | - | |
| pageHeader | 统一设置 PageHeader 的 ghost,参考 [PageHeader](/components/page-header) | { ghost: boolean } | true | |
| prefixCls | 设置统一样式前缀。注意:需要配合 `less` 变量 [@ant-prefix](https://github.com/ant-design/ant-design/blob/2c6c789e3a9356f96c47aea0083f5a15538315cf/components/style/themes/default.less#L7) 使用 | string | `ant` | |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | function(componentName: string): ReactNode | - | |
| space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------- | ---------------------------------- |
| autoInsertSpaceInButton | 设置为 `false` 时,移除按钮中 2 个汉字之间的空格 | boolean | true | |
| componentDisabled | 设置 antd 组件禁用状态 | boolean | - | 4.21.0 |
| componentSize | 设置 antd 组件大小 | `small` \| `middle` \| `large` | - | |
| csp | 设置 [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) 配置 | { nonce: string } | - | |
| direction | 设置文本展示方向。 [示例](#components-config-provider-demo-direction) | `ltr` \| `rtl` | `ltr` | |
| dropdownMatchSelectWidth | 下拉菜单和选择器同宽。默认将设置 `min-width`,当值小于选择框宽度时会被忽略。`false` 时会关闭虚拟滚动 | boolean \| number | - | 4.3.0 |
| form | 设置 Form 组件的通用属性 | { validateMessages?: [ValidateMessages](/components/form/#validateMessages), requiredMark?: boolean \| `optional`, colon?: boolean} | - | requiredMark: 4.8.0; colon: 4.18.0 |
| getPopupContainer | 弹出框(Select, Tooltip, Menu 等等)渲染父节点,默认渲染到 body 上。 | function(triggerNode) | () => document.body | |
| getTargetContainer | 配置 Affix、Anchor 滚动监听容器。 | () => HTMLElement | () => window | 4.2.0 |
| iconPrefixCls | 设置图标统一样式前缀。注意:需要配合 `less` 变量 [@iconfont-css-prefix](https://github.com/ant-design/ant-design/blob/d943b85a523bdf181dabc12c928226f3b4b893de/components/style/themes/default.less#L106) 使用 | string | `anticon` | 4.11.0 |
| input | 设置 Input 组件的通用属性 | { autoComplete?: string } | - | 4.2.0 |
| locale | 语言包配置,语言包可到 [antd/locale](http://unpkg.com/antd/locale/) 目录下寻找 | object | - | |
| pageHeader | 统一设置 PageHeader 的 ghost,参考 [PageHeader](/components/page-header) | { ghost: boolean } | true | |
| prefixCls | 设置统一样式前缀。注意:需要配合 `less` 变量 [@ant-prefix](https://github.com/ant-design/ant-design/blob/2c6c789e3a9356f96c47aea0083f5a15538315cf/components/style/themes/default.less#L7) 使用 | string | `ant` | |
| renderEmpty | 自定义组件空状态。参考 [空状态](/components/empty/) | function(componentName: string): ReactNode | - | |
| space | 设置 Space 的 `size`,参考 [Space](/components/space) | { size: `small` \| `middle` \| `large` \| `number` } | - | 4.1.0 |
| virtual | 设置 `false` 时关闭虚拟滚动 | boolean | - | 4.3.0 |
### ConfigProvider.config() `4.13.0+`

194
components/date-picker/index.en-US.md

@ -33,7 +33,7 @@ By clicking the input box, you can select a date from a popup calendar.
<code src="./demo/mode.tsx" debug>Controlled Panels</code>
<code src="./demo/start-end.tsx" debug>Customized Range Picker</code>
<code src="./demo/suffix.tsx" debug>Suffix</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
## API
@ -63,7 +63,7 @@ import locale from 'antd/es/date-picker/locale/zh_CN';
// The default locale is en-US, if you want to use other locale, just set locale in entry file globally.
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import locale from 'antd/es/locale/zh_CN';
import locale from 'antd/locale/zh_CN';
<ConfigProvider locale={locale}>
<DatePicker defaultValue={dayjs('2015-01-01', 'YYYY-MM-DD')} />
@ -74,36 +74,36 @@ import locale from 'antd/es/locale/zh_CN';
The following APIs are shared by DatePicker, RangePicker.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowClear | Whether to show clear button | boolean | true | |
| autoFocus | If get focus when component mounted | boolean | false | |
| bordered | Whether has border style | boolean | true | |
| className | The picker className | string | - | |
| dateRender | Custom rendering function for date cells | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | |
| disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| popupClassName | To customize the className of the popup calendar | string | - | 4.23.0 |
| getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | |
| locale | Localization configuration | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
| mode | The picker panel mode( [Cannot select year or month anymore?](/docs/react/faq#When-set-mode-to-DatePicker/RangePicker,-cannot-select-year-or-month-anymore?) ) | `time` \| `date` \| `month` \| `year` \| `decade` | - | |
| nextIcon | The custom next icon | ReactNode | - | 4.17.0 |
| open | The open state of picker | boolean | - | |
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | To customize the style of the input box | CSSProperties | {} | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |
| superPrevIcon | The custom super prev icon | ReactNode | - | 4.17.0 |
| onOpenChange | Callback function, can be executed whether the popup calendar is popped up or closed | function(open) | - | |
| onPanelChange | Callback when picker panel mode is changed | function(value, mode) | - | |
| Property | Description | Type | Default | Version |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------- |
| allowClear | Whether to show clear button | boolean | true | |
| autoFocus | If get focus when component mounted | boolean | false | |
| bordered | Whether has border style | boolean | true | |
| className | The picker className | string | - | |
| dateRender | Custom rendering function for date cells | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | |
| disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| popupClassName | To customize the className of the popup calendar | string | - | 4.23.0 |
| getPopupContainer | To set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - | |
| inputReadOnly | Set the `readonly` attribute of the input tag (avoids virtual keyboard on touch devices) | boolean | false | |
| locale | Localization configuration | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
| mode | The picker panel mode( [Cannot select year or month anymore?](/docs/react/faq#When-set-mode-to-DatePicker/RangePicker,-cannot-select-year-or-month-anymore?) ) | `time` \| `date` \| `month` \| `year` \| `decade` | - | |
| nextIcon | The custom next icon | ReactNode | - | 4.17.0 |
| open | The open state of picker | boolean | - | |
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | The placeholder of date input | string \| \[string,string] | - | |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 |
| size | To determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | `large` \| `middle` \| `small` | - | |
| status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | To customize the style of the input box | CSSProperties | {} | |
| suffixIcon | The custom suffix icon | ReactNode | - | |
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |
| superPrevIcon | The custom super prev icon | ReactNode | - | 4.17.0 |
| onOpenChange | Callback function, can be executed whether the popup calendar is popped up or closed | function(open) | - | |
| onPanelChange | Callback when picker panel mode is changed | function(value, mode) | - | |
### Common Methods
@ -114,88 +114,88 @@ The following APIs are shared by DatePicker, RangePicker.
### DatePicker
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date, if start time or end time is null or undefined, the date range will be an open interval | [dayjs](https://day.js.org/) | - | |
| disabledTime | To specify the time that cannot be selected | function(date) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting, support [Custom Format](#components-date-picker-demo-format) | string \| (value: dayjs) => string \| (string \| (value: dayjs) => string)\[] | `YYYY-MM-DD` | |
| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - | |
| showNow | Whether to show 'Now' button on panel when `showTime` is set | boolean | - | 4.4.0 |
| showTime | To provide an additional time selection | object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/) | dayjs() | |
| showToday | Whether to show `Today` button | boolean | true | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| onOk | Callback when click ok button | function() | - | |
| onPanelChange | Callback function for panel changing | function(value, mode) | - | |
| Property | Description | Type | Default | Version |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | -------------------------------------------------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date, if start time or end time is null or undefined, the date range will be an open interval | [dayjs](https://day.js.org/) | - | |
| disabledTime | To specify the time that cannot be selected | function(date) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting, support [Custom Format](#components-date-picker-demo-format) | string \| (value: dayjs) => string \| (string \| (value: dayjs) => string)\[] | `YYYY-MM-DD` | |
| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - | |
| showNow | Whether to show 'Now' button on panel when `showTime` is set | boolean | - | 4.4.0 |
| showTime | To provide an additional time selection | object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/) | dayjs() | |
| showToday | Whether to show `Today` button | boolean | true | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| onOk | Callback when click ok button | function() | - | |
| onPanelChange | Callback function for panel changing | function(value, mode) | - | |
### DatePicker\[picker=year]
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| Property | Description | Type | Default | Version |
| ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | ------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=quarter]
Added in `4.1.0`.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-\QQ` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| Property | Description | Type | Default | Version |
| ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | ---------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-\QQ` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=month]
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-MM` | |
| monthCellRender | Custom month cell content render method | function(date, locale): ReactNode | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| Property | Description | Type | Default | Version |
| ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | --------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-MM` | |
| monthCellRender | Custom month cell content render method | function(date, locale): ReactNode | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=week]
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-wo` | |
| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| Property | Description | Type | Default | Version |
| ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | --------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default date | [dayjs](https://day.js.org/) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-wo` | |
| renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### RangePicker
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | |
| dateRender | Customize date cell. `info` argument is added in 4.3.0 | function(currentDate: dayjs, today: dayjs, info: { range: `start` \| `end` }) => React.ReactNode | - | |
| defaultPickerValue | To set default picker date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| disabled | If disable start or end | \[boolean, boolean] | - | |
| disabledTime | To specify the time that cannot be selected | function(date: dayjs, partial: `start` \| `end`) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting | string \| string\[] | `YYYY-MM-DD HH:mm:ss` | |
| ranges | The preseted ranges for quick selection | { \[range: string]: [dayjs](https://day.js.org/)\[] } \| { \[range: string]: () => [dayjs](https://day.js.org/)\[] } | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| separator | Set separator between inputs | React.ReactNode | `<SwapRightOutlined />` | |
| showTime | To provide an additional time selection | object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/)\[] | \[dayjs(), dayjs()] | |
| value | To set date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| onCalendarChange | Callback function, can be executed when the start time or the end time of the range is changing. `info` argument is added in 4.4.0 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string], info: { range:`start`\|`end` }) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(dates: \[dayjs, dayjs], dateStrings: \[string, string]) | - | |
| Property | Description | Type | Default | Version |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------- |
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | |
| dateRender | Customize date cell. `info` argument is added in 4.3.0 | function(currentDate: dayjs, today: dayjs, info: { range: `start` \| `end` }) => React.ReactNode | - | |
| defaultPickerValue | To set default picker date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| disabled | If disable start or end | \[boolean, boolean] | - | |
| disabledTime | To specify the time that cannot be selected | function(date: dayjs, partial: `start` \| `end`) | - | |
| format | To set the date format, refer to [dayjs](https://day.js.org/). When an array is provided, all values are used for parsing and first value is used for formatting | string \| string\[] | `YYYY-MM-DD HH:mm:ss` | |
| ranges | The preseted ranges for quick selection | { \[range: string]: [dayjs](https://day.js.org/)\[] } \| { \[range: string]: () => [dayjs](https://day.js.org/)\[] } | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| separator | Set separator between inputs | React.ReactNode | `<SwapRightOutlined />` | |
| showTime | To provide an additional time selection | object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | To set default time of selected date, [demo](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/)\[] | \[dayjs(), dayjs()] | |
| value | To set date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| onCalendarChange | Callback function, can be executed when the start time or the end time of the range is changing. `info` argument is added in 4.4.0 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string], info: { range:`start`\|`end` }) | - | |
| onChange | Callback function, can be executed when the selected time is changing | function(dates: \[dayjs, dayjs], dateStrings: \[string, string]) | - | |
## FAQ

194
components/date-picker/index.zh-CN.md

@ -34,7 +34,7 @@ demo:
<code src="./demo/mode.tsx" debug>受控面板</code>
<code src="./demo/start-end.tsx" debug>自定义日期范围选择</code>
<code src="./demo/suffix.tsx" debug>后缀图标</code>
<code src="./demo/render-panel.tsx" debug>_InternalPanelDoNotUseOrYouWillBeFired</code>
<code src="./demo/render-panel.tsx" debug>\_InternalPanelDoNotUseOrYouWillBeFired</code>
## API
@ -64,7 +64,7 @@ import locale from 'antd/es/date-picker/locale/zh_CN';
// 默认语言为 en-US,如果你需要设置其他语言,推荐在入口文件全局设置 locale
import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import locale from 'antd/es/locale/zh_CN';
import locale from 'antd/locale/zh_CN';
<ConfigProvider locale={locale}>
<DatePicker defaultValue={dayjs('2015-01-01', 'YYYY-MM-DD')} />
@ -75,36 +75,36 @@ import locale from 'antd/es/locale/zh_CN';
以下 API 为 DatePicker、 RangePicker 共享的 API。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowClear | 是否显示清除按钮 | boolean | true | |
| autoFocus | 自动获取焦点 | boolean | false | |
| bordered | 是否有边框 | boolean | true | |
| className | 选择器 className | string | - | |
| dateRender | 自定义日期单元格的内容 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | |
| disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| popupClassName | 额外的弹出日历 className | string | - | 4.23.0 |
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | |
| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | |
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
| mode | 日期面板的状态([设置后无法选择年份/月份?](/docs/react/faq#当我指定了-DatePicker/RangePicker-的-mode-属性后,点击后无法选择年份/月份?)) | `time` \| `date` \| `month` \| `year` \| `decade` | - | |
| nextIcon | 自定义下一个图标 | ReactNode | - | 4.17.0 |
| open | 控制弹层是否展开 | boolean | - | |
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
| picker | 设置选择器类型 | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | 输入框提示文字 | string \| \[string, string] | - | |
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
| size | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义输入框样式 | CSSProperties | {} | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |
| superPrevIcon | 自定义 `>>` 切换图标 | ReactNode | - | 4.17.0 |
| onOpenChange | 弹出日历和关闭日历的回调 | function(open) | - | |
| onPanelChange | 日历面板切换的回调 | function(value, mode) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------- |
| allowClear | 是否显示清除按钮 | boolean | true | |
| autoFocus | 自动获取焦点 | boolean | false | |
| bordered | 是否有边框 | boolean | true | |
| className | 选择器 className | string | - | |
| dateRender | 自定义日期单元格的内容 | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | |
| disabled | 禁用 | boolean | false | |
| disabledDate | 不可选择的日期 | (currentDate: dayjs) => boolean | - | |
| popupClassName | 额外的弹出日历 className | string | - | 4.23.0 |
| getPopupContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | - | |
| inputReadOnly | 设置输入框为只读(避免在移动设备上打开虚拟键盘) | boolean | false | |
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) | |
| mode | 日期面板的状态([设置后无法选择年份/月份?](/docs/react/faq#当我指定了-DatePicker/RangePicker-的-mode-属性后,点击后无法选择年份/月份?)) | `time` \| `date` \| `month` \| `year` \| `decade` | - | |
| nextIcon | 自定义下一个图标 | ReactNode | - | 4.17.0 |
| open | 控制弹层是否展开 | boolean | - | |
| panelRender | 自定义渲染面板 | (panelNode) => ReactNode | - | 4.5.0 |
| picker | 设置选择器类型 | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | 输入框提示文字 | string \| \[string, string] | - | |
| placement | 选择框弹出的位置 | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | 额外的弹出日历样式 | CSSProperties | {} | |
| prevIcon | 自定义上一个图标 | ReactNode | - | 4.17.0 |
| size | 输入框大小,`large` 高度为 40px,`small` 为 24px,默认是 32px | `large` \| `middle` \| `small` | - | |
| status | 设置校验状态 | 'error' \| 'warning' | - | 4.19.0 |
| style | 自定义输入框样式 | CSSProperties | {} | |
| suffixIcon | 自定义的选择框后缀图标 | ReactNode | - | |
| superNextIcon | 自定义 `<<` 切换图标 | ReactNode | - | 4.17.0 |
| superPrevIcon | 自定义 `>>` 切换图标 | ReactNode | - | 4.17.0 |
| onOpenChange | 弹出日历和关闭日历的回调 | function(open) | - | |
| onPanelChange | 日历面板切换的回调 | function(value, mode) | - | |
### 共同的方法
@ -115,88 +115,88 @@ import locale from 'antd/es/locale/zh_CN';
### DatePicker
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期,如果开始时间或结束时间为 `null` 或者 `undefined`,日期范围将是一个开区间 | [dayjs](https://day.js.org/) | - | |
| disabledTime | 不可选择的时间 | function(date) | - | |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs](https://day.js.org/),支持[自定义格式](#components-date-picker-demo-format) | string \| (value: dayjs) => string \| (string \| (value: dayjs) => string)\[] | `YYYY-MM-DD` | |
| renderExtraFooter | 在面板中添加额外的页脚 | (mode) => React.ReactNode | - | |
| showNow | 当设定了 `showTime` 的时候,面板是否显示“此刻”按钮 | boolean | - | 4.4.0 |
| showTime | 增加时间选择功能 | Object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/) | dayjs() | |
| showToday | 是否展示“今天”按钮 | boolean | true | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调 | function(date: dayjs, dateString: string) | - | |
| onOk | 点击确定按钮的回调 | function() | - | |
| onPanelChange | 日期面板变化时的回调 | function(value, mode) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------- | ----- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期,如果开始时间或结束时间为 `null` 或者 `undefined`,日期范围将是一个开区间 | [dayjs](https://day.js.org/) | - | |
| disabledTime | 不可选择的时间 | function(date) | - | |
| format | 设置日期格式,为数组时支持多格式匹配,展示以第一个为准。配置参考 [dayjs](https://day.js.org/),支持[自定义格式](#components-date-picker-demo-format) | string \| (value: dayjs) => string \| (string \| (value: dayjs) => string)\[] | `YYYY-MM-DD` | |
| renderExtraFooter | 在面板中添加额外的页脚 | (mode) => React.ReactNode | - | |
| showNow | 当设定了 `showTime` 的时候,面板是否显示“此刻”按钮 | boolean | - | 4.4.0 |
| showTime | 增加时间选择功能 | Object \| boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/) | dayjs() | |
| showToday | 是否展示“今天”按钮 | boolean | true | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调 | function(date: dayjs, dateString: string) | - | |
| onOk | 点击确定按钮的回调 | function() | - | |
| onPanelChange | 日期面板变化时的回调 | function(value, mode) | - | |
### DatePicker\[picker=year]
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY` | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------------------ | ----------------------------------------------------- | ----------------------------------------- | ------ | ---- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY` | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=quarter]
`4.1.0` 新增。
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-\QQ` | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------------------ | ----------------------------------------------------- | ----------------------------------------- | ---------- | ---- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-\QQ` | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=month]
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-MM` | |
| monthCellRender | 自定义的月份内容渲染方法 | function(date, locale): ReactNode | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------------------ | ----------------------------------------------------- | ----------------------------------------- | --------- | ---- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-MM` | |
| monthCellRender | 自定义的月份内容渲染方法 | function(date, locale): ReactNode | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=week]
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-wo` | |
| renderExtraFooter | 在面板中添加额外的页脚 | (mode) => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| ------------------ | ----------------------------------------------------- | ----------------------------------------- | --------- | ---- |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/) | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/) | - | |
| format | 展示的日期格式,配置参考 [dayjs](https://day.js.org/) | string | `YYYY-wo` | |
| renderExtraFooter | 在面板中添加额外的页脚 | (mode) => React.ReactNode | - | |
| value | 日期 | [dayjs](https://day.js.org/) | - | |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: dayjs, dateString: string) | - | |
### RangePicker
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --- | --- | --- | --- | --- |
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
| dateRender | 自定义日期单元格的内容。`info` 参数自 4.3.0 添加 | function(currentDate: dayjs, today: dayjs, info: { range: `start` \| `end` }) => React.ReactNode | - | |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/)\[] | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/)\[] | - | |
| disabled | 禁用起始项 | \[boolean, boolean] | - | |
| disabledTime | 不可选择的时间 | function(date: dayjs, partial: `start` \| `end`) | - | |
| format | 展示的日期格式 | string | `YYYY-MM-DD HH:mm:ss` | |
| ranges | 预设时间范围快捷选择 | { \[range: string]: [dayjs](https://day.js.org/)\[] } \| { \[range: string]: () => [dayjs](https://day.js.org/)\[] } | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| separator | 设置分隔符 | React.ReactNode | `<SwapRightOutlined />` | |
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/)\[] | \[dayjs(), dayjs()] | |
| value | 日期 | [dayjs](https://day.js.org/)\[] | - | |
| onCalendarChange | 待选日期发生变化的回调。`info` 参数自 4.4.0 添加 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string], info: { range:`start`\|`end` }) | - | |
| onChange | 日期范围发生变化的回调 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string]) | - | |
| 参数 | 说明 | 类型 | 默认值 | 版本 |
| --------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ---- |
| allowEmpty | 允许起始项部分为空 | \[boolean, boolean] | \[false, false] | |
| dateRender | 自定义日期单元格的内容。`info` 参数自 4.3.0 添加 | function(currentDate: dayjs, today: dayjs, info: { range: `start` \| `end` }) => React.ReactNode | - | |
| defaultPickerValue | 默认面板日期 | [dayjs](https://day.js.org/)\[] | - | |
| defaultValue | 默认日期 | [dayjs](https://day.js.org/)\[] | - | |
| disabled | 禁用起始项 | \[boolean, boolean] | - | |
| disabledTime | 不可选择的时间 | function(date: dayjs, partial: `start` \| `end`) | - | |
| format | 展示的日期格式 | string | `YYYY-MM-DD HH:mm:ss` | |
| ranges | 预设时间范围快捷选择 | { \[range: string]: [dayjs](https://day.js.org/)\[] } \| { \[range: string]: () => [dayjs](https://day.js.org/)\[] } | - | |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - | |
| separator | 设置分隔符 | React.ReactNode | `<SwapRightOutlined />` | |
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) | |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](#components-date-picker-demo-disabled-date) | [dayjs](https://day.js.org/)\[] | \[dayjs(), dayjs()] | |
| value | 日期 | [dayjs](https://day.js.org/)\[] | - | |
| onCalendarChange | 待选日期发生变化的回调。`info` 参数自 4.4.0 添加 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string], info: { range:`start`\|`end` }) | - | |
| onChange | 日期范围发生变化的回调 | function(dates: \[dayjs, dayjs], dateStrings: \[string, string]) | - | |
## FAQ

6
components/dropdown/__tests__/index.test.tsx

@ -4,7 +4,7 @@ import Dropdown from '..';
import type { DropDownProps } from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { act, fireEvent, render, sleep } from '../../../tests/utils';
import { act, fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import Menu from '../../menu';
import { resetWarned } from '../../_util/warning';
@ -56,6 +56,7 @@ describe('Dropdown', () => {
});
it('support Menu expandIcon', async () => {
jest.useFakeTimers();
const props: DropDownProps = {
overlay: (
<Menu expandIcon={<span id="customExpandIcon" />}>
@ -74,8 +75,9 @@ describe('Dropdown', () => {
<button type="button">button</button>
</Dropdown>,
);
await sleep(500);
await waitFakeTimer();
expect(container.querySelectorAll('#customExpandIcon').length).toBe(1);
jest.useRealTimers();
});
it('should warn if use topCenter or bottomCenter', () => {

2
components/form/FormItemLabel.tsx

@ -4,7 +4,7 @@ import * as React from 'react';
import type { ColProps } from '../grid/col';
import Col from '../grid/col';
import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import defaultLocale from '../locale/en_US';
import type { TooltipProps } from '../tooltip';
import Tooltip from '../tooltip';
import type { FormContextProps } from './context';

117
components/form/__tests__/list.test.tsx

@ -1,27 +1,59 @@
import React from 'react';
import type { FormListFieldData, FormListOperation } from '..';
import Form from '..';
import { fireEvent, render, sleep, act } from '../../../tests/utils';
import { fireEvent, render, waitFakeTimer } from '../../../tests/utils';
import Button from '../../button';
import Input from '../../input';
describe('Form.List', () => {
const change = async (
wrapper: ReturnType<typeof render>['container'],
index: number,
// const change = async (
// wrapper: ReturnType<typeof render>['container'],
// index: number,
// value: string,
// ) => {
// fireEvent.change(wrapper.getElementsByClassName('ant-input')?.[index], { target: { value } });
// await sleep();
// };
const changeValue = async (
input: HTMLElement | null | number,
value: string,
advTimer = 1000,
) => {
fireEvent.change(wrapper.getElementsByClassName('ant-input')?.[index], { target: { value } });
await sleep();
let element: HTMLElement;
if (typeof input === 'number') {
element = document.querySelectorAll('input')[input];
}
expect(element!).toBeTruthy();
fireEvent.change(element!, {
target: {
value,
},
});
if (advTimer) {
await waitFakeTimer(advTimer / 20);
}
};
beforeEach(() => {
document.body.innerHTML = '';
jest.useFakeTimers();
});
afterAll(() => {
jest.clearAllTimers();
jest.useRealTimers();
});
const testList = (
name: string,
renderField: (value: FormListFieldData) => React.ReactNode,
): void => {
it(name, async () => {
jest.useFakeTimers();
const { container } = render(
<Form>
<Form.List name="list">
@ -39,39 +71,31 @@ describe('Form.List', () => {
</Form>,
);
function operate(className: string) {
async function operate(className: string) {
fireEvent.click(container.querySelector(className)!);
act(() => {
jest.runAllTimers();
});
await waitFakeTimer();
}
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(1);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(1);
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(2);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(2);
operate('.add');
expect(container.getElementsByClassName('ant-input').length).toBe(3);
await operate('.add');
expect(container.querySelectorAll('.ant-input').length).toBe(3);
await change(container, 2, '');
for (let i = 0; i < 10; i += 1) {
act(() => {
jest.runAllTimers();
});
}
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(1);
await changeValue(2, '');
operate('.remove-0');
expect(container.getElementsByClassName('ant-input').length).toBe(2);
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(1);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(1);
operate('.remove-1');
expect(container.getElementsByClassName('ant-input').length).toBe(1);
expect(container.getElementsByClassName('ant-form-item-explain').length).toBe(0);
await operate('.remove-0');
expect(container.querySelectorAll('.ant-input').length).toBe(2);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(1);
jest.useRealTimers();
await operate('.remove-1');
expect(container.querySelectorAll('.ant-input').length).toBe(1);
expect(container.querySelectorAll('.ant-form-item-explain').length).toBe(0);
});
};
@ -131,28 +155,26 @@ describe('Form.List', () => {
);
await click(container, '.add');
await change(container, 0, 'input1');
await changeValue(0, 'input1');
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input1'] });
await click(container, '.add');
await change(container, 1, 'input2');
await changeValue(1, 'input2');
await click(container, '.add');
await change(container, 2, 'input3');
await changeValue(2, 'input3');
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input1', 'input2', 'input3'] });
await click(container, '.remove'); // will remove first input
fireEvent.submit(container.querySelector('form')!);
await sleep();
await waitFakeTimer();
expect(onFinish).toHaveBeenLastCalledWith({ list: ['input2', 'input3'] });
});
it('list errors', async () => {
jest.useFakeTimers();
let operation: FormListOperation;
const { container } = render(
<Form>
@ -177,15 +199,8 @@ describe('Form.List', () => {
);
async function addItem() {
await act(async () => {
operation.add();
await sleep(100);
jest.runAllTimers();
});
act(() => {
jest.runAllTimers();
});
operation.add();
await waitFakeTimer();
}
await addItem();
@ -193,8 +208,6 @@ describe('Form.List', () => {
await addItem();
expect(container.getElementsByClassName('ant-form-item-explain div')).toHaveLength(0);
jest.useRealTimers();
});
it('should render empty without errors', () => {
@ -243,7 +256,7 @@ describe('Form.List', () => {
const { container } = render(<Demo />);
fireEvent.click(container.querySelector('button')!);
await sleep();
await waitFakeTimer();
expect(errorSpy).not.toHaveBeenCalled();

2
components/form/util.ts

@ -22,7 +22,7 @@ export function getFieldId(namePath: InternalNamePath, formName?: string): strin
return `${formName}_${mergedId}`;
}
const isIllegalName = formItemNameBlackList.indexOf(mergedId) >= 0;
const isIllegalName = formItemNameBlackList.includes(mergedId);
return isIllegalName ? `${defaultItemNamePrefixCls}_${mergedId}` : mergedId;
}

4
components/input/__tests__/__snapshots__/Password.test.tsx.snap

@ -80,7 +80,7 @@ exports[`Input.Password should change type when click 1`] = `
exports[`Input.Password should change type when click 2`] = `
<span
class="ant-input-affix-wrapper ant-input-password"
class="ant-input-affix-wrapper ant-input-affix-wrapper-focused ant-input-password"
>
<input
class="ant-input"
@ -116,7 +116,7 @@ exports[`Input.Password should change type when click 2`] = `
exports[`Input.Password should change type when click 3`] = `
<span
class="ant-input-affix-wrapper ant-input-password"
class="ant-input-affix-wrapper ant-input-affix-wrapper-focused ant-input-password"
>
<input
class="ant-input"

3
components/input/__tests__/index.test.tsx

@ -79,8 +79,7 @@ describe('Input', () => {
it('click outside should also get focus', () => {
const { container } = render(<Input suffix={<span className="test-suffix" />} />);
const onFocus = jest.spyOn(container.querySelector('input')!, 'focus');
fireEvent.mouseDown(container.querySelector('.test-suffix')!);
fireEvent.mouseUp(container.querySelector('.test-suffix')!);
fireEvent.click(container.querySelector('.test-suffix')!);
expect(onFocus).toHaveBeenCalled();
});

6
components/list/style/index.tsx

@ -341,9 +341,9 @@ export default genComponentStyleHook(
const listToken = mergeToken<ListToken>(token, {
listBorderedCls: `${token.componentCls}-bordered`,
minHeight: token.controlHeightLG,
listItemPadding: `${token.paddingSM}px 0`,
listItemPaddingSM: `${token.paddingXS}px ${token.padding}px`,
listItemPaddingLG: `${token.padding}px ${token.paddingLG}px`,
listItemPadding: `${token.paddingContentVertical}px ${token.paddingContentHorizontalLG}px`,
listItemPaddingSM: `${token.paddingContentVerticalSM}px ${token.paddingContentHorizontal}px`,
listItemPaddingLG: `${token.paddingContentVerticalLG}px ${token.paddingContentHorizontalLG}px`,
});
return [genBaseStyle(listToken), genBorderedStyle(listToken), genResponsiveStyle(listToken)];

2
components/locale-provider/LocaleReceiver.tsx

@ -2,7 +2,7 @@ import * as React from 'react';
import type { Locale } from '.';
import type { LocaleContextProps } from './context';
import LocaleContext from './context';
import defaultLocaleData from './default';
import defaultLocaleData from '../locale/en_US';
export type LocaleComponentName = Exclude<keyof Locale, 'locale'>;

3
components/locale-provider/default.tsx

@ -1,3 +0,0 @@
import locale from '../locale/default';
export default locale;

1
components/locale/ar_EG.tsx

@ -40,6 +40,7 @@ const localeValues: Locale = {
cancelText: 'إلغاء',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'ابحث هنا',
itemUnit: 'عنصر',
itemsUnit: 'عناصر',

1
components/locale/bg_BG.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Отказ',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Търсене',
itemUnit: 'избор',
itemsUnit: 'избори',

1
components/locale/da_DK.tsx

@ -37,6 +37,7 @@ const localeValues: Locale = {
cancelText: 'Afbryd',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Søg her',
itemUnit: 'element',
itemsUnit: 'elementer',

136
components/locale/default.tsx

@ -1,136 +0,0 @@
/* eslint-disable no-template-curly-in-string */
import Pagination from 'rc-pagination/lib/locale/en_US';
import Calendar from '../calendar/locale/en_US';
import DatePicker from '../date-picker/locale/en_US';
import type { Locale } from '../locale-provider';
import TimePicker from '../time-picker/locale/en_US';
const typeTemplate = '${label} is not a valid ${type}';
const localeValues: Locale = {
locale: 'en',
Pagination,
DatePicker,
TimePicker,
Calendar,
global: {
placeholder: 'Please select',
},
Table: {
filterTitle: 'Filter menu',
filterConfirm: 'OK',
filterReset: 'Reset',
filterEmptyText: 'No filters',
filterCheckall: 'Select all items',
filterSearchPlaceholder: 'Search in filters',
emptyText: 'No data',
selectAll: 'Select current page',
selectInvert: 'Invert current page',
selectNone: 'Clear all data',
selectionAll: 'Select all data',
sortTitle: 'Sort',
expand: 'Expand row',
collapse: 'Collapse row',
triggerDesc: 'Click to sort descending',
triggerAsc: 'Click to sort ascending',
cancelSort: 'Click to cancel sorting',
},
Modal: {
okText: 'OK',
cancelText: 'Cancel',
justOkText: 'OK',
},
Popconfirm: {
okText: 'OK',
cancelText: 'Cancel',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Search here',
itemUnit: 'item',
itemsUnit: 'items',
remove: 'Remove',
selectCurrent: 'Select current page',
removeCurrent: 'Remove current page',
selectAll: 'Select all data',
removeAll: 'Remove all data',
selectInvert: 'Invert current page',
},
Upload: {
uploading: 'Uploading...',
removeFile: 'Remove file',
uploadError: 'Upload error',
previewFile: 'Preview file',
downloadFile: 'Download file',
},
Empty: {
description: 'No Data',
},
Icon: {
icon: 'icon',
},
Text: {
edit: 'Edit',
copy: 'Copy',
copied: 'Copied',
expand: 'Expand',
},
PageHeader: {
back: 'Back',
},
Form: {
optional: '(optional)',
defaultValidateMessages: {
default: 'Field validation error for ${label}',
required: 'Please enter ${label}',
enum: '${label} must be one of [${enum}]',
whitespace: '${label} cannot be a blank character',
date: {
format: '${label} date format is invalid',
parse: '${label} cannot be converted to a date',
invalid: '${label} is an invalid date',
},
types: {
string: typeTemplate,
method: typeTemplate,
array: typeTemplate,
object: typeTemplate,
number: typeTemplate,
date: typeTemplate,
boolean: typeTemplate,
integer: typeTemplate,
float: typeTemplate,
regexp: typeTemplate,
email: typeTemplate,
url: typeTemplate,
hex: typeTemplate,
},
string: {
len: '${label} must be ${len} characters',
min: '${label} must be at least ${min} characters',
max: '${label} must be up to ${max} characters',
range: '${label} must be between ${min}-${max} characters',
},
number: {
len: '${label} must be equal to ${len}',
min: '${label} must be minimum ${min}',
max: '${label} must be maximum ${max}',
range: '${label} must be between ${min}-${max}',
},
array: {
len: 'Must be ${len} ${label}',
min: 'At least ${min} ${label}',
max: 'At most ${max} ${label}',
range: 'The amount of ${label} must be between ${min}-${max}',
},
pattern: {
mismatch: '${label} does not match the pattern ${pattern}',
},
},
},
Image: {
preview: 'Preview',
},
};
export default localeValues;

1
components/locale/el_GR.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Άκυρο',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Αναζήτηση',
itemUnit: 'αντικείμενο',
itemsUnit: 'αντικείμενα',

137
components/locale/en_US.tsx

@ -1,3 +1,136 @@
import defaultLocale from './default';
/* eslint-disable no-template-curly-in-string */
import Pagination from 'rc-pagination/lib/locale/en_US';
import Calendar from '../calendar/locale/en_US';
import DatePicker from '../date-picker/locale/en_US';
import type { Locale } from '../locale-provider';
import TimePicker from '../time-picker/locale/en_US';
export default defaultLocale;
const typeTemplate = '${label} is not a valid ${type}';
const localeValues: Locale = {
locale: 'en',
Pagination,
DatePicker,
TimePicker,
Calendar,
global: {
placeholder: 'Please select',
},
Table: {
filterTitle: 'Filter menu',
filterConfirm: 'OK',
filterReset: 'Reset',
filterEmptyText: 'No filters',
filterCheckall: 'Select all items',
filterSearchPlaceholder: 'Search in filters',
emptyText: 'No data',
selectAll: 'Select current page',
selectInvert: 'Invert current page',
selectNone: 'Clear all data',
selectionAll: 'Select all data',
sortTitle: 'Sort',
expand: 'Expand row',
collapse: 'Collapse row',
triggerDesc: 'Click to sort descending',
triggerAsc: 'Click to sort ascending',
cancelSort: 'Click to cancel sorting',
},
Modal: {
okText: 'OK',
cancelText: 'Cancel',
justOkText: 'OK',
},
Popconfirm: {
okText: 'OK',
cancelText: 'Cancel',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Search here',
itemUnit: 'item',
itemsUnit: 'items',
remove: 'Remove',
selectCurrent: 'Select current page',
removeCurrent: 'Remove current page',
selectAll: 'Select all data',
removeAll: 'Remove all data',
selectInvert: 'Invert current page',
},
Upload: {
uploading: 'Uploading...',
removeFile: 'Remove file',
uploadError: 'Upload error',
previewFile: 'Preview file',
downloadFile: 'Download file',
},
Empty: {
description: 'No Data',
},
Icon: {
icon: 'icon',
},
Text: {
edit: 'Edit',
copy: 'Copy',
copied: 'Copied',
expand: 'Expand',
},
PageHeader: {
back: 'Back',
},
Form: {
optional: '(optional)',
defaultValidateMessages: {
default: 'Field validation error for ${label}',
required: 'Please enter ${label}',
enum: '${label} must be one of [${enum}]',
whitespace: '${label} cannot be a blank character',
date: {
format: '${label} date format is invalid',
parse: '${label} cannot be converted to a date',
invalid: '${label} is an invalid date',
},
types: {
string: typeTemplate,
method: typeTemplate,
array: typeTemplate,
object: typeTemplate,
number: typeTemplate,
date: typeTemplate,
boolean: typeTemplate,
integer: typeTemplate,
float: typeTemplate,
regexp: typeTemplate,
email: typeTemplate,
url: typeTemplate,
hex: typeTemplate,
},
string: {
len: '${label} must be ${len} characters',
min: '${label} must be at least ${min} characters',
max: '${label} must be up to ${max} characters',
range: '${label} must be between ${min}-${max} characters',
},
number: {
len: '${label} must be equal to ${len}',
min: '${label} must be minimum ${min}',
max: '${label} must be maximum ${max}',
range: '${label} must be between ${min}-${max}',
},
array: {
len: 'Must be ${len} ${label}',
min: 'At least ${min} ${label}',
max: 'At most ${max} ${label}',
range: 'The amount of ${label} must be between ${min}-${max}',
},
pattern: {
mismatch: '${label} does not match the pattern ${pattern}',
},
},
},
Image: {
preview: 'Preview',
},
};
export default localeValues;

1
components/locale/fi_FI.tsx

@ -31,6 +31,7 @@ const localeValues: Locale = {
cancelText: 'Peruuta',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Etsi täältä',
itemUnit: 'kohde',
itemsUnit: 'kohdetta',

1
components/locale/fr_BE.tsx

@ -26,6 +26,7 @@ const localeValues: Locale = {
cancelText: 'Annuler',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Recherche',
itemUnit: 'élément',
itemsUnit: 'éléments',

1
components/locale/fr_CA.tsx

@ -35,6 +35,7 @@ const localeValues: Locale = {
cancelText: 'Annuler',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Rechercher',
itemUnit: 'élément',
itemsUnit: 'éléments',

1
components/locale/gl_ES.tsx

@ -34,6 +34,7 @@ const localeValues: Locale = {
cancelText: 'Cancelar',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Buscar aquí',
itemUnit: 'elemento',
itemsUnit: 'elementos',

1
components/locale/he_IL.tsx

@ -40,6 +40,7 @@ const localeValues: Locale = {
cancelText: 'ביטול',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'חפש כאן',
itemUnit: 'פריט',
itemsUnit: 'פריטים',

1
components/locale/hu_HU.tsx

@ -28,6 +28,7 @@ const localeValues: Locale = {
cancelText: 'Visszavonás',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Keresés',
itemUnit: 'elem',
itemsUnit: 'elemek',

1
components/locale/is_IS.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Hætta við',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Leita hér',
itemUnit: 'færsla',
itemsUnit: 'færslur',

1
components/locale/ja_JP.tsx

@ -38,6 +38,7 @@ const localeValues: Locale = {
cancelText: 'キャンセル',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'ここを検索',
itemUnit: 'アイテム',
itemsUnit: 'アイテム',

1
components/locale/km_KH.tsx

@ -40,6 +40,7 @@ const localeValues: Locale = {
cancelText: 'បោះបង់',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'ស្វែងរកនៅទីនេះ',
itemUnit: '',
itemsUnit: 'items',

1
components/locale/kmr_IQ.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Betal ke',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Lêgerîn',
itemUnit: 'tişt',
itemsUnit: 'tişt',

1
components/locale/ko_KR.tsx

@ -32,6 +32,7 @@ const localeValues: Locale = {
cancelText: '취소',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: '여기에 검색하세요',
itemUnit: '개',
itemsUnit: '개',

3
components/locale/ku_IQ.tsx

@ -4,7 +4,7 @@ import DatePicker from '../date-picker/locale/kmr_IQ';
import type { Locale } from '../locale-provider';
import TimePicker from '../time-picker/locale/kmr_IQ';
// please use antd/lib/locale/kmr_IQ instead
// please use antd/locale/kmr_IQ instead
// keep this file for compatibility
// https://github.com/ant-design/ant-design/issues/25778
@ -31,6 +31,7 @@ const localeValues: Locale = {
cancelText: 'Betal ke',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Lêgerîn',
itemUnit: 'tişt',
itemsUnit: 'tişt',

1
components/locale/lv_LV.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Atcelt',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Meklēt šeit',
itemUnit: 'vienumu',
itemsUnit: 'vienumus',

1
components/locale/mk_MK.tsx

@ -30,6 +30,7 @@ const localeValues: Locale = {
cancelText: 'Откажи',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Пребарај тука',
itemUnit: 'предмет',
itemsUnit: 'предмети',

1
components/locale/mn_MN.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Цуцлах',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Хайх',
itemUnit: 'Зүйл',
itemsUnit: 'Зүйлүүд',

1
components/locale/ms_MY.tsx

@ -43,6 +43,7 @@ const localeValues: Locale = {
cancelText: 'Batal',
},
Transfer: {
titles: ['', ''],
notFoundContent: 'Tidak dijumpai',
searchPlaceholder: 'Carian di sini',
itemUnit: 'item',

1
components/locale/sl_SI.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Prekliči',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Išči tukaj',
itemUnit: 'Objekt',
itemsUnit: 'Objektov',

1
components/locale/vi_VN.tsx

@ -27,6 +27,7 @@ const localeValues: Locale = {
cancelText: 'Huỷ',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: 'Tìm ở đây',
itemUnit: 'mục',
itemsUnit: 'mục',

1
components/locale/zh_CN.tsx

@ -45,6 +45,7 @@ const localeValues: Locale = {
okText: '确定',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: '请输入搜索内容',
itemUnit: '项',
itemsUnit: '项',

1
components/locale/zh_HK.tsx

@ -42,6 +42,7 @@ const localeValues: Locale = {
cancelText: '取消',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: '搜尋資料',
itemUnit: '項目',
itemsUnit: '項目',

1
components/locale/zh_TW.tsx

@ -42,6 +42,7 @@ const localeValues: Locale = {
cancelText: '取消',
},
Transfer: {
titles: ['', ''],
searchPlaceholder: '搜尋資料',
itemUnit: '項目',
itemsUnit: '項目',

2
components/message/style/index.tsx

@ -168,7 +168,7 @@ export default genComponentStyleHook(
const combinedToken = mergeToken<MessageToken>(token, {
messageNoticeContentPadding: `${
(token.controlHeightLG - token.fontSize * token.lineHeight) / 2
}px ${token.paddingSM}px`,
}px ${token.paddingContentVertical}px`,
});
return [genMessageStyle(combinedToken)];
},

2
components/modal/locale.tsx

@ -1,4 +1,4 @@
import defaultLocale from '../locale/default';
import defaultLocale from '../locale/en_US';
export interface ModalLocale {
okText: string;

2
components/modal/style/index.tsx

@ -165,7 +165,7 @@ const genModalStyle: GenerateStyle<ModalToken> = token => {
borderRadius: token.radiusLG,
boxShadow: token.boxShadowSecondary,
pointerEvents: 'auto',
padding: `${token.paddingTmp}px ${token.paddingLG}px`,
padding: `${token.paddingMD}px ${token.paddingContentHorizontalLG}px`,
},
[`${componentCls}-close`]: {

2
components/modal/useModal/HookModal.tsx

@ -1,7 +1,7 @@
import * as React from 'react';
import { ConfigContext } from '../../config-provider';
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import defaultLocale from '../../locale/default';
import defaultLocale from '../../locale/en_US';
import ConfirmDialog from '../ConfirmDialog';
import type { ModalFuncProps } from '../Modal';

4
components/notification/style/index.tsx

@ -261,7 +261,7 @@ const genNotificationStyle: GenerateStyle<NotificationToken> = token => {
export default genComponentStyleHook(
'Notification',
token => {
const notificationPaddingVertical = token.paddingTmp;
const notificationPaddingVertical = token.paddingMD;
const notificationPaddingHorizontal = token.paddingLG;
const notificationToken = mergeToken<NotificationToken>(token, {
@ -270,7 +270,7 @@ export default genComponentStyleHook(
notificationPaddingVertical,
notificationPaddingHorizontal,
// index.less variables
notificationPadding: `${notificationPaddingVertical}px ${notificationPaddingHorizontal}px`,
notificationPadding: `${token.paddingMD}px ${token.paddingContentHorizontalLG}px`,
notificationMarginBottom: token.margin,
notificationMarginEdge: token.marginLG,
animationMaxHeight: 150,

2
components/popconfirm/PurePanel.tsx

@ -6,7 +6,7 @@ import Button from '../button';
import { convertLegacyProps } from '../button/button';
import ActionButton from '../_util/ActionButton';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import defaultLocale from '../locale/en_US';
import { getRenderPropValue } from '../_util/getRenderPropValue';
import { ConfigContext } from '../config-provider';
import PopoverPurePanel from '../popover/PurePanel';

6
components/popover/style/index.tsx

@ -12,7 +12,7 @@ export interface ComponentToken {
export type PopoverToken = FullToken<'Popover'> & {
popoverBg: string;
popoverColor: string;
popoverPadding: number;
popoverPadding: number | string;
};
const genBaseStyle: GenerateStyle<PopoverToken> = token => {
@ -158,12 +158,12 @@ const genWireframeStyle: GenerateStyle<PopoverToken> = token => {
export default genComponentStyleHook(
'Popover',
token => {
const { colorBgElevated, colorText, paddingSM, wireframe } = token;
const { colorBgElevated, colorText, wireframe } = token;
const popoverToken = mergeToken<PopoverToken>(token, {
popoverBg: colorBgElevated,
popoverColor: colorText,
popoverPadding: paddingSM,
popoverPadding: 12, // Fixed Value
});
return [

4
components/progress/progress.tsx

@ -77,7 +77,7 @@ const Progress: React.FC<ProgressProps> = (props: ProgressProps) => {
function getProgressStatus() {
const { status } = props;
if (ProgressStatuses.indexOf(status!) < 0 && getPercentNumber() >= 100) {
if (!ProgressStatuses.includes(status!) && getPercentNumber() >= 100) {
return 'success';
}
return status || 'normal';
@ -123,7 +123,7 @@ const Progress: React.FC<ProgressProps> = (props: ProgressProps) => {
const strokeColorNotArray = Array.isArray(strokeColor) ? strokeColor[0] : strokeColor;
const strokeColorNotGradient =
typeof strokeColor === 'string' || Array.isArray(strokeColor) ? strokeColor : undefined;
let progress;
let progress: React.ReactNode;
// Render progress shape
if (type === 'line') {
progress = steps ? (

6
components/radio/style/index.tsx

@ -496,7 +496,6 @@ export default genComponentStyleHook('Radio', token => {
colorPrimary,
marginXS,
controlOutlineWidth,
paddingXXS,
wireframe,
} = token;
@ -506,10 +505,11 @@ export default genComponentStyleHook('Radio', token => {
const radioSize = fontSizeLG;
const radioTop = (Math.round(fontSize * lineHeight) - radioSize) / 2;
const radioDotDisabledSize = radioSize - paddingXXS * 2;
const dotPadding = 4; // Fixed value
const radioDotDisabledSize = radioSize - dotPadding * 2;
const radioDotSize = wireframe
? radioDotDisabledSize
: radioSize - (paddingXXS + controlLineWidth) * 2;
: radioSize - (dotPadding + controlLineWidth) * 2;
const radioCheckedColor = colorPrimary;
// Radio buttons

7
components/skeleton/Node.tsx

@ -1,6 +1,7 @@
import DotChartOutlined from '@ant-design/icons/DotChartOutlined';
import classNames from 'classnames';
import * as React from 'react';
import useStyle from './style';
import { ConfigContext } from '../config-provider';
import type { SkeletonElementProps } from './Element';
@ -13,6 +14,7 @@ const SkeletonNode: React.FC<SkeletonNodeProps> = props => {
const { prefixCls: customizePrefixCls, className, style, active, children } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('skeleton', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
const cls = classNames(
prefixCls,
@ -20,17 +22,18 @@ const SkeletonNode: React.FC<SkeletonNodeProps> = props => {
{
[`${prefixCls}-active`]: active,
},
hashId,
className,
);
const content = children ?? <DotChartOutlined />;
return (
return wrapSSR(
<div className={cls}>
<div className={classNames(`${prefixCls}-image`, className)} style={style}>
{content}
</div>
</div>
</div>,
);
};

8
components/slider/style/index.tsx

@ -237,7 +237,7 @@ const genBaseStyle: GenerateStyle<SliderToken> = token => {
// ============================ Horizontal ============================
const genDirectionStyle = (token: SliderToken, horizontal: boolean): CSSObject => {
const { componentCls, railSize, controlSize, handleSize, dotSize } = token;
const { componentCls, railSize, handleSize, dotSize } = token;
const railPadding: keyof React.CSSProperties = horizontal ? 'paddingBlock' : 'paddingInline';
const full: keyof React.CSSProperties = horizontal ? 'width' : 'height';
@ -247,7 +247,7 @@ const genDirectionStyle = (token: SliderToken, horizontal: boolean): CSSObject =
return {
[railPadding]: railSize,
[part]: controlSize,
[part]: railSize * 3,
[`${componentCls}-rail`]: {
[full]: '100%',
@ -259,7 +259,7 @@ const genDirectionStyle = (token: SliderToken, horizontal: boolean): CSSObject =
},
[`${componentCls}-handle`]: {
[handlePos]: (controlSize - handleSize) / 2,
[handlePos]: (railSize * 3 - handleSize) / 2,
},
[`${componentCls}-mark`]: {
@ -336,7 +336,7 @@ export default genComponentStyleHook(
const handleLineWidthHover = token.lineWidth + increaseHandleWidth * 3;
return {
controlSize,
railSize: controlSize / 3,
railSize: 4,
handleSize: controlSize,
handleSizeHover: controlSizeHover,
dotSize: (controlSize / 3) * 2,

2
components/statistic/utils.tsx

@ -41,7 +41,7 @@ export function formatTimeStr(duration: number, format: string) {
const templateText = format.replace(escapeRegex, '[]');
const replacedText = timeUnits.reduce((current, [name, unit]) => {
if (current.indexOf(name) !== -1) {
if (current.includes(name)) {
const value = Math.floor(leftDuration / unit);
leftDuration -= value * unit;
return current.replace(new RegExp(`${name}+`, 'g'), (match: string) => {

4
components/steps/style/index.tsx

@ -159,7 +159,7 @@ const genStepsItemStyle: GenerateStyle<StepsToken, CSSObject> = token => {
},
[`${stepsItemCls}-tail`]: {
position: 'absolute',
top: token.marginSM,
top: token.stepsIconSize / 2 - token.lineWidth,
insetInlineStart: 0,
width: '100%',
@ -347,7 +347,7 @@ export default genComponentStyleHook(
stepsIconSize,
stepsIconCustomSize: stepsIconSize,
stepsIconCustomTop: 0,
stepsIconCustomFontSize: fontSizeHeading3,
stepsIconCustomFontSize: controlHeightLG / 2,
stepsIconTop: -0.5, // magic for ui experience
stepsIconFontSize: fontSize,
stepsTitleLineHeight: controlHeight,

2
components/steps/style/small.ts

@ -47,7 +47,7 @@ const genStepsSmallStyle: GenerateStyle<StepsToken, CSSObject> = token => {
fontSize: fontSizeBase,
},
[`${componentCls}-item-tail`]: {
top: token.marginXS,
top: stepsSmallIconSize / 2 - token.lineWidth,
},
[`${componentCls}-item-custom ${componentCls}-item-icon`]: {
width: 'inherit',

4
components/steps/style/vertical.ts

@ -35,7 +35,7 @@ const genStepsVerticalStyle: GenerateStyle<StepsToken, CSSObject> = token => {
[`> ${componentCls}-item > ${componentCls}-item-container > ${componentCls}-item-tail`]: {
position: 'absolute',
top: 0,
insetInlineStart: token.margin,
insetInlineStart: token.stepsIconSize / 2 - token.lineWidth,
width: token.lineWidth,
height: '100%',
padding: `${stepsIconSize + token.marginXXS * 1.5}px 0 ${token.marginXXS * 1.5}px`,
@ -59,7 +59,7 @@ const genStepsVerticalStyle: GenerateStyle<StepsToken, CSSObject> = token => {
[`${componentCls}-item-tail`]: {
position: 'absolute',
top: 0,
insetInlineStart: token.marginSM,
insetInlineStart: token.stepsSmallIconSize / 2 - token.lineWidth,
padding: `${stepsSmallIconSize + token.marginXXS * 1.5}px 0 ${token.marginXXS * 1.5}px`,
},
[`${componentCls}-item-title`]: {

14
components/switch/style/index.tsx

@ -226,15 +226,15 @@ const genSwitchInnerStyle: GenerateStyle<SwitchToken, CSSObject> = token => {
[`&:not(${componentCls}-disabled):active`]: {
[`&:not(${componentCls}-checked) ${switchInnerCls}`]: {
[`${switchInnerCls}-unchecked`]: {
marginInlineStart: token.switchInnerMarginMax + token.marginXXS,
marginInlineEnd: token.switchInnerMarginMin - token.marginXXS,
marginInlineStart: token.switchInnerMarginMax + token.switchPadding * 2,
marginInlineEnd: token.switchInnerMarginMin - token.switchPadding * 2,
},
},
[`&${componentCls}-checked ${switchInnerCls}`]: {
[`${switchInnerCls}-checked`]: {
marginInlineStart: token.switchInnerMarginMin - token.marginXXS,
marginInlineEnd: token.switchInnerMarginMax + token.marginXXS,
marginInlineStart: token.switchInnerMarginMin - token.switchPadding * 2,
marginInlineEnd: token.switchInnerMarginMax + token.switchPadding * 2,
},
},
},
@ -299,7 +299,7 @@ const genSwitchStyle = (token: SwitchToken): CSSObject => {
export default genComponentStyleHook('Switch', token => {
const switchHeight = token.fontSize * token.lineHeight;
const switchHeightSM = token.controlHeight / 2;
const switchPadding = token.paddingXXS / 2;
const switchPadding = 2; // This is magic
const switchPinSize = switchHeight - switchPadding * 2;
const switchPinSizeSM = switchHeightSM - switchPadding * 2;
@ -310,14 +310,14 @@ export default genComponentStyleHook('Switch', token => {
switchColor: token.colorPrimary,
switchDisabledOpacity: token.opacityLoading,
switchInnerMarginMin: switchPinSize / 2,
switchInnerMarginMax: switchPinSize + switchPadding + token.paddingXXS,
switchInnerMarginMax: switchPinSize + switchPadding + switchPadding * 2,
switchPadding,
switchPinSize,
switchBg: token.colorBgContainer,
switchMinWidthSM: switchPinSizeSM * 2 + switchPadding * 2,
switchHeightSM,
switchInnerMarginMinSM: switchPinSizeSM / 2,
switchInnerMarginMaxSM: switchPinSizeSM + switchPadding + token.paddingXXS,
switchInnerMarginMaxSM: switchPinSizeSM + switchPadding + switchPadding * 2,
switchPinSizeSM,
switchHandleShadow: `0 2px 4px 0 ${new TinyColor('#00230b').setAlpha(0.2).toRgbString()}`,
switchLoadingIconSize: token.fontSizeIcon * 0.75,

4
components/table/Table.tsx

@ -476,8 +476,8 @@ function InternalTable<RecordType extends object = any>(
const defaultPosition = direction === 'rtl' ? 'left' : 'right';
const { position } = mergedPagination;
if (position !== null && Array.isArray(position)) {
const topPos = position.find(p => p.indexOf('top') !== -1);
const bottomPos = position.find(p => p.indexOf('bottom') !== -1);
const topPos = position.find(p => p.includes('top'));
const bottomPos = position.find(p => p.includes('bottom'));
const isDisable = position.every(p => `${p}` === 'none');
if (!topPos && !bottomPos && !isDisable) {
bottomPaginationNode = renderPagination(defaultPosition);

26
components/table/__tests__/Table.sorter.test.tsx

@ -108,6 +108,32 @@ describe('Table.sorter', () => {
expect(getNameColumn()?.getAttribute('aria-label')).toEqual('Name sortable');
});
it('aria-label should be use the first text content in element when title is ReactElement', () => {
const { container } = render(
createTable(
{
sortDirections: ['descend', 'ascend'],
},
{
title: (
<span>
<em>Name</em>
<b>kiner</b>
</span>
),
defaultSortOrder: 'descend',
},
),
);
const getNameColumn = () => container.querySelector('th');
fireEvent.click(container.querySelector('.ant-table-column-sorters')!);
expect(getNameColumn()?.getAttribute('aria-sort')).toEqual('ascending');
expect(getNameColumn()?.getAttribute('aria-label')).toEqual(null);
fireEvent.click(container.querySelector('.ant-table-column-sorters')!);
expect(getNameColumn()?.getAttribute('aria-label')).toEqual('Name sortable');
});
it('sort records', () => {
const { container } = render(createTable());
const getNameColumn = () => container.querySelector('th');

220
components/table/index.en-US.md

@ -108,33 +108,33 @@ const columns = [
### Table
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| bordered | Whether to show all table borders | boolean | false | |
| columns | Columns of table | [ColumnsType](#Column)\[] | - | |
| components | Override default table elements | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - | |
| dataSource | Data record array to be displayed | object\[] | - | |
| expandable | Config expandable content | [expandable](#expandable) | - | |
| footer | Table footer renderer | function(currentPageData) | - | |
| getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement | |
| loading | Loading status of table | boolean \| [Spin Props](/components/spin/#API) | false | |
| locale | The i18n text including filter, sort, empty text, etc | object | [Default Value](https://github.com/ant-design/ant-design/blob/6dae4a7e18ad1ba193aedd5ab6867e1d823e2aa4/components/locale/default.tsx#L19-L37) | |
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | - | |
| rowClassName | Row's className | function(record, index): string | - | |
| rowKey | Row's unique key, could be a string or function that returns a string | string \| function(record): string | `key` | |
| rowSelection | Row selection [config](#rowSelection) | object | - | |
| scroll | Whether the table can be scrollable, [config](#scroll) | object | - | |
| showHeader | Whether to show table header | boolean | true | |
| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \| [Tooltip props](/components/tooltip/#API) | true | |
| size | Size of table | `default` \| `middle` \| `small` | `default` | |
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | |
| sticky | Set sticky header and scroll bar | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 4.6.0 (getContainer: 4.7.0) |
| summary | Summary content | (currentData) => ReactNode | - | |
| tableLayout | The [table-layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout) attribute of table element | - \| `auto` \| `fixed` | -<hr />`fixed` when header/columns are fixed, or using `column.ellipsis` | |
| title | Table title renderer | function(currentPageData) | - | |
| onChange | Callback executed when pagination, filters or sorter is changed | function(pagination, filters, sorter, extra: { currentDataSource: \[], action: `paginate` \| `sort` \| `filter` }) | - | |
| onHeaderRow | Set props on per header row | function(columns, index) | - | |
| onRow | Set props on per row | function(record, index) | - | |
| Property | Description | Type | Default | Version |
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| bordered | Whether to show all table borders | boolean | false | |
| columns | Columns of table | [ColumnsType](#Column)\[] | - | |
| components | Override default table elements | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - | |
| dataSource | Data record array to be displayed | object\[] | - | |
| expandable | Config expandable content | [expandable](#expandable) | - | |
| footer | Table footer renderer | function(currentPageData) | - | |
| getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement | |
| loading | Loading status of table | boolean \| [Spin Props](/components/spin/#API) | false | |
| locale | The i18n text including filter, sort, empty text, etc | object | [Default Value](https://github.com/ant-design/ant-design/blob/6dae4a7e18ad1ba193aedd5ab6867e1d823e2aa4/components/locale/en_US.tsx#L19-L37) | |
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | - | |
| rowClassName | Row's className | function(record, index): string | - | |
| rowKey | Row's unique key, could be a string or function that returns a string | string \| function(record): string | `key` | |
| rowSelection | Row selection [config](#rowSelection) | object | - | |
| scroll | Whether the table can be scrollable, [config](#scroll) | object | - | |
| showHeader | Whether to show table header | boolean | true | |
| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \| [Tooltip props](/components/tooltip/#API) | true | |
| size | Size of table | `default` \| `middle` \| `small` | `default` | |
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | |
| sticky | Set sticky header and scroll bar | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 4.6.0 (getContainer: 4.7.0) |
| summary | Summary content | (currentData) => ReactNode | - | |
| tableLayout | The [table-layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout) attribute of table element | - \| `auto` \| `fixed` | -<hr />`fixed` when header/columns are fixed, or using `column.ellipsis` | |
| title | Table title renderer | function(currentPageData) | - | |
| onChange | Callback executed when pagination, filters or sorter is changed | function(pagination, filters, sorter, extra: { currentDataSource: \[], action: `paginate` \| `sort` \| `filter` }) | - | |
| onHeaderRow | Set props on per header row | function(columns, index) | - | |
| onRow | Set props on per row | function(record, index) | - | |
#### onRow usage
@ -163,40 +163,40 @@ Same as `onRow` `onHeaderRow` `onCell` `onHeaderCell`
One of the Table `columns` prop for describing the table's columns, Column has the same API.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| align | The specify which way that column is aligned | `left` \| `right` \| `center` | `left` | |
| className | The className of this column | string | - | |
| colSpan | Span of this column's title | number | - | |
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
| defaultFilteredValue | Default filtered values | string\[] | - | |
| filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false | |
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | |
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | showTitle: 4.3.0 |
| filterDropdown | Customized filter overlay | ReactNode \| (props: [FilterDropdownProps](https://github.com/ant-design/ant-design/blob/ecc54dda839619e921c0ace530408871f0281c2a/components/table/interface.tsx#L79)) => ReactNode | - | |
| filterDropdownOpen | Whether `filterDropdown` is visible | boolean | - | |
| filtered | Whether the `dataSource` is filtered | boolean | false | |
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
| filterIcon | Customized filter icon | ReactNode \| (filtered: boolean) => ReactNode | - | |
| filterMultiple | Whether multiple filters can be selected | boolean | true | |
| filterMode | To specify the filter interface | 'menu' \| 'tree' | 'menu' | 4.17.0 |
| filterSearch | Whether to be searchable for filter menu | boolean \| function(input, record):boolean | false | boolean:4.17.0 function:4.19.0 |
| filters | Filter menu config | object\[] | - | |
| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \| string | false | |
| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - | |
| render | Renderer of the table cell. The return value should be a ReactNode | function(text, record, index) {} | - | |
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - | 4.2.0 |
| shouldCellUpdate | Control cell render logic | (record, prevRecord) => boolean | - | 4.3.0 |
| showSorterTooltip | If header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean \| [Tooltip props](/components/tooltip/) | true | |
| sortDirections | Supported sort way, override `sortDirections` in `Table`, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | |
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | function \| boolean | - | |
| sortOrder | Order of sorted values: `ascend` `descend` `null` | `ascend` \| `descend` \| null | - | |
| title | Title of this column | ReactNode \| ({ sortOrder, sortColumn, filters }) => ReactNode | - | |
| width | Width of this column ([width not working?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241)) | string \| number | - | |
| onCell | Set props on per cell | function(record, rowIndex) | - | |
| onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - | |
| onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed | function(visible) {} | - | |
| onHeaderCell | Set props on per header cell | function(column) | - | |
| Property | Description | Type | Default | Version |
| --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------ |
| align | The specify which way that column is aligned | `left` \| `right` \| `center` | `left` | |
| className | The className of this column | string | - | |
| colSpan | Span of this column's title | number | - | |
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
| defaultFilteredValue | Default filtered values | string\[] | - | |
| filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false | |
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | |
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | showTitle: 4.3.0 |
| filterDropdown | Customized filter overlay | ReactNode \| (props: [FilterDropdownProps](https://github.com/ant-design/ant-design/blob/ecc54dda839619e921c0ace530408871f0281c2a/components/table/interface.tsx#L79)) => ReactNode | - | |
| filterDropdownOpen | Whether `filterDropdown` is visible | boolean | - | |
| filtered | Whether the `dataSource` is filtered | boolean | false | |
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
| filterIcon | Customized filter icon | ReactNode \| (filtered: boolean) => ReactNode | - | |
| filterMultiple | Whether multiple filters can be selected | boolean | true | |
| filterMode | To specify the filter interface | 'menu' \| 'tree' | 'menu' | 4.17.0 |
| filterSearch | Whether to be searchable for filter menu | boolean \| function(input, record):boolean | false | boolean:4.17.0 function:4.19.0 |
| filters | Filter menu config | object\[] | - | |
| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \| string | false | |
| key | Unique key of this column, you can ignore this prop if you've set a unique `dataIndex` | string | - | |
| render | Renderer of the table cell. The return value should be a ReactNode | function(text, record, index) {} | - | |
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](https://github.com/ant-design/ant-design/blob/015109b42b85c63146371b4e32b883cf97b088e8/components/_util/responsiveObserve.ts#L1)\[] | - | 4.2.0 |
| shouldCellUpdate | Control cell render logic | (record, prevRecord) => boolean | - | 4.3.0 |
| showSorterTooltip | If header show next sorter direction tooltip, override `showSorterTooltip` in table | boolean \| [Tooltip props](/components/tooltip/) | true | |
| sortDirections | Supported sort way, override `sortDirections` in `Table`, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | |
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | function \| boolean | - | |
| sortOrder | Order of sorted values: `ascend` `descend` `null` | `ascend` \| `descend` \| null | - | |
| title | Title of this column | ReactNode \| ({ sortOrder, sortColumn, filters }) => ReactNode | - | |
| width | Width of this column ([width not working?](https://github.com/ant-design/ant-design/issues/13825#issuecomment-449889241)) | string \| number | - | |
| onCell | Set props on per cell | function(record, rowIndex) | - | |
| onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - | |
| onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed | function(visible) {} | - | |
| onHeaderCell | Set props on per header cell | function(column) | - | |
### ColumnGroup
@ -208,8 +208,8 @@ One of the Table `columns` prop for describing the table's columns, Column has t
Properties for pagination.
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| Property | Description | Type | Default |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------- | ----- | ---------------- |
| position | Specify the position of `Pagination`, could be`topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
More about pagination, please check [`Pagination`](/components/pagination/).
@ -218,64 +218,64 @@ More about pagination, please check [`Pagination`](/components/pagination/).
Properties for expandable.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| childrenColumnName | The column contains children to display | string | children | |
| columnTitle | Set the title of the expand column | ReactNode | - | 4.23.0 |
| columnWidth | Set the width of the expand column | string \| number | - | |
| defaultExpandAllRows | Expand all rows initially | boolean | false | |
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - | |
| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - | |
| expandedRowKeys | Current expanded row keys | string\[] | - | |
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - | |
| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - | |
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false | |
| fixed | Whether the expansion icon is fixed. Optional true `left` `right` | boolean \| string | false | 4.16.0 |
| indentSize | Indent size in pixels of tree data | number | 15 | |
| rowExpandable | Enable row can be expandable | (record) => boolean | - | |
| showExpandColumn | Show expand column | boolean | true | 4.18.0 |
| onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | |
| onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - | |
| Property | Description | Type | Default | Version |
| ---------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------- | ------- |
| childrenColumnName | The column contains children to display | string | children | |
| columnTitle | Set the title of the expand column | ReactNode | - | 4.23.0 |
| columnWidth | Set the width of the expand column | string \| number | - | |
| defaultExpandAllRows | Expand all rows initially | boolean | false | |
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - | |
| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - | |
| expandedRowKeys | Current expanded row keys | string\[] | - | |
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - | |
| expandIcon | Customize row expand Icon. Ref [example](https://codesandbox.io/s/fervent-bird-nuzpr) | function(props): ReactNode | - | |
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | false | |
| fixed | Whether the expansion icon is fixed. Optional true `left` `right` | boolean \| string | false | 4.16.0 |
| indentSize | Indent size in pixels of tree data | number | 15 | |
| rowExpandable | Enable row can be expandable | (record) => boolean | - | |
| showExpandColumn | Show expand column | boolean | true | 4.18.0 |
| onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | |
| onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - | |
### rowSelection
Properties for row selection.
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 4.4.0 |
| columnTitle | Set the title of the selection column | ReactNode | - | |
| columnWidth | Set the width of the selection column | string \| number | `32px` | |
| fixed | Fixed selection column on the left | boolean | - | |
| getCheckboxProps | Get Checkbox or Radio props | function(record) | - | |
| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 4.3.0 |
| preserveSelectedRowKeys | Keep selection `key` even when it removed from `dataSource` | boolean | - | 4.4.0 |
| renderCell | Renderer of the table cell. Same as `render` in column | function(checked, record, index, originNode) {} | - | 4.1.0 |
| selectedRowKeys | Controlled selected row keys | string\[] \| number\[] | \[] | |
| selections | Custom selection [config](#selection), only displays default selections when set to `true` | object\[] \| boolean | - | |
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | |
| onChange | Callback executed when selected rows change | function(selectedRowKeys, selectedRows, info: { type }) | - | `info.type`: 4.21.0 |
| onSelect | Callback executed when select/deselect one row | function(record, selected, selectedRows, nativeEvent) | - | |
| onSelectAll | Callback executed when select/deselect all rows | function(selected, selectedRows, changeRows) | - | |
| onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - | |
| onSelectNone | Callback executed when row selection is cleared | function() | - | |
| onSelectMultiple | Callback executed when row selection is changed by pressing shift | function(selected, selectedRows, changeRows) | - | |
| Property | Description | Type | Default | Version |
| ----------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------- | ---------- | ------------------- |
| checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 4.4.0 |
| columnTitle | Set the title of the selection column | ReactNode | - | |
| columnWidth | Set the width of the selection column | string \| number | `32px` | |
| fixed | Fixed selection column on the left | boolean | - | |
| getCheckboxProps | Get Checkbox or Radio props | function(record) | - | |
| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 4.3.0 |
| preserveSelectedRowKeys | Keep selection `key` even when it removed from `dataSource` | boolean | - | 4.4.0 |
| renderCell | Renderer of the table cell. Same as `render` in column | function(checked, record, index, originNode) {} | - | 4.1.0 |
| selectedRowKeys | Controlled selected row keys | string\[] \| number\[] | \[] | |
| selections | Custom selection [config](#selection), only displays default selections when set to `true` | object\[] \| boolean | - | |
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | |
| onChange | Callback executed when selected rows change | function(selectedRowKeys, selectedRows, info: { type }) | - | `info.type`: 4.21.0 |
| onSelect | Callback executed when select/deselect one row | function(record, selected, selectedRows, nativeEvent) | - | |
| onSelectAll | Callback executed when select/deselect all rows | function(selected, selectedRows, changeRows) | - | |
| onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - | |
| onSelectNone | Callback executed when row selection is cleared | function() | - | |
| onSelectMultiple | Callback executed when row selection is changed by pressing shift | function(selected, selectedRows, changeRows) | - | |
### scroll
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - |
| x | Set horizontal scrolling, can also be used to specify the width of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | string \| number \| true | - |
| y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string \| number | - |
| Property | Description | Type | Default |
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ------- |
| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - |
| x | Set horizontal scrolling, can also be used to specify the width of the scroll area, could be number, percent value, true and ['max-content'](https://developer.mozilla.org/zh-CN/docs/Web/CSS/width#max-content) | string \| number \| true | - |
| y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string \| number | - |
### selection
| Property | Description | Type | Default |
| --- | --- | --- | --- |
| key | Unique key of this selection | string | - |
| text | Display text of this selection | ReactNode | - |
| onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - |
| Property | Description | Type | Default |
| -------- | ------------------------------------------------ | --------------------------- | ------- |
| key | Unique key of this selection | string | - |
| text | Display text of this selection | ReactNode | - |
| onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - |
## Using in TypeScript

2
components/table/style/expand.tsx

@ -10,7 +10,6 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
controlInteractiveSize: checkboxSize,
motionDurationSlow,
controlLineWidth,
paddingXXS,
paddingXS,
controlLineType,
tableBorderColor,
@ -24,6 +23,7 @@ const genExpandStyle: GenerateStyle<TableToken, CSSObject> = token => {
tablePaddingVertical,
tablePaddingHorizontal,
tableExpandedRowBg,
paddingXXS,
} = token;
const halfInnerSize = checkboxSize / 2 - controlLineWidth;
// must be odd number, unless it cannot align center

3
components/table/style/index.tsx

@ -90,6 +90,7 @@ const genTableStyle: GenerateStyle<TableToken, CSSObject> = token => {
tableSelectedRowHoverBg,
tableFooterTextColor,
tableFooterBg,
paddingContentVerticalLG,
wireframe,
} = token;
const tableBorder = `${controlLineWidth}px ${controlLineType} ${tableBorderColor}`;
@ -122,7 +123,7 @@ const genTableStyle: GenerateStyle<TableToken, CSSObject> = token => {
tfoot > tr > td
`]: {
position: 'relative',
padding: `${tablePaddingVertical}px ${tablePaddingHorizontal}px`,
padding: `${paddingContentVerticalLG}px ${tablePaddingHorizontal}px`,
overflowWrap: 'break-word',
},

23
components/table/util.ts

@ -1,4 +1,5 @@
/* eslint-disable import/prefer-default-export */
import React from 'react';
import type { ColumnTitle, ColumnTitleProps, ColumnType, Key } from './interface';
export function getColumnKey<RecordType>(column: ColumnType<RecordType>, defaultKey: string): Key {
@ -16,6 +17,22 @@ export function getColumnPos(index: number, pos?: string) {
return pos ? `${pos}-${index}` : `${index}`;
}
/**
* Get first text content in Element
*
* @param node
* @returns
*/
function getElementFirstTextContent(node: React.ReactElement): string {
if (!node || !node.props || !node.props.children) return '';
if (typeof node.props.children === 'string') return node.props.children;
return (
node.props.children?.map?.((item: React.ReactElement) =>
getElementFirstTextContent(item),
)?.[0] || ''
);
}
export function renderColumnTitle<RecordType>(
title: ColumnTitle<RecordType>,
props: ColumnTitleProps<RecordType>,
@ -23,6 +40,12 @@ export function renderColumnTitle<RecordType>(
if (typeof title === 'function') {
return title(props);
}
// fix: #38155
if (React.isValidElement(title)) {
// if title is a React Element, we should get first text content as result,
// if there has not text content in React Element, return origin title
return getElementFirstTextContent(title) || title;
}
return title;
}

2
components/tabs/style/index.tsx

@ -880,7 +880,7 @@ export default genComponentStyleHook(
}px ${token.padding}px`,
tabsCardHeight,
tabsCardGutter: token.marginXXS / 2,
tabsHorizontalGutter: token.marginXL,
tabsHorizontalGutter: 32, // Fixed Value
tabsCardHeadBackground: token.colorFillAlter,
dropdownEdgeChildVerticalPadding: token.paddingXXS,
tabsActiveTextShadow: '0 0 0.25px currentcolor',

8
components/tag/style/index.tsx

@ -5,12 +5,15 @@ import { genComponentStyleHook, mergeToken, PresetColors } from '../../theme';
import capitalize from '../../_util/capitalize';
import { resetComponent } from '../../style';
export interface ComponentToken {}
interface TagToken extends FullToken<'Tag'> {
tagFontSize: number;
tagLineHeight: React.CSSProperties['lineHeight'];
tagDefaultBg: string;
tagDefaultColor: string;
tagIconSize: number;
tagPaddingHorizontal: number;
}
// ============================== Styles ==============================
@ -55,8 +58,8 @@ const genTagColorStyle = (token: TagToken): CSSInterpolation =>
}, {} as CSSObject);
const genBaseStyle = (token: TagToken): CSSInterpolation => {
const { paddingXS, paddingXXS, controlLineWidth } = token;
const paddingInline = paddingXS - controlLineWidth;
const { paddingXXS, controlLineWidth, tagPaddingHorizontal } = token;
const paddingInline = tagPaddingHorizontal - controlLineWidth;
const iconMarginInline = paddingXXS - controlLineWidth;
return {
@ -160,6 +163,7 @@ export default genComponentStyleHook('Tag', token => {
tagDefaultBg,
tagDefaultColor,
tagIconSize: fontSizeIcon - 2 * controlLineWidth, // Tag icon is much more smaller
tagPaddingHorizontal: 8, // Fixed padding.
});
return [

2
components/theme/export.ts

@ -2,6 +2,7 @@
import { useToken as useInternalToken, defaultConfig } from '.';
import defaultAlgorithm from './themes/default';
import darkAlgorithm from './themes/dark';
import compactAlgorithm from './themes/compact';
import { defaultAlgorithmV4, darkAlgorithmV4 } from './themes/v4';
// ZombieJ: We export as object to user but array in internal.
@ -23,4 +24,5 @@ export default {
darkAlgorithm,
defaultAlgorithmV4,
darkAlgorithmV4,
compactAlgorithm,
};

91
components/theme/interface.ts

@ -6,9 +6,11 @@ import type { ComponentToken as BackTopComponentToken } from '../back-top/style'
import type { ComponentToken as ButtonComponentToken } from '../button/style';
import type { ComponentToken as FloatButtonComponentToken } from '../float-button/style';
import type { ComponentToken as CalendarComponentToken } from '../calendar/style';
import type { ComponentToken as CardComponentToken } from '../card/style';
import type { ComponentToken as CarouselComponentToken } from '../carousel/style';
import type { ComponentToken as CascaderComponentToken } from '../cascader/style';
import type { ComponentToken as CheckboxComponentToken } from '../checkbox/style';
import type { ComponentToken as CollapseComponentToken } from '../collapse/style';
import type { ComponentToken as DatePickerComponentToken } from '../date-picker/style';
import type { ComponentToken as DividerComponentToken } from '../divider/style';
import type { ComponentToken as DropdownComponentToken } from '../dropdown/style';
@ -38,6 +40,7 @@ import type { ComponentToken as SpinComponentToken } from '../spin/style';
import type { ComponentToken as StepsComponentToken } from '../steps/style';
import type { ComponentToken as TableComponentToken } from '../table/style';
import type { ComponentToken as TabsComponentToken } from '../tabs/style';
import type { ComponentToken as TagComponentToken } from '../tag/style';
import type { ComponentToken as TimelineComponentToken } from '../timeline/style';
import type { ComponentToken as TooltipComponentToken } from '../tooltip/style';
import type { ComponentToken as TransferComponentToken } from '../transfer/style';
@ -79,10 +82,11 @@ export interface ComponentTokenMap {
Badge?: {};
Button?: ButtonComponentToken;
Breadcrumb?: {};
Card?: CardComponentToken;
Carousel?: CarouselComponentToken;
Cascader?: CascaderComponentToken;
Checkbox?: CheckboxComponentToken;
Collapse?: {};
Collapse?: CollapseComponentToken;
DatePicker?: DatePickerComponentToken;
Descriptions?: {};
Divider?: DividerComponentToken;
@ -112,7 +116,7 @@ export interface ComponentTokenMap {
Spin?: SpinComponentToken;
Statistic?: {};
Switch?: {};
Tag?: {};
Tag?: TagComponentToken;
Tree?: {};
TreeSelect?: {};
Typography?: TypographyComponentToken;
@ -120,7 +124,6 @@ export interface ComponentTokenMap {
Transfer?: TransferComponentToken;
Tabs?: TabsComponentToken;
Calendar?: CalendarComponentToken;
Card?: {};
Steps?: StepsComponentToken;
Menu?: MenuComponentToken;
Modal?: ModalComponentToken;
@ -159,10 +162,6 @@ export interface SeedToken extends PresetColorType {
fontFamily: string;
fontSizeBase: number;
// Grid
gridUnit: number;
gridBaseStep: number;
// Line
/** Border width of base components */
lineWidth: number;
@ -296,24 +295,25 @@ export interface ColorMapToken extends NeutralColorMapToken {
colorBgMask: string;
}
export interface SizeMapToken {
// Size
sizeXXL: number;
sizeXL: number;
sizeLG: number;
sizeMD: number;
/** Same as size by default, but can be larger in compact mode */
sizeMS: number;
size: number;
sizeSM: number;
sizeXS: number;
sizeXXS: number;
}
export interface CommonMapToken {
// Font
fontSizes: number[];
lineHeights: number[];
// Size
sizeSpace: number;
sizeSpaceXS: number;
sizeSpaceXXS: number;
sizeSpaceSM: number;
// Grid
gridSpaceSM: number;
gridSpaceBase: number;
gridSpaceLG: number;
gridSpaceXL: number;
gridSpaceXXL: number;
// Line
lineWidthBold: number;
@ -339,7 +339,12 @@ export interface CommonMapToken {
// == Map Token ==
// ======================================================================
// 🔥🔥🔥🔥🔥🔥🔥 DO NOT MODIFY THIS. PLEASE CONTACT DESIGNER. 🔥🔥🔥🔥🔥🔥🔥
export interface MapToken extends SeedToken, ColorPalettes, ColorMapToken, CommonMapToken {}
export interface MapToken
extends SeedToken,
ColorPalettes,
ColorMapToken,
SizeMapToken,
CommonMapToken {}
// ======================================================================
// == Alias Token ==
@ -422,11 +427,35 @@ export interface AliasToken extends MapToken {
controlInteractiveSize: number;
controlItemBgActiveDisabled: string; // Note. It also is a color
// =============== Legacy: should be remove ===============
opacityLoading: number;
// Padding
paddingXXS: number;
paddingXS: number;
paddingSM: number;
padding: number;
paddingMD: number;
paddingLG: number;
paddingXL: number;
// Padding Content
paddingContentHorizontalLG: number;
paddingContentHorizontal: number;
paddingContentHorizontalSM: number;
paddingContentVerticalLG: number;
paddingContentVertical: number;
paddingContentVerticalSM: number;
// Margin
marginXXS: number;
marginXS: number;
marginSM: number;
margin: number;
marginMD: number;
marginLG: number;
marginXL: number;
marginXXL: number;
// =============== Legacy: should be remove ===============
opacityLoading: number;
boxShadow: string;
boxShadowSecondary: string;
@ -438,20 +467,6 @@ export interface AliasToken extends MapToken {
controlPaddingHorizontal: number;
controlPaddingHorizontalSM: number;
paddingSM: number;
paddingXS: number;
paddingXXS: number;
paddingLG: number;
paddingXL: number;
paddingTmp: number;
marginXXS: number;
marginXS: number;
marginSM: number;
marginLG: number;
marginXL: number;
marginXXL: number;
marginTmp: number;
// Media queries breakpoints
screenXS: number;
screenXSMin: number;

17
components/theme/themes/compact/genCompactSizeMapToken.ts

@ -0,0 +1,17 @@
import type { SeedToken, SizeMapToken } from '../../interface';
export default function genSizeMapToken(token: SeedToken): SizeMapToken {
const { sizeUnit, sizeBaseStep } = token;
return {
sizeXXL: sizeUnit * (sizeBaseStep + 10),
sizeXL: sizeUnit * (sizeBaseStep + 6),
sizeLG: sizeUnit * (sizeBaseStep + 2),
sizeMD: sizeUnit * (sizeBaseStep + 2),
sizeMS: sizeUnit * (sizeBaseStep + 1),
size: sizeUnit * sizeBaseStep,
sizeSM: sizeUnit * sizeBaseStep,
sizeXS: sizeUnit * (sizeBaseStep - 1),
sizeXXS: sizeUnit * (sizeBaseStep - 1),
};
}

15
components/theme/themes/compact/index.ts

@ -0,0 +1,15 @@
import type { DerivativeFunc } from '@ant-design/cssinjs';
import type { SeedToken, MapToken } from '../../interface';
import defaultAlgorithm from '../default';
import genCompactSizeMapToken from './genCompactSizeMapToken';
const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
const mergedMapToken = mapToken ?? defaultAlgorithm(token);
return {
...mergedMapToken,
...genCompactSizeMapToken(token),
};
};
export default derivative;

9
components/theme/themes/dark/index.ts

@ -3,8 +3,8 @@ import type { DerivativeFunc } from '@ant-design/cssinjs';
import type { ColorPalettes, MapToken, PresetColorType, SeedToken } from '../../interface';
import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
import genCommonMapToken from '../shared/genCommonMapToken';
import { generateColorPalettes, generateNeutralColorPalettes } from './colors';
import defaultAlgorithm from '../default';
const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
const colorPalettes = Object.keys(defaultPresetColors)
@ -24,11 +24,10 @@ const derivative: DerivativeFunc<SeedToken, MapToken> = (token, mapToken) => {
return prev;
}, {} as ColorPalettes);
return {
...token,
const mergedMapToken = mapToken ?? defaultAlgorithm(token);
// Other tokens
...(mapToken ?? genCommonMapToken(token)),
return {
...mergedMapToken,
// Dark tokens
...colorPalettes,

5
components/theme/themes/default/index.ts

@ -1,4 +1,5 @@
import { generate } from '@ant-design/colors';
import genSizeMapToken from '../shared/genSizeMapToken';
import type { ColorPalettes, MapToken, PresetColorType, SeedToken } from '../../interface';
import { defaultPresetColors } from '../seed';
import genColorMapToken from '../shared/genColorMapToken';
@ -31,7 +32,9 @@ export default function derivative(token: SeedToken): MapToken {
generateColorPalettes,
generateNeutralColorPalettes,
}),
// Size
...genSizeMapToken(token),
// Others
...genCommonMapToken(token),
};
}

4
components/theme/themes/seed.ts

@ -37,10 +37,6 @@ const seedToken: SeedToken = {
'Noto Color Emoji'`,
fontSizeBase: 14,
// Grid
gridUnit: 4,
gridBaseStep: 2,
// Line
lineWidth: 1,
lineType: 'solid',

28
components/theme/themes/shared/genCommonMapToken.ts

@ -3,24 +3,13 @@ import genFontSizes from './genFontSizes';
import genRadius from './genRadius';
export default function genCommonMapToken(token: SeedToken): CommonMapToken {
const {
motionUnit,
motionBase,
fontSizeBase,
sizeUnit,
sizeBaseStep,
gridUnit,
gridBaseStep,
radiusBase,
controlHeight,
lineWidth,
} = token;
const { motionUnit, motionBase, fontSizeBase, radiusBase, controlHeight, lineWidth } = token;
const fontSizes = genFontSizes(fontSizeBase);
return {
// motion
motionDurationFast: `${(motionBase + motionUnit * 1).toFixed(1)}s`,
motionDurationFast: `${(motionBase + motionUnit).toFixed(1)}s`,
motionDurationMid: `${(motionBase + motionUnit * 2).toFixed(1)}s`,
motionDurationSlow: `${(motionBase + motionUnit * 3).toFixed(1)}s`,
@ -28,19 +17,6 @@ export default function genCommonMapToken(token: SeedToken): CommonMapToken {
fontSizes: fontSizes.map(fs => fs.size),
lineHeights: fontSizes.map(fs => fs.lineHeight),
// size
sizeSpaceSM: sizeUnit * (sizeBaseStep - 1),
sizeSpace: sizeUnit * sizeBaseStep,
sizeSpaceXS: sizeUnit * (sizeBaseStep - 2),
sizeSpaceXXS: sizeUnit * (sizeBaseStep - 3),
// grid
gridSpaceSM: gridUnit * (gridBaseStep - 1),
gridSpaceBase: gridUnit * gridBaseStep,
gridSpaceLG: gridUnit * (gridBaseStep + 1),
gridSpaceXL: gridUnit * (gridBaseStep + 2),
gridSpaceXXL: gridUnit * (gridBaseStep + 5),
// line
lineWidthBold: lineWidth + 1,

17
components/theme/themes/shared/genSizeMapToken.ts

@ -0,0 +1,17 @@
import type { SeedToken, SizeMapToken } from '../../interface';
export default function genSizeMapToken(token: SeedToken): SizeMapToken {
const { sizeUnit, sizeBaseStep } = token;
return {
sizeXXL: sizeUnit * (sizeBaseStep + 8), // 48
sizeXL: sizeUnit * (sizeBaseStep + 4), // 32
sizeLG: sizeUnit * (sizeBaseStep + 2), // 24
sizeMD: sizeUnit * (sizeBaseStep + 1), // 20
sizeMS: sizeUnit * sizeBaseStep, // 16
size: sizeUnit * sizeBaseStep, // 16
sizeSM: sizeUnit * (sizeBaseStep - 1), // 12
sizeXS: sizeUnit * (sizeBaseStep - 2), // 8
sizeXXS: sizeUnit * (sizeBaseStep - 3), // 4
};
}

40
components/theme/util/alias.ts

@ -120,23 +120,29 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken
controlPaddingHorizontal: 12,
controlPaddingHorizontalSM: 8,
padding: 16,
margin: 16,
paddingXXS: 4,
paddingXS: 8,
paddingSM: 12,
paddingLG: 24,
paddingXL: 32,
paddingTmp: 20,
marginXXS: 4,
marginXS: 8,
marginSM: 12,
marginLG: 24,
marginXL: 32,
marginXXL: 48,
marginTmp: 20,
paddingXXS: mergedToken.sizeXXS,
paddingXS: mergedToken.sizeXS,
paddingSM: mergedToken.sizeSM,
padding: mergedToken.size,
paddingMD: mergedToken.sizeMD,
paddingLG: mergedToken.sizeLG,
paddingXL: mergedToken.sizeXL,
paddingContentHorizontalLG: mergedToken.sizeLG,
paddingContentVerticalLG: mergedToken.sizeMS,
paddingContentHorizontal: mergedToken.sizeMS,
paddingContentVertical: mergedToken.sizeSM,
paddingContentHorizontalSM: mergedToken.size,
paddingContentVerticalSM: mergedToken.sizeXS,
marginXXS: mergedToken.sizeXXS,
marginXS: mergedToken.sizeXS,
marginSM: mergedToken.sizeSM,
margin: mergedToken.size,
marginMD: mergedToken.sizeMD,
marginLG: mergedToken.sizeLG,
marginXL: mergedToken.sizeXL,
marginXXL: mergedToken.sizeXXL,
boxShadow: `
0 1px 2px 0 rgba(0, 0, 0, 0.03),

8
components/tooltip/index.tsx

@ -240,14 +240,14 @@ const Tooltip = React.forwardRef<unknown, TooltipProps>((props, ref) => {
const transformOrigin = { top: '50%', left: '50%' };
if (['top', 'Bottom'].includes(placement)) {
if (/top|Bottom/.test(placement)) {
transformOrigin.top = `${rect.height - align.offset![1]}px`;
} else if (['Top', 'bottom'].includes(placement)) {
} else if (/Top|bottom/.test(placement)) {
transformOrigin.top = `${-align.offset![1]}px`;
}
if (['left', 'Right'].includes(placement)) {
if (/left|Right/.test(placement)) {
transformOrigin.left = `${rect.width - align.offset![0]}px`;
} else if (['right', 'Left'].includes(placement)) {
} else if (/right|Left/.test(placement)) {
transformOrigin.left = `${-align.offset![0]}px`;
}
domNode.style.transformOrigin = `${transformOrigin.left} ${transformOrigin.top}`;

4
components/transfer/ListBody.tsx

@ -71,7 +71,7 @@ class ListBody<RecordType extends KeyWiseTransferItem> extends React.Component<
onItemSelect = (item: RecordType) => {
const { onItemSelect, selectedKeys } = this.props;
const checked = selectedKeys.indexOf(item.key) >= 0;
const checked = selectedKeys.includes(item.key);
onItemSelect(item.key, !checked);
};
@ -144,7 +144,7 @@ class ListBody<RecordType extends KeyWiseTransferItem> extends React.Component<
>
{this.getItems().map(({ renderedEl, renderedText, item }: RenderedItem<RecordType>) => {
const { disabled } = item;
const checked = selectedKeys.indexOf(item.key) >= 0;
const checked = selectedKeys.includes(item.key);
return (
<ListItem

2
components/transfer/ListItem.tsx

@ -4,7 +4,7 @@ import * as React from 'react';
import type { KeyWiseTransferItem } from '.';
import Checkbox from '../checkbox';
import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale/default';
import defaultLocale from '../locale/en_US';
import TransButton from '../_util/transButton';
type ListItemProps<RecordType> = {

3
components/transfer/__tests__/customize.test.tsx

@ -1,5 +1,6 @@
import React from 'react';
import { render } from '../../../tests/utils';
import type { TransferProps } from '../index';
import Transfer from '../index';
describe('Transfer.Customize', () => {
@ -15,7 +16,7 @@ describe('Transfer.Customize', () => {
it('props#body does not work anymore', () => {
const body = jest.fn();
const props = { body };
const props = { body } as TransferProps<any>;
render(<Transfer {...props} />);
expect(errorSpy).not.toHaveBeenCalled();
expect(body).not.toHaveBeenCalled();

10
components/transfer/__tests__/list.test.tsx

@ -14,6 +14,11 @@ const listCommonProps: TransferListProps<any> = {
notFoundContent: 'Not Found',
} as TransferListProps<any>;
const listProps: TransferListProps<any> = {
...listCommonProps,
dataSource: undefined as unknown as any[],
};
describe('Transfer.List', () => {
it('should render correctly', () => {
const { container } = render(<List {...listCommonProps} />);
@ -43,4 +48,9 @@ describe('Transfer.List', () => {
expect(instance.current?.handleFilter({ target: 'test' })).toBe(undefined);
expect(handleFilter).toHaveBeenCalled();
});
it('should render correctly when dataSource is not exists', () => {
expect(() => {
render(<List {...listProps} />);
}).not.toThrow();
});
});

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save