fix: update merge

This commit is contained in:
iamkun 2022-05-06 14:02:45 +08:00
commit 7dd0bd396d
579 changed files with 27031 additions and 16664 deletions

4
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,4 @@
package.json @ant-design/ant-design-core
.github/workflows/ @ant-design/ant-design-core
scripts/ @ant-design/ant-design-core
LICENSE @ant-design/ant-design-core

View File

@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: analysis
uses: actions-cool/issues-similarity-analysis@v1.1.0
uses: actions-cool/issues-similarity-analysis@v1
with:
filter-threshold: 0.5
title-excludes: ''

View File

@ -20,6 +20,6 @@ jobs:
with:
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.5
uses: cirrus-actions/rebase@1.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -1,6 +1,5 @@
# Upload 📷 UI snapshots to argos server, help visual regression testing.
name: UI Upload
name: 📷 UI Upload
on:
workflow_run:
@ -21,6 +20,9 @@ jobs:
if: >
github.event.workflow_run.conclusion == 'success'
steps:
- name: checkout
uses: actions/checkout@v3
- name: Download commit artifact
uses: dawidd6/action-download-artifact@v2
with:
@ -47,8 +49,15 @@ jobs:
workflow: ${{ github.event.workflow_run.workflow_id }}
workflow_conclusion: success
name: snapshots
path: imageSnapshots
- name: Install
run: npm i fast-glob lodash argos-cli
- name: Upload argos-ci
id: deploy
run: |
npx argos-cli upload ./ --token ${{ secrets.ARGOS_TOKEN }} --branch ${{ steps.branch.outputs.id }} --commit ${{ steps.commit.outputs.id }}
run: npm run argos
env:
ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }}
ARGOS_GITHUB_BRANCH: ${{ steps.branch.outputs.id }}
ARGOS_GITHUB_COMMIT: ${{ steps.commit.outputs.id }}

View File

@ -4,19 +4,14 @@ on:
pull_request:
types: [opened, synchronize, reopened]
push:
branches:
- master
# Cancel prev CI if new commit come
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
test:
imagesnapshot:
runs-on: ubuntu-latest
steps:
- name: checkout
@ -28,6 +23,10 @@ jobs:
path: package-temp-dir
key: lock-${{ github.sha }}
- uses: actions/setup-node@v3
with:
node-version: '16'
- name: create package-lock.json
run: npm i --package-lock-only --ignore-scripts

View File

@ -20,8 +20,9 @@ jobs:
forbid-files: 'CHANGELOG.zh-CN.md, CHANGELOG.en-US.md, LICENSE'
skip-verify-authority: 'write'
assignees: 'afc163, zombieJ, xrkffgg, MadCcc'
comment-mark: 'version'
comment: |
Hi @${{ github.event.pull_request.user.login }}. Thanks for your contribution. The path `.github/` or `scripts/` and `CHANGELOG` `package.json` is only maintained by team members. This current PR will be closed and team members will help on this.
Hi @${{ github.event.pull_request.user.login }}. Thanks for your contribution. The path `.github/` or `scripts/` and `CHANGELOG` is only maintained by team members. This current PR will be closed and team members will help on this.
close: true
set-failed: false
@ -30,6 +31,7 @@ jobs:
with:
forbid-files: 'components/style/themes/default.less'
skip-verify-authority: 'admin'
comment-mark: 'less'
comment: |
🚨 Hi @${{ github.event.pull_request.user.login }}. Thanks for your contribution, as the `default.less` file is currently being upgraded, changes are not recommended.

View File

@ -1,6 +1,7 @@
+v <ljw@live.jp>
07akioni <07akioni2@gmail.com>
17073025 <17073025@cnsuning.com>
2724635499 <2724635499@qq.com>
282159468 <282159468@qq.com>
778758944 <778758944@qq.com>
Aaron Cawte <aaron@bbncreative.co>
@ -9,6 +10,7 @@ Adam Stankiewicz <sheerun@sher.pl>
Aditya Padhi <aditya.padhi@outlook.com>
Adrian Dimitrov <dimitrov.adrian@gmail.com>
Adriano Ruberto <adriano.ruberto@gmail.com>
Aex <adaex@qq.com>
Ahmad Abdelaziz <ahmad.abdelaziz@robustastudio.com>
Ahmed AlSammany <ahmed.alsammany@incorta.com>
Ahmet Simsek <iamaroott@gmail.com>
@ -38,13 +40,18 @@ Alexey Vinogradov <vinogradov.a.i.93@gmail.com>
Alexey Yakovlev <yallie@yandex.ru>
Alfred Qiu <sc941203@gmail.com>
Ali Zhdanov <makedonec88@gmail.com>
Aliaksandr <puskin1914@gmail.com>
Alireza <alireza.mh@gmail.com>
Alvin Abia <alvin.abia@icloud.com>
Aminul Islam <me@aminul.net>
Amir Arafat <amirarafat1@gmail.com>
Amorites <751809522@qq.com>
Amour1688 <lcz_1996@foxmail.com>
Amumu <yoyo837@hotmail.com>
Anas Tawfeek <anas.tawfeek@outlook.com>
Andre Perunicic <andre@intoli.com>
Andre Zyczkowski <andre@oakslab.com>
Andrea Blanco <estefania_8_3@hotmail.com>
Andrew Murray <radarhere@gmail.com>
Andrew Shearer <andrew@ashearer.com>
Andrey G <plandem@gmail.com>
@ -52,6 +59,7 @@ Andrzej Dybionka <andrzej@arabel.la>
André <mazoni.andre@gmail.com>
Andrés <andresin87@gmail.com>
Ankit Babbar <ankit.babbar@valuebound.com>
Antee <gtavctt@protonmail.com>
Aobo Yang <yangaobo@gmail.com>
Ardo Kusuma <ardo@uber.com>
Arlindo Torres <arlindofontetorres@gmail.com>
@ -61,6 +69,7 @@ Artin <lengthmin@gmail.com>
Arvin Xu <arvinx@foxmail.com>
Ash Kumar <kumar.ashwin@outlook.com>
Ashot Mnatsakanyan <mnatsakanyan.ashot@gmail.com>
Austaras <austaras@outlook.com>
Aymen Bouchekoua <bouchekoua.aymen@gmail.com>
Aymen Chebbi <a.chebi@klarx.com>
BK Heleth <bon.hoo@hotmail.com>
@ -102,15 +111,20 @@ C.J. Winslow <whoaa512@gmail.com>
CORP\lianyufeng <15275222711@163.com>
Calin Vlad <vlad.s.calin@gmail.com>
Cam Song <neosoyn@gmail.com>
Camol <kwwnjujlc@sina.com>
Cang Ta <hoksilato176@gmail.com>
Canwen Xu <canwenxu@126.com>
Carlos Coves Prieto <carlos.coves@interacso.com>
Carter Feldman <carter@carter.at>
Caspian Chen <624081567@qq.com>
Cat-XHS <1349021570@qq.com>
Catalin Miron <mironcatalin@gmail.com>
Cee Cirno <i@cee.moe>
Cemre Mengu <cemremengu@gmail.com>
Chalk <chalkpe@gmail.com>
Chandler Moisen <chandlermoisen@gmail.com>
Chang Wang <cheapsteak@gmail.com>
Chang Wei <867597730@qq.com>
Charles Covey-Brandt <chazcb@gmail.com>
Charlie Jonas <charlie@callawaycloudconsulting.com>
Chelsea Huang <chelsea.huang@sap.com>
@ -130,10 +144,12 @@ Chuang Yu <cyu9960@gmail.com>
Chuck <wfhtzcf@gmail.com>
Claudio Restifo <claudio.restifo@gmail.com>
Cody Chan <int64ago@gmail.com>
Cole <cqiufan@outlook.com>
Colton Pierson <colton@coltonpierson.com>
Confiks <confiks@scriptbase.org>
Cong Yoo <roopher@163.com>
Cong Zhang <dancerphil1994@gmail.com>
Connor White <connor.bcw@gmail.com>
Conway Anderson <hello@conwayanderson.com>
Cordaro <elvis07@163.com>
CornerSkyless <573196853@qq.com>
@ -149,6 +165,7 @@ Daniel <caishujunchn@163.com>
Daniel Chang <danielchang2022@u.northwestern.edu>
Daniel Gomez <dgomez@orangeloops.com>
Daniel Harrison <daniel.equiknox@gmail.com>
Daniel Pfeffer <mail@danielpfeffer.de>
Danny Hoower Antonio Viasus Avila <danjavia@gmail.com>
Daphne Won <daphne.won@shopee.com>
Daqi Song <dqaria@gmail.com>
@ -161,13 +178,18 @@ David Broder-Rodgers <david.broder-rodgers@softwire.com>
David Hatten <dhatten@covermymeds.com>
David Schneider <davschne@gmail.com>
Davide Bianchi <bianchidavide12@gmail.com>
Dawnlck <chaokai.lck@antgroup.com>
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>
Derrick <derricktel@foxmail.com>
Di Wu <di@gridx.cn>
DiamondYuan <admin@diamondyuan.com>
DiamondYuan <541832074@qq.com>
Diego Barreiro <diego.barreiro.perez@gmail.com>
Diego Villacís <diegovillacis101@gmail.com>
Dimitri Mitropoulos <dimitrimitropoulos@gmail.com>
Dmitriy <dimusig@gmail.com>
Dmitriy Mironov <dima.dev01@gmail.com>
@ -178,9 +200,12 @@ Dmitry Manannikov <email@slonoed.net>
Dmitry Snegirev <rikkitp@gmail.com>
Dmitry Tikhomirov <tikhomirov@element-soft.com>
Dongcheng Wang <dongcheng6@qq.com>
Dony Sukardi <donysukardi@gmail.com>
Dorian <dorian@doma.io>
DosLin <doslino@gmail.com>
Douglas Mason <Demasonjr@gmail.com>
Dreamcreative <m543438924@163.com>
Dzmitry Yarmoshkin <spanb4@gmail.com>
Eager <1226393396@qq.com>
Eber Rodrigues <eberjoe@gmail.com>
EcmaProSrc.P/ka <asoiso@foxmail.com>
@ -193,14 +218,18 @@ Eduardo Ludi <eduludi@gmail.com>
Edward <7047924@qq.com>
Egor Yurtaev <yurtaev.egor@gmail.com>
Elaina Cherudim <shr9192@gmail.com>
Eldar Mirzabekov <prog.eldar@gmail.com>
Eldar Mustafaiev <eldar.mustafaiev@gmail.com>
Eli White <github@eli-white.com>
Eliot Sanford <eliot587@gmail.com>
Emerson Laurentino <emersonlaurentino@hotmail.com>
Emily Xiong <xiongemi@gmail.com>
Ender Lee <chnliquan@163.com>
Eric <84263800@qq.com>
Eric Bonow <ebonow@hotmail.com>
Eric Celeste <efc@clst.org>
Eric Chen <airkro@qq.com>
Eric Lee <ericfly33@gmail.com>
Eric Turriff <eric.turriff@gmail.com>
Eric Wang <eric@canva.com>
Eric Wang <wjun0912@gmail.com>
@ -210,36 +239,50 @@ Eugene Matvejev <eugene.matvejev@gmail.com>
Eugene Molokov <molokovev@gmail.com>
Eusen <179530591@qq.com>
Evan Charlton <evancharlton@gmail.com>
EvanOne文一 <1656081615@qq.com>
Evgeny Kuznetsov <jackk@ya.ru>
Eward Song <eward.song@gmail.com>
FJHou <firemybugs@gmail.com>
Fachreza Muslim <fachreza.muslim7396@gmail.com>
Federico Marcos <marcosfede@gmail.com>
Ferdi Koomen <info@madebyferdi.com>
Fergus Leung <fergusleung96@gmail.com>
Fernando Giarritiello <fgiarritiello@gmail.com>
Filip Kukovec <filip.kukovec1@gmail.com>
Filip Seman <filip.seman@protonmail.com>
Florian Orpelière <florian.orpeliere@gmail.com>
Flynn <li.fulin@foxmail.com>
Fog3211 <fog3211@163.com>
For177 <mengqiang.q@gmail.com>
Frank <frankdingv@gmail.com>
Frank Fang <FrankFang1990@gmail.com>
Frezc <Frezcw@gmail.com>
Fullstop000 <fullstop1005@gmail.com>
GJ Wang <itellboy@foxmail.com>
GSBL <2602140596@qq.com>
Gabe Medrash <gabeme@alleninstitute.org>
Gabriel Henrique <bielrockx@gmail.com>
Gabriel Le Breton <lebreton.gabriel@gmail.com>
Gabriel Mendez Reyes <gabriel.mendez0@hotmail.com>
Gabriel Nunes <gabriel@multiverso.me>
GalenWong <wonggalen1999@gmail.com>
GannonSun <GannonSun@gmail.com>
Gao Jiangmiao <tolbkni@gmail.com>
Garrick Crouch <garrickcrouch@gmail.com>
Gautier <rollingautier2@gmail.com>
Geoff Holden <geoff@brightloudnoise.com>
Geoffrey Bell <gtb104@yahoo.com>
George Gray <george@ummodesign.com>
Germini <634709523@qq.com>
Gerson Garrido <ggarridom23@gmail.com>
Gherciu Gheorghe <gherciu553@gmail.com>
Gianmarco Rengucci <rengucci.gianmarco@gmail.com>
Gin-X <xcbwyys@hotmail.com>
Go7hic <gtfx0209@qq.com>
Goh Zhao Yang <austinzy@airasia.com>
Golevka <huangsamfisher@163.com>
Googleplex <yyoung2001@gmail.com>
Gourav Saini <mailtogouravsaini@gmail.com>
Graeme Yeates <yeatesgraeme@gmail.com>
Graeme Yeates <gyeates@clearpath.ai>
Grant Klinsing <gklinsing@gmail.com>
@ -247,10 +290,14 @@ Gray Choi <gray.choi.1988@gmail.com>
Guan Hao <raptium@gmail.com>
Guan Yu Pan (Jacky) <jackypan1989@gmail.com>
Guichi Zhao <zgc910619@gmail.com>
Guojun Wang <itwanggj@163.com>
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>
Hanjun Kim <hallazzang@gmail.com>
Hanz Luo <lhz0516@gmail.com>
@ -262,6 +309,7 @@ Harshit Mehrotra <harshitmehrotra@hotmail.com>
Harutaka Kawamura <hkawamura0130@gmail.com>
He Linming <hlm52pk@163.com>
Heaven <ne_smalltown@163.com>
Hell Cat <hellcatvn@gmail.com>
Henri Normak <henri.normak@gmail.com>
HeskeyBaozi <hezhiyu233@foxmail.com>
HiddenMan <1196089730@qq.com>
@ -282,6 +330,7 @@ ILdar Nogmanov <nogmanov@gmail.com>
Igor <nemytyshew@yandex.ru>
Igor Andriushchenko <igor.andriushchenko@snowsoftware.com>
Igor G <i.gaidai4uk@gmail.com>
Ikko Ashimine <eltociear@gmail.com>
Ilan <hasanovtk@gmail.com>
Ilya Bondarenko <bondarenko.ik@gmail.com>
ImJoeHs <865439601@qq.com>
@ -297,6 +346,7 @@ Ivan Tsybulin <vanyatsybulin@gmail.com>
Ivan Yakovlev <addictional2013@gmail.com>
Ivo Stratev <ivo.stratev.tues@gmail.com>
JRoger <sjr_vip@126.com>
Jabir K.H <khjabir@gmail.com>
Jack <jacklee82814@gmail.com>
Jack Hsieh <jack@egenware.com>
Jack Lo <jack-lo@foxmail.com>
@ -305,21 +355,27 @@ Jackie.Ls <418292038@qq.com>
Jacques Kvam <jwkvam@gmail.com>
JaePil Jung <jjp5023@gmail.com>
Jaewook Ahn <ajw4586@gmail.com>
Jaimy de Graaf <me@vfour.dev>
Jake Richards <jake.richards@genesys.com>
Jaly <dcdlove@qq.com>
James <daoxingyue@live.cn>
James <james@schoolshape.com>
James Tsang <wentao_zeng1@163.com>
James Yeung <shunjiey@hotmail.com>
JamesYin <elantion@gmail.com>
Jan Václavík <jvaclavik@gmail.com>
Janry <wangzhili56@126.com>
Jaroslav Bereza <github.com@bereza.cz>
Jarret Moses <jarret.moses@gmail.com>
Jarvis1010 <travis.mair@gmail.com>
Jase Owens <jaseowens197@gmail.com>
Jase Pellerin <jasepellerin@gmail.com>
Jason <ceocjy@vip.qq.com>
Jason Chung <shiningjason1989@gmail.com>
Jason Yin <yinjiangsheng@gmail.com>
Jay Fong <fjc0kb@gmail.com>
Jean-Luc Sorak <jlsorak@icloud.com>
Jean-Philippe Roy <ohnoesmyoreos@gmail.com>
Jeff Wen <sinchangwen@gmail.com>
Jeffrey Carl Faden <jeffreyatw@gmail.com>
Jehu <z_zhihao@foxmail.com>
@ -332,9 +388,11 @@ Jesper We <jesper@journeyman.se>
Jesus The Hun <iletaitunefoisfenghuang@gmail.com>
Jiabin Peng <png.inside@gmail.com>
Jiahao <jahowchen@outlook.com>
Jiajun Chen <tychenjiajun@live.cn>
Jialei <jialeicui@126.com>
Jiawei Huang <hjiawei@gmail.com>
Jichao Zhong <felixjichao@gmail.com>
Jiehui <jeffylaiakanull@gmail.com>
Jieraaa <842533841@qq.com>
Jin ZHANG <jz.zhangjin@gmail.com>
JinXin <m18508218948@163.com>
@ -350,6 +408,8 @@ Joe <qiaolibo@126.com>
Joe Hsu <jhsu.x1@gmail.com>
Johannes Andersen <johannes.andersen@signifyd.com>
Johannes Loewe <johannes@loewe.pm>
John <John60676@qq.com>
John Carlo <johncarloaustria@pm.me>
John Johnson III <john@johnjohnson.cc>
John Nguyen <jtnguyen236@gmail.com>
Johnny Lim <izeye@naver.com>
@ -360,21 +420,29 @@ Jonathan Lee <1150974628@qq.com>
Jonny Buchanan <jonathan.buchanan@gmail.com>
Joo Wu <wuchu356@gmail.com>
Jordan Hornblow <jordan@jch254.com>
Joshua Chen <sidachen2003@gmail.com>
Josue Peralta <jperal77@gmail.com>
Josué <ujosuegt@outlook.com>
JounQin <admin@1stg.me>
JribiBelhassen <belha9inzaghi@gmail.com>
Jtree03 <wowns0903@gmail.com>
JuFeng Zhang <zjffun@gmail.com>
Juan Carlos Lloret H <juan.carlos.lloret@lansweeper.com>
Juan Rodrigo Venegas Boesch <jrvboesch@gmail.com>
Julia Passynkova <ipassynk@hotmail.com>
Julien Confetti <julien.confetti@ulg.ac.be>
Jun Wooram <chatoo2412@gmail.com>
JuniorTour <juniortour@qq.com>
Junwoo Ji <jydrogen@gmail.com>
Junyu Zhan <irrigator@yeah.net>
Juraj Carnogursky <durisvk2@gmail.com>
Justin Reich <reich.justin@gmail.com>
Justin Schuldt <justinschuldt@gmail.com>
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>
Kasra Bigdeli <kasra85@gmail.com>
Kayson Wu <772663139@qq.com>
Kelvin Chu <chubillkelvin@gmail.com>
@ -421,6 +489,7 @@ LeoYang <LeoY.Dev@gmail.com>
Leon Koole <leon@koole.io>
Leon Shi <superRaytin@163.com>
Leon Shi <superRaytin@gmail.com>
Lewis <lewisfidlers@gmail.com>
Li C. Pan <18306423262@163.com>
Li Chao <rftstars@qq.com>
Li Ming <armyiljfe@gmail.com>
@ -428,18 +497,25 @@ LiPinghai <twopoles@163.com>
LilyWakana <873435892@qq.com>
Liming Jin <jinliming2@gmail.com>
Liming Jin <jinlm@knownsec.com>
Liron Lavy <liron.lavy@gmail.com>
Liu Bowen <Mr_lbw@outlook.com>
Liu Ya <liuya54892@gmail.com>
Liu Yang <zation1@gmail.com>
LongYinan <lynweklm@gmail.com>
Loogeek <340017792@qq.com>
Loïc Huvé <loic.huve@protonmail.com>
Lu Yu <brantscube@hotmail.com>
Lucien Lee <lkiral7903@gmail.com>
Ludwig Bäcklund <ludli839@student.liu.se>
Luke Vella <me@lukevella.com>
Lyndon001 <lld207@126.com>
M Mitchell <mail@megmitchell.ca>
M. Burak Kalkan <mburakkalkan@gmail.com>
MD. Ariful Alam <swazan.arif@gmail.com>
MG12 <wuzhao.mail@gmail.com>
Ma Tianxiao <matx2215@outlook.com>
Maciej Czekaj <natanielcz@gmail.com>
MadCcc <1075746765@qq.com>
Madis Väin <madisvain@gmail.com>
Maksim Nesterenko <alendorff@gmail.com>
Maksim Slotvinskij <m.slotvinskij@gmail.com>
@ -449,6 +525,7 @@ Manjit Kumar <manjit1727@gmail.com>
Manoj Kumar <manoj.150283@gmail.com>
Manweill <mic.liangwenwei@foxmail.com>
MaoYiWei <137308365@qq.com>
Map1en_ <maplenagisa@gmail.com>
Marcel Jackwerth <marceljackwerth@gmail.com>
Marcela Bomfim <mbomfim@live.com>
Marcio Pamplona <marciopamplona79@gmail.com>
@ -469,6 +546,7 @@ Matt Lein <matt.lein@code42.com>
Matt Wilkinson <mattwilki17@gmail.com>
Max <maksym.mosyura@kruschecompany.com>
Maximilian Meyer <Maximilian.Meyer@br.de>
Md_ZubairAhmed <m-zubairahmed@protonmail.com>
MeiLin <postget.me@gmail.com>
MengZhaoFly <1424254461@qq.com>
Meow-z <372086270@qq.com>
@ -476,6 +554,7 @@ Meowu <474384902@qq.com>
Miaow <i@zfeng.net>
Micah Guttman <memtech3@gmail.com>
Michael Adams <mtadams007@gmail.com>
Michael Crenshaw <michael@crenshaw.dev>
Michael Krog <mic@apaq.dk>
Michael Salaverry <michaels@axonize.com>
Michael Salaverry <barakplasma@gmail.com>
@ -493,6 +572,7 @@ Min <dicklwm@163.com>
MinJeong Kim <min7859@gmail.com>
MinYuan <s1124yy@gmail.com>
Ming Hann <eldy8888@gmail.com>
Minh Quy <sugiacupit@gmail.com>
Minqi Pan <pmq2001@gmail.com>
Minsung Ryu <ryums0227@gmail.com>
Minwei Xu <faceswilliam@gmail.com>
@ -501,11 +581,14 @@ Misha Kav <misha.kav@gmail.com>
Mitchell Demler <mitchell.demler@harcourts.net>
Moein Alizadeh <truemoein@gmail.com>
Mohamed Seada <mohamed.seada.1994@gmail.com>
Mohammad Anas <mohammad.anas190@gmail.com>
Mohammad Arsalan <a4arshi@yahoo.com>
Mohammad Faisal <faisalhmohd@gmail.com>
Mohan Ban <banmohan@outlook.com>
Mohelm97 <mohelm97@gmail.com>
Mongkii <mongkii@hotmail.com>
Moni <usmoni@gmail.com>
Monty <montyma1207@163.com>
Mounish Sai <pvsmounish@gmail.com>
Mr.Biscuit <sunshuaiqi@gmail.com>
Mr.Tone <vector@malubei.com>
@ -523,12 +606,15 @@ Nathan Wells <nwwells@gmail.com>
Naveen <mailtomassnaveen@gmail.com>
Neekey <ni184775761@gmail.com>
Nekron <nekron.hyt@gmail.com>
Neo Tan <neotan12@hotmail.com>
Neto Braghetto <netow93@gmail.com>
Neverland <chenjiahan@buaa.edu.cn>
Nico <nicolas@freddelacompta.com>
Nidhi Agarwal <nidhi.agarwal@zomato.com>
Nihal Saxena <saxenanihal95@gmail.com>
Nikesh <nikesh.ramnani@gmail.com>
Nikita Bystrov <arttsesoft@gmail.com>
Nikita Marinosyan <nikita.marinosyan@gmail.com>
Nikitenkova <katrin_7nes7@mail.ru>
Niko Autio <niko.autio@fenten.fi>
Nikolay <veseliy07@gmail.com>
@ -551,6 +637,8 @@ 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>
Patryk <longer44@gmail.com>
@ -564,12 +652,16 @@ Peter Berg <atticusberg@gmail.com>
Phanupong Janthapoon <panupong.jtp@gmail.com>
Philip Oliver <philipodev@gmail.com>
Phyllis <escapiststupor@gmail.com>
Picsong <312465087@qq.com>
Pierre <pierre@bazoge.com>
Pierre Neter <pierreneter@gmail.com>
Piotr Monwid-Olechnowicz <hasparus@gmail.com>
Piper Chester <piperchester@gmail.com>
Pixy Yuan <pixy.bupt@gmail.com>
PlayerWho <who.zhuang@gmail.com>
Pob Ch <590650@gmail.com>
Pooya Parsa <pyapar@gmail.com>
Primlx <primulax@live.com>
Pubudu Kodikara <pubudu@leafycode.com>
Pyiner <lijiuyang1992@gmail.com>
Pyroboomka <qwaarty@mail.ru>
@ -597,6 +689,7 @@ Regan Langford <regan.reihana@gmail.com>
Renny Ren <rennyallen@hotmail.com>
Renovate Bot <bot@renovateapp.com>
Rex <zhangzilong.zzl@163.com>
Ricardo Morais <moraispgsi@gmail.com>
Ricardo Raphael Joson <rrjoson08@gmail.com>
Richard D. Worth <rdworth@gmail.com>
Rick Zhou <rinick@gmail.com>
@ -608,6 +701,7 @@ Rohan Malhotra <rohan.root@gmail.com>
Rongjian Zhang <pd4d10@gmail.com>
Rrrandom <emanonhere@gmail.com>
RunningCoderLee <sprint_l@aliyun.com>
Ryan Lee <yigexbl@gmail.com>
RyanHui <ryanhui1996@gmail.com>
Régis Foucault <regisfoucault@gmail.com>
SHEN Lin <shenlin192@gmail.com>
@ -622,6 +716,7 @@ Sam Lanning <sam@samlanning.com>
Sam Marks <sam@sammarks.me>
Sam Maxwell <sam@paybase.io>
Samed Düzçay <samedduzcay@gmail.com>
Sami Mäkinen <zakarfin@gmail.com>
Samuel Gaus <sam@gaus.co.uk>
Sanghyeon Lee <yongdamsh@gmail.com>
Sangle <whb97@163.com>
@ -632,14 +727,18 @@ Scott Goci <scottjg@gmail.com>
Scott Sturgeon <scott@tugboatlogic.com>
Sean Lin <sean@ejoy.com>
Sean Willis <sean.willis@incorta.com>
Seba Kerckhof <seba.kerckhof@gmail.com>
Sebastian Blade <blade254353074@hotmail.com>
Sebastian Busch <s.busch@obg-gruppe.de>
Sebastian Busch <mail@sebastian-bus.ch>
Sebastian Busch <s.busch@qbus-enet.de>
Sepush <sepush@outlook.com>
Sergey Kachanovskiy <sergey.kachanovskiy@gmx.de>
Sergey Levkovich <gosu.87@mail.ru>
Sergey Volynkin <sergey.volynkin@akvelon.com>
Sergio Crisostomo <sergiosbox@gmail.com>
Sevak <shaderzak@gmail.com>
Shanjie Chen <cnzxcsj@outlook.com>
Shawn Sit <xueqingxiao@gmail.com>
Shengnan <baxtergu@gmail.com>
ShiTengFei <shitengfei@goyoo.com>
@ -656,35 +755,46 @@ Sivaraj <contact@sdev.in>
SkyAo <csvwolf@qq.com>
Skylar艺璇 <mengxue.yx@gmail.com>
Snyk bot <github+bot@snyk.io>
Songhn <songhn233@gmail.com>
Sonjeet Paul <sonjeetcp.98@gmail.com>
SoraYama <sorayamahou@gmail.com>
Spencer <spjy@hawaii.edu>
Stanley Thijssen <stanley.thijssen@energybox.com>
Stanley Thijssen <stanley.thijssen@gmail.com>
Stef Schenkelaars <stef.schenkelaars@gmail.com>
Stephen Esser <Stephen.Esser@gmail.com>
Stevche Radevski <sradevski@live.com>
Steven.zhong <953665604@qq.com>
Subroto <shub1493biswas@gmail.com>
Suki小火车 <463355954@qq.com>
Sumit Vekariya <sumitvekariya7@gmail.com>
Sunny Luo <sunnylqm@gmail.com>
Sven Efftinge <sven.efftinge@typefox.io>
SyMind <dacongsama@live.com>
SylvanasGone <397009765@qq.com>
TTC <345866517@qq.com>
Tanmoy Bhowmik <tanmoy.openroot@gmail.com>
Tannmay S Gupta <tanmaygup123@gmail.com>
Tao <magicdawn@qq.com>
Tao Zhang <windse7en@gmail.com>
Taucher Christoph <taucher.ch@gmail.com>
Taylor Sabell <taylorsabell@gmail.com>
Ted Shin <ted.m.shin@gmail.com>
Teng YANG <morenyang88@gmail.com>
Teng YANG <yangteng@me.com>
Tengjiao Cai <caitengjiao1987@gmail.com>
Terence <trence320@163.com>
The Rock <zhoguoxin@126.com>
Themi Tsiotas von Pfaler <themi1234@gmail.com>
Theo Satloff <1golfball@gmail.com>
Thibault Derousseaux <tde@activeviam.com>
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>
Tom Xu <ycxzhkx@gmail.com>
Tom Xu <tom.xu@antcosa.com>
@ -699,10 +809,12 @@ TsesamLi <tsesamli17@gmail.com>
Ty Mick <ty@tymick.me>
Tyler <chaotyler@gmail.com>
Ubaldo Quintana <blkdr@hotmail.com>
Uladzimir Atroshchanka <Uladzimir.Atroshchanka@vertexinc.com>
Vadim Macagon <vadim.macagon@gmail.com>
Valentin Vichnal <valentin@vichnal.com>
Van Nguyen <vnguyen94@gmail.com>
Varun Dey <varundey20@gmail.com>
Varun Sharma <varunsh@stepsecurity.io>
Vemund Santi <vemund@santi.no>
Vic <709147950@qq.com>
Victor <saptefrativictor@gmail.com>
@ -714,6 +826,7 @@ Viorel Cojocaru <vio@beanon.com>
Vitaliy Mazurenko <vitaliymazurenko@gmail.com>
Vitaly Budovski <vbudovski@gmail.com>
ViviaRui <zr1450995198@163.com>
Vlad Vovk <wolfykey@gmail.com>
Vu Hoang Minh <vuhminh@gmail.com>
Vyacheslav Kamenev <vy.kamenev@gmail.com>
Walter Barbagallo <brb.walter@gmail.com>
@ -725,7 +838,9 @@ 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>
@ -737,6 +852,7 @@ William Stein <wstein@gmail.com>
WingGao <wing.gao@live.com>
Wu Haotian <whtsky@gmail.com>
WuJiali <18767152447@163.com>
X-Jagger <xl.jagger@gmail.com>
XBTop1! <xbtop1@gmail.com>
XTY <^@xty.dev>
Xiaoming <yokiming1994@gmail.com>
@ -744,6 +860,7 @@ Xie Guanglei <xieguanglei@hotmail.com>
Xinxing Li <lixinxing.2019@bytedance.com>
Xinzhe Wang <me@imwxz.com>
Xiping.wang <527409987@qq.com>
Xu Zhiwei <gcdxuzhiwei@gmail.com>
XuMM_12 <owiatsq@sina.cn>
Xudong Cai <fifsky@gmail.com>
Xudong Huang <me@xudong.dev>
@ -763,11 +880,13 @@ Yu <yutingzhao1991@sina.com>
Yu Mao <maoyulore@outlook.com>
Yu Mao <maoyu960320@hotmail.com>
YuChao Liang <l.yuch@foxmail.com>
YuTao <yutao0818@vip.qq.com>
Yuan <1076849402@qq.com>
Yuhang Liu <644186735@qq.com>
Yulia Maximova <juliam2007@mail.ru>
Yunfly <120562638@qq.com>
Yunus EŞ <yunus@yunuses.com>
Yunwoo Ji <unu12073@gmail.com>
Yuri Pirola <yuri.pirola@unimi.it>
Yury Kozyrev <urakozz@me.com>
Yusuke Ito <novi.mad@gmail.com>
@ -776,32 +895,40 @@ Yuxuan Huo <yuxuan.huo2011@gmail.com>
YuyingWu <wuyuying1128@gmail.com>
ZHANGYU <723156735@qq.com>
ZYSzys <zyszys98@gmail.com>
Zach Bird <546439325@qq.com>
Zack Amiton <zamiton@outlook.com>
Zack Craig <zack@zack6849.com>
Zap <a124116186@qq.com>
ZeroToOne <igeeke@163.com>
Zester Quinn Albano <zesterquinn.albano@gmail.com>
Zgo <zguoby@gmail.com>
Zhang Zhi <fytriht@gmail.com>
Zheeeng <hi@zheeeng.me>
Zhiqiang Gong <elory0513@hotmail.com>
ZhouZhen <503633021@qq.com>
Zhuo Chen <chenzhuo@caicloud.io>
Ziluo <gyfzzu@gmail.com>
Zohaib Ijaz <mzohaib.qc@gmail.com>
Zzzen <843968788@qq.com>
_XiaoTian <istianlei@qq.com>
aLIEzsss4 <15624958088@163.com>
aashutoshrathi <aashutoshrathi@gmail.com>
acfasj <acfasj@gmail.com>
adam <adamwu1992@163.com>
afc163 <afc163@gmail.com>
agent-z <1607291079@qq.com>
aghArdeshir <ardeshireo@gmail.com>
ahalimkara <ahalimkara@gmail.com>
ajuner <106791576@qq.com>
alekslario <aleksandlario@gmail.com>
alex89lj <379118572@qq.com>
alexchen <alexchen@easyops.cn>
amedora <americandragsterracing@gmail.com>
aoxiang78 <ao_xiang@live.com>
appleshell <appleshell@outlook.com>
arange <panpan2558@gmail.com>
arifemrecelik <ce.arifemre@gmail.com>
arturpfb <arturpfb@gmail.com>
ascodelife <394922886@qq.com>
ascoders <576625322@qq.com>
ashishg-qburst <ashishg@qburst.com>
atomoo <yangpein@gmail.com>
@ -812,9 +939,11 @@ baidumap <460807394@qq.com>
bang <sqibang@gmail.com>
baozefeng <727751065@qq.com>
bcd337 <b.bcd337@gmail.com>
benben <jinwentao0914@dingtalk.com>
bigbigbo <zxb141242@163.com>
binyellow <571704908@qq.com>
blankzust <450811238@qq.com>
btea <2356281422@qq.com>
bukas <yhz1219@gmail.com>
byuanama <byuan@ama.com.au>
byzyk <bohdan.kh@gmail.com>
@ -827,6 +956,7 @@ cc189 <cc189dev@gmail.com>
chaofeis <408067385@qq.com>
chchen <cc272309126@gmail.com>
chen wen jun <731028571@qq.com>
chen-jingjie <2383844893@qq.com>
chencheng (云谦) <sorrycc@gmail.com>
chenlei <745512023@qq.com>
chenlong <long.chen@abssqr.com>
@ -850,6 +980,7 @@ 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>
@ -861,6 +992,7 @@ 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>
edgji <j.edgji@gmail.com>
eidonjoe <806488716@qq.com>
@ -877,11 +1009,14 @@ flashback313 <windmark2012@gmail.com>
flyerH <hzw758@qq.com>
frezc <504021398@qq.com>
gaoryrt <gaoryrt@gmail.com>
gaozhenqian <837856276@qq.com>
genie <genie88@163.com>
gepd <guillermoepd@hotmail.com>
godfather <greenday.wj@foxmail.com>
gregahren <grega.hren@gmail.com>
gxvv <gaoxin18000@gmail.com>
gyh9457 <gyh9457@163.com>
gzq <zguoby@gmail.com>
haianweifeng <1531297152@qq.com>
haimrait <haimrait@gmail.com>
handy <lihandi@gmail.com>
@ -895,6 +1030,7 @@ hauwa123 <hauwa.aminu@outlook.com>
hebingchang <hebingchang1@live.com>
hehe <xpc1993@gmail.com>
hehe <xpc_kacl@163.com>
hello-chinese <841030329@qq.com>
henryv0 <henryvo94@gmail.com>
hi-caicai <hi@cai-cai.me>
hicrystal <295247343@qq.com>
@ -904,6 +1040,8 @@ huangyan.py <huangyan.py@bytedance.com>
hugorezende <hugorezendedev@gmail.com>
huishiyi <zhou1maple@gmail.com>
huzzbuzz <huzzbuzz@outlook.com>
hydraZty <670688667@qq.com>
i3web <itnote@qq.com>
iamcastelli <sowed@cyberdude.com>
iamkun <kunhello@outlook.com>
ibrahim velinov <ibsukru@gmail.com>
@ -912,6 +1050,8 @@ int2d <int2d@qq.com>
iojichervo <ioji@chervonagura.com.ar>
ioldfish <fish.wangl@gmail.com>
iorikingdom <iorikingdom@hotmail.com>
isakol <isakol@kth.se>
itibbers <jxn2014@gmail.com>
iugo <iugogogo@gmail.com>
j3l11234 <297259024@qq.com>
jasonslyvia <jasonslyvia@gmail.com>
@ -925,11 +1065,11 @@ jieniu$ <jienius@outlook.com>
jinouwuque <ee2win@gmail.com>
jinyaqiao1102 <405782493@QQ.com>
jojoLockLock <miffyschou@sina.com>
jueinin <1014397160@qq.com>
junjing.zhang <zhangjunjing@gmail.com>
kacjay <45483388@qq.com>
kaifei <150641329@qq.com>
kailunyao <kailunyao@163.com>
kanweiwei <kwwnjujlc@sina.com>
kanweiwei <475801900@qq.com>
kaoding <41830859@qq.com>
kasinooya <kasinooya@gmail.com>
@ -940,6 +1080,7 @@ kdepp <kdepp.cd@gmail.com>
keng <keng@renderinghouse.com>
kenve <zwei.xie@gmail.com>
kermolaev <kermolaev@cloudally.com>
kily zhou <keeliizhou@gmail.com>
klouskingsley <harry_tse@163.com>
ko <git@yaksok.net>
konakona <lovekonakona@gmail.com>
@ -962,6 +1103,7 @@ liekkas <zjq0717@163.com>
lihqi <455711093@qq.com>
lilun <lilun_cd@keruyun.com>
limingxin <906529775@qq.com>
lisenenkov <lisenenkov@mail.ru>
littleLane <857183384@qq.com>
liuchuzhang <liuweiminer@hotmail.com>
liuchuzhang <liuweiminer@126.com>
@ -975,6 +1117,7 @@ luyiming <luyimingchn@gmail.com>
lvren <luren6049@qq.com>
lxnxbnq <yuanddmail@163.com>
lyhper <lyhper@gmail.com>
lyon.han <lyon.han.china@outlook.com>
mArker <252133226@qq.com>
maks <pine3ree@gmail.com>
maximest-pierre <me@maximest-pierre.me>
@ -982,6 +1125,7 @@ melchior voidwolf <kmno4k2mno4@gmail.com>
memoryza <jincai.wang@foxmail.com>
mgrdevport <mgrdevport@gmail.com>
mingyan.yu <mingyan.yu@wormpex.com>
miracles1919 <516571350@qq.com>
mjfwebb <bugeyedboy@gmail.com>
mkermani144 <mkermani144@gmail.com>
mmmveggies <jakeselig@gmail.com>
@ -993,6 +1137,7 @@ mumiao <mumiao@dtstack.com>
mushan0x0 <mushan0x0@gmail.com>
muzuiget <muzuiget@gmail.com>
natergj <nater_nater@me.com>
netcon <netcon@live.com>
ngolin <poodll@163.com>
nick-ChenZe <chenze2168@gmail.com>
niko <644506165@qq.com>
@ -1003,6 +1148,7 @@ oldchicken <www.chao3208525@qq.com>
paleface001 <liuye10@yahoo.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>
@ -1022,6 +1168,7 @@ qramilq <ramirez99@mail.ru>
qubaoming <qubaoming@didichuxing.com>
ravirambles <ravirambles@gmail.com>
realEago <774855001@qq.com>
renzhao1113 <547249523@qq.com>
richardison <richard.ison@carleton.ca>
ryangun <ryangun@foxmail.com>
ryanhoho <hswacoal@gmail.com>
@ -1031,6 +1178,7 @@ sallen450 <jqh101@sina.com>
samir elsharkawy <samir.elsharkawy@gmail.com>
sdli <1669375803@qq.com>
seognil LC <seognil@gmail.com>
serializedowen <wjh199455@gmail.com>
sfturing <sfturing@gmail.com>
shangyuan.ning <shangyuan.ning@manaowan.com>
shawtung <shawtung@qq.com>
@ -1045,8 +1193,10 @@ snadn <snadn@snadn.cn>
snail <120216220@qq.com>
soeyi <sixinlei0927@gmail.com>
sojournerc <cmeyer@zvelo.com>
soso <ethdud1@gmail.com>
sosohime <theziming@126.com>
spideeee <spideeee@github.com>
stefango <nankongyinan@gmail.com>
stevenyuysy <stevenyuysy@gmail.com>
stickmy <stickmyc@gmail.com>
susiwen8 <susiwen8@gmail.com>
@ -1067,7 +1217,10 @@ toshi1127 <toshi.matsumoto.2n@stu.hosei.ac.jp>
twobin <twobin@live.com>
u3u <qwq@qwq.cat>
ubuntugod <adarshron@gmail.com>
uchanlee <uchanlee.dev@gmail.com>
undefined <johann@objekt.stream>
undefined <undefined>
unknown <tongsiyuan@corp.netease.com>
unknown <chenyizhongx@gmail.com>
ustccjw <317713370@qq.com>
vagusX <vagusxl@gmail.com>
@ -1075,10 +1228,15 @@ valleykid <valleykiddy@gmail.com>
vaytsel <vaytsel@gmail.com>
veveue <veveue@dingtalk.com>
vgeyi <vgeyiz@126.com>
vldh <lidahao@sisyphe.com.cn>
vldh <alwaysloseall@sina.com>
vouis <16nding@gmail.com>
wa-ri <tztl1995@gmail.com>
wadezhan <wadezhan@tencent.com>
wan wan <wancihua@qq.com>
wangao <jameslahm17@gmail.com>
wangao <1366463855@qq.com>
wangao <wa17@mails.tsinghua.edu.cn>
wangshantao <605682551@qq.com>
wangshuai <wangshuai@momenta.ai>
wangtao0101 <yuecjn@gmail.com>
@ -1093,6 +1251,7 @@ wendellhu <wendellhu95@gmail.com>
wenhong <wenhong.zw@antfin.com>
whinc <whincwu@163.com>
whtang906 <whtang906@gmail.com>
whwangms <whwangms@outlook.com>
willc001 <will.c001@163.com>
wizawu <wizawu@gmail.com>
wleven <408493323@qq.com>
@ -1117,6 +1276,8 @@ y-take <y.takey@gmail.com>
yanguoyu <841185308@qq.com>
yangwukang <yangwukang@boco.com.cn>
yangxiaolin <yangxiao2810279802@gmail.com>
yanm1ng <644169721@qq.com>
yaoweiprc <yaoweiprc@hotmail.com>
ycjcl868 <45808948@qq.com>
ye4241 <ye4241@gmail.com>
yehq <yedi728@qq.com>
@ -1125,7 +1286,9 @@ yeshan333 <1329441308@qq.com>
yibu.wang <yibu.wang@orion.co.com>
yiminanci <yiminanci@gmail.com>
yiminghe <yiminghe@gmail.com>
yingxirz <inseeing@gmail.com>
youmoo <youmoo@vellichor.me>
youngz <chinayangzhan@126.com>
yuche <i@yuche.me>
yuezk <yuezk001@gmail.com>
yui <1151815317@qq.com>
@ -1141,19 +1304,23 @@ zhangpc <zhangpc@tenxcloud.com>
zhangyangxue <383632607@qq.com>
zhangyanling77 <18783226594@163.com>
zhangzh <zhangzh@cnlemon.net>
zhao-huo-long <lijiuyi1995@outlook.com>
zhaocai <lzc09008@gmail.com>
zhaopeidong <lwindscar@gmail.com>
zhenfan.yu <fanerge@qq.com>
zhuguibiao <505418722@qq.com>
zhujun24 <zhujun87654321@gmail.com>
zhyupe <zyp108421@gmail.com>
zilong <jzlxiaohei@163.com>
zinkey <yaya@uloveit.com.cn>
zj9495 <zj9495@gmail.com>
zjf <zjffun@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>
zqran <uuxnet@gmail.com>
ztplz <mysticzt@gmail.com>
zuiidea <zuiiidea@gmail.com>
zx6658 <zx6658@naver.com>
@ -1167,9 +1334,10 @@ zytjs <yitongzhao@163.com>
不吃猫的鱼 <michael2ib1989@gmail.com>
丶尘殇 <sean.snow@live.com>
乔奕轩 <qiao_yixuan@163.com>
九思⚡⚡⚡ <2228429150@qq.com>
二哲 <kodo@forchange.cn>
二手掉包工程师 <rustin.liu@gmail.com>
二货机器人 <smith3816@gmail.com>
云剪者 <584518260@qq.com>
付引 <xxxquotes@gmail.com>
何乐 <work@imhele.com>
何志勇 <15988134176@163.com>
@ -1199,25 +1367,31 @@ zytjs <yitongzhao@163.com>
廖应龙 <vigossliao@gmail.com>
廖星 <liaoxing.lx@bytedance.com>
张大大 <249812928@qq.com>
张威 <1051127659@qq.com>
张秀玲 <18811794335@163.com>
徐坤龙 <272992168@qq.com>
徐新航 <xuxinhang@bytedance.com>
愚道 <tingzhao.ytz@antfin.com>
曾凯 <zengkai2009@foxmail.com>
期贤 <qixian.cs@antgroup.com>
朮厃 <cn.ah.liu@gmail.com>
李环冀 <158757774@qq.com>
杨兴洲 <dr2009.china@gmail.com>
杨哲迪 <yangzhedi@yidian-inc.com>
杨小事er <Uiryzd@163.com>
松子 <window.pibarr@gmail.com>
林煌东 <10543563@qq.com>
柚子男 <yozman@sina.com>
沐霖 <304647173@qq.com>
爱but的苍蝇 <354788473@qq.com>
王林涛 <hzwanglintao@corp.netease.com>
王浩 <boomler@hotmail.com>
王集鹄 <wjhu111@21cn.com>
琚致远 <juzhiyuan@apache.org>
白羊座小葛 <abeyuhang@gmail.com>
砖家 <brickspert.fjl@antfin.com>
砖家 <576679268@qq.com>
章鱼 <ryker.zy@gmail.com>
竹尔 <Juelchiang@gmail.com>
米老朱 <laozhu.me@gmail.com>
精武陈真 <546369005@qq.com>
@ -1232,11 +1406,12 @@ zytjs <yitongzhao@163.com>
蔡伦 <sliuqin@gmail.com>
薛定谔的猫 <weiran.zsd@outlook.com>
薛定谔的猫 <hh_2013@foxmail.com>
诸岳 <fuping.dfp@antgroup.com>
诸岳 <fuping.dfp@antfin.com>
诸岳 <fuping.dfp@antgroup.com>
诸岳 <dengfuping_private@163.com>
诸岳 <dengfuping_develop@163.com>
诸葛龙 <158362530@qq.com>
谭真 <736420282@qq.com>
超能刚哥 <margox@foxmail.com>
迷渡 <justjavac@gmail.com>
郑旭 <332171564@qq.com>
@ -1254,7 +1429,9 @@ zytjs <yitongzhao@163.com>
骗你是小猫咪 <darryshaw@gmail.com>
高力 <3071730@qq.com>
鬼厉 <shawdanon@qq.com>
麦谷 <1141453778@qq.com>
黄俊亮 <jayhuang@easyops.cn>
黄文鉴 <concefly@foxmail.com>
黄斌 <bin.huang02@hand-china.com>
黑雨 <wangning4567@163.com>
龚方闻 <fangwen.gong@baishancloud.com>

