Merge branch 'next' into v5-site-upgrade

This commit is contained in:
MadCcc 2022-10-27 17:50:50 +08:00
commit 73a1555637
128 changed files with 1182 additions and 903 deletions

View File

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

View File

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

1
.gitignore vendored
View File

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

View File

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

View File

@ -2,7 +2,7 @@ import * as React from 'react';
export const { isValidElement } = 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; return child && isValidElement(child) && child.type === React.Fragment;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,7 +8,8 @@ import { useToken } from '../../theme';
import theme from '../../theme/export'; import theme from '../../theme/export';
import { resetWarned } from '../../_util/warning'; import { resetWarned } from '../../_util/warning';
const { defaultAlgorithm, darkAlgorithm, defaultAlgorithmV4, darkAlgorithmV4 } = theme; const { defaultAlgorithm, darkAlgorithm, defaultAlgorithmV4, darkAlgorithmV4, compactAlgorithm } =
theme;
let mockCanUseDom = true; let mockCanUseDom = true;
@ -102,6 +103,33 @@ describe('ConfigProvider.Theme', () => {
expect(tokenRef?.colorPrimaryText).toBe('#177ddc'); 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', () => { it('should support algorithm array', () => {
let tokenRef: any; let tokenRef: any;
const Demo = () => { const Demo = () => {

View File

@ -45,25 +45,25 @@ Some components use dynamic style to support wave effect. You can config `csp` p
## API ## API
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | ------------------- | ------------------- |
| autoInsertSpaceInButton | Set false to remove space between 2 chinese characters on Button | boolean | true | | | autoInsertSpaceInButton | Set false to remove space between 2 chinese characters on Button | boolean | true | |
| componentDisabled | Config antd component `disabled` | boolean | - | 4.21.0 | | componentDisabled | Config antd component `disabled` | boolean | - | 4.21.0 |
| componentSize | Config antd component size | `small` \| `middle` \| `large` | - | | | 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 } | - | | | 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` | | | 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 | | 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 | | 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 | | | 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 | | 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 | | 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 | | 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 | - | | | 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 | | | 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` | | | 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 | - | | | 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 | | 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 | | virtual | Disable virtual scroll when set to `false` | boolean | - | 4.3.0 |
### ConfigProvider.config() `4.13.0+` ### ConfigProvider.config() `4.13.0+`

View File

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

View File

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

View File

@ -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/mode.tsx" debug>Controlled Panels</code>
<code src="./demo/start-end.tsx" debug>Customized Range Picker</code> <code src="./demo/start-end.tsx" debug>Customized Range Picker</code>
<code src="./demo/suffix.tsx" debug>Suffix</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 ## 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. // 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 from 'dayjs';
import 'dayjs/locale/zh-cn'; import 'dayjs/locale/zh-cn';
import locale from 'antd/es/locale/zh_CN'; import locale from 'antd/locale/zh_CN';
<ConfigProvider locale={locale}> <ConfigProvider locale={locale}>
<DatePicker defaultValue={dayjs('2015-01-01', 'YYYY-MM-DD')} /> <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. The following APIs are shared by DatePicker, RangePicker.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | ---------------- |
| allowClear | Whether to show clear button | boolean | true | | | allowClear | Whether to show clear button | boolean | true | |
| autoFocus | If get focus when component mounted | boolean | false | | | autoFocus | If get focus when component mounted | boolean | false | |
| bordered | Whether has border style | boolean | true | | | bordered | Whether has border style | boolean | true | |
| className | The picker className | string | - | | | className | The picker className | string | - | |
| dateRender | Custom rendering function for date cells | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | | | dateRender | Custom rendering function for date cells | function(currentDate: dayjs, today: dayjs) => React.ReactNode | - | |
| disabled | Determine whether the DatePicker is disabled | boolean | false | | | disabled | Determine whether the DatePicker is disabled | boolean | false | |
| disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | | | disabledDate | Specify the date that cannot be selected | (currentDate: dayjs) => boolean | - | |
| popupClassName | To customize the className of the popup calendar | string | - | 4.23.0 | | 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) | - | | | 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 | | | 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) | | | 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` | - | | | 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 | | nextIcon | The custom next icon | ReactNode | - | 4.17.0 |
| open | The open state of picker | boolean | - | | | open | The open state of picker | boolean | - | |
| panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 | | panelRender | Customize panel render | (panelNode) => ReactNode | - | 4.5.0 |
| picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 | | picker | Set picker type | `date` \| `week` \| `month` \| `quarter` \| `year` | `date` | `quarter`: 4.1.0 |
| placeholder | The placeholder of date input | string \| \[string,string] | - | | | placeholder | The placeholder of date input | string \| \[string,string] | - | |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | | | placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | |
| popupStyle | To customize the style of the popup calendar | CSSProperties | {} | | | popupStyle | To customize the style of the popup calendar | CSSProperties | {} | |
| prevIcon | The custom prev icon | ReactNode | - | 4.17.0 | | 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` | - | | | 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 | | status | Set validation status | 'error' \| 'warning' | - | 4.19.0 |
| style | To customize the style of the input box | CSSProperties | {} | | | style | To customize the style of the input box | CSSProperties | {} | |
| suffixIcon | The custom suffix icon | ReactNode | - | | | suffixIcon | The custom suffix icon | ReactNode | - | |
| superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 | | superNextIcon | The custom super next icon | ReactNode | - | 4.17.0 |
| superPrevIcon | The custom super prev 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) | - | | | 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) | - | | | onPanelChange | Callback when picker panel mode is changed | function(value, mode) | - | |
### Common Methods ### Common Methods
@ -114,88 +114,88 @@ The following APIs are shared by DatePicker, RangePicker.
### DatePicker ### DatePicker
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------- | -------------------------------------------------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | | | 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/) | - | | | 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) | - | | | 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` | | | 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 | - | | | 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 | | 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 | 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() | | | 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 | | | showToday | Whether to show `Today` button | boolean | true | |
| value | To set date | [dayjs](https://day.js.org/) | - | | | 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) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
| onOk | Callback when click ok button | function() | - | | | onOk | Callback when click ok button | function() | - | |
| onPanelChange | Callback function for panel changing | function(value, mode) | - | | | onPanelChange | Callback function for panel changing | function(value, mode) | - | |
### DatePicker\[picker=year] ### DatePicker\[picker=year]
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | ------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | | | defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default 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` | | | format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | | | renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | | | 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) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=quarter] ### DatePicker\[picker=quarter]
Added in `4.1.0`. Added in `4.1.0`.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | ---------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | | | defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default 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` | | | format | To set the date format, refer to [dayjs](https://day.js.org/) | string | `YYYY-\QQ` | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | | | renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | | | 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) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=month] ### DatePicker\[picker=month]
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | --------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | | | defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default 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` | | | 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 | - | | | monthCellRender | Custom month cell content render method | function(date, locale): ReactNode | - | |
| renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | | | renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | | | 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) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### DatePicker\[picker=week] ### DatePicker\[picker=week]
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ------------------ | --------------------------------------------------------------------- | ----------------------------------------- | --------- | ------- |
| defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | | | defaultPickerValue | To set default picker date | [dayjs](https://day.js.org/) | - | |
| defaultValue | To set default 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` | | | 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 | - | | | renderExtraFooter | Render extra footer in panel | (mode) => React.ReactNode | - | |
| value | To set date | [dayjs](https://day.js.org/) | - | | | 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) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(date: dayjs, dateString: string) | - | |
### RangePicker ### RangePicker
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | ------- |
| allowEmpty | Allow start or end input leave empty | \[boolean, boolean] | \[false, false] | | | 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 | - | | | 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/)] | - | | | 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/)] | - | | | defaultValue | To set default date | \[[dayjs](https://day.js.org/), [dayjs](https://day.js.org/)] | - | |
| disabled | If disable start or end | \[boolean, boolean] | - | | | disabled | If disable start or end | \[boolean, boolean] | - | |
| disabledTime | To specify the time that cannot be selected | function(date: dayjs, partial: `start` \| `end`) | - | | | 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` | | | 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/)\[] } | - | | | 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 | - | | | renderExtraFooter | Render extra footer in panel | () => React.ReactNode | - | |
| separator | Set separator between inputs | React.ReactNode | `<SwapRightOutlined />` | | | separator | Set separator between inputs | React.ReactNode | `<SwapRightOutlined />` | |
| showTime | To provide an additional time selection | object \| boolean | [TimePicker Options](/components/time-picker/#API) | | | 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()] | | | 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/)] | - | | | 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` }) | - | | | 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]) | - | | | onChange | Callback function, can be executed when the selected time is changing | function(dates: \[dayjs, dayjs], dateStrings: \[string, string]) | - | |
## FAQ ## FAQ

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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;

View File

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

View File

@ -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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -108,6 +108,32 @@ describe('Table.sorter', () => {
expect(getNameColumn()?.getAttribute('aria-label')).toEqual('Name sortable'); 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', () => { it('sort records', () => {
const { container } = render(createTable()); const { container } = render(createTable());
const getNameColumn = () => container.querySelector('th'); const getNameColumn = () => container.querySelector('th');

View File

@ -108,33 +108,33 @@ const columns = [
### Table ### Table
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| bordered | Whether to show all table borders | boolean | false | | | bordered | Whether to show all table borders | boolean | false | |
| columns | Columns of table | [ColumnsType](#Column)\[] | - | | | columns | Columns of table | [ColumnsType](#Column)\[] | - | |
| components | Override default table elements | [TableComponents](https://github.com/react-component/table/blob/75ee0064e54a4b3215694505870c9d6c817e9e4a/src/interface.ts#L129) | - | | | 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\[] | - | | | dataSource | Data record array to be displayed | object\[] | - | |
| expandable | Config expandable content | [expandable](#expandable) | - | | | expandable | Config expandable content | [expandable](#expandable) | - | |
| footer | Table footer renderer | function(currentPageData) | - | | | footer | Table footer renderer | function(currentPageData) | - | |
| getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement | | | getPopupContainer | The render container of dropdowns in table | (triggerNode) => HTMLElement | () => TableHtmlElement | |
| loading | Loading status of table | boolean \| [Spin Props](/components/spin/#API) | false | | | 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) | | | 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 | - | | | 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 | - | | | 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` | | | 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 | - | | | rowSelection | Row selection [config](#rowSelection) | object | - | |
| scroll | Whether the table can be scrollable, [config](#scroll) | object | - | | | scroll | Whether the table can be scrollable, [config](#scroll) | object | - | |
| showHeader | Whether to show table header | boolean | true | | | 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 | | | 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` | | | size | Size of table | `default` \| `middle` \| `small` | `default` | |
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | | | 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) | | 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 | - | | | 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` | | | 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) | - | | | 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` }) | - | | | 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) | - | | | onHeaderRow | Set props on per header row | function(columns, index) | - | |
| onRow | Set props on per row | function(record, index) | - | | | onRow | Set props on per row | function(record, index) | - | |
#### onRow usage #### 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. One of the Table `columns` prop for describing the table's columns, Column has the same API.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | --------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ------------------------------ |
| align | The specify which way that column is aligned | `left` \| `right` \| `center` | `left` | | | align | The specify which way that column is aligned | `left` \| `right` \| `center` | `left` | |
| className | The className of this column | string | - | | | className | The className of this column | string | - | |
| colSpan | Span of this column's title | number | - | | | colSpan | Span of this column's title | number | - | |
| dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | | | dataIndex | Display field of the data record, support nest path by string array | string \| string\[] | - | |
| defaultFilteredValue | Default filtered values | string\[] | - | | | defaultFilteredValue | Default filtered values | string\[] | - | |
| filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false | | | filterResetToDefaultFilteredValue | click the reset button, whether to restore the default filter | boolean | false | |
| defaultSortOrder | Default order of sorted values | `ascend` \| `descend` | - | | | 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 | | 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 | - | | | 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 | - | | | filterDropdownOpen | Whether `filterDropdown` is visible | boolean | - | |
| filtered | Whether the `dataSource` is filtered | boolean | false | | | filtered | Whether the `dataSource` is filtered | boolean | false | |
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | | | filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
| filterIcon | Customized filter icon | ReactNode \| (filtered: boolean) => ReactNode | - | | | filterIcon | Customized filter icon | ReactNode \| (filtered: boolean) => ReactNode | - | |
| filterMultiple | Whether multiple filters can be selected | boolean | true | | | filterMultiple | Whether multiple filters can be selected | boolean | true | |
| filterMode | To specify the filter interface | 'menu' \| 'tree' | 'menu' | 4.17.0 | | 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 | | 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\[] | - | | | filters | Filter menu config | object\[] | - | |
| fixed | (IE not support) Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \| string | false | | | 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 | - | | | 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) {} | - | | | 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 | | 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 | | 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 | | | 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`] | | | 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 | - | | | 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 | - | | | sortOrder | Order of sorted values: `ascend` `descend` `null` | `ascend` \| `descend` \| null | - | |
| title | Title of this column | ReactNode \| ({ sortOrder, sortColumn, filters }) => ReactNode | - | | | 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 | - | | | 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) | - | | | onCell | Set props on per cell | function(record, rowIndex) | - | |
| onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - | | | onFilter | Function that determines if the row is displayed when filtered | function(value, record) => boolean | - | |
| onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed | function(visible) {} | - | | | onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed | function(visible) {} | - | |
| onHeaderCell | Set props on per header cell | function(column) | - | | | onHeaderCell | Set props on per header cell | function(column) | - | |
### ColumnGroup ### ColumnGroup
@ -208,8 +208,8 @@ One of the Table `columns` prop for describing the table's columns, Column has t
Properties for pagination. 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`] | | position | Specify the position of `Pagination`, could be`topLeft` \| `topCenter` \| `topRight` \|`bottomLeft` \| `bottomCenter` \| `bottomRight` | Array | \[`bottomRight`] |
More about pagination, please check [`Pagination`](/components/pagination/). More about pagination, please check [`Pagination`](/components/pagination/).
@ -218,64 +218,64 @@ More about pagination, please check [`Pagination`](/components/pagination/).
Properties for expandable. Properties for expandable.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ---------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------- | -------- | ------- |
| childrenColumnName | The column contains children to display | string | children | | | childrenColumnName | The column contains children to display | string | children | |
| columnTitle | Set the title of the expand column | ReactNode | - | 4.23.0 | | columnTitle | Set the title of the expand column | ReactNode | - | 4.23.0 |
| columnWidth | Set the width of the expand column | string \| number | - | | | columnWidth | Set the width of the expand column | string \| number | - | |
| defaultExpandAllRows | Expand all rows initially | boolean | false | | | defaultExpandAllRows | Expand all rows initially | boolean | false | |
| defaultExpandedRowKeys | Initial expanded row keys | string\[] | - | | | defaultExpandedRowKeys | Initial expanded row keys | string\[] | - | |
| expandedRowClassName | Expanded row's className | function(record, index, indent): string | - | | | expandedRowClassName | Expanded row's className | function(record, index, indent): string | - | |
| expandedRowKeys | Current expanded row keys | string\[] | - | | | expandedRowKeys | Current expanded row keys | string\[] | - | |
| expandedRowRender | Expanded container render for each row | function(record, index, indent, expanded): ReactNode | - | | | 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 | - | | | 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 | | | 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 | | 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 | | | indentSize | Indent size in pixels of tree data | number | 15 | |
| rowExpandable | Enable row can be expandable | (record) => boolean | - | | | rowExpandable | Enable row can be expandable | (record) => boolean | - | |
| showExpandColumn | Show expand column | boolean | true | 4.18.0 | | showExpandColumn | Show expand column | boolean | true | 4.18.0 |
| onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | | | onExpand | Callback executed when the row expand icon is clicked | function(expanded, record) | - | |
| onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - | | | onExpandedRowsChange | Callback executed when the expanded rows change | function(expandedRows) | - | |
### rowSelection ### rowSelection
Properties for row selection. Properties for row selection.
| Property | Description | Type | Default | Version | | Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- | | ----------------------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------- | ---------- | ------------------- |
| checkStrictly | Check table row precisely; parent row and children rows are not associated | boolean | true | 4.4.0 | | 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 | - | | | columnTitle | Set the title of the selection column | ReactNode | - | |
| columnWidth | Set the width of the selection column | string \| number | `32px` | | | columnWidth | Set the width of the selection column | string \| number | `32px` | |
| fixed | Fixed selection column on the left | boolean | - | | | fixed | Fixed selection column on the left | boolean | - | |
| getCheckboxProps | Get Checkbox or Radio props | function(record) | - | | | getCheckboxProps | Get Checkbox or Radio props | function(record) | - | |
| hideSelectAll | Hide the selectAll checkbox and custom selection | boolean | false | 4.3.0 | | 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 | | 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 | | 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\[] | \[] | | | selectedRowKeys | Controlled selected row keys | string\[] \| number\[] | \[] | |
| selections | Custom selection [config](#selection), only displays default selections when set to `true` | object\[] \| boolean | - | | | selections | Custom selection [config](#selection), only displays default selections when set to `true` | object\[] \| boolean | - | |
| type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | | | type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | |
| onChange | Callback executed when selected rows change | function(selectedRowKeys, selectedRows, info: { type }) | - | `info.type`: 4.21.0 | | 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) | - | | | 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) | - | | | onSelectAll | Callback executed when select/deselect all rows | function(selected, selectedRows, changeRows) | - | |
| onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - | | | onSelectInvert | Callback executed when row selection is inverted | function(selectedRowKeys) | - | |
| onSelectNone | Callback executed when row selection is cleared | function() | - | | | onSelectNone | Callback executed when row selection is cleared | function() | - | |
| onSelectMultiple | Callback executed when row selection is changed by pressing shift | function(selected, selectedRows, changeRows) | - | | | onSelectMultiple | Callback executed when row selection is changed by pressing shift | function(selected, selectedRows, changeRows) | - | |
### scroll ### scroll
| Property | Description | Type | Default | | Property | Description | Type | Default |
| --- | --- | --- | --- | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | ------- |
| scrollToFirstRowOnChange | Whether to scroll to the top of the table when paging, sorting, filtering changes | boolean | - | | 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 | - | | 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 | - | | y | Set vertical scrolling, can also be used to specify the height of the scroll area, could be string or number | string \| number | - |
### selection ### selection
| Property | Description | Type | Default | | Property | Description | Type | Default |
| --- | --- | --- | --- | | -------- | ------------------------------------------------ | --------------------------- | ------- |
| key | Unique key of this selection | string | - | | key | Unique key of this selection | string | - |
| text | Display text of this selection | ReactNode | - | | text | Display text of this selection | ReactNode | - |
| onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - | | onSelect | Callback executed when this selection is clicked | function(changeableRowKeys) | - |
## Using in TypeScript ## Using in TypeScript

View File

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

View File

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

View File

@ -1,4 +1,5 @@
/* eslint-disable import/prefer-default-export */ /* eslint-disable import/prefer-default-export */
import React from 'react';
import type { ColumnTitle, ColumnTitleProps, ColumnType, Key } from './interface'; import type { ColumnTitle, ColumnTitleProps, ColumnType, Key } from './interface';
export function getColumnKey<RecordType>(column: ColumnType<RecordType>, defaultKey: string): Key { 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}`; 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>( export function renderColumnTitle<RecordType>(
title: ColumnTitle<RecordType>, title: ColumnTitle<RecordType>,
props: ColumnTitleProps<RecordType>, props: ColumnTitleProps<RecordType>,
@ -23,6 +40,12 @@ export function renderColumnTitle<RecordType>(
if (typeof title === 'function') { if (typeof title === 'function') {
return title(props); 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; return title;
} }

View File

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

View File

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

View File

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

View File

@ -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 ButtonComponentToken } from '../button/style';
import type { ComponentToken as FloatButtonComponentToken } from '../float-button/style'; import type { ComponentToken as FloatButtonComponentToken } from '../float-button/style';
import type { ComponentToken as CalendarComponentToken } from '../calendar/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 CarouselComponentToken } from '../carousel/style';
import type { ComponentToken as CascaderComponentToken } from '../cascader/style'; import type { ComponentToken as CascaderComponentToken } from '../cascader/style';
import type { ComponentToken as CheckboxComponentToken } from '../checkbox/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 DatePickerComponentToken } from '../date-picker/style';
import type { ComponentToken as DividerComponentToken } from '../divider/style'; import type { ComponentToken as DividerComponentToken } from '../divider/style';
import type { ComponentToken as DropdownComponentToken } from '../dropdown/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 StepsComponentToken } from '../steps/style';
import type { ComponentToken as TableComponentToken } from '../table/style'; import type { ComponentToken as TableComponentToken } from '../table/style';
import type { ComponentToken as TabsComponentToken } from '../tabs/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 TimelineComponentToken } from '../timeline/style';
import type { ComponentToken as TooltipComponentToken } from '../tooltip/style'; import type { ComponentToken as TooltipComponentToken } from '../tooltip/style';
import type { ComponentToken as TransferComponentToken } from '../transfer/style'; import type { ComponentToken as TransferComponentToken } from '../transfer/style';
@ -79,10 +82,11 @@ export interface ComponentTokenMap {
Badge?: {}; Badge?: {};
Button?: ButtonComponentToken; Button?: ButtonComponentToken;
Breadcrumb?: {}; Breadcrumb?: {};
Card?: CardComponentToken;
Carousel?: CarouselComponentToken; Carousel?: CarouselComponentToken;
Cascader?: CascaderComponentToken; Cascader?: CascaderComponentToken;
Checkbox?: CheckboxComponentToken; Checkbox?: CheckboxComponentToken;
Collapse?: {}; Collapse?: CollapseComponentToken;
DatePicker?: DatePickerComponentToken; DatePicker?: DatePickerComponentToken;
Descriptions?: {}; Descriptions?: {};
Divider?: DividerComponentToken; Divider?: DividerComponentToken;
@ -112,7 +116,7 @@ export interface ComponentTokenMap {
Spin?: SpinComponentToken; Spin?: SpinComponentToken;
Statistic?: {}; Statistic?: {};
Switch?: {}; Switch?: {};
Tag?: {}; Tag?: TagComponentToken;
Tree?: {}; Tree?: {};
TreeSelect?: {}; TreeSelect?: {};
Typography?: TypographyComponentToken; Typography?: TypographyComponentToken;
@ -120,7 +124,6 @@ export interface ComponentTokenMap {
Transfer?: TransferComponentToken; Transfer?: TransferComponentToken;
Tabs?: TabsComponentToken; Tabs?: TabsComponentToken;
Calendar?: CalendarComponentToken; Calendar?: CalendarComponentToken;
Card?: {};
Steps?: StepsComponentToken; Steps?: StepsComponentToken;
Menu?: MenuComponentToken; Menu?: MenuComponentToken;
Modal?: ModalComponentToken; Modal?: ModalComponentToken;
@ -159,10 +162,6 @@ export interface SeedToken extends PresetColorType {
fontFamily: string; fontFamily: string;
fontSizeBase: number; fontSizeBase: number;
// Grid
gridUnit: number;
gridBaseStep: number;
// Line // Line
/** Border width of base components */ /** Border width of base components */
lineWidth: number; lineWidth: number;
@ -296,24 +295,25 @@ export interface ColorMapToken extends NeutralColorMapToken {
colorBgMask: string; 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 { export interface CommonMapToken {
// Font // Font
fontSizes: number[]; fontSizes: number[];
lineHeights: number[]; lineHeights: number[];
// Size
sizeSpace: number;
sizeSpaceXS: number;
sizeSpaceXXS: number;
sizeSpaceSM: number;
// Grid
gridSpaceSM: number;
gridSpaceBase: number;
gridSpaceLG: number;
gridSpaceXL: number;
gridSpaceXXL: number;
// Line // Line
lineWidthBold: number; lineWidthBold: number;
@ -339,7 +339,12 @@ export interface CommonMapToken {
// == Map Token == // == Map Token ==
// ====================================================================== // ======================================================================
// 🔥🔥🔥🔥🔥🔥🔥 DO NOT MODIFY THIS. PLEASE CONTACT DESIGNER. 🔥🔥🔥🔥🔥🔥🔥 // 🔥🔥🔥🔥🔥🔥🔥 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 == // == Alias Token ==
@ -422,12 +427,36 @@ export interface AliasToken extends MapToken {
controlInteractiveSize: number; controlInteractiveSize: number;
controlItemBgActiveDisabled: string; // Note. It also is a color controlItemBgActiveDisabled: string; // Note. It also is a color
// 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 =============== // =============== Legacy: should be remove ===============
opacityLoading: number; opacityLoading: number;
padding: number;
margin: number;
boxShadow: string; boxShadow: string;
boxShadowSecondary: string; boxShadowSecondary: string;
@ -438,20 +467,6 @@ export interface AliasToken extends MapToken {
controlPaddingHorizontal: number; controlPaddingHorizontal: number;
controlPaddingHorizontalSM: 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 // Media queries breakpoints
screenXS: number; screenXS: number;
screenXSMin: number; screenXSMin: number;

View File

@ -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),
};
}

View File

@ -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;

View File

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

View File

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

View File

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

View File

@ -3,24 +3,13 @@ import genFontSizes from './genFontSizes';
import genRadius from './genRadius'; import genRadius from './genRadius';
export default function genCommonMapToken(token: SeedToken): CommonMapToken { export default function genCommonMapToken(token: SeedToken): CommonMapToken {
const { const { motionUnit, motionBase, fontSizeBase, radiusBase, controlHeight, lineWidth } = token;
motionUnit,
motionBase,
fontSizeBase,
sizeUnit,
sizeBaseStep,
gridUnit,
gridBaseStep,
radiusBase,
controlHeight,
lineWidth,
} = token;
const fontSizes = genFontSizes(fontSizeBase); const fontSizes = genFontSizes(fontSizeBase);
return { return {
// motion // motion
motionDurationFast: `${(motionBase + motionUnit * 1).toFixed(1)}s`, motionDurationFast: `${(motionBase + motionUnit).toFixed(1)}s`,
motionDurationMid: `${(motionBase + motionUnit * 2).toFixed(1)}s`, motionDurationMid: `${(motionBase + motionUnit * 2).toFixed(1)}s`,
motionDurationSlow: `${(motionBase + motionUnit * 3).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), fontSizes: fontSizes.map(fs => fs.size),
lineHeights: fontSizes.map(fs => fs.lineHeight), 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 // line
lineWidthBold: lineWidth + 1, lineWidthBold: lineWidth + 1,

View File

@ -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
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -14,6 +14,11 @@ const listCommonProps: TransferListProps<any> = {
notFoundContent: 'Not Found', notFoundContent: 'Not Found',
} as TransferListProps<any>; } as TransferListProps<any>;
const listProps: TransferListProps<any> = {
...listCommonProps,
dataSource: undefined as unknown as any[],
};
describe('Transfer.List', () => { describe('Transfer.List', () => {
it('should render correctly', () => { it('should render correctly', () => {
const { container } = render(<List {...listCommonProps} />); const { container } = render(<List {...listCommonProps} />);
@ -43,4 +48,9 @@ describe('Transfer.List', () => {
expect(instance.current?.handleFilter({ target: 'test' })).toBe(undefined); expect(instance.current?.handleFilter({ target: 'test' })).toBe(undefined);
expect(handleFilter).toHaveBeenCalled(); 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 have changed in this diff Show More