[
  {
    "path": ".all-contributorsrc",
    "content": "{\n  \"projectName\": \"downshift\",\n  \"projectOwner\": \"downshift-js\",\n  \"repoType\": \"github\",\n  \"files\": [\n    \"README.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"contributors\": [\n    {\n      \"login\": \"kentcdodds\",\n      \"name\": \"Kent C. Dodds\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/1500684?v=3\",\n      \"profile\": \"https://kentcdodds.com\",\n      \"contributions\": [\n        \"code\",\n        \"doc\",\n        \"infra\",\n        \"test\",\n        \"review\",\n        \"blog\",\n        \"bug\",\n        \"example\",\n        \"ideas\",\n        \"talk\"\n      ]\n    },\n    {\n      \"login\": \"ryanflorence\",\n      \"name\": \"Ryan Florence\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/100200?v=4\",\n      \"profile\": \"http://twitter.com/ryanflorence\",\n      \"contributions\": [\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"jaredly\",\n      \"name\": \"Jared Forsyth\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/112170?v=4\",\n      \"profile\": \"http://jaredforsyth.com\",\n      \"contributions\": [\n        \"ideas\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"jtmthf\",\n      \"name\": \"Jack Moore\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/8162598?v=4\",\n      \"profile\": \"https://github.com/jtmthf\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"souporserious\",\n      \"name\": \"Travis Arnold\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/2762082?v=4\",\n      \"profile\": \"https://souporserious.com/\",\n      \"contributions\": [\n        \"code\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"marcysutton\",\n      \"name\": \"Marcy Sutton\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1045233?v=4\",\n      \"profile\": \"http://marcysutton.com\",\n      \"contributions\": [\n        \"bug\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"tizmagik\",\n      \"name\": \"Jeremy Gayed\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/244704?v=4\",\n      \"profile\": \"http://www.jeremygayed.com\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"Haroenv\",\n      \"name\": \"Haroen Viaene\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/6270048?v=4\",\n      \"profile\": \"https://haroen.me\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"rezof\",\n      \"name\": \"monssef\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/15073300?v=4\",\n      \"profile\": \"https://github.com/rezof\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"FezVrasta\",\n      \"name\": \"Federico Zivolo\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/5382443?v=4\",\n      \"profile\": \"https://fezvrasta.github.io\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"divyenduz\",\n      \"name\": \"Divyendu Singh\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/746482?v=4\",\n      \"profile\": \"https://divyendusingh.com\",\n      \"contributions\": [\n        \"example\",\n        \"code\",\n        \"doc\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"salmanmanekia\",\n      \"name\": \"Muhammad Salman\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/841955?v=4\",\n      \"profile\": \"https://github.com/salmanmanekia\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"psicotropicos\",\n      \"name\": \"João Alberto\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/10820159?v=4\",\n      \"profile\": \"https://twitter.com/psicotropidev\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"bernard-lin\",\n      \"name\": \"Bernard Lin\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/16327281?v=4\",\n      \"profile\": \"https://github.com/bernard-lin\",\n      \"contributions\": [\n        \"code\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"geoffdavis92\",\n      \"name\": \"Geoff Davis\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/7330124?v=4\",\n      \"profile\": \"https://geoffdavis.info\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"reznord\",\n      \"name\": \"Anup\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/3415488?v=4\",\n      \"profile\": \"https://github.com/reznord\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"ferdinandsalis\",\n      \"name\": \"Ferdinand Salis\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/340520?v=4\",\n      \"profile\": \"http://ferdinandsalis.com\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"tkh44\",\n      \"name\": \"Kye Hohenberger\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/662750?v=4\",\n      \"profile\": \"https://github.com/tkh44\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"jgoux\",\n      \"name\": \"Julien Goux\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1443499?v=4\",\n      \"profile\": \"https://github.com/jgoux\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"jseminck\",\n      \"name\": \"Joachim Seminck\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/9586897?v=4\",\n      \"profile\": \"https://github.com/jseminck\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"the-simian\",\n      \"name\": \"Jesse Harlin\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/954596?v=4\",\n      \"profile\": \"http://jesseharlin.net/\",\n      \"contributions\": [\n        \"bug\",\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"pbomb\",\n      \"name\": \"Matt Parrish\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1402095?v=4\",\n      \"profile\": \"https://github.com/pbomb\",\n      \"contributions\": [\n        \"tool\",\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"thomhos\",\n      \"name\": \"thom\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/11661846?v=4\",\n      \"profile\": \"http://thom.kr\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"vutran\",\n      \"name\": \"Vu Tran\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/1088312?v=4\",\n      \"profile\": \"http://twitter.com/tranvu\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"codiemullins\",\n      \"name\": \"Codie Mullins\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/74193?v=4\",\n      \"profile\": \"https://github.com/codiemullins\",\n      \"contributions\": [\n        \"code\",\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"morajabi\",\n      \"name\": \"Mohammad Rajabifard\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/12202757?v=4\",\n      \"profile\": \"https://morajabi.me\",\n      \"contributions\": [\n        \"doc\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"tansongyang\",\n      \"name\": \"Frank Tan\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/9488719?v=4\",\n      \"profile\": \"https://github.com/tansongyang\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"srph\",\n      \"name\": \"Kier Borromeo\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/5093058?v=4\",\n      \"profile\": \"https://kierb.com\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"paul-veevers\",\n      \"name\": \"Paul Veevers\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/8969456?v=4\",\n      \"profile\": \"https://github.com/paul-veevers\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Ronolibert\",\n      \"name\": \"Ron Cruz\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/13622298?v=4\",\n      \"profile\": \"https://github.com/Ronolibert\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"rickMcGavin\",\n      \"name\": \"Rick McGavin\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/13605633?v=4\",\n      \"profile\": \"http://rickmcgavin.github.io\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"vejersele\",\n      \"name\": \"Jelle Versele\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/869669?v=4\",\n      \"profile\": \"http://twitter.com/vejersele\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"brentertz\",\n      \"name\": \"Brent Ertz\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/202773?v=4\",\n      \"profile\": \"https://github.com/brentertz\",\n      \"contributions\": [\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"Dajust\",\n      \"name\": \"Justice Mba \",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/8015514?v=4\",\n      \"profile\": \"https://github.com/Dajust\",\n      \"contributions\": [\n        \"code\",\n        \"doc\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"ellismarkf\",\n      \"name\": \"Mark Ellis\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/3925281?v=4\",\n      \"profile\": \"http://mfellis.com\",\n      \"contributions\": [\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"usandfriends\",\n      \"name\": \"us͡an̸df͘rien͜ds͠\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/3241922?v=4\",\n      \"profile\": \"http://ronak.io/\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"robin-drexler\",\n      \"name\": \"Robin Drexler\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/474248?v=4\",\n      \"profile\": \"https://www.robin-drexler.com/\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"arturoromeroslc\",\n      \"name\": \"Arturo Romero\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/7406639?v=4\",\n      \"profile\": \"http://arturoromero.info/\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"yp\",\n      \"name\": \"yp\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/275483?v=4\",\n      \"profile\": \"http://algolab.eu/pirola\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"ifyoumakeit\",\n      \"name\": \"Dave Garwacke\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/3998604?v=4\",\n      \"profile\": \"http://www.warbyparker.com\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"Drapegnik\",\n      \"name\": \"Ivan Pazhitnykh\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/11758660?v=4\",\n      \"profile\": \"http://linkedin.com/in/drapegnik\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"Rendez\",\n      \"name\": \"Luis Merino\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/61776?v=4\",\n      \"profile\": \"https://github.com/Rendez\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"arahansen\",\n      \"name\": \"Andrew Hansen\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/8746094?v=4\",\n      \"profile\": \"http://twitter.com/arahansen\",\n      \"contributions\": [\n        \"code\",\n        \"test\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"Jwhiles\",\n      \"name\": \"John Whiles\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/20307225?v=4\",\n      \"profile\": \"http://www.johnwhiles.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"wKovacs64\",\n      \"name\": \"Justin Hall\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/1288694?v=4\",\n      \"profile\": \"https://github.com/wKovacs64\",\n      \"contributions\": [\n        \"infra\"\n      ]\n    },\n    {\n      \"login\": \"petetnt\",\n      \"name\": \"Pete Nykänen\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/7641760?v=4\",\n      \"profile\": \"https://twitter.com/pete_tnt\",\n      \"contributions\": [\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"jaredpalmer\",\n      \"name\": \"Jared Palmer\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/4060187?v=4\",\n      \"profile\": \"http://jaredpalmer.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"philipyoungg\",\n      \"name\": \"Philip Young\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/11477718?v=4\",\n      \"profile\": \"http://www.philipyoungg.com\",\n      \"contributions\": [\n        \"code\",\n        \"test\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"alexandernanberg\",\n      \"name\": \"Alexander Nanberg\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/8997319?v=4\",\n      \"profile\": \"https://alexandernanberg.com\",\n      \"contributions\": [\n        \"doc\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"httpete-ire\",\n      \"name\": \"Pete Redmond\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/1556430?v=4\",\n      \"profile\": \"https://httpete.com\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"Zashy\",\n      \"name\": \"Nick Lavin\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/1706342?v=4\",\n      \"profile\": \"https://github.com/Zashy\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"jlongster\",\n      \"name\": \"James Long\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/17031?v=4\",\n      \"profile\": \"http://jlongster.com\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"cycomachead\",\n      \"name\": \"Michael Ball\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1505907?v=4\",\n      \"profile\": \"http://michaelball.co\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"Julienng\",\n      \"name\": \"CAVALEIRO Julien\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/8990614?v=4\",\n      \"profile\": \"https://github.com/Julienng\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"kimgronqvist\",\n      \"name\": \"Kim Grönqvist\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/3421067?v=4\",\n      \"profile\": \"http://www.kimgronqvist.se\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"tiansijie\",\n      \"name\": \"Sijie\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/3675602?v=4\",\n      \"profile\": \"http://sijietian.com\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"donysukardi\",\n      \"name\": \"Dony Sukardi\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/410792?v=4\",\n      \"profile\": \"http://dsds.io\",\n      \"contributions\": [\n        \"example\",\n        \"question\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"dmmulroy\",\n      \"name\": \"Dillon Mulroy\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/2755722?v=4\",\n      \"profile\": \"https://dillonmulroy.com\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"curtiswilkinson\",\n      \"name\": \"Curtis Tate Wilkinson\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/12440573?v=4\",\n      \"profile\": \"https://twitter.com/curtytate\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"brikou\",\n      \"name\": \"Brice BERNARD\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/383212?v=4\",\n      \"profile\": \"https://github.com/brikou\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"xutopia\",\n      \"name\": \"Tony Xu\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/14304503?v=4\",\n      \"profile\": \"https://github.com/xutopia\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"newyork-anthonyng\",\n      \"name\": \"Anthony Ng\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/14035529?v=4\",\n      \"profile\": \"http://anthonyng.me\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"notruth\",\n      \"name\": \"S S\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/11996139?v=4\",\n      \"profile\": \"https://github.com/notruth\",\n      \"contributions\": [\n        \"question\",\n        \"code\",\n        \"doc\",\n        \"ideas\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"austintackaberry\",\n      \"name\": \"Austin Tackaberry\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/29493001?v=4\",\n      \"profile\": \"http://austintackaberry.co\",\n      \"contributions\": [\n        \"question\",\n        \"code\",\n        \"doc\",\n        \"bug\",\n        \"example\",\n        \"ideas\",\n        \"review\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"jduthon\",\n      \"name\": \"Jean Duthon\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/4168055?v=4\",\n      \"profile\": \"https://github.com/jduthon\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Antontelesh\",\n      \"name\": \"Anton Telesh\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/3889580?v=4\",\n      \"profile\": \"http://antontelesh.github.io\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"ericedem\",\n      \"name\": \"Eric Edem\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/1060669?v=4\",\n      \"profile\": \"https://github.com/ericedem\",\n      \"contributions\": [\n        \"code\",\n        \"doc\",\n        \"ideas\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"indiesquidge\",\n      \"name\": \"Austin Wood\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/3409645?v=4\",\n      \"profile\": \"https://github.com/indiesquidge\",\n      \"contributions\": [\n        \"question\",\n        \"doc\",\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"mmmurray\",\n      \"name\": \"Mark Murray\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/14275790?v=4\",\n      \"profile\": \"https://github.com/mmmurray\",\n      \"contributions\": [\n        \"infra\"\n      ]\n    },\n    {\n      \"login\": \"gsimone\",\n      \"name\": \"Gianmarco\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1862172?v=4\",\n      \"profile\": \"https://github.com/gsimone\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"pastr\",\n      \"name\": \"Emmanuel Pastor\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/6838136?v=4\",\n      \"profile\": \"https://github.com/pastr\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"dalehurwitz\",\n      \"name\": \"dalehurwitz\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/10345034?v=4\",\n      \"profile\": \"https://github.com/dalehurwitz\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"blobor\",\n      \"name\": \"Bogdan Lobor\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/4813007?v=4\",\n      \"profile\": \"https://github.com/blobor\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"infiniteluke\",\n      \"name\": \"Luke Herrington\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/1127238?v=4\",\n      \"profile\": \"https://github.com/infiniteluke\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"drobannx\",\n      \"name\": \"Brandon Clemons\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/6361167?v=4\",\n      \"profile\": \"https://github.com/drobannx\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"aMollusk\",\n      \"name\": \"Kieran\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/10591587?v=4\",\n      \"profile\": \"https://github.com/aMollusk\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"Brushedoctopus\",\n      \"name\": \"Brushedoctopus\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/11570627?v=4\",\n      \"profile\": \"https://github.com/Brushedoctopus\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"cameronprattedwards\",\n      \"name\": \"Cameron Edwards\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/5456216?v=4\",\n      \"profile\": \"http://cameronpedwards.com\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"stereobooster\",\n      \"name\": \"stereobooster\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/179534?v=4\",\n      \"profile\": \"https://github.com/stereobooster\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"1Copenut\",\n      \"name\": \"Trevor Pierce\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/934879?v=4\",\n      \"profile\": \"https://github.com/1Copenut\",\n      \"contributions\": [\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"franklixuefei\",\n      \"name\": \"Xuefei Li\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/1334982?v=4\",\n      \"profile\": \"http://xuefei-frank.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"alfredringstad\",\n      \"name\": \"Alfred Ringstad\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/7252803?v=4\",\n      \"profile\": \"https://hyperlab.se\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"dovidweisz\",\n      \"name\": \"D[oa]vid Weisz\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/6895497?v=4\",\n      \"profile\": \"https://github.com/dovidweisz\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"RoystonS\",\n      \"name\": \"Royston Shufflebotham\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/19773?v=4\",\n      \"profile\": \"https://github.com/RoystonS\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"MichaelDeBoey\",\n      \"name\": \"Michaël De Boey\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/6643991?v=4\",\n      \"profile\": \"http://michaeldeboey.be\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"EricHenry\",\n      \"name\": \"Henry\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/4412771?v=4\",\n      \"profile\": \"https://github.com/EricHenry\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"green-arrow\",\n      \"name\": \"Andrew Walton\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/2180127?v=4\",\n      \"profile\": \"http://www.greenarrow.me\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"arthurdenner\",\n      \"name\": \"Arthur Denner\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/13774309?v=4\",\n      \"profile\": \"https://github.com/arthurdenner\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"stipsan\",\n      \"name\": \"Cody Olsen\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/81981?v=4\",\n      \"profile\": \"http://twitter.com/stipsan\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"TLadd\",\n      \"name\": \"Thomas Ladd\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/5084492?v=4\",\n      \"profile\": \"https://github.com/TLadd\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"lixualinta\",\n      \"name\": \"lixualinta\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/34634369?v=4\",\n      \"profile\": \"https://github.com/lixualinta\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"JCofman\",\n      \"name\": \"Jacob Cofman\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/2118956?v=4\",\n      \"profile\": \"https://twitter.com/JCofman\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"jf248\",\n      \"name\": \"Joshua Freedman\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/19275184?v=4\",\n      \"profile\": \"https://github.com/jf248\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"AmyScript\",\n      \"name\": \"Amy\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/24494020?v=4\",\n      \"profile\": \"https://github.com/AmyScript\",\n      \"contributions\": [\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"roginfarrer\",\n      \"name\": \"Rogin Farrer\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/9063669?v=4\",\n      \"profile\": \"http://twitter.com/roginfarrer\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"rifler\",\n      \"name\": \"Dmitrii Kanatnikov\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/871583\",\n      \"profile\": \"https://github.com/rifler\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"dallonf\",\n      \"name\": \"Dallon Feldner\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/346300?v=4\",\n      \"profile\": \"https://github.com/dallonf\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"samuelfullerthomas\",\n      \"name\": \"Samuel Fuller Thomas\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/10165959?v=4\",\n      \"profile\": \"https://samuelfullerthomas.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"audiolion\",\n      \"name\": \"Ryan Castner\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/2430381?v=4\",\n      \"profile\": \"http://audiolion.github.io\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"silviuavram\",\n      \"name\": \"Silviu Alexandru Avram\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/11275392?v=4\",\n      \"profile\": \"https://github.com/silviuavram\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"akronb\",\n      \"name\": \"Anton Volkov\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/15676655?v=4\",\n      \"profile\": \"https://github.com/akronb\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"keeganstreet\",\n      \"name\": \"Keegan Street\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/513363?v=4\",\n      \"profile\": \"http://keegan.st\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"mdugue\",\n      \"name\": \"Manuel Dugué\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/894149?v=4\",\n      \"profile\": \"http://manueldugue.de\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"mkaradeniz\",\n      \"name\": \"Max Karadeniz\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/12477983?v=4\",\n      \"profile\": \"https://github.com/mkaradeniz\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"GonchuB\",\n      \"name\": \"Gonzalo Beviglia\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/857221?v=4\",\n      \"profile\": \"https://medium.com/@gonchub\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"review\"\n      ]\n    },\n    {\n      \"login\": \"kilrain\",\n      \"name\": \"Brian Kilrain\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/47700687?v=4\",\n      \"profile\": \"https://github.com/kilrain\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\",\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"rincedd\",\n      \"name\": \"Gerd Zschaler\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/321265?v=4\",\n      \"profile\": \"http://www.gzschaler.de\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"gaskar\",\n      \"name\": \"Karen Gasparyan\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/491166?v=4\",\n      \"profile\": \"https://github.com/gaskar\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"kserjey\",\n      \"name\": \"Sergey Korchinskiy\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/19753880?v=4\",\n      \"profile\": \"https://github.com/kserjey\",\n      \"contributions\": [\n        \"bug\",\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"edygar\",\n      \"name\": \"Edygar Oliveira\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/566280?v=3\",\n      \"profile\": \"https://github.com/edygar\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"epeicher\",\n      \"name\": \"epeicher\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/3519124?v=4\",\n      \"profile\": \"https://github.com/epeicher\",\n      \"contributions\": [\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"francoischalifour\",\n      \"name\": \"François Chalifour\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/6137112?v=4\",\n      \"profile\": \"https://francoischalifour.com\",\n      \"contributions\": [\n        \"code\",\n        \"test\",\n        \"platform\"\n      ]\n    },\n    {\n      \"login\": \"maxmalov\",\n      \"name\": \"Maxim Malov\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/284129?v=4\",\n      \"profile\": \"https://github.com/maxmalov\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"epiqueras\",\n      \"name\": \"Enrique Piqueras\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/19157096?v=4\",\n      \"profile\": \"https://epiqueras.github.io\",\n      \"contributions\": [\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"layershifter\",\n      \"name\": \"Oleksandr Fediashov\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/14183168?v=4\",\n      \"profile\": \"https://twitter.com/layershifter\",\n      \"contributions\": [\n        \"code\",\n        \"infra\",\n        \"ideas\"\n      ]\n    },\n    {\n      \"login\": \"saitonakamura\",\n      \"name\": \"Mikhail Bashurov\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/1552189?v=4\",\n      \"profile\": \"https://github.com/saitonakamura\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"jgodi\",\n      \"name\": \"Joshua Godi\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/870799?v=4\",\n      \"profile\": \"http://www.joshuagodi.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"lukyth\",\n      \"name\": \"Kanitkorn Sujautra\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/7040242?v=4\",\n      \"profile\": \"https://github.com/lukyth\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"jorgemoya\",\n      \"name\": \"Jorge Moya\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/196129?v=4\",\n      \"profile\": \"https://github.com/jorgemoya\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"KubaJastrz\",\n      \"name\": \"Jakub Jastrzębski\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/6443113?v=4\",\n      \"profile\": \"https://kubajastrz.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"mufasa71\",\n      \"name\": \"Shukhrat Mukimov\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/626420?v=4\",\n      \"profile\": \"https://github.com/mufasa71\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"jhonnymoreira\",\n      \"name\": \"Jhonny Moreira\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/2177742?v=4\",\n      \"profile\": \"http://jhonnymoreira.dev\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"stefanprobst\",\n      \"name\": \"stefanprobst\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/20753323?v=4\",\n      \"profile\": \"https://github.com/stefanprobst\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"louisaspicer\",\n      \"name\": \"Louisa Spicer\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/20270031?v=4\",\n      \"profile\": \"https://github.com/louisaspicer\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"neet\",\n      \"name\": \"Ryō Igarashi\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/19276905?v=4\",\n      \"profile\": \"https://neet.love\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"rlue\",\n      \"name\": \"Ryan Lue\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/12194123?v=4\",\n      \"profile\": \"http://ryanlue.com/\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"mattleonowicz\",\n      \"name\": \"Mateusz Leonowicz\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/9438872?v=4\",\n      \"profile\": \"https://github.com/mattleonowicz\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"atomicpages\",\n      \"name\": \"Dennis Thompson\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/1824291?v=4\",\n      \"profile\": \"https://github.com/atomicpages\",\n      \"contributions\": [\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"mayicodefuture\",\n      \"name\": \"Maksym Boytsov\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/32408893?v=4\",\n      \"profile\": \"https://mayicodefuture.live\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"IwalkAlone\",\n      \"name\": \"Sergey Skrynnikov\",\n      \"avatar_url\": \"https://avatars1.githubusercontent.com/u/5685800?v=4\",\n      \"profile\": \"http://dataart.com\",\n      \"contributions\": [\n        \"code\",\n        \"test\"\n      ]\n    },\n    {\n      \"login\": \"vvo\",\n      \"name\": \"Vincent Voyer\",\n      \"avatar_url\": \"https://avatars0.githubusercontent.com/u/123822?v=4\",\n      \"profile\": \"https://www.linkedin.com/in/vvoyer\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"limejoe\",\n      \"name\": \"limejoe\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/7977551?v=4\",\n      \"profile\": \"https://github.com/limejoe\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"k88manish\",\n      \"name\": \"Manish Kumar\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/19614770?v=4\",\n      \"profile\": \"https://github.com/k88manish\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"fcrezza\",\n      \"name\": \"Anang Fachreza\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/48123020?v=4\",\n      \"profile\": \"https://github.com/fcrezza\",\n      \"contributions\": [\n        \"doc\",\n        \"example\"\n      ]\n    },\n    {\n      \"login\": \"ndeom\",\n      \"name\": \"Nick Deom\",\n      \"avatar_url\": \"https://avatars2.githubusercontent.com/u/56491159?v=4\",\n      \"profile\": \"http://nickdeom.com\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"clementgarbay\",\n      \"name\": \"Clément Garbay\",\n      \"avatar_url\": \"https://avatars3.githubusercontent.com/u/12433625?v=4\",\n      \"profile\": \"https://github.com/clementgarbay\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"KaiminHuang\",\n      \"name\": \"Kaimin Huang\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/5600404?v=4\",\n      \"profile\": \"https://github.com/KaiminHuang\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"davewelling\",\n      \"name\": \"David Welling\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/1242456?v=4\",\n      \"profile\": \"http://theredcircuit.com\",\n      \"contributions\": [\n        \"code\",\n        \"bug\",\n        \"ideas\",\n        \"research\"\n      ]\n    },\n    {\n      \"login\": \"chandrasekhar1996\",\n      \"name\": \"chandrasekhar1996\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/33996892?v=4\",\n      \"profile\": \"https://github.com/chandrasekhar1996\",\n      \"contributions\": [\n        \"bug\",\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"drewbrend\",\n      \"name\": \"Brendan Drew\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/5375799?v=4\",\n      \"profile\": \"https://github.com/drewbrend\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"jeanpan\",\n      \"name\": \"Jean Pan\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/1307026?v=4\",\n      \"profile\": \"https://github.com/jeanpan\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"tjenkinson\",\n      \"name\": \"Tom Jenkinson\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/3259993?v=4\",\n      \"profile\": \"https://tjenkinson.me\",\n      \"contributions\": [\n        \"infra\"\n      ]\n    },\n    {\n      \"login\": \"aliceHendicott\",\n      \"name\": \"Alice Hendicott\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/40346716?v=4\",\n      \"profile\": \"https://github.com/aliceHendicott\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    },\n    {\n      \"login\": \"zmdavis\",\n      \"name\": \"Zach Davis\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/25305144?v=4\",\n      \"profile\": \"https://github.com/zmdavis\",\n      \"contributions\": [\n        \"code\",\n        \"bug\"\n      ]\n    }\n  ],\n  \"repoHost\": \"https://github.com\",\n  \"contributorsPerLine\": 7,\n  \"skipCi\": true,\n  \"commitConvention\": \"angular\",\n  \"commitType\": \"docs\"\n}\n"
  },
  {
    "path": ".flowconfig",
    "content": "[ignore]\n.*/node_modules/\n\n[include]\n./test\n\n[libs]\n\n[lints]\nall=error\n\n[options]\n"
  },
  {
    "path": ".gitattributes",
    "content": "* text=auto\n*.js text eol=lf\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE.md",
    "content": "<!--\nThanks for your interest in the project. I appreciate bugs filed and PRs submitted!\nPlease make sure that you are familiar with and follow the Code of Conduct for\nthis project (found in the CODE_OF_CONDUCT.md file).\n\nPlease fill out this template with all the relevant information so we can\nunderstand what's going on and fix the issue.\n\nI'll probably ask you to submit the fix (after giving some direction). If you've\nnever done that before, that's great! Check this free short video tutorial to\nlearn how: http://kcd.im/pull-request\n-->\n\n- `downshift` version:\n- `node` version:\n- `npm` (or `yarn`) version:\n\n**Relevant code or config**\n\n```javascript\n```\n\n**What you did**:\n\n**What happened**:\n\n<!-- Please provide the full error message/screenshots/anything -->\n\n**Reproduction repository**:\n\n<!--\nIf possible, please create a repository that reproduces the issue with the\nminimal amount of code possible.\n-->\n\n**Problem description**:\n\n**Suggested solution**:\n"
  },
  {
    "path": ".github/PULL_REQUEST_TEMPLATE.md",
    "content": "<!--\nThanks for your interest in the project. Bugs filed and PRs submitted are appreciated!\n\nPlease make sure that you are familiar with and follow the Code of Conduct for\nthis project (found in the CODE_OF_CONDUCT.md file).\n\nAlso, please make sure you're familiar with and follow the instructions in the\ncontributing guidelines (found in the CONTRIBUTING.md file).\n\nIf you're new to contributing to open source projects, you might find this free\nvideo course helpful: http://kcd.im/pull-request\n\nPlease fill out the information below to expedite the review and (hopefully)\nmerge of your pull request!\n-->\n\n# Pull Request\n\n## What\n\n<!-- What changes are being made? (What feature/bug is being fixed here?) -->\n\n## Why\n\n<!-- Why are these changes necessary? -->\n\n## How\n\n<!-- How were these changes implemented? -->\n\n## Changes\n\n<!-- List the specific changes made (files modified, configurations updated, etc.) -->\n\n## Checklist\n\n<!-- add \"N/A\" to the end of each line that's irrelevant to your changes -->\n<!-- to check an item, place an \"x\" in the box like so: \"- [x] Documentation\" -->\n\n- [ ] Documentation\n- [ ] Tests\n- [ ] TypeScript Types\n- [ ] Ready to be merged\n      <!-- In your opinion, is this ready to be merged as soon as it's reviewed? -->\n\n<!-- feel free to add additional comments -->\n"
  },
  {
    "path": ".github/workflows/validate.yml",
    "content": "name: validate\non:\n  push:\n    branches:\n      - '+([0-9])?(.{+([0-9]),x}).x'\n      - 'master'\n      - 'next'\n      - 'next-major'\n      - 'beta'\n      - 'alpha'\n      - '!all-contributors/**'\n  pull_request: {}\njobs:\n  main:\n    # ignore all-contributors PRs\n    if: ${{ !contains(github.head_ref, 'all-contributors') }}\n    strategy:\n      matrix:\n        node: [20, 22, 24]\n    runs-on: ubuntu-latest\n    steps:\n      - name: 🛑 Cancel Previous Runs\n        uses: styfle/cancel-workflow-action@0.12.1\n        with:\n          access_token: ${{ secrets.GITHUB_TOKEN }}\n      - name: ⬇️ Checkout repo\n        uses: actions/checkout@v5\n\n      - name: Increase watchers\n        run:\n          echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf\n          && sudo sysctl -p\n\n      - name: ⎔ Setup node\n        uses: actions/setup-node@v5\n        with:\n          node-version: ${{ matrix.node }}\n\n      - name: 📥 Download deps\n        uses: bahmutov/npm-install@v1\n        with:\n          useLockFile: false\n\n      - name: ▶️ Run validate script\n        run: npm run validate\n        env:\n          FORCE_COLOR: true\n\n      - name: ⬆️ Upload coverage report\n        uses: codecov/codecov-action@v5\n        env:\n          CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n\n  release:\n    needs: main\n    runs-on: ubuntu-latest\n    if:\n      ${{ github.repository == 'downshift-js/downshift' &&\n      contains('refs/heads/master,refs/heads/beta,refs/heads/next,refs/heads/alpha',\n      github.ref) && github.event_name == 'push' }}\n    steps:\n      - name: 🛑 Cancel Previous Runs\n        uses: styfle/cancel-workflow-action@0.12.1\n        with:\n          access_token: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: ⬇️ Checkout repo\n        uses: actions/checkout@v5\n\n      - name: ⎔ Setup node\n        uses: actions/setup-node@v5\n        with:\n          node-version: 24\n\n      - name: 📥 Download deps\n        uses: bahmutov/npm-install@v1\n        with:\n          useLockFile: false\n\n      - name: 🏗 Run build script\n        run: npm run build\n\n      - name: 🚀 Release\n        uses: cycjimmy/semantic-release-action@v6\n        with:\n          semantic_version: 24\n          branches: |\n            [\n              '+([0-9])?(.{+([0-9]),x}).x',\n              'master',\n              'next',\n              'next-major',\n              {name: 'beta', prerelease: true},\n              {name: 'alpha', prerelease: true}\n            ]\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}"
  },
  {
    "path": ".gitignore",
    "content": "node_modules\ncoverage\ndist\n.opt-in\n.opt-out\n.DS_Store\n.next\n.eslintcache\npreact/\n\ncypress/videos\ncypress/screenshots\n\n# these cause more harm than good\n# when working with contributors\npackage-lock.json\nyarn.lock\nflow-coverage/\n\n# Production\n/build\n\n# Generated files\n.docusaurus\n.cache-loader\n\n# Misc\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\nnpm-debug.log*\n\n# IDE settings\n.idea\n"
  },
  {
    "path": ".npmrc",
    "content": "registry=https://registry.npmjs.org/\npackage-lock=false\n"
  },
  {
    "path": ".nvmrc",
    "content": "16.14.0\n"
  },
  {
    "path": ".prettierignore",
    "content": "node_modules/\ncoverage/\ndist/\npreact/\npackage-lock.json\npackage.json\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "# CHANGELOG\n\nThe changelog is automatically updated using\n[semantic-release](https://github.com/semantic-release/semantic-release). You\ncan see it on the [releases page](../../releases).\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n**Table of Contents**\n\n- [Our Pledge](#our-pledge)\n- [Our Standards](#our-standards)\n- [Our Responsibilities](#our-responsibilities)\n- [Scope](#scope)\n- [Enforcement](#enforcement)\n- [Attribution](#attribution)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of\nexperience, nationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, or to ban temporarily or permanently any\ncontributor for other behaviors that they deem inappropriate, threatening,\noffensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team at kent+coc@doddsfamily.us. All\ncomplaints will be reviewed and investigated and will result in a response that\nis deemed necessary and appropriate to the circumstances. The project team is\nobligated to maintain confidentiality with regard to the reporter of an\nincident. Further details of specific enforcement policies may be posted\nseparately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.4, available at [http://contributor-covenant.org/version/1/4][version]\n\n[homepage]: http://contributor-covenant.org\n[version]: http://contributor-covenant.org/version/1/4/\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing\n\nThanks for being willing to contribute!\n\n**Working on your first Pull Request?** You can learn how from this _free_\nseries [How to Contribute to an Open Source Project on GitHub][egghead]\n\n## Project setup\n\n1.  Fork and clone the repo\n2.  `npm run setup` to setup and validate your clone of the project\n3.  Create a branch for your PR\n\n> Tip: Keep your `master` branch pointing at the original repository and make\n> pull requests from branches on your fork. To do this, run:\n>\n> ```\n> git remote add upstream https://github.com/downshift-js/downshift.git\n> git fetch upstream\n> git branch --set-upstream-to=upstream/master master\n> ```\n>\n> This will add the original repository as a \"remote\" called \"upstream,\" Then\n> fetch the git information from that remote, then set your local `master`\n> branch to use the upstream master branch whenever you run `git pull`. Then you\n> can make all of your pull request branches based on this `master` branch.\n> Whenever you want to update your version of `master`, do a regular `git pull`.\n\n## Committing and Pushing changes\n\nPlease make sure to run the tests before you commit your changes. You can run\n`npm run test:update` which will update any snapshots that need updating. Make\nsure to include those changes (if they exist) in your commit. We also track the\nbundle sizes in a `.size-snapshot.json` file, this will likely update when you\nmake changes to the codebase.\n\n### Tests\n\nThere are quite a few test scripts that run as part of a `validate` script in\nthis project:\n\n- lint - ESLint stuff, pretty basic. Please fix any errors/warnings :)\n- build-and-test - This ensures that the built version of `downshift` is what we\n  expect. These tests live in `other/misc-tests/__tests__`.\n- test:cover - This is primarily unit tests on the source code and accounts for\n  most of the coverage. We enforce 100% code coverage on this library. These\n  tests live in `src/__tests__`\n- test:ts - This runs `tsc` on the codebase to make sure the type script\n  definitions are correct for the `tsx` files in the `test` directory.\n- test:ssr - This ensures that downshift works with server-side rendering (it\n  can run and render in an environment without the DOM). These tests live in\n  `other/ssr/__tests__`\n- test:cypress - This runs tests in an actual browser. It runs and tests the\n  storybook examples. These tests live in `cypress/integration`.\n\n### opt into git hooks\n\nThere are git hooks set up with this project that are automatically installed\nwhen you install dependencies. They're really handy, but are turned off by\ndefault (so as to not hinder new contributors). You can opt into these by\ncreating a file called `.opt-in` at the root of the project and putting this\ninside:\n\n```\npre-commit\n```\n\n## Help needed\n\nPlease checkout the [the open issues][issues]\n\nAlso, please watch the repo and respond to questions/bug reports/feature\nrequests! Thanks!\n\n[egghead]:\n  https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github\n[all-contributors]: https://github.com/kentcdodds/all-contributors\n[issues]: https://github.com/downshift-js/downshift/issues\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\nCopyright (c) 2017 PayPal\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n  downshift 🏎\n  <br>\n  <img src=\"https://raw.githubusercontent.com/downshift-js/downshift/master/other/public/logo/downshift.svg\" alt=\"downshift logo\" title=\"downshift logo\" width=\"300\">\n  <br>\n</h1>\n<p align=\"center\" style=\"font-size: 1.2rem;\">Primitives to build simple, flexible, WAI-ARIA compliant React\nautocomplete, combobox or select dropdown components.</p>\n\n> [Read the docs](https://downshift-js.com) |\n> [See the intro blog post](https://kentcdodds.com/blog/introducing-downshift-for-react)\n> |\n> [Listen to the Episode 79 of the Full Stack Radio podcast](https://fullstackradio.com/79)\n\n<hr />\n\n[![Build Status][build-badge]][build]\n[![Code Coverage][coverage-badge]][coverage]\n[![downloads][downloads-badge]][npmcharts] [![version][version-badge]][package]\n[![MIT License][license-badge]][license]\n\n[![All Contributors](https://img.shields.io/badge/all_contributors-113-orange.svg?style=flat-square)](#contributors)\n[![PRs Welcome][prs-badge]][prs] [![Chat][chat-badge]][chat]\n[![Code of Conduct][coc-badge]][coc]\n[![Join the community on Spectrum][spectrum-badge]][spectrum]\n\n[![Supports React and Preact][react-badge]][react]\n[![size][size-badge]][unpkg-dist] [![gzip size][gzip-badge]][unpkg-dist]\n[![module formats: umd, cjs, and es][module-formats-badge]][unpkg-dist]\n\n## The problem\n\nYou need an autocomplete, a combobox or a select experience in your application\nand you want it to be accessible. You also want it to be simple and flexible to\naccount for your use cases. Finally, it should follow the [ARIA][aria] design\npattern for a [combobox][combobox-aria-example] or a\n[select][select-aria-example], depending on your use case.\n\n## This solution\n\nThe library offers a couple of solutions. The first solution, which is the one\nwe recommend you to try first, is a set of React hooks. Each hook provides the\nstateful logic needed to make the corresponding component functional and\naccessible. Navigate to the documentation for each by using the links in the\nlist below.\n\n- [useSelect][useselect-readme] for a custom select component.\n- [useCombobox][combobox-readme] for a combobox or autocomplete input.\n- [useTagGroup][tag-group-readme] for a tag group component. Also useful to\n  build a multiple selection combobox or select component with tags.\n\nThe second solution is the `Downshift` component, which can also be used to\ncreate accessible combobox and select components, providing the logic in the\nform of a render prop. It served as inspiration for developing the hooks and it\nhas been around for a while. It established a successful pattern for making\ncomponents accessible and functional while giving developers complete freedom\nwhen building the UI.\n\nBoth _useSelect_ and _useCombobox_ support the latest ARIA combobox patterns for\nW3C, which _Downshift_ does not. Consequently, we strongly recommend the you use\nthe hooks. The hooks have been migrated to the ARIA 1.2 combobox pattern in the\nversion 7 of _downshift_. There is a [Migration Guide][migration-guide-v7] that\ndocuments the changes introduced in version 7.\n\nThe `README` on this page covers only the component while each hook has its own\n`README` page. You can navigate to the [hooks page][hooks-readme] or go directly\nto the hook you need by using the links in the list above.\n\nFor examples on how to use the hooks or the Downshift component, check out our\n[docsite][docsite]!\n\n**🚨 Use the Downshift hooks 🚨**\n\nIf you are new to the library, consider the _useSelect_ and _useCombobox_ hooks\nas the first option. As mentioned above, the hooks benefit from the updated ARIA\npatterns and are actively maintained and improved. If there are use cases that\nare supported by the _Downshift_ component and not by the hooks, please create\nan issue in our repo. The _Downshift_ component is going to be removed\ncompletely once the hooks become mature.\n\n### Downshift\n\nThis is a component that controls user interactions and state for you so you can\ncreate autocomplete, combobox or select dropdown components. It uses a [render\nprop][use-a-render-prop] which gives you maximum flexibility with a minimal API\nbecause you are responsible for the rendering of everything and you simply apply\nprops to what you're rendering.\n\nThis differs from other solutions which render things for their use case and\nthen expose many options to allow for extensibility resulting in a bigger API\nthat is less flexible as well as making the implementation more complicated and\nharder to contribute to.\n\n> NOTE: The original use case of this component is autocomplete, however the API\n> is powerful and flexible enough to build things like dropdowns as well.\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [Basic Props](#basic-props)\n  - [children](#children)\n  - [itemToString](#itemtostring)\n  - [onChange](#onchange)\n  - [stateReducer](#statereducer)\n- [Advanced Props](#advanced-props)\n  - [initialSelectedItem](#initialselecteditem)\n  - [initialInputValue](#initialinputvalue)\n  - [initialHighlightedIndex](#initialhighlightedindex)\n  - [initialIsOpen](#initialisopen)\n  - [defaultHighlightedIndex](#defaulthighlightedindex)\n  - [defaultIsOpen](#defaultisopen)\n  - [selectedItemChanged](#selecteditemchanged)\n  - [getA11yStatusMessage](#geta11ystatusmessage)\n  - [onSelect](#onselect)\n  - [onStateChange](#onstatechange)\n  - [onInputValueChange](#oninputvaluechange)\n  - [itemCount](#itemcount)\n  - [highlightedIndex](#highlightedindex)\n  - [inputValue](#inputvalue)\n  - [isOpen](#isopen)\n  - [selectedItem](#selecteditem)\n  - [id](#id)\n  - [inputId](#inputid)\n  - [labelId](#labelid)\n  - [menuId](#menuid)\n  - [getItemId](#getitemid)\n  - [environment](#environment)\n  - [onOuterClick](#onouterclick)\n  - [scrollIntoView](#scrollintoview)\n- [stateChangeTypes](#statechangetypes)\n- [Control Props](#control-props)\n- [Children Function](#children-function)\n  - [prop getters](#prop-getters)\n  - [actions](#actions)\n  - [state](#state)\n  - [props](#props)\n- [Event Handlers](#event-handlers)\n  - [default handlers](#default-handlers)\n  - [customizing handlers](#customizing-handlers)\n- [Utilities](#utilities)\n  - [resetIdCounter](#resetidcounter)\n- [React Native](#react-native)\n  - [Gotchas](#gotchas)\n- [Advanced React Component Patterns course](#advanced-react-component-patterns-course)\n- [Examples](#examples)\n- [FAQ](#faq)\n- [Inspiration](#inspiration)\n- [Other Solutions](#other-solutions)\n- [Bindings for ReasonML](#bindings-for-reasonml)\n- [Contributors](#contributors)\n- [LICENSE](#license)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Installation\n\nThis module is distributed via [npm][npm] which is bundled with [node][node] and\nshould be installed as one of your project's `dependencies`:\n\n```\nnpm install --save downshift\n```\n\n> This package also depends on `react`. Please make sure you have it installed\n> as well.\n\n> Note also this library supports `preact` out of the box. If you are using\n> `preact` then use the corresponding module in the `preact/dist` folder. You\n> can even `import Downshift from 'downshift/preact'` 👍\n\n## Usage\n\n> [Try it out in the browser][code-sandbox-try-it-out]\n\n```jsx\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport Downshift from 'downshift'\n\nconst items = [\n  {value: 'apple'},\n  {value: 'pear'},\n  {value: 'orange'},\n  {value: 'grape'},\n  {value: 'banana'},\n]\n\nrender(\n  <Downshift\n    onChange={selection =>\n      alert(selection ? `You selected ${selection.value}` : 'Selection Cleared')\n    }\n    itemToString={item => (item ? item.value : '')}\n  >\n    {({\n      getInputProps,\n      getItemProps,\n      getLabelProps,\n      getMenuProps,\n      isOpen,\n      inputValue,\n      highlightedIndex,\n      selectedItem,\n      getRootProps,\n    }) => (\n      <div>\n        <label {...getLabelProps()}>Enter a fruit</label>\n        <div\n          style={{display: 'inline-block'}}\n          {...getRootProps({}, {suppressRefError: true})}\n        >\n          <input {...getInputProps()} />\n        </div>\n        <ul {...getMenuProps()}>\n          {isOpen\n            ? items\n                .filter(item => !inputValue || item.value.includes(inputValue))\n                .map((item, index) => (\n                  <li\n                    {...getItemProps({\n                      key: item.value,\n                      index,\n                      item,\n                      style: {\n                        backgroundColor:\n                          highlightedIndex === index ? 'lightgray' : 'white',\n                        fontWeight: selectedItem === item ? 'bold' : 'normal',\n                      },\n                    })}\n                  >\n                    {item.value}\n                  </li>\n                ))\n            : null}\n        </ul>\n      </div>\n    )}\n  </Downshift>,\n  document.getElementById('root'),\n)\n```\n\nThere is also an [example without getRootProps][code-sandbox-no-get-root-props].\n\n> Warning: The example without `getRootProps` is not fully accessible with\n> screen readers as it's not possible to achieve the HTML structure suggested by\n> ARIA. We recommend following the example with `getRootProps`. Examples on how\n> to use `Downshift` component with and without `getRootProps` are on the\n> [docsite](https://downshift-js.com/).\n\n`Downshift` is the only component exposed by this package. It doesn't render\nanything itself, it just calls the render function and renders that. [\"Use a\nrender prop!\"][use-a-render-prop]!\n`<Downshift>{downshift => <div>/* your JSX here! */</div>}</Downshift>`.\n\n## Basic Props\n\nThis is the list of props that you should probably know about. There are some\n[advanced props](#advanced-props) below as well.\n\n### children\n\n> `function({})` | _required_\n\nThis is called with an object. Read more about the properties of this object in\nthe section \"[Children Function](#children-function)\".\n\n### itemToString\n\n> `function(item: any)` | defaults to: `item => (item ? String(item) : '')`\n\nIf your items are stored as, say, objects instead of strings, downshift still\nneeds a string representation for each one (e.g., to set `inputValue`).\n\n**Note:** This callback _must_ include a null check: it is invoked with `null`\nwhenever the user abandons input via `<Esc>`.\n\n### onChange\n\n> `function(selectedItem: any, stateAndHelpers: object)` | optional, no useful\n> default\n\nCalled when the selected item changes, either by the user selecting an item or\nthe user clearing the selection. Called with the item that was selected or\n`null` and the new state of `downshift`. (see `onStateChange` for more info on\n`stateAndHelpers`).\n\n- `selectedItem`: The item that was just selected. `null` if the selection was\n  cleared.\n- `stateAndHelpers`: This is the same thing your `children` function is called\n  with (see [Children Function](#children-function))\n\n### stateReducer\n\n> `function(state: object, changes: object)` | optional\n\n**🚨 This is a really handy power feature 🚨**\n\nThis function will be called each time `downshift` sets its internal state (or\ncalls your `onStateChange` handler for control props). It allows you to modify\nthe state change that will take place which can give you fine grain control over\nhow the component interacts with user updates without having to use\n[Control Props](#control-props). It gives you the current state and the state\nthat will be set, and you return the state that you want to set.\n\n- `state`: The full current state of downshift.\n- `changes`: These are the properties that are about to change. This also has a\n  `type` property which you can learn more about in the\n  [`stateChangeTypes`](#statechangetypes) section.\n\n```jsx\nconst ui = (\n  <Downshift stateReducer={stateReducer}>{/* your callback */}</Downshift>\n)\n\nfunction stateReducer(state, changes) {\n  // this prevents the menu from being closed when the user\n  // selects an item with a keyboard or mouse\n  switch (changes.type) {\n    case Downshift.stateChangeTypes.keyDownEnter:\n    case Downshift.stateChangeTypes.clickItem:\n      return {\n        ...changes,\n        isOpen: state.isOpen,\n        highlightedIndex: state.highlightedIndex,\n      }\n    default:\n      return changes\n  }\n}\n```\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<input onBlur={handleBlur} />` should be\n> `<input {...getInputProps({onBlur: handleBlur})} />`). Also, your reducer\n> function should be \"pure.\" This means it should do nothing other than return\n> the state changes you want to have happen.\n\n## Advanced Props\n\n### initialSelectedItem\n\n> `any` | defaults to `null`\n\nPass an item or an array of items that should be selected when downshift is\ninitialized.\n\n### initialInputValue\n\n> `string` | defaults to `''`\n\nThis is the initial input value when downshift is initialized.\n\n### initialHighlightedIndex\n\n> `number`/`null` | defaults to `defaultHighlightedIndex`\n\nThis is the initial value to set the highlighted index to when downshift is\ninitialized.\n\n### initialIsOpen\n\n> `boolean` | defaults to `defaultIsOpen`\n\nThis is the initial `isOpen` value when downshift is initialized.\n\n### defaultHighlightedIndex\n\n> `number`/`null` | defaults to `null`\n\nThis is the value to set the `highlightedIndex` to anytime downshift is reset,\nwhen the selection is cleared, when an item is selected or when the inputValue\nis changed.\n\n### defaultIsOpen\n\n> `boolean` | defaults to `false`\n\nThis is the value to set the `isOpen` to anytime downshift is reset, when the\nthe selection is cleared, or when an item is selected.\n\n### selectedItemChanged\n\n> `function(prevItem: any, item: any)` | defaults to:\n> `(prevItem, item) => (prevItem !== item)`\n\nUsed to determine if the new `selectedItem` has changed compared to the previous\n`selectedItem` and properly update Downshift's internal state.\n\n### getA11yStatusMessage\n\n> `function({/* see below */})` | default messages provided in English\n\nThis function is passed as props to a `Status` component nested within and\nallows you to create your own assertive ARIA statuses.\n\nA default `getA11yStatusMessage` function is provided that will check\n`resultCount` and return \"No results are available.\" or if there are results ,\n\"`resultCount` results are available, use up and down arrow keys to navigate.\nPress Enter key to select.\"\n\nThe object you are passed to generate your status message has the following\nproperties:\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property              | type            | description                                                                                  |\n| --------------------- | --------------- | -------------------------------------------------------------------------------------------- |\n| `highlightedIndex`    | `number`/`null` | The currently highlighted index                                                              |\n| `highlightedItem`     | `any`           | The value of the highlighted item                                                            |\n| `inputValue`          | `string`        | The current input value                                                                      |\n| `isOpen`              | `boolean`       | The `isOpen` state                                                                           |\n| `itemToString`        | `function(any)` | The `itemToString` function (see props) for getting the string value from one of the options |\n| `previousResultCount` | `number`        | The total items showing in the dropdown the last time the status was updated                 |\n| `resultCount`         | `number`        | The total items showing in the dropdown                                                      |\n| `selectedItem`        | `any`           | The value of the currently selected item                                                     |\n\n### onSelect\n\n> `function(selectedItem: any, stateAndHelpers: object)` | optional, no useful\n> default\n\nCalled when the user selects an item, regardless of the previous selected item.\nCalled with the item that was selected and the new state of `downshift`. (see\n`onStateChange` for more info on `stateAndHelpers`).\n\n- `selectedItem`: The item that was just selected\n- `stateAndHelpers`: This is the same thing your `children` function is called\n  with (see [Children Function](#children-function))\n\n### onStateChange\n\n> `function(changes: object, stateAndHelpers: object)` | optional, no useful\n> default\n\nThis function is called anytime the internal state changes. This can be useful\nif you're using downshift as a \"controlled\" component, where you manage some or\nall of the state (e.g., isOpen, selectedItem, highlightedIndex, etc) and then\npass it as props, rather than letting downshift control all its state itself.\nThe parameters both take the shape of internal state\n(`{highlightedIndex: number, inputValue: string, isOpen: boolean, selectedItem: any}`)\nbut differ slightly.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n- `stateAndHelpers`: This is the exact same thing your `children` function is\n  called with (see [Children Function](#children-function))\n\n> Tip: This function will be called any time _any_ state is changed. The best\n> way to determine whether any particular state was changed, you can use\n> `changes.hasOwnProperty('propName')`.\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<input onBlur={handleBlur} />` should be\n> `<input {...getInputProps({onBlur: handleBlur})} />`).\n\n### onInputValueChange\n\n> `function(inputValue: string, stateAndHelpers: object)` | optional, no useful\n> default\n\nCalled whenever the input value changes. Useful to use instead or in combination\nof `onStateChange` when `inputValue` is a controlled prop to\n[avoid issues with cursor positions](https://github.com/downshift-js/downshift/issues/217).\n\n- `inputValue`: The current value of the input\n- `stateAndHelpers`: This is the same thing your `children` function is called\n  with (see [Children Function](#children-function))\n\n### itemCount\n\n> `number` | optional, defaults the number of times you call getItemProps\n\nThis is useful if you're using some kind of virtual listing component for\n\"windowing\" (like\n[`react-virtualized`](https://github.com/bvaughn/react-virtualized)).\n\n### highlightedIndex\n\n> `number` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe index that should be highlighted\n\n### inputValue\n\n> `string` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe value the input should have\n\n### isOpen\n\n> `boolean` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nWhether the menu should be considered open or closed. Some aspects of the\ndownshift component respond differently based on this value (for example, if\n`isOpen` is true when the user hits \"Enter\" on the input field, then the item at\nthe `highlightedIndex` item is selected).\n\n### selectedItem\n\n> `any`/`Array(any)` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe currently selected item.\n\n### id\n\n> `string` | defaults to a generated ID\n\nYou should not normally need to set this prop. It's only useful if you're server\nrendering items (which each have an `id` prop generated based on the `downshift`\n`id`). For more information see the `FAQ` below.\n\n### inputId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`input`) you use\n[`getInputProps`](#getinputprops) with.\n\n### labelId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`label`) you use\n[`getLabelProps`](#getlabelprops) with.\n\n### menuId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`ul`) you use\n[`getMenuProps`](#getmenuprops) with.\n\n### getItemId\n\n> `function(index)` | defaults to a function that generates an ID based on the\n> index\n\nUsed for `aria` attributes and the `id` prop of the element (`li`) you use\n[`getInputProps`](#getinputprops) with.\n\n### environment\n\n> `window` | defaults to `window`\n\nThis prop is only useful if you're rendering downshift within a different\n`window` context from where your JavaScript is running; for example, an iframe\nor a shadow-root. If the given context is lacking `document` and/or\n`add|removeEventListener` on its prototype (as is the case for a shadow-root)\nthen you will need to pass in a custom object that is able to provide\n[access to these properties](https://gist.github.com/Rendez/1dd55882e9b850dd3990feefc9d6e177)\nfor downshift.\n\n### onOuterClick\n\n> `function(stateAndHelpers: object)` | optional\n\nA helper callback to help control internal state of downshift like `isOpen` as\nmentioned in [this issue](https://github.com/downshift-js/downshift/issues/206).\nThe same behavior can be achieved using `onStateChange`, but this prop is\nprovided as a helper because it's a fairly common use-case if you're controlling\nthe `isOpen` state:\n\n```jsx\nconst ui = (\n  <Downshift\n    isOpen={this.state.menuIsOpen}\n    onOuterClick={() => this.setState({menuIsOpen: false})}\n  >\n    {/* your callback */}\n  </Downshift>\n)\n```\n\nThis callback will only be called if `isOpen` is `true`.\n\n### scrollIntoView\n\n> `function(node: HTMLElement, menuNode: HTMLElement)` | defaults to internal\n> implementation\n\nThis allows you to customize how the scrolling works when the highlighted index\nchanges. It receives the node to be scrolled to and the root node (the root node\nyou render in downshift). Internally we use\n[`compute-scroll-into-view`](https://www.npmjs.com/package/compute-scroll-into-view)\nso if you use that package then you wont be adding any additional bytes to your\nbundle :)\n\n## stateChangeTypes\n\nThere are a few props that expose changes to state\n([`onStateChange`](#onstatechange) and [`stateReducer`](#statereducer)). For you\nto make the most of these APIs, it's important for you to understand why state\nis being changed. To accomplish this, there's a `type` property on the `changes`\nobject you get. This `type` corresponds to a `Downshift.stateChangeTypes`\nproperty.\n\nThe list of all possible values this `type` property can take is defined in\n[this file](https://github.com/downshift-js/downshift/blob/master/src/stateChangeTypes.js)\nand is as follows:\n\n- `Downshift.stateChangeTypes.unknown`\n- `Downshift.stateChangeTypes.mouseUp`\n- `Downshift.stateChangeTypes.itemMouseEnter`\n- `Downshift.stateChangeTypes.keyDownArrowUp`\n- `Downshift.stateChangeTypes.keyDownArrowDown`\n- `Downshift.stateChangeTypes.keyDownEscape`\n- `Downshift.stateChangeTypes.keyDownEnter`\n- `Downshift.stateChangeTypes.keyDownHome`\n- `Downshift.stateChangeTypes.keyDownEnd`\n- `Downshift.stateChangeTypes.clickItem`\n- `Downshift.stateChangeTypes.blurInput`\n- `Downshift.stateChangeTypes.changeInput`\n- `Downshift.stateChangeTypes.keyDownSpaceButton`\n- `Downshift.stateChangeTypes.clickButton`\n- `Downshift.stateChangeTypes.blurButton`\n- `Downshift.stateChangeTypes.controlledPropUpdatedSelectedItem`\n- `Downshift.stateChangeTypes.touchEnd`\n\nSee [`stateReducer`](#statereducer) for a concrete example on how to use the\n`type` property.\n\n## Control Props\n\ndownshift manages its own state internally and calls your `onChange` and\n`onStateChange` handlers with any relevant changes. The state that downshift\nmanages includes: `isOpen`, `selectedItem`, `inputValue`, and\n`highlightedIndex`. Your Children function (read more below) can be used to\nmanipulate this state and can likely support many of your use cases.\n\nHowever, if more control is needed, you can pass any of these pieces of state as\na prop (as indicated above) and that state becomes controlled. As soon as\n`this.props[statePropKey] !== undefined`, internally, `downshift` will determine\nits state based on your prop's value rather than its own internal state. You\nwill be required to keep the state up to date (this is where `onStateChange`\ncomes in really handy), but you can also control the state from anywhere, be\nthat state from other components, `redux`, `react-router`, or anywhere else.\n\n> Note: This is very similar to how normal controlled components work elsewhere\n> in react (like `<input />`). If you want to learn more about this concept, you\n> can learn about that from this the\n> [Advanced React Component Patterns course](#advanced-react-component-patterns-course)\n\n## Children Function\n\nThis is where you render whatever you want to based on the state of `downshift`.\nYou use it like so:\n\n```javascript\nconst ui = (\n  <Downshift>\n    {downshift => (\n      // use downshift utilities and state here, like downshift.isOpen,\n      // downshift.getInputProps, etc.\n      <div>{/* more jsx here */}</div>\n    )}\n  </Downshift>\n)\n```\n\nThe properties of this `downshift` object can be split into three categories as\nindicated below:\n\n### prop getters\n\n> See\n> [the blog post about prop getters](https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters)\n\n> NOTE: These prop-getters provide important `aria-` attributes which are very\n> important to your component being accessible. It's recommended that you\n> utilize these functions and apply the props they give you to your components.\n\nThese functions are used to apply props to the elements that you render. This\ngives you maximum flexibility to render what, when, and wherever you like. You\ncall these on the element in question (for example:\n`<input {...getInputProps()}`)). It's advisable to pass all your props to that\nfunction rather than applying them on the element yourself to avoid your props\nbeing overridden (or overriding the props returned). For example:\n`getInputProps({onKeyUp(event) {console.log(event)}})`.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property               | type              | description                                                                                    |\n| ---------------------- | ----------------- | ---------------------------------------------------------------------------------------------- |\n| `getToggleButtonProps` | `function({})`    | returns the props you should apply to any menu toggle button element you render.               |\n| `getInputProps`        | `function({})`    | returns the props you should apply to the `input` element that you render.                     |\n| `getItemProps`         | `function({})`    | returns the props you should apply to any menu item elements you render.                       |\n| `getLabelProps`        | `function({})`    | returns the props you should apply to the `label` element that you render.                     |\n| `getMenuProps`         | `function({},{})` | returns the props you should apply to the `ul` element (or root of your menu) that you render. |\n| `getRootProps`         | `function({},{})` | returns the props you should apply to the root element that you render. It can be optional.    |\n\n#### `getRootProps`\n\n<details>\n\n<summary>\n  If you cannot render a div as the root element, then read this\n</summary>\n\nMost of the time, you can just render a `div` yourself and `Downshift` will\napply the props it needs to do its job (and you don't need to call this\nfunction). However, if you're rendering a composite component (custom component)\nas the root element, then you'll need to call `getRootProps` and apply that to\nyour root element (downshift will throw an error otherwise).\n\nThere are no required properties for this method.\n\nOptional properties:\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getRootProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<div ref={props.innerRef} />`.\n  It defaults to `ref`.\n\nIf you're rendering a composite component, `Downshift` checks that\n`getRootProps` is called and that `refKey` is a prop of the returned composite\ncomponent. This is done to catch common causes of errors but, in some cases, the\ncheck could fail even if the ref is correctly forwarded to the root DOM\ncomponent. In these cases, you can provide the object\n`{suppressRefError : true}` as the second argument to `getRootProps` to\ncompletely bypass the check.\\\n**Please use it with extreme care and only if you are absolutely sure that the\nref is correctly forwarded otherwise `Downshift` will unexpectedly fail.**\\\nSee [#235](https://github.com/downshift-js/downshift/issues/235) for the\ndiscussion that lead to this.\n\n</details>\n\n#### `getInputProps`\n\nThis method should be applied to the `input` you render. It is recommended that\nyou pass all props as an object to this method which will compose together any\nof the event handlers you need to apply to the `input` while preserving the ones\nthat `downshift` needs to apply to make the `input` behave.\n\nThere are no required properties for this method.\n\nOptional properties:\n\n- `disabled`: If this is set to true, then no event handlers will be returned\n  from `getInputProps` and a `disabled` prop will be returned (effectively\n  disabling the input).\n\n- `aria-label`: By default the menu will add an `aria-labelledby` that refers to\n  the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\n#### `getLabelProps`\n\nThis method should be applied to the `label` you render. It is useful for\nensuring that the `for` attribute on the `<label>` (`htmlFor` as a react prop)\nis the same as the `id` that appears on the `input`. If no `htmlFor` is provided\n(the normal case) then an ID will be generated and used for the `input` and the\n`label` `for` attribute.\n\nThere are no required properties for this method.\n\n> Note: For accessibility purposes, calling this method is highly recommended.\n\n#### `getMenuProps`\n\nThis method should be applied to the element which contains your list of items.\nTypically, this will be a `<div>` or a `<ul>` that surrounds a `map` expression.\nThis handles the proper ARIA roles and attributes.\n\nOptional properties:\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getMenuProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<ul ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n  Please keep in mind that menus, for accessibility purposes, should always be\n  rendered, regardless of whether you hide it or not. Otherwise, `getMenuProps`\n  may throw error if you unmount and remount the menu.\n\n- `aria-label`: By default the menu will add an `aria-labelledby` that refers to\n  the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getMenuProps`. **Please use it with extreme care and only if you are absolutely\nsure that the ref is correctly forwarded otherwise `Downshift` will unexpectedly\nfail.**\n\n```jsx\n<ul {...getMenuProps()}>\n  {!isOpen\n    ? null\n    : items.map((item, index) => (\n        <li {...getItemProps({item, index, key: item.id})}>{item.name}</li>\n      ))}\n</ul>\n```\n\n> Note that for accessibility reasons it's best if you always render this\n> element whether or not downshift is in an `isOpen` state.\n\n#### `getItemProps`\n\nThe props returned from calling this function should be applied to any menu\nitems you render.\n\n**This is an impure function**, so it should only be called when you will\nactually be applying the props to an item.\n\n<details>\n\n<summary>What do you mean by impure function?</summary>\n\nBasically just don't do this:\n\n```jsx\nitems.map(item => {\n  const props = getItemProps({item}) // we're calling it here\n  if (!shouldRenderItem(item)) {\n    return null // but we're not using props, and downshift thinks we are...\n  }\n  return <div {...props} />\n})\n```\n\nInstead, you could do this:\n\n```jsx\nitems.filter(shouldRenderItem).map(item => <div {...getItemProps({item})} />)\n```\n\n</details>\n\nRequired properties:\n\n- `item`: this is the item data that will be selected when the user selects a\n  particular item.\n\nOptional properties:\n\n- `index`: This is how `downshift` keeps track of your item when updating the\n  `highlightedIndex` as the user keys around. By default, `downshift` will\n  assume the `index` is the order in which you're calling `getItemProps`. This\n  is often good enough, but if you find odd behavior, try setting this\n  explicitly. It's probably best to be explicit about `index` when using a\n  windowing library like `react-virtualized`.\n- `disabled`: If this is set to `true`, then all of the downshift item event\n  handlers will be omitted. Items will not be highlighted when hovered, and\n  items will not be selected when clicked.\n\n#### `getToggleButtonProps`\n\nCall this and apply the returned props to a `button`. It allows you to toggle\nthe `Menu` component. You can definitely build something like this yourself (all\nof the available APIs are exposed to you), but this is nice because it will also\napply all of the proper ARIA attributes.\n\nOptional properties:\n\n- `disabled`: If this is set to `true`, then all of the downshift button event\n  handlers will be omitted (it wont toggle the menu when clicked).\n- `aria-label`: The `aria-label` prop is in English. You should probably\n  override this yourself so you can provide translations:\n\n```jsx\nconst myButton = (\n  <button\n    {...getToggleButtonProps({\n      'aria-label': translateWithId(isOpen ? 'close.menu' : 'open.menu'),\n    })}\n  />\n)\n```\n\n### actions\n\nThese are functions you can call to change the state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property                | type                                                             | description                                                                                                                                                                                                                                                            |\n| ----------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `clearSelection`        | `function(cb: Function)`                                         | clears the selection                                                                                                                                                                                                                                                   |\n| `clearItems`            | `function()`                                                     | Clears downshift's record of all the items. Only really useful if you render your items asynchronously within downshift. See [#186](https://github.com/downshift-js/downshift/issues/186)                                                                              |\n| `closeMenu`             | `function(cb: Function)`                                         | closes the menu                                                                                                                                                                                                                                                        |\n| `openMenu`              | `function(cb: Function)`                                         | opens the menu                                                                                                                                                                                                                                                         |\n| `selectHighlightedItem` | `function(otherStateToSet: object, cb: Function)`                | selects the item that is currently highlighted                                                                                                                                                                                                                         |\n| `selectItem`            | `function(item: any, otherStateToSet: object, cb: Function)`     | selects the given item                                                                                                                                                                                                                                                 |\n| `selectItemAtIndex`     | `function(index: number, otherStateToSet: object, cb: Function)` | selects the item at the given index                                                                                                                                                                                                                                    |\n| `setHighlightedIndex`   | `function(index: number, otherStateToSet: object, cb: Function)` | call to set a new highlighted index                                                                                                                                                                                                                                    |\n| `toggleMenu`            | `function(otherStateToSet: object, cb: Function)`                | toggle the menu open state                                                                                                                                                                                                                                             |\n| `reset`                 | `function(otherStateToSet: object, cb: Function)`                | this resets downshift's state to a reasonable default                                                                                                                                                                                                                  |\n| `setItemCount`          | `function(count: number)`                                        | this sets the `itemCount`. Handy in situations where you're using windowing and the items are loaded asynchronously from within downshift (so you can't use the `itemCount` prop.                                                                                      |\n| `unsetItemCount`        | `function()`                                                     | this unsets the `itemCount` which means the item count will be calculated instead by the `itemCount` prop or based on how many times you call `getItemProps`.                                                                                                          |\n| `setState`              | `function(stateToSet: object, cb: Function)`                     | This is a general `setState` function. It uses downshift's `internalSetState` function which works with control props and calls your `onSelect`, `onChange`, etc. (Note, you can specify a `type` which you can reference in some other APIs like the `stateReducer`). |\n\n> `otherStateToSet` refers to an object to set other internal state. It is\n> recommended to avoid abusing this, but is available if you need it.\n\n### state\n\nThese are values that represent the current state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property           | type              | description                                    |\n| ------------------ | ----------------- | ---------------------------------------------- |\n| `highlightedIndex` | `number` / `null` | the currently highlighted item                 |\n| `inputValue`       | `string` / `null` | the current value of the `getInputProps` input |\n| `isOpen`           | `boolean`         | the menu open state                            |\n| `selectedItem`     | `any`             | the currently selected item input              |\n\n### props\n\nAs a convenience, the `id` and `itemToString` props which you pass to\n`<Downshift />` are available here as well.\n\n## Event Handlers\n\nDownshift has a few events for which it provides implicit handlers. Several of\nthese handlers call `event.preventDefault()`. Their additional functionality is\ndescribed below.\n\n### default handlers\n\n- `ArrowDown`: if menu is closed, opens it and moves the highlighted index to\n  `defaultHighlightedIndex + 1`, if `defaultHighlightedIndex` is provided, or to\n  the top-most item, if not. If menu is open, it moves the highlighted index\n  down by 1. If the shift key is held when this event fires, the highlighted\n  index will jump down 5 indices instead of 1. NOTE: if the current highlighted\n  index is within the bottom 5 indices, the top-most index will be highlighted.)\n\n- `ArrowUp`: if menu is closed, opens it and moves the highlighted index to\n  `defaultHighlightedIndex - 1`, if `defaultHighlightedIndex` is provided, or to\n  the bottom-most item, if not. If menu is open, moves the highlighted index up\n  by 1. If the shift key is held when this event fires, the highlighted index\n  will jump up 5 indices instead of 1. NOTE: if the current highlighted index is\n  within the top 5 indices, the bottom-most index will be highlighted.)\n\n- `Home`: if menu is closed, it will not add any other behavior. If menu is\n  open, the top-most index will get highlighted.\n\n- `End`: if menu is closed, it will not add any other behavior. If menu is open,\n  the bottom-most index will get highlighted.\n\n- `Enter`: if the menu is open, selects the currently highlighted item. If the\n  menu is open, the usual 'Enter' event is prevented by Downshift's default\n  implicit enter handler; so, for example, a form submission event will not work\n  as one might expect (though if the menu is closed the form submission will\n  work normally). See below for customizing the handlers.\n\n- `Escape`: will clear downshift's state. This means that `highlightedIndex`\n  will be set to the `defaultHighlightedIndex` and the `isOpen` state will be\n  set to the `defaultIsOpen`. If `isOpen` is already false, the `inputValue`\n  will be set to an empty string and `selectedItem` will be set to `null`\n\n### customizing handlers\n\nYou can provide your own event handlers to Downshift which will be called before\nthe default handlers:\n\n```javascript\nconst ui = (\n  <Downshift>\n    {({getInputProps}) => (\n      <input\n        {...getInputProps({\n          onKeyDown: event => {\n            // your handler code\n          },\n        })}\n      />\n    )}\n  </Downshift>\n)\n```\n\nIf you would like to prevent the default handler behavior in some cases, you can\nset the event's `preventDownshiftDefault` property to `true`:\n\n```javascript\nconst ui = (\n  <Downshift>\n    {({getInputProps}) => (\n      <input\n        {...getInputProps({\n          onKeyDown: event => {\n            if (event.key === 'Enter') {\n              // Prevent Downshift's default 'Enter' behavior.\n              event.nativeEvent.preventDownshiftDefault = true\n\n              // your handler code\n            }\n          },\n        })}\n      />\n    )}\n  </Downshift>\n)\n```\n\nIf you would like to completely override Downshift's behavior for a handler, in\nfavor of your own, you can bypass prop getters:\n\n```javascript\nconst ui = (\n  <Downshift>\n    {({getInputProps}) => (\n      <input\n        {...getInputProps()}\n        onKeyDown={event => {\n          // your handler code\n        }}\n      />\n    )}\n  </Downshift>\n)\n```\n\n## Utilities\n\n### resetIdCounter\n\nAllows reseting the internal id counter which is used to generate unique ids for\nDownshift component.\n\n**This is unnecessary if you are using React 18 or newer**\n\nYou should never need to use this in the browser. Only if you are running an\nuniversal React app that is rendered on the server you should call\n[resetIdCounter](#resetidcounter) before every render so that the ids that get\ngenerated on the server match the ids generated in the browser.\n\n```javascript\nimport {resetIdCounter} from 'downshift';\n\nresetIdCounter()\nReactDOMServer.renderToString(...);\n```\n\n## React Native\n\nSince Downshift renders it's UI using render props, Downshift supports rendering\non React Native with ease. Use components like `<View>`, `<Text>`,\n`<TouchableOpacity>` and others inside of your render method to generate awesome\nautocomplete, dropdown, or selection components.\n\n### Gotchas\n\n- Your root view will need to either pass a ref to `getRootProps` or call\n  `getRootProps` with `{ suppressRefError: true }`. This ref is used to catch a\n  common set of errors around composite components.\n  [Learn more in `getRootProps`](#getrootprops).\n- When using a `<FlatList>` or `<ScrollView>`, be sure to supply the\n  [`keyboardShouldPersistTaps`](https://facebook.github.io/react-native/docs/scrollview.html#keyboardshouldpersisttaps)\n  prop to ensure that your text input stays focus, while allowing for taps on\n  the touchables rendered for your items.\n\n## Advanced React Component Patterns course\n\n[Kent C. Dodds](https://twitter.com/kentcdodds) has created learning material\nbased on the patterns implemented in this component. You can find it on various\nplatforms:\n\n1.  [egghead.io](https://egghead.io/courses/advanced-react-component-patterns)\n2.  [Frontend Masters](https://frontendmasters.com/courses/advanced-react-patterns/)\n3.  YouTube (for free!):\n    [Part 1](https://www.youtube.com/watch?v=SuzutbwjUp8&list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf)\n    and\n    [Part 2](https://www.youtube.com/watch?v=ubXtOROjILU&list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf)\n\n## Examples\n\n> 🚨 We're in the process of moving all examples to the\n> [downshift-examples](https://github.com/downshift-js/downshift-examples) repo\n> (which you can open, interact with, and contribute back to live on\n> [codesandbox](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1))\n\n> 🚨 We're also in the process of updating our examples from the\n> [downshift-docs](https://github.com/downshift-js/downshift-docs) repo which is\n> actually used to create our docsite at [downshift-js.com][docsite]). Make sure\n> to check it out for the most relevant Downshift examples or try out the new\n> hooks that aim to replace Downshift.\n\n**Ordered Examples:**\n\nIf you're just learning downshift, review these in order:\n\n0.  [basic automplete with getRootProps](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F00-get-root-props-example.js%3A11%2C21&moduleview=1) -\n    the same as example #1 but using the correct HTML structure as suggested by\n    ARIA-WCAG.\n1.  [basic autocomplete](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F01-basic-autocomplete.js&moduleview=1) -\n    very bare bones, not styled at all. Good place to start.\n2.  [styled autocomplete](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F02-complete-autocomplete.js&moduleview=1) -\n    more complete autocomplete solution using emotion for styling and\n    match-sorter for filtering the items.\n3.  [typeahead](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F03-typeahead.js&moduleview=1) -\n    Shows how to control the `selectedItem` so the selected item can be one of\n    your items or whatever the user types.\n4.  [multi-select](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F04-multi-select.js&moduleview=1) -\n    Shows how to create a MultiDownshift component that allows for an array of\n    selectedItems for multiple selection using a state reducer\n\n**Other Examples:**\n\nCheck out these examples of more advanced use/edge cases:\n\n- [dropdown with select by key](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fother-examples%2Fdropdown-select-by-key%2FCustomDropdown%2Findex.js&moduleview=1) -\n  An example of using the render prop pattern to utilize a reusable component to\n  provide the downshift dropdown component with the functionality of being able\n  to highlight a selection item that starts with the key pressed.\n- [using actions](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fother-examples%2Fusing-actions.js&moduleview=1) -\n  An example of using one of downshift's actions as an event handler.\n- [gmail's composition recipients field](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fother-examples%2Fgmail%2Findex.js&moduleview=1) -\n  An example of a highly complex autocomplete component featuring asynchronously\n  loading items, multiple selection, and windowing (with react-virtualized)\n- [Downshift HOC and Compound Components](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fother-examples%2Fhoc%2Findex.js&moduleview=1) -\n  An example of how to implementat compound components with\n  `React.createContext` and a downshift higher order component. This is\n  generally not recommended because the render prop API exported by downshift is\n  generally good enough for everyone, but there's nothing technically wrong with\n  doing something like this.\n\n**Old Examples exist on [codesandbox.io][examples]:**\n\n_🚨 This is a great contribution opportunity!_ These are examples that have not\nyet been migrated to\n[downshift-examples](https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1).\nYou're more than welcome to make PRs to the examples repository to move these\nexamples over there.\n[Watch this to learn how to contribute completely in the browser](https://www.youtube.com/watch?v=3PAQbhdkTtI&index=2&t=21s&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u)\n\n- [Integration with Apollo](https://codesandbox.io/s/m5zrvqj85p)\n- [Integration with Redux](https://codesandbox.io/s/3ywmnyr0zq)\n- [Integration with `react-instantsearch`](https://codesandbox.io/s/kvn0lpp83)\n  from Algolia\n- [Material-UI (1.0.0-beta.4) Combobox Using Downshift](https://codesandbox.io/s/QMGq4kAY)\n- [Material-UI (1.0.0-beta.33) Multiple select with autocomplete](https://codesandbox.io/s/7k3674z09q)\n- [Integration with `GenieJS`](https://codesandbox.io/s/jRLKrxwgl)\n  ([learn more about `genie` here](https://github.com/kentcdodds/genie))\n- [Handling and displaying errors](https://codesandbox.io/s/zKE37vorr)\n- [Integration with React Router](https://codesandbox.io/s/ww9lwloy8w)\n- [Windowing with `react-tiny-virtual-list`](https://codesandbox.io/s/v670kq95l)\n- [Section/option group example](https://codesandbox.io/s/zx1kj58npl)\n- [Integration with `fuzzaldrin-plus` (Fuzzy matching)](https://codesandbox.io/s/pyq3v4o3j)\n- [Dropdown/select implementation with Bootstrap](https://codesandbox.io/s/53y8jvpj0k)\n- [Multiple editable tag selection](https://codesandbox.io/s/o4yp9vmm8z)\n- [Downshift implemented as compound components and a Higher Order Component](https://codesandbox.io/s/017n1jqo00)\n  (exposes a `withDownshift` higher order component which you can use to get at\n  the state, actions, prop getters in a rendered downshift tree).\n- [Downshift Spectre.css example](https://codesandbox.io/s/M89KQOBRB)\n- [Integration with `redux-form`](https://codesandbox.io/s/k594964z13)\n- [Integration with `react-final-form`](https://codesandbox.io/s/qzm43nn2mj)\n- [Provider Pattern](https://codesandbox.io/s/mywzk3133p) - how to avoid\n  prop-drilling if you like to break up your render method into more components\n- [React Native example](https://snack.expo.io/SkE0LxXqM)\n- [React VR example](https://github.com/infiniteluke/bassdrop)\n- [Multiple checkbox selection](https://codesandbox.io/s/5z711pmr3l)\n\n## FAQ\n\n<details>\n\n<summary>\n  How do I avoid the checksum error when server rendering (SSR)?\n</summary>\n\nThe checksum error you're seeing is most likely due to the automatically\ngenerated `id` and/or `htmlFor` prop you get from `getInputProps` and\n`getLabelProps` (respectively). It could also be from the automatically\ngenerated `id` prop you get from `getItemProps` (though this is not likely as\nyou're probably not rendering any items when rendering a downshift component on\nthe server).\n\nTo avoid these problems, simply call [resetIdCounter](#resetidcounter) before\n`ReactDOM.renderToString`.\n\nAlternatively you could provide your own ids via the id props where you render\n`<Downshift />`:\n\n```javascript\nconst ui = (\n  <Downshift\n    id=\"autocomplete\"\n    labelId=\"autocomplete-label\"\n    inputId=\"autocomplete-input\"\n    menuId=\"autocomplete-menu\"\n  >\n    {({getInputProps, getLabelProps}) => <div>{/* your UI */}</div>}\n  </Downshift>\n)\n```\n\n</details>\n\n## Inspiration\n\nI was heavily inspired by [Ryan Florence][ryan]. Watch his (free) lesson about\n[\"Compound Components\"][compound-components-lecture]. Initially downshift was a\ngroup of compound components using context to communicate. But then [Jared\nForsyth][jared] suggested I expose functions (the prop getters) to get props to\napply to the elements rendered. That bit of inspiration made a big impact on the\nflexibility and simplicity of this API.\n\nI also took a few ideas from the code in\n[`react-autocomplete`][react-autocomplete] and [jQuery UI's\nAutocomplete][jquery-complete].\n\nYou can watch me build the first iteration of `downshift` on YouTube:\n\n- [Part 1](https://www.youtube.com/watch?v=2kzD1IjDy5s&list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE&index=11)\n- [Part 2](https://www.youtube.com/watch?v=w1Z7Jvj08_s&list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE&index=10)\n\nYou'll find more recordings of me working on `downshift` on [my livestream\nYouTube playlist][yt-playlist].\n\n## Other Solutions\n\nYou can implement these other solutions using `downshift`, but if you'd prefer\nto use these out of the box solutions, then that's fine too:\n\n- [`react-select`](https://github.com/JedWatson/react-select)\n- [`react-autosuggest`](https://github.com/moroshko/react-autosuggest)\n\n## Bindings for ReasonML\n\nIf you're developing some React in ReasonML, check out the\n[`Downshift` bindings](https://github.com/reasonml-community/bs-downshift) for\nthat.\n\n## Contributors\n\nThanks goes to these people ([emoji key][emojis]):\n\n<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->\n<!-- prettier-ignore-start -->\n<!-- markdownlint-disable -->\n<table>\n  <tbody>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://kentcdodds.com\"><img src=\"https://avatars.githubusercontent.com/u/1500684?v=3?s=100\" width=\"100px;\" alt=\"Kent C. Dodds\"/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=kentcdodds\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kentcdodds\" title=\"Documentation\">📖</a> <a href=\"#infra-kentcdodds\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kentcdodds\" title=\"Tests\">⚠️</a> <a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3Akentcdodds\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"#blog-kentcdodds\" title=\"Blogposts\">📝</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Akentcdodds\" title=\"Bug reports\">🐛</a> <a href=\"#example-kentcdodds\" title=\"Examples\">💡</a> <a href=\"#ideas-kentcdodds\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"#talk-kentcdodds\" title=\"Talks\">📢</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/ryanflorence\"><img src=\"https://avatars0.githubusercontent.com/u/100200?v=4?s=100\" width=\"100px;\" alt=\"Ryan Florence\"/><br /><sub><b>Ryan Florence</b></sub></a><br /><a href=\"#ideas-ryanflorence\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://jaredforsyth.com\"><img src=\"https://avatars3.githubusercontent.com/u/112170?v=4?s=100\" width=\"100px;\" alt=\"Jared Forsyth\"/><br /><sub><b>Jared Forsyth</b></sub></a><br /><a href=\"#ideas-jaredly\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=jaredly\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jtmthf\"><img src=\"https://avatars1.githubusercontent.com/u/8162598?v=4?s=100\" width=\"100px;\" alt=\"Jack Moore\"/><br /><sub><b>Jack Moore</b></sub></a><br /><a href=\"#example-jtmthf\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://souporserious.com/\"><img src=\"https://avatars1.githubusercontent.com/u/2762082?v=4?s=100\" width=\"100px;\" alt=\"Travis Arnold\"/><br /><sub><b>Travis Arnold</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=souporserious\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=souporserious\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://marcysutton.com\"><img src=\"https://avatars0.githubusercontent.com/u/1045233?v=4?s=100\" width=\"100px;\" alt=\"Marcy Sutton\"/><br /><sub><b>Marcy Sutton</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Amarcysutton\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-marcysutton\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.jeremygayed.com\"><img src=\"https://avatars2.githubusercontent.com/u/244704?v=4?s=100\" width=\"100px;\" alt=\"Jeremy Gayed\"/><br /><sub><b>Jeremy Gayed</b></sub></a><br /><a href=\"#example-tizmagik\" title=\"Examples\">💡</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://haroen.me\"><img src=\"https://avatars3.githubusercontent.com/u/6270048?v=4?s=100\" width=\"100px;\" alt=\"Haroen Viaene\"/><br /><sub><b>Haroen Viaene</b></sub></a><br /><a href=\"#example-Haroenv\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/rezof\"><img src=\"https://avatars2.githubusercontent.com/u/15073300?v=4?s=100\" width=\"100px;\" alt=\"monssef\"/><br /><sub><b>monssef</b></sub></a><br /><a href=\"#example-rezof\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://fezvrasta.github.io\"><img src=\"https://avatars2.githubusercontent.com/u/5382443?v=4?s=100\" width=\"100px;\" alt=\"Federico Zivolo\"/><br /><sub><b>Federico Zivolo</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=FezVrasta\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://divyendusingh.com\"><img src=\"https://avatars3.githubusercontent.com/u/746482?v=4?s=100\" width=\"100px;\" alt=\"Divyendu Singh\"/><br /><sub><b>Divyendu Singh</b></sub></a><br /><a href=\"#example-divyenduz\" title=\"Examples\">💡</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=divyenduz\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=divyenduz\" title=\"Documentation\">📖</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=divyenduz\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/salmanmanekia\"><img src=\"https://avatars1.githubusercontent.com/u/841955?v=4?s=100\" width=\"100px;\" alt=\"Muhammad Salman\"/><br /><sub><b>Muhammad Salman</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=salmanmanekia\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://twitter.com/psicotropidev\"><img src=\"https://avatars3.githubusercontent.com/u/10820159?v=4?s=100\" width=\"100px;\" alt=\"João Alberto\"/><br /><sub><b>João Alberto</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=psicotropicos\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/bernard-lin\"><img src=\"https://avatars0.githubusercontent.com/u/16327281?v=4?s=100\" width=\"100px;\" alt=\"Bernard Lin\"/><br /><sub><b>Bernard Lin</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=bernard-lin\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=bernard-lin\" title=\"Documentation\">📖</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://geoffdavis.info\"><img src=\"https://avatars1.githubusercontent.com/u/7330124?v=4?s=100\" width=\"100px;\" alt=\"Geoff Davis\"/><br /><sub><b>Geoff Davis</b></sub></a><br /><a href=\"#example-geoffdavis92\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/reznord\"><img src=\"https://avatars0.githubusercontent.com/u/3415488?v=4?s=100\" width=\"100px;\" alt=\"Anup\"/><br /><sub><b>Anup</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=reznord\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://ferdinandsalis.com\"><img src=\"https://avatars0.githubusercontent.com/u/340520?v=4?s=100\" width=\"100px;\" alt=\"Ferdinand Salis\"/><br /><sub><b>Ferdinand Salis</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Aferdinandsalis\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=ferdinandsalis\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/tkh44\"><img src=\"https://avatars2.githubusercontent.com/u/662750?v=4?s=100\" width=\"100px;\" alt=\"Kye Hohenberger\"/><br /><sub><b>Kye Hohenberger</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Atkh44\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jgoux\"><img src=\"https://avatars0.githubusercontent.com/u/1443499?v=4?s=100\" width=\"100px;\" alt=\"Julien Goux\"/><br /><sub><b>Julien Goux</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ajgoux\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=jgoux\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=jgoux\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jseminck\"><img src=\"https://avatars2.githubusercontent.com/u/9586897?v=4?s=100\" width=\"100px;\" alt=\"Joachim Seminck\"/><br /><sub><b>Joachim Seminck</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jseminck\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://jesseharlin.net/\"><img src=\"https://avatars3.githubusercontent.com/u/954596?v=4?s=100\" width=\"100px;\" alt=\"Jesse Harlin\"/><br /><sub><b>Jesse Harlin</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Athe-simian\" title=\"Bug reports\">🐛</a> <a href=\"#example-the-simian\" title=\"Examples\">💡</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/pbomb\"><img src=\"https://avatars0.githubusercontent.com/u/1402095?v=4?s=100\" width=\"100px;\" alt=\"Matt Parrish\"/><br /><sub><b>Matt Parrish</b></sub></a><br /><a href=\"#tool-pbomb\" title=\"Tools\">🔧</a> <a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3Apbomb\" title=\"Reviewed Pull Requests\">👀</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://thom.kr\"><img src=\"https://avatars1.githubusercontent.com/u/11661846?v=4?s=100\" width=\"100px;\" alt=\"thom\"/><br /><sub><b>thom</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=thomhos\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/tranvu\"><img src=\"https://avatars2.githubusercontent.com/u/1088312?v=4?s=100\" width=\"100px;\" alt=\"Vu Tran\"/><br /><sub><b>Vu Tran</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=vutran\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/codiemullins\"><img src=\"https://avatars1.githubusercontent.com/u/74193?v=4?s=100\" width=\"100px;\" alt=\"Codie Mullins\"/><br /><sub><b>Codie Mullins</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=codiemullins\" title=\"Code\">💻</a> <a href=\"#example-codiemullins\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://morajabi.me\"><img src=\"https://avatars3.githubusercontent.com/u/12202757?v=4?s=100\" width=\"100px;\" alt=\"Mohammad Rajabifard\"/><br /><sub><b>Mohammad Rajabifard</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=morajabi\" title=\"Documentation\">📖</a> <a href=\"#ideas-morajabi\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/tansongyang\"><img src=\"https://avatars3.githubusercontent.com/u/9488719?v=4?s=100\" width=\"100px;\" alt=\"Frank Tan\"/><br /><sub><b>Frank Tan</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=tansongyang\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://kierb.com\"><img src=\"https://avatars3.githubusercontent.com/u/5093058?v=4?s=100\" width=\"100px;\" alt=\"Kier Borromeo\"/><br /><sub><b>Kier Borromeo</b></sub></a><br /><a href=\"#example-srph\" title=\"Examples\">💡</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/paul-veevers\"><img src=\"https://avatars1.githubusercontent.com/u/8969456?v=4?s=100\" width=\"100px;\" alt=\"Paul Veevers\"/><br /><sub><b>Paul Veevers</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=paul-veevers\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Ronolibert\"><img src=\"https://avatars2.githubusercontent.com/u/13622298?v=4?s=100\" width=\"100px;\" alt=\"Ron Cruz\"/><br /><sub><b>Ron Cruz</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=Ronolibert\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://rickmcgavin.github.io\"><img src=\"https://avatars1.githubusercontent.com/u/13605633?v=4?s=100\" width=\"100px;\" alt=\"Rick McGavin\"/><br /><sub><b>Rick McGavin</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=rickMcGavin\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/vejersele\"><img src=\"https://avatars0.githubusercontent.com/u/869669?v=4?s=100\" width=\"100px;\" alt=\"Jelle Versele\"/><br /><sub><b>Jelle Versele</b></sub></a><br /><a href=\"#example-vejersele\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/brentertz\"><img src=\"https://avatars1.githubusercontent.com/u/202773?v=4?s=100\" width=\"100px;\" alt=\"Brent Ertz\"/><br /><sub><b>Brent Ertz</b></sub></a><br /><a href=\"#ideas-brentertz\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Dajust\"><img src=\"https://avatars3.githubusercontent.com/u/8015514?v=4?s=100\" width=\"100px;\" alt=\"Justice Mba \"/><br /><sub><b>Justice Mba </b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=Dajust\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Dajust\" title=\"Documentation\">📖</a> <a href=\"#ideas-Dajust\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://mfellis.com\"><img src=\"https://avatars2.githubusercontent.com/u/3925281?v=4?s=100\" width=\"100px;\" alt=\"Mark Ellis\"/><br /><sub><b>Mark Ellis</b></sub></a><br /><a href=\"#ideas-ellismarkf\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://ronak.io/\"><img src=\"https://avatars1.githubusercontent.com/u/3241922?v=4?s=100\" width=\"100px;\" alt=\"us͡an̸df͘rien͜ds͠\"/><br /><sub><b>us͡an̸df͘rien͜ds͠</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ausandfriends\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=usandfriends\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=usandfriends\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.robin-drexler.com/\"><img src=\"https://avatars0.githubusercontent.com/u/474248?v=4?s=100\" width=\"100px;\" alt=\"Robin Drexler\"/><br /><sub><b>Robin Drexler</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Arobin-drexler\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=robin-drexler\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://arturoromero.info/\"><img src=\"https://avatars0.githubusercontent.com/u/7406639?v=4?s=100\" width=\"100px;\" alt=\"Arturo Romero\"/><br /><sub><b>Arturo Romero</b></sub></a><br /><a href=\"#example-arturoromeroslc\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://algolab.eu/pirola\"><img src=\"https://avatars1.githubusercontent.com/u/275483?v=4?s=100\" width=\"100px;\" alt=\"yp\"/><br /><sub><b>yp</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ayp\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=yp\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=yp\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.warbyparker.com\"><img src=\"https://avatars0.githubusercontent.com/u/3998604?v=4?s=100\" width=\"100px;\" alt=\"Dave Garwacke\"/><br /><sub><b>Dave Garwacke</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=ifyoumakeit\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://linkedin.com/in/drapegnik\"><img src=\"https://avatars3.githubusercontent.com/u/11758660?v=4?s=100\" width=\"100px;\" alt=\"Ivan Pazhitnykh\"/><br /><sub><b>Ivan Pazhitnykh</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=Drapegnik\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Drapegnik\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Rendez\"><img src=\"https://avatars0.githubusercontent.com/u/61776?v=4?s=100\" width=\"100px;\" alt=\"Luis Merino\"/><br /><sub><b>Luis Merino</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=Rendez\" title=\"Documentation\">📖</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/arahansen\"><img src=\"https://avatars0.githubusercontent.com/u/8746094?v=4?s=100\" width=\"100px;\" alt=\"Andrew Hansen\"/><br /><sub><b>Andrew Hansen</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=arahansen\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=arahansen\" title=\"Tests\">⚠️</a> <a href=\"#ideas-arahansen\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.johnwhiles.com\"><img src=\"https://avatars3.githubusercontent.com/u/20307225?v=4?s=100\" width=\"100px;\" alt=\"John Whiles\"/><br /><sub><b>John Whiles</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=Jwhiles\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/wKovacs64\"><img src=\"https://avatars1.githubusercontent.com/u/1288694?v=4?s=100\" width=\"100px;\" alt=\"Justin Hall\"/><br /><sub><b>Justin Hall</b></sub></a><br /><a href=\"#infra-wKovacs64\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://twitter.com/pete_tnt\"><img src=\"https://avatars2.githubusercontent.com/u/7641760?v=4?s=100\" width=\"100px;\" alt=\"Pete Nykänen\"/><br /><sub><b>Pete Nykänen</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3Apetetnt\" title=\"Reviewed Pull Requests\">👀</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://jaredpalmer.com\"><img src=\"https://avatars2.githubusercontent.com/u/4060187?v=4?s=100\" width=\"100px;\" alt=\"Jared Palmer\"/><br /><sub><b>Jared Palmer</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jaredpalmer\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.philipyoungg.com\"><img src=\"https://avatars3.githubusercontent.com/u/11477718?v=4?s=100\" width=\"100px;\" alt=\"Philip Young\"/><br /><sub><b>Philip Young</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=philipyoungg\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=philipyoungg\" title=\"Tests\">⚠️</a> <a href=\"#ideas-philipyoungg\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://alexandernanberg.com\"><img src=\"https://avatars3.githubusercontent.com/u/8997319?v=4?s=100\" width=\"100px;\" alt=\"Alexander Nanberg\"/><br /><sub><b>Alexander Nanberg</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=alexandernanberg\" title=\"Documentation\">📖</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=alexandernanberg\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://httpete.com\"><img src=\"https://avatars2.githubusercontent.com/u/1556430?v=4?s=100\" width=\"100px;\" alt=\"Pete Redmond\"/><br /><sub><b>Pete Redmond</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ahttpete-ire\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Zashy\"><img src=\"https://avatars2.githubusercontent.com/u/1706342?v=4?s=100\" width=\"100px;\" alt=\"Nick Lavin\"/><br /><sub><b>Nick Lavin</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3AZashy\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Zashy\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Zashy\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://jlongster.com\"><img src=\"https://avatars2.githubusercontent.com/u/17031?v=4?s=100\" width=\"100px;\" alt=\"James Long\"/><br /><sub><b>James Long</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ajlongster\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=jlongster\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://michaelball.co\"><img src=\"https://avatars0.githubusercontent.com/u/1505907?v=4?s=100\" width=\"100px;\" alt=\"Michael Ball\"/><br /><sub><b>Michael Ball</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Acycomachead\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=cycomachead\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=cycomachead\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Julienng\"><img src=\"https://avatars0.githubusercontent.com/u/8990614?v=4?s=100\" width=\"100px;\" alt=\"CAVALEIRO Julien\"/><br /><sub><b>CAVALEIRO Julien</b></sub></a><br /><a href=\"#example-Julienng\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.kimgronqvist.se\"><img src=\"https://avatars1.githubusercontent.com/u/3421067?v=4?s=100\" width=\"100px;\" alt=\"Kim Grönqvist\"/><br /><sub><b>Kim Grönqvist</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=kimgronqvist\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kimgronqvist\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://sijietian.com\"><img src=\"https://avatars2.githubusercontent.com/u/3675602?v=4?s=100\" width=\"100px;\" alt=\"Sijie\"/><br /><sub><b>Sijie</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Atiansijie\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=tiansijie\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://dsds.io\"><img src=\"https://avatars0.githubusercontent.com/u/410792?v=4?s=100\" width=\"100px;\" alt=\"Dony Sukardi\"/><br /><sub><b>Dony Sukardi</b></sub></a><br /><a href=\"#example-donysukardi\" title=\"Examples\">💡</a> <a href=\"#question-donysukardi\" title=\"Answering Questions\">💬</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=donysukardi\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=donysukardi\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://dillonmulroy.com\"><img src=\"https://avatars1.githubusercontent.com/u/2755722?v=4?s=100\" width=\"100px;\" alt=\"Dillon Mulroy\"/><br /><sub><b>Dillon Mulroy</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=dmmulroy\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://twitter.com/curtytate\"><img src=\"https://avatars3.githubusercontent.com/u/12440573?v=4?s=100\" width=\"100px;\" alt=\"Curtis Tate Wilkinson\"/><br /><sub><b>Curtis Tate Wilkinson</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=curtiswilkinson\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/brikou\"><img src=\"https://avatars3.githubusercontent.com/u/383212?v=4?s=100\" width=\"100px;\" alt=\"Brice BERNARD\"/><br /><sub><b>Brice BERNARD</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Abrikou\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=brikou\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/xutopia\"><img src=\"https://avatars3.githubusercontent.com/u/14304503?v=4?s=100\" width=\"100px;\" alt=\"Tony Xu\"/><br /><sub><b>Tony Xu</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=xutopia\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://anthonyng.me\"><img src=\"https://avatars1.githubusercontent.com/u/14035529?v=4?s=100\" width=\"100px;\" alt=\"Anthony Ng\"/><br /><sub><b>Anthony Ng</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=newyork-anthonyng\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/notruth\"><img src=\"https://avatars2.githubusercontent.com/u/11996139?v=4?s=100\" width=\"100px;\" alt=\"S S\"/><br /><sub><b>S S</b></sub></a><br /><a href=\"#question-notruth\" title=\"Answering Questions\">💬</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=notruth\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=notruth\" title=\"Documentation\">📖</a> <a href=\"#ideas-notruth\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=notruth\" title=\"Tests\">⚠️</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://austintackaberry.co\"><img src=\"https://avatars0.githubusercontent.com/u/29493001?v=4?s=100\" width=\"100px;\" alt=\"Austin Tackaberry\"/><br /><sub><b>Austin Tackaberry</b></sub></a><br /><a href=\"#question-austintackaberry\" title=\"Answering Questions\">💬</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=austintackaberry\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=austintackaberry\" title=\"Documentation\">📖</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Aaustintackaberry\" title=\"Bug reports\">🐛</a> <a href=\"#example-austintackaberry\" title=\"Examples\">💡</a> <a href=\"#ideas-austintackaberry\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3Aaustintackaberry\" title=\"Reviewed Pull Requests\">👀</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=austintackaberry\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jduthon\"><img src=\"https://avatars3.githubusercontent.com/u/4168055?v=4?s=100\" width=\"100px;\" alt=\"Jean Duthon\"/><br /><sub><b>Jean Duthon</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ajduthon\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=jduthon\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://antontelesh.github.io\"><img src=\"https://avatars3.githubusercontent.com/u/3889580?v=4?s=100\" width=\"100px;\" alt=\"Anton Telesh\"/><br /><sub><b>Anton Telesh</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3AAntontelesh\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Antontelesh\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/ericedem\"><img src=\"https://avatars3.githubusercontent.com/u/1060669?v=4?s=100\" width=\"100px;\" alt=\"Eric Edem\"/><br /><sub><b>Eric Edem</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=ericedem\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=ericedem\" title=\"Documentation\">📖</a> <a href=\"#ideas-ericedem\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=ericedem\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/indiesquidge\"><img src=\"https://avatars3.githubusercontent.com/u/3409645?v=4?s=100\" width=\"100px;\" alt=\"Austin Wood\"/><br /><sub><b>Austin Wood</b></sub></a><br /><a href=\"#question-indiesquidge\" title=\"Answering Questions\">💬</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=indiesquidge\" title=\"Documentation\">📖</a> <a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3Aindiesquidge\" title=\"Reviewed Pull Requests\">👀</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mmmurray\"><img src=\"https://avatars3.githubusercontent.com/u/14275790?v=4?s=100\" width=\"100px;\" alt=\"Mark Murray\"/><br /><sub><b>Mark Murray</b></sub></a><br /><a href=\"#infra-mmmurray\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/gsimone\"><img src=\"https://avatars0.githubusercontent.com/u/1862172?v=4?s=100\" width=\"100px;\" alt=\"Gianmarco\"/><br /><sub><b>Gianmarco</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Agsimone\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=gsimone\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/pastr\"><img src=\"https://avatars2.githubusercontent.com/u/6838136?v=4?s=100\" width=\"100px;\" alt=\"Emmanuel Pastor\"/><br /><sub><b>Emmanuel Pastor</b></sub></a><br /><a href=\"#example-pastr\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/dalehurwitz\"><img src=\"https://avatars2.githubusercontent.com/u/10345034?v=4?s=100\" width=\"100px;\" alt=\"dalehurwitz\"/><br /><sub><b>dalehurwitz</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=dalehurwitz\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/blobor\"><img src=\"https://avatars1.githubusercontent.com/u/4813007?v=4?s=100\" width=\"100px;\" alt=\"Bogdan Lobor\"/><br /><sub><b>Bogdan Lobor</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ablobor\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=blobor\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/infiniteluke\"><img src=\"https://avatars0.githubusercontent.com/u/1127238?v=4?s=100\" width=\"100px;\" alt=\"Luke Herrington\"/><br /><sub><b>Luke Herrington</b></sub></a><br /><a href=\"#example-infiniteluke\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/drobannx\"><img src=\"https://avatars2.githubusercontent.com/u/6361167?v=4?s=100\" width=\"100px;\" alt=\"Brandon Clemons\"/><br /><sub><b>Brandon Clemons</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=drobannx\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/aMollusk\"><img src=\"https://avatars0.githubusercontent.com/u/10591587?v=4?s=100\" width=\"100px;\" alt=\"Kieran\"/><br /><sub><b>Kieran</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=aMollusk\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/Brushedoctopus\"><img src=\"https://avatars3.githubusercontent.com/u/11570627?v=4?s=100\" width=\"100px;\" alt=\"Brushedoctopus\"/><br /><sub><b>Brushedoctopus</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3ABrushedoctopus\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=Brushedoctopus\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://cameronpedwards.com\"><img src=\"https://avatars3.githubusercontent.com/u/5456216?v=4?s=100\" width=\"100px;\" alt=\"Cameron Edwards\"/><br /><sub><b>Cameron Edwards</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=cameronprattedwards\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=cameronprattedwards\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/stereobooster\"><img src=\"https://avatars2.githubusercontent.com/u/179534?v=4?s=100\" width=\"100px;\" alt=\"stereobooster\"/><br /><sub><b>stereobooster</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=stereobooster\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=stereobooster\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/1Copenut\"><img src=\"https://avatars0.githubusercontent.com/u/934879?v=4?s=100\" width=\"100px;\" alt=\"Trevor Pierce\"/><br /><sub><b>Trevor Pierce</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3A1Copenut\" title=\"Reviewed Pull Requests\">👀</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://xuefei-frank.com\"><img src=\"https://avatars1.githubusercontent.com/u/1334982?v=4?s=100\" width=\"100px;\" alt=\"Xuefei Li\"/><br /><sub><b>Xuefei Li</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=franklixuefei\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://hyperlab.se\"><img src=\"https://avatars0.githubusercontent.com/u/7252803?v=4?s=100\" width=\"100px;\" alt=\"Alfred Ringstad\"/><br /><sub><b>Alfred Ringstad</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=alfredringstad\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/dovidweisz\"><img src=\"https://avatars0.githubusercontent.com/u/6895497?v=4?s=100\" width=\"100px;\" alt=\"D[oa]vid Weisz\"/><br /><sub><b>D[oa]vid Weisz</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=dovidweisz\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/RoystonS\"><img src=\"https://avatars0.githubusercontent.com/u/19773?v=4?s=100\" width=\"100px;\" alt=\"Royston Shufflebotham\"/><br /><sub><b>Royston Shufflebotham</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3ARoystonS\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=RoystonS\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://michaeldeboey.be\"><img src=\"https://avatars3.githubusercontent.com/u/6643991?v=4?s=100\" width=\"100px;\" alt=\"Michaël De Boey\"/><br /><sub><b>Michaël De Boey</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=MichaelDeBoey\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/EricHenry\"><img src=\"https://avatars3.githubusercontent.com/u/4412771?v=4?s=100\" width=\"100px;\" alt=\"Henry\"/><br /><sub><b>Henry</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=EricHenry\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.greenarrow.me\"><img src=\"https://avatars3.githubusercontent.com/u/2180127?v=4?s=100\" width=\"100px;\" alt=\"Andrew Walton\"/><br /><sub><b>Andrew Walton</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Agreen-arrow\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=green-arrow\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=green-arrow\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/arthurdenner\"><img src=\"https://avatars0.githubusercontent.com/u/13774309?v=4?s=100\" width=\"100px;\" alt=\"Arthur Denner\"/><br /><sub><b>Arthur Denner</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=arthurdenner\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/stipsan\"><img src=\"https://avatars2.githubusercontent.com/u/81981?v=4?s=100\" width=\"100px;\" alt=\"Cody Olsen\"/><br /><sub><b>Cody Olsen</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=stipsan\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/TLadd\"><img src=\"https://avatars0.githubusercontent.com/u/5084492?v=4?s=100\" width=\"100px;\" alt=\"Thomas Ladd\"/><br /><sub><b>Thomas Ladd</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=TLadd\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/lixualinta\"><img src=\"https://avatars3.githubusercontent.com/u/34634369?v=4?s=100\" width=\"100px;\" alt=\"lixualinta\"/><br /><sub><b>lixualinta</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=lixualinta\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://twitter.com/JCofman\"><img src=\"https://avatars2.githubusercontent.com/u/2118956?v=4?s=100\" width=\"100px;\" alt=\"Jacob Cofman\"/><br /><sub><b>Jacob Cofman</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=JCofman\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jf248\"><img src=\"https://avatars3.githubusercontent.com/u/19275184?v=4?s=100\" width=\"100px;\" alt=\"Joshua Freedman\"/><br /><sub><b>Joshua Freedman</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jf248\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/AmyScript\"><img src=\"https://avatars1.githubusercontent.com/u/24494020?v=4?s=100\" width=\"100px;\" alt=\"Amy\"/><br /><sub><b>Amy</b></sub></a><br /><a href=\"#example-AmyScript\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://twitter.com/roginfarrer\"><img src=\"https://avatars1.githubusercontent.com/u/9063669?v=4?s=100\" width=\"100px;\" alt=\"Rogin Farrer\"/><br /><sub><b>Rogin Farrer</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=roginfarrer\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/rifler\"><img src=\"https://avatars3.githubusercontent.com/u/871583?s=100\" width=\"100px;\" alt=\"Dmitrii Kanatnikov\"/><br /><sub><b>Dmitrii Kanatnikov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=rifler\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/dallonf\"><img src=\"https://avatars2.githubusercontent.com/u/346300?v=4?s=100\" width=\"100px;\" alt=\"Dallon Feldner\"/><br /><sub><b>Dallon Feldner</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Adallonf\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=dallonf\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://samuelfullerthomas.com\"><img src=\"https://avatars2.githubusercontent.com/u/10165959?v=4?s=100\" width=\"100px;\" alt=\"Samuel Fuller Thomas\"/><br /><sub><b>Samuel Fuller Thomas</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=samuelfullerthomas\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://audiolion.github.io\"><img src=\"https://avatars1.githubusercontent.com/u/2430381?v=4?s=100\" width=\"100px;\" alt=\"Ryan Castner\"/><br /><sub><b>Ryan Castner</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=audiolion\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/silviuavram\"><img src=\"https://avatars2.githubusercontent.com/u/11275392?v=4?s=100\" width=\"100px;\" alt=\"Silviu Alexandru Avram\"/><br /><sub><b>Silviu Alexandru Avram</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Asilviuavram\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=silviuavram\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=silviuavram\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/akronb\"><img src=\"https://avatars1.githubusercontent.com/u/15676655?v=4?s=100\" width=\"100px;\" alt=\"Anton Volkov\"/><br /><sub><b>Anton Volkov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=akronb\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=akronb\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://keegan.st\"><img src=\"https://avatars3.githubusercontent.com/u/513363?v=4?s=100\" width=\"100px;\" alt=\"Keegan Street\"/><br /><sub><b>Keegan Street</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Akeeganstreet\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=keeganstreet\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://manueldugue.de\"><img src=\"https://avatars1.githubusercontent.com/u/894149?v=4?s=100\" width=\"100px;\" alt=\"Manuel Dugué\"/><br /><sub><b>Manuel Dugué</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=mdugue\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mkaradeniz\"><img src=\"https://avatars2.githubusercontent.com/u/12477983?v=4?s=100\" width=\"100px;\" alt=\"Max Karadeniz\"/><br /><sub><b>Max Karadeniz</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=mkaradeniz\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://medium.com/@gonchub\"><img src=\"https://avatars3.githubusercontent.com/u/857221?v=4?s=100\" width=\"100px;\" alt=\"Gonzalo Beviglia\"/><br /><sub><b>Gonzalo Beviglia</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3AGonchuB\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=GonchuB\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/pulls?q=is%3Apr+reviewed-by%3AGonchuB\" title=\"Reviewed Pull Requests\">👀</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/kilrain\"><img src=\"https://avatars2.githubusercontent.com/u/47700687?v=4?s=100\" width=\"100px;\" alt=\"Brian Kilrain\"/><br /><sub><b>Brian Kilrain</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Akilrain\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kilrain\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kilrain\" title=\"Tests\">⚠️</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kilrain\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.gzschaler.de\"><img src=\"https://avatars0.githubusercontent.com/u/321265?v=4?s=100\" width=\"100px;\" alt=\"Gerd Zschaler\"/><br /><sub><b>Gerd Zschaler</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=rincedd\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Arincedd\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/gaskar\"><img src=\"https://avatars1.githubusercontent.com/u/491166?v=4?s=100\" width=\"100px;\" alt=\"Karen Gasparyan\"/><br /><sub><b>Karen Gasparyan</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=gaskar\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/kserjey\"><img src=\"https://avatars1.githubusercontent.com/u/19753880?v=4?s=100\" width=\"100px;\" alt=\"Sergey Korchinskiy\"/><br /><sub><b>Sergey Korchinskiy</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Akserjey\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kserjey\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=kserjey\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/edygar\"><img src=\"https://avatars.githubusercontent.com/u/566280?v=3?s=100\" width=\"100px;\" alt=\"Edygar Oliveira\"/><br /><sub><b>Edygar Oliveira</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=edygar\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Aedygar\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/epeicher\"><img src=\"https://avatars1.githubusercontent.com/u/3519124?v=4?s=100\" width=\"100px;\" alt=\"epeicher\"/><br /><sub><b>epeicher</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Aepeicher\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://francoischalifour.com\"><img src=\"https://avatars3.githubusercontent.com/u/6137112?v=4?s=100\" width=\"100px;\" alt=\"François Chalifour\"/><br /><sub><b>François Chalifour</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=francoischalifour\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=francoischalifour\" title=\"Tests\">⚠️</a> <a href=\"#platform-francoischalifour\" title=\"Packaging/porting to new platform\">📦</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/maxmalov\"><img src=\"https://avatars2.githubusercontent.com/u/284129?v=4?s=100\" width=\"100px;\" alt=\"Maxim Malov\"/><br /><sub><b>Maxim Malov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Amaxmalov\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=maxmalov\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://epiqueras.github.io\"><img src=\"https://avatars2.githubusercontent.com/u/19157096?v=4?s=100\" width=\"100px;\" alt=\"Enrique Piqueras\"/><br /><sub><b>Enrique Piqueras</b></sub></a><br /><a href=\"#ideas-epiqueras\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://twitter.com/layershifter\"><img src=\"https://avatars0.githubusercontent.com/u/14183168?v=4?s=100\" width=\"100px;\" alt=\"Oleksandr Fediashov\"/><br /><sub><b>Oleksandr Fediashov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=layershifter\" title=\"Code\">💻</a> <a href=\"#infra-layershifter\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a> <a href=\"#ideas-layershifter\" title=\"Ideas, Planning, & Feedback\">🤔</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/saitonakamura\"><img src=\"https://avatars1.githubusercontent.com/u/1552189?v=4?s=100\" width=\"100px;\" alt=\"Mikhail Bashurov\"/><br /><sub><b>Mikhail Bashurov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=saitonakamura\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Asaitonakamura\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://www.joshuagodi.com\"><img src=\"https://avatars1.githubusercontent.com/u/870799?v=4?s=100\" width=\"100px;\" alt=\"Joshua Godi\"/><br /><sub><b>Joshua Godi</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jgodi\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/lukyth\"><img src=\"https://avatars3.githubusercontent.com/u/7040242?v=4?s=100\" width=\"100px;\" alt=\"Kanitkorn Sujautra\"/><br /><sub><b>Kanitkorn Sujautra</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Alukyth\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=lukyth\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jorgemoya\"><img src=\"https://avatars3.githubusercontent.com/u/196129?v=4?s=100\" width=\"100px;\" alt=\"Jorge Moya\"/><br /><sub><b>Jorge Moya</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jorgemoya\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Ajorgemoya\" title=\"Bug reports\">🐛</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://kubajastrz.com\"><img src=\"https://avatars0.githubusercontent.com/u/6443113?v=4?s=100\" width=\"100px;\" alt=\"Jakub Jastrzębski\"/><br /><sub><b>Jakub Jastrzębski</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=KubaJastrz\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mufasa71\"><img src=\"https://avatars1.githubusercontent.com/u/626420?v=4?s=100\" width=\"100px;\" alt=\"Shukhrat Mukimov\"/><br /><sub><b>Shukhrat Mukimov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=mufasa71\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://jhonnymoreira.dev\"><img src=\"https://avatars0.githubusercontent.com/u/2177742?v=4?s=100\" width=\"100px;\" alt=\"Jhonny Moreira\"/><br /><sub><b>Jhonny Moreira</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jhonnymoreira\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/stefanprobst\"><img src=\"https://avatars0.githubusercontent.com/u/20753323?v=4?s=100\" width=\"100px;\" alt=\"stefanprobst\"/><br /><sub><b>stefanprobst</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=stefanprobst\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=stefanprobst\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/louisaspicer\"><img src=\"https://avatars1.githubusercontent.com/u/20270031?v=4?s=100\" width=\"100px;\" alt=\"Louisa Spicer\"/><br /><sub><b>Louisa Spicer</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=louisaspicer\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Alouisaspicer\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://neet.love\"><img src=\"https://avatars2.githubusercontent.com/u/19276905?v=4?s=100\" width=\"100px;\" alt=\"Ryō Igarashi\"/><br /><sub><b>Ryō Igarashi</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Aneet\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=neet\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://ryanlue.com/\"><img src=\"https://avatars2.githubusercontent.com/u/12194123?v=4?s=100\" width=\"100px;\" alt=\"Ryan Lue\"/><br /><sub><b>Ryan Lue</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=rlue\" title=\"Documentation\">📖</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/mattleonowicz\"><img src=\"https://avatars3.githubusercontent.com/u/9438872?v=4?s=100\" width=\"100px;\" alt=\"Mateusz Leonowicz\"/><br /><sub><b>Mateusz Leonowicz</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=mattleonowicz\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/atomicpages\"><img src=\"https://avatars2.githubusercontent.com/u/1824291?v=4?s=100\" width=\"100px;\" alt=\"Dennis Thompson\"/><br /><sub><b>Dennis Thompson</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=atomicpages\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://mayicodefuture.live\"><img src=\"https://avatars1.githubusercontent.com/u/32408893?v=4?s=100\" width=\"100px;\" alt=\"Maksym Boytsov\"/><br /><sub><b>Maksym Boytsov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=mayicodefuture\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://dataart.com\"><img src=\"https://avatars1.githubusercontent.com/u/5685800?v=4?s=100\" width=\"100px;\" alt=\"Sergey Skrynnikov\"/><br /><sub><b>Sergey Skrynnikov</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=IwalkAlone\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=IwalkAlone\" title=\"Tests\">⚠️</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://www.linkedin.com/in/vvoyer\"><img src=\"https://avatars0.githubusercontent.com/u/123822?v=4?s=100\" width=\"100px;\" alt=\"Vincent Voyer\"/><br /><sub><b>Vincent Voyer</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=vvo\" title=\"Documentation\">📖</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/limejoe\"><img src=\"https://avatars2.githubusercontent.com/u/7977551?v=4?s=100\" width=\"100px;\" alt=\"limejoe\"/><br /><sub><b>limejoe</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=limejoe\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Alimejoe\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/k88manish\"><img src=\"https://avatars2.githubusercontent.com/u/19614770?v=4?s=100\" width=\"100px;\" alt=\"Manish Kumar\"/><br /><sub><b>Manish Kumar</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=k88manish\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/fcrezza\"><img src=\"https://avatars2.githubusercontent.com/u/48123020?v=4?s=100\" width=\"100px;\" alt=\"Anang Fachreza\"/><br /><sub><b>Anang Fachreza</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=fcrezza\" title=\"Documentation\">📖</a> <a href=\"#example-fcrezza\" title=\"Examples\">💡</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://nickdeom.com\"><img src=\"https://avatars2.githubusercontent.com/u/56491159?v=4?s=100\" width=\"100px;\" alt=\"Nick Deom\"/><br /><sub><b>Nick Deom</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=ndeom\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Andeom\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/clementgarbay\"><img src=\"https://avatars3.githubusercontent.com/u/12433625?v=4?s=100\" width=\"100px;\" alt=\"Clément Garbay\"/><br /><sub><b>Clément Garbay</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=clementgarbay\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/KaiminHuang\"><img src=\"https://avatars.githubusercontent.com/u/5600404?v=4?s=100\" width=\"100px;\" alt=\"Kaimin Huang\"/><br /><sub><b>Kaimin Huang</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=KaiminHuang\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3AKaiminHuang\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"http://theredcircuit.com\"><img src=\"https://avatars.githubusercontent.com/u/1242456?v=4?s=100\" width=\"100px;\" alt=\"David Welling\"/><br /><sub><b>David Welling</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=davewelling\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Adavewelling\" title=\"Bug reports\">🐛</a> <a href=\"#ideas-davewelling\" title=\"Ideas, Planning, & Feedback\">🤔</a> <a href=\"#research-davewelling\" title=\"Research\">🔬</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/chandrasekhar1996\"><img src=\"https://avatars.githubusercontent.com/u/33996892?v=4?s=100\" width=\"100px;\" alt=\"chandrasekhar1996\"/><br /><sub><b>chandrasekhar1996</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Achandrasekhar1996\" title=\"Bug reports\">🐛</a> <a href=\"https://github.com/downshift-js/downshift/commits?author=chandrasekhar1996\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/drewbrend\"><img src=\"https://avatars.githubusercontent.com/u/5375799?v=4?s=100\" width=\"100px;\" alt=\"Brendan Drew\"/><br /><sub><b>Brendan Drew</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=drewbrend\" title=\"Code\">💻</a></td>\n    </tr>\n    <tr>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/jeanpan\"><img src=\"https://avatars.githubusercontent.com/u/1307026?v=4?s=100\" width=\"100px;\" alt=\"Jean Pan\"/><br /><sub><b>Jean Pan</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=jeanpan\" title=\"Code\">💻</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://tjenkinson.me\"><img src=\"https://avatars.githubusercontent.com/u/3259993?v=4?s=100\" width=\"100px;\" alt=\"Tom Jenkinson\"/><br /><sub><b>Tom Jenkinson</b></sub></a><br /><a href=\"#infra-tjenkinson\" title=\"Infrastructure (Hosting, Build-Tools, etc)\">🚇</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/aliceHendicott\"><img src=\"https://avatars.githubusercontent.com/u/40346716?v=4?s=100\" width=\"100px;\" alt=\"Alice Hendicott\"/><br /><sub><b>Alice Hendicott</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=aliceHendicott\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3AaliceHendicott\" title=\"Bug reports\">🐛</a></td>\n      <td align=\"center\" valign=\"top\" width=\"14.28%\"><a href=\"https://github.com/zmdavis\"><img src=\"https://avatars.githubusercontent.com/u/25305144?v=4?s=100\" width=\"100px;\" alt=\"Zach Davis\"/><br /><sub><b>Zach Davis</b></sub></a><br /><a href=\"https://github.com/downshift-js/downshift/commits?author=zmdavis\" title=\"Code\">💻</a> <a href=\"https://github.com/downshift-js/downshift/issues?q=author%3Azmdavis\" title=\"Bug reports\">🐛</a></td>\n    </tr>\n  </tbody>\n</table>\n\n<!-- markdownlint-restore -->\n<!-- prettier-ignore-end -->\n\n<!-- ALL-CONTRIBUTORS-LIST:END -->\n\nThis project follows the [all-contributors][all-contributors] specification.\nContributions of any kind welcome!\n\n## LICENSE\n\nMIT\n\n[npm]: https://www.npmjs.com/\n[node]: https://nodejs.org\n[build-badge]:\n  https://img.shields.io/github/actions/workflow/status/downshift-js/downshift/validate.yml?branch=master&logo=github&style=flat-square\n[build]:\n  https://github.com/downshift-js/downshift/actions?query=workflow%3Avalidate+branch%3Amaster\n[coverage-badge]:\n  https://img.shields.io/codecov/c/github/downshift-js/downshift.svg?style=flat-square\n[coverage]: https://codecov.io/github/downshift-js/downshift\n[version-badge]: https://img.shields.io/npm/v/downshift.svg?style=flat-square\n[package]: https://www.npmjs.com/package/downshift\n[downloads-badge]: https://img.shields.io/npm/dm/downshift.svg?style=flat-square\n[npmcharts]: http://npmcharts.com/compare/downshift\n[license-badge]: https://img.shields.io/npm/l/downshift.svg?style=flat-square\n[license]: https://github.com/downshift-js/downshift/blob/master/LICENSE\n[prs-badge]:\n  https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square\n[prs]: http://makeapullrequest.com\n[chat]: https://gitter.im/downshift-js/downshift\n[chat-badge]:\n  https://img.shields.io/gitter/room/downshift-js/downshift.svg?style=flat-square\n[coc-badge]:\n  https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square\n[coc]: https://github.com/downshift-js/downshift/blob/master/CODE_OF_CONDUCT.md\n[react-badge]:\n  https://img.shields.io/badge/%E2%9A%9B%EF%B8%8F-(p)react-00d8ff.svg?style=flat-square\n[react]: https://facebook.github.io/react/\n[gzip-badge]:\n  http://img.badgesize.io/https://unpkg.com/downshift/dist/downshift.umd.min.js?compression=gzip&label=gzip%20size&style=flat-square\n[size-badge]:\n  http://img.badgesize.io/https://unpkg.com/downshift/dist/downshift.umd.min.js?label=size&style=flat-square\n[unpkg-dist]: https://unpkg.com/downshift/dist/\n[module-formats-badge]:\n  https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20es-green.svg?style=flat-square\n[spectrum-badge]: https://withspectrum.github.io/badge/badge.svg\n[spectrum]: https://spectrum.chat/downshift\n[emojis]: https://github.com/kentcdodds/all-contributors#emoji-key\n[all-contributors]: https://github.com/kentcdodds/all-contributors\n[ryan]: https://github.com/ryanflorence\n[compound-components-lecture]:\n  https://courses.reacttraining.com/courses/advanced-react/lectures/3060560\n[react-autocomplete]: https://www.npmjs.com/package/react-autocomplete\n[jquery-complete]: https://jqueryui.com/autocomplete/\n[examples]:\n  https://codesandbox.io/search?refinementList%5Btags%5D%5B0%5D=downshift%3Aexample&page=1\n[yt-playlist]:\n  https://www.youtube.com/playlist?list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE\n[jared]: https://github.com/jaredly\n[controlled-components-lecture]:\n  https://courses.reacttraining.com/courses/advanced-react/lectures/3172720\n[react-training]: https://reacttraining.com/\n[advanced-react]: https://courses.reacttraining.com/courses/enrolled/200086\n[use-a-render-prop]: https://medium.com/@mjackson/use-a-render-prop-50de598f11ce\n[semver]: http://semver.org/\n[hooks-readme]: https://github.com/downshift-js/downshift/blob/master/src/hooks\n[useselect-readme]:\n  https://github.com/downshift-js/downshift/blob/master/src/hooks/useSelect\n[combobox-readme]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useCombobox\n[tag-group-readme]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useTagGroup\n[bundle-phobia-link]: https://bundlephobia.com/result?p=downshift@3.4.8\n[aria]: https://www.w3.org/TR/wai-aria-practices/\n[combobox-aria-example]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-list.html\n[select-aria-example]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-select-only.html\n[docsite]: https://downshift-js.com/\n[code-sandbox-try-it-out]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F00-get-root-props-example.js&moduleview=1\n[code-sandbox-no-get-root-props]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fdownshift%2Fordered-examples%2F01-basic-autocomplete.js&moduleview=1\n[migration-guide-v7]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md\n"
  },
  {
    "path": "babel.config.js",
    "content": "const originalPreset = require('kcd-scripts/babel')\nconst customPreset = api => {\n  api.cache(true)\n  const evaluatedPreset = originalPreset(api)\n  const plugins = [\n    require.resolve('babel-plugin-dynamic-import-node'),\n    ['no-side-effect-class-properties'],\n    ['@babel/plugin-proposal-private-property-in-object', {loose: true}], // cypress warning because loose is false in preset-env\n    ['@babel/plugin-proposal-private-methods', {loose: true}], // cypress warning because loose is false in preset-env\n    ...evaluatedPreset.plugins,\n  ]\n  return {\n    presets: evaluatedPreset.presets,\n    plugins,\n  }\n}\nmodule.exports = customPreset\n"
  },
  {
    "path": "cypress/.eslintrc",
    "content": "{\n  \"plugins\": [\"cypress\"],\n  \"globals\": {\n    \"cy\": true,\n    \"Cypress\": true,\n    \"before\": true\n  }\n}\n"
  },
  {
    "path": "cypress/e2e/combobox.cy.js",
    "content": "// the combobox happens to be in the center of the page.\n// without specifying an x and y for the body events\n// we actually wind up firing events on the combobox.\nconst bodyX = 100\nconst bodyY = 500\n\ndescribe('combobox', () => {\n  before(() => {\n    cy.visit('/combobox')\n  })\n\n  beforeEach(() => {\n    cy.findByTestId('clear-button').click()\n  })\n\n  it('can select an item', () => {\n    cy.findByTestId('combobox-input')\n      .type('ee{downarrow}{enter}')\n      .should('have.value', 'Green')\n  })\n\n  it('can arrow up to select last item', () => {\n    cy.findByTestId('combobox-input')\n      .type('{uparrow}{enter}') // open menu, last option is focused\n      .should('have.value', 'Skyblue')\n  })\n\n  it('can arrow down to select first item', () => {\n    cy.findByTestId('combobox-input')\n      .type('{downarrow}{enter}') // open menu, first option is focused\n      .should('have.value', 'Black')\n  })\n\n  it('can down arrow to select an item', () => {\n    cy.findByTestId('combobox-input')\n      .type('{downarrow}{downarrow}{enter}') // open and select second item\n      .should('have.value', 'Red')\n  })\n\n  it('can use home arrow to select first item', () => {\n    cy.findByTestId('combobox-input')\n      .type('{downarrow}{downarrow}{home}{enter}') // open to first, go down to second, return to first by home.\n      .should('have.value', 'Black')\n  })\n\n  it('can use end arrow to select last item', () => {\n    cy.findByTestId('combobox-input')\n      .type('{downarrow}{end}{enter}') // open to first, go to last by end.\n      .should('have.value', 'Skyblue')\n  })\n\n  it('resets the item on blur', () => {\n    cy.findByTestId('combobox-input')\n      .type('{downarrow}{enter}') // open and select first item\n      .should('have.value', 'Black')\n      .get('body')\n      .click(bodyX, bodyY, {force: true})\n      .findByTestId('combobox-input')\n      .should('have.value', 'Black')\n  })\n\n  it('can use the mouse to click an item', () => {\n    cy.findByTestId('combobox-input').type('red')\n    cy.findByTestId('downshift-item-0').click()\n    cy.findByTestId('combobox-input').should('have.value', 'Red')\n  })\n\n  it('does not reset the input when mouseup outside while the input is focused', () => {\n    cy.findByTestId('combobox-input').type('red')\n    cy.findByTestId('downshift-item-0').click()\n    cy.findByTestId('combobox-input')\n      .should('have.value', 'Red')\n      .type('{backspace}{backspace}')\n      .should('have.value', 'R')\n      .click()\n      .get('body')\n      .trigger('mouseup', bodyX, bodyY, {force: true})\n      .findByTestId('combobox-input')\n      .should('have.value', 'R')\n      .blur()\n      .get('body')\n      .trigger('click', bodyX, bodyY, {force: true})\n      .findByTestId('combobox-input')\n      .should('have.value', 'Red')\n  })\n\n  it('resets when bluring the input', () => {\n    cy.findByTestId('combobox-input')\n      .type('re')\n      .blur()\n      // https://github.com/kentcdodds/cypress-testing-library/issues/13\n      // eslint-disable-next-line testing-library/await-async-utils\n      .wait(1)\n      .findByTestId('downshift-item-0', {timeout: 10})\n      .should('not.exist')\n  })\n\n  it('does not reset when tabbing from input to the toggle button', () => {\n    cy.findByTestId('combobox-input').type('pu')\n    cy.findByTestId('combobox-toggle-button').focus()\n    cy.findByTestId('downshift-item-0').click()\n    cy.findByTestId('combobox-input').should('have.value', 'Purple')\n  })\n\n  it('does not reset when tabbing from the toggle button to the input', () => {\n    cy.findByTestId('combobox-toggle-button').click()\n    cy.findByTestId('combobox-input').focus()\n    cy.findByTestId('downshift-item-0').click()\n    cy.findByTestId('combobox-input').should('have.value', 'Black')\n  })\n\n  it('resets when tapping outside on a touch screen', () => {\n    cy.findByTestId('combobox-input')\n      .type('re')\n      .get('body')\n      .trigger('touchstart', bodyX, bodyY, {force: true})\n      .trigger('touchend', bodyX, bodyY, {force: true})\n    cy.findByTestId('downshift-item-0', {timeout: 100}).should('not.exist')\n  })\n\n  it('does not reset when swiping outside to scroll a touch screen', () => {\n    cy.findByTestId('combobox-input')\n      .type('re')\n      .get('body')\n      .trigger('touchstart', bodyX, bodyY, {force: true})\n      .trigger('touchmove', bodyX, bodyY + 20, {force: true})\n      .trigger('touchend', bodyX, bodyY + 20, {force: true})\n    cy.findByTestId('downshift-item-0', {timeout: 10}).should('be.visible')\n  })\n})\n"
  },
  {
    "path": "cypress/e2e/useCombobox.cy.js",
    "content": "describe('useCombobox', () => {\n  before(() => {\n    cy.visit('/useCombobox')\n  })\n\n  it('should keep focus on the input when selecting by click', () => {\n    cy.findByTestId('combobox-toggle-button').click()\n    cy.findByTestId('downshift-item-0').click()\n    cy.findByTestId('combobox-input').should('have.focus')\n  })\n})\n"
  },
  {
    "path": "cypress/e2e/useMultipleCombobox.cy.js",
    "content": "describe('useMultipleCombobox', () => {\n  before(() => {\n    cy.visit('/useMultipleCombobox')\n  })\n\n  it('can select multiple items', () => {\n    cy.findByRole('button', {name: 'toggle menu'}).click()\n    cy.findByRole('option', {name: 'Green'}).click()\n    cy.findByRole('option', {name: 'Gray'}).click()\n    cy.findByRole('button', {name: 'toggle menu'}).click()\n    cy.findByText('Black').should('be.visible')\n    cy.findByText('Red').should('be.visible')\n    cy.findByText('Green').should('be.visible')\n    cy.findByText('Gray').should('be.visible')\n  })\n})\n"
  },
  {
    "path": "cypress/e2e/useMultipleSelect.cy.js",
    "content": "describe('useMultipleSelect', () => {\n  before(() => {\n    cy.visit('/useMultipleSelect')\n  })\n\n  it('can select multiple options', () => {\n    cy.findByRole('combobox').click()\n    cy.findByRole('option', {name: 'Green'}).click()\n    cy.findByRole('option', {name: 'Blue'}).click()\n    cy.findByRole('combobox').click()\n    cy.findByText('Black').should('be.visible')\n    cy.findByText('Red').should('be.visible')\n    cy.findByText('Green').should('be.visible')\n    cy.findByText('Blue').should('be.visible')\n  })\n})\n"
  },
  {
    "path": "cypress/e2e/useSelect.cy.js",
    "content": "describe('useSelect', () => {\n  before(() => {\n    cy.visit('/useSelect')\n  })\n\n  it('can open and close a menu', () => {\n    cy.findByRole('combobox')\n      .click()\n    cy.findAllByRole('option')\n      .should('have.length.above', 0)\n    cy.findByRole('combobox')\n      .click()\n    cy.findAllByRole('option')\n      .should('have.length', 0)\n    cy.findByRole('combobox')\n      .click()\n    cy.findAllByRole('option')\n      .should('have.length.above', 0)\n  })\n})\n"
  },
  {
    "path": "cypress/e2e/useTagGroup.cy.js",
    "content": "describe('useTagGroup', () => {\n  const colors = ['Black', 'Red', 'Green', 'Blue', 'Orange']\n\n  beforeEach(() => {\n    cy.visit('/useTagGroup')\n\n    // Ensure the listbox exists\n    cy.findByRole('listbox', {name: /colors example/i}).should('exist')\n\n    // Ensure it has 5 color tags\n    cy.findAllByRole('option').should('have.length', 5)\n  })\n\n  it('clicks a tag and navigates with circular arrow keys', () => {\n    // Click first tag (\"Black\")\n    cy.findByRole('option', {name: /Black/i}).click().should('have.focus')\n\n    // Arrow Right navigation through all tags\n    for (let index = 0; index < colors.length; index++) {\n      const nextIndex = (index + 1) % colors.length\n      cy.focused().trigger('keydown', {key: 'ArrowRight'})\n      cy.findByRole('option', {name: colors[nextIndex]}).should('have.focus')\n    }\n\n    // Arrow Left navigation through all tags (circular)\n    for (let index = colors.length - 1; index >= 0; index--) {\n      const prevIndex = (index + colors.length) % colors.length\n      cy.focused().trigger('keydown', {key: 'ArrowLeft'})\n      cy.findByRole('option', {name: colors[prevIndex]}).should('have.focus')\n    }\n\n    // Circular on the left.\n    cy.focused().trigger('keydown', {key: 'ArrowLeft'})\n    cy.findByRole('option', {name: colors[colors.length - 1]}).should(\n      'have.focus',\n    )\n  })\n\n  it('deletes a tag using Delete and Backspace', () => {\n    // Focus \"Red\"\n    cy.findByRole('option', {name: /Red/i}).click()\n\n    // Delete key\n    cy.focused().trigger('keydown', {key: 'Delete'})\n    cy.findAllByRole('option').should('have.length', 4)\n\n    // Next tag should be \"Green\"\n    cy.focused().should('contain.text', 'Green')\n\n    // Backspace key removes \"Green\"\n    cy.focused().trigger('keydown', {key: 'Backspace'})\n    cy.findAllByRole('option').should('have.length', 3)\n\n    // Focus should now be on \"Blue\"\n    cy.focused().should('contain.text', 'Blue')\n  })\n\n  it('removes a tag via remove button', () => {\n    // Remove \"Blue\" via its remove button\n    cy.findByRole('option', {name: /Blue/i}).within(() => {\n      cy.findByRole('button', {name: /remove/i}).click()\n    })\n\n    // Verify 4 tags remain\n    cy.findAllByRole('option').should('have.length', 4)\n\n    // Orange tag should have focus.\n    cy.findByRole('option', {name: /Orange/i}).should('have.focus')\n  })\n\n  it('adds a tag from the list', () => {\n    // Focus \"Red\"\n    cy.findByRole('option', {name: /Red/i}).click()\n\n    // Clicks the Lime option from the add tags list.\n    cy.findByRole('button', {name: /Lime/i}).click()\n\n    // Verify 6 tags are visible\n    cy.findAllByRole('option').should('have.length', 6)\n\n    cy.findByRole('option', {name: /Lime/i}).should('be.visible')\n    // Including the new option\n  })\n})\n"
  },
  {
    "path": "cypress/fixtures/example.json",
    "content": "{}\n"
  },
  {
    "path": "cypress/plugins/index.js",
    "content": "module.exports = (_on, _config) => {\n  // `on` is used to hook into various events Cypress emits\n  // `config` is the resolved Cypress config\n}\n"
  },
  {
    "path": "cypress/support/e2e.js",
    "content": "// eslint-disable-next-line\nimport '@testing-library/cypress/add-commands'\n"
  },
  {
    "path": "cypress.config.js",
    "content": "const webpackPreprocessor = require('@cypress/webpack-preprocessor')\nconst {defineConfig} = require('cypress')\n\nmodule.exports = defineConfig({\n  e2e: {\n    baseUrl: 'http://localhost:6006',\n    video: false,\n    testIsolation: false,\n    setupNodeEvents(on) {\n      on(\n        'file:preprocessor',\n        webpackPreprocessor({\n          webpackOptions: {\n            ...webpackPreprocessor.defaultOptions.webpackOptions,\n            target: 'web',\n          },\n        }),\n      )\n    },\n  },\n})\n"
  },
  {
    "path": "docusaurus/pages/combobox.js",
    "content": "import * as React from 'react'\n\nimport Downshift from '../../src'\nimport {colors, containerStyles, menuStyles} from '../utils'\n\nexport default function ComboBox() {\n  return (\n    <Downshift>\n      {({\n        getInputProps,\n        getItemProps,\n        getMenuProps,\n        getLabelProps,\n        getToggleButtonProps,\n        highlightedIndex,\n        inputValue,\n        isOpen,\n        selectedItem,\n        getRootProps,\n        clearSelection,\n      }) => (\n        <div style={containerStyles}>\n          <label\n            style={{\n              fontWeight: 'bolder',\n              color: selectedItem ? selectedItem : 'black',\n            }}\n            {...getLabelProps()}\n          >\n            Choose an element:\n          </label>\n          <div {...getRootProps({}, {suppressRefError: true})}>\n            <input\n              style={{padding: '4px'}}\n              {...getInputProps()}\n              data-testid=\"combobox-input\"\n            />\n            <button\n              style={{padding: '4px 8px'}}\n              aria-label=\"toggle menu\"\n              data-testid=\"combobox-toggle-button\"\n              {...getToggleButtonProps()}\n            >\n              {isOpen ? <>&#8593;</> : <>&#8595;</>}\n            </button>\n            <button\n              style={{padding: '4px 8px'}}\n              aria-label=\"toggle menu\"\n              data-testid=\"clear-button\"\n              onClick={clearSelection}\n            >\n              &#10007;\n            </button>\n          </div>\n          <ul {...getMenuProps()} style={menuStyles}>\n            {isOpen\n              ? (inputValue\n                  ? colors.filter(i =>\n                      i.toLowerCase().includes(inputValue.toLowerCase()),\n                    )\n                  : colors\n                ).map((item, index) => (\n                  <li\n                    style={{\n                      padding: '4px',\n                      backgroundColor:\n                        highlightedIndex === index ? '#bde4ff' : null,\n                    }}\n                    key={`${item}${index}`}\n                    {...getItemProps({\n                      item,\n                      index,\n                      'data-testid': `downshift-item-${index}`,\n                    })}\n                  >\n                    {item}\n                  </li>\n                ))\n              : null}\n          </ul>\n        </div>\n      )}\n    </Downshift>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/index.js",
    "content": "import * as React from 'react'\n\nexport default function Docs() {\n  return (\n    <div style={{width: '50%', margin: '200px auto'}}>\n      Downshift Docs for E2E Testing\n      <ul>\n        <li>\n          <a href=\"./useCombobox\">useCombobox</a>\n        </li>\n        <li>\n          <a href=\"./useSelect\">useSelect</a>\n        </li>\n        <li>\n          <a href=\"./useMultipleCombobox\">useMultipleCombobox</a>\n        </li>\n        <li>\n          <a href=\"./useMultipleSelect\">useMultipleSelect</a>\n        </li>\n        <li>\n          <a href=\"./combobox\">Downshift</a>\n        </li>\n        <li>\n          <a href=\"./useTagGroup\">useTagGroup</a>\n        </li>\n        <li>\n          <a href=\"./useTagGroupCombobox\">useTagGroupCombobox</a>\n        </li>\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useCombobox.js",
    "content": "import * as React from 'react'\n\nimport {useCombobox} from '../../src'\nimport {colors, containerStyles, menuStyles} from '../utils'\n\nexport default function DropdownCombobox() {\n  const [inputItems, setInputItems] = React.useState(colors)\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n    selectedItem,\n    selectItem,\n  } = useCombobox({\n    items: inputItems,\n    onInputValueChange: ({inputValue}) => {\n      setInputItems(\n        colors.filter(item =>\n          item.toLowerCase().startsWith(inputValue.toLowerCase()),\n        ),\n      )\n    },\n  })\n  return (\n    <div style={containerStyles}>\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div>\n        <input\n          style={{padding: '4px'}}\n          {...getInputProps()}\n          data-testid=\"combobox-input\"\n        />\n        <button\n          style={{padding: '4px 8px'}}\n          aria-label=\"toggle menu\"\n          data-testid=\"combobox-toggle-button\"\n          {...getToggleButtonProps()}\n        >\n          {isOpen ? <>&#8593;</> : <>&#8595;</>}\n        </button>\n        <button\n          style={{padding: '4px 8px'}}\n          aria-label=\"toggle menu\"\n          data-testid=\"clear-button\"\n          onClick={() => selectItem(null)}\n        >\n          &#10007;\n        </button>\n      </div>\n      <ul {...getMenuProps()} style={menuStyles}>\n        {isOpen ?\n          inputItems.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n                'data-testid': `downshift-item-${index}`,\n              })}\n            >\n              {item}\n            </li>\n          )) : null}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useMultipleCombobox.js",
    "content": "import * as React from 'react'\n\nimport {useCombobox, useMultipleSelection} from '../../src'\nimport {\n  colors,\n  containerStyles,\n  menuStyles,\n  tagGroupSyles,\n  tagStyles,\n} from '../utils'\n\nconst initialSelectedItems = [colors[0], colors[1]]\n\nfunction getFilteredItems(selectedItems, inputValue) {\n  const lowerCasedInputValue = inputValue.toLowerCase()\n\n  return colors.filter(\n    colour =>\n      !selectedItems.includes(colour) &&\n      colour.toLowerCase().startsWith(lowerCasedInputValue),\n  )\n}\n\nexport default function DropdownMultipleCombobox() {\n  const [inputValue, setInputValue] = React.useState('')\n  const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems)\n  const items = React.useMemo(\n    () => getFilteredItems(selectedItems, inputValue),\n    [selectedItems, inputValue],\n  )\n\n  const {getSelectedItemProps, getDropdownProps, removeSelectedItem} =\n    useMultipleSelection({\n      selectedItems,\n      onStateChange({selectedItems: newSelectedItems, type}) {\n        switch (type) {\n          case useMultipleSelection.stateChangeTypes\n            .SelectedItemKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:\n          case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:\n            setSelectedItems(newSelectedItems)\n            break\n          default:\n            break\n        }\n      },\n    })\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n    selectedItem,\n    clearSelection,\n  } = useCombobox({\n    items,\n    inputValue,\n    selectedItem: null,\n    stateReducer(state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n        case useCombobox.stateChangeTypes.InputBlur:\n          return {\n            ...changes,\n            ...(changes.selectedItem && {isOpen: true, highlightedIndex: 0}),\n          }\n        default:\n          return changes\n      }\n    },\n    onStateChange({\n      inputValue: newInputValue,\n      type,\n      selectedItem: newSelectedItem,\n    }) {\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n          setSelectedItems([...selectedItems, newSelectedItem])\n\n          break\n        case useCombobox.stateChangeTypes.InputChange:\n          setInputValue(newInputValue)\n          break\n        default:\n          break\n      }\n    },\n  })\n  return (\n    <div style={containerStyles}>\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div style={tagGroupSyles}>\n        {selectedItems.map(function renderSelectedItem(\n          selectedItemForRender,\n          index,\n        ) {\n          return (\n            <span\n              style={tagStyles}\n              key={`selected-item-${index}`}\n              {...getSelectedItemProps({\n                selectedItem: selectedItemForRender,\n                index,\n              })}\n            >\n              {selectedItemForRender}\n              {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n              <span\n                style={{padding: '4px', cursor: 'pointer'}}\n                onClick={e => {\n                  e.stopPropagation()\n                  removeSelectedItem(selectedItemForRender)\n                }}\n              >\n                &#10005;\n              </span>\n            </span>\n          )\n        })}\n        <div>\n          <input\n            style={{padding: '4px'}}\n            {...getInputProps(getDropdownProps({preventKeyAction: isOpen}))}\n            data-testid=\"combobox-input\"\n          />\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"toggle menu\"\n            data-testid=\"combobox-toggle-button\"\n            {...getToggleButtonProps()}\n          >\n            {isOpen ? <>&#8593;</> : <>&#8595;</>}\n          </button>\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"clear selection\"\n            data-testid=\"clear-button\"\n            onClick={clearSelection}\n          >\n            &#10007;\n          </button>\n        </div>\n      </div>\n      <ul {...getMenuProps()} style={menuStyles}>\n        {isOpen\n          ? items.map((item, index) => (\n              <li\n                style={{\n                  padding: '4px',\n                  backgroundColor:\n                    highlightedIndex === index ? '#bde4ff' : null,\n                }}\n                key={`${item}${index}`}\n                {...getItemProps({\n                  item,\n                  index,\n                  'data-testid': `downshift-item-${index}`,\n                })}\n              >\n                {item}\n              </li>\n            ))\n          : null}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useMultipleSelect.js",
    "content": "import * as React from 'react'\n\nimport {useSelect, useMultipleSelection} from '../../src'\nimport {\n  colors,\n  containerStyles,\n  menuStyles,\n  tagGroupSyles,\n  tagStyles,\n} from '../utils'\n\nconst initialSelectedItems = [colors[0], colors[1]]\n\nfunction getFilteredItems(selectedItems) {\n  return colors.filter(colour => !selectedItems.includes(colour))\n}\n\nexport default function DropdownMultipleSelect() {\n  const {\n    getSelectedItemProps,\n    getDropdownProps,\n    addSelectedItem,\n    removeSelectedItem,\n    selectedItems,\n  } = useMultipleSelection({initialSelectedItems})\n  const items = getFilteredItems(selectedItems)\n  const {\n    isOpen,\n    selectedItem,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    highlightedIndex,\n    getItemProps,\n  } = useSelect({\n    selectedItem: null,\n    defaultHighlightedIndex: 0, // after selection, highlight the first item.\n    items,\n    stateReducer: (state, actionAndChanges) => {\n      const {changes, type} = actionAndChanges\n      switch (type) {\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n        case useSelect.stateChangeTypes.ItemClick:\n          return {\n            ...changes,\n            isOpen: true, // keep the menu open after selection.\n          }\n        default:\n          return changes\n      }\n    },\n    onStateChange: ({type, selectedItem: newSelectedItem}) => {\n      switch (type) {\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n        case useSelect.stateChangeTypes.ItemClick:\n          if (newSelectedItem) {\n            addSelectedItem(newSelectedItem)\n          }\n          break\n        default:\n          break\n      }\n    },\n  })\n\n  return (\n    <div style={containerStyles}>\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div style={tagGroupSyles}>\n        {selectedItems.map(function renderSelectedItem(\n          selectedItemForRender,\n          index,\n        ) {\n          return (\n            <span\n              style={tagStyles}\n              key={`selected-item-${index}`}\n              {...getSelectedItemProps({\n                selectedItem: selectedItemForRender,\n                index,\n              })}\n            >\n              {selectedItemForRender}\n              {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n              <span\n                style={{padding: '4px', cursor: 'pointer'}}\n                onClick={e => {\n                  e.stopPropagation()\n                  removeSelectedItem(selectedItemForRender)\n                }}\n              >\n                &#10005;\n              </span>\n            </span>\n          )\n        })}\n        <div\n          style={{\n            padding: '4px',\n            textAlign: 'center',\n            border: '1px solid black',\n            backgroundColor: 'lightgray',\n            cursor: 'pointer',\n          }}\n          type=\"button\"\n          {...getToggleButtonProps(\n            getDropdownProps({preventKeyAction: isOpen}),\n          )}\n        >\n          Pick some colors {isOpen ? <>&#8593;</> : <>&#8595;</>}\n        </div>\n      </div>\n      <ul {...getMenuProps()} style={menuStyles}>\n        {isOpen\n          ? items.map((item, index) => (\n              <li\n                style={{\n                  padding: '4px',\n                  backgroundColor:\n                    highlightedIndex === index ? '#bde4ff' : null,\n                }}\n                key={`${item}${index}`}\n                {...getItemProps({\n                  item,\n                  index,\n                  'data-testid': `downshift-item-${index}`,\n                })}\n              >\n                {item}\n              </li>\n            ))\n          : null}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useSelect.js",
    "content": "import * as React from 'react'\n\nimport {useSelect} from '../../src'\nimport {colors, containerStyles, menuStyles} from '../utils'\n\nexport default function DropdownSelect() {\n  const {\n    isOpen,\n    selectedItem,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    highlightedIndex,\n    getItemProps,\n  } = useSelect({items: colors})\n\n  return (\n    <div style={containerStyles}>\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div\n        style={{\n          padding: '4px',\n          textAlign: 'center',\n          border: '1px solid black',\n          backgroundColor: 'lightgray',\n          cursor: 'pointer',\n        }}\n        {...getToggleButtonProps()}\n      >\n        {selectedItem ?? 'Elements'}\n        {isOpen ? <>&#8593;</> : <>&#8595;</>}\n      </div>\n      <ul {...getMenuProps()} style={menuStyles}>\n        {isOpen ?\n          colors.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n                'data-testid': `downshift-item-${index}`,\n              })}\n            >\n              {item}\n            </li>\n          )) : null}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useTagGroup.css",
    "content": ".tag-group {\n  display: inline-flex;\n  gap: 8px;\n  align-items: center;\n  flex-wrap: wrap;\n  padding: 6px;\n}\n\n.tag {\n  border: solid 1px darkgreen;\n  background-color: green;\n  padding: 0 6px;\n  margin: 0 2px;\n  border-radius: 10px;\n  cursor: default;\n}\n\n.tag:hover {\n  opacity: 0.5;\n}\n\n.tag:focus {\n  background-color: red;\n  border-color: darkred;\n}\n\n.tag-remove-button {\n  padding: 4px;\n  cursor: pointer;\n  border: none;\n  background-color: transparent;\n}\n\n.item-to-add {\n  cursor: pointer;\n}\n\n.selected-tag {\n  font-style: italic;\n}\n"
  },
  {
    "path": "docusaurus/pages/useTagGroup.tsx",
    "content": "import * as React from 'react'\n\nimport {useTagGroup} from '../../src'\nimport {colors} from '../utils'\n\nimport './useTagGroup.css'\n\nexport default function TagGroup() {\n  const initialItems = colors.slice(0, 5)\n  const {\n    addItem,\n    getTagProps,\n    getTagRemoveProps,\n    getTagGroupProps,\n    items,\n    activeIndex,\n  } = useTagGroup({initialItems})\n  const itemsToAdd = colors.filter(color => !items.includes(color))\n\n  return (\n    <div>\n      <div\n        {...getTagGroupProps({'aria-label': 'colors example'})}\n        className=\"tag-group\"\n      >\n        {items.map((color, index) => (\n          <span\n            className={`${index === activeIndex ? 'selected-tag' : ''} tag`}\n            key={color}\n            {...getTagProps({index, 'aria-label': color})}\n          >\n            {color}\n            <button\n              className=\"tag-remove-button\"\n              type=\"button\"\n              {...getTagRemoveProps({index, 'aria-label': 'remove'})}\n            >\n              &#10005;\n            </button>\n          </span>\n        ))}\n      </div>\n      <div>Add more items:</div>\n      <ul>\n        {itemsToAdd.map(item => (\n          <li key={item}>\n            <button\n              className=\"item-to-add\"\n              tabIndex={0}\n              onClick={() => {\n                addItem(item)\n              }}\n              onKeyDown={({key}) => {\n                key === 'Enter' && addItem(item)\n              }}\n            >\n              {item}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/pages/useTagGroupCombobox.css",
    "content": ".wrapper {\n  width: 18rem;\n  display: flex;\n  flex-direction: column;\n  gap: 0.25rem;\n}\n\n.wrapper label {\n  width: fit-content;\n}\n\n.input-wrapper {\n  display: flex;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);\n  background-color: white;\n  gap: 0.125rem;\n}\n\n.text-input {\n  width: 100%;\n  padding: 0.375rem;\n}\n\n.toggle-button {\n  padding-left: 0.5rem;\n  padding-right: 0.5rem;\n}\n\n.menu {\n  position: absolute;\n  width: 18rem;\n  background-color: white;\n  margin-top: 0.25rem;\n  box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);\n  max-height: 20rem;\n  overflow-y: scroll;\n  padding: 0;\n  z-index: 10;\n}\n\n.menu.hidden {\n  display: none;\n}\n\n.menu-item {\n  padding: 0.5rem 0.75rem;\n  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);\n  display: flex;\n  flex-direction: column;\n}\n\n.menu-item.highlighted {\n  background-color: #93c5fd;\n}\n"
  },
  {
    "path": "docusaurus/pages/useTagGroupCombobox.tsx",
    "content": "import * as React from 'react'\n\nimport {UseComboboxInterface} from '../../typings'\nimport {useTagGroup, useCombobox as useComboboxUntyped} from '../../src'\nimport {colors} from '../utils'\n\nimport './useTagGroupCombobox.css'\n\nexport default function TagGroup() {\n  const initialItems = colors.slice(0, 5)\n  const [inputValue, setInputValue] = React.useState('')\n  const {\n    addItem,\n    getTagProps,\n    getTagRemoveProps,\n    getTagGroupProps,\n    items,\n    activeIndex,\n  } = useTagGroup({initialItems})\n\n  const itemsToAdd = colors.filter(\n    color =>\n      !items.includes(color) &&\n      (!inputValue || color.toLowerCase().includes(inputValue.toLowerCase())),\n  )\n  const useCombobox = useComboboxUntyped as UseComboboxInterface\n\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n  } = useCombobox({\n    items: itemsToAdd,\n    inputValue,\n    onInputValueChange: changes => {\n      setInputValue(changes.inputValue)\n    },\n    onSelectedItemChange(changes) {\n      if (changes.selectedItem) {\n        addItem(changes.selectedItem)\n      }\n    },\n    selectedItem: null,\n    stateReducer(_state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n\n      if (\n        changes.selectedItem &&\n        type !== useCombobox.stateChangeTypes.InputBlur\n      ) {\n        return {...changes, inputValue: '', highlightedIndex: 0, isOpen: true}\n      }\n\n      return changes\n    },\n  })\n\n  return (\n    <div>\n      <div\n        {...getTagGroupProps({'aria-label': 'colors example'})}\n        className=\"tag-group\"\n      >\n        {items.map((color, index) => (\n          <span\n            className={`${index === activeIndex ? 'selected-tag' : ''} tag`}\n            key={color}\n            {...getTagProps({index, 'aria-label': color})}\n          >\n            {color}\n            <button\n              className=\"tag-remove-button\"\n              type=\"button\"\n              {...getTagRemoveProps({index, 'aria-label': 'remove'})}\n            >\n              &#10005;\n            </button>\n          </span>\n        ))}\n      </div>\n      <div className=\"wrapper\">\n        <label {...getLabelProps()}>Choose your favorite book:</label>\n        <div className=\"input-wrapper\">\n          <input\n            placeholder=\"Best book ever\"\n            className=\"text-input\"\n            {...getInputProps()}\n          />\n          <button\n            aria-label=\"toggle menu\"\n            className=\"toggle-button\"\n            type=\"button\"\n            {...getToggleButtonProps()}\n          >\n            {isOpen ? <>&#8593;</> : <>&#8595;</>}\n          </button>\n        </div>\n      </div>\n      <ul className=\"menu\" {...getMenuProps()}>\n        {isOpen\n          ? itemsToAdd.map((item, index) => (\n              <li\n                className={`menu-item${index === highlightedIndex ? ' highlighted' : ''}`}\n                key={item}\n                {...getItemProps({item, index})}\n              >\n                <span>{item}</span>\n              </li>\n            ))\n          : null}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "docusaurus/plugins/webpack5polyfills.js",
    "content": "const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')\n\n// eslint-disable-next-line\nmodule.exports = function () {\n  return {\n    name: 'custom-docusaurus-webpack-config-plugin',\n    configureWebpack() {\n      return {\n        resolve: {\n          fallback: {\n            fs: false,\n            module: false,\n          },\n        },\n        plugins: [new NodePolyfillPlugin()],\n      }\n    },\n  }\n}\n"
  },
  {
    "path": "docusaurus/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": true,\n  },\n  \"include\": [\"./**/*.tsx*\", \"../typings/**/*.d.ts\"]\n}"
  },
  {
    "path": "docusaurus/utils.ts",
    "content": "import {type CSSProperties} from 'react'\n\nexport const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nexport const menuStyles: CSSProperties = {\n  listStyle: 'none',\n  width: '100%',\n  padding: '0',\n  margin: '4px 0 0 0',\n  maxHeight: 120,\n  overflowY: 'scroll',\n}\n\nexport const containerStyles: CSSProperties = {\n  display: 'flex',\n  flexDirection: 'column',\n  width: 'fit-content',\n  justifyContent: 'center',\n  marginTop: 100,\n  alignSelf: 'center',\n}\n\nexport const tagGroupSyles: CSSProperties = {\n  display: 'inline-flex',\n  gap: '8px',\n  alignItems: 'center',\n  flexWrap: 'wrap',\n  padding: '6px',\n}\n\nexport const tagStyles: CSSProperties = {\n  backgroundColor: 'green',\n  padding: '0 6px',\n  margin: '0 2px',\n  borderRadius: '10px',\n  cursor: 'auto',\n}\n\nexport const removeTagStyles: CSSProperties = {\n  padding: '4px',\n  cursor: 'pointer',\n}\n"
  },
  {
    "path": "docusaurus.config.js",
    "content": "// @ts-check\n// Note: type annotations allow type checking and IDEs autocompletion\n\n/** @type {import('@docusaurus/types').Config} */\nconst config = {\n  title: 'Downshift',\n  url: 'https://downshift-js.com',\n  baseUrl: '/',\n  onBrokenLinks: 'throw',\n  onBrokenMarkdownLinks: 'warn',\n\n  // GitHub pages deployment config.\n  // If you aren't using GitHub pages, you don't need these.\n  organizationName: 'downshift-js', // Usually your GitHub org/user name.\n  projectName: 'downshift', // Usually your repo name.\n\n  // Even if you don't use internalization, you can use this field to set useful\n  // metadata like html lang. For example, if your site is Chinese, you may want\n  // to replace \"en\" with \"zh-Hans\".\n  i18n: {\n    defaultLocale: 'en',\n    locales: ['en'],\n  },\n\n  presets: [\n    [\n      'classic',\n      /** @type {import('@docusaurus/preset-classic').Options} */\n      ({\n        docs: false,\n        blog: false,\n        pages: {\n          path: 'docusaurus/pages',\n          include: ['**/*.{js,jsx,tsx}'],\n        },\n      }),\n    ],\n  ],\n  plugins: [\n    // @ts-ignore\n    () => ({\n      name: 'configure-webpack-target',\n      configureWebpack(webpackConfig, isServer) {\n        webpackConfig.target = isServer ? 'node' : 'web'\n      },\n    }),\n    require.resolve('./docusaurus/plugins/webpack5polyfills.js'),\n  ],\n}\n\nmodule.exports = config\n"
  },
  {
    "path": "flow-typed/npm/downshift_v2.x.x.js.flow",
    "content": "/**\n * Flowtype definitions for index\n * Generated by Flowgen from a Typescript Definition\n * Flowgen v1.2.0\n * Author: [Joar Wilk](http://twitter.com/joarwilk)\n * Repo: http://github.com/joarwilk/flowgen\n */\n\nimport * as React from 'react'\n\ndeclare module downshift {\n  declare type StateChangeTypes = {\n    unknown: '__autocomplete_unknown__',\n    mouseUp: '__autocomplete_mouseup__',\n    itemMouseEnter: '__autocomplete_item_mouseenter__',\n    keyDownArrowUp: '__autocomplete_keydown_arrow_up__',\n    keyDownArrowDown: '__autocomplete_keydown_arrow_down__',\n    keyDownEscape: '__autocomplete_keydown_escape__',\n    keyDownEnter: '__autocomplete_keydown_enter__',\n    clickItem: '__autocomplete_click_item__',\n    blurInput: '__autocomplete_blur_input__',\n    changeInput: '__autocomplete_change_input__',\n    keyDownSpaceButton: '__autocomplete_keydown_space_button__',\n    clickButton: '__autocomplete_click_button__',\n    blurButton: '__autocomplete_blur_button__',\n    controlledPropUpdatedSelectedItem: '__autocomplete_controlled_prop_updated_selected_item__',\n  }\n  declare type StateChangeValues =\n    | '__autocomplete_unknown__'\n    | '__autocomplete_mouseup__'\n    | '__autocomplete_item_mouseenter__'\n    | '__autocomplete_keydown_arrow_up__'\n    | '__autocomplete_keydown_arrow_down__'\n    | '__autocomplete_keydown_escape__'\n    | '__autocomplete_keydown_enter__'\n    | '__autocomplete_click_item__'\n    | '__autocomplete_blur_input__'\n    | '__autocomplete_change_input__'\n    | '__autocomplete_keydown_space_button__'\n    | '__autocomplete_click_button__'\n    | '__autocomplete_blur_button__'\n    | '__autocomplete_controlled_prop_updated_selected_item__'\n  declare type Callback = () => void\n  declare export interface DownshiftState<Item> {\n    highlightedIndex: number | null;\n    inputValue: string | null;\n    isOpen: boolean;\n    selectedItem: Item | null;\n  }\n  declare export interface DownshiftProps<Item> {\n    defaultSelectedItem?: Item;\n    defaultHighlightedIndex?: number | null;\n    defaultInputValue?: string;\n    defaultIsOpen?: boolean;\n    itemToString?: (item: Item) => string;\n    selectedItemChanged?: (prevItem: Item, item: Item) => boolean;\n    getA11yStatusMessage?: (options: A11yStatusMessageOptions<Item>) => string;\n    onChange?: (\n      selectedItem: Item,\n      stateAndHelpers: ControllerStateAndHelpers<Item>,\n    ) => void;\n    onSelect?: (\n      selectedItem: Item,\n      stateAndHelpers: ControllerStateAndHelpers<Item>,\n    ) => void;\n    onStateChange?: (\n      options: StateChangeOptions<Item>,\n      stateAndHelpers: ControllerStateAndHelpers<Item>,\n    ) => void;\n    onInputValueChange?: (\n      inputValue: string,\n      stateAndHelpers: ControllerStateAndHelpers<Item>,\n    ) => void;\n    stateReducer?: (\n      state: DownshiftState<Item>,\n      changes: StateChangeOptions<Item>,\n    ) => StateChangeOptions<Item>;\n    itemCount?: number;\n    highlightedIndex?: number;\n    inputValue?: string;\n    isOpen?: boolean;\n    selectedItem?: Item;\n    children: ChildrenFunction<Item>;\n    id?: string;\n    environment?: Environment;\n    onOuterClick?: () => void;\n    onUserAction?: (\n      options: StateChangeOptions<Item>,\n      stateAndHelpers: ControllerStateAndHelpers<Item>,\n    ) => void;\n  }\n  declare export interface Environment {\n    addEventListener: typeof window.addEventListener;\n    removeEventListener: typeof window.removeEventListener;\n    document: Document;\n  }\n  declare export interface A11yStatusMessageOptions<Item> {\n    highlightedIndex: number | null;\n    inputValue: string;\n    isOpen: boolean;\n    itemToString: (item: Item) => string;\n    previousResultCount: number;\n    resultCount: number;\n    selectedItem: Item;\n  }\n  declare export interface StateChangeOptions<Item> {\n    type: StateChangeValues;\n    highlightedIndex: number;\n    inputValue: string;\n    isOpen: boolean;\n    selectedItem: Item;\n  }\n  declare export type StateChangeFunction<Item> = (\n    state: DownshiftState<Item>,\n  ) => StateChangeOptions<Item>\n\n  declare export type GetRootPropsReturn = {\n    role: 'combobox';\n    'aria-expanded': boolean;\n    'aria-haspopup': 'listbox';\n    'aria-owns': string | null;\n    'aria-labelledby': string;\n  }\n  declare export interface GetRootPropsOptions {\n    refKey: string;\n  }\n\n  declare type GetToggleButtonCallbacks = {\n    onMouseMove: (e: SyntheticEvent<Element>) => void;\n    onMouseDown: (e: SyntheticEvent<Element>) => void;\n    onBlur: (e: SyntheticEvent<Element>) => void;\n  } | {\n    onPress: (e: SyntheticEvent<Element>) => void; // should be react native type\n  } | {}\n  declare export type GetToggleButtonReturn = {\n    type: 'button';\n    role: 'button';\n    'aria-label': 'close menu' | 'open menu';\n    'aria-haspopup': true;\n    'data-toggle': true;\n  } & GetInputPropsCallbacks\n  declare export interface getToggleButtonPropsOptions\n    extends React.HTMLProps<HTMLButtonElement> {}\n\n  declare export interface GetLabelPropsReturn {\n    htmlFor: string;\n    id: string;\n  }\n  declare export interface GetLabelPropsOptions\n    extends React.HTMLProps<HTMLLabelElement> {}\n\n  declare export type getMenuPropsReturn = {\n    role: 'listbox';\n    'aria-labelledby': string | null;\n    id: string;\n  }\n\n  declare type GetInputPropsCallbacks = ({\n    onKeyDown: (e: SyntheticEvent<Element>) => void;\n    onBlur: (e: SyntheticEvent<Element>) => void;\n  } & ({\n    onInput: (e: SyntheticEvent<Element>) => void;\n  } | {\n    onChangeText: (e: SyntheticEvent<Element>) => void;\n  } | {\n    onChange: (e: SyntheticEvent<Element>) => void;\n  })) | {}\n  declare export type GetInputPropsReturn = {\n    'aria-autocomplete': 'list';\n    'aria-activedescendant': string | null;\n    'aria-controls': string | null;\n    'aria-labelledby': string;\n    autoComplete: 'off';\n    value: string;\n    id: string;\n  } & GetInputPropsCallbacks;\n  declare export interface GetInputPropsOptions\n    extends React.HTMLProps<HTMLInputElement> {}\n\n  declare type GetItemPropsCallbacks = {\n    onMouseMove: (e: SyntheticEvent<Element>) => void;\n    onMouseDown: (e: SyntheticEvent<Element>) => void;\n  } & ({\n    onPress: (e: SyntheticEvent<Element>) => void;\n  } | {\n    onClick: (e: SyntheticEvent<Element>) => void;\n  })\n  declare export type GetItemPropsReturn = {\n    id: string;\n    role: 'option';\n    'aria-selected': boolean;\n  } & GetItemPropsCallbacks\n  declare export type GetItemPropsOptions<Item> = {\n    index?: number,\n    item: Item,\n  }\n\n  declare export interface PropGetters<Item> {\n    getRootProps: <T:{}>(options: GetRootPropsOptions & T) => GetRootPropsReturn & T;\n    getButtonProps: <T:{}>(options?: getToggleButtonPropsOptions & T) => GetToggleButtonReturn & T;\n    getToggleButtonProps: <T:{}>(options?: getToggleButtonPropsOptions & T) => GetToggleButtonReturn & T;\n    getLabelProps: <T:{}>(options?: GetLabelPropsOptions & T) => GetLabelPropsReturn & T;\n    getMenuProps: <T:{}>(options?: T) => getMenuPropsReturn & T;\n    getInputProps: <T:{}>(options?: GetInputPropsOptions & T) => GetInputPropsReturn & T;\n    getItemProps: <T:{}>(options: GetItemPropsOptions<Item> & T) => GetItemPropsReturn & T;\n  }\n  declare export interface Actions<Item> {\n    reset: (otherStateToSet?: {}, cb?: Callback) => void;\n    openMenu: (cb?: Callback) => void;\n    closeMenu: (cb?: Callback) => void;\n    toggleMenu: (otherStateToSet?: {}, cb?: Callback) => void;\n    selectItem: (item: Item | null, otherStateToSet?: {}, cb?: Callback) => void;\n    selectItemAtIndex: (\n      index: number,\n      otherStateToSet?: {},\n      cb?: Callback,\n    ) => void;\n    selectHighlightedItem: (otherStateToSet?: {}, cb?: Callback) => void;\n    setHighlightedIndex: (\n      index: number,\n      otherStateToSet?: {},\n      cb?: Callback,\n    ) => void;\n    clearSelection: (cb?: Callback) => void;\n    clearItems: () => void;\n    setItemCount: (count: number) => void;\n    unsetItemCount: () => void;\n    setState: (\n      stateToSet: StateChangeOptions<Item> | StateChangeFunction<Item>,\n      cb?: Callback,\n    ) => void;\n    // props\n    itemToString: (item: Item) => string;\n  }\n  declare export type ControllerStateAndHelpers<Item> = DownshiftState<Item> &\n    PropGetters<Item> &\n    Actions<Item>\n  declare export type ChildrenFunction<Item> = (\n    options: ControllerStateAndHelpers<Item>,\n  ) => React.ReactNode\n  declare export type DownshiftType<Item> = Class<\n    React.Component<DownshiftProps<Item>, DownshiftState<Item>>,\n  > & {\n    stateChangeTypes: StateChangeTypes,\n  }\n  declare var DownshiftComponent: DownshiftType<any>\n  declare export default DownshiftComponent\n}\n"
  },
  {
    "path": "jest.config.js",
    "content": "const jestConfig = require('kcd-scripts/jest')\n\nmodule.exports = Object.assign(jestConfig, {\n  coveragePathIgnorePatterns: [\n    ...jestConfig.coveragePathIgnorePatterns,\n    '.macro.js$',\n    '<rootDir>/src/stateChangeTypes.js',\n  ],\n  setupFilesAfterEnv: ['<rootDir>/test/setup.ts'],\n  moduleFileExtensions: ['ts', 'js', 'tsx', 'jsx'],\n})\n"
  },
  {
    "path": "netlify.toml",
    "content": "# COMMENT: This a rule for Single Page Applications as Docz site is one\n[[redirects]]\n  from = \"/*\"\n  to = \"/\"\n  status = 200"
  },
  {
    "path": "other/MAINTAINING.md",
    "content": "# Maintaining\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n**Table of Contents**\n\n- [Code of Conduct](#code-of-conduct)\n- [Issues](#issues)\n- [Pull Requests](#pull-requests)\n- [Release](#release)\n- [Thanks!](#thanks)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\nThis is documentation for maintainers of this project.\n\n## Code of Conduct\n\nPlease review, understand, and be an example of it. Violations of the code of\nconduct are taken seriously, even (especially) for maintainers.\n\n## Issues\n\nWe want to support and build the community. We do that best by helping people\nlearn to solve their own problems. We have an issue template and hopefully most\nfolks follow it. If it's not clear what the issue is, invite them to create a\nminimal reproduction of what they're trying to accomplish or the bug they think\nthey've found.\n\nOnce it's determined that a code change is necessary, point people to\n[makeapullrequest.com](http://makeapullrequest.com) and invite them to make a\npull request. If they're the one who needs the feature, they're the one who can\nbuild it. If they need some hand holding and you have time to lend a hand,\nplease do so. It's an investment into another human being, and an investment\ninto a potential maintainer.\n\nRemember that this is open source, so the code is not yours, it's ours. If\nsomeone needs a change in the codebase, you don't have to make it happen\nyourself. Commit as much time to the project as you want/need to. Nobody can ask\nany more of you than that.\n\n## Pull Requests\n\nAs a maintainer, you're fine to make your branches on the main repo or on your\nown fork. Either way is fine.\n\nWhen we receive a pull request, a travis build is kicked off automatically (see\nthe `.travis.yml` for what runs in the travis build). We avoid merging anything\nthat breaks the travis build.\n\nPlease review PRs and focus on the code rather than the individual. You never\nknow when this is someone's first ever PR and we want their experience to be as\npositive as possible, so be uplifting and constructive.\n\nWhen you merge the pull request, 99% of the time you should use the\n[Squash and merge](https://help.github.com/articles/merging-a-pull-request/)\nfeature. This keeps our git history clean, but more importantly, this allows us\nto make any necessary changes to the commit message so we release what we want\nto release. See the next section on Releases for more about that.\n\n## Release\n\nOur releases are automatic. They happen whenever code lands into `master`. A\ntravis build gets kicked off and if it's successful, a tool called\n[`semantic-release`](https://github.com/semantic-release/semantic-release) is\nused to automatically publish a new release to npm as well as a changelog to\nGitHub. It is only able to determine the version and whether a release is\nnecessary by the git commit messages. With this in mind, **please brush up on\n[the commit message convention][commit] which drives our releases.**\n\n> One important note about this: Please make sure that commit messages do NOT\n> contain the words \"BREAKING CHANGE\" in them unless we want to push a major\n> version. I've been burned by this more than once where someone will include\n> \"BREAKING CHANGE: None\" and it will end up releasing a new major version. Not\n> a huge deal honestly, but kind of annoying...\n\n## Thanks!\n\nThank you so much for helping to maintain this project!\n\n[commit]: https://github.com/conventional-changelog-archived-repos/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md\n"
  },
  {
    "path": "other/TYPESCRIPT_USAGE.md",
    "content": "# Typescript Usage\n\nThe current bundled Typescript definitions are incomplete and based around the\nneeds of the developers who contributed them.\n\nPull requests to improve them are welcome and appreciated. If you've never\ncontributed to open source before, then you may find\n[this free video course](https://app.egghead.io/playlists/how-to-contribute-to-an-open-source-project-on-github)\nhelpful.\n"
  },
  {
    "path": "other/USERS.md",
    "content": "# Users\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\nIf you or your company uses this project, add your name to this list! Eventually\nwe may have a website to showcase these (wanna build it!?)\n\n> No users have been added yet!\n\n<!--\nThis file should just be a bulleted list like this:\n\n- [Company/Project/Person](https://example.com) uses it in [some app](https://example.com)\n-->\n"
  },
  {
    "path": "other/manual-releases.md",
    "content": "# manual-releases\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\nThis project has an automated release set up. So things are only released when\nthere are useful changes in the code that justify a release. But sometimes\nthings get messed up one way or another and we need to trigger the release\nourselves. When this happens, simply bump the number below and commit that with\nthe following commit message based on your needs:\n\n**Major**\n\n```\nfix(release): manually release a major version\n\nThere was an issue with a major release, so this manual-releases.md\nchange is to release a new major version.\n\nReference: #<the number of a relevant pull request, issue, or commit>\n\nBREAKING CHANGE: <mention any relevant breaking changes (this is what triggers the major version change so don't skip this!)>\n```\n\n**Minor**\n\n```\nfeat(release): manually release a minor version\n\nThere was an issue with a minor release, so this manual-releases.md\nchange is to release a new minor version.\n\nReference: #<the number of a relevant pull request, issue, or commit>\n```\n\n**Patch**\n\n```\nfix(release): manually release a patch version\n\nThere was an issue with a patch release, so this manual-releases.md\nchange is to release a new patch version.\n\nReference: #<the number of a relevant pull request, issue, or commit>\n```\n\nThe number of times we've had to do a manual release is: 12\n"
  },
  {
    "path": "other/misc-tests/__tests__/build.js",
    "content": "/*\n * This file is here to validate that the built version\n * of the library exposes the module in the way that we\n * want it to. Specifically that the ES6 module import can\n * get the downshift function via default import. Also that\n * the CommonJS require returns the downshift function\n * (rather than an object that has the downshift as a\n * `default` property).\n *\n * This file is unable to validate the global export.\n */\nimport assert from 'assert'\n\nimport esImport, {\n  useCombobox as useComboboxEsImport,\n  useSelect as useSelectEsImport,\n  useMultipleSelection as useMultipleSelectionEsImport,\n} from '../../../dist/downshift.esm.mjs'\n\nimport cjsImport, {\n  useCombobox as useComboboxCjsImport,\n  useSelect as useSelectCjsImport,\n  useMultipleSelection as useMultipleSelectionCjsImport,\n} from '../../../' // picks up the main from package.json\n\nimport umdImport, {\n  useCombobox as useComboboxUmdImport,\n  useSelect as useSelectUmdImport,\n  useMultipleSelection as useMultipleSelectionUmdImport,\n} from '../../../dist/downshift.umd'\n\nimport rnImport, {\n  useCombobox as useComboboxRnImport,\n  useSelect as useSelectRnImport,\n  useMultipleSelection as useMultipleSelectionRnImport,\n} from '../../../dist/downshift.native.cjs.cjs'\n\nimport rnWebImport, {\n  useCombobox as useComboboxRnWebImport,\n  useSelect as useSelectRnWebImport,\n  useMultipleSelection as useMultipleSelectionRnWebImport,\n} from '../../../dist/downshift.nativeweb.cjs.cjs'\n\n// intentionally left out because you shouldn't ever\n// try to require the ES file in CommonJS\n// const esRequire = require('../../../dist/downshift.es')\nconst cjsRequire = require('../../../') // picks up the main from package.json\nconst umdRequire = require('../../../dist/downshift.umd')\nconst rnCjsRequire = require('../../../dist/downshift.native.cjs.cjs')\nconst rnWebCjsRequire = require('../../../dist/downshift.nativeweb.cjs.cjs')\n\ntest('downshift component is imported', () => {\n  assert(\n    isDownshiftComponent(esImport),\n    'ES build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(cjsImport),\n    'CJS build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(cjsRequire.default),\n    'CJS build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(umdImport),\n    'UMD build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(umdRequire.default),\n    'UMD build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(rnImport),\n    'React Native build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnCjsRequire.default),\n    'React Native build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(rnWebImport),\n    'React Native Web build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnWebCjsRequire.default),\n    'React Native Web build has a problem with CJS',\n  )\n\n  // TODO: how could we validate the global export?\n})\n\ntest('useSelect hook is imported', () => {\n  assert(\n    isDownshiftComponent(useSelectEsImport),\n    'ES build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(useSelectCjsImport),\n    'CJS build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(cjsRequire.useSelect),\n    'CJS build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useSelectUmdImport),\n    'UMD build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(umdRequire.useSelect),\n    'UMD build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useSelectRnImport),\n    'React Native build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnCjsRequire.useSelect),\n    'React Native build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useSelectRnWebImport),\n    'React Native Web build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnWebCjsRequire.useSelect),\n    'React Native Web build has a problem with CJS',\n  )\n})\n\ntest('useCombobox hook is imported', () => {\n  assert(\n    isDownshiftComponent(useComboboxEsImport),\n    'ES build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(useComboboxCjsImport),\n    'CJS build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(cjsRequire.useCombobox),\n    'CJS build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useComboboxUmdImport),\n    'UMD build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(umdRequire.useCombobox),\n    'UMD build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useComboboxRnImport),\n    'React Native build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnCjsRequire.useCombobox),\n    'React Native build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useComboboxRnWebImport),\n    'React Native Web build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnWebCjsRequire.useCombobox),\n    'React Native Web build has a problem with CJS',\n  )\n})\n\ntest('useMultipleSelection hook is imported', () => {\n  assert(\n    isDownshiftComponent(useMultipleSelectionEsImport),\n    'ES build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(useMultipleSelectionCjsImport),\n    'CJS build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(cjsRequire.useMultipleSelection),\n    'CJS build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useMultipleSelectionUmdImport),\n    'UMD build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(umdRequire.useMultipleSelection),\n    'UMD build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useMultipleSelectionRnImport),\n    'React Native build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnCjsRequire.useMultipleSelection),\n    'React Native build has a problem with CJS',\n  )\n\n  assert(\n    isDownshiftComponent(useMultipleSelectionRnWebImport),\n    'React Native Web build has a problem with ES Modules',\n  )\n\n  assert(\n    isDownshiftComponent(rnWebCjsRequire.useMultipleSelection),\n    'React Native Web build has a problem with CJS',\n  )\n})\n\nfunction isDownshiftComponent(thing) {\n  if (typeof thing !== 'function') {\n    console.error(\n      `downshift thing should be a function. It's a ${typeof thing} with the properties of: ${Object.keys(\n        thing,\n      ).join(', ')}`,\n    )\n    return false\n  }\n  return true\n}\n\n/*\n eslint\n  no-console: 0,\n  import/extensions: 0,\n  import/no-unresolved: 0,\n  import/no-duplicates: 0,\n  no-duplicate-imports: 0,\n */\n"
  },
  {
    "path": "other/misc-tests/__tests__/preact.js",
    "content": "// Tell Babel to transform JSX into preact.h() calls:\n/** @jsx preact.h */\n/*\neslint-disable\nreact/prop-types,\nno-console,\nreact/display-name,\nimport/extensions,\nimport/no-unresolved\n*/\n\n/*\nTesting the preact version is a tiny bit complicated because\nwe need the preact build (the one that imports 'preact' rather\nthan 'react') otherwise things don't work very well.\nSo there's a script `test.build` which will run the cjs build\nfor preact before running this test.\n */\n\nimport preact from 'preact'\nimport {render} from '@testing-library/preact'\nimport Downshift from '../../../preact'\n\ntest('works with preact', () => {\n  const childrenSpy = jest.fn(({getInputProps, getItemProps}) => (\n    <div>\n      <input {...getInputProps()} />\n      <div>\n        <div {...getItemProps({item: 'foo', index: 0})}>foo</div>\n        <div {...getItemProps({item: 'bar', index: 1})}>bar</div>\n      </div>\n    </div>\n  ))\n  const ui = <Downshift>{childrenSpy}</Downshift>\n  render(ui)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      highlightedIndex: null,\n      selectedItem: null,\n      inputValue: '',\n    }),\n  )\n})\n\ntest('can render a composite component', () => {\n  const Div = ({innerRef, ...props}) => <div {...props} ref={innerRef} />\n  const childrenSpy = jest.fn(({getRootProps}) => (\n    <Div {...getRootProps({refKey: 'innerRef'})} />\n  ))\n  const ui = <Downshift>{childrenSpy}</Downshift>\n  render(ui)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      highlightedIndex: null,\n      selectedItem: null,\n      inputValue: '',\n    }),\n  )\n})\n\ntest('getInputProps composes onChange with onInput', () => {\n  const onChange = jest.fn()\n  const onInput = jest.fn()\n  const Input = jest.fn(props => <input {...props} />)\n  const {ui} = setup({\n    children({getInputProps}) {\n      return (\n        <div>\n          <Input {...getInputProps({onChange, onInput})} />\n        </div>\n      )\n    },\n  })\n  render(ui)\n  expect(Input).toHaveBeenCalledTimes(1)\n  const [[firstArg]] = Input.mock.calls\n  expect(firstArg).toMatchObject({\n    onInput: expect.any(Function),\n  })\n  expect(firstArg.onChange).toBeUndefined()\n  const fakeEvent = {defaultPrevented: false, target: {value: ''}}\n  firstArg.onInput(fakeEvent)\n  expect(onChange).toHaveBeenCalledTimes(1)\n  expect(onChange).toHaveBeenCalledWith(fakeEvent)\n  expect(onInput).toHaveBeenCalledTimes(1)\n  expect(onInput).toHaveBeenCalledWith(fakeEvent)\n})\n\ntest('can use children instead of render prop', () => {\n  const childrenSpy = jest.fn()\n  render(<Downshift>{childrenSpy}</Downshift>)\n  expect(childrenSpy).toHaveBeenCalledTimes(1)\n})\n\nfunction setup({children = () => <div />, ...props} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return children(controllerArg)\n  })\n  const ui = <Downshift {...props}>{childrenSpy}</Downshift>\n  return {childrenSpy, ui, ...renderArg}\n}\n"
  },
  {
    "path": "other/misc-tests/jest.config.js",
    "content": "const jestConfig = require('kcd-scripts/config').jest\nconst babelHelpersList = require('@babel/helpers').list\n\nmodule.exports = Object.assign(jestConfig, {\n  roots: ['.'],\n  testEnvironment: 'jsdom',\n  transform: {\n    '^.+\\\\.(js|jsx|mjs)$': ['babel-jest', { rootMode: 'upward' }],\n  },\n  transformIgnorePatterns: [\n    'node_modules/(?!(dedent|@testing-library/preact)/)',\n  ],\n  moduleNameMapper: babelHelpersList.reduce(\n    (aliasMap, helper) => {\n      aliasMap[`@babel/runtime/helpers/esm/${helper}`] =\n        `@babel/runtime/helpers/${helper}`\n      return aliasMap\n    },\n    {\n      '^preact(/(.*)|$)': 'preact$1',\n    },\n  ),\n})\n"
  },
  {
    "path": "other/react-native/.babelrc",
    "content": "{\n  \"presets\": [\"react-native\"]\n}\n"
  },
  {
    "path": "other/react-native/__tests__/__snapshots__/render-tests.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`renders with React Native components 1`] = `\n<View\n  aria-expanded={false}\n  aria-haspopup=\"listbox\"\n  aria-labelledby=\"downshift-0-label\"\n  aria-owns={null}\n  role=\"combobox\"\n>\n  <TextInput\n    allowFontScaling={true}\n    aria-activedescendant={null}\n    aria-autocomplete=\"list\"\n    aria-controls={null}\n    aria-labelledby=\"downshift-0-label\"\n    autoComplete=\"off\"\n    id=\"downshift-0-input\"\n    onBlur={[Function]}\n    onChange={[Function]}\n    onChangeText={[Function]}\n    underlineColorAndroid=\"transparent\"\n    value=\"\"\n  />\n  <View>\n    <Text\n      aria-selected={false}\n      id=\"downshift-0-item-0\"\n      onMouseDown={[Function]}\n      onMouseMove={[Function]}\n      onPress={[Function]}\n      role=\"option\"\n    >\n      foo\n    </Text>\n    <Text\n      aria-selected={false}\n      id=\"downshift-0-item-1\"\n      onMouseDown={[Function]}\n      onMouseMove={[Function]}\n      onPress={[Function]}\n      role=\"option\"\n    >\n      bar\n    </Text>\n  </View>\n</View>\n`;\n"
  },
  {
    "path": "other/react-native/__tests__/onBlur-tests.js",
    "content": "import {Text, TextInput, View} from 'react-native'\nimport * as React from 'react'\n\n// Note: test renderer must be required after react-native.\nimport TestRenderer from 'react-test-renderer'\n\nimport Downshift from '../../../dist/downshift.native.cjs'\n\nconst RootView = ({innerRef, ...rest}) => <View ref={innerRef} {...rest} />\n\ntest('calls onBlur and does not crash when there is no document', () => {\n  const Input = jest.fn(props => <TextInput {...props} />)\n\n  const element = (\n    <Downshift>\n      {({getRootProps, getInputProps, getItemProps}) => (\n        <RootView {...getRootProps({refKey: 'innerRef'})}>\n          <Input {...getInputProps()} />\n          <View>\n            <Text {...getItemProps({item: 'foo', index: 0})}>foo</Text>\n            <Text {...getItemProps({item: 'bar', index: 1})}>bar</Text>\n          </View>\n        </RootView>\n      )}\n    </Downshift>\n  )\n  TestRenderer.create(element)\n\n  const [[firstArg]] = Input.mock.calls\n  expect(firstArg).toMatchObject({\n    onBlur: expect.any(Function),\n  })\n  const fakeEvent = 'blur'\n  firstArg.onBlur(fakeEvent)\n})\n\n/*\n eslint\n  react/prop-types: 0,\n  import/extensions: 0,\n  import/no-unresolved: 0\n */\n"
  },
  {
    "path": "other/react-native/__tests__/onChange-tests.js",
    "content": "import {Text, TextInput, View} from 'react-native'\nimport * as React from 'react'\n\n// Note: test renderer must be required after react-native.\nimport TestRenderer from 'react-test-renderer'\n\nimport Downshift from '../../../dist/downshift.native.cjs'\n\nconst RootView = ({innerRef, ...rest}) => <View ref={innerRef} {...rest} />\n\ntest('calls onChange when TextInput changes values', () => {\n  const onChange = jest.fn()\n  const Input = jest.fn(props => <TextInput {...props} />)\n\n  const element = (\n    <Downshift>\n      {({getRootProps, getInputProps, getItemProps}) => (\n        <RootView {...getRootProps({refKey: 'innerRef'})}>\n          <Input {...getInputProps({onChange})} />\n          <View>\n            <Text {...getItemProps({item: 'foo', index: 0})}>foo</Text>\n            <Text {...getItemProps({item: 'bar', index: 1})}>bar</Text>\n          </View>\n        </RootView>\n      )}\n    </Downshift>\n  )\n  TestRenderer.create(element)\n\n  const [[firstArg]] = Input.mock.calls\n  expect(firstArg).toMatchObject({\n    onChange: expect.any(Function),\n  })\n  const fakeEvent = {nativeEvent: {text: 'foobar'}}\n  firstArg.onChange(fakeEvent)\n\n  expect(onChange).toHaveBeenCalledTimes(1)\n  expect(onChange).toHaveBeenCalledWith(fakeEvent)\n})\n\ntest('calls onChangeText when TextInput changes values', () => {\n  const onChangeText = jest.fn()\n  const Input = jest.fn(props => <TextInput {...props} />)\n\n  const element = (\n    <Downshift>\n      {({getRootProps, getInputProps, getItemProps}) => (\n        <RootView {...getRootProps({refKey: 'innerRef'})}>\n          <Input {...getInputProps({onChangeText})} />\n          <View>\n            <Text {...getItemProps({item: 'foo', index: 0})}>foo</Text>\n            <Text {...getItemProps({item: 'bar', index: 1})}>bar</Text>\n          </View>\n        </RootView>\n      )}\n    </Downshift>\n  )\n  TestRenderer.create(element)\n\n  const [[firstArg]] = Input.mock.calls\n  expect(firstArg).toMatchObject({\n    onChangeText: expect.any(Function),\n  })\n  const fakeEvent = 'foobar'\n  firstArg.onChangeText(fakeEvent)\n\n  expect(onChangeText).toHaveBeenCalledTimes(1)\n  expect(onChangeText).toHaveBeenCalledWith(fakeEvent)\n})\n\n/*\n eslint\n  react/prop-types: 0,\n  import/extensions: 0,\n  import/no-unresolved: 0\n */\n"
  },
  {
    "path": "other/react-native/__tests__/render-tests.js",
    "content": "import {Text, TextInput, View} from 'react-native'\nimport * as React from 'react'\n\n// Note: test renderer must be required after react-native.\nimport TestRenderer from 'react-test-renderer'\n\nimport Downshift from '../../../dist/downshift.native.cjs'\n\ntest('renders with React Native components', () => {\n  const RootView = ({innerRef, ...rest}) => <View ref={innerRef} {...rest} />\n  const childrenSpy = jest.fn(({getRootProps, getInputProps, getItemProps}) => (\n    <RootView {...getRootProps({refKey: 'innerRef'})}>\n      <TextInput {...getInputProps()} />\n      <View>\n        <Text {...getItemProps({item: 'foo', index: 0})}>foo</Text>\n        <Text {...getItemProps({item: 'bar', index: 1})}>bar</Text>\n      </View>\n    </RootView>\n  ))\n  const element = <Downshift>{childrenSpy}</Downshift>\n  const renderer = TestRenderer.create(element)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      highlightedIndex: null,\n      selectedItem: null,\n      inputValue: '',\n    }),\n  )\n  const tree = renderer.toJSON()\n  expect(tree).toMatchSnapshot()\n})\n\ntest('can use children instead of render prop', () => {\n  const RootView = ({innerRef, ...rest}) => <View ref={innerRef} {...rest} />\n  const childrenSpy = jest.fn(({getRootProps, getInputProps, getItemProps}) => (\n    <RootView {...getRootProps({refKey: 'innerRef'})}>\n      <TextInput {...getInputProps()} />\n      <View>\n        <Text {...getItemProps({item: 'foo', index: 0})}>foo</Text>\n        <Text {...getItemProps({item: 'bar', index: 1})}>bar</Text>\n      </View>\n    </RootView>\n  ))\n  const element = <Downshift>{childrenSpy}</Downshift>\n  TestRenderer.create(element)\n  expect(childrenSpy).toHaveBeenCalledTimes(1)\n})\n\n/*\n eslint\n  react/prop-types: 0,\n  import/extensions: 0,\n  import/no-unresolved: 0\n */\n"
  },
  {
    "path": "other/react-native/jest.config.js",
    "content": "// const jestConfig = require('kcd-scripts/config').jest\n\nmodule.exports = {\n  preset: 'react-native',\n  rootDir: '../../',\n  roots: ['.'],\n  transform: {\n    '^.+\\\\.js$': '<rootDir>/node_modules/react-native/jest/preprocessor.js',\n  },\n  testMatch: ['<rootDir>/other/react-native/__tests__/**/*.js?(x)'],\n}\n"
  },
  {
    "path": "other/ssr/__tests__/index.js",
    "content": "import * as React from 'react'\nimport * as ReactDOMServer from 'react-dom/server'\nimport Downshift, {resetIdCounter} from '../../../src'\n\ntest('does not throw an error when server rendering', () => {\n  expect(() => {\n    ReactDOMServer.renderToString(\n      <Downshift id=\"my-autocomplete-component\">\n        {({getInputProps, getLabelProps}) => (\n          <div>\n            <label {...getLabelProps({htmlFor: 'my-autocomplete-input'})} />\n            <input {...getInputProps({id: 'my-autocomplete-input'})} />\n          </div>\n        )}\n      </Downshift>,\n    )\n  }).not.toThrow()\n})\n\nif (!('useId' in React)) {\n  test('resets idCounter', () => {\n    const getRenderedString = () => {\n      resetIdCounter()\n      return ReactDOMServer.renderToString(\n        <Downshift id=\"my-autocomplete-component\">\n          {({getInputProps, getLabelProps}) => (\n            <div>\n              <label {...getLabelProps()} />\n              <input {...getInputProps()} />\n            </div>\n          )}\n        </Downshift>,\n      )\n    }\n\n    const firstRun = getRenderedString()\n    const secondRun = getRenderedString()\n\n    expect(firstRun).toBe(secondRun)\n  })\n}\n\n/* eslint jsx-a11y/label-has-for:0 */\n"
  },
  {
    "path": "other/ssr/jest.config.js",
    "content": "// This is separate because the test environment is set via the config\n// and we want most of our tests to run with jsdom, but we still want\n// to make sure that the server rendering use case continues to work.\nconst jestConfig = require('kcd-scripts/config').jest\n\nmodule.exports = Object.assign(jestConfig, {\n  roots: ['.'],\n  testEnvironment: 'node',\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"downshift\",\n  \"version\": \"0.0.0-semantically-released\",\n  \"description\": \"🏎 A set of primitives to build simple, flexible, WAI-ARIA compliant React autocomplete, combobox or select dropdown components.\",\n  \"main\": \"dist/downshift.cjs.cjs\",\n  \"react-native\": \"dist/downshift.native.cjs.cjs\",\n  \"module\": \"dist/downshift.esm.mjs\",\n  \"typings\": \"typings/index.d.ts\",\n  \"types\": \"typings/index.d.ts\",\n  \"sideEffects\": false,\n  \"browserslist\": [],\n  \"scripts\": {\n    \"build\": \"npm run build:web --silent && npm run build:native --silent && npm run build:nativeWeb --silent\",\n    \"build:web\": \"kcd-scripts build --bundle --p-react --no-clean --size-snapshot\",\n    \"build:native\": \"cross-env BUILD_REACT_NATIVE=true BUILD_FILENAME_SUFFIX=.native kcd-scripts build --bundle cjs --no-clean\",\n    \"build:nativeWeb\": \"cross-env BUILD_REACT_NATIVE_WEB=true BUILD_FILENAME_SUFFIX=.nativeweb kcd-scripts build --bundle cjs --no-clean\",\n    \"lint\": \"kcd-scripts lint\",\n    \"test\": \"kcd-scripts test\",\n    \"test:cover\": \"kcd-scripts test --coverage\",\n    \"test:ssr\": \"kcd-scripts test --config other/ssr/jest.config.js --no-watch\",\n    \"test:update\": \"npm run test:cover -s -- --updateSnapshot\",\n    \"test:ts\": \"tsc --noEmit -p ./tsconfig.json\",\n    \"test:flow\": \"flow\",\n    \"test:flow:coverage\": \"flow-coverage-report\",\n    \"test:build\": \"jest --config other/misc-tests/jest.config.js\",\n    \"// FIXME: test:build\": \"jest --projects other/misc-tests other/react-native\",\n    \"test:cypress:dev\": \"npm-run-all --parallel --race docs:dev cy:open\",\n    \"pretest:cypress\": \"npm run docs:build --silent\",\n    \"test:cypress\": \"start-server-and-test docs:serve http://localhost:6006 cy:run\",\n    \"cy:run\": \"cypress run\",\n    \"cy:open\": \"cypress open\",\n    \"build-and-test\": \"npm run build -s && npm run test:build -s\",\n    \"docs:build\": \"docusaurus build\",\n    \"docs:dev\": \"docusaurus start\",\n    \"docs:serve\": \"docusaurus serve --port 6006\",\n    \"docs:clear\": \"docusaurus clear\",\n    \"setup\": \"npm install && npm run validate\",\n    \"validate\": \"kcd-scripts validate lint,build-and-test,test:cover,test:ts,test:ssr,test:cypress\"\n  },\n  \"husky\": {\n    \"hooks\": {\n      \"pre-commit\": \"kcd-scripts pre-commit\"\n    }\n  },\n  \"files\": [\n    \"dist\",\n    \"typings\",\n    \"preact\",\n    \"flow-typed\"\n  ],\n  \"exports\": {\n    \".\": {\n      \"import\": \"./dist/downshift.esm.mjs\",\n      \"require\": \"./dist/downshift.cjs.cjs\",\n      \"types\": \"./typings/index.d.ts\",\n      \"default\": \"./dist/downshift.esm.mjs\"\n    },\n    \"./react-native\": {\n      \"require\": \"./dist/downshift.native.cjs.cjs\",\n      \"types\": \"./typings/index.d.ts\"\n    }\n  },\n  \"keywords\": [\n    \"enhanced input\",\n    \"react\",\n    \"autocomplete\",\n    \"autosuggest\",\n    \"typeahead\",\n    \"dropdown\",\n    \"select\",\n    \"combobox\",\n    \"omnibox\",\n    \"accessibility\",\n    \"WAI-ARIA\",\n    \"multiselect\",\n    \"multiple selection\"\n  ],\n  \"author\": \"Kent C. Dodds <kent@doddsfamily.us> (http://kentcdodds.com/)\",\n  \"license\": \"MIT\",\n  \"peerDependencies\": {\n    \"react\": \">=16.12.0\"\n  },\n  \"dependencies\": {\n    \"@babel/runtime\": \"^7.28.6\",\n    \"compute-scroll-into-view\": \"^3.1.1\",\n    \"prop-types\": \"^15.8.1\",\n    \"react-is\": \"^18.2.0\",\n    \"tslib\": \"^2.8.1\"\n  },\n  \"devDependencies\": {\n    \"@babel/helpers\": \"^7.28.6\",\n    \"@babel/plugin-proposal-private-methods\": \"^7.18.6\",\n    \"@babel/plugin-proposal-private-property-in-object\": \"^7.21.11\",\n    \"@cypress/webpack-preprocessor\": \"^7.0.2\",\n    \"@docusaurus/core\": \"3.3.2\",\n    \"@docusaurus/module-type-aliases\": \"3.3.2\",\n    \"@docusaurus/preset-classic\": \"3.3.2\",\n    \"@mdx-js/react\": \"^3.0.1\",\n    \"@rollup/plugin-babel\": \"^6.1.0\",\n    \"@rollup/plugin-commonjs\": \"^29.0.0\",\n    \"@testing-library/cypress\": \"^10.1.0\",\n    \"@testing-library/dom\": \"^10.4.1\",\n    \"@testing-library/jest-dom\": \"^6.9.1\",\n    \"@testing-library/preact\": \"^3.2.4\",\n    \"@testing-library/react\": \"^16.3.2\",\n    \"@testing-library/user-event\": \"^14.6.1\",\n    \"@types/jest\": \"^30.0.0\",\n    \"@types/prop-types\": \"^15.7.15\",\n    \"@types/react\": \"^18.2.0\",\n    \"@typescript-eslint/eslint-plugin\": \"^8.54.0\",\n    \"@typescript-eslint/parser\": \"^8.54.0\",\n    \"babel-plugin-macros\": \"^3.1.0\",\n    \"babel-plugin-no-side-effect-class-properties\": \"0.0.7\",\n    \"babel-preset-react-native\": \"^4.0.1\",\n    \"buble\": \"^0.20.0\",\n    \"cpy-cli\": \"^6.0.0\",\n    \"cross-env\": \"^10.1.0\",\n    \"cypress\": \"15.9.0\",\n    \"eslint\": \"^8.57.0\",\n    \"eslint-plugin-cypress\": \"^3.6.0\",\n    \"eslint-plugin-react\": \"7.37.5\",\n    \"flow-bin\": \"^0.299.0\",\n    \"flow-coverage-report\": \"^0.8.0\",\n    \"get-pkg-repo\": \"5.0.0\",\n    \"kcd-scripts\": \"^17.0.0\",\n    \"node-polyfill-webpack-plugin\": \"^4.1.0\",\n    \"npm-run-all\": \"^4.1.5\",\n    \"preact\": \"^10.28.2\",\n    \"prism-react-renderer\": \"^2.4.1\",\n    \"react\": \"^18.3.1\",\n    \"react-dom\": \"^18.3.1\",\n    \"react-is\": \"^18.3.1\",\n    \"react-native\": \"^0.76.0\",\n    \"react-test-renderer\": \"^18.3.1\",\n    \"serve\": \"^14.2.5\",\n    \"start-server-and-test\": \"^2.1.3\",\n    \"typescript\": \"^5.9.3\"\n  },\n  \"eslintConfig\": {\n    \"parserOptions\": {\n      \"ecmaVersion\": 2023,\n      \"project\": [\n        \"./tsconfig.json\",\n        \"./docusaurus/tsconfig.json\",\n        \"./test/tsconfig.json\"\n      ],\n      \"sourceType\": \"module\"\n    },\n    \"settings\": {\n      \"import/no-unresolved\": [\n        2,\n        {\n          \"ignore\": [\n            \"^@theme\"\n          ]\n        }\n      ],\n      \"import/resolver\": {\n        \"node\": {\n          \"extensions\": [\n            \".js\",\n            \".jsx\",\n            \".ts\",\n            \".tsx\"\n          ]\n        }\n      }\n    },\n    \"extends\": \"./node_modules/kcd-scripts/eslint.js\",\n    \"rules\": {\n      \"react/jsx-indent\": \"off\",\n      \"react/prop-types\": \"off\",\n      \"max-lines-per-function\": \"off\",\n      \"jsx-a11y/label-has-for\": \"off\",\n      \"jsx-a11y/label-has-associated-control\": \"off\",\n      \"jsx-a11y/autocomplete-valid\": \"off\",\n      \"testing-library/prefer-user-event\": \"off\",\n      \"testing-library/no-node-access\": \"off\",\n      \"testing-library/no-container\": \"off\",\n      \"testing-library/render-result-naming-convention\": \"off\"\n    },\n    \"overrides\": [\n      {\n        \"files\": [\n          \"cypress/**/*.js\"\n        ],\n        \"rules\": {\n          \"testing-library/prefer-screen-queries\": \"off\",\n          \"testing-library/await-async-query\": \"off\"\n        }\n      }\n    ]\n  },\n  \"eslintIgnore\": [\n    \"node_modules\",\n    \"coverage\",\n    \"dist\",\n    \".docusaurus\",\n    \"build\",\n    \"typings\",\n    \"test\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/downshift-js/downshift.git\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/downshift-js/downshift/issues\"\n  },\n  \"homepage\": \"https://downshift-js.com\",\n  \"flow-coverage-report\": {\n    \"includeGlob\": [\n      \"test/**/*.js\"\n    ],\n    \"threshold\": 90,\n    \"type\": [\n      \"text\"\n    ]\n  }\n}\n"
  },
  {
    "path": "prettier.config.js",
    "content": "// this is really only here for editor integrations\nmodule.exports = require('kcd-scripts/prettier')\n"
  },
  {
    "path": "rollup.config.js",
    "content": "const commonjs = require('@rollup/plugin-commonjs')\nconst {babel} = require('@rollup/plugin-babel')\nconst config = require('kcd-scripts/dist/config/rollup.config')\n\nconst babelPlugin = babel({\n  babelHelpers: 'runtime',\n  extensions: ['.js', '.jsx', '.ts', '.tsx'],\n  exclude: '**/node_modules/**',\n})\nconst cjsPlugin = commonjs({include: 'node_modules/**'})\n\nconfig.plugins = [\n  babelPlugin,\n  cjsPlugin,\n  ...config.plugins.filter(\n    p => !['babel', 'typescript', 'commonjs'].includes(p.name),\n  ),\n]\n\nconst prevExternal = config.external\nconfig.external = id => {\n  if (id.includes('productionEnum.macro') || id.includes('is.macro')) {\n    return true\n  }\n  if (typeof prevExternal === 'function') return prevExternal(id)\n  if (Array.isArray(prevExternal)) return prevExternal.includes(id)\n  return false\n}\n\nmodule.exports = config\n"
  },
  {
    "path": "src/__mocks__/set-a11y-status.js",
    "content": "module.exports = {setStatus: jest.fn(), cleanupStatusDiv: jest.fn()}\n"
  },
  {
    "path": "src/__mocks__/utils.js",
    "content": "const actualUtils = jest.requireActual('../utils')\nmodule.exports = Object.assign(actualUtils, {\n  scrollIntoView: jest.fn(), // hard to write tests for this thing...\n})\n"
  },
  {
    "path": "src/__tests__/.eslintrc",
    "content": "{\n  \"rules\": {\n    \"jsx-a11y/label-has-for\": \"off\",\n    \"jsx-a11y/click-events-have-key-events\": \"off\",\n    \"react/prop-types\": \"off\",\n    \"react/display-name\": \"off\",\n    \"react/no-deprecated\": \"off\",\n    \"no-console\": \"off\"\n  }\n}\n"
  },
  {
    "path": "src/__tests__/__snapshots__/downshift.aria.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`basic snapshot 1`] = `\n<div\n  aria-expanded=false\n  aria-haspopup=listbox\n  aria-labelledby=downshift-0-label\n  role=combobox\n>\n  <label\n    data-testid=label\n    for=downshift-0-input\n    id=downshift-0-label\n  >\n    label\n  </label>\n  <input\n    aria-autocomplete=list\n    aria-labelledby=downshift-0-label\n    autocomplete=off\n    data-testid=input\n    id=downshift-0-input\n    value=item\n  />\n  <button\n    aria-haspopup=true\n    aria-label=open menu\n    data-testid=button\n    data-toggle=true\n    role=button\n    type=button\n  />\n  <ul\n    aria-labelledby=downshift-0-label\n    data-testid=menu\n    id=downshift-0-menu\n    role=listbox\n  >\n    <li\n      aria-selected=false\n      data-testid=item-0\n      id=downshift-0-item-0\n      role=option\n    >\n      item\n    </li>\n  </ul>\n</div>\n`;\n\nexports[`can override the ids 1`] = `\n<div\n  aria-expanded=false\n  aria-haspopup=listbox\n  aria-labelledby=custom-label-id\n  role=combobox\n>\n  <label\n    data-testid=label\n    for=custom-input-id\n    id=custom-label-id\n  >\n    label\n  </label>\n  <input\n    aria-autocomplete=list\n    aria-labelledby=custom-label-id\n    autocomplete=off\n    data-testid=input\n    id=custom-input-id\n    value=\n  />\n  <button\n    aria-haspopup=true\n    aria-label=open menu\n    data-testid=button\n    data-toggle=true\n    role=button\n    type=button\n  />\n  <ul\n    aria-labelledby=custom-label-id\n    data-testid=menu\n    id=custom-menu-id\n    role=listbox\n  >\n    <li\n      aria-selected=false\n      data-testid=item-0\n      id=custom-item-id-0\n      role=option\n    >\n      item\n    </li>\n  </ul>\n</div>\n`;\n"
  },
  {
    "path": "src/__tests__/__snapshots__/downshift.get-item-props.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`getItemProps defaults the index when no index is given 1`] = `\n<div\n  aria-expanded=false\n  aria-haspopup=listbox\n  aria-labelledby=downshift-1-label\n  role=combobox\n>\n  <span\n    aria-selected=false\n    id=downshift-1-item-0\n    role=option\n  >\n    0\n  </span>\n  <span\n    aria-selected=false\n    id=downshift-1-item-1\n    role=option\n  >\n    1\n  </span>\n  <span\n    aria-selected=false\n    id=downshift-1-item-2\n    role=option\n  >\n    2\n  </span>\n  <span\n    aria-selected=false\n    id=downshift-1-item-3\n    role=option\n  >\n    3\n  </span>\n  <span\n    aria-selected=false\n    id=downshift-1-item-4\n    role=option\n  >\n    4\n  </span>\n</div>\n`;\n\nexports[`getItemProps logs a helpful error when no object is given 1`] = `The property \"item\" is required in \"getItemProps\"`;\n\nexports[`getItemProps logs error when no item is given 1`] = `The property \"item\" is required in \"getItemProps\"`;\n"
  },
  {
    "path": "src/__tests__/__snapshots__/downshift.get-menu-props.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`not applying the ref prop results in an error 1`] = `downshift: The ref prop \"ref\" from getMenuProps was not applied correctly on your menu element.`;\n\nexports[`using a composite component and calling getMenuProps without a refKey results in an error 1`] = `downshift: The ref prop \"ref\" from getMenuProps was not applied correctly on your menu element.`;\n"
  },
  {
    "path": "src/__tests__/__snapshots__/downshift.get-root-props.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`not applying the ref prop results in an error 1`] = `downshift: You must apply the ref prop \"ref\" from getRootProps onto your root element.`;\n\nexports[`returning a DOM element and calling getRootProps with a refKey results in an error 1`] = `downshift: You returned a DOM element. You should not specify a refKey in getRootProps. You specified \"blah\"`;\n\nexports[`returning a composite component and calling getRootProps without a refKey results in an error 1`] = `downshift: You returned a non-DOM element. You must specify a refKey in getRootProps`;\n\nexports[`returning a composite component without calling getRootProps results in an error 1`] = `downshift: If you return a non-DOM element, you must apply the getRootProps function`;\n"
  },
  {
    "path": "src/__tests__/__snapshots__/downshift.misc.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`expect console.warn to fire—depending on process.env.NODE_ENV value it should warn exactly one time when value !== production 1`] = `\n[\n  downshift: An object was passed to the default implementation of \\`itemToString\\`. You should probably provide your own \\`itemToString\\` implementation. Please refer to the \\`itemToString\\` API documentation.,\n  The object that was passed:,\n  {\n    label: test,\n    value: any,\n  },\n]\n`;\n\nexports[`warns when controlled component becomes uncontrolled 1`] = `\n[\n  [\n    downshift: A component has changed the controlled prop \"selectedItem\" to be uncontrolled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props,\n  ],\n]\n`;\n\nexports[`warns when uncontrolled component becomes controlled 1`] = `\n[\n  [\n    downshift: A component has changed the uncontrolled prop \"selectedItem\" to be controlled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props,\n  ],\n]\n`;\n"
  },
  {
    "path": "src/__tests__/__snapshots__/set-a11y-status.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`creates new status div if there is none 1`] = `\n[\n  [\n    id,\n    a11y-status-message,\n  ],\n  [\n    role,\n    status,\n  ],\n  [\n    aria-live,\n    polite,\n  ],\n  [\n    aria-relevant,\n    additions text,\n  ],\n]\n`;\n\nexports[`escapes HTML 1`] = `\n<div\n  aria-live=polite\n  aria-relevant=additions text\n  id=a11y-status-message\n  role=status\n  style=border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;\n>\n  &lt;script&gt;alert(\"!!!\")&lt;/script&gt;\n</div>\n`;\n\nexports[`performs cleanup after a timeout 1`] = `\n<div\n  aria-live=polite\n  aria-relevant=additions text\n  id=a11y-status-message\n  role=status\n  style=border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;\n/>\n`;\n\nexports[`replaces the status with a different one 1`] = `\n<div\n  aria-live=polite\n  aria-relevant=additions text\n  id=a11y-status-message\n  role=status\n  style=border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;\n>\n  goodbye\n</div>\n`;\n\nexports[`sets the status 1`] = `\n<div\n  aria-live=polite\n  aria-relevant=additions text\n  id=a11y-status-message\n  role=status\n  style=border: 0px; height: 1px; margin: -1px; overflow: hidden; padding: 0px; position: absolute; width: 1px;\n>\n  hello\n</div>\n`;\n"
  },
  {
    "path": "src/__tests__/downshift.aria.js",
    "content": "import * as React from 'react'\nimport {render, screen} from '@testing-library/react'\nimport Downshift from '../'\nimport {resetIdCounter} from '../utils-ts'\n\nbeforeEach(() => {\n  if (!('useId' in React)) resetIdCounter()\n})\n\ntest('basic snapshot', () => {\n  const {container} = renderDownshift({props: {selectedItem: 'item'}})\n  expect(container.firstChild).toMatchSnapshot()\n})\n\ntest('can override the ids', () => {\n  const {container} = renderDownshift({\n    props: {\n      inputId: 'custom-input-id',\n      labelId: 'custom-label-id',\n      menuId: 'custom-menu-id',\n      getItemId: index => `custom-item-id-${index}`,\n    },\n  })\n  expect(container.firstChild).toMatchSnapshot()\n})\n\ntest('if aria-label is provided to the menu then aria-labelledby is not applied to the menu', () => {\n  const customLabel = 'custom menu label'\n  const {menu} = renderDownshift({\n    menuProps: {'aria-label': customLabel},\n  })\n  expect(menu).not.toHaveAttribute('aria-labelledby')\n  expect(menu).toHaveAttribute('aria-label', customLabel)\n})\n\ntest('if aria-label is provided to the input then aria-labelledby is not applied to the input', () => {\n  const customLabel = 'custom menu label'\n  const {input} = renderDownshift({\n    inputProps: {'aria-label': customLabel},\n  })\n  expect(input).not.toHaveAttribute('aria-labelledby')\n  expect(input).toHaveAttribute('aria-label', customLabel)\n})\n\nfunction renderDownshift({renderFn, props, menuProps, inputProps} = {}) {\n  function defaultRenderFn({\n    getInputProps,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getItemProps,\n  }) {\n    return (\n      <div>\n        <label data-testid=\"label\" {...getLabelProps()}>\n          label\n        </label>\n        <input data-testid=\"input\" {...getInputProps(inputProps)} />\n        <button data-testid=\"button\" {...getToggleButtonProps()} />\n        <ul data-testid=\"menu\" {...getMenuProps(menuProps)}>\n          <li data-testid=\"item-0\" {...getItemProps({item: 'item', index: 0})}>\n            item\n          </li>\n        </ul>\n      </div>\n    )\n  }\n\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return renderFn || defaultRenderFn(controllerArg)\n  })\n  const utils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  return {\n    ...utils,\n    renderArg,\n    root: screen.queryByTestId('root'),\n    input: screen.queryByTestId('input'),\n    menu: screen.queryByTestId('menu'),\n  }\n}\n"
  },
  {
    "path": "src/__tests__/downshift.focus-restoration.js",
    "content": "import * as React from 'react'\nimport {render, screen} from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\nimport Downshift from '../'\n\ntest('focus restored upon item mouse click', () => {\n  renderDownshift(['A', 'B'])\n  const inputNode = screen.getByRole(`textbox`)\n  const buttonNode = screen.getByRole('button')\n  const item = screen.queryByTestId('A')\n\n  expect(document.activeElement.nodeName).toEqual('BODY')\n\n  inputNode.focus()\n  expect(inputNode).toHaveFocus()\n\n  userEvent.click(item)\n  expect(inputNode).toHaveFocus()\n\n  buttonNode.focus()\n  expect(buttonNode).toHaveFocus()\n\n  userEvent.click(item)\n  expect(buttonNode).toHaveFocus()\n})\n\nfunction renderDownshift(items) {\n  const id = 'languages[0].name'\n\n  return render(\n    <Downshift id={id}>\n      {({getInputProps, getItemProps, getToggleButtonProps}) => (\n        <div>\n          <input {...getInputProps()} />\n          <button {...getToggleButtonProps()} />\n          <div>\n            {items.map(item => (\n              <div data-testid={item} key={item} {...getItemProps({item})}>\n                {item}\n              </div>\n            ))}\n          </div>\n        </div>\n      )}\n    </Downshift>,\n  )\n}\n"
  },
  {
    "path": "src/__tests__/downshift.get-button-props.js",
    "content": "import * as React from 'react'\nimport {render, fireEvent, act} from '@testing-library/react'\nimport Downshift from '../'\n\njest.useFakeTimers()\n\ntest('space on button opens and closes the menu', () => {\n  const {button, childrenSpy} = setup()\n  fireEvent.keyDown(button, {key: ' '})\n  fireEvent.keyUp(button, {key: ' '})\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: true}),\n  )\n  fireEvent.keyDown(button, {key: ' '})\n  fireEvent.keyUp(button, {key: ' '})\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false}),\n  )\n})\n\ntest('clicking on the button opens and closes the menu', () => {\n  const {button, childrenSpy} = setup()\n  fireEvent.click(button)\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: true}),\n  )\n  fireEvent.click(button)\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false}),\n  )\n})\n\ntest('button ignores key events it does not handle', () => {\n  const {button, childrenSpy} = setup()\n  childrenSpy.mockClear()\n  fireEvent.keyDown(button, {key: 's'})\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on button blur resets the state', () => {\n  const {button, childrenSpy} = setup()\n  fireEvent.blur(button)\n  act(() => {\n    jest.runAllTimers()\n  })\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n    }),\n  )\n})\n\ntest('on button blur does not reset the state when the mouse is down', () => {\n  const {button, childrenSpy} = setup()\n  childrenSpy.mockClear()\n  // mousedown somwhere\n  fireEvent.mouseDown(document.body)\n  fireEvent.blur(button)\n  jest.runAllTimers()\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on open it will highlight item if state has highlightedIndex', () => {\n  const highlightedIndex = 4\n  const {button, childrenSpy} = setup({props: {highlightedIndex}})\n  fireEvent.click(button)\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex}),\n  )\n})\n\ntest('getToggleButtonProps returns all given props', () => {\n  const buttonProps = {'data-foo': 'bar'}\n  const Button = jest.fn(props => <button {...props} />)\n  setup({buttonProps, Button})\n  expect(Button).toHaveBeenCalledTimes(1)\n  expect(Button).toHaveBeenCalledWith(expect.objectContaining(buttonProps), expect.anything())\n})\n\n// normally this test would be like the others where we render and then simulate a click on the\n// button to ensure that a disabled button cannot be clicked, however this is only a problem in IE11\n// so we have to get into the implementation details a little bit (unless we want to run these tests\n// in IE11... no thank you 🙅)\ntest(`getToggleButtonProps doesn't include event handlers when disabled is passed (for IE11 support)`, () => {\n  const {getToggleButtonProps} = setup()\n  const props = getToggleButtonProps({disabled: true})\n  const entry = Object.entries(props).find(\n    ([_key, value]) => typeof value === 'function',\n  )\n  // eslint-disable-next-line jest/no-conditional-in-test\n  if (entry) {\n    throw new Error(\n      `getToggleButtonProps should not have any props that are callbacks. It has ${entry[0]}.`,\n    )\n  }\n})\n\ndescribe('Expect timer to trigger on process.env.NODE_ENV !== test value', () => {\n  const originalEnv = process.env.NODE_ENV\n\n  afterEach(() => {\n    process.env.NODE_ENV = originalEnv\n  })\n\n  test('clicking on the button opens and closes the menu for test', () => {\n    process.env.NODE_ENV = 'production'\n    const {button, childrenSpy} = setup()\n    fireEvent.click(button)\n    act(() => {\n      jest.runAllTimers()\n    })\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({isOpen: true}),\n    )\n    fireEvent.click(button)\n    act(() => {\n      jest.runAllTimers()\n    })\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({isOpen: false}),\n    )\n  })\n})\n\nfunction setup({\n  buttonProps,\n  props,\n  Button = propsArg => <button {...propsArg} />,\n} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return (\n      <div>\n        {/* Added items to test toggleMenu with highlight. */}\n        <div {...controllerArg.getItemProps({item: 'test item', index: 0})} />\n        <div {...controllerArg.getItemProps({item: 'test item2', index: 1})} />\n        <Button {...controllerArg.getToggleButtonProps(buttonProps)} />\n      </div>\n    )\n  })\n  const utils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  const button = utils.container.querySelector('button')\n  return {...utils, button, childrenSpy, ...renderArg}\n}\n"
  },
  {
    "path": "src/__tests__/downshift.get-input-props.js",
    "content": "import * as React from 'react'\nimport {\n  render,\n  fireEvent,\n  screen,\n  createEvent,\n  act,\n} from '@testing-library/react'\nimport Downshift from '../'\n\njest.useFakeTimers()\n\nconst colors = [\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Palevioletred',\n  'Rebeccapurple',\n  'Navy Blue',\n]\n\ntest('manages arrow up and down behavior', () => {\n  const {arrowUpInput, arrowDownInput, childrenSpy, endOnInput, homeOnInput} =\n    renderDownshift()\n  // ↓\n  arrowDownInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: true, highlightedIndex: 0}),\n  )\n\n  // ↓\n  arrowDownInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 1}),\n  )\n\n  // ↓\n  arrowDownInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 2}),\n  )\n\n  // <Shift>↓\n  arrowDownInput({shiftKey: true})\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 7}),\n  )\n\n  // ↑\n  arrowUpInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 6}),\n  )\n\n  // ↑\n  arrowUpInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 5}),\n  )\n\n  // <Shift>↑\n  arrowUpInput({shiftKey: true})\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 0}),\n  )\n\n  // ↑\n  arrowUpInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: colors.length - 1}),\n  )\n\n  // ↓\n  arrowDownInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 0}),\n  )\n\n  endOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: colors.length - 1}),\n  )\n\n  homeOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 0}),\n  )\n})\n\ndescribe('arrow down opens menu and highlights item at index', () => {\n  test('0 by default', () => {\n    const {arrowDownInput, childrenSpy} = renderDownshift()\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({isOpen: true, highlightedIndex: 0}),\n    )\n  })\n\n  test('initialHighlightedIndex + 1', () => {\n    const initialHighlightedIndex = 3\n    const {arrowDownInput, childrenSpy} = renderDownshift({\n      // provide only highlightedIndex\n      props: {initialHighlightedIndex},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: initialHighlightedIndex + 1,\n      }),\n    )\n  })\n\n  test('defaultHighlightedIndex + 1', () => {\n    const defaultHighlightedIndex = 2\n    const {arrowDownInput, childrenSpy} = renderDownshift({\n      // provide only defaultHighlightedIndex\n      props: {defaultHighlightedIndex},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: defaultHighlightedIndex + 1,\n      }),\n    )\n  })\n\n  test('initialHighlightedIndex + 1 then defaultHighlightedIndex + 1', () => {\n    const initialHighlightedIndex = 3\n    const defaultHighlightedIndex = 2\n    const {arrowDownInput, escapeOnInput, childrenSpy} = renderDownshift({\n      // provide both initialHighlightedIndex and defaultHighlightedIndex\n      props: {defaultHighlightedIndex, initialHighlightedIndex},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: initialHighlightedIndex + 1,\n      }),\n    )\n    escapeOnInput()\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: defaultHighlightedIndex + 1,\n      }),\n    )\n  })\n\n  test('0 if defaultHighlightedIndex is length - 1', () => {\n    const defaultHighlightedIndex = colors.length - 1\n    const {arrowDownInput, childrenSpy} = renderDownshift({\n      // provide defaultHighlightedIndex as last in the list.\n      props: {defaultHighlightedIndex},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: 0,\n      }),\n    )\n  })\n\n  test('0 if defaultHighlightedIndex is out of bounds', () => {\n    const {arrowDownInput, childrenSpy} = renderDownshift({\n      // provide defaultHighlightedIndex as invalid\n      props: {defaultHighlightedIndex: colors.length + 5},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: 0,\n      }),\n    )\n  })\n\n  test('highlightedIndex if controlled', () => {\n    const highlightedIndex = 2\n    const {arrowDownInput, childrenSpy} = renderDownshift({\n      // control highlightedIndex\n      props: {highlightedIndex},\n    })\n    // ↓\n    arrowDownInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex,\n      }),\n    )\n  })\n})\n\ndescribe('arrow up opens menu and highlights item at index', () => {\n  test('length - 1 by default', () => {\n    const {arrowUpInput, childrenSpy} = renderDownshift()\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: colors.length - 1,\n      }),\n    )\n  })\n\n  test('initialHighlightedIndex - 1', () => {\n    const initialHighlightedIndex = 3\n    const {arrowUpInput, childrenSpy} = renderDownshift({\n      // provide only initialHighlightedIndex\n      props: {initialHighlightedIndex},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: initialHighlightedIndex - 1,\n      }),\n    )\n  })\n\n  test('defaultHighlightedIndex - 1', () => {\n    const defaultHighlightedIndex = 2\n    const {arrowUpInput, childrenSpy} = renderDownshift({\n      // provide only defaultHighlightedIndex\n      props: {defaultHighlightedIndex},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: defaultHighlightedIndex - 1,\n      }),\n    )\n  })\n\n  test('initialHighlightedIndex - 1 then defaultHighlightedIndex - 1', () => {\n    const initialHighlightedIndex = 3\n    const defaultHighlightedIndex = 2\n    const {arrowUpInput, escapeOnInput, childrenSpy} = renderDownshift({\n      // provide both initialHighlightedIndex and defaultHighlightedIndex\n      props: {defaultHighlightedIndex, initialHighlightedIndex},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: initialHighlightedIndex - 1,\n      }),\n    )\n    escapeOnInput()\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: defaultHighlightedIndex - 1,\n      }),\n    )\n  })\n\n  test('length - 1 if defaultHighlightedIndex is 0', () => {\n    const defaultHighlightedIndex = 0\n    const {arrowUpInput, childrenSpy} = renderDownshift({\n      // provide defaultHighlightedIndex as first in the list\n      props: {defaultHighlightedIndex},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: colors.length - 1,\n      }),\n    )\n  })\n\n  test('length - 1 if defaultHighlightedIndex is out of bounds', () => {\n    const {arrowUpInput, childrenSpy} = renderDownshift({\n      // provide defaultHighlightedIndex as invalid\n      props: {defaultHighlightedIndex: colors.length + 5},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex: colors.length - 1,\n      }),\n    )\n  })\n\n  test('highlightedIndex if controlled', () => {\n    const highlightedIndex = 2\n    const {arrowUpInput, childrenSpy} = renderDownshift({\n      // control highlightedIndex\n      props: {highlightedIndex},\n    })\n    // ↑\n    arrowUpInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({\n        isOpen: true,\n        highlightedIndex,\n      }),\n    )\n  })\n})\n\ntest('navigation key down events do nothing when no items are rendered', () => {\n  const {\n    arrowDownInput,\n    arrowUpInput,\n    endOnInput,\n    homeOnInput,\n    escapeOnInput,\n    childrenSpy,\n  } = renderDownshift({items: []})\n  const keysOnInput = [arrowDownInput, arrowUpInput, endOnInput, homeOnInput]\n  // ↓ ↑ end home\n  keysOnInput.forEach(keyOnInput => {\n    keyOnInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({highlightedIndex: null}),\n    )\n    escapeOnInput() // close dropdown after each opening.\n  })\n  // ↓ ↑ end home\n  keysOnInput.forEach(keyOnInput => {\n    keyOnInput()\n    expect(childrenSpy).toHaveBeenLastCalledWith(\n      expect.objectContaining({highlightedIndex: null}),\n    )\n    // do not close dropdown, but still there should be no update.\n  })\n})\n\ntest('home and end keys should not call highlighting method when menu is closed', () => {\n  const {childrenSpy, endOnInput, homeOnInput} = renderDownshift()\n  // home\n  homeOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false, highlightedIndex: null}),\n  )\n\n  // end\n  endOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false, highlightedIndex: null}),\n  )\n})\n\ntest('home and end keys should not prevent event default when menu is closed', () => {\n  const {input} = renderDownshift()\n  const homeKeyDownEvent = createEvent.keyDown(input, {key: 'Home'})\n  const endKeyDownEvent = createEvent.keyDown(input, {key: 'End'})\n  // home\n  fireEvent(input, homeKeyDownEvent)\n  expect(homeKeyDownEvent.defaultPrevented).toBe(false)\n\n  // end\n  fireEvent(input, endKeyDownEvent)\n  expect(endKeyDownEvent.defaultPrevented).toBe(false)\n})\n\ntest('home and end keys should prevent event default when menu is open', () => {\n  const {input} = renderDownshift({props: {defaultIsOpen: true}})\n  const homeKeyDownEvent = createEvent.keyDown(input, {key: 'Home'})\n  const endKeyDownEvent = createEvent.keyDown(input, {key: 'End'})\n  // home\n  fireEvent(input, homeKeyDownEvent)\n  expect(homeKeyDownEvent.defaultPrevented).toBe(true)\n\n  // end\n  fireEvent(input, endKeyDownEvent)\n  expect(endKeyDownEvent.defaultPrevented).toBe(true)\n})\n\ntest('enter on an input with a closed menu does nothing', () => {\n  const {enterOnInput, childrenSpy} = renderDownshift()\n  childrenSpy.mockClear()\n  // ENTER\n  enterOnInput()\n  // does not even rerender\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('enter on an input with an open menu does nothing without a highlightedIndex', () => {\n  const {enterOnInput, childrenSpy} = renderDownshift({props: {isOpen: true}})\n  childrenSpy.mockClear()\n  // ENTER\n  enterOnInput()\n  // does not even rerender\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('enter on an input with an open menu and a highlightedIndex but with IME composing will not select that item', () => {\n  const {enterOnInput, childrenSpy} = renderDownshift({\n    props: {initialIsOpen: true, initialHighlightedIndex: 0},\n  })\n  const extraEventProps = {keyCode: 229}\n  childrenSpy.mockClear()\n\n  // Enter but for IME\n  enterOnInput(extraEventProps)\n\n  // does not even rerender\n  expect(childrenSpy).not.toHaveBeenCalled()\n\n  // Enter without IME\n  enterOnInput()\n\n  // now it behaves normally\n  expect(childrenSpy).toHaveBeenCalledTimes(1)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      selectedItem: colors[0],\n      inputValue: colors[0],\n      isOpen: false,\n      highlightedIndex: null,\n    }),\n  )\n})\n\ntest('enter on an input with an open menu and a highlightedIndex selects that item', () => {\n  const onChange = jest.fn()\n  const isOpen = true\n  const {arrowDownInput, enterOnInput, childrenSpy} = renderDownshift({\n    props: {isOpen, onChange},\n  })\n  // ↓\n  arrowDownInput()\n  // ENTER\n  enterOnInput()\n  expect(onChange).toHaveBeenCalledTimes(1)\n  const newState = expect.objectContaining({\n    selectedItem: colors[0],\n    isOpen,\n    highlightedIndex: null,\n    inputValue: colors[0],\n  })\n  expect(onChange).toHaveBeenCalledWith(colors[0], newState)\n  expect(childrenSpy).toHaveBeenLastCalledWith(newState)\n})\n\ntest('escape on an input without a selection should reset downshift and close the menu', () => {\n  const {changeInputValue, input, escapeOnInput, childrenSpy} =\n    renderDownshift()\n  changeInputValue('p')\n  escapeOnInput()\n  expect(input).toHaveValue('')\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      selectedItem: null,\n      inputValue: '',\n    }),\n  )\n})\n\ntest('escape on an input with a selection and open should only reset downshift', () => {\n  const {escapeOnInput, childrenSpy} = renderDownshift()\n  escapeOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false}),\n  )\n})\n\ntest('escape on an input with a selection and closed menu should reset downshift, clear input and close the menu', () => {\n  const {escapeOnInput, childrenSpy} = setupDownshiftWithState()\n  escapeOnInput()\n  escapeOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      inputValue: '',\n      selectedItem: null,\n    }),\n  )\n})\n\ntest('on input blur resets the state', () => {\n  const {blurOnInput, childrenSpy, items} = setupDownshiftWithState()\n  blurOnInput()\n  act(() => {\n    jest.runAllTimers()\n  })\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n      inputValue: items[0],\n      selectedItem: items[0],\n    }),\n  )\n})\n\ntest('on input blur does not reset the state when the mouse is down', () => {\n  const {blurOnInput, childrenSpy} = setupDownshiftWithState()\n  // mousedown somwhere\n  fireEvent.mouseDown(document.body)\n  blurOnInput()\n  jest.runAllTimers()\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on input blur does not reset the state when new focus is on downshift button', () => {\n  const {blurOnInput, childrenSpy, button} = setupDownshiftWithState()\n  blurOnInput()\n  button.focus()\n  jest.runAllTimers()\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on toggle button blur does not reset the state when there is no environment', () => {\n  const items = ['animal', 'bug', 'cat']\n  const utils = renderDownshift({items, props: {environment: null}})\n  const {childrenSpy, changeInputValue, arrowDownInput, enterOnInput, button} =\n    utils\n  changeInputValue('a')\n  // ↓\n  arrowDownInput()\n  // ENTER to select the first one\n  enterOnInput()\n\n  childrenSpy.mockReset()\n  button.focus()\n  button.blur()\n  act(() => {\n    jest.runAllTimers()\n  })\n\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on toggle button blur does not reset the state when input gets focused', () => {\n  const items = ['animal', 'bug', 'cat']\n  const utils = renderDownshift({\n    items,\n  })\n  const {childrenSpy, changeInputValue, arrowDownInput, enterOnInput, button, input} =\n    utils\n  changeInputValue('a')\n  // ↓\n  arrowDownInput()\n  // ENTER to select the first one\n  enterOnInput()\n\n  childrenSpy.mockReset()\n  button.focus()\n  button.blur()\n  input.focus()\n  act(() => {\n    jest.runAllTimers()\n  })\n\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('keydown of things that are not handled do nothing', () => {\n  const modifiers = [undefined, 'Shift']\n  const {input, childrenSpy} = renderDownshift()\n  childrenSpy.mockClear()\n  modifiers.forEach(key => {\n    fireEvent.keyDown(input, {key})\n  })\n  // does not even rerender\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('highlightedIndex uses the given itemCount prop to determine the last index', () => {\n  const itemCount = 200\n  const {arrowUpInput, childrenSpy} = renderDownshift({\n    props: {itemCount, isOpen: true},\n  })\n  // ↑\n  arrowUpInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: itemCount - 1}),\n  )\n})\n\ntest('itemCount can be set and unset asynchronously', () => {\n  let downshift\n  const childrenSpy = jest.fn(d => {\n    downshift = d\n    return (\n      <div>\n        <input {...d.getInputProps({'data-testid': 'input'})} />\n      </div>\n    )\n  })\n  render(\n    <Downshift isOpen={true} itemCount={10}>\n      {childrenSpy}\n    </Downshift>,\n  )\n  const input = screen.queryByTestId('input')\n  const up = () => fireEvent.keyDown(input, {key: 'ArrowUp'})\n  const down = () => fireEvent.keyDown(input, {key: 'ArrowDown'})\n\n  downshift.setItemCount(100)\n  up()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 99}),\n  )\n  down()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 0}),\n  )\n  downshift.setItemCount(40)\n  up()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 39}),\n  )\n  down()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 0}),\n  )\n  downshift.unsetItemCount()\n  up()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({highlightedIndex: 9}),\n  )\n})\n\ntest('Enter when there is no item at index 0 still selects the highlighted item', () => {\n  // test inspired by https://github.com/downshift-js/downshift/issues/119\n  // use case is virtualized lists\n  const items = [\n    {value: 'cat', index: 1},\n    {value: 'dog', index: 2},\n    {value: 'bird', index: 3},\n    {value: 'cheetah', index: 4},\n  ]\n  const {arrowDownInput, enterOnInput, childrenSpy} = renderDownshift({\n    items,\n    props: {\n      itemToString: i => i.value,\n      defaultHighlightedIndex: 1,\n      isOpen: true,\n    },\n  })\n\n  // ↓\n  arrowDownInput()\n  // ENTER\n  childrenSpy.mockClear()\n  enterOnInput()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      selectedItem: items[1],\n    }),\n  )\n})\n\n// normally this test would be like the others where we render and then simulate a click on the\n// button to ensure that a disabled input cannot be interacted with, however this is only a problem in IE11\n// so we have to get into the implementation details a little bit (unless we want to run these tests\n// in IE11... no thank you 🙅)\ntest(`getInputProps doesn't include event handlers when disabled is passed (for IE11 support)`, () => {\n  const {getInputProps} = setupWithDownshiftController()\n  const props = getInputProps({disabled: true})\n  const entry = Object.entries(props).find(\n    ([_key, value]) => typeof value === 'function',\n  )\n  // eslint-disable-next-line jest/no-conditional-in-test\n  if (entry) {\n    throw new Error(\n      `getInputProps should not have any props that are callbacks. It has ${entry[0]}.`,\n    )\n  }\n})\n\ntest('highlightedIndex is reset to defaultHighlightedIndex when inputValue changes', () => {\n  const defaultHighlightedIndex = 0\n  const {childrenSpy, arrowDownInput, changeInputValue} = renderDownshift({\n    props: {defaultHighlightedIndex},\n  })\n\n  childrenSpy.mockClear()\n  arrowDownInput() // highlightedIndex = 1\n  changeInputValue('r')\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      highlightedIndex: defaultHighlightedIndex,\n    }),\n  )\n})\n\ntest('highlight should be removed on inputValue change if defaultHighlightedIndex is not provided', () => {\n  const {childrenSpy, arrowDownInput, changeInputValue} = renderDownshift()\n\n  childrenSpy.mockClear()\n  arrowDownInput() // highlightedIndex = 1\n  changeInputValue('r')\n\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({\n      highlightedIndex: null,\n    }),\n  )\n})\n\nfunction setupDownshiftWithState() {\n  const items = ['animal', 'bug', 'cat']\n  const utils = renderDownshift({items})\n  const {input, changeInputValue, arrowDownInput, enterOnInput, childrenSpy} =\n    utils\n  // input.fireEvent('keydown')\n  changeInputValue('a')\n  // ↓\n  arrowDownInput()\n  // ENTER to select the first one\n  enterOnInput()\n  expect(input).toHaveValue(items[0])\n  // ↓\n  arrowDownInput()\n  changeInputValue('bu')\n  childrenSpy.mockClear()\n  return {childrenSpy, items, ...utils}\n}\n\nfunction setup({items = colors} = {}) {\n  /* eslint-disable react/jsx-closing-bracket-location */\n  const childrenSpy = jest.fn(\n    ({isOpen, getInputProps, getToggleButtonProps, getItemProps}) => (\n      <div>\n        <input {...getInputProps({'data-testid': 'input'})} />\n        <button {...getToggleButtonProps({'data-testid': 'button'})} />\n        {isOpen ? (\n          <div>\n            {items.map((item, index) => (\n              <div\n                key={item.index || index}\n                {...getItemProps({item, index: item.index || index})}\n              >\n                {item.value ? item.value : item}\n              </div>\n            ))}\n          </div>\n        ) : null}\n      </div>\n    ),\n  )\n\n  function BasicDownshift(props) {\n    return <Downshift {...props}>{childrenSpy}</Downshift>\n  }\n  return {\n    Component: BasicDownshift,\n    childrenSpy,\n  }\n}\n\nfunction renderDownshift({items, props} = {}) {\n  const {Component, childrenSpy} = setup({items})\n  const utils = render(<Component {...props} />)\n  const input = screen.queryByTestId('input')\n  return {\n    Component,\n    childrenSpy,\n    ...utils,\n    input,\n    button: screen.queryByTestId('button'),\n    arrowDownInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'ArrowDown', ...extraEventProps}),\n    arrowUpInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'ArrowUp', ...extraEventProps}),\n    endOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'End', ...extraEventProps}),\n    escapeOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'Escape', ...extraEventProps}),\n    enterOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'Enter', ...extraEventProps}),\n    homeOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'Home', ...extraEventProps}),\n    changeInputValue: (value, extraEventProps) =>\n      fireEvent.change(input, {target: {value}, ...extraEventProps}),\n    blurOnInput: extraEventProps => fireEvent.blur(input, extraEventProps),\n  }\n}\n\nfunction setupWithDownshiftController() {\n  let renderArg\n  render(\n    <Downshift>\n      {controllerArg => {\n        renderArg = controllerArg\n        return null\n      }}\n    </Downshift>,\n  )\n  return renderArg\n}\n"
  },
  {
    "path": "src/__tests__/downshift.get-item-props.js",
    "content": "import * as React from 'react'\nimport {render, fireEvent, screen} from '@testing-library/react'\nimport Downshift from '../'\nimport {setIdCounter} from '../utils-ts'\n\nbeforeEach(() => {\n  setIdCounter(1)\n  jest.spyOn(console, 'error').mockImplementation(() => {})\n})\n\nafterEach(() => console.error.mockRestore())\n\ntest('clicking on a DOM node within an item selects that item', () => {\n  // inspiration: https://github.com/downshift-js/downshift/issues/113\n  const items = [{item: 'Chess'}, {item: 'Dominion'}, {item: 'Checkers'}]\n  const {childrenSpy} = renderDownshift({items})\n  const firstButton = screen.queryByTestId('item-0-button')\n  childrenSpy.mockClear()\n  fireEvent.click(firstButton)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      selectedItem: items[0].item,\n    }),\n  )\n})\n\ntest('clicking anywhere within the rendered downshift but outside an item does not select an item', () => {\n  const childrenSpy = jest.fn(() => (\n    <div>\n      <button />\n    </div>\n  ))\n  const {container} = render(<Downshift>{childrenSpy}</Downshift>)\n  childrenSpy.mockClear()\n  fireEvent.click(container.querySelector('button'))\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on mousemove of an item updates the highlightedIndex to that item', () => {\n  const {childrenSpy} = renderDownshift()\n  const thirdButton = screen.queryByTestId('item-2-button')\n  childrenSpy.mockClear()\n  fireEvent.mouseMove(thirdButton)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      highlightedIndex: 2,\n    }),\n  )\n})\n\ntest('on mousemove of the highlighted item should not emit changes', () => {\n  const {childrenSpy} = renderDownshift({\n    props: {defaultHighlightedIndex: 1},\n  })\n  const secondButton = screen.queryByTestId('item-1-button')\n  childrenSpy.mockClear()\n  fireEvent.mouseMove(secondButton)\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('on mousedown of the item should not change current focused element', () => {\n  const childrenSpy = jest.fn(({getItemProps}) => (\n    <div>\n      <button data-testid=\"external-button\" />\n      <div {...getItemProps({item: 'item-0'})}>\n        <button data-testid=\"in-item-button\" />\n      </div>\n    </div>\n  ))\n  render(<Downshift>{childrenSpy}</Downshift>)\n  const externalButton = screen.queryByTestId('external-button')\n  const inItemButton = screen.queryByTestId('in-item-button')\n  childrenSpy.mockClear()\n\n  externalButton.focus()\n  expect(externalButton).toHaveFocus()\n  fireEvent.mouseDown(inItemButton)\n  expect(externalButton).toHaveFocus()\n})\n\ntest('after selecting an item highlightedIndex should be reset to defaultHighlightIndex', () => {\n  const {childrenSpy} = renderDownshift({\n    props: {defaultHighlightedIndex: 1},\n  })\n  const firstButton = screen.queryByTestId('item-0-button')\n  childrenSpy.mockClear()\n  fireEvent.click(firstButton)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      highlightedIndex: 1,\n    }),\n  )\n})\n\ntest('getItemProps logs a helpful error when no object is given', () => {\n  render(\n    <Downshift>\n      {({getItemProps}) => (\n        <div>\n          <span {...getItemProps()} />\n        </div>\n      )}\n    </Downshift>,\n  )\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\ntest('getItemProps defaults the index when no index is given', () => {\n  expect(\n    render(\n      <Downshift>\n        {({getItemProps}) => (\n          <div>\n            <span {...getItemProps({item: 0})}>0</span>\n            <span {...getItemProps({item: 1})}>1</span>\n            <span {...getItemProps({item: 2})}>2</span>\n            <span {...getItemProps({item: 3})}>3</span>\n            <span {...getItemProps({item: 4})}>4</span>\n          </div>\n        )}\n      </Downshift>,\n    ).container.firstChild,\n  ).toMatchSnapshot()\n})\n\ntest('getItemProps logs error when no item is given', () => {\n  render(\n    <Downshift>\n      {({getItemProps}) => (\n        <div>\n          <span {...getItemProps({index: 0})} />\n        </div>\n      )}\n    </Downshift>,\n  )\n\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\n// normally this test would be like the others where we render and then simulate a click on an\n// item to ensure that a disabled item cannot be clicked, however this is only a problem in IE11\n// so we have to get into the implementation details a little bit (unless we want to run these tests\n// in IE11... no thank you 🙅)\ntest(`getItemProps doesn't include event handlers when disabled is passed (for IE11 support)`, () => {\n  const {getItemProps} = setupWithDownshiftController()\n  const props = getItemProps({item: 'dog', disabled: true})\n  const entry = Object.entries(props).find(\n    // eslint-disable-next-line jest/no-conditional-in-test\n    ([key, value]) => key !== 'onMouseDown' && typeof value === 'function',\n  )\n  // eslint-disable-next-line jest/no-conditional-in-test\n  if (entry) {\n    throw new Error(\n      `getItemProps should not have any props that are callbacks. It has ${entry[0]}.`,\n    )\n  }\n})\n\ntest(`disabled item can't be selected by pressing enter`, () => {\n  const items = [\n    {item: 'Chess', disabled: true},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers', disabled: true},\n  ]\n  const utils = renderDownshift({items})\n  const {input, arrowDownInput, enterOnInput, changeInputValue} = utils\n\n  const firstItem = screen.queryByTestId('item-0')\n  // eslint-disable-next-line jest-dom/prefer-enabled-disabled\n  expect(firstItem).toHaveAttribute('disabled')\n\n  changeInputValue('c')\n  // ↓\n  arrowDownInput()\n  // ENTER to select the first one\n  enterOnInput()\n  // item was not selected -> input value should still be 'c'\n  expect(input).toHaveValue('c')\n})\n\ntest(`disabled item can't be highlighted when navigating via keyDown`, () => {\n  const items = [\n    {item: 'Chess'},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers'},\n    {item: 'Backgammon'},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 0}})\n  const {input, arrowDownInput, enterOnInput} = utils\n\n  // ↓\n  arrowDownInput()\n  // ↓ (should skip the first and second option)\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Checkers')\n})\n\ntest(`disabled item can't be highlighted and may wrap when navigating via keyDown`, () => {\n  const items = [\n    {item: 'Chess'},\n    {item: 'Dominion'},\n    {item: 'Checkers', disabled: true},\n    {item: 'Backgammon', disabled: true},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 1}})\n  const {input, arrowDownInput, enterOnInput} = utils\n\n  // ↓\n  arrowDownInput()\n  // ↓ (should skip the first and second option)\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Chess')\n})\n\ntest(`disabled item can't be highlighted when navigating via keyUp`, () => {\n  const items = [\n    {item: 'Chess'},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers'},\n    {item: 'Backgammon'},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 2}})\n  const {input, arrowUpInput, enterOnInput} = utils\n\n  // ↑\n  arrowUpInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Chess')\n})\n\ntest(`disabled item can't be highlighted and it may wrap when navigating via keyUp`, () => {\n  const items = [\n    {item: 'Chess', disabled: true},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers'},\n    {item: 'Backgammon'},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 2}})\n  const {input, arrowUpInput, enterOnInput} = utils\n\n  // ↑\n  arrowUpInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Backgammon')\n})\n\ntest(`disabled item can't be highlighted when navigating via end`, () => {\n  const items = [\n    {item: 'Backgammon'},\n    {item: 'Chess'},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers', disabled: true},\n  ]\n  const utils = renderDownshift({items})\n  const {input, endOnInput, enterOnInput} = utils\n\n  // end\n  endOnInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Chess')\n})\n\ntest(`disabled item can't be highlighted when navigating via home`, () => {\n  const items = [\n    {item: 'Chess', disabled: true},\n    {item: 'Dominion', disabled: true},\n    {item: 'Checkers'},\n    {item: 'Backgammon'},\n  ]\n  const utils = renderDownshift({items})\n  const {input, homeOnInput, enterOnInput} = utils\n\n  // home\n  homeOnInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Checkers')\n})\n\ntest(`highlight wrapping works with disabled items upwards`, () => {\n  const items = [\n    {item: 'Chess', disabled: true},\n    {item: 'Dominion'},\n    {item: 'Checkers'},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 1}})\n  const {input, arrowUpInput, enterOnInput} = utils\n\n  // ↑\n  arrowUpInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Checkers')\n})\n\ntest(`highlight wrapping works with disabled items downwards`, () => {\n  const items = [\n    {item: 'Chess'},\n    {item: 'Dominion'},\n    {item: 'Checkers', disabled: true},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 1}})\n  const {input, arrowDownInput, enterOnInput} = utils\n\n  // ↓\n  arrowDownInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Chess')\n})\n\ntest('cannot check if node is disabled without environment', () => {\n  const items = [\n    {item: 'Chess', disabled: true},\n  ]\n  const utils = renderDownshift({items, props: {initialHighlightedIndex: 1, environment: null}})\n  const {input, arrowDownInput, enterOnInput} = utils\n\n  // ↓\n  arrowDownInput()\n  // ENTER to select\n  enterOnInput()\n\n  expect(input).toHaveValue('Chess')\n})\n\nfunction renderDownshift({\n  items = [{item: 'Chess'}, {item: 'Dominion'}, {item: 'Checkers'}],\n  props,\n} = {}) {\n  const childrenSpy = jest.fn(({getItemProps, getInputProps}) => (\n    <div>\n      <input {...getInputProps({'data-testid': 'input'})} />\n      {items.map((item, index) => (\n        <div\n          {...getItemProps({\n            ...item,\n            index,\n          })}\n          key={index}\n          data-testid={`item-${index}`}\n        >\n          <button data-testid={`item-${index}-button`}>{item.item}</button>\n        </div>\n      ))}\n    </div>\n  ))\n  const utils = render(\n    <Downshift\n      isOpen={true}\n      onChange={() => {}}\n      children={childrenSpy}\n      {...props}\n    />,\n  )\n  const input = screen.queryByTestId('input')\n  return {\n    ...utils,\n    childrenSpy,\n    input,\n    homeOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'Home', ...extraEventProps}),\n    endOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'End', ...extraEventProps}),\n    arrowDownInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'ArrowDown', ...extraEventProps}),\n    arrowUpInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'ArrowUp', ...extraEventProps}),\n    enterOnInput: extraEventProps =>\n      fireEvent.keyDown(input, {key: 'Enter', ...extraEventProps}),\n    changeInputValue: (value, extraEventProps) => {\n      fireEvent.change(input, {target: {value}, ...extraEventProps})\n    },\n  }\n}\n\nfunction setupWithDownshiftController() {\n  let renderArg\n  render(\n    <Downshift>\n      {controllerArg => {\n        renderArg = controllerArg\n        return null\n      }}\n    </Downshift>,\n  )\n  return renderArg\n}\n"
  },
  {
    "path": "src/__tests__/downshift.get-label-props.js",
    "content": "import * as React from 'react'\nimport {render, screen} from '@testing-library/react'\nimport Downshift from '../'\n\nbeforeEach(() => jest.spyOn(console, 'error').mockImplementation(() => {}))\nafterEach(() => console.error.mockRestore())\n\ntest('label \"for\" attribute is set to the input \"id\" attribute', () => {\n  const {label, input} = renderDownshift()\n  expect(label).toHaveAttribute('for', input.getAttribute('id'))\n})\n\ntest('when the inputId prop is set, the label for is set to it', () => {\n  const id = 'foo'\n  const {label, input} = renderDownshift({\n    props: {inputId: id},\n  })\n  expect(label).toHaveAttribute('for', input.getAttribute('id'))\n  expect(label).toHaveAttribute('for', id)\n})\n\nfunction renderDownshift({props} = {}) {\n  const utils = render(<BasicDownshift {...props} />)\n  return {\n    ...utils,\n    input: screen.queryByTestId('input'),\n    label: screen.queryByTestId('label'),\n  }\n}\n\nfunction BasicDownshift({\n  inputProps,\n  labelProps,\n  getLabelPropsFirst = false,\n  ...rest\n}) {\n  return (\n    <Downshift {...rest}>\n      {({getInputProps, getLabelProps}) => {\n        if (getLabelPropsFirst) {\n          labelProps = getLabelProps(labelProps)\n          inputProps = getInputProps(inputProps)\n        } else {\n          inputProps = getInputProps(inputProps)\n          labelProps = getLabelProps(labelProps)\n        }\n        return (\n          <div>\n            <input data-testid=\"input\" {...inputProps} />\n            <label data-testid=\"label\" {...labelProps} />\n          </div>\n        )\n      }}\n    </Downshift>\n  )\n}\n"
  },
  {
    "path": "src/__tests__/downshift.get-menu-props.js",
    "content": "import * as React from 'react'\nimport {render} from '@testing-library/react'\nimport Downshift from '../'\n\nbeforeEach(() => jest.spyOn(console, 'error').mockImplementation(() => {}))\nafterEach(() => console.error.mockRestore())\n\nconst Menu = ({innerRef, ...rest}) => <div ref={innerRef} {...rest} />\nconst RefMenu = React.forwardRef((props, ref) => <div ref={ref} {...props} />)\n\ntest('using a composite component and calling getMenuProps without a refKey results in an error', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => (\n        <div>\n          <Menu {...getMenuProps()} />\n        </div>\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls[1][0]).toMatchSnapshot()\n})\n\ntest('not applying the ref prop results in an error', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => {\n        getMenuProps()\n        return (\n          <div>\n            <ul />\n          </div>\n        )\n      }}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\ntest('renders fine when rendering a composite component and applying getMenuProps properly', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => (\n        <div>\n          <Menu {...getMenuProps({refKey: 'innerRef'})} />\n        </div>\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('using a composite component and calling getMenuProps without a refKey does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => (\n        <div>\n          <Menu {...getMenuProps({}, {suppressRefError: true})} />\n        </div>\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('returning a DOM element and calling getMenuProps with a refKey does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => (\n        <div>\n          <ul {...getMenuProps({refKey: 'blah'}, {suppressRefError: true})} />\n        </div>\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(1)\n})\n\ntest('not applying the ref prop results in an error does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => {\n        getMenuProps({}, {suppressRefError: true})\n        return (\n          <div>\n            <ul />\n          </div>\n        )\n      }}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('renders fine when rendering a composite component and applying getMenuProps properly even if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getMenuProps}) => (\n        <div>\n          <Menu\n            {...getMenuProps({refKey: 'innerRef'}, {suppressRefError: true})}\n          />\n        </div>\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('has access to element when a ref function is passed to getMenuProps', () => {\n  const ref = {current: null}\n\n  const MyComponent = () => {\n    return (\n      <Downshift\n        children={({getMenuProps}) => (\n          <div>\n            <RefMenu\n              {...getMenuProps({\n                ref: e => {\n                  ref.current = e\n                },\n              })}\n            />\n          </div>\n        )}\n      />\n    )\n  }\n\n  render(<MyComponent />)\n  expect(ref.current).not.toBeNull()\n  expect(ref.current).toBeInstanceOf(HTMLDivElement)\n})\n"
  },
  {
    "path": "src/__tests__/downshift.get-root-props.js",
    "content": "import * as React from 'react'\nimport {render} from '@testing-library/react'\nimport Downshift from '../'\n\nconst MyDiv = ({innerRef, ...rest}) => <div ref={innerRef} {...rest} />\n\nconst MyDivWithForwardedRef = React.forwardRef((props, ref) => (\n  <div ref={ref} {...props} />\n))\n\nconst oldError = console.error\n\nbeforeEach(() => {\n  console.error = jest.fn()\n})\n\nafterEach(() => {\n  console.error = oldError\n})\n\ntest('no children provided renders nothing', () => {\n  const MyComponent = () => <Downshift />\n  expect(render(<MyComponent />).container).toBeEmptyDOMElement()\n})\n\ntest('returning null renders nothing', () => {\n  const MyComponent = () => <Downshift children={() => null} />\n  expect(render(<MyComponent />).container).toBeEmptyDOMElement()\n})\n\ntest('returning a composite component without calling getRootProps results in an error', () => {\n  const MyComponent = () => <Downshift children={() => <MyDiv />} />\n  expect(() => render(<MyComponent />)).toThrowErrorMatchingSnapshot()\n})\n\ntest('returning a composite component and calling getRootProps without a refKey results in an error', () => {\n  const MyComponent = () => (\n    <Downshift children={({getRootProps}) => <MyDiv {...getRootProps()} />} />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\ntest('returning a DOM element and calling getRootProps with a refKey results in an error', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => <div {...getRootProps({refKey: 'blah'})} />}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\ntest('not applying the ref prop results in an error', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => {\n        const {onClick} = getRootProps()\n        return <div onClick={onClick} />\n      }}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls[0][0]).toMatchSnapshot()\n})\n\ntest('renders fine when rendering a composite component and applying getRootProps properly', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <MyDiv {...getRootProps({refKey: 'innerRef'})} />\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('returning a composite component and calling getRootProps without a refKey does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <MyDiv {...getRootProps({}, {suppressRefError: true})} />\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('returning a DOM element and calling getRootProps with a refKey does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <div {...getRootProps({refKey: 'blah'}, {suppressRefError: true})} />\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('not applying the ref prop results in an error does not result in an error if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => {\n        const {onClick} = getRootProps({}, {suppressRefError: true})\n        return <div onClick={onClick} />\n      }}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('renders fine when rendering a composite component and applying getRootProps properly even if suppressRefError is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <MyDiv\n          {...getRootProps({refKey: 'innerRef'}, {suppressRefError: true})}\n        />\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('renders fine when rendering a composite component and suppressRefError prop is true', () => {\n  const MyComponent = () => (\n    <Downshift\n      suppressRefError\n      children={({getRootProps}) => <MyDiv {...getRootProps()} />}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('renders fine when rendering a composite component that uses refs forwarding', () => {\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <MyDivWithForwardedRef {...getRootProps()} />\n      )}\n    />\n  )\n  render(<MyComponent />)\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('has access to element when a ref is passed to getRootProps', () => {\n  const ref = {current: null}\n\n  const MyComponent = () => (\n    <Downshift\n      children={({getRootProps}) => (\n        <MyDivWithForwardedRef\n          {...getRootProps({\n            ref: e => {\n              ref.current = e\n            },\n          })}\n        />\n      )}\n    />\n  )\n\n  render(<MyComponent />)\n  expect(ref.current).not.toBeNull()\n  expect(ref.current).toBeInstanceOf(HTMLDivElement)\n})\n"
  },
  {
    "path": "src/__tests__/downshift.lifecycle.js",
    "content": "import * as React from 'react'\n\nimport {act, fireEvent, render, screen} from '@testing-library/react'\nimport Downshift from '../'\nimport {setStatus, scrollIntoView} from '../utils-ts'\n\njest.useFakeTimers()\njest.mock('../utils-ts/scrollIntoView.ts', () => ({\n  scrollIntoView: jest.fn(),\n}))\njest.mock('../utils-ts/setA11yStatus.ts', () => ({\n  setStatus: jest.fn(),\n}))\n\nafterEach(() => {\n  scrollIntoView.mockReset()\n})\n\ntest('do not set state after unmount', () => {\n  const handleStateChange = jest.fn()\n  const childrenSpy = jest.fn(({getInputProps}) => (\n    <div>\n      <input {...getInputProps({'data-testid': 'input'})} />\n      <button {...getInputProps({'data-testid': 'button'})}>Toggle</button>\n    </div>\n  ))\n  const MyComponent = () => (\n    <Downshift onStateChange={handleStateChange}>{childrenSpy}</Downshift>\n  )\n  const {container, unmount} = render(<MyComponent />)\n  const button = screen.queryByTestId('button')\n  document.body.appendChild(container)\n\n  // blur toggle button\n  fireEvent.blur(button)\n  handleStateChange.mockClear()\n\n  // unmount\n  unmount()\n  expect(handleStateChange).toHaveBeenCalledTimes(0)\n})\n\ntest('handles mouse events properly to reset state', () => {\n  const handleStateChange = jest.fn()\n  const childrenSpy = jest.fn(({getInputProps}) => (\n    <div>\n      <input {...getInputProps({'data-testid': 'input'})} />\n    </div>\n  ))\n  const MyComponent = () => (\n    <Downshift onStateChange={handleStateChange}>{childrenSpy}</Downshift>\n  )\n  const {container, unmount} = render(<MyComponent />)\n  const input = screen.queryByTestId('input')\n  document.body.appendChild(container)\n\n  // open the menu\n  fireEvent.keyDown(input, {key: 'ArrowDown'})\n  handleStateChange.mockClear()\n\n  // mouse down and up on within the autocomplete node\n  mouseDownAndUp(input)\n  expect(handleStateChange).toHaveBeenCalledTimes(0)\n\n  // mouse down and up on outside the autocomplete node\n  mouseDownAndUp(document.body)\n  expect(handleStateChange).toHaveBeenCalledTimes(1)\n\n  childrenSpy.mockClear()\n  // does not call our state change handler when no state changes\n  mouseDownAndUp(document.body)\n  expect(handleStateChange).toHaveBeenCalledTimes(1)\n  // does not rerender when no state changes\n  expect(childrenSpy).not.toHaveBeenCalled()\n\n  // cleans up\n  unmount()\n  mouseDownAndUp(document.body)\n  expect(handleStateChange).toHaveBeenCalledTimes(1)\n})\n\ntest('handles state change for touchevent events', () => {\n  const handleStateChange = jest.fn()\n  const childrenSpy = jest.fn(({getToggleButtonProps}) => (\n    <button {...getToggleButtonProps({'data-testid': 'button'})} />\n  ))\n\n  const MyComponent = () => (\n    <Downshift onStateChange={handleStateChange}>{childrenSpy}</Downshift>\n  )\n  const {container, unmount} = render(<MyComponent />)\n  document.body.appendChild(container)\n\n  const button = screen.queryByTestId('button')\n\n  // touch outside for coverage\n  fireEvent.touchStart(document.body)\n  fireEvent.touchEnd(document.body)\n\n  // open menu\n  fireEvent.click(button)\n  jest.runAllTimers()\n\n  expect(handleStateChange).toHaveBeenCalledTimes(1)\n\n  // touchmove (scroll) outside downshift should not trigger state change\n  fireEvent.touchStart(document.body)\n  fireEvent.touchMove(document.body)\n  fireEvent.touchEnd(document.body)\n\n  jest.runAllTimers()\n  expect(handleStateChange).toHaveBeenCalledTimes(1)\n\n  // touch outside downshift\n  fireEvent.touchStart(document.body)\n  fireEvent.touchEnd(document.body)\n\n  jest.runAllTimers()\n  expect(handleStateChange).toHaveBeenCalledTimes(2)\n\n  unmount()\n})\n\ntest('props update causes the a11y status to be updated', () => {\n  setStatus.mockReset()\n  const MyComponent = () => (\n    <Downshift isOpen={false}>\n      {({getInputProps, getItemProps, isOpen}) => (\n        <div>\n          <input {...getInputProps()} />\n          {/* eslint-disable-next-line jest/no-conditional-in-test */}\n          {isOpen ? <div {...getItemProps({item: 'foo', index: 0})} /> : null}\n        </div>\n      )}\n    </Downshift>\n  )\n  const {container, unmount} = render(<MyComponent />)\n  render(<MyComponent isOpen={true} />, {container})\n  jest.runAllTimers()\n  expect(setStatus).toHaveBeenCalledTimes(1)\n  render(<MyComponent isOpen={false} />, {container})\n  unmount()\n  jest.runAllTimers()\n  expect(setStatus).toHaveBeenCalledTimes(1)\n})\n\ntest('inputValue initializes properly if the selectedItem is controlled and set', () => {\n  const childrenSpy = jest.fn(() => null)\n  render(<Downshift selectedItem=\"foo\">{childrenSpy}</Downshift>)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      inputValue: 'foo',\n    }),\n  )\n})\n\ntest('inputValue initializes properly if selectedItem is set to 0', () => {\n  const childrenSpy = jest.fn(() => null)\n  render(<Downshift selectedItem={0}>{childrenSpy}</Downshift>)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      inputValue: '0',\n    }),\n  )\n})\n\ntest('props update of selectedItem will update the inputValue state', () => {\n  const childrenSpy = jest.fn(() => null)\n  const {container} = render(\n    <Downshift selectedItem={null}>{childrenSpy}</Downshift>,\n  )\n  childrenSpy.mockClear()\n  render(<Downshift selectedItem=\"foo\">{childrenSpy}</Downshift>, {container})\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      inputValue: 'foo',\n    }),\n  )\n})\n\ntest('the callback is invoked on selected item only if it is a function', () => {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return <div />\n  })\n  const callbackSpy = jest.fn(x => x)\n  render(<Downshift selectedItem=\"foo\">{childrenSpy}</Downshift>)\n\n  childrenSpy.mockClear()\n  callbackSpy.mockClear()\n  act(() => {\n    renderArg.selectItem('foo', {}, callbackSpy)\n  })\n  expect(callbackSpy).toHaveBeenCalledTimes(1)\n  act(() => {\n    renderArg.selectItem('foo', {})\n  })\n})\n\ntest('props update of selectedItem will not update inputValue state', () => {\n  const onInputValueChangeSpy = jest.fn(() => null)\n  const initialProps = {\n    onInputValueChange: onInputValueChangeSpy,\n    selectedItemChanged: (prevItem, item) => prevItem.id !== item.id,\n    selectedItem: {id: '123', value: 'wow'},\n    // eslint-disable-next-line jest/no-conditional-in-test\n    itemToString: i => (i ? i.value : ''),\n    render: () => null,\n  }\n  const {container} = render(<Downshift {...initialProps} />)\n  onInputValueChangeSpy.mockClear()\n  render(\n    <Downshift\n      {...initialProps}\n      selectedItem={{id: '123', value: 'not wow'}}\n    />,\n    {container},\n  )\n  expect(onInputValueChangeSpy).not.toHaveBeenCalled()\n})\n\ntest('controlled highlighted index change scrolls the item into view', () => {\n  // sadly, testing scroll is really difficult in a jsdom environment.\n  // Perhaps eventually we'll add real integration tests with cypress\n  // or something, but for now we'll just mock the implementation of\n  // utils.scrollIntoView and ensure it's called with the proper arguments\n  // assuming that the test suite for utils.scrollIntoView will ensure\n  // this functionality doesn't break.\n  const oneHundredItems = Array.from({length: 100})\n  const renderFn = jest.fn(({getItemProps, getMenuProps}) => (\n    <div>\n      <div data-testid=\"menu\" {...getMenuProps()}>\n        {oneHundredItems.map((x, i) => (\n          <div key={i} {...getItemProps({item: i})} data-testid={`item-${i}`} />\n        ))}\n      </div>\n    </div>\n  ))\n  const {container, updateProps} = setup({\n    highlightedIndex: 1,\n    render: renderFn,\n  })\n  document.body.appendChild(container)\n  renderFn.mockClear()\n  updateProps({highlightedIndex: 75})\n  expect(renderFn).toHaveBeenCalledTimes(1)\n\n  expect(scrollIntoView).toHaveBeenCalledTimes(1)\n  const menuDiv = screen.queryByTestId('menu')\n  expect(scrollIntoView).toHaveBeenCalledWith(\n    screen.queryByTestId('item-75'),\n    menuDiv,\n  )\n})\n\nfunction mouseDownAndUp(node) {\n  fireEvent.mouseDown(node)\n  fireEvent.mouseUp(node)\n}\n\nfunction setup({render: renderFn = () => <div />, ...props} = {}) {\n  // eslint-disable-next-line prefer-const\n  let container, renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return renderFn(controllerArg)\n  })\n  const updateProps = newProps => {\n    return render(\n      <Downshift children={childrenSpy} {...props} {...newProps} />,\n      {\n        container,\n      },\n    )\n  }\n  const renderUtils = updateProps()\n  container = renderUtils.container\n  return {\n    childrenSpy,\n    updateProps,\n    ...renderUtils,\n    ...renderArg,\n  }\n}\n"
  },
  {
    "path": "src/__tests__/downshift.misc-with-utils-mocked.js",
    "content": "// this is stuff that I couldn't think fit anywhere else\n// but we still want to have tested.\n\nimport * as React from 'react'\nimport {render, fireEvent, screen} from '@testing-library/react'\nimport Downshift from '../'\nimport {scrollIntoView} from '../utils-ts'\n\njest.useFakeTimers()\njest.mock('../utils-ts/scrollIntoView.ts', () => ({\n  scrollIntoView: jest.fn(),\n}))\n\ntest('does not scroll from an onMouseMove event', () => {\n  class HighlightedIndexController extends React.Component {\n    state = {highlightedIndex: 10}\n    handleStateChange = changes => {\n      // eslint-disable-next-line jest/no-conditional-in-test\n      if (changes.hasOwnProperty('highlightedIndex')) {\n        this.setState({highlightedIndex: changes.highlightedIndex})\n      }\n    }\n    render() {\n      return (\n        <Downshift\n          onStateChange={this.handleStateChange}\n          highlightedIndex={this.state.highlightedIndex}\n        >\n          {({getInputProps, getItemProps}) => (\n            <div>\n              <input data-testid=\"input\" {...getInputProps()} />\n              <div {...getItemProps({item: 'hi', 'data-testid': 'item-1'})} />\n              <div {...getItemProps({item: 'hey', 'data-testid': 'item-2'})} />\n            </div>\n          )}\n        </Downshift>\n      )\n    }\n  }\n  render(<HighlightedIndexController />)\n  const input = screen.queryByTestId('input')\n  const item = screen.queryByTestId('item-2')\n  fireEvent.mouseMove(item)\n  jest.runAllTimers()\n  expect(scrollIntoView).not.toHaveBeenCalled()\n  // now let's make sure that we can still scroll items into view\n  // ↓\n  fireEvent.keyDown(input, {key: 'ArrowDown'})\n  expect(scrollIntoView).toHaveBeenCalledWith(item, undefined)\n})\n"
  },
  {
    "path": "src/__tests__/downshift.misc.js",
    "content": "// this is stuff that I couldn't think fit anywhere else\n// but we still want to have tested.\n\nimport * as React from 'react'\nimport * as ReactDOM from 'react-dom/client'\nimport {act, render} from '@testing-library/react'\nimport Downshift from '../'\n\nbeforeEach(() => jest.spyOn(console, 'error').mockImplementation(() => {}))\nafterEach(() => console.error.mockRestore())\n\ntest('closeMenu closes the menu', () => {\n  const {openMenu, closeMenu, childrenSpy} = setup()\n  openMenu()\n  closeMenu()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({isOpen: false}),\n  )\n})\n\ntest('clearSelection clears an existing selection', () => {\n  const {openMenu, selectItem, childrenSpy, clearSelection} = setup()\n  openMenu()\n  selectItem('foo')\n  clearSelection()\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({selectedItem: null}),\n  )\n})\n\ntest('selectItemAtIndex does nothing if there is no item at that index', () => {\n  const {openMenu, selectItemAtIndex, childrenSpy} = setup()\n  openMenu()\n  childrenSpy.mockClear()\n  selectItemAtIndex(100)\n  expect(childrenSpy).not.toHaveBeenCalled()\n})\n\ntest('selectItemAtIndex can select item that is an empty string', () => {\n  const items = ['Chess', '']\n  const children = ({getItemProps}) => (\n    <div>\n      {items.map((item, index) => (\n        <div key={index} {...getItemProps({item})}>\n          {item}\n        </div>\n      ))}\n    </div>\n  )\n  const {selectItemAtIndex, childrenSpy} = setup({children})\n  act(() => {\n    selectItemAtIndex(1)\n  })\n  expect(childrenSpy).toHaveBeenLastCalledWith(\n    expect.objectContaining({selectedItem: ''}),\n  )\n})\n\ntest('toggleMenu can take no arguments at all', () => {\n  const {toggleMenu, childrenSpy} = setup()\n  act(() => {\n    toggleMenu()\n  })\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: true,\n    }),\n  )\n})\n\ntest('clearItems clears all items', () => {\n  const item = 'Chess'\n\n  const children = ({getItemProps}) => (\n    <div>\n      <div key={item} {...getItemProps({item})}>\n        {item}\n      </div>\n    </div>\n  )\n\n  // Wrap Downshift to expose its instance methods through a ref\n  const DownshiftWrapper = React.forwardRef((_props, ref) => {\n    const innerRef = React.useRef(null)\n\n    React.useImperativeHandle(ref, () => innerRef.current)\n\n    return <Downshift ref={innerRef}>{children}</Downshift>\n  })\n\n  const container = document.createElement('div')\n  const root = ReactDOM.createRoot(container)\n\n  const dsRef = React.createRef()\n\n  // eslint-disable-next-line testing-library/no-unnecessary-act\n  act(() => {\n    root.render(<DownshiftWrapper ref={dsRef} />)\n  })\n\n  const downshiftInstance = dsRef.current\n\n  expect(downshiftInstance.items).toEqual([item])\n\n  act(() => {\n    downshiftInstance.clearItems()\n  })\n\n  expect(downshiftInstance.items).toEqual([])\n\n  root.unmount()\n})\n\ntest('reset can take no arguments at all', () => {\n  const {reset, childrenSpy} = setup()\n  reset()\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: false,\n    }),\n  )\n})\n\ntest('setHighlightedIndex can take no arguments at all', () => {\n  const defaultHighlightedIndex = 2\n  const {setHighlightedIndex, childrenSpy} = setup({\n    defaultHighlightedIndex,\n  })\n  setHighlightedIndex()\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      highlightedIndex: defaultHighlightedIndex,\n    }),\n  )\n})\n\ntest('can specify a custom ID which is used in item IDs (good for SSR)', () => {\n  const id = 'my-custom-id'\n  const {getItemProps} = setup({id})\n  expect(getItemProps({item: 'blah'}).id).toContain(id)\n})\n\ntest('can use children instead of render prop', () => {\n  const childrenSpy = jest.fn()\n  render(<Downshift>{childrenSpy}</Downshift>)\n  expect(childrenSpy).toHaveBeenCalledTimes(1)\n})\n\ntest('should not log error during strict mode during reset', () => {\n  const children = () => <div />\n  render(\n    <React.StrictMode>\n      <Downshift>{children}</Downshift>\n    </React.StrictMode>,\n  )\n\n  expect(console.error.mock.calls).toHaveLength(0)\n})\n\ntest('can use setState for ultimate power', () => {\n  const {childrenSpy, setState} = setup()\n  childrenSpy.mockClear()\n  act(() => {\n    setState({isOpen: true, selectedItem: 'hi'})\n  })\n  expect(childrenSpy).toHaveBeenCalledTimes(1)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({isOpen: true, selectedItem: 'hi'}),\n  )\n})\n\ntest('warns when controlled component becomes uncontrolled', () => {\n  const children = () => <div />\n  const {rerender} = render(<Downshift selectedItem=\"hi\">{children}</Downshift>)\n  rerender(<Downshift selectedItem={undefined}>{children}</Downshift>)\n  expect(console.error.mock.calls).toHaveLength(1)\n  expect(console.error.mock.calls).toMatchSnapshot()\n})\n\ntest('warns when uncontrolled component becomes controlled', () => {\n  const children = () => <div />\n  const {rerender} = render(<Downshift>{children}</Downshift>)\n  rerender(<Downshift selectedItem=\"hi\">{children}</Downshift>)\n  expect(console.error.mock.calls).toHaveLength(1)\n  expect(console.error.mock.calls).toMatchSnapshot()\n})\n\ndescribe('expect console.warn to fire—depending on process.env.NODE_ENV value', () => {\n  const originalEnv = process.env.NODE_ENV\n\n  beforeEach(() => {\n    jest.spyOn(console, 'warn').mockImplementation(() => {})\n  })\n\n  afterEach(() => {\n    process.env.NODE_ENV = originalEnv\n    console.warn.mockRestore()\n  })\n\n  test(`it shouldn't log anything when value === 'production'`, () => {\n    process.env.NODE_ENV = 'production'\n    setup({selectedItem: {label: 'test', value: 'any'}})\n\n    expect(console.warn).toHaveBeenCalledTimes(0)\n  })\n\n  test('it should warn exactly one time when value !== production', () => {\n    process.env.NODE_ENV = 'development'\n    setup({selectedItem: {label: 'test', value: 'any'}})\n\n    expect(console.warn).toHaveBeenCalledTimes(1)\n    expect(console.warn.mock.calls[0]).toMatchSnapshot()\n  })\n})\n\ntest('initializes with the initial* props', () => {\n  const initialState = {\n    initialIsOpen: true,\n    initialHighlightedIndex: 2,\n    initialInputValue: 'hey',\n    initialSelectedItem: 'sup',\n  }\n  const {childrenSpy} = setup(initialState)\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: initialState.initialIsOpen,\n      highlightedIndex: initialState.initialHighlightedIndex,\n      inputValue: initialState.initialInputValue,\n      selectedItem: initialState.initialSelectedItem,\n    }),\n  )\n})\n\nfunction setup({children = () => <div />, ...props} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return children(controllerArg)\n  })\n  const renderUtils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  return {childrenSpy, ...renderUtils, ...renderArg}\n}\n"
  },
  {
    "path": "src/__tests__/downshift.props.js",
    "content": "// this is stuff that I couldn't think fit anywhere else\n// but we still want to have tested.\n\nimport * as React from 'react'\nimport {render, fireEvent, act} from '@testing-library/react'\nimport Downshift from '../'\n\ntest('onStateChange called with changes and downshift state and helpers', () => {\n  const handleStateChange = jest.fn()\n  const controlledState = {\n    inputValue: '',\n    selectedItem: null,\n  }\n  const {selectItem} = setup({\n    ...controlledState,\n    onStateChange: handleStateChange,\n  })\n  const itemToSelect = 'foo'\n  act(() => {\n    selectItem(itemToSelect)\n  })\n  const changes = {\n    type: Downshift.stateChangeTypes.unknown,\n    inputValue: itemToSelect,\n    selectedItem: itemToSelect,\n  }\n  const stateAndHelpers = {\n    ...controlledState,\n    isOpen: false,\n    highlightedIndex: null,\n    selectItem,\n  }\n  expect(handleStateChange).toHaveBeenLastCalledWith(\n    changes,\n    expect.objectContaining(stateAndHelpers),\n  )\n})\n\ntest('onChange called when clearSelection is triggered', () => {\n  const handleChange = jest.fn()\n  const {clearSelection} = setup({\n    selectedItem: 'foo',\n    onChange: handleChange,\n  })\n  act(() => {\n    clearSelection()\n  })\n  expect(handleChange).toHaveBeenCalledTimes(1)\n  expect(handleChange).toHaveBeenCalledWith(null, expect.any(Object))\n})\n\ntest('onChange only called when the selection changes', () => {\n  const handleChange = jest.fn()\n  const {selectItem} = setup({\n    onChange: handleChange,\n  })\n  act(() => {\n    selectItem('foo')\n  })\n  expect(handleChange).toHaveBeenCalledTimes(1)\n  expect(handleChange).toHaveBeenCalledWith('foo', expect.any(Object))\n  handleChange.mockClear()\n  act(() => {\n    selectItem('foo')\n  })\n  expect(handleChange).toHaveBeenCalledTimes(0)\n})\n\ntest('onSelect called whenever selection happens, even if the item is the same', () => {\n  const handleSelect = jest.fn()\n  const {selectItem} = setup({\n    onSelect: handleSelect,\n  })\n  act(() => {\n    selectItem('foo')\n  })\n  expect(handleSelect).toHaveBeenCalledTimes(1)\n  expect(handleSelect).toHaveBeenCalledWith('foo', expect.any(Object))\n  handleSelect.mockClear()\n  act(() => {\n    selectItem('foo')\n  })\n  expect(handleSelect).toHaveBeenCalledTimes(1)\n})\n\ntest('onSelect not called when nothing was selected', () => {\n  const handleSelect = jest.fn()\n  const {openMenu} = setup({\n    onSelect: handleSelect,\n  })\n  act(() => {\n    openMenu()\n  })\n  expect(handleSelect).not.toHaveBeenCalled()\n})\n\ntest('uses given environment', () => {\n  const environment = {\n    addEventListener: jest.fn(),\n    removeEventListener: jest.fn(),\n    Node,\n    document: {\n      getElementById: jest.fn(() => document.createElement('div')),\n      createElement: jest.fn(),\n      body: {},\n      activeElement: {},\n    },\n  }\n  const {unmount, setHighlightedIndex} = setup({environment})\n  act(() => {\n    setHighlightedIndex()\n  })\n  unmount()\n  expect(environment.addEventListener).toHaveBeenCalledTimes(5)\n  expect(environment.removeEventListener).toHaveBeenCalledTimes(5)\n})\n\ntest('can override onOuterClick callback to maintain isOpen state', () => {\n  const renderFn = () => <div />\n  const onOuterClick = jest.fn()\n  const {openMenu} = setup({render: renderFn, onOuterClick})\n  act(() => {\n    openMenu()\n  })\n  mouseDownAndUp(document.body)\n  expect(onOuterClick).toHaveBeenCalledTimes(1)\n  expect(onOuterClick).toHaveBeenCalledWith(\n    expect.objectContaining({\n      // just verify that it's the controller object\n      isOpen: false,\n      getItemProps: expect.any(Function),\n    }),\n  )\n})\n\ntest('onInputValueChange called when changes contain inputValue', () => {\n  const handleInputValueChange = jest.fn()\n  const {selectItem} = setup({\n    onInputValueChange: handleInputValueChange,\n  })\n  act(() => {\n    selectItem('foo')\n  })\n  expect(handleInputValueChange).toHaveBeenCalledTimes(1)\n  expect(handleInputValueChange).toHaveBeenCalledWith('foo', expect.any(Object))\n})\n\ntest('onInputValueChange not called when changes do not contain inputValue', () => {\n  const handleInputValueChange = jest.fn()\n  const {openMenu} = setup({\n    onInputValueChange: handleInputValueChange,\n  })\n\n  act(() => {\n    openMenu()\n  })\n\n  expect(handleInputValueChange).toHaveBeenCalledTimes(0)\n})\n\ntest('onInputValueChange called with empty string on reset', () => {\n  const handleInputValueChange = jest.fn()\n  const {reset} = setup({\n    onInputValueChange: handleInputValueChange,\n  })\n  act(() => {\n    reset()\n  })\n  expect(handleInputValueChange).toHaveBeenCalledTimes(1)\n  expect(handleInputValueChange).toHaveBeenCalledWith('', expect.any(Object))\n})\n\ntest('defaultHighlightedIndex will be used for the highlighted index on reset', () => {\n  const {reset, childrenSpy} = setup({defaultHighlightedIndex: 0})\n  childrenSpy.mockClear()\n  act(() => {\n    reset()\n  })\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      highlightedIndex: 0,\n    }),\n  )\n})\n\ntest('stateReducer customizes the final state after keyDownEnter handled', () => {\n  const {childrenSpy, openMenu, selectHighlightedItem} = setup({\n    defaultHighlightedIndex: 0,\n    stateReducer: (state, stateToSet) => {\n      // eslint-disable-next-line jest/no-conditional-in-test\n      switch (stateToSet.type) {\n        case Downshift.stateChangeTypes.keyDownEnter:\n          return {\n            ...stateToSet,\n            isOpen: state.isOpen,\n            highlightedIndex: state.highlightedIndex,\n          }\n        default:\n          return stateToSet\n      }\n    },\n  })\n  childrenSpy.mockClear()\n  act(() => {\n    openMenu()\n  })\n  selectHighlightedItem({\n    type: Downshift.stateChangeTypes.keyDownEnter,\n  })\n  expect(childrenSpy).toHaveBeenCalledWith(\n    expect.objectContaining({\n      isOpen: true,\n      highlightedIndex: 0,\n    }),\n  )\n})\n\nfunction mouseDownAndUp(node) {\n  fireEvent.mouseDown(node)\n  fireEvent.mouseUp(node)\n}\n\nfunction setup({render: renderFn = () => <div />, ...props} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return renderFn(controllerArg)\n  })\n  const utils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  return {childrenSpy, ...utils, ...renderArg}\n}\n"
  },
  {
    "path": "src/__tests__/portal-support.js",
    "content": "import * as React from 'react'\nimport * as ReactDOM from 'react-dom'\nimport {render, fireEvent, screen, act} from '@testing-library/react'\nimport Downshift from '../'\n\ntest('will not reset when clicking within the menu', () => {\n  class MyMenu extends React.Component {\n    el = document.createElement('div')\n    componentDidMount() {\n      document.body.appendChild(this.el)\n    }\n    componentWillUnmount() {\n      document.body.removeChild(this.el)\n    }\n    render() {\n      return ReactDOM.createPortal(\n        <div {...this.props.getMenuProps({'data-testid': 'menu'})}>\n          <button data-testid=\"not-an-item\">I am not an item</button>\n          <button\n            {...this.props.getItemProps({\n              item: 'The item',\n              'data-testid': 'item',\n            })}\n          >\n            The item\n          </button>\n        </div>,\n        this.el,\n      )\n    }\n  }\n  function MyPortalAutocomplete() {\n    return (\n      <Downshift>\n        {({\n          getMenuProps,\n          getItemProps,\n          getToggleButtonProps,\n          isOpen,\n          selectedItem,\n        }) => (\n          <div>\n            {/* eslint-disable-next-line jest/no-conditional-in-test */}\n            {selectedItem ? (\n              <div data-testid=\"selection\">{selectedItem}</div>\n            ) : null}\n            <button {...getToggleButtonProps({'data-testid': 'button'})}>\n              Open Menu\n            </button>\n            {/* eslint-disable-next-line jest/no-conditional-in-test */}\n            {isOpen ? <MyMenu {...{getMenuProps, getItemProps}} /> : null}\n          </div>\n        )}\n      </Downshift>\n    )\n  }\n  render(<MyPortalAutocomplete />)\n  expect(screen.queryByTestId('menu')).not.toBeInTheDocument()\n  act(() => {\n    screen.getByTestId('button').click()\n  })\n  expect(screen.getByTestId('menu')).toBeInstanceOf(HTMLElement)\n\n  const notAnItem = screen.getByTestId('not-an-item')\n\n  // Mouse events\n  fireEvent.mouseDown(notAnItem)\n  notAnItem.focus() // sets document.activeElement\n  fireEvent.mouseUp(notAnItem)\n  expect(screen.getByTestId('menu')).toBeInstanceOf(HTMLElement)\n\n  // Touch events\n  fireEvent.touchStart(notAnItem)\n  fireEvent.touchEnd(notAnItem)\n  notAnItem.focus() // sets document.activeElement\n  expect(screen.getByTestId('menu')).toBeInstanceOf(HTMLElement)\n\n  act(() => {\n    screen.getByTestId('item').click()\n  })\n  expect(screen.queryByTestId('menu')).not.toBeInTheDocument()\n  expect(screen.getByTestId('selection')).toHaveTextContent('The item')\n})\n"
  },
  {
    "path": "src/__tests__/set-a11y-status.js",
    "content": "jest.useFakeTimers()\n\nbeforeEach(() => {\n  document.body.innerHTML = ''\n})\n\ntest('sets the status', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('hello', document)\n  expect(document.body.firstChild).toMatchSnapshot()\n})\n\ntest('replaces the status with a different one', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('hello', document)\n  setA11yStatus('goodbye', document)\n  expect(document.body.firstChild).toMatchSnapshot()\n})\n\ntest('does add anything for an empty string', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('', document)\n  expect(document.body).toBeEmptyDOMElement()\n})\n\ntest('escapes HTML', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('<script>alert(\"!!!\")</script>', document)\n  expect(document.body.firstChild).toMatchSnapshot()\n})\n\ntest('performs cleanup after a timeout', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('hello', document)\n  jest.runAllTimers()\n  expect(document.body.firstChild).toMatchSnapshot()\n})\n\ntest('creates new status div if there is none', () => {\n  const statusDiv = {setAttribute: jest.fn(), style: {}}\n  const document = {\n    getElementById: jest.fn(() => null),\n    createElement: jest.fn().mockReturnValue(statusDiv),\n    body: {\n      appendChild: jest.fn(),\n    },\n  }\n\n  const setA11yStatus = setup()\n  setA11yStatus('hello', document)\n\n  expect(document.getElementById).toHaveBeenCalledTimes(1)\n  expect(document.getElementById).toHaveBeenCalledWith('a11y-status-message')\n  expect(document.createElement).toHaveBeenCalledTimes(1)\n  expect(document.createElement).toHaveBeenCalledWith('div')\n  expect(statusDiv.setAttribute).toHaveBeenCalledTimes(4)\n  expect(statusDiv.setAttribute.mock.calls).toMatchSnapshot()\n  expect(statusDiv.style).toEqual({\n    border: '0',\n    clip: 'rect(0 0 0 0)',\n    height: '1px',\n    margin: '-1px',\n    overflow: 'hidden',\n    padding: '0',\n    position: 'absolute',\n    width: '1px',\n  })\n  expect(document.body.appendChild).toHaveBeenCalledTimes(1)\n  expect(document.body.appendChild).toHaveBeenCalledWith(statusDiv)\n  // eslint-disable-next-line jest-dom/prefer-to-have-text-content\n  expect(statusDiv.textContent).toEqual('hello')\n})\n\ntest('creates no status div if there is no document', () => {\n  const setA11yStatus = setup()\n  setA11yStatus('<script>alert(\"!!!\")</script>')\n  expect(document.body).toBeEmptyDOMElement()\n})\n\nfunction setup() {\n  jest.resetModules()\n  return require('../utils-ts').setStatus\n}\n"
  },
  {
    "path": "src/__tests__/utils.call-all-event-handlers.js",
    "content": "import {callAllEventHandlers} from '../utils'\n\ntest('prevent default handlers when defaultDownshiftPrevented is true', () => {\n  const customHandler = jest.fn(e => {\n    e.preventDownshiftDefault = true\n  })\n  const defaultHandler = jest.fn()\n\n  const composedHandler = callAllEventHandlers(customHandler, defaultHandler)\n\n  composedHandler({})\n  expect(customHandler).toHaveBeenCalledTimes(1)\n  expect(defaultHandler).toHaveBeenCalledTimes(0)\n})\n\ntest('call default handler when defaultDownshiftPrevented and defaultPrevented are false', () => {\n  const customHandler = jest.fn()\n  const defaultHandler = jest.fn()\n\n  const composedHandler = callAllEventHandlers(customHandler, defaultHandler)\n\n  composedHandler({})\n  expect(customHandler).toHaveBeenCalledTimes(1)\n  expect(defaultHandler).toHaveBeenCalledTimes(1)\n})\n"
  },
  {
    "path": "src/__tests__/utils.get-a11y-status-message.js",
    "content": "import {getA11yStatusMessage} from '../utils'\n\nconst tests = [\n  {\n    input: {\n      isOpen: false,\n      selectedItem: null,\n    },\n    output: '',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 0,\n    },\n    output: 'No results are available.',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 10,\n    },\n    output:\n      '10 results are available, use up and down arrow keys to navigate. Press Enter key to select.',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 9,\n      previousResultCount: 12,\n    },\n    output:\n      '9 results are available, use up and down arrow keys to navigate. Press Enter key to select.',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 8,\n      previousResultCount: 20,\n      highlightedItem: 'Oranges',\n    },\n    output:\n      '8 results are available, use up and down arrow keys to navigate. Press Enter key to select.',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 1,\n    },\n    output:\n      '1 result is available, use up and down arrow keys to navigate. Press Enter key to select.',\n  },\n  {\n    input: {\n      isOpen: true,\n      resultCount: 5,\n      previousResultCount: 5,\n    },\n    output: '',\n  },\n]\n\ntests.forEach(({input, output}) => {\n  test(`${JSON.stringify(input)} results in ${JSON.stringify(output)}`, () => {\n    expect(getA11yStatusMessage(input)).toBe(output)\n  })\n})\n"
  },
  {
    "path": "src/__tests__/utils.get-highlighted-index.js",
    "content": "import {getHighlightedIndex} from '../utils'\n\ntest('should return next index', () => {\n  const offset = 1\n  const start = 0\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n\n  expect(getHighlightedIndex(start, offset, items, isItemDisabled)).toEqual(1)\n})\n\ntest('should return previous index', () => {\n  const offset = -1\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n\n  expect(getHighlightedIndex(start, offset, items, isItemDisabled)).toEqual(1)\n})\n\ntest('should return previous index based on moveAmount', () => {\n  const offset = -2\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n\n  expect(getHighlightedIndex(start, offset, items, isItemDisabled)).toEqual(0)\n})\n\ntest('should wrap to first if circular and reached end', () => {\n  const offset = 3\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(0)\n})\n\ntest('should not wrap to first if not circular and reached end', () => {\n  const offset = 3\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n  const circular = false\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(2)\n})\n\ntest('should wrap to last if circular and reached start', () => {\n  const offset = -3\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(2)\n})\n\ntest('should not wrap to last if not circular and reached start', () => {\n  const offset = -3\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled() {\n    return false\n  }\n  const circular = false\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(0)\n})\n\ntest('should skip disabled when moving downwards', () => {\n  const offset = 1\n  const start = 0\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 1\n  }\n\n  expect(getHighlightedIndex(start, offset, items, isItemDisabled)).toEqual(2)\n})\n\ntest('should skip disabled when moving upwards', () => {\n  const offset = -1\n  const start = 2\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 1\n  }\n\n  expect(getHighlightedIndex(start, offset, items, isItemDisabled)).toEqual(0)\n})\n\ntest('should skip disabled and wrap to last if circular when reaching first', () => {\n  const offset = -1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 0\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(2)\n})\n\ntest('should skip disabled and wrap to second to last if circular when reaching first and last is disabled', () => {\n  const offset = -1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return [0, 2].includes(index)\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(1)\n})\n\ntest('should skip disabled and not wrap to last if circular when reaching first', () => {\n  const offset = -1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 0\n  }\n  const circular = false\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(1)\n})\n\ntest('should skip disabled and wrap to first if circular when reaching last', () => {\n  const offset = 1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 2\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(0)\n})\n\ntest('should skip disabled and wrap to second if circular when reaching last and first is disabled', () => {\n  const offset = 1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return [0, 2].includes(index)\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(1)\n})\n\ntest('should skip disabled and not wrap to first if circular when reaching last', () => {\n  const offset = 1\n  const start = 1\n  const items = {length: 3}\n  function isItemDisabled(_item, index) {\n    return index === 2\n  }\n  const circular = false\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(1)\n})\n\ntest('should not select any if all disabled when arrow up', () => {\n  const offset = -1\n  const start = -1\n  const items = {length: 3}\n  function isItemDisabled() {\n    return true\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(-1)\n})\n\ntest('should not select any if all disabled when arrow down', () => {\n  const offset = 1\n  const start = -1\n  const items = {length: 3}\n  function isItemDisabled() {\n    return true\n  }\n  const circular = true\n\n  expect(\n    getHighlightedIndex(start, offset, items, isItemDisabled, circular),\n  ).toEqual(-1)\n})\n"
  },
  {
    "path": "src/__tests__/utils.handle-refs.js",
    "content": "import {handleRefs} from '../utils'\n\ntest('handle object and functinonal refs', () => {\n  const refValue = 'Here could be your HTMLElement'\n\n  const objectRef = {current: null}\n\n  let functionRefValue = null\n  const functionRef = ref => {\n    functionRefValue = ref\n  }\n\n  const handleRef = handleRefs(functionRef, objectRef)\n\n  handleRef(refValue)\n  expect(objectRef.current).toBe(refValue)\n  expect(functionRefValue).toBe(refValue)\n})\n"
  },
  {
    "path": "src/__tests__/utils.pick-state.js",
    "content": "import {pickState} from '../utils'\n\ntest('pickState only picks state that downshift cares about', () => {\n  const otherStateToSet = {\n    isOpen: true,\n    foo: 0,\n  }\n  const result = pickState(otherStateToSet)\n  const expected = {isOpen: true}\n  const resultKeys = Object.keys(result)\n  const expectedKeys = Object.keys(expected)\n  resultKeys.sort()\n  expectedKeys.sort()\n  expect(result).toEqual(expected)\n  expect(resultKeys).toEqual(expectedKeys)\n})\n"
  },
  {
    "path": "src/__tests__/utils.reset-id-counter.js",
    "content": "import * as React from 'react'\nimport {render, screen} from '@testing-library/react'\nimport Downshift from '../'\nimport {resetIdCounter} from '../utils-ts'\n\njest.mock('react', () => {\n  const {useId, ...react} = jest.requireActual('react')\n  return react\n})\n\ntest('renders with correct and predictable auto generated id upon resetIdCounter call', () => {\n  resetIdCounter()\n\n  const renderDownshift = ({getInputProps, getLabelProps, getItemProps}) => (\n    <div>\n      <label data-testid=\"label\" {...getLabelProps()} />\n      <input data-testid=\"input\" {...getInputProps()} />\n      <div>\n        <span\n          {...getItemProps({\n            item: 0,\n            'data-testid': 'item-0',\n          })}\n        >\n          0\n        </span>\n      </div>\n    </div>\n  )\n\n  const setup1 = setup({renderDownshift})\n  expect(setup1.id).toBe('downshift-0')\n  expect(setup1.label).toHaveAttribute('for', 'downshift-0-input')\n  expect(setup1.input).toHaveAttribute('id', 'downshift-0-input')\n  expect(setup1.item).toHaveAttribute('id', 'downshift-0-item-0')\n  setup1.unmount()\n\n  const setup2 = setup({renderDownshift})\n  expect(setup2.id).toBe('downshift-1')\n  expect(setup2.label).toHaveAttribute('for', 'downshift-1-input')\n  expect(setup2.input).toHaveAttribute('id', 'downshift-1-input')\n  expect(setup2.item).toHaveAttribute('id', 'downshift-1-item-0')\n  setup2.unmount()\n\n  resetIdCounter()\n\n  const setup3 = setup({renderDownshift})\n  expect(setup3.id).toBe('downshift-0')\n  expect(setup3.label).toHaveAttribute('for', 'downshift-0-input')\n  expect(setup3.input).toHaveAttribute('id', 'downshift-0-input')\n  expect(setup3.item).toHaveAttribute('id', 'downshift-0-item-0')\n  setup3.unmount()\n})\n\nfunction setup({renderDownshift = () => <div />, ...props} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return renderDownshift(controllerArg)\n  })\n  const utils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  const input = screen.queryByTestId('input')\n  const label = screen.queryByTestId('label')\n  const item = screen.queryByTestId('item-0')\n  return {...utils, input, label, item, ...renderArg}\n}\n"
  },
  {
    "path": "src/__tests__/utils.reset-id-counter.r18.js",
    "content": "import * as React from 'react'\nimport {render, screen} from '@testing-library/react'\nimport Downshift from '..'\nimport {resetIdCounter} from '../utils-ts'\n\nafterAll(() => {\n  jest.restoreAllMocks()\n})\n\ntest('renders with correct and predictable auto generated id upon resetIdCounter call', () => {\n  jest.spyOn(console, 'warn').mockImplementation(() => {})\n\n  const renderDownshift = ({getInputProps, getLabelProps, getItemProps}) => (\n    <div>\n      <label data-testid=\"label\" {...getLabelProps()} />\n      <input data-testid=\"input\" {...getInputProps()} />\n      <div>\n        <span\n          {...getItemProps({\n            item: 0,\n            'data-testid': 'item-0',\n          })}\n        >\n          0\n        </span>\n      </div>\n    </div>\n  )\n\n  setup({renderDownshift})\n  resetIdCounter()\n\n  expect(console.warn).toHaveBeenCalledTimes(1)\n  expect(console.warn).toHaveBeenCalledWith(\n    'It is not necessary to call resetIdCounter when using React 18+',\n  )\n})\n\nfunction setup({renderDownshift = () => <div />, ...props} = {}) {\n  let renderArg\n  const childrenSpy = jest.fn(controllerArg => {\n    renderArg = controllerArg\n    return renderDownshift(controllerArg)\n  })\n  const utils = render(<Downshift {...props}>{childrenSpy}</Downshift>)\n  const input = screen.queryByTestId('input')\n  const label = screen.queryByTestId('label')\n  const item = screen.queryByTestId('item-0')\n  return {...utils, input, label, item, ...renderArg}\n}\n"
  },
  {
    "path": "src/__tests__/utils.scroll-into-view.js",
    "content": "import {scrollIntoView} from '../utils-ts'\n\ntest('does not throw with a null node', () => {\n  expect(() => scrollIntoView(null)).not.toThrow()\n})\n\ntest('does not throw if the given node is the root node', () => {\n  const node = getNode()\n  expect(() => scrollIntoView(node, node)).not.toThrow()\n})\n\ntest('does nothing if the node is within the scrollable area', () => {\n  const scrollableScrollTop = 2\n  const node = getNode({height: 10, top: 6})\n  const scrollableNode = getScrollableNode({\n    scrollTop: scrollableScrollTop,\n    children: [node],\n  })\n  scrollIntoView(node, scrollableNode)\n  expect(scrollableNode.scrollTop).toBe(scrollableScrollTop)\n})\n\ntest('does nothing if parent.top is above view area and node within view', () => {\n  const scrollableScrollTop = 1000\n  const node = getNode({height: 40, top: 300})\n  // parent bounds is [-1000 + 1000, -500 + 1000] = [0, 500]\n  // node bounds is [300, 340] => node within visible area\n  const scrollableNode = getScrollableNode({\n    top: -1000,\n    height: 500,\n    scrollTop: scrollableScrollTop,\n    children: [node],\n  })\n  scrollIntoView(node, scrollableNode)\n  expect(scrollableNode.scrollTop).toBe(scrollableScrollTop)\n})\n\ntest('aligns to top when the node is above the scrollable parent', () => {\n  // TODO: make this test a tiny bit more readable/maintainable...\n  const nodeTop = 2\n  const scrollableScrollTop = 13\n  const node = getNode({height: 10, top: nodeTop})\n  const scrollableNode = getScrollableNode({\n    top: 10,\n    scrollTop: scrollableScrollTop,\n    children: [node],\n  })\n  scrollIntoView(node, scrollableNode)\n  expect(scrollableNode.scrollTop).toBe(5)\n})\n\ntest('aligns to top of scrollable parent when the node is above view area', () => {\n  const node = getNode({height: 40, top: -50})\n  const scrollableNode = getScrollableNode({\n    top: 50,\n    scrollTop: 100,\n    children: [node],\n  })\n  scrollIntoView(node, scrollableNode)\n  expect(scrollableNode.scrollTop).toBe(0)\n})\n\ntest('aligns to bottom when the node is below the scrollable parent', () => {\n  const nodeTop = 115\n  const node = getNode({height: 10, top: nodeTop})\n  const scrollableNode = getScrollableNode({\n    height: 100,\n    children: [node],\n  })\n  scrollIntoView(node, scrollableNode)\n  expect(scrollableNode.scrollTop).toBe(25)\n})\n\nfunction getScrollableNode(overrides = {}) {\n  return getNode({\n    height: 100,\n    top: 0,\n    scrollTop: 0,\n    scrollHeight: 150,\n    ...overrides,\n  })\n}\n\nfunction getNode({\n  top = 0,\n  height = 0,\n  scrollTop = 0,\n  scrollHeight = height,\n  clientHeight = height,\n  children = [],\n  borderBottomWidth = 0,\n  borderTopWidth = 0,\n} = {}) {\n  const div = document.createElement('div')\n  div.getBoundingClientRect = () => ({\n    width: 50,\n    height,\n    top,\n    left: 0,\n    right: 50,\n    bottom: top + height,\n  })\n  div.style.borderTopWidth = borderTopWidth\n  div.style.borderBottomWidth = borderBottomWidth\n  div.scrollTop = scrollTop\n\n  Object.defineProperties(div, {\n    clientHeight: {value: clientHeight},\n    offsetHeight: {value: clientHeight},\n    scrollHeight: {value: scrollHeight},\n  })\n  children.forEach(child => div.appendChild(child))\n  document.documentElement.appendChild(div)\n  return div\n}\n"
  },
  {
    "path": "src/downshift.js",
    "content": "/* eslint camelcase:0 */\n\nimport PropTypes from 'prop-types'\nimport {Component, cloneElement} from 'react'\nimport {isForwardRef} from 'react-is'\nimport {isPreact, isReactNative, isReactNativeWeb} from './is.macro'\nimport * as stateChangeTypes from './stateChangeTypes'\nimport {\n  handleRefs,\n  callAllEventHandlers,\n  cbToCb,\n  debounce,\n  getA11yStatusMessage,\n  getElementProps,\n  isDOMElement,\n  targetWithinDownshift,\n  isPlainObject,\n  normalizeArrowKey,\n  pickState,\n  requiredProp,\n  unwrapArray,\n  isControlledProp,\n  validateControlledUnchanged,\n  getHighlightedIndex,\n  getNonDisabledIndex,\n} from './utils'\nimport {generateId, scrollIntoView, setStatus, getState, noop} from './utils-ts'\n\nclass Downshift extends Component {\n  static propTypes = {\n    children: PropTypes.func,\n    defaultHighlightedIndex: PropTypes.number,\n    defaultIsOpen: PropTypes.bool,\n    initialHighlightedIndex: PropTypes.number,\n    initialSelectedItem: PropTypes.any,\n    initialInputValue: PropTypes.string,\n    initialIsOpen: PropTypes.bool,\n    getA11yStatusMessage: PropTypes.func,\n    itemToString: PropTypes.func,\n    onChange: PropTypes.func,\n    onSelect: PropTypes.func,\n    onStateChange: PropTypes.func,\n    onInputValueChange: PropTypes.func,\n    onUserAction: PropTypes.func,\n    onOuterClick: PropTypes.func,\n    selectedItemChanged: PropTypes.func,\n    stateReducer: PropTypes.func,\n    itemCount: PropTypes.number,\n    id: PropTypes.string,\n    environment: PropTypes.shape({\n      addEventListener: PropTypes.func.isRequired,\n      removeEventListener: PropTypes.func.isRequired,\n      document: PropTypes.shape({\n        createElement: PropTypes.func.isRequired,\n        getElementById: PropTypes.func.isRequired,\n        activeElement: PropTypes.any.isRequired,\n        body: PropTypes.any.isRequired,\n      }).isRequired,\n      Node: PropTypes.func.isRequired,\n    }),\n    suppressRefError: PropTypes.bool,\n    scrollIntoView: PropTypes.func,\n    // things we keep in state for uncontrolled components\n    // but can accept as props for controlled components\n    /* eslint-disable react/no-unused-prop-types */\n    selectedItem: PropTypes.any,\n    isOpen: PropTypes.bool,\n    inputValue: PropTypes.string,\n    highlightedIndex: PropTypes.number,\n    labelId: PropTypes.string,\n    inputId: PropTypes.string,\n    menuId: PropTypes.string,\n    getItemId: PropTypes.func,\n    /* eslint-enable react/no-unused-prop-types */\n  }\n\n  static defaultProps = {\n    defaultHighlightedIndex: null,\n    defaultIsOpen: false,\n    getA11yStatusMessage,\n    itemToString: i => {\n      if (i == null) {\n        return ''\n      }\n      if (\n        process.env.NODE_ENV !== 'production' &&\n        isPlainObject(i) &&\n        !i.hasOwnProperty('toString')\n      ) {\n        // eslint-disable-next-line no-console\n        console.warn(\n          'downshift: An object was passed to the default implementation of `itemToString`. You should probably provide your own `itemToString` implementation. Please refer to the `itemToString` API documentation.',\n          'The object that was passed:',\n          i,\n        )\n      }\n      return String(i)\n    },\n    onStateChange: noop,\n    onInputValueChange: noop,\n    onUserAction: noop,\n    onChange: noop,\n    onSelect: noop,\n    onOuterClick: noop,\n    selectedItemChanged: (prevItem, item) => prevItem !== item,\n    environment:\n      /* istanbul ignore next (ssr) */\n      typeof window === 'undefined' || isReactNative ? undefined : window,\n    stateReducer: (state, stateToSet) => stateToSet,\n    suppressRefError: false,\n    scrollIntoView,\n  }\n\n  static stateChangeTypes = stateChangeTypes\n\n  constructor(props) {\n    super(props)\n    // fancy destructuring + defaults + aliases\n    // this basically says each value of state should either be set to\n    // the initial value or the default value if the initial value is not provided\n    const {\n      defaultHighlightedIndex,\n      initialHighlightedIndex: highlightedIndex = defaultHighlightedIndex,\n      defaultIsOpen,\n      initialIsOpen: isOpen = defaultIsOpen,\n      initialInputValue: inputValue = '',\n      initialSelectedItem: selectedItem = null,\n    } = this.props\n    const state = this.getState({\n      highlightedIndex,\n      isOpen,\n      inputValue,\n      selectedItem,\n    })\n    if (\n      state.selectedItem != null &&\n      this.props.initialInputValue === undefined\n    ) {\n      state.inputValue = this.props.itemToString(state.selectedItem)\n    }\n    this.state = state\n  }\n\n  id = this.props.id || `downshift-${generateId()}`\n  menuId = this.props.menuId || `${this.id}-menu`\n  labelId = this.props.labelId || `${this.id}-label`\n  inputId = this.props.inputId || `${this.id}-input`\n  getItemId = this.props.getItemId || (index => `${this.id}-item-${index}`)\n\n  items = []\n  // itemCount can be changed asynchronously\n  // from within downshift (so it can't come from a prop)\n  // this is why we store it as an instance and use\n  // getItemCount rather than just use items.length\n  // (to support windowing + async)\n  itemCount = null\n  previousResultCount = 0\n\n  timeoutIds = []\n\n  /**\n   * @param {Function} fn the function to call after the time\n   * @param {Number} time the time to wait\n   */\n  internalSetTimeout = (fn, time) => {\n    const id = setTimeout(() => {\n      this.timeoutIds = this.timeoutIds.filter(i => i !== id)\n      fn()\n    }, time)\n\n    this.timeoutIds.push(id)\n  }\n\n  /**\n   * Clear all running timeouts\n   */\n  internalClearTimeouts() {\n    this.timeoutIds.forEach(id => {\n      clearTimeout(id)\n    })\n\n    this.timeoutIds = []\n  }\n\n  /**\n   * Gets the state based on internal state or props\n   * If a state value is passed via props, then that\n   * is the value given, otherwise it's retrieved from\n   * stateToMerge\n   *\n   * @param {Object} stateToMerge defaults to this.state\n   * @return {Object} the state\n   */\n  getState(stateToMerge = this.state) {\n    return getState(stateToMerge, this.props)\n  }\n\n  getItemCount() {\n    // things read better this way. They're in priority order:\n    // 1. `this.itemCount`\n    // 2. `this.props.itemCount`\n    // 3. `this.items.length`\n    let itemCount = this.items.length\n    if (this.itemCount != null) {\n      itemCount = this.itemCount\n    } else if (this.props.itemCount !== undefined) {\n      itemCount = this.props.itemCount\n    }\n    return itemCount\n  }\n\n  setItemCount = count => {\n    this.itemCount = count\n  }\n\n  unsetItemCount = () => {\n    this.itemCount = null\n  }\n\n  getItemNodeFromIndex(index) {\n    return this.props.environment\n      ? this.props.environment.document.getElementById(this.getItemId(index))\n      : null\n  }\n\n  isItemDisabled = (_item, index) => {\n    const currentElementNode = this.getItemNodeFromIndex(index)\n\n    return currentElementNode && currentElementNode.hasAttribute('disabled')\n  }\n\n  setHighlightedIndex = (\n    highlightedIndex = this.props.defaultHighlightedIndex,\n    otherStateToSet = {},\n  ) => {\n    otherStateToSet = pickState(otherStateToSet)\n    this.internalSetState({highlightedIndex, ...otherStateToSet})\n  }\n\n  scrollHighlightedItemIntoView() {\n    /* istanbul ignore else (react-native) */\n    if (!isReactNative) {\n      const node = this.getItemNodeFromIndex(this.getState().highlightedIndex)\n      this.props.scrollIntoView(node, this._menuNode)\n    }\n  }\n\n  moveHighlightedIndex(amount, otherStateToSet) {\n    const itemCount = this.getItemCount()\n    const {highlightedIndex} = this.getState()\n    if (itemCount > 0) {\n      const nextHighlightedIndex = getHighlightedIndex(\n        highlightedIndex,\n        amount,\n        {length: itemCount},\n        this.isItemDisabled,\n        true,\n      )\n      this.setHighlightedIndex(nextHighlightedIndex, otherStateToSet)\n    }\n  }\n\n  clearSelection = cb => {\n    this.internalSetState(\n      {\n        selectedItem: null,\n        inputValue: '',\n        highlightedIndex: this.props.defaultHighlightedIndex,\n        isOpen: this.props.defaultIsOpen,\n      },\n      cb,\n    )\n  }\n\n  selectItem = (item, otherStateToSet, cb) => {\n    otherStateToSet = pickState(otherStateToSet)\n    this.internalSetState(\n      {\n        isOpen: this.props.defaultIsOpen,\n        highlightedIndex: this.props.defaultHighlightedIndex,\n        selectedItem: item,\n        inputValue: this.props.itemToString(item),\n        ...otherStateToSet,\n      },\n      cb,\n    )\n  }\n\n  selectItemAtIndex = (itemIndex, otherStateToSet, cb) => {\n    const item = this.items[itemIndex]\n    if (item == null) {\n      return\n    }\n    this.selectItem(item, otherStateToSet, cb)\n  }\n\n  selectHighlightedItem = (otherStateToSet, cb) => {\n    return this.selectItemAtIndex(\n      this.getState().highlightedIndex,\n      otherStateToSet,\n      cb,\n    )\n  }\n\n  // any piece of our state can live in two places:\n  // 1. Uncontrolled: it's internal (this.state)\n  //    We will call this.setState to update that state\n  // 2. Controlled: it's external (this.props)\n  //    We will call this.props.onStateChange to update that state\n  //\n  // In addition, we'll call this.props.onChange if the\n  // selectedItem is changed.\n  internalSetState = (stateToSet, cb) => {\n    let isItemSelected, onChangeArg\n\n    const onStateChangeArg = {}\n    const isStateToSetFunction = typeof stateToSet === 'function'\n\n    // we want to call `onInputValueChange` before the `setState` call\n    // so someone controlling the `inputValue` state gets notified of\n    // the input change as soon as possible. This avoids issues with\n    // preserving the cursor position.\n    // See https://github.com/downshift-js/downshift/issues/217 for more info.\n    if (!isStateToSetFunction && stateToSet.hasOwnProperty('inputValue')) {\n      this.props.onInputValueChange(stateToSet.inputValue, {\n        ...this.getStateAndHelpers(),\n        ...stateToSet,\n      })\n    }\n    return this.setState(\n      state => {\n        state = this.getState(state)\n        let newStateToSet = isStateToSetFunction\n          ? stateToSet(state)\n          : stateToSet\n\n        // Your own function that could modify the state that will be set.\n        newStateToSet = this.props.stateReducer(state, newStateToSet)\n\n        // checks if an item is selected, regardless of if it's different from\n        // what was selected before\n        // used to determine if onSelect and onChange callbacks should be called\n        isItemSelected = newStateToSet.hasOwnProperty('selectedItem')\n        // this keeps track of the object we want to call with setState\n        const nextState = {}\n        // this is just used to tell whether the state changed\n        const nextFullState = {}\n        // we need to call on change if the outside world is controlling any of our state\n        // and we're trying to update that state. OR if the selection has changed and we're\n        // trying to update the selection\n        if (\n          isItemSelected &&\n          newStateToSet.selectedItem !== state.selectedItem\n        ) {\n          onChangeArg = newStateToSet.selectedItem\n        }\n        newStateToSet.type ||= stateChangeTypes.unknown\n\n        Object.keys(newStateToSet).forEach(key => {\n          // onStateChangeArg should only have the state that is\n          // actually changing\n          if (state[key] !== newStateToSet[key]) {\n            onStateChangeArg[key] = newStateToSet[key]\n          }\n          // the type is useful for the onStateChangeArg\n          // but we don't actually want to set it in internal state.\n          // this is an undocumented feature for now... Not all internalSetState\n          // calls support it and I'm not certain we want them to yet.\n          // But it enables users controlling the isOpen state to know when\n          // the isOpen state changes due to mouseup events which is quite handy.\n          if (key === 'type') {\n            return\n          }\n          nextFullState[key] = newStateToSet[key]\n          // if it's coming from props, then we don't care to set it internally\n          if (!isControlledProp(this.props, key)) {\n            nextState[key] = newStateToSet[key]\n          }\n        })\n\n        // if stateToSet is a function, then we weren't able to call onInputValueChange\n        // earlier, so we'll call it now that we know what the inputValue state will be.\n        if (\n          isStateToSetFunction &&\n          newStateToSet.hasOwnProperty('inputValue')\n        ) {\n          this.props.onInputValueChange(newStateToSet.inputValue, {\n            ...this.getStateAndHelpers(),\n            ...newStateToSet,\n          })\n        }\n\n        return nextState\n      },\n      () => {\n        // call the provided callback if it's a function\n        cbToCb(cb)()\n\n        // only call the onStateChange and onChange callbacks if\n        // we have relevant information to pass them.\n        const hasMoreStateThanType = Object.keys(onStateChangeArg).length > 1\n        if (hasMoreStateThanType) {\n          this.props.onStateChange(onStateChangeArg, this.getStateAndHelpers())\n        }\n\n        if (isItemSelected) {\n          this.props.onSelect(\n            stateToSet.selectedItem,\n            this.getStateAndHelpers(),\n          )\n        }\n\n        if (onChangeArg !== undefined) {\n          this.props.onChange(onChangeArg, this.getStateAndHelpers())\n        }\n        // this is currently undocumented and therefore subject to change\n        // We'll try to not break it, but just be warned.\n        this.props.onUserAction(onStateChangeArg, this.getStateAndHelpers())\n      },\n    )\n  }\n\n  getStateAndHelpers() {\n    const {highlightedIndex, inputValue, selectedItem, isOpen} = this.getState()\n    const {itemToString} = this.props\n    const {id} = this\n    const {\n      getRootProps,\n      getToggleButtonProps,\n      getLabelProps,\n      getMenuProps,\n      getInputProps,\n      getItemProps,\n      openMenu,\n      closeMenu,\n      toggleMenu,\n      selectItem,\n      selectItemAtIndex,\n      selectHighlightedItem,\n      setHighlightedIndex,\n      clearSelection,\n      clearItems,\n      reset,\n      setItemCount,\n      unsetItemCount,\n      internalSetState: setState,\n    } = this\n    return {\n      // prop getters\n      getRootProps,\n      getToggleButtonProps,\n      getLabelProps,\n      getMenuProps,\n      getInputProps,\n      getItemProps,\n\n      // actions\n      reset,\n      openMenu,\n      closeMenu,\n      toggleMenu,\n      selectItem,\n      selectItemAtIndex,\n      selectHighlightedItem,\n      setHighlightedIndex,\n      clearSelection,\n      clearItems,\n      setItemCount,\n      unsetItemCount,\n      setState,\n\n      // props\n      itemToString,\n\n      // derived\n      id,\n\n      // state\n      highlightedIndex,\n      inputValue,\n      isOpen,\n      selectedItem,\n    }\n  }\n\n  //////////////////////////// ROOT\n\n  rootRef = node => (this._rootNode = node)\n\n  getRootProps = (\n    {refKey = 'ref', ref, ...rest} = {},\n    {suppressRefError = false} = {},\n  ) => {\n    // this is used in the render to know whether the user has called getRootProps.\n    // It uses that to know whether to apply the props automatically\n    this.getRootProps.called = true\n    this.getRootProps.refKey = refKey\n    this.getRootProps.suppressRefError = suppressRefError\n    const {isOpen} = this.getState()\n    return {\n      [refKey]: handleRefs(ref, this.rootRef),\n      role: 'combobox',\n      'aria-expanded': isOpen,\n      'aria-haspopup': 'listbox',\n      'aria-owns': isOpen ? this.menuId : undefined,\n      'aria-labelledby': this.labelId,\n      ...rest,\n    }\n  }\n\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ROOT\n\n  keyDownHandlers = {\n    ArrowDown(event) {\n      event.preventDefault()\n\n      if (this.getState().isOpen) {\n        const amount = event.shiftKey ? 5 : 1\n        this.moveHighlightedIndex(amount, {\n          type: stateChangeTypes.keyDownArrowDown,\n        })\n      } else {\n        this.internalSetState(\n          {\n            isOpen: true,\n            type: stateChangeTypes.keyDownArrowDown,\n          },\n          () => {\n            const itemCount = this.getItemCount()\n            if (itemCount > 0) {\n              const {highlightedIndex} = this.getState()\n              const nextHighlightedIndex = getHighlightedIndex(\n                highlightedIndex,\n                1,\n                {length: itemCount},\n                this.isItemDisabled,\n                true,\n              )\n\n              this.setHighlightedIndex(nextHighlightedIndex, {\n                type: stateChangeTypes.keyDownArrowDown,\n              })\n            }\n          },\n        )\n      }\n    },\n\n    ArrowUp(event) {\n      event.preventDefault()\n\n      if (this.getState().isOpen) {\n        const amount = event.shiftKey ? -5 : -1\n        this.moveHighlightedIndex(amount, {\n          type: stateChangeTypes.keyDownArrowUp,\n        })\n      } else {\n        this.internalSetState(\n          {\n            isOpen: true,\n            type: stateChangeTypes.keyDownArrowUp,\n          },\n          () => {\n            const itemCount = this.getItemCount()\n            if (itemCount > 0) {\n              const {highlightedIndex} = this.getState()\n              const nextHighlightedIndex = getHighlightedIndex(\n                highlightedIndex,\n                -1,\n                {length: itemCount},\n                this.isItemDisabled,\n                true,\n              )\n\n              this.setHighlightedIndex(nextHighlightedIndex, {\n                type: stateChangeTypes.keyDownArrowUp,\n              })\n            }\n          },\n        )\n      }\n    },\n\n    Enter(event) {\n      if (event.which === 229) {\n        return\n      }\n\n      const {isOpen, highlightedIndex} = this.getState()\n      if (isOpen && highlightedIndex != null) {\n        event.preventDefault()\n        const item = this.items[highlightedIndex]\n        const itemNode = this.getItemNodeFromIndex(highlightedIndex)\n        if (item == null || (itemNode && itemNode.hasAttribute('disabled'))) {\n          return\n        }\n        this.selectHighlightedItem({\n          type: stateChangeTypes.keyDownEnter,\n        })\n      }\n    },\n\n    Escape(event) {\n      event.preventDefault()\n      this.reset({\n        type: stateChangeTypes.keyDownEscape,\n        ...(!this.state.isOpen && {selectedItem: null, inputValue: ''}),\n      })\n    },\n  }\n\n  //////////////////////////// BUTTON\n\n  buttonKeyDownHandlers = {\n    ...this.keyDownHandlers,\n\n    ' '(event) {\n      event.preventDefault()\n      this.toggleMenu({type: stateChangeTypes.keyDownSpaceButton})\n    },\n  }\n\n  inputKeyDownHandlers = {\n    ...this.keyDownHandlers,\n    Home(event) {\n      const {isOpen} = this.getState()\n\n      if (!isOpen) {\n        return\n      }\n\n      event.preventDefault()\n\n      const itemCount = this.getItemCount()\n\n      if (itemCount <= 0 || !isOpen) {\n        return\n      }\n\n      // get next non-disabled starting downwards from 0 if that's disabled.\n      const newHighlightedIndex = getNonDisabledIndex(\n        0,\n        false,\n        {length: itemCount},\n        this.isItemDisabled,\n      )\n\n      this.setHighlightedIndex(newHighlightedIndex, {\n        type: stateChangeTypes.keyDownHome,\n      })\n    },\n\n    End(event) {\n      const {isOpen} = this.getState()\n\n      if (!isOpen) {\n        return\n      }\n\n      event.preventDefault()\n\n      const itemCount = this.getItemCount()\n\n      if (itemCount <= 0 || !isOpen) {\n        return\n      }\n\n      // get next non-disabled starting upwards from last index if that's disabled.\n      const newHighlightedIndex = getNonDisabledIndex(\n        itemCount - 1,\n        true,\n        {length: itemCount},\n        this.isItemDisabled,\n      )\n\n      this.setHighlightedIndex(newHighlightedIndex, {\n        type: stateChangeTypes.keyDownEnd,\n      })\n    },\n  }\n\n  getToggleButtonProps = ({\n    onClick,\n    onPress,\n    onKeyDown,\n    onKeyUp,\n    onBlur,\n    ...rest\n  } = {}) => {\n    const {isOpen} = this.getState()\n    const enabledEventHandlers =\n      isReactNative || isReactNativeWeb\n        ? /* istanbul ignore next (react-native) */\n          {\n            onPress: callAllEventHandlers(onPress, this.buttonHandleClick),\n          }\n        : {\n            onClick: callAllEventHandlers(onClick, this.buttonHandleClick),\n            onKeyDown: callAllEventHandlers(\n              onKeyDown,\n              this.buttonHandleKeyDown,\n            ),\n            onKeyUp: callAllEventHandlers(onKeyUp, this.buttonHandleKeyUp),\n            onBlur: callAllEventHandlers(onBlur, this.buttonHandleBlur),\n          }\n    const eventHandlers = rest.disabled ? {} : enabledEventHandlers\n    return {\n      type: 'button',\n      role: 'button',\n      'aria-label': isOpen ? 'close menu' : 'open menu',\n      'aria-haspopup': true,\n      'data-toggle': true,\n      ...eventHandlers,\n      ...rest,\n    }\n  }\n\n  buttonHandleKeyUp = event => {\n    // Prevent click event from emitting in Firefox\n    event.preventDefault()\n  }\n\n  buttonHandleKeyDown = event => {\n    const key = normalizeArrowKey(event)\n    if (this.buttonKeyDownHandlers[key]) {\n      this.buttonKeyDownHandlers[key].call(this, event)\n    }\n  }\n\n  buttonHandleClick = event => {\n    event.preventDefault()\n    // handle odd case for Safari and Firefox which\n    // don't give the button the focus properly.\n    /* istanbul ignore if (can't reasonably test this) */\n    if (!isReactNative && this.props.environment) {\n      const {body, activeElement} = this.props.environment.document\n\n      if (body && body === activeElement) {\n        event.target.focus()\n      }\n    }\n    // to simplify testing components that use downshift, we'll not wrap this in a setTimeout\n    // if the NODE_ENV is test. With the proper build system, this should be dead code eliminated\n    // when building for production and should therefore have no impact on production code.\n    if (process.env.NODE_ENV === 'test') {\n      this.toggleMenu({type: stateChangeTypes.clickButton})\n    } else {\n      // Ensure that toggle of menu occurs after the potential blur event in iOS\n      this.internalSetTimeout(() =>\n        this.toggleMenu({type: stateChangeTypes.clickButton}),\n      )\n    }\n  }\n\n  buttonHandleBlur = event => {\n    const blurTarget = event.target // Save blur target for comparison with activeElement later\n    // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not body element\n    this.internalSetTimeout(() => {\n      if (this.isMouseDown || !this.props.environment) {\n        return\n      }\n\n      const {activeElement} = this.props.environment.document\n\n      if (\n        (activeElement == null || activeElement.id !== this.inputId) &&\n        activeElement !== blurTarget // Do nothing if we refocus the same element again (to solve issue in Safari on iOS)\n      ) {\n        this.reset({type: stateChangeTypes.blurButton})\n      }\n    })\n  }\n\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ BUTTON\n\n  /////////////////////////////// LABEL\n\n  getLabelProps = props => {\n    return {htmlFor: this.inputId, id: this.labelId, ...props}\n  }\n\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ LABEL\n\n  /////////////////////////////// INPUT\n\n  getInputProps = ({\n    onKeyDown,\n    onBlur,\n    onChange,\n    onInput,\n    onChangeText,\n    ...rest\n  } = {}) => {\n    let onChangeKey\n    let eventHandlers = {}\n\n    /* istanbul ignore next (preact) */\n    if (isPreact) {\n      onChangeKey = 'onInput'\n    } else {\n      onChangeKey = 'onChange'\n    }\n    const {inputValue, isOpen, highlightedIndex} = this.getState()\n\n    if (!rest.disabled) {\n      eventHandlers = {\n        [onChangeKey]: callAllEventHandlers(\n          onChange,\n          onInput,\n          this.inputHandleChange,\n        ),\n        onKeyDown: callAllEventHandlers(onKeyDown, this.inputHandleKeyDown),\n        onBlur: callAllEventHandlers(onBlur, this.inputHandleBlur),\n      }\n    }\n\n    /* istanbul ignore if (react-native) */\n    if (isReactNative) {\n      eventHandlers = {\n        onChange: callAllEventHandlers(\n          onChange,\n          onInput,\n          this.inputHandleChange,\n        ),\n        onChangeText: callAllEventHandlers(onChangeText, onInput, text =>\n          this.inputHandleChange({nativeEvent: {text}}),\n        ),\n        onBlur: callAllEventHandlers(onBlur, this.inputHandleBlur),\n      }\n    }\n\n    return {\n      'aria-autocomplete': 'list',\n      'aria-activedescendant':\n        isOpen && typeof highlightedIndex === 'number' && highlightedIndex >= 0\n          ? this.getItemId(highlightedIndex)\n          : undefined,\n      'aria-controls': isOpen ? this.menuId : undefined,\n      'aria-labelledby': rest && rest['aria-label'] ? undefined : this.labelId,\n      // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion\n      // revert back since autocomplete=\"nope\" is ignored on latest Chrome and Opera\n      autoComplete: 'off',\n      value: inputValue,\n      id: this.inputId,\n      ...eventHandlers,\n      ...rest,\n    }\n  }\n\n  inputHandleKeyDown = event => {\n    const key = normalizeArrowKey(event)\n    if (key && this.inputKeyDownHandlers[key]) {\n      this.inputKeyDownHandlers[key].call(this, event)\n    }\n  }\n\n  inputHandleChange = event => {\n    this.internalSetState({\n      type: stateChangeTypes.changeInput,\n      isOpen: true,\n      inputValue:\n        isReactNative || isReactNativeWeb\n          ? /* istanbul ignore next (react-native) */ event.nativeEvent.text\n          : event.target.value,\n      highlightedIndex: this.props.defaultHighlightedIndex,\n    })\n  }\n\n  inputHandleBlur = () => {\n    // Need setTimeout, so that when the user presses Tab, the activeElement is the next focused element, not the body element\n    this.internalSetTimeout(() => {\n      if (this.isMouseDown || !this.props.environment) {\n        return\n      }\n\n      const {activeElement} = this.props.environment.document\n      const downshiftButtonIsActive =\n        activeElement?.dataset?.toggle &&\n        this._rootNode &&\n        this._rootNode.contains(activeElement)\n\n      if (!downshiftButtonIsActive) {\n        this.reset({type: stateChangeTypes.blurInput})\n      }\n    })\n  }\n\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ INPUT\n\n  /////////////////////////////// MENU\n\n  menuRef = node => {\n    this._menuNode = node\n  }\n\n  getMenuProps = (\n    {refKey = 'ref', ref, ...props} = {},\n    {suppressRefError = false} = {},\n  ) => {\n    this.getMenuProps.called = true\n    this.getMenuProps.refKey = refKey\n    this.getMenuProps.suppressRefError = suppressRefError\n\n    return {\n      [refKey]: handleRefs(ref, this.menuRef),\n      role: 'listbox',\n      'aria-labelledby':\n        props && props['aria-label'] ? undefined : this.labelId,\n      id: this.menuId,\n      ...props,\n    }\n  }\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ MENU\n\n  /////////////////////////////// ITEM\n  getItemProps = ({\n    onMouseMove,\n    onMouseDown,\n    onClick,\n    onPress,\n    index,\n    item = process.env.NODE_ENV === 'production'\n      ? /* istanbul ignore next */ undefined\n      : requiredProp('getItemProps', 'item'),\n    ...rest\n  } = {}) => {\n    if (index === undefined) {\n      this.items.push(item)\n      index = this.items.indexOf(item)\n    } else {\n      this.items[index] = item\n    }\n\n    const onSelectKey =\n      isReactNative || isReactNativeWeb\n        ? /* istanbul ignore next (react-native) */ 'onPress'\n        : 'onClick'\n    const customClickHandler = isReactNative\n      ? /* istanbul ignore next (react-native) */ onPress\n      : onClick\n\n    const enabledEventHandlers = {\n      // onMouseMove is used over onMouseEnter here. onMouseMove\n      // is only triggered on actual mouse movement while onMouseEnter\n      // can fire on DOM changes, interrupting keyboard navigation\n      onMouseMove: callAllEventHandlers(onMouseMove, () => {\n        if (index === this.getState().highlightedIndex) {\n          return\n        }\n        this.setHighlightedIndex(index, {\n          type: stateChangeTypes.itemMouseEnter,\n        })\n\n        // We never want to manually scroll when changing state based\n        // on `onMouseMove` because we will be moving the element out\n        // from under the user which is currently scrolling/moving the\n        // cursor\n        this.avoidScrolling = true\n        this.internalSetTimeout(() => (this.avoidScrolling = false), 250)\n      }),\n      onMouseDown: callAllEventHandlers(onMouseDown, event => {\n        // This prevents the activeElement from being changed\n        // to the item so it can remain with the current activeElement\n        // which is a more common use case.\n        event.preventDefault()\n      }),\n      [onSelectKey]: callAllEventHandlers(customClickHandler, () => {\n        this.selectItemAtIndex(index, {\n          type: stateChangeTypes.clickItem,\n        })\n      }),\n    }\n\n    // Passing down the onMouseDown handler to prevent redirect\n    // of the activeElement if clicking on disabled items\n    const eventHandlers = rest.disabled\n      ? {onMouseDown: enabledEventHandlers.onMouseDown}\n      : enabledEventHandlers\n\n    return {\n      id: this.getItemId(index),\n      role: 'option',\n      'aria-selected': this.getState().highlightedIndex === index,\n      ...eventHandlers,\n      ...rest,\n    }\n  }\n  //\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ ITEM\n\n  clearItems = () => {\n    this.items = []\n  }\n\n  reset = (otherStateToSet = {}, cb) => {\n    otherStateToSet = pickState(otherStateToSet)\n    this.internalSetState(\n      ({selectedItem}) => ({\n        isOpen: this.props.defaultIsOpen,\n        highlightedIndex: this.props.defaultHighlightedIndex,\n        inputValue: this.props.itemToString(selectedItem),\n        ...otherStateToSet,\n      }),\n      cb,\n    )\n  }\n\n  toggleMenu = (otherStateToSet = {}, cb) => {\n    otherStateToSet = pickState(otherStateToSet)\n    this.internalSetState(\n      ({isOpen}) => {\n        return {\n          isOpen: !isOpen,\n          ...(isOpen && {\n            highlightedIndex: this.props.defaultHighlightedIndex,\n          }),\n          ...otherStateToSet,\n        }\n      },\n      () => {\n        const {isOpen, highlightedIndex} = this.getState()\n        if (isOpen) {\n          if (this.getItemCount() > 0 && typeof highlightedIndex === 'number') {\n            this.setHighlightedIndex(highlightedIndex, otherStateToSet)\n          }\n        }\n        cbToCb(cb)()\n      },\n    )\n  }\n\n  openMenu = cb => {\n    this.internalSetState({isOpen: true}, cb)\n  }\n\n  closeMenu = cb => {\n    this.internalSetState({isOpen: false}, cb)\n  }\n\n  updateStatus = debounce(() => {\n    if (!this.props?.environment?.document) {\n      return\n    }\n\n    const state = this.getState()\n    const item = this.items[state.highlightedIndex]\n    const resultCount = this.getItemCount()\n    const status = this.props.getA11yStatusMessage({\n      itemToString: this.props.itemToString,\n      previousResultCount: this.previousResultCount,\n      resultCount,\n      highlightedItem: item,\n      ...state,\n    })\n    this.previousResultCount = resultCount\n\n    setStatus(status, this.props.environment.document)\n  }, 200)\n\n  componentDidMount() {\n    /* istanbul ignore if (react-native) */\n    if (\n      process.env.NODE_ENV !== 'production' &&\n      !isReactNative &&\n      this.getMenuProps.called &&\n      !this.getMenuProps.suppressRefError\n    ) {\n      validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps)\n    }\n\n    /* istanbul ignore if (react-native or SSR) */\n    if (isReactNative || !this.props.environment) {\n      this.cleanup = () => {\n        this.internalClearTimeouts()\n      }\n    } else {\n      // this.isMouseDown helps us track whether the mouse is currently held down.\n      // This is useful when the user clicks on an item in the list, but holds the mouse\n      // down long enough for the list to disappear (because the blur event fires on the input)\n      // this.isMouseDown is used in the blur handler on the input to determine whether the blur event should\n      // trigger hiding the menu.\n      const onMouseDown = () => {\n        this.isMouseDown = true\n      }\n      const onMouseUp = event => {\n        this.isMouseDown = false\n        // if the target element or the activeElement is within a downshift node\n        // then we don't want to reset downshift\n        const contextWithinDownshift = targetWithinDownshift(\n          event.target,\n          [this._rootNode, this._menuNode],\n          this.props.environment,\n        )\n        if (!contextWithinDownshift && this.getState().isOpen) {\n          this.reset({type: stateChangeTypes.mouseUp}, () =>\n            this.props.onOuterClick(this.getStateAndHelpers()),\n          )\n        }\n      }\n      // Touching an element in iOS gives focus and hover states, but touching out of\n      // the element will remove hover, and persist the focus state, resulting in the\n      // blur event not being triggered.\n      // this.isTouchMove helps us track whether the user is tapping or swiping on a touch screen.\n      // If the user taps outside of Downshift, the component should be reset,\n      // but not if the user is swiping\n      const onTouchStart = () => {\n        this.isTouchMove = false\n      }\n\n      const onTouchMove = () => {\n        this.isTouchMove = true\n      }\n\n      const onTouchEnd = event => {\n        const contextWithinDownshift = targetWithinDownshift(\n          event.target,\n          [this._rootNode, this._menuNode],\n          this.props.environment,\n          false,\n        )\n        if (\n          !this.isTouchMove &&\n          !contextWithinDownshift &&\n          this.getState().isOpen\n        ) {\n          this.reset({type: stateChangeTypes.touchEnd}, () =>\n            this.props.onOuterClick(this.getStateAndHelpers()),\n          )\n        }\n      }\n      const {environment} = this.props\n\n      environment.addEventListener('mousedown', onMouseDown)\n      environment.addEventListener('mouseup', onMouseUp)\n      environment.addEventListener('touchstart', onTouchStart)\n      environment.addEventListener('touchmove', onTouchMove)\n      environment.addEventListener('touchend', onTouchEnd)\n\n      this.cleanup = () => {\n        this.internalClearTimeouts()\n        this.updateStatus.cancel()\n\n        environment.removeEventListener('mousedown', onMouseDown)\n        environment.removeEventListener('mouseup', onMouseUp)\n        environment.removeEventListener('touchstart', onTouchStart)\n        environment.removeEventListener('touchmove', onTouchMove)\n        environment.removeEventListener('touchend', onTouchEnd)\n      }\n    }\n  }\n\n  shouldScroll(prevState, prevProps) {\n    const {highlightedIndex: currentHighlightedIndex} =\n      this.props.highlightedIndex === undefined ? this.getState() : this.props\n    const {highlightedIndex: prevHighlightedIndex} =\n      prevProps.highlightedIndex === undefined ? prevState : prevProps\n    const scrollWhenOpen =\n      currentHighlightedIndex && this.getState().isOpen && !prevState.isOpen\n    const scrollWhenNavigating =\n      currentHighlightedIndex !== prevHighlightedIndex\n\n    return scrollWhenOpen || scrollWhenNavigating\n  }\n\n  componentDidUpdate(prevProps, prevState) {\n    if (process.env.NODE_ENV !== 'production') {\n      validateControlledUnchanged(this.state, prevProps, this.props)\n      /* istanbul ignore if (react-native) */\n      if (\n        !isReactNative &&\n        this.getMenuProps.called &&\n        !this.getMenuProps.suppressRefError\n      ) {\n        validateGetMenuPropsCalledCorrectly(this._menuNode, this.getMenuProps)\n      }\n    }\n\n    if (\n      isControlledProp(this.props, 'selectedItem') &&\n      this.props.selectedItemChanged(\n        prevProps.selectedItem,\n        this.props.selectedItem,\n      )\n    ) {\n      this.internalSetState({\n        type: stateChangeTypes.controlledPropUpdatedSelectedItem,\n        inputValue: this.props.itemToString(this.props.selectedItem),\n      })\n    }\n\n    if (!this.avoidScrolling && this.shouldScroll(prevState, prevProps)) {\n      this.scrollHighlightedItemIntoView()\n    }\n\n    /* istanbul ignore else (react-native) */\n    if (!isReactNative) {\n      this.updateStatus()\n    }\n  }\n\n  componentWillUnmount() {\n    this.cleanup() // avoids memory leak\n  }\n\n  render() {\n    const children = unwrapArray(this.props.children, noop)\n    // because the items are rerendered every time we call the children\n    // we clear this out each render and it will be populated again as\n    // getItemProps is called.\n    this.clearItems()\n    // we reset this so we know whether the user calls getRootProps during\n    // this render. If they do then we don't need to do anything,\n    // if they don't then we need to clone the element they return and\n    // apply the props for them.\n    this.getRootProps.called = false\n    this.getRootProps.refKey = undefined\n    this.getRootProps.suppressRefError = undefined\n    // we do something similar for getMenuProps\n    this.getMenuProps.called = false\n    this.getMenuProps.refKey = undefined\n    this.getMenuProps.suppressRefError = undefined\n    // we do something similar for getLabelProps\n    this.getLabelProps.called = false\n    // and something similar for getInputProps\n    this.getInputProps.called = false\n    const element = unwrapArray(children(this.getStateAndHelpers()))\n    if (!element) {\n      return null\n    }\n\n    if (this.getRootProps.called || this.props.suppressRefError) {\n      if (\n        process.env.NODE_ENV !== 'production' &&\n        !this.getRootProps.suppressRefError &&\n        !this.props.suppressRefError\n      ) {\n        validateGetRootPropsCalledCorrectly(element, this.getRootProps)\n      }\n      return element\n    } else if (isDOMElement(element)) {\n      // they didn't apply the root props, but we can clone\n      // this and apply the props ourselves\n      return cloneElement(element, this.getRootProps(getElementProps(element)))\n    }\n\n    /* istanbul ignore else */\n    if (process.env.NODE_ENV !== 'production') {\n      // they didn't apply the root props, but they need to\n      // otherwise we can't query around the autocomplete\n\n      throw new Error(\n        'downshift: If you return a non-DOM element, you must apply the getRootProps function',\n      )\n    }\n\n    /* istanbul ignore next */\n    return undefined\n  }\n}\n\nexport default Downshift\n\nfunction validateGetMenuPropsCalledCorrectly(node, {refKey}) {\n  if (!node) {\n    // eslint-disable-next-line no-console\n    console.error(\n      `downshift: The ref prop \"${refKey}\" from getMenuProps was not applied correctly on your menu element.`,\n    )\n  }\n}\n\nfunction validateGetRootPropsCalledCorrectly(element, {refKey}) {\n  const refKeySpecified = refKey !== 'ref'\n  const isComposite = !isDOMElement(element)\n  if (isComposite && !refKeySpecified && !isForwardRef(element)) {\n    // eslint-disable-next-line no-console\n    console.error(\n      'downshift: You returned a non-DOM element. You must specify a refKey in getRootProps',\n    )\n  } else if (!isComposite && refKeySpecified) {\n    // eslint-disable-next-line no-console\n    console.error(\n      `downshift: You returned a DOM element. You should not specify a refKey in getRootProps. You specified \"${refKey}\"`,\n    )\n  }\n  if (!isForwardRef(element) && !getElementProps(element)[refKey]) {\n    // eslint-disable-next-line no-console\n    console.error(\n      `downshift: You must apply the ref prop \"${refKey}\" from getRootProps onto your root element.`,\n    )\n  }\n}\n"
  },
  {
    "path": "src/hooks/MIGRATION_V7.md",
    "content": "# Migration from v6 to v7\n\nSince version _7.0.0_ Downshift follows the (ARIA 1.2 guideline for the\ncombobox)[select-aria]. This brought a series of changes that are considered\nbreaking, both to the API and the behaviour of downshift's _useSelect_ and\n_useCombobox_ hooks. The list of changes, as well as the migration itself, is\ndetailed below.\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [useSelect](#useselect)\n  - [Focus](#focus)\n  - [HTML Attributes](#html-attributes)\n  - [Events](#events)\n  - [stateChangeTypes](#statechangetypes)\n  - [circularNavigation](#circularnavigation)\n- [useCombobox](#usecombobox)\n  - [HTML Attributes](#html-attributes-1)\n  - [Events](#events-1)\n  - [stateChangeTypes](#statechangetypes-1)\n  - [circularNavigation](#circularnavigation-1)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## useSelect\n\n### Focus\n\nSince [ARIA 1.2](select-aria-example), focus stays on the trigger element at all\ntimes. (Previously)[deprecated-select-aria], it toggled between the trigger and\nthe menu depending on the open state of the _select_ element. If any of your\ncustom implementation involved the focus on the menu element, please change it\nas the focus stays on the trigger even when the menu is open.\n\n### HTML Attributes\n\nSimilar to 1.1, _useSelect_ communicates to the screen reader the currently\nhighlighted item via the _aria-activedescendant_ attribute. However, since now\nthe focus is always on the trigger element, this attribute, along with others,\nhave shifted as shown below:\n\n- getToggleButtonProps additions:\n  - role=combobox\n  - aria-activedescendant=${highlightedItemId}\n  - aria-controls=${menuId}\n  - tabindex=0\n- getMenuProps removals:\n  - aria-activedescendant=${highlightedItemId}\n- getItemProps changes:\n  - aria-selected=${item === selectedItem} - now the item that is selected\n    received _aria-selected=true_, the rest receive it as false. Previously, the\n    highlighted item was marked with _aria-selected=true_.\n\n### Events\n\nEvent changes occured because of the focus shift, as well as new accessibility\npattern recommendantions.\n\n- getToggleButtonProps additions:\n\n  - _ArrowDown+Alt_: opens the menu without any item highlighted.\n  - _ArrowUp+Alt_: closes the menu and selects the highlighted item.\n  - _End_: highlights the last item and opens the menu if closed.\n  - _Home_: highlights the first item and opens the menu if closed.\n  - _PageUp_: if menu is open, moves highlight by 10 positions to the start.\n  - _PageDown_: if menu is open, moves highlight by 10 positions to the end.\n  - _${characterKey}_: always opens the menu if closed, highlights the item\n    starting with that key (same behaviour as before when the menu is opened).\n  - _Enter_: if menu is open, closes the menu and selects the highlighted item.\n  - _SpaceBar_: if menu is open, closes the menu and selects the highlighted\n    item. If the space is part of a search query, it will be added to the search\n    query instead.\n  - _Escape_: closes the menu if open, without selecting anything.\n  - _Tab_ or any other _Blur_: closes the menu if open, selects highlighted\n    item, focus moves naturally.\n  - _ArrowUp_: moves highlight one position up. _Shift_ modifier is not\n    supported anymore.\n  - _ArrowDown_: moves highlight one position down. _Shift_ modifier is not\n    supported anymore.\n\n- getToggleButtonProps changes:\n\n  - _ArrowUp_: if there is an item selected, opens the menu with that item\n    highlighted, not with the -1 offset as it did in v6 (ARIA 1.1).\n  - _ArrowDown_: if there is an item selected, opens the menu with that item\n    highlighted, not with the +1 offset as it did in v6 (ARIA 1.1).\n\n- getMenuProps removals:\n  - _ArrowUp_, _ArrowDown_, _End_, _Home_, _Enter_, _Escape_, _SpaceBar_, _Tab_.\n\n### stateChangeTypes\n\nAs a consequence of the [event changes](#events), the _stateChangeTypes_\nreceived in the _stateReducer_ and _on${statePropery}Change_ received the\nfollowing modifications:\n\n- MenuKeyDownArrowDown -> ToggleButtonKeyDownArrowDown\n- MenuKeyDownArrowUp -> ToggleButtonKeyDownArrowUp\n- MenuKeyDownEscape -> ToggleButtonKeyDownEscape\n- MenuKeyDownHome -> ToggleButtonKeyDownHome\n- MenuKeyDownEnd -> ToggleButtonKeyDownEnd\n- MenuKeyDownEnter -> ToggleButtonKeyDownEnter\n- MenuKeyDownSpaceButton -> ToggleButtonKeyDownSpaceButton\n- MenuKeyDownCharacter -> ToggleButtonKeyDownCharacter\n- MenuBlur -> ToggleButtonBlur\n- ToggleButtonKeyDownPageUp: new state change type.\n- ToggleButtonKeyDownPageDown: new state change type.\n\nPlease change your reducer / onChange code accordingly. For instance:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n  switch (type) {\n    case useSelect.stateChangeTypes.MenuKeyDownEnter:\n    case useSelect.stateChangeTypes.MenuKeyDownSpaceButton:\n    case useSelect.stateChangeTypes.ItemClick:\n      return {\n        ...changes,\n        isOpen: true, // keep the menu open after selection.\n      }\n    default:\n      return changes\n  }\n}\n```\n\nBecomes:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n  switch (type) {\n    case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n    case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n    case useSelect.stateChangeTypes.ItemClick:\n      return {\n        ...changes,\n        isOpen: true, // keep the menu open after selection.\n      }\n    default:\n      return changes\n  }\n}\n```\n\n> Another thing to mention is that, since ARIA 1.2 does not recommend a\n> `<button>` element for the toggle button anymore, _Enter_ and _SpaceBar_ do\n> not perform click events out of the box, when the menu is closed.\n> Consequently, we are triggering these events manually. For backwards\n> compatibility to pre v7, when menu is closed and toggle element receives\n> _Enter_ or _SpaceBar_ key event, it will trigger a\n> `useSelect.stateChangeTypes.ToggleButtonClick` type change.\n\n### circularNavigation\n\nThe prop _circularNavigation_ has been removed. Navigation inside the menu is\nstandard and non-circular. If you wish to make it circular, use the\n_stateReducer_:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n  switch (type) {\n    case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown:\n      if (state.highlightedIndex === items.length - 1) {\n        return {...changes, highlightedIndex: state.highlightedIndex}\n      } else {\n        return changes\n      }\n    case useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp:\n      if (state.highlightedIndex === 0) {\n        return {...changes, highlightedIndex: state.highlightedIndex}\n      } else {\n        return changes\n      }\n    default:\n      return changes\n  }\n}\n```\n\n## useCombobox\n\n### HTML Attributes\n\nThe biggest change in [ARIA 1.2](combobox-aria-example) is that the input\nwrapper element does not receive the combobox role attributes anymore.\nPreviously[deprecated-combobox-aria], the role of _combobox_, as well as other\nHTML attributes, had to be added on the input parent element. Some attributes\nthat belonged to the wrapper element are now added on the input element. The\nchanges are as follows:\n\n- getComboboxProps has been removed.\n- getInputProps additions:\n  - role=combobox\n  - aria-expanded=${isOpen}\n- getToggleButtonProps additions:\n  - aria-controls=${menuId}\n  - aria-expanded=${isOpen}\n\n### Events\n\nAs a result of the 1.2 pattern, there are a few event handling changes detailed\nbelow.\n\n- getInputProps additions:\n\n  - _ArrowDown+Alt_: opens the menu without any item highlighted.\n  - _ArrowUp+Alt_: closes the menu and selects the highlighted item.\n  - _PageUp_: if menu is open, moves highlight by 10 positions to the start.\n  - _PageDown_: if menu is open, moves highlight by 10 positions to the end.\n  - _Focus_: if menu is closed, opens the menu.\n\n- getInputProps changes:\n  - _ArrowUp_: moves highlight one position up. _Shift_ modifier is not\n    supported anymore.\n  - _ArrowDown_: moves highlight one position down. _Shift_ modifier is not\n    supported anymore.\n\n### stateChangeTypes\n\nAs a consequence of the [event changes](#events), the _stateChangeTypes_\nreceived in the _stateReducer_ and _on${statePropery}Change_ received the\nfollowing additions:\n\n- InputKeyDownPageUp\n- InputKeyDownPageDown\n- InputFocus\n\nYou don't need to change your reducer if you want to keep the 1.2 functionality\nprovided by default. However, if you want to keep the menu closed when the input\ngets focus, you can do:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n  switch (type) {\n    case useCombobox.stateChangeTypes.InputFocus:\n      return {\n        ...changes,\n        isOpen: state.isOpen, // keep the menu closed when input gets focused.\n      }\n    default:\n      return changes\n  }\n}\n```\n\n### circularNavigation\n\nThe prop _circularNavigation_ has been removed. Navigation inside the menu is\nstandard and circular. If you wish to make it non-circular, use the\n_stateReducer_:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n\n  switch (type) {\n    case useCombobox.stateChangeTypes.InputKeyDownArrowDown:\n      if (state.highlightedIndex === items.length - 1) {\n        return {...changes, highlightedIndex: state.highlightedIndex}\n      }\n      break\n    case useCombobox.stateChangeTypes.InputKeyDownArrowUp:\n      if (state.highlightedIndex === 0) {\n        return {...changes, highlightedIndex: state.highlightedIndex}\n      }\n      break\n    default:\n      return changes\n  }\n\n  return changes\n}\n```\n\n[combobox-aria]: https://w3c.github.io/aria-practices/#combobox\n[select-aria-example]:\n  https://w3c.github.io/aria-practices/examples/combobox/combobox-select-only.html\n[combobox-aria-example]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-list.html\n[deprecated-select-aria]:\n  https://www.w3.org/TR/2017/NOTE-wai-aria-practices-1.1-20171214/examples/listbox/listbox-collapsible.html\n[deprecated-combobox-aria]:\n  https://www.w3.org/TR/2017/NOTE-wai-aria-practices-1.1-20171214/examples/combobox/aria1.1pattern/listbox-combo.html\n"
  },
  {
    "path": "src/hooks/MIGRATION_V8.md",
    "content": "# Migration from v7 to v8\n\nDownshift v8 receives a list of breaking changes, which are necessary to improve\nboth the user and the developer experience. The changes are only affecting the\nhooks and are detailed below.\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [isItemDisabled](#isitemdisabled)\n- [useCombobox input click](#usecombobox-input-click)\n- [Getter props return value types](#getter-props-return-value-types)\n- [environment propTypes](#environment-proptypes)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## isItemDisabled\n\nBoth `useCombobox` and `useSelect` now support the `isItemDisabled` function.\nThis new API is used to mark menu items as disabled, and as such remove the from\nthe navigation and prevent them from being selected. The old API required\npassing the `disabled` prop to the `getItemProps` function. This old API has\nbeen removed and you will receive a console warning if you are trying to use the\n`disabled` prop in getItemProps.\n\nExample of API migration:\n\nOld:\n\n```jsx\nconst items = [{value: 'item1'}, {value: 'item2'}]\n\nconst {getInputProps, ...rest} = useCombobox({items})\n\nreturn (\n  // ... rest\n  <li {...getItemProps({item, disabled: item.value === 'item2'})}>\n)\n```\n\nNew:\n\n```jsx\nconst items = [{value: 'item1'}, {value: 'item2'}]\n\nconst {getInputProps, ...rest} = useCombobox({items, isItemDisabled(item, _index) { return item.value === 'item2' }})\n\nreturn (\n  // ... rest\n  <li {...getItemProps({item})}>\n)\n```\n\nThe API for Downshift remains unchange.\n\nRelated PR: https://github.com/downshift-js/downshift/pull/1510\n\n## useCombobox input click\n\n[ARIA 1.2](combobox-aria-example) recommends to toggle the menu open state at\ninput click. Previously, in v7, the menu was opened on receiving focus, from\nboth mouse and keyboard. Starting with v8, input focus will not trigger any\nstate change anymore. Only the input click event will be handled and will\ntrigger a menu toggle. Consequently:\n\n- getInputProps **will not** return any _Focus_ event handler.\n- getInputProps **will** return a _Click_ event handler.\n- `useCombobox.stateChangeTypes.InputFocus` has been removed.\n- `useCombobox.stateChangeTypes.InputClick` has been added instead.\n\nWe recommend having the default toggle on input click behaviour as it's part of\nthe official ARIA combobox 1.2 spec, but if you wish to override it and not\ntoggle the menu on click, use the stateReducer:\n\n```js\nfunction stateReducer(state, actionAndChanges) {\n  const {changes, type} = actionAndChanges\n  switch (type) {\n    case useCombobox.stateChangeTypes.InputClick:\n      return {\n        ...changes,\n        isOpen: state.isOpen, // do not toggle the menu when input is clicked.\n      }\n    default:\n      return changes\n  }\n}\n```\n\nIf you want to return to the v7 behaviour and open the menu on focus, keep the\nreducer above so you remove the toggle behaviour, and call the _openMenu_\nimperative function, returned by useCombobox, in a _onFocus_ handler passed to\n_getInputProps_:\n\n```js\n<input\n  {...getInputProps({\n    onFocus() {\n      openMenu()\n    },\n  })}\n/>\n```\n\nConsider to use the default 1.2 ARIA behaviour provided by default in order to\nalign your widget to the accessibility official spec. This behaviour consistency\nimproves the user experience, since all comboboxes should behave the same and\nthere won't be need for any additional guess work done by your users.\n\nRelated PR: https://github.com/downshift-js/downshift/pull/1440\n\n## Getter props return value types\n\nPreviously, the return value from the getter props returned by both Downshift\nand the hooks was `any`. In v8 we improved the types in order to reflect what is\nactually returned: the default values return by the getter prop function and\nwhatever else the user passes as arguments. The type changes are done in\n[this PR](https://github.com/downshift-js/downshift/pull/1482) and the\n[8.0.2 PR](https://github.com/downshift-js/downshift/pull/1524). Make sure you\nadapt your TS code, if applicable.\n\nAlso, in the `Downshift` component, the return values for some getter prop\nvalues have changed from `null` to `undefined`, since that is what HTML elements\nexpect (value or undefined). These values are also reflected in the TS types.\n\n- getRootProps: 'aria-owns': isOpen ? this.menuId : ~~null~~undefined,\n- getInputProps:\n  - 'aria-controls': isOpen ? this.menuId : ~~null~~undefined\n  - 'aria-activedescendant': isOpen && typeof highlightedIndex === 'number' &&\n    highlightedIndex >= 0 ? this.getItemId(highlightedIndex) : ~~null~~undefined\n- getMenuProps: props && props['aria-label'] ? ~~null~~undefined : this.labelId,\n\nRelated PR: https://github.com/downshift-js/downshift/pull/1482\n\n## environment propTypes\n\nThe `environment` prop is useful when you are using downshift in a context\ndifferent than regular DOM. Its TS type has been updated to contain `Node` and\nthe propTypes have also been updated to reflect the properties which are\nrequired by downshift from `environment`.\n\nRelated PR: https://github.com/downshift-js/downshift/pull/1463\n\n[combobox-aria-example]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-list.html\n"
  },
  {
    "path": "src/hooks/MIGRATION_V9.md",
    "content": "# Migration from v8 to v9\n\nDownshift v8 receives a list of breaking changes, which are necessary to improve\nboth the user and the developer experience. The changes are only affecting the\nhooks and are detailed below.\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [onChange Typescript Improvements](#onchange-typescript-improvements)\n- [getA11ySelectionMessage](#geta11yselectionmessage)\n- [getA11yRemovalMessage](#geta11yremovalmessage)\n- [getA11yStatusMessage](#geta11ystatusmessage)\n- [selectedItemChanged](#selecteditemchanged)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## onChange Typescript Improvements\n\nThe handlers below have their types improved to reflect that they will always\nget called with their corresponding state prop:\n\n- useCombobox\n\n  - onSelectedItemChange: selectedItem is non optional\n  - onIsOpenChange: isOpen is non optional\n  - onHighlightedIndexChange: highlightedIndex is non optional\n\n- useSelect\n\n  - onSelectedItemChange: selectedItem is non optional\n  - onIsOpenChange: isOpen is non optional\n  - onHighlightedIndexChange: highlightedIndex is non optional\n  - onInputValueChange: inputValue is non optional\n\n- useMultipleSelection\n  - onActiveIndexChange: activeIndex is non optional\n  - onSelectedItemsChange: selectedItems is non optional\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## getA11ySelectionMessage\n\nThe prop has been removed from useSelect and useCombobox. If you still need an\na11y selection message, use either `getA11yStatusMessage` or your own aria-live\nimplementation inside a `onStateChange` callback.\n\n## getA11yRemovalMessage\n\nThe prop has been removed from useMultipleSelection. If you still need an a11y\nremoval message, use either `getA11yStatusMessage` or your own aria-live\nimplementation inside a `onStateChange` callback.\n\n## getA11yStatusMessage\n\nThe prop has been also added to useMultipleSelection, but has some changes\nreflected in each of the hook's readme.\n\n- there is no default function provided, so you will not get any aria-live\n  message anymore if you don't provide the prop directly to the hooks.\n- the function is called only with the hook's state, and you should already have\n  access to the props, such as items or itemToString. Values such as\n  highlightedItem or resultsCount have been removed, so you need to compute them\n  yourself if needed.\n- `Downshift` is not affected, it has the same `getA11yStatusMessage` as before,\n  no changes there at all.\n\nThe HTML markup with the ARIA attributes we provide through the getter props\nshould be enough for screen readers to report:\n\n- results count.\n- highlighted item.\n- item selection.\n- what actions the user can take.\n\nIf you need anything more specific as part of an aria-live region, please use\nthe new version of `getA11yStatusMessage` or your own aria-live implementation.\n\nReferences:\n\n- [useCombobox docs](https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#geta11ystatusmessage)\n- [useSelect docs](https://github.com/downshift-js/downshift/blob/master/src/hooks/useSelect/README.md#geta11ystatusmessage)\n- [useMultipleSelection docs](https://github.com/downshift-js/downshift/blob/master/src/hooks/useMultipleSelection/README.md#geta11ystatusmessage)\n\n## selectedItemChanged\n\nThis prop has been removed from `useCombobox`. You should use `itemToKey`\ninstead.\n\nReference:\n\n[itemToKey docs](https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/README.md#itemtokey)\n"
  },
  {
    "path": "src/hooks/README.md",
    "content": "# Downshift Hooks\n\nA set of hooks to build simple, flexible, WAI-ARIA compliant React dropdown\ncomponents. Developed as a follow up on [this issue][hooks-issue] about using\nhooks in our API.\n\n## Migration to v7\n\n`useSelect` and `useCombobox` received some changes related to their API and how\nthey works in version 7, as a conequence of adapting both hooks to the ARIA 1.2\ncombobox patterns. If you were using _useSelect_ and/or _useCombobox_ previous\nto 7.0.0, check the [migration guide][migration-guide] and update if necessary.\n\n## Hooks\n\nCheck out one of the hooks below to use in your application and create fully\naccessible widgets without any constraint about the UI library used.\n\n### useSelect\n\nFor a custom `select` dropdown check out [useSelect][select-readme].\n\n### useCombobox\n\nFor a `combobox/autocomplete` input check out [useCombobox][combobox-readme].\n\n### useTagGroup\n\nFor a `tag group` that could also be used to build a multiple selection `select`\nor a `combobox` with tags, check out [useTagGroup][tag-group-readme].\n\n## Downshift Hooks API talk\n\n[Silviu](https://silviuaavram.com/) delivered a talk about using the Downshift\nhooks at the [axe-con][axe-con] 2021 conference. The talk, which is also\n[recorded][axe-con-recording], illustrates how to build an accessible select,\ncombobox, and support multiple selection using Downshift hooks and custom\ncomponents from [ChakraUI][chakra-ui]. It offers a brief crash course to:\n\n- build a custom Select.\n- build a custom Combobox.\n- enhance the Select and Combobox with multiple selection.\n- use custom features like control props, the state reducer and action props.\n\n## Roadmap and contributions\n\nNext steps:\n\n- iterate on `useSelect`, `useCombobox`, `useMultipleSelection` to improve them.\n- remove the `Downshift` component once the hooks are mature.\n\n[hooks-issue]: https://github.com/downshift-js/downshift/issues/683\n[select-readme]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useSelect\n[combobox-readme]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useCombobox\n[tag-group-readme]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useTagGroup\n[migration-guide]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md\n[axe-con]: https://www.deque.com/axe-con/\n[axe-con-recording]:\n  https://www.youtube.com/watch?v=iDEETM9Pa4Q&ab_channel=DequeSystems\n[chakra-ui]: https://chakra-ui.com/\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n"
  },
  {
    "path": "src/hooks/__tests__/__snapshots__/utils.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`utils useMouseAndTouchTracker adds and removes listeners to environment: element change rerender adding events 1`] = `\n[\n  [\n    mousedown,\n    [Function],\n  ],\n  [\n    mouseup,\n    [Function],\n  ],\n  [\n    touchstart,\n    [Function],\n  ],\n  [\n    touchmove,\n    [Function],\n  ],\n  [\n    touchend,\n    [Function],\n  ],\n]\n`;\n\nexports[`utils useMouseAndTouchTracker adds and removes listeners to environment: element change rerender remove events 1`] = `\n[\n  [\n    mousedown,\n    [Function],\n  ],\n  [\n    mouseup,\n    [Function],\n  ],\n  [\n    touchstart,\n    [Function],\n  ],\n  [\n    touchmove,\n    [Function],\n  ],\n  [\n    touchend,\n    [Function],\n  ],\n]\n`;\n\nexports[`utils useMouseAndTouchTracker adds and removes listeners to environment: initial adding events 1`] = `\n[\n  [\n    mousedown,\n    [Function],\n  ],\n  [\n    mouseup,\n    [Function],\n  ],\n  [\n    touchstart,\n    [Function],\n  ],\n  [\n    touchmove,\n    [Function],\n  ],\n  [\n    touchend,\n    [Function],\n  ],\n]\n`;\n\nexports[`utils useMouseAndTouchTracker adds and removes listeners to environment: unmount remove events 1`] = `\n[\n  [\n    mousedown,\n    [Function],\n  ],\n  [\n    mouseup,\n    [Function],\n  ],\n  [\n    touchstart,\n    [Function],\n  ],\n  [\n    touchmove,\n    [Function],\n  ],\n  [\n    touchend,\n    [Function],\n  ],\n]\n`;\n"
  },
  {
    "path": "src/hooks/__tests__/utils.test.js",
    "content": "import {renderHook} from '@testing-library/react'\nimport {useMouseAndTouchTracker, isDropdownsStateEqual} from '../utils'\nimport {getInitialValue, getDefaultValue, getItemAndIndex} from '../utils-ts'\nimport {dropdownDefaultProps} from '../utils.dropdown'\n\ndescribe('utils', () => {\n  describe('itemToString', () => {\n    test('returns empty string if item is falsy', () => {\n      const emptyString = dropdownDefaultProps.itemToString(null)\n      expect(emptyString).toBe('')\n    })\n  })\n\n  describe('getItemAndIndex', () => {\n    test('returns arguments if passed as defined', () => {\n      expect(getItemAndIndex({}, 5, [])).toEqual([{}, 5])\n    })\n\n    test('throws an error when item and index are not passed', () => {\n      const errorMessage = 'Pass either item or index to the item getter prop!'\n\n      expect(() =>\n        getItemAndIndex(undefined, undefined, [1, 2, 3], errorMessage),\n      ).toThrow(errorMessage)\n    })\n\n    test('returns index if item is passed', () => {\n      const item = {}\n\n      expect(getItemAndIndex(item, undefined, [{x: 1}, item, {x: 2}])).toEqual([\n        item,\n        1,\n      ])\n    })\n\n    test('returns item if index is passed', () => {\n      const index = 2\n      const item = {x: 2}\n      expect(getItemAndIndex(undefined, 2, [{x: 1}, {x: 3}, item])).toEqual([\n        item,\n        index,\n      ])\n    })\n  })\n\n  test('getInitialValue will not return undefined as initial value', () => {\n    const defaults = {bogusValue: 'hello'}\n    const value = getInitialValue(\n      {initialBogusValue: undefined},\n      'bogusValue',\n      defaults,\n    )\n\n    expect(value).toEqual(defaults.bogusValue)\n  })\n\n  test('getInitialValue will not return undefined as value', () => {\n    const defaults = {bogusValue: 'hello'}\n    const value = getInitialValue(\n      {bogusValue: undefined},\n      'bogusValue',\n      defaults,\n    )\n\n    expect(value).toEqual(defaults.bogusValue)\n  })\n\n  test('getDefaultValue will not return undefined as value', () => {\n    const defaults = {bogusValue: 'hello'}\n    const value = getDefaultValue(\n      {defaultBogusValue: undefined},\n      'bogusValue',\n      defaults,\n    )\n\n    expect(value).toEqual(defaults.bogusValue)\n  })\n\n  describe('useMouseAndTouchTracker', () => {\n    test('renders without error', () => {\n      expect(() => {\n        renderHook(() => useMouseAndTouchTracker(undefined, jest.fn(), []))\n      }).not.toThrow()\n    })\n\n    test('adds and removes listeners to environment', () => {\n      const environment = {\n        addEventListener: jest.fn(),\n        removeEventListener: jest.fn(),\n      }\n      const elements = [{}, {}]\n      const handleBlur = jest.fn()\n      const initialProps = {environment, handleBlur, elements}\n\n      const {unmount, rerender, result} = renderHook(\n        props =>\n          useMouseAndTouchTracker(\n            props.environment,\n            props.handleBlur,\n            props.elements,\n          ),\n        {initialProps},\n      )\n\n      expect(environment.addEventListener).toHaveBeenCalledTimes(5)\n      expect(environment.removeEventListener).not.toHaveBeenCalled()\n      expect(environment.addEventListener.mock.calls).toMatchSnapshot(\n        'initial adding events',\n      )\n\n      environment.addEventListener.mockReset()\n      environment.removeEventListener.mockReset()\n      rerender(initialProps)\n\n      expect(environment.removeEventListener).not.toHaveBeenCalled()\n      expect(environment.addEventListener).not.toHaveBeenCalled()\n\n      rerender({...initialProps, elements: [...elements]})\n\n      expect(environment.addEventListener).toHaveBeenCalledTimes(5)\n      expect(environment.removeEventListener).toHaveBeenCalledTimes(5)\n      expect(environment.addEventListener.mock.calls).toMatchSnapshot(\n        'element change rerender adding events',\n      )\n      expect(environment.removeEventListener.mock.calls).toMatchSnapshot(\n        'element change rerender remove events',\n      )\n\n      environment.addEventListener.mockReset()\n      environment.removeEventListener.mockReset()\n\n      unmount()\n\n      expect(environment.addEventListener).not.toHaveBeenCalled()\n      expect(environment.removeEventListener).toHaveBeenCalledTimes(5)\n      expect(environment.removeEventListener.mock.calls).toMatchSnapshot(\n        'unmount remove events',\n      )\n\n      expect(result.current).toEqual({\n        isMouseDown: false,\n        isTouchMove: false,\n        isTouchEnd: false,\n      })\n    })\n  })\n\n  describe('isDropdownsStateEqual', () => {\n    test('is true when each property is equal', () => {\n      const selectedItem = 'hello'\n      const prevState = {\n        highlightedIndex: 2,\n        isOpen: true,\n        selectedItem,\n        inputValue: selectedItem,\n      }\n      const newState = {\n        ...prevState,\n      }\n\n      expect(isDropdownsStateEqual(prevState, newState)).toBe(true)\n    })\n\n    test('is false when at least one property is not equal', () => {\n      const selectedItem = {value: 'hello'}\n      const prevState = {\n        highlightedIndex: 2,\n        isOpen: true,\n        selectedItem,\n        inputValue: selectedItem,\n      }\n      const newState = {\n        ...prevState,\n        selectedItem: {...selectedItem},\n      }\n\n      expect(isDropdownsStateEqual(prevState, newState)).toBe(false)\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/index.ts",
    "content": "export {default as useSelect} from './useSelect'\nexport {default as useCombobox} from './useCombobox'\nexport {default as useMultipleSelection} from './useMultipleSelection'\nexport {default as useTagGroup} from './useTagGroup'\n"
  },
  {
    "path": "src/hooks/reducer.js",
    "content": "import {getHighlightedIndexOnOpen, getDefaultHighlightedIndex} from './utils'\nimport {getDefaultValue} from './utils-ts'\nimport {dropdownDefaultStateValues} from './utils.dropdown'\n\nexport default function downshiftCommonReducer(\n  state,\n  props,\n  action,\n  stateChangeTypes,\n) {\n  const {type} = action\n  let changes\n\n  switch (type) {\n    case stateChangeTypes.ItemMouseMove:\n      changes = {\n        highlightedIndex: action.disabled ? -1 : action.index,\n      }\n\n      break\n    case stateChangeTypes.MenuMouseLeave:\n      changes = {\n        highlightedIndex: -1,\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonClick:\n    case stateChangeTypes.FunctionToggleMenu:\n      changes = {\n        isOpen: !state.isOpen,\n        highlightedIndex: state.isOpen\n          ? -1\n          : getHighlightedIndexOnOpen(props, state, 0),\n      }\n\n      break\n    case stateChangeTypes.FunctionOpenMenu:\n      changes = {\n        isOpen: true,\n        highlightedIndex: getHighlightedIndexOnOpen(props, state, 0),\n      }\n\n      break\n    case stateChangeTypes.FunctionCloseMenu:\n      changes = {\n        isOpen: false,\n      }\n\n      break\n    case stateChangeTypes.FunctionSetHighlightedIndex:\n      changes = {\n        highlightedIndex: props.isItemDisabled(\n          props.items[action.highlightedIndex],\n          action.highlightedIndex,\n        )\n          ? -1\n          : action.highlightedIndex,\n      }\n\n      break\n    case stateChangeTypes.FunctionSetInputValue:\n      changes = {\n        inputValue: action.inputValue,\n      }\n\n      break\n    case stateChangeTypes.FunctionReset:\n      changes = {\n        highlightedIndex: getDefaultHighlightedIndex(props),\n        isOpen: getDefaultValue(props, 'isOpen', dropdownDefaultStateValues),\n        selectedItem: getDefaultValue(\n          props,\n          'selectedItem',\n          dropdownDefaultStateValues,\n        ),\n        inputValue: getDefaultValue(\n          props,\n          'inputValue',\n          dropdownDefaultStateValues,\n        ),\n      }\n\n      break\n    default:\n      throw new Error('Reducer called without proper action type.')\n  }\n\n  return {\n    ...state,\n    ...changes,\n  }\n}\n/* eslint-enable complexity */\n"
  },
  {
    "path": "src/hooks/testUtils.js",
    "content": "import React from 'react'\nimport {screen, act} from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nexport const items = [\n  'Neptunium',\n  'Plutonium',\n  'Americium',\n  'Curium',\n  'Berkelium',\n  'Californium',\n  'Einsteinium',\n  'Fermium',\n  'Mendelevium',\n  'Nobelium',\n  'Lawrencium',\n  'Rutherfordium',\n  'Dubnium',\n  'Seaborgium',\n  'Bohrium',\n  'Hassium',\n  'Meitnerium',\n  'Darmstadtium',\n  'Roentgenium',\n  'Copernicium',\n  'Nihonium',\n  'Flerovium',\n  'Moscovium',\n  'Livermorium',\n  'Tennessine',\n  'Oganesson',\n]\n\nexport const dataTestIds = {\n  toggleButton: 'toggle-button-id',\n  menu: 'menu-id',\n  item: index => `item-id-${index}`,\n  input: 'input-id',\n  selectedItemPrefix: 'selected-item-id',\n  selectedItem: index => `selected-item-id-${index}`,\n}\n\nexport const defaultIds = {\n  labelId: 'downshift-test-id-label',\n  menuId: 'downshift-test-id-menu',\n  getItemId: index => `downshift-test-id-item-${index}`,\n  toggleButtonId: 'downshift-test-id-toggle-button',\n  inputId: 'downshift-test-id-input',\n}\n\nexport const waitForDebouncedA11yStatusUpdate = (shouldBeCleared = false) =>\n  act(() => jest.advanceTimersByTime(shouldBeCleared ? 700 : 200))\n\nexport const MemoizedItem = React.memo(function Item({\n  index,\n  item,\n  getItemProps,\n  stringItem,\n  ...rest\n}) {\n  return (\n    <li\n      data-testid={dataTestIds.item(index)}\n      key={`${stringItem}${index}`}\n      {...getItemProps({item, index, ...rest})}\n    >\n      {stringItem}\n    </li>\n  )\n})\n\nexport const user = userEvent.setup({delay: null})\n\nexport function getLabel() {\n  return screen.getByText(/choose an element/i)\n}\nexport function getMenu() {\n  return screen.getByRole('listbox')\n}\nexport function getToggleButton() {\n  return screen.getByTestId(dataTestIds.toggleButton)\n}\nexport function getItemAtIndex(index) {\n  return getItems()[index]\n}\nexport function getItems() {\n  return screen.queryAllByRole('option')\n}\nexport async function clickOnItemAtIndex(index) {\n  await user.click(getItemAtIndex(index))\n}\nexport async function clickOnToggleButton() {\n  await user.click(getToggleButton())\n}\nexport async function mouseMoveItemAtIndex(index) {\n  await user.hover(getItemAtIndex(index))\n}\nexport async function mouseLeaveItemAtIndex(index) {\n  await user.unhover(getItemAtIndex(index))\n}\nexport async function keyDownOnToggleButton(keys) {\n  if (document.activeElement !== getToggleButton()) {\n    getToggleButton().focus()\n  }\n\n  await user.keyboard(keys)\n}\nexport function getA11yStatusContainer() {\n  return screen.queryByRole('status')\n}\nexport async function tab(shiftKey = false) {\n  await user.tab({shift: shiftKey})\n}\n\n// format is: [initialIsOpen, defaultIsOpen, props.isOpen]\nexport const initialFocusAndOpenTestCases = [\n  [undefined, undefined, true, true],\n  [true, true, true, true],\n  [true, false, true, true],\n  [false, true, true, true],\n  [false, false, true, true],\n  [true, undefined, undefined, true],\n  [true, false, undefined, true],\n  [true, true, undefined, true],\n  [undefined, true, undefined, true],\n]\n\n// format is: [initialIsOpen, defaultIsOpen, props.isOpen]\nexport const initialNoFocusOrOpenTestCases = [\n  [undefined, undefined, undefined, false],\n  [undefined, undefined, false, false],\n  [true, true, false, false],\n  [true, false, false, false],\n  [false, true, false, false],\n  [false, false, false, false],\n  [false, undefined, undefined, false],\n  [false, false, undefined, false],\n  [false, true, undefined, false],\n  [undefined, false, undefined, false],\n]\n"
  },
  {
    "path": "src/hooks/useCombobox/README.md",
    "content": "# useCombobox\n\n## The problem\n\nYou have a combobox or autocomplete dropdown in your application and you want it\nto be accessible and functional. For consistency reasons you want it to follow\nthe [ARIA design pattern][combobox-aria-example] for a combobox. You also want\nthis solution to be simple to use and flexible so you can tailor it further to\nyour specific needs.\n\n## This solution\n\n`useCombobox` is a React hook that manages all the stateful logic needed to make\nthe combobox functional and accessible. It returns a set of props that are meant\nto be called and their results destructured on the combobox's elements: its\nlabel, toggle button, input, list and list items. The props are similar to the\nones provided by vanilla `<Downshift>` to the children render prop.\n\nThese props are called getter props and their return values are destructured as\na set of ARIA attributes and event listeners. Together with the action props and\nstate props, they create all the stateful logic needed for the combobox to\nimplement the corresponding ARIA pattern. Every functionality needed should be\nprovided out-of-the-box: menu toggle, item selection and up/down movement\nbetween them, screen reader support, highlight by character keys etc.\n\n## Types of Autocomplete\n\nBy default, our implementation and examples illustrate an autocomplete of type\n_list_. This involves performing your own items filtering logic as well as\nkeeping the _aria_autocomplete_ value returned by the\n[getInputProps](#getinputprops).\n\nThere are, in total, 3 types of autocomplete you can opt for, and these are as\nfollows:\n\n- no autocomplete:\n  - [ARIA example][combobox-aria-example-none]\n  - use _aria-autocomplete=\"none\"_ attribute to override the default value from\n    _getInputProps_.\n  - do not implement any filtering logic yourself, just render the listbox\n    items. Basically, take the [code example](#usage) below, remove the useState\n    with items, the onInputValueChange function, pass _colors_ as _items_ prop\n    and render the _colors_ if _isOpen_ is _true_.\n- list autocomplete:\n  - [ARIA example][combobox-aria-example]\n  - just use the [example provided below](#usage) or anything equivalent.\n  - filtering logic inside the menu is done by the _useCombobox_ consumer.\n- list and inline autocomplete:\n  - [ARIA example][combobox-aria-example-both]\n  - use _aria-autocomplete=\"both\"_ attribute to override the default value from\n    _getInputProps_.\n  - filtering logic inside the menu is done by the _useCombobox_ consumer.\n  - inline autocomplete based on the highlighted item in the menu is also\n    performed by the consumer.\n\n## Migration through breaking changes\n\nThe hook received breaking changes related to how it works, as well as the API,\nstarting with v7. They are documented here:\n\n- [v7 migration guide][migration-guide-v7]\n- [v8 migration guide][migration-guide-v8]\n- [v9 migration guide][migration-guide-v9]\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Usage](#usage)\n- [Basic Props](#basic-props)\n  - [items](#items)\n  - [itemToString](#itemtostring)\n  - [onSelectedItemChange](#onselecteditemchange)\n  - [stateReducer](#statereducer)\n- [Advanced Props](#advanced-props)\n  - [isItemDisabled](#isitemdisabled)\n  - [initialSelectedItem](#initialselecteditem)\n  - [initialIsOpen](#initialisopen)\n  - [initialHighlightedIndex](#initialhighlightedindex)\n  - [initialInputValue](#initialinputvalue)\n  - [defaultSelectedItem](#defaultselecteditem)\n  - [defaultIsOpen](#defaultisopen)\n  - [defaultHighlightedIndex](#defaulthighlightedindex)\n  - [defaultInputValue](#defaultinputvalue)\n  - [itemToKey](#itemtokey)\n  - [getA11yStatusMessage](#geta11ystatusmessage)\n  - [onHighlightedIndexChange](#onhighlightedindexchange)\n  - [onIsOpenChange](#onisopenchange)\n  - [onInputValueChange](#oninputvaluechange)\n  - [onStateChange](#onstatechange)\n  - [highlightedIndex](#highlightedindex)\n  - [isOpen](#isopen)\n  - [selectedItem](#selecteditem)\n  - [inputValue](#inputvalue)\n  - [id](#id)\n  - [labelId](#labelid)\n  - [menuId](#menuid)\n  - [toggleButtonId](#togglebuttonid)\n  - [inputId](#inputid)\n  - [getItemId](#getitemid)\n  - [environment](#environment)\n- [stateChangeTypes](#statechangetypes)\n- [Control Props](#control-props)\n- [Returned props](#returned-props)\n  - [prop getters](#prop-getters)\n  - [actions](#actions)\n  - [state](#state)\n- [Event Handlers](#event-handlers)\n  - [Default handlers](#default-handlers)\n  - [Customizing Handlers](#customizing-handlers)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Usage\n\n> [Try it out in the browser][sandbox-example]\n\n```jsx\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useCombobox} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nfunction DropdownCombobox() {\n  const [inputItems, setInputItems] = React.useState(colors)\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n    selectedItem,\n    selectItem,\n  } = useCombobox({\n    items: inputItems,\n    onInputValueChange: ({inputValue}) => {\n      setInputItems(\n        colors.filter(item =>\n          item.toLowerCase().startsWith(inputValue.toLowerCase()),\n        ),\n      )\n    },\n  })\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'column',\n        width: 'fit-content',\n        justifyContent: 'center',\n        marginTop: 100,\n        alignSelf: 'center',\n      }}\n    >\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div>\n        <input\n          style={{padding: '4px'}}\n          {...getInputProps()}\n          data-testid=\"combobox-input\"\n        />\n        <button\n          style={{padding: '4px 8px'}}\n          aria-label=\"toggle menu\"\n          data-testid=\"combobox-toggle-button\"\n          {...getToggleButtonProps()}\n        >\n          {isOpen ? <>&#8593;</> : <>&#8595;</>}\n        </button>\n        <button\n          style={{padding: '4px 8px'}}\n          aria-label=\"toggle menu\"\n          data-testid=\"clear-button\"\n          onClick={() => selectItem(null)}\n        >\n          &#10007;\n        </button>\n      </div>\n      <ul\n        {...getMenuProps()}\n        style={{\n          listStyle: 'none',\n          width: '100%',\n          padding: '0',\n          margin: '4px 0 0 0',\n        }}\n      >\n        {isOpen &&\n          inputItems.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n\nrender(<DropdownCombobox />, document.getElementById('root'))\n```\n\n## Basic Props\n\nThis is the list of props that you should probably know about. There are some\n[advanced props](#advanced-props) below as well.\n\n### items\n\n> `any[]` | _required_\n\nThe main difference from vanilla `Downshift` is that we pass the items we want\nto render to the hook as well. Opening the menu with an item already selected\nmeans the hook has to know in advance what items you plan to render and what is\nthe position of that item in the list. Consequently, there won't be any need for\ntwo state changes: one for opening the menu and one for setting the highlighted\nindex, like in `Downshift`.\n\n### itemToString\n\n> `function(item: any)` | defaults to: `item => (item ? String(item) : '')`\n\nIf your items are stored as, say, objects instead of strings, downshift still\nneeds a string representation for each one. This is required for accessibility\naria-live messages (e.g., after making a selection).\n\n**Note:** This callback _must_ include a null check: it is invoked with `null`\nwhenever the user abandons input via `<Esc>`.\n\n### onSelectedItemChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the selected item was changed. Selection can be performed by\nitem click, Enter Key while item is highlighted or by blurring the menu while an\nitem is highlighted (Tab, Shift-Tab or clicking away).\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `selectedItem` property\n  with the newly selected value. This also has a `type` property which you can\n  learn more about in the [`stateChangeTypes`](#statechangetypes) section. This\n  property will be part of the actions that can trigger a `selectedItem` change,\n  for example `useCombobox.stateChangeTypes.ItemClick`.\n\n### stateReducer\n\n> `function(state: object, actionAndChanges: object)` | optional\n\n**🚨 This is a really handy power feature 🚨**\n\nThis function will be called each time `useCombobox` sets its internal state (or\ncalls your `onStateChange` handler for control props). It allows you to modify\nthe state change that will take place which can give you fine grain control over\nhow the component interacts with user updates. It gives you the current state\nand the state that will be set, and you return the state that you want to set.\n\n- `state`: The full current state of downshift.\n- `actionAndChanges`: Object that contains the action `type`, props needed to\n  return a new state based on that type and the changes suggested by the\n  Downshift default reducer. About the `type` property you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n```javascript\nimport {useCombobox} from 'downshift'\nimport {items} from './utils'\n\nconst {getMenuProps, getItemProps, ...rest} = useCombobox({\n  items,\n  stateReducer,\n})\n\nfunction stateReducer(state, actionAndChanges) {\n  const {type, changes} = actionAndChanges\n  // this prevents the menu from being closed when the user selects an item with 'Enter' or mouse\n  switch (type) {\n    case useCombobox.stateChangeTypes.InputKeyDownEnter:\n    case useCombobox.stateChangeTypes.ItemClick:\n      return {\n        ...changes, // default Downshift new state changes on item selection.\n        isOpen: state.isOpen, // but keep menu open.\n        highlightedIndex: state.highlightedIndex, // with the item highlighted.\n      }\n    default:\n      return changes // otherwise business as usual.\n  }\n}\n```\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example `<button onBlur={handleBlur} />` should be\n> `<button {...getToggleButtonProps({onBlur: handleBlur})} />`). Also, your\n> reducer function should be \"pure.\" This means it should do nothing other than\n> return the state changes you want to have happen.\n\n## Advanced Props\n\n### isItemDisabled\n\n> `function(item: any, index: number)` | defaults to: `(_item, _index) => false`\n\nIf an item needs to be marked as disabled, this function needs to return `true`\nfor that item. Disabled items will be skipped from keyboard navigation, will not\nbe selected and will be marked as disabled for screen readers.\n\n### initialSelectedItem\n\n> `any` | defaults to `null`\n\nPass an item that should be selected when downshift is initialized.\n\n### initialIsOpen\n\n> `boolean` | defaults to `false`\n\nPass a boolean that sets the open state of the menu when downshift is\ninitialized.\n\n### initialHighlightedIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the highlighted item when downshift is\ninitialized.\n\n### initialInputValue\n\n> `string` | defaults to `''`\n\nPass a string that sets the content of the input when downshift is initialized.\n\n### defaultSelectedItem\n\n> `any` | defaults to `null`\n\nPass an item that should be selected when downshift is reset.\n\n### defaultIsOpen\n\n> `boolean` | defaults to `false`\n\nPass a boolean that sets the open state of the menu when downshift is reset or\nwhen an item is selected.\n\n### defaultHighlightedIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the highlighted item when downshift is\nreset or when an item is selected.\n\n### defaultInputValue\n\n> `string` | defaults to `''`\n\nPass a string that sets the content of the input when downshift is reset or when\nan item is selected.\n\n### itemToKey\n\n> `function(item: any)` | defaults to: `item => item`\n\nUsed to determine the uniqueness of an item when searching for the item or\ncomparing the item with another. Returns the item itself, by default, so the\ncomparing/searching is done internally via referential equality.\n\nIf using items as objects and their reference will change during use, you can\nuse the function to generate a unique key for each item, such as an `id` prop.\n\n```js\nfunction itemToKey(item) {\n  return item.id\n}\n```\n\n> This deprecates the \"selectedItemChanged\" prop. If you are using the prop\n> already, make sure you change to \"itemToKey\" as the former is removed in v9. A\n> migration example:\n\n```js\n// initial items.\nconst items = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n// the same items but with different references, for any reason.\nconst newItems = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n\n// previously, if you probably had something like this.\nfunction selectedItemChanged(item1, item2) {\n  return item1.id === item2.id\n}\n\n// moving forward, switch to this one.\nfunction itemToKey(item) {\n  return item.id\n  // and we will do the comparison like: const isChanged = itemToKey(prevSelectedItem) !== itemToKey(nextSelectedItem)\n}\n```\n\n### getA11yStatusMessage\n\n> `function({/* see below */})` | default messages provided in English\n\nThis function is passed as props to a status updating function nested within\nthat allows you to create your own ARIA statuses. It is called when the state\nchanges: `selectedItem`, `highlightedIndex`, `inputValue` or `isOpen`.\n\nThere is no default function provided anymore since v9, so if there's no prop\npassed, no aria live status message is created. An implementation that resembles\nthe previous default is written below, should you want to keep pre v9 behaviour.\n\nWe don't provide this as a default anymore since we consider that screen readers\nhave been significantly improved and they can convey information about items\ncount, possible actions and highlighted items only from the HTML markup, without\nthe need for aria-live regions.\n\n```js\nfunction getA11yStatusMessage(state) {\n  if (!state.isOpen) {\n    return ''\n  }\n  // you need to get resultCount and previousResultCount yourself now, since we don't pass them as arguments anymore\n  const resultCount = items.length\n  const previousResultCount = previousResultCountRef.current\n\n  if (!resultCount) {\n    return 'No results are available.'\n  }\n\n  if (resultCount !== previousResultCount) {\n    return `${resultCount} result${\n      resultCount === 1 ? ' is' : 's are'\n    } available, use up and down arrow keys to navigate. Press Enter key to select.`\n  }\n\n  return ''\n}\n```\n\n### onHighlightedIndexChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the highlighted item was changed. Items can be highlighted\nwhile hovering the mouse over them or by keyboard keys such as Up Arrow, Down\nArrow, Home and End. Items can also be highlighted by hitting character keys\nthat are part of their starting string equivalent.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `highlightedIndex`\n  property with the new value. This also has a `type` property which you can\n  learn more about in the [`stateChangeTypes`](#statechangetypes) section. This\n  property will be part of the actions that can trigger a `highlightedIndex`\n  change, for example `useCombobox.stateChangeTypes.InputKeyDownArrowUp`.\n\n### onIsOpenChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the menu is open or closed. Menu can be open by toggle button\nclick, Enter, Space, Up Arrow or Down Arrow keys. Can be closed by selecting an\nitem, blur (Tab, Shift-Tab or clicking outside), clicking the toggle button\nagain or hitting Escape key.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `isOpen` property with\n  the new value. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section. This property will be\n  part of the actions that can trigger a `isOpen` change, for example\n  `useCombobox.stateChangeTypes.ToggleButtonClick`.\n\n### onInputValueChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the value in the input text changes. The input value should\nchange like any input of type text, at any character key press, `Space`,\n`Backspace`, `Escape` etc.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `inputValue` property\n  with the new value. This also has a `type` property which you can learn more\n  about in the [`stateChangeTypes`](#statechangetypes) section. This property\n  will be part of the actions that can trigger a `inputValue` change, for\n  example `useCombobox.stateChangeTypes.InputChange`.\n\n### onStateChange\n\n> `function(changes: object)` | optional, no useful default\n\nThis function is called anytime the internal state changes. This can be useful\nif you're using downshift as a \"controlled\" component, where you manage some or\nall of the state (e.g., isOpen, selectedItem, highlightedIndex, etc) and then\npass it as props, rather than letting downshift control all its state itself.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n> Tip: This function will be called any time _any_ state is changed. The best\n> way to determine whether any particular state was changed, you can use\n> `changes.hasOwnProperty('propName')` or use the `on[statePropKey]Change` props\n> described above.\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<button onBlur={handleBlur} />` should be\n> `<button {...getToggleButtonProps({onBlur: handleBlur})} />`).\n\n### highlightedIndex\n\n> `number` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe index of the item that should be highlighted when menu is open.\n\n### isOpen\n\n> `boolean` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe open state of the menu.\n\n### selectedItem\n\n> `any` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe item that should be selected.\n\n### inputValue\n\n> `string` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe value to be displayed in the text input.\n\n🚨 Important 🚨\n\nIf you use `onInputValueChange`, `onStateChange` or anything similar in order to\nupdate a state variable that will end up controlling `inputValue`, you will\nencounter a\n[cursor jump issue](https://github.com/downshift-js/downshift/issues/1108).\nThere's no way to properly fix this in our current `React.useReducer` setup, so\nin order to work around the issue, consider the change below.\n\n```jsx\nconst [value, setValue] = useState('')\nconst {getInputProps} = useCombobox({\n  items: [],\n  inputValue: value,\n  // change this:\n  onInputValueChange: ({inputValue}) => {\n    setValue(inputValue)\n  },\n})\n\nreturn (\n  <input\n    {...getInputProps({\n      // to this:\n      onChange: e => {\n        setValue(e.target.value)\n      },\n    })}\n  />\n)\n```\n\n### id\n\n> `string` | defaults to a generated ID\n\nUsed to generate the first part of the `Downshift` id on the elements. You can\noverride this `id` with one of your own, provided as a prop, or you can override\nthe `id` for each element altogether using the props below.\n\n### labelId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`label`) you use\n[`getLabelProps`](#getlabelprops) with.\n\n### menuId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`ul`) you use\n[`getMenuProps`](#getmenuprops) with.\n\n### toggleButtonId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`button`) you use\n[`getToggleButtonProps`](#gettogglebuttonprops) with.\n\n### inputId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`input`) you use\n[`getInputProps`](#getmenuprops) with.\n\n### getItemId\n\n> `function(index)` | defaults to a function that generates an ID based on the\n> index\n\nUsed for `aria` attributes and the `id` prop of the element (`li`) you use\n[`getItemProps`](#getitemprops) with.\n\n### environment\n\n> `window` | defaults to `window`\n\nThis prop is only useful if you're rendering downshift within a different\n`window` context from where your JavaScript is running; for example, an iframe\nor a shadow-root. If the given context is lacking `document` and/or\n`add|removeEventListener` on its prototype (as is the case for a shadow-root)\nthen you will need to pass in a custom object that is able to provide\n[access to these properties](https://gist.github.com/Rendez/1dd55882e9b850dd3990feefc9d6e177)\nfor downshift.\n\n## stateChangeTypes\n\nThere are a few props that expose changes to state\n([`onStateChange`](#onstatechange) and [`stateReducer`](#statereducer)). For you\nto make the most of these APIs, it's important for you to understand why state\nis being changed. To accomplish this, there's a `type` property on the `changes`\nobject you get. This `type` corresponds to a `stateChangeTypes` property.\n\nThe list of all possible values this `type` property can take is defined in\n[this file][state-change-file] and is as follows:\n\n- `useCombobox.stateChangeTypes.InputKeyDownArrowDown`\n- `useCombobox.stateChangeTypes.InputKeyDownArrowUp`\n- `useCombobox.stateChangeTypes.InputKeyDownEscape`\n- `useCombobox.stateChangeTypes.InputKeyDownHome`\n- `useCombobox.stateChangeTypes.InputKeyDownEnd`\n- `useCombobox.stateChangeTypes.InputKeyDownPageUp`\n- `useCombobox.stateChangeTypes.InputKeyDownPadeDown`\n- `useCombobox.stateChangeTypes.InputKeyDownEnter`\n- `useCombobox.stateChangeTypes.InputChange`\n- `useCombobox.stateChangeTypes.InputClick`\n- `useCombobox.stateChangeTypes.InputBlur`\n- `useCombobox.stateChangeTypes.MenuMouseLeave`\n- `useCombobox.stateChangeTypes.ItemMouseMove`\n- `useCombobox.stateChangeTypes.ItemClick`\n- `useCombobox.stateChangeTypes.ToggleButtonClick`\n- `useCombobox.stateChangeTypes.FunctionToggleMenu`\n- `useCombobox.stateChangeTypes.FunctionOpenMenu`\n- `useCombobox.stateChangeTypes.FunctionCloseMenu`\n- `useCombobox.stateChangeTypes.FunctionSetHighlightedIndex`\n- `useCombobox.stateChangeTypes.FunctionSelectItem`\n- `useCombobox.stateChangeTypes.FunctionSetInputValue`\n- `useCombobox.stateChangeTypes.FunctionReset`\n\nSee [`stateReducer`](#statereducer) for a concrete example on how to use the\n`type` property.\n\n## Control Props\n\nDownshift manages its own state internally and calls your\n`onSelectedItemChange`, `onIsOpenChange`, `onHighlightedIndexChange`,\n`onInputChange` and `onStateChange` handlers with any relevant changes. The\nstate that downshift manages includes: `isOpen`, `selectedItem`, `inputValue`\nand `highlightedIndex`. Returned action function (read more below) can be used\nto manipulate this state and can likely support many of your use cases.\n\nHowever, if more control is needed, you can pass any of these pieces of state as\na prop (as indicated above) and that state becomes controlled. As soon as\n`this.props[statePropKey] !== undefined`, internally, `downshift` will determine\nits state based on your prop's value rather than its own internal state. You\nwill be required to keep the state up to date (this is where `onStateChange`\ncomes in really handy), but you can also control the state from anywhere, be\nthat state from other components, `redux`, `react-router`, or anywhere else.\n\n> Note: This is very similar to how normal controlled components work elsewhere\n> in react (like `<input />`). If you want to learn more about this concept, you\n> can learn about that from the [Advanced React Component Patterns\n> course][advanced-react-component-patterns-course]\n\n## Returned props\n\nYou use the hook like so:\n\n```javascript\nimport {useCombobox} from 'downshift'\nimport {items} from './utils'\n\nconst {getInputProps, reset, ...rest} = useCombobox({\n  items,\n  ...otherProps,\n})\n\nreturn (\n  <div>\n    <input {...getInputProps()} />\n    {/* render the menu and items */}\n    {/* render a button that resets the select to defaults */}\n    <button\n      onClick={() => {\n        reset()\n      }}\n    >\n      Reset\n    </button>\n  </div>\n)\n```\n\n> NOTE: In this example we used both a getter prop `getInputProps` and an action\n> prop `reset`. The properties of `useCombobox` can be split into three\n> categories as indicated below:\n\n### prop getters\n\n> See [the blog post about prop getters][blog-post-prop-getters]\n\n> NOTE: These prop-getters provide `aria-` attributes which are very important\n> to your component being accessible. It's recommended that you utilize these\n> functions and apply the props they give you to your components.\n\nThese functions are used to apply props to the elements that you render. This\ngives you maximum flexibility to render what, when, and wherever you like. You\ncall these on the element in question, for example on the toggle button:\n`<button {...getToggleButtonProps()}`. It's advisable to pass all your props to\nthat function rather than applying them on the element yourself to avoid your\nprops being overridden (or overriding the props returned). For example:\n`getToggleButtonProps({onKeyDown(event) {console.log(event)}})`.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property               | type           | description                                                                                    |\n| ---------------------- | -------------- | ---------------------------------------------------------------------------------------------- |\n| `getToggleButtonProps` | `function({})` | returns the props you should apply to any menu toggle button element you render.               |\n| `getItemProps`         | `function({})` | returns the props you should apply to any menu item elements you render.                       |\n| `getLabelProps`        | `function({})` | returns the props you should apply to the `label` element that you render.                     |\n| `getMenuProps`         | `function({})` | returns the props you should apply to the `ul` element (or root of your menu) that you render. |\n| `getInputProps`        | `function({})` | returns the props you should apply to the `input` element that you render.                     |\n\n#### `getLabelProps`\n\nThis method should be applied to the `label` you render. It will generate an\n`id` that will be used to label the toggle button and the menu.\n\nThere are no required properties for this method.\n\n> Note: For accessibility purposes, calling this method is highly recommended.\n\n#### `getMenuProps`\n\nThis method should be applied to the element which contains your list of items.\nTypically, this will be a `<div>` or a `<ul>` that surrounds a `map` expression.\nThis handles the proper ARIA roles and attributes.\n\nOptional properties:\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getMenuProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<ul ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n  Please keep in mind that menus, for accessiblity purposes, should always be\n  rendered, regardless of whether you hide it or not. Otherwise, `getMenuProps`\n  may throw error if you unmount and remount the menu.\n\n- `aria-label`: By default the menu will add an `aria-labelledby` that refers to\n  the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getMenuProps`. **Please use it with extreme care and only if you are absolutely\nsure that the ref is correctly forwarded otherwise `useCombobox` will\nunexpectedly fail.**\n\n```jsx\nconst {getMenuProps} = useCombobox({items})\nconst ui = (\n  <ul {...getMenuProps()}>\n    {!isOpen\n      ? null\n      : items.map((item, index) => (\n          <li {...getItemProps({item, index, key: item.id})}>{item.name}</li>\n        ))}\n  </ul>\n)\n```\n\n> Note that for accessibility reasons it's best if you always render this\n> element whether or not downshift is in an `isOpen` state.\n\n#### `getItemProps`\n\nThe props returned from calling this function should be applied to any menu\nitems you render.\n\n**This is an impure function**, so it should only be called when you will\nactually be applying the props to an item.\n\n<details>\n\n<summary>What do you mean by impure function?</summary>\n\nBasically just don't do this:\n\n```jsx\nitems.map((item, index) => {\n  const props = getItemProps({item, index}) // we're calling it here\n  if (!shouldRenderItem(item)) {\n    return null // but we're not using props, and downshift thinks we are...\n  }\n  return <div {...props} />\n})\n```\n\nInstead, you could do this:\n\n```jsx\nitems.filter(shouldRenderItem).map(item => <div {...getItemProps({item})} />)\n```\n\n</details>\n\nRequired properties:\n\nThe main difference from vanilla `Downshift` is that we require the items as\nprops before rendering. The reason is to open the menu with items already\nhighlighted, and we need to know the items before the actual render. It is still\nrequired to pass either `item` or `index` to `getItemProps`.\n\n- `item`: this is the item data that will be selected when the user selects a\n  particular item.\n- `index`: This is how `downshift` keeps track of your item when updating the\n  `highlightedIndex` as the user keys around. By default, `downshift` will\n  assume the `index` is the order in which you're calling `getItemProps`. This\n  is often good enough, but if you find odd behavior, try setting this\n  explicitly. It's probably best to be explicit about `index` when using a\n  windowing library like `react-virtualized`.\n\nOptional properties:\n\n- `ref`: if you need to access the item element via a ref object, you'd call the\n  function like this: `getItemProps({ref: yourItemRef})`. As a result, the item\n  element will receive a composed `ref` property, which guarantees that both\n  your code and `useCombobox` use the same correct reference to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getItemProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<li ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n#### `getToggleButtonProps`\n\nCall this and apply the returned props to a `button`. It allows you to toggle\nthe `Menu` component.\n\nOptional properties:\n\n- `ref`: if you need to access the button element via a ref object, you'd call\n  the function like this: `getToggleButton({ref: yourButtonRef})`. As a result,\n  the button element will receive a composed `ref` property, which guarantees\n  that both your code and `useCombobox` use the same correct reference to the\n  element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getToggleButton({refKey: 'innerRef'})`\n  and your composite component would forward like:\n  `<button ref={props.innerRef} />`. However, if you are just rendering a\n  primitive component like `<div>`, there is no need to specify this property.\n  It defaults to `ref`.\n\n- `disabled`: If this is set to `true`, then all of the downshift button event\n  handlers will be omitted (it won't toggle the menu when clicked).\n\n```jsx\nconst {getToggleButtonProps} = useCombobox({items})\nconst myButton = (\n  <button {...getToggleButtonProps()}>Click me</button>\n  {/* menu and items */}\n)\n```\n\n#### `getInputProps`\n\nThis method should be applied to the `input` you render. It is recommended that\nyou pass all props as an object to this method which will compose together any\nof the event handlers you need to apply to the `input` while preserving the ones\nthat `downshift` needs to apply to make the `input` behave.\n\nThere are no required properties for this method.\n\nOptional properties:\n\n- `disabled`: If this is set to true, then no event handlers will be returned\n  from `getInputProps` and a `disabled` prop will be returned (effectively\n  disabling the input).\n\n- `ref`: if you need to access the input element via a ref object, you'd call\n  the function like this: `getInputProps({ref: yourInputRef})`. As a result, the\n  input element will receive a composed `ref` property, which guarantees that\n  both your code and `useCombobox` use the same correct reference to the\n  element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getInputProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<input ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n- `aria-label`: By default the input will add an `aria-labelledby` that refers\n  to the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getInput`. **Please use it with extreme care and only if you are absolutely\nsure that the ref is correctly forwarded otherwise `useCombobox` will\nunexpectedly fail.**\n\n### actions\n\nThese are functions you can call to change the state of the downshift\n`useCombobox` hook.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property              | type                      | description                                           |\n| --------------------- | ------------------------- | ----------------------------------------------------- |\n| `closeMenu`           | `function()`              | closes the menu                                       |\n| `openMenu`            | `function()`              | opens the menu                                        |\n| `selectItem`          | `function(item: any)`     | selects the given item                                |\n| `setHighlightedIndex` | `function(index: number)` | call to set a new highlighted index                   |\n| `setInputValue`       | `function(value: string)` | call to set a new value in the input                  |\n| `toggleMenu`          | `function()`              | toggle the menu open state                            |\n| `reset`               | `function()`              | this resets downshift's state to a reasonable default |\n\n### state\n\nThese are values that represent the current state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property           | type      | description                       |\n| ------------------ | --------- | --------------------------------- |\n| `highlightedIndex` | `number`  | the currently highlighted item    |\n| `isOpen`           | `boolean` | the menu open state               |\n| `selectedItem`     | `any`     | the currently selected item input |\n| `inputValue`       | `string`  | the value in the input            |\n\n## Event Handlers\n\nDownshift has a few events for which it provides implicit handlers. Several of\nthese handlers call `event.preventDefault()`. Their additional functionality is\ndescribed below.\n\n### Default handlers\n\n#### Toggle Button\n\n- `Click`: If the menu is not displayed, it will open it. Otherwise it will\n  close it. It will additionally move focus on the input in order for screen\n  readers to correctly narrate which item is currently highlighted. If there is\n  already an item selected, the menu will be opened with that item already\n  highlighted.\n- `Enter`: Has the same effect as `Click`. Button not in the tab order by\n  default.\n- `Space`: Has the same effect as `Click`. Button not in the tab order by\n  default.\n\n#### Input\n\n- `ArrowDown`: Moves `highlightedIndex` one position down. When reaching the\n  last option, `ArrowDown` will move `highlightedIndex` to first position.\n- `ArrowUp`: Moves `highlightedIndex` one position up. When reaching the first\n  option, `ArrowUp` will move `highlightedIndex` to last position.\n- `Alt+ArrowDown`: If the menu is closed, it will open it, without highlighting\n  any item.\n- `Alt+ArrowUp`: If the menu is open, it will close it and will select the item\n  that was highlighted.\n- `CharacterKey`: Will change the `inputValue` according to the value visible in\n  the `<input>`. `Backspace` or `Space` trigger the same event.\n- `End`: If the menu is open, it will highlight the last item in the list.\n- `Home`: If the menu is open, it will highlight the first item in the list.\n- `PageUp`: If the menu is open, it will move the highlight the item 10\n  positions before the current selection.\n- `PageDown`: If the menu is open, it will move the highlight the item 10\n  positions after the current selection.\n- `Enter`: If there is a highlighted option, it will select it and close the\n  menu.\n- `Escape`: It will close the menu if open. If the menu is closed, it will clear\n  selection: the value in the `input` will become an empty string and the item\n  stored as `selectedItem` will become `null`.\n- `Click`: If the menu is closed, it will open it. If the menu is open, it will\n  close it.\n- `Blur(Tab, Shift+Tab)`: It will close the menu and select the highlighted\n  item, if any. The focus will move naturally to the next/previous element in\n  the Tab order.\n- `Blur(mouse click outside)`: It will close the menu without selecting any\n  element, even if there is one highlighted.\n\n#### Menu\n\n- `MouseLeave`: Will clear the value of the `highlightedIndex` if it was set.\n\n#### Item\n\n- `Click`: It will select the item, close the menu and move focus to the toggle\n  button (unless `defaultIsOpen` is true).\n- `MouseOver`: It will highlight the item.\n\n### Customizing Handlers\n\nYou can provide your own event handlers to `useCombobox` which will be called\nbefore the default handlers:\n\n```javascript\nconst items = [...] // items here.\nconst {getMenuProps} = useCombobox({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n      },\n    })}\n  />\n)\n```\n\nIf you would like to prevent the default handler behavior in some cases, you can\nset the event's `preventDownshiftDefault` property to `true`:\n\n```javascript\nconst {getMenuProps} = useCombobox({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n        if (event.key === 'Enter') {\n          // Prevent Downshift's default 'Enter' behavior.\n          event.nativeEvent.preventDownshiftDefault = true\n\n          // your handler code\n        }\n      },\n    })}\n  />\n)\n```\n\nIf you would like to completely override Downshift's behavior for a handler, in\nfavor of your own, you can bypass prop getters:\n\n```javascript\nconst items = [...] // items here.\nconst {getMenuProps} = useCombobox({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps()}\n    onKeyDown={event => {\n      // your custom keyDown handler here.\n    }}\n  />\n)\n```\n\n## Examples\n\nUsage examples are kept on the [downshift docsite][docsite] and also on [the\nsandbox repo][sandbox-repo]. Each example has a link to its own Codesandbox\nversion, so check the docs.\n\nIt can be a great contributing opportunity to provide relevant use cases as\ndocsite examples. If you have such an example, please create an issue with the\nsuggestion and the Codesandbox for it, and we will take it from there.\n\n[combobox-aria-example]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-list.html\n[combobox-aria-example-none]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-none.html\n[combobox-aria-example-both]:\n  https://www.w3.org/WAI/ARIA/apg/example-index/combobox/combobox-autocomplete-both.html\n[sandbox-example]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseCombobox%2Fbasic-usage.js&moduleview=1\n[state-change-file]:\n  https://github.com/downshift-js/downshift/blob/master/src/hooks/useCombobox/stateChangeTypes.js\n[blog-post-prop-getters]:\n  https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf\n[docsite]: https://downshift-js.com/\n[sandbox-repo]: https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1\n[advanced-react-component-patterns-course]:\n  https://github.com/downshift-js/downshift#advanced-react-component-patterns-course\n[migration-guide-v7]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md#usecombobox\n[migration-guide-v8]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V8.md\n[migration-guide-v9]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V9.md\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/__snapshots__/getInputProps.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`getInputProps event handlers on key down arrow down defaultHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Americium,\n    2,\n  ],\n  [\n    Neptunium,\n    0,\n  ],\n]\n`;\n\nexports[`getInputProps event handlers on key down arrow down initialHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Neptunium,\n    0,\n  ],\n]\n`;\n\nexports[`getInputProps event handlers on key down arrow up defaultHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Americium,\n    2,\n  ],\n  [\n    Oganesson,\n    25,\n  ],\n]\n`;\n\nexports[`getInputProps event handlers on key down arrow up initialHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Oganesson,\n    25,\n  ],\n]\n`;\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/getInputProps.test.js",
    "content": "import * as React from 'react'\nimport {act, renderHook, fireEvent, createEvent} from '@testing-library/react'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport {noop} from '../../../utils-ts'\nimport {\n  renderUseCombobox,\n  renderCombobox,\n  items,\n  defaultIds,\n  changeInputValue,\n  getInput,\n  getItems,\n  keyDownOnInput,\n  mouseLeaveItemAtIndex,\n  mouseMoveItemAtIndex,\n  tab,\n  clickOnInput,\n  initialFocusAndOpenTestCases,\n  initialNoFocusOrOpenTestCases,\n} from '../testUtils'\nimport utils from '../../utils'\nimport useCombobox from '..'\n\ndescribe('getInputProps', () => {\n  describe('hook props', () => {\n    test(\"assign 'combobox' to role\", () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps.role).toEqual('combobox')\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps.id).toEqual(defaultIds.inputId)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const props = {\n        inputId: 'my-custom-input-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps.id).toEqual(props.inputId)\n    })\n\n    test(\"assign 'list' to 'aria-autocomplete'\", () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-autocomplete']).toEqual('list')\n    })\n\n    test(\"assign 'off' to autoComplete\", () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps.autoComplete).toEqual('off')\n    })\n\n    test('assign default value to aria-controls', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-controls']).toEqual(`${defaultIds.menuId}`)\n    })\n\n    test('assign custom value passed by user to aria-controls', () => {\n      const props = {\n        menuId: 'my-custom-menu-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-controls']).toEqual(`${props.menuId}`)\n    })\n\n    test('assign id of highlighted item to aria-activedescendant if item is highlighted and menu is open', () => {\n      const {result} = renderUseCombobox({highlightedIndex: 2, isOpen: true})\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-activedescendant']).toEqual(\n        defaultIds.getItemId(2),\n      )\n    })\n\n    test('assign no aria-activedescendant if item is highlighted and menu is closed', () => {\n      const {result} = renderUseCombobox({highlightedIndex: 2})\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-activedescendant']).toBe('')\n    })\n\n    test('do not assign aria-activedescendant if no item is highlighted', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-activedescendant']).toBe('')\n    })\n\n    test('do not assign aria-activedescendant if no item is highlighted and menu is open', () => {\n      const {result} = renderUseCombobox({isOpen: true})\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-activedescendant']).toBe('')\n    })\n\n    test('assign default value to aria-labelledby', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-labelledby']).toEqual(`${defaultIds.labelId}`)\n    })\n\n    test('assign custom value passed by user to aria-labelledby', () => {\n      const props = {\n        labelId: 'my-custom-label-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-labelledby']).toEqual(`${props.labelId}`)\n    })\n\n    test(\"assign 'false' value to aria-expanded when menu is closed\", () => {\n      const {result} = renderUseCombobox({isOpen: false})\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-expanded']).toEqual(false)\n    })\n\n    test(\"assign 'true' value to aria-expanded when menu is open\", () => {\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef} = result.current.getInputProps()\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n      })\n\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps['aria-expanded']).toEqual(true)\n    })\n\n    test('handlers are not returned if input is disabled', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps({\n        disabled: true,\n      })\n\n      expect(inputProps.onChange).toBeUndefined()\n      expect(inputProps.onKeyDown).toBeUndefined()\n      expect(inputProps.onBlur).toBeUndefined()\n      expect(inputProps.onClick).toBeUndefined()\n      expect(inputProps.disabled).toBe(true)\n    })\n\n    test(\"do not assign 'aria-labelledby' if it has aria-label\", () => {\n      const ariaLabel = 'not so fast'\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps({'aria-label': ariaLabel})\n\n      expect(inputProps['aria-labelledby']).toBeUndefined()\n      expect(inputProps['aria-label']).toBe(ariaLabel)\n    })\n\n    test('handlers are returned if input is not disabled', () => {\n      const {result} = renderUseCombobox()\n      const inputProps = result.current.getInputProps()\n\n      expect(inputProps.onChange).toBeDefined()\n      expect(inputProps.onKeyDown).toBeDefined()\n      expect(inputProps.onClick).toBeDefined()\n      expect(inputProps.onBlur).toBeDefined()\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseCombobox()\n\n      expect(result.current.getInputProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('custom ref passed by the user is used', () => {\n      const {result} = renderUseCombobox()\n      const refFn = jest.fn()\n      const inputNode = {}\n\n      act(() => {\n        const {ref} = result.current.getInputProps({ref: refFn})\n\n        ref(inputNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(inputNode)\n    })\n\n    test('custom ref with custom name passed by the user is used', () => {\n      const {result} = renderUseCombobox()\n      const refFn = jest.fn()\n      const inputNode = {}\n\n      act(() => {\n        const {blablaRef} = result.current.getInputProps({\n          refKey: 'blablaRef',\n          blablaRef: refFn,\n        })\n\n        blablaRef(inputNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(inputNode)\n    })\n\n    test('event handler onKeyDown is called along with downshift handler', () => {\n      const userOnKeyDown = jest.fn()\n      const {result} = renderUseCombobox()\n\n      const {ref: inputRef, onKeyDown} = result.current.getInputProps({\n        onKeyDown: userOnKeyDown,\n      })\n      inputRef({focus: noop})\n      act(() => {\n        result.current.toggleMenu()\n      })\n      act(() => {\n        onKeyDown({key: 'Escape', preventDefault: noop, stopPropagation: noop})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test(\"event handler onKeyDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnKeyDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onKeyDown} = result.current.getInputProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onKeyDown({key: 'Escape', preventDefault: noop})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('event handler onBlur is called along with downshift handler', () => {\n      const userOnBlur = jest.fn()\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {ref: inputRef, onBlur} = result.current.getInputProps({\n          onBlur: userOnBlur,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onBlur({})\n      })\n\n      expect(userOnBlur).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test(\"event handler onBlur is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnBlur = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onBlur} = result.current.getInputProps({\n          onBlur: userOnBlur,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onBlur({preventDefault: noop})\n      })\n\n      expect(userOnBlur).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('event handler onChange is called along with downshift handler', () => {\n      const userOnChange = jest.fn()\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onChange} = result.current.getInputProps({\n          onChange: userOnChange,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onChange({target: {value: 'lalaland'}})\n      })\n\n      expect(userOnChange).toHaveBeenCalledTimes(1)\n      expect(result.current.inputValue).toBe('lalaland')\n    })\n\n    test(\"event handler onChange is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnChange = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onChange} = result.current.getInputProps({\n          onChange: userOnChange,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onChange({target: {value: 'lalaland'}})\n      })\n\n      expect(userOnChange).toHaveBeenCalledTimes(1)\n      expect(result.current.inputValue).toBe('')\n    })\n\n    test('event handler onInput is called along with downshift handler', () => {\n      const userOnInput = jest.fn()\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onChange} = result.current.getInputProps({\n          onInput: userOnInput,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onChange({target: {value: 'a'}})\n      })\n\n      expect(userOnInput).toHaveBeenCalledTimes(1)\n      expect(result.current.inputValue).toBe('a')\n    })\n\n    test(\"event handler onInput is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnInput = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onChange} = result.current.getInputProps({\n          onInput: userOnInput,\n        })\n\n        inputRef({focus: noop})\n        result.current.toggleMenu()\n        onChange({target: {value: 'a'}})\n      })\n\n      expect(userOnInput).toHaveBeenCalledTimes(1)\n      expect(result.current.inputValue).toBe('')\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onClick} = result.current.getInputProps({\n          onClick: userOnClick,\n        })\n\n        inputRef({focus: noop})\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {ref: inputRef, onClick} = result.current.getInputProps({\n          onClick: userOnClick,\n        })\n\n        inputRef({focus: noop})\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n  })\n\n  describe('initial focus', () => {\n    test.each(initialFocusAndOpenTestCases)(\n      'is grabbed when initialIsOpen: %s, defaultIsOpen: %s, props.isOpen: %s',\n      (initialIsOpen, defaultIsOpen, isOpen) => {\n        renderCombobox({isOpen, defaultIsOpen, initialIsOpen})\n\n        expect(getInput()).toHaveFocus()\n        expect(getItems()).toHaveLength(items.length)\n      },\n    )\n\n    test.each(initialNoFocusOrOpenTestCases)(\n      'is not grabbed when initialIsOpen: %s, defaultIsOpen: %s, props.isOpen: %s',\n      (initialIsOpen, defaultIsOpen, isOpen) => {\n        renderCombobox({isOpen, defaultIsOpen, initialIsOpen})\n\n        expect(getInput()).not.toHaveFocus()\n        expect(getItems()).toHaveLength(0)\n      },\n    )\n  })\n\n  describe('event handlers', () => {\n    describe('on change', () => {\n      test('should open the menu and keep the input value', async () => {\n        renderCombobox()\n\n        await changeInputValue('california')\n\n        expect(getItems()).toHaveLength(items.length)\n        expect(getInput()).toHaveValue('california')\n      })\n\n      test('should remove the highlightedIndex', async () => {\n        renderCombobox({initialHighlightedIndex: 2})\n\n        await changeInputValue('california')\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('should reset to defaultHighlightedIndex', async () => {\n        const defaultHighlightedIndex = 2\n        renderCombobox({defaultHighlightedIndex})\n\n        await changeInputValue('a')\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n\n        await keyDownOnInput('{ArrowDown}')\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex + 1),\n        )\n\n        await changeInputValue('a')\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('should not reset to defaultHighlightedIndex if disabled', async () => {\n        const defaultHighlightedIndex = 2\n        renderCombobox({\n          defaultHighlightedIndex,\n          isItemDisabled(_item, index) {\n            return index === defaultHighlightedIndex\n          },\n        })\n\n        await keyDownOnInput('{ArrowDown}')\n        await changeInputValue('a')\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n\n    describe('on key down', () => {\n      describe('arrow up', () => {\n        test('it prevents the default event behavior', () => {\n          renderCombobox()\n          const input = getInput()\n          const keyDownEvent = createEvent.keyDown(input, {key: 'ArrowUp'})\n\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('it does not open or highlight anything if there are no options', async () => {\n          renderCombobox({items: []})\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it does not highlight anything if there are no options', async () => {\n          renderCombobox({\n            items: [],\n            isOpen: true,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it opens the menu and highlights the last option', async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('it opens the closed menu with selected option highlighted', async () => {\n          const selectedIndex = 4\n          renderCombobox({\n            initialSelectedItem: items[selectedIndex],\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(selectedIndex),\n          )\n        })\n\n        test('it opens the closed menu at initialHighlightedIndex, but on first arrow up only', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            initialIsOpen: true,\n          })\n\n          await keyDownOnInput('{Escape}')\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('it opens the closed menu at defaultHighlightedIndex, on every arrow up', async () => {\n          const defaultHighlightedIndex = 3\n          renderCombobox({\n            defaultHighlightedIndex,\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n\n          await keyDownOnInput('{Escape}')\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('initialHighlightedIndex is ignored if item is disabled', async () => {\n          const initialHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === initialHighlightedIndex,\n            )\n          renderCombobox({\n            initialHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()\n        })\n\n        test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n          const initialHighlightedIndex = 0\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return items.indexOf(item) === initialHighlightedIndex\n            },\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n          const defaultHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === defaultHighlightedIndex,\n            )\n          renderCombobox({\n            defaultHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()\n        })\n\n        test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n          const initialHighlightedIndex = 1\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return [\n                initialHighlightedIndex,\n                defaultHighlightedIndex,\n              ].includes(items.indexOf(item))\n            },\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('it opens the closed menu and keeps focus on the combobox', async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveFocus()\n        })\n\n        test('it highlights the last option number if none is highlighted', async () => {\n          renderCombobox({isOpen: true})\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('it highlights the previous item', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex - 1),\n          )\n        })\n\n        test('with Alt it selects the item and closes the menu', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveValue(items[initialHighlightedIndex])\n        })\n\n        test('with Alt it opens the menu without any additional change', async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(getItems()).toHaveLength(items.length)\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('with Alt selects highlighted item and resets to user defaults', async () => {\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(input).toHaveValue(items[defaultHighlightedIndex])\n          expect(getItems()).toHaveLength(items.length)\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('with Alt closes the menu without resetting to user defaults if no item is highlighted', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          })\n          const input = getInput()\n\n          await mouseMoveItemAtIndex(defaultHighlightedIndex)\n          await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n          await keyDownOnInput('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(input).toHaveValue(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('with Alt closes the menu without resetting to user defaults if lhe list is empty', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(input).toHaveValue(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('will continue from 0 to last item', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: 0,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('skips disabled items', async () => {\n          renderCombobox({\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n            initialIsOpen: true,\n            initialHighlightedIndex: 3,\n          })\n\n          await keyDownOnInput('{ArrowUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n      })\n\n      describe('arrow down', () => {\n        test('it prevents the default event behavior', () => {\n          renderCombobox()\n          const input = getInput()\n          const keyDownEvent = createEvent.keyDown(input, {key: 'ArrowDown'})\n\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('it does not opne on highlight anything if there are no options', async () => {\n          renderCombobox({items: []})\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it does not highlight anything if there are no options', async () => {\n          renderCombobox({\n            items: [],\n            isOpen: true,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test(\"it opens the menu and highlights option number '0'\", async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('opens the closed menu with selected option highlighted', async () => {\n          const selectedIndex = 4\n          renderCombobox({\n            initialSelectedItem: items[selectedIndex],\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(selectedIndex),\n          )\n        })\n\n        test('it opens the menu and highlights initialHighlightedIndex only once', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            initialIsOpen: true,\n          })\n\n          await keyDownOnInput('{Escape}')\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('it opens the menu and highlights defaultHighlightedIndex always', async () => {\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            defaultHighlightedIndex,\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n          expect(getItems()).toHaveLength(items.length)\n\n          await keyDownOnInput('{Escape}')\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('initialHighlightedIndex is ignored if item is disabled', async () => {\n          const initialHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === initialHighlightedIndex,\n            )\n          renderCombobox({\n            initialHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()\n        })\n\n        test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n          const initialHighlightedIndex = 0\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return items.indexOf(item) === initialHighlightedIndex\n            },\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n          const defaultHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === defaultHighlightedIndex,\n            )\n          renderCombobox({\n            defaultHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()\n        })\n\n        test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n          const initialHighlightedIndex = 1\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return [\n                initialHighlightedIndex,\n                defaultHighlightedIndex,\n              ].includes(items.indexOf(item))\n            },\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('opens the closed menu and keeps focus on the button', async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveFocus()\n        })\n\n        test(\"it highlights option number '0' if none is highlighted\", async () => {\n          renderCombobox({isOpen: true})\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('it highlights the next item', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 1),\n          )\n        })\n\n        test('with Alt it opens the menu and highlights no item', async () => {\n          renderCombobox()\n\n          await keyDownOnInput('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('with Alt it opens the menu and highlights with selected item highlighted', async () => {\n          const itemIndex = 8\n          renderCombobox({selectedItem: items[itemIndex]})\n\n          await keyDownOnInput('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(itemIndex),\n          )\n        })\n\n        test('with Alt it highlights the next item without any additional change', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({initialHighlightedIndex, isOpen: true})\n\n          await keyDownOnInput('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 1),\n          )\n        })\n\n        test('will continue from last item to 0', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: items.length - 1,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('skips disabled items', async () => {\n          renderCombobox({\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n            initialIsOpen: true,\n            initialHighlightedIndex: 1,\n          })\n\n          await keyDownOnInput('{ArrowDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(3),\n          )\n        })\n      })\n\n      describe('end', () => {\n        test('highlights the last option number', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n          })\n\n          await keyDownOnInput('{End}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('prevents the default event and calls dispatch only when menu is open', () => {\n          const {rerender, renderSpy} = renderCombobox({isOpen: false})\n          const input = getInput()\n          const keyDownEvent = createEvent.keyDown(input, {key: 'End'})\n\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n          expect(renderSpy).not.toHaveBeenCalled()\n\n          rerender({isOpen: true})\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n          expect(renderSpy).toHaveBeenCalledTimes(1)\n        })\n\n        test('highlights previous non-disabled option', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: 0,\n            isItemDisabled(_item, index) {\n              return index === items.length - 1\n            },\n          })\n\n          await keyDownOnInput('{End}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 2),\n          )\n        })\n      })\n\n      describe('home', () => {\n        test('home highlights the first option number', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n          })\n\n          await keyDownOnInput('{Home}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('home prevents the default event calls dispatch only when menu is open', () => {\n          const {rerender, renderSpy} = renderCombobox({isOpen: false})\n          const input = getInput()\n          const keyDownEvent = createEvent.keyDown(input, {key: 'Home'})\n\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n          expect(renderSpy).not.toHaveBeenCalled()\n\n          rerender({isOpen: true})\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n          expect(renderSpy).toHaveBeenCalledTimes(1) // re-render on key\n        })\n\n        test('highlights next non-disabled option', async () => {\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n            isItemDisabled(_item, index) {\n              return index === 0\n            },\n          })\n\n          await keyDownOnInput('{Home}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n      })\n\n      describe('escape', () => {\n        test('with menu open has the menu closed and focused kept on input', async () => {\n          renderCombobox({\n            initialIsOpen: true,\n            initialHighlightedIndex: 2,\n            initialSelectedItem: items[0],\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Escape}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveValue(items[0])\n          expect(input).toHaveFocus()\n        })\n\n        test('with closed menu has item removed and focused kept on input', async () => {\n          renderCombobox({\n            initialHighlightedIndex: 2,\n            initialSelectedItem: items[0],\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Escape}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveValue('')\n          expect(input).toHaveFocus()\n        })\n\n        test('prevents the rerender when menu is closed, no selectedItem and no inputValue', async () => {\n          const {renderSpy, rerender} = renderCombobox({\n            isOpen: false,\n            inputValue: '',\n          })\n\n          await keyDownOnInput('{Escape}') // focus input and close the menu.\n          renderSpy.mockClear()\n\n          await keyDownOnInput('{Escape}')\n\n          expect(renderSpy).toHaveBeenCalledTimes(0) // no re-render\n\n          rerender({isOpen: true, inputValue: ''})\n          renderSpy.mockClear() // reset rerender and initial render\n          await keyDownOnInput('{Escape}')\n\n          expect(renderSpy).toHaveBeenCalledTimes(1) // re-render on key\n\n          rerender({isOpen: false, inputValue: 'still'})\n          renderSpy.mockClear() // reset rerender and initial render\n          await keyDownOnInput('{Escape}')\n\n          expect(renderSpy).toHaveBeenCalledTimes(1) // re-render on key\n        })\n\n        test('stops propagation when it closes the menu or clears the input', () => {\n          renderCombobox({\n            initialIsOpen: true,\n            initialSelectedItem: items[0],\n          })\n          const input = getInput()\n          const keyDownEvents = [\n            createEvent.keyDown(input, {key: 'Escape'}),\n            createEvent.keyDown(input, {key: 'Escape'}),\n            createEvent.keyDown(input, {key: 'Escape'}),\n          ]\n\n          for (let index = 0; index < keyDownEvents.length; index++) {\n            fireEvent(input, keyDownEvents[index])\n            expect(keyDownEvents[index].defaultPrevented).toBe(\n              index !== keyDownEvents.length - 1,\n            )\n          }\n        })\n      })\n\n      describe('enter', () => {\n        test('closes the menu and selects highlighted item', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{Enter}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(getInput()).toHaveValue(items[initialHighlightedIndex])\n        })\n\n        test('selects highlighted item and resets to user defaults', async () => {\n          const defaultHighlightedIndex = 2\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Enter}')\n\n          expect(input).toHaveValue(items[defaultHighlightedIndex])\n          expect(getItems()).toHaveLength(items.length)\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('closes the menu without resetting to user defaults if no item is highlighted', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          })\n          const input = getInput()\n\n          await mouseMoveItemAtIndex(defaultHighlightedIndex)\n          await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n          await keyDownOnInput('{Enter}')\n\n          expect(input).toHaveValue(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('closes the menu without resetting to user defaults if the list is empty', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderCombobox({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          })\n          const input = getInput()\n\n          await keyDownOnInput('{Enter}')\n\n          expect(input).toHaveValue(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('while IME composing will not select highlighted item', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialHighlightedIndex,\n            initialIsOpen: true,\n          })\n          const input = getInput()\n\n          fireEvent.keyDown(getInput(), {key: 'Enter', keyCode: 229})\n\n          expect(input).toHaveValue('')\n          expect(getItems()).toHaveLength(items.length)\n          expect(input).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex),\n          )\n\n          await keyDownOnInput('{Enter}')\n\n          expect(input).toHaveValue(items[2])\n          expect(getItems()).toHaveLength(0)\n          expect(input).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('with a closed menu does nothing', async () => {\n          renderCombobox({\n            initialHighlightedIndex: 2,\n            initialIsOpen: false,\n          })\n\n          await keyDownOnInput('{Enter}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(getInput()).not.toHaveValue()\n        })\n\n        test('with an open menu closes the menu without a highlightedIndex', async () => {\n          renderCombobox({\n            initialIsOpen: true,\n          })\n\n          await keyDownOnInput('{Enter}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(getInput()).not.toHaveValue()\n        })\n\n        test('with closed menu or composing event, it will not rerender or prevent event default', () => {\n          const {renderSpy, rerender} = renderCombobox({\n            isOpen: false,\n            highlightedIndex: -1,\n          })\n          const input = getInput()\n          let keyDownEvent = createEvent.keyDown(input, {key: 'Enter'})\n\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(renderSpy).not.toHaveBeenCalled()\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n\n          keyDownEvent = createEvent.keyDown(input, {\n            key: 'Enter',\n            keyCode: 229,\n          })\n          rerender({\n            isOpen: true,\n            highlightedIndex: 2,\n          })\n          renderSpy.mockClear()\n          fireEvent(input, keyDownEvent)\n\n          expect(renderSpy).not.toHaveBeenCalled()\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n\n          keyDownEvent = createEvent.keyDown(input, {key: 'Enter'})\n          fireEvent(input, keyDownEvent)\n\n          expect(renderSpy).toHaveBeenCalledTimes(1)\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n      })\n\n      test('tab it closes the menu and selects highlighted item', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox(\n          {initialIsOpen: true, initialHighlightedIndex: 2},\n          ui => {\n            return (\n              <>\n                {ui}\n                <div tabIndex={0}>Second element</div>\n              </>\n            )\n          },\n        )\n\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue(items[initialHighlightedIndex])\n      })\n\n      test('tab closes the menu if there is no highlighted item', async () => {\n        const defaultHighlightedIndex = 2\n        const initialSelectedItem = items[0]\n\n        renderCombobox(\n          {\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          },\n          ui => {\n            return (\n              <>\n                {ui}\n                <div tabIndex={0}>Second element</div>\n              </>\n            )\n          },\n        )\n\n        await mouseMoveItemAtIndex(defaultHighlightedIndex)\n        await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue(initialSelectedItem)\n      })\n\n      test('tab closes the menu if there is no items', async () => {\n        const defaultHighlightedIndex = 2\n        const initialSelectedItem = items[0]\n\n        renderCombobox(\n          {\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          },\n          ui => {\n            return (\n              <>\n                {ui}\n                <div tabIndex={0}>Second element</div>\n              </>\n            )\n          },\n        )\n\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue(initialSelectedItem)\n      })\n\n      test('shift+tab it closes the menu', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox(\n          {initialIsOpen: true, initialHighlightedIndex: 2},\n          ui => {\n            return (\n              <>\n                <div tabIndex={0}>First element</div>\n                {ui}\n              </>\n            )\n          },\n        )\n\n        await tab(true)\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue(items[initialHighlightedIndex])\n      })\n\n      test(\"other than the ones supported don't affect anything\", async () => {\n        const highlightedIndex = 2\n        renderCombobox({\n          initialIsOpen: true,\n          initialHighlightedIndex: highlightedIndex,\n          initialSelectedItem: items[highlightedIndex],\n        })\n        const input = getInput()\n\n        await keyDownOnInput('{Alt}')\n        await keyDownOnInput('{Control}')\n\n        expect(input).toHaveFocus()\n        expect(input).toHaveValue(items[highlightedIndex])\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n        expect(getItems()).toHaveLength(items.length)\n      })\n\n      describe('pageUp', () => {\n        test('pageUp jumps highlight up by 10 options', async () => {\n          const initialHighlightedIndex = 12\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{PageUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex - 10),\n          )\n        })\n\n        test('skips the disabled option to the previous non-disabled one', async () => {\n          const initialHighlightedIndex = 12\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n          })\n\n          await keyDownOnInput('{PageUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n\n        test('jumps highlight the first option if highlightedIndex is 10 or smaller', async () => {\n          const initialHighlightedIndex = 7\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{PageUp}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('prevents the default event behavior with the menu open', () => {\n          renderCombobox({isOpen: true})\n          const toggleButton = getInput()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageUp',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('does not prevent the default event behavior with the menu closed', () => {\n          renderCombobox()\n          const toggleButton = getInput()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageUp',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n        })\n      })\n\n      describe('pageDown', () => {\n        test('pageDown jumps highlight down by 10 options', async () => {\n          const initialHighlightedIndex = 12\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{PageDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 10),\n          )\n        })\n\n        test('skips the disabled option to the next non-disabled one', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n            isItemDisabled(_item, index) {\n              return index === 12\n            },\n          })\n\n          await keyDownOnInput('{PageDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(13),\n          )\n        })\n\n        test('pageDown jumps highlight the last option if highlightedIndex is closer than 10 indeces to the end', async () => {\n          const initialHighlightedIndex = items.length - 5\n          renderCombobox({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnInput('{PageDown}')\n\n          expect(getInput()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('pageDown prevents the default event behavior with the menu open', () => {\n          renderCombobox({isOpen: true})\n          const toggleButton = getInput()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageDown',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('pageDown does not prevent the default event behavior with the menu closed', () => {\n          renderCombobox()\n          const toggleButton = getInput()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageDown',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n        })\n      })\n    })\n\n    describe('on blur', () => {\n      test('by tab the open menu will be closed and highlighted item will be selected', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox({\n          initialIsOpen: true,\n          initialHighlightedIndex,\n        })\n\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue(items[initialHighlightedIndex])\n      })\n\n      test('by tab the open menu will be closed and highlighted item will not be selected if the highlight by mouse leaves the menu', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox({\n          initialIsOpen: true,\n          initialHighlightedIndex,\n        })\n\n        await mouseMoveItemAtIndex(initialHighlightedIndex)\n        await mouseLeaveItemAtIndex(initialHighlightedIndex)\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getInput()).toHaveValue('')\n      })\n\n      test('by tab the value in the input will stay the same', async () => {\n        const inputValue = 'test me'\n        renderCombobox({\n          initialIsOpen: true,\n        })\n\n        await changeInputValue(inputValue)\n        await tab()\n\n        expect(getInput()).toHaveValue(inputValue)\n      })\n\n      test('by mouse is not triggered if target is within downshift', () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        const {container} = renderCombobox({\n          isOpen: true,\n          highlightedIndex: 0,\n          stateReducer,\n        })\n        const input = getInput()\n        document.body.appendChild(container)\n\n        fireEvent.mouseDown(input)\n        fireEvent.mouseUp(input)\n\n        expect(stateReducer).not.toHaveBeenCalled()\n\n        fireEvent.mouseDown(document.body)\n        fireEvent.mouseUp(document.body)\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          {\n            highlightedIndex: 0,\n            inputValue: '',\n            isOpen: true,\n            selectedItem: null,\n          },\n          expect.objectContaining({\n            type: stateChangeTypes.InputBlur,\n            changes: {\n              highlightedIndex: -1,\n              inputValue: '',\n              isOpen: false,\n              selectedItem: null,\n            },\n          }),\n        )\n      })\n\n      test('by touch is not triggered if target is within downshift', () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        const {container} = renderCombobox({\n          isOpen: true,\n          highlightedIndex: 0,\n          stateReducer,\n        })\n        const input = getInput()\n        document.body.appendChild(container)\n\n        fireEvent.touchStart(input)\n        fireEvent.touchMove(input)\n        fireEvent.touchEnd(input)\n\n        expect(stateReducer).not.toHaveBeenCalled()\n\n        fireEvent.touchStart(document.body)\n        fireEvent.touchEnd(document.body)\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          {\n            highlightedIndex: 0,\n            inputValue: '',\n            isOpen: true,\n            selectedItem: null,\n          },\n          expect.objectContaining({\n            type: stateChangeTypes.InputBlur,\n            changes: {\n              highlightedIndex: -1,\n              inputValue: '',\n              isOpen: false,\n              selectedItem: null,\n            },\n          }),\n        )\n      })\n\n      test('from the last element in the tab order, the open menu will be closed and highlighted item will be selected', () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        renderCombobox({\n          isOpen: true,\n          highlightedIndex: 0,\n          stateReducer,\n        })\n\n        // tab from the last element in the tab order ends up on the body.\n        const body = {}\n        const actualBody = document.body\n        global.document = {...document, activeElement: body, body}\n        fireEvent.blur(getInput(), {relatedTarget: null}) // and also has relatedTarget: null, like when blur is performed by tab change\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          {\n            highlightedIndex: 0,\n            inputValue: '',\n            isOpen: true,\n            selectedItem: null,\n          },\n          expect.objectContaining({\n            type: stateChangeTypes.InputBlur,\n            changes: {\n              highlightedIndex: -1,\n              inputValue: '',\n              isOpen: false,\n              selectedItem: null,\n            },\n          }),\n        )\n        global.document = {...document, body: actualBody}\n      })\n\n      test('by tab change the open menu will be closed and highlighted item will not be selected', () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        renderCombobox({\n          isOpen: true,\n          highlightedIndex: 0,\n          stateReducer,\n        })\n        const input = getInput()\n\n        fireEvent.blur(input, {relatedTarget: null}) // to simulate blur by tab change\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          {\n            highlightedIndex: 0,\n            inputValue: '',\n            isOpen: true,\n            selectedItem: null,\n          },\n          expect.objectContaining({\n            type: stateChangeTypes.InputBlur,\n            changes: {\n              highlightedIndex: -1,\n              inputValue: '',\n              isOpen: false,\n              selectedItem: null,\n            },\n          }),\n        )\n      })\n\n      test('does nothing in environment is not defined', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox({\n          initialIsOpen: true,\n          initialHighlightedIndex,\n          environment: undefined,\n        })\n\n        await tab()\n\n        expect(getItems()).toHaveLength(items.length)\n        expect(getInput()).not.toHaveValue()\n      })\n    })\n\n    test('on focus does nothing', async () => {\n      renderCombobox()\n\n      await tab()\n\n      expect(getItems()).toHaveLength(0)\n    })\n\n    describe('on click', () => {\n      test('it opens the menu', async () => {\n        renderCombobox()\n\n        await clickOnInput()\n\n        expect(getItems()).toHaveLength(items.length)\n      })\n\n      test('it closes the open menu', async () => {\n        renderCombobox({initialIsOpen: true})\n\n        await clickOnInput()\n\n        expect(getItems()).toHaveLength(0)\n      })\n\n      test('toggles the menu', async () => {\n        renderCombobox()\n\n        await clickOnInput()\n\n        expect(getItems()).toHaveLength(items.length)\n\n        await clickOnInput()\n\n        expect(getItems()).toHaveLength(0)\n\n        await clickOnInput()\n\n        expect(getItems()).toHaveLength(items.length)\n      })\n\n      test('it opens the menu with selected option highlighted', async () => {\n        const selectedIndex = 4\n        renderCombobox({\n          initialSelectedItem: items[selectedIndex],\n        })\n\n        await clickOnInput()\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(selectedIndex),\n        )\n      })\n\n      test('it opens the closed menu at initialHighlightedIndex, but on first click only', async () => {\n        const initialHighlightedIndex = 2\n        renderCombobox({\n          initialHighlightedIndex,\n        })\n        const input = getInput()\n\n        await clickOnInput()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(initialHighlightedIndex),\n        )\n\n        await clickOnInput()\n        await clickOnInput()\n\n        expect(input).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('it opens the closed menu at defaultHighlightedIndex, on every click', async () => {\n        const defaultHighlightedIndex = 3\n        renderCombobox({\n          defaultHighlightedIndex,\n        })\n        const input = getInput()\n\n        await clickOnInput()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n\n        await clickOnInput()\n        await clickOnInput()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('initialHighlightedIndex is ignored if item is disabled', async () => {\n        const initialHighlightedIndex = 2\n        const isItemDisabled = jest\n          .fn()\n          .mockImplementation(\n            item => items.indexOf(item) === initialHighlightedIndex,\n          )\n        renderCombobox({\n          initialHighlightedIndex,\n          isItemDisabled,\n        })\n\n        await clickOnInput()\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n        expect(isItemDisabled).toHaveBeenNthCalledWith(\n          1,\n          items[initialHighlightedIndex],\n          initialHighlightedIndex,\n        )\n      })\n\n      test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n        const initialHighlightedIndex = 0\n        const defaultHighlightedIndex = 2\n        renderCombobox({\n          initialHighlightedIndex,\n          defaultHighlightedIndex,\n          isItemDisabled(item) {\n            return items.indexOf(item) === initialHighlightedIndex\n          },\n        })\n\n        await clickOnInput()\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n        const defaultHighlightedIndex = 2\n        const isItemDisabled = jest\n          .fn()\n          .mockImplementation(\n            item => items.indexOf(item) === defaultHighlightedIndex,\n          )\n        renderCombobox({\n          defaultHighlightedIndex,\n          isItemDisabled,\n        })\n\n        await clickOnInput()\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n        expect(isItemDisabled).toHaveBeenNthCalledWith(\n          1,\n          items[defaultHighlightedIndex],\n          defaultHighlightedIndex,\n        )\n      })\n\n      test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n        const initialHighlightedIndex = 0\n        const defaultHighlightedIndex = 2\n        renderCombobox({\n          initialHighlightedIndex,\n          defaultHighlightedIndex,\n          isItemDisabled(item) {\n            return [initialHighlightedIndex, defaultHighlightedIndex].includes(\n              items.indexOf(item),\n            )\n          },\n        })\n\n        await clickOnInput()\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeEach(() => {\n      // usually it is disabled by test utils.\n      const {useGetterPropsCalledChecker} = jest.requireActual('../../utils')\n      jest\n        .spyOn(utils, 'useGetterPropsCalledChecker')\n        .mockImplementation(useGetterPropsCalledChecker)\n      jest.spyOn(console, 'error').mockImplementation(() => {})\n    })\n\n    test('will be displayed if getInputProps is not called', () => {\n      renderHook(() => {\n        const {getMenuProps} = useCombobox({items})\n        getMenuProps({}, {suppressRefError: true})\n      })\n\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: You forgot to call the getInputProps getter function on your component / element.`,\n      )\n    })\n\n    test('will not be displayed if getInputProps is not called on subsequent renders', () => {\n      let firstRender = true\n      const {rerender} = renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n        getMenuProps({}, {suppressRefError: true})\n\n        // eslint-disable-next-line jest/no-if, jest/no-conditional-in-test\n        if (firstRender) {\n          firstRender = false\n          getInputProps({}, {suppressRefError: true})\n        }\n      })\n\n      rerender()\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will be displayed if element ref is not set and suppressRefError is false', () => {\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n        getInputProps()\n      })\n\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: The ref prop \"ref\" from getInputProps was not applied correctly on your element.`,\n      )\n    })\n\n    test('will not be displayed if element ref is not set and suppressRefError is true', () => {\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n        getInputProps({}, {suppressRefError: true})\n      })\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will not be displayed if called with a correct ref', () => {\n      const refFn = jest.fn()\n      const inputNode = {}\n\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n\n        const {ref} = getInputProps({\n          ref: refFn,\n        })\n        ref(inputNode)\n      })\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/getItemProps.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport {\n  renderCombobox,\n  renderUseCombobox,\n  items,\n  defaultIds,\n  mouseMoveItemAtIndex,\n  getItemAtIndex,\n  getInput,\n  getItems,\n  clickOnItemAtIndex,\n  keyDownOnInput,\n} from '../testUtils'\nimport useCombobox from '..'\n\ndescribe('getItemProps', () => {\n  test('throws error if no index or item has been passed', () => {\n    const {result} = renderUseCombobox()\n\n    expect(result.current.getItemProps).toThrow(\n      'Pass either item or index to getItemProps!',\n    )\n  })\n\n  describe('hook props', () => {\n    test(\"assign 'option' to role\", () => {\n      const {result} = renderUseCombobox()\n      const itemProps = result.current.getItemProps({index: 0})\n\n      expect(itemProps.role).toEqual('option')\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseCombobox()\n      const itemProps = result.current.getItemProps({index: 0})\n\n      expect(itemProps.id).toEqual(`${defaultIds.getItemId(0)}`)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const getItemId = index => `my-custom-item-id-${index}`\n      const {result} = renderUseCombobox({getItemId})\n      const itemProps = result.current.getItemProps({index: 0})\n\n      expect(itemProps.id).toEqual(getItemId(0))\n    })\n\n    test(\"assign 'true' to aria-selected if item is highlighted\", () => {\n      const {result} = renderUseCombobox({highlightedIndex: 2})\n      const itemProps = result.current.getItemProps({index: 2})\n\n      expect(itemProps['aria-selected']).toEqual(true)\n    })\n\n    test(\"assign 'false' to aria-selected if item is not highlighted\", () => {\n      const {result} = renderUseCombobox({highlightedIndex: 1})\n      const itemProps = result.current.getItemProps({index: 2})\n\n      expect(itemProps['aria-selected']).toEqual(false)\n    })\n\n    test(\"click handler is not called if it's disabled\", () => {\n      const {result} = renderUseCombobox({\n        isItemDisabled(_item, index) {\n          return index === 0\n        },\n      })\n      const itemProps = result.current.getItemProps({\n        index: 0,\n      })\n\n      expect(itemProps.onClick).toBeUndefined()\n      expect(itemProps.onMouseMove).toBeDefined()\n      expect(itemProps.onMouseDown).toBeDefined()\n      expect(itemProps['aria-disabled']).toBe(true)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseCombobox()\n\n      expect(\n        result.current.getItemProps({index: 0, foo: 'bar'}),\n      ).toHaveProperty('foo', 'bar')\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onClick} = result.current.getItemProps({\n          index: 0,\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n      expect(result.current.selectedItem).not.toBeNull()\n    })\n\n    test('event handler onMouseMove is called along with downshift handler', () => {\n      const userOnMouseMove = jest.fn()\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseMove} = result.current.getItemProps({\n          index: 1,\n          onMouseMove: userOnMouseMove,\n        })\n\n        onMouseMove({})\n      })\n\n      expect(userOnMouseMove).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(1)\n    })\n\n    test('event handler onMouseDown is called along with downshift handler', () => {\n      const userOnMouseDown = jest.fn()\n      const preventDefault = jest.fn()\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseDown} = result.current.getItemProps({\n          index: 1,\n          onMouseDown: userOnMouseDown,\n        })\n\n        onMouseDown({preventDefault})\n      })\n\n      expect(userOnMouseDown).toHaveBeenCalledTimes(1)\n      expect(preventDefault).toHaveBeenCalledTimes(1)\n    })\n\n    test(\"event handler onMouseDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnMouseDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const preventDefault = jest.fn()\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseDown} = result.current.getItemProps({\n          index: 0,\n          onMouseDown: userOnMouseDown,\n        })\n\n        onMouseDown({preventDefault})\n      })\n\n      expect(userOnMouseDown).toHaveBeenCalledTimes(1)\n      expect(preventDefault).not.toHaveBeenCalled()\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onClick} = result.current.getItemProps({\n          index: 0,\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n      expect(result.current.selectedItem).toBeNull()\n    })\n\n    test(\"event handler onMouseMove is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const useronMouseMove = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseMove} = result.current.getItemProps({\n          index: 1,\n          onMouseMove: useronMouseMove,\n        })\n\n        onMouseMove({})\n      })\n\n      expect(useronMouseMove).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on mouse over', () => {\n      test('it highlights the item', async () => {\n        const index = 1\n        renderCombobox({\n          isOpen: true,\n        })\n\n        await mouseMoveItemAtIndex(index)\n\n        expect(getItemAtIndex(index)).toHaveAttribute('aria-selected', 'true')\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n      })\n\n      test('it removes highlight from the previously highlighted item', async () => {\n        const index = 1\n        const previousIndex = 2\n        renderCombobox({\n          isOpen: true,\n          initialHighlightedIndex: previousIndex,\n        })\n\n        await mouseMoveItemAtIndex(index)\n\n        expect(getItemAtIndex(index)).toHaveAttribute('aria-selected', 'true')\n        expect(getItemAtIndex(previousIndex)).toHaveAttribute(\n          'aria-selected',\n          'false',\n        )\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n        expect(getItemAtIndex(previousIndex)).toHaveAttribute(\n          'aria-selected',\n          'false',\n        )\n      })\n\n      it('keeps highlight on multiple events', async () => {\n        const index = 1\n        renderCombobox({\n          isOpen: true,\n        })\n\n        await mouseMoveItemAtIndex(index)\n        await mouseMoveItemAtIndex(index)\n        await mouseMoveItemAtIndex(index)\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n        expect(getItemAtIndex(index)).toHaveAttribute('aria-selected', 'true')\n      })\n\n      it('removes highlight from previous item even if current item is disabled', async () => {\n        const disabledIndex = 1\n        const highlightedIndex = 2\n\n        renderCombobox({\n          items,\n          isOpen: true,\n          isItemDisabled(_item, index) {\n            return index === disabledIndex\n          },\n        })\n        const input = getInput()\n\n        await mouseMoveItemAtIndex(highlightedIndex)\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n\n        await mouseMoveItemAtIndex(disabledIndex)\n        expect(input).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      // Test that we don't call the mouse move handler on mobile.\n      test('does not set highlight the item if a touch event ocurred', async () => {\n        let touchEndHandler\n        const index = 1\n\n        renderCombobox({\n          isOpen: true,\n          environment: {\n            addEventListener: (name, handler) => {\n              // eslint-disable-next-line jest/no-conditional-in-test\n              if (name === 'touchend') {\n                touchEndHandler = handler\n              }\n            },\n            removeEventListener: () => {},\n            document: {\n              createElement: () => {},\n              getElementById: () => {},\n              activeElement: () => {},\n              body: {},\n            },\n            Node: () => {},\n          },\n        })\n\n        act(() => touchEndHandler({target: null}))\n        await mouseMoveItemAtIndex(index)\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n\n    describe('on click', () => {\n      test('it selects the item and keeps focus on the input', async () => {\n        const index = 1\n        renderCombobox({\n          initialIsOpen: true,\n        })\n        const input = getInput()\n\n        await clickOnItemAtIndex(index)\n\n        expect(getItems()).toHaveLength(0)\n        expect(input).toHaveValue(items[index])\n        expect(input).toHaveFocus()\n      })\n\n      test('it selects the item and resets to user defined defaults', async () => {\n        const index = 1\n        const defaultHighlightedIndex = 2\n        renderCombobox({\n          defaultIsOpen: true,\n          defaultHighlightedIndex,\n        })\n        const input = getInput()\n\n        await clickOnItemAtIndex(index)\n\n        expect(input).toHaveValue(items[index])\n        expect(getItems()).toHaveLength(items.length)\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('it selects the item and resets to user defined defaults, but considers disabled status for an item', async () => {\n        const index = 1\n        const defaultHighlightedIndex = 2\n        renderCombobox({\n          defaultIsOpen: true,\n          defaultHighlightedIndex,\n          isItemDisabled(_item, idx) {\n            return idx === defaultHighlightedIndex\n          },\n        })\n        const input = getInput()\n\n        await clickOnItemAtIndex(index)\n\n        expect(input).toHaveValue(items[index])\n        expect(getItems()).toHaveLength(items.length)\n        expect(input).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n  })\n\n  describe('scrolling', () => {\n    test('is performed by the menu to the item if highlighted and not 100% visible', async () => {\n      const scrollIntoView = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        scrollIntoView,\n      })\n\n      await keyDownOnInput('{End}')\n\n      expect(scrollIntoView).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeAll(() => {\n      jest.spyOn(console, 'warn').mockImplementation(() => {})\n    })\n\n    afterAll(() => {\n      jest.restoreAllMocks()\n    })\n\n    test('will be displayed if getInputProps is not called', () => {\n      renderHook(() => {\n        const {getItemProps} = useCombobox({items})\n        getItemProps({disabled: true, index: 1})\n      })\n\n      expect(console.warn.mock.calls[0][0]).toMatchInlineSnapshot(\n        `Passing \"disabled\" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.`,\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/getLabelProps.test.js",
    "content": "import {renderUseCombobox} from '../testUtils'\nimport {defaultIds} from '../../testUtils'\n\ndescribe('getLabelProps', () => {\n  test('should have a default id assigned', () => {\n    const {result} = renderUseCombobox()\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.id).toEqual(defaultIds.labelId)\n  })\n\n  test('should have custom id if set by the user', () => {\n    const props = {\n      labelId: 'my-custom-label-id',\n    }\n    const {result} = renderUseCombobox(props)\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.id).toEqual(props.labelId)\n  })\n\n  test('should assign htmlFor with the value of the input id', () => {\n    const props = {\n      inputId: 'my-custom-input-id',\n    }\n    const {result} = renderUseCombobox(props)\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.htmlFor).toEqual(props.inputId)\n  })\n\n  test('passes props downwards', () => {\n    const {result} = renderUseCombobox()\n    const props = {foo: 'bar'}\n    const labelProps = result.current.getLabelProps(props)\n\n    expect(labelProps).toEqual(expect.objectContaining({foo: 'bar'}))\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/getMenuProps.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport {noop} from '../../../utils-ts'\nimport {getInput, renderCombobox, renderUseCombobox} from '../testUtils'\nimport {\n  defaultIds,\n  items,\n  mouseLeaveItemAtIndex,\n  mouseMoveItemAtIndex,\n} from '../../testUtils'\nimport utils from '../../utils'\nimport useCombobox from '..'\n\ndescribe('getMenuProps', () => {\n  describe('hook props', () => {\n    test('assign default value to aria-labelledby', () => {\n      const {result} = renderUseCombobox()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps['aria-labelledby']).toEqual(`${defaultIds.labelId}`)\n    })\n\n    test('assign custom value passed by user to aria-labelledby', () => {\n      const props = {\n        labelId: 'my-custom-label-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps['aria-labelledby']).toEqual(`${props.labelId}`)\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseCombobox()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.id).toEqual(`${defaultIds.menuId}`)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const props = {\n        menuId: 'my-custom-menu-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.id).toEqual(`${props.menuId}`)\n    })\n\n    test(\"assign 'listbox' to role\", () => {\n      const {result} = renderUseCombobox()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.role).toEqual('listbox')\n    })\n\n    test(\"do not assign 'aria-labelledby' if it has aria-label\", () => {\n      const ariaLabel = 'not so fast'\n      const {result} = renderUseCombobox()\n      const menuProps = result.current.getMenuProps({'aria-label': ariaLabel})\n\n      expect(menuProps['aria-labelledby']).toBeUndefined()\n      expect(menuProps['aria-label']).toBe(ariaLabel)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseCombobox()\n\n      expect(result.current.getMenuProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('event handler onMouseLeave is called along with downshift handler', () => {\n      const userOnMouseLeave = jest.fn()\n      const {result} = renderUseCombobox({\n        initialHighlightedIndex: 2,\n        initialIsOpen: true,\n      })\n\n      act(() => {\n        const {onMouseLeave} = result.current.getMenuProps({\n          onMouseLeave: userOnMouseLeave,\n        })\n\n        onMouseLeave({preventDefault: noop})\n      })\n\n      expect(userOnMouseLeave).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n\n    test(\"event handler onMouseLeave is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnMouseLeave = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox({\n        initialHighlightedIndex: 2,\n        initialIsOpen: true,\n      })\n\n      act(() => {\n        const {onMouseLeave} = result.current.getMenuProps({\n          onMouseLeave: userOnMouseLeave,\n        })\n\n        onMouseLeave({preventDefault: noop})\n      })\n\n      expect(userOnMouseLeave).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(2)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on key down', () => {\n      describe('on mouse leave', () => {\n        test('the highlightedIndex should be reset', async () => {\n          const initialHighlightedIndex = 2\n          renderCombobox({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await mouseMoveItemAtIndex(initialHighlightedIndex)\n          await mouseLeaveItemAtIndex(initialHighlightedIndex)\n\n          expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n        })\n      })\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeEach(() => {\n      // usally disabled by test utils.\n      const {useGetterPropsCalledChecker} = jest.requireActual('../../utils')\n      jest\n        .spyOn(utils, 'useGetterPropsCalledChecker')\n        .mockImplementation(useGetterPropsCalledChecker)\n      jest.spyOn(console, 'error').mockImplementation(() => {})\n    })\n\n    test('will be displayed if getMenuProps is not called', () => {\n      renderHook(() => {\n        const {getInputProps} = useCombobox({items})\n        getInputProps({}, {suppressRefError: true})\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: You forgot to call the getMenuProps getter function on your component / element.`,\n      )\n    })\n\n    test('will not be displayed if getMenuProps is not called on subsequent renders', () => {\n      let firstRender = true\n      const {rerender} = renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n        getInputProps({}, {suppressRefError: true})\n\n        // eslint-disable-next-line jest/no-if, jest/no-conditional-in-test\n        if (firstRender) {\n          firstRender = false\n          getMenuProps({}, {suppressRefError: true})\n        }\n      })\n\n      rerender()\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will be displayed if element ref is not set and suppressRefError is false', () => {\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getInputProps({}, {suppressRefError: true})\n        getMenuProps()\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: The ref prop \"ref\" from getMenuProps was not applied correctly on your element.`,\n      )\n    })\n\n    test('will not be displayed if element ref is not set and suppressRefError is true', () => {\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n        getInputProps({}, {suppressRefError: true})\n      })\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will not be displayed if called with a correct ref', () => {\n      const refFn = jest.fn()\n      const menuNode = {}\n\n      renderHook(() => {\n        const {getInputProps, getMenuProps} = useCombobox({\n          items,\n        })\n\n        getInputProps({}, {suppressRefError: true})\n\n        const {ref} = getMenuProps({\n          ref: refFn,\n        })\n        ref(menuNode)\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/getToggleButtonProps.test.js",
    "content": "import {act} from '@testing-library/react'\nimport {\n  renderCombobox,\n  renderUseCombobox,\n  items,\n  defaultIds,\n  clickOnToggleButton,\n  getInput,\n  getItems,\n} from '../testUtils'\n\ndescribe('getToggleButtonProps', () => {\n  describe('hook props', () => {\n    test('assign default value to id', () => {\n      const {result} = renderUseCombobox()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.id).toEqual(defaultIds.toggleButtonId)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const props = {\n        toggleButtonId: 'my-custom-toggle-button-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.id).toEqual(props.toggleButtonId)\n    })\n\n    test('assign tabindex of -1', () => {\n      const {result} = renderUseCombobox()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.tabIndex).toEqual(-1)\n    })\n\n    test('assign default value to aria-controls', () => {\n      const {result} = renderUseCombobox()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-controls']).toEqual(`${defaultIds.menuId}`)\n    })\n\n    test('assign custom value passed by user to aria-controls', () => {\n      const props = {\n        menuId: 'my-custom-menu-id',\n      }\n      const {result} = renderUseCombobox(props)\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-controls']).toEqual(`${props.menuId}`)\n    })\n\n    test(\"assign 'false' value to aria-expanded when menu is closed\", () => {\n      const {result} = renderUseCombobox({isOpen: false})\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-expanded']).toEqual(false)\n    })\n\n    test(\"assign 'true' value to aria-expanded when menu is open\", () => {\n      const {result} = renderUseCombobox({isOpen: true})\n\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-expanded']).toEqual(true)\n    })\n\n    test('omit event handlers when disabled', () => {\n      const {result} = renderUseCombobox()\n      const toggleButtonProps = result.current.getToggleButtonProps({\n        disabled: true,\n      })\n\n      expect(toggleButtonProps.onClick).toBeUndefined()\n      expect(toggleButtonProps.disabled).toBe(true)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseCombobox()\n\n      expect(result.current.getToggleButtonProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {onClick} = result.current.getToggleButtonProps({\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseCombobox()\n\n      act(() => {\n        const {onClick} = result.current.getToggleButtonProps({\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on click', () => {\n      test('opens the closed menu', async () => {\n        renderCombobox()\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n      })\n\n      test('closes the open menu', async () => {\n        renderCombobox({\n          initialIsOpen: true,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n      })\n\n      test('opens and closes menu at consecutive clicks', async () => {\n        renderCombobox()\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n      })\n\n      test('opens the closed menu without any option highlighted', async () => {\n        renderCombobox()\n\n        await clickOnToggleButton()\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('opens the closed menu with selected option highlighted', async () => {\n        const selectedIndex = 3\n        renderCombobox({\n          initialSelectedItem: items[selectedIndex],\n        })\n\n        await clickOnToggleButton()\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(selectedIndex),\n        )\n      })\n\n      test('opens the closed menu at initialHighlightedIndex, but on first click only', async () => {\n        const initialHighlightedIndex = 3\n        renderCombobox({\n          initialHighlightedIndex,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getInput()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(initialHighlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('opens the closed menu at defaultHighlightedIndex, on every click', async () => {\n        const defaultHighlightedIndex = 3\n        renderCombobox({\n          defaultHighlightedIndex,\n        })\n        const input = getInput()\n\n        await clickOnToggleButton()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('opens the closed menu at highlightedIndex from props, on every click', async () => {\n        const highlightedIndex = 3\n        renderCombobox({\n          highlightedIndex,\n        })\n        const input = getInput()\n\n        await clickOnToggleButton()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(input).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n      })\n\n      test('opens the closed menu and sets focus on the input', async () => {\n        renderCombobox()\n\n        await clickOnToggleButton()\n\n        expect(getInput()).toHaveFocus()\n      })\n\n      test('opens the closed menu and sets no focus if there is no environment', async () => {\n        renderCombobox({environment: undefined})\n\n        await clickOnToggleButton()\n\n        expect(getInput()).not.toHaveFocus()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/memo.test.js",
    "content": "import React from 'react'\nimport {\n  renderUseCombobox,\n  renderCombobox,\n  getInput,\n  keyDownOnInput,\n} from '../testUtils'\nimport {items, defaultIds, MemoizedItem} from '../../testUtils'\n\ntest('functions are memoized', () => {\n  const {result, rerender} = renderUseCombobox()\n  const firstRenderResult = result.current\n  rerender()\n  const secondRenderResult = result.current\n  expect(firstRenderResult).toEqual(secondRenderResult)\n})\n\ntest('will skip disabled items after component rerenders and items are memoized', async () => {\n  function renderItem(props) {\n    return <MemoizedItem key={props.index} {...props} />\n  }\n  function isItemDisabled(_item, index) {\n    return index === items.length - 2\n  }\n\n  const {rerender} = renderCombobox({\n    isItemDisabled,\n    isOpen: true,\n    initialHighlightedIndex: items.length - 1,\n    renderItem,\n  })\n\n  rerender({renderItem, isOpen: true, isItemDisabled})\n  await keyDownOnInput('{ArrowUp}')\n\n  expect(getInput()).toHaveAttribute(\n    'aria-activedescendant',\n    defaultIds.getItemId(items.length - 3),\n  )\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/props.test.js",
    "content": "import React from 'react'\nimport {renderHook, act} from '@testing-library/react'\nimport {\n  renderCombobox,\n  renderUseCombobox,\n  items,\n  defaultIds,\n  waitForDebouncedA11yStatusUpdate,\n  getToggleButton,\n  getMenu,\n  getLabel,\n  getInput,\n  getItems,\n  clickOnItemAtIndex,\n  getA11yStatusContainer,\n  keyDownOnInput,\n  clickOnToggleButton,\n  changeInputValue,\n  mouseMoveItemAtIndex,\n  mouseLeaveItemAtIndex,\n  tab,\n  clickOnInput,\n} from '../testUtils'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport useCombobox from '..'\n\ndescribe('props', () => {\n  test('if falsy then prop types error is thrown', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    renderHook(() => useCombobox())\n\n    expect(global.console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `Warning: Failed prop type: The prop \\`items\\` is marked as required in \\`useCombobox\\`, but its value is \\`undefined\\`.`,\n    )\n  })\n\n  describe('id', () => {\n    test('if passed will override downshift default', () => {\n      renderCombobox({\n        id: 'my-custom-little-id',\n      })\n      const elements = [getToggleButton(), getMenu(), getLabel(), getInput()]\n\n      elements.forEach(element => {\n        expect(element).toHaveAttribute(\n          'id',\n          expect.stringContaining('my-custom-little-id'),\n        )\n      })\n    })\n  })\n\n  describe('items', () => {\n    test('if passed as empty then menu will not open', () => {\n      renderCombobox({items: [], isOpen: true})\n\n      expect(getItems()).toHaveLength(0)\n    })\n\n    test('passed as objects should work with custom itemToString', async () => {\n      jest.useFakeTimers()\n      renderCombobox({\n        items: [{str: 'aaa'}, {str: 'bbb'}],\n        itemToString: item => item.str,\n        initialIsOpen: true,\n      })\n\n      await clickOnItemAtIndex(0)\n\n      expect(getInput()).toHaveValue('aaa')\n    })\n  })\n\n  describe('itemToKey', () => {\n    test('props update of selectedItem will update inputValue state with default itemToKey referential equality check', () => {\n      const initialSelectedItem = {id: 3, value: 'init'}\n      const selectedItem = {id: 1, value: 'wow'}\n      const newSelectedItem = {id: 1, value: 'not wow'}\n      function itemToString(item) {\n        return item.value\n      }\n      const stateReducer = jest\n        .fn()\n        .mockImplementation((_state, {changes}) => changes)\n\n      const {rerender} = renderCombobox({\n        stateReducer,\n        itemToString,\n        selectedItem: initialSelectedItem,\n      })\n\n      expect(stateReducer).not.toHaveBeenCalled() // won't get called on first render\n\n      rerender({\n        stateReducer,\n        itemToString,\n        selectedItem,\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenCalledWith(\n        {\n          inputValue: itemToString(initialSelectedItem),\n          selectedItem,\n          highlightedIndex: -1,\n          isOpen: false,\n        },\n        expect.objectContaining({\n          type: useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem,\n          changes: {\n            inputValue: itemToString(selectedItem),\n            selectedItem,\n            highlightedIndex: -1,\n            isOpen: false,\n          },\n        }),\n      )\n\n      stateReducer.mockClear()\n      rerender({\n        stateReducer,\n        selectedItem: newSelectedItem,\n        itemToString,\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenCalledWith(\n        {\n          inputValue: itemToString(selectedItem),\n          selectedItem: newSelectedItem,\n          highlightedIndex: -1,\n          isOpen: false,\n        },\n        expect.objectContaining({\n          changes: {\n            inputValue: itemToString(newSelectedItem),\n            selectedItem: newSelectedItem,\n            highlightedIndex: -1,\n            isOpen: false,\n          },\n          type: useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem,\n        }),\n      )\n      expect(getInput()).toHaveValue(itemToString(newSelectedItem))\n    })\n\n    test('props update of selectedItem will not update inputValue state if itemToKey returns equal values', () => {\n      const initialSelectedItem = {id: 1, value: 'hmm'}\n      const selectedItem = {id: 1, value: 'wow'}\n      function itemToString(item) {\n        return item.value\n      }\n      const itemToKey = jest.fn().mockImplementation(item => item.id)\n      const stateReducer = jest\n        .fn()\n        .mockImplementation((_state, {changes}) => changes)\n\n      const {rerender} = renderCombobox({\n        itemToKey,\n        stateReducer,\n        selectedItem: initialSelectedItem,\n        itemToString,\n      })\n\n      rerender({\n        itemToKey,\n        stateReducer,\n        selectedItem,\n        itemToString,\n      })\n\n      expect(getInput()).toHaveValue(itemToString(initialSelectedItem))\n      expect(itemToKey).toHaveBeenCalledTimes(2)\n      expect(itemToKey).toHaveBeenNthCalledWith(1, selectedItem)\n      expect(itemToKey).toHaveBeenNthCalledWith(2, initialSelectedItem)\n    })\n  })\n\n  describe('getA11yStatusMessage', () => {\n    beforeEach(() => jest.useFakeTimers())\n    afterEach(() => {\n      act(() => jest.runAllTimers())\n    })\n    afterAll(() => jest.useRealTimers())\n\n    test('adds no status message element to the DOM if not passed', async () => {\n      renderCombobox({\n        items,\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('calls the function only on state changes', async () => {\n      const getA11yStatusMessage = jest.fn()\n      const {rerender} = renderCombobox({\n        getA11yStatusMessage,\n      })\n\n      await changeInputValue('h')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        inputValue: 'h',\n        highlightedIndex: -1,\n        isOpen: true,\n        selectedItem: null,\n      })\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n\n      getA11yStatusMessage.mockClear()\n      rerender({getA11yStatusMessage})\n\n      expect(getA11yStatusMessage).not.toHaveBeenCalled()\n    })\n\n    test('adds a status message element with the text returned', async () => {\n      const a11yStatusMessage1 = 'Dropdown is open'\n      const a11yStatusMessage2 = 'Dropdown is still open'\n      const getA11yStatusMessage = jest\n        .fn()\n        .mockReturnValueOnce(a11yStatusMessage1)\n        .mockReturnValueOnce(a11yStatusMessage2)\n      renderCombobox({\n        items,\n        getA11yStatusMessage,\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).toHaveTextContent(a11yStatusMessage1)\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        highlightedIndex: -1,\n        inputValue: '',\n        isOpen: true,\n        selectedItem: null,\n      })\n\n      getA11yStatusMessage.mockClear()\n\n      await keyDownOnInput('{ArrowDown}')\n\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).toHaveTextContent(a11yStatusMessage2)\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        highlightedIndex: 0,\n        inputValue: '',\n        isOpen: true,\n        selectedItem: null,\n      })\n    })\n\n    test('clears the text content after 500ms', async () => {\n      renderCombobox({\n        items,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate(true)\n\n      expect(getA11yStatusContainer()).toBeEmptyDOMElement()\n    })\n\n    test('removes the message element from the DOM on unmount', async () => {\n      const {unmount} = renderCombobox({\n        items,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate(true)\n      unmount()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('is added to the document provided by the user as prop', async () => {\n      const environment = {\n        document: {\n          getElementById: jest.fn().mockReturnValue({remove: jest.fn()}),\n          createElement: jest.fn(),\n          activeElement: {},\n          body: {},\n        },\n        addEventListener: jest.fn(),\n        removeEventListener: jest.fn(),\n        Node,\n      }\n      renderCombobox({\n        items,\n        environment,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(environment.document.getElementById).toHaveBeenCalledTimes(1)\n      expect(environment.document.getElementById).toHaveBeenCalledWith(\n        'a11y-status-message',\n      )\n    })\n  })\n\n  test('highlightedIndex controls the state property if passed', async () => {\n    const highlightedIndex = 1\n    const expectedItemId = defaultIds.getItemId(highlightedIndex)\n    renderCombobox({\n      isOpen: true,\n      highlightedIndex,\n    })\n    const input = getInput()\n\n    expect(input).toHaveAttribute('aria-activedescendant', expectedItemId)\n\n    await keyDownOnInput('{ArrowDown}')\n\n    expect(input).toHaveAttribute('aria-activedescendant', expectedItemId)\n\n    await keyDownOnInput('{End}')\n\n    expect(input).toHaveAttribute('aria-activedescendant', expectedItemId)\n\n    await keyDownOnInput('{ArrowUp}')\n\n    expect(input).toHaveAttribute('aria-activedescendant', expectedItemId)\n  })\n\n  describe('inputValue', () => {\n    test('controls the state property if passed', async () => {\n      renderCombobox({isOpen: true, inputValue: 'Dohn Joe'})\n      const input = getInput()\n\n      await keyDownOnInput('{ArrowDown}')\n      await keyDownOnInput('{Enter}')\n\n      expect(input).toHaveValue('Dohn Joe')\n\n      await clickOnItemAtIndex(0)\n\n      expect(input).toHaveValue('Dohn Joe')\n\n      await keyDownOnInput('{ArrowUp}')\n      await keyDownOnInput('{Enter}')\n\n      expect(input).toHaveValue('Dohn Joe')\n\n      await tab()\n\n      expect(input).toHaveValue('Dohn Joe')\n    })\n\n    test('is changed once a new selectedItem comes from props', () => {\n      const initialSelectedItem = 'John Doe'\n      const finalSelectedItem = 'John Wick'\n      const {rerender} = renderCombobox({\n        isOpen: true,\n        selectedItem: initialSelectedItem,\n      })\n      const input = getInput()\n\n      expect(input).toHaveValue(initialSelectedItem)\n\n      rerender({selectedItem: finalSelectedItem, isOpen: true})\n\n      expect(input).toHaveValue(finalSelectedItem)\n    })\n  })\n\n  test('isOpen controls the state property if passed', async () => {\n    renderCombobox({isOpen: true})\n\n    expect(getItems()).toHaveLength(items.length)\n\n    await clickOnToggleButton()\n\n    expect(getItems()).toHaveLength(items.length)\n\n    await keyDownOnInput('{Escape}')\n\n    expect(getItems()).toHaveLength(items.length)\n\n    await tab()\n\n    expect(getItems()).toHaveLength(items.length)\n  })\n\n  test('selectedItem controls the state property if passed', async () => {\n    const selectedItem = items[2]\n\n    renderCombobox({\n      selectedItem,\n      initialIsOpen: true,\n    })\n    const input = getInput()\n\n    expect(input).toHaveValue(selectedItem)\n\n    await keyDownOnInput('{ArrowDown}')\n    await keyDownOnInput('{Enter}')\n    await clickOnToggleButton()\n\n    expect(input).toHaveValue(selectedItem)\n\n    await keyDownOnInput('{ArrowUp}')\n    await keyDownOnInput('{Enter}')\n    await clickOnToggleButton()\n\n    expect(input).toHaveValue(selectedItem)\n\n    await keyDownOnInput('{Escape}')\n    await clickOnToggleButton()\n\n    expect(input).toHaveValue(selectedItem)\n\n    await clickOnItemAtIndex(1)\n    await clickOnToggleButton()\n\n    expect(input).toHaveValue(selectedItem)\n  })\n\n  test('selectedItem change updates the input value', async () => {\n    const selectedItem = items[2]\n    const newSelectedItem = items[4]\n    const nullSelectedItem = null\n    const lastSelectedItem = items[1]\n    const stateReducer = jest.fn().mockImplementation((s, a) => a.changes)\n\n    const {rerender} = renderCombobox({\n      selectedItem,\n      stateReducer,\n    })\n    const input = getInput()\n\n    expect(input).toHaveValue(selectedItem)\n    expect(stateReducer).not.toHaveBeenCalled() // don't call on first render.\n\n    rerender({\n      selectedItem: newSelectedItem,\n      stateReducer,\n    })\n\n    expect(stateReducer).toHaveBeenCalledTimes(1)\n    expect(stateReducer).toHaveBeenCalledWith(\n      expect.anything(),\n      expect.objectContaining({\n        type: useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem,\n      }),\n    )\n    expect(input).toHaveValue(newSelectedItem)\n\n    stateReducer.mockClear()\n    rerender({\n      selectedItem: nullSelectedItem,\n      stateReducer,\n    })\n\n    expect(stateReducer).toHaveBeenCalledTimes(1)\n    expect(stateReducer).toHaveBeenCalledWith(\n      expect.anything(),\n      expect.objectContaining({\n        type: useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem,\n      }),\n    )\n    expect(input).toHaveValue('')\n\n    stateReducer.mockClear()\n    rerender({\n      selectedItem: lastSelectedItem,\n      stateReducer,\n    })\n\n    expect(stateReducer).toHaveBeenCalledTimes(1)\n    expect(stateReducer).toHaveBeenCalledWith(\n      expect.anything(),\n      expect.objectContaining({\n        type: useCombobox.stateChangeTypes.ControlledPropUpdatedSelectedItem,\n      }),\n    )\n    expect(input).toHaveValue(lastSelectedItem)\n  })\n\n  describe('stateReducer', () => {\n    beforeEach(() => jest.useFakeTimers())\n    afterEach(() => {\n      act(() => jest.runAllTimers())\n    })\n    afterAll(jest.useRealTimers)\n\n    test('is called at each state change with the function change type', () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      const {result} = renderUseCombobox({stateReducer})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionToggleMenu}),\n      )\n\n      act(() => {\n        result.current.openMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(2)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionOpenMenu}),\n      )\n\n      act(() => {\n        result.current.closeMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(3)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionCloseMenu}),\n      )\n\n      act(() => {\n        result.current.reset()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(4)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionReset}),\n      )\n\n      act(() => {\n        result.current.selectItem({})\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(5)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionSelectItem}),\n      )\n\n      act(() => {\n        result.current.setHighlightedIndex(5)\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(6)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({\n          type: stateChangeTypes.FunctionSetHighlightedIndex,\n        }),\n      )\n\n      act(() => {\n        result.current.setInputValue({})\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(7)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionSetInputValue}),\n      )\n    })\n\n    test('is called at each state change with the appropriate change type', async () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      renderCombobox({stateReducer})\n      const initialState = {\n        isOpen: false,\n        highlightedIndex: -1,\n        inputValue: '',\n        selectedItem: null,\n      }\n      const testCases = [\n        {\n          step: clickOnInput,\n          state: {\n            isOpen: true,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputClick,\n        },\n        {\n          step: tab,\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputBlur,\n        },\n        {\n          step: clickOnInput,\n          state: {\n            isOpen: true,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputClick,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{Enter}',\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownEnter,\n        },\n        {\n          step: clickOnToggleButton,\n          state: {\n            isOpen: true,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.ToggleButtonClick,\n        },\n        {\n          step: mouseMoveItemAtIndex,\n          args: 2,\n          state: {\n            isOpen: true,\n            highlightedIndex: 2,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.ItemMouseMove,\n        },\n        {\n          step: mouseLeaveItemAtIndex,\n          args: 2,\n          state: {\n            isOpen: true,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.MenuMouseLeave,\n        },\n        {\n          step: changeInputValue,\n          args: 'c',\n          state: {\n            isOpen: true,\n            highlightedIndex: -1,\n            inputValue: 'c',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputChange,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{ArrowDown}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 0,\n            inputValue: 'c',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownArrowDown,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{Enter}',\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: items[0],\n            selectedItem: items[0],\n          },\n          type: stateChangeTypes.InputKeyDownEnter,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{Escape}',\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownEscape,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{ArrowDown}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 0,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownArrowDown,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{ArrowUp}',\n          state: {\n            isOpen: true,\n            highlightedIndex: items.length - 1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownArrowUp,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{Home}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 0,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownHome,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{End}',\n          state: {\n            isOpen: true,\n            highlightedIndex: items.length - 1,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownEnd,\n        },\n        {\n          step: mouseMoveItemAtIndex,\n          args: 3,\n          state: {\n            isOpen: true,\n            highlightedIndex: 3,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.ItemMouseMove,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{PageDown}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 13,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownPageDown,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{PageUp}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 3,\n            inputValue: '',\n            selectedItem: null,\n          },\n          type: stateChangeTypes.InputKeyDownPageUp,\n        },\n        {\n          step: clickOnItemAtIndex,\n          args: 3,\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: items[3],\n            selectedItem: items[3],\n          },\n          type: stateChangeTypes.ItemClick,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{ArrowUp}',\n          state: {\n            isOpen: true,\n            highlightedIndex: 3,\n            inputValue: items[3],\n            selectedItem: items[3],\n          },\n          type: stateChangeTypes.InputKeyDownArrowUp,\n        },\n        {\n          step: keyDownOnInput,\n          args: '{Escape}',\n          state: {\n            isOpen: false,\n            highlightedIndex: -1,\n            inputValue: items[3],\n            selectedItem: items[3],\n          },\n          type: stateChangeTypes.InputKeyDownEscape,\n        },\n      ]\n\n      expect(stateReducer).not.toHaveBeenCalled()\n\n      for (let index = 0; index < testCases.length; index++) {\n        const {step, state, args, type} = testCases[index]\n        // eslint-disable-next-line jest/no-conditional-in-test\n        const previousState = testCases[index - 1]?.state ?? initialState\n\n        // eslint-disable-next-line no-await-in-loop\n        await step(args)\n\n        expect(stateReducer).toHaveBeenCalledTimes(index + 1)\n        expect(stateReducer).toHaveBeenLastCalledWith(\n          previousState,\n          expect.objectContaining({changes: state, type}),\n        )\n      }\n    })\n\n    test('replaces prop values with user defined', async () => {\n      const inputValue = 'Robin Hood'\n      const stateReducer = jest.fn((s, a) => {\n        const changes = a.changes\n        changes.inputValue = inputValue\n        return changes\n      })\n      renderCombobox({stateReducer})\n\n      await clickOnToggleButton()\n\n      expect(getInput()).toHaveValue('Robin Hood')\n    })\n\n    test('receives state, changes and type', async () => {\n      const stateReducer = jest.fn((s, a) => {\n        expect(a.type).not.toBeUndefined()\n        expect(a.type).not.toBeNull()\n\n        expect(s).not.toBeUndefined()\n        expect(s).not.toBeNull()\n\n        expect(a.changes).not.toBeUndefined()\n        expect(a.changes).not.toBeNull()\n\n        return a.changes\n      })\n      renderCombobox({stateReducer})\n\n      await clickOnToggleButton()\n    })\n\n    test('changes are visible in onChange handlers', async () => {\n      const highlightedIndex = 2\n      const selectedItem = {foo: 'bar'}\n      const isOpen = true\n      const inputValue = 'test'\n      const stateReducer = jest.fn(() => ({\n        highlightedIndex,\n        isOpen,\n        selectedItem,\n        inputValue,\n      }))\n      const onInputValueChange = jest.fn()\n      const onSelectedItemChange = jest.fn()\n      const onHighlightedIndexChange = jest.fn()\n      const onIsOpenChange = jest.fn()\n      const onStateChange = jest.fn()\n      renderCombobox({\n        stateReducer,\n        onStateChange,\n        onSelectedItemChange,\n        onHighlightedIndexChange,\n        onIsOpenChange,\n        onInputValueChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onInputValueChange).toHaveBeenCalledTimes(1)\n      expect(onInputValueChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          inputValue,\n        }),\n      )\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex,\n        }),\n      )\n      expect(onSelectedItemChange).toHaveBeenCalledTimes(1)\n      expect(onSelectedItemChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItem,\n        }),\n      )\n      expect(onIsOpenChange).toHaveBeenCalledTimes(1)\n      expect(onIsOpenChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen,\n        }),\n      )\n      expect(onStateChange).toHaveBeenCalledTimes(1)\n      expect(onStateChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen,\n          type: stateChangeTypes.ToggleButtonClick,\n        }),\n      )\n    })\n  })\n\n  describe('onInputValueChange', () => {\n    test('is called at inputValue change', async () => {\n      const inputValue = 'test'\n      const onInputValueChange = jest.fn()\n      renderCombobox({\n        onInputValueChange,\n      })\n\n      await changeInputValue(inputValue)\n\n      expect(onInputValueChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          inputValue,\n        }),\n      )\n    })\n\n    test('is not called at if inputValue is the same', async () => {\n      const inputValue = items[0]\n      const onInputValueChange = jest.fn()\n      renderCombobox({\n        initialInputValue: inputValue,\n        initialIsOpen: true,\n        onInputValueChange,\n      })\n\n      await clickOnItemAtIndex(0)\n\n      expect(onInputValueChange).not.toHaveBeenCalled()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let inputValue = 'nep'\n      const {rerender} = renderCombobox({\n        inputValue,\n        onSelectedItemChange: changes => {\n          inputValue = changes.inputValue\n        },\n      })\n\n      await changeInputValue('nept')\n      rerender({inputValue})\n\n      expect(getInput()).toHaveValue(inputValue)\n    })\n\n    test('can have downshift actions executed', () => {\n      const highlightedIndex = 3\n      const {result} = renderUseCombobox({\n        onInputValueChange: () => {\n          result.current.setHighlightedIndex(highlightedIndex)\n        },\n      })\n\n      act(() => {\n        result.current.getInputProps().onChange({target: {value: 'ala'}})\n      })\n\n      expect(result.current.highlightedIndex).toEqual(highlightedIndex)\n    })\n  })\n\n  describe('onSelectedItemChange', () => {\n    test('is called at selectedItem change', async () => {\n      const itemIndex = 0\n      const onSelectedItemChange = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        onSelectedItemChange,\n      })\n\n      await clickOnItemAtIndex(itemIndex)\n\n      expect(onSelectedItemChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItem: items[itemIndex],\n          type: stateChangeTypes.ItemClick,\n        }),\n      )\n    })\n\n    test('is not called at if selectedItem is the same', async () => {\n      const itemIndex = 0\n      const onSelectedItemChange = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        initialSelectedItem: items[itemIndex],\n        onSelectedItemChange,\n      })\n\n      await clickOnItemAtIndex(itemIndex)\n\n      expect(onSelectedItemChange).not.toHaveBeenCalled()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let selectedItem = items[2]\n      const selectionIndex = 3\n      const {rerender} = renderCombobox({\n        initialIsOpen: true,\n        selectedItem,\n        onSelectedItemChange: changes => {\n          selectedItem = changes.selectedItem\n        },\n      })\n\n      await clickOnItemAtIndex(selectionIndex)\n      rerender({selectedItem})\n\n      expect(getInput()).toHaveValue(items[selectionIndex])\n    })\n\n    test('works correctly with the corresponding control prop in strict mode', async () => {\n      const onSelectedItemChange = jest.fn()\n      const itemIndex = 0\n      renderCombobox(\n        {selectedItem: null, initialIsOpen: true, onSelectedItemChange},\n        ui => <React.StrictMode>{ui}</React.StrictMode>,\n      )\n\n      await clickOnItemAtIndex(itemIndex)\n\n      expect(onSelectedItemChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItem: items[itemIndex],\n        }),\n      )\n    })\n\n    test('can have downshift actions executed', () => {\n      const {result} = renderUseCombobox({\n        initialIsOpen: true,\n        onSelectedItemChange: () => {\n          result.current.openMenu()\n        },\n      })\n\n      act(() => {\n        result.current.getItemProps({index: 2}).onClick({})\n      })\n\n      expect(result.current.isOpen).toEqual(true)\n    })\n  })\n\n  describe('onHighlightedIndexChange', () => {\n    test('is called at each highlightedIndex change', async () => {\n      const onHighlightedIndexChange = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        onHighlightedIndexChange,\n      })\n\n      await keyDownOnInput('{ArrowDown}')\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 0,\n          type: stateChangeTypes.InputKeyDownArrowDown,\n        }),\n      )\n    })\n\n    test('is not called if highlightedIndex is the same', async () => {\n      const onHighlightedIndexChange = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        initialHighlightedIndex: 0,\n        onHighlightedIndexChange,\n      })\n\n      await keyDownOnInput('{Home}')\n\n      expect(onHighlightedIndexChange).not.toHaveBeenCalled()\n\n      await mouseMoveItemAtIndex(0)\n\n      expect(onHighlightedIndexChange).not.toHaveBeenCalled()\n    })\n\n    test('is called on first open when initialSelectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderCombobox({\n        initialSelectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('is called on first open when selectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderCombobox({\n        selectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('is called on first open when defaultSelectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderCombobox({\n        defaultSelectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let highlightedIndex = 2\n      const {rerender} = renderCombobox({\n        isOpen: true,\n        highlightedIndex,\n        onHighlightedIndexChange: changes => {\n          highlightedIndex = changes.highlightedIndex\n        },\n      })\n\n      await keyDownOnInput('{ArrowDown}')\n      rerender({highlightedIndex, isOpen: true})\n\n      expect(getInput()).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(3),\n      )\n    })\n\n    test('can have downshift actions executed', () => {\n      const highlightedIndex = 3\n      const {result} = renderUseCombobox({\n        initialIsOpen: true,\n        onHighlightedIndexChange: () => {\n          result.current.setHighlightedIndex(highlightedIndex)\n        },\n      })\n\n      act(() => {\n        result.current\n          .getInputProps()\n          .onKeyDown({key: 'ArrowDown', preventDefault: jest.fn()})\n      })\n\n      expect(result.current.highlightedIndex).toEqual(highlightedIndex)\n    })\n  })\n\n  describe('onIsOpenChange', () => {\n    test('is called at each isOpen change', async () => {\n      const onIsOpenChange = jest.fn()\n      renderCombobox({\n        initialIsOpen: true,\n        onIsOpenChange,\n      })\n\n      await keyDownOnInput('{Escape}')\n\n      expect(onIsOpenChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen: false,\n          type: stateChangeTypes.InputKeyDownEscape,\n        }),\n      )\n    })\n\n    test('is not called at if isOpen is the same', async () => {\n      const onIsOpenChange = jest.fn()\n      renderCombobox({\n        defaultIsOpen: true,\n        onIsOpenChange,\n      })\n\n      await clickOnItemAtIndex(0)\n\n      expect(onIsOpenChange).not.toHaveBeenCalledWith()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let isOpen = true\n      const {rerender} = renderCombobox({\n        isOpen,\n        onIsOpenChange: changes => {\n          isOpen = changes.isOpen\n        },\n      })\n\n      await keyDownOnInput('{Escape}')\n      rerender({isOpen})\n\n      expect(getItems()).toHaveLength(0)\n    })\n\n    test('can have downshift actions executed', () => {\n      const highlightedIndex = 3\n      const {result} = renderUseCombobox({\n        onIsOpenChange: () => {\n          result.current.setHighlightedIndex(highlightedIndex)\n        },\n      })\n\n      act(() => {\n        result.current.getToggleButtonProps().onClick({})\n      })\n\n      expect(result.current.highlightedIndex).toEqual(highlightedIndex)\n    })\n  })\n\n  describe('onStateChange', () => {\n    test('is called at each state property change', async () => {\n      const onStateChange = jest.fn()\n      renderCombobox({onStateChange})\n\n      await clickOnToggleButton()\n\n      expect(onStateChange).toHaveBeenCalledTimes(1)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          isOpen: true,\n          type: stateChangeTypes.ToggleButtonClick,\n        }),\n      )\n\n      await changeInputValue('t')\n\n      expect(onStateChange).toHaveBeenCalledTimes(2)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          inputValue: 't',\n          type: stateChangeTypes.InputChange,\n        }),\n      )\n\n      await mouseMoveItemAtIndex(2)\n\n      expect(onStateChange).toHaveBeenCalledTimes(3)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 2,\n          type: stateChangeTypes.ItemMouseMove,\n        }),\n      )\n\n      await clickOnItemAtIndex(2)\n\n      expect(onStateChange).toHaveBeenCalledTimes(4)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItem: items[2],\n          highlightedIndex: -1,\n          inputValue: items[2],\n          type: stateChangeTypes.ItemClick,\n        }),\n      )\n\n      await keyDownOnInput('{ArrowDown}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(5)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 2,\n          type: stateChangeTypes.InputKeyDownArrowDown,\n        }),\n      )\n\n      await keyDownOnInput('{End}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(6)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: items.length - 1,\n          type: stateChangeTypes.InputKeyDownEnd,\n        }),\n      )\n\n      await keyDownOnInput('{Home}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(7)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 0,\n          type: stateChangeTypes.InputKeyDownHome,\n        }),\n      )\n\n      await keyDownOnInput('{Enter}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(8)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: -1,\n          selectedItem: items[0],\n          inputValue: items[0],\n          type: stateChangeTypes.InputKeyDownEnter,\n        }),\n      )\n\n      await keyDownOnInput('{Escape}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(9)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItem: null,\n          inputValue: '',\n          type: stateChangeTypes.InputKeyDownEscape,\n        }),\n      )\n\n      await keyDownOnInput('{ArrowUp}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(10)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 25,\n          isOpen: true,\n          type: stateChangeTypes.InputKeyDownArrowUp,\n        }),\n      )\n\n      await mouseMoveItemAtIndex(25)\n      await mouseLeaveItemAtIndex(25)\n\n      expect(onStateChange).toHaveBeenCalledTimes(11)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          highlightedIndex: -1,\n          type: stateChangeTypes.MenuMouseLeave,\n        }),\n      )\n\n      await tab()\n\n      expect(onStateChange).toHaveBeenCalledTimes(12)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          isOpen: false,\n          type: stateChangeTypes.InputBlur,\n        }),\n      )\n\n      await clickOnInput()\n\n      expect(onStateChange).toHaveBeenCalledTimes(13)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          isOpen: true,\n          type: stateChangeTypes.InputClick,\n        }),\n      )\n\n      await clickOnInput()\n\n      expect(onStateChange).toHaveBeenCalledTimes(14)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          isOpen: false,\n          type: stateChangeTypes.InputClick,\n        }),\n      )\n    })\n\n    test('can have downshift actions executed', () => {\n      const {result} = renderUseCombobox({\n        initialIsOpen: true,\n        onStateChange: () => {\n          result.current.openMenu()\n        },\n      })\n\n      act(() => {\n        result.current.getItemProps({index: 2}).onClick({})\n      })\n\n      expect(result.current.isOpen).toEqual(true)\n    })\n  })\n\n  test('that are uncontrolled should not become controlled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderCombobox()\n\n    rerender({selectedItem: 'controlled'})\n\n    // eslint-disable-next-line no-console\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the uncontrolled prop \"selectedItem\" to be controlled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n\n  test('that are controlled should not become uncontrolled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderCombobox({inputValue: 'controlled value'})\n\n    rerender({})\n\n    // eslint-disable-next-line no-console\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the controlled prop \"inputValue\" to be uncontrolled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n\n  test('initialHighlightedIndex is ignored if item is disabled and menu is intially open', () => {\n    const initialHighlightedIndex = 2\n    const isItemDisabled = jest\n      .fn()\n      .mockImplementation(\n        item => items.indexOf(item) === initialHighlightedIndex,\n      )\n    renderCombobox({\n      initialHighlightedIndex,\n      isItemDisabled,\n      initialIsOpen: true,\n    })\n\n    expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n    expect(isItemDisabled).toHaveBeenNthCalledWith(\n      1,\n      items[initialHighlightedIndex],\n      initialHighlightedIndex,\n    )\n  })\n\n  test('defaultHighlightedIndex is ignored if item is disabled and menu is intially open', () => {\n    const defaultHighlightedIndex = 2\n    const isItemDisabled = jest\n      .fn()\n      .mockImplementation(\n        item => items.indexOf(item) === defaultHighlightedIndex,\n      )\n    renderCombobox({\n      defaultHighlightedIndex,\n      isItemDisabled,\n      initialIsOpen: true,\n    })\n\n    expect(getInput()).toHaveAttribute('aria-activedescendant', '')\n    expect(isItemDisabled).toHaveBeenNthCalledWith(\n      1,\n      items[defaultHighlightedIndex],\n      defaultHighlightedIndex,\n    )\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/returnProps.test.js",
    "content": "import {act} from '@testing-library/react'\n\nimport {renderUseCombobox} from '../testUtils'\nimport {items} from '../../testUtils'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport useCombobox from '..'\n\ndescribe('returnProps', () => {\n  test('should have stateChangeTypes attached to hook', () => {\n    expect(useCombobox).toHaveProperty('stateChangeTypes', stateChangeTypes)\n  })\n\n  describe('prop getters', () => {\n    test('are returned as functions', () => {\n      const {result} = renderUseCombobox()\n\n      expect(result.current.getMenuProps).toBeInstanceOf(Function)\n      expect(result.current.getItemProps).toBeInstanceOf(Function)\n      expect(result.current.getLabelProps).toBeInstanceOf(Function)\n      expect(result.current.getToggleButtonProps).toBeInstanceOf(Function)\n      expect(result.current.getInputProps).toBeInstanceOf(Function)\n    })\n  })\n\n  describe('actions', () => {\n    test('openMenu opens the closed menu', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.openMenu()\n      })\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('openMenu does nothing to open menu', () => {\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        result.current.openMenu()\n      })\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('closeMenu closes the open menu', () => {\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        result.current.closeMenu()\n      })\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('closeMenu does nothing to closed menu', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.closeMenu()\n      })\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('toggleMenu opens closed menu', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('toggleMenu closes open menu', () => {\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('setHighlightedIndex sets highlightedIndex', () => {\n      const {result} = renderUseCombobox({initialIsOpen: true})\n\n      act(() => {\n        result.current.setHighlightedIndex(2)\n      })\n\n      expect(result.current.highlightedIndex).toBe(2)\n    })\n\n    test('setHighlightedIndex does not set highlightedIndex if item is disabled', () => {\n      const highlightedIndex = 2\n      const {result} = renderUseCombobox({\n        initialIsOpen: true,\n        isItemDisabled(_item, index) {\n          return index === highlightedIndex\n        },\n      })\n\n      act(() => {\n        result.current.setHighlightedIndex(highlightedIndex)\n      })\n\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n\n    test('setInputValue sets inputValue', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.setInputValue(\"I'm Batman!\")\n      })\n\n      expect(result.current.inputValue).toBe(\"I'm Batman!\")\n    })\n\n    test('selectItem sets selectedItem and inputValue', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.selectItem(items[2])\n      })\n\n      expect(result.current.selectedItem).toBe(items[2])\n      expect(result.current.inputValue).toBe(items[2])\n    })\n\n    test('selectItem with null clears inputvalue', () => {\n      const {result} = renderUseCombobox({initialSelectedItem: items[2]})\n\n      act(() => {\n        result.current.selectItem(null)\n      })\n\n      expect(result.current.selectedItem).toBe(null)\n      expect(result.current.inputValue).toBe('')\n    })\n\n    test('reset sets the state to default values', () => {\n      const {result} = renderUseCombobox({})\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[2])\n        result.current.setHighlightedIndex(3)\n        result.current.setInputValue('bla-blu')\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(null)\n      expect(result.current.highlightedIndex).toBe(-1)\n      expect(result.current.inputValue).toBe('')\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('reset sets the state to default prop values passed by user', () => {\n      const props = {\n        defaultIsOpen: false,\n        defaultHighlightedIndex: 3,\n        defaultSelectedItem: items[2],\n        defaultInputValue: 'my-default',\n      }\n      const {result} = renderUseCombobox(props)\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[4])\n        result.current.setHighlightedIndex(1)\n        result.current.setInputValue('something different')\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(props.defaultSelectedItem)\n      expect(result.current.highlightedIndex).toBe(\n        props.defaultHighlightedIndex,\n      )\n      expect(result.current.isOpen).toBe(props.defaultIsOpen)\n      expect(result.current.inputValue).toBe(props.defaultInputValue)\n    })\n\n    test('reset does not set the defaultHighlightedIndex if item is disabled', () => {\n      const props = {\n        defaultIsOpen: false,\n        defaultHighlightedIndex: 3,\n        defaultSelectedItem: items[2],\n        isItemDisabled(_item, index) {\n          return index === 3\n        },\n      }\n      const {result} = renderUseCombobox(props)\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[4])\n        result.current.setHighlightedIndex(1)\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(props.defaultSelectedItem)\n      expect(result.current.highlightedIndex).toBe(-1)\n      expect(result.current.isOpen).toBe(props.defaultIsOpen)\n    })\n  })\n\n  describe('state and props', () => {\n    test('highlightedIndex is returned', () => {\n      const {result} = renderUseCombobox({highlightedIndex: 4})\n\n      expect(result.current.highlightedIndex).toBe(4)\n    })\n\n    test('isOpen is returned', () => {\n      const {result} = renderUseCombobox({isOpen: false})\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('selectedItem is returned', () => {\n      const {result} = renderUseCombobox({selectedItem: items[1]})\n\n      expect(result.current.selectedItem).toBe(items[1])\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/__tests__/utils.test.js",
    "content": "import reducer from '../reducer'\n\ndescribe('utils', () => {\n  test('reducer throws error if called without proper action type', () => {\n    expect(() => {\n      reducer({}, {}, {type: 'super-bogus'})\n    }).toThrow('Reducer called without proper action type.')\n  })\n})\n"
  },
  {
    "path": "src/hooks/useCombobox/index.js",
    "content": "import {useRef, useEffect, useCallback, useMemo} from 'react'\nimport {isPreact, isReactNative, isReactNativeWeb} from '../../is.macro'\nimport {handleRefs, normalizeArrowKey, callAllEventHandlers} from '../../utils'\nimport {useLatestRef, validatePropTypes} from '../../utils-ts'\nimport {\n  useMouseAndTouchTracker,\n  useGetterPropsCalledChecker,\n  useScrollIntoView,\n  useControlPropsValidator,\n  isDropdownsStateEqual,\n} from '../utils'\nimport {\n  getItemAndIndex,\n  getInitialValue,\n  useIsInitialMount,\n  useA11yMessageStatus,\n} from '../utils-ts'\nimport {defaultStateValues} from '../utils.dropdown/defaultStateValues'\nimport {\n  getInitialState,\n  defaultProps,\n  useControlledReducer,\n  propTypes,\n} from './utils'\nimport downshiftUseComboboxReducer from './reducer'\nimport * as stateChangeTypes from './stateChangeTypes'\nimport {useElementIds} from '../utils.dropdown/useElementIds'\n\nuseCombobox.stateChangeTypes = stateChangeTypes\n\nfunction useCombobox(userProps = {}) {\n  validatePropTypes(userProps, useCombobox, propTypes)\n  // Props defaults and destructuring.\n  const props = {\n    ...defaultProps,\n    ...userProps,\n  }\n  const {items, scrollIntoView, environment, getA11yStatusMessage} = props\n  // Initial state depending on controlled props.\n  const [state, dispatch] = useControlledReducer(\n    downshiftUseComboboxReducer,\n    props,\n    getInitialState,\n    isDropdownsStateEqual,\n  )\n  const {isOpen, highlightedIndex, selectedItem, inputValue} = state\n\n  // Element refs.\n  const menuRef = useRef(null)\n  const itemRefs = useRef({})\n  const inputRef = useRef(null)\n  const toggleButtonRef = useRef(null)\n  const isInitialMount = useIsInitialMount()\n\n  // prevent id re-generation between renders.\n  const elementIds = useElementIds(props)\n  // used to keep track of how many items we had on previous cycle.\n  const previousResultCountRef = useRef()\n  // utility callback to get item element.\n  const latest = useLatestRef({state, props})\n\n  const getItemNodeFromIndex = useCallback(\n    index => itemRefs.current[elementIds.getItemId(index)],\n    [elementIds],\n  )\n\n  // Effects.\n  // Adds an a11y aria live status message if getA11yStatusMessage is passed.\n  useA11yMessageStatus(\n    getA11yStatusMessage,\n    state,\n    [isOpen, highlightedIndex, selectedItem, inputValue],\n    environment,\n  )\n  // Scroll on highlighted item if change comes from keyboard.\n  const shouldScrollRef = useScrollIntoView({\n    menuElement: menuRef.current,\n    highlightedIndex,\n    isOpen,\n    itemRefs,\n    scrollIntoView,\n    getItemNodeFromIndex,\n  })\n  useControlPropsValidator({\n    props,\n    state,\n  })\n  // Focus the input on first render if required.\n  useEffect(() => {\n    const focusOnOpen = getInitialValue(props, 'isOpen', defaultStateValues)\n\n    if (focusOnOpen && inputRef.current) {\n      inputRef.current.focus()\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n  useEffect(() => {\n    if (!isInitialMount) {\n      previousResultCountRef.current = items.length\n    }\n  })\n\n  const handleBlurInTracker = useCallback(\n    function handleBlur() {\n      if (latest.current.state.isOpen) {\n        dispatch({\n          type: stateChangeTypes.InputBlur,\n        })\n      }\n    },\n    [dispatch, latest],\n  )\n  const downshiftRefs = useMemo(() => [menuRef, toggleButtonRef, inputRef], [])\n  const mouseAndTouchTrackers = useMouseAndTouchTracker(\n    environment,\n    handleBlurInTracker,\n    downshiftRefs,\n  )\n  const setGetterPropCallInfo = useGetterPropsCalledChecker(\n    'getInputProps',\n    'getMenuProps',\n  )\n  // Reset itemRefs on close.\n  useEffect(() => {\n    if (!isOpen) {\n      itemRefs.current = {}\n    }\n  }, [isOpen])\n  // Reset itemRefs on close.\n  useEffect(() => {\n    if (!isOpen || !environment?.document || !inputRef?.current?.focus) {\n      return\n    }\n\n    if (environment.document.activeElement !== inputRef.current) {\n      inputRef.current.focus()\n    }\n  }, [isOpen, environment])\n\n  /* Event handler functions */\n  const inputKeyDownHandlers = useMemo(\n    () => ({\n      ArrowDown(event) {\n        event.preventDefault()\n        dispatch({\n          type: stateChangeTypes.InputKeyDownArrowDown,\n          altKey: event.altKey,\n        })\n      },\n      ArrowUp(event) {\n        event.preventDefault()\n        dispatch({\n          type: stateChangeTypes.InputKeyDownArrowUp,\n          altKey: event.altKey,\n        })\n      },\n      Home(event) {\n        if (!latest.current.state.isOpen) {\n          return\n        }\n\n        event.preventDefault()\n        dispatch({\n          type: stateChangeTypes.InputKeyDownHome,\n        })\n      },\n      End(event) {\n        if (!latest.current.state.isOpen) {\n          return\n        }\n\n        event.preventDefault()\n        dispatch({\n          type: stateChangeTypes.InputKeyDownEnd,\n        })\n      },\n      Escape(event) {\n        const latestState = latest.current.state\n        if (\n          latestState.isOpen ||\n          latestState.inputValue ||\n          latestState.selectedItem ||\n          latestState.highlightedIndex > -1\n        ) {\n          event.preventDefault()\n\n          dispatch({\n            type: stateChangeTypes.InputKeyDownEscape,\n          })\n        }\n      },\n      Enter(event) {\n        const latestState = latest.current.state\n        // if closed or no highlighted index, do nothing.\n        if (\n          !latestState.isOpen ||\n          event.which === 229 // if IME composing, wait for next Enter keydown event.\n        ) {\n          return\n        }\n\n        event.preventDefault()\n        dispatch({\n          type: stateChangeTypes.InputKeyDownEnter,\n        })\n      },\n      PageUp(event) {\n        if (latest.current.state.isOpen) {\n          event.preventDefault()\n\n          dispatch({\n            type: stateChangeTypes.InputKeyDownPageUp,\n          })\n        }\n      },\n      PageDown(event) {\n        if (latest.current.state.isOpen) {\n          event.preventDefault()\n\n          dispatch({\n            type: stateChangeTypes.InputKeyDownPageDown,\n          })\n        }\n      },\n    }),\n    [dispatch, latest],\n  )\n\n  // Getter props.\n  const getLabelProps = useCallback(\n    labelProps => ({\n      id: elementIds.labelId,\n      htmlFor: elementIds.inputId,\n      ...labelProps,\n    }),\n    [elementIds],\n  )\n  const getMenuProps = useCallback(\n    (\n      {onMouseLeave, refKey = 'ref', ref, ...rest} = {},\n      {suppressRefError = false} = {},\n    ) => {\n      setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef)\n      return {\n        [refKey]: handleRefs(ref, menuNode => {\n          menuRef.current = menuNode\n        }),\n        id: elementIds.menuId,\n        role: 'listbox',\n        'aria-labelledby':\n          rest && rest['aria-label'] ? undefined : `${elementIds.labelId}`,\n        onMouseLeave: callAllEventHandlers(onMouseLeave, () => {\n          dispatch({\n            type: stateChangeTypes.MenuMouseLeave,\n          })\n        }),\n        ...rest,\n      }\n    },\n    [dispatch, setGetterPropCallInfo, elementIds],\n  )\n\n  const getItemProps = useCallback(\n    ({\n      item: itemProp,\n      index: indexProp,\n      refKey = 'ref',\n      ref,\n      onMouseMove,\n      onMouseDown,\n      onClick,\n      onPress,\n      disabled: disabledProp,\n      ...rest\n    } = {}) => {\n      if (disabledProp !== undefined) {\n        console.warn(\n          'Passing \"disabled\" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useCombobox.',\n        )\n      }\n\n      const {props: latestProps, state: latestState} = latest.current\n      const [item, index] = getItemAndIndex(\n        itemProp,\n        indexProp,\n        latestProps.items,\n        'Pass either item or index to getItemProps!',\n      )\n      const disabled = latestProps.isItemDisabled(item, index)\n      const onSelectKey =\n        isReactNative || isReactNativeWeb\n          ? /* istanbul ignore next (react-native) */ 'onPress'\n          : 'onClick'\n      const customClickHandler = isReactNative\n        ? /* istanbul ignore next (react-native) */ onPress\n        : onClick\n\n      const itemHandleMouseMove = () => {\n        if (\n          mouseAndTouchTrackers.isTouchEnd ||\n          index === latestState.highlightedIndex\n        ) {\n          return\n        }\n\n        shouldScrollRef.current = false\n\n        dispatch({\n          type: stateChangeTypes.ItemMouseMove,\n          index,\n          disabled,\n        })\n      }\n      const itemHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.ItemClick,\n          index,\n        })\n      }\n      const itemHandleMouseDown = e => e.preventDefault() // keep focus on the input after item click select.\n\n      return {\n        [refKey]: handleRefs(ref, itemNode => {\n          if (itemNode) {\n            itemRefs.current[elementIds.getItemId(index)] = itemNode\n          }\n        }),\n        'aria-disabled': disabled,\n        'aria-selected': index === latestState.highlightedIndex,\n        id: elementIds.getItemId(index),\n        role: 'option',\n        ...(!disabled && {\n          [onSelectKey]: callAllEventHandlers(\n            customClickHandler,\n            itemHandleClick,\n          ),\n        }),\n        onMouseMove: callAllEventHandlers(onMouseMove, itemHandleMouseMove),\n        onMouseDown: callAllEventHandlers(onMouseDown, itemHandleMouseDown),\n        ...rest,\n      }\n    },\n\n    [dispatch, elementIds, latest, mouseAndTouchTrackers, shouldScrollRef],\n  )\n\n  const getToggleButtonProps = useCallback(\n    ({onClick, onPress, refKey = 'ref', ref, ...rest} = {}) => {\n      const latestState = latest.current.state\n      const toggleButtonHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.ToggleButtonClick,\n        })\n      }\n\n      return {\n        [refKey]: handleRefs(ref, toggleButtonNode => {\n          toggleButtonRef.current = toggleButtonNode\n        }),\n        'aria-controls': elementIds.menuId,\n        'aria-expanded': latestState.isOpen,\n        id: elementIds.toggleButtonId,\n        tabIndex: -1,\n        ...(!rest.disabled && {\n          ...(isReactNative || isReactNativeWeb\n            ? /* istanbul ignore next (react-native) */ {\n                onPress: callAllEventHandlers(onPress, toggleButtonHandleClick),\n              }\n            : {\n                onClick: callAllEventHandlers(onClick, toggleButtonHandleClick),\n              }),\n        }),\n        ...rest,\n      }\n    },\n    [dispatch, latest, elementIds],\n  )\n  const getInputProps = useCallback(\n    (\n      {\n        onKeyDown,\n        onChange,\n        onInput,\n        onBlur,\n        onChangeText,\n        onClick,\n        refKey = 'ref',\n        ref,\n        ...rest\n      } = {},\n      {suppressRefError = false} = {},\n    ) => {\n      setGetterPropCallInfo('getInputProps', suppressRefError, refKey, inputRef)\n\n      const latestState = latest.current.state\n      const inputHandleKeyDown = event => {\n        const key = normalizeArrowKey(event)\n        if (key && inputKeyDownHandlers[key]) {\n          inputKeyDownHandlers[key](event)\n        }\n      }\n      const inputHandleChange = event => {\n        dispatch({\n          type: stateChangeTypes.InputChange,\n          inputValue:\n            isReactNative || isReactNativeWeb\n              ? /* istanbul ignore next (react-native) */ event.nativeEvent.text\n              : event.target.value,\n        })\n      }\n      const inputHandleBlur = event => {\n        /* istanbul ignore else */\n        if (\n          environment?.document &&\n          latestState.isOpen &&\n          !mouseAndTouchTrackers.isMouseDown\n        ) {\n          const isBlurByTabChange =\n            event.relatedTarget === null &&\n            environment.document.activeElement !== environment.document.body\n\n          dispatch({\n            type: stateChangeTypes.InputBlur,\n            selectItem: !isBlurByTabChange,\n          })\n        }\n      }\n\n      const inputHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.InputClick,\n        })\n      }\n\n      /* istanbul ignore next (preact) */\n      const onChangeKey = isPreact ? 'onInput' : 'onChange'\n      let eventHandlers = {}\n\n      if (!rest.disabled) {\n        eventHandlers = {\n          [onChangeKey]: callAllEventHandlers(\n            onChange,\n            onInput,\n            inputHandleChange,\n          ),\n          onKeyDown: callAllEventHandlers(onKeyDown, inputHandleKeyDown),\n          onBlur: callAllEventHandlers(onBlur, inputHandleBlur),\n          onClick: callAllEventHandlers(onClick, inputHandleClick),\n        }\n      }\n\n      /* istanbul ignore if (react-native) */\n      if (isReactNative) {\n        eventHandlers.onChange = callAllEventHandlers(\n          onChange,\n          onInput,\n          inputHandleChange,\n        )\n        eventHandlers.onChangeText = callAllEventHandlers(\n          onChangeText,\n          onInput,\n          text => {\n            inputHandleChange({nativeEvent: {text}})\n          },\n        )\n      }\n\n      return {\n        [refKey]: handleRefs(ref, inputNode => {\n          inputRef.current = inputNode\n        }),\n        'aria-activedescendant':\n          latestState.isOpen && latestState.highlightedIndex > -1\n            ? elementIds.getItemId(latestState.highlightedIndex)\n            : '',\n        'aria-autocomplete': 'list',\n        'aria-controls': elementIds.menuId,\n        'aria-expanded': latestState.isOpen,\n        'aria-labelledby':\n          rest && rest['aria-label'] ? undefined : elementIds.labelId,\n        // https://developer.mozilla.org/en-US/docs/Web/Security/Securing_your_site/Turning_off_form_autocompletion\n        // revert back since autocomplete=\"nope\" is ignored on latest Chrome and Opera\n        autoComplete: 'off',\n        id: elementIds.inputId,\n        role: 'combobox',\n        value: latestState.inputValue,\n        ...eventHandlers,\n        ...rest,\n      }\n    },\n    [\n      dispatch,\n      elementIds,\n      environment,\n      inputKeyDownHandlers,\n      latest,\n      mouseAndTouchTrackers,\n      setGetterPropCallInfo,\n    ],\n  )\n\n  // returns\n  const toggleMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionToggleMenu,\n    })\n  }, [dispatch])\n  const closeMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionCloseMenu,\n    })\n  }, [dispatch])\n  const openMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionOpenMenu,\n    })\n  }, [dispatch])\n  const setHighlightedIndex = useCallback(\n    newHighlightedIndex => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetHighlightedIndex,\n        highlightedIndex: newHighlightedIndex,\n      })\n    },\n    [dispatch],\n  )\n  const selectItem = useCallback(\n    newSelectedItem => {\n      dispatch({\n        type: stateChangeTypes.FunctionSelectItem,\n        selectedItem: newSelectedItem,\n      })\n    },\n    [dispatch],\n  )\n  const setInputValue = useCallback(\n    newInputValue => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetInputValue,\n        inputValue: newInputValue,\n      })\n    },\n    [dispatch],\n  )\n  const reset = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionReset,\n    })\n  }, [dispatch])\n\n  return {\n    // prop getters.\n    getItemProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    getToggleButtonProps,\n    // actions.\n    toggleMenu,\n    openMenu,\n    closeMenu,\n    setHighlightedIndex,\n    setInputValue,\n    selectItem,\n    reset,\n    // state.\n    highlightedIndex,\n    isOpen,\n    selectedItem,\n    inputValue,\n  }\n}\n\nexport default useCombobox\n"
  },
  {
    "path": "src/hooks/useCombobox/reducer.js",
    "content": "import {\n  getHighlightedIndexOnOpen,\n  getChangesOnSelection,\n  getDefaultHighlightedIndex,\n} from '../utils'\nimport {getDefaultValue} from '../utils-ts'\nimport {getHighlightedIndex, getNonDisabledIndex} from '../../utils'\nimport commonReducer from '../reducer'\nimport {dropdownDefaultStateValues} from '../utils.dropdown'\nimport * as stateChangeTypes from './stateChangeTypes'\n\n/* eslint-disable complexity */\nexport default function downshiftUseComboboxReducer(state, props, action) {\n  const {type, altKey} = action\n  let changes\n\n  switch (type) {\n    case stateChangeTypes.ItemClick:\n      changes = {\n        isOpen: getDefaultValue(props, 'isOpen', dropdownDefaultStateValues),\n        highlightedIndex: getDefaultHighlightedIndex(props),\n        selectedItem: props.items[action.index],\n        inputValue: props.itemToString(props.items[action.index]),\n      }\n\n      break\n    case stateChangeTypes.InputKeyDownArrowDown:\n      if (state.isOpen) {\n        changes = {\n          highlightedIndex: getHighlightedIndex(\n            state.highlightedIndex,\n            1,\n            props.items,\n            props.isItemDisabled,\n            true,\n          ),\n        }\n      } else {\n        changes = {\n          highlightedIndex:\n            altKey && state.selectedItem == null\n              ? -1\n              : getHighlightedIndexOnOpen(props, state, 1),\n          isOpen: props.items.length >= 0,\n        }\n      }\n      break\n    case stateChangeTypes.InputKeyDownArrowUp:\n      if (state.isOpen) {\n        if (altKey) {\n          changes = getChangesOnSelection(props, state.highlightedIndex)\n        } else {\n          changes = {\n            highlightedIndex: getHighlightedIndex(\n              state.highlightedIndex,\n              -1,\n              props.items,\n              props.isItemDisabled,\n              true,\n            ),\n          }\n        }\n      } else {\n        changes = {\n          highlightedIndex: getHighlightedIndexOnOpen(props, state, -1),\n          isOpen: props.items.length >= 0,\n        }\n      }\n      break\n    case stateChangeTypes.InputKeyDownEnter:\n      changes = getChangesOnSelection(props, state.highlightedIndex)\n\n      break\n    case stateChangeTypes.InputKeyDownEscape:\n      changes = {\n        isOpen: false,\n        highlightedIndex: -1,\n        ...(!state.isOpen && {\n          selectedItem: null,\n          inputValue: '',\n        }),\n      }\n      break\n    case stateChangeTypes.InputKeyDownPageUp:\n      changes = {\n        highlightedIndex: getHighlightedIndex(\n          state.highlightedIndex,\n          -10,\n          props.items,\n          props.isItemDisabled,\n          true,\n        ),\n      }\n      break\n    case stateChangeTypes.InputKeyDownPageDown:\n      changes = {\n        highlightedIndex: getHighlightedIndex(\n          state.highlightedIndex,\n          10,\n          props.items,\n          props.isItemDisabled,\n          true,\n        ),\n      }\n      break\n    case stateChangeTypes.InputKeyDownHome:\n      changes = {\n        highlightedIndex: getNonDisabledIndex(\n          0,\n          false,\n          props.items,\n          props.isItemDisabled,\n        ),\n      }\n      break\n    case stateChangeTypes.InputKeyDownEnd:\n      changes = {\n        highlightedIndex: getNonDisabledIndex(\n          props.items.length - 1,\n          true,\n          props.items,\n          props.isItemDisabled,\n        ),\n      }\n      break\n    case stateChangeTypes.InputBlur:\n      changes = {\n        isOpen: false,\n        highlightedIndex: -1,\n        ...(state.highlightedIndex >= 0 &&\n          props.items?.length &&\n          action.selectItem && {\n            selectedItem: props.items[state.highlightedIndex],\n            inputValue: props.itemToString(props.items[state.highlightedIndex]),\n          }),\n      }\n      break\n    case stateChangeTypes.InputChange:\n      changes = {\n        isOpen: true,\n        highlightedIndex: getDefaultHighlightedIndex(props),\n        inputValue: action.inputValue,\n      }\n      break\n    case stateChangeTypes.InputClick:\n      changes = {\n        isOpen: !state.isOpen,\n        highlightedIndex: state.isOpen\n          ? -1\n          : getHighlightedIndexOnOpen(props, state, 0),\n      }\n      break\n    case stateChangeTypes.FunctionSelectItem:\n      changes = {\n        selectedItem: action.selectedItem,\n        inputValue: props.itemToString(action.selectedItem),\n      }\n      break\n    case stateChangeTypes.ControlledPropUpdatedSelectedItem:\n      changes = {\n        inputValue: action.inputValue,\n      }\n      break\n    default:\n      return commonReducer(state, props, action, stateChangeTypes)\n  }\n\n  return {\n    ...state,\n    ...changes,\n  }\n}\n/* eslint-enable complexity */\n"
  },
  {
    "path": "src/hooks/useCombobox/stateChangeTypes.js",
    "content": "import productionEnum from '../../productionEnum.macro'\n\nexport const InputKeyDownArrowDown = productionEnum(\n  '__input_keydown_arrow_down__',\n)\nexport const InputKeyDownArrowUp = productionEnum('__input_keydown_arrow_up__')\nexport const InputKeyDownEscape = productionEnum('__input_keydown_escape__')\nexport const InputKeyDownHome = productionEnum('__input_keydown_home__')\nexport const InputKeyDownEnd = productionEnum('__input_keydown_end__')\nexport const InputKeyDownPageUp = productionEnum('__input_keydown_page_up__')\nexport const InputKeyDownPageDown = productionEnum(\n  '__input_keydown_page_down__',\n)\nexport const InputKeyDownEnter = productionEnum('__input_keydown_enter__')\nexport const InputChange = productionEnum('__input_change__')\nexport const InputBlur = productionEnum('__input_blur__')\nexport const InputClick = productionEnum('__input_click__')\n\nexport const MenuMouseLeave = productionEnum('__menu_mouse_leave__')\n\nexport const ItemMouseMove = productionEnum('__item_mouse_move__')\nexport const ItemClick = productionEnum('__item_click__')\n\nexport const ToggleButtonClick = productionEnum('__togglebutton_click__')\n\nexport const FunctionToggleMenu = productionEnum('__function_toggle_menu__')\nexport const FunctionOpenMenu = productionEnum('__function_open_menu__')\nexport const FunctionCloseMenu = productionEnum('__function_close_menu__')\nexport const FunctionSetHighlightedIndex = productionEnum(\n  '__function_set_highlighted_index__',\n)\nexport const FunctionSelectItem = productionEnum('__function_select_item__')\nexport const FunctionSetInputValue = productionEnum(\n  '__function_set_input_value__',\n)\nexport const FunctionReset = productionEnum('__function_reset__')\nexport const ControlledPropUpdatedSelectedItem = productionEnum(\n  '__controlled_prop_updated_selected_item__',\n)\n"
  },
  {
    "path": "src/hooks/useCombobox/testUtils.js",
    "content": "import * as React from 'react'\nimport {render, screen, renderHook} from '@testing-library/react'\nimport {dropdownDefaultProps} from '../utils.dropdown'\nimport {dataTestIds, items, user} from '../testUtils'\nimport useCombobox from '.'\n\nexport * from '../testUtils'\n\n// We are using React 18.\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\njest.mock('../utils', () => {\n  const utils = jest.requireActual('../utils')\n  const hooksUtils = jest.requireActual('../../utils-ts')\n\n  return {\n    ...utils,\n    useGetterPropsCalledChecker: () => hooksUtils.noop,\n  }\n})\n\nbeforeEach(jest.resetAllMocks)\nafterAll(jest.restoreAllMocks)\n\nexport function getInput() {\n  return screen.getByRole('combobox')\n}\n\nexport async function keyDownOnInput(keys) {\n  if (document.activeElement !== getInput()) {\n    getInput().focus()\n  }\n\n  await user.keyboard(keys)\n}\n\nexport async function changeInputValue(inputValue) {\n  if (document.activeElement !== getInput()) {\n    getInput().focus()\n  }\n\n  await user.keyboard(inputValue)\n}\n\nexport async function clickOnInput() {\n  await user.click(getInput())\n}\n\nexport const renderCombobox = (props, uiCallback) => {\n  const renderSpy = jest.fn()\n  const ui = <DropdownCombobox renderSpy={renderSpy} {...props} />\n  const utils = render(uiCallback ? uiCallback(ui) : ui)\n  const rerender = newProps =>\n    utils.rerender(<DropdownCombobox renderSpy={renderSpy} {...newProps} />)\n\n  return {\n    ...utils,\n    renderSpy,\n    rerender,\n  }\n}\n\nfunction DropdownCombobox({renderSpy, renderItem, ...props}) {\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    getItemProps,\n  } = useCombobox({items, ...props})\n  const {itemToString} = props.itemToString ? props : dropdownDefaultProps\n\n  renderSpy()\n\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div>\n        <input data-testid={dataTestIds.input} {...getInputProps()} />\n        <button\n          data-testid={dataTestIds.toggleButton}\n          {...getToggleButtonProps()}\n        >\n          Toggle\n        </button>\n      </div>\n      <ul data-testid={dataTestIds.menu} {...getMenuProps()}>\n        {isOpen\n          ? (props.items || items).map((item, index) => {\n              const stringItem =\n                item instanceof Object ? itemToString(item) : item\n              return renderItem ? (\n                renderItem({index, item, getItemProps, stringItem})\n              ) : (\n                <li\n                  data-testid={dataTestIds.item(index)}\n                  key={`${stringItem}${index}`}\n                  {...getItemProps({item, index, disabled: item.disabled})}\n                >\n                  {stringItem}\n                </li>\n              )\n            })\n          : null}\n      </ul>\n    </div>\n  )\n}\n\nexport const renderUseCombobox = props => {\n  return renderHook(() => useCombobox({items, ...props}))\n}\n"
  },
  {
    "path": "src/hooks/useCombobox/utils.js",
    "content": "import {useRef, useEffect} from 'react'\nimport PropTypes from 'prop-types'\nimport {isControlledProp} from '../../utils'\nimport {getState} from '../../utils-ts'\nimport {getInitialState as getInitialStateCommon} from '../utils'\nimport {dropdownDefaultProps, dropdownPropTypes} from '../utils.dropdown'\nimport {useIsInitialMount, useEnhancedReducer} from '../utils-ts'\nimport {ControlledPropUpdatedSelectedItem} from './stateChangeTypes'\n\nexport function getInitialState(props) {\n  const initialState = getInitialStateCommon(props)\n  const {selectedItem} = initialState\n  let {inputValue} = initialState\n\n  if (\n    inputValue === '' &&\n    selectedItem &&\n    props.defaultInputValue === undefined &&\n    props.initialInputValue === undefined &&\n    props.inputValue === undefined\n  ) {\n    inputValue = props.itemToString(selectedItem)\n  }\n\n  return {\n    ...initialState,\n    inputValue,\n  }\n}\n\nexport const propTypes = {\n  ...dropdownPropTypes,\n  items: PropTypes.array.isRequired,\n  isItemDisabled: PropTypes.func,\n  inputValue: PropTypes.string,\n  defaultInputValue: PropTypes.string,\n  initialInputValue: PropTypes.string,\n  inputId: PropTypes.string,\n  onInputValueChange: PropTypes.func,\n}\n\n/**\n * The useCombobox version of useControlledReducer, which also\n * checks if the controlled prop selectedItem changed between\n * renders. If so, it will also update inputValue with its\n * string equivalent. It uses the common useEnhancedReducer to\n * compute the rest of the state.\n *\n * @param {Function} reducer Reducer function from downshift.\n * @param {Object} props The hook props, also passed to createInitialState.\n * @param {Function} createInitialState Function that returns the initial state.\n * @param {Function} isStateEqual Function that checks if a previous state is equal to the next.\n * @returns {Array} An array with the state and an action dispatcher.\n */\nexport function useControlledReducer(\n  reducer,\n  props,\n  createInitialState,\n  isStateEqual,\n) {\n  const previousSelectedItemRef = useRef()\n  const [state, dispatch] = useEnhancedReducer(\n    reducer,\n    props,\n    createInitialState,\n    isStateEqual,\n  )\n  const isInitialMount = useIsInitialMount()\n\n  useEffect(() => {\n    if (!isControlledProp(props, 'selectedItem')) {\n      return\n    }\n\n    if (\n      !isInitialMount // on first mount we already have the proper inputValue for a initial selected item.\n    ) {\n      const shouldCallDispatch =\n        props.itemToKey(props.selectedItem) !==\n        props.itemToKey(previousSelectedItemRef.current)\n\n      if (shouldCallDispatch) {\n        dispatch({\n          type: ControlledPropUpdatedSelectedItem,\n          inputValue: props.itemToString(props.selectedItem),\n        })\n      }\n    }\n\n    previousSelectedItemRef.current =\n      state.selectedItem === previousSelectedItemRef.current\n        ? props.selectedItem\n        : state.selectedItem\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [state.selectedItem, props.selectedItem])\n\n  return [getState(state, props), dispatch]\n}\n\nexport const defaultProps = {\n  ...dropdownDefaultProps,\n  isItemDisabled() {\n    return false\n  },\n}\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/MIGRATION_GUIDE.md",
    "content": "# useMultipleSelection to useTagGroup\n\n## Usage examples\n\nLet's consider the following usage examples:\n\n```javascript\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useCombobox, useMultipleSelection} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nconst initialSelectedItems = [colors[0], colors[1]]\n\nfunction getFilteredItems(selectedItems, inputValue) {\n  const lowerCasedInputValue = inputValue.toLowerCase()\n\n  return colors.filter(\n    colour =>\n      !selectedItems.includes(colour) &&\n      colour.toLowerCase().startsWith(lowerCasedInputValue),\n  )\n}\n\nfunction DropdownMultipleCombobox() {\n  const [inputValue, setInputValue] = React.useState('')\n  const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems)\n  const items = React.useMemo(\n    () => getFilteredItems(selectedItems, inputValue),\n    [selectedItems, inputValue],\n  )\n\n  const {getSelectedItemProps, getDropdownProps, removeSelectedItem} =\n    useMultipleSelection({\n      selectedItems,\n      onStateChange({selectedItems: newSelectedItems, type}) {\n        switch (type) {\n          case useMultipleSelection.stateChangeTypes\n            .SelectedItemKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:\n          case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:\n            setSelectedItems(newSelectedItems)\n            break\n          default:\n            break\n        }\n      },\n    })\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n    selectedItem,\n    clearSelection,\n  } = useCombobox({\n    items,\n    inputValue,\n    selectedItem: null,\n    stateReducer(state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n        case useCombobox.stateChangeTypes.InputBlur:\n          return {\n            ...changes,\n            ...(changes.selectedItem && {isOpen: true, highlightedIndex: 0}),\n          }\n        default:\n          return changes\n      }\n    },\n    onStateChange({\n      inputValue: newInputValue,\n      type,\n      selectedItem: newSelectedItem,\n    }) {\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n          setSelectedItems([...selectedItems, newSelectedItem])\n\n          break\n        case useCombobox.stateChangeTypes.InputChange:\n          setInputValue(newInputValue)\n          break\n        default:\n          break\n      }\n    },\n  })\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'column',\n        width: 'fit-content',\n        justifyContent: 'center',\n        marginTop: 100,\n        alignSelf: 'center',\n      }}\n    >\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div\n        style={{\n          display: 'inline-flex',\n          gap: '8px',\n          alignItems: 'center',\n          flexWrap: 'wrap',\n          padding: '6px',\n        }}\n      >\n        {selectedItems.map(\n          function renderSelectedItem(selectedItemForRender, index) {\n            return (\n              <span\n                style={{\n                  backgroundColor: 'lightgray',\n                  paddingLeft: '4px',\n                  paddingRight: '4px',\n                  borderRadius: '6px',\n                }}\n                key={`selected-item-${index}`}\n                {...getSelectedItemProps({\n                  selectedItem: selectedItemForRender,\n                  index,\n                })}\n              >\n                {selectedItemForRender}\n                {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n                <span\n                  style={{padding: '4px', cursor: 'pointer'}}\n                  onClick={e => {\n                    e.stopPropagation()\n                    removeSelectedItem(null)\n                  }}\n                >\n                  &#10005;\n                </span>\n              </span>\n            )\n          },\n        )}\n        <div>\n          <input\n            style={{padding: '4px'}}\n            {...getInputProps(getDropdownProps({preventKeyAction: isOpen}))}\n            data-testid=\"combobox-input\"\n          />\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"toggle menu\"\n            data-testid=\"combobox-toggle-button\"\n            {...getToggleButtonProps()}\n          >\n            {isOpen ? <>&#8593;</> : <>&#8595;</>}\n          </button>\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"clear selection\"\n            data-testid=\"clear-button\"\n            onClick={clearSelection}\n          >\n            &#10007;\n          </button>\n        </div>\n      </div>\n      <ul\n        {...getMenuProps()}\n        style={{\n          listStyle: 'none',\n          width: '100%',\n          padding: '0',\n          margin: '4px 0 0 0',\n        }}\n      >\n        {isOpen &&\n          items.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n                'data-testid': `downshift-item-${index}`,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n\nrender(<DropdownMultipleCombobox />, document.getElementById('root'))\n```\n\nAnd `useTagGroup`:\n\n```jsx\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useTagGroup, useCombobox} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nfunction TagGroup() {\n  const initialItems = colors.slice(0, 5)\n  const [inputValue, setInputValue] = React.useState('')\n  const {\n    addItem,\n    getTagProps,\n    getTagRemoveProps,\n    getTagGroupProps,\n    items,\n    activeIndex,\n  } = useTagGroup({initialItems})\n\n  const itemsToAdd = colors.filter(\n    color =>\n      !items.includes(color) &&\n      (!inputValue || color.toLowerCase().includes(inputValue.toLowerCase())),\n  )\n\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n  } = useCombobox({\n    items: itemsToAdd,\n    inputValue,\n    onInputValueChange: changes => {\n      setInputValue(changes.inputValue)\n    },\n    onSelectedItemChange(changes) {\n      if (changes.selectedItem) {\n        addItem(changes.selectedItem)\n      }\n    },\n    selectedItem: null,\n    stateReducer(_state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n\n      if (\n        changes.selectedItem &&\n        type !== useCombobox.stateChangeTypes.InputBlur\n      ) {\n        return {...changes, inputValue: '', highlightedIndex: 0, isOpen: true}\n      }\n\n      return changes\n    },\n  })\n\n  return (\n    <div>\n      <div\n        {...getTagGroupProps({'aria-label': 'colors example'})}\n        className=\"tag-group\"\n      >\n        {items.map((color, index) => (\n          <span\n            className={`${index === activeIndex ? 'selected-tag' : ''} tag`}\n            key={color}\n            {...getTagProps({index, 'aria-label': color})}\n          >\n            {color}\n            <button\n              className=\"tag-remove-button\"\n              type=\"button\"\n              {...getTagRemoveProps({index, 'aria-label': 'remove'})}\n            >\n              &#10005;\n            </button>\n          </span>\n        ))}\n      </div>\n      <div className=\"wrapper\">\n        <label {...getLabelProps()}>Choose your favorite book:</label>\n        <div className=\"input-wrapper\">\n          <input\n            placeholder=\"Best book ever\"\n            className=\"text-input\"\n            {...getInputProps()}\n          />\n          <button\n            aria-label=\"toggle menu\"\n            className=\"toggle-button\"\n            type=\"button\"\n            {...getToggleButtonProps()}\n          >\n            {isOpen ? <>&#8593;</> : <>&#8595;</>}\n          </button>\n        </div>\n      </div>\n      <ul className=\"menu\" {...getMenuProps()}>\n        {isOpen\n          ? itemsToAdd.map((item, index) => (\n              <li\n                className={`menu-item${index === highlightedIndex ? ' highlighted' : ''}`}\n                key={item}\n                {...getItemProps({item, index})}\n              >\n                <span>{item}</span>\n              </li>\n            ))\n          : null}\n      </ul>\n    </div>\n  )\n}\n\nrender(<TagGroup />, document.getElementById('root'))\n```\n\n## Selection state\n\nFor starters:\n\n- both hooks manage state internally\n- both hooks accept controlled props\n- both hooks return an imperative function to add items to selection\n\nThe difference is that one could easly rely on the default internal state of\n`useTagGroup` in order to build the multiple seleciton combobox. Consequently,\nthere's no need for:\n\n```js\nconst [selectedItems, setSelectedItems] = React.useState(initialSelectedItems)\n```\n\nAlso, since we rely on `useTagGroup` to manage state on its own, there's no need\nto hook an `onStageChange` prop in order to capture the removal state changes\nand update `selectedItems` as a result. Consequently, just remove these props:\n\n```js\n  selectedItems,\n  onStateChange({selectedItems: newSelectedItems, type}) {\n    switch (type) {\n      case useMultipleSelection.stateChangeTypes\n        .SelectedItemKeyDownBackspace:\n      case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:\n      case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:\n      case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:\n        setSelectedItems(newSelectedItems)\n        break\n      default:\n        break\n    }\n  },\n```\n\nWe also need to make sure that adding items to the selection works, so we still\nneed to use `addItem` when we detect a combobox selection. Since we don't have a\ncontrolled state anymore, the `onStateChange` from `useCombobox` turns from:\n\n```js\n    onStateChange({\n      inputValue: newInputValue,\n      type,\n      selectedItem: newSelectedItem,\n    }) {\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n          setSelectedItems([...selectedItems, newSelectedItem])\n\n          break\n        case useCombobox.stateChangeTypes.InputChange:\n          setInputValue(newInputValue)\n          break\n        default:\n          break\n      }\n    },\n```\n\nTo:\n\n```js\n    onSelectedItemChange(changes) {\n      if (changes.selectedItem) {\n        addItem(changes.selectedItem)\n      }\n    },\n```\n\nNotice that in the `useMultipleSelection` example we use a different\nimplementation to filter the examples, hence the `setInputValue` on\n`useCombobox.stateChangeTypes.InputChange`. Both strategies achieve the same\npurpose, but the `useTagGroup` example has a cleaner code. Anyway, choose\nwhatever you prefer, or any other way to filter the combobox items from the\nalready selected and the input value.\n\nNotice we keep controlling the `selectedItem` for `useCombobox` since we are\ncontrolling the actual selection, and `useCombobox` only supports single\nselection by default.\n\n## JSX\n\nOur tag group is now accessible, and this involves some getter prop function\nchanges.\n\n### getTagGroupProps\n\nThis is new, just use it on the tags container. Looking at the\n`useMultipleSelectionCode` that will be the `<div>` after the `<label>`.\n\n### getTagProps\n\nThis is equivalent to `getSelectedItemProps`.\n\n### getDropdownProps\n\nThis has no equivalent in `useTagGroup`, and it involved a coupling between the\n`useMultipleSelection` and `useCombobox` hooks. No need for it now, just use\n`getInputProps` without anything extra.\n\n### getTagRemoveProps\n\nIn the `useMultipleSelection` example, notice we have an explicit `onClick` prop\nthat removed the selected item imperatively. With `useTagGroup` we will do it\ndeclaratively by using the `getTagRemoveProps`. Basically, your X icon button\nwill turn from this:\n\n```javascript\n  <span\n    style={{padding: '4px', cursor: 'pointer'}}\n    onClick={e => {\n      e.stopPropagation()\n      removeSelectedItem(null)\n    }}\n  >\n```\n\nTo this\n\n```javascript\n  <button\n    style={{padding: '4px', cursor: 'pointer'}}\n    type=\"button\"\n    {...getTagRemoveProps({index, 'aria-label': 'remove'})}\n  >\n```\n\nUsing a `button` element might be better, but keep in mind that by default we\nare removing it from the tab order, since keyboard users can just use\nDelete/Backspace when a selected item is focused.\n\n## Wrapping up\n\nWe hope that `useTagGroup` provides a way better experience for your users,\nsince we aimed to make it more accessible, either when using it by itself or\nbuilding tag based multiple selection for selects and comboboxes. Feel free to\nprovide more tips for usage or migrations by opeing a PR / issue in Github in\norder to help other users. Thank you!\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/README.md",
    "content": "# useMultipleSelection\n\n## Deprecated: migration to useTagGroup\n\nIf you're considering a multiple selection solution with tags, check\n[useTagGroup][use-tag-group].\n\nIf you're already using the hook, migrate to `useTagGroup`. It's meant to offer\na more intuitive and extensible API than `useMultipleSelection`. The\n[migration-guide][migration-guide] shows how to migrate to the new hook.\n\n## The problem\n\nYou have a custom `select` or a `combobox` in your applications which performs a\nmultiple selection. You want want the whole experience to be accessible,\nincluding adding and removing items from selection, navigating between the items\nand back to the dropdown. You also want this solution to be simple to use and\nflexible so you can tailor it further to your specific needs.\n\n## This solution\n\n`useMultipleSelection` is a React hook that manages all the stateful logic\nneeded to make the multiple selection dropdown functional and accessible. It\nreturns a set of props that are meant to be called and their results\ndestructured on the dropdown's elements that involve the multiple selection\nexperience: the dropdown main element itself, which can be either an `input` (if\nyou are building a `combobox`) or a `button` (if you are building a `select`),\nand the selected items. The props are similar to the ones provided by vanilla\n`Downshift` to the children render prop.\n\nThese props are called getter props and their return values are destructured as\na set of ARIA attributes and event listeners. Together with the action props and\nstate props, they create all the stateful logic needed for the dropdown to\nbecome accessible. Every functionality needed should be provided out-of-the-box:\narrow navigation between dropdown and items, navigation between the items\nthemselves, removing and adding items, and also helpful `aria-live` messages\nsuch as when an item has been removed from selection.\n\n## Migration through breaking changes\n\nThe hook received breaking changes related to how it works, as well as the API,\nstarting with v8. They are documented here:\n\n- [v8 migration guide][migration-guide-v8]\n- [v9 migration guide][migration-guide-v9]\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Usage](#usage)\n- [Basic Props](#basic-props)\n  - [itemToString](#itemtostring)\n  - [onSelectedItemsChange](#onselecteditemschange)\n  - [stateReducer](#statereducer)\n- [Advanced Props](#advanced-props)\n  - [keyNavigationNext](#keynavigationnext)\n  - [keyNavigationPrevious](#keynavigationprevious)\n  - [initialSelectedItems](#initialselecteditems)\n  - [initialActiveIndex](#initialactiveindex)\n  - [defaultSelectedItems](#defaultselecteditems)\n  - [defaultActiveIndex](#defaultactiveindex)\n  - [itemToKey](#itemtokey)\n  - [getA11yStatusMessage](#geta11ystatusmessage)\n  - [onActiveIndexChange](#onactiveindexchange)\n  - [onStateChange](#onstatechange)\n  - [activeIndex](#activeindex)\n  - [selectedItems](#selecteditems)\n  - [environment](#environment)\n- [stateChangeTypes](#statechangetypes)\n- [Control Props](#control-props)\n- [Returned props](#returned-props)\n  - [prop getters](#prop-getters)\n  - [actions](#actions)\n  - [state](#state)\n- [Event Handlers](#event-handlers)\n  - [Default handlers](#default-handlers)\n  - [Customizing Handlers](#customizing-handlers)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Usage\n\n> [Try it out in the browser][sandbox-example]\n\n```javascript\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useCombobox, useMultipleSelection} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nconst initialSelectedItems = [colors[0], colors[1]]\n\nfunction getFilteredItems(selectedItems, inputValue) {\n  const lowerCasedInputValue = inputValue.toLowerCase()\n\n  return colors.filter(\n    colour =>\n      !selectedItems.includes(colour) &&\n      colour.toLowerCase().startsWith(lowerCasedInputValue),\n  )\n}\n\nfunction DropdownMultipleCombobox() {\n  const [inputValue, setInputValue] = React.useState('')\n  const [selectedItems, setSelectedItems] = React.useState(initialSelectedItems)\n  const items = React.useMemo(\n    () => getFilteredItems(selectedItems, inputValue),\n    [selectedItems, inputValue],\n  )\n\n  const {getSelectedItemProps, getDropdownProps, removeSelectedItem} =\n    useMultipleSelection({\n      selectedItems,\n      onStateChange({selectedItems: newSelectedItems, type}) {\n        switch (type) {\n          case useMultipleSelection.stateChangeTypes\n            .SelectedItemKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:\n          case useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace:\n          case useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem:\n            setSelectedItems(newSelectedItems)\n            break\n          default:\n            break\n        }\n      },\n    })\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    highlightedIndex,\n    getItemProps,\n    selectedItem,\n    clearSelection,\n  } = useCombobox({\n    items,\n    inputValue,\n    selectedItem: null,\n    stateReducer(state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n        case useCombobox.stateChangeTypes.InputBlur:\n          return {\n            ...changes,\n            ...(changes.selectedItem && {isOpen: true, highlightedIndex: 0}),\n          }\n        default:\n          return changes\n      }\n    },\n    onStateChange({\n      inputValue: newInputValue,\n      type,\n      selectedItem: newSelectedItem,\n    }) {\n      switch (type) {\n        case useCombobox.stateChangeTypes.InputKeyDownEnter:\n        case useCombobox.stateChangeTypes.ItemClick:\n          setSelectedItems([...selectedItems, newSelectedItem])\n\n          break\n        case useCombobox.stateChangeTypes.InputChange:\n          setInputValue(newInputValue)\n          break\n        default:\n          break\n      }\n    },\n  })\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'column',\n        width: 'fit-content',\n        justifyContent: 'center',\n        marginTop: 100,\n        alignSelf: 'center',\n      }}\n    >\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div\n        style={{\n          display: 'inline-flex',\n          gap: '8px',\n          alignItems: 'center',\n          flexWrap: 'wrap',\n          padding: '6px',\n        }}\n      >\n        {selectedItems.map(\n          function renderSelectedItem(selectedItemForRender, index) {\n            return (\n              <span\n                style={{\n                  backgroundColor: 'lightgray',\n                  paddingLeft: '4px',\n                  paddingRight: '4px',\n                  borderRadius: '6px',\n                }}\n                key={`selected-item-${index}`}\n                {...getSelectedItemProps({\n                  selectedItem: selectedItemForRender,\n                  index,\n                })}\n              >\n                {selectedItemForRender}\n                {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events */}\n                <span\n                  style={{padding: '4px', cursor: 'pointer'}}\n                  onClick={e => {\n                    e.stopPropagation()\n                    removeSelectedItem(null)\n                  }}\n                >\n                  &#10005;\n                </span>\n              </span>\n            )\n          },\n        )}\n        <div>\n          <input\n            style={{padding: '4px'}}\n            {...getInputProps(getDropdownProps({preventKeyAction: isOpen}))}\n            data-testid=\"combobox-input\"\n          />\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"toggle menu\"\n            data-testid=\"combobox-toggle-button\"\n            {...getToggleButtonProps()}\n          >\n            {isOpen ? <>&#8593;</> : <>&#8595;</>}\n          </button>\n          <button\n            style={{padding: '4px 8px'}}\n            aria-label=\"clear selection\"\n            data-testid=\"clear-button\"\n            onClick={clearSelection}\n          >\n            &#10007;\n          </button>\n        </div>\n      </div>\n      <ul\n        {...getMenuProps()}\n        style={{\n          listStyle: 'none',\n          width: '100%',\n          padding: '0',\n          margin: '4px 0 0 0',\n        }}\n      >\n        {isOpen &&\n          items.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n                'data-testid': `downshift-item-${index}`,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n\nrender(<DropdownMultipleCombobox />, document.getElementById('root'))\n```\n\nThe equivalent example with `useSelect` is [here][select-sandbox-example].\n\n## Basic Props\n\nThis is the list of props that you should probably know about. There are some\n[advanced props](#advanced-props) below as well.\n\n### itemToString\n\n> `function(item: any)` | defaults to: `i => (i == null ? '' : String(i))`\n\nIf your items are stored as, say, objects instead of strings, downshift still\nneeds a string representation for each one. This is required for accessibility\naria-live messages (e.g., after removing a selection).\n\n### onSelectedItemsChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the selected items array changes. Especially useful when items\nare removed, as there are many ways to do that: `Backspace` from dropdown,\n`Backspace` or `Delete` while focus is the item, executing `removeSelectedItem`\nwhen clicking an associated `X` icon for the item.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `selectedItems`\n  property with the new array value. This also has a `type` property which you\n  can learn more about in the [`stateChangeTypes`](#statechangetypes) section.\n  This property will be part of the actions that can trigger an `selectedItems`\n  change, for example `useSelect.stateChangeTypes.DropdownKeyDownBackspace`.\n\n### stateReducer\n\n> `function(state: object, actionAndChanges: object)` | optional\n\n**🚨 This is a really handy power feature 🚨**\n\nThis function will be called each time `useMultipleSelection` sets its internal\nstate (or calls your `onStateChange` handler for control props). It allows you\nto modify the state change that will take place which can give you fine grain\ncontrol over how the component interacts with user updates. It gives you the\ncurrent state and the state that will be set, and you return the state that you\nwant to set.\n\n- `state`: The full current state of downshift.\n- `actionAndChanges`: Object that contains the action `type`, props needed to\n  return a new state based on that type and the changes suggested by the\n  Downshift default reducer. About the `type` property you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n```javascript\nimport {useMultipleSelection} from 'downshift'\nimport {items} from './utils'\n\nconst {getDropdownProps, getSelectedItemProps, ...rest} = useMultipleSelection({\n  initialSelectedItems: [items[0], items[1]],\n  stateReducer,\n})\n\nfunction stateReducer(state, actionAndChanges) {\n  const {type, changes} = actionAndChanges\n  // this adds focus to the dropdown when item is removed by keyboard action.\n  switch (type) {\n    case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownBackspace:\n    case useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete:\n      return {\n        ...changes,\n        activeIndex: -1, // the focus will move to the input/button\n      }\n    default:\n      return changes // otherwise business as usual.\n  }\n}\n```\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example `<button onBlur={handleBlur} />` should be\n> `<button {...getToggleButtonProps({onBlur: handleBlur})} />`). Also, your\n> reducer function should be \"pure.\" This means it should do nothing other than\n> return the state changes you want to have happen.\n\n## Advanced Props\n\n### keyNavigationNext\n\n> `string` | defaults to `ArrowRight`\n\nThe navigation key that increments `activeIndex` and moves focus to the selected\nitem whose index corresponds to the new value. For a `RTL` scenario, a common\noverriden value could be `ArrowLeft`. In some scenarios it can be `ArrowDown`.\nIt mostly depends on the UI the user is presented with.\n\n### keyNavigationPrevious\n\n> `string` | defaults to `ArrowLeft`\n\nThe navigation key that decrements `activeIndex` and moves focus to the selected\nitem whose index corresponds to the new value. Also moves focus from `dropdown`\nto item with the last index. For a `RTL` scenario, a common overriden value\ncould be `ArrowRight`. In some scenarios it can be `ArrowUp`. It mostly depends\non the UI the user is presented with.\n\n### initialSelectedItems\n\n> `any[]` | defaults to `[]`\n\nPass an initial array of items that are considered to be selected.\n\n### initialActiveIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the focused / active selected item when\ndownshift is initialized.\n\n### defaultSelectedItems\n\n> `any[]` | defaults to `[]`\n\nPass an array of items that are going to be used when downshift is reset.\n\n### defaultActiveIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the focused / active selected item when\ndownshift is reset.\n\n### itemToKey\n\n> `function(item: any)` | defaults to: `item => item`\n\nUsed to determine the uniqueness of an item when searching for the item or\ncomparing the item with another. Returns the item itself, by default, so the\ncomparing/searching is done internally via referential equality.\n\nIf using items as objects and their reference will change during use, you can\nuse the function to generate a unique key for each item, such as an `id` prop.\n\n```js\n// initial items.\nconst selectedItems = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n// the same items but with different references, for any reason.\nconst newSelectedItems = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n\nfunction itemToKey(item) {\n  return item.id\n  // and we will do the comparison like: const isChanged = itemToKey(prevSelectedItem) !== itemToKey(nextSelectedItem)\n}\n```\n\n### getA11yStatusMessage\n\n> `function({/* see below */})` | default messages provided in English\n\nThis function is passed as props to a status updating function nested within\nthat allows you to create your own ARIA statuses. It is called when the state\nchanges: `selectedItem`, `highlightedIndex`, `inputValue` or `isOpen`.\n\nThere is no default function provided anymore since v9, so if there's no prop\npassed, no aria live status message is created. An implementation that resembles\nthe previous default is written below, should you want to keep pre v9 behaviour.\n\nWe don't provide this as a default anymore since we consider that screen readers\nhave been significantly improved and they can convey information about items\ncount, possible actions and highlighted items only from the HTML markup, without\nthe need for aria-live regions.\n\n```js\nfunction getA11yStatusMessage(state) {\n  const {selectedItems} = state\n\n  if (selectedItems.length === previousSelectedItemsRef.current.length) {\n    return ''\n  }\n\n  const removedSelectedItem = previousSelectedItemsRef.current.find(\n    selectedItem =>\n      selectedItems.findIndex(\n        item => props.itemToKey(item) === props.itemToKey(selectedItem),\n      ) < 0,\n  )\n\n  // where itemToString is a function that returns the string equivalent for an item.\n  return `${itemToString(removedSelectedItem)} has been removed.`\n}\n```\n\n### onActiveIndexChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the index of the active item changes. When an item becomes\nactive, it receives focus, so it can receive keyboard events. To change\n`activeIndex` you can either click on the item or use navigation keys between\nthe items and the dropdown.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `activeIndex` property\n  with the new value. This also has a `type` property which you can learn more\n  about in the [`stateChangeTypes`](#statechangetypes) section. This property\n  will be part of the actions that can trigger a `activeIndex` change, for\n  example `useSelect.stateChangeTypes.ItemClick`.\n\n### onStateChange\n\n> `function(changes: object)` | optional, no useful default\n\nThis function is called anytime the internal state changes. This can be useful\nif you're using downshift as a \"controlled\" component, where you manage some or\nall of the state (e.g. selectedItems and activeIndex) and then pass it as props,\nrather than letting downshift control all its state itself.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n> Tip: This function will be called any time _any_ state is changed. The best\n> way to determine whether any particular state was changed, you can use\n> `changes.hasOwnProperty('propName')` or use the `on[statePropKey]Change` props\n> described above.\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<button onBlur={handleBlur} />` should be\n> `<button {...getDropdownProps({onBlur: handleBlur})} />`).\n\n### activeIndex\n\n> `number` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe index of the item that should be active and focused.\n\n### selectedItems\n\n> `any[]` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe items that are considered selected at the time.\n\n### environment\n\n> `window` | defaults to `window`\n\nThis prop is only useful if you're rendering downshift within a different\n`window` context from where your JavaScript is running; for example, an iframe\nor a shadow-root. If the given context is lacking `document` and/or\n`add|removeEventListener` on its prototype (as is the case for a shadow-root)\nthen you will need to pass in a custom object that is able to provide\n[access to these properties](https://gist.github.com/Rendez/1dd55882e9b850dd3990feefc9d6e177)\nfor downshift.\n\n## stateChangeTypes\n\nThere are a few props that expose changes to state\n([`onStateChange`](#onstatechange) and [`stateReducer`](#statereducer)). For you\nto make the most of these APIs, it's important for you to understand why state\nis being changed. To accomplish this, there's a `type` property on the `changes`\nobject you get. This `type` corresponds to a `stateChangeTypes` property.\n\nThe list of all possible values this `type` property can take is defined in\n[this file][state-change-file] and is as follows:\n\n- `useMultipleSelection.stateChangeTypes.SelectedItemClick`\n- `useMultipleSelection.stateChangeTypes.SelectedItemKeyDownDelete`\n- `useMultipleSelection.stateChangeTypes.SelectedItemKeyDownBackspace`\n- `useMultipleSelection.stateChangeTypes.SelectedItemKeyDownNavigationNext`\n- `useMultipleSelection.stateChangeTypes.SelectedItemKeyDownNavigationPrevious`\n- `useMultipleSelection.stateChangeTypes.DropdownKeyDownNavigationPrevious`\n- `useMultipleSelection.stateChangeTypes.DropdownKeyDownBackspace`\n- `useMultipleSelection.stateChangeTypes.DropdownClick`\n- `useMultipleSelection.stateChangeTypes.FunctionAddSelectedItem`\n- `useMultipleSelection.stateChangeTypes.FunctionRemoveSelectedItem`\n- `useMultipleSelection.stateChangeTypes.FunctionSetSelectedItems`\n- `useMultipleSelection.stateChangeTypes.FunctionSetActiveIndex`\n- `useMultipleSelection.stateChangeTypes.FunctionReset`\n\nSee [`stateReducer`](#statereducer) for a concrete example on how to use the\n`type` property.\n\n## Control Props\n\nDownshift manages its own state internally and calls your\n`onSelectedItemsChange`, `onActiveIndexChange` and `onStateChange` handlers with\nany relevant changes. The state that downshift manages includes: `selectedItems`\nand `activeIndex`. Returned action function (read more below) can be used to\nmanipulate this state and can likely support many of your use cases.\n\nHowever, if more control is needed, you can pass any of these pieces of state as\na prop (as indicated above) and that state becomes controlled. As soon as\n`this.props[statePropKey] !== undefined`, internally, `downshift` will determine\nits state based on your prop's value rather than its own internal state. You\nwill be required to keep the state up to date (this is where `onStateChange`\ncomes in really handy), but you can also control the state from anywhere, be\nthat state from other components, `redux`, `react-router`, or anywhere else.\n\n> Note: This is very similar to how normal controlled components work elsewhere\n> in react (like `<input />`). If you want to learn more about this concept, you\n> can learn about that from this the [Advanced React Component Patterns\n> course][advanced-react-component-patterns-course]\n\n## Returned props\n\nYou use the hook like so:\n\n```javascript\nimport {useMultipleSelection} from 'downshift'\nimport {items} from './utils'\n\nconst {getDropdownProps, getSelectedItemProps, selectedItems, reset, ...rest} =\n  useMultipleSelection({\n    initialSelectedItems: [items[0], [items[1]]],\n    ...otherProps,\n  })\n\nreturn (\n  <div>\n    {/* render the selected items */}\n    {selectedItems.map((selectedItem, index) => (\n      <span\n        key={`selected-item-${index}`}\n        {...getSelectedItemProps({selectedItem, index})}\n      >\n        {selectedItem}\n      </span>\n    ))}\n    {/* render the dropdown itself, with getToggleButtonProps coming from useSelect if you choose to build a <select> with useSelect */}\n    <button {...getDropdownProps(getToggleButtonProps())}>\n      Select options\n    </button>\n    {/* render the menu and items */}\n    {/* render a button that resets the select to defaults */}\n    <button\n      onClick={() => {\n        reset()\n      }}\n    >\n      Reset\n    </button>\n  </div>\n)\n```\n\n> NOTE: In this example we used both the getter props `getSelectedItemProps` and\n> `getDropdownProps` and an action prop `reset`. The properties of\n> `useMultipleSelection` can be split into three categories as indicated below:\n\n### prop getters\n\n> See [the blog post about prop getters][blog-post-prop-getters]\n\n> NOTE: These prop-getters provide `aria-` attributes which are very important\n> to your component being accessible. It's recommended that you utilize these\n> functions and apply the props they give you to your components.\n\nThese functions are used to apply props to the elements that you render. This\ngives you maximum flexibility to render what, when, and wherever you like. You\ncall these on the element in question, for example on the toggle button:\n`<button {...getDropdownProps()}`. It's advisable to pass all your props to that\nfunction rather than applying them on the element yourself to avoid your props\nbeing overridden (or overriding the props returned). For example:\n`getDropdownProps({onKeyDown(event) {console.log(event)}})`.\n\nYou will most probably use this hook along with `useSelect` or `useCombobox`,\nand you can call the getter props from both of them. In the case of `select` you\ncan call the props on the dropdown like\n`getDropdownProps(getToggleButtonProps({onKeyDown(event) {//your custom event}}))`.\nSimilar story with `combobox` but with `getInputProps` instead of\n`getToggleButtonProps`.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property               | type           | description                                                                                      |\n| ---------------------- | -------------- | ------------------------------------------------------------------------------------------------ |\n| `getDropdownProps`     | `function({})` | returns the props you should apply to either your input or toggle button, depending on the case. |\n| `getSelectedItemProps` | `function({})` | returns the props you should apply to any selected item elements you render.                     |\n\n#### `getSelectedItemProps`\n\nThe props returned from calling this function should be applied to any selected\nitems you render. It allows changing the activeIndex by using arrow keys or by\nclicking, but also removing items by `Delete` or `Backspace` on active item. It\nalso ensures that focus moves along with the activeIndex, and it keeps a\n`tabindex=\"0\"` on the active element even if user decides to `Tab` away. That\nway, when tabbing back, the user can pick up where he left off with selection.\n\n**This is an impure function**, so it should only be called when you will\nactually be applying the props to an item.\n\n<details>\n\n<summary>What do you mean by impure function?</summary>\n\nBasically just don't do this:\n\n```javascript\nselectedItems.map((selectedItem, index) => {\n  const props = getSelectedItemProps({selectedItem, index}) // we're calling it here\n  if (!shouldRenderItem(item)) {\n    return null // but we're not using props, and downshift thinks we are...\n  }\n  return <div {...props} />\n})\n```\n\nInstead, you could do this:\n\n```jsx\nselectedItems\n  .filter(shouldRenderItem)\n  .map(selectedItem => <div {...getSelectedItemProps({selectedItem})} />)\n```\n\n</details>\n\nRequired properties:\n\nIt is required to pass either `selectedItem` or `index` to\n`getSelectedItemProps` in order to be able to apply the activeIndex logic.\n\n- `selectedItem`: this is the item data that will be selected when the user\n  selects a particular item.\n- `index`: This is how `downshift` keeps track of your item when updating the\n  `activeIndex` as the user keys around. By default, `downshift` will assume the\n  `index` is the order in which you're calling `getSelectedItemProps`. This is\n  often good enough, but if you find odd behavior, try setting this explicitly.\n  It's probably best to be explicit about `index` when using a windowing library\n  like `react-virtualized`.\n\nOptional properties:\n\n- `ref`: if you need to access the dropdown element via a ref object, you'd call\n  the function like this: `getDropdown({ref: yourDropdownRef})`. As a result,\n  the dropdown element will receive a composed `ref` property, which guarantees\n  that both your code and `useMultipleSelection` use the same correct reference\n  to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call:\n  `getSelectedItemProps({refKey: 'innerRef'})` and your composite component\n  would forward like: `<li ref={props.innerRef} />`. However, if you are just\n  rendering a primitive component like `<div>`, there is no need to specify this\n  property. It defaults to `ref`.\n\n#### `getDropdownProps`\n\nCall this and apply the returned props to a `button` if you are building a\n`select` or to an `input` if you're building a `combobox`. It allows you to move\nfocus from this element to the last item selected by using `ArrowLeft` and also\nto remove the last item using `Backspace`.\n\nOptional properties:\n\n- `preventKeyAction`: tells `useMultipleSelection` if `dropdown` is allowed to\n  execute `downshift` handlers on `keydown`. For example, you can pass `isOpen`\n  as value and user will not be able to delete selecteditems by `Backspace` or\n  to navigate to them by arrow keys. This is useful if you don't want to mix key\n  actions from multiple selection with the ones from the dropdown. Once the\n  dropdown is closed then deletion / navigation can be resumed for multiple\n  selection. The value is `false` by default.\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getDropdownProps({refKey: 'innerRef'})`\n  and your composite component would forward like:\n  `<button ref={props.innerRef} />`. However, if you are just rendering a\n  primitive component like `<div>`, there is no need to specify this property.\n  It defaults to `ref`.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getDropdownProps`. **Please use it with extreme care and only if you are\nabsolutely sure that the ref is correctly forwarded otherwise\n`useMultipleSelection` will unexpectedly fail.**\n\n```javascript\nconst {getDropdownProps} = useMultipleSelection()\nconst {isOpen, ...rest} = useSelect({items})\nconst myButton = (\n  {/* selected items */}\n  <button {...getDropdownProps({preventKeyAction: isOpen})}>Click me</button>\n  {/* menu and items */}\n)\n```\n\n### actions\n\nThese are functions you can call to change the state of the downshift\n`useMultipleSelection` hook.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property             | type                      | description                                           |\n| -------------------- | ------------------------- | ----------------------------------------------------- |\n| `addSelectedItem`    | `function(item: any)`     | adds an item to the selected array                    |\n| `removeSelectedItem` | `function(item: any)`     | removes an item from the selected array               |\n| `reset`              | `function()`              | resets the selectedItems and active index to defaults |\n| `setActiveIndex`     | `function(index: number)` | sets activeIndex to the new value                     |\n| `setSelectedItems`   | `function(items: any[])`  | sets selectedItems to the new value                   |\n\n### state\n\nThese are values that represent the current state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property        | type     | description                           |\n| --------------- | -------- | ------------------------------------- |\n| `activeIndex`   | `number` | the index of thecurrently active item |\n| `selectedItems` | `any[]`  | the items of the selection            |\n\n## Event Handlers\n\nDownshift has a few events for which it provides implicit handlers. Several of\nthese handlers call `event.preventDefault()`. Their additional functionality is\ndescribed below.\n\n### Default handlers\n\n#### Dropdown - button or input\n\n- `ArrowLeft`: Moves focus from `button`/`input` to the last selected item and\n  makes `activeIndex` to be `selectedItems.length - 1`. Performs this action if\n  there are any items selected. `ArrowLeft` can be overriden with any other key\n  depeding on the requirements. More info on\n  [`keyNavigationPrevious`](#keynavigationprevious).\n- `Backspace`: Removes the last selected item from selection. It always performs\n  this action on a non-input element. If the `dropdown` is a `combobox` the text\n  cursor of the `input` must be at the start of the `input` and not highlight\n  any text in order for the removal to work.\n\n#### Item\n\n- `Click`: It will make the item active, will modify `activeIndex` to reflect\n  the new change, and will add focus to that item.\n- `Delete`: It will remove the item from selection. `activeIndex` will stay the\n  same if the item removed was not the last one, but focus will move to the item\n  which now has that index. If the last item was removed, the `activeIndex` will\n  decrease by one and will also move focus to the corresponding item. If there\n  are no items available anymore, the focus moves to the dropdown and\n  `activeIndex` becomes `-1`.\n- `Backspace`: Same effect as `Delete`.\n- `ArrowLeft`: Moves `activeIndex` and focus to previous item. It stops at the\n  first item in the selection. `ArrowLeft` can be overriden with any other key\n  depeding on the requirements. More info on\n  [`keyNavigationPrevious`](#keynavigationprevious).\n- `ArrowRight`: Moves `activeIndex` and focus to next item. It will move focus\n  to the `dropdown` if it occurs on the last selected item. `ArrowRight` can be\n  overriden with any other key depeding on the requirements. More info on\n  [`keyNavigationNext`](#keynavigationnext).\n\n### Customizing Handlers\n\nYou can provide your own event handlers to `useMultipleSelection` which will be\ncalled before the default handlers:\n\n```javascript\nconst items = [...] // items here.\nconst {getDropdownProps} = useMultipleSelection()\nconst {getInputProps} = useCombobox({items})\nconst ui = (\n  /* label, selected items, ... */\n  <input\n    {...getInputProps(\n      getDropdownProps({\n        onKeyDown: event => {\n          // your custom keyDown handler here.\n        },\n      }),\n    )}\n  />\n)\n```\n\nIf you would like to prevent the default handler behavior in some cases, you can\nset the event's `preventDownshiftDefault` property to `true`:\n\n```javascript\nconst items = [...] // items here.\nconst {getDropdownProps} = useMultipleSelection()\nconst {getInputProps} = useCombobox({items})\nconst ui = (\n  /* label, selected items, ... */\n  <input\n    {...getInputProps(\n      getDropdownProps({\n        onKeyDown: event => {\n          // your custom keyDown handler here.\n          if (event.key === 'Enter') {\n            // Prevent Downshift's default 'Enter' behavior.\n            event.nativeEvent.preventDownshiftDefault = true\n\n            // your handler code\n          }\n        },\n      }),\n    )}\n  />\n)\n```\n\nIf you would like to completely override Downshift's behavior for a handler, in\nfavor of your own, you can bypass prop getters:\n\n```javascript\nconst items = [...] // items here.\nconst {getDropdownProps} = useMultipleSelection()\nconst {getInputProps} = useCombobox({items})\nconst ui = (\n  /* label, selected items, ... */\n  <input\n    {...getInputProps(\n      getDropdownProps({\n        onKeyDown: event => {\n          // your custom keyDown handler here.\n        },\n      }),\n    )}\n    onKeyDown={event => {\n      // your custom keyDown handler here.\n    }}\n  />\n)\n```\n\n## Examples\n\nUsage examples are kept on the [downshift docsite][docsite] and also on [the\nsandbox repo][sandbox-repo]. Each example has a link to its own Codesandbox\nversion, so check the docs.\n\nIt can be a great contributing opportunity to provide relevant use cases as\ndocsite examples. If you have such an example, please create an issue with the\nsuggestion and the Codesandbox for it, and we will take it from there.\n\n[sandbox-example]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseMultipleSelection%2Fcombobox.js&moduleview=1\n[select-sandbox-example]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseMultipleSelection%2Fselect.js&moduleview=1\n[state-change-file]:\n  https://github.com/downshift-js/downshift/blob/master/src/hooks/useMultipleSelection/stateChangeTypes.js\n[blog-post-prop-getters]:\n  https://kentcdodds.com/blog/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf\n[docsite]: https://downshift-js.com/\n[sandbox-repo]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1\n[advanced-react-component-patterns-course]:\n  https://github.com/downshift-js/downshift#advanced-react-component-patterns-course\n[migration-guide-v8]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V8.md\n[migration-guide-v9]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V9.md\n[use-tag-group]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useTagGroup/README.md\n[migration-guide]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/useMultipleSelection/MIGRATION_GUIDE.md\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/getDropdownProps.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport {\n  clickOnInput,\n  focusSelectedItemAtIndex,\n  getSelectedItemAtIndex,\n  getSelectedItems,\n  renderMultipleCombobox,\n  renderUseMultipleSelection,\n  getInput,\n  keyDownOnInput,\n  items,\n} from '../testUtils'\nimport utils from '../../utils'\nimport useMultipleSelection from '..'\n\ndescribe('getDropdownProps', () => {\n  test('returns no keydown events if preventKeyAction is true', () => {\n    const {result} = renderUseMultipleSelection()\n    const dropdownProps = result.current.getDropdownProps({\n      preventKeyAction: true,\n    })\n\n    expect(dropdownProps.onKeyDown).toBeUndefined()\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseMultipleSelection()\n\n      expect(result.current.getDropdownProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('custom ref passed by the user is used', () => {\n      const {result} = renderUseMultipleSelection()\n      const refFn = jest.fn()\n      const dropdownNode = {}\n\n      act(() => {\n        const {ref} = result.current.getDropdownProps({\n          ref: refFn,\n        })\n\n        ref(dropdownNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(dropdownNode)\n    })\n\n    test('custom ref with custom name passed by the user is used', () => {\n      const {result} = renderUseMultipleSelection()\n      const refFn = jest.fn()\n      const dropdownNode = {}\n\n      act(() => {\n        const {blablaRef} = result.current.getDropdownProps({\n          refKey: 'blablaRef',\n          blablaRef: refFn,\n        })\n\n        blablaRef(dropdownNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(dropdownNode)\n    })\n\n    test('event handler onKeyDown is called along with downshift handler', () => {\n      const userOnKeyDown = jest.fn()\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0]],\n      })\n\n      act(() => {\n        const {onKeyDown} = result.current.getDropdownProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(0)\n    })\n\n    test(\"event handler onKeyDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnKeyDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0]],\n      })\n\n      act(() => {\n        const {onKeyDown} = result.current.getDropdownProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(-1)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on keydown', () => {\n      test('arrow left should make first selected item active', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n          },\n        })\n\n        await keyDownOnInput('{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n      })\n\n      test('arrow left should not work if pressed with modifier keys', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {initialSelectedItems: [items[0], items[1]]},\n        })\n\n        await keyDownOnInput('{Shift>}{ArrowLeft}{/Shift}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Alt>}{ArrowLeft}{/Alt}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Meta>}{ArrowLeft}{/Meta}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Control>}{ArrowLeft}{/Control}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n      })\n\n      test('backspace should remove the first selected item', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {initialSelectedItems: [items[0], items[1]]},\n        })\n\n        await keyDownOnInput('{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(1)\n      })\n\n      test('backspace should not work if pressed with modifier keys', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {initialSelectedItems: [items[0], items[1]]},\n        })\n\n        await keyDownOnInput('{Shift>}{Backspace}{/Shift}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Alt>}{ArrowLeft}{/Alt}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Meta>}{ArrowLeft}{/Meta}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n\n        await keyDownOnInput('{Control>}{ArrowLeft}{/Control}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n      })\n\n      test('backspace should not work if pressed with cursor not on first position', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n          },\n          comboboxProps: {initialInputValue: 'test'},\n        })\n        const input = getInput()\n\n        input.selectionStart = 1\n        input.selectionEnd = 1\n        await keyDownOnInput('{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n      })\n\n      test('backspace should not work if pressed with cursor highlighting text', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n          },\n          comboboxProps: {initialInputValue: 'test'},\n        })\n        const input = getInput()\n\n        input.selectionStart = 0\n        input.selectionEnd = 3\n        await keyDownOnInput('{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n      })\n\n      test(\"other than the ones supported don't affect anything\", async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n          },\n        })\n\n        await keyDownOnInput('{Alt}')\n        await keyDownOnInput('{Control}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getInput()).toHaveFocus()\n      })\n    })\n\n    test('on click it should remove active status from item if any', async () => {\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          initialActiveIndex: 1,\n        },\n      })\n\n      focusSelectedItemAtIndex(1)\n      await clickOnInput()\n\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n\n      await keyDownOnInput('{ArrowLeft}')\n\n      expect(getSelectedItemAtIndex(1)).toHaveFocus()\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeEach(() => {\n      // usually disabled by test utils.\n      const {useGetterPropsCalledChecker} = jest.requireActual('../../utils')\n      jest\n        .spyOn(utils, 'useGetterPropsCalledChecker')\n        .mockImplementation(useGetterPropsCalledChecker)\n      jest.spyOn(console, 'error').mockImplementation(() => {})\n    })\n\n    test('will be displayed if getDropdownProps is not called', () => {\n      renderHook(() => {\n        useMultipleSelection()\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: You forgot to call the getDropdownProps getter function on your component / element.`,\n      )\n    })\n\n    test('will not be displayed if getDropdownProps is not called on subsequent renders', () => {\n      let firstRender = true\n      const {rerender} = renderHook(() => {\n        const {getDropdownProps} = useMultipleSelection()\n\n        // eslint-disable-next-line jest/no-if, jest/no-conditional-in-test\n        if (firstRender) {\n          firstRender = false\n          getDropdownProps({}, {suppressRefError: true})\n        }\n      })\n\n      rerender()\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will be displayed if element ref is not set and suppressRefError is false', () => {\n      renderHook(() => {\n        const {getDropdownProps} = useMultipleSelection()\n\n        getDropdownProps()\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: The ref prop \"ref\" from getDropdownProps was not applied correctly on your element.`,\n      )\n    })\n\n    test('will not be displayed if element ref is not set but suppressRefError is true', () => {\n      renderHook(() => {\n        const {getDropdownProps} = useMultipleSelection()\n\n        getDropdownProps({}, {suppressRefError: true})\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will not be displayed if called with a correct ref', () => {\n      const refFn = jest.fn()\n      const dropdownNode = {}\n\n      renderHook(() => {\n        const {getDropdownProps} = useMultipleSelection()\n        const {ref} = getDropdownProps({\n          ref: refFn,\n        })\n        ref(dropdownNode)\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/getSelectedItemProps.test.js",
    "content": "import {act} from '@testing-library/react'\n\nimport {\n  renderUseMultipleSelection,\n  renderMultipleCombobox,\n  clickOnSelectedItemAtIndex,\n  getSelectedItemAtIndex,\n  focusSelectedItemAtIndex,\n  keyDownOnSelectedItemAtIndex,\n  getSelectedItems,\n  getInput,\n  items,\n  tab,\n} from '../testUtils'\n\ndescribe('getSelectedItemProps', () => {\n  test('throws error if no index or item has been passed', () => {\n    const {result} = renderUseMultipleSelection()\n\n    expect(result.current.getSelectedItemProps).toThrow(\n      'Pass either item or index to getSelectedItemProps!',\n    )\n  })\n\n  describe('hook props', () => {\n    test(\"assign '-1' to tabindex for a non-active item\", () => {\n      const {result} = renderUseMultipleSelection()\n      const itemProps = result.current.getSelectedItemProps({\n        index: 0,\n        selectedItem: items[0],\n      })\n\n      expect(itemProps.tabIndex).toEqual(-1)\n    })\n\n    test(\"assign '0' to tabindex for an active item\", () => {\n      const {result} = renderUseMultipleSelection({activeIndex: 0})\n      const itemProps = result.current.getSelectedItemProps({\n        index: 0,\n        selectedItem: items[0],\n      })\n\n      expect(itemProps.tabIndex).toEqual(0)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseMultipleSelection()\n\n      expect(\n        result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          foo: 'bar',\n        }),\n      ).toHaveProperty('foo', 'bar')\n    })\n\n    test('custom ref passed by the user is used', () => {\n      const {result} = renderUseMultipleSelection()\n      const refFn = jest.fn()\n      const itemNode = {}\n\n      act(() => {\n        const {ref} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          ref: refFn,\n        })\n\n        ref(itemNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(itemNode)\n    })\n\n    test('custom ref with custom name passed by the user is used', () => {\n      const {result} = renderUseMultipleSelection()\n      const refFn = jest.fn()\n      const itemNode = {}\n\n      act(() => {\n        const {blablaRef} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          refKey: 'blablaRef',\n          blablaRef: refFn,\n        })\n\n        blablaRef(itemNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(itemNode)\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0], items[1]],\n      })\n\n      act(() => {\n        const {onClick} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(1)\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0], items[1]],\n      })\n\n      act(() => {\n        const {onClick} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(-1)\n    })\n\n    test('event handler onKeyDown is called along with downshift handler', () => {\n      const userOnKeyDown = jest.fn()\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0], items[1]],\n      })\n\n      act(() => {\n        const {onKeyDown} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(0)\n    })\n\n    test(\"event handler onKeyDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnKeyDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: [items[0], items[1]],\n      })\n\n      act(() => {\n        const {onKeyDown} = result.current.getSelectedItemProps({\n          index: 1,\n          selectedItem: items[1],\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(-1)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on click', () => {\n      test('sets tabindex to \"0\"', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {initialSelectedItems: [items[0], items[1]]},\n        })\n\n        await clickOnSelectedItemAtIndex(0)\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(0)).toHaveFocus()\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n      })\n\n      test('keeps tabindex \"0\" to an already active item', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        await clickOnSelectedItemAtIndex(0)\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(0)).toHaveFocus()\n      })\n    })\n\n    describe('on key down', () => {\n      test('arrow left should change active item descendently', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n            initialActiveIndex: 1,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(0)).toHaveFocus()\n      })\n\n      test(`arrow left should not change active item if it's the first one added`, async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(0, '{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(0)).toHaveFocus()\n      })\n\n      test('arrow right should change active item ascendently', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(0, '{ArrowRight}')\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n      })\n\n      test(`arrow right should make no item active if it's on last one added`, async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1]],\n            initialActiveIndex: 1,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowRight}')\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n        expect(getInput()).toHaveFocus()\n      })\n\n      test('arrow navigation moves focus back and forth', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 2,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(2, '{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n\n        await keyDownOnSelectedItemAtIndex(0, '{ArrowRight}')\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n\n        await keyDownOnSelectedItemAtIndex(2, '{ArrowRight}')\n\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n      })\n\n      test('backspace removes item and moves focus to next item if any', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 1,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(1, '{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n        expect(getSelectedItemAtIndex(1)).toHaveTextContent(items[2])\n      })\n\n      test('backspace removes item and moves focus to input if no items left', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(0, '{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(0)\n        expect(getInput()).toHaveFocus()\n      })\n\n      test('backspace removes item and moves focus to previous item if it was the last in the array', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 2,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(2, '{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n        expect(getSelectedItemAtIndex(1)).toHaveTextContent(items[1])\n      })\n\n      test('delete removes item and moves focus to next item if any', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 1,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(1, '{Delete}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n        expect(getSelectedItemAtIndex(1)).toHaveTextContent(items[2])\n      })\n\n      test('delete removes item and moves focus to input if no items left', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(0, '{Delete}')\n\n        expect(getSelectedItems()).toHaveLength(0)\n        expect(getInput()).toHaveFocus()\n      })\n\n      test('delete removes item and moves focus to previous item if it was the last in the array', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 2,\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(2, '{Delete}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n        expect(getSelectedItemAtIndex(1)).toHaveTextContent(items[1])\n      })\n\n      test('backspace and delete change nothing if there is no selected item focused', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n          },\n        })\n\n        await keyDownOnSelectedItemAtIndex(2, '{Backspace}')\n\n        expect(getSelectedItems()).toHaveLength(3)\n\n        await keyDownOnSelectedItemAtIndex(1, '{Delete}')\n\n        expect(getSelectedItems()).toHaveLength(3)\n      })\n\n      test('navigation works correctly with both click and arrow keys', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n          },\n        })\n\n        await clickOnSelectedItemAtIndex(1)\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n\n        await clickOnSelectedItemAtIndex(1)\n\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n\n        await keyDownOnSelectedItemAtIndex(2, '{ArrowRight}')\n\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n      })\n\n      test(\"other than the ones supported don't affect anything\", async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {initialSelectedItems: [items[0], items[1]]},\n        })\n\n        await keyDownOnSelectedItemAtIndex(1, '{Alt}')\n        await keyDownOnSelectedItemAtIndex(1, '{Control}')\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowUp}')\n        await keyDownOnSelectedItemAtIndex(1, '{ArrowDown}')\n        await keyDownOnSelectedItemAtIndex(1, '{Enter}')\n\n        expect(getSelectedItems()).toHaveLength(2)\n        expect(getSelectedItemAtIndex(1)).toHaveFocus()\n      })\n    })\n\n    describe('on focus', () => {\n      test('keeps tabindex \"0\" when focusing input by tab/click so user can return via tab', async () => {\n        renderMultipleCombobox({\n          multipleSelectionProps: {\n            initialSelectedItems: [items[0], items[1], items[2]],\n            initialActiveIndex: 0,\n          },\n        })\n\n        focusSelectedItemAtIndex(0)\n        await tab()\n\n        expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n        expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n        expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '-1')\n        expect(getInput()).toHaveFocus()\n      })\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/memo.test.js",
    "content": "import {renderUseMultipleSelection} from '../testUtils'\n\ntest('functions are memoized', () => {\n  const {result, rerender} = renderUseMultipleSelection()\n  const firstRenderResult = result.current\n  rerender()\n  const secondRenderResult = result.current\n  expect(firstRenderResult).toEqual(secondRenderResult)\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/props.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport {\n  renderUseMultipleSelection,\n  renderMultipleCombobox,\n  keyDownOnSelectedItemAtIndex,\n  getSelectedItems,\n  focusSelectedItemAtIndex,\n  clickOnSelectedItemAtIndex,\n  getSelectedItemAtIndex,\n  clickOnInput,\n  getA11yStatusContainer,\n  getInput,\n  items,\n  keyDownOnInput,\n  waitForDebouncedA11yStatusUpdate,\n} from '../testUtils'\nimport useMultipleSelection from '..'\n\njest.useFakeTimers()\n\ndescribe('props', () => {\n  test('if falsy then no prop types error is thrown', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n\n    renderHook(() => {\n      const {getDropdownProps} = useMultipleSelection()\n      getDropdownProps({}, {suppressRefError: true})\n    })\n\n    // eslint-disable-next-line no-console\n    expect(console.error).not.toHaveBeenCalled()\n  })\n\n  describe('selectedItems', () => {\n    test('control the state property if passed', async () => {\n      const inputItems = [items[0], items[1]]\n\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems: inputItems,\n          initialActiveIndex: 0,\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(0, '{Delete}')\n\n      expect(getSelectedItems()).toHaveLength(2)\n    })\n  })\n\n  describe('getA11yStatusMessage', () => {\n    beforeEach(() => jest.useFakeTimers())\n    afterEach(() => {\n      act(() => jest.runAllTimers())\n    })\n    afterAll(() => jest.useRealTimers())\n\n    test('adds no status message element to the DOM if not passed', async () => {\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems: [items[0], items[1]],\n          initialActiveIndex: 0,\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('calls the function only on state changes', async () => {\n      const getA11yStatusMessage = jest.fn()\n      const selectedItems = [items[0], items[1]]\n      const multipleSelectionProps = {\n        selectedItems,\n        initialActiveIndex: 1,\n        getA11yStatusMessage,\n      }\n      const {rerender} = renderMultipleCombobox({\n        multipleSelectionProps,\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        activeIndex: 0,\n        selectedItems,\n      })\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n\n      getA11yStatusMessage.mockClear()\n      rerender({\n        multipleSelectionProps: {...multipleSelectionProps, activeIndex: 0},\n      })\n\n      expect(getA11yStatusMessage).not.toHaveBeenCalled()\n    })\n\n    test('adds a status message element with the text returned', async () => {\n      const a11yStatusMessage1 = 'to the left to the left'\n      const a11yStatusMessage2 = 'to the right?'\n      const selectedItems = [items[0], items[1]]\n      const getA11yStatusMessage = jest\n        .fn()\n        .mockReturnValueOnce(a11yStatusMessage1)\n        .mockReturnValueOnce(a11yStatusMessage2)\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems,\n          initialActiveIndex: 1,\n          getA11yStatusMessage,\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).toHaveTextContent(a11yStatusMessage1)\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        activeIndex: 0,\n        selectedItems,\n      })\n\n      getA11yStatusMessage.mockClear()\n\n      await keyDownOnSelectedItemAtIndex(0, '{ArrowRight}')\n\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).toHaveTextContent(a11yStatusMessage2)\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        activeIndex: 1,\n        selectedItems,\n      })\n    })\n\n    test('clears the text content after 500ms', async () => {\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems: [items[0], items[1]],\n          initialActiveIndex: 1,\n          getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate(true)\n\n      expect(getA11yStatusContainer()).toBeEmptyDOMElement()\n    })\n\n    test('removes the message element from the DOM on unmount', async () => {\n      const {unmount} = renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems: [items[0], items[1]],\n          initialActiveIndex: 0,\n          getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate(true)\n      unmount()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('is added to the document provided by the user as prop', async () => {\n      const environment = {\n        document: {\n          getElementById: jest.fn().mockReturnValue({remove: jest.fn()}),\n          createElement: jest.fn(),\n          activeElement: {},\n          body: {},\n        },\n        addEventListener: jest.fn(),\n        removeEventListener: jest.fn(),\n        Node,\n      }\n\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems: [items[0], items[1]],\n          initialActiveIndex: 1,\n          getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n          environment,\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(environment.document.getElementById).toHaveBeenCalledTimes(1)\n      expect(environment.document.getElementById).toHaveBeenCalledWith(\n        'a11y-status-message',\n      )\n    })\n  })\n\n  describe('activeIndex', () => {\n    test('controls the state property if passed', async () => {\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          activeIndex: 1,\n        },\n      })\n\n      focusSelectedItemAtIndex(1)\n      await clickOnSelectedItemAtIndex(0)\n\n      expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n      expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowRight}')\n\n      expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '-1')\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '0')\n    })\n  })\n\n  describe('stateReducer', () => {\n    test('is called at each state change with the function change type', () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      const {result} = renderUseMultipleSelection({stateReducer})\n\n      act(() => {\n        result.current.addSelectedItem(items[0])\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([items[0]]),\n          }),\n          type: stateChangeTypes.FunctionAddSelectedItem,\n        }),\n      )\n\n      act(() => {\n        result.current.removeSelectedItem(items[0])\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(2)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[0]]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([]),\n          }),\n          type: stateChangeTypes.FunctionRemoveSelectedItem,\n        }),\n      )\n\n      act(() => {\n        result.current.setSelectedItems([items[0], items[1]])\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(3)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([items[0], items[1]]),\n          }),\n          type: stateChangeTypes.FunctionSetSelectedItems,\n        }),\n      )\n\n      act(() => {\n        result.current.setActiveIndex(1)\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(4)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          activeIndex: -1,\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: 1,\n          }),\n          type: stateChangeTypes.FunctionSetActiveIndex,\n        }),\n      )\n\n      act(() => {\n        result.current.reset()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(5)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[0], items[1]]),\n          activeIndex: 1,\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([]),\n            activeIndex: -1,\n          }),\n          type: stateChangeTypes.FunctionReset,\n        }),\n      )\n    })\n\n    test('is called at each state change with the appropriate change type', async () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1], items[2]],\n          stateReducer,\n        },\n      })\n\n      await keyDownOnInput('{Backspace}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[0], items[1], items[2]]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([items[0], items[1]]),\n          }),\n          type: stateChangeTypes.DropdownKeyDownBackspace,\n        }),\n      )\n\n      await keyDownOnInput('{ArrowLeft}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(2)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({activeIndex: -1}),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: 1,\n          }),\n          type: stateChangeTypes.DropdownKeyDownNavigationPrevious,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(3)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({activeIndex: 1}),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: 0,\n          }),\n          type: stateChangeTypes.SelectedItemKeyDownNavigationPrevious,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{ArrowRight}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(4)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({activeIndex: 0}),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: 1,\n          }),\n          type: stateChangeTypes.SelectedItemKeyDownNavigationNext,\n        }),\n      )\n\n      await clickOnInput()\n\n      expect(stateReducer).toHaveBeenCalledTimes(5)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({activeIndex: 1}),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: -1,\n          }),\n          type: stateChangeTypes.DropdownClick,\n        }),\n      )\n\n      await clickOnSelectedItemAtIndex(0)\n\n      expect(stateReducer).toHaveBeenCalledTimes(6)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({activeIndex: -1}),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            activeIndex: 0,\n          }),\n          type: stateChangeTypes.SelectedItemClick,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{Delete}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(7)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[0], items[1]]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([items[1]]),\n          }),\n          type: stateChangeTypes.SelectedItemKeyDownDelete,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{Backspace}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(8)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[1]]),\n        }),\n        expect.objectContaining({\n          changes: expect.objectContaining({\n            selectedItems: expect.arrayContaining([]),\n          }),\n          type: stateChangeTypes.SelectedItemKeyDownBackspace,\n        }),\n      )\n    })\n\n    test('replaces prop values with user defined', async () => {\n      const stateReducer = jest.fn((s, a) => {\n        const changes = a.changes\n        changes.activeIndex = 0\n        return changes\n      })\n\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          stateReducer,\n        },\n      })\n\n      await clickOnSelectedItemAtIndex(1)\n\n      expect(getSelectedItemAtIndex(1)).toHaveAttribute('tabindex', '-1')\n      expect(getSelectedItemAtIndex(0)).toHaveAttribute('tabindex', '0')\n      expect(getSelectedItemAtIndex(0)).toHaveFocus()\n    })\n\n    test('receives state, changes and type', async () => {\n      const stateReducer = jest.fn((s, a) => {\n        expect(a.type).not.toBeUndefined()\n        expect(a.type).not.toBeNull()\n\n        expect(s).not.toBeUndefined()\n        expect(s).not.toBeNull()\n\n        expect(a.changes).not.toBeUndefined()\n        expect(a.changes).not.toBeNull()\n\n        return a.changes\n      })\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          stateReducer,\n        },\n      })\n\n      await clickOnSelectedItemAtIndex(0)\n    })\n\n    test('changes are visible in onChange handlers', async () => {\n      const activeIndex = 0\n      const inputItems = ['foo', 'bar']\n      const stateReducer = jest.fn(() => ({\n        activeIndex,\n        selectedItems: [],\n      }))\n      const onSelectedItemsChange = jest.fn()\n      const onActiveIndexChange = jest.fn()\n      const onStateChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          stateReducer,\n          onStateChange,\n          onActiveIndexChange,\n          onSelectedItemsChange,\n          selectedItems: inputItems,\n        },\n      })\n\n      await keyDownOnInput('{ArrowLeft}')\n\n      expect(onActiveIndexChange).toHaveBeenCalledTimes(1)\n      expect(onActiveIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          activeIndex,\n        }),\n      )\n      expect(onSelectedItemsChange).toHaveBeenCalledTimes(1)\n      expect(onSelectedItemsChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItems: [],\n        }),\n      )\n      expect(onStateChange).toHaveBeenCalledTimes(1)\n      expect(onStateChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          activeIndex,\n          selectedItems: [],\n          type: stateChangeTypes.DropdownKeyDownNavigationPrevious,\n        }),\n      )\n    })\n  })\n\n  describe('onActiveIndexChange', () => {\n    test('is called at activeIndex change', async () => {\n      const onActiveIndexChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          onActiveIndexChange,\n        },\n      })\n\n      await clickOnSelectedItemAtIndex(1)\n\n      expect(onActiveIndexChange).toHaveBeenCalledTimes(1)\n      expect(onActiveIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          activeIndex: 1,\n        }),\n      )\n    })\n\n    test('is not called at if selectedItem is the same', async () => {\n      const onActiveIndexChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          onActiveIndexChange,\n          initialActiveIndex: 1,\n        },\n      })\n\n      await clickOnSelectedItemAtIndex(1)\n\n      expect(onActiveIndexChange).not.toHaveBeenCalled()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let activeIndex = 3\n      const {rerender} = renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: items,\n          activeIndex,\n          onActiveIndexChange: changes => {\n            activeIndex = changes.activeIndex\n          },\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(3, '{ArrowLeft}')\n      rerender({multipleSelectionProps: {activeIndex}})\n\n      expect(getSelectedItemAtIndex(2)).toHaveAttribute('tabindex', '0')\n    })\n\n    test('can have downshift actions executed', () => {\n      const {result} = renderUseMultipleSelection({\n        onActiveIndexChange: () => {\n          result.current.setSelectedItems([items[0]])\n        },\n        initialSelectedItems: items,\n      })\n\n      act(() => {\n        result.current.getSelectedItemProps({index: 3}).onClick({})\n      })\n\n      expect(result.current.selectedItems).toEqual([items[0]])\n    })\n  })\n\n  describe('onSelectedItemsChange', () => {\n    test('is called at items change', async () => {\n      const onSelectedItemsChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          onSelectedItemsChange,\n          initialActiveIndex: 1,\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(1, '{Delete}')\n\n      expect(onSelectedItemsChange).toHaveBeenCalledTimes(1)\n      expect(onSelectedItemsChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItems: [items[0]],\n        }),\n      )\n    })\n\n    test('is not called at if items is the same', async () => {\n      const onSelectedItemsChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1]],\n          onSelectedItemsChange,\n          initialActiveIndex: 1,\n        },\n      })\n\n      await clickOnSelectedItemAtIndex(0)\n\n      expect(onSelectedItemsChange).not.toHaveBeenCalled()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let selectedItems = [items[0], items[1]]\n      const {rerender} = renderMultipleCombobox({\n        multipleSelectionProps: {\n          selectedItems,\n          initialActiveIndex: 0,\n          onSelectedItemsChange: changes => {\n            selectedItems = changes.selectedItems\n          },\n        },\n      })\n\n      await keyDownOnSelectedItemAtIndex(0, '{Delete}')\n      rerender({multipleSelectionProps: {selectedItems}})\n\n      expect(getSelectedItems()).toHaveLength(1)\n    })\n\n    test('can have downshift actions executed', () => {\n      const initialActiveIndex = 3\n      const {result} = renderUseMultipleSelection({\n        onSelectedItemsChange: () => {\n          result.current.setActiveIndex(1)\n        },\n        initialSelectedItems: items,\n        initialActiveIndex,\n      })\n\n      act(() => {\n        result.current\n          .getSelectedItemProps({index: initialActiveIndex})\n          .onKeyDown({key: 'Backspace'})\n      })\n\n      expect(result.current.activeIndex).toEqual(1)\n    })\n  })\n\n  describe('onStateChange', () => {\n    test('is called at each state property change', async () => {\n      const onStateChange = jest.fn()\n      renderMultipleCombobox({\n        multipleSelectionProps: {\n          initialSelectedItems: [items[0], items[1], items[2]],\n          onStateChange,\n        },\n      })\n\n      await keyDownOnInput('{Backspace}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(1)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[0], items[1]]),\n          type: stateChangeTypes.DropdownKeyDownBackspace,\n        }),\n      )\n\n      await keyDownOnInput('{ArrowLeft}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(2)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          activeIndex: 1,\n          type: stateChangeTypes.DropdownKeyDownNavigationPrevious,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(3)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          activeIndex: 0,\n          type: stateChangeTypes.SelectedItemKeyDownNavigationPrevious,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{ArrowRight}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(4)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          activeIndex: 1,\n          type: stateChangeTypes.SelectedItemKeyDownNavigationNext,\n        }),\n      )\n\n      await clickOnSelectedItemAtIndex(0)\n\n      expect(onStateChange).toHaveBeenCalledTimes(5)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          activeIndex: 0,\n          type: stateChangeTypes.SelectedItemClick,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{Delete}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(6)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([items[1]]),\n          type: stateChangeTypes.SelectedItemKeyDownDelete,\n        }),\n      )\n\n      await keyDownOnSelectedItemAtIndex(0, '{Backspace}')\n\n      expect(onStateChange).toHaveBeenCalledTimes(7)\n      expect(onStateChange).toHaveBeenLastCalledWith(\n        expect.objectContaining({\n          selectedItems: expect.arrayContaining([]),\n          type: stateChangeTypes.SelectedItemKeyDownBackspace,\n        }),\n      )\n    })\n  })\n\n  test('overrides navigation previos and next keys correctly', async () => {\n    renderMultipleCombobox({\n      multipleSelectionProps: {\n        keyNavigationPrevious: 'ArrowRight',\n        keyNavigationNext: 'ArrowLeft',\n        initialSelectedItems: [items[0], items[1]],\n      },\n    })\n\n    await keyDownOnInput('{ArrowRight}')\n\n    expect(getSelectedItemAtIndex(1)).toHaveFocus()\n\n    await keyDownOnSelectedItemAtIndex(1, '{ArrowRight}')\n\n    expect(getSelectedItemAtIndex(0)).toHaveFocus()\n\n    await keyDownOnSelectedItemAtIndex(0, '{ArrowLeft}')\n\n    expect(getSelectedItemAtIndex(1)).toHaveFocus()\n\n    await keyDownOnSelectedItemAtIndex(1, '{ArrowLeft}')\n\n    expect(getInput()).toHaveFocus()\n  })\n\n  test('can have downshift actions executed', () => {\n    const {result} = renderUseMultipleSelection({\n      initialSelectedItems: items,\n      onStateChange: () => {\n        result.current.setActiveIndex(4)\n      },\n    })\n\n    act(() => {\n      result.current.getSelectedItemProps({index: 2}).onClick({})\n    })\n\n    expect(result.current.activeIndex).toEqual(4)\n  })\n\n  test('that are uncontrolled should not become controlled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderMultipleCombobox()\n\n    rerender({multipleSelectionProps: {activeIndex: 1}})\n\n    // eslint-disable-next-line no-console\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the uncontrolled prop \"activeIndex\" to be controlled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n\n  test('that are controlled should not become uncontrolled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderMultipleCombobox({\n      multipleSelectionProps: {selectedItems: [items[1]]},\n    })\n\n    rerender({})\n\n    // eslint-disable-next-line no-console\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the controlled prop \"selectedItems\" to be uncontrolled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/returnProps.test.js",
    "content": "import {act} from '@testing-library/react'\n\nimport {renderUseMultipleSelection} from '../testUtils'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport useMultipleSelection from '..'\n\ndescribe('returnProps', () => {\n  test('should have stateChangeTypes attached to hook', () => {\n    expect(useMultipleSelection).toHaveProperty(\n      'stateChangeTypes',\n      stateChangeTypes,\n    )\n  })\n\n  describe('prop getters', () => {\n    test('are returned as functions', () => {\n      const {result} = renderUseMultipleSelection()\n\n      expect(result.current.getDropdownProps).toBeInstanceOf(Function)\n      expect(result.current.getSelectedItemProps).toBeInstanceOf(Function)\n    })\n  })\n\n  describe('actions', () => {\n    test('addSelectedItem adds an item to the selected array', () => {\n      const {result} = renderUseMultipleSelection()\n\n      act(() => {\n        result.current.addSelectedItem('test')\n      })\n\n      expect(result.current.selectedItems).toStrictEqual(['test'])\n    })\n\n    test('removeSelectedItem removes an item from the selected array and keeps active index if not last item', () => {\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: ['test', 'more test'],\n        initialActiveIndex: 0,\n      })\n\n      act(() => {\n        result.current.removeSelectedItem('test')\n      })\n\n      expect(result.current.selectedItems).toStrictEqual(['more test'])\n      expect(result.current.activeIndex).toEqual(0)\n    })\n\n    test('removeSelectedItem removes an item from the selected array and decreases active index if last item', () => {\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: ['test', 'more test'],\n        initialActiveIndex: 1,\n      })\n\n      act(() => {\n        result.current.removeSelectedItem('more test')\n      })\n\n      expect(result.current.selectedItems).toStrictEqual(['test'])\n      expect(result.current.activeIndex).toEqual(0)\n    })\n\n    test('removeSelectedItem handles undefined item without modifying the selected array or the active index', () => {\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: ['test', 'more test'],\n        initialActiveIndex: 1,\n      })\n\n      act(() => {\n        result.current.removeSelectedItem(undefined)\n      })\n\n      expect(result.current.selectedItems).toStrictEqual(['test', 'more test'])\n      expect(result.current.activeIndex).toEqual(1)\n    })\n\n    test('removeSelectedItem handles null item without modifying the selected array or the active index', () => {\n      const {result} = renderUseMultipleSelection({\n        initialSelectedItems: ['test', 'more test'],\n        initialActiveIndex: 1,\n      })\n\n      act(() => {\n        result.current.removeSelectedItem(null)\n      })\n\n      expect(result.current.selectedItems).toStrictEqual(['test', 'more test'])\n      expect(result.current.activeIndex).toEqual(1)\n    })\n\n    test('setActiveIndex sets activeIndex', () => {\n      const {result} = renderUseMultipleSelection()\n\n      act(() => {\n        result.current.setActiveIndex(3)\n      })\n\n      expect(result.current.activeIndex).toBe(3)\n    })\n\n    test('setSelectedItems sets selectedItems', () => {\n      const inputItems = [1, 2, 3]\n      const {result} = renderUseMultipleSelection()\n\n      act(() => {\n        result.current.setSelectedItems(inputItems)\n      })\n\n      expect(result.current.selectedItems).toBe(inputItems)\n    })\n\n    test('reset sets the state to default values', () => {\n      const {result} = renderUseMultipleSelection()\n\n      act(() => {\n        result.current.setSelectedItems([1, 2, 3])\n        result.current.setActiveIndex(5)\n        result.current.reset()\n      })\n\n      expect(result.current.activeIndex).toBe(-1)\n      expect(result.current.selectedItems).toStrictEqual([])\n    })\n\n    test('reset sets the state to default prop values passed by user', () => {\n      const {result} = renderUseMultipleSelection({\n        defaultSelectedItems: [3, 4],\n        defaultActiveIndex: 0,\n      })\n\n      act(() => {\n        result.current.setSelectedItems([1, 2])\n        result.current.setActiveIndex(1)\n        result.current.reset()\n      })\n\n      expect(result.current.activeIndex).toBe(0)\n      expect(result.current.selectedItems).toStrictEqual([3, 4])\n    })\n  })\n\n  describe('state and props', () => {\n    test('activeIndex is returned', () => {\n      const {result} = renderUseMultipleSelection({activeIndex: 4})\n\n      expect(result.current.activeIndex).toBe(4)\n    })\n\n    test('selectedItems is returned', () => {\n      const itemsInput = [1, 2, 3]\n      const {result} = renderUseMultipleSelection({selectedItems: itemsInput})\n\n      expect(result.current.selectedItems).toBe(itemsInput)\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/__tests__/utils.test.js",
    "content": "import reducer from '../reducer'\n\ndescribe('utils', () => {\n  test('reducer throws error if called without proper action type', () => {\n    expect(() => {\n      reducer({}, {}, {type: 'super-bogus'})\n    }).toThrow('Reducer called without proper action type.')\n  })\n})\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/index.js",
    "content": "import {useRef, useEffect, useCallback, useMemo} from 'react'\nimport {handleRefs, callAllEventHandlers, normalizeArrowKey} from '../../utils'\nimport {useLatestRef} from '../../utils-ts'\nimport {useGetterPropsCalledChecker, useControlPropsValidator} from '../utils'\nimport {\n  useControlledReducer,\n  useIsInitialMount,\n  useA11yMessageStatus,\n  getItemAndIndex,\n} from '../utils-ts'\nimport {\n  getInitialState,\n  defaultProps,\n  isKeyDownOperationPermitted,\n  validatePropTypes,\n  isStateEqual,\n} from './utils'\nimport downshiftMultipleSelectionReducer from './reducer'\nimport * as stateChangeTypes from './stateChangeTypes'\n\nuseMultipleSelection.stateChangeTypes = stateChangeTypes\n\nfunction useMultipleSelection(userProps = {}) {\n  validatePropTypes(userProps, useMultipleSelection)\n  // Props defaults and destructuring.\n  const props = {\n    ...defaultProps,\n    ...userProps,\n  }\n  const {\n    getA11yStatusMessage,\n    environment,\n    keyNavigationNext,\n    keyNavigationPrevious,\n  } = props\n\n  // Reducer init.\n  const [state, dispatch] = useControlledReducer(\n    downshiftMultipleSelectionReducer,\n    props,\n    getInitialState,\n    isStateEqual,\n  )\n  const {activeIndex, selectedItems} = state\n\n  // Refs.\n  const isInitialMount = useIsInitialMount()\n  const dropdownRef = useRef(null)\n  const selectedItemRefs = useRef()\n  selectedItemRefs.current = []\n  const latest = useLatestRef({state, props})\n\n  // Effects.\n  // Adds an a11y aria live status message if getA11yStatusMessage is passed.\n  useA11yMessageStatus(\n    getA11yStatusMessage,\n    state,\n    [activeIndex, selectedItems],\n    environment,\n  )\n  // Sets focus on active item.\n  useEffect(() => {\n    if (isInitialMount) {\n      return\n    }\n\n    if (activeIndex === -1 && dropdownRef.current) {\n      dropdownRef.current.focus()\n    } else if (selectedItemRefs.current[activeIndex]) {\n      selectedItemRefs.current[activeIndex].focus()\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [activeIndex])\n  useControlPropsValidator({\n    props,\n    state,\n  })\n  const setGetterPropCallInfo = useGetterPropsCalledChecker('getDropdownProps')\n\n  // Event handler functions.\n  const selectedItemKeyDownHandlers = useMemo(\n    () => ({\n      [keyNavigationPrevious]() {\n        dispatch({\n          type: stateChangeTypes.SelectedItemKeyDownNavigationPrevious,\n        })\n      },\n      [keyNavigationNext]() {\n        dispatch({\n          type: stateChangeTypes.SelectedItemKeyDownNavigationNext,\n        })\n      },\n      Delete() {\n        dispatch({\n          type: stateChangeTypes.SelectedItemKeyDownDelete,\n        })\n      },\n      Backspace() {\n        dispatch({\n          type: stateChangeTypes.SelectedItemKeyDownBackspace,\n        })\n      },\n    }),\n    [dispatch, keyNavigationNext, keyNavigationPrevious],\n  )\n  const dropdownKeyDownHandlers = useMemo(\n    () => ({\n      [keyNavigationPrevious](event) {\n        if (isKeyDownOperationPermitted(event)) {\n          dispatch({\n            type: stateChangeTypes.DropdownKeyDownNavigationPrevious,\n          })\n        }\n      },\n      Backspace(event) {\n        if (isKeyDownOperationPermitted(event)) {\n          dispatch({\n            type: stateChangeTypes.DropdownKeyDownBackspace,\n          })\n        }\n      },\n    }),\n    [dispatch, keyNavigationPrevious],\n  )\n\n  // Getter props.\n  const getSelectedItemProps = useCallback(\n    ({\n      refKey = 'ref',\n      ref,\n      onClick,\n      onKeyDown,\n      selectedItem: selectedItemProp,\n      index: indexProp,\n      ...rest\n    } = {}) => {\n      const {state: latestState} = latest.current\n      const [, index] = getItemAndIndex(\n        selectedItemProp,\n        indexProp,\n        latestState.selectedItems,\n        'Pass either item or index to getSelectedItemProps!',\n      )\n      const isFocusable = index > -1 && index === latestState.activeIndex\n\n      const selectedItemHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.SelectedItemClick,\n          index,\n        })\n      }\n      const selectedItemHandleKeyDown = event => {\n        const key = normalizeArrowKey(event)\n        if (key && selectedItemKeyDownHandlers[key]) {\n          selectedItemKeyDownHandlers[key](event)\n        }\n      }\n\n      return {\n        [refKey]: handleRefs(ref, selectedItemNode => {\n          if (selectedItemNode) {\n            selectedItemRefs.current.push(selectedItemNode)\n          }\n        }),\n        tabIndex: isFocusable ? 0 : -1,\n        onClick: callAllEventHandlers(onClick, selectedItemHandleClick),\n        onKeyDown: callAllEventHandlers(onKeyDown, selectedItemHandleKeyDown),\n        ...rest,\n      }\n    },\n    [dispatch, latest, selectedItemKeyDownHandlers],\n  )\n  const getDropdownProps = useCallback(\n    (\n      {\n        refKey = 'ref',\n        ref,\n        onKeyDown,\n        onClick,\n        preventKeyAction = false,\n        ...rest\n      } = {},\n      {suppressRefError = false} = {},\n    ) => {\n      setGetterPropCallInfo(\n        'getDropdownProps',\n        suppressRefError,\n        refKey,\n        dropdownRef,\n      )\n\n      const dropdownHandleKeyDown = event => {\n        const key = normalizeArrowKey(event)\n        if (key && dropdownKeyDownHandlers[key]) {\n          dropdownKeyDownHandlers[key](event)\n        }\n      }\n      const dropdownHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.DropdownClick,\n        })\n      }\n\n      return {\n        [refKey]: handleRefs(ref, dropdownNode => {\n          if (dropdownNode) {\n            dropdownRef.current = dropdownNode\n          }\n        }),\n        ...(!preventKeyAction && {\n          onKeyDown: callAllEventHandlers(onKeyDown, dropdownHandleKeyDown),\n          onClick: callAllEventHandlers(onClick, dropdownHandleClick),\n        }),\n        ...rest,\n      }\n    },\n    [dispatch, dropdownKeyDownHandlers, setGetterPropCallInfo],\n  )\n\n  // returns\n  const addSelectedItem = useCallback(\n    selectedItem => {\n      dispatch({\n        type: stateChangeTypes.FunctionAddSelectedItem,\n        selectedItem,\n      })\n    },\n    [dispatch],\n  )\n  const removeSelectedItem = useCallback(\n    selectedItem => {\n      dispatch({\n        type: stateChangeTypes.FunctionRemoveSelectedItem,\n        selectedItem,\n      })\n    },\n    [dispatch],\n  )\n  const setSelectedItems = useCallback(\n    newSelectedItems => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetSelectedItems,\n        selectedItems: newSelectedItems,\n      })\n    },\n    [dispatch],\n  )\n  const setActiveIndex = useCallback(\n    newActiveIndex => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetActiveIndex,\n        activeIndex: newActiveIndex,\n      })\n    },\n    [dispatch],\n  )\n  const reset = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionReset,\n    })\n  }, [dispatch])\n\n  return {\n    getSelectedItemProps,\n    getDropdownProps,\n    addSelectedItem,\n    removeSelectedItem,\n    setSelectedItems,\n    setActiveIndex,\n    reset,\n    selectedItems,\n    activeIndex,\n  }\n}\n\nexport default useMultipleSelection\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/reducer.js",
    "content": "import {getDefaultValue} from './utils'\nimport * as stateChangeTypes from './stateChangeTypes'\n\n/* eslint-disable complexity */\nexport default function downshiftMultipleSelectionReducer(\n  state,\n  props,\n  action,\n) {\n  const {type, index, selectedItem} = action\n  const {activeIndex, selectedItems} = state\n  let changes\n\n  switch (type) {\n    case stateChangeTypes.SelectedItemClick:\n      changes = {\n        activeIndex: index,\n      }\n\n      break\n    case stateChangeTypes.SelectedItemKeyDownNavigationPrevious:\n      changes = {\n        activeIndex: activeIndex - 1 < 0 ? 0 : activeIndex - 1,\n      }\n\n      break\n    case stateChangeTypes.SelectedItemKeyDownNavigationNext:\n      changes = {\n        activeIndex:\n          activeIndex + 1 >= selectedItems.length ? -1 : activeIndex + 1,\n      }\n\n      break\n    case stateChangeTypes.SelectedItemKeyDownBackspace:\n    case stateChangeTypes.SelectedItemKeyDownDelete: {\n      if (activeIndex < 0) {\n        break\n      }\n\n      let newActiveIndex = activeIndex\n\n      if (selectedItems.length === 1) {\n        newActiveIndex = -1\n      } else if (activeIndex === selectedItems.length - 1) {\n        newActiveIndex = selectedItems.length - 2\n      }\n\n      changes = {\n        selectedItems: [\n          ...selectedItems.slice(0, activeIndex),\n          ...selectedItems.slice(activeIndex + 1),\n        ],\n        ...{activeIndex: newActiveIndex},\n      }\n\n      break\n    }\n\n    case stateChangeTypes.DropdownKeyDownNavigationPrevious:\n      changes = {\n        activeIndex: selectedItems.length - 1,\n      }\n      break\n    case stateChangeTypes.DropdownKeyDownBackspace:\n      changes = {\n        selectedItems: selectedItems.slice(0, selectedItems.length - 1),\n      }\n      break\n    case stateChangeTypes.FunctionAddSelectedItem:\n      changes = {\n        selectedItems: [...selectedItems, selectedItem],\n      }\n      break\n    case stateChangeTypes.DropdownClick:\n      changes = {\n        activeIndex: -1,\n      }\n      break\n    case stateChangeTypes.FunctionRemoveSelectedItem: {\n      let newActiveIndex = activeIndex\n      const selectedItemIndex = selectedItems.findIndex(\n        item => props.itemToKey(item) === props.itemToKey(selectedItem),\n      )\n\n      if (selectedItemIndex < 0) {\n        break\n      }\n\n      if (selectedItems.length === 1) {\n        newActiveIndex = -1\n      } else if (selectedItemIndex === selectedItems.length - 1) {\n        newActiveIndex = selectedItems.length - 2\n      }\n\n      changes = {\n        selectedItems: [\n          ...selectedItems.slice(0, selectedItemIndex),\n          ...selectedItems.slice(selectedItemIndex + 1),\n        ],\n        activeIndex: newActiveIndex,\n      }\n\n      break\n    }\n    case stateChangeTypes.FunctionSetSelectedItems: {\n      const {selectedItems: newSelectedItems} = action\n      changes = {\n        selectedItems: newSelectedItems,\n      }\n      break\n    }\n    case stateChangeTypes.FunctionSetActiveIndex: {\n      const {activeIndex: newActiveIndex} = action\n      changes = {\n        activeIndex: newActiveIndex,\n      }\n      break\n    }\n    case stateChangeTypes.FunctionReset:\n      changes = {\n        activeIndex: getDefaultValue(props, 'activeIndex'),\n        selectedItems: getDefaultValue(props, 'selectedItems'),\n      }\n      break\n    default:\n      throw new Error('Reducer called without proper action type.')\n  }\n\n  return {\n    ...state,\n    ...changes,\n  }\n}\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/stateChangeTypes.js",
    "content": "import productionEnum from '../../productionEnum.macro'\n\nexport const SelectedItemClick = productionEnum('__selected_item_click__')\nexport const SelectedItemKeyDownDelete = productionEnum(\n  '__selected_item_keydown_delete__',\n)\nexport const SelectedItemKeyDownBackspace = productionEnum(\n  '__selected_item_keydown_backspace__',\n)\nexport const SelectedItemKeyDownNavigationNext = productionEnum(\n  '__selected_item_keydown_navigation_next__',\n)\nexport const SelectedItemKeyDownNavigationPrevious = productionEnum(\n  '__selected_item_keydown_navigation_previous__',\n)\n\nexport const DropdownKeyDownNavigationPrevious = productionEnum(\n  '__dropdown_keydown_navigation_previous__',\n)\nexport const DropdownKeyDownBackspace = productionEnum(\n  '__dropdown_keydown_backspace__',\n)\nexport const DropdownClick = productionEnum('__dropdown_click__')\n\nexport const FunctionAddSelectedItem = productionEnum(\n  '__function_add_selected_item__',\n)\nexport const FunctionRemoveSelectedItem = productionEnum(\n  '__function_remove_selected_item__',\n)\nexport const FunctionSetSelectedItems = productionEnum(\n  '__function_set_selected_items__',\n)\nexport const FunctionSetActiveIndex = productionEnum(\n  '__function_set_active_index__',\n)\nexport const FunctionReset = productionEnum('__function_reset__')\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/testUtils.js",
    "content": "import * as React from 'react'\n\nimport {render, screen, renderHook} from '@testing-library/react'\nimport {dropdownDefaultProps} from '../utils.dropdown'\nimport {items, user, dataTestIds} from '../testUtils'\nimport useCombobox from '../useCombobox'\nimport {getInput, keyDownOnInput} from '../useCombobox/testUtils'\nimport useMultipleSelection from '.'\n\nexport * from '../testUtils'\nexport {getInput, keyDownOnInput}\n\n// We are using React 18.\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\njest.mock('../utils', () => {\n  const utils = jest.requireActual('../utils')\n  const hooksUtils = jest.requireActual('../../utils-ts')\n\n  return {\n    ...utils,\n    useGetterPropsCalledChecker: () => hooksUtils.noop,\n  }\n})\n\nbeforeEach(jest.resetAllMocks)\nafterAll(jest.restoreAllMocks)\n\nexport function getSelectedItemAtIndex(index) {\n  return screen.getByTestId(dataTestIds.selectedItem(index))\n}\n\nexport function getSelectedItems() {\n  return screen.queryAllByTestId(new RegExp(dataTestIds.selectedItemPrefix))\n}\n\nexport async function clickOnSelectedItemAtIndex(index) {\n  await user.click(getSelectedItemAtIndex(index))\n}\nexport async function keyDownOnSelectedItemAtIndex(index, key) {\n  const selectedItem = getSelectedItemAtIndex(index)\n\n  if (document.activeElement !== selectedItem) {\n    selectedItem.focus()\n  }\n\n  await user.keyboard(key)\n}\n\nexport function focusSelectedItemAtIndex(index) {\n  getSelectedItemAtIndex(index).focus()\n}\n\nexport async function clickOnInput() {\n  await user.click(getInput())\n}\n\nconst DropdownMultipleCombobox = ({\n  multipleSelectionProps = {},\n  comboboxProps = {},\n}) => {\n  const {getSelectedItemProps, getDropdownProps, selectedItems} =\n    useMultipleSelection(multipleSelectionProps)\n  const {getToggleButtonProps, getLabelProps, getMenuProps, getInputProps} =\n    useCombobox({\n      items,\n      ...comboboxProps,\n    })\n  const {itemToString} = dropdownDefaultProps\n\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div style={{display: 'flex', flexWrap: 'wrap'}}>\n        {selectedItems.map((selectedItem, index) => (\n          <span\n            key={`selected-item-${index}`}\n            data-testid={dataTestIds.selectedItem(index)}\n            {...getSelectedItemProps({selectedItem, index})}\n          >\n            {itemToString(selectedItem)}\n          </span>\n        ))}\n        <div data-testid={dataTestIds.combobox}>\n          <input\n            data-testid={dataTestIds.input}\n            {...getInputProps(getDropdownProps())}\n          />\n          <button {...getToggleButtonProps()}>Toggle</button>\n        </div>\n      </div>\n      <ul data-testid={dataTestIds.menu} {...getMenuProps()} />\n    </div>\n  )\n}\n\nexport const renderMultipleCombobox = props => {\n  const utils = render(<DropdownMultipleCombobox {...props} />)\n  const rerender = newProps =>\n    utils.rerender(<DropdownMultipleCombobox {...newProps} />)\n\n  return {\n    ...utils,\n    rerender,\n  }\n}\n\nexport const renderUseMultipleSelection = props => {\n  return renderHook(() => useMultipleSelection(props))\n}\n"
  },
  {
    "path": "src/hooks/useMultipleSelection/utils.js",
    "content": "import PropTypes from 'prop-types'\n\nimport {noop} from '../../utils-ts'\nimport {\n  getInitialValue as getInitialValueCommon,\n  getDefaultValue as getDefaultValueCommon,\n} from '../utils-ts'\nimport {dropdownDefaultProps, dropdownPropTypes} from '../utils.dropdown'\n\nconst defaultStateValues = {\n  activeIndex: -1,\n  selectedItems: [],\n}\n\n/**\n * Returns the initial value for a state key in the following order:\n * 1. controlled prop, 2. initial prop, 3. default prop, 4. default\n * value from Downshift.\n *\n * @param {Object} props Props passed to the hook.\n * @param {string} propKey Props key to generate the value for.\n * @returns {any} The initial value for that prop.\n */\nfunction getInitialValue(props, propKey) {\n  return getInitialValueCommon(props, propKey, defaultStateValues)\n}\n\n/**\n * Returns the default value for a state key in the following order:\n * 1. controlled prop, 2. default prop, 3. default value from Downshift.\n *\n * @param {Object} props Props passed to the hook.\n * @param {string} propKey Props key to generate the value for.\n * @returns {any} The initial value for that prop.\n */\nfunction getDefaultValue(props, propKey) {\n  return getDefaultValueCommon(props, propKey, defaultStateValues)\n}\n\n/**\n * Gets the initial state based on the provided props. It uses initial, default\n * and controlled props related to state in order to compute the initial value.\n *\n * @param {Object} props Props passed to the hook.\n * @returns {Object} The initial state.\n */\nfunction getInitialState(props) {\n  const activeIndex = getInitialValue(props, 'activeIndex')\n  const selectedItems = getInitialValue(props, 'selectedItems')\n\n  return {\n    activeIndex,\n    selectedItems,\n  }\n}\n\n/**\n * Returns true if dropdown keydown operation is permitted. Should not be\n * allowed on keydown with modifier keys (ctrl, alt, shift, meta), on\n * input element with text content that is either highlighted or selection\n * cursor is not at the starting position.\n *\n * @param {KeyboardEvent} event The event from keydown.\n * @returns {boolean} Whether the operation is allowed.\n */\nfunction isKeyDownOperationPermitted(event) {\n  if (event.shiftKey || event.metaKey || event.ctrlKey || event.altKey) {\n    return false\n  }\n\n  const element = event.target\n\n  if (\n    element instanceof HTMLInputElement && // if element is a text input\n    element.value !== '' && // and we have text in it\n    // and cursor is either not at the start or is currently highlighting text.\n    (element.selectionStart !== 0 || element.selectionEnd !== 0)\n  ) {\n    return false\n  }\n\n  return true\n}\n\n/**\n * Check if a state is equal for taglist, by comparing active index and selected items.\n * Used by useSelect and useCombobox.\n *\n * @param {Object} prevState\n * @param {Object} newState\n * @returns {boolean} Wheather the states are deeply equal.\n */\nfunction isStateEqual(prevState, newState) {\n  return (\n    prevState.selectedItems === newState.selectedItems &&\n    prevState.activeIndex === newState.activeIndex\n  )\n}\n\nconst propTypes = {\n  stateReducer: dropdownPropTypes.stateReducer,\n  itemToKey: dropdownPropTypes.itemToKey,\n  environment: dropdownPropTypes.environment,\n  selectedItems: PropTypes.array,\n  initialSelectedItems: PropTypes.array,\n  defaultSelectedItems: PropTypes.array,\n  getA11yStatusMessage: PropTypes.func,\n  activeIndex: PropTypes.number,\n  initialActiveIndex: PropTypes.number,\n  defaultActiveIndex: PropTypes.number,\n  onActiveIndexChange: PropTypes.func,\n  onSelectedItemsChange: PropTypes.func,\n  keyNavigationNext: PropTypes.string,\n  keyNavigationPrevious: PropTypes.string,\n}\n\nexport const defaultProps = {\n  itemToKey: dropdownDefaultProps.itemToKey,\n  stateReducer: dropdownDefaultProps.stateReducer,\n  environment: dropdownDefaultProps.environment,\n  keyNavigationNext: 'ArrowRight',\n  keyNavigationPrevious: 'ArrowLeft',\n}\n\n// eslint-disable-next-line import/no-mutable-exports\nlet validatePropTypes = noop\n/* istanbul ignore next */\nif (process.env.NODE_ENV !== 'production') {\n  validatePropTypes = (options, caller) => {\n    PropTypes.checkPropTypes(propTypes, options, 'prop', caller.name)\n  }\n}\n\nexport {\n  validatePropTypes,\n  getDefaultValue,\n  getInitialState,\n  isKeyDownOperationPermitted,\n  isStateEqual,\n}\n"
  },
  {
    "path": "src/hooks/useSelect/README.md",
    "content": "# useSelect\n\n## The problem\n\nYou have a custom select dropdown in your application and you want it to behave\nexactly the same as the native HTML `<select>` in terms of accessibility and\nfunctionality. For consistency reasons you want it to follow the [ARIA design\npattern][select-aria] for a dropdown select. You also want this solution to be\nsimple to use and flexible so you can tailor it further to your specific needs.\n\n## This solution\n\n`useSelect` is a React hook that manages all the stateful logic needed to make\nthe dropdown functional and accessible. It returns a set of props that are meant\nto be called and their results destructured on the dropdown's elements: its\nlabel, toggle button, list and list items. The props are similar to the ones\nprovided by vanilla `<Downshift>` to the children render prop.\n\nThese props are called getter props and their return values are destructured as\na set of ARIA attributes and event listeners. Together with the action props and\nstate props, they create all the stateful logic needed for the dropdown to\nimplement the corresponding ARIA pattern. Every functionality needed should be\nprovided out-of-the-box: menu toggle, item selection and up/down movement\nbetween them, screen reader support, highlight by character keys etc.\n\n## Migration through breaking changes\n\nThe hook received breaking changes related to how it works, as well as the API,\nstarting with v7. They are documented here:\n\n- [v7 migration guide][migration-guide-v7]\n- [v8 migration guide][migration-guide-v8]\n- [v9 migration guide][migration-guide-v9]\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Usage](#usage)\n- [Basic Props](#basic-props)\n  - [items](#items)\n  - [itemToString](#itemtostring)\n  - [onSelectedItemChange](#onselecteditemchange)\n  - [stateReducer](#statereducer)\n- [Advanced Props](#advanced-props)\n  - [isItemDisabled](#isitemdisabled)\n  - [initialSelectedItem](#initialselecteditem)\n  - [initialIsOpen](#initialisopen)\n  - [initialHighlightedIndex](#initialhighlightedindex)\n  - [defaultSelectedItem](#defaultselecteditem)\n  - [defaultIsOpen](#defaultisopen)\n  - [defaultHighlightedIndex](#defaulthighlightedindex)\n  - [itemToKey](#itemtokey)\n  - [getA11yStatusMessage](#geta11ystatusmessage)\n  - [onHighlightedIndexChange](#onhighlightedindexchange)\n  - [onIsOpenChange](#onisopenchange)\n  - [onStateChange](#onstatechange)\n  - [highlightedIndex](#highlightedindex)\n  - [isOpen](#isopen)\n  - [selectedItem](#selecteditem)\n  - [id](#id)\n  - [labelId](#labelid)\n  - [menuId](#menuid)\n  - [toggleButtonId](#togglebuttonid)\n  - [getItemId](#getitemid)\n  - [environment](#environment)\n- [stateChangeTypes](#statechangetypes)\n- [Control Props](#control-props)\n- [Returned props](#returned-props)\n  - [prop getters](#prop-getters)\n  - [actions](#actions)\n  - [state](#state)\n- [Event Handlers](#event-handlers)\n  - [Default handlers](#default-handlers)\n  - [Customizing Handlers](#customizing-handlers)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Usage\n\n> [Try it out in the browser][sandbox-example]\n\n```jsx\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useSelect} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nfunction DropdownSelect() {\n  const {\n    isOpen,\n    selectedItem,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    highlightedIndex,\n    getItemProps,\n  } = useSelect({items: colors})\n\n  return (\n    <div\n      style={{\n        display: 'flex',\n        flexDirection: 'column',\n        width: 'fit-content',\n        justifyContent: 'center',\n        marginTop: 100,\n        alignSelf: 'center',\n      }}\n    >\n      <label\n        style={{\n          fontWeight: 'bolder',\n          color: selectedItem ? selectedItem : 'black',\n        }}\n        {...getLabelProps()}\n      >\n        Choose an element:\n      </label>\n      <div\n        style={{\n          padding: '4px',\n          textAlign: 'center',\n          border: '1px solid black',\n          backgroundColor: 'lightgray',\n          cursor: 'pointer',\n        }}\n        {...getToggleButtonProps()}\n      >\n        {selectedItem ?? 'Elements'}\n        {isOpen ? <>&#8593;</> : <>&#8595;</>}\n      </div>\n      <ul\n        {...getMenuProps()}\n        style={{\n          listStyle: 'none',\n          width: '100%',\n          padding: '0',\n          margin: '4px 0 0 0',\n        }}\n      >\n        {isOpen &&\n          colors.map((item, index) => (\n            <li\n              style={{\n                padding: '4px',\n                backgroundColor: highlightedIndex === index ? '#bde4ff' : null,\n              }}\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n\nrender(<DropdownSelect />, document.getElementById('root'))\n```\n\n## Basic Props\n\nThis is the list of props that you should probably know about. There are some\n[advanced props](#advanced-props) below as well.\n\n### items\n\n> `any[]` | _required_\n\nThe main difference from vanilla `Downshift` is that we pass the items we want\nto render to the hook as well. Opening the menu with an item already selected\nmeans the hook has to know in advance what items you plan to render and what is\nthe position of that item in the list. Consequently, there won't be any need for\ntwo state changes: one for opening the menu and one for setting the highlighted\nindex, like in `Downshift`.\n\n### itemToString\n\n> `function(item: any)` | defaults to: `item => (item ? String(item) : '')`\n\nIf your items are stored as, say, objects instead of strings, downshift still\nneeds a string representation for each one. This is required for accessibility\naria-live messages (e.g., after making a selection) and for keyboard interaction\nfeatures (e.g., highlighting an item by typing its first few characters).\n\n### onSelectedItemChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the selected item was changed. Selection can be performed by\nitem click, Enter or Space Key while item is highlighted or by using character\nkeys while the toggle button is focused and menu is closed.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `selectedItem` property\n  with the newly selected value. This also has a `type` property which you can\n  learn more about in the [`stateChangeTypes`](#statechangetypes) section. This\n  property will be part of the actions that can trigger a `selectedItem` change,\n  for example `useSelect.stateChangeTypes.ItemClick`.\n\n### stateReducer\n\n> `function(state: object, actionAndChanges: object)` | optional\n\n**🚨 This is a really handy power feature 🚨**\n\nThis function will be called each time `useSelect` sets its internal state (or\ncalls your `onStateChange` handler for control props). It allows you to modify\nthe state change that will take place which can give you fine grain control over\nhow the component interacts with user updates. It gives you the current state\nand the state that will be set, and you return the state that you want to set.\n\n- `state`: The full current state of downshift.\n- `actionAndChanges`: Object that contains the action `type`, props needed to\n  return a new state based on that type and the changes suggested by the\n  Downshift default reducer. About the `type` property you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n```javascript\nimport {useSelect} from 'downshift'\nimport {items} from './utils'\n\nconst {getMenuProps, getItemProps, ...rest} = useSelect({\n  items,\n  stateReducer,\n})\n\nfunction stateReducer(state, actionAndChanges) {\n  const {type, changes} = actionAndChanges\n  // this prevents the menu from being closed when the user selects an item with 'Enter' or mouse\n  switch (type) {\n    case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n    case useSelect.stateChangeTypes.ItemClick:\n      return {\n        ...changes, // default Downshift new state changes on item selection.\n        isOpen: state.isOpen, // but keep menu open.\n        highlightedIndex: state.highlightedIndex, // with the item highlighted.\n      }\n    default:\n      return changes // otherwise business as usual.\n  }\n}\n```\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example `<button onBlur={handleBlur} />` should be\n> `<button {...getToggleButtonProps({onBlur: handleBlur})} />`). Also, your\n> reducer function should be \"pure.\" This means it should do nothing other than\n> return the state changes you want to have happen.\n\n## Advanced Props\n\n### isItemDisabled\n\n> `function(item: any, index: number)` | defaults to: `(_item, _index) => false`\n\nIf an item needs to be marked as disabled, this function needs to return `true`\nfor that item. Disabled items will be skipped from keyboard navigation, will not\nbe selected and will be marked as disabled for screen readers.\n\n### initialSelectedItem\n\n> `any` | defaults to `null`\n\nPass an item that should be selected when downshift is initialized.\n\n### initialIsOpen\n\n> `boolean` | defaults to `false`\n\nPass a boolean that sets the open state of the menu when downshift is\ninitialized.\n\n### initialHighlightedIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the highlighted item when downshift is\ninitialized.\n\n### defaultSelectedItem\n\n> `any` | defaults to `null`\n\nPass an item that should be selected when downshift is reset.\n\n### defaultIsOpen\n\n> `boolean` | defaults to `false`\n\nPass a boolean that sets the open state of the menu when downshift is reset or\nwhen an item is selected.\n\n### defaultHighlightedIndex\n\n> `number` | defaults to `-1`\n\nPass a number that sets the index of the highlighted item when downshift is\nreset or when an item is selected.\n\n### itemToKey\n\n> `function(item: any)` | defaults to: `item => item`\n\nUsed to determine the uniqueness of an item when searching for the item or\ncomparing the item with another. Returns the item itself, by default, so the\ncomparing/searching is done internally via referential equality.\n\nIf using items as objects and their reference will change during use, you can\nuse the function to generate a unique key for each item, such as an `id` prop.\n\n```js\n// initial items.\nconst items = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n// the same items but with different references, for any reason.\nconst newItems = [\n  {id: 1, value: 'Apples'},\n  {id: 2, value: 'Oranges'},\n]\n\nfunction itemToKey(item) {\n  return item.id\n  // and we will do the comparison like: const isChanged = itemToKey(prevSelectedItem) !== itemToKey(nextSelectedItem)\n}\n```\n\n### getA11yStatusMessage\n\n> `function({/* see below */})` | default messages provided in English\n\nThis function is passed as props to a status updating function nested within\nthat allows you to create your own ARIA statuses. It is called when the state\nchanges: `selectedItem`, `highlightedIndex`, `inputValue` or `isOpen`.\n\nThere is no default function provided anymore since v9, so if there's no prop\npassed, no aria live status message is created. An implementation that resembles\nthe previous default is written below, should you want to keep pre v9 behaviour.\n\nWe don't provide this as a default anymore since we consider that screen readers\nhave been significantly improved and they can convey information about items\ncount, possible actions and highlighted items only from the HTML markup, without\nthe need for aria-live regions.\n\n```js\nfunction getA11yStatusMessage(state) {\n  if (!state.isOpen) {\n    return ''\n  }\n  // you need to get resultCount and previousResultCount yourself now, since we don't pass them as arguments anymore\n  const resultCount = items.length\n  const previousResultCount = previousResultCountRef.current\n\n  if (!resultCount) {\n    return 'No results are available.'\n  }\n\n  if (resultCount !== previousResultCount) {\n    return `${resultCount} result${\n      resultCount === 1 ? ' is' : 's are'\n    } available, use up and down arrow keys to navigate. Press Enter or Space Bar keys to select.`\n  }\n\n  return ''\n}\n```\n\n### onHighlightedIndexChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the highlighted item was changed. Items can be highlighted\nwhile hovering the mouse over them or by keyboard keys such as Up Arrow, Down\nArrow, Home and End. Items can also be highlighted by hitting character keys\nthat are part of their starting string equivalent.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `highlightedIndex`\n  property with the new value. This also has a `type` property which you can\n  learn more about in the [`stateChangeTypes`](#statechangetypes) section. This\n  property will be part of the actions that can trigger a `highlightedIndex`\n  change, for example `useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp`.\n\n### onIsOpenChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the menu is open or closed. Menu can be open by toggle button\nclick, Enter, Space, Up Arrow or Down Arrow keys. Can be closed by selecting an\nitem, blur (Tab, Shift-Tab or clicking outside), clicking the toggle button\nagain or hitting Escape key.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `isOpen` property with\n  the new value. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section. This property will be\n  part of the actions that can trigger a `isOpen` change, for example\n  `useSelect.stateChangeTypes.ToggleButtonClick`.\n\n### onStateChange\n\n> `function(changes: object)` | optional, no useful default\n\nThis function is called anytime the internal state changes. This can be useful\nif you're using downshift as a \"controlled\" component, where you manage some or\nall of the state (e.g., isOpen, selectedItem, highlightedIndex, etc) and then\npass it as props, rather than letting downshift control all its state itself.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n> Tip: This function will be called any time _any_ state is changed. The best\n> way to determine whether any particular state was changed, you can use\n> `changes.hasOwnProperty('propName')` or use the `on[statePropKey]Change` props\n> described above.\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<button onBlur={handleBlur} />` should be\n> `<button {...getToggleButtonProps({onBlur: handleBlur})} />`).\n\n### highlightedIndex\n\n> `number` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe index of the item that should be highlighted when menu is open.\n\n### isOpen\n\n> `boolean` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe open state of the menu.\n\n### selectedItem\n\n> `any` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe item that should be selected.\n\n### id\n\n> `string` | defaults to a generated ID\n\nUsed to generate the first part of the `Downshift` id on the elements. You can\noverride this `id` with one of your own, provided as a prop, or you can override\nthe `id` for each element altogether using the props below.\n\n### labelId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`label`) you use\n[`getLabelProps`](#getlabelprops) with.\n\n### menuId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`ul`) you use\n[`getMenuProps`](#getmenuprops) with.\n\n### toggleButtonId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element (`button`) you use\n[`getToggleButtonProps`](#gettogglebuttonprops) with.\n\n### getItemId\n\n> `function(index)` | defaults to a function that generates an ID based on the\n> index\n\nUsed for `aria` attributes and the `id` prop of the element (`li`) you use\n[`getItemProps`](#getitemprops) with.\n\n### environment\n\n> `window` | defaults to `window`\n\nThis prop is only useful if you're rendering downshift within a different\n`window` context from where your JavaScript is running; for example, an iframe\nor a shadow-root. If the given context is lacking `document` and/or\n`add|removeEventListener` on its prototype (as is the case for a shadow-root)\nthen you will need to pass in a custom object that is able to provide\n[access to these properties](https://gist.github.com/Rendez/1dd55882e9b850dd3990feefc9d6e177)\nfor downshift.\n\n## stateChangeTypes\n\nThere are a few props that expose changes to state\n([`onStateChange`](#onstatechange) and [`stateReducer`](#statereducer)). For you\nto make the most of these APIs, it's important for you to understand why state\nis being changed. To accomplish this, there's a `type` property on the `changes`\nobject you get. This `type` corresponds to a `stateChangeTypes` property.\n\nThe list of all possible values this `type` property can take is defined in\n[this file][state-change-file] and is as follows:\n\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownArrowDown`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownArrowUp`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownEscape`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownHome`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownEnd`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownPageUp`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownPageDown`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownEnter`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton`\n- `useSelect.stateChangeTypes.ToggleButtonKeyDownCharacter`\n- `useSelect.stateChangeTypes.ToggleButtonBlur`\n- `useSelect.stateChangeTypes.ToggleButtonClick`\n- `useSelect.stateChangeTypes.MenuMouseLeave`\n- `useSelect.stateChangeTypes.ItemMouseMove`\n- `useSelect.stateChangeTypes.ItemClick`\n- `useSelect.stateChangeTypes.FunctionToggleMenu`\n- `useSelect.stateChangeTypes.FunctionOpenMenu`\n- `useSelect.stateChangeTypes.FunctionCloseMenu`\n- `useSelect.stateChangeTypes.FunctionSetHighlightedIndex`\n- `useSelect.stateChangeTypes.FunctionSelectItem`\n- `useSelect.stateChangeTypes.FunctionSetInputValue`\n- `useSelect.stateChangeTypes.FunctionReset`\n\nSee [`stateReducer`](#statereducer) for a concrete example on how to use the\n`type` property.\n\n## Control Props\n\nDownshift manages its own state internally and calls your\n`onSelectedItemChange`, `onIsOpenChange`, `onHighlightedIndexChange`,\n`onInputChange` and `onStateChange` handlers with any relevant changes. The\nstate that downshift manages includes: `isOpen`, `selectedItem`, `inputValue`\nand `highlightedIndex`. Returned action function (read more below) can be used\nto manipulate this state and can likely support many of your use cases.\n\nHowever, if more control is needed, you can pass any of these pieces of state as\na prop (as indicated above) and that state becomes controlled. As soon as\n`this.props[statePropKey] !== undefined`, internally, `downshift` will determine\nits state based on your prop's value rather than its own internal state. You\nwill be required to keep the state up to date (this is where `onStateChange`\ncomes in really handy), but you can also control the state from anywhere, be\nthat state from other components, `redux`, `react-router`, or anywhere else.\n\n> Note: This is very similar to how normal controlled components work elsewhere\n> in react (like `<input />`). If you want to learn more about this concept, you\n> can learn about that from the [Advanced React Component Patterns\n> course][advanced-react-component-patterns-course]\n\n## Returned props\n\nYou use the hook like so:\n\n```javascript\nimport {useSelect} from 'downshift'\nimport {items} from './utils'\n\nconst {getToggleButtonProps, reset, ...rest} = useSelect({\n  items,\n  ...otherProps,\n})\n\nreturn (\n  <div>\n    <div {...getToggleButtonProps()}>Options</div>\n    {/* render the menu and items */}\n    {/* render a button that resets the select to defaults */}\n    <button\n      onClick={() => {\n        reset()\n      }}\n    >\n      Reset\n    </button>\n  </div>\n)\n```\n\n> NOTE: In this example we used both a getter prop `getToggleButtonProps` and an\n> action prop `reset`. The properties of `useSelect` can be split into three\n> categories as indicated below:\n\n### prop getters\n\n> See [the blog post about prop getters][blog-post-prop-getters]\n\n> NOTE: These prop-getters provide `aria-` attributes which are very important\n> to your component being accessible. It's recommended that you utilize these\n> functions and apply the props they give you to your components.\n\nThese functions are used to apply props to the elements that you render. This\ngives you maximum flexibility to render what, when, and wherever you like. You\ncall these on the element in question, for example on the toggle button:\n`<button {...getToggleButtonProps()}`. It's advisable to pass all your props to\nthat function rather than applying them on the element yourself to avoid your\nprops being overridden (or overriding the props returned). For example:\n`getToggleButtonProps({onKeyDown(event) {console.log(event)}})`.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property               | type           | description                                                                                                                                     |\n| ---------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |\n| `getToggleButtonProps` | `function({})` | returns the props you should apply to the trigger element you render. Since it will receive the role of `combobox`, we chose a `<div>` element. |\n| `getItemProps`         | `function({})` | returns the props you should apply to any menu item elements you render.                                                                        |\n| `getLabelProps`        | `function({})` | returns the props you should apply to the `label` element that you render.                                                                      |\n| `getMenuProps`         | `function({})` | returns the props you should apply to the `ul` element (or root of your menu) that you render.                                                  |\n\n#### `getLabelProps`\n\nThis method should be applied to the `label` you render. It will generate an\n`id` that will be used to label the toggle button and the menu.\n\nThere are no required properties for this method.\n\n> Note: For accessibility purposes, calling this method is highly recommended.\n\n#### `getMenuProps`\n\nThis method should be applied to the element which contains your list of items.\nTypically, this will be a `<div>` or a `<ul>` that surrounds a `map` expression.\nThis handles the proper ARIA roles and attributes.\n\nOptional properties:\n\n- `ref`: if you need to access the menu element via a ref object, you'd call the\n  function like this: `getMenuProps({ref: yourMenuRef})`. As a result, the menu\n  element will receive a composed `ref` property, which guarantees that both\n  your code and `useSelect` use the same correct reference to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getMenuProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<ul ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n  Please keep in mind that menus, for accessiblity purposes, should always be\n  rendered, regardless of whether you hide it or not. Otherwise, `getMenuProps`\n  may throw error if you unmount and remount the menu.\n\n- `aria-label`: By default the menu will add an `aria-labelledby` that refers to\n  the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getMenuProps`. **Please use it with extreme care and only if you are absolutely\nsure that the ref is correctly forwarded otherwise `useSelect` will unexpectedly\nfail.**\n\n```jsx\nconst {getMenuProps} = useSelect({items})\nconst ui = (\n  <ul {...getMenuProps()}>\n    {!isOpen\n      ? null\n      : items.map((item, index) => (\n          <li {...getItemProps({item, index, key: item.id})}>{item.name}</li>\n        ))}\n  </ul>\n)\n```\n\n> Note that for accessibility reasons it's best if you always render this\n> element whether or not downshift is in an `isOpen` state.\n\n#### `getItemProps`\n\nThe props returned from calling this function should be applied to any menu\nitems you render.\n\n**This is an impure function**, so it should only be called when you will\nactually be applying the props to an item.\n\n<details>\n\n<summary>What do you mean by impure function?</summary>\n\nBasically just don't do this:\n\n```jsx\nitems.map((item, index) => {\n  const props = getItemProps({item, index}) // we're calling it here\n  if (!shouldRenderItem(item)) {\n    return null // but we're not using props, and downshift thinks we are...\n  }\n  return <div {...props} />\n})\n```\n\nInstead, you could do this:\n\n```jsx\nitems.filter(shouldRenderItem).map(item => <div {...getItemProps({item})} />)\n```\n\n</details>\n\nRequired properties:\n\nThe main difference from vanilla `Downshift` is that we require the items as\nprops before rendering. The reason is to open the menu with items already\nhighlighted, and we need to know the items before the actual render. It is still\nrequired to pass either `item` or `index` to `getItemProps`.\n\n- `item`: this is the item data that will be selected when the user selects a\n  particular item.\n- `index`: This is how `downshift` keeps track of your item when updating the\n  `highlightedIndex` as the user keys around. By default, `downshift` will\n  assume the `index` is the order in which you're calling `getItemProps`. This\n  is often good enough, but if you find odd behavior, try setting this\n  explicitly. It's probably best to be explicit about `index` when using a\n  windowing library like `react-virtualized`.\n\nOptional properties:\n\n- `ref`: if you need to access the item element via a ref object, you'd call the\n  function like this: `getItemProps({ref: yourItemRef})`. As a result, the item\n  element will receive a composed `ref` property, which guarantees that both\n  your code and `useSelect` use the same correct reference to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getItemProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<li ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\n#### `getToggleButtonProps`\n\nCall this and apply the returned props to a `div` element that will act as a\ntriggering button. Since ARIA 1.2, the trigger element will reiceve a `combobox`\nrole, so a `<button>` is not considered correct markup anymore. It allows you to\ntoggle the `Menu` component.\n\nOptional properties:\n\n- `disabled`: If this is set to `true`, then all of the downshift button event\n  handlers will be omitted (it won't toggle the menu when clicked).\n\n- `ref`: if you need to access the button element via a ref object, you'd call\n  the function like this: `getToggleButtonProps({ref: yourToggleButtonRef})`. As\n  a result, the button element will receive a composed `ref` property, which\n  guarantees that both your code and `useSelect` use the same correct reference\n  to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getToggleButton({refKey: 'innerRef'})`\n  and your composite component would forward like:\n  `<button ref={props.innerRef} />`. However, if you are just rendering a\n  primitive component like `<div>`, there is no need to specify this property.\n  It defaults to `ref`.\n\n- `aria-label`: By default the toggle element will add an `aria-labelledby` that\n  refers to the `<label>` rendered with `getLabelProps`. However, if you provide\n  `aria-label` to give a more specific label that describes the options\n  available, then `aria-labelledby` will not be provided and screen readers can\n  use your `aria-label` instead.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getToggleButtonProps`. **Please use it with extreme care and only if you are\nabsolutely sure that the ref is correctly forwarded otherwise `useSelect` will\nunexpectedly fail.**\n\n```jsx\nconst {getToggleButtonProps} = useSelect({items})\nconst myButton = (\n  <button {...getToggleButtonProps()}>Click me</button>\n  {/* menu and items */}\n)\n```\n\n### actions\n\nThese are functions you can call to change the state of the downshift\n`useSelect` hook.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property              | type                      | description                                           |\n| --------------------- | ------------------------- | ----------------------------------------------------- |\n| `closeMenu`           | `function()`              | closes the menu                                       |\n| `openMenu`            | `function()`              | opens the menu                                        |\n| `selectItem`          | `function(item: any)`     | selects the given item                                |\n| `setHighlightedIndex` | `function(index: number)` | call to set a new highlighted index                   |\n| `toggleMenu`          | `function()`              | toggle the menu open state                            |\n| `reset`               | `function()`              | this resets downshift's state to a reasonable default |\n\n### state\n\nThese are values that represent the current state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property           | type      | description                       |\n| ------------------ | --------- | --------------------------------- |\n| `highlightedIndex` | `number`  | the currently highlighted item    |\n| `isOpen`           | `boolean` | the menu open state               |\n| `selectedItem`     | `any`     | the currently selected item input |\n| `keysSoFar`        | `string`  | the character keys typed so far   |\n\n## Event Handlers\n\nDownshift has a few events for which it provides implicit handlers. Several of\nthese handlers call `event.preventDefault()`. Their additional functionality is\ndescribed below.\n\n### Default handlers\n\n#### Toggle Button\n\n- `Click`: If the menu is not displayed, it will open it. Otherwise it will\n  close it. If there is already an item selected, the menu will be opened with\n  that item already highlighted.\n- `Enter`: If the menu is closed, opens the menu. If the menu is opened and\n  there is an item highlighted, it will select that item.\n- `Space`: If the menu is closed, opens the menu. If the menu is opened and\n  there is an item highlighted, it will select that item. If the user has typed\n  character keys before pressing `Space`, the space character will concatenate\n  to the search query. This allows search for options such as `Republic of ..`.\n- `CharacterKey`: Opens the menu if closed and highlights the first option that\n  starts with that key. For instance, typing `C` will select the option that\n  starts with `C`. Pressing `C` again will move the highlight to the next item\n  that starts with `C`. Typing keys into rapid succession (in less than 500ms\n  each) will select the option starting with that key combination, for instance\n  typing `CAL` will select `californium` if this option exists.\n- `ArrowDown`: If the menu is closed, it will open it. If there is already an\n  item selected, it will open the menu with the selected item highlighted.\n  Otherwise, it will open the menu with the first option highlighted. If the\n  menu is already open, it will highlight the next item.\n- `ArrowUp`: If the menu is closed, it will open it. If there is already an item\n  selected, it will open the menu with the selected item highlighted. Otherwise,\n  it will open the menu with the last option highlighted. If the menu is already\n  open, it will highlight the previous item.\n- `Alt+ArrowDown`: If the menu is closed, it will open it, without highlighting\n  any item.\n- `Alt+ArrowUp`: If the menu is open, it will close it and will select the item\n  that was highlighted.\n- `End`: If the menu is closed, it will open it. It will also highlight the last\n  item in the list.\n- `Home`: If the menu is closed, it will open it. It will also highlight the\n  first item in the list.\n- `PageUp`: If the menu is open, it will move the highlight the item 10\n  positions before the current selection.\n- `PageDown`: If the menu is open, it will move the highlight the item 10\n  positions after the current selection.\n- `Escape`: It will close the menu without selecting anything and keeps focus to\n  the toggle button.\n- `Blur(Tab, Shift+Tab, MouseClick outside)`: It will close the menu will select\n  the highlighted item if any. Focus is handled naturally (next / previous\n  elemenent in the tab order, body element if click outside.).\n\n#### Menu\n\n- `MouseLeave`: Will clear the value of the `highlightedIndex` if it was set.\n\n#### Item\n\n- `Click`: It will select the item and close the menu.\n- `MouseOver`: It will highlight the item.\n\n#### Label\n\n- `Click`: It will move focus to the toggle element.\n\n### Customizing Handlers\n\nYou can provide your own event handlers to `useSelect` which will be called\nbefore the default handlers:\n\n```javascript\nconst items = [...] // items here.\nconst {getMenuProps} = useSelect({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n      },\n    })}\n  />\n)\n```\n\nIf you would like to prevent the default handler behavior in some cases, you can\nset the event's `preventDownshiftDefault` property to `true`:\n\n```javascript\nconst {getMenuProps} = useSelect({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n        if (event.key === 'Enter') {\n          // Prevent Downshift's default 'Enter' behavior.\n          event.nativeEvent.preventDownshiftDefault = true\n\n          // your handler code\n        }\n      },\n    })}\n  />\n)\n```\n\nIf you would like to completely override Downshift's behavior for a handler, in\nfavor of your own, you can bypass prop getters:\n\n```javascript\nconst items = [...] // items here.\nconst {getMenuProps} = useSelect({items})\nconst ui = (\n  /* button, label, ... */\n  <ul\n    {...getMenuProps()}\n    onKeyDown={event => {\n      // your custom keyDown handler here.\n    }}\n  />\n)\n```\n\n## Examples\n\nUsage examples are kept on the [downshift docsite][docsite] and also on [the\nsandbox repo][sandbox-repo]. Each example has a link to its own Codesandbox\nversion, so check the docs.\n\nIt can be a great contributing opportunity to provide relevant use cases as\ndocsite examples. If you have such an example, please create an issue with the\nsuggestion and the Codesandbox for it, and we will take it from there.\n\n[select-aria]:\n  https://w3c.github.io/aria-practices/examples/combobox/combobox-select-only.html\n[sandbox-example]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseSelect%2Fbasic-usage.js&moduleview=1\n[state-change-file]:\n  https://github.com/downshift-js/downshift/blob/master/src/hooks/useSelect/stateChangeTypes.js\n[blog-post-prop-getters]:\n  https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf\n[docsite]: https://downshift-js.com/\n[sandbox-repo]: https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1\n[advanced-react-component-patterns-course]:\n  https://github.com/downshift-js/downshift#advanced-react-component-patterns-course\n[migration-guide-v7]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V7.md#useselect\n[migration-guide-v8]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V8.md\n[migration-guide-v9]:\n  https://github.com/downshift-js/downshift/tree/master/src/hooks/MIGRATION_V9.md\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/__snapshots__/getToggleButtonProps.test.js.snap",
    "content": "// Jest Snapshot v1, https://goo.gl/fbAQLP\n\nexports[`getToggleButtonProps event handlers on keydown arrow down defaultHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Americium,\n    2,\n  ],\n  [\n    Neptunium,\n    0,\n  ],\n]\n`;\n\nexports[`getToggleButtonProps event handlers on keydown arrow down initialHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Neptunium,\n    0,\n  ],\n]\n`;\n\nexports[`getToggleButtonProps event handlers on keydown arrow up defaultHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Americium,\n    2,\n  ],\n  [\n    Oganesson,\n    25,\n  ],\n]\n`;\n\nexports[`getToggleButtonProps event handlers on keydown arrow up initialHighlightedIndex is ignored if item is disabled 1`] = `\n[\n  [\n    Americium,\n    2,\n  ],\n  [\n    Oganesson,\n    25,\n  ],\n]\n`;\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/getItemProps.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport {\n  renderUseSelect,\n  renderSelect,\n  mouseMoveItemAtIndex,\n  getItemAtIndex,\n  getToggleButton,\n  clickOnItemAtIndex,\n  getItems,\n  keyDownOnToggleButton,\n  clickOnToggleButton,\n  items,\n  defaultIds,\n} from '../testUtils'\nimport useSelect from '..'\n\ndescribe('getItemProps', () => {\n  test('throws error if no index or item has been passed', () => {\n    const {result} = renderUseSelect()\n\n    expect(result.current.getItemProps).toThrow(\n      'Pass either item or index to getItemProps!',\n    )\n  })\n\n  describe('hook props', () => {\n    test(\"assign 'option' to role\", () => {\n      const {result} = renderUseSelect()\n\n      expect(result.current.getItemProps({index: 0}).role).toEqual('option')\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseSelect()\n\n      expect(result.current.getItemProps({index: 0}).id).toEqual(\n        `${defaultIds.getItemId(0)}`,\n      )\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const getItemId = index => `my-custom-item-id-${index}`\n      const {result} = renderUseSelect({getItemId})\n\n      expect(result.current.getItemProps({index: 0}).id).toEqual(getItemId(0))\n    })\n\n    test(\"assign 'true' to aria-selected if item is selected\", () => {\n      const {result} = renderUseSelect({\n        highlightedIndex: 2,\n        selectedItem: items[3],\n      })\n      const item2Props = result.current.getItemProps({index: 2})\n      const item3Props = result.current.getItemProps({index: 3})\n\n      expect(item2Props['aria-selected']).toEqual(false)\n      expect(item3Props['aria-selected']).toEqual(true)\n    })\n\n    test(\"assign 'false' to aria-selected if item is not highlighted\", () => {\n      const {result} = renderUseSelect({highlightedIndex: 1})\n      const itemProps = result.current.getItemProps({index: 2})\n\n      expect(itemProps['aria-selected']).toEqual(false)\n    })\n\n    test('omit click handler when disabled', () => {\n      const {result} = renderUseSelect({\n        isItemDisabled(_item, index) {\n          return index === 0\n        },\n      })\n      const itemProps = result.current.getItemProps({\n        index: 0,\n      })\n\n      expect(itemProps.onMouseMove).toBeDefined()\n      expect(itemProps.onClick).toBeUndefined()\n      expect(itemProps['aria-disabled']).toBe(true)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseSelect()\n\n      expect(\n        result.current.getItemProps({index: 0, foo: 'bar'}),\n      ).toHaveProperty('foo', 'bar')\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onClick} = result.current.getItemProps({\n          index: 0,\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n      expect(result.current.selectedItem).not.toBeNull()\n    })\n\n    test('event handler onMouseMove is called along with downshift handler', () => {\n      const userOnMouseMove = jest.fn()\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseMove} = result.current.getItemProps({\n          index: 1,\n          onMouseMove: userOnMouseMove,\n        })\n\n        onMouseMove({})\n      })\n\n      expect(userOnMouseMove).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(1)\n    })\n\n    test('event handler onMouseDown is called along with downshift handler', () => {\n      const userOnMouseDown = jest.fn()\n      const preventDefault = jest.fn()\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseDown} = result.current.getItemProps({\n          index: 1,\n          onMouseDown: userOnMouseDown,\n        })\n\n        onMouseDown({preventDefault})\n      })\n\n      expect(userOnMouseDown).toHaveBeenCalledTimes(1)\n      expect(preventDefault).toHaveBeenCalledTimes(1)\n    })\n\n    test(\"event handler onMouseDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnMouseDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const preventDefault = jest.fn()\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseDown} = result.current.getItemProps({\n          index: 0,\n          onMouseDown: userOnMouseDown,\n        })\n\n        onMouseDown({preventDefault})\n      })\n\n      expect(userOnMouseDown).toHaveBeenCalledTimes(1)\n      expect(preventDefault).not.toHaveBeenCalled()\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onClick} = result.current.getItemProps({\n          index: 0,\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n      expect(result.current.selectedItem).toBeNull()\n    })\n\n    test(\"event handler onMouseMove is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnMouseMove = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onMouseMove} = result.current.getItemProps({\n          index: 1,\n          onMouseMove: userOnMouseMove,\n        })\n\n        onMouseMove({})\n      })\n\n      expect(userOnMouseMove).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on mouse over', () => {\n      test('it highlights the item', async () => {\n        const index = 1\n\n        renderSelect({\n          isOpen: true,\n        })\n\n        await mouseMoveItemAtIndex(index)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n      })\n\n      test('it highlights successive items item', async () => {\n        renderSelect({\n          isOpen: true,\n        })\n\n        await mouseMoveItemAtIndex(3)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(3),\n        )\n\n        await mouseMoveItemAtIndex(2)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(2),\n        )\n\n        await mouseMoveItemAtIndex(1)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(1),\n        )\n      })\n\n      test('it removes highlight from the previously highlighted item', async () => {\n        const index = 1\n        const previousIndex = 2\n        renderSelect({\n          isOpen: true,\n          initialHighlightedIndex: previousIndex,\n        })\n        const toggleButton = getToggleButton()\n\n        await mouseMoveItemAtIndex(index)\n\n        expect(toggleButton).not.toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(previousIndex),\n        )\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n      })\n\n      it('keeps highlight on multiple events', async () => {\n        const index = 1\n        renderSelect({\n          isOpen: true,\n        })\n\n        await mouseMoveItemAtIndex(index)\n        await mouseMoveItemAtIndex(index)\n        await mouseMoveItemAtIndex(index)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(index),\n        )\n      })\n\n      it('removes highlight from previous item even if current item is disabled', async () => {\n        const disabledIndex = 1\n        const highlightedIndex = 2\n\n        renderSelect({\n          items,\n          isOpen: true,\n          isItemDisabled(_item, index) {\n            return index === disabledIndex\n          },\n        })\n        const toggleButton = getToggleButton()\n\n        await mouseMoveItemAtIndex(highlightedIndex)\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n\n        await mouseMoveItemAtIndex(disabledIndex)\n        expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      // Test that we don't call the mouse move handler on mobile.\n      test('does not set highlight the item if a touch event ocurred', async () => {\n        let touchEndHandler\n        const index = 1\n\n        renderSelect({\n          isOpen: true,\n          environment: {\n            addEventListener: (name, handler) => {\n              // eslint-disable-next-line jest/no-conditional-in-test\n              if (name === 'touchend') {\n                touchEndHandler = handler\n              }\n            },\n            removeEventListener: () => {},\n            document: {\n              createElement: () => {},\n              getElementById: () => {},\n              activeElement: () => {},\n              body: {},\n            },\n            Node: () => {},\n          },\n        })\n\n        act(() => touchEndHandler({target: null}))\n        await mouseMoveItemAtIndex(index)\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n\n    describe('on click', () => {\n      test('it selects the item', async () => {\n        const index = 1\n        renderSelect({\n          initialIsOpen: true,\n          defaultHighlightedIndex: 3,\n        })\n\n        await clickOnItemAtIndex(index)\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(items[index])\n\n        await clickOnToggleButton()\n\n        expect(getItemAtIndex(index)).toHaveAttribute('aria-selected', 'true')\n      })\n\n      test('it selects the item and resets to user defined defaults', async () => {\n        const index = 1\n        renderSelect({\n          defaultIsOpen: true,\n          defaultHighlightedIndex: 2,\n        })\n        const toggleButton = getToggleButton()\n\n        await clickOnItemAtIndex(index)\n\n        expect(toggleButton).toHaveTextContent(items[index])\n        expect(getItems()).toHaveLength(items.length)\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(2),\n        )\n      })\n\n      test('it selects the item and resets to user defined defaults, but considers disabled status for an item', async () => {\n        const index = 1\n        const defaultHighlightedIndex = 2\n        renderSelect({\n          defaultIsOpen: true,\n          defaultHighlightedIndex,\n          isItemDisabled(_item, idx) {\n            return idx === defaultHighlightedIndex\n          },\n        })\n        const toggleButton = getToggleButton()\n\n        await clickOnItemAtIndex(index)\n\n        expect(toggleButton).toHaveTextContent(items[index])\n        expect(getItems()).toHaveLength(items.length)\n        expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n  })\n\n  describe('scrolling', () => {\n    test('is performed by the menu to the item if highlighted and not 100% visible', async () => {\n      const scrollIntoView = jest.fn()\n      renderSelect({\n        initialIsOpen: true,\n        scrollIntoView,\n      })\n\n      await keyDownOnToggleButton('{End}')\n\n      expect(scrollIntoView).toHaveBeenCalledTimes(1)\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeAll(() => {\n      jest.spyOn(console, 'warn').mockImplementation(() => {})\n    })\n\n    afterAll(() => {\n      jest.restoreAllMocks()\n    })\n\n    test('will be displayed if getInputProps is not called', () => {\n      renderHook(() => {\n        const {getItemProps} = useSelect({items})\n        getItemProps({disabled: true, index: 9})\n      })\n\n      expect(console.warn.mock.calls[0][0]).toMatchInlineSnapshot(\n        `Passing \"disabled\" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.`,\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/getLabelProps.test.js",
    "content": "import {screen} from '@testing-library/react'\nimport {renderSelect, renderUseSelect} from '../testUtils'\nimport {defaultIds, getToggleButton, user} from '../../testUtils'\n\ndescribe('getLabelProps', () => {\n  test('should have a default id assigned', () => {\n    const {result} = renderUseSelect()\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.id).toEqual(defaultIds.labelId)\n  })\n\n  test('should have custom id if set by the user', () => {\n    const props = {\n      labelId: 'my-custom-label-id',\n    }\n    const {result} = renderUseSelect(props)\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.id).toEqual(props.labelId)\n  })\n\n  test('should assign htmlFor with the value of the toggleButton id', () => {\n    const props = {\n      toggleButtonId: 'my-custom-toggle-button-id',\n    }\n    const {result} = renderUseSelect(props)\n    const labelProps = result.current.getLabelProps()\n\n    expect(labelProps.htmlFor).toEqual(props.toggleButtonId)\n  })\n\n  test('passes props downwards', () => {\n    const {result} = renderUseSelect()\n    const props = {foo: 'bar'}\n    const labelProps = result.current.getLabelProps(props)\n\n    expect(labelProps).toEqual(expect.objectContaining({foo: 'bar'}))\n  })\n\n  test('on click moves focus to the toggle button', async () => {\n    renderSelect()\n\n    await user.click(\n      screen.getByText('Choose an element:', {selector: 'label'}),\n    )\n\n    expect(getToggleButton()).toHaveFocus()\n  })\n\n  test('event handler onClick is called along with downshift handler', () => {\n    const userOnClick = jest.fn()\n    const mockToggleButton = {focus: jest.fn()}\n    const {result} = renderUseSelect()\n\n    const {onClick} = result.current.getLabelProps({\n      onClick: userOnClick,\n    })\n    const {ref} = result.current.getToggleButtonProps()\n    ref(mockToggleButton)\n\n    onClick({})\n\n    expect(userOnClick).toHaveBeenCalledTimes(1)\n    expect(mockToggleButton.focus).toHaveBeenCalledTimes(1)\n  })\n\n  test(\"the downshift handler is not called if 'preventDownshiftDefault' is passed in user event\", () => {\n    const userOnClick = jest.fn(event => {\n      event.preventDownshiftDefault = true\n    })\n    const mockToggleButton = {focus: jest.fn()}\n    const {result} = renderUseSelect()\n\n    const {onClick} = result.current.getLabelProps({\n      onClick: userOnClick,\n    })\n    const {ref} = result.current.getToggleButtonProps()\n    ref(mockToggleButton)\n\n    onClick({})\n\n    expect(userOnClick).toHaveBeenCalledTimes(1)\n    expect(mockToggleButton.focus).not.toHaveBeenCalled()\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/getMenuProps.test.js",
    "content": "import {renderHook, act} from '@testing-library/react'\nimport {\n  renderUseSelect,\n  renderSelect,\n  getToggleButton,\n  mouseLeaveItemAtIndex,\n  mouseMoveItemAtIndex,\n} from '../testUtils'\nimport {defaultIds, items} from '../../testUtils'\nimport utils from '../../utils'\nimport useSelect from '..'\n\ndescribe('getMenuProps', () => {\n  describe('hook props', () => {\n    test('assign default value to aria-labelledby', () => {\n      const {result} = renderUseSelect()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps['aria-labelledby']).toEqual(`${defaultIds.labelId}`)\n    })\n\n    test('assign custom value passed by user to aria-labelledby', () => {\n      const props = {\n        labelId: 'my-custom-label-id',\n      }\n      const {result} = renderUseSelect(props)\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps['aria-labelledby']).toEqual(`${props.labelId}`)\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseSelect()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.id).toEqual(`${defaultIds.menuId}`)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const props = {\n        menuId: 'my-custom-menu-id',\n      }\n      const {result} = renderUseSelect(props)\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.id).toEqual(`${props.menuId}`)\n    })\n\n    test(\"assign 'listbox' to role\", () => {\n      const {result} = renderUseSelect()\n      const menuProps = result.current.getMenuProps()\n\n      expect(menuProps.role).toEqual('listbox')\n    })\n\n    test(\"do not assign 'aria-labelledby' if it has aria-label\", () => {\n      const ariaLabel = 'not so fast'\n      const {result} = renderUseSelect()\n      const menuProps = result.current.getMenuProps({'aria-label': ariaLabel})\n\n      expect(menuProps['aria-labelledby']).toBeUndefined()\n      expect(menuProps['aria-label']).toBe(ariaLabel)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseSelect()\n\n      expect(result.current.getMenuProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('custom ref passed by the user is used', () => {\n      const {result} = renderUseSelect()\n      const refFn = jest.fn()\n      const menuNode = {}\n\n      act(() => {\n        const {ref} = result.current.getMenuProps({ref: refFn})\n\n        ref(menuNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(menuNode)\n    })\n\n    test('custom ref with custom name passed by the user is used', () => {\n      const {result} = renderUseSelect()\n      const refFn = jest.fn()\n      const menuNode = {}\n\n      act(() => {\n        const {blablaRef} = result.current.getMenuProps({\n          refKey: 'blablaRef',\n          blablaRef: refFn,\n        })\n\n        blablaRef(menuNode)\n      })\n\n      expect(refFn).toHaveBeenCalledTimes(1)\n      expect(refFn).toHaveBeenCalledWith(menuNode)\n    })\n\n    test('event handler onMouseLeave is called along with downshift handler', () => {\n      const userOnMouseLeave = jest.fn()\n      const {result} = renderUseSelect({\n        initialHighlightedIndex: 2,\n        initialIsOpen: true,\n      })\n\n      act(() => {\n        const {onMouseLeave} = result.current.getMenuProps({\n          onMouseLeave: userOnMouseLeave,\n        })\n\n        onMouseLeave({})\n      })\n\n      expect(userOnMouseLeave).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n\n    test(\"event handler onMouseLeave is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnMouseLeave = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect({\n        initialHighlightedIndex: 2,\n        initialIsOpen: true,\n      })\n\n      act(() => {\n        const {onMouseLeave} = result.current.getMenuProps({\n          onMouseLeave: userOnMouseLeave,\n        })\n\n        onMouseLeave({})\n      })\n\n      expect(userOnMouseLeave).toHaveBeenCalledTimes(1)\n      expect(result.current.highlightedIndex).toBe(2)\n    })\n  })\n\n  describe('event handlers', () => {\n    describe('on mouse leave', () => {\n      test('the highlightedIndex should be reset', async () => {\n        renderSelect({\n          initialIsOpen: true,\n        })\n        const itemIndex = 2\n\n        await mouseMoveItemAtIndex(itemIndex)\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(itemIndex),\n        )\n\n        await mouseLeaveItemAtIndex(itemIndex)\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n      })\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeEach(() => {\n      const {useGetterPropsCalledChecker} = jest.requireActual('../../utils')\n      jest\n        .spyOn(utils, 'useGetterPropsCalledChecker')\n        .mockImplementation(useGetterPropsCalledChecker)\n      jest.spyOn(console, 'error').mockImplementation(() => {})\n    })\n\n    test('will be displayed if getMenuProps is not called', () => {\n      renderHook(() => {\n        const {getToggleButtonProps} = useSelect({items})\n        getToggleButtonProps({}, {suppressRefError: true})\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: You forgot to call the getMenuProps getter function on your component / element.`,\n      )\n    })\n\n    test('will not be displayed if getMenuProps is not called on subsequent renders', () => {\n      let firstRender = true\n      const {rerender} = renderHook(() => {\n        const {getToggleButtonProps, getMenuProps} = useSelect({items})\n        getToggleButtonProps({}, {suppressRefError: true})\n\n        // eslint-disable-next-line jest/no-if, jest/no-conditional-in-test\n        if (firstRender) {\n          firstRender = false\n          getMenuProps({}, {suppressRefError: true})\n        }\n      })\n\n      rerender()\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will be displayed if element ref is not set and suppressRefError is false', () => {\n      renderHook(() => {\n        const {getMenuProps, getToggleButtonProps} = useSelect({\n          items,\n        })\n\n        getToggleButtonProps({}, {suppressRefError: true})\n        getMenuProps()\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: The ref prop \"ref\" from getMenuProps was not applied correctly on your element.`,\n      )\n    })\n\n    // this test will cover also the equivalent getToggleButtonProps case.\n    test('will not be displayed if element ref is not set and suppressRefError is true', () => {\n      renderHook(() => {\n        const {getMenuProps, getToggleButtonProps} = useSelect({\n          items,\n        })\n\n        getToggleButtonProps({}, {suppressRefError: true})\n        getMenuProps({}, {suppressRefError: true})\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will not be displayed if called with a correct ref', () => {\n      const refFn = jest.fn()\n      const menuNode = {}\n\n      renderHook(() => {\n        const {getToggleButtonProps, getMenuProps} = useSelect({\n          items,\n        })\n\n        getToggleButtonProps({}, {suppressRefError: true})\n\n        const {ref} = getMenuProps({\n          ref: refFn,\n        })\n        ref(menuNode)\n      })\n\n      // eslint-disable-next-line no-console\n      expect(console.error).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/getToggleButtonProps.test.js",
    "content": "import * as React from 'react'\nimport {\n  act,\n  createEvent,\n  fireEvent,\n  screen,\n  renderHook,\n} from '@testing-library/react'\nimport {noop} from '../../../utils-ts'\nimport {\n  renderUseSelect,\n  renderSelect,\n  getItemIndexByCharacter,\n  clickOnToggleButton,\n  getItems,\n  getToggleButton,\n  keyDownOnToggleButton,\n  tab,\n} from '../testUtils'\nimport utils from '../../utils'\nimport {\n  items,\n  defaultIds,\n  mouseMoveItemAtIndex,\n  mouseLeaveItemAtIndex,\n  initialFocusAndOpenTestCases,\n  initialNoFocusOrOpenTestCases,\n} from '../../testUtils'\nimport useSelect from '..'\nimport * as stateChangeTypes from '../stateChangeTypes'\n\ndescribe('getToggleButtonProps', () => {\n  describe('hook props', () => {\n    test('assign default value to aria-labelledby', () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-labelledby']).toEqual(defaultIds.labelId)\n    })\n\n    test('assign custom value passed by user to aria-labelledby', () => {\n      const props = {\n        labelId: 'my-custom-label-id',\n      }\n      const {result} = renderUseSelect(props)\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-labelledby']).toEqual(props.labelId)\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.id).toEqual(defaultIds.toggleButtonId)\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const props = {\n        toggleButtonId: 'my-custom-toggle-button-id',\n      }\n      const {result} = renderUseSelect(props)\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.id).toEqual(props.toggleButtonId)\n    })\n\n    test(\"assign 'listbox' to aria-haspopup\", () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-haspopup']).toEqual('listbox')\n    })\n\n    test(\"assign 'false' value to aria-expanded when menu is closed\", () => {\n      const {result} = renderUseSelect({isOpen: false})\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-expanded']).toEqual(false)\n    })\n\n    test(\"assign 'true' value to aria-expanded when menu is open\", () => {\n      const {result} = renderUseSelect({isOpen: true})\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-expanded']).toEqual(true)\n    })\n\n    test('omit event handlers when disabled', () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps({\n        disabled: true,\n      })\n\n      expect(toggleButtonProps.onClick).toBeUndefined()\n      expect(toggleButtonProps.onKeyDown).toBeUndefined()\n      expect(toggleButtonProps.disabled).toBe(true)\n    })\n\n    test('assigns the role of combobox, tabindex 0, aria-haspopup listbox and aria-controls pointing to the menu', () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps.role).toEqual('combobox')\n      expect(toggleButtonProps.tabIndex).toEqual(0)\n      expect(toggleButtonProps['aria-controls']).toEqual(defaultIds.menuId)\n      expect(toggleButtonProps['aria-haspopup']).toEqual('listbox')\n    })\n\n    test('assign id of highlighted item to aria-activedescendant if item is highlighted and menu is open', () => {\n      const {result} = renderUseSelect({highlightedIndex: 2, isOpen: true})\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-activedescendant']).toEqual(\n        defaultIds.getItemId(2),\n      )\n    })\n\n    test('do not assign aria-activedescendant if item is highlighted and menu is closed', () => {\n      const {result} = renderUseSelect({highlightedIndex: 2})\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-activedescendant']).toEqual('')\n    })\n\n    test('do not assign aria-activedescendant if no item is highlighted', () => {\n      const {result} = renderUseSelect()\n      const toggleButtonProps = result.current.getToggleButtonProps()\n\n      expect(toggleButtonProps['aria-activedescendant']).toEqual('')\n    })\n\n    test(\"do not assign 'aria-labelledby' if it has aria-label\", () => {\n      const ariaLabel = 'not so fast'\n      const {result} = renderUseSelect()\n      const menuProps = result.current.getToggleButtonProps({\n        'aria-label': ariaLabel,\n      })\n\n      expect(menuProps['aria-labelledby']).toBeUndefined()\n      expect(menuProps['aria-label']).toBe(ariaLabel)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseSelect()\n\n      expect(result.current.getToggleButtonProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseSelect()\n\n      act(() => {\n        const {onClick} = result.current.getToggleButtonProps({\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('event handler onKeyDown is called along with downshift handler', () => {\n      const userOnKeyDown = jest.fn()\n      const {result} = renderUseSelect()\n\n      act(() => {\n        const {onKeyDown} = result.current.getToggleButtonProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowDown', preventDefault: noop})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test(\"event handler onClick is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect()\n\n      act(() => {\n        const {onClick} = result.current.getToggleButtonProps({\n          onClick: userOnClick,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test(\"event handler onKeyDown is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnKeyDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect()\n\n      act(() => {\n        const {onKeyDown} = result.current.getToggleButtonProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({\n          key: 'ArrowDown',\n          preventDefault: noop,\n        })\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('event handler onBlur is called along with downshift handler', () => {\n      const userOnBlur = jest.fn()\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onBlur} = result.current.getToggleButtonProps({\n          onBlur: userOnBlur,\n        })\n\n        onBlur({})\n      })\n\n      expect(userOnBlur).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test(\"event handler onBlur is called without downshift handler if 'preventDownshiftDefault' is passed in user event\", () => {\n      const userOnBlur = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        const {onBlur} = result.current.getToggleButtonProps({\n          onBlur: userOnBlur,\n        })\n\n        onBlur({})\n      })\n\n      expect(userOnBlur).toHaveBeenCalledTimes(1)\n      expect(result.current.isOpen).toBe(true)\n    })\n  })\n\n  describe('initial focus', () => {\n    test.each(initialFocusAndOpenTestCases)(\n      'is grabbed when initialIsOpen: %s, defaultIsOpen: %s, props.isOpen: %s',\n      (initialIsOpen, defaultIsOpen, isOpen) => {\n        renderSelect({isOpen, defaultIsOpen, initialIsOpen})\n\n        expect(getToggleButton()).toHaveFocus()\n        expect(getItems()).toHaveLength(items.length)\n      },\n    )\n\n    test.each(initialNoFocusOrOpenTestCases)(\n      'is not grabbed when initialIsOpen: %s, defaultIsOpen: %s, props.isOpen: %s',\n      (initialIsOpen, defaultIsOpen, isOpen) => {\n        renderSelect({isOpen, defaultIsOpen, initialIsOpen})\n\n        expect(getToggleButton()).not.toHaveFocus()\n        expect(getItems()).toHaveLength(0)\n      },\n    )\n  })\n\n  describe('event handlers', () => {\n    describe('on click', () => {\n      test('opens the closed menu', async () => {\n        renderSelect()\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n      })\n\n      test('closes the open menu', async () => {\n        renderSelect({\n          initialIsOpen: true,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n      })\n\n      test('opens and closes menu at consecutive clicks', async () => {\n        renderSelect()\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(items.length)\n\n        await clickOnToggleButton()\n\n        expect(getItems()).toHaveLength(0)\n      })\n\n      test('opens the closed menu without any option highlighted', async () => {\n        renderSelect()\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('opens the closed menu with selected option highlighted', async () => {\n        const selectedIndex = 3\n        renderSelect({\n          initialSelectedItem: items[selectedIndex],\n        })\n        getToggleButton()\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(selectedIndex),\n        )\n      })\n\n      test('opens the closed menu at initialHighlightedIndex, but on first click only', async () => {\n        const initialHighlightedIndex = 3\n        renderSelect({\n          initialHighlightedIndex,\n        })\n        const toggleButton = getToggleButton()\n\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(initialHighlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('opens the closed menu at defaultHighlightedIndex, on every click', async () => {\n        const defaultHighlightedIndex = 3\n        renderSelect({\n          defaultHighlightedIndex,\n        })\n        const toggleButton = getToggleButton()\n\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('initialHighlightedIndex is ignored if item is disabled', async () => {\n        const initialHighlightedIndex = 2\n        const isItemDisabled = jest\n          .fn()\n          .mockImplementation(\n            item => items.indexOf(item) === initialHighlightedIndex,\n          )\n        renderSelect({\n          initialHighlightedIndex,\n          isItemDisabled,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n        expect(isItemDisabled).toHaveBeenNthCalledWith(\n          1,\n          items[initialHighlightedIndex],\n          initialHighlightedIndex,\n        )\n      })\n\n      test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n        const initialHighlightedIndex = 0\n        const defaultHighlightedIndex = 2\n        renderSelect({\n          initialHighlightedIndex,\n          defaultHighlightedIndex,\n          isItemDisabled(item) {\n            return items.indexOf(item) === initialHighlightedIndex\n          },\n        })\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(defaultHighlightedIndex),\n        )\n      })\n\n      test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n        const defaultHighlightedIndex = 2\n        const isItemDisabled = jest\n          .fn()\n          .mockImplementation(\n            item => items.indexOf(item) === defaultHighlightedIndex,\n          )\n        renderSelect({\n          defaultHighlightedIndex,\n          isItemDisabled,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n        expect(isItemDisabled).toHaveBeenNthCalledWith(\n          1,\n          items[defaultHighlightedIndex],\n          defaultHighlightedIndex,\n        )\n      })\n\n      test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n        const initialHighlightedIndex = 0\n        const defaultHighlightedIndex = 2\n        renderSelect({\n          initialHighlightedIndex,\n          defaultHighlightedIndex,\n          isItemDisabled(item) {\n            return [initialHighlightedIndex, defaultHighlightedIndex].includes(\n              items.indexOf(item),\n            )\n          },\n        })\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n      })\n\n      test('opens the closed menu at highlightedIndex from props, on every click', async () => {\n        const highlightedIndex = 3\n        renderSelect({\n          highlightedIndex,\n        })\n        const toggleButton = getToggleButton()\n\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n\n        await clickOnToggleButton()\n        await clickOnToggleButton()\n\n        expect(toggleButton).toHaveAttribute(\n          'aria-activedescendant',\n          defaultIds.getItemId(highlightedIndex),\n        )\n      })\n\n      test('opens the closed menu and keepns focus on the button', async () => {\n        renderSelect()\n        getToggleButton()\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveFocus()\n      })\n\n      test('closes the open menu and keeps focus on the toggle button', async () => {\n        renderSelect({\n          initialIsOpen: true,\n        })\n\n        await clickOnToggleButton()\n\n        expect(getToggleButton()).toHaveFocus()\n      })\n    })\n\n    describe('on keydown', () => {\n      describe('character key', () => {\n        beforeEach(() => jest.useFakeTimers())\n        afterEach(() => {\n          act(() => jest.runAllTimers())\n        })\n        afterAll(jest.useRealTimers)\n\n        test('should highlight the first item that starts with that key', async () => {\n          const char = 'c'\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(char),\n          )\n          renderSelect()\n\n          await keyDownOnToggleButton(char)\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should highlight the second item that starts with that key after typing it twice', async () => {\n          const char = 'c'\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(char, getItemIndexByCharacter(char) + 1),\n          )\n\n          renderSelect()\n\n          await keyDownOnToggleButton(char)\n          act(() => jest.runOnlyPendingTimers())\n          await keyDownOnToggleButton(char)\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should highlight the first item again if the items are depleated', async () => {\n          const char = 'b'\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(char),\n          )\n          renderSelect()\n\n          await keyDownOnToggleButton(char)\n          act(() => jest.runOnlyPendingTimers())\n          await keyDownOnToggleButton(char)\n          act(() => jest.runOnlyPendingTimers())\n          await keyDownOnToggleButton(char)\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should not highlight anything if no item starts with that key', async () => {\n          const char = 'x'\n          renderSelect()\n\n          await keyDownOnToggleButton(char)\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should highlight the first item that starts with the keys typed in rapid succession', async () => {\n          const chars = ['m', 'e']\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(chars.join('')),\n          )\n          renderSelect()\n\n          await keyDownOnToggleButton(chars[0])\n          act(() => jest.advanceTimersByTime(200))\n          await keyDownOnToggleButton(chars[1])\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should highlight the second item that starts with the keys typed in rapid succession, with first valid item initially highlighted', async () => {\n          const chars = ['m', 'e']\n          const firstValidItemIndex = getItemIndexByCharacter(chars.join(''))\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(chars.join(''), firstValidItemIndex + 1),\n          )\n          // Mendelevium is already highlighted before typing chars.\n          renderSelect({initialHighlightedIndex: firstValidItemIndex})\n\n          await keyDownOnToggleButton(chars[0])\n          act(() => jest.advanceTimersByTime(200))\n          await keyDownOnToggleButton(chars[1])\n\n          // highlight should go on Meitnerium which is the second in the list.\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should highlight the first item that starts with the keys typed in rapid succession, with first valid item initially highlighted, but made invalid after third character', async () => {\n          const chars = ['m', 'e', 'n']\n          const firstValidItemIndex = getItemIndexByCharacter(chars.join(''))\n          const expectedItemId = defaultIds.getItemId(firstValidItemIndex)\n          // Mendelevium is already highlighted before typing chars.\n          renderSelect({initialHighlightedIndex: firstValidItemIndex})\n\n          await keyDownOnToggleButton(chars[0])\n          act(() => jest.advanceTimersByTime(200))\n          await keyDownOnToggleButton(chars[1])\n          act(() => jest.advanceTimersByTime(200))\n          // Meitnerium is highlighted right now, as we typed \"me\".\n          await keyDownOnToggleButton(chars[2])\n          act(() => jest.advanceTimersByTime(200))\n\n          // now we go back to Mendelevium, since we also typed \"n\".\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should become first character after timeout passes', async () => {\n          const chars = ['c', 'a', 'l']\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(chars[2]),\n          )\n          renderSelect()\n\n          await keyDownOnToggleButton(chars[0])\n          act(() => jest.advanceTimersByTime(200))\n          await keyDownOnToggleButton(chars[1])\n          act(() => jest.runAllTimers())\n          await keyDownOnToggleButton(chars[2])\n          act(() => jest.advanceTimersByTime(200))\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should account space character as search query', async () => {\n          const itemToSelectIndex = 3\n          const itemsWithSpaces = ['1 2 3', '4 3 2', '2 1 3', '1 2 4']\n          const itemToSelect = itemsWithSpaces[itemToSelectIndex]\n          const expectedItemId = defaultIds.getItemId(itemToSelectIndex)\n          renderSelect({items: itemsWithSpaces})\n\n          const toggleButton = getToggleButton()\n\n          // should highlight \"1 2 3\" until we pass the last character, \"4\".\n          for (let index = 0; index < itemToSelect.length; index++) {\n            // eslint-disable-next-line no-await-in-loop\n            await keyDownOnToggleButton(itemToSelect[index])\n\n            act(() => jest.advanceTimersByTime(200))\n          }\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(itemsWithSpaces.length)\n          expect(toggleButton).toHaveTextContent('Elements')\n        })\n\n        /* Here we just want to make sure the keys cleanup works. */\n        test('should not go to the second option starting with the key if timeout did not pass', async () => {\n          const char = 'l'\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(char),\n          )\n          renderSelect()\n\n          await keyDownOnToggleButton(char)\n          act(() => jest.advanceTimersByTime(200)) // wait some time but not enough to trigger debounce.\n          await keyDownOnToggleButton(char)\n          act(() => jest.advanceTimersByTime(200)) // wait some time but not enough to trigger debounce.\n          await keyDownOnToggleButton(char)\n          act(() => jest.advanceTimersByTime(200)) // wait some time but not enough to trigger debounce.\n          await keyDownOnToggleButton(char)\n          act(() => jest.advanceTimersByTime(200)) // wait some time but not enough to trigger debounce.\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('should not attempt update after unmount', async () => {\n          const consoleErrorSpy = jest.spyOn(console, 'error')\n          const char = 'c'\n          const {unmount} = renderSelect({\n            isOpen: true,\n          })\n\n          // Enter key twice to queue up debounced call\n          await keyDownOnToggleButton(char)\n          await keyDownOnToggleButton(char)\n          unmount()\n          jest.runAllTimers()\n\n          expect(consoleErrorSpy).not.toHaveBeenCalled()\n        })\n\n        test('should skip disabled items', async () => {\n          const char = 'c'\n          const firstMatchIndex = getItemIndexByCharacter(char)\n          const expectedItemId = defaultIds.getItemId(\n            getItemIndexByCharacter(char, firstMatchIndex + 1),\n          )\n          renderSelect({\n            isItemDisabled(_item, index) {\n              return index === firstMatchIndex\n            },\n          })\n\n          await keyDownOnToggleButton(char)\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            expectedItemId,\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n      })\n\n      describe('arrow up', () => {\n        test('it prevents the default event behavior', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'ArrowUp',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('it does not open or highlight anything if there are no options', async () => {\n          renderSelect({\n            items: [],\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it does not highlight anything if there are no options', async () => {\n          renderSelect({\n            items: [],\n            isOpen: true,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it opens the closed menu with last option highlighted', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('it opens the closed menu with selected option highlighted', async () => {\n          const selectedIndex = 4\n          renderSelect({\n            initialSelectedItem: items[selectedIndex],\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(selectedIndex),\n          )\n        })\n\n        test('it opens the closed menu at initialHighlightedIndex, but on first arrow up only', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            initialHighlightedIndex,\n          })\n\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex),\n          )\n\n          await keyDownOnToggleButton('{Escape}')\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('it opens the closed menu at defaultHighlightedIndex, on every arrow up', async () => {\n          const defaultHighlightedIndex = 3\n          renderSelect({\n            defaultHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n\n          await keyDownOnToggleButton('{Escape}')\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('initialHighlightedIndex is ignored if item is disabled', async () => {\n          const initialHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === initialHighlightedIndex,\n            )\n          renderSelect({\n            initialHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()\n        })\n\n        test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n          const initialHighlightedIndex = 0\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return items.indexOf(item) === initialHighlightedIndex\n            },\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n          const defaultHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === defaultHighlightedIndex,\n            )\n          renderSelect({\n            defaultHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()\n        })\n\n        test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n          const initialHighlightedIndex = 0\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return [\n                initialHighlightedIndex,\n                defaultHighlightedIndex,\n              ].includes(items.indexOf(item))\n            },\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('it opens the closed menu and keeps focus on the combobox', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveFocus()\n        })\n\n        test('highlights last option number if none is highlighted', async () => {\n          renderSelect({\n            isOpen: true,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('highlights the previous item', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex - 1),\n          )\n        })\n\n        test('will stop at first item', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: 0,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('with Alt it selects the item and closes the menu', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveTextContent(items[initialHighlightedIndex])\n        })\n\n        test('with Alt it opens the menu without any additional change', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(getItems()).toHaveLength(items.length)\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('with Alt selects highlighted item and resets to user defaults', async () => {\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(toggleButton).toHaveTextContent(items[defaultHighlightedIndex])\n          expect(getItems()).toHaveLength(items.length)\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('with Alt closes the menu without resetting to user defaults if no item is highlighted', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          })\n          const toggleButton = getToggleButton()\n\n          await mouseMoveItemAtIndex(defaultHighlightedIndex)\n          await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n          await keyDownOnToggleButton('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(toggleButton).toHaveTextContent(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('with Alt closes the menu without resetting to user defaults if the list is empty', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Alt>}{ArrowUp}{/Alt}')\n\n          expect(toggleButton).toHaveTextContent(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('skips disabled items', async () => {\n          renderSelect({\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n            initialIsOpen: true,\n            initialHighlightedIndex: 3,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n      })\n\n      describe('arrow down', () => {\n        test('it prevents the default event behavior', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'ArrowDown',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('it does not open or highlight anything if there are no options', async () => {\n          renderSelect({\n            items: [],\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('it does not highlight anything if there are no options', async () => {\n          renderSelect({\n            items: [],\n            isOpen: true,\n          })\n\n          await keyDownOnToggleButton('{ArrowUp}')\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n          expect(getItems()).toHaveLength(0)\n        })\n\n        test('opens the closed menu with first option highlighted', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(getItems()).toHaveLength(items.length)\n        })\n\n        test('opens the closed menu with selected option highlighted', async () => {\n          const selectedIndex = 4\n          renderSelect({\n            initialSelectedItem: items[selectedIndex],\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(selectedIndex),\n          )\n        })\n\n        test('opens the closed menu at initialHighlightedIndex, but on first arrow down only', async () => {\n          const initialHighlightedIndex = 3\n          renderSelect({\n            initialHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex),\n          )\n\n          await keyDownOnToggleButton('{Escape}')\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('opens the closed menu at defaultHighlightedIndex, on every arrow down', async () => {\n          const defaultHighlightedIndex = 3\n          renderSelect({\n            defaultHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n\n          await keyDownOnToggleButton('{Escape}')\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('initialHighlightedIndex is ignored if item is disabled', async () => {\n          const initialHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === initialHighlightedIndex,\n            )\n          renderSelect({\n            initialHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 2)).toMatchSnapshot()\n        })\n\n        test('initialHighlightedIndex is ignored and defaultHighlightedIndex is chosen if enabled', async () => {\n          const initialHighlightedIndex = 0\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return items.indexOf(item) === initialHighlightedIndex\n            },\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('defaultHighlightedIndex is ignored if item is disabled', async () => {\n          const defaultHighlightedIndex = 2\n          const isItemDisabled = jest\n            .fn()\n            .mockImplementation(\n              item => items.indexOf(item) === defaultHighlightedIndex,\n            )\n          renderSelect({\n            defaultHighlightedIndex,\n            isItemDisabled,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n          expect(isItemDisabled.mock.calls.slice(0, 3)).toMatchSnapshot()\n        })\n\n        test('both defaultHighlightedIndex and initialHighlightedIndex are ignored if items are disabled', async () => {\n          const initialHighlightedIndex = 1\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            initialHighlightedIndex,\n            defaultHighlightedIndex,\n            isItemDisabled(item) {\n              return [\n                initialHighlightedIndex,\n                defaultHighlightedIndex,\n              ].includes(items.indexOf(item))\n            },\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('opens the closed menu and keeps focus on the button', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveFocus()\n        })\n\n        test(\"it highlights option number '0' if none is highlighted\", async () => {\n          renderSelect({\n            isOpen: true,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('it highlights the next item', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 1),\n          )\n        })\n\n        test('with Alt it opens the menu and highlights no item', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('with Alt it opens the menu and highlights with selected item highlighted', async () => {\n          const itemIndex = 8\n          renderSelect({selectedItem: items[itemIndex]})\n\n          await keyDownOnToggleButton('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(itemIndex),\n          )\n        })\n\n        test('with Alt it highlights the next item without any additional change', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({initialHighlightedIndex, isOpen: true})\n\n          await keyDownOnToggleButton('{Alt>}{ArrowDown}{/Alt}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 1),\n          )\n        })\n\n        test('will stop at last item', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: items.length - 1,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('skips disabled items', async () => {\n          renderSelect({\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n            initialIsOpen: true,\n            initialHighlightedIndex: 1,\n          })\n\n          await keyDownOnToggleButton('{ArrowDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(3),\n          )\n        })\n      })\n\n      describe('end', () => {\n        test('opens the menu and highlights the last option', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{End}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('highlights the last option', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n          })\n\n          await keyDownOnToggleButton('{End}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('prevents the default event behavior', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: 'End'})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('highlights previous non-disabled option', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: 0,\n            isItemDisabled(_item, index) {\n              return index === items.length - 1\n            },\n          })\n\n          await keyDownOnToggleButton('{End}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 2),\n          )\n        })\n      })\n\n      describe('home', () => {\n        test('opens the menu and highlights the first option', async () => {\n          renderSelect()\n\n          await keyDownOnToggleButton('{Home}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('highlights the first option', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n          })\n\n          await keyDownOnToggleButton('{Home}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('prevents the default event behavior', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: 'Home'})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('highlights next non-disabled option', async () => {\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex: 2,\n            isItemDisabled(_item, index) {\n              return index === 0\n            },\n          })\n\n          await keyDownOnToggleButton('{Home}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n      })\n\n      test('escape has the menu closed and keeps focus on the button', async () => {\n        renderSelect({\n          initialIsOpen: true,\n        })\n\n        await keyDownOnToggleButton('{Escape}')\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveFocus()\n      })\n\n      test('escape does nothing if menu is already closed', async () => {\n        renderSelect()\n\n        await keyDownOnToggleButton('{Escape}')\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveFocus()\n      })\n\n      describe('enter', () => {\n        test('opens the menu without any item selected', async () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Enter}')\n\n          expect(getItems()).toHaveLength(items.length)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n          expect(toggleButton).toHaveFocus()\n        })\n\n        test('closes the menu and selects highlighted item', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Enter}')\n\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveTextContent(items[initialHighlightedIndex])\n          expect(toggleButton).toHaveFocus()\n        })\n\n        test('selects the highlighted item and resets to user defaults', async () => {\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Enter}')\n\n          expect(toggleButton).toHaveTextContent(items[defaultHighlightedIndex])\n          expect(getItems()).toHaveLength(items.length)\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('enter closes the menu without resetting to user defaults if no item is highlighted', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          })\n          const toggleButton = getToggleButton()\n\n          await mouseMoveItemAtIndex(defaultHighlightedIndex)\n          await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n          await keyDownOnToggleButton('{Enter}')\n\n          expect(toggleButton).toHaveTextContent(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('enter closes the menu without resetting to user defaults if the list is empty', async () => {\n          const defaultHighlightedIndex = 2\n          const initialSelectedItem = items[0]\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton('{Enter}')\n\n          expect(toggleButton).toHaveTextContent(initialSelectedItem)\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n        })\n\n        test('prevents the default event behavior with the menu open', () => {\n          renderSelect({isOpen: true})\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: 'Enter'})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('prevents the default event behavior with the menu closed', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: 'Enter'})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n      })\n\n      describe('space', () => {\n        test('opens the menu without any item selected', async () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton(' ')\n\n          expect(getItems()).toHaveLength(items.length)\n          expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n          expect(toggleButton).toHaveFocus()\n        })\n\n        test('closes the menu and selects highlighted item', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            initialIsOpen: true,\n            initialHighlightedIndex,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton(' ')\n\n          expect(getItems()).toHaveLength(0)\n          expect(toggleButton).toHaveTextContent(items[initialHighlightedIndex])\n          expect(toggleButton).toHaveFocus()\n        })\n\n        test('selects the highlighted item and resets to user defaults', async () => {\n          const defaultHighlightedIndex = 2\n          renderSelect({\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n          })\n          const toggleButton = getToggleButton()\n\n          await keyDownOnToggleButton(' ')\n\n          expect(toggleButton).toHaveTextContent(items[defaultHighlightedIndex])\n          expect(getItems()).toHaveLength(items.length)\n          expect(toggleButton).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(defaultHighlightedIndex),\n          )\n        })\n\n        test('prevents the default event behavior when select is open', () => {\n          renderSelect({isOpen: true})\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: ' '})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('prevents the default event behavior when select is closed', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {key: ' '})\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n      })\n\n      test('tab closes the menu and selects highlighted item', async () => {\n        const initialHighlightedIndex = 2\n        const firstFocusableItemTestId = 'test-id-1'\n        const secondFocusableItemTestId = 'test-id-2'\n        renderSelect({initialIsOpen: true, initialHighlightedIndex}, ui => {\n          return (\n            <>\n              <div tabIndex={0} data-testid={firstFocusableItemTestId}>\n                First element\n              </div>\n              {ui}\n              <div tabIndex={0} data-testid={secondFocusableItemTestId}>\n                Second element\n              </div>\n            </>\n          )\n        })\n\n        // focus is already on the toggle button, tab should blur\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(\n          items[initialHighlightedIndex],\n        )\n        expect(screen.getByTestId(secondFocusableItemTestId)).toHaveFocus()\n      })\n\n      test('tab closes the menu if there is no highlighted item', async () => {\n        const defaultHighlightedIndex = 2\n        const initialSelectedItem = items[0]\n\n        renderSelect(\n          {\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n          },\n          ui => {\n            return (\n              <>\n                {ui}\n                <div tabIndex={0}>Second element</div>\n              </>\n            )\n          },\n        )\n\n        await mouseMoveItemAtIndex(defaultHighlightedIndex)\n        await mouseLeaveItemAtIndex(defaultHighlightedIndex)\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(initialSelectedItem)\n      })\n\n      test('tab closes the menu if there is no items', async () => {\n        const defaultHighlightedIndex = 2\n        const initialSelectedItem = items[0]\n\n        renderSelect(\n          {\n            defaultHighlightedIndex,\n            defaultIsOpen: true,\n            initialSelectedItem,\n            items: [],\n          },\n          ui => {\n            return (\n              <>\n                {ui}\n                <div tabIndex={0}>Second element</div>\n              </>\n            )\n          },\n        )\n\n        await tab()\n        await tab()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(initialSelectedItem)\n      })\n\n      test('shift+tab closes the menu and selects highlighted item', async () => {\n        const initialHighlightedIndex = 1\n        const firstFocusableItemTestId = 'test-id-1'\n        const secondFocusableItemTestId = 'test-id-2'\n        renderSelect({initialIsOpen: true, initialHighlightedIndex}, ui => {\n          return (\n            <>\n              <div tabIndex={0} data-testid={firstFocusableItemTestId}>\n                First element\n              </div>\n              {ui}\n              <div tabIndex={0} data-testid={secondFocusableItemTestId}>\n                Second element\n              </div>\n            </>\n          )\n        })\n\n        // focus is already on the toggle button, tab should blur\n        await tab(true)\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(\n          items[initialHighlightedIndex],\n        )\n        expect(screen.getByTestId(firstFocusableItemTestId)).toHaveFocus()\n      })\n\n      test(\"other than te ones supported don't affect anything\", async () => {\n        renderSelect()\n        const toggleButton = getToggleButton()\n\n        await keyDownOnToggleButton('{Alt}')\n        await keyDownOnToggleButton('{Control}')\n\n        expect(toggleButton).toHaveTextContent('Elements')\n        expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n        expect(getItems()).toHaveLength(0)\n      })\n\n      describe('pageUp', () => {\n        test('jumps highlight up by 10 options', async () => {\n          const initialHighlightedIndex = 12\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{PageUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex - 10),\n          )\n        })\n\n        test('skips the disabled option to the previous non-disabled one', async () => {\n          const initialHighlightedIndex = 12\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n            isItemDisabled(_item, index) {\n              return index === 2\n            },\n          })\n\n          await keyDownOnToggleButton('{PageUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(1),\n          )\n        })\n\n        test('jumps highlight the first option if highlightedIndex is 10 or smaller', async () => {\n          const initialHighlightedIndex = 7\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{PageUp}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(0),\n          )\n        })\n\n        test('prevents the default event behavior with the menu open', () => {\n          renderSelect({isOpen: true})\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageUp',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('does not prevent the default event behavior with the menu closed', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageUp',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n        })\n      })\n\n      describe('pageDown', () => {\n        test('jumps highlight down by 10 options', async () => {\n          const initialHighlightedIndex = 12\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{PageDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(initialHighlightedIndex + 10),\n          )\n        })\n\n        test('skips the disabled option to the next non-disabled one', async () => {\n          const initialHighlightedIndex = 2\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n            isItemDisabled(_item, index) {\n              return index === 12\n            },\n          })\n\n          await keyDownOnToggleButton('{PageDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(13),\n          )\n        })\n\n        test('jumps highlight the last option if highlightedIndex is closer than 10 indeces to the end', async () => {\n          const initialHighlightedIndex = items.length - 5\n          renderSelect({\n            isOpen: true,\n            initialHighlightedIndex,\n          })\n\n          await keyDownOnToggleButton('{PageDown}')\n\n          expect(getToggleButton()).toHaveAttribute(\n            'aria-activedescendant',\n            defaultIds.getItemId(items.length - 1),\n          )\n        })\n\n        test('prevents the default event behavior with the menu open', () => {\n          renderSelect({isOpen: true})\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageDown',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(true)\n        })\n\n        test('does not prevent the default event behavior with the menu closed', () => {\n          renderSelect()\n          const toggleButton = getToggleButton()\n          const keyDownEvent = createEvent.keyDown(toggleButton, {\n            key: 'PageDown',\n          })\n\n          fireEvent(toggleButton, keyDownEvent)\n\n          expect(keyDownEvent.defaultPrevented).toBe(false)\n        })\n      })\n    })\n\n    describe('blur', () => {\n      test('with the menu closed does nothing', async () => {\n        const initialHighlightedIndex = 2\n        renderSelect({initialHighlightedIndex}, ui => {\n          return (\n            <>\n              {ui}\n              <div tabIndex={0}>Second element</div>\n            </>\n          )\n        })\n\n        await tab() // focus the button\n        screen.getByText(/Second element/).focus()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).not.toHaveTextContent()\n      })\n\n      test('by focusing another element should behave as a normal blur', async () => {\n        const initialHighlightedIndex = 2\n        renderSelect({initialIsOpen: true, initialHighlightedIndex}, ui => {\n          return (\n            <>\n              {ui}\n              <div tabIndex={0}>Second element</div>\n            </>\n          )\n        })\n\n        await tab() // focus the button\n        screen.getByText(/Second element/).focus()\n\n        expect(getItems()).toHaveLength(0)\n        expect(getToggleButton()).toHaveTextContent(\n          items[initialHighlightedIndex],\n        )\n      })\n\n      test('by mouse is not triggered if target is within downshift', async () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        const {container} = renderSelect({\n          isOpen: true,\n          stateReducer,\n        })\n        const toggleButton = getToggleButton()\n        document.body.appendChild(container)\n\n        fireEvent.mouseDown(toggleButton)\n        fireEvent.mouseUp(toggleButton)\n\n        expect(stateReducer).not.toHaveBeenCalled()\n\n        fireEvent.mouseDown(document.body)\n        fireEvent.mouseUp(document.body)\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          expect.objectContaining({}),\n          expect.objectContaining({type: stateChangeTypes.ToggleButtonBlur}),\n        )\n      })\n\n      test('by touch is not triggered if target is within downshift', () => {\n        const stateReducer = jest.fn().mockImplementation(s => s)\n        const {container} = renderSelect({\n          isOpen: true,\n          stateReducer,\n        })\n        const toggleButton = getToggleButton()\n        document.body.appendChild(container)\n\n        fireEvent.touchStart(toggleButton)\n        fireEvent.touchMove(toggleButton)\n        fireEvent.touchEnd(toggleButton)\n\n        expect(stateReducer).not.toHaveBeenCalled()\n\n        fireEvent.touchStart(document.body)\n        fireEvent.touchEnd(document.body)\n\n        expect(stateReducer).toHaveBeenCalledTimes(1)\n        expect(stateReducer).toHaveBeenCalledWith(\n          expect.objectContaining({}),\n          expect.objectContaining({type: stateChangeTypes.ToggleButtonBlur}),\n        )\n      })\n    })\n  })\n\n  describe('non production errors', () => {\n    beforeEach(() => {\n      const {useGetterPropsCalledChecker} = jest.requireActual('../../utils')\n      jest\n        .spyOn(utils, 'useGetterPropsCalledChecker')\n        .mockImplementation(useGetterPropsCalledChecker)\n      jest.spyOn(console, 'error').mockImplementation(() => {})\n    })\n\n    test('will be displayed if getToggleButtonProps is not called', () => {\n      renderHook(() => {\n        const {getMenuProps} = useSelect({items})\n        getMenuProps({}, {suppressRefError: true})\n      })\n\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: You forgot to call the getToggleButtonProps getter function on your component / element.`,\n      )\n    })\n\n    test('will not be displayed if getToggleButtonProps is not called on subsequent renders', () => {\n      let firstRender = true\n      const {rerender} = renderHook(() => {\n        const {getMenuProps, getToggleButtonProps} = useSelect({items})\n        getMenuProps({}, {suppressRefError: true})\n\n        // eslint-disable-next-line jest/no-if, jest/no-conditional-in-test\n        if (firstRender) {\n          firstRender = false\n          getToggleButtonProps({}, {suppressRefError: true})\n        }\n      })\n\n      rerender()\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n\n    test('will be displayed if element ref is not set and suppressRefError is false', () => {\n      renderHook(() => {\n        const {getMenuProps, getToggleButtonProps} = useSelect({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n        getToggleButtonProps()\n      })\n\n      expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n        `downshift: The ref prop \"ref\" from getToggleButtonProps was not applied correctly on your element.`,\n      )\n    })\n\n    test('will not be displayed if called with a correct ref', () => {\n      const refFn = jest.fn()\n      const toggleButtonNode = {}\n\n      renderHook(() => {\n        const {getToggleButtonProps, getMenuProps} = useSelect({\n          items,\n        })\n\n        getMenuProps({}, {suppressRefError: true})\n\n        const {ref} = getToggleButtonProps({\n          ref: refFn,\n        })\n        ref(toggleButtonNode)\n      })\n\n      expect(console.error).not.toHaveBeenCalled()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/memo.test.js",
    "content": "import React from 'react'\nimport {\n  renderUseSelect,\n  renderSelect,\n  keyDownOnToggleButton,\n  getToggleButton,\n  items,\n  defaultIds,\n  MemoizedItem,\n} from '../testUtils'\n\ntest('functions are memoized', () => {\n  const {result, rerender} = renderUseSelect()\n  const firstRenderResult = result.current\n  rerender()\n  const secondRenderResult = result.current\n  expect(firstRenderResult).toEqual(secondRenderResult)\n})\n\ntest('will skip disabled items after component rerenders and items are memoized', async () => {\n  function renderItem(props) {\n    return <MemoizedItem key={props.index} {...props} />\n  }\n  function isItemDisabled(_item, index) {\n    return index === items.length - 2\n  }\n\n  const {rerender} = renderSelect({\n    isItemDisabled,\n    isOpen: true,\n    initialHighlightedIndex: items.length - 1,\n    renderItem,\n  })\n\n  rerender({renderItem, isOpen: true, isItemDisabled})\n  await keyDownOnToggleButton('{ArrowUp}')\n\n  expect(getToggleButton()).toHaveAttribute(\n    'aria-activedescendant',\n    defaultIds.getItemId(items.length - 3),\n  )\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/props.test.js",
    "content": "import {act, renderHook} from '@testing-library/react'\nimport {\n  clickOnItemAtIndex,\n  clickOnToggleButton,\n  getA11yStatusContainer,\n  getItems,\n  getLabel,\n  getMenu,\n  getToggleButton,\n  keyDownOnToggleButton,\n  renderSelect,\n  renderUseSelect,\n  stateChangeTestCases,\n  tab,\n} from '../testUtils'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport {\n  items,\n  defaultIds,\n  waitForDebouncedA11yStatusUpdate,\n} from '../../testUtils'\nimport useSelect from '..'\n\ndescribe('props', () => {\n  test('if falsy then prop types error is thrown', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    renderHook(() => useSelect())\n\n    expect(global.console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `Warning: Failed prop type: The prop \\`items\\` is marked as required in \\`useSelect\\`, but its value is \\`undefined\\`.`,\n    )\n  })\n\n  test('id if passed will override downshift default', () => {\n    renderSelect({\n      id: 'my-custom-little-id',\n    })\n    const elements = [getToggleButton(), getMenu(), getLabel()]\n\n    elements.forEach(element => {\n      expect(element).toHaveAttribute(\n        'id',\n        expect.stringContaining('my-custom-little-id'),\n      )\n    })\n  })\n\n  describe('items', () => {\n    test('if passed as empty then menu will not open', () => {\n      renderSelect({items: [], isOpen: true})\n\n      expect(getItems()).toHaveLength(0)\n    })\n\n    test('passed as objects should work with custom itemToString', async () => {\n      renderSelect({\n        items: [{str: 'aaa'}, {str: 'bbb'}],\n        itemToString: item => item.str,\n        initialIsOpen: true,\n      })\n\n      await keyDownOnToggleButton('b')\n\n      expect(getToggleButton()).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(1),\n      )\n    })\n  })\n\n  describe('getA11yStatusMessage', () => {\n    beforeEach(() => jest.useFakeTimers())\n    afterEach(() => {\n      act(() => jest.runAllTimers())\n    })\n    afterAll(() => jest.useRealTimers())\n\n    test('adds no status message element to the DOM if not passed', async () => {\n      renderSelect({\n        items,\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('calls the function only on state changes', async () => {\n      const getA11yStatusMessage = jest.fn()\n      const {rerender} = renderSelect({\n        getA11yStatusMessage,\n      })\n\n      await keyDownOnToggleButton('h')\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        inputValue: 'h',\n        highlightedIndex: 15,\n        isOpen: true,\n        selectedItem: null,\n      })\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n\n      getA11yStatusMessage.mockClear()\n      rerender({getA11yStatusMessage})\n\n      expect(getA11yStatusMessage).not.toHaveBeenCalled()\n    })\n\n    test('adds a status message element with the text returned', async () => {\n      const a11yStatusMessage1 = 'Dropdown is open'\n      const a11yStatusMessage2 = 'Dropdown is still open'\n      const getA11yStatusMessage = jest\n        .fn()\n        .mockReturnValueOnce(a11yStatusMessage1)\n        .mockReturnValueOnce(a11yStatusMessage2)\n      renderSelect({\n        items,\n        getA11yStatusMessage,\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusContainer()).toHaveTextContent(a11yStatusMessage1)\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        highlightedIndex: -1,\n        inputValue: '',\n        isOpen: true,\n        selectedItem: null,\n      })\n\n      getA11yStatusMessage.mockClear()\n\n      await keyDownOnToggleButton('{ArrowDown}')\n\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(getA11yStatusMessage).toHaveBeenCalledTimes(1)\n      expect(getA11yStatusMessage).toHaveBeenCalledWith({\n        highlightedIndex: 0,\n        inputValue: '',\n        isOpen: true,\n        selectedItem: null,\n      })\n    })\n\n    test('clears the text content after 500ms', async () => {\n      renderSelect({\n        items,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate(true)\n\n      expect(getA11yStatusContainer()).toBeEmptyDOMElement()\n    })\n\n    test('removes the message element from the DOM on unmount', async () => {\n      const {unmount} = renderSelect({\n        items,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n      act(() => jest.advanceTimersByTime(500))\n      unmount()\n\n      expect(getA11yStatusContainer()).not.toBeInTheDocument()\n    })\n\n    test('is added to the document provided by the user as prop', async () => {\n      const environment = {\n        document: {\n          getElementById: jest.fn().mockReturnValue({remove: jest.fn()}),\n          createElement: jest.fn(),\n          activeElement: {},\n          body: {},\n        },\n        addEventListener: jest.fn(),\n        removeEventListener: jest.fn(),\n        Node,\n      }\n      renderSelect({\n        items,\n        environment,\n        getA11yStatusMessage: jest.fn().mockReturnValue('bla bla'),\n      })\n\n      await clickOnToggleButton()\n      waitForDebouncedA11yStatusUpdate()\n\n      expect(environment.document.getElementById).toHaveBeenCalledTimes(1)\n      expect(environment.document.getElementById).toHaveBeenCalledWith(\n        'a11y-status-message',\n      )\n    })\n  })\n\n  test('highlightedIndex controls the state property if passed', async () => {\n    const highlightedIndex = 1\n    renderSelect({\n      isOpen: true,\n      highlightedIndex,\n    })\n    const toggleButton = getToggleButton()\n    const keys = [\n      '{ArrowDown}',\n      '{End}',\n      '{ArrowUp}',\n      '{PageUp}',\n      '{PageDown}',\n      'c',\n    ]\n\n    expect(toggleButton).toHaveAttribute(\n      'aria-activedescendant',\n      defaultIds.getItemId(highlightedIndex),\n    )\n\n    for (const key of keys) {\n      // eslint-disable-next-line no-await-in-loop\n      await keyDownOnToggleButton(key)\n\n      expect(toggleButton).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(highlightedIndex),\n      )\n    }\n  })\n\n  test('isOpen controls the state property if passed', async () => {\n    renderSelect({isOpen: true})\n    expect(getItems()).toHaveLength(items.length)\n    await tab() // focus toggle button\n\n    await clickOnToggleButton()\n    expect(getItems()).toHaveLength(items.length)\n\n    await keyDownOnToggleButton('{Escape}')\n    expect(getItems()).toHaveLength(items.length)\n\n    await tab()\n    expect(getItems()).toHaveLength(items.length)\n  })\n\n  describe('selectedItem', () => {\n    test('controls the state property if passed', async () => {\n      const selectedItem = items[2]\n      renderSelect({selectedItem, isOpen: true})\n      const toggleButton = getToggleButton()\n\n      expect(toggleButton).toHaveTextContent(items[2])\n\n      await keyDownOnToggleButton('{ArrowDown}')\n      await keyDownOnToggleButton('{Enter}')\n\n      expect(toggleButton).toHaveTextContent(items[2])\n\n      await clickOnItemAtIndex(4)\n\n      expect(toggleButton).toHaveTextContent(items[2])\n    })\n\n    test('highlightedIndex on open gets computed based on the selectedItem prop value', async () => {\n      const expectedHighlightedIndex = 2\n      const selectedItem = items[expectedHighlightedIndex]\n      renderSelect({selectedItem})\n      const toggleButton = getToggleButton()\n\n      expect(toggleButton).toHaveAttribute('aria-activedescendant', '')\n\n      await clickOnToggleButton()\n\n      expect(toggleButton).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(expectedHighlightedIndex),\n      )\n\n      await keyDownOnToggleButton('{ArrowDown}')\n      await keyDownOnToggleButton('{Enter}')\n\n      expect(toggleButton).toHaveTextContent(items[expectedHighlightedIndex])\n\n      await clickOnToggleButton()\n      await clickOnItemAtIndex(3)\n\n      expect(toggleButton).toHaveTextContent(items[expectedHighlightedIndex])\n    })\n\n    test('highlightedIndex computed based on the selectedItem prop value in initial state as well', async () => {\n      const expectedHighlightedIndex = 2\n      const selectedItem = items[expectedHighlightedIndex]\n      // open dropdown in the initial state to check highlighted index.\n      renderSelect({selectedItem, initialIsOpen: true})\n      const toggleButton = getToggleButton()\n\n      expect(toggleButton).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(expectedHighlightedIndex),\n      )\n\n      await keyDownOnToggleButton('{ArrowDown}')\n      await keyDownOnToggleButton('{Enter}')\n\n      expect(toggleButton).toHaveTextContent(items[expectedHighlightedIndex])\n      await clickOnToggleButton()\n      await clickOnItemAtIndex(3)\n\n      expect(toggleButton).toHaveTextContent(items[expectedHighlightedIndex])\n    })\n  })\n\n  describe('stateReducer', () => {\n    beforeEach(() => jest.useFakeTimers())\n    afterEach(() => {\n      act(() => jest.runAllTimers())\n    })\n    afterAll(jest.useRealTimers)\n\n    test('is called at each state change with the function change type', () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      const {result} = renderUseSelect({stateReducer})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionToggleMenu}),\n      )\n\n      act(() => {\n        result.current.openMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(2)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionOpenMenu}),\n      )\n\n      act(() => {\n        result.current.closeMenu()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(3)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionCloseMenu}),\n      )\n\n      act(() => {\n        result.current.reset()\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(4)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionReset}),\n      )\n\n      act(() => {\n        result.current.selectItem({})\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(5)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionSelectItem}),\n      )\n\n      act(() => {\n        result.current.setHighlightedIndex(5)\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(6)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({\n          type: stateChangeTypes.FunctionSetHighlightedIndex,\n        }),\n      )\n\n      act(() => {\n        result.current.setInputValue({})\n      })\n\n      expect(stateReducer).toHaveBeenCalledTimes(7)\n      expect(stateReducer).toHaveBeenLastCalledWith(\n        expect.objectContaining({}),\n        expect.objectContaining({type: stateChangeTypes.FunctionSetInputValue}),\n      )\n    })\n\n    test('is called at each state change with the appropriate change type', async () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n      renderSelect({stateReducer})\n      const initialState = {\n        isOpen: false,\n        highlightedIndex: -1,\n        selectedItem: null,\n        inputValue: '',\n      }\n\n      expect(stateReducer).not.toHaveBeenCalled()\n\n      for (let index = 0; index < stateChangeTestCases.length; index++) {\n        const {step, state, arg, type} = stateChangeTestCases[index]\n        const previousState =\n          // eslint-disable-next-line jest/no-conditional-in-test\n          stateChangeTestCases[index - 1]?.state ?? initialState\n\n        // eslint-disable-next-line no-await-in-loop\n        await step(arg)\n\n        expect(stateReducer).toHaveBeenCalledTimes(index + 1)\n        expect(stateReducer).toHaveBeenLastCalledWith(\n          previousState,\n          expect.objectContaining({changes: state, type}),\n        )\n      }\n    })\n\n    test('receives state, changes and type', async () => {\n      const stateReducer = jest.fn((s, a) => {\n        expect(a.type).not.toBeUndefined()\n        expect(a.type).not.toBeNull()\n\n        expect(s).not.toBeUndefined()\n        expect(s).not.toBeNull()\n\n        expect(a.changes).not.toBeUndefined()\n        expect(a.changes).not.toBeNull()\n\n        return a.changes\n      })\n      renderSelect({stateReducer})\n\n      await clickOnToggleButton()\n    })\n\n    // should check that no blur state change occurs after item selection.\n    // https://github.com/downshift-js/downshift/issues/965\n    test('is called only once on item selection', async () => {\n      const stateReducer = jest.fn((s, a) => a.changes)\n\n      renderSelect({\n        stateReducer,\n        initialIsOpen: true,\n        initialHighlightedIndex: 0,\n      })\n\n      await clickOnItemAtIndex(0)\n\n      expect(stateReducer).toHaveBeenCalledTimes(1)\n\n      await clickOnToggleButton()\n      await keyDownOnToggleButton('{ArrowDown}')\n      await keyDownOnToggleButton('{Enter}')\n\n      expect(stateReducer).toHaveBeenCalledTimes(4)\n\n      await clickOnToggleButton()\n      await keyDownOnToggleButton('{ArrowDown}')\n      await keyDownOnToggleButton(' ')\n\n      expect(stateReducer).toHaveBeenCalledTimes(7)\n    })\n\n    test('changes are visible in onChange handlers', async () => {\n      const highlightedIndex = 2\n      const selectedItem = {foo: 'bar'}\n      const isOpen = true\n      const stateReducer = jest.fn(() => ({\n        highlightedIndex,\n        isOpen,\n        selectedItem,\n      }))\n      const onSelectedItemChange = jest.fn()\n      const onHighlightedIndexChange = jest.fn()\n      const onIsOpenChange = jest.fn()\n      const onStateChange = jest.fn()\n      renderSelect({\n        stateReducer,\n        onStateChange,\n        onSelectedItemChange,\n        onHighlightedIndexChange,\n        onIsOpenChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex,\n        }),\n      )\n      expect(onSelectedItemChange).toHaveBeenCalledTimes(1)\n      expect(onSelectedItemChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItem,\n        }),\n      )\n      expect(onIsOpenChange).toHaveBeenCalledTimes(1)\n      expect(onIsOpenChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen,\n        }),\n      )\n      expect(onStateChange).toHaveBeenCalledTimes(1)\n      expect(onStateChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen,\n          type: stateChangeTypes.ToggleButtonClick,\n        }),\n      )\n    })\n  })\n\n  describe('onSelectedItemChange', () => {\n    test('is called at selectedItem change', async () => {\n      const onSelectedItemChange = jest.fn()\n      const index = 2\n      renderSelect({\n        initialIsOpen: true,\n        onSelectedItemChange,\n      })\n\n      await clickOnItemAtIndex(index)\n\n      expect(onSelectedItemChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          selectedItem: items[index],\n          type: stateChangeTypes.ItemClick,\n        }),\n      )\n    })\n\n    test('is not called at if selectedItem is the same', async () => {\n      const index = 1\n      const onSelectedItemChange = jest.fn()\n      renderSelect({\n        initialIsOpen: true,\n        initialSelectedItem: items[index],\n        onSelectedItemChange,\n      })\n\n      await clickOnItemAtIndex(index)\n\n      expect(onSelectedItemChange).not.toHaveBeenCalled()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let selectedItem = items[2]\n      const selectionIndex = 3\n      const {rerender} = renderSelect({\n        initialIsOpen: true,\n        selectedItem,\n        onSelectedItemChange: changes => {\n          selectedItem = changes.selectedItem\n        },\n      })\n\n      await clickOnItemAtIndex(selectionIndex)\n      rerender({selectedItem})\n\n      expect(getToggleButton()).toHaveTextContent(items[selectionIndex])\n    })\n\n    test('can have downshift actions executed', () => {\n      const {result} = renderUseSelect({\n        initialIsOpen: true,\n        onSelectedItemChange: () => {\n          result.current.openMenu()\n        },\n      })\n\n      act(() => {\n        result.current.getItemProps({index: 2}).onClick({})\n      })\n\n      expect(result.current.isOpen).toEqual(true)\n    })\n  })\n\n  describe('onHighlightedIndexChange', () => {\n    test('is called at each highlightedIndex change', async () => {\n      const onHighlightedIndexChange = jest.fn()\n      renderSelect({\n        initialIsOpen: true,\n        onHighlightedIndexChange,\n      })\n\n      await keyDownOnToggleButton('{ArrowDown}')\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: 0,\n          type: stateChangeTypes.ToggleButtonKeyDownArrowDown,\n        }),\n      )\n    })\n\n    test('is not called if highlightedIndex is the same', async () => {\n      const onHighlightedIndexChange = jest.fn()\n      renderSelect({\n        initialIsOpen: true,\n        initialHighlightedIndex: 0,\n        onHighlightedIndexChange,\n      })\n\n      await keyDownOnToggleButton('{ArrowUp}')\n\n      expect(onHighlightedIndexChange).not.toHaveBeenCalled()\n\n      await keyDownOnToggleButton('{Home}')\n\n      expect(onHighlightedIndexChange).not.toHaveBeenCalled()\n    })\n\n    test('is called on first open when initialSelectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderSelect({\n        initialSelectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('is called on first open when selectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderSelect({\n        selectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('is called on first open when defaultSelectedItem is set', async () => {\n      const index = 2\n      const onHighlightedIndexChange = jest.fn()\n      renderSelect({\n        defaultSelectedItem: items[index],\n        onHighlightedIndexChange,\n      })\n\n      await clickOnToggleButton()\n\n      expect(onHighlightedIndexChange).toHaveBeenCalledTimes(1)\n      expect(onHighlightedIndexChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          highlightedIndex: index,\n        }),\n      )\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let highlightedIndex = 2\n      const {rerender} = renderSelect({\n        isOpen: true,\n        highlightedIndex,\n        onHighlightedIndexChange: changes => {\n          highlightedIndex = changes.highlightedIndex\n        },\n      })\n\n      await keyDownOnToggleButton('{ArrowDown}')\n      rerender({isOpen: true, highlightedIndex})\n\n      expect(getToggleButton()).toHaveAttribute(\n        'aria-activedescendant',\n        defaultIds.getItemId(3),\n      )\n    })\n\n    test('can have downshift actions executed', () => {\n      const highlightedIndex = 3\n      const {result} = renderUseSelect({\n        initialIsOpen: true,\n        onHighlightedIndexChange: () => {\n          result.current.setHighlightedIndex(highlightedIndex)\n        },\n      })\n\n      act(() => {\n        result.current\n          .getToggleButtonProps()\n          .onKeyDown({key: 'ArrowDown', preventDefault: jest.fn()})\n      })\n\n      expect(result.current.highlightedIndex).toEqual(highlightedIndex)\n    })\n  })\n\n  describe('onIsOpenChange', () => {\n    test('is called at each isOpen change', async () => {\n      const onIsOpenChange = jest.fn()\n      renderSelect({\n        initialIsOpen: true,\n        onIsOpenChange,\n      })\n\n      await keyDownOnToggleButton('{Escape}')\n\n      expect(onIsOpenChange).toHaveBeenCalledWith(\n        expect.objectContaining({\n          isOpen: false,\n          type: stateChangeTypes.ToggleButtonKeyDownEscape,\n        }),\n      )\n    })\n\n    test('is not called at if isOpen is the same', async () => {\n      const onIsOpenChange = jest.fn()\n      renderSelect({\n        defaultIsOpen: true,\n        onIsOpenChange,\n      })\n\n      await clickOnItemAtIndex(0)\n\n      expect(onIsOpenChange).not.toHaveBeenCalledWith()\n    })\n\n    test('works correctly with the corresponding control prop', async () => {\n      let isOpen = true\n      const {rerender} = renderSelect({\n        isOpen,\n        onIsOpenChange: changes => {\n          isOpen = changes.isOpen\n        },\n      })\n\n      await keyDownOnToggleButton('{Escape}')\n      rerender({isOpen})\n\n      expect(getItems()).toHaveLength(0)\n    })\n\n    test('can have downshift actions executed', () => {\n      const highlightedIndex = 3\n      const {result} = renderUseSelect({\n        onIsOpenChange: () => {\n          result.current.setHighlightedIndex(highlightedIndex)\n        },\n      })\n\n      act(() => {\n        result.current.getToggleButtonProps().onClick({})\n      })\n\n      expect(result.current.highlightedIndex).toEqual(highlightedIndex)\n    })\n  })\n\n  describe('onStateChange', () => {\n    beforeEach(() => jest.useFakeTimers())\n    beforeEach(jest.clearAllTimers)\n    afterAll(jest.useRealTimers)\n    test('is called at each state property change but only with changed props', async () => {\n      jest.useFakeTimers()\n      const onStateChange = jest.fn()\n      const initialState = {\n        isOpen: false,\n        highlightedIndex: -1,\n        selectedItem: null,\n        inputValue: '',\n      }\n\n      renderSelect({onStateChange})\n\n      for (let index = 0; index < stateChangeTestCases.length; index++) {\n        const {step, state, arg, type} = stateChangeTestCases[index]\n        const previousState =\n          // eslint-disable-next-line jest/no-conditional-in-test\n          stateChangeTestCases[index - 1]?.state ?? initialState\n        const newState = Object.keys(state).reduce(\n          (acc, key) =>\n            // eslint-disable-next-line jest/no-conditional-in-test\n            state[key] === previousState[key]\n              ? acc\n              : {...acc, [key]: state[key]},\n          {},\n        )\n\n        // eslint-disable-next-line no-await-in-loop\n        await step(arg)\n\n        expect(onStateChange).toHaveBeenCalledTimes(index + 1)\n        expect(onStateChange).toHaveBeenLastCalledWith(\n          expect.objectContaining({\n            ...newState,\n            type,\n          }),\n        )\n      }\n\n      jest.runAllTimers()\n      jest.useRealTimers()\n    })\n\n    test('can have downshift actions executed', () => {\n      const {result} = renderUseSelect({\n        initialIsOpen: true,\n        onStateChange: () => {\n          result.current.openMenu()\n        },\n      })\n\n      act(() => {\n        result.current.getItemProps({index: 2}).onClick({})\n      })\n\n      expect(result.current.isOpen).toEqual(true)\n    })\n  })\n\n  test('that are uncontrolled should not become controlled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderSelect()\n\n    rerender({isOpen: true})\n\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the uncontrolled prop \"isOpen\" to be controlled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n\n  test('that are controlled should not become uncontrolled', () => {\n    jest.spyOn(console, 'error').mockImplementation(() => {})\n    const {rerender} = renderSelect({highlightedIndex: 3})\n\n    rerender({})\n\n    expect(console.error.mock.calls[0][0]).toMatchInlineSnapshot(\n      `downshift: A component has changed the controlled prop \"highlightedIndex\" to be uncontrolled. This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`,\n    )\n  })\n\n  test('initialHighlightedIndex is ignored if item is disabled and menu is intially open', () => {\n    const initialHighlightedIndex = 2\n    const isItemDisabled = jest\n      .fn()\n      .mockImplementation(\n        item => items.indexOf(item) === initialHighlightedIndex,\n      )\n    renderSelect({\n      initialHighlightedIndex,\n      isItemDisabled,\n      initialIsOpen: true,\n    })\n\n    expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n    expect(isItemDisabled).toHaveBeenNthCalledWith(\n      1,\n      items[initialHighlightedIndex],\n      initialHighlightedIndex,\n    )\n  })\n  \n  test('defaultHighlightedIndex is ignored if item is disabled and menu is intially open', () => {\n    const defaultHighlightedIndex = 2\n    const isItemDisabled = jest\n      .fn()\n      .mockImplementation(\n        item => items.indexOf(item) === defaultHighlightedIndex,\n      )\n    renderSelect({\n      defaultHighlightedIndex,\n      isItemDisabled,\n      initialIsOpen: true,\n    })\n\n    expect(getToggleButton()).toHaveAttribute('aria-activedescendant', '')\n    expect(isItemDisabled).toHaveBeenNthCalledWith(\n      1,\n      items[defaultHighlightedIndex],\n      defaultHighlightedIndex,\n    )\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/returnProps.test.js",
    "content": "import {act} from '@testing-library/react'\nimport {items} from '../../testUtils'\nimport {renderUseSelect} from '../testUtils'\n\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport useSelect from '..'\n\ndescribe('returnProps', () => {\n  test('should have stateChangeTypes attached to hook', () => {\n    expect(useSelect).toHaveProperty('stateChangeTypes', stateChangeTypes)\n  })\n\n  describe('prop getters', () => {\n    test('are returned as functions', () => {\n      const {result} = renderUseSelect()\n\n      expect(result.current.getMenuProps).toBeInstanceOf(Function)\n      expect(result.current.getItemProps).toBeInstanceOf(Function)\n      expect(result.current.getLabelProps).toBeInstanceOf(Function)\n      expect(result.current.getToggleButtonProps).toBeInstanceOf(Function)\n    })\n  })\n\n  describe('actions', () => {\n    test('openMenu opens the closed menu', () => {\n      const {result} = renderUseSelect()\n\n      act(() => {\n        result.current.openMenu()\n      })\n\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('openMenu does nothing to open menu', () => {\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        result.current.openMenu()\n      })\n\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('closeMenu closes the open menu', () => {\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        result.current.closeMenu()\n      })\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('closeMenu does nothing to closed menu', () => {\n      const {result} = renderUseSelect()\n\n      act(() => {\n        result.current.closeMenu()\n      })\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('toggleMenu opens closed menu', () => {\n      const {result} = renderUseSelect({})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(result.current.isOpen).toBe(true)\n    })\n\n    test('toggleMenu closes open menu', () => {\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        result.current.toggleMenu()\n      })\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('setHighlightedIndex sets highlightedIndex', () => {\n      const {result} = renderUseSelect({initialIsOpen: true})\n\n      act(() => {\n        result.current.setHighlightedIndex(2)\n      })\n\n      expect(result.current.highlightedIndex).toBe(2)\n    })\n\n    test('setHighlightedIndex does not set highlightedIndex if item is disabled', () => {\n      const highlightedIndex = 2\n      const {result} = renderUseSelect({initialIsOpen: true, isItemDisabled(_item, index) {\n        return index === highlightedIndex\n      }})\n\n      act(() => {\n        result.current.setHighlightedIndex(highlightedIndex)\n      })\n\n      expect(result.current.highlightedIndex).toBe(-1)\n    })\n\n    test('selectItem sets selectedItem', () => {\n      const {result} = renderUseSelect()\n\n      act(() => {\n        result.current.selectItem(items[2])\n      })\n\n      expect(result.current.selectedItem).toBe(items[2])\n    })\n\n    test('reset sets the state to default values', () => {\n      const {result} = renderUseSelect()\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[2])\n        result.current.setHighlightedIndex(3)\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(null)\n      expect(result.current.highlightedIndex).toBe(-1)\n      expect(result.current.isOpen).toBe(false)\n      expect(result.current.inputValue).toBe('')\n    })\n\n    test('reset sets the state to default prop values passed by user', () => {\n      const props = {\n        defaultIsOpen: false,\n        defaultHighlightedIndex: 3,\n        defaultSelectedItem: items[2],\n      }\n      const {result} = renderUseSelect(props)\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[4])\n        result.current.setHighlightedIndex(1)\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(props.defaultSelectedItem)\n      expect(result.current.highlightedIndex).toBe(\n        props.defaultHighlightedIndex,\n      )\n      expect(result.current.isOpen).toBe(props.defaultIsOpen)\n    })\n\n    test('reset does not set the defaultHighlightedIndex if item is disabled', () => {\n      const props = {\n        defaultIsOpen: false,\n        defaultHighlightedIndex: 3,\n        defaultSelectedItem: items[2],\n        isItemDisabled(_item, index) {\n          return index === 3\n        },\n      }\n      const {result} = renderUseSelect(props)\n\n      act(() => {\n        result.current.openMenu()\n        result.current.selectItem(items[4])\n        result.current.setHighlightedIndex(1)\n        result.current.reset()\n      })\n\n      expect(result.current.selectedItem).toBe(props.defaultSelectedItem)\n      expect(result.current.highlightedIndex).toBe(-1)\n      expect(result.current.isOpen).toBe(props.defaultIsOpen)\n    })\n  })\n\n  describe('state and props', () => {\n    test('highlightedIndex is returned', () => {\n      const {result} = renderUseSelect({highlightedIndex: 4})\n\n      expect(result.current.highlightedIndex).toBe(4)\n    })\n\n    test('isOpen is returned', () => {\n      const {result} = renderUseSelect({isOpen: false})\n\n      expect(result.current.isOpen).toBe(false)\n    })\n\n    test('selectedItem is returned', () => {\n      const {result} = renderUseSelect({selectedItem: items[1]})\n\n      expect(result.current.selectedItem).toBe(items[1])\n    })\n\n    test('inputValue is returned', () => {\n      const {result} = renderUseSelect({inputValue: 'bla'})\n\n      expect(result.current.inputValue).toBe('bla')\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useSelect/__tests__/utils.test.ts",
    "content": "import {getItemIndexByCharacterKey} from '../utils'\nimport reducer from '../reducer'\n\ndescribe('getItemIndexByCharacterKey', () => {\n  const items = ['a', 'b', 'aba', 'aab', 'bab']\n  const itemToString = jest.fn().mockImplementation(item => item)\n  const isItemDisabled = jest.fn().mockReturnValue(false)\n\n  test('returns to check from start if from highlightedIndex does not find anything', () => {\n    const index = getItemIndexByCharacterKey({\n      keysSoFar: 'a',\n      highlightedIndex: 3,\n      items,\n      itemToString,\n      isItemDisabled,\n    })\n    expect(index).toBe(0)\n  })\n\n  test('checks from highlightedIndex position inclusively if there is more than one key', () => {\n    const index = getItemIndexByCharacterKey({\n      keysSoFar: 'aba',\n      highlightedIndex: 2,\n      items,\n      itemToString,\n      isItemDisabled,\n    })\n    expect(index).toBe(2)\n  })\n\n  test('checks from highlightedIndex position exclusively if there is only one key', () => {\n    const index = getItemIndexByCharacterKey({\n      keysSoFar: 'a',\n      highlightedIndex: 2,\n      items,\n      itemToString,\n      isItemDisabled,\n    })\n    expect(index).toBe(3)\n  })\n\n  test('skips disabled item and moves to next', () => {\n    const keysSoFar = 'b'\n    const highlightedIndex = 0\n\n    expect(\n      getItemIndexByCharacterKey({\n        keysSoFar,\n        highlightedIndex,\n        items,\n        itemToString,\n        isItemDisabled: jest\n          .fn()\n          .mockImplementation((_item, index) => index === 1),\n      }),\n    ).toEqual(4)\n  })\n})\n\ntest('reducer throws error if called without proper action type', () => {\n  expect(() => {\n    reducer({}, {}, {type: 'super-bogus'})\n  }).toThrow('Reducer called without proper action type.')\n})\n"
  },
  {
    "path": "src/hooks/useSelect/index.js",
    "content": "import {useRef, useEffect, useCallback, useMemo} from 'react'\nimport {useLatestRef, validatePropTypes} from '../../utils-ts'\nimport {\n  callAllEventHandlers,\n  handleRefs,\n  debounce,\n  normalizeArrowKey,\n} from '../../utils'\nimport {\n  isAcceptedCharacterKey,\n  getInitialState,\n  useGetterPropsCalledChecker,\n  useScrollIntoView,\n  useControlPropsValidator,\n  useMouseAndTouchTracker,\n  isDropdownsStateEqual,\n} from '../utils'\nimport {\n  useControlledReducer,\n  getInitialValue,\n  getItemAndIndex,\n  useA11yMessageStatus,\n} from '../utils-ts'\nimport {defaultStateValues} from '../utils.dropdown/defaultStateValues'\nimport {isReactNative, isReactNativeWeb} from '../../is.macro'\nimport downshiftSelectReducer from './reducer'\nimport {defaultProps, propTypes} from './utils'\nimport * as stateChangeTypes from './stateChangeTypes'\nimport { useElementIds } from '../utils.dropdown/useElementIds'\n\nuseSelect.stateChangeTypes = stateChangeTypes\n\nfunction useSelect(userProps = {}) {\n  validatePropTypes(userProps, useSelect, propTypes)\n  // Props defaults and destructuring.\n  const props = {\n    ...defaultProps,\n    ...userProps,\n  }\n  const {scrollIntoView, environment, getA11yStatusMessage} = props\n  // Initial state depending on controlled props.\n  const [state, dispatch] = useControlledReducer(\n    downshiftSelectReducer,\n    props,\n    getInitialState,\n    isDropdownsStateEqual,\n  )\n  const {isOpen, highlightedIndex, selectedItem, inputValue} = state\n  // Element efs.\n  const toggleButtonRef = useRef(null)\n  const menuRef = useRef(null)\n  const itemRefs = useRef({})\n\n  // used to keep the inputValue clearTimeout object between renders.\n  const clearTimeoutRef = useRef(null)\n  // prevent id re-generation between renders.\n  const elementIds = useElementIds(props)\n  // utility callback to get item element.\n  const latest = useLatestRef({\n    state,\n    props,\n  })\n\n  // Some utils.\n  const getItemNodeFromIndex = useCallback(\n    index => itemRefs.current[elementIds.getItemId(index)],\n    [elementIds],\n  )\n\n  // Effects.\n  // Adds an a11y aria live status message if getA11yStatusMessage is passed.\n  useA11yMessageStatus(\n    getA11yStatusMessage,\n    state,\n    [isOpen, highlightedIndex, selectedItem, inputValue],\n    environment,\n  )\n  // Scroll on highlighted item if change comes from keyboard.\n  const shouldScrollRef = useScrollIntoView({\n    menuElement: menuRef.current,\n    highlightedIndex,\n    isOpen,\n    itemRefs,\n    scrollIntoView,\n    getItemNodeFromIndex,\n  })\n  // Sets cleanup for the keysSoFar callback, debounded after 500ms.\n  useEffect(() => {\n    // init the clean function here as we need access to dispatch.\n    clearTimeoutRef.current = debounce(outerDispatch => {\n      outerDispatch({\n        type: stateChangeTypes.FunctionSetInputValue,\n        inputValue: '',\n      })\n    }, 500)\n\n    // Cancel any pending debounced calls on mount\n    return () => {\n      clearTimeoutRef.current.cancel()\n    }\n  }, [])\n  // Invokes the keysSoFar callback set up above.\n  useEffect(() => {\n    if (!inputValue) {\n      return\n    }\n\n    clearTimeoutRef.current(dispatch)\n  }, [dispatch, inputValue])\n\n  useControlPropsValidator({\n    props,\n    state,\n  })\n  // Focus the toggle button on first render if required.\n  useEffect(() => {\n    const focusOnOpen = getInitialValue(props, 'isOpen', defaultStateValues)\n\n    if (focusOnOpen && toggleButtonRef.current) {\n      toggleButtonRef.current.focus()\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [])\n\n  const handleBlurInTracker = useCallback(\n    function handleBlur() {\n      if (latest.current.state.isOpen) {\n        dispatch({\n          type: stateChangeTypes.ToggleButtonBlur,\n        })\n      }\n    },\n    [dispatch, latest],\n  )\n  const downshiftRefs = useMemo(() => [menuRef, toggleButtonRef], [])\n  const mouseAndTouchTrackers = useMouseAndTouchTracker(\n    environment,\n    handleBlurInTracker,\n    downshiftRefs,\n  )\n  const setGetterPropCallInfo = useGetterPropsCalledChecker(\n    'getMenuProps',\n    'getToggleButtonProps',\n  )\n  // Reset itemRefs on close.\n  useEffect(() => {\n    if (!isOpen) {\n      itemRefs.current = {}\n    }\n  }, [isOpen])\n\n  // Event handler functions.\n  const toggleButtonKeyDownHandlers = useMemo(\n    () => ({\n      ArrowDown(event) {\n        event.preventDefault()\n\n        dispatch({\n          type: stateChangeTypes.ToggleButtonKeyDownArrowDown,\n          altKey: event.altKey,\n        })\n      },\n      ArrowUp(event) {\n        event.preventDefault()\n\n        dispatch({\n          type: stateChangeTypes.ToggleButtonKeyDownArrowUp,\n          altKey: event.altKey,\n        })\n      },\n      Home(event) {\n        event.preventDefault()\n\n        dispatch({\n          type: stateChangeTypes.ToggleButtonKeyDownHome,\n        })\n      },\n      End(event) {\n        event.preventDefault()\n\n        dispatch({\n          type: stateChangeTypes.ToggleButtonKeyDownEnd,\n        })\n      },\n      Escape() {\n        if (latest.current.state.isOpen) {\n          dispatch({\n            type: stateChangeTypes.ToggleButtonKeyDownEscape,\n          })\n        }\n      },\n      Enter(event) {\n        event.preventDefault()\n\n        dispatch({\n          type: latest.current.state.isOpen\n            ? stateChangeTypes.ToggleButtonKeyDownEnter\n            : stateChangeTypes.ToggleButtonClick,\n        })\n      },\n      PageUp(event) {\n        if (latest.current.state.isOpen) {\n          event.preventDefault()\n\n          dispatch({\n            type: stateChangeTypes.ToggleButtonKeyDownPageUp,\n          })\n        }\n      },\n      PageDown(event) {\n        if (latest.current.state.isOpen) {\n          event.preventDefault()\n\n          dispatch({\n            type: stateChangeTypes.ToggleButtonKeyDownPageDown,\n          })\n        }\n      },\n      ' '(event) {\n        event.preventDefault()\n\n        const currentState = latest.current.state\n\n        if (!currentState.isOpen) {\n          dispatch({type: stateChangeTypes.ToggleButtonClick})\n          return\n        }\n\n        if (currentState.inputValue) {\n          dispatch({\n            type: stateChangeTypes.ToggleButtonKeyDownCharacter,\n            key: ' ',\n          })\n        } else {\n          dispatch({type: stateChangeTypes.ToggleButtonKeyDownSpaceButton})\n        }\n      },\n    }),\n    [dispatch, latest],\n  )\n\n  // Action functions.\n  const toggleMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionToggleMenu,\n    })\n  }, [dispatch])\n  const closeMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionCloseMenu,\n    })\n  }, [dispatch])\n  const openMenu = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionOpenMenu,\n    })\n  }, [dispatch])\n  const setHighlightedIndex = useCallback(\n    newHighlightedIndex => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetHighlightedIndex,\n        highlightedIndex: newHighlightedIndex,\n      })\n    },\n    [dispatch],\n  )\n  const selectItem = useCallback(\n    newSelectedItem => {\n      dispatch({\n        type: stateChangeTypes.FunctionSelectItem,\n        selectedItem: newSelectedItem,\n      })\n    },\n    [dispatch],\n  )\n  const reset = useCallback(() => {\n    dispatch({\n      type: stateChangeTypes.FunctionReset,\n    })\n  }, [dispatch])\n  const setInputValue = useCallback(\n    newInputValue => {\n      dispatch({\n        type: stateChangeTypes.FunctionSetInputValue,\n        inputValue: newInputValue,\n      })\n    },\n    [dispatch],\n  )\n  // Getter functions.\n  const getLabelProps = useCallback(\n    ({onClick, ...labelProps} = {}) => {\n      const labelHandleClick = () => {\n        toggleButtonRef.current?.focus()\n      }\n\n      return {\n        id: elementIds.labelId,\n        htmlFor: elementIds.toggleButtonId,\n        onClick: callAllEventHandlers(onClick, labelHandleClick),\n        ...labelProps,\n      }\n    },\n    [elementIds],\n  )\n  const getMenuProps = useCallback(\n    (\n      {onMouseLeave, refKey = 'ref', ref, ...rest} = {},\n      {suppressRefError = false} = {},\n    ) => {\n      const menuHandleMouseLeave = () => {\n        dispatch({\n          type: stateChangeTypes.MenuMouseLeave,\n        })\n      }\n\n      setGetterPropCallInfo('getMenuProps', suppressRefError, refKey, menuRef)\n\n      return {\n        [refKey]: handleRefs(ref, menuNode => {\n          menuRef.current = menuNode\n        }),\n        id: elementIds.menuId,\n        role: 'listbox',\n        'aria-labelledby':\n          rest && rest['aria-label'] ? undefined : `${elementIds.labelId}`,\n        onMouseLeave: callAllEventHandlers(onMouseLeave, menuHandleMouseLeave),\n        ...rest,\n      }\n    },\n    [dispatch, setGetterPropCallInfo, elementIds],\n  )\n  const getToggleButtonProps = useCallback(\n    (\n      {onBlur, onClick, onPress, onKeyDown, refKey = 'ref', ref, ...rest} = {},\n      {suppressRefError = false} = {},\n    ) => {\n      const latestState = latest.current.state\n      const toggleButtonHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.ToggleButtonClick,\n        })\n      }\n      const toggleButtonHandleBlur = () => {\n        if (latestState.isOpen && !mouseAndTouchTrackers.isMouseDown) {\n          dispatch({\n            type: stateChangeTypes.ToggleButtonBlur,\n          })\n        }\n      }\n      const toggleButtonHandleKeyDown = event => {\n        const key = normalizeArrowKey(event)\n        if (key && toggleButtonKeyDownHandlers[key]) {\n          toggleButtonKeyDownHandlers[key](event)\n        } else if (isAcceptedCharacterKey(key)) {\n          dispatch({\n            type: stateChangeTypes.ToggleButtonKeyDownCharacter,\n            key,\n          })\n        }\n      }\n      const toggleProps = {\n        [refKey]: handleRefs(ref, toggleButtonNode => {\n          toggleButtonRef.current = toggleButtonNode\n        }),\n        'aria-activedescendant':\n          latestState.isOpen && latestState.highlightedIndex > -1\n            ? elementIds.getItemId(latestState.highlightedIndex)\n            : '',\n        'aria-controls': elementIds.menuId,\n        'aria-expanded': latest.current.state.isOpen,\n        'aria-haspopup': 'listbox',\n        'aria-labelledby':\n          rest && rest['aria-label'] ? undefined : `${elementIds.labelId}`,\n        id: elementIds.toggleButtonId,\n        role: 'combobox',\n        tabIndex: 0,\n        onBlur: callAllEventHandlers(onBlur, toggleButtonHandleBlur),\n        ...rest,\n      }\n\n      if (!rest.disabled) {\n        /* istanbul ignore if (react-native) */\n        if (isReactNative || isReactNativeWeb) {\n          toggleProps.onPress = callAllEventHandlers(\n            onPress,\n            toggleButtonHandleClick,\n          )\n        } else {\n          toggleProps.onClick = callAllEventHandlers(\n            onClick,\n            toggleButtonHandleClick,\n          )\n          toggleProps.onKeyDown = callAllEventHandlers(\n            onKeyDown,\n            toggleButtonHandleKeyDown,\n          )\n        }\n      }\n\n      setGetterPropCallInfo(\n        'getToggleButtonProps',\n        suppressRefError,\n        refKey,\n        toggleButtonRef,\n      )\n\n      return toggleProps\n    },\n    [\n      dispatch,\n      elementIds,\n      latest,\n      mouseAndTouchTrackers,\n      setGetterPropCallInfo,\n      toggleButtonKeyDownHandlers,\n    ],\n  )\n  const getItemProps = useCallback(\n    ({\n      item: itemProp,\n      index: indexProp,\n      onMouseMove,\n      onClick,\n      onMouseDown,\n      onPress,\n      refKey = 'ref',\n      disabled: disabledProp,\n      ref,\n      ...rest\n    } = {}) => {\n      if (disabledProp !== undefined) {\n        console.warn(\n          'Passing \"disabled\" as an argument to getItemProps is not supported anymore. Please use the isItemDisabled prop from useSelect.',\n        )\n      }\n\n      const {state: latestState, props: latestProps} = latest.current\n      const [item, index] = getItemAndIndex(\n        itemProp,\n        indexProp,\n        latestProps.items,\n        'Pass either item or index to getItemProps!',\n      )\n      const disabled = latestProps.isItemDisabled(item, index)\n\n      const itemHandleMouseMove = () => {\n        if (\n          mouseAndTouchTrackers.isTouchEnd ||\n          index === latestState.highlightedIndex\n        ) {\n          return\n        }\n        shouldScrollRef.current = false\n        dispatch({\n          type: stateChangeTypes.ItemMouseMove,\n          index,\n          disabled,\n        })\n      }\n      const itemHandleClick = () => {\n        dispatch({\n          type: stateChangeTypes.ItemClick,\n          index,\n        })\n      }\n      const itemHandleMouseDown = e => e.preventDefault() // keep focus on the toggle after item click select.\n\n      const itemProps = {\n        [refKey]: handleRefs(ref, itemNode => {\n          if (itemNode) {\n            itemRefs.current[elementIds.getItemId(index)] = itemNode\n          }\n        }),\n        'aria-disabled': disabled,\n        'aria-selected': item === latestState.selectedItem,\n        id: elementIds.getItemId(index),\n        role: 'option',\n        ...rest,\n      }\n\n      if (!disabled) {\n        /* istanbul ignore next (react-native) */\n        if (isReactNative || isReactNativeWeb) {\n          itemProps.onPress = callAllEventHandlers(onPress, itemHandleClick)\n        } else {\n          itemProps.onClick = callAllEventHandlers(onClick, itemHandleClick)\n        }\n      }\n\n      itemProps.onMouseMove = callAllEventHandlers(\n        onMouseMove,\n        itemHandleMouseMove,\n      )\n      itemProps.onMouseDown = callAllEventHandlers(\n        onMouseDown,\n        itemHandleMouseDown,\n      )\n\n      return itemProps\n    },\n    [latest, elementIds, mouseAndTouchTrackers, shouldScrollRef, dispatch],\n  )\n\n  return {\n    // prop getters.\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getItemProps,\n    // actions.\n    toggleMenu,\n    openMenu,\n    closeMenu,\n    setHighlightedIndex,\n    selectItem,\n    reset,\n    setInputValue,\n    // state.\n    highlightedIndex,\n    isOpen,\n    selectedItem,\n    inputValue,\n  }\n}\n\nexport default useSelect\n"
  },
  {
    "path": "src/hooks/useSelect/reducer.js",
    "content": "import {getNonDisabledIndex, getHighlightedIndex} from '../../utils'\nimport {\n  getHighlightedIndexOnOpen,\n  getChangesOnSelection,\n  getDefaultHighlightedIndex,\n} from '../utils'\nimport {getDefaultValue} from '../utils-ts'\nimport commonReducer from '../reducer'\nimport {defaultStateValues} from '../utils.dropdown/defaultStateValues'\nimport {getItemIndexByCharacterKey} from './utils'\nimport * as stateChangeTypes from './stateChangeTypes'\n\n/* eslint-disable complexity */\nexport default function downshiftSelectReducer(state, props, action) {\n  const {type, altKey} = action\n  let changes\n\n  switch (type) {\n    case stateChangeTypes.ItemClick:\n      changes = {\n        isOpen: getDefaultValue(props, 'isOpen', defaultStateValues),\n        highlightedIndex: getDefaultHighlightedIndex(props),\n        selectedItem: props.items[action.index],\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownCharacter:\n      {\n        const lowercasedKey = action.key\n        const inputValue = `${state.inputValue}${lowercasedKey}`\n        const prevHighlightedIndex =\n          !state.isOpen && state.selectedItem\n            ? props.items.findIndex(\n                item =>\n                  props.itemToKey(item) === props.itemToKey(state.selectedItem),\n              )\n            : state.highlightedIndex\n        const highlightedIndex = getItemIndexByCharacterKey({\n          keysSoFar: inputValue,\n          highlightedIndex: prevHighlightedIndex,\n          items: props.items,\n          itemToString: props.itemToString,\n          isItemDisabled: props.isItemDisabled,\n        })\n\n        changes = {\n          inputValue,\n          highlightedIndex,\n          isOpen: true,\n        }\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownArrowDown:\n      {\n        const highlightedIndex = state.isOpen\n          ? getHighlightedIndex(\n              state.highlightedIndex,\n              1,\n              props.items,\n              props.isItemDisabled,\n            )\n          : altKey && state.selectedItem == null\n          ? -1\n          : getHighlightedIndexOnOpen(props, state, 1)\n        changes = {\n          highlightedIndex,\n          isOpen: true,\n        }\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownArrowUp:\n      if (state.isOpen && altKey) {\n        changes = getChangesOnSelection(props, state.highlightedIndex, false)\n      } else {\n        const highlightedIndex = state.isOpen\n          ? getHighlightedIndex(\n              state.highlightedIndex,\n              -1,\n              props.items,\n              props.isItemDisabled,\n            )\n          : getHighlightedIndexOnOpen(props, state, -1)\n        changes = {\n          highlightedIndex,\n          isOpen: true,\n        }\n      }\n\n      break\n    // only triggered when menu is open.\n    case stateChangeTypes.ToggleButtonKeyDownEnter:\n    case stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n      changes = getChangesOnSelection(props, state.highlightedIndex, false)\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownHome:\n      changes = {\n        highlightedIndex: getNonDisabledIndex(\n          0,\n          false,\n          props.items,\n          props.isItemDisabled,\n        ),\n        isOpen: true,\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownEnd:\n      changes = {\n        highlightedIndex: getNonDisabledIndex(\n          props.items.length - 1,\n          true,\n          props.items,\n          props.isItemDisabled,\n        ),\n        isOpen: true,\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonKeyDownPageUp:\n      changes = {\n        highlightedIndex: getHighlightedIndex(\n          state.highlightedIndex,\n          -10,\n          props.items,\n          props.isItemDisabled,\n        ),\n      }\n      break\n    case stateChangeTypes.ToggleButtonKeyDownPageDown:\n      changes = {\n        highlightedIndex: getHighlightedIndex(\n          state.highlightedIndex,\n          10,\n          props.items,\n          props.isItemDisabled,\n        ),\n      }\n      break\n    case stateChangeTypes.ToggleButtonKeyDownEscape:\n      changes = {\n        isOpen: false,\n        highlightedIndex: -1,\n      }\n\n      break\n    case stateChangeTypes.ToggleButtonBlur:\n      changes = {\n        isOpen: false,\n        highlightedIndex: -1,\n        ...(state.highlightedIndex >= 0 &&\n          props.items?.length && {\n            selectedItem: props.items[state.highlightedIndex],\n          }),\n      }\n\n      break\n    case stateChangeTypes.FunctionSelectItem:\n      changes = {\n        selectedItem: action.selectedItem,\n      }\n\n      break\n    default:\n      return commonReducer(state, props, action, stateChangeTypes)\n  }\n\n  return {\n    ...state,\n    ...changes,\n  }\n}\n/* eslint-enable complexity */\n"
  },
  {
    "path": "src/hooks/useSelect/stateChangeTypes.js",
    "content": "import productionEnum from '../../productionEnum.macro'\n\nexport const ToggleButtonClick = productionEnum('__togglebutton_click__')\nexport const ToggleButtonKeyDownArrowDown = productionEnum(\n  '__togglebutton_keydown_arrow_down__',\n)\nexport const ToggleButtonKeyDownArrowUp = productionEnum(\n  '__togglebutton_keydown_arrow_up__',\n)\nexport const ToggleButtonKeyDownCharacter = productionEnum(\n  '__togglebutton_keydown_character__',\n)\nexport const ToggleButtonKeyDownEscape = productionEnum(\n  '__togglebutton_keydown_escape__',\n)\nexport const ToggleButtonKeyDownHome = productionEnum(\n  '__togglebutton_keydown_home__',\n)\nexport const ToggleButtonKeyDownEnd = productionEnum(\n  '__togglebutton_keydown_end__',\n)\nexport const ToggleButtonKeyDownEnter = productionEnum(\n  '__togglebutton_keydown_enter__',\n)\nexport const ToggleButtonKeyDownSpaceButton = productionEnum(\n  '__togglebutton_keydown_space_button__',\n)\nexport const ToggleButtonKeyDownPageUp = productionEnum(\n  '__togglebutton_keydown_page_up__',\n)\nexport const ToggleButtonKeyDownPageDown = productionEnum(\n  '__togglebutton_keydown_page_down__',\n)\nexport const ToggleButtonBlur = productionEnum('__togglebutton_blur__')\n\nexport const MenuMouseLeave = productionEnum('__menu_mouse_leave__')\n\nexport const ItemMouseMove = productionEnum('__item_mouse_move__')\nexport const ItemClick = productionEnum('__item_click__')\n\nexport const FunctionToggleMenu = productionEnum('__function_toggle_menu__')\nexport const FunctionOpenMenu = productionEnum('__function_open_menu__')\nexport const FunctionCloseMenu = productionEnum('__function_close_menu__')\nexport const FunctionSetHighlightedIndex = productionEnum(\n  '__function_set_highlighted_index__',\n)\nexport const FunctionSelectItem = productionEnum('__function_select_item__')\nexport const FunctionSetInputValue = productionEnum(\n  '__function_set_input_value__',\n)\nexport const FunctionReset = productionEnum('__function_reset__')\n"
  },
  {
    "path": "src/hooks/useSelect/testUtils.js",
    "content": "import * as React from 'react'\nimport {render, act, renderHook} from '@testing-library/react'\nimport {dropdownDefaultProps} from '../utils.dropdown'\nimport {\n  clickOnItemAtIndex,\n  clickOnToggleButton,\n  dataTestIds,\n  items,\n  keyDownOnToggleButton,\n  mouseLeaveItemAtIndex,\n  mouseMoveItemAtIndex,\n  tab,\n} from '../testUtils'\nimport * as stateChangeTypes from './stateChangeTypes'\nimport useSelect from '.'\n\nexport * from '../testUtils'\n\njest.mock('../utils', () => {\n  const utils = jest.requireActual('../utils')\n  const hooksUtils = jest.requireActual('../../utils-ts')\n\n  return {\n    ...utils,\n    useGetterPropsCalledChecker: () => hooksUtils.noop,\n  }\n})\n\n// We are using React 18.\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\nbeforeEach(jest.resetAllMocks)\nafterAll(jest.restoreAllMocks)\n\nexport function renderUseSelect(props) {\n  return renderHook(() => useSelect({items, ...props}))\n}\nexport function renderSelect(props, uiCallback) {\n  const renderSpy = jest.fn()\n  const ui = <DropdownSelect renderSpy={renderSpy} {...props} />\n  const utils = render(uiCallback ? uiCallback(ui) : ui)\n  const rerender = p =>\n    utils.rerender(<DropdownSelect renderSpy={renderSpy} {...p} />)\n\n  return {\n    ...utils,\n    renderSpy,\n    rerender,\n  }\n}\n\nexport function DropdownSelect({renderSpy, renderItem, ...props}) {\n  const {\n    isOpen,\n    selectedItem,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getItemProps,\n  } = useSelect({items, ...props})\n  const itemToString = props?.itemToString ?? dropdownDefaultProps.itemToString\n\n  renderSpy()\n\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div data-testid={dataTestIds.toggleButton} {...getToggleButtonProps()}>\n        {(selectedItem && selectedItem instanceof Object\n          ? itemToString(selectedItem)\n          : selectedItem) || 'Elements'}\n      </div>\n      <ul data-testid={dataTestIds.menu} {...getMenuProps()}>\n        {isOpen\n          ? (props.items || items).map((item, index) => {\n              const stringItem =\n                item instanceof Object ? itemToString(item) : item\n              return renderItem ? (\n                renderItem({index, item, getItemProps, stringItem})\n              ) : (\n                <li\n                  data-testid={dataTestIds.item(index)}\n                  key={`${stringItem}${index}`}\n                  {...getItemProps({item, index})}\n                >\n                  {stringItem}\n                </li>\n              )\n            })\n          : null}\n      </ul>\n    </div>\n  )\n}\n\n/**\n * Return the id of the item that strats with the caracter.\n * @param {string} character The start of the item string.\n * @param {number} startIndex The index to start searching.\n * @returns number The index of the item.\n */\nexport function getItemIndexByCharacter(character, startIndex = 0) {\n  return (\n    items.slice(startIndex).findIndex(item => {\n      // console.log(item.toLowerCase(), character.toLowerCase(), item.toLowerCase().startsWith(character.toLowerCase()))\n\n      return item.toLowerCase().startsWith(character.toLowerCase())\n    }) + startIndex\n  )\n}\n\nexport const stateChangeTestCases = [\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Enter}',\n    state: {\n      isOpen: true,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: null,\n    },\n    type: stateChangeTypes.ToggleButtonClick,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Enter}',\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: null,\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownEnter,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: ' ',\n    state: {\n      isOpen: true,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: null,\n    },\n    type: stateChangeTypes.ToggleButtonClick,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Escape}',\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: null,\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownEscape,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{ArrowDown}',\n    state: {\n      isOpen: true,\n      highlightedIndex: 0,\n      inputValue: '',\n      selectedItem: null,\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Enter}',\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[0],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownEnter,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{End}',\n    state: {\n      isOpen: true,\n      highlightedIndex: items.length - 1,\n      inputValue: '',\n      selectedItem: items[0],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownEnd,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: ' ',\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownSpaceButton,\n  },\n  {\n    step: clickOnToggleButton,\n    state: {\n      isOpen: true,\n      highlightedIndex: items.length - 1,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ToggleButtonClick,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{PageUp}',\n    state: {\n      isOpen: true,\n      highlightedIndex: items.length - 11,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownPageUp,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{PageDown}',\n    state: {\n      isOpen: true,\n      highlightedIndex: items.length - 1,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownPageDown,\n  },\n  {\n    step: mouseMoveItemAtIndex,\n    arg: items.length - 2,\n    state: {\n      isOpen: true,\n      highlightedIndex: items.length - 2,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ItemMouseMove,\n  },\n  {\n    step: mouseLeaveItemAtIndex,\n    arg: items.length - 2,\n    state: {\n      isOpen: true,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.MenuMouseLeave,\n  },\n  {\n    step: mouseMoveItemAtIndex,\n    arg: 2,\n    state: {\n      isOpen: true,\n      highlightedIndex: 2,\n      inputValue: '',\n      selectedItem: items[items.length - 1],\n    },\n    type: stateChangeTypes.ItemMouseMove,\n  },\n  {\n    step: clickOnItemAtIndex,\n    arg: 2,\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[2],\n    },\n    type: stateChangeTypes.ItemClick,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Alt>}{ArrowDown}{/Alt}',\n    state: {\n      isOpen: true,\n      highlightedIndex: 2,\n      inputValue: '',\n      selectedItem: items[2],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Alt>}{ArrowDown}{/Alt}',\n    state: {\n      isOpen: true,\n      highlightedIndex: 3,\n      inputValue: '',\n      selectedItem: items[2],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownArrowDown,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: '{Alt>}{ArrowUp}{/Alt}',\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[3],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownArrowUp,\n  },\n  {\n    step: keyDownOnToggleButton,\n    arg: 'c',\n    state: {\n      isOpen: true,\n      highlightedIndex: 5,\n      inputValue: 'c',\n      selectedItem: items[3],\n    },\n    type: stateChangeTypes.ToggleButtonKeyDownCharacter,\n  },\n  {\n    step: () =>\n      // just to have all steps async.\n      new Promise(resolve => {\n        act(() => jest.runAllTimers())\n        resolve()\n      }),\n    state: {\n      isOpen: true,\n      highlightedIndex: 5,\n      inputValue: '',\n      selectedItem: items[3],\n    },\n    type: stateChangeTypes.FunctionSetInputValue,\n  },\n  {\n    step: tab,\n    state: {\n      isOpen: false,\n      highlightedIndex: -1,\n      inputValue: '',\n      selectedItem: items[5],\n    },\n    type: stateChangeTypes.ToggleButtonBlur,\n  },\n]\n"
  },
  {
    "path": "src/hooks/useSelect/utils/defaultProps.ts",
    "content": "import {dropdownDefaultProps} from '../../utils.dropdown'\n\nexport const defaultProps = {\n  ...dropdownDefaultProps,\n  isItemDisabled() {\n    return false\n  },\n}\n"
  },
  {
    "path": "src/hooks/useSelect/utils/getItemIndexByCharacterKey.ts",
    "content": "type GetItemIndexByCharacterKeyOptions<Item> = {\n  keysSoFar: string\n  highlightedIndex: number\n  items: Item[]\n  itemToString(item: Item | null): string\n  isItemDisabled(item: Item, index: number): boolean\n}\n\nexport function getItemIndexByCharacterKey<Item>({\n  keysSoFar,\n  highlightedIndex,\n  items,\n  itemToString,\n  isItemDisabled,\n}: GetItemIndexByCharacterKeyOptions<Item>) {\n  const lowerCasedKeysSoFar = keysSoFar.toLowerCase()\n\n  for (let index = 0; index < items.length; index++) {\n    // if we already have a search query in progress, we also consider the current highlighted item.\n    const offsetIndex =\n      (index + highlightedIndex + (keysSoFar.length < 2 ? 1 : 0)) % items.length\n\n    const item = items[offsetIndex]\n\n    if (\n      item !== undefined &&\n      itemToString(item).toLowerCase().startsWith(lowerCasedKeysSoFar) &&\n      !isItemDisabled(item, offsetIndex)\n    ) {\n      return offsetIndex\n    }\n  }\n\n  return highlightedIndex\n}\n"
  },
  {
    "path": "src/hooks/useSelect/utils/index.ts",
    "content": "export {propTypes} from './propTypes'\nexport {defaultProps} from './defaultProps'\nexport {getItemIndexByCharacterKey} from './getItemIndexByCharacterKey'\n"
  },
  {
    "path": "src/hooks/useSelect/utils/propTypes.ts",
    "content": "import PropTypes from 'prop-types'\nimport {dropdownPropTypes} from '../../utils.dropdown'\n\nexport const propTypes = {\n  ...dropdownPropTypes,\n  items: PropTypes.array.isRequired,\n  isItemDisabled: PropTypes.func,\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/README.md",
    "content": "# useTagGroup\n\n## The problem\n\nYou want to build a tag group component in your app that's accessible and offers\na great user experience. There is no dedicated ARIA design pattern for this\ncomponent, but since it's widely used, we compiled the list of specifications\nand implemented them through a React hook that's compliant with Downshift's\nprinciples.\n\n## This solution\n\n`useTagGroup` is a React hook that manages all the stateful logic needed to make\nthe tag group functional and accessible. It returns a set of props that are\nmeant to be called and their results destructured on the tag group's elements:\nits container, tag item and tag remove button. The props are similar to the ones\nprovided by vanilla `<Downshift>` to the children render prop.\n\nThese props are called getter props, and their return values are destructured as\na set of ARIA attributes and event listeners. Together with the action props and\nstate props, they create all the stateful logic needed for the tag group to\nimplement the list of requirements. Every functionality needed should be\nprovided out-of-the-box: item removal and selection, and left/right arrow\nmovement between items, screen reader support etc.\n\n## Table of Contents\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Usage](#usage)\n- [Basic Props](#basic-props)\n  - [removeElementDescription](#removeelementdescription)\n  - [onItemsChange](#onitemschange)\n  - [stateReducer](#statereducer)\n- [Advanced Props](#advanced-props)\n  - [isItemDisabled](#isitemdisabled)\n  - [initialItems](#initialitems)\n  - [initialActiveIndex](#initialactiveindex)\n  - [onActiveIndexChange](#onactiveindexchange)\n  - [onStateChange](#onstatechange)\n  - [activeIndex](#activeindex)\n  - [items](#items)\n  - [id](#id)\n  - [tagGroupId](#taggroupid)\n  - [getTagId](#gettagid)\n  - [environment](#environment)\n- [stateChangeTypes](#statechangetypes)\n- [Control Props](#control-props)\n- [Returned props](#returned-props)\n  - [prop getters](#prop-getters)\n    - [`getTagGroupProps`](#gettaggroupprops)\n    - [`getTagProps`](#gettagprops)\n    - [`getTagRemoveProps`](#gettagremoveprops)\n  - [actions](#actions)\n  - [state](#state)\n- [Event Handlers](#event-handlers)\n  - [Default handlers](#default-handlers)\n    - [Tag Group](#tag-group)\n    - [Tag](#tag)\n  - [Tag Remove](#tag-remove)\n  - [Customizing Handlers](#customizing-handlers)\n- [Examples](#examples)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## Usage\n\n> [Try it out in the browser][sandbox-example]\n\n```jsx\nimport * as React from 'react'\nimport {render} from 'react-dom'\nimport {useTagGroup} from 'downshift'\n\nconst colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nfunction TagGroup() {\n  const initialItems = colors.slice(0, 5)\n  const {\n    addItem,\n    getTagProps,\n    getTagRemoveProps,\n    getTagGroupProps,\n    items,\n    activeIndex,\n  } = useTagGroup({initialItems})\n\n  const itemsToAdd = colors.filter(color => !items.includes(color))\n\n  return (\n    <div>\n      <div\n        {...getTagGroupProps({'aria-label': 'colors example'})}\n        style={{\n          display: 'inline-flex',\n          gap: '8px',\n          alignItems: 'center',\n          flexWrap: 'wrap',\n          padding: '6px',\n        }}\n      >\n        {items.map((color, index) => (\n          <span\n            key={color}\n            {...getTagProps({index, 'aria-label': color})}\n            style={{\n              border: '1px solid darkgreen',\n              backgroundColor: 'green',\n              padding: '0 6px',\n              margin: '0 2px',\n              borderRadius: '10px',\n              cursor: 'default',\n              fontStyle: index === activeIndex ? 'italic' : 'normal',\n            }}\n          >\n            {color}\n            <button\n              type=\"button\"\n              {...getTagRemoveProps({index, 'aria-label': 'remove'})}\n              style={{\n                padding: '4px',\n                cursor: 'pointer',\n                border: 'none',\n                backgroundColor: 'transparent',\n              }}\n            >\n              &#10005;\n            </button>\n          </span>\n        ))}\n      </div>\n\n      <div>Add more items:</div>\n\n      <ul>\n        {itemsToAdd.map(item => (\n          <li key={item}>\n            <button\n              tabIndex={0}\n              onClick={() => addItem(item)}\n              onKeyDown={({key}) => {\n                key === 'Enter' && addItem(item)\n              }}\n              style={{\n                cursor: 'pointer',\n                background: 'none',\n                border: 'none',\n                padding: 0,\n              }}\n            >\n              {item}\n            </button>\n          </li>\n        ))}\n      </ul>\n    </div>\n  )\n}\n\nrender(<TagGroup />, document.getElementById('root'))\n```\n\n## Basic Props\n\nThis is the list of props that you should probably know about. There are some\n[advanced props](#advanced-props) below as well.\n\n### removeElementDescription\n\n> `string` | defaults to: `\"Press Delete or Backspace to remove tag.\"`\n\nAn accessible description that gets added to the DOM in an invisible element and\ngets picked up by screen readers when encountering tags. It should instruct\nusers how to remove tags with the keyboard. Useful for localized messages.\n\n### onItemsChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the items in state changed. Adding items can be done using\n`addItem`, while removing items could be done with mouse and keyboard actions.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `items` property with\n  the newly selected value. This also has a `type` property which you can learn\n  more about in the [`stateChangeTypes`](#statechangetypes) section. This\n  property will be part of the actions that can trigger a `items` change, for\n  example `useTagGroup.stateChangeTypes.FunctionAddItem`.\n\n### stateReducer\n\n> `function(state: object, actionAndChanges: object)` | optional\n\n**🚨 This is a really handy power feature 🚨**\n\nThis function will be called each time `useTagGroup` sets its internal state (or\ncalls your `onStateChange` handler for control props). It allows you to modify\nthe state change that will take place which can give you fine grain control over\nhow the component interacts with user updates. It gives you the current state\nand the state that will be set, and you return the state that you want to set.\n\n- `state`: The full current state of useTagGroup.\n- `actionAndChanges`: Object that contains the action `type`, props needed to\n  return a new state based on that type and the changes suggested by the\n  useTagGroup default reducer. About the `type` property you can learn more\n  about in the [`stateChangeTypes`](#statechangetypes) section.\n\n```javascript\nimport {useTagGroup} from 'downshift'\nimport {items} from './utils'\n\nconst {getTagGroupProps, getTagProps, getTagRemoveProps, ...rest} = useTagGroup(\n  {\n    initialItems: items.slice(0, 4),\n    stateReducer,\n  },\n)\n\nfunction stateReducer(state, actionAndChanges) {\n  const {type, changes} = actionAndChanges\n  // resets active item to the first when removing an item\n  switch (type) {\n    case useSelect.stateChangeTypes.TagGroupKeyDownBackspace:\n    case useSelect.stateChangeTypes.TagGroupKeyDownDelete:\n      return {\n        ...changes, // default tagGroup new state changes on item removal.\n        activeIndex: changes.items.length === 0 ? -1 : 0,\n      }\n    default:\n      return changes // otherwise business as usual.\n  }\n}\n```\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> use this to handle events. If you wish to handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example `<button onBlur={handleBlur} />` should be\n> `<button {...getTagRemoveProps({onBlur: handleBlur})} />`). Also, your reducer\n> function should be \"pure.\" This means it should do nothing other than return\n> the state changes you want to have happen.\n\n## Advanced Props\n\n### isItemDisabled\n\n> `function(item: any, index: number)` | defaults to: `(_item, _index) => false`\n\nIf an item needs to be marked as disabled, this function needs to return `true`\nfor that item. Disabled items will be skipped from keyboard navigation, will not\nbe removed and will be marked as disabled for screen readers.\n\n### initialItems\n\n> `any[]` | defaults to `null`\n\nPass an items collection that should be selected when useTagGroup is\ninitialized.\n\n### initialActiveIndex\n\n> `number` | defaults to `0` if there are initial items or `-1` otherwise.\n\nPass a boolean that sets the open state of the menu when useTagGroup is\ninitialized.\n\n### onActiveIndexChange\n\n> `function(changes: object)` | optional, no useful default\n\nCalled each time the active index was changed. Active item can be changed by\nclicking the tag or by using left and right arrows while a tag is focused.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This object is guaranteed to contain the `activeIndex` property\n  with the new value. This also has a `type` property which you can learn more\n  about in the [`stateChangeTypes`](#statechangetypes) section. This property\n  will be part of the actions that can trigger a `activeIndex` change, for\n  example `useTagGroup.stateChangeTypes.TagGroupKeyDownArrowRight`.\n\n### onStateChange\n\n> `function(changes: object)` | optional, no useful default\n\nThis function is called anytime the internal state changes. This can be useful\nif you're using useTagGroup as a \"controlled\" hook, where you manage some or all\nof the state (e.g., isOpen, selectedItem, highlightedIndex, etc) and then pass\nit as props, rather than letting useTagGroup control all its state itself.\n\n- `changes`: These are the properties that actually have changed since the last\n  state change. This also has a `type` property which you can learn more about\n  in the [`stateChangeTypes`](#statechangetypes) section.\n\n> Tip: This function will be called any time _any_ state is changed. The best\n> way to determine whether any particular state was changed, you can use\n> `changes.hasOwnProperty('propName')` or use the `on[statePropKey]Change` props\n> described above.\n\n> NOTE: This is only called when state actually changes. You should not attempt\n> to use this to handle events. If you wish handle events, put your event\n> handlers directly on the elements (make sure to use the prop getters though!\n> For example: `<span onBlur={handleBlur} />` should be\n> `<span {...getTagProps({onBlur: handleBlur})} />`).\n\n### activeIndex\n\n> `number` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe index of the item that should be active so that it receives focus.\n\n### items\n\n> `any[]` | **control prop** (read more about this in\n> [the Control Props section](#control-props))\n\nThe items that are part of the tag group\n\n### id\n\n> `string` | defaults to a generated ID\n\nUsed to generate the first part of the useTagGroup id on the elements. You can\noverride this `id` with one of your own, provided as a prop, or you can override\nthe `id` for each element altogether using the props below.\n\n### tagGroupId\n\n> `string` | defaults to a generated ID\n\nUsed for `aria` attributes and the `id` prop of the element you use\n[`getTagGroupProps`](#gettaggroupprops) with.\n\n### getTagId\n\n> `function(index)` | defaults to a function that generates an ID based on the\n> index\n\nUsed for `aria` attributes and the `id` prop of the element (`li`) you use\n[`getTagProps`](#gettagprops) and [`getTagRemoveProps`](#gettagremoveprops)\nwith.\n\n### environment\n\n> `window` | defaults to `window`\n\nThis prop is only useful if you're rendering downshift within a different\n`window` context from where your JavaScript is running; for example, an iframe\nor a shadow-root. If the given context is lacking `document` and/or\n`add|removeEventListener` on its prototype (as is the case for a shadow-root)\nthen you will need to pass in a custom object that is able to provide\n[access to these properties](https://gist.github.com/Rendez/1dd55882e9b850dd3990feefc9d6e177)\nfor downshift.\n\n## stateChangeTypes\n\nThere are a few props that expose changes to state\n([`onStateChange`](#onstatechange) and [`stateReducer`](#statereducer)). For you\nto make the most of these APIs, it's important for you to understand why state\nis being changed. To accomplish this, there's a `type` property on the `changes`\nobject you get. This `type` corresponds to a `stateChangeTypes` property.\n\nThe list of all possible values this `type` property can take is defined in\n[this file][state-change-file] and is as follows:\n\n- `useTagGroup.stateChangeTypes.TagClick`\n- `useTagGroup.stateChangeTypes.TagGroupKeyDownArrowLeft`\n- `useTagGroup.stateChangeTypes.TagGroupKeyDownArrowRight`\n- `useTagGroup.stateChangeTypes.TagGroupKeyDownBackspace`\n- `useTagGroup.stateChangeTypes.TagGroupKeyDownDelete`\n- `useTagGroup.stateChangeTypes.TagRemoveClick`\n- `useTagGroup.stateChangeTypes.FunctionAddItem`\n\nSee [`stateReducer`](#statereducer) for a concrete example on how to use the\n`type` property.\n\n## Control Props\n\nDownshift manages its own state internally and calls your `onItemsChange`,\n`onActiveIndexChange` and `onStateChange` handlers with any relevant changes.\nThe state that downshift manages includes: `items` and `activeIndex`. Returned\naction function (read more below) can be used to manipulate this state and can\nlikely support many of your use cases.\n\nHowever, if more control is needed, you can pass any of these pieces of state as\na prop (as indicated above) and that state becomes controlled. As soon as\n`this.props[statePropKey] !== undefined`, internally, `downshift` will determine\nits state based on your prop's value rather than its own internal state. You\nwill be required to keep the state up to date (this is where `onStateChange`\ncomes in really handy), but you can also control the state from anywhere, be\nthat state from other components, `redux`, `react-router`, or anywhere else.\n\n> Note: This is very similar to how normal controlled components work elsewhere\n> in react (like `<input />`). If you want to learn more about this concept, you\n> can learn about that from the [Advanced React Component Patterns\n> course][advanced-react-component-patterns-course]\n\n## Returned props\n\nYou use the hook like so:\n\n```javascript\nimport {useTagGroup} from 'downshift'\nimport {items} from './utils'\n\nconst {getTagGroupProps, addItem, ...rest} = useTagGroup({\n  initialItems: items.slice(0, 2),\n  ...otherProps,\n})\n\nreturn (\n  <div>\n    <div {...getTagGroupProps()}>{/* render the items */}</div>\n    <div>\n      {/* render items to be added */}\n      <button\n        onClick={() => {\n          addItem(itemToBeAdded)\n        }}\n      >\n        {`Add ${itemToBeAdded.title}`}\n      </button>\n    </div>\n  </div>\n)\n```\n\n> NOTE: In this example we used both a getter prop `getTagGroupProps` and an\n> action prop `addItem`. The properties of `useTagGroup` can be split into three\n> categories as indicated below:\n\n### prop getters\n\n> See [the blog post about prop getters][blog-post-prop-getters]\n\n> NOTE: These prop-getters provide `aria-` attributes which are very important\n> to your component being accessible. It's recommended that you utilize these\n> functions and apply the props they give you to your components.\n\nThese functions are used to apply props to the elements that you render. This\ngives you maximum flexibility to render what, when, and wherever you like. You\ncall these on the element in question, for example on the toggle button:\n`<button {...getTagRemoveProps()}`. It's advisable to pass all your props to\nthat function rather than applying them on the element yourself to avoid your\nprops being overridden (or overriding the props returned). For example:\n`getTagRemoveProps({onKeyDown(event) {console.log(event)}})`.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property            | type           | description                                                                          |\n| ------------------- | -------------- | ------------------------------------------------------------------------------------ |\n| `getTagGroupProps`  | `function({})` | returns the props you should apply to the tag group container element you render.    |\n| `getTagProps`       | `function({})` | returns the props you should apply to any tag elements you render.                   |\n| `getTagRemoveProps` | `function({})` | returns the props you should apply to the tag remove button element that you render. |\n|                     |\n\n#### `getTagGroupProps`\n\nThis method should be applied to the tag group container you render. It applies\na `listbox` role along with some other ARIA properties and event handlers.\n\n#### `getTagProps`\n\nThis method should be applied to the tag element that makes up the list of tags\nin the tag group. Typically, this will be a `<span>` or a `<div>`. This applies\nARIA attributes and event handlers.\n\nRequired properties:\n\n- `index`: This is how `useTagRemoveGroup` keeps track of your item when\n  changing the active element through `activeIndex`. By default,\n  `useTagRemoveGroup` will assume the `index` is the order in which you're\n  calling `getTagProps`. This is often good enough, but if you find odd\n  behavior, try setting this explicitly. It's probably best to be explicit about\n  `index` when using a windowing library like `react-virtualized`.\n\nOptional properties:\n\n- `ref`: if you need to access the menu element via a ref object, you'd call the\n  function like this: `getTagProps({ref: yourTagRef})`. As a result, the tag\n  element will receive a composed `ref` property, which guarantees that both\n  your code and `useTagGroup` use the same correct reference to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getTagProps({refKey: 'innerRef'})` and\n  your composite component would forward like: `<span ref={props.innerRef} />`.\n  However, if you are just rendering a primitive component like `<div>`, there\n  is no need to specify this property. It defaults to `ref`.\n\nIn some cases, you might want to completely bypass the `refKey` check. Then you\ncan provide the object `{suppressRefError : true}` as the second argument to\n`getTagProps`. **Please use it with extreme care and only if you are absolutely\nsure that the ref is correctly forwarded otherwise `useTagGroup` will\nunexpectedly fail.**\n\n```jsx\nconst {getTagProps, getTagGroupProps, items} = useTagGroup()\nconst ui = (\n  <div {...getTagGroupProps()}>\n    {!items.length === 0\n      ? null\n      : items.map((item, index) => (\n          <span {...getTagProps({index, key: item.id})}>{item.name}</span>\n        ))}\n  </div>\n)\n```\n\n**This is an impure function**, so it should only be called when you will\nactually be applying the props to an item.\n\n<details>\n\n<summary>What do you mean by impure function?</summary>\n\nBasically just don't do this:\n\n```jsx\nitems.map((item, index) => {\n  const props = getTagProps({item, index}) // we're calling it here\n  if (!shouldRenderItem(item)) {\n    return null // but we're not using props, and downshift thinks we are...\n  }\n  return <div {...props} />\n})\n```\n\nInstead, you could do this:\n\n```jsx\nitems.filter(shouldRenderItem).map(item => <div {...getTagProps({item})} />)\n```\n\n</details>\n\n#### `getTagRemoveProps`\n\nThe props returned from calling this function should be applied to the remove\nbutton element for each tag, if there is need for such an element\n\n**This is an impure function**, same as `getTagProps`.\n\n```jsx\n// rendering the tag\n<span {...getTagProps({index, key: item.id})}>\n  {item.name}\n  <button {...getTagRemoveProps({index, 'aria-label': 'Remove'})}>X</button>\n</span>\n```\n\nRequired properties:\n\n- `index`: This is how `useTagRemoveGroup` keeps track of your item when\n  labelling the remove button. By default, `useTagRemoveGroup` will assume the\n  `index` is the order in which you're calling `getItemProps`. This is often\n  good enough, but if you find odd behavior, try setting this explicitly. It's\n  probably best to be explicit about `index` when using a windowing library like\n  `react-virtualized`.\n\nOptional properties:\n\n- `ref`: if you need to access the item element via a ref object, you'd call the\n  function like this: `getTagRemoveProps({ref: yourTagRef})`. As a result, the\n  tag element will receive a composed `ref` property, which guarantees that both\n  your code and `useSelect` use the same correct reference to the element.\n\n- `refKey`: if you're rendering a composite component, that component will need\n  to accept a prop which it forwards to the root DOM element. Commonly, folks\n  call this `innerRef`. So you'd call: `getTagRemoveProps({refKey: 'innerRef'})`\n  and your composite component would forward like:\n  `<button ref={props.innerRef} />`. However, if you are just rendering a\n  primitive component like `<div>`, there is no need to specify this property.\n  It defaults to `ref`.\n\n### actions\n\nThese are functions you can call to change the state of the downshift\n`useTagGroup` hook.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property  | type                  | description                              |\n| --------- | --------------------- | ---------------------------------------- |\n| `addItem` | `function(item: any)` | it adds the given item to the collection |\n\n### state\n\nThese are values that represent the current state of the downshift component.\n\n<!-- This table was generated via http://www.tablesgenerator.com/markdown_tables -->\n\n| property      | type     | description                |\n| ------------- | -------- | -------------------------- |\n| `activeIndex` | `number` | the currently active item  |\n| `items`       | `any[]`  | the items in the selection |\n\n## Event Handlers\n\nDownshift has a few events for which it provides implicit handlers. Several of\nthese handlers call `event.preventDefault()`. Their additional functionality is\ndescribed below.\n\n### Default handlers\n\n#### Tag Group\n\n- `ArrowLeft`: if a tag is focused and there are multiple tags in the tag group,\n  it will move focus to the tag that's placed to the left. If the tag has no\n  neighbours to the left, it will move focus to the last tag.\n- `ArrowRight`: if a tag is focused and there are multiple tags in the tag\n  group, it will move focus to the tag that's placed to the right. If the tag\n  has no neighbours to the right, it will move focus to the first tag.\n\n#### Tag\n\n- `Click`: moves the focus to this tag. Useful to execute optional actions as\n  well, such as opening a non-modal with additional information.\n- `Delete, Backspace`: removes the tag from the tag group and moves the focus to\n  either the next or previous tag, depending on the removed tag index.\n\n### Tag Remove\n\n- `Click`: removes the tag from the tag group and moves the focus to either the\n  next or previous tag, depending on the removed tag index.\n\n### Customizing Handlers\n\nYou can provide your own event handlers to `useTagGroup` which will be called\nbefore the default handlers:\n\n```javascript\nconst items = [...] // items here.\nconst {getTagGroup} = useTagGroup({initialItems: items})\nconst ui = (\n  <div\n    {...getTagGroup({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n      },\n    })}\n  />\n)\n```\n\nIf you would like to prevent the default handler behavior in some cases, you can\nset the event's `preventDownshiftDefault` property to `true`:\n\n```javascript\nconst items = [...] // items here.\nconst {getTagGroup} = useTagGroup({initialItems: items})\nconst ui = (\n  <div\n    {...getTagGroup({\n      onKeyDown: event => {\n        // your custom keyDown handler here.\n        if (event.key === 'ArrowLeft' || event.key === 'ArrowRight') {\n          // Prevent Downshift's default 'Enter' behavior.\n          event.nativeEvent.preventDownshiftDefault = true\n\n          // your handler code\n        }\n      },\n    })}\n  />\n)\n```\n\nIf you would like to completely override Downshift's behavior for a handler, in\nfavor of your own, you can bypass prop getters:\n\n```javascript\nconst items = [...] // items here.\nconst {getTagGroup} = useTagGroup({initialItems: items})\nconst ui = (\n  <div\n    {...getTagGroup()}\n    onKeyDown={event => {\n      // your custom keyDown handler here.\n    }}\n  />\n)\n```\n\n## Examples\n\nUsage examples are kept on the [downshift docsite][docsite] and also on [the\nsandbox repo][sandbox-repo]. Each example has a link to its own Codesandbox\nversion, so check the docs.\n\nIt can be a great contributing opportunity to provide relevant use cases as\ndocsite examples. If you have such an example, please create an issue with the\nsuggestion and the Codesandbox for it, and we will take it from there.\n\n[sandbox-example]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Fhooks%2FuseTagGroup%2Fbasic-usage.js&moduleview=1\n[state-change-file]:\n  https://github.com/downshift-js/downshift/blob/master/src/hooks/useTagGroup/stateChangeTypes.ts\n[blog-post-prop-getters]:\n  https://blog.kentcdodds.com/how-to-give-rendering-control-to-users-with-prop-getters-549eaef76acf\n[docsite]: https://downshift-js.com/\n[sandbox-repo]:\n  https://codesandbox.io/p/sandbox/github/kentcdodds/downshift-examples?file=%2Fsrc%2Findex.js&moduleview=1\n[advanced-react-component-patterns-course]:\n  https://github.com/downshift-js/downshift#advanced-react-component-patterns-course\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/getTagGroupProps.test.ts",
    "content": "import {\n  act,\n  screen,\n  defaultProps,\n  renderTagGroup,\n  renderUseTagGroup,\n} from './utils'\n\n// We are using React 18.\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\ndescribe('getTagGroupProps', () => {\n  describe('hook props', () => {\n    test('assign assigns a role of \"listbox\" and aria live attributes', () => {\n      const {result} = renderUseTagGroup()\n      const tagGroupProps = result.current.getTagGroupProps()\n\n      expect(tagGroupProps.role).toEqual('listbox')\n      expect(tagGroupProps.id).toEqual('downshift-test-id-tag-group')\n      expect(tagGroupProps['aria-live']).toEqual('polite')\n      expect(tagGroupProps['aria-atomic']).toEqual('false')\n      expect(tagGroupProps['aria-relevant']).toEqual('additions')\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.getTagGroupProps({foo: 'bar'})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('event handler onKeyDown is called along with downshift handler', () => {\n      const userOnKeyDown = jest.fn()\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        const {onKeyDown} = result.current.getTagGroupProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(1)\n    })\n\n    test('event handler onKeyDown is called along without downshift handler if event.preventDownshiftDefault is passed', () => {\n      const userOnKeyDown = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        const {onKeyDown} = result.current.getTagGroupProps({\n          onKeyDown: userOnKeyDown,\n        })\n\n        onKeyDown({key: 'ArrowLeft'})\n      })\n\n      expect(userOnKeyDown).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(2)\n    })\n  })\n\n  describe('keydown', () => {\n    test('arrow left moves selection to the previous item', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      await clickOnTag(2)\n      await user.keyboard('{ArrowLeft}')\n\n      const tags = getTags()\n\n      expect(tags[2]).toHaveAttribute('tabindex', '-1')\n      expect(tags[1]).toHaveAttribute('tabindex', '0')\n    })\n\n    test('arrow left moves selection to the last item if first item was active', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      await clickOnTag(0)\n      await user.keyboard('{ArrowLeft}')\n\n      const tags = getTags()\n\n      expect(tags[tags.length - 1]).toHaveAttribute('tabindex', '0')\n      expect(tags[0]).toHaveAttribute('tabindex', '-1')\n    })\n\n    test('arrow right moves selection to the previous item', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      await clickOnTag(2)\n      await user.keyboard('{ArrowRight}')\n\n      const tags = getTags()\n\n      expect(tags[2]).toHaveAttribute('tabindex', '-1')\n      expect(tags[3]).toHaveAttribute('tabindex', '0')\n    })\n\n    test('arrow right moves selection to the first item if last item was active', async () => {\n      const lastItemIndex = defaultProps.initialItems.length - 1\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      await clickOnTag(lastItemIndex)\n      await user.keyboard('{ArrowRight}')\n\n      const tags = getTags()\n\n      expect(tags[lastItemIndex]).toHaveAttribute('tabindex', '-1')\n      expect(tags[0]).toHaveAttribute('tabindex', '0')\n    })\n\n    test('arrows move selection correctly', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      await clickOnTag(0)\n      await user.keyboard('{ArrowRight}')\n      await user.keyboard('{ArrowRight}')\n\n      const tags = getTags()\n\n      expect(tags[0]).toHaveAttribute('tabindex', '-1')\n      expect(tags[1]).toHaveAttribute('tabindex', '-1')\n      expect(tags[2]).toHaveAttribute('tabindex', '0')\n\n      await user.keyboard('{ArrowLeft}')\n\n      expect(tags[2]).toHaveAttribute('tabindex', '-1')\n      expect(tags[1]).toHaveAttribute('tabindex', '0')\n\n      await user.keyboard('{ArrowRight}')\n\n      expect(tags[1]).toHaveAttribute('tabindex', '-1')\n      expect(tags[2]).toHaveAttribute('tabindex', '0')\n    })\n\n    test('delete removes the active item', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      const tagsCount = getTags().length\n\n      await clickOnTag(2)\n      await user.keyboard('{Delete}')\n\n      const newTagsCount = getTags().length\n\n      expect(newTagsCount).toEqual(tagsCount - 1)\n      expect(\n        screen.queryByRole('tag', {name: defaultProps.initialItems[2]}),\n      ).not.toBeInTheDocument()\n      expect(getTags()[2]).toHaveAttribute('tabindex', '0')\n      expect(getTags()[2]).toHaveFocus()\n    })\n\n    test('delete removes the active last item and the second to last item becomes active', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      const tagsCount = getTags().length\n\n      await clickOnTag(tagsCount - 1)\n      await user.keyboard('{Delete}')\n\n      expect(getTags()[tagsCount - 2]).toHaveAttribute('tabindex', '0')\n      expect(getTags()[tagsCount - 2]).toHaveFocus()\n    })\n\n    test('delete removes the only active item', async () => {\n      const {clickOnTag, user, queryByRole} = renderTagGroup({\n        initialItems: [defaultProps.initialItems[0] as string],\n      })\n\n      await clickOnTag(0)\n      await user.keyboard('{Delete}')\n\n      // eslint-disable-next-line testing-library/prefer-screen-queries\n      expect(queryByRole('tag')).not.toBeInTheDocument()\n    })\n\n     test('backspace removes the active item', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      const tagsCount = getTags().length\n\n      await clickOnTag(2)\n      await user.keyboard('{Backspace}')\n\n      const newTagsCount = getTags().length\n\n      expect(newTagsCount).toEqual(tagsCount - 1)\n      expect(\n        screen.queryByRole('tag', {name: defaultProps.initialItems[2]}),\n      ).not.toBeInTheDocument()\n      expect(getTags()[2]).toHaveAttribute('tabindex', '0')\n      expect(getTags()[2]).toHaveFocus()\n    })\n\n    test('backspace removes the active last item and the second to last item becomes active', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup()\n\n      const tagsCount = getTags().length\n\n      await clickOnTag(tagsCount - 1)\n      await user.keyboard('{Backspace}')\n\n      expect(getTags()[tagsCount - 2]).toHaveAttribute('tabindex', '0')\n      expect(getTags()[tagsCount - 2]).toHaveFocus()\n    })\n\n    test('backspace removes the only active item', async () => {\n      const {clickOnTag, user, queryByRole} = renderTagGroup({\n        initialItems: [defaultProps.initialItems[0] as string],\n      })\n\n      await clickOnTag(0)\n      await user.keyboard('{Backspace}')\n\n      // eslint-disable-next-line testing-library/prefer-screen-queries\n      expect(queryByRole('tag')).not.toBeInTheDocument()\n    })\n\n    test('any other key does nothing', async () => {\n      const {clickOnTag, user, getTags} = renderTagGroup({\n        defaultActiveIndex: 2,\n      })\n\n      await clickOnTag(2)\n      await user.keyboard('{Space}')\n\n      const tags = getTags()\n\n      expect(tags).toHaveLength(defaultProps.initialItems.length)\n      expect(tags[2]).toHaveAttribute('tabindex', '0')\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/getTagProps.test.ts",
    "content": "import {A11Y_DESCRIPTION_ELEMENT_ID} from '../utils'\nimport {renderTagGroup, renderUseTagGroup, defaultIds, act} from './utils'\n\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\ndescribe('getTagProps', () => {\n  describe('hook props', () => {\n    test('assign assigns a role of \"option\"', () => {\n      const {result} = renderUseTagGroup()\n      const tagProps = result.current.getTagProps({index: 0})\n\n      expect(tagProps.role).toEqual('option')\n      expect(tagProps.tabIndex).toEqual(0)\n      expect(tagProps['aria-describedby']).toEqual(A11Y_DESCRIPTION_ELEMENT_ID)\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.getTagProps({index: 0}).id).toEqual(\n        `${defaultIds.getTagId(0)}`,\n      )\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const getTagId = (index: number) => `my-custom-item-id-${index}`\n      const {result} = renderUseTagGroup({getTagId})\n\n      expect(result.current.getTagProps({index: 0}).id).toEqual(getTagId(0))\n    })\n\n    test('calling it without index results in error', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(() =>\n        result.current.getTagProps({index: -2}),\n      ).toThrowErrorMatchingInlineSnapshot(\"Pass correct item index to getTagProps!\")\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.getTagProps({foo: 'bar', index: 0})).toHaveProperty(\n        'foo',\n        'bar',\n      )\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        const {onClick} = result.current.getTagProps({\n          onClick: userOnClick,\n          index: 1,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(1)\n    })\n\n    test('event handler onClick is called along without downshift handler if event.preventDownshiftDefault is passed', () => {\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        const {onClick} = result.current.getTagProps({\n          onClick: userOnClick,\n          index: 1,\n        })\n\n        onClick({})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.activeIndex).toBe(2)\n    })\n  })\n\n  describe('click', () => {\n    test('sets the item as active and focusable', async () => {\n      const {clickOnTag, getTags} = renderTagGroup({initialActiveIndex: 1})\n\n      await clickOnTag(2)\n\n      const tags = getTags()\n\n      expect(tags[2]).toHaveAttribute('tabindex', '0')\n      expect(tags[1]).toHaveAttribute('tabindex', '-1')\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/getTagRemoveProps.test.ts",
    "content": "import {\n  renderTagGroup,\n  renderUseTagGroup,\n  defaultIds,\n  act,\n  defaultProps,\n  screen,\n} from './utils'\n\n// We are using React 18.\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'test-id'\n    },\n  }\n})\n\ndescribe('getTagRemoveProps', () => {\n  describe('hook props', () => {\n    test('assign assigns tabindex of -1 and aria-labelledby', () => {\n      const {result} = renderUseTagGroup()\n      const tagRemoveProps = result.current.getTagRemoveProps({index: 0})\n      const tagId = defaultIds.getTagId(0)\n\n      expect(tagRemoveProps.tabIndex).toEqual(-1)\n      expect(tagRemoveProps['aria-labelledby']).toEqual(\n        `${tagId}-remove ${tagId}`,\n      )\n    })\n\n    test('assign default value to id', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.getTagRemoveProps({index: 0}).id).toEqual(\n        `${defaultIds.getTagId(0)}-remove`,\n      )\n    })\n\n    test('assign custom value passed by user to id', () => {\n      const getTagId = (index: number) => `my-custom-item-id-${index}`\n      const {result} = renderUseTagGroup({getTagId})\n\n      expect(result.current.getTagRemoveProps({index: 0}).id).toEqual(\n        `${getTagId(0)}-remove`,\n      )\n    })\n\n    test('calling it without index results in error', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(() =>\n        result.current.getTagRemoveProps({index: -3}),\n      ).toThrowErrorMatchingInlineSnapshot(`Pass correct item index to getTagRemoveProps!`)\n    })\n  })\n\n  describe('user props', () => {\n    test('are passed down', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(\n        result.current.getTagRemoveProps({foo: 'bar', index: 0}),\n      ).toHaveProperty('foo', 'bar')\n    })\n\n    test('event handler onClick is called along with downshift handler', () => {\n      const userOnClick = jest.fn()\n      const stopPropagation = jest.fn()\n      const {result} = renderUseTagGroup()\n\n      act(() => {\n        const {onClick} = result.current.getTagRemoveProps({\n          onClick: userOnClick,\n          index: 1,\n        })\n\n        onClick({stopPropagation})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.items).not.toContain(defaultProps.initialItems[1])\n      expect(stopPropagation).toHaveBeenCalledTimes(1)\n    })\n\n    test('event handler onClick is called along without downshift handler if event.preventDownshiftDefault is passed', () => {\n      const stopPropagation = jest.fn()\n      const userOnClick = jest.fn(event => {\n        event.preventDownshiftDefault = true\n      })\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        const {onClick} = result.current.getTagRemoveProps({\n          onClick: userOnClick,\n          index: 1,\n        })\n\n        onClick({stopPropagation})\n      })\n\n      expect(userOnClick).toHaveBeenCalledTimes(1)\n      expect(result.current.items).toContain(defaultProps.initialItems[1])\n      expect(stopPropagation).not.toHaveBeenCalled()\n    })\n  })\n\n  describe('click', () => {\n    test('removes the tag', async () => {\n      const {clickOnRemoveTag} = renderTagGroup()\n\n      await clickOnRemoveTag(2)\n\n      expect(\n        screen.getByRole('option', {name: defaultProps.initialItems[1]}),\n      ).toBeInTheDocument()\n    })\n\n\n    test('click removes the active last item and the second to last item becomes active', async () => {\n      const {clickOnRemoveTag, getTags} = renderTagGroup()\n\n      const tagsCount = getTags().length\n\n      await clickOnRemoveTag(tagsCount - 1)\n\n      expect(getTags()[tagsCount - 2]).toHaveAttribute('tabindex', '0')\n    })\n\n    test('click removes the only active item', async () => {\n      const {clickOnRemoveTag, queryByRole} = renderTagGroup({\n        initialItems: [defaultProps.initialItems[0] as string],\n      })\n\n      await clickOnRemoveTag(0)\n\n      // eslint-disable-next-line testing-library/prefer-screen-queries\n      expect(queryByRole('tag')).not.toBeInTheDocument()\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/props.test.ts",
    "content": "/* eslint-disable testing-library/prefer-screen-queries */\nimport useTagGroup from '..'\nimport {A11Y_DESCRIPTION_ELEMENT_ID} from '../utils'\nimport {\n  renderTagGroup,\n  renderHook,\n  colors,\n  renderUseTagGroup,\n  act,\n  defaultProps,\n} from './utils'\n\ndescribe('props', () => {\n  test('passing no props object will still work', () => {\n    const result = renderHook(() => useTagGroup())\n\n    expect(result.result.current.getTagGroupProps).toBeDefined()\n  })\n\n  test('initialActiveIndex sets activeIndex at mount', () => {\n    const {getTags} = renderTagGroup({\n      initialActiveIndex: 1,\n    })\n\n    const tags = getTags()\n\n    expect(tags[0]).toHaveAttribute('tabindex', '-1')\n    expect(tags[1]).toHaveAttribute('tabindex', '0')\n    expect(tags[2]).toHaveAttribute('tabindex', '-1')\n  })\n\n  test('initialActiveIndex does not focus active item at mount', () => {\n    const {getTags} = renderTagGroup({\n      initialActiveIndex: 1,\n    })\n\n    const tags = getTags()\n\n    expect(tags[1]).not.toHaveFocus()\n  })\n\n  test('activeIndex controls the activeIndex state', async () => {\n    const {getTags, clickOnTag} = renderTagGroup({\n      activeIndex: 1,\n    })\n\n    const tags = getTags()\n\n    await clickOnTag(0)\n\n    expect(tags[0]).toHaveAttribute('tabindex', '-1')\n    expect(tags[1]).toHaveAttribute('tabindex', '0')\n\n    await clickOnTag(2)\n\n    expect(tags[2]).toHaveAttribute('tabindex', '-1')\n    expect(tags[1]).toHaveAttribute('tabindex', '0')\n  })\n\n  test('initialItems sets items at mount', () => {\n    const {getTags} = renderTagGroup({\n      initialItems: [colors[2] as string],\n    })\n\n    const tags = getTags()\n\n    expect(tags).toHaveLength(1)\n    expect(tags[0]).toHaveTextContent(colors[2] as string)\n  })\n\n  test('items controls the items state', async () => {\n    const {result} = renderUseTagGroup({items: [colors[2] as string]})\n\n    act(() => {\n      result.current.addItem(colors[3] as string)\n    })\n\n    expect(result.current.items).toEqual([colors[2]])\n  })\n\n  test('id if passed will override downshift default', () => {\n    const {getTagGroup, getTags} = renderTagGroup({\n      id: 'my-custom-little-id',\n    })\n    const elements = [getTagGroup(), ...getTags()]\n\n    elements.forEach(element => {\n      expect(element).toHaveAttribute(\n        'id',\n        expect.stringContaining('my-custom-little-id'),\n      )\n    })\n  })\n\n  test('tagGroupId if passed will override downshift default', () => {\n    const {getTagGroup} = renderTagGroup({\n      tagGroupId: 'my-custom-little-id',\n      id: 'should-be-overriden',\n    })\n\n    expect(getTagGroup()).toHaveAttribute('id', 'my-custom-little-id')\n  })\n\n  test('getTagId if passed will override downshift default', () => {\n    const {getTags} = renderTagGroup({\n      getTagId(index) {\n        return `custom-${index}`\n      },\n      id: 'should-be-overriden',\n    })\n\n    getTags().forEach((element, index) => {\n      expect(element).toHaveAttribute('id', `custom-${index}`)\n    })\n  })\n\n  test('stateReducer gets called before using addItem', () => {\n    const stateReducer = jest.fn().mockImplementation((_s, c) => c.changes)\n    const {result} = renderUseTagGroup({stateReducer})\n\n    act(() => {\n      result.current.addItem('test')\n    })\n\n    expect(stateReducer).toHaveBeenCalledTimes(1)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      1,\n      {\n        activeIndex: 0,\n        items: defaultProps.initialItems,\n      },\n      {\n        changes: {\n          activeIndex: 0,\n          items: [...defaultProps.initialItems, 'test'],\n        },\n        index: undefined,\n        item: 'test',\n        type: useTagGroup.stateChangeTypes.FunctionAddItem,\n      },\n    )\n  })\n\n  test('stateReducer gets called before any new state update', async () => {\n    const stateReducer = jest.fn().mockImplementation((_s, c) => c.changes)\n    const {user, clickOnTag, clickOnRemoveTag} = renderTagGroup({\n      stateReducer,\n    })\n\n    expect(stateReducer).not.toHaveBeenCalled()\n\n    await clickOnTag(3)\n\n    expect(stateReducer).toHaveBeenCalledTimes(1)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      1,\n      {\n        activeIndex: 0,\n        items: defaultProps.initialItems,\n      },\n      {\n        changes: {\n          activeIndex: 3,\n          items: defaultProps.initialItems,\n        },\n        index: 3,\n        type: useTagGroup.stateChangeTypes.TagClick,\n      },\n    )\n\n    await user.keyboard('{ArrowLeft}')\n\n    expect(stateReducer).toHaveBeenCalledTimes(2)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      2,\n      {\n        activeIndex: 3,\n        items: defaultProps.initialItems,\n      },\n      {\n        changes: {\n          activeIndex: 2,\n          items: defaultProps.initialItems,\n        },\n        type: useTagGroup.stateChangeTypes.TagGroupKeyDownArrowLeft,\n      },\n    )\n\n    await user.keyboard('{ArrowRight}')\n\n    expect(stateReducer).toHaveBeenCalledTimes(3)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      3,\n      {\n        activeIndex: 2,\n        items: defaultProps.initialItems,\n      },\n      {\n        changes: {\n          activeIndex: 3,\n          items: defaultProps.initialItems,\n        },\n        type: useTagGroup.stateChangeTypes.TagGroupKeyDownArrowRight,\n      },\n    )\n\n    await user.keyboard('{Backspace}')\n\n    const newItemsAfterBackspace = [\n      ...defaultProps.initialItems.slice(0, 3),\n      ...defaultProps.initialItems.slice(4),\n    ]\n\n    expect(stateReducer).toHaveBeenCalledTimes(4)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      4,\n      {\n        activeIndex: 3,\n        items: defaultProps.initialItems,\n      },\n      {\n        changes: {\n          activeIndex: 3,\n          items: newItemsAfterBackspace,\n        },\n        type: useTagGroup.stateChangeTypes.TagGroupKeyDownBackspace,\n      },\n    )\n\n    await user.keyboard('{Delete}')\n\n    const newItemsAfterDelete = [\n      ...defaultProps.initialItems.slice(0, 3),\n      ...defaultProps.initialItems.slice(5),\n    ]\n\n    expect(stateReducer).toHaveBeenCalledTimes(5)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      5,\n      {\n        activeIndex: 3,\n        items: newItemsAfterBackspace,\n      },\n      {\n        changes: {\n          activeIndex: 3,\n          items: newItemsAfterDelete,\n        },\n        type: useTagGroup.stateChangeTypes.TagGroupKeyDownDelete,\n      },\n    )\n\n    await clickOnRemoveTag(0)\n\n    const newItemsAfterRemoveClick = [\n      ...defaultProps.initialItems.slice(1, 3),\n      ...defaultProps.initialItems.slice(5),\n    ]\n\n    expect(stateReducer).toHaveBeenCalledTimes(6)\n    expect(stateReducer).toHaveBeenNthCalledWith(\n      6,\n      {\n        activeIndex: 3,\n        items: newItemsAfterDelete,\n      },\n      {\n        changes: {\n          activeIndex: 0,\n          items: newItemsAfterRemoveClick,\n        },\n        index: 0,\n        type: useTagGroup.stateChangeTypes.TagRemoveClick,\n      },\n    )\n  })\n\n  test('environment substitutes window when passed', () => {\n    const element = {style: {}, setAttribute: jest.fn(), remove: jest.fn()}\n    const environment = {\n      document: {\n        body: {appendChild: jest.fn()},\n        createElement: jest.fn().mockReturnValue(element),\n      } as unknown as Document,\n      addEventListener: jest.fn(),\n      removeEventListener: jest.fn(),\n      Node: {} as unknown as typeof window.Node,\n    }\n    const {unmount} = renderTagGroup({\n      environment,\n    })\n\n    expect(environment.document.createElement).toHaveBeenCalledTimes(1)\n    expect(environment.document.createElement).toHaveBeenCalledWith('div')\n    expect(element.setAttribute).toHaveBeenCalledTimes(1)\n    expect(element.setAttribute).toHaveBeenCalledWith(\n      'id',\n      A11Y_DESCRIPTION_ELEMENT_ID,\n    )\n    expect(environment.document.body.appendChild).toHaveBeenCalledTimes(1)\n    expect(environment.document.body.appendChild).toHaveBeenCalledWith(element)\n    expect(element.remove).not.toHaveBeenCalled()\n\n    unmount()\n\n    expect(element.remove).toHaveBeenCalledTimes(1)\n  })\n\n  test('removeElementDescription can be overriden', () => {\n    const removeElementDescription = 'just remove it'\n    const {getByText, queryByText} = renderTagGroup({\n      removeElementDescription,\n    })\n\n    expect(getByText(removeElementDescription)).toBeInTheDocument()\n    expect(queryByText('Press Delete or Backspace to remove tag.')).not.toBeInTheDocument()\n  })\n\n  test('removeElementDescription has a default options', () => {\n    const {getByText} = renderTagGroup()\n\n    expect(getByText('Press Delete or Backspace to remove tag.')).toBeInTheDocument()\n  })\n\n  test('onStateChange is called after adding an item', () => {\n    const onStateChange = jest.fn()\n    const {result} = renderUseTagGroup({onStateChange})\n\n    act(() => {\n      result.current.addItem('test')\n    })\n\n    expect(onStateChange).toHaveBeenCalledTimes(1)\n    expect(onStateChange).toHaveBeenNthCalledWith(1, {\n      items: [...defaultProps.initialItems, 'test'],\n      type: useTagGroup.stateChangeTypes.FunctionAddItem,\n    })\n  })\n\n  test('onStateChange is called after each user action', async () => {\n    const onStateChange = jest.fn()\n    const {clickOnTag, clickOnRemoveTag, user} = renderTagGroup({onStateChange})\n\n    await clickOnTag(2)\n\n    expect(onStateChange).toHaveBeenCalledTimes(1)\n    expect(onStateChange).toHaveBeenCalledWith({\n      type: useTagGroup.stateChangeTypes.TagClick,\n      activeIndex: 2,\n    })\n\n    await clickOnRemoveTag(4)\n\n    expect(onStateChange).toHaveBeenCalledTimes(2)\n    expect(onStateChange).toHaveBeenNthCalledWith(2, {\n      type: useTagGroup.stateChangeTypes.TagRemoveClick,\n      activeIndex: 4,\n      items: [\n        ...defaultProps.initialItems.slice(0, 4),\n        ...defaultProps.initialItems.slice(5),\n      ],\n    })\n\n    await user.keyboard('{ArrowLeft}')\n\n    expect(onStateChange).toHaveBeenCalledTimes(3)\n    expect(onStateChange).toHaveBeenNthCalledWith(3, {\n      type: useTagGroup.stateChangeTypes.TagGroupKeyDownArrowLeft,\n      activeIndex: 3,\n    })\n\n    await user.keyboard('{ArrowRight}')\n\n    expect(onStateChange).toHaveBeenCalledTimes(4)\n    expect(onStateChange).toHaveBeenNthCalledWith(4, {\n      type: useTagGroup.stateChangeTypes.TagGroupKeyDownArrowRight,\n      activeIndex: 4,\n    })\n\n    await user.keyboard('{Delete}')\n\n    expect(onStateChange).toHaveBeenCalledTimes(5)\n    expect(onStateChange).toHaveBeenNthCalledWith(5, {\n      type: useTagGroup.stateChangeTypes.TagGroupKeyDownDelete,\n      items: [\n        ...defaultProps.initialItems.slice(0, 4),\n        ...defaultProps.initialItems.slice(6),\n      ],\n    })\n\n    await user.keyboard('{Backspace}')\n\n    expect(onStateChange).toHaveBeenCalledTimes(6)\n    expect(onStateChange).toHaveBeenNthCalledWith(6, {\n      type: useTagGroup.stateChangeTypes.TagGroupKeyDownBackspace,\n      items: [\n        ...defaultProps.initialItems.slice(0, 4),\n        ...defaultProps.initialItems.slice(7),\n      ],\n    })\n  })\n\n  test('onActiveIndexChange is called after active index changes', async () => {\n    const onActiveIndexChange = jest.fn()\n    const {clickOnTag, clickOnRemoveTag} = renderTagGroup({onActiveIndexChange})\n\n    await clickOnTag(2)\n\n    expect(onActiveIndexChange).toHaveBeenCalledTimes(1)\n    expect(onActiveIndexChange).toHaveBeenCalledWith({\n      type: useTagGroup.stateChangeTypes.TagClick,\n      activeIndex: 2,\n      items: defaultProps.initialItems,\n    })\n\n    onActiveIndexChange.mockClear()\n    await clickOnRemoveTag(2)\n\n    expect(onActiveIndexChange).not.toHaveBeenCalled()\n  })\n\n  test('onItemsChange is called after items change', async () => {\n    const onItemsChange = jest.fn()\n    const {clickOnTag, clickOnRemoveTag} = renderTagGroup({onItemsChange})\n\n    await clickOnTag(2)\n\n    expect(onItemsChange).not.toHaveBeenCalled()\n\n    await clickOnRemoveTag(2)\n\n    expect(onItemsChange).toHaveBeenCalledTimes(1)\n    expect(onItemsChange).toHaveBeenCalledWith({\n      type: useTagGroup.stateChangeTypes.TagRemoveClick,\n      activeIndex: 2,\n      items: [\n        ...defaultProps.initialItems.slice(0, 2),\n        ...defaultProps.initialItems.slice(3),\n      ],\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/reducer.test.ts",
    "content": "import {UseTagGroupReducerAction} from '../index.types'\nimport {useTagGroupReducer} from '../reducer'\n\ntest('reducer throws error if called without proper action type', () => {\n  expect(() => {\n    useTagGroupReducer(\n      {activeIndex: 0, items: []},\n      {\n        stateReducer(state) {\n          return state\n        },\n        removeElementDescription: 'bla bla',\n      },\n      {type: 'super-bogus'} as unknown as UseTagGroupReducerAction<unknown>,\n    )\n  }).toThrow('Invalid useTagGroup reducer action.')\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/returnProps.test.ts",
    "content": "import useTagGroup from '..'\nimport * as stateChangeTypes from '../stateChangeTypes'\nimport {renderUseTagGroup, act, defaultProps} from './utils'\n\n\ndescribe('returnProps', () => {\n  test('should have stateChangeTypes attached to hook', () => {\n    expect(useTagGroup).toHaveProperty('stateChangeTypes', stateChangeTypes)\n  })\n\n  describe('prop getters', () => {\n    test('are returned as functions', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.getTagGroupProps).toBeInstanceOf(Function)\n      expect(result.current.getTagProps).toBeInstanceOf(Function)\n      expect(result.current.getTagRemoveProps).toBeInstanceOf(Function)\n    })\n  })\n\n  describe('actions', () => {\n    test('addItem adds an item to the group', () => {\n      const {result} = renderUseTagGroup()\n\n      const previousItems = result.current.items\n\n      act(() => {\n        result.current.addItem('test')\n      })\n\n      expect(result.current.items).toEqual([...previousItems, 'test'])\n    })\n\n    test('addItem keeps the active item if previously set', () => {\n      const {result} = renderUseTagGroup({initialActiveIndex: 2})\n\n      act(() => {\n        result.current.addItem('test')\n      })\n\n      expect(result.current.activeIndex).toEqual(2)\n    })\n\n    test('addItem makes the last item active if there is no previous active item', () => {\n      const {result} = renderUseTagGroup({initialActiveIndex: -1})\n\n      act(() => {\n        result.current.addItem('test')\n      })\n\n      expect(result.current.activeIndex).toEqual(\n        result.current.items.length - 1,\n      )\n    })\n\n    test('addItem adds an item to the group at the index specified', () => {\n      const {result} = renderUseTagGroup()\n\n      const previousItems = result.current.items\n\n      act(() => {\n        result.current.addItem('test', 0)\n      })\n\n      expect(result.current.items).toEqual(['test', ...previousItems])\n    })\n  })\n\n  describe('state and props', () => {\n    test('activeIndex is returned', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.activeIndex).toBe(0)\n    })\n\n    test('items is returned', () => {\n      const {result} = renderUseTagGroup()\n\n      expect(result.current.items).toBe(defaultProps.initialItems)\n    })\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/utils/defaultIds.ts",
    "content": "export const defaultIds = {\n  tagGroupId: 'downshift-test-id-tag-group',\n  getTagId: (index: number) => `downshift-test-id-tag-${index}`,\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/utils/defaultProps.ts",
    "content": "export const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nexport const defaultProps = {\n  initialItems: colors.slice(0, 8),\n}"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/utils/index.ts",
    "content": "export * from '@testing-library/react'\n\nexport {defaultProps, colors} from './defaultProps'\nexport {renderTagGroup} from './renderTagGroup'\nexport {renderUseTagGroup} from './renderUseTagGroup'\nexport {defaultIds} from './defaultIds'\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/utils/renderTagGroup.tsx",
    "content": "/* eslint-disable testing-library/prefer-screen-queries */\nimport * as React from 'react'\nimport {render} from '@testing-library/react'\nimport userEvent from '@testing-library/user-event'\n\nimport useTagGroup from '../..'\nimport {UseTagGroupProps} from '../../index.types'\nimport {defaultProps} from './defaultProps'\n\nexport function renderTagGroup(props: Partial<UseTagGroupProps<string>> = {}) {\n  const utils = render(<TagGroup {...defaultProps} {...props} />)\n  const user = userEvent.setup()\n\n  function getTags() {\n    return utils.getAllByRole('option')\n  }\n\n  function getTagGroup() {\n    return utils.getByRole('listbox')\n  }\n\n  function getTagsRemoves() {\n    return utils.getAllByRole('button')\n  }\n\n  async function clickOnTag(index: number) {\n    const tags = getTags()\n    const tag = tags[index] as HTMLElement\n\n    await user.click(tag)\n  }\n\n  async function clickOnRemoveTag(index: number) {\n    const removeButtons = getTagsRemoves()\n\n    await user.click(removeButtons[index] as HTMLElement)\n  }\n\n  return {\n    ...utils,\n    getTags,\n    getTagGroup,\n    clickOnTag,\n    clickOnRemoveTag,\n    getTagsRemoves,\n    user,\n  }\n}\n\nfunction TagGroup(props: Partial<UseTagGroupProps<string>> = {}) {\n  const {getTagProps, getTagRemoveProps, getTagGroupProps, items, activeIndex} =\n    useTagGroup(props)\n\n  return (\n    <div {...getTagGroupProps()} className=\"tag-group\">\n      {items.map((color, index) => (\n        <span\n          className={`${index === activeIndex ? 'selected-tag' : ''} tag`}\n          key={color}\n          aria-label={color}\n          {...getTagProps({index})}\n        >\n          {color}\n          <button\n            className=\"tag-remove-button\"\n            {...getTagRemoveProps({index, 'aria-label': 'remove color'})}\n          >\n            &#10005;\n          </button>\n        </span>\n      ))}\n    </div>\n  )\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/__tests__/utils/renderUseTagGroup.ts",
    "content": "import {renderHook} from '@testing-library/react'\n\nimport {UseTagGroupProps, UseTagGroupReturnValue} from '../../index.types'\nimport useTagGroup from '../..'\nimport {defaultProps} from './defaultProps'\n\nexport function renderUseTagGroup(\n  initialProps: Partial<UseTagGroupProps<string>> = {},\n) {\n  return renderHook<\n    UseTagGroupReturnValue<string>,\n    Partial<UseTagGroupProps<string>>\n  >((props = {}) => useTagGroup(props), {\n    initialProps: {...defaultProps, ...initialProps},\n  })\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/index.ts",
    "content": "import {useCallback} from 'react'\n\nimport {\n  callAllEventHandlers,\n  handleRefs,\n  useLatestRef,\n  validatePropTypes,\n} from '../../utils-ts'\nimport {useControlledReducer} from '../utils-ts'\nimport * as stateChangeTypes from './stateChangeTypes'\nimport {\n  GetTagGroupPropsOptions,\n  GetTagPropsOptions,\n  GetTagRemovePropsOptions,\n  UseTagGroupInterface,\n  UseTagGroupProps,\n  UseTagGroupMergedProps,\n  UseTagGroupReducerAction,\n  UseTagGroupReturnValue,\n  UseTagGroupState,\n  UseTagGroupStateChangeTypes,\n  GetTagRemovePropsReturnValue,\n  GetTagPropsReturnValue,\n  GetTagGroupPropsReturnValue,\n} from './index.types'\nimport {useTagGroupReducer} from './reducer'\nimport {\n  getInitialState,\n  isStateEqual,\n  propTypes,\n  useElementIds,\n  useAccessibleDescription,\n  A11Y_DESCRIPTION_ELEMENT_ID,\n  getMergedProps,\n  useRovingTagFocus,\n} from './utils'\n\nconst useTagGroup: UseTagGroupInterface = <Item>(\n  userProps: UseTagGroupProps<Item> = {},\n) => {\n  /* State and Props */\n\n  validatePropTypes(userProps, useTagGroup, propTypes)\n\n  const props = getMergedProps(userProps)\n\n  const [state, dispatch] = useControlledReducer<\n    UseTagGroupState<Item>,\n    UseTagGroupMergedProps<Item>,\n    UseTagGroupStateChangeTypes,\n    UseTagGroupReducerAction<Item>\n  >(useTagGroupReducer, props, getInitialState, isStateEqual)\n\n  const {activeIndex, items} = state\n\n  /* Refs */\n\n  const latest = useLatestRef({state, props})\n  const elementIds = useElementIds({\n    getTagId: props.getTagId,\n    id: props.id,\n    tagGroupId: props.tagGroupId,\n  })\n\n  /* Effects */\n\n  useAccessibleDescription(\n    props.environment?.document,\n    props.removeElementDescription,\n  )\n\n  const itemRefs = useRovingTagFocus(\n    activeIndex,\n    items.length,\n    elementIds.getTagId,\n  )\n\n  /* Getter functions */\n\n  const getTagGroupProps = useCallback(\n    <Extra extends Record<string, unknown>>(\n      options?: GetTagGroupPropsOptions & Extra,\n    ) => {\n      const {onKeyDown, ...rest} =\n        options ?? ({} as GetTagGroupPropsOptions & Extra)\n      const handleKeyDown = (e: React.KeyboardEvent<HTMLElement>): void => {\n        switch (e.key) {\n          case 'ArrowLeft':\n            dispatch({\n              type: stateChangeTypes.TagGroupKeyDownArrowLeft,\n            })\n            break\n          case 'ArrowRight':\n            dispatch({\n              type: stateChangeTypes.TagGroupKeyDownArrowRight,\n            })\n            break\n          case 'Delete':\n            dispatch({\n              type: stateChangeTypes.TagGroupKeyDownDelete,\n            })\n            break\n          case 'Backspace':\n            dispatch({\n              type: stateChangeTypes.TagGroupKeyDownBackspace,\n            })\n            break\n          default:\n        }\n      }\n\n      const tagGroupProps = {\n        id: elementIds.tagGroupId,\n        'aria-live': 'polite',\n        'aria-atomic': 'false',\n        'aria-relevant': 'additions',\n        role: 'listbox',\n        onKeyDown: callAllEventHandlers(onKeyDown, handleKeyDown),\n        ...rest,\n      } as GetTagGroupPropsReturnValue & Extra\n\n      return tagGroupProps\n    },\n    [dispatch, elementIds.tagGroupId],\n  )\n\n  const getTagProps = useCallback(\n    <Extra extends Record<string, unknown>>(\n      options: GetTagPropsOptions & Extra,\n    ) => {\n      const {index, refKey = 'ref', ref, onClick, ...rest} = options\n\n      if (!Number.isInteger(index) || index < 0) {\n        throw new Error('Pass correct item index to getTagProps!')\n      }\n\n      const latestState = latest.current.state\n\n      const handleClick = () => {\n        dispatch({type: stateChangeTypes.TagClick, index})\n      }\n      const tagId = elementIds.getTagId(index)\n\n      return {\n        'aria-describedby': A11Y_DESCRIPTION_ELEMENT_ID,\n        [refKey]: handleRefs(ref, itemNode => {\n          if (itemNode) {\n            itemRefs.current[tagId] = itemNode\n          }\n        }),\n        'aria-labelledby': tagId,\n        role: 'option',\n        id: tagId,\n        onClick: callAllEventHandlers(onClick, handleClick),\n        tabIndex: latestState.activeIndex === index ? 0 : -1,\n        ...rest,\n      } as GetTagPropsReturnValue & Extra\n    },\n    [dispatch, elementIds, latest, itemRefs],\n  )\n\n  const getTagRemoveProps = useCallback(\n    <Extra extends Record<string, unknown>>(\n      options: GetTagRemovePropsOptions & Extra,\n    ) => {\n      const {index, onClick, ...rest} = options\n\n      if (!Number.isInteger(index) || index < 0) {\n        throw new Error('Pass correct item index to getTagRemoveProps!')\n      }\n\n      const handleClick = (event: React.MouseEvent) => {\n        event.stopPropagation()\n        dispatch({type: stateChangeTypes.TagRemoveClick, index})\n      }\n\n      const tagId = elementIds.getTagId(index)\n      const tagRemoveId = `${tagId}-remove`\n\n      return {\n        id: tagRemoveId,\n        tabIndex: -1,\n        'aria-labelledby': `${tagRemoveId} ${tagId}`,\n        onClick: callAllEventHandlers(onClick, handleClick),\n        ...rest,\n      } as GetTagRemovePropsReturnValue & Extra\n    },\n    [elementIds, dispatch],\n  )\n\n  /* Imperative Functions */\n\n  const addItem = useCallback<UseTagGroupReturnValue<Item>['addItem']>(\n    (item, index): void => {\n      dispatch({type: stateChangeTypes.FunctionAddItem, item, index})\n    },\n    [dispatch],\n  )\n\n  return {\n    activeIndex,\n    addItem,\n    getTagGroupProps,\n    getTagProps,\n    getTagRemoveProps,\n    items,\n  }\n}\n\nuseTagGroup.stateChangeTypes = stateChangeTypes\n\nexport default useTagGroup\n"
  },
  {
    "path": "src/hooks/useTagGroup/index.types.ts",
    "content": "import {Action, State} from '../../utils-ts'\n\nexport interface UseTagGroupState<Item> extends State {\n  activeIndex: number\n  items: Item[]\n}\n\nexport interface Environment {\n  addEventListener: typeof window.addEventListener\n  removeEventListener: typeof window.removeEventListener\n  document: Document\n  Node: typeof window.Node\n}\n\nexport interface UseTagGroupStateChange<Item> extends Partial<\n  UseTagGroupState<Item>\n> {\n  type: UseTagGroupStateChangeTypes\n}\n\nexport interface UseTagGroupActiveIndexChange<\n  Item,\n> extends UseTagGroupStateChange<Item> {\n  activeIndex: number\n}\n\nexport interface UseTagGroupItemsChange<\n  Item,\n> extends UseTagGroupStateChange<Item> {\n  items: Item[]\n}\n\nexport interface UseTagGroupProps<Item> extends Partial<\n  UseTagGroupState<Item>\n> {\n  environment?: Environment\n  getTagId?: (index: number) => string\n  id?: string\n  initialActiveIndex?: number\n  initialItems?: Item[]\n  onActiveIndexChange?: (changes: UseTagGroupActiveIndexChange<Item>) => void\n  onItemsChange?: (changes: UseTagGroupItemsChange<Item>) => void\n  onStateChange?: (changes: UseTagGroupStateChange<Item>) => void\n  removeElementDescription?: string\n  stateReducer?(\n    state: UseTagGroupState<Item>,\n    actionAndChanges: Action<UseTagGroupStateChangeTypes> & {\n      changes: Partial<UseTagGroupState<Item>>\n    },\n  ): Partial<UseTagGroupState<Item>>\n  tagGroupId?: string\n}\n\nexport type UseTagGroupMergedProps<Item> = Required<\n  Pick<UseTagGroupProps<Item>, 'stateReducer' | 'removeElementDescription'>\n> &\n  UseTagGroupProps<Item>\n\nexport interface UseTagGroupInterface {\n  <Item>(props?: UseTagGroupProps<Item>): UseTagGroupReturnValue<Item>\n  stateChangeTypes: {\n    TagClick: UseTagGroupStateChangeTypes.TagClick\n    TagGroupKeyDownArrowLeft: UseTagGroupStateChangeTypes.TagGroupKeyDownArrowLeft\n    TagGroupKeyDownArrowRight: UseTagGroupStateChangeTypes.TagGroupKeyDownArrowRight\n    TagGroupKeyDownBackspace: UseTagGroupStateChangeTypes.TagGroupKeyDownBackspace\n    TagGroupKeyDownDelete: UseTagGroupStateChangeTypes.TagGroupKeyDownDelete\n    TagRemoveClick: UseTagGroupStateChangeTypes.TagRemoveClick\n    FunctionAddItem: UseTagGroupStateChangeTypes.FunctionAddItem\n  }\n}\n\nexport interface UseTagGroupReturnValue<Item> {\n  activeIndex: number\n  addItem: (item: Item, index?: number) => void\n  getTagGroupProps: GetTagGroupProps\n  getTagProps: GetTagProps\n  getTagRemoveProps: GetTagRemoveProps\n  items: Item[]\n}\n\nexport interface GetTagPropsOptions extends React.HTMLProps<HTMLElement> {\n  index: number\n  refKey?: string\n  ref?: React.MutableRefObject<HTMLElement>\n}\n\nexport interface GetTagPropsReturnValue {\n  'aria-describedby': string\n  id: string\n  role: 'option'\n  onPress?: (event: React.BaseSyntheticEvent) => void\n  onClick?: React.MouseEventHandler\n  tabIndex: 0 | -1\n}\n\nexport interface GetTagRemovePropsOptions extends React.HTMLProps<HTMLElement> {\n  index: number\n}\n\nexport interface GetTagRemovePropsReturnValue {\n  id: string\n  'aria-labelledby': string\n  onPress?: (event: React.BaseSyntheticEvent) => void\n  onClick?: React.MouseEventHandler\n  tabIndex: number\n}\n\nexport interface GetTagGroupPropsOptions extends React.HTMLProps<HTMLElement> {\n  refKey?: string\n  ref?: React.MutableRefObject<HTMLElement>\n}\n\nexport interface GetTagGroupPropsReturnValue {\n  id: string\n  role: 'listbox'\n  'aria-live': 'polite'\n  'aria-atomic': 'false'\n  'aria-relevant': 'additions'\n  onKeyDown: React.KeyboardEventHandler\n}\nexport type GetTagGroupProps = <Extra extends Record<string, unknown> = {}>(\n  options?: GetTagGroupPropsOptions & Extra,\n) => GetTagGroupPropsReturnValue & Extra\n\nexport type GetTagProps = <Extra extends Record<string, unknown> = {}>(\n  options: GetTagPropsOptions & Extra,\n) => GetTagPropsReturnValue & Extra\n\nexport type GetTagRemoveProps = <Extra extends Record<string, unknown> = {}>(\n  options: GetTagRemovePropsOptions & Extra,\n) => GetTagRemovePropsReturnValue & Extra\n\nexport enum UseTagGroupStateChangeTypes {\n  TagClick = '__tag_click__',\n  TagGroupKeyDownArrowLeft = '__taggroup_keydown_arrowleft__',\n  TagGroupKeyDownArrowRight = '__taggroup_keydown_arrowright__',\n  TagGroupKeyDownBackspace = '__taggroup_keydown_backspace__',\n  TagGroupKeyDownDelete = '__taggroup_keydown_delete__',\n  TagRemoveClick = '__tagremove_click__',\n  FunctionAddItem = '__function_add_item__',\n}\n\nexport type UseTagGroupReducerAction<Item> =\n  | UseTagGroupTagClickReducerAction\n  | UseTagGroupTagKeyDownArrowLeftAction\n  | UseTagGroupTagKeyDownArrowRightAction\n  | UseTagGroupTagKeyDownBackspaceAction\n  | UseTagGroupTagKeyDownDeleteAction\n  | UseTagGroupTagRemoveClickAction\n  | UseTagGroupFunctionAddItem<Item>\n\nexport type UseTagGroupTagClickReducerAction = {\n  type: UseTagGroupStateChangeTypes.TagClick\n  index: number\n}\n\nexport type UseTagGroupTagKeyDownArrowLeftAction = {\n  type: UseTagGroupStateChangeTypes.TagGroupKeyDownArrowLeft\n}\nexport type UseTagGroupTagKeyDownArrowRightAction = {\n  type: UseTagGroupStateChangeTypes.TagGroupKeyDownArrowRight\n}\nexport type UseTagGroupTagKeyDownBackspaceAction = {\n  type: UseTagGroupStateChangeTypes.TagGroupKeyDownBackspace\n}\nexport type UseTagGroupTagKeyDownDeleteAction = {\n  type: UseTagGroupStateChangeTypes.TagGroupKeyDownDelete\n}\nexport type UseTagGroupTagRemoveClickAction = {\n  type: UseTagGroupStateChangeTypes.TagRemoveClick\n  index: number\n}\nexport type UseTagGroupFunctionAddItem<Item> = {\n  type: UseTagGroupStateChangeTypes.FunctionAddItem\n  item: Item\n  index?: number\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/reducer.ts",
    "content": "import {\n  UseTagGroupProps,\n  UseTagGroupReducerAction,\n  UseTagGroupState,\n} from './index.types'\nimport * as stateChangeTypes from './stateChangeTypes'\n\nexport function useTagGroupReducer<Item>(\n  state: UseTagGroupState<Item>,\n  _props: UseTagGroupProps<Item>,\n  action: UseTagGroupReducerAction<Item>,\n): UseTagGroupState<Item> {\n  const {type} = action\n\n  let changes\n\n  switch (type) {\n    case stateChangeTypes.TagClick:\n      changes = {\n        activeIndex: action.index,\n      }\n      break\n    case stateChangeTypes.TagGroupKeyDownArrowLeft:\n      changes = {\n        activeIndex:\n          state.activeIndex === 0\n            ? state.items.length - 1\n            : state.activeIndex - 1,\n      }\n      break\n    case stateChangeTypes.TagGroupKeyDownArrowRight:\n      changes = {\n        activeIndex:\n          state.activeIndex === state.items.length - 1\n            ? 0\n            : state.activeIndex + 1,\n      }\n      break\n    case stateChangeTypes.TagGroupKeyDownBackspace:\n    case stateChangeTypes.TagGroupKeyDownDelete: {\n      const newItems = [\n        ...state.items.slice(0, state.activeIndex),\n        ...state.items.slice(state.activeIndex + 1),\n      ]\n      const newActiveIndex =\n        newItems.length === 0\n          ? -1\n          : newItems.length === state.activeIndex\n          ? state.activeIndex - 1\n          : state.activeIndex\n      changes = {\n        items: [\n          ...state.items.slice(0, state.activeIndex),\n          ...state.items.slice(state.activeIndex + 1),\n        ],\n        activeIndex: newActiveIndex,\n      }\n      break\n    }\n    case stateChangeTypes.TagRemoveClick:\n      {\n        const newItems = [\n          ...state.items.slice(0, action.index),\n          ...state.items.slice(action.index + 1),\n        ]\n        const newActiveIndex =\n          newItems.length === 0\n            ? -1\n            : newItems.length === action.index\n            ? action.index - 1\n            : action.index\n        changes = {\n          items: newItems,\n          activeIndex: newActiveIndex,\n        }\n      }\n      break\n    case stateChangeTypes.FunctionAddItem: {\n      let newItems: Item[] = []\n\n      if (action.index === undefined) {\n        newItems = [...state.items, action.item]\n      } else {\n        newItems = [\n          ...state.items.slice(0, action.index),\n          action.item,\n          ...state.items.slice(action.index),\n        ]\n      }\n\n      const newActiveIndex =\n        state.activeIndex === -1 ? newItems.length - 1 : state.activeIndex\n\n      changes = {\n        items: newItems,\n        activeIndex: newActiveIndex,\n      }\n      break\n    }\n    default:\n      throw new Error('Invalid useTagGroup reducer action.')\n  }\n\n  return {...state, ...changes}\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/stateChangeTypes.ts",
    "content": "import productionEnum from '../../productionEnum.macro'\nimport {UseTagGroupStateChangeTypes} from './index.types'\n\nexport const TagClick = productionEnum(\n  '__tag_click__',\n) as UseTagGroupStateChangeTypes.TagClick\n\nexport const TagGroupKeyDownArrowLeft = productionEnum(\n  '__taggroup_keydown_arrowleft__',\n) as UseTagGroupStateChangeTypes.TagGroupKeyDownArrowLeft\nexport const TagGroupKeyDownArrowRight = productionEnum(\n  '__taggroup_keydown_arrowright__',\n) as UseTagGroupStateChangeTypes.TagGroupKeyDownArrowRight\nexport const TagGroupKeyDownDelete = productionEnum(\n  '__taggroup_keydown_delete__',\n) as UseTagGroupStateChangeTypes.TagGroupKeyDownDelete\nexport const TagGroupKeyDownBackspace = productionEnum(\n  '__taggroup_keydown_backspace__',\n) as UseTagGroupStateChangeTypes.TagGroupKeyDownBackspace\n\nexport const TagRemoveClick = productionEnum(\n  '__tagremove_click__',\n) as UseTagGroupStateChangeTypes.TagRemoveClick\n\nexport const FunctionAddItem = productionEnum(\n  '__function_add_item__',\n) as UseTagGroupStateChangeTypes.FunctionAddItem\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/__tests__/useAccessibleDescription.test.ts",
    "content": "import {renderHook, act} from '@testing-library/react'\nimport {A11Y_DESCRIPTION_ELEMENT_ID, useAccessibleDescription} from '..'\n\ndescribe('useAccessibleDescription', () => {\n  test('does nothing if document is undefined', () => {\n    const {result} = renderHook(() =>\n      useAccessibleDescription(undefined, 'description'),\n    )\n\n    expect(result.current).toBeUndefined()\n  })\n\n  test('adds a div element to the document that serves as accessible description', () => {\n    const divElement = {\n      setAttribute: jest.fn(),\n      remove: jest.fn(),\n      style: {display: ''},\n      textContent: '',\n    }\n\n    const document: Document = {\n      createElement: jest.fn().mockReturnValue(divElement),\n      body: {\n        appendChild: jest.fn(),\n      },\n    } as unknown as Document\n    const description = 'press delete to remove'\n\n    const {unmount} = renderHook(() =>\n      useAccessibleDescription(document, description),\n    )\n\n    expect(document.createElement).toHaveBeenCalledTimes(1)\n    expect(document.createElement).toHaveBeenCalledWith('div')\n    expect(divElement.setAttribute).toHaveBeenCalledTimes(1)\n    expect(divElement.setAttribute).toHaveBeenCalledWith(\n      'id',\n      A11Y_DESCRIPTION_ELEMENT_ID,\n    )\n    // eslint-disable-next-line jest-dom/prefer-to-have-style\n    expect(divElement.style.display).toEqual('none')\n    // eslint-disable-next-line jest-dom/prefer-to-have-text-content\n    expect(divElement.textContent).toEqual(description)\n    expect(document.body.appendChild).toHaveBeenCalledTimes(1)\n    expect(document.body.appendChild).toHaveBeenCalledWith(divElement)\n\n    act(() => {\n      unmount()\n    })\n\n    expect(divElement.remove).toHaveBeenCalledTimes(1)\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/__tests__/useElementIds.legacy.test.ts",
    "content": "import {renderHook} from '@testing-library/react'\nimport {useElementIds} from '..'\n\njest.mock('react', () => {\n  const {useId, ...react} = jest.requireActual('react')\n  return react\n})\n\ndescribe('useElementIds for React < 18', () => {\n  test('uses generateId()', () => {\n    const {result} = renderHook(() => useElementIds({}))\n\n    expect(result.current).toEqual({\n      getTagId: expect.any(Function),\n      tagGroupId: 'downshift-0-tag-group',\n    })\n    expect(result.current.getTagId(12)).toEqual('downshift-0-tag-12')\n  })\n\n  test('returns the same reference on re-renders when the props do not change', () => {\n      const getTestTagId = () => 'test-tag-id'\n      const {result, rerender} = renderHook(useElementIds, {\n        initialProps: {\n          id: 'test-id',\n          tagGroupId: 'test-tag-group-id',\n          getTagId: getTestTagId,\n        },\n      })\n      const renderOneResult = result.current\n      rerender({\n        id: 'test-id',\n        tagGroupId: 'test-tag-group-id',\n        getTagId: getTestTagId,\n      })\n      const renderTwoResult = result.current\n      expect(renderOneResult).toBe(renderTwoResult)\n    })\n  \n    test('returns a new reference on re-renders when the props change', () => {\n      const {result, rerender} = renderHook(useElementIds, {\n        initialProps: {\n          id: 'test-id',\n          tagGroupId: 'test-tag-group-id',\n          getTagId: () => 'test-tag-id',\n        },\n      })\n      const renderOneResult = result.current\n      rerender({\n        id: 'test-id',\n        tagGroupId: 'test-tag-group-id',\n        getTagId: () => 'test-tag-id',\n      })\n      const renderTwoResult = result.current\n      expect(renderOneResult).not.toBe(renderTwoResult)\n    })\n\n  test('generates stable IDs across re-renders', () => {\n    const {result, rerender} = renderHook(() => useElementIds({}))\n\n    const firstTagGroupId = result.current.tagGroupId\n    const firstTagId = result.current.getTagId(0)\n\n    rerender()\n\n    expect(result.current.tagGroupId).toBe(firstTagGroupId)\n    expect(result.current.getTagId(0)).toBe(firstTagId)\n  })\n\n  test('uses provided id prop', () => {\n    const {result} = renderHook(() => useElementIds({id: 'custom-id'}))\n\n    expect(result.current.tagGroupId).toBe('custom-id-tag-group')\n    expect(result.current.getTagId(7)).toBe('custom-id-tag-7')\n  })\n\n  test('uses custom tagGroupId and getTagId when provided', () => {\n    const customGetTagId = (index: number) => `my-tag-${index}`\n    const {result} = renderHook(() =>\n      useElementIds({\n        tagGroupId: 'my-tag-group',\n        getTagId: customGetTagId,\n      }),\n    )\n\n    expect(result.current.tagGroupId).toBe('my-tag-group')\n    expect(result.current.getTagId(4)).toBe('my-tag-4')\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/__tests__/useElementIds.r18.test.ts",
    "content": "import {renderHook} from '@testing-library/react'\nimport {useElementIds} from '..'\n\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'mocked-id'\n    },\n  }\n})\n\ndescribe('useElementIds for React >= 18', () => {\n  test('uses React.useId', () => {\n    const {result} = renderHook(() => useElementIds({}))\n\n    expect(result.current).toEqual({\n      getTagId: expect.any(Function),\n      tagGroupId: 'downshift-mocked-id-tag-group',\n    })\n    expect(result.current.getTagId(10)).toEqual(\"downshift-mocked-id-tag-10\")\n  })\n\n  test('returns the same reference on re-renders when the props do not change', () => {\n      const getTestTagId = () => 'test-tag-id'\n      const {result, rerender} = renderHook(useElementIds, {\n        initialProps: {\n          id: 'test-id',\n          tagGroupId: 'test-tag-group-id',\n          getTagId: getTestTagId,\n        },\n      })\n      const renderOneResult = result.current\n      rerender({\n        id: 'test-id',\n        tagGroupId: 'test-tag-group-id',\n        getTagId: getTestTagId,\n      })\n      const renderTwoResult = result.current\n      expect(renderOneResult).toBe(renderTwoResult)\n    })\n  \n    test('returns a new reference on re-renders when the props change', () => {\n      const {result, rerender} = renderHook(useElementIds, {\n        initialProps: {\n          id: 'test-id',\n          tagGroupId: 'test-tag-group-id',\n          getTagId: () => 'test-tag-id',\n        },\n      })\n      const renderOneResult = result.current\n      rerender({\n        id: 'test-id',\n        tagGroupId: 'test-tag-group-id',\n        getTagId: () => 'test-tag-id',\n      })\n      const renderTwoResult = result.current\n      expect(renderOneResult).not.toBe(renderTwoResult)\n    })\n\n  test('generates stable IDs across re-renders', () => {\n    const {result, rerender} = renderHook(() => useElementIds({}))\n\n    const firstTagGroupId = result.current.tagGroupId\n    const firstTagId = result.current.getTagId(0)\n\n    rerender()\n\n    expect(result.current.tagGroupId).toBe(firstTagGroupId)\n    expect(result.current.getTagId(0)).toBe(firstTagId)\n  })\n\n  test('uses provided id prop', () => {\n    const {result} = renderHook(() => useElementIds({id: 'custom-id'}))\n\n    expect(result.current.tagGroupId).toBe('custom-id-tag-group')\n    expect(result.current.getTagId(5)).toBe('custom-id-tag-5')\n  })\n\n  test('uses custom tagGroupId and getTagId when provided', () => {\n    const customGetTagId = (index: number) => `my-tag-${index}`\n    const {result} = renderHook(() =>\n      useElementIds({\n        tagGroupId: 'my-tag-group',\n        getTagId: customGetTagId,\n      }),\n    )\n\n    expect(result.current.tagGroupId).toBe('my-tag-group')\n    expect(result.current.getTagId(3)).toBe('my-tag-3')\n  })\n})\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/getInitialState.ts",
    "content": "import {UseTagGroupProps, UseTagGroupState} from '../index.types'\n\nexport function getInitialState<I>(\n  props: UseTagGroupProps<I>,\n): UseTagGroupState<I> {\n  const items = props.items ?? props.initialItems ?? []\n  const activeIndex =\n    props.activeIndex ??\n    props.initialActiveIndex ??\n    (items.length === 0 ? -1 : 0)\n\n  return {\n    activeIndex,\n    items,\n  }\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/getMergedProps.ts",
    "content": "import {UseTagGroupProps, UseTagGroupMergedProps} from '../index.types'\n\nimport {isReactNative} from '../../../is.macro.js'\n\nexport function getMergedProps<Item>(\n  userProps: UseTagGroupProps<Item>,\n): UseTagGroupMergedProps<Item> {\n  return {\n    stateReducer(_s, {changes}) {\n      return changes\n    },\n    environment:\n      /* istanbul ignore next (ssr) */\n      typeof window === 'undefined' || isReactNative ? undefined : window,\n    removeElementDescription: 'Press Delete or Backspace to remove tag.',\n    ...userProps,\n  }\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/index.ts",
    "content": "import PropTypes from 'prop-types'\n\nexport {useElementIds} from './useElementIds'\nexport {getInitialState} from './getInitialState'\nexport {isStateEqual} from './isStateEqual'\nexport {\n  useAccessibleDescription,\n  A11Y_DESCRIPTION_ELEMENT_ID,\n} from './useAccessibleDescription'\nexport {getMergedProps} from './getMergedProps'\nexport {useRovingTagFocus} from './useRovingTagFocus'\n\nexport const propTypes: Record<\n  string,\n  PropTypes.Requireable<(...args: unknown[]) => unknown>\n> = {\n  isItemDisabled: PropTypes.func,\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/isStateEqual.ts",
    "content": "import {UseTagGroupState} from '../index.types'\n\nexport function isStateEqual<I>(\n  oldState: UseTagGroupState<I>,\n  newState: UseTagGroupState<I>,\n): boolean {\n  return (\n    oldState.activeIndex === newState.activeIndex &&\n    oldState.items === newState.items\n  )\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/useAccessibleDescription.ts",
    "content": "import * as React from 'react'\n\nexport const A11Y_DESCRIPTION_ELEMENT_ID = 'tag-group-a11y-description'\n\nexport function useAccessibleDescription(\n  document: Document | undefined,\n  description: string,\n) {\n  React.useEffect(() => {\n    if (!document) {\n      return\n    }\n\n    const accessibleDescriptionElement = document.createElement('div')\n\n    accessibleDescriptionElement.setAttribute('id', A11Y_DESCRIPTION_ELEMENT_ID)\n    accessibleDescriptionElement.style.display = 'none'\n    accessibleDescriptionElement.textContent = description\n\n    document.body.appendChild(accessibleDescriptionElement)\n\n    return () => {\n      accessibleDescriptionElement.remove()\n    }\n  }, [description, document])\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/useElementIds.ts",
    "content": "import * as React from 'react'\n\nimport {generateId} from '../../../utils-ts'\n\ntype UseElementIdsProps = {\n  id?: string\n  tagGroupId?: string\n  getTagId?: (index: number) => string\n}\n\ntype UseElementIdsReturnValue = {\n  tagGroupId: string\n  getTagId: (index: number) => string\n}\n\n// https://github.com/downshift-js/downshift/issues/1674#issuecomment-3924320872\nconst SafeReact = {...React}\nconst reactUseId = SafeReact.useId\n\n// istanbul ignore next\nexport const useElementIds: (\n  props: UseElementIdsProps,\n) => UseElementIdsReturnValue =\n  typeof reactUseId === 'function' ? useElementIdsR18 : useElementIdsLegacy\n\nfunction useElementIdsR18({\n  id,\n  tagGroupId,\n  getTagId,\n}: UseElementIdsProps): UseElementIdsReturnValue {\n  const reactId = `downshift-${reactUseId()}`\n  if (!id) {\n    id = reactId\n  }\n\n  const elementIds = React.useMemo(\n    () => ({\n      tagGroupId: tagGroupId ?? `${id}-tag-group`,\n      getTagId: getTagId ?? (index => `${id}-tag-${index}`),\n    }),\n    [getTagId, id, tagGroupId],\n  )\n\n  return elementIds\n}\n\nfunction useElementIdsLegacy({\n  id,\n  getTagId,\n  tagGroupId,\n}: UseElementIdsProps): UseElementIdsReturnValue {\n  const baseIdRef = React.useRef(id ?? `downshift-${generateId()}`)\n  const baseId = baseIdRef.current\n\n  const elementIds = React.useMemo(\n    () => ({\n      tagGroupId: tagGroupId ?? `${baseId}-tag-group`,\n      getTagId: getTagId ?? (index => `${baseId}-tag-${index}`),\n    }),\n    [getTagId, baseId, tagGroupId],\n  )\n\n  return elementIds\n}\n"
  },
  {
    "path": "src/hooks/useTagGroup/utils/useRovingTagFocus.ts",
    "content": "import * as React from 'react'\nimport {UseTagGroupProps} from '../index.types'\n\n/**\n * Focuses the tag at activeIndex when it changes or when an item is removed.\n */\nexport function useRovingTagFocus(\n  activeIndex: number,\n  itemsLength: number,\n  getTagId: NonNullable<UseTagGroupProps<unknown>['getTagId']>,\n): React.MutableRefObject<Record<string, HTMLElement>> {\n  const itemRefs = React.useRef<Record<string, HTMLElement>>({})\n  const previousActiveIndexRef = React.useRef(activeIndex)\n  const previousItemsLengthRef = React.useRef(itemsLength)\n\n  React.useEffect(() => {\n    if (\n      (activeIndex !== -1 &&\n        previousActiveIndexRef.current !== -1 &&\n        activeIndex !== previousActiveIndexRef.current) ||\n      previousItemsLengthRef.current === itemsLength + 1\n    ) {\n      itemRefs.current[getTagId(activeIndex)]?.focus()\n    }\n\n    previousActiveIndexRef.current = activeIndex\n    previousItemsLengthRef.current = itemsLength\n  }, [activeIndex, getTagId, itemsLength])\n\n  return itemRefs\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/__tests__/getItemAndIndex.test.ts",
    "content": "import {getItemAndIndex} from '../getItemAndIndex'\n\ntest('returns the props if both are passed', () => {\n  const item = {hi: 'hello'}\n  const index = 5\n\n  expect(getItemAndIndex(item, index, [item], 'bla')).toEqual([item, index])\n})\n\ntest('throws error when index is not passed and item is not found in the array', () => {\n  const item = {hi: 'hello'}\n  const errorMessage = 'no item found'\n\n  expect(() => getItemAndIndex(item, undefined, [], errorMessage)).toThrow(\n    errorMessage,\n  )\n})\n\ntest('returns the item and the index found', () => {\n  const item = {hi: 'hello'}\n\n  expect(getItemAndIndex(item, undefined, [item], 'bla')).toEqual([item, 0])\n})\n\ntest('throws error when item is not passed and item is not found in the array', () => {\n  const item = {hi: 'hello'}\n  const errorMessage = 'no item found at index'\n\n  expect(() =>\n    getItemAndIndex(undefined, 1, [item], errorMessage),\n  ).toThrow(errorMessage)\n})\n\ntest('returns the index and the item found', () => {\n  const item = {hi: 'hello'}\n  const index = 0\n\n  expect(getItemAndIndex(undefined, index, [item], 'bla')).toEqual([item, 0])\n})\n\ntest('throws error when both index and item are not passed', () => {\n  const errorMessage = 'it is all wrong'\n  \n  expect(() =>\n    getItemAndIndex(undefined, undefined, [{item: 'bla'}], errorMessage),\n  ).toThrow(errorMessage)\n})\n"
  },
  {
    "path": "src/hooks/utils-ts/callOnChangeProps.ts",
    "content": "import {Action, Props, State} from '../../utils-ts'\nimport {capitalizeString} from './capitalizeString'\n\nexport function callOnChangeProps<\n  S extends State,\n  P extends Partial<S> & Props<S, T>,\n  T,\n>(action: Action<T>, props: P, state: S, newState: S) {\n  const {type} = action\n  const changes: Partial<State> = {}\n  const keys = Object.keys(state)\n\n  for (const key of keys) {\n    invokeOnChangeHandler(key, action, props, state, newState)\n\n    if (newState[key] !== state[key]) {\n      changes[key] = newState[key]\n    }\n  }\n\n  if (props.onStateChange && Object.keys(changes).length) {\n    props.onStateChange({type, ...changes})\n  }\n}\n\nfunction invokeOnChangeHandler<\n  S extends State,\n  P extends Partial<S> & Props<S, T>,\n  T,\n>(key: string, action: Action<T>, props: P, state: S, newState: S) {\n  if (newState[key] === state[key]) {\n    return\n  }\n\n  const handlerKey = `on${capitalizeString(key)}Change`\n  const handler = props[handlerKey]\n\n  if (typeof handler !== 'function') {\n    return\n  }\n\n  const {type} = action\n\n  handler({type, ...newState})\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/capitalizeString.ts",
    "content": "export function capitalizeString(string: string): string {\n  return `${string.slice(0, 1).toUpperCase()}${string.slice(1)}`\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/getDefaultValue.ts",
    "content": "import {State} from '../../utils-ts'\nimport {capitalizeString} from './capitalizeString'\n\nexport function getDefaultValue<S extends State, P extends Partial<S>>(\n  props: P,\n  propKey: keyof S,\n  defaultStateValues: S,\n): S[keyof S] {\n  const defaultValue = props[`default${capitalizeString(propKey as string)}`]\n\n  if (defaultValue !== undefined) {\n    return defaultValue as S[keyof S]\n  }\n\n  return defaultStateValues[propKey]\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/getInitialValue.ts",
    "content": "import {State} from '../../utils-ts'\nimport {capitalizeString} from './capitalizeString'\nimport {getDefaultValue} from './getDefaultValue'\n\nexport function getInitialValue<S extends State, P extends Partial<S>>(\n  props: P,\n  propKey: keyof S,\n  defaultStateValues: S,\n): S[keyof S] {\n  const value = props[propKey] as keyof S | undefined\n\n  if (value !== undefined) {\n    return value as S[keyof S]\n  }\n\n  const initialValue = props[`initial${capitalizeString(propKey as string)}`]\n\n  if (initialValue !== undefined) {\n    return initialValue as S[keyof S]\n  }\n\n  return getDefaultValue(props, propKey, defaultStateValues)\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/getItemAndIndex.ts",
    "content": "/**\n * Returns both the item and index when both or either is passed.\n *\n * @param itemProp The item which could be undefined.\n * @param indexProp The index which could be undefined.\n * @param items The array of items to get the item based on index.\n * @param errorMessage The error to be thrown if index and item could not be returned for any reason.\n * @returns An array with item and index.\n */\nexport function getItemAndIndex<Item>(\n  itemProp: Item | undefined,\n  indexProp: number | undefined,\n  items: Item[],\n  errorMessage: string,\n): [Item, number] {\n  if (itemProp !== undefined && indexProp !== undefined) {\n    return [itemProp, indexProp]\n  }\n\n  if (itemProp !== undefined) {\n    const index = items.indexOf(itemProp)\n\n    if (index < 0) {\n      throw new Error(errorMessage)\n    }\n\n    return [itemProp, items.indexOf(itemProp)]\n  }\n\n  if (indexProp !== undefined) {\n    const item = items[indexProp]\n\n    if (item === undefined) {\n      throw new Error(errorMessage)\n    }\n    return [item, indexProp]\n  }\n\n  throw new Error(errorMessage)\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/index.ts",
    "content": "export {useControlledReducer} from './useControlledReducer'\nexport {useEnhancedReducer} from './useEnhancedReducer'\nexport {callOnChangeProps} from './callOnChangeProps'\nexport {getItemAndIndex} from './getItemAndIndex'\nexport {useIsInitialMount} from './useIsInitialMount'\nexport {stateReducer} from './stateReducer'\nexport {propTypes as commonPropTypes} from './propTypes'\nexport {capitalizeString} from './capitalizeString'\nexport {getDefaultValue} from './getDefaultValue'\nexport {getInitialValue} from './getInitialValue'\nexport {useA11yMessageStatus} from './useA11yMessageStatus'\n"
  },
  {
    "path": "src/hooks/utils-ts/propTypes.ts",
    "content": "import PropTypes from 'prop-types'\n\n// Shared between all exports.\nexport const propTypes = {\n  environment: PropTypes.shape({\n    addEventListener: PropTypes.func.isRequired,\n    removeEventListener: PropTypes.func.isRequired,\n    document: PropTypes.shape({\n      createElement: PropTypes.func.isRequired,\n      getElementById: PropTypes.func.isRequired,\n      activeElement: PropTypes.any.isRequired,\n      body: PropTypes.any.isRequired,\n    }).isRequired,\n    Node: PropTypes.func.isRequired,\n  }),\n  itemToKey: PropTypes.func,\n  stateReducer: PropTypes.func,\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/stateReducer.ts",
    "content": "import {Action, State} from '../../utils-ts'\n\n/**\n * Default state reducer that returns the changes.\n *\n */\nexport function stateReducer<T>(_s: State, a: Action<T>) {\n  return a.changes\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/useA11yMessageStatus.ts",
    "content": "import * as React from 'react'\n\nimport {cleanupStatusDiv, debounce, setStatus} from '../../utils-ts'\nimport {isReactNative} from '../../is.macro.js'\nimport {useIsInitialMount} from './useIsInitialMount'\n\n/**\n * Debounced call for updating the a11y message.\n */\nconst updateA11yStatus = debounce((status: string, document: Document) => {\n  setStatus(status, document)\n}, 200)\n\n/**\n * Adds an a11y aria live status message if getA11yStatusMessage is passed.\n * @param getA11yStatusMessage The function that builds the status message.\n * @param options The options to be passed to getA11yStatusMessage if called.\n * @param dependencyArray The dependency array that triggers the status message setter via useEffect.\n * @param environment The environment object containing the document.\n */\nexport function useA11yMessageStatus<Options>(\n  getA11yStatusMessage: ((options: Options) => string) | undefined,\n  options: Options,\n  dependencyArray: unknown[],\n  environment: {document: Document | undefined} | undefined,\n) {\n  const document = environment?.document\n  const isInitialMount = useIsInitialMount()\n\n  // Adds an a11y aria live status message if getA11yStatusMessage is passed.\n  React.useEffect(() => {\n    if (!getA11yStatusMessage || isInitialMount || isReactNative || !document) {\n      return\n    }\n\n    const status = getA11yStatusMessage(options)\n\n    updateA11yStatus(status, document)\n\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, dependencyArray)\n\n  // Cleanup the status message container.\n  React.useEffect(() => {\n    return () => {\n      updateA11yStatus.cancel()\n      cleanupStatusDiv(document)\n    }\n  }, [document])\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/useControlledReducer.ts",
    "content": "import {getState, type Action, type State, type Props} from '../../utils-ts'\nimport {useEnhancedReducer} from './useEnhancedReducer'\n\n/**\n * Wraps the useEnhancedReducer and applies the controlled prop values before\n * returning the new state.\n *\n * @param {Function} reducer Reducer function from downshift.\n * @param {Object} props The hook props, also passed to createInitialState.\n * @param {Function} createInitialState Function that returns the initial state.\n * @param {Function} isStateEqual Function that checks if a previous state is equal to the next.\n * @returns {Array} An array with the state and an action dispatcher.\n */\nexport function useControlledReducer<\n  S extends State,\n  P extends Partial<S> & Props<S, T>,\n  T,\n  A extends Action<T>,\n>(\n  reducer: (state: S, props: P, action: A) => S,\n  props: P,\n  createInitialState: (props: P) => S,\n  isStateEqual: (prevState: S, newState: S) => boolean,\n): [S, (action: A) => void] {\n  const [state, dispatch] = useEnhancedReducer(\n    reducer,\n    props,\n    createInitialState,\n    isStateEqual,\n  )\n\n  return [getState(state, props), dispatch]\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/useEnhancedReducer.ts",
    "content": "import * as React from 'react'\n\nimport {\n  type Action,\n  type Props,\n  type State,\n  getState,\n  useLatestRef,\n} from '../../utils-ts'\nimport {callOnChangeProps} from './callOnChangeProps'\n\n/**\n * Computes the controlled state using a the previous state, props,\n * two reducers, one from downshift and an optional one from the user.\n * Also calls the onChange handlers for state values that have changed.\n *\n * @param {Function} reducer Reducer function from downshift.\n * @param {Object} props The hook props, also passed to createInitialState.\n * @param {Function} createInitialState Function that returns the initial state.\n * @param {Function} isStateEqual Function that checks if a previous state is equal to the next.\n * @returns {Array} An array with the state and an action dispatcher.\n */\nexport function useEnhancedReducer<\n  S extends State,\n  P extends Partial<S> & Props<S, T>,\n  T,\n  A extends Action<T>,\n>(\n  reducer: (state: S, props: P, action: A) => S,\n  props: P,\n  createInitialState: (props: P) => S,\n  isStateEqual: (prevState: S, newState: S) => boolean,\n): [S, (action: A) => void] {\n  const prevStateRef = React.useRef<S | null>(null)\n  const actionRef = React.useRef<A>(undefined)\n  const propsRef = useLatestRef(props)\n\n  const enhancedReducer = React.useCallback(\n    (state: S, action: A): S => {\n      actionRef.current = action\n      state = getState(state, propsRef.current)\n\n      const changes = reducer(state, propsRef.current, action)\n      const newState = propsRef.current.stateReducer(state, {\n        ...action,\n        changes,\n      })\n\n      return {...state, ...newState}\n    },\n    [propsRef, reducer],\n  )\n  const [state, dispatch] = React.useReducer(\n    enhancedReducer,\n    props,\n    createInitialState,\n  )\n\n  const action = actionRef.current\n\n  React.useEffect(() => {\n    const prevState = getState(\n      prevStateRef.current ?? ({} as S),\n      propsRef.current,\n    )\n    const shouldCallOnChangeProps =\n      action && prevStateRef.current && !isStateEqual(prevState, state)\n\n    if (shouldCallOnChangeProps) {\n      callOnChangeProps(action, propsRef.current, prevState, state)\n    }\n\n    prevStateRef.current = state\n  }, [state, action, isStateEqual, propsRef])\n\n  return [state, dispatch]\n}\n"
  },
  {
    "path": "src/hooks/utils-ts/useIsInitialMount.ts",
    "content": "import * as React from 'react'\n\n/**\n * Tracks if it's the first render.\n */\nexport function useIsInitialMount(): boolean {\n  const isInitialMountRef = React.useRef(true)\n\n  React.useEffect(() => {\n    isInitialMountRef.current = false\n\n    return () => {\n      isInitialMountRef.current = true\n    }\n  }, [])\n\n  return isInitialMountRef.current\n}\n"
  },
  {
    "path": "src/hooks/utils.dropdown/__tests__/useElementIds.legacy.test.ts",
    "content": "import {renderHook} from '@testing-library/react'\n\nimport {useElementIds} from '../useElementIds'\n\njest.mock('react', () => {\n  const {useId, ...react} = jest.requireActual('react')\n  return react\n})\n\ndescribe('useElementIds', () => {\n  test('uses generateId() for React < 18', () => {\n    const {result} = renderHook(() => useElementIds({}))\n\n    expect(result.current).toEqual({\n      getItemId: expect.any(Function),\n      inputId: 'downshift-0-input',\n      labelId: 'downshift-0-label',\n      menuId: 'downshift-0-menu',\n      toggleButtonId: 'downshift-0-toggle-button',\n    })\n    expect(result.current.getItemId(5)).toEqual('downshift-0-item-5')\n  })\n\n  test('returns the same reference on re-renders when the props do not change', () => {\n    const getTestItemId = () => 'test-item-id'\n    const {result, rerender} = renderHook(useElementIds, {\n      initialProps: {\n        id: 'test-id',\n        labelId: 'test-label-id',\n        menuId: 'test-menu-id',\n        getItemId: getTestItemId,\n        toggleButtonId: 'test-toggle-button-id',\n        inputId: 'test-input-id',\n      },\n    })\n    const renderOneResult = result.current\n    rerender({\n      id: 'test-id',\n      labelId: 'test-label-id',\n      menuId: 'test-menu-id',\n      getItemId: getTestItemId,\n      toggleButtonId: 'test-toggle-button-id',\n      inputId: 'test-input-id',\n    })\n    const renderTwoResult = result.current\n    expect(renderOneResult).toBe(renderTwoResult)\n  })\n\n  test('returns a new reference on re-renders when the props change', () => {\n    const {result, rerender} = renderHook(useElementIds, {\n      initialProps: {\n        id: 'test-id',\n        labelId: 'test-label-id',\n        menuId: 'test-menu-id',\n        getItemId: () => 'test-item-id',\n        toggleButtonId: 'test-toggle-button-id',\n        inputId: 'test-input-id',\n      },\n    })\n    const renderOneResult = result.current\n    rerender({\n      id: 'test-id',\n      labelId: 'test-label-id',\n      menuId: 'test-menu-id',\n      getItemId: () => 'test-item-id',\n      toggleButtonId: 'test-toggle-button-id',\n      inputId: 'test-input-id',\n    })\n    const renderTwoResult = result.current\n    expect(renderOneResult).not.toBe(renderTwoResult)\n  })\n\n  test('generates stable IDs across re-renders', () => {\n    const {result, rerender} = renderHook(() => useElementIds({}))\n\n    const firstInputId = result.current.inputId\n    const firstLabelId = result.current.labelId\n    const firstMenuId = result.current.menuId\n    const firstToggleButtonId = result.current.toggleButtonId\n    const firstGetItemId = result.current.getItemId(0)\n\n    rerender()\n\n    expect(result.current.inputId).toBe(firstInputId)\n    expect(result.current.labelId).toBe(firstLabelId)\n    expect(result.current.menuId).toBe(firstMenuId)\n    expect(result.current.toggleButtonId).toBe(firstToggleButtonId)\n    expect(result.current.getItemId(0)).toBe(firstGetItemId)\n  })\n\n  test('uses provided id prop', () => {\n    const {result} = renderHook(() => useElementIds({id: 'custom-id'}))\n\n    expect(result.current.inputId).toBe('custom-id-input')\n    expect(result.current.labelId).toBe('custom-id-label')\n    expect(result.current.menuId).toBe('custom-id-menu')\n    expect(result.current.toggleButtonId).toBe('custom-id-toggle-button')\n    expect(result.current.getItemId(7)).toBe('custom-id-item-7')\n  })\n\n  test('uses custom IDs when provided', () => {\n    const customGetItemId = (index: number) => `my-item-${index}`\n    const {result} = renderHook(() =>\n      useElementIds({\n        labelId: 'my-label',\n        menuId: 'my-menu',\n        getItemId: customGetItemId,\n        toggleButtonId: 'my-toggle-button',\n        inputId: 'my-input',\n      }),\n    )\n\n    expect(result.current.labelId).toBe('my-label')\n    expect(result.current.menuId).toBe('my-menu')\n    expect(result.current.toggleButtonId).toBe('my-toggle-button')\n    expect(result.current.inputId).toBe('my-input')\n    expect(result.current.getItemId(3)).toBe('my-item-3')\n  })\n})\n"
  },
  {
    "path": "src/hooks/utils.dropdown/__tests__/useElementIds.r18.test.ts",
    "content": "import {renderHook} from '@testing-library/react'\n\nimport {useElementIds} from '../useElementIds'\n\njest.mock('react', () => {\n  return {\n    ...jest.requireActual('react'),\n    useId() {\n      return 'mocked-id'\n    },\n  }\n})\n\ndescribe('useElementIds', () => {\n  test('uses React.useId for React >= 18', () => {\n    const {result} = renderHook(() => useElementIds({}))\n\n    expect(result.current).toEqual({\n      getItemId: expect.any(Function),\n      inputId: 'downshift-mocked-id-input',\n      labelId: 'downshift-mocked-id-label',\n      menuId: 'downshift-mocked-id-menu',\n      toggleButtonId: 'downshift-mocked-id-toggle-button',\n    })\n    expect(result.current.getItemId(5)).toEqual('downshift-mocked-id-item-5')\n  })\n\n  test('returns the same reference on re-renders when the props do not change', () => {\n    const getTestItemId = () => 'test-item-id'\n    const {result, rerender} = renderHook(useElementIds, {\n      initialProps: {\n        id: 'test-id',\n        labelId: 'test-label-id',\n        menuId: 'test-menu-id',\n        getItemId: getTestItemId,\n        toggleButtonId: 'test-toggle-button-id',\n        inputId: 'test-input-id',\n      },\n    })\n    const renderOneResult = result.current\n    rerender({\n      id: 'test-id',\n      labelId: 'test-label-id',\n      menuId: 'test-menu-id',\n      getItemId: getTestItemId,\n      toggleButtonId: 'test-toggle-button-id',\n      inputId: 'test-input-id',\n    })\n    const renderTwoResult = result.current\n    expect(renderOneResult).toBe(renderTwoResult)\n  })\n\n  test('returns a new reference on re-renders when the props change', () => {\n    const {result, rerender} = renderHook(useElementIds, {\n      initialProps: {\n        id: 'test-id',\n        labelId: 'test-label-id',\n        menuId: 'test-menu-id',\n        getItemId: () => 'test-item-id',\n        toggleButtonId: 'test-toggle-button-id',\n        inputId: 'test-input-id',\n      },\n    })\n    const renderOneResult = result.current\n    rerender({\n      id: 'test-id',\n      labelId: 'test-label-id',\n      menuId: 'test-menu-id',\n      getItemId: () => 'test-item-id',\n      toggleButtonId: 'test-toggle-button-id',\n      inputId: 'test-input-id',\n    })\n    const renderTwoResult = result.current\n    expect(renderOneResult).not.toBe(renderTwoResult)\n  })\n\n  test('generates stable IDs across re-renders', () => {\n    const {result, rerender} = renderHook(() => useElementIds({}))\n\n    const firstInputId = result.current.inputId\n    const firstLabelId = result.current.labelId\n    const firstMenuId = result.current.menuId\n    const firstToggleButtonId = result.current.toggleButtonId\n    const firstGetItemId = result.current.getItemId(0)\n\n    rerender()\n\n    expect(result.current.inputId).toBe(firstInputId)\n    expect(result.current.labelId).toBe(firstLabelId)\n    expect(result.current.menuId).toBe(firstMenuId)\n    expect(result.current.toggleButtonId).toBe(firstToggleButtonId)\n    expect(result.current.getItemId(0)).toBe(firstGetItemId)\n  })\n\n  test('uses provided id prop', () => {\n    const {result} = renderHook(() => useElementIds({id: 'custom-id'}))\n\n    expect(result.current.inputId).toBe('custom-id-input')\n    expect(result.current.labelId).toBe('custom-id-label')\n    expect(result.current.menuId).toBe('custom-id-menu')\n    expect(result.current.toggleButtonId).toBe('custom-id-toggle-button')\n    expect(result.current.getItemId(7)).toBe('custom-id-item-7')\n  })\n\n  test('uses custom IDs when provided', () => {\n    const customGetItemId = (index: number) => `my-item-${index}`\n    const {result} = renderHook(() =>\n      useElementIds({\n        labelId: 'my-label',\n        menuId: 'my-menu',\n        getItemId: customGetItemId,\n        toggleButtonId: 'my-toggle-button',\n        inputId: 'my-input',\n      }),\n    )\n\n    expect(result.current.labelId).toBe('my-label')\n    expect(result.current.menuId).toBe('my-menu')\n    expect(result.current.toggleButtonId).toBe('my-toggle-button')\n    expect(result.current.inputId).toBe('my-input')\n    expect(result.current.getItemId(3)).toBe('my-item-3')\n  })\n})\n"
  },
  {
    "path": "src/hooks/utils.dropdown/defaultProps.ts",
    "content": "import {scrollIntoView} from '../../utils-ts'\nimport {stateReducer} from '../utils-ts'\n\nimport {isReactNative} from '../../is.macro.js'\n\nexport const defaultProps = {\n  itemToString(item: unknown) {\n    return item ? String(item) : ''\n  },\n  itemToKey(item: unknown) {\n    return item\n  },\n  stateReducer,\n  scrollIntoView,\n  environment:\n    /* istanbul ignore next (ssr) */\n    typeof window === 'undefined' || isReactNative ? undefined : window,\n}\n"
  },
  {
    "path": "src/hooks/utils.dropdown/defaultStateValues.ts",
    "content": "export const defaultStateValues = {\n  highlightedIndex: -1,\n  isOpen: false,\n  selectedItem: null as unknown,\n  inputValue: '',\n}\n"
  },
  {
    "path": "src/hooks/utils.dropdown/index.ts",
    "content": "export {propTypes as dropdownPropTypes} from './propTypes'\nexport {defaultProps as dropdownDefaultProps} from './defaultProps'\nexport {defaultStateValues as dropdownDefaultStateValues} from './defaultStateValues'\n"
  },
  {
    "path": "src/hooks/utils.dropdown/propTypes.ts",
    "content": "import PropTypes from 'prop-types'\n\nimport {commonPropTypes} from '../utils-ts'\n\n// Shared between useSelect, useCombobox, Downshift.\nexport const propTypes = {\n  ...commonPropTypes,\n  getA11yStatusMessage: PropTypes.func,\n  highlightedIndex: PropTypes.number,\n  defaultHighlightedIndex: PropTypes.number,\n  initialHighlightedIndex: PropTypes.number,\n  isOpen: PropTypes.bool,\n  defaultIsOpen: PropTypes.bool,\n  initialIsOpen: PropTypes.bool,\n  selectedItem: PropTypes.any,\n  initialSelectedItem: PropTypes.any,\n  defaultSelectedItem: PropTypes.any,\n  id: PropTypes.string,\n  labelId: PropTypes.string,\n  menuId: PropTypes.string,\n  getItemId: PropTypes.func,\n  toggleButtonId: PropTypes.string,\n  onSelectedItemChange: PropTypes.func,\n  onHighlightedIndexChange: PropTypes.func,\n  onStateChange: PropTypes.func,\n  onIsOpenChange: PropTypes.func,\n  scrollIntoView: PropTypes.func,\n}\n"
  },
  {
    "path": "src/hooks/utils.dropdown/useElementIds.ts",
    "content": "import * as React from 'react'\n\nimport {generateId} from '../../utils-ts'\n\ntype UseElementIdsProps = {\n  id?: string\n  labelId?: string\n  menuId?: string\n  getItemId?: (index: number) => string\n  toggleButtonId?: string\n  inputId?: string\n}\n\ntype UseElementIdsReturnValue = {\n  labelId: string\n  menuId: string\n  getItemId: (index: number) => string\n  toggleButtonId: string\n  inputId: string\n}\n\n// https://github.com/downshift-js/downshift/issues/1674#issuecomment-3924320872\nconst SafeReact = {...React}\nconst reactUseId = SafeReact.useId\n\nexport const useElementIds =\n  typeof reactUseId === 'function' ? useElementIdsR18 : useElementIdsLegacy\n\nfunction useElementIdsR18({\n  id,\n  labelId,\n  menuId,\n  getItemId,\n  toggleButtonId,\n  inputId,\n}: UseElementIdsProps): UseElementIdsReturnValue {\n  const reactId = `downshift-${reactUseId()}`\n  if (!id) {\n    id = reactId\n  }\n\n  const elementIds = React.useMemo(\n    () => ({\n      labelId: labelId ?? `${id}-label`,\n      menuId: menuId ?? `${id}-menu`,\n      getItemId: getItemId ?? (index => `${id}-item-${index}`),\n      toggleButtonId: toggleButtonId ?? `${id}-toggle-button`,\n      inputId: inputId ?? `${id}-input`,\n    }),\n    [getItemId, id, inputId, labelId, menuId, toggleButtonId],\n  )\n\n  return elementIds\n}\n\nfunction useElementIdsLegacy({\n  id,\n  labelId,\n  menuId,\n  getItemId,\n  toggleButtonId,\n  inputId,\n}: UseElementIdsProps): UseElementIdsReturnValue {\n  const baseIdRef = React.useRef(id ?? `downshift-${generateId()}`)\n  const baseId = baseIdRef.current\n\n  const elementIds = React.useMemo(\n    () => ({\n      labelId: labelId ?? `${baseId}-label`,\n      menuId: menuId ?? `${baseId}-menu`,\n      getItemId: getItemId ?? (index => `${baseId}-item-${index}`),\n      toggleButtonId: toggleButtonId ?? `${baseId}-toggle-button`,\n      inputId: inputId ?? `${baseId}-input`,\n    }),\n    [getItemId, inputId, labelId, menuId, toggleButtonId, baseId],\n  )\n\n  return elementIds\n}\n"
  },
  {
    "path": "src/hooks/utils.js",
    "content": "import * as React from 'react'\nimport {isReactNative} from '../is.macro'\nimport {validateControlledUnchanged, targetWithinDownshift} from '../utils'\nimport {noop} from '../utils-ts'\nimport {useIsInitialMount, getDefaultValue, getInitialValue} from './utils-ts'\nimport {dropdownDefaultStateValues} from './utils.dropdown'\n\n// istanbul ignore next\nconst useIsomorphicLayoutEffect =\n  typeof window !== 'undefined' &&\n  typeof window.document !== 'undefined' &&\n  typeof window.document.createElement !== 'undefined'\n    ? React.useLayoutEffect\n    : React.useEffect\n\nfunction isAcceptedCharacterKey(key) {\n  return /^\\S{1}$/.test(key)\n}\n\nfunction getInitialState(props) {\n  const selectedItem = getInitialValue(\n    props,\n    'selectedItem',\n    dropdownDefaultStateValues,\n  )\n  const isOpen = getInitialValue(props, 'isOpen', dropdownDefaultStateValues)\n  const highlightedIndex = getInitialHighlightedIndex(props)\n  const inputValue = getInitialValue(\n    props,\n    'inputValue',\n    dropdownDefaultStateValues,\n  )\n\n  return {\n    highlightedIndex:\n      highlightedIndex < 0 && selectedItem && isOpen\n        ? props.items.findIndex(\n            item => props.itemToKey(item) === props.itemToKey(selectedItem),\n          )\n        : highlightedIndex,\n    isOpen,\n    selectedItem,\n    inputValue,\n  }\n}\n\nfunction getHighlightedIndexOnOpen(props, state, offset) {\n  const {\n    items,\n    initialHighlightedIndex,\n    defaultHighlightedIndex,\n    isItemDisabled,\n    itemToKey,\n  } = props\n  const {selectedItem, highlightedIndex} = state\n\n  if (items.length === 0) {\n    return -1\n  }\n\n  // initialHighlightedIndex will give value to highlightedIndex on initial state only.\n  if (\n    initialHighlightedIndex !== undefined &&\n    highlightedIndex === initialHighlightedIndex &&\n    !isItemDisabled(items[initialHighlightedIndex], initialHighlightedIndex)\n  ) {\n    return initialHighlightedIndex\n  }\n\n  if (\n    defaultHighlightedIndex !== undefined &&\n    !isItemDisabled(items[defaultHighlightedIndex], defaultHighlightedIndex)\n  ) {\n    return defaultHighlightedIndex\n  }\n\n  if (selectedItem) {\n    return items.findIndex(item => itemToKey(selectedItem) === itemToKey(item))\n  }\n\n  if (\n    offset < 0 &&\n    !isItemDisabled(items[items.length - 1], items.length - 1)\n  ) {\n    return items.length - 1\n  }\n\n  if (offset > 0 && !isItemDisabled(items[0], 0)) {\n    return 0\n  }\n\n  return -1\n}\n/**\n * Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.\n *\n * @param {Window} environment The environment to add the event listeners to, for instance window.\n * @param {() => void} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.\n * @param {Array<{current: HTMLElement}>} downshiftElementsRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.\n * @returns {{isMouseDown: boolean, isTouchMove: boolean, isTouchEnd: boolean}} The mouse and touch events information, if any of are happening.\n */\nfunction useMouseAndTouchTracker(\n  environment,\n  handleBlur,\n  downshiftRefs,\n) {\n  const mouseAndTouchTrackersRef = React.useRef({\n    isMouseDown: false,\n    isTouchMove: false,\n    isTouchEnd: false,\n  })\n\nconst getDownshiftElements = React.useCallback(\n  () => downshiftRefs.map(ref => ref.current),\n  [downshiftRefs],\n);\n\n  React.useEffect(() => {\n    if (isReactNative || !environment) {\n      return noop\n    }\n\n    function onMouseDown() {\n      mouseAndTouchTrackersRef.current.isTouchEnd = false // reset this one.\n      mouseAndTouchTrackersRef.current.isMouseDown = true\n    }\n    function onMouseUp(event) {\n      mouseAndTouchTrackersRef.current.isMouseDown = false\n\n      if (\n        !targetWithinDownshift(\n          event.target,\n          getDownshiftElements(),\n          environment,\n        )\n      ) {\n        handleBlur()\n      }\n    }\n    function onTouchStart() {\n      mouseAndTouchTrackersRef.current.isTouchEnd = false\n      mouseAndTouchTrackersRef.current.isTouchMove = false\n    }\n    function onTouchMove() {\n      mouseAndTouchTrackersRef.current.isTouchMove = true\n    }\n    function onTouchEnd(event) {\n      mouseAndTouchTrackersRef.current.isTouchEnd = true\n\n      if (\n        !mouseAndTouchTrackersRef.current.isTouchMove &&\n        !targetWithinDownshift(\n          event.target,\n          getDownshiftElements(),\n          environment,\n          false,\n        )\n      ) {\n        handleBlur()\n      }\n    }\n\n    environment.addEventListener('mousedown', onMouseDown)\n    environment.addEventListener('mouseup', onMouseUp)\n    environment.addEventListener('touchstart', onTouchStart)\n    environment.addEventListener('touchmove', onTouchMove)\n    environment.addEventListener('touchend', onTouchEnd)\n\n    return function cleanup() {\n      environment.removeEventListener('mousedown', onMouseDown)\n      environment.removeEventListener('mouseup', onMouseUp)\n      environment.removeEventListener('touchstart', onTouchStart)\n      environment.removeEventListener('touchmove', onTouchMove)\n      environment.removeEventListener('touchend', onTouchEnd)\n    }\n  }, [environment, getDownshiftElements, handleBlur])\n\n  return mouseAndTouchTrackersRef.current\n}\n\n/* istanbul ignore next */\n// eslint-disable-next-line import/no-mutable-exports\nlet useGetterPropsCalledChecker = () => noop\n/**\n * Custom hook that checks if getter props are called correctly.\n *\n * @param  {...any} propKeys Getter prop names to be handled.\n * @returns {Function} Setter function called inside getter props to set call information.\n */\n/* istanbul ignore next */\nif (process.env.NODE_ENV !== 'production') {\n  useGetterPropsCalledChecker = (...propKeys) => {\n    const getterPropsCalledRef = React.useRef(\n      propKeys.reduce((acc, propKey) => {\n        acc[propKey] = {}\n\n        return acc\n      }, {}),\n    )\n\n    React.useEffect(() => {\n      Object.keys(getterPropsCalledRef.current).forEach(propKey => {\n        const propCallInfo = getterPropsCalledRef.current[propKey]\n\n        if (!Object.keys(propCallInfo).length) {\n          // eslint-disable-next-line no-console\n          console.error(\n            `downshift: You forgot to call the ${propKey} getter function on your component / element.`,\n          )\n          return\n        }\n\n        const {suppressRefError, refKey, elementRef} = propCallInfo\n\n        if (suppressRefError) {\n          return\n        }\n\n        if (!elementRef?.current) {\n          // eslint-disable-next-line no-console\n          console.error(\n            `downshift: The ref prop \"${refKey}\" from ${propKey} was not applied correctly on your element.`,\n          )\n        }\n      })\n    }, [])\n\n    const setGetterPropCallInfo = React.useCallback(\n      (propKey, suppressRefError, refKey, elementRef) => {\n        getterPropsCalledRef.current[propKey] = {\n          suppressRefError,\n          refKey,\n          elementRef,\n        }\n      },\n      [],\n    )\n\n    return setGetterPropCallInfo\n  }\n}\n\nfunction useScrollIntoView({\n  highlightedIndex,\n  isOpen,\n  itemRefs,\n  getItemNodeFromIndex,\n  menuElement,\n  scrollIntoView: scrollIntoViewProp,\n}) {\n  // used not to scroll on highlight by mouse.\n  const shouldScrollRef = React.useRef(true)\n  // Scroll on highlighted item if change comes from keyboard.\n  useIsomorphicLayoutEffect(() => {\n    if (\n      highlightedIndex < 0 ||\n      !isOpen ||\n      !Object.keys(itemRefs.current).length\n    ) {\n      return\n    }\n\n    if (shouldScrollRef.current === false) {\n      shouldScrollRef.current = true\n    } else {\n      scrollIntoViewProp(getItemNodeFromIndex(highlightedIndex), menuElement)\n    }\n    // eslint-disable-next-line react-hooks/exhaustive-deps\n  }, [highlightedIndex])\n\n  return shouldScrollRef\n}\n\n// eslint-disable-next-line import/no-mutable-exports\nlet useControlPropsValidator = noop\n/* istanbul ignore next */\nif (process.env.NODE_ENV !== 'production') {\n  useControlPropsValidator = ({props, state}) => {\n    // used for checking when props are moving from controlled to uncontrolled.\n    const prevPropsRef = React.useRef(props)\n    const isInitialMount = useIsInitialMount()\n\n    React.useEffect(() => {\n      if (isInitialMount) {\n        return\n      }\n\n      validateControlledUnchanged(state, prevPropsRef.current, props)\n      prevPropsRef.current = props\n    }, [state, props, isInitialMount])\n  }\n}\n\n/**\n * Handles selection on Enter / Alt + ArrowUp. Closes the menu and resets the highlighted index, unless there is a highlighted.\n * In that case, selects the item and resets to defaults for open state and highlighted idex.\n * @param {Object} props The useCombobox props.\n * @param {number} highlightedIndex The index from the state.\n * @param {boolean} inputValue Also return the input value for state.\n * @returns The changes for the state.\n */\nfunction getChangesOnSelection(props, highlightedIndex, inputValue = true) {\n  const shouldSelect = props.items?.length && highlightedIndex >= 0\n\n  return {\n    isOpen: false,\n    highlightedIndex: -1,\n    ...(shouldSelect && {\n      selectedItem: props.items[highlightedIndex],\n      isOpen: getDefaultValue(props, 'isOpen', dropdownDefaultStateValues),\n      highlightedIndex: getDefaultValue(\n        props,\n        'highlightedIndex',\n        dropdownDefaultStateValues,\n      ),\n      ...(inputValue && {\n        inputValue: props.itemToString(props.items[highlightedIndex]),\n      }),\n    }),\n  }\n}\n\n/**\n * Check if a state is equal for dropdowns, by comparing isOpen, inputValue, highlightedIndex and selected item.\n * Used by useSelect and useCombobox.\n *\n * @param {Object} prevState\n * @param {Object} newState\n * @returns {boolean} Wheather the states are deeply equal.\n */\nfunction isDropdownsStateEqual(prevState, newState) {\n  return (\n    prevState.isOpen === newState.isOpen &&\n    prevState.inputValue === newState.inputValue &&\n    prevState.highlightedIndex === newState.highlightedIndex &&\n    prevState.selectedItem === newState.selectedItem\n  )\n}\n\n/**\n * Returns the new highlightedIndex based on the defaultHighlightedIndex prop, if it's not disabled.\n *\n * @param {Object} props Props from useCombobox or useSelect.\n * @returns {number} The highlighted index.\n */\nfunction getDefaultHighlightedIndex(props) {\n  const highlightedIndex = getDefaultValue(\n    props,\n    'highlightedIndex',\n    dropdownDefaultStateValues,\n  )\n  if (\n    highlightedIndex > -1 &&\n    props.isItemDisabled(props.items[highlightedIndex], highlightedIndex)\n  ) {\n    return -1\n  }\n\n  return highlightedIndex\n}\n\n/**\n * Returns the new highlightedIndex based on the initialHighlightedIndex prop, if not disabled.\n *\n * @param {Object} props Props from useCombobox or useSelect.\n * @returns {number} The highlighted index.\n */\nfunction getInitialHighlightedIndex(props) {\n  const highlightedIndex = getInitialValue(\n    props,\n    'highlightedIndex',\n    dropdownDefaultStateValues,\n  )\n\n  if (\n    highlightedIndex > -1 &&\n    props.isItemDisabled(props.items[highlightedIndex], highlightedIndex)\n  ) {\n    return -1\n  }\n\n  return highlightedIndex\n}\n\nexport {\n  useControlPropsValidator,\n  useScrollIntoView,\n  useGetterPropsCalledChecker,\n  useMouseAndTouchTracker,\n  getHighlightedIndexOnOpen,\n  isAcceptedCharacterKey,\n  getChangesOnSelection,\n  isDropdownsStateEqual,\n  getDefaultHighlightedIndex,\n  getInitialState,\n}\n"
  },
  {
    "path": "src/index.ts",
    "content": "export {default} from './downshift'\nexport {resetIdCounter} from './utils-ts'\nexport {useSelect, useCombobox, useTagGroup} from './hooks'\n/**\n * @deprecated Not maintained anymore. Use the new useTagGroup instead.\n * See the migration guide: https://github.com/downshift-js/downshift/tree/master/src/hooks/useMultipleSelection/MIGRATION_GUIDE.md\n */\nexport {useMultipleSelection} from './hooks'\n"
  },
  {
    "path": "src/is.macro.d.ts",
    "content": "export declare const isPreact: boolean,\n  isReactNative: boolean,\n  isReactNativeWeb: boolean\n"
  },
  {
    "path": "src/is.macro.js",
    "content": "const {createMacro, MacroError} = require('babel-plugin-macros')\n\nconst importToEnvVar = {\n  isPreact: 'BUILD_PREACT',\n  isReactNative: 'BUILD_REACT_NATIVE',\n  isReactNativeWeb: 'BUILD_REACT_NATIVE_WEB',\n}\n\nconst arrToStr = arr => arr.join(', ')\n\nmodule.exports = createMacro(({references, babel: {types: t}}) => {\n  const usedReferences = Object.keys(references)\n  const allowedReferences = Object.keys(importToEnvVar)\n\n  if (usedReferences.filter(ref => !importToEnvVar[ref]).length !== 0) {\n    throw new MacroError(\n      `${__filename} handles only those named exports: ${arrToStr(\n        allowedReferences,\n      )}, you have tried to use it with: ${arrToStr(usedReferences)}.`,\n    )\n  }\n\n  usedReferences.forEach(refName => {\n    const envVar = importToEnvVar[refName]\n\n    references[refName].forEach(usedRef => {\n      const envValue = process.env[envVar]\n      usedRef.replaceWith(\n        t.booleanLiteral((envValue && envValue !== 'false') || false),\n      )\n    })\n  })\n})\n"
  },
  {
    "path": "src/productionEnum.macro.d.ts",
    "content": "declare function productionEnum<T extends string>(value: T): T;\n\nexport default productionEnum;"
  },
  {
    "path": "src/productionEnum.macro.js",
    "content": "const {createMacro, MacroError} = require('babel-plugin-macros')\n\nmodule.exports = createMacro(({references, babel: {types: t, template}}) => {\n  const usedReferences = Object.keys(references)\n\n  if (usedReferences.length > 1 || usedReferences[0] !== 'default') {\n    throw new MacroError(\n      `${__filename} must be used as default import, instead you have used it as: ${usedReferences.join(\n        ', ',\n      )}.`,\n    )\n  }\n\n  const tmpl = template(\n    'process.env.NODE_ENV !== \"production\" ? DEV_VALUE : NUMBER',\n    {placeholderPattern: /(DEV_VALUE)|(NUMBER)/},\n  )\n\n  references.default.forEach(({parentPath: ref}, index) => {\n    if (!ref.isCallExpression()) {\n      throw new Error(\n        `${__filename} is supposed to be used as function call, instead you have used it as ${ref.node.type}.`,\n      )\n    }\n    ref.replaceWith(\n      tmpl({DEV_VALUE: ref.node.arguments[0], NUMBER: t.numericLiteral(index)}),\n    )\n  })\n})\n"
  },
  {
    "path": "src/stateChangeTypes.js",
    "content": "import productionEnum from './productionEnum.macro'\n\nexport const unknown = productionEnum('__autocomplete_unknown__')\nexport const mouseUp = productionEnum('__autocomplete_mouseup__')\nexport const itemMouseEnter = productionEnum('__autocomplete_item_mouseenter__')\nexport const keyDownArrowUp = productionEnum(\n  '__autocomplete_keydown_arrow_up__',\n)\nexport const keyDownArrowDown = productionEnum(\n  '__autocomplete_keydown_arrow_down__',\n)\nexport const keyDownEscape = productionEnum('__autocomplete_keydown_escape__')\nexport const keyDownEnter = productionEnum('__autocomplete_keydown_enter__')\nexport const keyDownHome = productionEnum('__autocomplete_keydown_home__')\nexport const keyDownEnd = productionEnum('__autocomplete_keydown_end__')\nexport const clickItem = productionEnum('__autocomplete_click_item__')\nexport const blurInput = productionEnum('__autocomplete_blur_input__')\nexport const changeInput = productionEnum('__autocomplete_change_input__')\nexport const keyDownSpaceButton = productionEnum(\n  '__autocomplete_keydown_space_button__',\n)\nexport const clickButton = productionEnum('__autocomplete_click_button__')\nexport const blurButton = productionEnum('__autocomplete_blur_button__')\nexport const controlledPropUpdatedSelectedItem = productionEnum(\n  '__autocomplete_controlled_prop_updated_selected_item__',\n)\nexport const touchEnd = productionEnum('__autocomplete_touchend__')\n"
  },
  {
    "path": "src/utils-ts/__tests__/getState.test.ts",
    "content": "import {getState, Props} from '../getState'\n\ntest('returns state if no props are passed', () => {\n  const state = {a: 'b'}\n\n  expect(getState(state, undefined)).toEqual(state)\n})\n\ntest('merges state with props', () => {\n  const state = {a: 'b', c: 'd'}\n  const props = {b: 'e', c: 'f'} as unknown as Props<unknown, unknown>\n\n  expect(getState(state, props)).toEqual({a: 'b', c: 'f'})\n})\n"
  },
  {
    "path": "src/utils-ts/__tests__/handleRefs.test.ts",
    "content": "import {handleRefs} from '../handleRefs'\n\ntest('handleRefs handles both ref functions and objects', () => {\n  const refFunction = jest.fn() as unknown as React.RefCallback<HTMLElement>\n  const refObject = {\n    current: null,\n  } as unknown as React.MutableRefObject<HTMLElement>\n  const refs = [refFunction, refObject]\n  const node = {} as unknown as HTMLElement\n  const ref = handleRefs(...refs)\n\n  ref(node)\n\n  expect(refFunction).toHaveBeenCalledTimes(1)\n  expect(refFunction).toHaveBeenCalledWith(node)\n  expect(refObject.current).toEqual(node)\n})\n"
  },
  {
    "path": "src/utils-ts/callAllEventHandlers.ts",
    "content": "/**\n * This is intended to be used to compose event handlers.\n * They are executed in order until one of them sets\n * `event.preventDownshiftDefault = true`.\n * @param fns the event handler functions\n * @return the event handler to add to an element\n */\nexport function callAllEventHandlers(...fns: (Function | undefined)[]) {\n  return (\n    event: React.SyntheticEvent & {\n      preventDownshiftDefault?: boolean\n      nativeEvent: {preventDownshiftDefault?: boolean}\n    },\n    ...args: unknown[]\n  ) =>\n    fns.some(fn => {\n      if (fn) {\n        fn(event, ...args)\n      }\n      return (\n        event.preventDownshiftDefault ||\n        (event.hasOwnProperty('nativeEvent') &&\n          event.nativeEvent.preventDownshiftDefault)\n      )\n    })\n}\n"
  },
  {
    "path": "src/utils-ts/debounce.ts",
    "content": "/**\n * Simple debounce implementation. Will call the given\n * function once after the time given has passed since\n * it was last called.\n */\nexport function debounce(\n  fn: Function,\n  time: number,\n): Function & {cancel: Function} {\n  let timeoutId: NodeJS.Timeout | undefined | null\n\n  function cancel() {\n    if (timeoutId) {\n      clearTimeout(timeoutId)\n    }\n  }\n\n  function wrapper(...args: unknown[]) {\n    cancel()\n    timeoutId = setTimeout(() => {\n      timeoutId = null\n      fn(...args)\n    }, time)\n  }\n\n  wrapper.cancel = cancel\n\n  return wrapper\n}\n"
  },
  {
    "path": "src/utils-ts/generateId.ts",
    "content": "import * as React from 'react'\n\nlet idCounter = 0\n\n/**\n * This generates a unique ID for an instance of Downshift\n * @return {string} the unique ID\n */\nexport function generateId(): string {\n  return String(idCounter++)\n}\n\n/**\n * This is only used in tests\n * @param {number} num the number to set the idCounter to\n */\nexport function setIdCounter(num: number): void {\n  idCounter = num\n}\n\n/**\n * Resets idCounter to 0. Used for SSR.\n */\nexport function resetIdCounter() {\n  // istanbul ignore next\n  if ('useId' in React) {\n    console.warn(\n      `It is not necessary to call resetIdCounter when using React 18+`,\n    )\n\n    return\n  }\n\n  idCounter = 0\n}\n"
  },
  {
    "path": "src/utils-ts/getState.ts",
    "content": "export interface Action<T> extends Record<string, unknown> {\n  type: T\n}\n\nexport type State = Record<string, unknown>\n\nexport interface Props<S, T> {\n  onStateChange?(typeAndChanges: unknown): void\n  stateReducer(\n    state: S,\n    actionAndChanges: Action<T> & {changes: Partial<S>},\n  ): Partial<S>\n}\n\n/**\n * This will perform a shallow merge of the given state object\n * with the state coming from props\n * (for the controlled component scenario)\n * This is used in state updater functions so they're referencing\n * the right state regardless of where it comes from.\n *\n * @param state The state of the component/hook.\n * @param props The props that may contain controlled values.\n * @returns The merged controlled state.\n */\nexport function getState<\n  S extends State,\n  P extends Partial<S> & Props<S, T>,\n  T,\n>(state: S, props?: P): S {\n  if (!props) {\n    return state\n  }\n\n  const keys = Object.keys(state) as (keyof S)[]\n\n  return keys.reduce(\n    (newState, key) => {\n      if (props[key] !== undefined) {\n        newState[key] = (props as Partial<S>)[key] as S[typeof key]\n      }\n      return newState\n    },\n    {...state},\n  )\n}\n"
  },
  {
    "path": "src/utils-ts/handleRefs.ts",
    "content": "import * as React from 'react'\n\nexport function handleRefs(\n  ...refs: (\n    | React.MutableRefObject<HTMLElement>\n    | React.RefCallback<HTMLElement>\n    | undefined\n  )[]\n) {\n  return (node: HTMLElement) => {\n    refs.forEach(ref => {\n      if (typeof ref === 'function') {\n        ref(node)\n      } else if (ref) {\n        ref.current = node\n      }\n    })\n  }\n}\n"
  },
  {
    "path": "src/utils-ts/index.ts",
    "content": "export {generateId, setIdCounter, resetIdCounter} from './generateId'\nexport {useLatestRef} from './useLatestRef'\nexport {handleRefs} from './handleRefs'\nexport {callAllEventHandlers} from './callAllEventHandlers'\nexport {debounce} from './debounce'\nexport {setStatus, cleanupStatusDiv} from './setA11yStatus'\nexport {noop} from './noop'\nexport {validatePropTypes} from './validatePropTypes'\nexport {getState} from './getState'\nexport type {Action, Props, State} from './getState'\nexport {scrollIntoView} from './scrollIntoView'\n"
  },
  {
    "path": "src/utils-ts/noop.ts",
    "content": "export function noop() {}\n"
  },
  {
    "path": "src/utils-ts/scrollIntoView.ts",
    "content": "import {compute} from 'compute-scroll-into-view'\n\n/**\n * Scroll node into view if necessary\n * @param {HTMLElement} node the element that should scroll into view\n * @param {HTMLElement} menuNode the menu element of the component\n */\nexport function scrollIntoView(\n  node: HTMLElement | undefined,\n  menuNode: HTMLElement | undefined,\n) {\n  if (!node) {\n    return\n  }\n\n  const actions = compute(node, {\n    boundary: menuNode,\n    block: 'nearest',\n    scrollMode: 'if-needed',\n  })\n  actions.forEach(({el, top, left}) => {\n    el.scrollTop = top\n    el.scrollLeft = left\n  })\n}\n"
  },
  {
    "path": "src/utils-ts/setA11yStatus.ts",
    "content": "import {debounce} from './debounce'\n\nconst cleanupStatus = debounce((document: Document) => {\n  getStatusDiv(document).textContent = ''\n}, 500)\n\n/**\n * Get the status node or create it if it does not already exist.\n */\nfunction getStatusDiv(document: Document) {\n  let statusDiv = document.getElementById('a11y-status-message')\n  if (statusDiv) {\n    return statusDiv\n  }\n\n  statusDiv = document.createElement('div')\n  statusDiv.setAttribute('id', 'a11y-status-message')\n  statusDiv.setAttribute('role', 'status')\n  statusDiv.setAttribute('aria-live', 'polite')\n  statusDiv.setAttribute('aria-relevant', 'additions text')\n  Object.assign(statusDiv.style, {\n    border: '0',\n    clip: 'rect(0 0 0 0)',\n    height: '1px',\n    margin: '-1px',\n    overflow: 'hidden',\n    padding: '0',\n    position: 'absolute',\n    width: '1px',\n  })\n\n  document.body.appendChild(statusDiv)\n  return statusDiv\n}\n\n/**\n * Sets aria live status to a div element that's visually hidden.\n */\nexport function setStatus(status: string, document: Document | undefined) {\n  if (!status || !document) {\n    return\n  }\n\n  const div = getStatusDiv(document)\n\n  div.textContent = status\n  cleanupStatus(document)\n}\n\n/**\n * Removes the status element from the DOM\n */\nexport function cleanupStatusDiv(document: Document | undefined) {\n  const statusDiv = document?.getElementById('a11y-status-message')\n\n  if (statusDiv) {\n    statusDiv.remove()\n  }\n}\n"
  },
  {
    "path": "src/utils-ts/useLatestRef.ts",
    "content": "import * as React from 'react'\n\nexport function useLatestRef<T>(val: T): React.MutableRefObject<T> {\n  const ref = React.useRef(val)\n  // technically this is not \"concurrent mode safe\" because we're manipulating\n  // the value during render (so it's not idempotent). However, the places this\n  // hook is used is to support memoizing callbacks which will be called\n  // *during* render, so we need the latest values *during* render.\n  // If not for this, then we'd probably want to use useLayoutEffect instead.\n  ref.current = val\n  return ref\n}\n"
  },
  {
    "path": "src/utils-ts/validatePropTypes.ts",
    "content": "import PropTypes from 'prop-types'\nimport {noop} from './noop'\n\n// eslint-disable-next-line import/no-mutable-exports\nexport let validatePropTypes = noop as (\n  options: unknown,\n  caller: Function,\n  propTypes: Record<\n    string,\n    PropTypes.Requireable<(...args: unknown[]) => unknown>\n  >,\n) => void\n/* istanbul ignore next */\nif (process.env.NODE_ENV !== 'production') {\n  validatePropTypes = (\n    options: unknown,\n    caller: Function,\n    propTypes: Record<\n      string,\n      PropTypes.Requireable<(...args: unknown[]) => unknown>\n    >,\n  ): void => {\n    PropTypes.checkPropTypes(propTypes, options, 'prop', caller.name)\n  }\n}\n"
  },
  {
    "path": "src/utils.js",
    "content": "import {isPreact} from './is.macro'\nimport { noop } from './utils-ts'\n\n/**\n * Accepts a parameter and returns it if it's a function\n * or a noop function if it's not. This allows us to\n * accept a callback, but not worry about it if it's not\n * passed.\n * @param {Function} cb the callback\n * @return {Function} a function\n */\nfunction cbToCb(cb) {\n  return typeof cb === 'function' ? cb : noop\n}\n\n/**\n * @param {HTMLElement} parent the parent node\n * @param {HTMLElement} child the child node\n * @param {Window} environment The window context where downshift renders.\n * @return {Boolean} whether the parent is the child or the child is in the parent\n */\nfunction isOrContainsNode(parent, child, environment) {\n  const result =\n    parent === child ||\n    (child instanceof environment.Node &&\n      parent.contains &&\n      parent.contains(child))\n  return result\n}\n\n/**\n * Simple debounce implementation. Will call the given\n * function once after the time given has passed since\n * it was last called.\n * @param {Function} fn the function to call after the time\n * @param {Number} time the time to wait\n * @return {Function} the debounced function\n */\nfunction debounce(fn, time) {\n  let timeoutId\n\n  function cancel() {\n    if (timeoutId) {\n      clearTimeout(timeoutId)\n    }\n  }\n\n  function wrapper(...args) {\n    cancel()\n    timeoutId = setTimeout(() => {\n      timeoutId = null\n      fn(...args)\n    }, time)\n  }\n\n  wrapper.cancel = cancel\n\n  return wrapper\n}\n\n/**\n * This is intended to be used to compose event handlers.\n * They are executed in order until one of them sets\n * `event.preventDownshiftDefault = true`.\n * @param {...Function} fns the event handler functions\n * @return {Function} the event handler to add to an element\n */\nfunction callAllEventHandlers(...fns) {\n  return (event, ...args) =>\n    fns.some(fn => {\n      if (fn) {\n        fn(event, ...args)\n      }\n      return (\n        event.preventDownshiftDefault ||\n        (event.hasOwnProperty('nativeEvent') &&\n          event.nativeEvent.preventDownshiftDefault)\n      )\n    })\n}\n\nfunction handleRefs(...refs) {\n  return node => {\n    refs.forEach(ref => {\n      if (typeof ref === 'function') {\n        ref(node)\n      } else if (ref) {\n        ref.current = node\n      }\n    })\n  }\n}\n\n/**\n * Default implementation for status message. Only added when menu is open.\n * Will specify if there are results in the list, and if so, how many,\n * and what keys are relevant.\n *\n * @param {Object} param the downshift state and other relevant properties\n * @return {String} the a11y status message\n */\nfunction getA11yStatusMessage({isOpen, resultCount, previousResultCount}) {\n  if (!isOpen) {\n    return ''\n  }\n\n  if (!resultCount) {\n    return 'No results are available.'\n  }\n\n  if (resultCount !== previousResultCount) {\n    return `${resultCount} result${\n      resultCount === 1 ? ' is' : 's are'\n    } available, use up and down arrow keys to navigate. Press Enter key to select.`\n  }\n\n  return ''\n}\n\n/**\n * Takes an argument and if it's an array, returns the first item in the array\n * otherwise returns the argument\n * @param {*} arg the maybe-array\n * @param {*} defaultValue the value if arg is falsey not defined\n * @return {*} the arg or it's first item\n */\nfunction unwrapArray(arg, defaultValue) {\n  arg = Array.isArray(arg) ? /* istanbul ignore next (preact) */ arg[0] : arg\n  if (!arg && defaultValue) {\n    return defaultValue\n  } else {\n    return arg\n  }\n}\n\n/**\n * @param {Object} element (P)react element\n * @return {Boolean} whether it's a DOM element\n */\nfunction isDOMElement(element) {\n  /* istanbul ignore if */\n  if (isPreact) {\n    // then this is preact or preact X\n    return (\n      typeof element.type === 'string' || typeof element.nodeName === 'string'\n    )\n  }\n\n  // then we assume this is react\n  return typeof element.type === 'string'\n}\n\n/**\n * @param {Object} element (P)react element\n * @return {Object} the props\n */\nfunction getElementProps(element) {\n  // props for react, attributes for preact\n\n  /* istanbul ignore if */\n  if (isPreact) {\n    return element.props || element.attributes\n  }\n\n  return element.props\n}\n\n/**\n * Throws a helpful error message for required properties. Useful\n * to be used as a default in destructuring or object params.\n * @param {String} fnName the function name\n * @param {String} propName the prop name\n */\nfunction requiredProp(fnName, propName) {\n  // eslint-disable-next-line no-console\n  console.error(`The property \"${propName}\" is required in \"${fnName}\"`)\n}\n\nconst stateKeys = [\n  'highlightedIndex',\n  'inputValue',\n  'isOpen',\n  'selectedItem',\n  'type',\n]\n/**\n * @param {Object} state the state object\n * @return {Object} state that is relevant to downshift\n */\nfunction pickState(state = {}) {\n  const result = {}\n  stateKeys.forEach(k => {\n    if (state.hasOwnProperty(k)) {\n      result[k] = state[k]\n    }\n  })\n  return result\n}\n\n/**\n * This determines whether a prop is a \"controlled prop\" meaning it is\n * state which is controlled by the outside of this component rather\n * than within this component.\n *\n * @param {Object} props The props that may contain controlled values.\n * @param {String} key the key to check\n * @return {Boolean} whether it is a controlled controlled prop\n */\nfunction isControlledProp(props, key) {\n  return props[key] !== undefined\n}\n\n/**\n * Normalizes the 'key' property of a KeyboardEvent in IE/Edge\n * @param {Object} event a keyboardEvent object\n * @return {String} keyboard key\n */\nfunction normalizeArrowKey(event) {\n  const {key, keyCode} = event\n  /* istanbul ignore next (ie) */\n  if (keyCode >= 37 && keyCode <= 40 && key.indexOf('Arrow') !== 0) {\n    return `Arrow${key}`\n  }\n  return key\n}\n\n/**\n * Simple check if the value passed is object literal\n * @param {*} obj any things\n * @return {Boolean} whether it's object literal\n */\nfunction isPlainObject(obj) {\n  return Object.prototype.toString.call(obj) === '[object Object]'\n}\n\n/**\n * Returns the next non-disabled highlightedIndex value.\n *\n * @param {number} start The current highlightedIndex.\n * @param {number} offset The offset from the current highlightedIndex to start searching.\n * @param {unknown[]} items The items array.\n * @param {(item: unknown, index: number) => boolean} isItemDisabled Function that tells if an item is disabled or not.\n * @param {boolean?} circular If the search reaches the end, if it can search again starting from the other end.\n * @returns {number} The next highlightedIndex.\n */\nfunction getHighlightedIndex(\n  start,\n  offset,\n  items,\n  isItemDisabled,\n  circular = false,\n) {\n  const count = items.length\n  if (count === 0) {\n    return -1\n  }\n\n  const itemsLastIndex = count - 1\n\n  if (typeof start !== 'number' || start < 0 || start > itemsLastIndex) {\n    start = offset > 0 ? -1 : itemsLastIndex + 1\n  }\n\n  let current = start + offset\n\n  if (current < 0) {\n    current = circular ? itemsLastIndex : 0\n  } else if (current > itemsLastIndex) {\n    current = circular ? 0 : itemsLastIndex\n  }\n\n  const highlightedIndex = getNonDisabledIndex(\n    current,\n    offset < 0,\n    items,\n    isItemDisabled,\n    circular,\n  )\n\n  if (highlightedIndex === -1) {\n    return start >= count ? -1 : start\n  }\n\n  return highlightedIndex\n}\n\n/**\n * Returns the next non-disabled highlightedIndex value.\n *\n * @param {number} start The current highlightedIndex.\n * @param {boolean} backwards If true, it will search backwards from the start.\n * @param {unknown[]} items The items array.\n * @param {(item: unknown, index: number) => boolean} isItemDisabled Function that tells if an item is disabled or not.\n * @param {boolean} circular If the search reaches the end, if it can search again starting from the other end.\n * @returns {number} The next non-disabled index.\n */\nfunction getNonDisabledIndex(\n  start,\n  backwards,\n  items,\n  isItemDisabled,\n  circular = false,\n) {\n  const count = items.length\n\n  if (backwards) {\n    for (let index = start; index >= 0; index--) {\n      if (!isItemDisabled(items[index], index)) {\n        return index\n      }\n    }\n  } else {\n    for (let index = start; index < count; index++) {\n      if (!isItemDisabled(items[index], index)) {\n        return index\n      }\n    }\n  }\n\n  if (circular) {\n    return getNonDisabledIndex(\n      backwards ? count - 1 : 0,\n      backwards,\n      items,\n      isItemDisabled,\n    )\n  }\n\n  return -1\n}\n\n/**\n * Checks if event target is within the downshift elements.\n *\n * @param {EventTarget} target Target to check.\n * @param {HTMLElement[]} downshiftElements The elements that form downshift (list, toggle button etc).\n * @param {Window} environment The window context where downshift renders.\n * @param {boolean} checkActiveElement Whether to also check activeElement.\n *\n * @returns {boolean} Whether or not the target is within downshift elements.\n */\nfunction targetWithinDownshift(\n  target,\n  downshiftElements,\n  environment,\n  checkActiveElement = true,\n) {\n  return (\n    environment &&\n    downshiftElements.some(\n      contextNode =>\n        contextNode &&\n        (isOrContainsNode(contextNode, target, environment) ||\n          (checkActiveElement &&\n            isOrContainsNode(\n              contextNode,\n              environment.document.activeElement,\n              environment,\n            ))),\n    )\n  )\n}\n\n// eslint-disable-next-line import/no-mutable-exports\nlet validateControlledUnchanged = noop\n/* istanbul ignore next */\nif (process.env.NODE_ENV !== 'production') {\n  validateControlledUnchanged = (state, prevProps, nextProps) => {\n    const warningDescription = `This prop should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled Downshift element for the lifetime of the component. More info: https://github.com/downshift-js/downshift#control-props`\n\n    Object.keys(state).forEach(propKey => {\n      if (\n        prevProps[propKey] !== undefined &&\n        nextProps[propKey] === undefined\n      ) {\n        // eslint-disable-next-line no-console\n        console.error(\n          `downshift: A component has changed the controlled prop \"${propKey}\" to be uncontrolled. ${warningDescription}`,\n        )\n      } else if (\n        prevProps[propKey] === undefined &&\n        nextProps[propKey] !== undefined\n      ) {\n        // eslint-disable-next-line no-console\n        console.error(\n          `downshift: A component has changed the uncontrolled prop \"${propKey}\" to be controlled. ${warningDescription}`,\n        )\n      }\n    })\n  }\n}\n\nexport {\n  cbToCb,\n  callAllEventHandlers,\n  handleRefs,\n  debounce,\n  getA11yStatusMessage,\n  unwrapArray,\n  isDOMElement,\n  getElementProps,\n  requiredProp,\n  pickState,\n  isPlainObject,\n  normalizeArrowKey,\n  targetWithinDownshift,\n  isControlledProp,\n  validateControlledUnchanged,\n  getHighlightedIndex,\n  getNonDisabledIndex,\n}\n"
  },
  {
    "path": "test/basic.test.js",
    "content": "// @flow\nimport * as React from 'react'\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport Downshift, {\n  type StateChangeOptions,\n  type ControllerStateAndHelpers,\n  type DownshiftType,\n} from 'downshift' // eslint-disable-line import/no-unresolved\n\ntype Item = string\nconst DownshiftTyped: DownshiftType<Item> = Downshift\n\ntype Props = {}\n\ntype State = {\n  items: Array<Item>,\n}\n\nexport default class App extends React.Component<Props, State> {\n  state: State = {\n    items: ['apple', 'orange', 'carrot'],\n  }\n\n  onChange = (selectedItem: Item) => {\n    // eslint-disable-next-line no-console\n    console.log('selectedItem', selectedItem)\n  }\n\n  onUserAction = (changes: StateChangeOptions<Item>) => {\n    // eslint-disable-next-line no-console\n    console.log('type', changes.type)\n  }\n\n  render() {\n    const items = this.state.items\n\n    return (\n      <DownshiftTyped onChange={this.onChange}>\n        {({\n          getToggleButtonProps,\n          getInputProps,\n          getItemProps,\n          isOpen,\n          inputValue,\n          selectedItem,\n          highlightedIndex,\n        }: ControllerStateAndHelpers<Item>) => (\n          <div>\n            <input\n              {...getInputProps({\n                placeholder: 'Favorite color ?',\n              })}\n            />\n            <button {...getToggleButtonProps()} />\n            {isOpen ? (\n              <div style={{border: '1px solid #ccc'}}>\n                {items\n                  .filter(\n                    i =>\n                      inputValue === null ||\n                      i.toLowerCase().includes(inputValue.toLowerCase()),\n                  )\n                  .map((item: Item, index: number) => (\n                    <div\n                      {...getItemProps({item, index})}\n                      key={item}\n                      style={{\n                        backgroundColor:\n                          highlightedIndex === index ? 'gray' : 'white',\n                        fontWeight: selectedItem === item ? 'bold' : 'normal',\n                      }}\n                    >\n                      {item}\n                    </div>\n                  ))}\n              </div>\n            ) : null}\n          </div>\n        )}\n      </DownshiftTyped>\n    )\n  }\n}\n"
  },
  {
    "path": "test/basic.test.tsx",
    "content": "import * as React from 'react'\nimport Downshift, {StateChangeOptions} from '..'\n\ntype Item = string\n\ninterface Props {}\n\ninterface State {\n  items: Array<Item>\n}\n\nexport default class App extends React.Component<Props, State> {\n  state: State = {\n    items: ['apple', 'orange', 'carrot'],\n  }\n\n  onChange = (selectedItem: Item | null) => {\n    console.log('selectedItem', selectedItem)\n  }\n\n  onUserAction = (changes: StateChangeOptions<Item>) => {\n    console.log('type', changes.type)\n  }\n\n  render() {\n    const items = this.state.items\n\n    return (\n      <Downshift onChange={this.onChange}>\n        {({\n          getToggleButtonProps,\n          getInputProps,\n          getItemProps,\n          getLabelProps,\n          getRootProps,\n          getMenuProps,\n          isOpen,\n          inputValue,\n          selectedItem,\n          highlightedIndex,\n        }) => {\n          return (\n            <div>\n              <div {...getRootProps({}, {})}></div>\n              <label\n                {...getLabelProps()}\n              >\n                Hello:\n              </label>\n              <label {...getLabelProps()}>Hello:</label>\n              <input\n                {...getInputProps({\n                  placeholder: 'Favorite color ?',\n                })}\n              />\n              <button {...getToggleButtonProps()} />\n              {isOpen ? (\n                <div style={{border: '1px solid #ccc'}} {...getMenuProps()}>\n                  {items\n                    .filter(\n                      i =>\n                        !inputValue ||\n                        i.toLowerCase().includes(inputValue.toLowerCase()),\n                    )\n                    .map((item, index: number) => (\n                      <div\n                        {...getItemProps({item, index})}\n                        key={item}\n                        style={{\n                          backgroundColor:\n                            highlightedIndex === index ? 'gray' : 'white',\n                          fontWeight: selectedItem === item ? 'bold' : 'normal',\n                        }}\n                      >\n                        {item}\n                      </div>\n                    ))}\n                </div>\n              ) : null}\n            </div>\n          )\n        }}\n      </Downshift>\n    )\n  }\n}\n"
  },
  {
    "path": "test/custom.test.js",
    "content": "// @flow\nimport * as React from 'react'\n// eslint-disable-next-line import/no-extraneous-dependencies\nimport Downshift, {\n  type ControllerStateAndHelpers,\n  type DownshiftType,\n} from 'downshift' // eslint-disable-line import/no-unresolved\n\ntype Item = string\nconst DownshiftTyped: DownshiftType<Item> = Downshift\n\ntype Props = {}\n\ntype State = {\n  items: Array<Item>,\n}\n\nconst CustomList = ({\n  isOpen,\n  children,\n}: {\n  isOpen: boolean,\n  children: React.Node,\n}) => <div className={isOpen ? 'open' : ''}>{children}</div>\n\nconst CustomListItem = ({\n  isSelected,\n  children,\n}: {\n  isSelected: boolean,\n  children: React.Node,\n}) => <div className={isSelected ? 'selected' : ''}>{children}</div>\n\nexport default class App extends React.Component<Props, State> {\n  state: State = {\n    items: ['apple', 'orange', 'carrot'],\n  }\n\n  onChange = (selectedItem: Item) => {\n    // eslint-disable-next-line no-console\n    console.log('selectedItem', selectedItem)\n  }\n\n  render() {\n    const items = this.state.items\n    const defaultSelectedItem = this.state.items[0]\n\n    return (\n      <DownshiftTyped defaultSelectedItem={defaultSelectedItem}>\n        {({\n          getToggleButtonProps,\n          getItemProps,\n          selectedItem,\n          isOpen,\n        }: ControllerStateAndHelpers<Item>) => {\n          return (\n            <div style={{position: 'relative'}}>\n              <div {...getToggleButtonProps()}>{selectedItem}</div>\n              <CustomList isOpen={isOpen}>\n                {items.map((item, index) => (\n                  <CustomListItem\n                    key={index}\n                    {...getItemProps({\n                      item,\n                      index,\n                      isSelected: selectedItem === item,\n                    })}\n                  >\n                    {item}\n                  </CustomListItem>\n                ))}\n              </CustomList>\n            </div>\n          )\n        }}\n      </DownshiftTyped>\n    )\n  }\n}\n"
  },
  {
    "path": "test/custom.test.tsx",
    "content": "import * as React from 'react'\nimport Downshift, { ControllerStateAndHelpers } from '..'\n\ntype Item = string\n\ninterface Props {}\n\ninterface State {\n  items: Array<Item>\n}\n\nconst CustomList: React.FunctionComponent<\n  React.PropsWithChildren<{isOpen: boolean}>\n> = ({isOpen, children}) => (\n  <div className={isOpen ? 'open' : ''}>{children}</div>\n)\n\nconst CustomListItem: React.FunctionComponent<\n  React.PropsWithChildren<{isSelected: boolean}>\n> = ({isSelected, children}) => (\n  <div className={isSelected ? 'selected' : ''}>{children}</div>\n)\n\nexport default class App extends React.Component<Props, State> {\n  state: State = {\n    items: ['apple', 'orange', 'carrot'],\n  }\n\n  onChange = (selectedItem: Item) => {\n    console.log('selectedItem', selectedItem)\n  }\n\n  render() {\n    const items = this.state.items\n    const initialSelectedItem = this.state.items[0]\n\n    return (\n      <Downshift initialSelectedItem={initialSelectedItem}>\n        {({\n          getToggleButtonProps,\n          getItemProps,\n          selectedItem,\n          isOpen,\n        }: ControllerStateAndHelpers<Item>) => (\n          <div style={{position: 'relative'}}>\n            <div {...getToggleButtonProps()}>{selectedItem}</div>\n            <CustomList isOpen={isOpen}>\n              {items.map((item, index) => (\n                <CustomListItem\n                  key={index}\n                  {...getItemProps({\n                    item,\n                    index,\n                    isSelected: selectedItem === item,\n                  })}\n                >\n                  {item}\n                </CustomListItem>\n              ))}\n            </CustomList>\n          </div>\n        )}\n      </Downshift>\n    )\n  }\n}\n"
  },
  {
    "path": "test/downshift.test.tsx",
    "content": "import * as React from 'react'\nimport Downshift from '..'\n\nexport const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nexport default function ComboBox() {\n  return (\n    <Downshift>\n      {({\n        getInputProps,\n        getItemProps,\n        getMenuProps,\n        getLabelProps,\n        getToggleButtonProps,\n        inputValue,\n        isOpen,\n        getRootProps,\n        clearSelection,\n      }) => (\n        <div>\n          <label {...getLabelProps()}>Choose an element:</label>\n          <div {...getRootProps({}, {suppressRefError: true})}>\n            <input {...getInputProps()} data-testid=\"combobox-input\" />\n            <button\n              {...getToggleButtonProps({\n                'aria-label': 'toggle menu',\n                'data-testid': 'combobox-toggle-button',\n              })}\n            >\n              {isOpen ? <>&#8593;</> : <>&#8595;</>}\n            </button>\n            <button\n              aria-label=\"toggle menu\"\n              data-testid=\"clear-button\"\n              onClick={() => clearSelection()}\n            >\n              &#10007;\n            </button>\n          </div>\n          <ul {...getMenuProps()}>\n            {isOpen &&\n              (inputValue\n                ? colors.filter(i =>\n                    i.toLowerCase().includes(inputValue.toLowerCase()),\n                  )\n                : colors\n              ).map((item, index) => (\n                <li\n                  key={`${item}${index}`}\n                  {...getItemProps({\n                    item,\n                    index,\n                    'data-testid': `downshift-item-${index}`,\n                  })}\n                >\n                  {item}\n                </li>\n              ))}\n          </ul>\n        </div>\n      )}\n    </Downshift>\n  )\n}\n"
  },
  {
    "path": "test/setup.ts",
    "content": "import '@testing-library/jest-dom'\n"
  },
  {
    "path": "test/tsconfig.json",
    "content": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"noEmit\": true,\n  },\n  \"include\": [\"../typings/index.d.ts\"],\n  \"exclude\": []\n}"
  },
  {
    "path": "test/useCombobox.test.tsx",
    "content": "import * as React from 'react'\nimport {useCombobox} from '..'\n\nexport const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nexport default function DropdownCombobox() {\n  const [inputItems, setInputItems] = React.useState(colors)\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getInputProps,\n    getItemProps,\n    selectItem,\n  } = useCombobox({\n    items: inputItems,\n    onInputValueChange: ({inputValue}) => {\n      setInputItems(\n        colors.filter(item =>\n          item.toLowerCase().startsWith(inputValue.toLowerCase()),\n        ),\n      )\n    },\n  })\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div>\n        <input {...getInputProps()} data-testid=\"combobox-input\" />\n        <button\n          aria-label=\"toggle menu\"\n          data-testid=\"combobox-toggle-button\"\n          {...getToggleButtonProps()}\n        >\n          {isOpen ? <>&#8593;</> : <>&#8595;</>}\n        </button>\n        <button\n          aria-label=\"toggle menu\"\n          data-testid=\"clear-button\"\n          onClick={() => selectItem(null)}\n        >\n          &#10007;\n        </button>\n      </div>\n      <ul {...getMenuProps()}>\n        {isOpen &&\n          inputItems.map((item, index) => (\n            <li\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n                'data-testid': `downshift-item-${index}`,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "test/useMultipleSelect.test.tsx",
    "content": "import * as React from 'react'\n\nimport {useMultipleSelection, useSelect} from '..'\n\nexport const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nconst initialSelectedItems = colors.slice(0, 2)\n\nfunction getFilteredItems(selectedItems: string[]): string[] {\n  return colors.filter(colour => !selectedItems.includes(colour))\n}\n\nexport default function DropdownMultipleSelect() {\n  const {\n    getSelectedItemProps,\n    getDropdownProps,\n    addSelectedItem,\n    removeSelectedItem,\n    selectedItems,\n  } = useMultipleSelection({initialSelectedItems})\n  const items = getFilteredItems(selectedItems)\n  const {\n    isOpen,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getItemProps,\n  } = useSelect({\n    items,\n    selectedItem: null,\n    stateReducer(_state, actionAndChanges) {\n      const {changes, type} = actionAndChanges\n      switch (type) {\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n        case useSelect.stateChangeTypes.ItemClick:\n          return {\n            ...changes,\n            isOpen: true, // keep the menu open after selection.\n          }\n        default:\n          return changes\n      }\n    },\n    onStateChange({type, selectedItem: newSelectedItem}) {\n      switch (type) {\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownEnter:\n        case useSelect.stateChangeTypes.ToggleButtonKeyDownSpaceButton:\n        case useSelect.stateChangeTypes.ItemClick:\n          if (newSelectedItem) {\n            addSelectedItem(newSelectedItem)\n          }\n          break\n        default:\n          break\n      }\n    },\n  })\n\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div>\n        {selectedItems.map(function renderSelectedItem(\n          selectedItemForRender,\n          index,\n        ) {\n          return (\n            <span\n              key={`selected-item-${index}`}\n              {...getSelectedItemProps({\n                selectedItem: selectedItemForRender,\n                index,\n              })}\n            >\n              {selectedItemForRender}\n              <span\n                onClick={e => {\n                  e.stopPropagation()\n                  removeSelectedItem(selectedItemForRender)\n                }}\n              >\n                &#10005;\n              </span>\n            </span>\n          )\n        })}\n        <div\n          {...getToggleButtonProps(\n            getDropdownProps({preventKeyAction: isOpen}),\n          )}\n        >\n          Elements {isOpen ? <>&#8593;</> : <>&#8595;</>}\n        </div>\n      </div>\n      <ul {...getMenuProps()}>\n        {isOpen &&\n          colors.map((item, index) => (\n            <li\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "test/useSelect.test.tsx",
    "content": "import * as React from 'react'\n\nimport {useSelect} from '..'\n\nexport const colors = [\n  'Black',\n  'Red',\n  'Green',\n  'Blue',\n  'Orange',\n  'Purple',\n  'Pink',\n  'Orchid',\n  'Aqua',\n  'Lime',\n  'Gray',\n  'Brown',\n  'Teal',\n  'Skyblue',\n]\n\nexport default function DropdownSelect() {\n  const {\n    isOpen,\n    selectedItem,\n    getToggleButtonProps,\n    getLabelProps,\n    getMenuProps,\n    getItemProps,\n  } = useSelect({items: colors})\n\n  return (\n    <div>\n      <label {...getLabelProps()}>Choose an element:</label>\n      <div {...getToggleButtonProps()}>\n        {selectedItem ?? 'Elements'}\n        {isOpen ? <>&#8593;</> : <>&#8595;</>}\n      </div>\n      <ul {...getMenuProps()}>\n        {isOpen &&\n          colors.map((item, index) => (\n            <li\n              key={`${item}${index}`}\n              {...getItemProps({\n                item,\n                index,\n              })}\n            >\n              {item}\n            </li>\n          ))}\n      </ul>\n    </div>\n  )\n}\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"allowJs\": true,\n    \"esModuleInterop\": true,\n    \"jsx\": \"react\",\n    \"moduleResolution\": \"node\",\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"resolveJsonModule\": true,\n    \"noUncheckedIndexedAccess\": true,\n    \"module\": \"ESNext\",\n    \"typeRoots\": [\n      \"./typings\",\n      \"./node_modules/@types\",\n      \"./node_modules/@testing-library\"\n    ],\n    \"strictNullChecks\": true,\n    \"outDir\": \"dist\",\n    \"declaration\": true,\n    \"declarationDir\": \"dist\",\n    \"emitDeclarationOnly\": true,\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"*\": [\"*\"]\n    }\n  },\n  \"include\": [\"src/**/*.ts\", \"src/**/*.tsx\"],\n  \"exclude\": [\"typings\"]\n}\n"
  },
  {
    "path": "tsconfig.preact.json",
    "content": "{\n  \"extends\": \"./tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"preact/dist\"\n  }\n}\n"
  },
  {
    "path": "typings/index.d.ts",
    "content": "export * from './index.legacy'\nimport Downshift from './index.legacy'\nexport default Downshift\n\nexport {\n  UseTagGroupState,\n  UseTagGroupProps,\n  UseTagGroupReturnValue,\n  GetTagGroupProps,\n  GetTagGroupPropsOptions,\n  GetTagGroupPropsReturnValue,\n  GetTagProps,\n  GetTagPropsOptions,\n  GetTagPropsReturnValue,\n  GetTagRemoveProps,\n  GetTagRemovePropsOptions,\n  GetTagRemovePropsReturnValue,\n  UseTagGroupStateChangeTypes,\n} from '../dist/hooks/useTagGroup/index.types'\n\nimport {UseTagGroupInterface} from '../dist/hooks/useTagGroup/index.types'\nexport const useTagGroup: UseTagGroupInterface\nexport {UseTagGroupInterface}\n"
  },
  {
    "path": "typings/index.legacy.d.ts",
    "content": "import React from \"react\"\n\nimport {Environment} from '../dist/hooks/useTagGroup/index.types'\n\nexport {Environment}\n\nexport type Callback = () => void\n\nexport type Overwrite<T, U> = Pick<T, Exclude<keyof T, keyof U>> & U\n\nexport interface DownshiftState<Item> {\n  highlightedIndex: number | null\n  inputValue: string | null\n  isOpen: boolean\n  selectedItem: Item | null\n}\n\nexport enum StateChangeTypes {\n  unknown = '__autocomplete_unknown__',\n  mouseUp = '__autocomplete_mouseup__',\n  itemMouseEnter = '__autocomplete_item_mouseenter__',\n  keyDownArrowUp = '__autocomplete_keydown_arrow_up__',\n  keyDownArrowDown = '__autocomplete_keydown_arrow_down__',\n  keyDownEscape = '__autocomplete_keydown_escape__',\n  keyDownEnter = '__autocomplete_keydown_enter__',\n  clickItem = '__autocomplete_click_item__',\n  blurInput = '__autocomplete_blur_input__',\n  changeInput = '__autocomplete_change_input__',\n  keyDownSpaceButton = '__autocomplete_keydown_space_button__',\n  clickButton = '__autocomplete_click_button__',\n  blurButton = '__autocomplete_blur_button__',\n  controlledPropUpdatedSelectedItem = '__autocomplete_controlled_prop_updated_selected_item__',\n  touchEnd = '__autocomplete_touchend__',\n}\n\nexport interface DownshiftProps<Item> {\n  initialSelectedItem?: Item\n  initialInputValue?: string\n  initialHighlightedIndex?: number | null\n  initialIsOpen?: boolean\n  defaultHighlightedIndex?: number | null\n  defaultIsOpen?: boolean\n  itemToString?: (item: Item | null) => string\n  selectedItemChanged?: (prevItem: Item, item: Item) => boolean\n  getA11yStatusMessage?: (options: A11yStatusMessageOptions<Item>) => string\n  onChange?: (\n    selectedItem: Item | null,\n    stateAndHelpers: ControllerStateAndHelpers<Item>,\n  ) => void\n  onSelect?: (\n    selectedItem: Item | null,\n    stateAndHelpers: ControllerStateAndHelpers<Item>,\n  ) => void\n  onStateChange?: (\n    options: StateChangeOptions<Item>,\n    stateAndHelpers: ControllerStateAndHelpers<Item>,\n  ) => void\n  onInputValueChange?: (\n    inputValue: string,\n    stateAndHelpers: ControllerStateAndHelpers<Item>,\n  ) => void\n  stateReducer?: (\n    state: DownshiftState<Item>,\n    changes: StateChangeOptions<Item>,\n  ) => Partial<StateChangeOptions<Item>>\n  itemCount?: number\n  highlightedIndex?: number | null\n  inputValue?: string | null\n  isOpen?: boolean\n  selectedItem?: Item | null\n  children?: ChildrenFunction<Item>\n  id?: string\n  inputId?: string\n  labelId?: string\n  menuId?: string\n  getItemId?: (index?: number) => string\n  environment?: Environment\n  onOuterClick?: (stateAndHelpers: ControllerStateAndHelpers<Item>) => void\n  scrollIntoView?: (node: HTMLElement, menuNode: HTMLElement) => void\n  onUserAction?: (\n    options: StateChangeOptions<Item>,\n    stateAndHelpers: ControllerStateAndHelpers<Item>,\n  ) => void\n  suppressRefError?: boolean\n}\n\nexport interface A11yStatusMessageOptions<Item> {\n  highlightedIndex: number | null\n  inputValue: string\n  isOpen: boolean\n  itemToString: (item: Item | null) => string\n  previousResultCount: number\n  resultCount: number\n  highlightedItem: Item\n  selectedItem: Item | null\n}\n\nexport interface StateChangeOptions<Item> extends Partial<DownshiftState<Item>> {\n  type: StateChangeTypes\n}\n\nexport type StateChangeFunction<Item> = (\n  state: DownshiftState<Item>,\n) => Partial<StateChangeOptions<Item>>\n\nexport interface GetRootPropsOptions {\n  refKey?: string\n  ref?: React.RefObject<any>\n}\n\nexport interface GetRootPropsReturnValue {\n  'aria-expanded': boolean\n  'aria-haspopup': 'listbox'\n  'aria-labelledby': string\n  'aria-owns': string | undefined\n  ref?: React.RefObject<any>\n  role: 'combobox'\n}\n\nexport interface GetInputPropsOptions extends React.HTMLProps<HTMLInputElement> {\n  disabled?: boolean\n}\n\nexport interface GetInputPropsReturnValue {\n  'aria-autocomplete': 'list'\n  'aria-activedescendant': string | undefined\n  'aria-controls': string | undefined\n  'aria-labelledby': string | undefined\n  autoComplete: 'off'\n  id: string\n  onChange?: React.ChangeEventHandler\n  onChangeText?: React.ChangeEventHandler\n  onInput?: React.FormEventHandler\n  onKeyDown?: React.KeyboardEventHandler\n  onBlur?: React.FocusEventHandler\n  value: string\n}\n\nexport interface GetLabelPropsOptions extends React.HTMLProps<HTMLLabelElement> {}\n\nexport interface GetLabelPropsReturnValue {\n  htmlFor: string\n  id: string\n}\n\nexport interface GetToggleButtonPropsOptions extends React.HTMLProps<HTMLButtonElement> {\n  disabled?: boolean\n  onPress?: (event: React.BaseSyntheticEvent) => void\n}\n\nexport interface GetToggleButtonPropsReturnValue {\n  'aria-label': 'close menu' | 'open menu'\n  'aria-haspopup': true\n  'data-toggle': true\n  onPress?: (event: React.BaseSyntheticEvent) => void\n  onClick?: React.MouseEventHandler\n  onKeyDown?: React.KeyboardEventHandler\n  onKeyUp?: React.KeyboardEventHandler\n  onBlur?: React.FocusEventHandler\n  role: 'button'\n  type: 'button'\n}\n\nexport interface GetMenuPropsOptions\n  extends React.HTMLProps<HTMLElement>, GetPropsWithRefKey {\n  ['aria-label']?: string\n}\n\nexport interface GetMenuPropsReturnValue {\n  'aria-labelledby': string | undefined\n  ref?: React.RefObject<any>\n  role: 'listbox'\n  id: string\n}\n\nexport interface GetPropsCommonOptions {\n  suppressRefError?: boolean\n}\n\nexport interface GetPropsWithRefKey {\n  refKey?: string\n}\n\nexport interface GetItemPropsOptions<Item> extends React.HTMLProps<HTMLElement> {\n  index?: number\n  item: Item\n  isSelected?: boolean\n  disabled?: boolean\n}\n\nexport interface GetItemPropsReturnValue {\n  'aria-selected': boolean\n  id: string\n  onClick?: React.MouseEventHandler\n  onMouseDown?: React.MouseEventHandler\n  onMouseMove?: React.MouseEventHandler\n  onPress?: React.MouseEventHandler\n  role: 'option'\n}\n\nexport interface PropGetters<Item> {\n  getRootProps: <Options>(\n    options?: GetRootPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<GetRootPropsReturnValue, Options>\n  getToggleButtonProps: <Options>(\n    options?: GetToggleButtonPropsOptions & Options,\n  ) => Overwrite<GetToggleButtonPropsReturnValue, Options>\n  getLabelProps: <Options>(\n    options?: GetLabelPropsOptions & Options,\n  ) => Overwrite<GetLabelPropsReturnValue, Options>\n  getMenuProps: <Options>(\n    options?: GetMenuPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<GetMenuPropsReturnValue, Options>\n  getInputProps: <Options>(\n    options?: GetInputPropsOptions & Options,\n  ) => Overwrite<GetInputPropsReturnValue, Options>\n  getItemProps: <Options>(\n    options: GetItemPropsOptions<Item> & Options,\n  ) => Omit<Overwrite<GetItemPropsReturnValue, Options>, 'index' | 'item'>\n}\n\nexport interface Actions<Item> {\n  reset: (\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  openMenu: (cb?: Callback) => void\n  closeMenu: (cb?: Callback) => void\n  toggleMenu: (\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  selectItem: (\n    item: Item | null,\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  selectItemAtIndex: (\n    index: number,\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  selectHighlightedItem: (\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  setHighlightedIndex: (\n    index: number,\n    otherStateToSet?: Partial<StateChangeOptions<Item>>,\n    cb?: Callback,\n  ) => void\n  clearSelection: (cb?: Callback) => void\n  clearItems: () => void\n  setItemCount: (count: number) => void\n  unsetItemCount: () => void\n  setState: (\n    stateToSet: Partial<StateChangeOptions<Item>> | StateChangeFunction<Item>,\n    cb?: Callback,\n  ) => void\n  itemToString: (item: Item | null) => string\n}\n\nexport type ControllerStateAndHelpers<Item> = DownshiftState<Item> &\n  PropGetters<Item> &\n  Actions<Item>\n\nexport type ChildrenFunction<Item> = (\n  options: ControllerStateAndHelpers<Item>,\n) => React.ReactNode\n\nexport default class Downshift<Item = any> extends React.Component<\n  DownshiftProps<Item>\n> {\n  static stateChangeTypes: {\n    unknown: StateChangeTypes.unknown\n    mouseUp: StateChangeTypes.mouseUp\n    itemMouseEnter: StateChangeTypes.itemMouseEnter\n    keyDownArrowUp: StateChangeTypes.keyDownArrowUp\n    keyDownArrowDown: StateChangeTypes.keyDownArrowDown\n    keyDownEscape: StateChangeTypes.keyDownEscape\n    keyDownEnter: StateChangeTypes.keyDownEnter\n    clickItem: StateChangeTypes.clickItem\n    blurInput: StateChangeTypes.blurInput\n    changeInput: StateChangeTypes.changeInput\n    keyDownSpaceButton: StateChangeTypes.keyDownSpaceButton\n    clickButton: StateChangeTypes.clickButton\n    blurButton: StateChangeTypes.blurButton\n    controlledPropUpdatedSelectedItem: StateChangeTypes.controlledPropUpdatedSelectedItem\n    touchEnd: StateChangeTypes.touchEnd\n  }\n}\n\nexport function resetIdCounter(): void\n\n/* useSelect Types */\n\nexport interface UseSelectState<Item> {\n  highlightedIndex: number\n  selectedItem: Item | null\n  isOpen: boolean\n  inputValue: string\n}\n\nexport enum UseSelectStateChangeTypes {\n  ToggleButtonClick = '__togglebutton_click__',\n  ToggleButtonKeyDownArrowDown = '__togglebutton_keydown_arrow_down__',\n  ToggleButtonKeyDownArrowUp = '__togglebutton_keydown_arrow_up__',\n  ToggleButtonKeyDownCharacter = '__togglebutton_keydown_character__',\n  ToggleButtonKeyDownEscape = '__togglebutton_keydown_escape__',\n  ToggleButtonKeyDownHome = '__togglebutton_keydown_home__',\n  ToggleButtonKeyDownEnd = '__togglebutton_keydown_end__',\n  ToggleButtonKeyDownEnter = '__togglebutton_keydown_enter__',\n  ToggleButtonKeyDownSpaceButton = '__togglebutton_keydown_space_button__',\n  ToggleButtonKeyDownPageUp = '__togglebutton_keydown_page_up__',\n  ToggleButtonKeyDownPageDown = '__togglebutton_keydown_page_down__',\n  ToggleButtonBlur = '__togglebutton_blur__',\n  MenuMouseLeave = '__menu_mouse_leave__',\n  ItemMouseMove = '__item_mouse_move__',\n  ItemClick = '__item_click__',\n  FunctionToggleMenu = '__function_toggle_menu__',\n  FunctionOpenMenu = '__function_open_menu__',\n  FunctionCloseMenu = '__function_close_menu__',\n  FunctionSetHighlightedIndex = '__function_set_highlighted_index__',\n  FunctionSelectItem = '__function_select_item__',\n  FunctionSetInputValue = '__function_set_input_value__',\n  FunctionReset = '__function_reset__',\n}\n\nexport interface UseSelectProps<Item> {\n  items: Item[]\n  isItemDisabled?(item: Item, index: number): boolean\n  itemToString?: (item: Item | null) => string\n  itemToKey?: (item: Item | null) => any\n  getA11yStatusMessage?: (options: UseSelectState<Item>) => string\n  highlightedIndex?: number\n  initialHighlightedIndex?: number\n  defaultHighlightedIndex?: number\n  isOpen?: boolean\n  initialIsOpen?: boolean\n  defaultIsOpen?: boolean\n  selectedItem?: Item | null\n  initialSelectedItem?: Item | null\n  defaultSelectedItem?: Item | null\n  id?: string\n  labelId?: string\n  menuId?: string\n  toggleButtonId?: string\n  getItemId?: (index: number) => string\n  scrollIntoView?: (node: HTMLElement, menuNode: HTMLElement) => void\n  stateReducer?: (\n    state: UseSelectState<Item>,\n    actionAndChanges: UseSelectStateChangeOptions<Item>,\n  ) => Partial<UseSelectState<Item>>\n  onSelectedItemChange?: (changes: UseSelectSelectedItemChange<Item>) => void\n  onIsOpenChange?: (changes: UseSelectIsOpenChange<Item>) => void\n  onHighlightedIndexChange?: (\n    changes: UseSelectHighlightedIndexChange<Item>,\n  ) => void\n  onStateChange?: (changes: UseSelectStateChange<Item>) => void\n  environment?: Environment\n}\n\nexport interface UseSelectStateChangeOptions<\n  Item,\n> extends UseSelectDispatchAction<Item> {\n  changes: Partial<UseSelectState<Item>>\n}\n\nexport interface UseSelectDispatchAction<Item> {\n  type: UseSelectStateChangeTypes\n  altKey?: boolean\n  key?: string\n  index?: number\n  highlightedIndex?: number\n  selectedItem?: Item | null\n  inputValue?: string\n}\n\nexport interface UseSelectStateChange<Item> extends Partial<UseSelectState<Item>> {\n  type: UseSelectStateChangeTypes\n}\n\nexport interface UseSelectSelectedItemChange<Item> extends UseSelectStateChange<Item> {\n  selectedItem: Item | null\n}\n\nexport interface UseSelectHighlightedIndexChange<\n  Item,\n> extends UseSelectStateChange<Item> {\n  highlightedIndex: number\n}\n\nexport interface UseSelectIsOpenChange<Item> extends UseSelectStateChange<Item> {\n  isOpen: boolean\n}\n\nexport interface UseSelectGetMenuPropsOptions\n  extends GetPropsWithRefKey, GetMenuPropsOptions {}\n\nexport interface UseSelectGetMenuReturnValue extends GetMenuPropsReturnValue {\n  onMouseLeave: React.MouseEventHandler\n}\n\nexport interface UseSelectGetToggleButtonPropsOptions\n  extends GetPropsWithRefKey, React.HTMLProps<HTMLElement> {\n  onPress?: (event: React.BaseSyntheticEvent) => void\n}\n\nexport interface UseSelectGetToggleButtonReturnValue extends Pick<\n  GetToggleButtonPropsReturnValue,\n  'onBlur' | 'onClick' | 'onPress' | 'onKeyDown'\n> {\n  'aria-activedescendant': string\n  'aria-controls': string\n  'aria-expanded': boolean\n  'aria-haspopup': 'listbox'\n  'aria-labelledby': string | undefined\n  id: string\n  ref?: React.RefObject<any>\n  role: 'combobox'\n  tabIndex: 0\n}\n\nexport interface UseSelectGetLabelPropsOptions extends GetLabelPropsOptions {}\n\nexport interface UseSelectGetLabelPropsReturnValue extends GetLabelPropsReturnValue {\n  onClick: React.MouseEventHandler\n}\n\nexport interface UseSelectGetItemPropsOptions<Item>\n  extends Omit<GetItemPropsOptions<Item>, 'disabled'>, GetPropsWithRefKey {}\n\nexport interface UseSelectGetItemPropsReturnValue extends GetItemPropsReturnValue {\n  'aria-disabled': boolean\n  ref?: React.RefObject<any>\n}\n\nexport interface UseSelectPropGetters<Item> {\n  getToggleButtonProps: <Options>(\n    options?: UseSelectGetToggleButtonPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<UseSelectGetToggleButtonReturnValue, Options>\n  getLabelProps: <Options>(\n    options?: UseSelectGetLabelPropsOptions & Options,\n  ) => Overwrite<UseSelectGetLabelPropsReturnValue, Options>\n  getMenuProps: <Options>(\n    options?: UseSelectGetMenuPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<UseSelectGetMenuReturnValue, Options>\n  getItemProps: <Options>(\n    options: UseSelectGetItemPropsOptions<Item> & Options,\n  ) => Omit<\n    Overwrite<UseSelectGetItemPropsReturnValue, Options>,\n    'index' | 'item'\n  >\n}\n\nexport interface UseSelectActions<Item> {\n  reset: () => void\n  openMenu: () => void\n  closeMenu: () => void\n  toggleMenu: () => void\n  selectItem: (item: Item | null) => void\n  setHighlightedIndex: (index: number) => void\n}\n\nexport type UseSelectReturnValue<Item> = UseSelectState<Item> &\n  UseSelectPropGetters<Item> &\n  UseSelectActions<Item>\n\nexport interface UseSelectInterface {\n  <Item>(props: UseSelectProps<Item>): UseSelectReturnValue<Item>\n  stateChangeTypes: {\n    ToggleButtonClick: UseSelectStateChangeTypes.ToggleButtonClick\n    ToggleButtonKeyDownArrowDown: UseSelectStateChangeTypes.ToggleButtonKeyDownArrowDown\n    ToggleButtonKeyDownArrowUp: UseSelectStateChangeTypes.ToggleButtonKeyDownArrowUp\n    ToggleButtonKeyDownCharacter: UseSelectStateChangeTypes.ToggleButtonKeyDownCharacter\n    ToggleButtonKeyDownEscape: UseSelectStateChangeTypes.ToggleButtonKeyDownEscape\n    ToggleButtonKeyDownHome: UseSelectStateChangeTypes.ToggleButtonKeyDownHome\n    ToggleButtonKeyDownEnd: UseSelectStateChangeTypes.ToggleButtonKeyDownEnd\n    ToggleButtonKeyDownEnter: UseSelectStateChangeTypes.ToggleButtonKeyDownEnter\n    ToggleButtonKeyDownSpaceButton: UseSelectStateChangeTypes.ToggleButtonKeyDownSpaceButton\n    ToggleButtonKeyDownPageUp: UseSelectStateChangeTypes.ToggleButtonKeyDownPageUp\n    ToggleButtonKeyDownPageDown: UseSelectStateChangeTypes.ToggleButtonKeyDownPageDown\n    ToggleButtonBlur: UseSelectStateChangeTypes.ToggleButtonBlur\n    MenuMouseLeave: UseSelectStateChangeTypes.MenuMouseLeave\n    ItemMouseMove: UseSelectStateChangeTypes.ItemMouseMove\n    ItemClick: UseSelectStateChangeTypes.ItemClick\n    FunctionToggleMenu: UseSelectStateChangeTypes.FunctionToggleMenu\n    FunctionOpenMenu: UseSelectStateChangeTypes.FunctionOpenMenu\n    FunctionCloseMenu: UseSelectStateChangeTypes.FunctionCloseMenu\n    FunctionSetHighlightedIndex: UseSelectStateChangeTypes.FunctionSetHighlightedIndex\n    FunctionSelectItem: UseSelectStateChangeTypes.FunctionSelectItem\n    FunctionSetInputValue: UseSelectStateChangeTypes.FunctionSetInputValue\n    FunctionReset: UseSelectStateChangeTypes.FunctionReset\n  }\n}\n\nexport const useSelect: UseSelectInterface\n\n/* useCombobox Types */\n\nexport interface UseComboboxState<Item> {\n  highlightedIndex: number\n  selectedItem: Item | null\n  isOpen: boolean\n  inputValue: string\n}\n\nexport enum UseComboboxStateChangeTypes {\n  InputKeyDownArrowDown = '__input_keydown_arrow_down__',\n  InputKeyDownArrowUp = '__input_keydown_arrow_up__',\n  InputKeyDownEscape = '__input_keydown_escape__',\n  InputKeyDownHome = '__input_keydown_home__',\n  InputKeyDownEnd = '__input_keydown_end__',\n  InputKeyDownPageUp = '__input_keydown_page_up__',\n  InputKeyDownPageDown = '__input_keydown_page_down__',\n  InputKeyDownEnter = '__input_keydown_enter__',\n  InputChange = '__input_change__',\n  InputBlur = '__input_blur__',\n  InputClick = '__input_click__',\n  MenuMouseLeave = '__menu_mouse_leave__',\n  ItemMouseMove = '__item_mouse_move__',\n  ItemClick = '__item_click__',\n  ToggleButtonClick = '__togglebutton_click__',\n  FunctionToggleMenu = '__function_toggle_menu__',\n  FunctionOpenMenu = '__function_open_menu__',\n  FunctionCloseMenu = '__function_close_menu__',\n  FunctionSetHighlightedIndex = '__function_set_highlighted_index__',\n  FunctionSelectItem = '__function_select_item__',\n  FunctionSetInputValue = '__function_set_input_value__',\n  FunctionReset = '__function_reset__',\n  ControlledPropUpdatedSelectedItem = '__controlled_prop_updated_selected_item__',\n}\n\nexport interface UseComboboxProps<Item> {\n  items: Item[]\n  isItemDisabled?(item: Item, index: number): boolean\n  itemToString?: (item: Item | null) => string\n  itemToKey?: (item: Item | null) => any\n  getA11yStatusMessage?: (options: UseComboboxState<Item>) => string\n  highlightedIndex?: number\n  initialHighlightedIndex?: number\n  defaultHighlightedIndex?: number\n  isOpen?: boolean\n  initialIsOpen?: boolean\n  defaultIsOpen?: boolean\n  selectedItem?: Item | null\n  initialSelectedItem?: Item | null\n  defaultSelectedItem?: Item | null\n  inputValue?: string\n  initialInputValue?: string\n  defaultInputValue?: string\n  id?: string\n  labelId?: string\n  menuId?: string\n  toggleButtonId?: string\n  inputId?: string\n  getItemId?: (index: number) => string\n  scrollIntoView?: (node: HTMLElement, menuNode: HTMLElement) => void\n  stateReducer?: (\n    state: UseComboboxState<Item>,\n    actionAndChanges: UseComboboxStateChangeOptions<Item>,\n  ) => Partial<UseComboboxState<Item>>\n  onSelectedItemChange?: (changes: UseComboboxSelectedItemChange<Item>) => void\n  onIsOpenChange?: (changes: UseComboboxIsOpenChange<Item>) => void\n  onHighlightedIndexChange?: (\n    changes: UseComboboxHighlightedIndexChange<Item>,\n  ) => void\n  onStateChange?: (changes: UseComboboxStateChange<Item>) => void\n  onInputValueChange?: (changes: UseComboboxInputValueChange<Item>) => void\n  environment?: Environment\n}\n\nexport interface UseComboboxStateChangeOptions<\n  Item,\n> extends UseComboboxDispatchAction<Item> {\n  changes: Partial<UseComboboxState<Item>>\n}\n\nexport interface UseComboboxDispatchAction<Item> {\n  type: UseComboboxStateChangeTypes\n  altKey?: boolean\n  inputValue?: string\n  index?: number\n  highlightedIndex?: number\n  selectedItem?: Item | null\n  selectItem?: boolean\n}\n\nexport interface UseComboboxStateChange<Item> extends Partial<UseComboboxState<Item>> {\n  type: UseComboboxStateChangeTypes\n}\n\nexport interface UseComboboxSelectedItemChange<\n  Item,\n> extends UseComboboxStateChange<Item> {\n  selectedItem: Item | null\n}\n\nexport interface UseComboboxHighlightedIndexChange<\n  Item,\n> extends UseComboboxStateChange<Item> {\n  highlightedIndex: number\n}\n\nexport interface UseComboboxIsOpenChange<Item> extends UseComboboxStateChange<Item> {\n  isOpen: boolean\n}\n\nexport interface UseComboboxInputValueChange<\n  Item,\n> extends UseComboboxStateChange<Item> {\n  inputValue: string\n}\n\nexport interface UseComboboxGetMenuPropsOptions\n  extends GetPropsWithRefKey, GetMenuPropsOptions {}\n\nexport interface UseComboboxGetMenuPropsReturnValue extends UseSelectGetMenuReturnValue {}\n\nexport interface UseComboboxGetToggleButtonPropsOptions\n  extends GetPropsWithRefKey, GetToggleButtonPropsOptions {}\n\nexport interface UseComboboxGetToggleButtonPropsReturnValue {\n  'aria-controls': string\n  'aria-expanded': boolean\n  id: string\n  onPress?: (event: React.BaseSyntheticEvent) => void\n  onClick?: React.MouseEventHandler\n  ref?: React.RefObject<any>\n  tabIndex: -1\n}\n\nexport interface UseComboboxGetLabelPropsOptions extends GetLabelPropsOptions {}\n\nexport interface UseComboboxGetLabelPropsReturnValue extends GetLabelPropsReturnValue {}\n\nexport interface UseComboboxGetItemPropsOptions<Item>\n  extends Omit<GetItemPropsOptions<Item>, 'disabled'>, GetPropsWithRefKey {}\n\nexport interface UseComboboxGetItemPropsReturnValue extends GetItemPropsReturnValue {\n  'aria-disabled': boolean\n  ref?: React.RefObject<any>\n}\n\nexport interface UseComboboxGetInputPropsOptions\n  extends GetInputPropsOptions, GetPropsWithRefKey {}\n\nexport interface UseComboboxGetInputPropsReturnValue extends GetInputPropsReturnValue {\n  'aria-activedescendant': string\n  'aria-controls': string\n  'aria-expanded': boolean\n  role: 'combobox'\n  onClick: React.MouseEventHandler\n}\n\nexport interface UseComboboxPropGetters<Item> {\n  getToggleButtonProps: <Options>(\n    options?: UseComboboxGetToggleButtonPropsOptions & Options,\n  ) => Overwrite<UseComboboxGetToggleButtonPropsReturnValue, Options>\n  getLabelProps: <Options>(\n    options?: UseComboboxGetLabelPropsOptions & Options,\n  ) => Overwrite<UseComboboxGetLabelPropsReturnValue, Options>\n  getMenuProps: <Options>(\n    options?: UseComboboxGetMenuPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<UseComboboxGetMenuPropsReturnValue, Options>\n  getItemProps: <Options>(\n    options: UseComboboxGetItemPropsOptions<Item> & Options,\n  ) => Omit<\n    Overwrite<UseComboboxGetItemPropsReturnValue, Options>,\n    'index' | 'item'\n  >\n  getInputProps: <Options>(\n    options?: UseComboboxGetInputPropsOptions & Options,\n    otherOptions?: GetPropsCommonOptions,\n  ) => Overwrite<UseComboboxGetInputPropsReturnValue, Options>\n}\n\nexport interface UseComboboxActions<Item> {\n  reset: () => void\n  openMenu: () => void\n  closeMenu: () => void\n  toggleMenu: () => void\n  selectItem: (item: Item | null) => void\n  setHighlightedIndex: (index: number) => void\n  setInputValue: (inputValue: string) => void\n}\n\nexport type UseComboboxReturnValue<Item> = UseComboboxState<Item> &\n  UseComboboxPropGetters<Item> &\n  UseComboboxActions<Item>\n\nexport interface UseComboboxInterface {\n  <Item>(props: UseComboboxProps<Item>): UseComboboxReturnValue<Item>\n  stateChangeTypes: {\n    InputKeyDownArrowDown: UseComboboxStateChangeTypes.InputKeyDownArrowDown\n    InputKeyDownArrowUp: UseComboboxStateChangeTypes.InputKeyDownArrowUp\n    InputKeyDownEscape: UseComboboxStateChangeTypes.InputKeyDownEscape\n    InputKeyDownHome: UseComboboxStateChangeTypes.InputKeyDownHome\n    InputKeyDownEnd: UseComboboxStateChangeTypes.InputKeyDownEnd\n    InputKeyDownPageDown: UseComboboxStateChangeTypes.InputKeyDownPageDown\n    InputKeyDownPageUp: UseComboboxStateChangeTypes.InputKeyDownPageUp\n    InputKeyDownEnter: UseComboboxStateChangeTypes.InputKeyDownEnter\n    InputChange: UseComboboxStateChangeTypes.InputChange\n    InputBlur: UseComboboxStateChangeTypes.InputBlur\n    InputClick: UseComboboxStateChangeTypes.InputClick\n    MenuMouseLeave: UseComboboxStateChangeTypes.MenuMouseLeave\n    ItemMouseMove: UseComboboxStateChangeTypes.ItemMouseMove\n    ItemClick: UseComboboxStateChangeTypes.ItemClick\n    ToggleButtonClick: UseComboboxStateChangeTypes.ToggleButtonClick\n    FunctionToggleMenu: UseComboboxStateChangeTypes.FunctionToggleMenu\n    FunctionOpenMenu: UseComboboxStateChangeTypes.FunctionOpenMenu\n    FunctionCloseMenu: UseComboboxStateChangeTypes.FunctionCloseMenu\n    FunctionSetHighlightedIndex: UseComboboxStateChangeTypes.FunctionSetHighlightedIndex\n    FunctionSelectItem: UseComboboxStateChangeTypes.FunctionSelectItem\n    FunctionSetInputValue: UseComboboxStateChangeTypes.FunctionSetInputValue\n    FunctionReset: UseComboboxStateChangeTypes.FunctionReset\n    ControlledPropUpdatedSelectedItem: UseComboboxStateChangeTypes.ControlledPropUpdatedSelectedItem\n  }\n}\n\nexport const useCombobox: UseComboboxInterface\n\n// useMultipleSelection types.\n\nexport interface UseMultipleSelectionState<Item> {\n  selectedItems: Item[]\n  activeIndex: number\n}\n\nexport enum UseMultipleSelectionStateChangeTypes {\n  SelectedItemClick = '__selected_item_click__',\n  SelectedItemKeyDownDelete = '__selected_item_keydown_delete__',\n  SelectedItemKeyDownBackspace = '__selected_item_keydown_backspace__',\n  SelectedItemKeyDownNavigationNext = '__selected_item_keydown_navigation_next__',\n  SelectedItemKeyDownNavigationPrevious = '__selected_item_keydown_navigation_previous__',\n  DropdownKeyDownNavigationPrevious = '__dropdown_keydown_navigation_previous__',\n  DropdownKeyDownBackspace = '__dropdown_keydown_backspace__',\n  DropdownClick = '__dropdown_click__',\n  FunctionAddSelectedItem = '__function_add_selected_item__',\n  FunctionRemoveSelectedItem = '__function_remove_selected_item__',\n  FunctionSetSelectedItems = '__function_set_selected_items__',\n  FunctionSetActiveIndex = '__function_set_active_index__',\n  FunctionReset = '__function_reset__',\n}\n\nexport interface UseMultipleSelectionProps<Item> {\n  selectedItems?: Item[]\n  initialSelectedItems?: Item[]\n  defaultSelectedItems?: Item[]\n  itemToKey?: (item: Item | null) => any\n  getA11yStatusMessage?: (options: UseMultipleSelectionState<Item>) => string\n  stateReducer?: (\n    state: UseMultipleSelectionState<Item>,\n    actionAndChanges: UseMultipleSelectionStateChangeOptions<Item>,\n  ) => Partial<UseMultipleSelectionState<Item>>\n  activeIndex?: number\n  initialActiveIndex?: number\n  defaultActiveIndex?: number\n  onActiveIndexChange?: (\n    changes: UseMultipleSelectionActiveIndexChange<Item>,\n  ) => void\n  onSelectedItemsChange?: (\n    changes: UseMultipleSelectionSelectedItemsChange<Item>,\n  ) => void\n  onStateChange?: (changes: UseMultipleSelectionStateChange<Item>) => void\n  keyNavigationNext?: string\n  keyNavigationPrevious?: string\n  environment?: Environment\n}\n\nexport interface UseMultipleSelectionStateChangeOptions<\n  Item,\n> extends UseMultipleSelectionDispatchAction<Item> {\n  changes: Partial<UseMultipleSelectionState<Item>>\n}\n\nexport interface UseMultipleSelectionDispatchAction<Item> {\n  type: UseMultipleSelectionStateChangeTypes\n  index?: number\n  selectedItem?: Item | null\n  selectedItems?: Item[]\n  activeIndex?: number\n}\n\nexport interface UseMultipleSelectionStateChange<Item> extends Partial<\n  UseMultipleSelectionState<Item>\n> {\n  type: UseMultipleSelectionStateChangeTypes\n}\n\nexport interface UseMultipleSelectionActiveIndexChange<\n  Item,\n> extends UseMultipleSelectionStateChange<Item> {\n  activeIndex: number\n}\n\nexport interface UseMultipleSelectionSelectedItemsChange<\n  Item,\n> extends UseMultipleSelectionStateChange<Item> {\n  selectedItems: Item[]\n}\n\nexport interface A11yRemovalMessage<Item> {\n  itemToString: (item: Item) => string\n  resultCount: number\n  activeSelectedItem: Item\n  removedSelectedItem: Item\n  activeIndex: number\n}\n\nexport interface UseMultipleSelectionGetSelectedItemPropsOptions<Item>\n  extends React.HTMLProps<HTMLElement>, GetPropsWithRefKey {\n  index?: number\n  selectedItem: Item\n}\n\nexport interface UseMultipleSelectionGetSelectedItemReturnValue {\n  ref?: React.RefObject<any>\n  tabIndex: 0 | -1\n  onClick: React.MouseEventHandler\n  onKeyDown: React.KeyboardEventHandler\n}\n\nexport interface UseMultipleSelectionGetDropdownPropsOptions extends React.HTMLProps<HTMLElement> {\n  preventKeyAction?: boolean\n}\n\nexport interface UseMultipleSelectionGetDropdownReturnValue {\n  ref?: React.RefObject<any>\n  onClick?: React.MouseEventHandler\n  onKeyDown?: React.KeyboardEventHandler\n}\n\nexport interface UseMultipleSelectionPropGetters<Item> {\n  getDropdownProps: <Options>(\n    options?: UseMultipleSelectionGetDropdownPropsOptions & Options,\n    extraOptions?: GetPropsCommonOptions,\n  ) => Omit<\n    Overwrite<UseMultipleSelectionGetDropdownReturnValue, Options>,\n    'preventKeyAction'\n  >\n  getSelectedItemProps: <Options>(\n    options: UseMultipleSelectionGetSelectedItemPropsOptions<Item> & Options,\n  ) => Omit<\n    Overwrite<UseMultipleSelectionGetSelectedItemReturnValue, Options>,\n    'index' | 'selectedItem'\n  >\n}\n\nexport interface UseMultipleSelectionActions<Item> {\n  reset: () => void\n  addSelectedItem: (item: Item) => void\n  removeSelectedItem: (item: Item) => void\n  setSelectedItems: (items: Item[]) => void\n  setActiveIndex: (index: number) => void\n}\n\nexport type UseMultipleSelectionReturnValue<Item> = UseMultipleSelectionState<Item> &\n  UseMultipleSelectionPropGetters<Item> &\n  UseMultipleSelectionActions<Item>\n\nexport interface UseMultipleSelectionInterface {\n  <Item>(\n    props?: UseMultipleSelectionProps<Item>,\n  ): UseMultipleSelectionReturnValue<Item>\n  stateChangeTypes: {\n    SelectedItemClick: UseMultipleSelectionStateChangeTypes.SelectedItemClick\n    SelectedItemKeyDownDelete: UseMultipleSelectionStateChangeTypes.SelectedItemKeyDownDelete\n    SelectedItemKeyDownBackspace: UseMultipleSelectionStateChangeTypes.SelectedItemKeyDownBackspace\n    SelectedItemKeyDownNavigationNext: UseMultipleSelectionStateChangeTypes.SelectedItemKeyDownNavigationNext\n    SelectedItemKeyDownNavigationPrevious: UseMultipleSelectionStateChangeTypes.SelectedItemKeyDownNavigationPrevious\n    DropdownKeyDownNavigationPrevious: UseMultipleSelectionStateChangeTypes.DropdownKeyDownNavigationPrevious\n    DropdownKeyDownBackspace: UseMultipleSelectionStateChangeTypes.DropdownKeyDownBackspace\n    DropdownClick: UseMultipleSelectionStateChangeTypes.DropdownClick\n    FunctionAddSelectedItem: UseMultipleSelectionStateChangeTypes.FunctionAddSelectedItem\n    FunctionRemoveSelectedItem: UseMultipleSelectionStateChangeTypes.FunctionRemoveSelectedItem\n    FunctionSetSelectedItems: UseMultipleSelectionStateChangeTypes.FunctionSetSelectedItems\n    FunctionSetActiveIndex: UseMultipleSelectionStateChangeTypes.FunctionSetActiveIndex\n    FunctionReset: UseMultipleSelectionStateChangeTypes.FunctionReset\n  }\n}\n\nexport const useMultipleSelection: UseMultipleSelectionInterface\n"
  }
]