View File

@ -15,6 +15,96 @@ timeline: true
---
## 4.20.2
`2022-04-30`
- Segmented
- 🐞 Fix Segmented inconsisit height with other controls. [#35281](https://github.com/ant-design/ant-design/pull/35281)
- 🐞 Fix Segmented animation not working correct in StrictMode mode. [#35281](https://github.com/ant-design/ant-design/pull/35281)
- 🆕 Segmented `options` now supports `icon` property. [#35256](https://github.com/ant-design/ant-design/pull/35256)
- Table
- ⌨️ Improve Table columns sorter a11y experience. [#35269](https://github.com/ant-design/ant-design/pull/35269)
- 🇪🇸 Added Table filter localization for es_ES. [#35309](https://github.com/ant-design/ant-design/pull/35309) [@agarciaguillo](https://github.com/agarciaguillo)
- 💄 Fix Switch color in dark theme. [#35332](https://github.com/ant-design/ant-design/pull/35332)
- 💄 Tweak Breadcrumb link hover color. [#35324](https://github.com/ant-design/ant-design/pull/35324)
- 🐞 Fix Space throws `Encountered two children with the same key` warning in some cases. [#35311](https://github.com/ant-design/ant-design/pull/35311)
- 🐞 Fix Select tag remove icon position issue. [#35336](https://github.com/ant-design/ant-design/pull/35336) [@walidcherhane](https://github.com/walidcherhane)
## 4.20.1
`2022-04-26`
- 🐞 Fix Breadcrumb extra padding and margin style. [#35235](https://github.com/ant-design/ant-design/pull/35235)
- 🐞 Fix Input.Seach inconsistent behavior of triggering `onSearch` when press enter using Chinese inputting method. [#35164](https://github.com/ant-design/ant-design/pull/35164) [@qyzzzz](https://github.com/qyzzzz)
- 🐞 Fix circle reference between Upload and Dragger. [#34379](https://github.com/ant-design/ant-design/pull/34379) [@kanweiwei](https://github.com/kanweiwei)
## 4.20.0
`2022-04-24`
- 🔥 React 18 Support. Fix related known issue.
- 🐞 Fix Form with React 18 StrictMode missing error message update. [#35096](https://github.com/ant-design/ant-design/pull/35096)
- 🐞 Fix Notification and Message throw `createRoot` warning in React 18. [#35030](https://github.com/ant-design/ant-design/pull/35030)
- 🐞 Fix BackTop not working in StrictMode. [#34858](https://github.com/ant-design/ant-design/pull/34858) [@tmkx](https://github.com/tmkx)
- 🔥 New component Segmented. [#34319](https://github.com/ant-design/ant-design/pull/34319)
- 🛠 Since v4.20.0 `Segemented` props `onChange` callback function parameter adjusted from `ChangeEvent` (v4.20.0-alpha.0, v4.20.0-alpha.1) to `value` to simplify API. [#35187](https://github.com/ant-design/ant-design/pull/35187) [@vagusX](https://github.com/vagusX)
- Form
- 🔥 Form support `useWatch` to get current field value. [#35036](https://github.com/ant-design/ant-design/pull/35036)
- 🆕 Form support `useFormInstance` to get current context form instance. [#35039](https://github.com/ant-design/ant-design/pull/35039)
- 💄 Fix Form broken layout when set `labelCol={{ sm: 24 }}` and `wrapperCol={{ sm: 24 }}`. [#34907](https://github.com/ant-design/ant-design/pull/34907)
- 🛎 Menu support `items` for perf prepare, and `children` will be removed in next major version. [#34559](https://github.com/ant-design/ant-design/pull/34559)
- 🆕 Image PreviewGroup Support top progress rendering. [#35038](https://github.com/ant-design/ant-design/pull/35038) [@zpc7](https://github.com/zpc7)
- Upload
- 🆕 Upload support `crossOrigin` for images in `picture-card` mode. [#34981](https://github.com/ant-design/ant-design/pull/34981) [@dragmove](https://github.com/dragmove)
- 🐞 Fix Upload `prefixCls` don't work on file list. [#34944](https://github.com/ant-design/ant-design/pull/34944) [@swchen](https://github.com/swchen)
- 💄 Improve Upload action styles. [#35052](https://github.com/ant-design/ant-design/pull/35052)
- Table
- 🆕 Support reset to the default value rather than empty, when click reset in Table column filter. [#34355](https://github.com/ant-design/ant-design/pull/34355) [@heiyu4585](https://github.com/heiyu4585)
- 💄 Fix Table head background and selection column width styling issues when `size="small"`. [#34963](https://github.com/ant-design/ant-design/pull/34963)
- 🇩🇪 Improve German translations for Table. [#34836](https://github.com/ant-design/ant-design/pull/34836) [@pfedan](https://github.com/pfedan)
- ⚡️ Optimize Table filter calculation perfromance. [#35064](https://github.com/ant-design/ant-design/pull/35064) [@nieyuyao](https://github.com/nieyuyao)
- 💄 Improve small and middle size Table selection dropdown margin style. [#35173](https://github.com/ant-design/ant-design/pull/35173)
- Tree
- 🆕 Tree `switcherIcon` prop support render-prop. [#34470](https://github.com/ant-design/ant-design/pull/34470) [@zqran](https://github.com/zqran)
- 🆕 Tree support `rootClassName` and `rootStyle`. [#34578](https://github.com/ant-design/ant-design/pull/34578)
- Breadcrumb
- 🐞 Fix Breadcrumb deprecated warning of Dropdown `placement`. [#35162](https://github.com/ant-design/ant-design/pull/35162)
- 🐞 Fix Breadcrumb show the number when is unexpected. [#35123](https://github.com/ant-design/ant-design/pull/35123)
- ⌨️ Make structure of Breadcrumb be accessible. [#34082](https://github.com/ant-design/ant-design/pull/34082) [@VladimirOtroshchenko](https://github.com/VladimirOtroshchenko)
- Anchor
- 🆕 Anchor `getCurrentAnchor` has active link as argument. [#34799](https://github.com/ant-design/ant-design/pull/34799)
- 🛠 Refactor Anchor to Function component. [#35073](https://github.com/ant-design/ant-design/pull/35073) [@LongHaoo](https://github.com/LongHaoo)
- Cascader
- 🆕 Cascader supports `showCheckedStrategy ` for value display strategy. [#34568](https://github.com/ant-design/ant-design/pull/34568) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 Fix Cascader search result do not fill the entrie panel. [#35019](https://github.com/ant-design/ant-design/pull/35019) [@boomler](https://github.com/boomler)
- 🆕 Click event object can be accessed in `onCopy` function of Typography. [#34655](https://github.com/ant-design/ant-design/pull/34655) [@yzwxk](https://github.com/yzwxk)
- 🆕 Grid supports `justify="space-evenly"`. [#34606](https://github.com/ant-design/ant-design/pull/34606) [@gp5251](https://github.com/gp5251)
- 🆕 Support `rootClassName` property for Dialog and Image. [#34574](https://github.com/ant-design/ant-design/pull/34574) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 Fix error `Nothing was returned from render` when Skeleton use falsy `loading` props without `children`. [#34872](https://github.com/ant-design/ant-design/pull/34872) [@AlbertAZ1992](https://github.com/AlbertAZ1992)
- 💄 Optimize Switch disabled color to fit colorful background. [#35103](https://github.com/ant-design/ant-design/pull/35103)
- 💄 Remove Tabs `overflow: hidden` style to fix Select and sticky Table display problem inside Tabs. [#35195](https://github.com/ant-design/ant-design/pull/35195)
- 💄 Fix Steps style issues in RTL mode. [#35088](https://github.com/ant-design/ant-design/pull/35088) [@zpc7](https://github.com/zpc7)
- 💄 Fix animation for standalone Badge in RTL mode. [#34899](https://github.com/ant-design/ant-design/pull/34899) [@hmz22](https://github.com/hmz22)
- 🛠 Optimize Modal id generate logic to improve accessibility experience. [#35072](https://github.com/ant-design/ant-design/pull/35072)
- 🐞 Fix Select and AutoComplete scroll abnormal behavior when navigate via keyboard.。[#35025](https://github.com/ant-design/ant-design/pull/35025)
- Spin
- 💄 Fix Spin animation style being abnormally parsed in Parcel. [#35005](https://github.com/ant-design/ant-design/pull/35005)
- ⌨️ Spin add `aria` attribute to improve accessibility. [#34408](https://github.com/ant-design/ant-design/pull/34408) [@heiyu4585](https://github.com/heiyu4585)
- ⌨️ Dropdown support select option by keyboard. [#34738](https://github.com/ant-design/ant-design/pull/34738)
- 🐞 Fix Title, Text, Paragraph components cannot get `ref` bug. [#34847](https://github.com/ant-design/ant-design/pull/34847) [@MQuy](https://github.com/MQuy)
- Input
- 💄 Input.Group prevent components from getting style from Form.Item. [#34764](https://github.com/ant-design/ant-design/pull/34764)
- 💄 Adjust TextArea style in Form. [#34714](https://github.com/ant-design/ant-design/pull/34714)
- ⌨️ Fix `aria-checked` attribute for Checkbox, to avoid screen reader getting an incorrect status. [#34862](https://github.com/ant-design/ant-design/pull/34862) [@SpaNb4](https://github.com/SpaNb4)
- Less
- 💄 Replace less html selector with related variable. [#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
- 💄 Modify less `danger` value from the function to variable. [#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
- 🐞 Arrow border radius variable use fixed value. [#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc)
- TypeScript
- 🤖 Fixed `Upload` component `UploadChangeParam<T>` internal `fileList` not using generics. [#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
- 🤖 Update TypeScript definition for `@types/react@18` compatible. [#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
## 4.19.5
`2022-04-02`

View File

@ -15,6 +15,96 @@ timeline: true
---
## 4.20.2
`2022-04-30`
- Segmented
- 🐞 修复 Segmented 组件高度和其他控件不一致的问题。[#35281](https://github.com/ant-design/ant-design/pull/35281)
- 🐞 修复 React StrictMode 下 Segmented 动画丢失的问题。[#35281](https://github.com/ant-design/ant-design/pull/35281)
- 🆕 Segmented `options` 支持设置 `icon` 属性。[#35256](https://github.com/ant-design/ant-design/pull/35256)
- Table
- ⌨️ 优化 Table 排序按钮的键盘可访问性。[#35269](https://github.com/ant-design/ant-design/pull/35269)
- 🇪🇸 补充 Table 西班牙语筛选文案。[#35309](https://github.com/ant-design/ant-design/pull/35309) [@agarciaguillo](https://github.com/agarciaguillo)
- 🐞 修复 Switch 在暗黑主题下关闭时的颜色问题。[#35332](https://github.com/ant-design/ant-design/pull/35332)
- 💄 微调 Breadcrumb 链接 hover 色为中性色。[#35324](https://github.com/ant-design/ant-design/pull/35324)
- 🐞 修复 Space 在某些情况下抛出 `Encountered two children with the same key` 警告的问题。[#35311](https://github.com/ant-design/ant-design/pull/35311)
- 🐞 修复 Select 多选标签移除图标位置偏下的问题。[#35336](https://github.com/ant-design/ant-design/pull/35336) [@walidcherhane](https://github.com/walidcherhane)
## 4.20.1
`2022-04-26`
- 🐞 修复 Breadcrumb 多余的 `padding``margin` 样式。[#35235](https://github.com/ant-design/ant-design/pull/35235)
- 🐞 修复 Input.Search 在中文输入法下回车键触发 `onSearch` 的行为不一致的问题。[#35164](https://github.com/ant-design/ant-design/pull/35164) [@qyzzzz](https://github.com/qyzzzz)
- 🐞 修复 Upload 和 Upload.Dragger 之间循环依赖的问题。[#34379](https://github.com/ant-design/ant-design/pull/34379) [@kanweiwei](https://github.com/kanweiwei)
## 4.20.0
`2022-04-24`
- 🔥 支持 React 18 以及严格模式,修复了相关已知问题。
- 🐞 修复 Form 在 React 18 的 StrictMode 下,错误信息无法更新的问题。[#35096](https://github.com/ant-design/ant-design/pull/35096)
- 🐞 修复 Notification 和 Message 在 React 18 下抛出使用 `createRoot` 的警告信息。[#35030](https://github.com/ant-design/ant-design/pull/35030)
- 🐞 修复 BackTop 组件在严格模式下不能正常工作的问题。[#34858](https://github.com/ant-design/ant-design/pull/34858) [@tmkx](https://github.com/tmkx)
- 🔥 新增 Segmented 分段控制器组件。[#34319](https://github.com/ant-design/ant-design/pull/34319)
- 🛠 4.20.0 正式版后Segemented 的 `onChange` 回调函数的参数从 `ChangeEvent` 调整为 `value`。如果你使用了 `4.20.0-alpha.0` `4.20.0-alpha.1`,请注意这个变化。[#35187](https://github.com/ant-design/ant-design/pull/35187) [@vagusX](https://github.com/vagusX)
- Form
- 🔥 Form 添加 `useWatch` 支持获取当前字段值。[#35036](https://github.com/ant-design/ant-design/pull/35036)
- 🆕 Form 支持 `useFormInstance` 以获取当前上下文中的 Form 实例。[#35039](https://github.com/ant-design/ant-design/pull/35039)
- 💄 修复 Form `labelCol={{ sm: 24 }}``wrapperCol={{ sm: 24 }}` 时样式错乱的问题。[#34907](https://github.com/ant-design/ant-design/pull/34907)
- 🛎 Menu 添加 `items` 数据化菜单项支持以为将来性能提升做准备,并且 `children` 将会在下个大版本中废弃。[#34559](https://github.com/ant-design/ant-design/pull/34559)
- 🆕 Image PreviewGroup 支持顶部进度渲染。[#35038](https://github.com/ant-design/ant-design/pull/35038) [@zpc7](https://github.com/zpc7)
- Upload
- 🆕 Upload `picture-card` 模式支持配置图片的 `crossOrigin` 属性。[#34981](https://github.com/ant-design/ant-design/pull/34981) [@dragmove](https://github.com/dragmove)
- 🐞 修复 Upload `prefixCls` 对列表不生效的问题。[#34944](https://github.com/ant-design/ant-design/pull/34944) [@swchen](https://github.com/swchen)
- 💄 优化 Upload 操作按钮的样式细节。[#35052](https://github.com/ant-design/ant-design/pull/35052)
- Table
- 🆕 Table 列筛选条件重置时,支持重置为默认值而非空值。[#34355](https://github.com/ant-design/ant-design/pull/34355) [@heiyu4585](https://github.com/heiyu4585)
- 💄 修复 Table `size="small"` 时列头背景色和选择列宽度的样式问题。[#34963](https://github.com/ant-design/ant-design/pull/34963)
- 🇩🇪 补全 Table 的德语国际化文案。[#34836](https://github.com/ant-design/ant-design/pull/34836) [@pfedan](https://github.com/pfedan)
- ⚡️ 优化 Table 过滤列表的计算性能。[#35064](https://github.com/ant-design/ant-design/pull/35064) [@nieyuyao](https://github.com/nieyuyao)
- 💄 优化 Table `size="small"``size="middle"` 时选择下拉菜单的边距样式。[#35173](https://github.com/ant-design/ant-design/pull/35173)
- Tree
- 🆕 Tree 组件的 `switcherIcon` 属性支持 render-prop。[#34470](https://github.com/ant-design/ant-design/pull/34470) [@zqran](https://github.com/zqran)
- 🆕 Tree 支持 `rootClassName` and `rootStyle`。[#34578](https://github.com/ant-design/ant-design/pull/34578)
- Breadcrumb
- 🐞 修复 Breadcrumb 抛出 `placement` 废弃警告的问题。[#35162](https://github.com/ant-design/ant-design/pull/35162)
- 🐞 修复 Breadcrumb 展示非预期的数字符号的样式问题。[#35123](https://github.com/ant-design/ant-design/pull/35123)
- ⌨️ 为 Breadcrumb 层次结构增加可访问性支持。[#34082](https://github.com/ant-design/ant-design/pull/34082) [@VladimirOtroshchenko](https://github.com/VladimirOtroshchenko)
- Anchor
- 🆕 Anchor `getCurrentAnchor` 参数中返回默认高亮项。[#34799](https://github.com/ant-design/ant-design/pull/34799)
- 🛠 重构 Anchor 为函数组件。[#35073](https://github.com/ant-design/ant-design/pull/35073) [@LongHaoo](https://github.com/LongHaoo)
- Cascader
- 🆕 Cascader 添加 `showCheckedStrategy` 用于配置回填方式。[#34568](https://github.com/ant-design/ant-design/pull/34568) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 修复 Cascader 的搜索结果未占满整个面板的问题。[#35019](https://github.com/ant-design/ant-design/pull/35019) [@boomler](https://github.com/boomler)
- 🆕 Typography 的 `onCopy` 方法支持获取点击事件对象。[#34655](https://github.com/ant-design/ant-design/pull/34655) [@yzwxk](https://github.com/yzwxk)
- 🆕 Grid 支持 `justify="space-evenly"`。[#34606](https://github.com/ant-design/ant-design/pull/34606) [@gp5251](https://github.com/gp5251)
- 🆕 Dialog 及 Image 支持 `rootClassName` 属性。[#34574](https://github.com/ant-design/ant-design/pull/34574) [@heiyu4585](https://github.com/heiyu4585)
- 🐞 修复 Skeleton 在没有 `children` 并设置 `loading` 为 false 时提示 `Nothing was returned from render` 的问题。[#34872](https://github.com/ant-design/ant-design/pull/34872) [@AlbertAZ1992](https://github.com/AlbertAZ1992)
- 💄 优化 Switch 禁用色以更好适应非白底背景。[#35103](https://github.com/ant-design/ant-design/pull/35103)
- 💄 移除 Tabs `overflow: hidden` 样式以修复 Select 和 sticky Table 在 Tabs 中的展现问题。[#35195](https://github.com/ant-design/ant-design/pull/35195)
- 💄 修正 Steps 在 RTL 模式下样式问题。[#35088](https://github.com/ant-design/ant-design/pull/35088) [@zpc7](https://github.com/zpc7)
- 💄 修复 Badge 在 RTL 模式下、独立使用时的动画效果。[#34899](https://github.com/ant-design/ant-design/pull/34899) [@hmz22](https://github.com/hmz22)
- 🛠 优化 Modal id 生成逻辑,以优化无障碍体验。[#35072](https://github.com/ant-design/ant-design/pull/35072)
- 🐞 修复 Select 和 AutoComplete 使用键盘向下滚动时行为异常的问题。[#35025](https://github.com/ant-design/ant-design/pull/35025)
- Spin
- 💄 修复 Spin 动画样式在 Parcel 解析异常的问题。[#35005](https://github.com/ant-design/ant-design/pull/35005)
- ⌨️ Spin 添加 `aria` 属性以提升可访问性。[#34408](https://github.com/ant-design/ant-design/pull/34408) [@heiyu4585](https://github.com/heiyu4585)
- ⌨️ Dropdown 支持方向键切换选项。[#34738](https://github.com/ant-design/ant-design/pull/34738)
- 🐞 修复 Title、Text、Paragraph 组件不支持 `ref` 的问题。[#34847](https://github.com/ant-design/ant-design/pull/34847) [@MQuy](https://github.com/MQuy)
- Input
- 💄 Input.Group 对子组件屏蔽 Form.Item 的样式。[#34764](https://github.com/ant-design/ant-design/pull/34764)
- 💄 调整 Form 下 TextArea 的样式。[#34714](https://github.com/ant-design/ant-design/pull/34714) [@MadCcc](https://github.com/MadCcc)
- ⌨️ 修复 Checkbox 缺少 `aria-checked` 属性导致屏幕阅读器识别错误的问题。[#34862](https://github.com/ant-design/ant-design/pull/34862) [@SpaNb4](https://github.com/SpaNb4)
- Less
- 💄 替换 less 中的 html 选择器为对应变量。[#35186](https://github.com/ant-design/ant-design/pull/35186) [@jeffdrumgod](https://github.com/jeffdrumgod)
- 💄 修改 less 中 `danger` 值从函数改为变量。[#35113](https://github.com/ant-design/ant-design/pull/35113) [@TrickyPi](https://github.com/TrickyPi)
- 🐞 箭头圆角使用固定值 2px 变量。[#35086](https://github.com/ant-design/ant-design/pull/35086) [@MadCcc](https://github.com/MadCcc)
- TypeScript
- 🤖 修正 Upload 组件中 `UploadChangeParam<T>` 内部 `fileList` 不使用泛型问题。[#35158](https://github.com/ant-design/ant-design/pull/35158) [@rendaoer](https://github.com/rendaoer)
- 🤖 更新 TypeScript 定义以兼容 `@types/react@18`。[#35075](https://github.com/ant-design/ant-design/pull/35075) [@AliRezaBeigy](https://github.com/AliRezaBeigy) [#35076](https://github.com/ant-design/ant-design/pull/35076) [@littledian](https://github.com/littledian)
## 4.19.5
`2022-04-02`

View File

@ -1,12 +0,0 @@
# CODEOWNERS syntax
# A CODEOWNERS file uses a pattern that follows the same rules used in gitignore files.
# The pattern is followed by one or more GitHub usernames or team names using the standard @username or @org/team-name format.
# You can also refer to a user by an email address that has been added to their GitHub account, for example user@example.com.
# no default file owner
#/* @afc163
/components/tree/* @zombieJ
/components/tree-select/* @zombieJ
# ...
# other components

View File

@ -12,7 +12,7 @@ Uma solução empresarial de design e biblioteca UI para React.
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
@ -22,10 +22,6 @@ Uma solução empresarial de design e biblioteca UI para React.
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
@ -44,6 +40,8 @@ Uma solução empresarial de design e biblioteca UI para React.
[unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css
[issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square
[issues-helper-url]: https://github.com/actions-cool/issues-helper
[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square
[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498
</div>

View File

@ -12,7 +12,7 @@
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
@ -22,10 +22,6 @@
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
@ -44,6 +40,8 @@
[unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css
[issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square
[issues-helper-url]: https://github.com/actions-cool/issues-helper
[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square
[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498
</div>

View File

@ -12,7 +12,7 @@
[![CI status][github-action-image]][github-action-url] [![codecov][codecov-image]][codecov-url] [![NPM version][npm-image]][npm-url] [![NPM downloads][download-image]][download-url]
[![david deps][david-image]][david-url] [![david devDeps][david-dev-image]][david-dev-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Renovate status][renovate-image]][renovate-dashboard-url] [![Total alerts][lgtm-image]][lgtm-url] [![][bundlesize-js-image]][unpkg-js-url] [![][bundlesize-css-image]][unpkg-css-url]
[![Follow Twitter][twitter-image]][twitter-url] [![FOSSA Status][fossa-image]][fossa-url] [![Discussions][discussions-image]][discussions-url] [![][issues-helper-image]][issues-helper-url] [![Issues need help][help-wanted-image]][help-wanted-url]
@ -22,10 +22,6 @@
[github-action-url]: https://github.com/ant-design/ant-design/actions?query=workflow%3A%22%E2%9C%85+test%22
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/ant-design/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/ant-design/branch/master
[david-image]: https://img.shields.io/david/ant-design/ant-design?style=flat-square
[david-dev-url]: https://david-dm.org/ant-design/ant-design?type=dev
[david-dev-image]: https://img.shields.io/david/dev/ant-design/ant-design?style=flat-square
[david-url]: https://david-dm.org/ant-design/ant-design
[download-image]: https://img.shields.io/npm/dm/antd.svg?style=flat-square
[download-url]: https://npmjs.org/package/antd
[lgtm-image]: https://flat.badgen.net/lgtm/alerts/g/ant-design/ant-design
@ -44,6 +40,8 @@
[unpkg-css-url]: https://unpkg.com/browse/antd/dist/antd.min.css
[issues-helper-image]: https://img.shields.io/badge/using-issues--helper-orange?style=flat-square
[issues-helper-url]: https://github.com/actions-cool/issues-helper
[renovate-image]: https://img.shields.io/badge/renovate-enabled-brightgreen.svg?style=flat-square
[renovate-dashboard-url]: https://github.com/ant-design/ant-design/issues/32498
</div>

View File

@ -1,8 +0,0 @@
import UnreachableException from '../unreachableException';
describe('UnreachableException', () => {
it('error thrown matches snapshot', () => {
const exception = new UnreachableException('some value');
expect(exception.error.message).toMatchInlineSnapshot(`"unreachable case: \\"some value\\""`);
});
});

View File

@ -148,7 +148,9 @@ describe('Test utils function', () => {
button
</button>
</Wave>,
).instance();
)
.find(Wave)
.instance();
expect(wrapper.bindAnimationEvent()).toBe(undefined);
});
@ -159,7 +161,9 @@ describe('Test utils function', () => {
button
</button>
</Wave>,
).instance();
)
.find(Wave)
.instance();
expect(wrapper.bindAnimationEvent()).toBe(undefined);
});
@ -168,7 +172,9 @@ describe('Test utils function', () => {
<Wave>
<input />
</Wave>,
).instance();
)
.find(Wave)
.instance();
expect(wrapper.bindAnimationEvent()).toBe(undefined);
});

View File

@ -4,4 +4,9 @@ export { resetWarned };
export default (valid: boolean, component: string, message: string): void => {
devWarning(valid, `[antd: ${component}] ${message}`);
// StrictMode will inject console which will not throw warning in React 17.
if (process.env.NODE_ENV === 'test') {
resetWarned();
}
};

View File

@ -3,20 +3,9 @@ import { CSSInterpolation, Theme, useCacheToken, useStyleRegister } from '@ant-d
import genComponentStyleHook from './util/genComponentStyleHook';
import defaultSeedToken, { derivative as defaultDerivative } from './themes/default';
import version from '../../version';
import { resetComponent, resetIcon, clearFix, roundedArrow } from './util';
import { resetComponent, resetIcon, clearFix, roundedArrow, operationUnit } from './util';
import formatToken from './util/alias';
import {
initSlideMotion,
slideUpIn,
slideUpOut,
slideDownIn,
slideDownOut,
slideLeftIn,
slideLeftOut,
slideRightIn,
slideRightOut,
} from './util/slide';
import statisticToken, { merge as mergeToken } from './util/statistic';
import statisticToken, { merge as mergeToken, statistic } from './util/statistic';
import { GlobalToken, PresetColors } from './interface';
import type {
SeedToken,
@ -28,25 +17,20 @@ import type {
import type { FullToken } from './util/genComponentStyleHook';
export {
PresetColors,
// css utils
resetComponent,
resetIcon,
clearFix,
roundedArrow,
initSlideMotion,
slideUpIn,
slideUpOut,
slideDownIn,
slideDownOut,
slideLeftIn,
slideLeftOut,
slideRightIn,
slideRightOut,
useStyleRegister,
operationUnit,
// colors
PresetColors,
// Statistic
statistic,
statisticToken,
mergeToken,
// GenComponentStyleHook
// hooks
useStyleRegister,
genComponentStyleHook,
};
@ -73,6 +57,11 @@ export const DesignTokenContext = React.createContext<{
});
// ================================== Hook ==================================
// In dev env, we refresh salt per hour to avoid user use this
// Note: Do not modify this to real time update which will make debug harder
const saltPrefix =
process.env.NODE_ENV === 'production' ? version : `${version}-${new Date().getHours()}`;
export function useToken(): [Theme<SeedToken, DerivativeToken>, GlobalToken, string] {
const {
token: rootDesignToken,
@ -81,7 +70,7 @@ export function useToken(): [Theme<SeedToken, DerivativeToken>, GlobalToken, str
hashed,
} = React.useContext(DesignTokenContext);
const salt = `${version}-${hashed || ''}`;
const salt = `${saltPrefix}-${hashed || ''}`;
const [token, hashId] = useCacheToken<GlobalToken, SeedToken>(
theme,
@ -100,5 +89,4 @@ export type UseComponentStyleResult = [(node: React.ReactNode) => React.ReactEle
export type GenerateStyle<ComponentToken extends object, ReturnType = CSSInterpolation> = (
token: ComponentToken,
hashId?: string,
) => ReturnType;

View File

@ -1,6 +1,8 @@
import type * as React from 'react';
import type { ComponentToken as AnchorComponentToken } from '../../anchor/style';
import type { ComponentToken as ButtonComponentToken } from '../../button/style';
import type { ComponentToken as DividerComponentToken } from '../../divider/style';
import type { ComponentToken as DropdownComponentToken } from '../../dropdown/style';
import type { ComponentToken as EmptyComponentToken } from '../../empty/style';
import type { ComponentToken as CascaderComponentToken } from '../../cascader/style';
import type { ComponentToken as InputNumberComponentToken } from '../../input-number/style';
@ -8,7 +10,13 @@ import type { ComponentToken as MentionsComponentToken } from '../../mentions/st
import type { ComponentToken as SelectComponentToken } from '../../select/style';
import type { ComponentToken as SliderComponentToken } from '../../slider/style';
import type { ComponentToken as TypographyComponentToken } from '../../typography/style';
import type { ComponentToken as BackTopComponentToken } from '../../back-top/style';
import type { ComponentToken as DatePickerComponentToken } from '../../date-picker/style';
import type { ComponentToken as TimelineComponentToken } from '../../timeline/style';
import type { ComponentToken as MenuComponentToken } from '../../menu/style';
import type { ComponentToken as UploadComponentToken } from '../../upload/style';
import type { ComponentToken as CarouselComponentToken } from '../../carousel/style';
import type { ComponentToken as SpaceComponentToken } from '../../space/style';
export const PresetColors = [
'blue',
@ -42,15 +50,20 @@ export interface OverrideToken {
// Customize component
Affix?: {};
Alert?: {};
Anchor?: AnchorComponentToken;
Avatar?: {};
BackTop?: BackTopComponentToken;
Badge?: {};
Button?: ButtonComponentToken;
Carousel?: {};
Carousel?: CarouselComponentToken;
Cascader?: CascaderComponentToken;
Checkbox?: {};
Collapse?: {};
DatePicker?: DatePickerComponentToken;
Descriptions?: {};
Divider?: DividerComponentToken;
Drawer?: {};
Dropdown?: DropdownComponentToken;
Empty?: EmptyComponentToken;
Form?: {};
Grid?: {};
@ -75,6 +88,13 @@ export interface OverrideToken {
Typography?: TypographyComponentToken;
Timeline?: TimelineComponentToken;
Tabs?: {};
Card?: {};
Steps?: {};
Menu?: MenuComponentToken;
Layout?: {};
Upload?: UploadComponentToken;
Tooltip?: {};
Space?: SpaceComponentToken;
}
/** Final token which contains the components level override */
@ -123,6 +143,7 @@ export interface SeedToken extends PresetColorType {
// Size
sizeUnit: number;
sizeBaseStep: number;
sizePopupArrow: number;
// Control Base
controlHeight: number;
@ -287,6 +308,8 @@ export interface AliasToken extends Omit<DerivativeToken, OmitDerivativeKey> {
colorBgComponentDisabled: string;
// =============== Legacy: should be remove ===============
colorLoadingOpacity: number;
padding: number;
margin: number;

View File

@ -73,9 +73,9 @@ export function derivative(token: SeedToken): DerivativeToken {
...colorPalettes,
// motion
motionDurationFast: `${motionBase + motionUnit * 1}s`,
motionDurationMid: `${motionBase + motionUnit * 2}s`,
motionDurationSlow: `${motionBase + motionUnit * 3}s`,
motionDurationFast: `${(motionBase + motionUnit * 1).toFixed(1)}s`,
motionDurationMid: `${(motionBase + motionUnit * 2).toFixed(1)}s`,
motionDurationSlow: `${(motionBase + motionUnit * 3).toFixed(1)}s`,
// font
fontSizes: fontSizes.map(fs => fs.size),
@ -189,6 +189,7 @@ const seedToken: SeedToken = {
// Size
sizeUnit: 4,
sizeBaseStep: 4,
sizePopupArrow: 8 * Math.sqrt(2),
// Control Base
controlHeight: 32,

View File

@ -99,6 +99,8 @@ export default function formatToken(derivativeToken: RawMergedToken): AliasToken
// warningColors,
// errorColors,
colorLoadingOpacity: 0.65,
colorSuccessSecondary: successColors[2],
colorWarningSecondary: warningColors[2],
colorErrorSecondary: errorColors[2],

View File

@ -9,16 +9,22 @@ export type OverrideTokenWithoutDerivative = Omit<OverrideToken, 'derivative'>;
export type OverrideComponent = keyof OverrideTokenWithoutDerivative;
export type GlobalTokenWithComponent<ComponentName extends OverrideComponent> = GlobalToken &
OverrideToken[ComponentName];
export type StyleInfo = {
export interface StyleInfo {
hashId: string;
prefixCls: string;
rootPrefixCls: string;
iconPrefixCls: string;
};
}
export type TokenWithCommonCls<T> = T & {
/** Wrap component class with `.` prefix */
componentCls: string;
/** Origin prefix which do not have `.` prefix */
prefixCls: string;
/** Wrap icon class with `.` prefix */
iconCls: string;
/** Wrap ant prefixCls class with `.` prefix */
antCls: string;
};
export type FullToken<ComponentName extends OverrideComponent> = TokenWithCommonCls<
@ -38,7 +44,7 @@ function genComponentStyleHook<ComponentName extends OverrideComponent>(
const rootPrefixCls = getPrefixCls();
return [
useStyleRegister({ theme, token, hashId, path: [prefixCls] }, () => {
useStyleRegister({ theme, token, hashId, path: [component, prefixCls] }, () => {
const { token: proxyToken, flush } = statisticToken(token);
const defaultComponentToken =

View File

@ -2,6 +2,7 @@
import { CSSObject } from '@ant-design/cssinjs';
import type { DerivativeToken } from '..';
export { operationUnit } from './operationUnit';
export { roundedArrow } from './roundedArrow';
export const resetComponent = (token: DerivativeToken): CSSObject => ({

View File

@ -20,7 +20,7 @@ export const roundedArrow = (width: number, outerRadius: number, bgColor: string
const ey = fy + outerRadius * (1 / Math.sqrt(2));
return {
borderRadius: `0 0 ${radiusBase}px 0`,
borderRadius: { _skip_check_: true, value: `0 0 2px` },
pointerEvents: 'none',
'&::before': {

View File

@ -1,7 +0,0 @@
export default class UnreachableException {
error: Error;
constructor(value: never) {
this.error = new Error(`unreachable case: ${JSON.stringify(value)}`);
}
}

View File

@ -5,7 +5,7 @@ import { getObserverEntities } from '../utils';
import Button from '../../button';
import rtlTest from '../../../tests/shared/rtlTest';
import accessibilityTest from '../../../tests/shared/accessibilityTest';
import { sleep } from '../../../tests/utils';
import { sleep, render } from '../../../tests/utils';
const events: Partial<Record<keyof HTMLElementEventMap, (ev: Partial<Event>) => void>> = {};
@ -97,57 +97,47 @@ describe('Affix Render', () => {
};
it('Anchor render perfectly', async () => {
document.body.innerHTML = '<div id="mounter" />';
affixMounterWrapper = mount(<AffixMounter />, { attachTo: document.getElementById('mounter') });
const { container } = render(<AffixMounter />);
await sleep(20);
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
expect(container.querySelector('.ant-affix')).toBeFalsy();
await movePlaceholder(-100);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
expect(container.querySelector('.ant-affix')).toBeTruthy();
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
expect(container.querySelector('.ant-affix')).toBeFalsy();
});
it('support offsetBottom', async () => {
document.body.innerHTML = '<div id="mounter" />';
affixMounterWrapper = mount(<AffixMounter offsetBottom={0} />, {
attachTo: document.getElementById('mounter'),
});
const { container } = render(<AffixMounter offsetBottom={0} />);
await sleep(20);
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
expect(container.querySelector('.ant-affix')).toBeTruthy();
await movePlaceholder(0);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeFalsy();
expect(container.querySelector('.ant-affix')).toBeFalsy();
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
expect(container.querySelector('.ant-affix')).toBeTruthy();
});
it('updatePosition when offsetTop changed', async () => {
document.body.innerHTML = '<div id="mounter" />';
const onChange = jest.fn();
affixMounterWrapper = mount(<AffixMounter offsetTop={0} onChange={onChange} />, {
attachTo: document.getElementById('mounter'),
});
const { container, rerender } = render(<AffixMounter offsetTop={0} onChange={onChange} />);
await sleep(20);
await movePlaceholder(-100);
expect(onChange).toHaveBeenLastCalledWith(true);
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(0);
affixMounterWrapper.setProps({
offsetTop: 10,
});
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: 0 });
rerender(<AffixMounter offsetTop={10} onChange={onChange} />);
await sleep(20);
expect(affixMounterWrapper.instance().affix.state.affixStyle?.top).toBe(10);
expect(container.querySelector('.ant-affix')).toHaveStyle({ top: `10px` });
});
describe('updatePosition when target changed', () => {
@ -201,7 +191,9 @@ describe('Affix Render', () => {
await sleep(20);
await movePlaceholder(300);
expect(affixMounterWrapper.instance().affix.state.affixStyle).toBeTruthy();
expect(
(affixMounterWrapper.find(AffixMounter).instance() as any).affix.state.affixStyle,
).toBeTruthy();
await sleep(20);
affixMounterWrapper.update();

View File

@ -38,5 +38,5 @@ const Demo: React.FC = () => {
);
};
export default () => <Demo />;
export default Demo;
```

View File

@ -35,5 +35,5 @@ const Demo: React.FC = () => {
);
};
export default () => <Demo />;
export default Demo;
```

View File

@ -30,7 +30,7 @@ const Demo: React.FC = () => {
);
};
export default () => <Demo />;
export default Demo;
```
<style>

View File

@ -32,5 +32,5 @@ const App: React.FC = () => {
);
};
export default () => <App />;
export default App;
```

View File

@ -3,12 +3,13 @@ import classNames from 'classnames';
import memoizeOne from 'memoize-one';
import addEventListener from 'rc-util/lib/Dom/addEventListener';
import Affix from '../affix';
import AnchorLink from './AnchorLink';
import { ConfigContext, ConfigConsumerProps } from '../config-provider';
import scrollTo from '../_util/scrollTo';
import getScroll from '../_util/getScroll';
import AnchorContext from './context';
import useStyle from './style';
export type AnchorContainer = HTMLElement | Window;
function getDefaultContainer() {
@ -62,6 +63,11 @@ export interface AnchorProps {
onChange?: (currentActiveLink: string) => void;
}
interface InternalAnchorProps extends AnchorProps {
anchorPrefixCls: string;
rootClassName: string;
}
export interface AnchorState {
activeLink: null | string;
}
@ -84,9 +90,7 @@ export interface AntAnchor {
) => void;
}
export default class Anchor extends React.Component<AnchorProps, AnchorState, ConfigConsumerProps> {
static Link: typeof AnchorLink;
class Anchor extends React.Component<InternalAnchorProps, AnchorState, ConfigConsumerProps> {
static defaultProps = {
affix: true,
showInkInFixed: false,
@ -268,9 +272,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
);
render() {
const { getPrefixCls, direction } = this.context;
const { direction } = this.context;
const {
prefixCls: customizePrefixCls,
anchorPrefixCls: prefixCls,
className = '',
style,
offsetTop,
@ -278,11 +282,10 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
showInkInFixed,
children,
onClick,
rootClassName,
} = this.props;
const { activeLink } = this.state;
const prefixCls = getPrefixCls('anchor', customizePrefixCls);
// To support old version react.
// Have to add prefixCls on the instance.
// https://github.com/facebook/react/issues/12397
@ -293,6 +296,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
});
const wrapperClass = classNames(
rootClassName,
`${prefixCls}-wrapper`,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
@ -335,3 +339,25 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState, Co
);
}
}
// just use in test
export type InternalAnchorClass = Anchor;
const AnchorFC = React.forwardRef<Anchor, AnchorProps>((props, ref) => {
const { prefixCls: customizePrefixCls } = props;
const { getPrefixCls } = React.useContext(ConfigContext);
const anchorPrefixCls = getPrefixCls('anchor', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(anchorPrefixCls);
const anchorProps: InternalAnchorProps = {
...props,
anchorPrefixCls,
rootClassName: hashId,
};
return wrapSSR(<Anchor {...anchorProps} ref={ref} />);
});
export default AnchorFC;

View File

@ -1,7 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import Anchor from '..';
import { sleep } from '../../../tests/utils';
import type { InternalAnchorClass } from '../Anchor';
import { sleep, render, fireEvent } from '../../../tests/utils';
const { Link } = Anchor;
@ -47,41 +47,54 @@ describe('Anchor Render', () => {
it('Anchor render perfectly', () => {
const hash = getHashUrl();
const wrapper = mount<Anchor>(
<Anchor>
let anchorInstance: InternalAnchorClass;
const { container } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.find(`a[href="#${hash}"]`).simulate('click');
wrapper.instance().handleScroll();
expect(wrapper.instance().state).not.toBe(null);
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
anchorInstance!.handleScroll();
expect(anchorInstance!.state).not.toBe(null);
});
it('Anchor render perfectly for complete href - click', () => {
const hash = getHashUrl();
const wrapper = mount<Anchor>(
<Anchor>
let anchorInstance: InternalAnchorClass;
const { container } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`http://www.example.com/#${hash}`} title={hash} />
</Anchor>,
);
wrapper.find(`a[href="http://www.example.com/#${hash}"]`).simulate('click');
expect(wrapper.instance().state.activeLink).toBe(`http://www.example.com/#${hash}`);
fireEvent.click(container.querySelector(`a[href="http://www.example.com/#${hash}"]`)!);
expect(anchorInstance!.state!.activeLink).toBe(`http://www.example.com/#${hash}`);
});
it('Anchor render perfectly for complete href - hash router', async () => {
const root = createDiv();
const scrollToSpy = jest.spyOn(window, 'scrollTo');
mount(<div id="/faq?locale=en#Q1">Q1</div>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<div id="/faq?locale=en#Q1">Q1</div>, { container: root });
let anchorInstance: InternalAnchorClass;
render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href="/#/faq?locale=en#Q1" title="Q1" />
</Anchor>,
);
wrapper.instance().handleScrollTo('/#/faq?locale=en#Q1');
expect(wrapper.instance().state.activeLink).toBe('/#/faq?locale=en#Q1');
anchorInstance!.handleScrollTo('/#/faq?locale=en#Q1');
expect(anchorInstance!.state.activeLink).toBe('/#/faq?locale=en#Q1');
expect(scrollToSpy).not.toHaveBeenCalled();
await sleep(1000);
expect(scrollToSpy).toHaveBeenCalled();
@ -90,28 +103,39 @@ describe('Anchor Render', () => {
it('Anchor render perfectly for complete href - scroll', () => {
const hash = getHashUrl();
const root = createDiv();
mount(<div id={hash}>Hello</div>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<div id={hash}>Hello</div>, { container: root });
let anchorInstance: InternalAnchorClass;
render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`http://www.example.com/#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScroll();
expect(wrapper.instance().state.activeLink).toBe(`http://www.example.com/#${hash}`);
anchorInstance!.handleScroll();
expect(anchorInstance!.state!.activeLink).toBe(`http://www.example.com/#${hash}`);
});
it('Anchor render perfectly for complete href - scrollTo', async () => {
const hash = getHashUrl();
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<div id={`#${hash}`}>Hello</div>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<div id={`#${hash}`}>Hello</div>, { container: root });
let anchorInstance: InternalAnchorClass;
render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`##${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`##${hash}`);
expect(wrapper.instance().state.activeLink).toBe(`##${hash}`);
anchorInstance!.handleScrollTo(`##${hash}`);
expect(anchorInstance!.state.activeLink).toBe(`##${hash}`);
const calls = scrollToSpy.mock.calls.length;
await sleep(1000);
expect(scrollToSpy.mock.calls.length).toBeGreaterThan(calls);
@ -119,50 +143,60 @@ describe('Anchor Render', () => {
it('should remove listener when unmount', async () => {
const hash = getHashUrl();
const wrapper = mount<Anchor>(
<Anchor>
let anchorInstance: InternalAnchorClass;
const { unmount } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
wrapper.unmount();
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
unmount();
expect(removeListenerSpy).toHaveBeenCalled();
});
it('should unregister link when unmount children', async () => {
it('should unregister link when unmount children', () => {
const hash = getHashUrl();
const wrapper = mount<Anchor>(
const { container, rerender } = render(
<Anchor>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
expect((wrapper.instance() as any).links).toEqual([`#${hash}`]);
wrapper.setProps({ children: null });
expect((wrapper.instance() as any).links).toEqual([]);
expect(container.querySelectorAll('.ant-anchor-link-title')).toHaveLength(1);
expect(container.querySelector('.ant-anchor-link-title')).toHaveAttribute('href', `#${hash}`);
rerender(<Anchor />);
expect(container.querySelector('.ant-anchor-link-title')).toBeFalsy();
});
it('should update links when link href update', async () => {
const hash = getHashUrl();
let anchorInstance: Anchor | null = null;
let anchorInstance: InternalAnchorClass;
function AnchorUpdate({ href }: { href: string }) {
return (
<Anchor
ref={c => {
anchorInstance = c;
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={href} title={hash} />
</Anchor>
);
}
const wrapper = mount(<AnchorUpdate href={`#${hash}`} />);
const { rerender } = render(<AnchorUpdate href={`#${hash}`} />);
if (anchorInstance == null) {
if (anchorInstance! == null) {
throw new Error('anchorInstance should not be null');
}
expect((anchorInstance as any).links).toEqual([`#${hash}`]);
wrapper.setProps({ href: `#${hash}_1` });
expect((anchorInstance as any).links).toEqual([`#${hash}_1`]);
expect((anchorInstance as any)!.links).toEqual([`#${hash}`]);
rerender(<AnchorUpdate href={`#${hash}_1`} />);
expect((anchorInstance as any)!.links).toEqual([`#${hash}_1`]);
});
it('Anchor onClick event', () => {
@ -179,16 +213,20 @@ describe('Anchor Render', () => {
const href = `#${hash}`;
const title = hash;
const wrapper = mount<Anchor>(
<Anchor onClick={handleClick}>
let anchorInstance: InternalAnchorClass;
const { container } = render(
<Anchor
onClick={handleClick}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={href} title={title} />
</Anchor>,
);
wrapper.find(`a[href="${href}"]`).simulate('click');
wrapper.instance().handleScroll();
fireEvent.click(container.querySelector(`a[href="${href}"]`)!);
anchorInstance!.handleScroll();
expect(event).not.toBe(undefined);
expect(link).toEqual({ href, title });
});
@ -196,18 +234,28 @@ describe('Anchor Render', () => {
it('Different function returns the same DOM', async () => {
const hash = getHashUrl();
const root = createDiv();
mount(<div id={hash}>Hello</div>, { attachTo: root });
render(<div id={hash}>Hello</div>, { container: root });
const getContainerA = createGetContainer(hash);
const getContainerB = createGetContainer(hash);
const wrapper = mount<Anchor>(
<Anchor getContainer={getContainerA}>
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
getContainer={getContainerA}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
rerender(
<Anchor getContainer={getContainerB}>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
expect(removeListenerSpy).not.toHaveBeenCalled();
});
@ -215,53 +263,73 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const root = createDiv();
mount(
render(
<div>
<div id={hash1}>Hello</div>
<div id={hash2}>World</div>
</div>,
{ attachTo: root },
{ container: root },
);
const getContainerA = createGetContainer(hash1);
const getContainerB = createGetContainer(hash2);
const wrapper = mount<Anchor>(
<Anchor getContainer={getContainerA}>
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
getContainer={getContainerA}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await sleep(1000);
wrapper.setProps({ getContainer: getContainerB });
rerender(
<Anchor getContainer={getContainerB}>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
expect(removeListenerSpy).toHaveBeenCalled();
});
it('Same function returns the same DOM', () => {
const hash = getHashUrl();
const root = createDiv();
mount(<div id={hash}>Hello</div>, { attachTo: root });
render(<div id={hash}>Hello</div>, { container: root });
const getContainer = createGetContainer(hash);
const wrapper = mount(
<Anchor getContainer={getContainer}>
let anchorInstance: InternalAnchorClass;
const { container } = render(
<Anchor
getContainer={getContainer}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.find(`a[href="#${hash}"]`).simulate('click');
(wrapper.instance() as any).handleScroll();
expect(wrapper.instance().state).not.toBe(null);
fireEvent.click(container.querySelector(`a[href="#${hash}"]`)!);
anchorInstance!.handleScroll();
expect(anchorInstance!.state).not.toBe(null);
});
it('Same function returns different DOM', async () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const root = createDiv();
mount(
render(
<div>
<div id={hash1}>Hello</div>
<div id={hash2}>World</div>
</div>,
{ attachTo: root },
{ container: root },
);
const holdContainer = {
container: document.getElementById(hash1),
@ -272,17 +340,28 @@ describe('Anchor Render', () => {
}
return holdContainer.container;
};
const wrapper = mount(
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
getContainer={getContainer}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
const removeListenerSpy = jest.spyOn((anchorInstance! as any).scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await sleep(1000);
holdContainer.container = document.getElementById(hash2);
rerender(
<Anchor getContainer={getContainer}>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
const removeListenerSpy = jest.spyOn((wrapper.instance() as any).scrollEvent, 'remove');
expect(removeListenerSpy).not.toHaveBeenCalled();
await sleep(1000);
holdContainer.container = document.getElementById(hash2);
wrapper.setProps({ 'data-only-trigger-re-render': true });
expect(removeListenerSpy).toHaveBeenCalled();
});
@ -305,25 +384,45 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<h1 id={hash}>Hello</h1>, { container: root });
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`#${hash}`);
const setProps = (props: Record<string, any>) =>
rerender(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
{...props}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ offsetTop: 100 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ targetOffset: 200 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -350,25 +449,43 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<h1 id={hash}>Hello</h1>, { container: root });
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`#${hash}`);
const setProps = (props: Record<string, any>) =>
rerender(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
{...props}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ offsetTop: 100 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ targetOffset: 200 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -379,29 +496,44 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const onChange = jest.fn();
const wrapper = mount<Anchor>(
<Anchor onChange={onChange}>
let anchorInstance: InternalAnchorClass;
render(
<Anchor
onChange={onChange}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
// @ts-ignore
{ legacyRoot: true },
);
expect(onChange).toHaveBeenCalledTimes(1);
wrapper.instance().handleScrollTo(hash2);
anchorInstance!.handleScrollTo(hash2);
expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith(hash2);
});
it('invalid hash', async () => {
const wrapper = mount<Anchor>(
<Anchor>
let anchorInstance: InternalAnchorClass;
const { container } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href="notexsited" title="title" />
</Anchor>,
);
wrapper.find(`a[href="notexsited"]`).simulate('click');
fireEvent.click(container.querySelector(`a[href="notexsited"]`)!);
wrapper.instance().handleScrollTo('notexsited');
expect(wrapper.instance().state).not.toBe(null);
anchorInstance!.handleScrollTo('notexsited');
expect(anchorInstance!.state).not.toBe(null);
});
it('test edge case when getBoundingClientRect return zero size', async () => {
@ -428,25 +560,42 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor>
render(<h1 id={hash}>Hello</h1>, { container: root });
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`#${hash}`);
const setProps = (props: Record<string, any>) =>
rerender(
<Anchor
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
{...props}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 1000);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ offsetTop: 100 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 900);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ targetOffset: 200 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -477,25 +626,44 @@ describe('Anchor Render', () => {
const scrollToSpy = jest.spyOn(window, 'scrollTo');
const root = createDiv();
mount(<h1 id={hash}>Hello</h1>, { attachTo: root });
const wrapper = mount<Anchor>(
<Anchor getContainer={() => document.body}>
render(<h1 id={hash}>Hello</h1>, { container: root });
let anchorInstance: InternalAnchorClass;
const { rerender } = render(
<Anchor
getContainer={() => document.body}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
wrapper.instance().handleScrollTo(`#${hash}`);
const setProps = (props: Record<string, any>) =>
rerender(
<Anchor
getContainer={() => document.body}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
{...props}
>
<Link href={`#${hash}`} title={hash} />
</Anchor>,
);
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock = dataNowMockFn();
wrapper.setProps({ offsetTop: 100 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ offsetTop: 100 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
dateNowMock = dataNowMockFn();
wrapper.setProps({ targetOffset: 200 });
wrapper.instance().handleScrollTo(`#${hash}`);
setProps({ targetOffset: 200 });
anchorInstance!.handleScrollTo(`#${hash}`);
await sleep(30);
expect(scrollToSpy).toHaveBeenLastCalledWith(0, 800);
@ -507,13 +675,20 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const getCurrentAnchor = () => `#${hash2}`;
const wrapper = mount<Anchor>(
<Anchor getCurrentAnchor={getCurrentAnchor}>
let anchorInstance: InternalAnchorClass;
render(
<Anchor
getCurrentAnchor={getCurrentAnchor}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
expect(wrapper.instance().state.activeLink).toBe(`#${hash2}`);
expect(anchorInstance!.state.activeLink).toBe(`#${hash2}`);
});
// https://github.com/ant-design/ant-design/issues/30584
@ -521,14 +696,25 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const onChange = jest.fn();
const wrapper = mount<Anchor>(
<Anchor onChange={onChange} getCurrentAnchor={() => hash1}>
let anchorInstance: InternalAnchorClass;
render(
<Anchor
onChange={onChange}
getCurrentAnchor={() => hash1}
ref={node => {
anchorInstance = node as InternalAnchorClass;
}}
>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
// https://github.com/testing-library/react-testing-library/releases/tag/v13.0.0
// @ts-ignore
{ legacyRoot: true },
);
expect(onChange).toHaveBeenCalledTimes(1);
wrapper.instance().handleScrollTo(hash2);
anchorInstance!.handleScrollTo(hash2);
expect(onChange).toHaveBeenCalledTimes(2);
expect(onChange).toHaveBeenCalledWith(hash2);
});
@ -538,16 +724,16 @@ describe('Anchor Render', () => {
const hash1 = getHashUrl();
const hash2 = getHashUrl();
const getCurrentAnchor = jest.fn();
const wrapper = mount<Anchor>(
const { container } = render(
<Anchor getCurrentAnchor={getCurrentAnchor}>
<Link href={`#${hash1}`} title={hash1} />
<Link href={`#${hash2}`} title={hash2} />
</Anchor>,
);
wrapper.find(`a[href="#${hash1}"]`).simulate('click');
fireEvent.click(container.querySelector(`a[href="#${hash1}"]`)!);
expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash1}`);
wrapper.find(`a[href="#${hash2}"]`).simulate('click');
fireEvent.click(container.querySelector(`a[href="#${hash2}"]`)!);
expect(getCurrentAnchor).toHaveBeenCalledWith(`#${hash2}`);
});
});

View File

@ -1,7 +1,7 @@
import React, { memo, useState, useRef, useContext } from 'react';
import { mount } from 'enzyme';
import Anchor from '../Anchor';
import AnchorContext from '../context';
import { getNodeText, render, fireEvent } from '../../../tests/utils';
// we use'memo' here in order to only render inner component while context changed.
const CacheInner = memo(() => {
@ -38,14 +38,16 @@ const CacheOuter = () => {
};
it("Rendering on Anchor without changed AnchorContext won't trigger rendering on child component.", () => {
const wrapper = mount(<CacheOuter />);
const childCount = wrapper.find('#child_count').text();
wrapper.find('#parent_btn').at(0).simulate('click');
expect(wrapper.find('#parent_count').text()).toBe('2');
const { container } = render(<CacheOuter />);
const childCount = getNodeText(container.querySelector('#child_count')!);
fireEvent.click(container.querySelector('#parent_btn')!);
expect(getNodeText(container.querySelector('#parent_count')!)).toBe('2');
// child component won't rerender
expect(wrapper.find('#child_count').text()).toBe(childCount);
wrapper.find('#parent_btn').at(0).simulate('click');
expect(wrapper.find('#parent_count').text()).toBe('3');
expect(getNodeText(container.querySelector('#child_count')!)).toBe(childCount);
fireEvent.click(container.querySelector('#parent_btn')!);
expect(getNodeText(container.querySelector('#parent_count')!)).toBe('3');
// child component won't rerender
expect(wrapper.find('#child_count').text()).toBe(childCount);
expect(getNodeText(container.querySelector('#child_count')!)).toBe(childCount);
});

View File

@ -1,8 +1,16 @@
import Anchor from './Anchor';
import InternalAnchor from './Anchor';
import AnchorLink from './AnchorLink';
export { AnchorProps } from './Anchor';
export { AnchorLinkProps } from './AnchorLink';
type InternalAnchorType = typeof InternalAnchor;
interface AnchorInterface extends InternalAnchorType {
Link: typeof AnchorLink;
}
const Anchor = InternalAnchor as AnchorInterface;
Anchor.Link = AnchorLink;
export default Anchor;

View File

@ -1,87 +1,87 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
// @import '../../style/themes/index';
// @import '../../style/mixins/index';
@anchor-border-width: 2px;
// @anchor-border-width: 2px;
.@{ant-prefix}-anchor {
.reset-component();
// .@{ant-prefix}-anchor {
// .reset-component();
position: relative;
padding-left: @anchor-border-width;
// position: relative;
// padding-left: @anchor-border-width;
&-wrapper {
margin-left: -4px;
padding-left: 4px;
overflow: auto;
background-color: @anchor-bg;
}
// &-wrapper {
// margin-left: -4px;
// padding-left: 4px;
// overflow: auto;
// background-color: @anchor-bg;
// }
&-ink {
position: absolute;
top: 0;
left: 0;
height: 100%;
// &-ink {
// position: absolute;
// top: 0;
// left: 0;
// height: 100%;
&::before {
position: relative;
display: block;
width: @anchor-border-width;
height: 100%;
margin: 0 auto;
background-color: @anchor-border-color;
content: ' ';
}
// &::before {
// position: relative;
// display: block;
// width: @anchor-border-width;
// height: 100%;
// margin: 0 auto;
// background-color: @anchor-border-color;
// content: ' ';
// }
&-ball {
position: absolute;
left: 50%;
display: none;
width: 8px;
height: 8px;
background-color: @component-background;
border: 2px solid @primary-color;
border-radius: 8px;
transform: translateX(-50%);
transition: top 0.3s ease-in-out;
// &-ball {
// position: absolute;
// left: 50%;
// display: none;
// width: 8px;
// height: 8px;
// background-color: @component-background;
// border: 2px solid @primary-color;
// border-radius: 8px;
// transform: translateX(-50%);
// transition: top 0.3s ease-in-out;
&.visible {
display: inline-block;
}
}
}
// &.visible {
// display: inline-block;
// }
// }
// }
&-fixed &-ink &-ink-ball {
display: none;
}
// &-fixed &-ink &-ink-ball {
// display: none;
// }
&-link {
padding: @anchor-link-padding;
line-height: 1.143;
// &-link {
// padding: @anchor-link-padding;
// line-height: 1.143;
&-title {
position: relative;
display: block;
margin-bottom: 6px;
overflow: hidden;
color: @text-color;
white-space: nowrap;
text-overflow: ellipsis;
transition: all 0.3s;
// &-title {
// position: relative;
// display: block;
// margin-bottom: 6px;
// overflow: hidden;
// color: @text-color;
// white-space: nowrap;
// text-overflow: ellipsis;
// transition: all 0.3s;
&:only-child {
margin-bottom: 0;
}
}
// &:only-child {
// margin-bottom: 0;
// }
// }
&-active > &-title {
color: @primary-color;
}
}
// &-active > &-title {
// color: @primary-color;
// }
// }
&-link &-link {
padding-top: 5px;
padding-bottom: 5px;
}
}
// &-link &-link {
// padding-top: 5px;
// padding-bottom: 5px;
// }
// }
@import './rtl';
// @import './rtl';

View File

@ -1,5 +1,124 @@
import '../../style/index.less';
import './index.less';
// deps-lint-skip-all
import { CSSObject } from '@ant-design/cssinjs';
import { genComponentStyleHook, mergeToken, resetComponent } from '../../_util/theme';
import type { GenerateStyle, FullToken } from '../../_util/theme';
// style dependencies
import '../../affix/style';
export interface ComponentToken {}
interface AnchorToken extends FullToken<'Anchor'> {
anchorLinkTop: number;
anchorLinkLeft: number;
}
// ============================== Shared ==============================
const genSharedAnchorStyle: GenerateStyle<AnchorToken> = (token): CSSObject => {
const { componentCls } = token;
return {
[`${componentCls}-wrapper`]: {
// FIX ME
marginBlockStart: -4,
// FIX ME
paddingBlockStart: 4,
// delete overflow: auto
// overflow: 'auto',
// @anchor-bg
backgroundColor: 'transparent',
[componentCls]: {
...resetComponent(token),
position: 'relative',
// FIX ME @anchor-border-width
paddingInlineStart: 2,
[`${componentCls}-ink`]: {
position: 'absolute',
// top: 0
insetBlockStart: 0,
// left: 0
insetInlineStart: 0,
height: '100%',
'&::before': {
position: 'relative',
display: 'block',
// FIX ME
width: 2,
height: '100%',
margin: '0 auto',
// FIX ME @border-color-split
backgroundColor: 'rgba(0, 0, 0, 0.06)',
content: '" "',
},
},
[`${componentCls}-ink-ball`]: {
position: 'absolute',
// left 50%
insetInlineStart: '50%',
display: 'none',
// FIX ME
width: 8,
// FIX ME
height: 8,
// FIX '@component-background'
backgroundColor: '#fff',
border: `2px solid ${token.colorPrimary}`,
borderRadius: 8,
transform: 'translateX(-50%)',
transition: `top ${token.motionDurationSlow} ease-in-out`,
'&.visible': {
display: 'inline-block',
},
},
[`${componentCls}-link`]: {
// FIX ME @anchor-link-padding
paddingBlock: token.anchorLinkTop,
paddingInline: `${token.anchorLinkLeft}px 0`,
lineHeight: '1.143',
'&-title': {
position: 'relative',
display: 'block',
// FIX ME margin-bottom
marginBlockEnd: 6,
overflow: 'hidden',
color: token.colorText,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
transition: `all ${token.motionDurationSlow}s`,
'&:only-child': {
marginBlockEnd: 0,
},
},
[`&-active > ${componentCls}-link-title`]: {
color: token.colorPrimary,
},
// link link
[`${componentCls}-link`]: {
paddingBlock: 5,
},
},
},
[`${componentCls}-fixed ${componentCls}-ink ${componentCls}-ink-ball`]: {
display: 'none',
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('Anchor', token => {
const anchorToken = mergeToken<AnchorToken>(token, {
anchorLinkTop: 7,
anchorLinkLeft: 16,
});
return [genSharedAnchorStyle(anchorToken)];
});

View File

@ -34,7 +34,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -80,7 +80,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -163,7 +163,7 @@ exports[`renders ./components/auto-complete/demo/certain-category.md extend cont
<div>
<div
class="ant-select-dropdown certain-category-search-dropdown"
style="opacity:0;pointer-events:none;width:500px"
style="opacity:0;width:500px"
>
<div>
<div
@ -501,7 +501,7 @@ exports[`renders ./components/auto-complete/demo/custom.md extend context correc
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -571,7 +571,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -640,7 +640,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -785,7 +785,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -898,7 +898,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -993,7 +993,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1066,7 +1066,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1206,7 +1206,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1280,7 +1280,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1429,7 +1429,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1503,7 +1503,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-tree-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1652,7 +1652,7 @@ exports[`renders ./components/auto-complete/demo/form-debug.md extend context co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1732,7 +1732,7 @@ exports[`renders ./components/auto-complete/demo/non-case-sensitive.md extend co
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1862,7 +1862,7 @@ exports[`renders ./components/auto-complete/demo/options.md extend context corre
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1915,7 +1915,7 @@ exports[`renders ./components/auto-complete/demo/status.md extend context correc
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1961,7 +1961,7 @@ exports[`renders ./components/auto-complete/demo/status.md extend context correc
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -2045,7 +2045,7 @@ exports[`renders ./components/auto-complete/demo/uncertain-category.md extend co
<div>
<div
class="ant-select-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none;width:252px"
style="opacity:0;width:252px"
>
<div>
<div

View File

@ -32,7 +32,7 @@ exports[`AutoComplete legacy dataSource should accept react element option 1`] =
<div>
<div
class="ant-select-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: 0; width: 0px;"
style="opacity: 0; min-width: 0; width: 0px;"
>
<div>
<div

View File

@ -2,8 +2,7 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { render, fireEvent } from '../../../tests/utils';
import Avatar from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -88,7 +87,7 @@ describe('Avatar Render', () => {
// https://github.com/jsdom/jsdom/issues/1816
wrapper.find('img').simulate('error');
expect(wrapper).toMatchSnapshot();
expect(wrapper.render()).toMatchSnapshot();
expect(div.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC);
wrapper.detach();
@ -106,7 +105,7 @@ describe('Avatar Render', () => {
const wrapper = mount(<Avatar src={LOAD_FAILURE_SRC}>Fallback</Avatar>, { attachTo: div });
wrapper.find('img').simulate('error');
expect(wrapper).toMatchSnapshot();
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find('.ant-avatar-string').length).toBe(1);
// children should show, when image load error without onError return false
expect(wrapper.find('.ant-avatar-string').prop('style')).not.toHaveProperty('opacity', 0);
@ -115,7 +114,7 @@ describe('Avatar Render', () => {
wrapper.setProps({ src: LOAD_SUCCESS_SRC });
wrapper.update();
expect(wrapper).toMatchSnapshot();
expect(wrapper.render()).toMatchSnapshot();
expect(wrapper.find('.ant-avatar-image').length).toBe(1);
// cleanup
@ -124,8 +123,8 @@ describe('Avatar Render', () => {
});
it('should calculate scale of avatar children correctly', () => {
const wrapper = mount(<Avatar>Avatar</Avatar>);
expect(wrapper.find('.ant-avatar-string')).toMatchSnapshot();
const { container, rerender } = render(<Avatar>Avatar</Avatar>);
expect(container.querySelector('.ant-avatar-string')).toMatchSnapshot();
Object.defineProperty(HTMLElement.prototype, 'offsetWidth', {
get() {
@ -135,13 +134,14 @@ describe('Avatar Render', () => {
return 40;
},
});
wrapper.setProps({ children: 'xx' });
expect(wrapper.find('.ant-avatar-string')).toMatchSnapshot();
rerender(<Avatar>xx</Avatar>);
expect(container.querySelector('.ant-avatar-string')).toMatchSnapshot();
});
it('should calculate scale of avatar children correctly with gap', () => {
const wrapper = mount(<Avatar gap={2}>Avatar</Avatar>);
expect(wrapper.find('.ant-avatar-string')).toMatchSnapshot();
expect(wrapper.find('.ant-avatar-string').render()).toMatchSnapshot();
});
it('should warning when pass a string as icon props', () => {
@ -176,8 +176,8 @@ describe('Avatar Render', () => {
it('support onMouseEnter', () => {
const onMouseEnter = jest.fn();
const wrapper = mount(<Avatar onMouseEnter={onMouseEnter}>TestString</Avatar>);
wrapper.simulate('mouseenter');
const { container } = render(<Avatar onMouseEnter={onMouseEnter}>TestString</Avatar>);
fireEvent.mouseEnter(container.firstChild);
expect(onMouseEnter).toHaveBeenCalled();
});

View File

@ -110,14 +110,8 @@ exports[`Avatar Render rtl render component should be rendered correctly in RTL
exports[`Avatar Render should calculate scale of avatar children correctly 1`] = `
<span
className="ant-avatar-string"
style={
Object {
"WebkitTransform": "scale(0.72) translateX(-50%)",
"msTransform": "scale(0.72) translateX(-50%)",
"transform": "scale(0.72) translateX(-50%)",
}
}
class="ant-avatar-string"
style="transform: scale(0.72) translateX(-50%);"
>
Avatar
</span>
@ -125,14 +119,8 @@ exports[`Avatar Render should calculate scale of avatar children correctly 1`] =
exports[`Avatar Render should calculate scale of avatar children correctly 2`] = `
<span
className="ant-avatar-string"
style={
Object {
"WebkitTransform": "scale(0.72) translateX(-50%)",
"msTransform": "scale(0.72) translateX(-50%)",
"transform": "scale(0.72) translateX(-50%)",
}
}
class="ant-avatar-string"
style="transform: scale(0.72) translateX(-50%);"
>
xx
</span>
@ -140,93 +128,44 @@ exports[`Avatar Render should calculate scale of avatar children correctly 2`] =
exports[`Avatar Render should calculate scale of avatar children correctly with gap 1`] = `
<span
className="ant-avatar-string"
style={
Object {
"WebkitTransform": "scale(0.36) translateX(-50%)",
"msTransform": "scale(0.36) translateX(-50%)",
"transform": "scale(0.36) translateX(-50%)",
}
}
class="ant-avatar-string"
style="transform: scale(0.36) translateX(-50%);"
>
Avatar
</span>
`;
exports[`Avatar Render should handle onError correctly 1`] = `
<Foo>
<Avatar
onError={[Function]}
shape="circle"
size="default"
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<img
src="https://joeschmoe.io/api/v1/random"
>
<span
className="ant-avatar ant-avatar-circle ant-avatar-image"
style={Object {}}
>
<img
onError={[Function]}
src="https://joeschmoe.io/api/v1/random"
/>
</span>
</Avatar>
</Foo>
/>
</span>
`;
exports[`Avatar Render should show image on success after a failure state 1`] = `
<Avatar
shape="circle"
size="default"
src="http://error.url"
<span
class="ant-avatar ant-avatar-circle"
>
<span
className="ant-avatar ant-avatar-circle"
style={Object {}}
class="ant-avatar-string"
style="transform: scale(1) translateX(-50%);"
>
<ResizeObserver
onResize={[Function]}
>
<SingleObserver
key="rc-observer-key-0"
onResize={[Function]}
>
<DomWrapper>
<span
className="ant-avatar-string"
style={
Object {
"WebkitTransform": "scale(1) translateX(-50%)",
"msTransform": "scale(1) translateX(-50%)",
"transform": "scale(1) translateX(-50%)",
}
}
>
Fallback
</span>
</DomWrapper>
</SingleObserver>
</ResizeObserver>
Fallback
</span>
</Avatar>
</span>
`;
exports[`Avatar Render should show image on success after a failure state 2`] = `
<Avatar
shape="circle"
size="default"
src="https://joeschmoe.io/api/v1/random"
<span
class="ant-avatar ant-avatar-circle ant-avatar-image"
>
<span
className="ant-avatar ant-avatar-circle ant-avatar-image"
style={Object {}}
>
<img
onError={[Function]}
src="https://joeschmoe.io/api/v1/random"
/>
</span>
</Avatar>
<img
src="https://joeschmoe.io/api/v1/random"
/>
</span>
`;
exports[`Avatar Render support size is number 1`] = `

View File

@ -383,7 +383,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -468,7 +468,7 @@ Array [
<div>
<div
class="ant-popover ant-avatar-group-popover"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-popover-content"
@ -514,7 +514,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -604,7 +604,7 @@ Array [
<div>
<div
class="ant-popover ant-avatar-group-popover"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-popover-content"
@ -650,7 +650,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -740,7 +740,7 @@ Array [
<div>
<div
class="ant-popover ant-avatar-group-popover"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-popover-content"
@ -786,7 +786,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"

View File

@ -66,5 +66,5 @@ const Demo = () => (
</>
);
export default () => <Demo />;
export default Demo;
```

View File

@ -71,5 +71,5 @@ const App: React.FC = () => {
);
};
export default () => <App />;
export default App;
```

View File

@ -11,6 +11,8 @@ import getScroll from '../_util/getScroll';
import scrollTo from '../_util/scrollTo';
import { cloneElement } from '../_util/reactNode';
import useStyle from './style';
export interface BackTopProps {
visibilityHeight?: number;
onClick?: React.MouseEventHandler<HTMLElement>;
@ -105,7 +107,10 @@ const BackTop: React.FC<BackTopProps> = props => {
const { prefixCls: customizePrefixCls, className = '' } = props;
const prefixCls = getPrefixCls('back-top', customizePrefixCls);
const rootPrefixCls = getPrefixCls();
const [wrapSSR, hashId] = useStyle(prefixCls);
const classString = classNames(
hashId,
prefixCls,
{
[`${prefixCls}-rtl`]: direction === 'rtl',
@ -123,10 +128,10 @@ const BackTop: React.FC<BackTopProps> = props => {
'visible',
]);
return (
return wrapSSR(
<div {...divProps} className={classString} onClick={scrollToTop} ref={ref}>
{renderChildren({ prefixCls, rootPrefixCls })}
</div>
</div>,
);
};

View File

@ -1,49 +1,49 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
// @import '../../style/themes/index';
// @import '../../style/mixins/index';
@backtop-prefix-cls: ~'@{ant-prefix}-back-top';
// @backtop-prefix-cls: ~'@{ant-prefix}-back-top';
.@{backtop-prefix-cls} {
.reset-component();
// .@{backtop-prefix-cls} {
// .reset-component();
position: fixed;
right: 100px;
bottom: 50px;
z-index: @zindex-back-top;
width: 40px;
height: 40px;
cursor: pointer;
// position: fixed;
// right: 100px;
// bottom: 50px;
// z-index: @zindex-back-top;
// width: 40px;
// height: 40px;
// cursor: pointer;
&:empty {
display: none;
}
// &:empty {
// display: none;
// }
&-rtl {
right: auto;
left: 100px;
direction: rtl;
}
// &-rtl {
// right: auto;
// left: 100px;
// direction: rtl;
// }
&-content {
width: 40px;
height: 40px;
overflow: hidden;
color: @back-top-color;
text-align: center;
background-color: @back-top-bg;
border-radius: 20px;
transition: all 0.3s;
// &-content {
// width: 40px;
// height: 40px;
// overflow: hidden;
// color: @back-top-color;
// text-align: center;
// background-color: @back-top-bg;
// border-radius: 20px;
// transition: all 0.3s;
&:hover {
background-color: @back-top-hover-bg;
transition: all 0.3s;
}
}
// &:hover {
// background-color: @back-top-hover-bg;
// transition: all 0.3s;
// }
// }
&-icon {
font-size: 24px;
line-height: 40px;
}
}
// &-icon {
// font-size: 24px;
// line-height: 40px;
// }
// }
@import './responsive';
// @import './responsive';

View File

@ -1,2 +1,108 @@
import '../../style/index.less';
import './index.less';
// deps-lint-skip-all
import { CSSObject } from '@ant-design/cssinjs';
import {
resetComponent,
genComponentStyleHook,
GenerateStyle,
FullToken,
mergeToken,
} from '../../_util/theme';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {}
type BackTopToken = FullToken<'BackTop'> & {
backTopBackground: string;
backTopColor: string;
backTopHoverBackground: string;
};
// ============================== Shared ==============================
const genSharedBackTopStyle: GenerateStyle<BackTopToken, CSSObject> = (token): CSSObject => {
const { componentCls } = token;
return {
[componentCls]: {
...resetComponent(token),
position: 'fixed',
// FIXME right
insetInlineEnd: 100,
// FIXME bottom
insetBlockEnd: 50,
// FIX ME @zindex-back-top
zIndex: token.zIndexPopup,
width: 40,
height: 40,
cursor: 'pointer',
'&:empty': {
display: 'none',
},
[`${componentCls}-content`]: {
width: 40,
height: 40,
overflow: 'hidden',
// FIXME @back-top-color
color: token.backTopColor,
textAlign: 'center',
// FIXME @back-top-bg
backgroundColor: token.backTopBackground,
// FIXME
borderRadius: 20,
transition: `all ${token.motionDurationSlow}`,
'&:hover': {
// FIX ME @back-top-hover-bg
backgroundColor: token.backTopHoverBackground,
transition: `all ${token.motionDurationSlow}`,
},
},
// change to .backtop .backtop-icon
[`${componentCls}-icon`]: {
// FIXME
fontSize: 24,
// FIXME
lineHeight: '40px',
},
},
};
};
const genMediaBackTopStyle: GenerateStyle<BackTopToken> = (token): CSSObject => {
const { componentCls } = token;
return {
[`@media (max-width: ${token.screenMD}px)`]: {
[componentCls]: {
marginInlineEnd: 60,
},
},
[`@media (max-width: ${token.screenXS}px)`]: {
[componentCls]: {
marginInlineEnd: 20,
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook<'BackTop'>(
'BackTop',
token => {
const backTopBackground = 'rgb(16, 136, 233)';
const backTopColor = '#fff';
const backTopHoverBackground = '#000000d9';
const backTopToken = mergeToken<BackTopToken>(token, {
backTopBackground,
backTopColor,
backTopHoverBackground,
});
return [genSharedBackTopStyle(backTopToken), genMediaBackTopStyle(backTopToken)];
},
);

View File

@ -1,21 +1,21 @@
@media screen and (max-width: @screen-md) {
.@{backtop-prefix-cls} {
right: 60px;
// @media screen and (max-width: @screen-md) {
// .@{backtop-prefix-cls} {
// right: 60px;
&-rtl {
right: auto;
left: 60px;
}
}
}
// &-rtl {
// right: auto;
// left: 60px;
// }
// }
// }
@media screen and (max-width: @screen-xs) {
.@{backtop-prefix-cls} {
right: 20px;
// @media screen and (max-width: @screen-xs) {
// .@{backtop-prefix-cls} {
// right: 20px;
&-rtl {
right: auto;
left: 20px;
}
}
}
// &-rtl {
// right: auto;
// left: 20px;
// }
// }
// }

View File

@ -1,6 +1,7 @@
import React from 'react';
import { mount } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { fireEvent, render } from '@testing-library/react';
import Badge from '../index';
import Tooltip from '../../tooltip';
import mountTest from '../../../tests/shared/mountTest';
@ -54,14 +55,14 @@ describe('Badge', () => {
// https://github.com/ant-design/ant-design/issues/10626
it('should be composable with Tooltip', () => {
const ref = React.createRef();
const wrapper = mount(
const { container } = render(
<Tooltip title="Fix the error" ref={ref}>
<Badge status="error" />
</Tooltip>,
);
act(() => {
wrapper.find('Badge').simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('.ant-badge'));
jest.runAllTimers();
});
expect(ref.current.props.visible).toBeTruthy();

View File

@ -74,5 +74,5 @@ class Demo extends React.Component {
}
}
export default () => <Demo />;
export default Demo;
```

View File

@ -36,5 +36,5 @@ const Demo = () => {
);
};
export default () => <Demo />;
export default Demo;
```

View File

@ -54,10 +54,7 @@ const antBadgeLoadingCircle = new Keyframes('antBadgeLoadingCircle', {
},
});
const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
token: BadgeToken,
hashId: string,
): CSSObject => {
const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (token: BadgeToken): CSSObject => {
const { componentCls, iconCls, antCls } = token;
const numberPrefixCls = `${antCls}-scroll-number`;
const ribbonPrefixCls = `${antCls}-ribbon`;
@ -147,9 +144,10 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
transform: 'translate(50%, -50%)',
transformOrigin: '100% 0%',
[`${iconCls}-spin`]: {
animation: `${antBadgeLoadingCircle.getName(hashId)} ${
token.motionDurationFast
} infinite linear`,
animationName: antBadgeLoadingCircle,
animationDuration: token.motionDurationFast,
animationIterationCount: 'infinite',
animationTimingFunction: 'linear',
},
},
[`&${componentCls}-status`]: {
@ -181,7 +179,10 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
height: '100%',
border: `1px solid ${token.colorPrimary}`,
borderRadius: '50%',
animation: `${antStatusProcessing.getName(hashId)} 1.2s infinite ease-in-out`, // FIXME: hard code, copied from old less file
animationName: antStatusProcessing,
animationDuration: '1.2s',
animationIterationCount: 'infinite',
animationTimingFunction: 'ease-in-out',
content: '""',
},
},
@ -204,28 +205,28 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
},
},
[`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
animation: `${antZoomBadgeIn.getName(hashId)} ${token.motionDurationSlow} ${
token.motionEaseOutBack
}`,
animationName: antZoomBadgeIn,
animationDuration: token.motionDurationSlow,
animationTimingFunction: token.motionEaseOutBack,
animationFillMode: 'both',
},
[`${componentCls}-zoom-leave`]: {
animation: `${antZoomBadgeOut.getName(hashId)} ${token.motionDurationSlow} ${
token.motionEaseOutBack
}`,
animationName: antZoomBadgeOut,
animationDuration: token.motionDurationSlow,
animationTimingFunction: token.motionEaseOutBack,
animationFillMode: 'both',
},
[`&${componentCls}-not-a-wrapper`]: {
[`${componentCls}-zoom-appear, ${componentCls}-zoom-enter`]: {
animation: `${antNoWrapperZoomBadgeIn.getName(hashId)} ${token.motionDurationSlow} ${
token.motionEaseOutBack
}`,
animationName: antNoWrapperZoomBadgeIn,
animationDuration: token.motionDurationSlow,
animationTimingFunction: token.motionEaseOutBack,
},
[`${componentCls}-zoom-leave`]: {
animation: `${antNoWrapperZoomBadgeOut.getName(hashId)} ${token.motionDurationSlow} ${
token.motionEaseOutBack
}`,
animationName: antNoWrapperZoomBadgeOut,
animationDuration: token.motionDurationSlow,
animationTimingFunction: token.motionEaseOutBack,
},
[`&:not(${componentCls}-status)`]: {
verticalAlign: 'middle',
@ -296,7 +297,7 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
...statusRibbonPreset,
[`&${ribbonPrefixCls}-placement-end`]: {
insetInlineEnd: -1 * token.marginXS,
borderBottomRightRadius: 0,
borderEndEndRadius: 0,
[`${ribbonPrefixCls}-corner`]: {
insetInlineEnd: 0,
borderColor: 'currentcolor transparent transparent currentcolor',
@ -304,24 +305,18 @@ const genSharedBadgeStyle: GenerateStyle<BadgeToken> = (
},
[`&${ribbonPrefixCls}-placement-start`]: {
insetInlineStart: -1 * token.marginXS,
borderBottomLeftRadius: 0,
borderEndStartRadius: 0,
[`${ribbonPrefixCls}-corner`]: {
insetInlineStart: 0,
borderColor: 'currentcolor currentcolor transparent transparent',
},
},
antStatusProcessing,
antZoomBadgeIn,
antZoomBadgeOut,
antNoWrapperZoomBadgeIn,
antNoWrapperZoomBadgeOut,
antBadgeLoadingCircle,
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('Badge', (token, { hashId }) => {
export default genComponentStyleHook('Badge', token => {
const badgeZIndex = 'auto';
const badgeHeight = 20; // FIXME: hard code
const badgeTextColor = token.colorBgComponent;
@ -346,5 +341,5 @@ export default genComponentStyleHook('Badge', (token, { hashId }) => {
badgeStatusSize,
});
return [genSharedBadgeStyle(badgeToken, hashId), { display: 'none' }];
return [genSharedBadgeStyle(badgeToken), { display: 'none' }];
});

View File

@ -1,15 +1,15 @@
import * as React from 'react';
import DownOutlined from '@ant-design/icons/DownOutlined';
import DropDown, { DropDownProps } from '../dropdown/dropdown';
import Dropdown, { DropdownProps } from '../dropdown/dropdown';
import { ConfigContext } from '../config-provider';
export interface BreadcrumbItemProps {
prefixCls?: string;
separator?: React.ReactNode;
href?: string;
overlay?: DropDownProps['overlay'];
dropdownProps?: DropDownProps;
overlay?: DropdownProps['overlay'];
dropdownProps?: DropdownProps;
onClick?: React.MouseEventHandler<HTMLAnchorElement | HTMLSpanElement>;
className?: string;
children?: React.ReactNode;
@ -27,16 +27,16 @@ const BreadcrumbItem: BreadcrumbItemInterface = ({
}) => {
const { getPrefixCls } = React.useContext(ConfigContext);
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
/** If overlay is have Wrap a DropDown */
/** If overlay is have Wrap a Dropdown */
const renderBreadcrumbNode = (breadcrumbItem: React.ReactNode) => {
if (overlay) {
return (
<DropDown overlay={overlay} placement="bottomCenter" {...dropdownProps}>
<Dropdown overlay={overlay} placement="bottom" {...dropdownProps}>
<span className={`${prefixCls}-overlay-link`}>
{breadcrumbItem}
<DownOutlined />
</span>
</DropDown>
</Dropdown>
);
}
return breadcrumbItem;

View File

@ -1,7 +1,6 @@
import React from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { render } from '../../../tests/utils';
import Breadcrumb from '../index';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
@ -30,8 +29,7 @@ describe('Breadcrumb', () => {
<MyCom />
</Breadcrumb>,
);
expect(errorSpy.mock.calls).toHaveLength(1);
expect(errorSpy.mock.calls[0][0]).toMatch(
expect(errorSpy).toHaveBeenCalledWith(
"Warning: [antd: Breadcrumb] Only accepts Breadcrumb.Item and Breadcrumb.Separator as it's children",
);
});

View File

@ -134,10 +134,11 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-dropdown-inject="true"
data-menu-list="true"
role="menu"
tabindex="0"
@ -162,7 +163,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -201,7 +202,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -240,7 +241,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -267,7 +268,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -289,7 +290,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -311,7 +312,7 @@ exports[`renders ./components/breadcrumb/demo/overlay.md extend context correctl
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"

View File

@ -1,166 +1,72 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`react router react router 3 1`] = `
<Breadcrumb
params={
Object {
"id": 1,
}
}
routes={
Array [
Object {
"breadcrumbName": "Home",
"childRoutes": Array [
Object {
"breadcrumbName": "Application List",
"childRoutes": Array [
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
],
"name": "apps",
"path": "apps",
},
],
"name": "home",
"path": "/",
},
Object {
"breadcrumbName": "Application List",
"childRoutes": Array [
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
],
"name": "apps",
"path": "apps",
},
Object {
"breadcrumbName": "Application:id",
"childRoutes": Array [
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
],
"name": "app",
"path": ":id",
},
Object {
"breadcrumbName": "Detail",
"name": "detail",
"path": "detail",
},
]
}
<nav
class="ant-breadcrumb"
>
<nav
className="ant-breadcrumb"
>
<ol>
<BreadcrumbItem
key="Home"
separator="/"
<ol>
<li>
<span
class="ant-breadcrumb-link"
>
<li>
<span
className="ant-breadcrumb-link"
>
<a
href="#/"
>
Home
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</li>
</BreadcrumbItem>
<BreadcrumbItem
key="apps"
separator="/"
<a
href="#/"
>
Home
</a>
</span>
<span
class="ant-breadcrumb-separator"
>
<li>
<span
className="ant-breadcrumb-link"
>
<a
href="#/apps"
>
Application List
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</li>
</BreadcrumbItem>
<BreadcrumbItem
key="1"
separator="/"
/
</span>
</li>
<li>
<span
class="ant-breadcrumb-link"
>
<li>
<span
className="ant-breadcrumb-link"
>
<a
href="#/apps/1"
>
Application1
</a>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</li>
</BreadcrumbItem>
<BreadcrumbItem
key="detail"
separator="/"
<a
href="#/apps"
>
Application List
</a>
</span>
<span
class="ant-breadcrumb-separator"
>
<li>
<span
className="ant-breadcrumb-link"
>
<span>
Detail
</span>
</span>
<span
className="ant-breadcrumb-separator"
>
/
</span>
</li>
</BreadcrumbItem>
</ol>
</nav>
</Breadcrumb>
/
</span>
</li>
<li>
<span
class="ant-breadcrumb-link"
>
<a
href="#/apps/1"
>
Application1
</a>
</span>
<span
class="ant-breadcrumb-separator"
>
/
</span>
</li>
<li>
<span
class="ant-breadcrumb-link"
>
<span>
Detail
</span>
</span>
<span
class="ant-breadcrumb-separator"
>
/
</span>
</li>
</ol>
</nav>
`;

View File

@ -144,6 +144,6 @@ describe('react router', () => {
},
];
const wrapper = mount(<Breadcrumb routes={routes} params={{ id: 1 }} />);
expect(wrapper).toMatchSnapshot();
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -16,6 +16,9 @@
ol {
display: flex;
flex-wrap: wrap;
margin: 0;
padding: 0;
list-style: none;
}
a {

View File

@ -397,7 +397,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -480,7 +480,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -556,7 +556,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -631,7 +631,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -732,7 +732,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -815,7 +815,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -891,7 +891,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -966,7 +966,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1048,7 +1048,7 @@ Array [
class="ant-btn-group ant-btn-group-sm"
>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-sm"
type="button"
>
<span>
@ -1056,7 +1056,7 @@ Array [
</span>
</button>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-sm"
type="button"
>
<span>
@ -1068,7 +1068,7 @@ Array [
style="display:inline-block;cursor:not-allowed"
>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
style="pointer-events:none"
type="button"
@ -1097,7 +1097,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1119,7 +1119,7 @@ Array [
</div>
</div>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
type="button"
>
<span
@ -1145,7 +1145,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1223,7 +1223,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1271,7 +1271,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1300,7 +1300,7 @@ Array [
class="ant-btn-group ant-btn-group-lg"
>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-lg"
type="button"
>
<span>
@ -1308,7 +1308,7 @@ Array [
</span>
</button>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-lg"
type="button"
>
<span>
@ -1320,7 +1320,7 @@ Array [
style="display:inline-block;cursor:not-allowed"
>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-lg ant-btn-icon-only"
disabled=""
style="pointer-events:none"
type="button"
@ -1349,7 +1349,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1371,7 +1371,7 @@ Array [
</div>
</div>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-lg ant-btn-icon-only"
type="button"
>
<span
@ -1397,7 +1397,7 @@ Array [
<div>
<div
class="ant-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1671,10 +1671,11 @@ Array [
<div>
<div
class="ant-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<ul
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-vertical ant-dropdown-menu-light"
data-dropdown-inject="true"
data-menu-list="true"
role="menu"
tabindex="0"
@ -1693,7 +1694,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1726,7 +1727,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1759,7 +1760,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1786,7 +1787,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1808,7 +1809,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"
@ -1830,7 +1831,7 @@ Array [
<div>
<div
class="ant-tooltip ant-dropdown-menu-inline-collapsed-tooltip"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div
class="ant-tooltip-content"

View File

@ -856,7 +856,7 @@ Array [
class="ant-btn-group ant-btn-group-sm"
>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-sm"
type="button"
>
<span>
@ -864,7 +864,7 @@ Array [
</span>
</button>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-sm"
type="button"
>
<span>
@ -876,7 +876,7 @@ Array [
style="display:inline-block;cursor:not-allowed"
>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
disabled=""
style="pointer-events:none"
type="button"
@ -903,7 +903,7 @@ Array [
</button>
</span>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-sm ant-btn-icon-only"
type="button"
>
<span
@ -1012,7 +1012,7 @@ Array [
class="ant-btn-group ant-btn-group-lg"
>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-lg"
type="button"
>
<span>
@ -1020,7 +1020,7 @@ Array [
</span>
</button>
<button
class="ant-btn ant-btn-primary"
class="ant-btn ant-btn-primary ant-btn-lg"
type="button"
>
<span>
@ -1032,7 +1032,7 @@ Array [
style="display:inline-block;cursor:not-allowed"
>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-lg ant-btn-icon-only"
disabled=""
style="pointer-events:none"
type="button"
@ -1059,7 +1059,7 @@ Array [
</button>
</span>
<button
class="ant-btn ant-btn-primary ant-btn-icon-only"
class="ant-btn ant-btn-primary ant-btn-lg ant-btn-icon-only"
type="button"
>
<span

View File

@ -1,7 +1,5 @@
import React, { Component } from 'react';
import { mount } from 'enzyme';
import { render } from '@testing-library/react';
import '@testing-library/jest-dom';
import { act } from 'react-dom/test-utils';
import { SearchOutlined } from '@ant-design/icons';
import { resetWarned } from 'rc-util/lib/warning';
@ -9,7 +7,7 @@ import Button from '..';
import ConfigProvider from '../../config-provider';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep } from '../../../tests/utils';
import { sleep, render, fireEvent } from '../../../tests/utils';
import { SizeType } from '../../config-provider/SizeContext';
describe('Button', () => {
@ -38,14 +36,13 @@ describe('Button', () => {
});
it('warns if size is wrong', () => {
const mockWarn = jest.fn();
jest.spyOn(console, 'warn').mockImplementation(mockWarn);
resetWarned();
const mockWarn = jest.spyOn(console, 'error').mockImplementation(() => {});
const size = 'who am I' as any as SizeType;
mount(<Button.Group size={size} />);
expect(mockWarn).toHaveBeenCalledTimes(1);
expect(mockWarn.mock.calls[0][0]).toMatchObject({
message: 'unreachable case: "who am I"',
});
render(<Button.Group size={size} />);
expect(mockWarn).toHaveBeenCalledWith('Warning: [antd: Button.Group] Invalid prop `size`.');
mockWarn.mockRestore();
});
it('renders Chinese characters correctly', () => {
@ -86,22 +83,26 @@ describe('Button', () => {
it('renders Chinese characters correctly in HOC', () => {
const Text = ({ children }: { children: React.ReactNode }) => <span>{children}</span>;
const wrapper = mount(
const { container, rerender } = render(
<Button>
<Text></Text>
</Button>,
);
expect(wrapper.find('.ant-btn').hasClass('ant-btn-two-chinese-chars')).toBe(true);
wrapper.setProps({
children: <Text></Text>,
});
wrapper.update();
expect(wrapper.find('.ant-btn').hasClass('ant-btn-two-chinese-chars')).toBe(false);
wrapper.setProps({
children: <Text></Text>,
});
wrapper.update();
expect(wrapper.find('.ant-btn').hasClass('ant-btn-two-chinese-chars')).toBe(true);
expect(container.querySelector('.ant-btn')).toHaveClass('ant-btn-two-chinese-chars');
rerender(
<Button>
<Text></Text>
</Button>,
);
expect(container.querySelector('.ant-btn')).not.toHaveClass('ant-btn-two-chinese-chars');
rerender(
<Button>
<Text></Text>
</Button>,
);
expect(container.querySelector('.ant-btn')).toHaveClass('ant-btn-two-chinese-chars');
});
// https://github.com/ant-design/ant-design/issues/18118
@ -124,7 +125,7 @@ describe('Button', () => {
it('have static property for type detecting', () => {
const wrapper = mount(<Button>Button Text</Button>);
expect((wrapper.type() as any).__ANT_BUTTON).toBe(true);
expect((wrapper.find(Button).type() as any).__ANT_BUTTON).toBe(true);
});
it('should change loading state instantly by default', () => {
@ -195,12 +196,12 @@ describe('Button', () => {
it('should not clickable when button is loading', () => {
const onClick = jest.fn();
const wrapper = mount(
const { container } = render(
<Button loading onClick={onClick}>
button
</Button>,
);
wrapper.simulate('click');
fireEvent.click(container.firstChild!);
expect(onClick).not.toHaveBeenCalledWith();
});
@ -314,12 +315,12 @@ describe('Button', () => {
it('should not redirect when button is disabled', () => {
const onClick = jest.fn();
const wrapper = mount(
const { container } = render(
<Button href="https://ant.design" onClick={onClick} disabled>
click me
</Button>,
);
wrapper.simulate('click');
fireEvent.click(container.firstChild!);
expect(onClick).not.toHaveBeenCalled();
});

View File

@ -210,28 +210,6 @@
.@{btnClassName}-icon-only {
font-size: @font-size-base;
}
// size
&-lg > .@{btnClassName},
&-lg > span > .@{btnClassName} {
.button-size(@btn-height-lg; @btn-padding-horizontal-lg; @btn-font-size-lg; 0);
}
&-lg .@{btnClassName}.@{btnClassName}-icon-only {
.square(@btn-height-lg);
padding-right: 0;
padding-left: 0;
}
&-sm > .@{btnClassName},
&-sm > span > .@{btnClassName} {
.button-size(@btn-height-sm; @btn-padding-horizontal-sm; @font-size-base; 0);
> .@{iconfont-css-prefix} {
font-size: @font-size-base;
}
}
&-sm .@{btnClassName}.@{btnClassName}-icon-only {
.square(@btn-height-sm);
padding-right: 0;
padding-left: 0;
}
}
// Base styles of buttons
// --------------------------------------------------

View File

@ -1,8 +1,9 @@
import * as React from 'react';
import classNames from 'classnames';
import { SizeType } from '../config-provider/SizeContext';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import UnreachableException from '../_util/unreachableException';
import { ConfigContext } from '../config-provider';
import devWarning from '../_util/devWarning';
import { useToken } from '../_util/theme';
export interface ButtonGroupProps {
size?: SizeType;
@ -12,42 +13,49 @@ export interface ButtonGroupProps {
children?: React.ReactNode;
}
const ButtonGroup: React.FC<ButtonGroupProps> = props => (
<ConfigConsumer>
{({ getPrefixCls, direction }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, size, className, ...others } = props;
const prefixCls = getPrefixCls('btn-group', customizePrefixCls);
export const GroupSizeContext = React.createContext<SizeType | undefined>(undefined);
// large => lg
// small => sm
let sizeCls = '';
switch (size) {
case 'large':
sizeCls = 'lg';
break;
case 'small':
sizeCls = 'sm';
break;
case 'middle':
case undefined:
break;
default:
// eslint-disable-next-line no-console
console.warn(new UnreachableException(size).error);
}
const ButtonGroup: React.FC<ButtonGroupProps> = props => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const classes = classNames(
prefixCls,
{
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
);
const { prefixCls: customizePrefixCls, size, className, ...others } = props;
const prefixCls = getPrefixCls('btn-group', customizePrefixCls);
return <div {...others} className={classes} />;
}}
</ConfigConsumer>
);
// Here we only need hashId
const [, , hashId] = useToken();
// large => lg
// small => sm
let sizeCls = '';
switch (size) {
case 'large':
sizeCls = 'lg';
break;
case 'small':
sizeCls = 'sm';
break;
case 'middle':
case undefined:
break;
default:
devWarning(!size, 'Button.Group', 'Invalid prop `size`.');
}
const classes = classNames(
prefixCls,
{
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
hashId,
);
return (
<GroupSizeContext.Provider value={size}>
<div {...others} className={classes} />
</GroupSizeContext.Provider>
);
};
export default ButtonGroup;

View File

@ -3,7 +3,7 @@ import * as React from 'react';
import classNames from 'classnames';
import omit from 'rc-util/lib/omit';
import Group from './button-group';
import Group, { GroupSizeContext } from './button-group';
import { ConfigContext } from '../config-provider';
import Wave from '../_util/wave';
import { tuple } from '../_util/type';
@ -21,7 +21,7 @@ function isString(str: any) {
return typeof str === 'string';
}
function isUnborderedButtonType(type: ButtonType | undefined) {
function isUnBorderedButtonType(type: ButtonType | undefined) {
return type === 'text' || type === 'link';
}
@ -97,7 +97,11 @@ export function convertLegacyProps(type?: LegacyButtonType): ButtonProps {
export interface BaseButtonProps {
type?: ButtonType;
icon?: React.ReactNode;
/** @default default */
/**
* Shape of Button
*
* @default default
*/
shape?: ButtonShape;
size?: SizeType;
loading?: boolean | { delay?: number };
@ -161,12 +165,13 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
const [wrapSSR, hashId] = useStyle(prefixCls);
const size = React.useContext(SizeContext);
const groupSize = React.useContext(GroupSizeContext);
const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
const buttonRef = (ref as any) || React.createRef<HTMLElement>();
const isNeedInserted = () =>
React.Children.count(children) === 1 && !icon && !isUnborderedButtonType(type);
React.Children.count(children) === 1 && !icon && !isUnBorderedButtonType(type);
const fixTwoCNChar = () => {
// Fix for HOC usage like <FormatMessage />
@ -228,7 +233,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
);
devWarning(
!(ghost && isUnborderedButtonType(type)),
!(ghost && isUnBorderedButtonType(type)),
'Button',
"`link` or `text` button can't be a `ghost` button.",
);
@ -236,7 +241,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
const autoInsertSpace = autoInsertSpaceInButton !== false;
const sizeClassNameMap = { large: 'lg', small: 'sm', middle: undefined };
const sizeFullname = customizeSize || size;
const sizeFullname = groupSize || customizeSize || size;
const sizeCls = sizeFullname ? sizeClassNameMap[sizeFullname] || '' : '';
const iconType = innerLoading ? 'loading' : icon;
@ -249,7 +254,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
[`${prefixCls}-${type}`]: type,
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-icon-only`]: !children && children !== 0 && !!iconType,
[`${prefixCls}-background-ghost`]: ghost && !isUnborderedButtonType(type),
[`${prefixCls}-background-ghost`]: ghost && !isUnBorderedButtonType(type),
[`${prefixCls}-loading`]: innerLoading,
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,
[`${prefixCls}-block`]: block,
@ -294,7 +299,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
</button>
);
if (!isUnborderedButtonType(type)) {
if (!isUnBorderedButtonType(type)) {
buttonNode = <Wave disabled={!!innerLoading}>{buttonNode}</Wave>;
}

View File

@ -45,9 +45,3 @@ export default () => (
</>
);
```
```css
#components-button-demo-legacy-group .ant-btn {
margin: 0;
}
```

View File

@ -81,5 +81,5 @@ class App extends React.Component {
}
}
export default () => <App />;
export default App;
```

View File

@ -57,18 +57,14 @@ Following the Ant Design specification, we will add one space between if Button
<img src="https://gw.alipayobjects.com/zos/antfincdn/MY%26THAPZrW/38f06cb9-293a-4b42-b183-9f443e79ffea.png" style="box-shadow: none; margin: 0; width: 100px" alt="Button with two Chinese characters" />
<style>
[id^=components-button-demo-] .ant-btn {
[id^="components-button-demo-"]:not([id^="components-button-demo-legacy-group"]) .ant-btn {
margin-right: 8px;
margin-bottom: 12px;
}
[id^="components-button-demo-"] .ant-btn-rtl {
[id^="components-button-demo-"]:not([id^="components-button-demo-legacy-group"]) .ant-btn-rtl {
margin-right: 0;
margin-left: 8px;
}
[id^=components-button-demo-] .ant-btn-group > .ant-btn,
[id^=components-button-demo-] .ant-btn-group > span > .ant-btn {
margin-right: 0;
}
[data-theme="dark"] .site-button-ghost-wrapper {
background: rgba(255, 255, 255, 0.2);
}

View File

@ -62,17 +62,14 @@ cover: https://gw.alipayobjects.com/zos/alicdn/fNUKzY1sk/Button.svg
<img src="https://gw.alipayobjects.com/zos/antfincdn/MY%26THAPZrW/38f06cb9-293a-4b42-b183-9f443e79ffea.png" style="box-shadow: none; margin: 0; width: 100px" alt="移除两个汉字之间的空格" />
<style>
[id^="components-button-demo-"] .ant-btn {
[id^="components-button-demo-"]:not([id^="components-button-demo-legacy-group"]) .ant-btn {
margin-right: 8px;
margin-bottom: 12px;
}
[id^="components-button-demo-"] .ant-btn-rtl {
[id^="components-button-demo-"]:not([id^="components-button-demo-legacy-group"]) .ant-btn-rtl {
margin-right: 0;
margin-left: 8px;
}
[id^="components-button-demo-"] .ant-btn-group > .ant-btn {
margin-right: 0;
}
[data-theme="dark"] .site-button-ghost-wrapper {
background: rgba(255, 255, 255, 0.2);
}

View File

@ -0,0 +1,81 @@
import type { GenerateStyle } from '../../_util/theme';
import type { ButtonToken } from '.';
const genButtonBorderStyle = (buttonTypeCls: string, borderColor: string) => ({
// Border
[`> span, > ${buttonTypeCls}`]: {
'&:not(:last-child)': {
[`&, & > ${buttonTypeCls}`]: {
'&:not(:disabled)': {
borderInlineEndColor: borderColor,
},
},
},
'&:not(:first-child)': {
[`&, & > ${buttonTypeCls}`]: {
'&:not(:disabled)': {
borderInlineStartColor: borderColor,
},
},
},
},
});
const genGroupStyle: GenerateStyle<ButtonToken> = token => {
const { componentCls, fontSizeBase, controlLineWidth, colorPrimaryHover, colorErrorHover } =
token;
return {
[`${componentCls}-group`]: [
{
position: 'relative',
display: 'inline-flex',
// Border
[`> span, > ${componentCls}`]: {
'&:not(:last-child)': {
[`&, & > ${componentCls}`]: {
borderStartEndRadius: 0,
borderEndEndRadius: 0,
},
},
'&:not(:first-child)': {
marginInlineStart: -controlLineWidth,
[`&, & > ${componentCls}`]: {
borderStartStartRadius: 0,
borderEndStartRadius: 0,
},
},
},
[componentCls]: {
position: 'relative',
zIndex: 1,
[`&:hover,
&:focus,
&:active`]: {
zIndex: 2,
},
'&[disabled]': {
zIndex: 0,
},
},
[`${componentCls}-icon-only`]: {
fontSize: fontSizeBase,
},
},
// Border Color
genButtonBorderStyle(`${componentCls}-primary`, colorPrimaryHover),
genButtonBorderStyle(`${componentCls}-danger`, colorErrorHover),
],
};
};
export default genGroupStyle;

View File

@ -3,6 +3,7 @@ import { CSSInterpolation, CSSObject } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import { mergeToken, genComponentStyleHook } from '../../_util/theme';
import type { GenerateStyle, FullToken } from '../../_util/theme';
import genGroupStyle from './group';
/** Component only token. Which will handle additional calculation of alias token */
export interface ComponentToken {
@ -10,7 +11,7 @@ export interface ComponentToken {
colorBgTextActive: string;
}
interface ButtonToken extends FullToken<'Button'> {}
export interface ButtonToken extends FullToken<'Button'> {}
// ============================== Shared ==============================
const genSharedButtonStyle: GenerateStyle<ButtonToken, CSSObject> = (token): CSSObject => {
@ -325,7 +326,7 @@ const genSizeButtonStyle = (token: ButtonToken, sizePrefixCls: string = ''): CSS
// Loading
[`&${componentCls}-loading`]: {
opacity: 0.65,
opacity: token.colorLoadingOpacity,
cursor: 'default',
},
@ -383,6 +384,9 @@ export default genComponentStyleHook(
// Group (type, ghost, danger, disabled, loading)
genTypeButtonStyle(token),
// Button Group
genGroupStyle(token),
],
token => {
const { colorText } = token;

View File

@ -42,7 +42,7 @@ exports[`renders ./components/calendar/demo/basic.md extend context correctly 1`
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -489,7 +489,7 @@ exports[`renders ./components/calendar/demo/basic.md extend context correctly 1`
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -1627,7 +1627,7 @@ exports[`renders ./components/calendar/demo/card.md extend context correctly 1`]
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -2074,7 +2074,7 @@ exports[`renders ./components/calendar/demo/card.md extend context correctly 1`]
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -3274,7 +3274,7 @@ exports[`renders ./components/calendar/demo/customize-header.md extend context c
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -3724,7 +3724,7 @@ exports[`renders ./components/calendar/demo/customize-header.md extend context c
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -4819,7 +4819,7 @@ exports[`renders ./components/calendar/demo/notice-calendar.md extend context co
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -5266,7 +5266,7 @@ exports[`renders ./components/calendar/demo/notice-calendar.md extend context co
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -6814,7 +6814,7 @@ Array [
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -7261,7 +7261,7 @@ Array [
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div

View File

@ -1,5 +1,6 @@
import React from 'react';
import Dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import dayjsGenerateConfig from 'rc-picker/lib/generate/dayjs';
import { mount } from 'enzyme';
import MockDate from 'mockdate';
@ -10,7 +11,7 @@ import Group from '../../radio/group';
import Button from '../../radio/radioButton';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import 'dayjs/locale/zh-cn';
import { render, fireEvent } from '../../../tests/utils';
describe('Calendar', () => {
mountTest(Calendar);
@ -41,14 +42,20 @@ describe('Calendar', () => {
});
it('Calendar should be selectable', () => {
MockDate.set(Moment('2000-01-01').valueOf());
const onSelect = jest.fn();
const onChange = jest.fn();
const wrapper = mount(<Calendar onSelect={onSelect} onChange={onChange} />);
wrapper.find('.ant-picker-cell').at(0).simulate('click');
const { container } = render(<Calendar onSelect={onSelect} onChange={onChange} />);
fireEvent.click(container.querySelector('.ant-picker-cell'));
expect(onSelect).toHaveBeenCalledWith(expect.anything());
const value = onSelect.mock.calls[0][0];
expect(Dayjs.isDayjs(value)).toBe(true);
expect(onChange).toHaveBeenCalled();
MockDate.reset();
});
it('only Valid range should be selectable', () => {

View File

@ -47,5 +47,5 @@ class App extends React.Component {
}
}
export default () => <App />;
export default App;
```

View File

@ -139,6 +139,8 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
};
const triggerChange = (date: DateType) => {
console.log('trigger change!!!', isSameDate(date, mergedValue));
console.log('trigger change!!!', String(date), String(mergedValue));
setMergedValue(date);
if (!isSameDate(date, mergedValue)) {

View File

@ -912,11 +912,12 @@ Array [
<div>
<div
class="ant-tabs-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<ul
aria-label="expanded dropdown"
class="ant-tabs-dropdown-menu ant-tabs-dropdown-menu-root ant-tabs-dropdown-menu-vertical"
data-dropdown-inject="true"
data-menu-list="true"
id="null-more-popup"
role="listbox"
@ -1065,11 +1066,12 @@ Array [
<div>
<div
class="ant-tabs-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<ul
aria-label="expanded dropdown"
class="ant-tabs-dropdown-menu ant-tabs-dropdown-menu-root ant-tabs-dropdown-menu-vertical"
data-dropdown-inject="true"
data-menu-list="true"
id="null-more-popup"
role="listbox"

View File

@ -4,6 +4,7 @@ import Card from '../index';
import Button from '../../button/index';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render } from '../../../tests/utils';
describe('Card', () => {
mountTest(Card);
@ -84,20 +85,14 @@ describe('Card', () => {
});
it('get ref of card', () => {
class WrapperComponent extends React.Component {
render() {
return (
<Card
// eslint-disable-next-line react/no-string-refs
ref="firstRef"
title="Card title"
>
<p>Card content</p>
</Card>
);
}
}
const wrapper = mount(<WrapperComponent />);
expect(wrapper.ref('firstRef').className.includes('ant-card')).toBe(true);
const cardRef = React.createRef();
render(
<Card ref={cardRef} title="Card title">
<p>Card content</p>
</Card>,
);
expect(cardRef.current).toHaveClass('ant-card');
});
});

View File

@ -64,5 +64,5 @@ class App extends React.Component {
}
}
export default () => <App />;
export default App;
```

View File

@ -8,6 +8,7 @@ import Row from '../row';
import Col from '../col';
import { ConfigContext } from '../config-provider';
import SizeContext from '../config-provider/SizeContext';
import useStyle from './style';
function getAction(actions: React.ReactNode[]) {
const actionList = actions.map((action, index) => (
@ -103,6 +104,7 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
} = props;
const prefixCls = getPrefixCls('card', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
const loadingBlockStyle =
bodyStyle.padding === 0 || bodyStyle.padding === '0px' ? { padding: 24 } : undefined;
@ -193,15 +195,16 @@ const Card = React.forwardRef((props: CardProps, ref: React.Ref<HTMLDivElement>)
[`${prefixCls}-rtl`]: direction === 'rtl',
},
className,
hashId,
);
return (
return wrapSSR(
<div ref={ref} {...divProps} className={classString}>
{head}
{coverDom}
{body}
{actionDom}
</div>
</div>,
);
}) as CardInterface;

View File

@ -1,308 +1,308 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
// @import '../../style/themes/index';
// @import '../../style/mixins/index';
@card-prefix-cls: ~'@{ant-prefix}-card';
@card-hoverable-hover-border: transparent;
@card-action-icon-size: 16px;
// @card-prefix-cls: ~'@{ant-prefix}-card';
// @card-hoverable-hover-border: transparent;
// @card-action-icon-size: 16px;
@gradient-min: fade(@card-skeleton-bg, 20%);
@gradient-max: fade(@card-skeleton-bg, 40%);
// @gradient-min: fade(@card-skeleton-bg, 20%);
// @gradient-max: fade(@card-skeleton-bg, 40%);
.@{card-prefix-cls} {
.reset-component();
// .@{card-prefix-cls} {
// .reset-component();
position: relative;
background: @card-background;
border-radius: @card-radius;
// position: relative;
// background: @card-background;
// border-radius: @card-radius;
&-rtl {
direction: rtl;
}
// &-rtl {
// direction: rtl;
// }
&-hoverable {
cursor: pointer;
transition: box-shadow 0.3s, border-color 0.3s;
// &-hoverable {
// cursor: pointer;
// transition: box-shadow 0.3s, border-color 0.3s;
&:hover {
border-color: @card-hoverable-hover-border;
box-shadow: @card-shadow;
}
}
// &:hover {
// border-color: @card-hoverable-hover-border;
// box-shadow: @card-shadow;
// }
// }
&-bordered {
border: @border-width-base @border-style-base @border-color-split;
}
// &-bordered {
// border: @border-width-base @border-style-base @border-color-split;
// }
&-head {
min-height: @card-head-height;
margin-bottom: -1px; // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png
padding: 0 @card-padding-base;
color: @card-head-color;
font-weight: 500;
font-size: @card-head-font-size;
background: @card-head-background;
border-bottom: @border-width-base @border-style-base @border-color-split;
border-radius: @card-radius @card-radius 0 0;
.clearfix();
// &-head {
// min-height: @card-head-height;
// margin-bottom: -1px; // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png
// padding: 0 @card-padding-base;
// color: @card-head-color;
// font-weight: 500;
// font-size: @card-head-font-size;
// background: @card-head-background;
// border-bottom: @border-width-base @border-style-base @border-color-split;
// border-radius: @card-radius @card-radius 0 0;
// .clearfix();
&-wrapper {
display: flex;
align-items: center;
}
// &-wrapper {
// display: flex;
// align-items: center;
// }
&-title {
display: inline-block;
flex: 1;
padding: @card-head-padding 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
// &-title {
// display: inline-block;
// flex: 1;
// padding: @card-head-padding 0;
// overflow: hidden;
// white-space: nowrap;
// text-overflow: ellipsis;
> .@{ant-prefix}-typography,
> .@{ant-prefix}-typography-edit-content {
left: 0;
margin-top: 0;
margin-bottom: 0;
}
}
// > .@{ant-prefix}-typography,
// > .@{ant-prefix}-typography-edit-content {
// left: 0;
// margin-top: 0;
// margin-bottom: 0;
// }
// }
.@{ant-prefix}-tabs-top {
clear: both;
margin-bottom: @card-head-tabs-margin-bottom;
color: @text-color;
font-weight: normal;
font-size: @font-size-base;
// .@{ant-prefix}-tabs-top {
// clear: both;
// margin-bottom: @card-head-tabs-margin-bottom;
// color: @text-color;
// font-weight: normal;
// font-size: @font-size-base;
&-bar {
border-bottom: @border-width-base @border-style-base @border-color-split;
}
}
}
// &-bar {
// border-bottom: @border-width-base @border-style-base @border-color-split;
// }
// }
// }
&-extra {
float: right;
// https://stackoverflow.com/a/22429853/3040605
margin-left: auto;
padding: @card-head-padding 0;
color: @card-head-extra-color;
font-weight: normal;
font-size: @font-size-base;
// &-extra {
// float: right;
// // https://stackoverflow.com/a/22429853/3040605
// margin-left: auto;
// padding: @card-head-padding 0;
// color: @card-head-extra-color;
// font-weight: normal;
// font-size: @font-size-base;
.@{card-prefix-cls}-rtl & {
margin-right: auto;
margin-left: 0;
}
}
// .@{card-prefix-cls}-rtl & {
// margin-right: auto;
// margin-left: 0;
// }
// }
&-body {
padding: @card-padding-base;
.clearfix();
}
// &-body {
// padding: @card-padding-base;
// .clearfix();
// }
&-contain-grid:not(&-loading) &-body {
margin: -1px 0 0 -1px;
padding: 0;
}
// &-contain-grid:not(&-loading) &-body {
// margin: -1px 0 0 -1px;
// padding: 0;
// }
&-grid {
float: left;
width: 33.33%;
padding: @card-padding-base;
border: 0;
border-radius: 0;
box-shadow: 1px 0 0 0 @border-color-split, 0 1px 0 0 @border-color-split,
1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
0 1px 0 0 @border-color-split inset;
transition: all 0.3s;
// &-grid {
// float: left;
// width: 33.33%;
// padding: @card-padding-base;
// border: 0;
// border-radius: 0;
// box-shadow: 1px 0 0 0 @border-color-split, 0 1px 0 0 @border-color-split,
// 1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
// 0 1px 0 0 @border-color-split inset;
// transition: all 0.3s;
.@{card-prefix-cls}-rtl & {
float: right;
}
// .@{card-prefix-cls}-rtl & {
// float: right;
// }
&-hoverable {
&:hover {
position: relative;
z-index: 1;
box-shadow: @card-shadow;
}
}
}
// &-hoverable {
// &:hover {
// position: relative;
// z-index: 1;
// box-shadow: @card-shadow;
// }
// }
// }
&-contain-tabs > &-head &-head-title {
min-height: @card-head-height - @card-head-padding;
padding-bottom: 0;
}
// &-contain-tabs > &-head &-head-title {
// min-height: @card-head-height - @card-head-padding;
// padding-bottom: 0;
// }
&-contain-tabs > &-head &-extra {
padding-bottom: 0;
}
// &-contain-tabs > &-head &-extra {
// padding-bottom: 0;
// }
&-bordered &-cover {
margin-top: -1px;
margin-right: -1px;
margin-left: -1px;
}
// &-bordered &-cover {
// margin-top: -1px;
// margin-right: -1px;
// margin-left: -1px;
// }
&-cover {
> * {
display: block;
width: 100%;
}
// &-cover {
// > * {
// display: block;
// width: 100%;
// }
img {
border-radius: @card-radius @card-radius 0 0;
}
}
// img {
// border-radius: @card-radius @card-radius 0 0;
// }
// }
&-actions {
margin: 0;
padding: 0;
list-style: none;
background: @card-actions-background;
border-top: @border-width-base @border-style-base @border-color-split;
.clearfix();
// &-actions {
// margin: 0;
// padding: 0;
// list-style: none;
// background: @card-actions-background;
// border-top: @border-width-base @border-style-base @border-color-split;
// .clearfix();
& > li {
float: left;
margin: @card-actions-li-margin;
color: @text-color-secondary;
text-align: center;
// & > li {
// float: left;
// margin: @card-actions-li-margin;
// color: @text-color-secondary;
// text-align: center;
.@{card-prefix-cls}-rtl & {
float: right;
}
// .@{card-prefix-cls}-rtl & {
// float: right;
// }
> span {
position: relative;
display: block;
min-width: 32px;
font-size: @font-size-base;
line-height: @line-height-base;
cursor: pointer;
// > span {
// position: relative;
// display: block;
// min-width: 32px;
// font-size: @font-size-base;
// line-height: @line-height-base;
// cursor: pointer;
&:hover {
color: @primary-color;
transition: color 0.3s;
}
// &:hover {
// color: @primary-color;
// transition: color 0.3s;
// }
a:not(.@{ant-prefix}-btn),
> .@{iconfont-css-prefix} {
display: inline-block;
width: 100%;
color: @text-color-secondary;
line-height: 22px;
transition: color 0.3s;
// a:not(.@{ant-prefix}-btn),
// > .@{iconfont-css-prefix} {
// display: inline-block;
// width: 100%;
// color: @text-color-secondary;
// line-height: 22px;
// transition: color 0.3s;
&:hover {
color: @primary-color;
}
}
// &:hover {
// color: @primary-color;
// }
// }
> .@{iconfont-css-prefix} {
font-size: @card-action-icon-size;
line-height: 22px;
}
}
// > .@{iconfont-css-prefix} {
// font-size: @card-action-icon-size;
// line-height: 22px;
// }
// }
&:not(:last-child) {
border-right: @border-width-base @border-style-base @border-color-split;
// &:not(:last-child) {
// border-right: @border-width-base @border-style-base @border-color-split;
.@{card-prefix-cls}-rtl & {
border-right: none;
border-left: @border-width-base @border-style-base @border-color-split;
}
}
}
}
// .@{card-prefix-cls}-rtl & {
// border-right: none;
// border-left: @border-width-base @border-style-base @border-color-split;
// }
// }
// }
// }
&-type-inner &-head {
padding: 0 @card-padding-base;
background: @background-color-light;
// &-type-inner &-head {
// padding: 0 @card-padding-base;
// background: @background-color-light;
&-title {
padding: @card-inner-head-padding 0;
font-size: @font-size-base;
}
}
// &-title {
// padding: @card-inner-head-padding 0;
// font-size: @font-size-base;
// }
// }
&-type-inner &-body {
padding: 16px @card-padding-base;
}
// &-type-inner &-body {
// padding: 16px @card-padding-base;
// }
&-type-inner &-extra {
padding: @card-inner-head-padding + 1.5px 0;
}
// &-type-inner &-extra {
// padding: @card-inner-head-padding + 1.5px 0;
// }
&-meta {
margin: -4px 0;
.clearfix();
// &-meta {
// margin: -4px 0;
// .clearfix();
&-avatar {
float: left;
padding-right: 16px;
// &-avatar {
// float: left;
// padding-right: 16px;
.@{card-prefix-cls}-rtl & {
float: right;
padding-right: 0;
padding-left: 16px;
}
}
// .@{card-prefix-cls}-rtl & {
// float: right;
// padding-right: 0;
// padding-left: 16px;
// }
// }
&-detail {
overflow: hidden;
// &-detail {
// overflow: hidden;
> div:not(:last-child) {
margin-bottom: @margin-xs;
}
}
// > div:not(:last-child) {
// margin-bottom: @margin-xs;
// }
// }
&-title {
overflow: hidden;
color: @card-head-color;
font-weight: 500;
font-size: @font-size-lg;
white-space: nowrap;
text-overflow: ellipsis;
}
// &-title {
// overflow: hidden;
// color: @card-head-color;
// font-weight: 500;
// font-size: @font-size-lg;
// white-space: nowrap;
// text-overflow: ellipsis;
// }
&-description {
color: @text-color-secondary;
}
}
// &-description {
// color: @text-color-secondary;
// }
// }
&-loading {
overflow: hidden;
}
// &-loading {
// overflow: hidden;
// }
&-loading &-body {
user-select: none;
}
// &-loading &-body {
// user-select: none;
// }
&-loading-content {
p {
margin: 0;
}
}
// &-loading-content {
// p {
// margin: 0;
// }
// }
&-loading-block {
height: 14px;
margin: 4px 0;
background: linear-gradient(90deg, @gradient-min, @gradient-max, @gradient-min);
background-size: 600% 600%;
border-radius: @card-radius;
animation: card-loading 1.4s ease infinite;
}
}
// &-loading-block {
// height: 14px;
// margin: 4px 0;
// background: linear-gradient(90deg, @gradient-min, @gradient-max, @gradient-min);
// background-size: 600% 600%;
// border-radius: @card-radius;
// animation: card-loading 1.4s ease infinite;
// }
// }
@keyframes card-loading {
0%,
100% {
background-position: 0 50%;
}
// @keyframes card-loading {
// 0%,
// 100% {
// background-position: 0 50%;
// }
50% {
background-position: 100% 50%;
}
}
// 50% {
// background-position: 100% 50%;
// }
// }
@import './size';
// @import './size';

View File

@ -1,7 +1,460 @@
import '../../style/index.less';
import './index.less';
// import '../../style/index.less';
// import './index.less';
// style dependencies
import '../../tabs/style';
import '../../row/style';
import '../../col/style';
// import '../../tabs/style';
// import '../../row/style';
// import '../../col/style';
// deps-lint-skip-all
import { CSSObject, Keyframes } from '@ant-design/cssinjs';
import { TinyColor } from '@ctrl/tinycolor';
import {
resetComponent,
GenerateStyle,
genComponentStyleHook,
FullToken,
mergeToken,
clearFix,
} from '../../_util/theme';
interface CardToken extends FullToken<'Card'> {
rootPrefixCls: string;
cardHoverableHoverBorder: string;
cardShadow: string;
cardHeadHeight: number;
cardHeadHeightSM: number;
cardHeadPadding: number;
cardPaddingBase: number;
cardHeadTabsMarginBottom: number;
cardInnerHeadPadding: number;
cardActionsLiMargin: string;
cardActionsIconSize: number;
cardSkeletonBg: string;
borderColorSplit: string;
backgroundColorLight: string;
gradientMin: string;
gradientMax: string;
}
// ============================== Motion ==============================
const antCardLoading = new Keyframes('antCardLoading', {
'0%, 100%': {
backgroundPosition: '0 50%',
},
'50%': {
backgroundPosition: '100% 50%',
},
});
// ============================== Styles ==============================
// ============================== Head ==============================
const genCardHeadStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const {
rootPrefixCls,
componentCls,
cardHoverableHoverBorder,
cardHeadHeight,
cardHeadPadding,
cardPaddingBase,
cardHeadTabsMarginBottom,
borderColorSplit,
} = token;
return {
minHeight: cardHeadHeight,
marginBottom: -1, // Fix card grid overflow bug: https://gw.alipayobjects.com/zos/rmsportal/XonYxBikwpgbqIQBeuhk.png
padding: `0 ${cardPaddingBase}px`,
color: token.colorTextHeading,
fontWeight: 500, // FIXME: hardcode in v4
fontSize: token.fontSizeLG,
background: cardHoverableHoverBorder,
borderBottom: `${token.controlLineWidth}px ${token.controlLineType} ${borderColorSplit}`,
borderRadius: `${token.radiusBase}px ${token.radiusBase}px 0 0`,
...clearFix(),
'&-wrapper': {
display: 'flex',
alignItems: 'center',
},
'&-title': {
display: 'inline-block',
flex: 1,
padding: `${cardHeadPadding}px 0`,
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
[`
> ${componentCls}-typography,
> ${componentCls}-typography-edit-content
`]: {
insetInlineStart: 0,
marginTop: 0,
marginBottom: 0,
},
},
[`.${rootPrefixCls}-tabs-top`]: {
clear: 'both',
marginBottom: cardHeadTabsMarginBottom,
color: token.colorText,
fontWeight: 'normal',
fontSize: token.fontSizeBase,
'&-bar': {
borderBottom: `${token.controlLineWidth}px ${token.controlLineType} ${borderColorSplit}`,
},
},
};
};
// ============================== Grid ==============================
const genCardGridStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { cardPaddingBase, borderColorSplit, cardShadow } = token;
return {
width: '33.33%', // FIXME: hardcode in v4
padding: cardPaddingBase,
border: 0,
borderRadius: 0,
boxShadow: `
1px 0 0 0 ${borderColorSplit},
0 1px 0 0 ${borderColorSplit},
1px 1px 0 0 ${borderColorSplit},
1px 0 0 0 ${borderColorSplit} inset,
0 1px 0 0 ${borderColorSplit} inset;
transition: all ${token.motionDurationSlow}
`, // FIXME: hardcode in v4
'&-hoverable:hover': {
position: 'relative',
zIndex: 1,
boxShadow: cardShadow,
},
};
};
// ============================== Actions ==============================
const genCardActionsStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, iconCls, cardActionsLiMargin, cardActionsIconSize, borderColorSplit } =
token;
return {
margin: 0,
padding: 0,
listStyle: 'none',
background: token.colorBgComponent,
borderTop: `${token.controlLineWidth}px ${token.controlLineType} ${borderColorSplit}`,
display: 'flex',
...clearFix(),
'& > li': {
margin: cardActionsLiMargin,
color: token.colorTextSecondary,
textAlign: 'center',
'> span': {
position: 'relative',
display: 'block',
minWidth: 32, // FIXME: hardcode in v4
fontSize: token.fontSizeBase,
lineHeight: token.lineHeight,
cursor: 'pointer',
'&:hover': {
color: token.colorPrimary,
transition: `color ${token.motionDurationSlow}`,
},
[`a:not(${componentCls}-btn), > ${iconCls}`]: {
display: 'inline-block',
width: '100%',
color: token.colorTextSecondary,
lineHeight: '22px', // FIXME: hardcode in v4
transition: `color ${token.motionDurationSlow}`,
'&:hover': {
color: token.colorPrimary,
},
},
[`> ${iconCls}`]: {
fontSize: cardActionsIconSize,
lineHeight: '22px', // FIXME: hardcode in v4
},
},
'&:not(:last-child)': {
borderInlineEnd: `${token.controlLineWidth}px ${token.controlLineType} ${borderColorSplit}`,
},
},
};
};
// ============================== Meta ==============================
const genCardMetaStyle: GenerateStyle<CardToken> = (token): CSSObject => ({
margin: '-4px 0', // FIXME: hardcode in v4
display: 'flex',
...clearFix(),
'&-avatar': {
paddingInlineEnd: 16, // FIXME: hardcode in v4
},
'&-detail': {
overflow: 'hidden',
'> div:not(:last-child)': {
marginBottom: token.marginXS,
},
},
'&-title': {
overflow: 'hidden',
color: token.colorTextHeading,
fontWeight: 500, // FIXME: hardcode in v4
fontSize: token.fontSizeLG,
whiteSpace: 'nowrap',
textOverflow: 'ellipsis',
},
'&-description': {
color: token.colorTextSecondary,
},
});
// ============================== Inner ==============================
const genCardTypeInnerStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, cardPaddingBase, backgroundColorLight, cardInnerHeadPadding } = token;
return {
[`${componentCls}-head`]: {
padding: `0 ${cardPaddingBase}px`,
background: backgroundColorLight,
'&-title': {
padding: `${cardInnerHeadPadding}px 0`,
fontSize: token.fontSizeBase,
},
},
[`${componentCls}-body`]: {
padding: `16px ${cardPaddingBase}px`, // FIXME: hardcode in v4
},
[`${componentCls}-extra`]: {
padding: `${cardInnerHeadPadding + 1.5}px 0`,
},
};
};
// ============================== Loading ==============================
const genCardLoadingStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, gradientMin, gradientMax } = token;
return {
overflow: 'hidden',
[`${componentCls}-body`]: {
userSelect: 'none',
},
[`${componentCls}-loading-content p`]: {
margin: 0,
},
[`${componentCls}-loading-block`]: {
height: 14, // FIXME: hardcode in v4
margin: '4px 0', // FIXME: hardcode in v4
background: `linear-gradient(90deg, ${gradientMin}, ${gradientMax}, ${gradientMin})`,
backgroundSize: '600% 600%',
borderRadius: token.radiusBase,
animationName: antCardLoading,
animationDuration: '1.4s', // FIXME: hardcode
animationTimingFunction: 'ease',
animationIterationCount: 'infinite',
},
};
};
// ============================== Basic ==============================
const genCardStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const {
componentCls,
cardHoverableHoverBorder,
cardShadow,
cardHeadHeight,
cardHeadPadding,
cardPaddingBase,
borderColorSplit,
} = token;
return {
[componentCls]: {
...resetComponent(token),
position: 'relative',
background: token.colorBgComponent,
borderRadius: token.radiusBase,
[`${componentCls}-head`]: genCardHeadStyle(token),
[`${componentCls}-extra`]: {
// https://stackoverflow.com/a/22429853/3040605
marginInlineStart: 'auto',
padding: '',
color: '',
fontWeight: 'normal',
fontSize: token.fontSizeBase,
},
[`${componentCls}-body`]: {
padding: cardPaddingBase,
...clearFix(),
},
[`${componentCls}-grid`]: genCardGridStyle(token),
[`${componentCls}-cover`]: {
'> *': {
display: 'block',
width: '100%',
},
img: {
borderRadius: `${token.radiusBase}px ${token.radiusBase}px 0 0`,
},
},
[`${componentCls}-actions`]: genCardActionsStyle(token),
[`${componentCls}-meta`]: genCardMetaStyle(token),
},
[`${componentCls}-bordered`]: {
border: `${token.controlLineWidth}px ${token.controlLineType} ${borderColorSplit}`,
[`${componentCls}-cover`]: {
marginTop: -1,
marginInlineStart: -1,
marginInlineEnd: -1,
},
},
[`${componentCls}-hoverable`]: {
cursor: 'pointer',
transition: `box-shadow ${token.motionDurationSlow}, border-color ${token.motionDurationSlow}`,
'&:hover': {
borderColor: cardHoverableHoverBorder,
boxShadow: cardShadow,
},
},
[`${componentCls}-contain-grid`]: {
[`${componentCls}-body`]: {
display: 'flex',
flexWrap: 'wrap',
},
[`&:not(${componentCls}-loading) ${componentCls}-body`]: {
margin: {
_skip_check_: true,
value: '-1px 0 0 -1px', // FIXME: hardcode in v4
},
padding: 0,
},
},
[`${componentCls}-contain-tabs`]: {
[`> ${componentCls}-head`]: {
[`${componentCls}-head-title`]: {
minHeight: cardHeadHeight - cardHeadPadding,
paddingBottom: 0,
},
[`${componentCls}-extra`]: {
paddingBottom: 0,
},
},
},
[`${componentCls}-type-inner`]: genCardTypeInnerStyle(token),
[`${componentCls}-loading`]: genCardLoadingStyle(token),
[`${componentCls}-rtl`]: {
direction: 'rtl',
},
};
};
// ============================== Size ==============================
const genCardSizeStyle: GenerateStyle<CardToken> = (token): CSSObject => {
const { componentCls, cardHeadHeightSM, cardPaddingBase, cardHeadPadding } = token;
const cardPaddingBaseSM = cardPaddingBase / 2;
const cardHeadPaddingSM = cardHeadPadding / 2;
return {
[`${componentCls}-small`]: {
[`> ${componentCls}-head`]: {
minHeight: cardHeadHeightSM,
padding: `0 ${cardPaddingBaseSM}px`,
fontSize: token.fontSizeBase,
[`> ${componentCls}-head-wrapper`]: {
[`> ${componentCls}-head-title`]: {
padding: `${cardHeadPaddingSM}px 0`,
},
[`> ${componentCls}-extra`]: {
padding: `${cardHeadPaddingSM}px 0`,
fontSize: token.fontSizeBase,
},
},
},
[`> ${componentCls}-body`]: {
padding: cardPaddingBaseSM,
},
},
};
};
// ============================== Export ==============================
export default genComponentStyleHook('Card', (token, { rootPrefixCls }) => {
const cardToken = mergeToken<CardToken>(token, {
rootPrefixCls,
cardHoverableHoverBorder: 'transparent', // FIXME: hardcode in v4
cardShadow: `
0 1px 2px -2px ${new TinyColor('rgba(0, 0, 0, 0.16)').toRgbString()},
0 3px 6px 0 ${new TinyColor('rgba(0, 0, 0, 0.12)').toRgbString()},
0 5px 12px 4px ${new TinyColor('rgba(0, 0, 0, 0.09)').toRgbString()}
`, // FIXME: hardcode in v4
cardHeadHeight: 48, // FIXME: hardcode in v4
cardHeadHeightSM: 30, // FIXME: hardcode in v4
cardHeadPadding: 16, // FIXME: hardcode in v4
cardPaddingBase: 24, // FIXME: hardcode in v4
cardHeadTabsMarginBottom: -17, // FIXME: hardcode in v4
cardInnerHeadPadding: 12, // FIXME: hardcode in v4
cardActionsLiMargin: '12px 0', // FIXME: hardcode in v4
cardActionsIconSize: 16, // FIXME: hardcode in v4
borderColorSplit: new TinyColor({ h: 0, s: 0, v: 94 }).toHexString(), // FIXME: hardcode in v4
backgroundColorLight: new TinyColor({ h: 0, s: 0, v: 98 }).toHexString(), // FIXME: hardcode in v4
gradientMin: new TinyColor('#cfd8dc').setAlpha(0.2).toRgbString(), // FIXME: hardcode in v4
gradientMax: new TinyColor('#cfd8dc').setAlpha(0.4).toRgbString(), // FIXME: hardcode in v4
});
return [
// Style
genCardStyle(cardToken),
// Size
genCardSizeStyle(cardToken),
];
});

View File

@ -1,20 +1,20 @@
.@{card-prefix-cls}-small {
> .@{card-prefix-cls}-head {
min-height: @card-head-height-sm;
padding: 0 @card-padding-base-sm;
font-size: @card-head-font-size-sm;
// .@{card-prefix-cls}-small {
// > .@{card-prefix-cls}-head {
// min-height: @card-head-height-sm;
// padding: 0 @card-padding-base-sm;
// font-size: @card-head-font-size-sm;
> .@{card-prefix-cls}-head-wrapper {
> .@{card-prefix-cls}-head-title {
padding: @card-head-padding-sm 0;
}
> .@{card-prefix-cls}-extra {
padding: @card-head-padding-sm 0;
font-size: @card-head-font-size-sm;
}
}
}
> .@{card-prefix-cls}-body {
padding: @card-padding-base-sm;
}
}
// > .@{card-prefix-cls}-head-wrapper {
// > .@{card-prefix-cls}-head-title {
// padding: @card-head-padding-sm 0;
// }
// > .@{card-prefix-cls}-extra {
// padding: @card-head-padding-sm 0;
// font-size: @card-head-font-size-sm;
// }
// }
// }
// > .@{card-prefix-cls}-body {
// padding: @card-padding-base-sm;
// }
// }

View File

@ -3,7 +3,7 @@ import { mount } from 'enzyme';
import Carousel from '..';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { sleep } from '../../../tests/utils';
import { sleep, render } from '../../../tests/utils';
describe('Carousel', () => {
mountTest(Carousel);
@ -99,21 +99,28 @@ describe('Carousel', () => {
describe('should active when children change', () => {
it('should active', () => {
const wrapper = mount(<Carousel />);
wrapper.setProps({
children: <div />,
});
wrapper.update();
expect(wrapper.find('.slick-active').length).toBeTruthy();
const { rerender, container } = render(<Carousel />);
expect(container.querySelector('.slick-active')).toBeFalsy();
// Update children
rerender(
<Carousel>
<div />
</Carousel>,
);
expect(container.querySelector('.slick-active')).toBeTruthy();
});
it('should keep initialSlide', () => {
const wrapper = mount(<Carousel initialSlide={1} />);
wrapper.setProps({
children: [<div key="1" />, <div key="2" />, <div key="3" />],
});
wrapper.update();
expect(wrapper.find('.slick-dots li').at(1).hasClass('slick-active')).toBeTruthy();
const { rerender, container } = render(<Carousel initialSlide={1} />);
rerender(
<Carousel initialSlide={1}>
<div key="1" />
<div key="2" />
<div key="3" />
</Carousel>,
);
expect(container.querySelectorAll('.slick-dots li')[1]).toHaveClass('slick-active');
});
});

View File

@ -31,7 +31,17 @@ export interface CarouselRef {
}
const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
({ dots = true, arrows = false, draggable = false, dotPosition = 'bottom', ...props }, ref) => {
(
{
dots = true,
arrows = false,
draggable = false,
dotPosition = 'bottom',
vertical = dotPosition === 'left' || dotPosition === 'right',
...props
},
ref,
) => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const slickRef = React.useRef<any>();
@ -61,6 +71,7 @@ const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
}, [props.children]);
const newProps = {
vertical,
...props,
};
@ -70,7 +81,6 @@ const Carousel = React.forwardRef<CarouselRef, CarouselProps>(
const prefixCls = getPrefixCls('carousel', newProps.prefixCls);
const dotsClass = 'slick-dots';
newProps.vertical = dotPosition === 'left' || dotPosition === 'right';
const enableDots = !!dots;
const dsClass = classNames(

View File

@ -1,20 +1,15 @@
// deps-lint-skip-all
import type { CSSObject } from '@ant-design/cssinjs';
import {
resetComponent,
GenerateStyle,
genComponentStyleHook,
FullToken,
mergeToken,
} from '../../_util/theme';
import { genComponentStyleHook, resetComponent } from '../../_util/theme';
import type { GenerateStyle, FullToken } from '../../_util/theme';
interface CarouselToken extends FullToken<'Carousel'> {
export interface ComponentToken {
carouselDotWidth: CSSObject['width'];
carouselDotHeight: CSSObject['height'];
carouselDotActiveWidth: CSSObject['width'];
}
const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
const genCarouselStyle: GenerateStyle<FullToken<'Carousel'>> = token => {
const { componentCls, antCls } = token;
return {
@ -127,7 +122,7 @@ const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
// FIXME hardcode in v4
width: 20,
height: 20,
marginTop: '-10px',
marginTop: -10,
padding: 0,
color: 'transparent',
fontSize: 0,
@ -157,7 +152,7 @@ const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
insetInlineStart: -25,
'&::before': {
content: '←',
content: '""',
},
},
@ -166,7 +161,7 @@ const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
insetInlineEnd: -25,
'&::before': {
content: '→',
content: '""',
},
},
@ -186,12 +181,12 @@ const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
'&-bottom': {
// FIXME hardcode in v4
bottom: '12px',
bottom: 12,
},
'&-top': {
// FIXME hardcode in v4
top: '12px',
top: 12,
bottom: 'auto',
},
@ -250,7 +245,7 @@ const genCarouselStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
};
};
const genCarouselVerticalStyle: GenerateStyle<CarouselToken, CSSObject> = token => {
const genCarouselVerticalStyle: GenerateStyle<FullToken<'Carousel'>> = token => {
const { componentCls } = token;
const reverseSizeOfDot = {
@ -298,7 +293,7 @@ const genCarouselVerticalStyle: GenerateStyle<CarouselToken, CSSObject> = token
};
};
const genCarouselRtlStyle: GenerateStyle<CarouselToken> = token => {
const genCarouselRtlStyle: GenerateStyle<FullToken<'Carousel'>> = token => {
const { componentCls } = token;
return [
@ -327,16 +322,13 @@ const genCarouselRtlStyle: GenerateStyle<CarouselToken> = token => {
};
// ============================== Export ==============================
export default genComponentStyleHook('Carousel', token => {
const carouselToken = mergeToken<CarouselToken>(token, {
export default genComponentStyleHook(
'Carousel',
token => [genCarouselStyle(token), genCarouselVerticalStyle(token), genCarouselRtlStyle(token)],
{
// FIXME
carouselDotWidth: 16,
carouselDotHeight: 3,
carouselDotActiveWidth: 24,
});
return [
genCarouselStyle(carouselToken),
genCarouselVerticalStyle(carouselToken),
genCarouselRtlStyle(carouselToken),
];
});
},
);

View File

@ -34,7 +34,7 @@ exports[`renders ./components/cascader/demo/basic.md extend context correctly 1`
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -182,7 +182,7 @@ exports[`renders ./components/cascader/demo/change-on-select.md extend context c
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -332,7 +332,7 @@ exports[`renders ./components/cascader/demo/custom-dropdown.md extend context co
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div>
@ -507,7 +507,7 @@ exports[`renders ./components/cascader/demo/custom-render.md extend context corr
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -660,7 +660,7 @@ exports[`renders ./components/cascader/demo/custom-trigger.md extend context cor
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -785,7 +785,7 @@ exports[`renders ./components/cascader/demo/default-value.md extend context corr
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -959,7 +959,7 @@ exports[`renders ./components/cascader/demo/disabled-option.md extend context co
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1109,7 +1109,7 @@ exports[`renders ./components/cascader/demo/fields-name.md extend context correc
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1257,7 +1257,7 @@ exports[`renders ./components/cascader/demo/hover.md extend context correctly 1`
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1405,7 +1405,7 @@ exports[`renders ./components/cascader/demo/lazy.md extend context correctly 1`]
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1570,7 +1570,7 @@ exports[`renders ./components/cascader/demo/multiple.md extend context correctly
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1792,7 +1792,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -1940,7 +1940,7 @@ exports[`renders ./components/cascader/demo/search.md extend context correctly 1
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2119,7 +2119,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2310,7 +2310,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2474,7 +2474,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2621,7 +2621,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2768,7 +2768,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -2928,7 +2928,7 @@ exports[`renders ./components/cascader/demo/status.md extend context correctly 1
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -3080,7 +3080,7 @@ exports[`renders ./components/cascader/demo/status.md extend context correctly 1
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div
@ -3192,7 +3192,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -3341,7 +3341,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -3472,7 +3472,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div
@ -3621,7 +3621,7 @@ Array [
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown"
style="opacity:0;pointer-events:none;min-width:auto"
style="opacity:0;min-width:auto"
>
<div>
<div

View File

@ -3,7 +3,7 @@
exports[`Cascader can be selected 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -135,7 +135,7 @@ exports[`Cascader can be selected 1`] = `
exports[`Cascader can be selected 2`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -285,7 +285,7 @@ exports[`Cascader can be selected 2`] = `
exports[`Cascader can be selected 3`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-leave ant-slide-up-leave-start ant-slide-up"
style="pointer-events: none; min-width: auto;"
style="min-width: auto; pointer-events: none;"
>
<div>
<div
@ -435,7 +435,7 @@ exports[`Cascader can be selected 3`] = `
exports[`Cascader can be selected in RTL direction 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-cascader-dropdown-rtl ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -567,7 +567,7 @@ exports[`Cascader can be selected in RTL direction 1`] = `
exports[`Cascader can be selected in RTL direction 2`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-cascader-dropdown-rtl ant-slide-up-leave ant-slide-up-leave-start ant-slide-up"
style="pointer-events: none; min-width: auto;"
style="min-width: auto; pointer-events: none;"
>
<div>
<div
@ -717,7 +717,7 @@ exports[`Cascader can be selected in RTL direction 2`] = `
exports[`Cascader can be selected in RTL direction 3`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-cascader-dropdown-rtl ant-slide-up-leave ant-slide-up-leave-start ant-slide-up"
style="pointer-events: none; min-width: auto;"
style="min-width: auto; pointer-events: none;"
>
<div>
<div
@ -914,7 +914,7 @@ exports[`Cascader legacy props should support showCheckedStrategy child 1`] = `
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -1068,7 +1068,7 @@ exports[`Cascader legacy props should support showCheckedStrategy parent 1`] = `
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -1175,7 +1175,7 @@ exports[`Cascader legacy props should support showCheckedStrategy parent 1`] = `
exports[`Cascader popup correctly with defaultValue 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -1359,7 +1359,7 @@ exports[`Cascader popup correctly with defaultValue RTL 1`] = `
<div>
<div
class="ant-select-dropdown ant-cascader-dropdown ant-cascader-dropdown-rtl ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: auto;"
style="opacity: 0; min-width: auto;"
>
<div>
<div
@ -1622,7 +1622,7 @@ exports[`Cascader rtl render component should be rendered correctly in RTL direc
exports[`Cascader should highlight keyword and filter when search in Cascader 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: 0;"
style="opacity: 0; min-width: 0;"
>
<div>
<div
@ -1682,7 +1682,7 @@ exports[`Cascader should highlight keyword and filter when search in Cascader 1`
exports[`Cascader should highlight keyword and filter when search in Cascader with same field name of label and value 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: 0;"
style="opacity: 0; min-width: 0;"
>
<div>
<div
@ -1747,7 +1747,7 @@ exports[`Cascader should highlight keyword and filter when search in Cascader wi
exports[`Cascader should render not found content 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: 0;"
style="opacity: 0; min-width: 0;"
>
<div>
<div
@ -1823,7 +1823,7 @@ exports[`Cascader should render not found content 1`] = `
exports[`Cascader should show not found content when options.length is 0 1`] = `
<div
class="ant-select-dropdown ant-cascader-dropdown ant-select-dropdown-empty ant-slide-up-appear ant-slide-up-appear-prepare ant-slide-up"
style="opacity: 0; pointer-events: none; min-width: 0;"
style="opacity: 0; min-width: 0;"
>
<div>
<div

View File

@ -66,5 +66,5 @@ const App = () => {
);
};
export default () => <App />;
export default App;
```

View File

@ -29,6 +29,7 @@
// }
// &-menu {
// flex-grow: 1;
// min-width: 111px;
// height: 180px;
// margin: 0;

View File

@ -21,7 +21,7 @@ export interface ComponentToken {
type CascaderToken = FullToken<'Cascader'>;
// =============================== Base ===============================
const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
const genBaseStyle: GenerateStyle<CascaderToken> = token => {
const { prefixCls, componentCls } = token;
const cascaderMenuItemCls = `${componentCls}-menu-item`;
const iconCls = `
@ -49,7 +49,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
{
[`${componentCls}-dropdown`]: [
// ==================== Checkbox ====================
getCheckboxStyle(`${prefixCls}-checkbox`, token, hashId!),
getCheckboxStyle(`${prefixCls}-checkbox`, token),
{
[componentCls]: {
// ================== Checkbox ==================
@ -81,6 +81,7 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
// >>> Menu
'&-menu': {
flexGrow: 1,
minWidth: token.controlItemWidth,
height: token.dropdownHeight,
margin: `-${token.paddingXS}px 0`,
@ -157,12 +158,8 @@ const genBaseStyle: GenerateStyle<CascaderToken> = (token, hashId) => {
};
// ============================== Export ==============================
export default genComponentStyleHook(
'Cascader',
(token, { hashId }) => [genBaseStyle(token, hashId)],
{
controlWidth: 184,
controlItemWidth: 111,
dropdownHeight: 180,
},
);
export default genComponentStyleHook('Cascader', token => [genBaseStyle(token)], {
controlWidth: 184,
controlItemWidth: 111,
dropdownHeight: 180,
});

View File

@ -25,7 +25,7 @@ exports[`CheckboxGroup passes prefixCls down to checkbox 1`] = `
</label>
<label
class="my-checkbox-wrapper my-checkbox-group-item"
style="font-size:12px"
style="font-size: 12px;"
>
<span
class="my-checkbox"

View File

@ -1,5 +1,5 @@
import React from 'react';
import { mount } from 'enzyme';
import { render, fireEvent } from '../../../tests/utils';
import Checkbox from '..';
import focusTest from '../../../tests/shared/focusTest';
import { resetWarned } from '../../_util/devWarning';
@ -15,12 +15,14 @@ describe('Checkbox', () => {
const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn();
const wrapper = mount(<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
const { container } = render(
<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />,
);
wrapper.find('label').simulate('mouseenter');
fireEvent.mouseEnter(container.querySelector('label'));
expect(onMouseEnter).toHaveBeenCalled();
wrapper.find('label').simulate('mouseleave');
fireEvent.mouseLeave(container.querySelector('label'));
expect(onMouseLeave).toHaveBeenCalled();
});
@ -28,7 +30,7 @@ describe('Checkbox', () => {
resetWarned();
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
mount(<Checkbox value />);
render(<Checkbox value />);
expect(errorSpy).toHaveBeenCalledWith(
'Warning: [antd: Checkbox] `value` is not a valid prop, do you mean `checked`?',
);

View File

@ -1,10 +1,10 @@
import React, { useState } from 'react';
import { mount, render } from 'enzyme';
import Collapse from '../../collapse';
import Table from '../../table';
import Checkbox from '../index';
import mountTest from '../../../tests/shared/mountTest';
import rtlTest from '../../../tests/shared/rtlTest';
import { render, fireEvent } from '../../../tests/utils';
import Input from '../../input';
describe('CheckboxGroup', () => {
@ -13,16 +13,16 @@ describe('CheckboxGroup', () => {
it('should work basically', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Checkbox.Group options={['Apple', 'Pear', 'Orange']} onChange={onChange} />,
);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChange).toHaveBeenCalledWith(['Apple']);
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear']);
wrapper.find('.ant-checkbox-input').at(2).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[2]);
expect(onChange).toHaveBeenCalledWith(['Apple', 'Pear', 'Orange']);
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChange).toHaveBeenCalledWith(['Apple', 'Orange']);
});
@ -34,12 +34,12 @@ describe('CheckboxGroup', () => {
{ label: 'Pear', value: 'Pear' },
];
const groupWrapper = mount(
const { container } = render(
<Checkbox.Group options={options} onChange={onChangeGroup} disabled />,
);
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChangeGroup).not.toHaveBeenCalled();
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChangeGroup).not.toHaveBeenCalled();
});
@ -51,17 +51,17 @@ describe('CheckboxGroup', () => {
{ label: 'Orange', value: 'Orange', disabled: true },
];
const groupWrapper = mount(<Checkbox.Group options={options} onChange={onChangeGroup} />);
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change');
const { container } = render(<Checkbox.Group options={options} onChange={onChangeGroup} />);
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChangeGroup).toHaveBeenCalledWith(['Apple']);
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChangeGroup).toHaveBeenCalledWith(['Apple']);
});
it('all children should have a name property', () => {
const wrapper = mount(<Checkbox.Group name="checkboxgroup" options={['Yes', 'No']} />);
wrapper.find('input[type="checkbox"]').forEach(el => {
expect(el.props().name).toEqual('checkboxgroup');
const { container } = render(<Checkbox.Group name="checkboxgroup" options={['Yes', 'No']} />);
[...container.querySelectorAll('input[type="checkbox"]')].forEach(el => {
expect(el.getAttribute('name')).toEqual('checkboxgroup');
});
});
@ -71,9 +71,9 @@ describe('CheckboxGroup', () => {
{ label: 'Orange', value: 'Orange', style: { fontSize: 12 } },
];
const wrapper = render(<Checkbox.Group prefixCls="my-checkbox" options={options} />);
const { container } = render(<Checkbox.Group prefixCls="my-checkbox" options={options} />);
expect(wrapper).toMatchSnapshot();
expect(container.firstChild).toMatchSnapshot();
});
it('should be controlled by value', () => {
@ -81,23 +81,22 @@ describe('CheckboxGroup', () => {
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange' },
];
const wrapper = mount(<Checkbox.Group options={options} />);
expect(wrapper.find('.ant-checkbox-checked').length).toBe(0);
wrapper.setProps({ value: ['Apple'] });
wrapper.update();
expect(wrapper.find('.ant-checkbox-checked').length).toBe(1);
const renderCheckbox = props => <Checkbox.Group {...props} />;
const { container, rerender } = render(renderCheckbox({ options }));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
rerender(renderCheckbox({ options, value: 'Apple' }));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
});
// https://github.com/ant-design/ant-design/issues/12642
it('should trigger onChange in sub Checkbox', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Checkbox.Group>
<Checkbox value="my" onChange={onChange} />
</Checkbox.Group>,
);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChange).toHaveBeenCalled();
expect(onChange.mock.calls[0][0].target.value).toEqual('my');
});
@ -105,40 +104,44 @@ describe('CheckboxGroup', () => {
// https://github.com/ant-design/ant-design/issues/16376
it('onChange should filter removed value', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
<Checkbox.Group defaultValue={[1]} onChange={onChange}>
<Checkbox key={1} value={1} />
<Checkbox key={2} value={2} />
</Checkbox.Group>,
);
wrapper.setProps({
children: [<Checkbox key={2} value={2} />],
});
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
rerender(
<Checkbox.Group defaultValue={[1]} onChange={onChange}>
<Checkbox key={2} value={2} />
</Checkbox.Group>,
);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(onChange).toHaveBeenCalledWith([2]);
});
it('checkbox should register value again after value changed', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container, rerender } = render(
<Checkbox.Group defaultValue={[1]} onChange={onChange}>
<Checkbox key={1} value={1} />
</Checkbox.Group>,
);
wrapper.setProps({
children: [<Checkbox key={1} value={2} />],
});
expect(wrapper.find('.ant-checkbox-input').at(0).prop('checked')).toBe(false);
rerender(
<Checkbox.Group defaultValue={[1]} onChange={onChange}>
<Checkbox key={1} value={2} />
</Checkbox.Group>,
);
expect(container.querySelector('.ant-checkbox-input')).toHaveAttribute('checked');
});
// https://github.com/ant-design/ant-design/issues/17297
it('onChange should keep the order of the original values', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Checkbox.Group onChange={onChange}>
<Checkbox key={1} value={1} />
<Checkbox key={2} value={2} />
@ -146,20 +149,19 @@ describe('CheckboxGroup', () => {
<Checkbox key={4} value={4} />
</Checkbox.Group>,
);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChange).toHaveBeenCalledWith([1]);
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChange).toHaveBeenCalledWith([1, 2]);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChange).toHaveBeenCalledWith([2]);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[0]);
expect(onChange).toHaveBeenCalledWith([1, 2]);
});
// https://github.com/ant-design/ant-design/issues/21134
it('should work when checkbox is wrapped by other components', () => {
const wrapper = mount(
const { container } = render(
<Checkbox.Group>
<Collapse bordered={false}>
<Collapse.Panel header="test panel">
@ -170,28 +172,31 @@ describe('CheckboxGroup', () => {
</Collapse>
</Checkbox.Group>,
);
wrapper.find('.ant-collapse-item').at(0).find('.ant-collapse-header').simulate('click');
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
expect(wrapper.find('.ant-checkbox-checked').length).toBe(1);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
expect(wrapper.find('.ant-checkbox-checked').length).toBe(0);
fireEvent.click(
container.querySelector('.ant-collapse-item').querySelector('.ant-collapse-header'),
);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(1);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(container.querySelectorAll('.ant-checkbox-checked').length).toBe(0);
});
it('skipGroup', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Checkbox.Group onChange={onChange}>
<Checkbox value={1} />
<Checkbox value={2} skipGroup />
</Checkbox.Group>,
);
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChange).not.toHaveBeenCalled();
});
it('Table rowSelection', () => {
const onChange = jest.fn();
const wrapper = mount(
const { container } = render(
<Checkbox.Group onChange={onChange}>
<Table
dataSource={[{ key: 1, value: '1' }]}
@ -200,27 +205,32 @@ describe('CheckboxGroup', () => {
/>
</Checkbox.Group>,
);
wrapper.find('.ant-checkbox-input').at(1).simulate('change');
fireEvent.click(container.querySelectorAll('.ant-checkbox-input')[1]);
expect(onChange).not.toHaveBeenCalled();
});
it('should get div ref', () => {
mount(
const refCalls = [];
render(
<Checkbox.Group
options={['Apple', 'Pear', 'Orange']}
ref={node => {
expect(node.nodeName).toBe('DIV');
refCalls.push(node);
}}
/>,
);
const [mountCall] = refCalls;
expect(mountCall.nodeName).toBe('DIV');
});
it('should support number option', () => {
const onChange = jest.fn();
const wrapper = mount(<Checkbox.Group options={[1, 'Pear', 'Orange']} onChange={onChange} />);
const { container } = render(
<Checkbox.Group options={[1, 'Pear', 'Orange']} onChange={onChange} />,
);
wrapper.find('.ant-checkbox-input').at(0).simulate('change');
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(onChange).toHaveBeenCalledWith([1]);
});
@ -246,17 +256,13 @@ describe('CheckboxGroup', () => {
);
};
const wrapper = mount(<Demo />);
wrapper.find('.ant-checkbox-input').first().simulate('change');
const { container } = render(<Demo />);
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(onChange).toHaveBeenCalledWith([]);
wrapper.find('.ant-checkbox-input').first().simulate('change');
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(onChange).toHaveBeenCalledWith(['length1']);
wrapper
.find('.ant-input')
.first()
.simulate('change', { target: { value: '' } });
wrapper.find('.ant-checkbox-input').first().simulate('change');
fireEvent.change(container.querySelector('.ant-input'), { target: { value: '' } });
fireEvent.click(container.querySelector('.ant-checkbox-input'));
expect(onChange).toHaveBeenCalledWith(['A']);
});
});

View File

@ -49,5 +49,5 @@ const App = () => {
);
};
export default () => <App />;
export default App;
```

View File

@ -70,5 +70,5 @@ class App extends React.Component {
}
}
export default () => <App />;
export default App;
```

View File

@ -26,7 +26,7 @@ const antCheckboxEffect = new Keyframes('antCheckboxEffect', {
});
// ============================== Styles ==============================
export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token, hashId) => {
export const genCheckboxStyle: GenerateStyle<CheckboxToken> = token => {
const { checkboxCls } = token;
const wrapperCls = `${checkboxCls}-wrapper`;
@ -197,7 +197,9 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token, hashId) =>
border: `${token.controlLineWidth}px ${token.controlLineType} ${token.colorPrimary}`,
borderRadius: token.controlRadius,
visibility: 'hidden',
animation: `${antCheckboxEffect.getName(hashId)} ${token.motionDurationSlow} ease-in-out`,
animationName: antCheckboxEffect,
animationDuration: token.motionDurationSlow,
animationTimingFunction: 'ease-in-out',
animationFillMode: 'backwards',
content: '""',
},
@ -241,14 +243,14 @@ export const genCheckboxStyle: GenerateStyle<CheckboxToken> = (token, hashId) =>
};
// ============================== Export ==============================
export function getStyle(prefixCls: string, token: FullToken<'Checkbox'>, hashId: string) {
export function getStyle(prefixCls: string, token: FullToken<'Checkbox'>) {
const checkboxToken: CheckboxToken = mergeToken<CheckboxToken>(token, {
checkboxCls: `.${prefixCls}`,
});
return [genCheckboxStyle(checkboxToken, hashId), antCheckboxEffect];
return [genCheckboxStyle(checkboxToken)];
}
export default genComponentStyleHook('Checkbox', (token, { prefixCls, hashId }) => [
getStyle(prefixCls, token, hashId),
export default genComponentStyleHook('Checkbox', (token, { prefixCls }) => [
getStyle(prefixCls, token),
]);

View File

@ -10,6 +10,7 @@ import CollapsePanel, { CollapsibleType } from './CollapsePanel';
import { ConfigContext } from '../config-provider';
import collapseMotion from '../_util/motion';
import { cloneElement } from '../_util/reactNode';
import useStyle from './style';
export type ExpandIconPosition = 'left' | 'right' | undefined;
@ -52,6 +53,7 @@ const Collapse: CollapseInterface = props => {
const { getPrefixCls, direction } = React.useContext(ConfigContext);
const { prefixCls: customizePrefixCls, className = '', bordered = true, ghost } = props;
const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const [wrapSSR, hashId] = useStyle(prefixCls);
const getIconPosition = () => {
const { expandIconPosition } = props;
@ -90,6 +92,7 @@ const Collapse: CollapseInterface = props => {
[`${prefixCls}-ghost`]: !!ghost,
},
className,
hashId,
);
const openMotion: CSSMotionProps = {
...collapseMotion,
@ -114,7 +117,7 @@ const Collapse: CollapseInterface = props => {
});
};
return (
return wrapSSR(
<RcCollapse
openMotion={openMotion}
{...props}
@ -123,7 +126,7 @@ const Collapse: CollapseInterface = props => {
className={collapseClassName}
>
{getItems()}
</RcCollapse>
</RcCollapse>,
);
};

View File

@ -19,6 +19,7 @@ export interface CollapsePanelProps {
id?: string;
extra?: React.ReactNode;
collapsible?: CollapsibleType;
children?: React.ReactNode;
}
const CollapsePanel: React.FC<CollapsePanelProps> = props => {

View File

@ -814,7 +814,7 @@ Array [
<div>
<div
class="ant-select-dropdown"
style="opacity:0;pointer-events:none"
style="opacity:0"
>
<div>
<div

View File

@ -51,7 +51,7 @@ export default () => (
margin-bottom: 24px;
overflow: hidden;
background: #f7f7f7;
border: 0px;
border: 0px !important;
border-radius: 2px;
}
```

View File

@ -82,5 +82,5 @@ class Demo extends React.Component {
}
}
export default () => <Demo />;
export default Demo;
```

View File

@ -1,162 +1,162 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
// @import '../../style/themes/index';
// @import '../../style/mixins/index';
@collapse-prefix-cls: ~'@{ant-prefix}-collapse';
// @collapse-prefix-cls: ~'@{ant-prefix}-collapse';
.@{collapse-prefix-cls} {
.reset-component();
// .@{collapse-prefix-cls} {
// .reset-component();
background-color: @collapse-header-bg;
border: @border-width-base @border-style-base @border-color-base;
border-bottom: 0;
border-radius: @collapse-panel-border-radius;
// background-color: @collapse-header-bg;
// border: @border-width-base @border-style-base @border-color-base;
// border-bottom: 0;
// border-radius: @collapse-panel-border-radius;
& > &-item {
border-bottom: @border-width-base @border-style-base @border-color-base;
// & > &-item {
// border-bottom: @border-width-base @border-style-base @border-color-base;
&:last-child {
&,
& > .@{collapse-prefix-cls}-header {
border-radius: 0 0 @collapse-panel-border-radius @collapse-panel-border-radius;
}
}
// &:last-child {
// &,
// & > .@{collapse-prefix-cls}-header {
// border-radius: 0 0 @collapse-panel-border-radius @collapse-panel-border-radius;
// }
// }
> .@{collapse-prefix-cls}-header {
position: relative; // Compatible with old version of antd, should remove in next version
display: flex;
flex-wrap: nowrap;
align-items: flex-start;
padding: @collapse-header-padding;
color: @heading-color;
line-height: @line-height-base;
cursor: pointer;
transition: all 0.3s, visibility 0s;
// > .@{collapse-prefix-cls}-header {
// position: relative; // Compatible with old version of antd, should remove in next version
// display: flex;
// flex-wrap: nowrap;
// align-items: flex-start;
// padding: @collapse-header-padding;
// color: @heading-color;
// line-height: @line-height-base;
// cursor: pointer;
// transition: all 0.3s, visibility 0s;
.@{collapse-prefix-cls}-arrow {
display: inline-block;
margin-right: @margin-sm;
font-size: @font-size-sm;
vertical-align: -1px;
// .@{collapse-prefix-cls}-arrow {
// display: inline-block;
// margin-right: @margin-sm;
// font-size: @font-size-sm;
// vertical-align: -1px;
& svg {
transition: transform 0.24s;
}
}
// & svg {
// transition: transform 0.24s;
// }
// }
.@{collapse-prefix-cls}-extra {
margin-left: auto;
}
// .@{collapse-prefix-cls}-extra {
// margin-left: auto;
// }
&:focus {
outline: none;
}
}
// &:focus {
// outline: none;
// }
// }
.@{collapse-prefix-cls}-header-collapsible-only {
cursor: default;
.@{collapse-prefix-cls}-header-text {
cursor: pointer;
}
}
// .@{collapse-prefix-cls}-header-collapsible-only {
// cursor: default;
// .@{collapse-prefix-cls}-header-text {
// cursor: pointer;
// }
// }
&.@{collapse-prefix-cls}-no-arrow {
> .@{collapse-prefix-cls}-header {
padding-left: @padding-sm;
}
}
}
// &.@{collapse-prefix-cls}-no-arrow {
// > .@{collapse-prefix-cls}-header {
// padding-left: @padding-sm;
// }
// }
// }
// Expand Icon right
&-icon-position-right {
& > .@{collapse-prefix-cls}-item {
> .@{collapse-prefix-cls}-header {
position: relative;
padding: @collapse-header-padding;
padding-right: @collapse-header-padding-extra;
// // Expand Icon right
// &-icon-position-right {
// & > .@{collapse-prefix-cls}-item {
// > .@{collapse-prefix-cls}-header {
// position: relative;
// padding: @collapse-header-padding;
// padding-right: @collapse-header-padding-extra;
.@{collapse-prefix-cls}-arrow {
position: absolute;
top: 50%;
right: @padding-md;
left: auto;
margin: 0;
transform: translateY(-50%);
}
}
}
}
// .@{collapse-prefix-cls}-arrow {
// position: absolute;
// top: 50%;
// right: @padding-md;
// left: auto;
// margin: 0;
// transform: translateY(-50%);
// }
// }
// }
// }
&-content {
color: @text-color;
background-color: @collapse-content-bg;
border-top: @border-width-base @border-style-base @border-color-base;
// &-content {
// color: @text-color;
// background-color: @collapse-content-bg;
// border-top: @border-width-base @border-style-base @border-color-base;
& > &-box {
padding: @collapse-content-padding;
}
// & > &-box {
// padding: @collapse-content-padding;
// }
&-hidden {
display: none;
}
}
// &-hidden {
// display: none;
// }
// }
&-item:last-child {
> .@{collapse-prefix-cls}-content {
border-radius: 0 0 @collapse-panel-border-radius @collapse-panel-border-radius;
}
}
// &-item:last-child {
// > .@{collapse-prefix-cls}-content {
// border-radius: 0 0 @collapse-panel-border-radius @collapse-panel-border-radius;
// }
// }
&-borderless {
background-color: @collapse-header-bg;
border: 0;
}
// &-borderless {
// background-color: @collapse-header-bg;
// border: 0;
// }
&-borderless > &-item {
border-bottom: 1px solid @border-color-base;
}
// &-borderless > &-item {
// border-bottom: 1px solid @border-color-base;
// }
&-borderless > &-item:last-child,
&-borderless > &-item:last-child &-header {
border-radius: 0;
}
// &-borderless > &-item:last-child,
// &-borderless > &-item:last-child &-header {
// border-radius: 0;
// }
// hide the last border-bottom in borderless mode
&-borderless > &-item:last-child {
border-bottom: 0;
}
// // hide the last border-bottom in borderless mode
// &-borderless > &-item:last-child {
// border-bottom: 0;
// }
&-borderless > &-item > &-content {
background-color: transparent;
border-top: 0;
}
// &-borderless > &-item > &-content {
// background-color: transparent;
// border-top: 0;
// }
&-borderless > &-item > &-content > &-content-box {
padding-top: 4px;
}
// &-borderless > &-item > &-content > &-content-box {
// padding-top: 4px;
// }
&-ghost {
background-color: transparent;
border: 0;
> .@{collapse-prefix-cls}-item {
border-bottom: 0;
> .@{collapse-prefix-cls}-content {
background-color: transparent;
border-top: 0;
> .@{collapse-prefix-cls}-content-box {
padding-top: 12px;
padding-bottom: 12px;
}
}
}
}
// &-ghost {
// background-color: transparent;
// border: 0;
// > .@{collapse-prefix-cls}-item {
// border-bottom: 0;
// > .@{collapse-prefix-cls}-content {
// background-color: transparent;
// border-top: 0;
// > .@{collapse-prefix-cls}-content-box {
// padding-top: 12px;
// padding-bottom: 12px;
// }
// }
// }
// }
& &-item-disabled > &-header {
&,
& > .arrow {
color: @disabled-color;
cursor: not-allowed;
}
}
}
// & &-item-disabled > &-header {
// &,
// & > .arrow {
// color: @disabled-color;
// cursor: not-allowed;
// }
// }
// }
@import './rtl';
// @import './rtl';

View File

@ -1,2 +1,246 @@
import '../../style/index.less';
import './index.less';
// import '../../style/index.less';
// import './index.less';
// deps-lint-skip-all
import { CSSInterpolation } from '@ant-design/cssinjs';
import {
GenerateStyle,
resetComponent,
genComponentStyleHook,
mergeToken,
FullToken,
} from '../../_util/theme';
type CollapseToken = FullToken<'Collapse'> & {
collapseContentBg: string;
collapseContentPadding: number;
collapseHeaderBg: string;
collapseHeaderPadding: string;
collapseHeaderPaddingExtra: number;
collapsePanelBorderRadius: number;
};
export const genBaseStyle: GenerateStyle<CollapseToken> = token => {
const {
componentCls,
collapseContentBg,
collapseContentPadding,
collapseHeaderBg,
collapseHeaderPadding,
collapseHeaderPaddingExtra,
collapsePanelBorderRadius,
controlLineWidth,
controlLineType,
colorBorder,
colorText,
colorTextHeading,
colorTextDisabled,
lineHeight,
marginSM,
padding,
paddingSM,
fontSizeSM,
} = token;
const borderBase = `${controlLineWidth}px ${controlLineType} ${colorBorder}`;
return {
[componentCls]: {
...resetComponent(token),
backgroundColor: collapseHeaderBg,
border: borderBase,
borderBottom: 0,
borderRadius: `${collapsePanelBorderRadius}px`,
[`&-rtl`]: {
direction: 'rtl',
},
[`& > ${componentCls}-item`]: {
borderBottom: borderBase,
[`&:last-child`]: {
[`
&,
& > ${componentCls}-header`]: {
borderRadius: `0 0 ${collapsePanelBorderRadius}px ${collapsePanelBorderRadius}px`,
},
},
[`> ${componentCls}-header`]: {
position: 'relative', // Compatible with old version of antd, should remove in next version
display: 'flex',
flexWrap: 'nowrap',
alignItems: 'flex-start',
padding: collapseHeaderPadding,
color: colorTextHeading,
lineHeight,
cursor: 'pointer',
transition: `all 0.3s, visibility 0s`,
[`${componentCls}-arrow`]: {
display: 'inline-block',
marginInlineEnd: marginSM,
fontSize: fontSizeSM,
// FIXME
verticalAlign: '-1px',
[`${componentCls}-rtl &`]: {},
[`& svg`]: {
// FIXME
transition: 'transform 0.24s',
},
},
[`${componentCls}-extra`]: {
marginInlineStart: 'auto',
},
[`&:focus`]: {
outline: 'none',
},
},
[`${componentCls}-header-collapsible-only`]: {
cursor: 'default',
[`${componentCls}-header-text`]: {
cursor: 'pointer',
},
},
[`&${componentCls}-no-arrow`]: {
[`> ${componentCls}-header`]: {
paddingInlineStart: paddingSM,
},
},
},
[`&-icon-position-right`]: {
[`& > ${componentCls}-item `]: {
[`> ${componentCls}-header`]: {
position: 'relative',
padding: collapseHeaderPadding,
paddingInlineEnd: collapseHeaderPaddingExtra,
[`${componentCls}-arrow`]: {
position: 'absolute',
top: '50%',
insetInlineEnd: padding,
insetInlineStart: 'auto',
margin: 0,
transform: 'translateY(-50%)',
},
},
},
},
[`${componentCls}-content`]: {
color: colorText,
backgroundColor: collapseContentBg,
borderTop: borderBase,
[`& > ${componentCls}-content-box`]: {
padding: collapseContentPadding,
},
[`&-hidden`]: {
display: 'none',
},
},
[`${componentCls}-item:last-child`]: {
[`> ${componentCls}-content`]: {
borderRadius: `0 0 ${collapsePanelBorderRadius}px ${collapsePanelBorderRadius}px`,
},
},
[`& ${componentCls}-item-disabled > ${componentCls}-header`]: {
[`
&,
& > .arrow
`]: {
color: colorTextDisabled,
cursor: 'not-allowed',
},
},
[`&-ghost`]: {
backgroundColor: 'transparent',
border: 0,
[`> ${componentCls}-item`]: {
borderBottom: 0,
[`> ${componentCls}-content`]: {
backgroundColor: 'transparent',
border: 0,
[`> ${componentCls}-content-box`]: {
// FIXME
paddingTop: 12,
paddingBottom: 12,
},
},
},
},
},
};
};
const genBorderlessStyle: GenerateStyle<CollapseToken> = token => {
const {
componentCls,
collapseHeaderBg,
colorBorder,
} = token;
return {
[`${componentCls}-borderless`]: {
backgroundColor: collapseHeaderBg,
border: 0,
[`> ${componentCls}-item`]: {
borderBottom: `1px solid ${colorBorder}`,
},
[`
> ${componentCls}-item:last-child,
> ${componentCls}-item:last-child ${componentCls}-header
`]: {
borderRadius: 0,
},
[`> ${componentCls}-item:last-child`]: {
borderBottom: 0,
},
[`> ${componentCls}-item > ${componentCls}-content`]: {
backgroundColor: 'transparent',
borderTop: 0,
},
[`> ${componentCls}-item > ${componentCls}-content > ${componentCls}-content-box`]: {
// FIXME
paddingTop: 4,
},
},
};
};
export const genCollapseStyle: GenerateStyle<CollapseToken> = (
token: CollapseToken,
): CSSInterpolation => [genBaseStyle(token), genBorderlessStyle(token)];
export default genComponentStyleHook('Collapse', token => {
const collapseToken = mergeToken<CollapseToken>(token, {
collapseContentBg: token.colorBgComponent,
collapseContentPadding: token.padding,
collapseHeaderBg: token.colorBgComponentSecondary,
collapseHeaderPadding: `${token.paddingSM}px ${token.padding}px`,
// FIXME
collapseHeaderPaddingExtra: 40,
collapsePanelBorderRadius: token.radiusBase,
});
return [genCollapseStyle(collapseToken)];
});

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