autocompleteEditor.spec.js 75 KB

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