autocompleteEditor.spec.js 77 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019
  1. describe('AutocompleteEditor', () => {
  2. const id = 'testContainer';
  3. const choices = ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white', 'purple', 'lime', 'olive', 'cyan'];
  4. beforeEach(function() {
  5. this.$container = $(`<div id="${id}" style="width: 300px; height: 200px; overflow: auto"></div>`).appendTo('body');
  6. });
  7. afterEach(function() {
  8. if (this.$container) {
  9. destroy();
  10. this.$container.remove();
  11. }
  12. });
  13. describe('open editor', () => {
  14. it('should display editor (after hitting ENTER)', () => {
  15. handsontable({
  16. columns: [
  17. {
  18. editor: 'autocomplete',
  19. source: choices
  20. }
  21. ]
  22. });
  23. selectCell(0, 0);
  24. expect(isEditorVisible()).toBe(false);
  25. keyDownUp('enter');
  26. expect(isEditorVisible()).toBe(true);
  27. });
  28. it('should display editor (after hitting F2)', () => {
  29. handsontable({
  30. columns: [
  31. {
  32. editor: 'autocomplete',
  33. source: choices
  34. }
  35. ]
  36. });
  37. selectCell(0, 0);
  38. expect(isEditorVisible()).toBe(false);
  39. keyDownUp('f2');
  40. expect(isEditorVisible()).toBe(true);
  41. });
  42. it('should display editor (after doubleclicking)', () => {
  43. handsontable({
  44. columns: [
  45. {
  46. editor: 'autocomplete',
  47. source: choices
  48. }
  49. ]
  50. });
  51. selectCell(0, 0);
  52. expect(isEditorVisible()).toBe(false);
  53. mouseDoubleClick($(getCell(0, 0)));
  54. expect(isEditorVisible()).toBe(true);
  55. });
  56. // see https://github.com/handsontable/handsontable/issues/3380
  57. it('should not throw error while selecting the next cell by hitting enter key', () => {
  58. const spy = jasmine.createSpyObj('error', ['test']);
  59. const prevError = window.onerror;
  60. window.onerror = function() {
  61. spy.test();
  62. };
  63. handsontable({
  64. columns: [{
  65. editor: 'autocomplete',
  66. source: choices
  67. }]
  68. });
  69. selectCell(0, 0);
  70. keyDownUp('enter');
  71. keyDownUp('enter');
  72. keyDownUp('enter');
  73. expect(spy.test.calls.count()).toBe(0);
  74. window.onerror = prevError;
  75. });
  76. });
  77. describe('choices', () => {
  78. it('should display given choices (array)', (done) => {
  79. handsontable({
  80. columns: [
  81. {
  82. editor: 'autocomplete',
  83. source: choices
  84. }
  85. ]
  86. });
  87. selectCell(0, 0);
  88. const editor = $('.autocompleteEditor');
  89. keyDownUp('enter');
  90. setTimeout(() => {
  91. expect(editor.find('tbody td:eq(0)').text()).toEqual(choices[0]);
  92. expect(editor.find('tbody td:eq(1)').text()).toEqual(choices[1]);
  93. expect(editor.find('tbody td:eq(2)').text()).toEqual(choices[2]);
  94. expect(editor.find('tbody td:eq(3)').text()).toEqual(choices[3]);
  95. expect(editor.find('tbody td:eq(4)').text()).toEqual(choices[4]);
  96. done();
  97. }, 100);
  98. });
  99. it('should call source function with context set as cellProperties', (done) => {
  100. const source = jasmine.createSpy('source');
  101. let context;
  102. source.and.callFake(function(query, process) {
  103. process(choices);
  104. context = this;
  105. });
  106. const hot = handsontable({
  107. columns: [
  108. {
  109. editor: 'autocomplete',
  110. source
  111. }
  112. ]
  113. });
  114. selectCell(0, 0);
  115. source.calls.reset();
  116. keyDownUp('enter');
  117. setTimeout(() => {
  118. expect(context.instance).toBe(hot);
  119. expect(context.row).toBe(0);
  120. expect(context.col).toBe(0);
  121. done();
  122. }, 200);
  123. });
  124. it('should display given choices (sync function)', (done) => {
  125. const syncSources = jasmine.createSpy('syncSources');
  126. syncSources.and.callFake((query, process) => {
  127. process(choices);
  128. });
  129. handsontable({
  130. columns: [
  131. {
  132. editor: 'autocomplete',
  133. source: syncSources
  134. }
  135. ]
  136. });
  137. selectCell(0, 0);
  138. const editor = $('.autocompleteEditor');
  139. syncSources.calls.reset();
  140. keyDownUp('enter');
  141. setTimeout(() => {
  142. expect(editor.find('tbody td:eq(0)').text()).toEqual(choices[0]);
  143. expect(editor.find('tbody td:eq(1)').text()).toEqual(choices[1]);
  144. expect(editor.find('tbody td:eq(2)').text()).toEqual(choices[2]);
  145. expect(editor.find('tbody td:eq(3)').text()).toEqual(choices[3]);
  146. expect(editor.find('tbody td:eq(4)').text()).toEqual(choices[4]);
  147. done();
  148. }, 200);
  149. });
  150. it('should display given choices (async function)', (done) => {
  151. const asyncSources = jasmine.createSpy('asyncSources');
  152. asyncSources.and.callFake((process) => {
  153. process(choices);
  154. });
  155. handsontable({
  156. columns: [
  157. {
  158. editor: 'autocomplete',
  159. source(query, process) {
  160. setTimeout(() => {
  161. asyncSources(process);
  162. }, 0);
  163. }
  164. }
  165. ]
  166. });
  167. selectCell(0, 0);
  168. const editor = $('.autocompleteEditor');
  169. keyDownUp('enter');
  170. setTimeout(() => {
  171. expect(asyncSources.calls.count()).toEqual(1);
  172. expect(editor.find('tbody td:eq(0)').text()).toEqual(choices[0]);
  173. expect(editor.find('tbody td:eq(1)').text()).toEqual(choices[1]);
  174. expect(editor.find('tbody td:eq(2)').text()).toEqual(choices[2]);
  175. expect(editor.find('tbody td:eq(3)').text()).toEqual(choices[3]);
  176. expect(editor.find('tbody td:eq(4)').text()).toEqual(choices[4]);
  177. done();
  178. }, 200);
  179. });
  180. it('should NOT update choices list, after cursor leaves and enters the list (#1330)', (done) => {
  181. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'updateChoicesList').and.callThrough();
  182. const updateChoicesList = Handsontable.editors.AutocompleteEditor.prototype.updateChoicesList;
  183. const hot = handsontable({
  184. columns: [
  185. {
  186. editor: 'autocomplete',
  187. source: choices
  188. }
  189. ]
  190. });
  191. selectCell(0, 0);
  192. const editor = hot.getActiveEditor();
  193. keyDownUp('enter');
  194. setTimeout(() => {
  195. updateChoicesList.calls.reset();
  196. $(editor.htContainer).find('.htCore tr:eq(0) td:eq(0)').mouseenter();
  197. $(editor.htContainer).find('.htCore tr:eq(0) td:eq(0)').mouseleave();
  198. $(editor.htContainer).find('.htCore tr:eq(0) td:eq(0)').mouseenter();
  199. }, 200);
  200. setTimeout(() => {
  201. expect(updateChoicesList).not.toHaveBeenCalled();
  202. done();
  203. }, 300);
  204. });
  205. it('should update choices list exactly once after a key is pressed (#1330)', (done) => {
  206. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'updateChoicesList').and.callThrough();
  207. const updateChoicesList = Handsontable.editors.AutocompleteEditor.prototype.updateChoicesList;
  208. const hot = handsontable({
  209. columns: [
  210. {
  211. editor: 'autocomplete',
  212. source: choices
  213. }
  214. ]
  215. });
  216. selectCell(0, 0);
  217. const editor = hot.getActiveEditor();
  218. updateChoicesList.calls.reset();
  219. keyDownUp('enter');
  220. setTimeout(() => {
  221. updateChoicesList.calls.reset();
  222. editor.TEXTAREA.value = 'red';
  223. $(editor.TEXTAREA).simulate('keydown', {
  224. keyCode: 'd'.charCodeAt(0)
  225. });
  226. }, 200);
  227. setTimeout(() => {
  228. expect(updateChoicesList.calls.count()).toEqual(1);
  229. done();
  230. }, 100);
  231. });
  232. it('should not initialize the dropdown with unneeded scrollbars (scrollbar causing a scrollbar issue)', (done) => {
  233. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'updateChoicesList').and.callThrough();
  234. const updateChoicesList = Handsontable.editors.AutocompleteEditor.prototype.updateChoicesList;
  235. const hot = handsontable({
  236. data: [
  237. [
  238. 'blue'
  239. ],
  240. [],
  241. [],
  242. []
  243. ],
  244. columns: [
  245. {
  246. editor: 'autocomplete',
  247. source: choices
  248. }
  249. ]
  250. });
  251. selectCell(0, 0);
  252. const editor = hot.getActiveEditor();
  253. updateChoicesList.calls.reset();
  254. keyDownUp('enter');
  255. setTimeout(() => {
  256. expect(editor.htContainer.scrollWidth).toEqual(editor.htContainer.clientWidth);
  257. done();
  258. }, 200);
  259. });
  260. it('autocomplete list should have textarea dimensions', (done) => {
  261. const syncSources = jasmine.createSpy('syncSources');
  262. syncSources.and.callFake((query, process) => {
  263. process(choices);
  264. });
  265. handsontable({
  266. colWidths: [200],
  267. columns: [
  268. {
  269. editor: 'autocomplete',
  270. source: syncSources
  271. }
  272. ]
  273. });
  274. selectCell(0, 0);
  275. const editor = $('.handsontableInputHolder');
  276. syncSources.calls.reset();
  277. keyDownUp('enter');
  278. setTimeout(() => {
  279. // -2 for transparent borders
  280. expect(editor.find('.autocompleteEditor .htCore td').width()).toEqual(editor.find('.handsontableInput').width() - 2);
  281. expect(editor.find('.autocompleteEditor .htCore td').width()).toBeGreaterThan(187);
  282. done();
  283. }, 200);
  284. });
  285. it('autocomplete list should have the suggestion table dimensions, when trimDropdown option is set to false', (done) => {
  286. const syncSources = jasmine.createSpy('syncSources');
  287. syncSources.and.callFake((query, process) => {
  288. process(['long text', 'even longer text', 'extremely long text in the suggestion list', 'short text', 'text', 'another', 'yellow', 'black']);
  289. });
  290. handsontable({
  291. colWidths: [200],
  292. columns: [
  293. {
  294. editor: 'autocomplete',
  295. source: syncSources
  296. }
  297. ],
  298. trimDropdown: false,
  299. });
  300. selectCell(0, 0);
  301. const editor = $('.handsontableInputHolder');
  302. syncSources.calls.reset();
  303. keyDownUp('enter');
  304. setTimeout(() => {
  305. expect(editor.find('.autocompleteEditor .htCore td').eq(0).width()).toBeGreaterThan(editor.find('.handsontableInput').width());
  306. done();
  307. }, 200);
  308. });
  309. // TODO: This test never properly tests the case of refreshing editor after re-render the table. Previously this
  310. // test passes because sleep timeout was small enough to read the valid width before the editor element was resized.
  311. // Related issue #5103
  312. xit('autocomplete textarea should have cell dimensions (after render)', async() => {
  313. const data = [
  314. ['a', 'b'],
  315. ['c', 'd']
  316. ];
  317. handsontable({
  318. data,
  319. minRows: 4,
  320. minCols: 4,
  321. minSpareRows: 4,
  322. minSpareCols: 4,
  323. cells() {
  324. return {
  325. type: Handsontable.AutocompleteCell
  326. };
  327. }
  328. });
  329. selectCell(1, 1);
  330. keyDownUp('enter');
  331. await sleep(10);
  332. data[1][1] = 'dddddddddddddddddddd';
  333. render();
  334. await sleep(10);
  335. const $td = spec().$container.find('.htCore tbody tr:eq(1) td:eq(1)');
  336. expect(autocompleteEditor().width()).toEqual($td.width());
  337. });
  338. it('should invoke beginEditing only once after doubleclicking on a cell (#1011)', () => {
  339. const hot = handsontable({
  340. columns: [
  341. {},
  342. {},
  343. {
  344. type: 'autocomplete',
  345. source: choices
  346. }
  347. ]
  348. });
  349. selectCell(0, 2);
  350. spyOn(hot.getActiveEditor(), 'beginEditing');
  351. expect(hot.getActiveEditor().beginEditing.calls.count()).toBe(0);
  352. mouseDoubleClick(getCell(0, 2));
  353. expect(hot.getActiveEditor().beginEditing.calls.count()).toBe(1);
  354. mouseDoubleClick(getCell(1, 2));
  355. expect(hot.getActiveEditor().beginEditing.calls.count()).toBe(2);
  356. mouseDoubleClick(getCell(2, 2));
  357. expect(hot.getActiveEditor().beginEditing.calls.count()).toBe(3);
  358. });
  359. it('should not display all the choices from a long source list and not leave any unused space in the dropdown', async() => {
  360. handsontable({
  361. columns: [
  362. {
  363. type: 'autocomplete',
  364. source: [
  365. 'Acura', 'Audi', 'BMW', 'Buick', 'Cadillac', 'Chevrolet', 'Chrysler', 'Citroen', 'Dodge', 'Eagle', 'Ferrari',
  366. 'Ford', 'General Motors', 'GMC', 'Honda', 'Hummer', 'Hyundai', 'Infiniti', 'Isuzu', 'Jaguar', 'Jeep', 'Kia',
  367. 'Lamborghini', 'Land Rover', 'Lexus', 'Lincoln', 'Lotus', 'Mazda', 'Mercedes-Benz', 'Mercury', 'Mitsubishi',
  368. 'Nissan', 'Oldsmobile', 'Peugeot', 'Pontiac', 'Porsche', 'Regal', 'Renault', 'Saab', 'Saturn', 'Seat', 'Skoda',
  369. 'Subaru', 'Suzuki', 'Toyota', 'Volkswagen', 'Volvo']
  370. }
  371. ]
  372. });
  373. selectCell(0, 0);
  374. keyDownUp('enter');
  375. const $autocomplete = autocomplete();
  376. const $autocompleteHolder = $autocomplete.find('.ht_master .wtHolder').first();
  377. await sleep(100);
  378. expect($autocomplete.find('td').first().text()).toEqual('Acura');
  379. $autocompleteHolder.scrollTop($autocompleteHolder[0].scrollHeight);
  380. await sleep(100);
  381. expect($autocomplete.find('td').last().text()).toEqual('Volvo');
  382. });
  383. it('should display the choices, regardless if they\'re declared as string or numeric', (done) => {
  384. handsontable({
  385. columns: [
  386. {
  387. editor: 'autocomplete',
  388. source: ['1', '2', 3, '4', 5, 6]
  389. }
  390. ]
  391. });
  392. selectCell(0, 0);
  393. const editor = $('.autocompleteEditor');
  394. keyDownUp('enter');
  395. setTimeout(() => {
  396. expect(editor.find('tbody td:eq(0)').text()).toEqual('1');
  397. expect(editor.find('tbody td:eq(1)').text()).toEqual('2');
  398. expect(editor.find('tbody td:eq(2)').text()).toEqual('3');
  399. expect(editor.find('tbody td:eq(3)').text()).toEqual('4');
  400. expect(editor.find('tbody td:eq(4)').text()).toEqual('5');
  401. expect(editor.find('tbody td:eq(5)').text()).toEqual('6');
  402. done();
  403. }, 100);
  404. });
  405. it('should display the choices, regardless if they\'re declared as string or numeric, when data is present', (done) => {
  406. handsontable({
  407. data: Handsontable.helper.createSpreadsheetData(10, 1),
  408. columns: [
  409. {
  410. editor: 'autocomplete',
  411. source: ['1', '2', 3, '4', 5, 6]
  412. }
  413. ]
  414. });
  415. selectCell(0, 0);
  416. keyDownUp('backspace');
  417. const editor = $('.autocompleteEditor');
  418. keyDownUp('enter');
  419. setTimeout(() => {
  420. expect(editor.find('tbody td:eq(0)').text()).toEqual('1');
  421. expect(editor.find('tbody td:eq(1)').text()).toEqual('2');
  422. expect(editor.find('tbody td:eq(2)').text()).toEqual('3');
  423. expect(editor.find('tbody td:eq(3)').text()).toEqual('4');
  424. expect(editor.find('tbody td:eq(4)').text()).toEqual('5');
  425. expect(editor.find('tbody td:eq(5)').text()).toEqual('6');
  426. done();
  427. }, 100);
  428. });
  429. it('should display the dropdown above the editor, when there is not enough space below the cell AND there is more space above the cell', (done) => {
  430. handsontable({
  431. data: Handsontable.helper.createSpreadsheetData(30, 30),
  432. columns: [
  433. {
  434. editor: 'autocomplete',
  435. source: choices
  436. }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
  437. ],
  438. width: 400,
  439. height: 400
  440. });
  441. setDataAtCell(29, 0, '');
  442. selectCell(29, 0);
  443. mouseDoubleClick($(getCell(29, 0)));
  444. setTimeout(() => {
  445. const autocompleteEditor = $('.autocompleteEditor');
  446. expect(autocompleteEditor.css('position')).toEqual('absolute');
  447. expect(autocompleteEditor.css('top')).toEqual(`${(-1) * autocompleteEditor.height()}px`);
  448. done();
  449. }, 200);
  450. });
  451. it('should flip the dropdown upwards when there is no more room left below the cell after filtering the choice list', async() => {
  452. const hot = handsontable({
  453. data: Handsontable.helper.createSpreadsheetData(30, 30),
  454. columns: [
  455. {
  456. editor: 'autocomplete',
  457. source: choices
  458. }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}
  459. ],
  460. width: 400,
  461. height: 400
  462. });
  463. setDataAtCell(26, 0, 'b');
  464. selectCell(26, 0);
  465. hot.view.wt.wtTable.holder.scrollTop = 999;
  466. mouseDoubleClick($(getCell(26, 0)));
  467. const autocompleteEditor = $('.autocompleteEditor');
  468. await sleep(100);
  469. expect(autocompleteEditor.css('position')).toEqual('relative');
  470. autocompleteEditor.siblings('textarea').first().val('');
  471. keyDownUp('backspace');
  472. await sleep(100);
  473. expect(autocompleteEditor.css('position')).toEqual('absolute');
  474. expect(autocompleteEditor.css('top')).toEqual(`${(-1) * autocompleteEditor.height()}px`);
  475. });
  476. });
  477. describe('closing editor', () => {
  478. it('should destroy editor when value change with mouse click on suggestion', (done) => {
  479. const syncSources = jasmine.createSpy('syncSources');
  480. syncSources.and.callFake((query, process) => {
  481. process(choices);
  482. });
  483. handsontable({
  484. columns: [
  485. {
  486. editor: 'autocomplete',
  487. source: syncSources
  488. }
  489. ]
  490. });
  491. selectCell(0, 0);
  492. keyDownUp('enter');
  493. setTimeout(() => {
  494. autocomplete().find('tbody td:eq(3)').simulate('mousedown');
  495. expect(getDataAtCell(0, 0)).toEqual('green');
  496. done();
  497. }, 200);
  498. });
  499. it('should not change value type from `numeric` to `string` after mouse click suggestion - ' +
  500. 'test no. 1 #4143', (done) => {
  501. handsontable({
  502. columns: [
  503. {
  504. editor: 'autocomplete',
  505. source: [1, 2, 3, 4, 5, 11, 14]
  506. }
  507. ]
  508. });
  509. selectCell(0, 0);
  510. keyDownUp('enter');
  511. setTimeout(() => {
  512. autocomplete().find('tbody td:eq(0)').simulate('mousedown');
  513. expect(typeof getDataAtCell(0, 0)).toEqual('number');
  514. done();
  515. }, 200);
  516. });
  517. it('should not change value type from `numeric` to `string` after mouse click on suggestion - ' +
  518. 'test no. 2 #4143', (done) => {
  519. const syncSources = jasmine.createSpy('syncSources');
  520. const source = [1, 2, 3, 4, 5, 11, 14];
  521. syncSources.and.callFake((query, process) => {
  522. process(source);
  523. });
  524. handsontable({
  525. columns: [
  526. {
  527. editor: 'autocomplete',
  528. source: syncSources
  529. }
  530. ]
  531. });
  532. selectCell(0, 0);
  533. keyDownUp('enter');
  534. setTimeout(() => {
  535. autocomplete().find('tbody td:eq(0)').simulate('mousedown');
  536. expect(typeof getDataAtCell(0, 0)).toEqual('number');
  537. done();
  538. }, 200);
  539. });
  540. it('should call `afterChange` hook with proper value types - test no. 1 #4143', (done) => {
  541. let changesInside;
  542. const afterChange = (changes, source) => {
  543. if (source !== 'loadData') {
  544. changesInside = changes;
  545. }
  546. };
  547. handsontable({
  548. columns: [
  549. {
  550. editor: 'autocomplete',
  551. source: [1, 2, 3, 4, 5, 11, 14]
  552. }
  553. ],
  554. afterChange
  555. });
  556. selectCell(0, 0);
  557. keyDownUp('enter');
  558. setTimeout(() => {
  559. autocomplete().find('tbody td:eq(1)').simulate('mousedown');
  560. expect(changesInside[0]).toEqual([0, 0, null, 2]);
  561. done();
  562. }, 200);
  563. });
  564. it('should call `afterChange` hook with proper value types - test no. 2 #4143', (done) => {
  565. let changesInside;
  566. const afterChange = (changes, source) => {
  567. if (source !== 'loadData') {
  568. changesInside = changes;
  569. }
  570. };
  571. const syncSources = jasmine.createSpy('syncSources');
  572. const source = [1, 2, 3, 4, 5, 11, 14];
  573. syncSources.and.callFake((query, process) => {
  574. process(source);
  575. });
  576. handsontable({
  577. columns: [
  578. {
  579. editor: 'autocomplete',
  580. source: syncSources
  581. }
  582. ],
  583. afterChange
  584. });
  585. selectCell(0, 0);
  586. keyDownUp('enter');
  587. setTimeout(() => {
  588. autocomplete().find('tbody td:eq(1)').simulate('mousedown');
  589. expect(changesInside[0]).toEqual([0, 0, null, 2]);
  590. done();
  591. }, 200);
  592. });
  593. it('should not change value type from `numeric` to `string` when written down value from set of suggestions #4143', (done) => {
  594. const syncSources = jasmine.createSpy('syncSources');
  595. const source = [1, 2, 3, 4, 5, 11, 14];
  596. syncSources.and.callFake((query, process) => {
  597. process(source);
  598. });
  599. handsontable({
  600. columns: [
  601. {
  602. editor: 'autocomplete',
  603. source: syncSources
  604. }
  605. ]
  606. });
  607. selectCell(0, 0);
  608. keyDownUp('enter');
  609. keyDownUp('backspace');
  610. document.activeElement.value = '1';
  611. $(document.activeElement).simulate('keyup');
  612. setTimeout(() => {
  613. keyDownUp('enter');
  614. expect(getDataAtCell(0, 0)).toEqual(1);
  615. done();
  616. }, 200);
  617. });
  618. it('should destroy editor when value change with Enter on suggestion', (done) => {
  619. const syncSources = jasmine.createSpy('syncSources');
  620. syncSources.and.callFake((query, process) => {
  621. process(choices);
  622. });
  623. handsontable({
  624. columns: [
  625. {
  626. editor: 'autocomplete',
  627. source: syncSources
  628. }
  629. ]
  630. });
  631. selectCell(0, 0);
  632. keyDownUp('enter');
  633. setTimeout(() => {
  634. keyDownUp('arrow_down');
  635. keyDownUp('arrow_down');
  636. keyDownUp('arrow_down');
  637. keyDownUp('arrow_down');
  638. keyDownUp('enter');
  639. expect(getDataAtCell(0, 0)).toEqual('green');
  640. done();
  641. }, 200);
  642. });
  643. it('should destroy editor when pressed Enter then Esc', async() => {
  644. const syncSources = jasmine.createSpy('syncSources');
  645. syncSources.and.callFake((query, process) => {
  646. process(choices);
  647. });
  648. handsontable({
  649. columns: [
  650. {
  651. editor: 'autocomplete',
  652. source: syncSources
  653. }
  654. ]
  655. });
  656. selectCell(0, 0);
  657. keyDownUp('enter');
  658. await sleep(200);
  659. expect(isEditorVisible(autocompleteEditor())).toBe(true);
  660. keyDownUp('esc');
  661. expect(isEditorVisible(autocompleteEditor())).toBe(false);
  662. });
  663. it('should destroy editor when mouse double clicked then Esc', async() => {
  664. const syncSources = jasmine.createSpy('syncSources');
  665. syncSources.and.callFake((query, process) => {
  666. process(choices);
  667. });
  668. handsontable({
  669. columns: [
  670. {
  671. editor: 'autocomplete',
  672. source: syncSources
  673. }
  674. ]
  675. });
  676. selectCell(0, 0);
  677. mouseDoubleClick(getCell(0, 0));
  678. await sleep(200);
  679. expect(isEditorVisible(autocompleteEditor())).toBe(true);
  680. keyDownUp('esc');
  681. expect(isEditorVisible(autocompleteEditor())).toBe(false);
  682. });
  683. it('cancel editing (Esc) should restore the previous value', (done) => {
  684. const syncSources = jasmine.createSpy('syncSources');
  685. syncSources.and.callFake((query, process) => {
  686. process(choices);
  687. });
  688. handsontable({
  689. columns: [
  690. {
  691. editor: 'autocomplete',
  692. source: syncSources
  693. }
  694. ]
  695. });
  696. setDataAtCell(0, 0, 'black');
  697. selectCell(0, 0);
  698. keyDownUp('enter');
  699. setTimeout(() => {
  700. autocomplete().siblings('.handsontableInput').val('ye');
  701. keyDownUp(69); // e
  702. keyDownUp('esc');
  703. expect(getDataAtCell(0, 0)).toEqual('black');
  704. done();
  705. }, 200);
  706. });
  707. it('should destroy editor when clicked outside the table', async() => {
  708. const syncSources = jasmine.createSpy('syncSources');
  709. syncSources.and.callFake((query, process) => {
  710. process(choices);
  711. });
  712. handsontable({
  713. columns: [
  714. {
  715. editor: 'autocomplete',
  716. source: syncSources
  717. }
  718. ]
  719. });
  720. selectCell(0, 0);
  721. mouseDoubleClick(getCell(0, 0));
  722. await sleep(200);
  723. expect(isEditorVisible(autocompleteEditor())).toBe(true);
  724. $('body').simulate('mousedown');
  725. expect(isEditorVisible(autocompleteEditor())).toBe(false);
  726. });
  727. it('should show fillHandle element again after close editor', (done) => {
  728. const syncSources = jasmine.createSpy('syncSources');
  729. syncSources.plan = function(query, process) {
  730. process(choices.filter(choice => choice.indexOf(query) !== -1));
  731. };
  732. handsontable({
  733. columns: [
  734. {
  735. type: 'autocomplete',
  736. source: syncSources,
  737. strict: false
  738. },
  739. {}
  740. ]
  741. });
  742. selectCell(1, 0);
  743. keyDownUp('x'); // Trigger quick edit mode
  744. keyDownUp('enter');
  745. setTimeout(() => {
  746. expect($('#testContainer.handsontable > .handsontable .wtBorder.current.corner:visible').length).toEqual(1);
  747. done();
  748. }, 200);
  749. });
  750. });
  751. describe('non strict mode', () => {
  752. it('should allow any value in non strict mode (close editor with ENTER)', (done) => {
  753. const syncSources = jasmine.createSpy('syncSources');
  754. syncSources.and.callFake((query, process) => {
  755. process(choices);
  756. });
  757. handsontable({
  758. columns: [
  759. {
  760. editor: 'autocomplete',
  761. source: syncSources
  762. }
  763. ]
  764. });
  765. selectCell(0, 0);
  766. keyDownUp('enter');
  767. setTimeout(() => {
  768. const editor = $('.handsontableInput');
  769. editor.val('foo');
  770. keyDownUp('enter');
  771. expect(getDataAtCell(0, 0)).toEqual('foo');
  772. done();
  773. }, 200);
  774. });
  775. it('should allow any value in non strict mode (close editor by clicking on table)', (done) => {
  776. const syncSources = jasmine.createSpy('syncSources');
  777. syncSources.and.callFake((query, process) => {
  778. process(choices);
  779. });
  780. handsontable({
  781. columns: [
  782. {
  783. editor: 'autocomplete',
  784. source: syncSources
  785. }
  786. ]
  787. });
  788. selectCell(0, 0);
  789. keyDownUp('enter');
  790. setTimeout(() => {
  791. const editor = $('.handsontableInput');
  792. editor.val('foo');
  793. spec().$container.find('tbody tr:eq(1) td:eq(0)').simulate('mousedown');
  794. expect(getDataAtCell(0, 0)).toEqual('foo');
  795. done();
  796. }, 200);
  797. });
  798. it('should save the value from textarea after hitting ENTER', (done) => {
  799. const syncSources = jasmine.createSpy('syncSources');
  800. syncSources.and.callFake((query, process) => {
  801. process(choices.filter(choice => choice.indexOf(query) !== -1));
  802. });
  803. const hot = handsontable({
  804. columns: [
  805. {
  806. editor: 'autocomplete',
  807. source: syncSources
  808. }
  809. ]
  810. });
  811. selectCell(0, 0);
  812. const editorInput = $('.handsontableInput');
  813. expect(getDataAtCell(0, 0)).toBeNull();
  814. keyDownUp('enter');
  815. setTimeout(() => {
  816. syncSources.calls.reset();
  817. editorInput.val('b');
  818. keyDownUp('b'.charCodeAt(0));
  819. }, 200);
  820. setTimeout(() => {
  821. const ac = hot.getActiveEditor();
  822. const innerHot = ac.htEditor;
  823. expect(innerHot.getData()).toEqual([
  824. ['blue'],
  825. ['black']
  826. ]);
  827. const selected = innerHot.getSelected();
  828. expect(selected).toBeUndefined();
  829. keyDownUp('enter');
  830. expect(getDataAtCell(0, 0)).toEqual('b');
  831. done();
  832. }, 400);
  833. });
  834. });
  835. describe('strict mode', () => {
  836. it('strict mode should NOT use value if it DOES NOT match the list (sync reponse is empty)', (done) => {
  837. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  838. const onAfterChange = jasmine.createSpy('onAfterChange');
  839. const syncSources = jasmine.createSpy('syncSources');
  840. syncSources.and.callFake((query, process) => {
  841. process([]); // hardcoded empty result
  842. });
  843. handsontable({
  844. data: [
  845. ['one', 'two'],
  846. ['three', 'four']
  847. ],
  848. columns: [
  849. {
  850. type: 'autocomplete',
  851. source: syncSources,
  852. allowInvalid: false,
  853. strict: true
  854. },
  855. {}
  856. ],
  857. afterValidate: onAfterValidate,
  858. afterChange: onAfterChange
  859. });
  860. setDataAtCell(0, 0, 'unexistent');
  861. setTimeout(() => {
  862. expect(getData()).toEqual([
  863. ['one', 'two'],
  864. ['three', 'four']
  865. ]);
  866. expect(syncSources.calls.count()).toEqual(1);
  867. expect(onAfterValidate.calls.count()).toEqual(1);
  868. expect(onAfterChange.calls.count()).toEqual(1); // 1 for loadData (it is not called after failed edit)
  869. done();
  870. }, 200);
  871. });
  872. it('strict mode should use value if it DOES match the list (sync reponse is not empty)', (done) => {
  873. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  874. const onAfterChange = jasmine.createSpy('onAfterChange');
  875. const syncSources = jasmine.createSpy('asyncSources');
  876. syncSources.and.callFake((query, process) => {
  877. process(choices); // hardcoded empty result
  878. });
  879. handsontable({
  880. data: [
  881. ['one', 'two'],
  882. ['three', 'four']
  883. ],
  884. columns: [
  885. {
  886. type: 'autocomplete',
  887. source: syncSources,
  888. allowInvalid: false,
  889. strict: true
  890. },
  891. {}
  892. ],
  893. afterValidate: onAfterValidate,
  894. afterChange: onAfterChange
  895. });
  896. setDataAtCell(0, 0, 'yellow');
  897. setTimeout(() => {
  898. expect(getData()).toEqual([
  899. ['yellow', 'two'],
  900. ['three', 'four']
  901. ]);
  902. expect(syncSources.calls.count()).toEqual(1);
  903. expect(onAfterValidate.calls.count()).toEqual(1);
  904. expect(onAfterChange.calls.count()).toEqual(2); // 1 for loadData and 1 for setDataAtCell
  905. done();
  906. }, 200);
  907. });
  908. it('strict mode should NOT use value if it DOES NOT match the list (async reponse is empty)', (done) => {
  909. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  910. const onAfterChange = jasmine.createSpy('onAfterChange');
  911. const asyncSources = jasmine.createSpy('asyncSources');
  912. asyncSources.and.callFake((query, process) => {
  913. setTimeout(() => {
  914. process([]); // hardcoded empty result
  915. });
  916. });
  917. handsontable({
  918. data: [
  919. ['one', 'two'],
  920. ['three', 'four']
  921. ],
  922. columns: [
  923. {
  924. type: 'autocomplete',
  925. source: asyncSources,
  926. allowInvalid: false,
  927. strict: true
  928. },
  929. {}
  930. ],
  931. afterValidate: onAfterValidate,
  932. afterChange: onAfterChange
  933. });
  934. setDataAtCell(0, 0, 'unexistent');
  935. setTimeout(() => {
  936. expect(getData()).toEqual([
  937. ['one', 'two'],
  938. ['three', 'four']
  939. ]);
  940. expect(asyncSources.calls.count()).toEqual(1);
  941. expect(onAfterValidate.calls.count()).toEqual(1);
  942. expect(onAfterChange.calls.count()).toEqual(1); // 1 for loadData (it is not called after failed edit)
  943. done();
  944. }, 200);
  945. });
  946. it('strict mode should use value if it DOES match the list (async reponse is not empty)', (done) => {
  947. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  948. const onAfterChange = jasmine.createSpy('onAfterChange');
  949. const asyncSources = jasmine.createSpy('asyncSources');
  950. asyncSources.and.callFake((query, process) => {
  951. setTimeout(() => {
  952. process(choices); // hardcoded empty result
  953. });
  954. });
  955. handsontable({
  956. data: [
  957. ['one', 'two'],
  958. ['three', 'four']
  959. ],
  960. columns: [
  961. {
  962. type: 'autocomplete',
  963. source: asyncSources,
  964. allowInvalid: false,
  965. strict: true
  966. },
  967. {}
  968. ],
  969. afterValidate: onAfterValidate,
  970. afterChange: onAfterChange
  971. });
  972. setDataAtCell(0, 0, 'yellow');
  973. setTimeout(() => {
  974. expect(getData()).toEqual([
  975. ['yellow', 'two'],
  976. ['three', 'four']
  977. ]);
  978. expect(asyncSources.calls.count()).toEqual(1);
  979. expect(onAfterValidate.calls.count()).toEqual(1);
  980. expect(onAfterChange.calls.count()).toEqual(2); // 1 for loadData and 1 for setDataAtCell
  981. done();
  982. }, 200);
  983. });
  984. it('strict mode mark value as invalid if it DOES NOT match the list (sync reponse is empty)', (done) => {
  985. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  986. const onAfterChange = jasmine.createSpy('onAfterChange');
  987. const syncSources = jasmine.createSpy('syncSources');
  988. syncSources.and.callFake((query, process) => {
  989. process([]); // hardcoded empty result
  990. });
  991. handsontable({
  992. data: [
  993. ['one', 'two'],
  994. ['three', 'four']
  995. ],
  996. columns: [
  997. {
  998. type: 'autocomplete',
  999. source: syncSources,
  1000. allowInvalid: true,
  1001. strict: true
  1002. },
  1003. {}
  1004. ],
  1005. afterValidate: onAfterValidate,
  1006. afterChange: onAfterChange
  1007. });
  1008. expect(getCellMeta(0, 0).valid).not.toBe(false);
  1009. expect($(getCell(0, 0)).hasClass('htInvalid')).toBe(false);
  1010. setDataAtCell(0, 0, 'unexistent');
  1011. setTimeout(() => {
  1012. expect(getData()).toEqual([
  1013. ['unexistent', 'two'],
  1014. ['three', 'four']
  1015. ]);
  1016. expect(getCellMeta(0, 0).valid).toBe(false);
  1017. expect($(getCell(0, 0)).hasClass('htInvalid')).toBe(true);
  1018. done();
  1019. }, 200);
  1020. });
  1021. it('should select the best matching option after hitting ENTER', (done) => {
  1022. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  1023. const syncSources = jasmine.createSpy('syncSources');
  1024. syncSources.and.callFake((query, process) => {
  1025. process(choices.filter(choice => choice.indexOf(query) !== -1));
  1026. });
  1027. const hot = handsontable({
  1028. columns: [
  1029. {
  1030. editor: 'autocomplete',
  1031. source: syncSources,
  1032. strict: true
  1033. }
  1034. ],
  1035. afterValidate: onAfterValidate
  1036. });
  1037. selectCell(0, 0);
  1038. const editorInput = $('.handsontableInput');
  1039. expect(getDataAtCell(0, 0)).toBeNull();
  1040. keyDownUp('enter');
  1041. setTimeout(() => {
  1042. syncSources.calls.reset();
  1043. editorInput.val('b');
  1044. keyDownUp('b'.charCodeAt(0));
  1045. }, 200);
  1046. setTimeout(() => {
  1047. const ac = hot.getActiveEditor();
  1048. const innerHot = ac.htEditor;
  1049. expect(innerHot.getData()).toEqual([
  1050. ['blue'],
  1051. ['black']
  1052. ]);
  1053. const selected = innerHot.getSelected()[0];
  1054. const selectedData = innerHot.getDataAtCell(selected[0], selected[1]);
  1055. expect(selectedData).toEqual('blue');
  1056. onAfterValidate.calls.reset();
  1057. keyDownUp('enter');
  1058. }, 400);
  1059. setTimeout(() => {
  1060. expect(getDataAtCell(0, 0)).toEqual('blue');
  1061. done();
  1062. }, 600);
  1063. });
  1064. it('should select the best matching option after hitting TAB', (done) => {
  1065. const onAfterValidate = jasmine.createSpy('onAfterValidate');
  1066. const syncSources = jasmine.createSpy('syncSources');
  1067. syncSources.and.callFake((query, process) => {
  1068. process(choices.filter(choice => choice.indexOf(query) !== -1));
  1069. });
  1070. const hot = handsontable({
  1071. columns: [
  1072. {
  1073. editor: 'autocomplete',
  1074. source: syncSources,
  1075. strict: true
  1076. }
  1077. ],
  1078. afterValidate: onAfterValidate
  1079. });
  1080. selectCell(0, 0);
  1081. const editorInput = $('.handsontableInput');
  1082. expect(getDataAtCell(0, 0)).toBeNull();
  1083. keyDownUp('enter');
  1084. setTimeout(() => {
  1085. syncSources.calls.reset();
  1086. editorInput.val('b');
  1087. keyDownUp('b'.charCodeAt(0));
  1088. }, 200);
  1089. setTimeout(() => {
  1090. const ac = hot.getActiveEditor();
  1091. const innerHot = ac.htEditor;
  1092. expect(innerHot.getData()).toEqual([
  1093. ['blue'],
  1094. ['black']
  1095. ]);
  1096. const selected = innerHot.getSelected()[0];
  1097. const selectedData = innerHot.getDataAtCell(selected[0], selected[1]);
  1098. expect(selectedData).toEqual('blue');
  1099. onAfterValidate.calls.reset();
  1100. keyDownUp('tab');
  1101. }, 400);
  1102. setTimeout(() => {
  1103. expect(getDataAtCell(0, 0)).toEqual('blue');
  1104. done();
  1105. }, 600);
  1106. });
  1107. it('should mark list item corresponding to current cell value as selected', (done) => {
  1108. const syncSources = jasmine.createSpy('syncSources');
  1109. syncSources.and.callFake((query, process) => {
  1110. process(['red', 'dark-yellow', 'yellow', 'light-yellow', 'black']);
  1111. });
  1112. handsontable({
  1113. columns: [
  1114. {
  1115. editor: 'autocomplete',
  1116. source: syncSources,
  1117. strict: true
  1118. }
  1119. ],
  1120. data: [
  1121. ['yellow'],
  1122. ['red'],
  1123. ['blue']
  1124. ]
  1125. });
  1126. selectCell(0, 0);
  1127. keyDownUp('enter');
  1128. setTimeout(() => {
  1129. expect(autocomplete().find('.current').text()).toEqual(getDataAtCell(0, 0));
  1130. done();
  1131. }, 200);
  1132. });
  1133. });
  1134. describe('filtering', () => {
  1135. it('typing in textarea should filter the lookup list', (done) => {
  1136. const syncSources = jasmine.createSpy('syncSources');
  1137. syncSources.and.callFake((query, process) => {
  1138. process(choices.filter(choice => choice.indexOf(query) !== -1));
  1139. });
  1140. const hot = handsontable({
  1141. columns: [
  1142. {
  1143. editor: 'autocomplete',
  1144. source: syncSources
  1145. }
  1146. ]
  1147. });
  1148. selectCell(0, 0);
  1149. const editorInput = $('.handsontableInput');
  1150. expect(getDataAtCell(0, 0)).toBeNull();
  1151. keyDownUp('enter');
  1152. setTimeout(() => {
  1153. syncSources.calls.reset();
  1154. editorInput.val('e');
  1155. keyDownUp(69); // e
  1156. }, 200);
  1157. setTimeout(() => {
  1158. const ac = hot.getActiveEditor();
  1159. const innerHot = ac.htEditor;
  1160. expect(innerHot.getData()).toEqual([
  1161. ['red'],
  1162. ['yellow'],
  1163. ['green'],
  1164. ['blue'],
  1165. ['lime'],
  1166. ['white'],
  1167. ['olive'],
  1168. ['orange'],
  1169. ['purple']
  1170. ]);
  1171. syncSources.calls.reset();
  1172. editorInput.val('ed');
  1173. keyDownUp(68); // d
  1174. }, 400);
  1175. setTimeout(() => {
  1176. const ac = hot.getActiveEditor();
  1177. const innerHot = ac.htEditor;
  1178. expect(innerHot.getData()).toEqual([
  1179. ['red']
  1180. ]);
  1181. done();
  1182. }, 600);
  1183. });
  1184. it('default filtering should be case insensitive', (done) => {
  1185. const hot = handsontable({
  1186. columns: [
  1187. {
  1188. editor: 'autocomplete',
  1189. source: choices
  1190. }
  1191. ]
  1192. });
  1193. selectCell(0, 0);
  1194. const editorInput = $('.handsontableInput');
  1195. expect(getDataAtCell(0, 0)).toBeNull();
  1196. keyDownUp('enter');
  1197. editorInput.val('e');
  1198. keyDownUp(69); // e
  1199. setTimeout(() => {
  1200. const ac = hot.getActiveEditor();
  1201. const innerHot = ac.htEditor;
  1202. expect(innerHot.getData()).toEqual([
  1203. ['red'],
  1204. ['yellow'],
  1205. ['green'],
  1206. ['blue'],
  1207. ['lime'],
  1208. ['white'],
  1209. ['olive'],
  1210. ['orange'],
  1211. ['purple']
  1212. ]);
  1213. editorInput.val('e');
  1214. keyDownUp(69); // E (same as 'e')
  1215. }, 50);
  1216. setTimeout(() => {
  1217. const ac = hot.getActiveEditor();
  1218. const innerHot = ac.htEditor;
  1219. expect(innerHot.getData()).toEqual([
  1220. ['red'],
  1221. ['yellow'],
  1222. ['green'],
  1223. ['blue'],
  1224. ['lime'],
  1225. ['white'],
  1226. ['olive'],
  1227. ['orange'],
  1228. ['purple']
  1229. ]);
  1230. done();
  1231. }, 100);
  1232. });
  1233. it('default filtering should be case sensitive when filteringCaseSensitive is false', (done) => {
  1234. const hot = handsontable({
  1235. columns: [
  1236. {
  1237. editor: 'autocomplete',
  1238. source: choices,
  1239. filteringCaseSensitive: true
  1240. }
  1241. ]
  1242. });
  1243. selectCell(0, 0);
  1244. const editorInput = $('.handsontableInput');
  1245. expect(getDataAtCell(0, 0)).toBeNull();
  1246. keyDownUp('enter');
  1247. editorInput.val('e');
  1248. keyDownUp(69); // e
  1249. setTimeout(() => {
  1250. const ac = hot.getActiveEditor();
  1251. const innerHot = ac.htEditor;
  1252. expect(innerHot.getData()).toEqual([
  1253. ['red'],
  1254. ['yellow'],
  1255. ['green'],
  1256. ['blue'],
  1257. ['lime'],
  1258. ['white'],
  1259. ['olive'],
  1260. ['orange'],
  1261. ['purple']
  1262. ]);
  1263. editorInput.val('E');
  1264. keyDownUp(69); // E (same as 'e')
  1265. }, 50);
  1266. setTimeout(() => {
  1267. const ac = hot.getActiveEditor();
  1268. const innerHot = ac.htEditor;
  1269. expect(innerHot.getData()).toEqual([]);
  1270. expect(innerHot.getSourceData()).toEqual([]);
  1271. done();
  1272. }, 200);
  1273. });
  1274. it('typing in textarea should NOT filter the lookup list when filtering is disabled', (done) => {
  1275. const hot = handsontable({
  1276. columns: [
  1277. {
  1278. editor: 'autocomplete',
  1279. source: choices,
  1280. filter: false
  1281. }
  1282. ]
  1283. });
  1284. selectCell(0, 0);
  1285. const editorInput = $('.handsontableInput');
  1286. expect(getDataAtCell(0, 0)).toBeNull();
  1287. keyDownUp('enter');
  1288. setTimeout(() => {
  1289. editorInput.val('e');
  1290. keyDownUp('e'.charCodeAt(0)); // e
  1291. }, 20);
  1292. setTimeout(() => {
  1293. const ac = hot.getActiveEditor();
  1294. const innerHot = ac.htEditor;
  1295. expect(innerHot.getData()).toEqual(Handsontable.helper.pivot([choices]));
  1296. editorInput.val('ed');
  1297. keyDownUp('d'.charCodeAt(0)); // d
  1298. }, 40);
  1299. setTimeout(() => {
  1300. const ac = hot.getActiveEditor();
  1301. const innerHot = ac.htEditor;
  1302. expect(innerHot.getData()).toEqual(Handsontable.helper.pivot([choices]));
  1303. done();
  1304. }, 60);
  1305. });
  1306. it('typing in textarea should highlight the matching phrase', (done) => {
  1307. const choicesList = ['Male', 'Female'];
  1308. const syncSources = jasmine.createSpy('syncSources');
  1309. syncSources.and.callFake((query, process) => {
  1310. process(choicesList.filter(choice => choice.search(new RegExp(query, 'i')) !== -1));
  1311. });
  1312. const hot = handsontable({
  1313. columns: [
  1314. {
  1315. editor: 'autocomplete',
  1316. source: syncSources,
  1317. filter: false
  1318. }
  1319. ]
  1320. });
  1321. selectCell(0, 0);
  1322. const editorInput = $('.handsontableInput');
  1323. expect(getDataAtCell(0, 0)).toBeNull();
  1324. keyDownUp('enter');
  1325. setTimeout(() => {
  1326. syncSources.calls.reset();
  1327. editorInput.val('Male');
  1328. keyDownUp(69); // e
  1329. }, 200);
  1330. setTimeout(() => {
  1331. const ac = hot.getActiveEditor();
  1332. const innerHot = ac.htEditor;
  1333. const autocompleteList = $(innerHot.rootElement);
  1334. expect(autocompleteList.find('td:eq(0)').html()).toMatch(/<(strong|STRONG)>Male<\/(strong|STRONG)>/); // IE8 makes the tag names UPPERCASE
  1335. expect(autocompleteList.find('td:eq(1)').html()).toMatch(/Fe<(strong|STRONG)>male<\/(strong|STRONG)>/);
  1336. done();
  1337. }, 400);
  1338. });
  1339. it('text in textarea should not be interpreted as regexp', (done) => {
  1340. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'queryChoices').and.callThrough();
  1341. const queryChoices = Handsontable.editors.AutocompleteEditor.prototype.queryChoices;
  1342. const hot = handsontable({
  1343. columns: [
  1344. {
  1345. editor: 'autocomplete',
  1346. source: choices
  1347. }
  1348. ]
  1349. });
  1350. selectCell(0, 0);
  1351. const editorInput = $('.handsontableInput');
  1352. expect(getDataAtCell(0, 0)).toBeNull();
  1353. keyDownUp('enter');
  1354. setTimeout(() => {
  1355. queryChoices.calls.reset();
  1356. editorInput.val('yellow|red');
  1357. keyDownUp('d'.charCodeAt(0));
  1358. }, 200);
  1359. setTimeout(() => {
  1360. const ac = hot.getActiveEditor();
  1361. const innerHot = ac.htEditor;
  1362. expect(innerHot.getData().length).toEqual(0);
  1363. done();
  1364. }, 400);
  1365. });
  1366. it('text in textarea should not be interpreted as regexp when highlighting the matching phrase', (done) => {
  1367. const choicesList = ['Male', 'Female'];
  1368. const syncSources = jasmine.createSpy('syncSources');
  1369. syncSources.and.callFake((query, process) => {
  1370. process(choicesList.filter(choice => choice.search(new RegExp(query, 'i')) !== -1));
  1371. });
  1372. const hot = handsontable({
  1373. columns: [
  1374. {
  1375. editor: 'autocomplete',
  1376. source: syncSources,
  1377. filter: false
  1378. }
  1379. ]
  1380. });
  1381. selectCell(0, 0);
  1382. const editorInput = $('.handsontableInput');
  1383. expect(getDataAtCell(0, 0)).toBeNull();
  1384. keyDownUp('enter');
  1385. setTimeout(() => {
  1386. syncSources.calls.reset();
  1387. editorInput.val('M|F');
  1388. keyDownUp('F'.charCodeAt(0));
  1389. }, 200);
  1390. setTimeout(() => {
  1391. const ac = hot.getActiveEditor();
  1392. const innerHot = ac.htEditor;
  1393. const autocompleteList = $(innerHot.rootElement);
  1394. expect(autocompleteList.find('td:eq(0)').html()).toEqual('Male');
  1395. expect(autocompleteList.find('td:eq(1)').html()).toEqual('Female');
  1396. done();
  1397. }, 400);
  1398. });
  1399. it('should allow any value if filter === false and allowInvalid === true', (done) => {
  1400. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'queryChoices').and.callThrough();
  1401. const queryChoices = Handsontable.editors.AutocompleteEditor.prototype.queryChoices;
  1402. handsontable({
  1403. columns: [
  1404. {
  1405. editor: 'autocomplete',
  1406. source: choices,
  1407. filter: false,
  1408. strict: true,
  1409. allowInvalid: true
  1410. }
  1411. ]
  1412. });
  1413. selectCell(0, 0);
  1414. const editorInput = $('.handsontableInput');
  1415. expect(getDataAtCell(0, 0)).toBeNull();
  1416. keyDownUp('enter');
  1417. setTimeout(() => {
  1418. queryChoices.calls.reset();
  1419. editorInput.val('foobar');
  1420. keyDownUp(82); // r
  1421. }, 200);
  1422. setTimeout(() => {
  1423. keyDownUp(Handsontable.helper.KEY_CODES.ENTER);
  1424. expect(getDataAtCell(0, 0)).toEqual('foobar');
  1425. done();
  1426. }, 400);
  1427. });
  1428. it('typing in textarea should highlight best choice, if strict === true', (done) => {
  1429. const choicesList = ['Male', 'Female'];
  1430. const syncSources = jasmine.createSpy('syncSources');
  1431. syncSources.and.callFake((query, process) => {
  1432. process(choicesList.filter(choice => choice.search(new RegExp(query, 'i')) !== -1));
  1433. });
  1434. const hot = handsontable({
  1435. columns: [
  1436. {
  1437. editor: 'autocomplete',
  1438. source: syncSources,
  1439. filter: false,
  1440. strict: true
  1441. }
  1442. ]
  1443. });
  1444. selectCell(0, 0);
  1445. const editorInput = $('.handsontableInput');
  1446. expect(getDataAtCell(0, 0)).toBeNull();
  1447. keyDownUp('enter');
  1448. setTimeout(() => {
  1449. syncSources.calls.reset();
  1450. editorInput.val('e');
  1451. keyDownUp(69); // e
  1452. }, 200);
  1453. setTimeout(() => {
  1454. const ac = hot.getActiveEditor();
  1455. const innerHot = ac.htEditor;
  1456. expect(innerHot.getSelected()).toEqual([[1, 0, 1, 0]]);
  1457. done();
  1458. }, 400);
  1459. });
  1460. });
  1461. it('should restore the old value when hovered over a autocomplete menu item and then clicked outside of the table', (done) => {
  1462. const syncSources = jasmine.createSpy('syncSources');
  1463. syncSources.and.callFake((query, process) => {
  1464. process(choices);
  1465. });
  1466. handsontable({
  1467. columns: [
  1468. {
  1469. editor: 'autocomplete',
  1470. source: syncSources
  1471. }
  1472. ]
  1473. });
  1474. selectCell(0, 0);
  1475. expect(getDataAtCell(0, 0)).toBeNull();
  1476. keyDownUp('enter');
  1477. setTimeout(() => {
  1478. autocomplete().find('tbody td:eq(1)').simulate('mouseenter');
  1479. autocomplete().find('tbody td:eq(1)').simulate('mouseleave');
  1480. spec().$container.simulate('mousedown');
  1481. expect(getDataAtCell(0, 0)).toBeNull();
  1482. done();
  1483. }, 200);
  1484. });
  1485. it('should be able to use empty value ("")', (done) => {
  1486. const syncSources = jasmine.createSpy('syncSources');
  1487. syncSources.and.callFake((query, process) => {
  1488. process(['', 'BMW', 'Bentley']);
  1489. });
  1490. handsontable({
  1491. data: [
  1492. ['one', 'two'],
  1493. ['three', 'four']
  1494. ],
  1495. columns: [
  1496. {
  1497. editor: 'autocomplete',
  1498. source: syncSources,
  1499. filter: false
  1500. }
  1501. ]
  1502. });
  1503. selectCell(0, 0);
  1504. keyDownUp('enter');
  1505. setTimeout(() => {
  1506. expect(getDataAtCell(0, 0)).toEqual('one');
  1507. autocomplete().find('tbody td:eq(0)').simulate('mousedown');
  1508. expect(getDataAtCell(0, 0)).toEqual('');
  1509. done();
  1510. }, 200);
  1511. });
  1512. describe('allow html mode', () => {
  1513. it('should allow inject html items (async mode)', (done) => {
  1514. const hot = handsontable({
  1515. columns: [
  1516. {
  1517. type: 'autocomplete',
  1518. source(query, cb) {
  1519. cb(['<b>foo <span>zip</span></b>', '<i>bar</i>', '<strong>baz</strong>']);
  1520. },
  1521. allowHtml: true,
  1522. }
  1523. ]
  1524. });
  1525. selectCell(0, 0);
  1526. const editorInput = $('.handsontableInput');
  1527. expect(getDataAtCell(0, 0)).toBeNull();
  1528. keyDownUp('enter');
  1529. setTimeout(() => {
  1530. editorInput.val('b');
  1531. keyDownUp('b'.charCodeAt(0));
  1532. }, 200);
  1533. setTimeout(() => {
  1534. const ac = hot.getActiveEditor();
  1535. const innerHot = ac.htEditor;
  1536. expect(innerHot.getData()).toEqual([
  1537. ['<i>bar</i>'],
  1538. ['<strong>baz</strong>'],
  1539. ]);
  1540. editorInput.val('bar');
  1541. keyDownUp('a'.charCodeAt(0));
  1542. keyDownUp('r'.charCodeAt(0));
  1543. }, 400);
  1544. setTimeout(() => {
  1545. const ac = hot.getActiveEditor();
  1546. const innerHot = ac.htEditor;
  1547. expect(innerHot.getData()).toEqual([
  1548. ['<i>bar</i>']
  1549. ]);
  1550. keyDownUp('arrow_down');
  1551. keyDownUp('enter');
  1552. }, 600);
  1553. setTimeout(() => {
  1554. expect(getCell(0, 0).querySelector('i').textContent).toBe('bar');
  1555. done();
  1556. }, 700);
  1557. });
  1558. it('should allow inject html items (sync mode)', (done) => {
  1559. const hot = handsontable({
  1560. columns: [
  1561. {
  1562. type: 'autocomplete',
  1563. source: ['<b>foo <span>zip</span></b>', '<i>bar</i>', '<strong>baz</strong>'],
  1564. allowHtml: true,
  1565. }
  1566. ]
  1567. });
  1568. selectCell(0, 0);
  1569. const editorInput = $('.handsontableInput');
  1570. expect(getDataAtCell(0, 0)).toBeNull();
  1571. keyDownUp('enter');
  1572. setTimeout(() => {
  1573. editorInput.val('b');
  1574. keyDownUp('b'.charCodeAt(0));
  1575. }, 200);
  1576. setTimeout(() => {
  1577. const ac = hot.getActiveEditor();
  1578. const innerHot = ac.htEditor;
  1579. expect(innerHot.getData()).toEqual([
  1580. ['<i>bar</i>'],
  1581. ['<strong>baz</strong>'],
  1582. ]);
  1583. editorInput.val('bar');
  1584. keyDownUp('a'.charCodeAt(0));
  1585. keyDownUp('r'.charCodeAt(0));
  1586. }, 400);
  1587. setTimeout(() => {
  1588. const ac = hot.getActiveEditor();
  1589. const innerHot = ac.htEditor;
  1590. expect(innerHot.getData()).toEqual([
  1591. ['<i>bar</i>']
  1592. ]);
  1593. keyDownUp('arrow_down');
  1594. keyDownUp('enter');
  1595. }, 600);
  1596. setTimeout(() => {
  1597. expect(getCell(0, 0).querySelector('i').textContent).toBe('bar');
  1598. done();
  1599. }, 700);
  1600. });
  1601. });
  1602. describe('disallow html mode', () => {
  1603. it('should be disabled by default', () => {
  1604. const hot = handsontable({
  1605. columns: [
  1606. {
  1607. type: 'autocomplete',
  1608. source(query, cb) {
  1609. cb(['<b>foo <span>zip</span></b>', '<i>bar</i>', '<strong>baz</strong>']);
  1610. },
  1611. allowHtml: false,
  1612. }
  1613. ]
  1614. });
  1615. expect(hot.getCellMeta(0, 0).allowHtml).toBeFalsy();
  1616. });
  1617. it('should strip html from strings provided in source (async mode)', async() => {
  1618. const hot = handsontable({
  1619. columns: [
  1620. {
  1621. type: 'autocomplete',
  1622. source(query, cb) {
  1623. cb(['<b>foo <span>zip</span></b>', '<i>bar</i>', '<strong>baz</strong>']);
  1624. },
  1625. allowHtml: false,
  1626. }
  1627. ]
  1628. });
  1629. selectCell(0, 0);
  1630. const editorInput = $('.handsontableInput');
  1631. expect(getDataAtCell(0, 0)).toBeNull();
  1632. keyDownUp('enter');
  1633. await sleep(200);
  1634. editorInput.val('b');
  1635. keyDownUp('b'.charCodeAt(0));
  1636. await sleep(200);
  1637. {
  1638. const ac = hot.getActiveEditor();
  1639. const innerHot = ac.htEditor;
  1640. expect(innerHot.getData()).toEqual([
  1641. ['bar'],
  1642. ['baz'],
  1643. ]);
  1644. editorInput.val('bar');
  1645. keyDownUp('a'.charCodeAt(0));
  1646. keyDownUp('r'.charCodeAt(0));
  1647. }
  1648. await sleep(200);
  1649. {
  1650. const ac = hot.getActiveEditor();
  1651. const innerHot = ac.htEditor;
  1652. expect(innerHot.getData()).toEqual([
  1653. ['bar']
  1654. ]);
  1655. keyDownUp('arrow_down');
  1656. keyDownUp('enter');
  1657. }
  1658. await sleep(200);
  1659. expect(getCell(0, 0).querySelector('i')).toBeNull();
  1660. expect(getCell(0, 0).textContent).toMatch('bar');
  1661. });
  1662. it('should strip html from strings provided in source (sync mode)', async() => {
  1663. const hot = handsontable({
  1664. columns: [
  1665. {
  1666. type: 'autocomplete',
  1667. source: ['<b>foo <span>zip</span></b>', '<i>bar</i>', '<strong>baz</strong>'],
  1668. allowHtml: false,
  1669. }
  1670. ]
  1671. });
  1672. selectCell(0, 0);
  1673. const editorInput = $('.handsontableInput');
  1674. expect(getDataAtCell(0, 0)).toBeNull();
  1675. keyDownUp('enter');
  1676. await sleep(200);
  1677. editorInput.val('b');
  1678. keyDownUp('b'.charCodeAt(0));
  1679. await sleep(200);
  1680. {
  1681. const ac = hot.getActiveEditor();
  1682. const innerHot = ac.htEditor;
  1683. expect(innerHot.getData()).toEqual([
  1684. ['bar'],
  1685. ['baz'],
  1686. ]);
  1687. }
  1688. editorInput.val('bar');
  1689. keyDownUp('a'.charCodeAt(0));
  1690. keyDownUp('r'.charCodeAt(0));
  1691. await sleep(200);
  1692. {
  1693. const ac = hot.getActiveEditor();
  1694. const innerHot = ac.htEditor;
  1695. expect(innerHot.getData()).toEqual([
  1696. ['bar']
  1697. ]);
  1698. }
  1699. keyDownUp('arrow_down');
  1700. keyDownUp('enter');
  1701. await sleep(100);
  1702. expect(getCell(0, 0).querySelector('i')).toBeNull();
  1703. expect(getCell(0, 0).textContent).toMatch('bar');
  1704. });
  1705. });
  1706. describe('Autocomplete helper functions:', () => {
  1707. describe('sortByRelevance', () => {
  1708. it('should sort the provided array, so items more relevant to the provided value are listed first', () => {
  1709. const choicesList = [
  1710. 'Wayne', // 0
  1711. 'Draven', // 1
  1712. 'Banner', // 2
  1713. 'Stark', // 3
  1714. 'Parker', // 4
  1715. 'Kent', // 5
  1716. 'Gordon', // 6
  1717. 'Kyle', // 7
  1718. 'Simmons'// 8
  1719. ];
  1720. let value = 'a';
  1721. let sorted = Handsontable.editors.AutocompleteEditor.sortByRelevance(value, choicesList);
  1722. expect(sorted).toEqual([0, 2, 4, 3, 1]);
  1723. value = 'o';
  1724. sorted = Handsontable.editors.AutocompleteEditor.sortByRelevance(value, choicesList);
  1725. expect(sorted).toEqual([6, 8]);
  1726. value = 'er';
  1727. sorted = Handsontable.editors.AutocompleteEditor.sortByRelevance(value, choicesList);
  1728. expect(sorted).toEqual([2, 4]);
  1729. });
  1730. });
  1731. });
  1732. it('should not modify the suggestion lists\' order, when the sortByRelevance option is set to false', (done) => {
  1733. const choicesList = [
  1734. 'Wayne', 'Draven', 'Banner', 'Stark', 'Parker', 'Kent', 'Gordon', 'Kyle', 'Simmons'
  1735. ];
  1736. handsontable({
  1737. columns: [
  1738. {
  1739. editor: 'autocomplete',
  1740. source: choicesList,
  1741. sortByRelevance: false
  1742. }
  1743. ]
  1744. });
  1745. selectCell(0, 0);
  1746. keyDownUp('enter');
  1747. const $editorInput = $('.handsontableInput');
  1748. $editorInput.val('a');
  1749. keyDownUp('a'.charCodeAt(0));
  1750. Handsontable.dom.setCaretPosition($editorInput[0], 1);
  1751. setTimeout(() => {
  1752. const dropdownList = $('.autocompleteEditor tbody').first();
  1753. const listLength = dropdownList.find('tr').size();
  1754. expect(listLength).toBe(9);
  1755. for (let i = 1; i <= listLength; i++) {
  1756. expect(dropdownList.find(`tr:nth-child(${i}) td`).text()).toEqual(choicesList[i - 1]);
  1757. }
  1758. done();
  1759. }, 30);
  1760. });
  1761. it('should fire one afterChange event when value is changed', (done) => {
  1762. const onAfterChange = jasmine.createSpy('onAfterChange');
  1763. const syncSources = jasmine.createSpy('syncSources');
  1764. syncSources.and.callFake((query, process) => {
  1765. process(choices);
  1766. });
  1767. handsontable({
  1768. columns: [
  1769. {
  1770. editor: 'autocomplete',
  1771. source: syncSources
  1772. }
  1773. ],
  1774. afterChange: onAfterChange
  1775. });
  1776. selectCell(0, 0);
  1777. keyDownUp('enter');
  1778. setTimeout(() => {
  1779. onAfterChange.calls.reset();
  1780. autocomplete().find('tbody td:eq(1)').simulate('mousedown');
  1781. expect(getDataAtCell(0, 0)).toEqual('red');
  1782. expect(onAfterChange.calls.count()).toEqual(1);
  1783. expect(onAfterChange).toHaveBeenCalledWith([[0, 0, null, 'red']], 'edit', undefined, undefined, undefined, undefined);
  1784. done();
  1785. }, 200);
  1786. });
  1787. it('should not affect other cell values after clicking on autocomplete cell (#1021)', (done) => {
  1788. const syncSources = jasmine.createSpy('syncSources');
  1789. syncSources.and.callFake((query, process) => {
  1790. process(choices);
  1791. });
  1792. handsontable({
  1793. columns: [
  1794. {},
  1795. {},
  1796. {
  1797. editor: 'autocomplete',
  1798. source: syncSources
  1799. },
  1800. {}
  1801. ],
  1802. data: [
  1803. [null, null, 'yellow', null],
  1804. [null, null, 'red', null],
  1805. [null, null, 'blue', null]
  1806. ]
  1807. });
  1808. expect($(getCell(0, 2)).text()).toMatch('yellow');
  1809. mouseDoubleClick(getCell(0, 2));
  1810. expect($(getCell(1, 2)).text()).toMatch('red');
  1811. mouseDoubleClick(getCell(1, 2));
  1812. expect($(getCell(2, 2)).text()).toMatch('blue');
  1813. mouseDoubleClick(getCell(2, 2));
  1814. setTimeout(() => {
  1815. expect(getDataAtCol(2)).toEqual(['yellow', 'red', 'blue']);
  1816. done();
  1817. }, 200);
  1818. });
  1819. it('should handle editor if cell data is a function', (done) => {
  1820. spyOn(Handsontable.editors.AutocompleteEditor.prototype, 'updateChoicesList').and.callThrough();
  1821. const afterValidateCallback = jasmine.createSpy('afterValidateCallbak');
  1822. const hot = handsontable({
  1823. data: [
  1824. new Model({
  1825. id: 1,
  1826. name: 'Ted Right',
  1827. address: ''
  1828. }),
  1829. new Model({
  1830. id: 2,
  1831. name: 'Frank Honest',
  1832. address: ''
  1833. }),
  1834. new Model({
  1835. id: 3,
  1836. name: 'Joan Well',
  1837. address: ''
  1838. })],
  1839. dataSchema: Model,
  1840. colHeaders: ['ID', 'Name', 'Address'],
  1841. columns: [
  1842. {
  1843. data: createAccessorForProperty('id'),
  1844. type: 'autocomplete',
  1845. source: ['1', '2', '3'],
  1846. filter: false,
  1847. strict: true
  1848. },
  1849. {
  1850. data: createAccessorForProperty('name')
  1851. },
  1852. {
  1853. data: createAccessorForProperty('address')
  1854. }
  1855. ],
  1856. minSpareRows: 1,
  1857. afterValidate: afterValidateCallback
  1858. });
  1859. selectCell(0, 0);
  1860. expect(hot.getActiveEditor().isOpened()).toBe(false);
  1861. keyDownUp('enter');
  1862. setTimeout(() => {
  1863. expect(hot.getActiveEditor().isOpened()).toBe(true);
  1864. afterValidateCallback.calls.reset();
  1865. $(hot.getActiveEditor().htContainer).find('tr:eq(1) td:eq(0)').simulate('mousedown');
  1866. }, 200);
  1867. setTimeout(() => {
  1868. expect(getDataAtCell(0, 0)).toEqual('2');
  1869. done();
  1870. }, 400);
  1871. });
  1872. // Input element should be focused on cell selection othrwise it breaks IME editor functionality for Asian users.
  1873. it('should not lose the focus on input element while inserting new characters (#839)', async() => {
  1874. const focusListener = jasmine.createSpy('focus');
  1875. const hot = handsontable({
  1876. data: [
  1877. ['one', 'two'],
  1878. ['three', 'four']
  1879. ],
  1880. columns: [
  1881. {
  1882. type: 'autocomplete',
  1883. source: choices,
  1884. },
  1885. {},
  1886. ],
  1887. });
  1888. selectCell(0, 0);
  1889. hot.getActiveEditor().TEXTAREA.addEventListener('focus', focusListener);
  1890. await sleep(50);
  1891. expect(focusListener).toHaveBeenCalled();
  1892. hot.getActiveEditor().TEXTAREA.removeEventListener('focus', focusListener);
  1893. });
  1894. it('should not lose the focus from the editor after selecting items from the choice list', async() => {
  1895. const hot = handsontable({
  1896. data: [
  1897. ['', 'two'],
  1898. ['three', 'four']
  1899. ],
  1900. columns: [
  1901. {
  1902. type: 'autocomplete',
  1903. source: ['brown', 'yellow', 'green'],
  1904. },
  1905. {},
  1906. ],
  1907. });
  1908. selectCell(0, 0);
  1909. keyDownUp('enter');
  1910. await sleep(0);
  1911. keyDownUp('arrow_down');
  1912. keyDownUp('arrow_down');
  1913. keyDownUp('arrow_down');
  1914. hot.getActiveEditor().TEXTAREA.value = 'r';
  1915. keyDownUp('R'.charCodeAt(0));
  1916. await sleep(0);
  1917. // Check if ESCAPE key is responsive.
  1918. keyDownUp('esc');
  1919. expect(hot.isListening()).toBeTruthy();
  1920. expect(isEditorVisible($(hot.getActiveEditor().htEditor.rootElement))).toBeFalsy();
  1921. });
  1922. it('should not call the `source` has been selected', () => {
  1923. const syncSources = jasmine.createSpy('syncSources');
  1924. syncSources.and.callFake((query, process) => {
  1925. process([]); // hardcoded empty result
  1926. });
  1927. handsontable({
  1928. data: [
  1929. ['one', 'two'],
  1930. ['three', 'four']
  1931. ],
  1932. columns: [
  1933. {
  1934. type: 'autocomplete',
  1935. source: syncSources,
  1936. allowInvalid: false,
  1937. strict: true
  1938. },
  1939. {}
  1940. ],
  1941. cells(row, col) {
  1942. const cellProperties = {};
  1943. if (row === 0 && col === 0) {
  1944. cellProperties.readOnly = true;
  1945. }
  1946. return cellProperties;
  1947. }
  1948. });
  1949. expect(getCellMeta(0, 0).readOnly).toBe(true);
  1950. expect(syncSources).not.toHaveBeenCalled();
  1951. selectCell(0, 0);
  1952. expect(syncSources).not.toHaveBeenCalled();
  1953. expect(getCellMeta(1, 0).readOnly).toBeFalsy();
  1954. selectCell(1, 0);
  1955. expect(syncSources).not.toHaveBeenCalled();
  1956. });
  1957. it('should not call the `source` method if cell is read only and the arrow has been clicked', (done) => {
  1958. const syncSources = jasmine.createSpy('syncSources');
  1959. syncSources.and.callFake((query, process) => {
  1960. process([]); // hardcoded empty result
  1961. });
  1962. handsontable({
  1963. data: [
  1964. ['one', 'two'],
  1965. ['three', 'four']
  1966. ],
  1967. columns: [
  1968. {
  1969. type: 'autocomplete',
  1970. source: syncSources,
  1971. allowInvalid: false,
  1972. strict: true
  1973. },
  1974. {}
  1975. ],
  1976. cells(row, col) {
  1977. const cellProperties = {};
  1978. if (row === 0 && col === 0) {
  1979. cellProperties.readOnly = true;
  1980. }
  1981. return cellProperties;
  1982. }
  1983. });
  1984. expect(getCellMeta(0, 0).readOnly).toBe(true);
  1985. expect(syncSources).not.toHaveBeenCalled();
  1986. selectCell(0, 0);
  1987. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  1988. setTimeout(() => {
  1989. expect(syncSources).not.toHaveBeenCalled();
  1990. syncSources.calls.reset();
  1991. expect(getCellMeta(1, 0).readOnly).toBeFalsy();
  1992. selectCell(1, 0);
  1993. $(getCell(1, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  1994. }, 100);
  1995. setTimeout(() => {
  1996. expect(syncSources).toHaveBeenCalled();
  1997. expect(syncSources.calls.count()).toEqual(1);
  1998. done();
  1999. }, 200);
  2000. });
  2001. it('should add a scrollbar to the autocomplete dropdown, only if number of displayed choices exceeds 10', async() => {
  2002. const hot = handsontable({
  2003. data: [
  2004. ['', 'two', 'three'],
  2005. ['four', 'five', 'six']
  2006. ],
  2007. columns: [
  2008. {
  2009. type: 'autocomplete',
  2010. source: choices,
  2011. allowInvalid: false,
  2012. strict: false
  2013. },
  2014. {},
  2015. {}
  2016. ]
  2017. });
  2018. spec().$container.css({
  2019. height: 600
  2020. });
  2021. expect(choices.length).toBeGreaterThan(10);
  2022. selectCell(0, 0);
  2023. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  2024. const dropdownHolder = hot.getActiveEditor().htEditor.view.wt.wtTable.holder;
  2025. await sleep(30);
  2026. expect(dropdownHolder.scrollHeight).toBeGreaterThan(dropdownHolder.clientHeight);
  2027. keyDownUp('esc');
  2028. hot.getSettings().columns[0].source = hot.getSettings().columns[0].source.slice(0).splice(3);
  2029. hot.updateSettings({});
  2030. selectCell(0, 0);
  2031. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  2032. await sleep(30);
  2033. expect(dropdownHolder.scrollHeight > dropdownHolder.clientHeight).toBe(false);
  2034. });
  2035. it('should not close editor on scrolling', async() => {
  2036. const hot = handsontable({
  2037. data: [
  2038. ['', 'two', 'three'],
  2039. ['four', 'five', 'six']
  2040. ],
  2041. columns: [
  2042. {
  2043. type: 'autocomplete',
  2044. source: choices,
  2045. allowInvalid: false,
  2046. strict: false
  2047. },
  2048. {},
  2049. {}
  2050. ]
  2051. });
  2052. expect(choices.length).toBeGreaterThan(10);
  2053. selectCell(0, 0);
  2054. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  2055. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mouseup');
  2056. const dropdown = hot.getActiveEditor().htContainer;
  2057. hot.view.wt.wtOverlays.topOverlay.scrollTo(1);
  2058. await sleep(50);
  2059. expect($(dropdown).is(':visible')).toBe(true);
  2060. selectCell(0, 0);
  2061. await sleep(50);
  2062. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mousedown');
  2063. $(getCell(0, 0)).find('.htAutocompleteArrow').simulate('mouseup');
  2064. hot.view.wt.wtOverlays.topOverlay.scrollTo(3);
  2065. await sleep(50);
  2066. expect($(dropdown).is(':visible')).toBe(true);
  2067. });
  2068. it('should keep textarea caret position, after moving the selection to the suggestion list (pressing down arrow)', async() => {
  2069. const syncSources = jasmine.createSpy('syncSources');
  2070. syncSources.and.callFake((query, process) => {
  2071. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2072. });
  2073. handsontable({
  2074. columns: [
  2075. {
  2076. type: 'autocomplete',
  2077. source: syncSources,
  2078. strict: false
  2079. }
  2080. ]
  2081. });
  2082. selectCell(0, 0);
  2083. keyDownUp('enter');
  2084. const $editorInput = $('.handsontableInput');
  2085. $editorInput.val('an');
  2086. keyDownUp(65); // a
  2087. keyDownUp(78); // n
  2088. await sleep(0);
  2089. Handsontable.dom.setCaretPosition($editorInput[0], 1);
  2090. await sleep(200);
  2091. keyDownUp('arrow_down');
  2092. expect(Handsontable.dom.getCaretPosition($editorInput[0])).toEqual(1);
  2093. keyDownUp('arrow_down');
  2094. expect(Handsontable.dom.getCaretPosition($editorInput[0])).toEqual(1);
  2095. });
  2096. it('should keep textarea selection, after moving the selection to the suggestion list (pressing down arrow)', async() => {
  2097. const syncSources = jasmine.createSpy('syncSources');
  2098. syncSources.and.callFake((query, process) => {
  2099. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2100. });
  2101. handsontable({
  2102. columns: [
  2103. {
  2104. type: 'autocomplete',
  2105. source: syncSources,
  2106. strict: false
  2107. }
  2108. ]
  2109. });
  2110. selectCell(0, 0);
  2111. keyDownUp('enter');
  2112. const $editorInput = $('.handsontableInput');
  2113. $editorInput.val('an');
  2114. keyDownUp(65); // a
  2115. keyDownUp(78); // n
  2116. await sleep(0);
  2117. Handsontable.dom.setCaretPosition($editorInput[0], 1, 2);
  2118. await sleep(200);
  2119. keyDownUp('arrow_down');
  2120. expect(Handsontable.dom.getCaretPosition($editorInput[0])).toEqual(1);
  2121. expect(Handsontable.dom.getSelectionEndPosition($editorInput[0])).toEqual(2);
  2122. keyDownUp('arrow_down');
  2123. expect(Handsontable.dom.getCaretPosition($editorInput[0])).toEqual(1);
  2124. expect(Handsontable.dom.getSelectionEndPosition($editorInput[0])).toEqual(2);
  2125. });
  2126. it('should jump to the sibling cell, after pressing up key in quick edit mode', (done) => {
  2127. const syncSources = jasmine.createSpy('syncSources');
  2128. syncSources.and.callFake((query, process) => {
  2129. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2130. });
  2131. handsontable({
  2132. columns: [
  2133. {
  2134. type: 'autocomplete',
  2135. source: syncSources,
  2136. strict: false
  2137. },
  2138. {}
  2139. ]
  2140. });
  2141. selectCell(1, 0);
  2142. keyDownUp('x'); // trigger quick edit mode
  2143. const $editorInput = $('.handsontableInput');
  2144. $editorInput.val('an');
  2145. keyDownUp(65); // a
  2146. keyDownUp(78); // n
  2147. setTimeout(() => {
  2148. keyDownUp('arrow_up');
  2149. expect(getSelected()).toEqual([[0, 0, 0, 0]]);
  2150. done();
  2151. }, 200);
  2152. });
  2153. it('should jump to the next cell, after pressing right key in quick edit mode', (done) => {
  2154. const syncSources = jasmine.createSpy('syncSources');
  2155. syncSources.plan = function(query, process) {
  2156. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2157. };
  2158. handsontable({
  2159. columns: [
  2160. {
  2161. type: 'autocomplete',
  2162. source: syncSources,
  2163. strict: false
  2164. },
  2165. {}
  2166. ]
  2167. });
  2168. selectCell(1, 0);
  2169. keyDownUp('x'); // trigger quick edit mode
  2170. const $editorInput = $('.handsontableInput');
  2171. $editorInput.val('an');
  2172. keyDownUp(65); // a
  2173. keyDownUp(78); // n
  2174. setTimeout(() => {
  2175. keyDownUp('arrow_right');
  2176. expect(getSelected()).toEqual([[1, 1, 1, 1]]);
  2177. done();
  2178. }, 200);
  2179. });
  2180. it('should jump to the next cell, after pressing left key in quick edit mode', (done) => {
  2181. const syncSources = jasmine.createSpy('syncSources');
  2182. syncSources.and.callFake((query, process) => {
  2183. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2184. });
  2185. handsontable({
  2186. columns: [
  2187. {},
  2188. {
  2189. type: 'autocomplete',
  2190. source: syncSources,
  2191. strict: false
  2192. }
  2193. ]
  2194. });
  2195. selectCell(1, 1);
  2196. keyDownUp('x'); // trigger quick edit mode
  2197. const $editorInput = $('.handsontableInput');
  2198. $editorInput.val('an');
  2199. keyDownUp(65); // a
  2200. keyDownUp(78); // n
  2201. // put caret on the end of the text to ensure that editor will be closed after hit left arrow key
  2202. Handsontable.dom.setCaretPosition($editorInput[0], 2, 2);
  2203. setTimeout(() => {
  2204. keyDownUp('arrow_left');
  2205. expect(getSelected()).toEqual([[1, 0, 1, 0]]);
  2206. done();
  2207. }, 200);
  2208. });
  2209. it('should jump to the next cell, after pressing down key in quick edit mode', (done) => {
  2210. const syncSources = jasmine.createSpy('syncSources');
  2211. syncSources.and.callFake((query, process) => {
  2212. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2213. });
  2214. handsontable({
  2215. columns: [
  2216. {
  2217. type: 'autocomplete',
  2218. source: syncSources,
  2219. strict: false
  2220. },
  2221. {}
  2222. ]
  2223. });
  2224. selectCell(1, 0);
  2225. keyDownUp('x'); // trigger quick edit mode
  2226. const $editorInput = $('.handsontableInput');
  2227. $editorInput.val('an');
  2228. keyDownUp(65); // a
  2229. keyDownUp(78); // n
  2230. setTimeout(() => {
  2231. keyDownUp('arrow_down');
  2232. expect(getSelected()).toEqual([[1, 0, 1, 0]]);
  2233. done();
  2234. }, 200);
  2235. });
  2236. it('should jump to the next cell, after pressing down key in quick edit mode when no matching option list found', (done) => {
  2237. const syncSources = jasmine.createSpy('syncSources');
  2238. syncSources.and.callFake((query, process) => {
  2239. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2240. });
  2241. handsontable({
  2242. columns: [
  2243. {
  2244. type: 'autocomplete',
  2245. source: syncSources,
  2246. strict: false
  2247. },
  2248. {}
  2249. ]
  2250. });
  2251. selectCell(1, 0);
  2252. keyDownUp('x'); // trigger quick edit mode
  2253. const $editorInput = $('.handsontableInput');
  2254. $editorInput.val('anananan');
  2255. keyDownUp(65); // a
  2256. keyDownUp(78); // n
  2257. keyDownUp(65); // a
  2258. keyDownUp(78); // n
  2259. keyDownUp(65); // a
  2260. keyDownUp(78); // n
  2261. keyDownUp(65); // a
  2262. keyDownUp(78); // n
  2263. setTimeout(() => {
  2264. keyDownUp('arrow_down');
  2265. expect(getSelected()).toEqual([[2, 0, 2, 0]]);
  2266. done();
  2267. }, 200);
  2268. });
  2269. it('should not jump to the next cell, after pressing down key in quick edit mode when options list was opened', (done) => {
  2270. const syncSources = jasmine.createSpy('syncSources');
  2271. syncSources.and.callFake((query, process) => {
  2272. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2273. });
  2274. handsontable({
  2275. columns: [
  2276. {
  2277. type: 'autocomplete',
  2278. source: syncSources,
  2279. strict: false
  2280. },
  2281. {}
  2282. ]
  2283. });
  2284. selectCell(1, 0);
  2285. keyDownUp('x'); // trigger quick edit mode
  2286. const $editorInput = $('.handsontableInput');
  2287. $editorInput.val('an');
  2288. keyDownUp(65); // a
  2289. keyDownUp(78); // n
  2290. setTimeout(() => {
  2291. keyDownUp('arrow_down');
  2292. expect(getSelected()).toEqual([[1, 0, 1, 0]]);
  2293. done();
  2294. }, 200);
  2295. });
  2296. it('should select option in opened editor after pressing down key in quick edit mode', (done) => {
  2297. const syncSources = jasmine.createSpy('syncSources');
  2298. syncSources.and.callFake((query, process) => {
  2299. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2300. });
  2301. const hot = handsontable({
  2302. columns: [
  2303. {
  2304. type: 'autocomplete',
  2305. source: syncSources,
  2306. strict: false
  2307. },
  2308. {}
  2309. ]
  2310. });
  2311. selectCell(1, 0);
  2312. keyDownUp('x'); // Trigger quick edit mode
  2313. setTimeout(() => {
  2314. keyDownUp('arrow_down');
  2315. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[0, 0, 0, 0]]);
  2316. keyDownUp('arrow_down');
  2317. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[1, 0, 1, 0]]);
  2318. keyDownUp('arrow_down');
  2319. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[2, 0, 2, 0]]);
  2320. done();
  2321. }, 200);
  2322. });
  2323. it('should select option in opened editor after pressing up key in quick edit mode', (done) => {
  2324. const syncSources = jasmine.createSpy('syncSources');
  2325. syncSources.and.callFake((query, process) => {
  2326. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2327. });
  2328. const hot = handsontable({
  2329. columns: [
  2330. {
  2331. type: 'autocomplete',
  2332. source: syncSources,
  2333. strict: false
  2334. },
  2335. {}
  2336. ],
  2337. autoWrapCol: false,
  2338. autoWrapRow: false
  2339. });
  2340. selectCell(1, 0);
  2341. keyDownUp('x'); // Trigger quick edit mode
  2342. setTimeout(() => {
  2343. hot.getActiveEditor().htEditor.selectCell(2, 0);
  2344. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[2, 0, 2, 0]]);
  2345. keyDownUp('arrow_up');
  2346. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[1, 0, 1, 0]]);
  2347. keyDownUp('arrow_up');
  2348. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[0, 0, 0, 0]]);
  2349. keyDownUp('arrow_up');
  2350. expect(hot.getActiveEditor().htEditor.getSelected()).toEqual([[0, 0, 0, 0]]);
  2351. done();
  2352. }, 200);
  2353. });
  2354. it('should not close editor in quick edit mode after pressing down key when last option is selected', (done) => {
  2355. const syncSources = jasmine.createSpy('syncSources');
  2356. syncSources.and.callFake((query, process) => {
  2357. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2358. });
  2359. const hot = handsontable({
  2360. columns: [
  2361. {
  2362. type: 'autocomplete',
  2363. source: syncSources,
  2364. strict: false
  2365. },
  2366. {}
  2367. ]
  2368. });
  2369. selectCell(1, 0);
  2370. keyDownUp('x'); // Trigger quick edit mode
  2371. setTimeout(() => {
  2372. hot.getActiveEditor().htEditor.selectCell(7, 0);
  2373. hot.listen();
  2374. keyDownUp('arrow_down');
  2375. keyDownUp('arrow_down');
  2376. keyDownUp('arrow_down');
  2377. keyDownUp('arrow_down');
  2378. keyDownUp('arrow_down');
  2379. expect(hot.getActiveEditor().isOpened()).toBe(true);
  2380. done();
  2381. }, 200);
  2382. });
  2383. it('should close editor in quick edit mode after pressing up key when no option is selected', (done) => {
  2384. const syncSources = jasmine.createSpy('syncSources');
  2385. syncSources.and.callFake((query, process) => {
  2386. process(choices.filter(choice => choice.indexOf(query) !== -1));
  2387. });
  2388. const hot = handsontable({
  2389. columns: [
  2390. {
  2391. type: 'autocomplete',
  2392. source: syncSources,
  2393. strict: false
  2394. },
  2395. {}
  2396. ]
  2397. });
  2398. selectCell(1, 0);
  2399. keyDownUp('x'); // Trigger quick edit mode
  2400. setTimeout(() => {
  2401. hot.getActiveEditor().htEditor.selectCell(1, 0);
  2402. hot.listen();
  2403. keyDownUp('arrow_up');
  2404. keyDownUp('arrow_up');
  2405. keyDownUp('arrow_up');
  2406. expect(getSelected()).toEqual([[0, 0, 0, 0]]);
  2407. done();
  2408. }, 200);
  2409. });
  2410. describe('IME support', () => {
  2411. it('should focus editable element after selecting the cell', async() => {
  2412. handsontable({
  2413. columns: [
  2414. {
  2415. editor: 'autocomplete',
  2416. source: choices
  2417. }
  2418. ]
  2419. });
  2420. selectCell(0, 0, 0, 0, true, false);
  2421. await sleep(10);
  2422. expect(document.activeElement).toBe(getActiveEditor().TEXTAREA);
  2423. });
  2424. });
  2425. });