Merge branch 'master' into next-merge-master

This commit is contained in:
MadCcc 2022-10-26 14:56:44 +08:00
commit 242b1713f5
69 changed files with 380 additions and 204 deletions

View File

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

View File

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

View File

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

View File

@ -21,7 +21,7 @@ Alert component for feedback.
| banner | Whether to show as banner | boolean | false | |
| closable | Whether Alert can be closed | boolean | - | |
| closeText | Close text to show | ReactNode | - | |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| closeIcon | Custom close icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | Additional content of Alert | ReactNode | - | |
| icon | Custom icon, effective when `showIcon` is true | ReactNode | - | |
| message | Content of Alert | ReactNode | - | |

View File

@ -22,7 +22,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/8emPa3fjl/Alert.svg
| banner | 是否用作顶部公告 | boolean | false | |
| closable | 默认不显示关闭按钮 | boolean | - | |
| closeText | 自定义关闭按钮 | ReactNode | - | |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.17.0 |
| closeIcon | 自定义关闭 Icon | ReactNode | `<CloseOutlined />` | 4.18.0 |
| description | 警告提示的辅助性文字介绍 | ReactNode | - | |
| icon | 自定义图标,`showIcon` 为 true 时有效 | ReactNode | - | |
| message | 警告提示内容 | ReactNode | - | |

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,6 @@ type RangeValue = [Dayjs | null, Dayjs | null] | null;
const App: React.FC = () => {
const [dates, setDates] = useState<RangeValue>(null);
const [hackValue, setHackValue] = useState<RangeValue>(null);
const [value, setValue] = useState<RangeValue>(null);
const disabledDate = (current: Dayjs) => {
@ -38,16 +37,15 @@ const App: React.FC = () => {
const onOpenChange = (open: boolean) => {
if (open) {
setHackValue([null, null]);
setDates([null, null]);
} else {
setHackValue(null);
setDates(null);
}
};
return (
<RangePicker
value={hackValue || value}
value={dates || value}
disabledDate={disabledDate}
onCalendarChange={val => setDates(val)}
onChange={val => setValue(val)}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,9 +29,11 @@ interface RecordType {
}
interface DataType {
key: string;
title: string;
tag: string;
description: string;
disabled: boolean;
tag: string;
}
interface TableTransferProps extends TransferProps<TransferItem> {

View File

@ -71,7 +71,7 @@ export interface TransferProps<RecordType> {
prefixCls?: string;
className?: string;
disabled?: boolean;
dataSource: RecordType[];
dataSource?: RecordType[];
targetKeys?: string[];
selectedKeys?: string[];
render?: TransferRender<RecordType>;
@ -137,13 +137,6 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
static Search = Search;
static defaultProps = {
dataSource: [],
locale: {},
showSearch: false,
listStyle: () => {},
};
static getDerivedStateFromProps<T>({
selectedKeys,
targetKeys,
@ -177,8 +170,8 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
const { selectedKeys = [], targetKeys = [] } = props;
this.state = {
sourceSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) === -1),
targetSelectedKeys: selectedKeys.filter(key => targetKeys.indexOf(key) > -1),
sourceSelectedKeys: selectedKeys.filter(key => !targetKeys.includes(key)),
targetSelectedKeys: selectedKeys.filter(key => targetKeys.includes(key)),
};
}
@ -201,11 +194,10 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
return this.props.titles ?? transferLocale.titles ?? [];
}
getLocale = (transferLocale: TransferLocale, renderEmpty: RenderEmptyHandler) => ({
...transferLocale,
notFoundContent: renderEmpty('Transfer'),
...this.props.locale,
});
getLocale = (transferLocale: TransferLocale, renderEmpty: RenderEmptyHandler) => {
const { locale = {} } = this.props;
return { ...transferLocale, notFoundContent: renderEmpty('Transfer'), ...locale };
};
moveTo = (direction: TransferDirection) => {
const { targetKeys = [], dataSource = [], onChange } = this.props;
@ -213,13 +205,13 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
const moveKeys = direction === 'right' ? sourceSelectedKeys : targetSelectedKeys;
// filter the disabled options
const newMoveKeys = moveKeys.filter(
(key: string) => !dataSource.some(data => !!(key === data.key && data.disabled)),
key => !dataSource.some(data => !!(key === data.key && data.disabled)),
);
// move items to target box
const newTargetKeys =
direction === 'right'
? newMoveKeys.concat(targetKeys)
: targetKeys.filter(targetKey => newMoveKeys.indexOf(targetKey) === -1);
: targetKeys.filter(targetKey => !newMoveKeys.includes(targetKey));
// empty checked keys
const oppositeDirection = direction === 'right' ? 'left' : 'right';
@ -235,13 +227,13 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
onItemSelectAll = (direction: TransferDirection, selectedKeys: string[], checkAll: boolean) => {
this.setStateKeys(direction, prevKeys => {
let mergedCheckedKeys = [];
let mergedCheckedKeys: string[] = [];
if (checkAll) {
// Merge current keys with origin key
mergedCheckedKeys = Array.from(new Set([...prevKeys, ...selectedKeys]));
mergedCheckedKeys = Array.from(new Set<string>([...prevKeys, ...selectedKeys]));
} else {
// Remove current keys from origin keys
mergedCheckedKeys = prevKeys.filter((key: string) => selectedKeys.indexOf(key) === -1);
mergedCheckedKeys = prevKeys.filter(key => !selectedKeys.includes(key));
}
this.handleSelectChange(direction, mergedCheckedKeys);
@ -345,7 +337,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
};
separateDataSource() {
const { dataSource, rowKey, targetKeys = [] } = this.props;
const { dataSource = [], rowKey, targetKeys = [] } = this.props;
const leftDataSource: KeyWise<RecordType>[] = [];
const rightDataSource: KeyWise<RecordType>[] = new Array(targetKeys.length);
@ -386,10 +378,10 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
className,
disabled,
operations = [],
showSearch,
showSearch = false,
footer,
style,
listStyle,
listStyle = {},
operationStyle,
filterOption,
render,
@ -427,7 +419,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
<TransferFC prefixCls={prefixCls} className={cls} style={style}>
<List<KeyWise<RecordType>>
prefixCls={`${prefixCls}-list`}
titleText={titles[0]}
titleText={titles?.[0]}
dataSource={leftDataSource}
filterOption={filterOption}
style={this.handleListStyle(listStyle, 'left')}
@ -463,7 +455,7 @@ class Transfer<RecordType extends TransferItem = TransferItem> extends React.Com
/>
<List<KeyWise<RecordType>>
prefixCls={`${prefixCls}-list`}
titleText={titles[1]}
titleText={titles?.[1]}
dataSource={rightDataSource}
filterOption={filterOption}
style={this.handleListStyle(listStyle, 'right')}

View File

@ -81,12 +81,6 @@ interface TransferListState {
export default class TransferList<
RecordType extends KeyWiseTransferItem,
> extends React.PureComponent<TransferListProps<RecordType>, TransferListState> {
static defaultProps = {
dataSource: [],
titleText: '',
showSearch: false,
};
timer: number;
triggerScrollTimer: number;
@ -109,7 +103,7 @@ export default class TransferList<
if (checkedKeys.length === 0) {
return 'none';
}
if (filteredItems.every(item => checkedKeys.indexOf(item.key) >= 0 || !!item.disabled)) {
if (filteredItems.every(item => checkedKeys.includes(item.key) || !!item.disabled)) {
return 'all';
}
return 'part';
@ -161,7 +155,7 @@ export default class TransferList<
if (filterOption) {
return filterOption(filterValue, item);
}
return text.indexOf(filterValue) >= 0;
return text.includes(filterValue);
};
// =============================== Render ===============================
@ -305,12 +299,12 @@ export default class TransferList<
const { filterValue } = this.state;
const {
prefixCls,
dataSource,
titleText,
dataSource = [],
titleText = '',
checkedKeys,
disabled,
footer,
showSearch,
showSearch = false,
style,
searchPlaceholder,
notFoundContent,

View File

@ -52,10 +52,6 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
const cachedSelectedKeys = React.useRef<Key[]>();
const treeRef = React.createRef<RcTree>();
React.useImperativeHandle(ref, () => treeRef.current!);
const getInitExpandedKeys = () => {
const { keyEntities } = convertDataToEntities(getTreeData(props));
@ -191,7 +187,7 @@ const DirectoryTree: React.ForwardRefRenderFunction<RcTree, DirectoryTreeProps>
return (
<Tree
icon={getIcon}
ref={treeRef}
ref={ref}
blockNode
{...otherProps}
showIcon={showIcon}

View File

@ -65,12 +65,7 @@ export function calcRangeKeys({
// Append selection
keys.push(key);
}
if (expandedKeys.indexOf(key) === -1) {
return false;
}
return true;
return expandedKeys.includes(key);
});
return keys;

View File

@ -16,10 +16,6 @@ const Link = React.forwardRef<HTMLElement, LinkProps>(({ ellipsis, rel, ...restP
'`ellipsis` only supports boolean value.',
);
const baseRef = React.useRef<any>(null);
React.useImperativeHandle(ref, () => baseRef.current);
const mergedProps = {
...restProps,
rel: rel === undefined && restProps.target === '_blank' ? 'noopener noreferrer' : rel,
@ -28,7 +24,7 @@ const Link = React.forwardRef<HTMLElement, LinkProps>(({ ellipsis, rel, ...restP
// @ts-expect-error: https://github.com/ant-design/ant-design/issues/26622
delete mergedProps.navigate;
return <Base {...mergedProps} ref={baseRef} ellipsis={!!ellipsis} component="a" />;
return <Base {...mergedProps} ref={ref} ellipsis={!!ellipsis} component="a" />;
});
export default Link;

View File

@ -19,7 +19,7 @@ const Title = React.forwardRef<HTMLElement, TitleProps>((props, ref) => {
const { level = 1, ...restProps } = props;
let component: keyof JSX.IntrinsicElements;
if (TITLE_ELE_LIST.indexOf(level) !== -1) {
if (TITLE_ELE_LIST.includes(level)) {
component = `h${level}`;
} else {
warning(

View File

@ -262,14 +262,14 @@ describe('Typography', () => {
</Paragraph>,
);
if (triggerType === undefined || triggerType.indexOf('icon') !== -1) {
if (triggerType === undefined || triggerType.includes('icon')) {
if (icon) {
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBeGreaterThan(0);
} else {
expect(wrapper.querySelectorAll('.anticon-edit').length).toBeGreaterThan(0);
}
if (triggerType === undefined || triggerType.indexOf('text') === -1) {
if (triggerType === undefined || !triggerType.includes('text')) {
fireEvent.click(wrapper.firstChild!);
expect(onStart).not.toHaveBeenCalled();
}
@ -295,15 +295,15 @@ describe('Typography', () => {
fireEvent.click(wrapper.querySelectorAll('.ant-typography-edit')[0]);
expect(onStart).toHaveBeenCalled();
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
if (triggerType !== undefined && triggerType.includes('text')) {
fireEvent.keyDown(wrapper.querySelector('textarea')!, { keyCode: KeyCode.ESC });
fireEvent.keyUp(wrapper.querySelector('textarea')!, { keyCode: KeyCode.ESC });
expect(onChange).not.toHaveBeenCalled();
}
}
if (triggerType !== undefined && triggerType.indexOf('text') !== -1) {
if (triggerType.indexOf('icon') === -1) {
if (triggerType !== undefined && triggerType.includes('text')) {
if (!triggerType.includes('icon')) {
expect(wrapper.querySelectorAll('.anticon-highlight').length).toBe(0);
expect(wrapper.querySelectorAll('.anticon-edit').length).toBe(0);
}

View File

@ -49,6 +49,18 @@ See: https://ant.design/docs/react/customize-theme .
While you can override a component's style, we don't recommend doing so. antd is not only a set of React components, but also a design specification as well.
## How to avoid breaking change when update version?
antd will avoid breaking change in minor & patch version. You can safe do follow things:
- Official demo usage
- FAQ suggestion. Including codesandbox sample, marked as FAQ issue
And which you should avoid to do:
- Bug as feature. It will break in any other case (e.g. Use div as Tabs children)
- Use magic code to realize requirement but which can be realized with normal API
## How to use other data-time lib like Moment.js?
Please refer to [Use custom date library](/docs/react/use-custom-date-library).

View File

@ -49,6 +49,18 @@ title: FAQ
你可以覆盖它们的样式但是我们不推荐这么做。antd 是一系列 React 组件,但同样是一套设计规范。
## 如何避免升级导致的破坏性变更?
antd 在 minor 和 patch 版本迭代中会避免引入破坏性变更,遵从以下原则会确保不会破坏你的代码:
- 使用出现在官方 Demo 中的写法
- FAQ 中出现的解法,包含代码片段以及 codesandbox 示例、issue 中当前版本标记 FAQ label 的
而下述变更则需要开发者自行校验:
- 特定场景的错误用法BUG as Feature例如 Tabs 下直接包 div 的用法)
- 可以通过正常用法实现功能需求却魔改的
## 如何使用其他时间日期库如 Moment.js
可以参考[使用自定义日期库](/docs/react/use-custom-date-library)。

View File

@ -68,10 +68,13 @@ Let's create a `ProductList` component that we can use in multiple places to sho
Create `src/components/ProductList.tsx` by typing:
```js
```tsx
import { Table, Popconfirm, Button } from 'antd';
const ProductList = ({ onDelete, products }) => {
const ProductList: React.FC<{ products: { name: string }[]; onDelete: (id: string) => void }> = ({
onDelete,
products,
}) => {
const columns = [
{
title: 'Name',
@ -112,16 +115,12 @@ export function queryProductList() {
export function queryProductList() {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{
id: 1,
name: 'dva',
},
{
id: 2,
name: 'antd',
},
]);
resolve({
data: [
{ id: 1, name: 'dva' },
{ id: 2, name: 'antd' },
],
});
}, 2000);
});
}
@ -134,7 +133,7 @@ import { useRequest } from 'umi';
import { queryProductList } from '@/services/product';
export default function useProductList(params: { pageSize: number; current: number }) {
const msg = useRequest(() => queryUserList(params));
const msg = useRequest(() => queryProductList(params));
const deleteProducts = async (id: string) => {
try {

View File

@ -109,16 +109,12 @@ export function queryProductList() {
export function queryProductList() {
return new Promise(resolve => {
setTimeout(() => {
resolve([
{
id: 1,
name: 'dva',
},
{
id: 2,
name: 'antd',
},
]);
resolve({
data: [
{ id: 1, name: 'dva' },
{ id: 2, name: 'antd' },
],
});
}, 2000);
});
}

View File

@ -127,7 +127,7 @@
"rc-dropdown": "~4.0.0",
"rc-field-form": "~1.27.0",
"rc-image": "~5.9.0",
"rc-input": "~0.1.2",
"rc-input": "~0.1.3",
"rc-input-number": "~7.3.9",
"rc-mentions": "~1.10.0",
"rc-menu": "~9.6.3",

View File

@ -21,7 +21,7 @@ async function execute() {
logs = _.remove(logs, ({ author_email: email }) => {
for (let i = 0; i < excludes.length; i++) {
const item = excludes[i];
if (email.indexOf(item) !== -1) {
if (email.includes(item)) {
return false;
}
}

View File

@ -19,7 +19,7 @@ export default class Palette extends React.Component {
this.hexColors = {};
Object.keys(this.colorNodes).forEach(key => {
const computedColor = getComputedStyle(this.colorNodes[key])['background-color'];
if (computedColor.indexOf('rgba') >= 0) {
if (computedColor.includes('rgba')) {
this.hexColors[key] = computedColor;
} else {
this.hexColors[key] = rgbToHex(computedColor);

View File

@ -3,7 +3,7 @@ import MainContent from './MainContent';
import * as utils from '../utils';
function isChangelog(pathname) {
return pathname.indexOf('changelog') >= 0;
return pathname.includes('changelog');
}
export default collect(async nextProps => {

View File

@ -200,6 +200,8 @@ const logo = [
'Yahoo',
'Reddit',
'Sketch',
'WhatsApp',
'Dingtalk',
];
const datum = [...direction, ...suggestion, ...editor, ...data, ...logo];