checkboxRenderer.spec.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853
  1. describe('CheckboxRenderer', () => {
  2. const id = 'testContainer';
  3. beforeEach(function() {
  4. this.$container = $(`<div id="${id}" style="width: 300px; height: 200px;"></div>`).appendTo('body');
  5. });
  6. afterEach(function() {
  7. if (this.$container) {
  8. destroy();
  9. this.$container.remove();
  10. }
  11. });
  12. it('should render values as checkboxes', () => {
  13. handsontable({
  14. data: [[true], [false], [true]],
  15. columns: [
  16. { type: 'checkbox' }
  17. ]
  18. });
  19. expect($(getRenderedValue(0, 0)).is(':checkbox')).toBe(true);
  20. expect($(getRenderedValue(1, 0)).is(':checkbox')).toBe(true);
  21. expect($(getRenderedValue(2, 0)).is(':checkbox')).toBe(true);
  22. });
  23. it('should render check checkboxes for cell which value is true', () => {
  24. handsontable({
  25. data: [[true], [false], [true]],
  26. columns: [
  27. { type: 'checkbox' }
  28. ]
  29. });
  30. expect($(getRenderedContent(0, 0)).prop('checked')).toBe(true);
  31. expect($(getRenderedContent(1, 0)).prop('checked')).toBe(false);
  32. expect($(getRenderedContent(2, 0)).prop('checked')).toBe(true);
  33. });
  34. it('should use templates to check appropriate checkboxes', () => {
  35. handsontable({
  36. data: [['yes'], ['no'], ['yes']],
  37. columns: [
  38. {
  39. type: 'checkbox',
  40. checkedTemplate: 'yes',
  41. uncheckedTemplate: 'no'
  42. }
  43. ]
  44. });
  45. expect($(getRenderedContent(0, 0)).prop('checked')).toBe(true);
  46. expect($(getRenderedContent(1, 0)).prop('checked')).toBe(false);
  47. expect($(getRenderedContent(2, 0)).prop('checked')).toBe(true);
  48. });
  49. it('should select cell after checkbox click', async() => {
  50. const spy = jasmine.createSpyObj('error', ['test']);
  51. window.onerror = function() {
  52. spy.test();
  53. return false;
  54. };
  55. const hot = handsontable({
  56. data: [[true], [false], [true]],
  57. columns: [
  58. { type: 'checkbox' }
  59. ]
  60. });
  61. hot.selectCell(0, 0);
  62. spec().$container.find(':checkbox').eq(2).simulate('mousedown');
  63. spec().$container.find(':checkbox').eq(2).simulate('mouseup');
  64. spec().$container.find(':checkbox').eq(2).simulate('click');
  65. await sleep(100);
  66. expect(spy.test.calls.count()).toBe(0);
  67. expect(hot.getSelected()).toEqual([[2, 0, 2, 0]]);
  68. });
  69. it('should select cell after label click', () => {
  70. const hot = handsontable({
  71. data: [[true], [false], [true]],
  72. columns: [
  73. { type: 'checkbox', label: { position: 'before', value: 'Sure? ' } }
  74. ]
  75. });
  76. hot.selectCell(0, 0);
  77. spec().$container.find('td label').eq(2).simulate('mousedown');
  78. expect(hot.getSelected()).toEqual([[2, 0, 2, 0]]);
  79. });
  80. it('should reverse selection in checkboxes', () => {
  81. handsontable({
  82. data: [[true], [false], [true]],
  83. columns: [
  84. { type: 'checkbox' }
  85. ]
  86. });
  87. spec().$container.find(':checkbox').eq(0).simulate('click');
  88. spec().$container.find(':checkbox').eq(1).simulate('click');
  89. spec().$container.find(':checkbox').eq(2).simulate('click');
  90. expect(getData()).toEqual([[false], [true], [false]]);
  91. });
  92. it('shouldn\'t uncheck checkboxes', () => {
  93. handsontable({
  94. data: [[true], [true], [true]],
  95. columns: [
  96. { type: 'checkbox', readOnly: true }
  97. ]
  98. });
  99. spec().$container.find(':checkbox').trigger('click');
  100. expect(getData()).toEqual([[true], [true], [true]]);
  101. });
  102. it('should check single box after hitting space', () => {
  103. handsontable({
  104. data: [[true], [true], [true]],
  105. columns: [
  106. { type: 'checkbox' }
  107. ]
  108. });
  109. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  110. addHook('afterChange', afterChangeCallback);
  111. let checkboxes = spec().$container.find(':checkbox');
  112. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  113. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  114. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  115. expect(getData()).toEqual([[true], [true], [true]]);
  116. selectCell(0, 0);
  117. // spec().$container.find(':checkbox').eq(0).simulate('click');
  118. // spec().$container.simulate('keydown',{
  119. // keyCode: 32
  120. // });
  121. keyDown('space');
  122. checkboxes = spec().$container.find(':checkbox');
  123. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  124. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  125. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  126. expect(getData()).toEqual([[false], [true], [true]]);
  127. expect(afterChangeCallback.calls.count()).toEqual(1);
  128. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  129. });
  130. it('should not check single box after hitting space, if cell is readOnly', () => {
  131. handsontable({
  132. data: [[true], [true], [true]],
  133. columns: [
  134. { type: 'checkbox', readOnly: true }
  135. ]
  136. });
  137. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  138. addHook('afterChange', afterChangeCallback);
  139. let checkboxes = spec().$container.find(':checkbox');
  140. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  141. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  142. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  143. expect(getData()).toEqual([[true], [true], [true]]);
  144. selectCell(0, 0);
  145. keyDown('space');
  146. checkboxes = spec().$container.find(':checkbox');
  147. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  148. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  149. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  150. expect(getData()).toEqual([[true], [true], [true]]);
  151. expect(afterChangeCallback).not.toHaveBeenCalled();
  152. });
  153. it('should not check single box after hitting space, if last column is readOnly (#3562)', () => {
  154. handsontable({
  155. data: [[true, true], [false, false], [true, true]],
  156. columns: [
  157. { type: 'checkbox' },
  158. { type: 'checkbox', readOnly: true }
  159. ]
  160. });
  161. selectCell(0, 0);
  162. keyDown('space');
  163. selectCell(0, 1);
  164. keyDown('space');
  165. selectCell(1, 0);
  166. keyDown('space');
  167. selectCell(1, 1);
  168. keyDown('space');
  169. const checkboxes = spec().$container.find(':checkbox');
  170. // column 0
  171. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  172. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  173. expect(checkboxes.eq(4).prop('checked')).toBe(true);
  174. // column 1
  175. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  176. expect(checkboxes.eq(3).prop('checked')).toBe(false);
  177. expect(checkboxes.eq(5).prop('checked')).toBe(true);
  178. expect(getData()).toEqual([[false, true], [true, false], [true, true]]);
  179. });
  180. it('should change checkboxes values properly when data contains null or/and undefined', () => {
  181. handsontable({
  182. data: [[null], [undefined]],
  183. colHeaders: true,
  184. columns: [
  185. {
  186. type: 'checkbox'
  187. }
  188. ]
  189. });
  190. selectCell(0, 0, 1, 0);
  191. keyDown('space');
  192. expect(getDataAtCol(0)).toEqual([true, true]);
  193. selectCell(0, 0, 1, 0);
  194. keyDown('space');
  195. expect(getDataAtCol(0)).toEqual([false, false]);
  196. });
  197. it('should change checkboxes values for cells below the viewport (hot initialized by startRows) #4037', () => {
  198. handsontable({
  199. startRows: 200,
  200. colHeaders: true,
  201. columns: [
  202. {
  203. type: 'checkbox'
  204. }
  205. ]
  206. });
  207. selectCell(0, 0, 199, 0);
  208. keyDown('space');
  209. expect(getDataAtCell(199, 0)).toEqual(true);
  210. });
  211. it('should reverse checkboxes state after hitting space, when multiple cells are selected', () => {
  212. handsontable({
  213. data: [[true], [false], [true]],
  214. columns: [
  215. { type: 'checkbox' }
  216. ]
  217. });
  218. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  219. addHook('afterChange', afterChangeCallback);
  220. let checkboxes = spec().$container.find(':checkbox');
  221. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  222. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  223. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  224. expect(getData()).toEqual([[true], [false], [true]]);
  225. selectCell(0, 0, 2, 0);
  226. keyDown('space');
  227. checkboxes = spec().$container.find(':checkbox');
  228. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  229. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  230. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  231. expect(getData()).toEqual([[false], [true], [false]]);
  232. expect(afterChangeCallback.calls.count()).toEqual(1);
  233. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false], [1, 0, false, true], [2, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  234. });
  235. it('should reverse checkboxes state after hitting space, when multiple cells are selected and selStart > selEnd', () => {
  236. handsontable({
  237. data: [[true], [false], [true]],
  238. columns: [
  239. { type: 'checkbox' }
  240. ]
  241. });
  242. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  243. addHook('afterChange', afterChangeCallback);
  244. let checkboxes = spec().$container.find(':checkbox');
  245. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  246. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  247. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  248. expect(getData()).toEqual([[true], [false], [true]]);
  249. selectCell(2, 0, 0, 0); // selStart = [2,0], selEnd = [0,0]
  250. keyDown('space');
  251. checkboxes = spec().$container.find(':checkbox');
  252. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  253. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  254. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  255. expect(getData()).toEqual([[false], [true], [false]]);
  256. expect(afterChangeCallback.calls.count()).toEqual(1);
  257. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false], [1, 0, false, true], [2, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  258. });
  259. it('should toggle checkbox even if cell value is in another datatype', () => {
  260. // TODO: we MUST add additional layer in data transport, to filter stored data types into their defined data type (cellMeta.type)
  261. handsontable({
  262. data: [['true']],
  263. columns: [
  264. { type: 'checkbox' },
  265. ]
  266. });
  267. selectCell(0, 0);
  268. expect(getDataAtCell(0, 0)).toBe('true');
  269. keyDown('space');
  270. expect(getDataAtCell(0, 0)).toBe(false);
  271. });
  272. it('double click on checkbox cell should invert the value', () => {
  273. handsontable({
  274. data: [
  275. [true],
  276. [false],
  277. [true]
  278. ],
  279. columns: [
  280. { type: 'checkbox' }
  281. ]
  282. });
  283. selectCell(0, 0);
  284. mouseDoubleClick(getCell(0, 0));
  285. expect(getDataAtCell(0, 0)).toBe(false);
  286. mouseDoubleClick(getCell(0, 0));
  287. expect(getDataAtCell(0, 0)).toBe(true);
  288. mouseDoubleClick(getCell(0, 0));
  289. expect(getDataAtCell(0, 0)).toBe(false);
  290. });
  291. it('should change checkbox state from checked to unchecked after hitting ENTER', () => {
  292. handsontable({
  293. data: [[true], [true], [true]],
  294. columns: [
  295. { type: 'checkbox' }
  296. ]
  297. });
  298. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  299. addHook('afterChange', afterChangeCallback);
  300. let checkboxes = spec().$container.find(':checkbox');
  301. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  302. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  303. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  304. expect(getData()).toEqual([[true], [true], [true]]);
  305. selectCell(0, 0);
  306. keyDown('enter');
  307. checkboxes = spec().$container.find(':checkbox');
  308. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  309. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  310. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  311. expect(getData()).toEqual([[false], [true], [true]]);
  312. expect(afterChangeCallback.calls.count()).toEqual(1);
  313. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  314. });
  315. it('should move down without changing checkbox state when enterBeginsEditing equals false', () => {
  316. handsontable({
  317. enterBeginsEditing: false,
  318. data: [[true], [false], [true]],
  319. columns: [
  320. { type: 'checkbox' }
  321. ]
  322. });
  323. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  324. addHook('afterChange', afterChangeCallback);
  325. let checkboxes = spec().$container.find(':checkbox');
  326. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  327. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  328. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  329. expect(getData()).toEqual([[true], [false], [true]]);
  330. selectCell(0, 0);
  331. keyDown('enter');
  332. checkboxes = spec().$container.find(':checkbox');
  333. const selection = getSelected();
  334. expect(selection).toEqual([[1, 0, 1, 0]]);
  335. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  336. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  337. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  338. expect(getData()).toEqual([[true], [false], [true]]);
  339. expect(afterChangeCallback.calls.count()).toEqual(0);
  340. });
  341. it('should begin editing and changing checkbox state when enterBeginsEditing equals true', () => {
  342. handsontable({
  343. enterBeginsEditing: true,
  344. data: [[true], [false], [true]],
  345. columns: [
  346. { type: 'checkbox' }
  347. ]
  348. });
  349. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  350. addHook('afterChange', afterChangeCallback);
  351. let checkboxes = spec().$container.find(':checkbox');
  352. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  353. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  354. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  355. expect(getData()).toEqual([[true], [false], [true]]);
  356. selectCell(0, 0);
  357. keyDown('enter');
  358. checkboxes = spec().$container.find(':checkbox');
  359. const selection = getSelected();
  360. expect(selection).toEqual([[0, 0, 0, 0]]);
  361. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  362. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  363. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  364. expect(getData()).toEqual([[false], [false], [true]]);
  365. expect(afterChangeCallback.calls.count()).toEqual(1);
  366. });
  367. it('should change checkbox state from checked to unchecked after hitting ENTER using custom check/uncheck templates', () => {
  368. handsontable({
  369. data: [['yes'], ['yes'], ['no']],
  370. columns: [
  371. {
  372. type: 'checkbox',
  373. checkedTemplate: 'yes',
  374. uncheckedTemplate: 'no'
  375. }
  376. ]
  377. });
  378. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  379. addHook('afterChange', afterChangeCallback);
  380. let checkboxes = spec().$container.find(':checkbox');
  381. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  382. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  383. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  384. expect(getData()).toEqual([['yes'], ['yes'], ['no']]);
  385. selectCell(0, 0);
  386. keyDown('enter');
  387. checkboxes = spec().$container.find(':checkbox');
  388. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  389. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  390. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  391. expect(getData()).toEqual([['no'], ['yes'], ['no']]);
  392. expect(afterChangeCallback.calls.count()).toEqual(1);
  393. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, 'yes', 'no']], 'edit', undefined, undefined, undefined, undefined);
  394. });
  395. it('should change checkbox state from checked to unchecked after hitting ENTER using custom check/uncheck templates in numeric format', () => {
  396. handsontable({
  397. data: [[1], [1], [0]],
  398. columns: [
  399. {
  400. type: 'checkbox',
  401. checkedTemplate: 1,
  402. uncheckedTemplate: 0
  403. }
  404. ]
  405. });
  406. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  407. addHook('afterChange', afterChangeCallback);
  408. let checkboxes = spec().$container.find(':checkbox');
  409. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  410. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  411. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  412. expect(getData()).toEqual([[1], [1], [0]]);
  413. selectCell(0, 0);
  414. keyDown('enter');
  415. checkboxes = spec().$container.find(':checkbox');
  416. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  417. expect(checkboxes.eq(1).prop('checked')).toBe(true);
  418. expect(checkboxes.eq(2).prop('checked')).toBe(false);
  419. expect(getData()).toEqual([[0], [1], [0]]);
  420. expect(afterChangeCallback.calls.count()).toEqual(1);
  421. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, 1, 0]], 'edit', undefined, undefined, undefined, undefined);
  422. });
  423. it('should change checkbox state to unchecked after hitting DELETE', () => {
  424. handsontable({
  425. data: [[true], [false], [true]],
  426. columns: [
  427. { type: 'checkbox' }
  428. ]
  429. });
  430. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  431. addHook('afterChange', afterChangeCallback);
  432. let checkboxes = spec().$container.find(':checkbox');
  433. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  434. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  435. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  436. expect(getData()).toEqual([[true], [false], [true]]);
  437. selectCell(0, 0);
  438. keyDown('delete');
  439. selectCell(0, 1);
  440. keyDown('delete');
  441. checkboxes = spec().$container.find(':checkbox');
  442. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  443. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  444. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  445. expect(getData()).toEqual([[false], [false], [true]]);
  446. expect(afterChangeCallback.calls.count()).toEqual(2);
  447. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  448. });
  449. it('should change checkbox notte to unchecked after hitting BACKSPACE', () => {
  450. handsontable({
  451. data: [[true], [false], [true]],
  452. columns: [
  453. { type: 'checkbox' }
  454. ]
  455. });
  456. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  457. addHook('afterChange', afterChangeCallback);
  458. let checkboxes = spec().$container.find(':checkbox');
  459. expect(checkboxes.eq(0).prop('checked')).toBe(true);
  460. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  461. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  462. expect(getData()).toEqual([[true], [false], [true]]);
  463. selectCell(0, 0);
  464. keyDown('backspace');
  465. selectCell(0, 1);
  466. keyDown('backspace');
  467. checkboxes = spec().$container.find(':checkbox');
  468. expect(checkboxes.eq(0).prop('checked')).toBe(false);
  469. expect(checkboxes.eq(1).prop('checked')).toBe(false);
  470. expect(checkboxes.eq(2).prop('checked')).toBe(true);
  471. expect(getData()).toEqual([[false], [false], [true]]);
  472. expect(afterChangeCallback.calls.count()).toEqual(2);
  473. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, true, false]], 'edit', undefined, undefined, undefined, undefined);
  474. });
  475. it('should change notkbox state to unchecked after hitting DELETE (from #bad-value# state)', () => {
  476. handsontable({
  477. data: [['foo'], ['bar']],
  478. columns: [
  479. { type: 'checkbox' }
  480. ]
  481. });
  482. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  483. addHook('afterChange', afterChangeCallback);
  484. expect(getDataAtCell(0, 0)).toBe('foo');
  485. expect(getDataAtCell(1, 0)).toBe('bar');
  486. selectCell(0, 0);
  487. keyDown('delete');
  488. selectCell(1, 0);
  489. keyDown('delete');
  490. expect(getDataAtCell(0, 0)).toBe(false);
  491. expect(getDataAtCell(1, 0)).toBe(false);
  492. expect(getData()).toEqual([[false], [false]]);
  493. expect(afterChangeCallback.calls.count()).toEqual(2);
  494. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, 'foo', false]], 'edit', undefined, undefined, undefined, undefined);
  495. });
  496. it('should change checkbox note to unchecked after hitting BACKSPACE (from #bad-value# state)', () => {
  497. handsontable({
  498. data: [['foo'], ['bar']],
  499. columns: [
  500. { type: 'checkbox' }
  501. ]
  502. });
  503. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  504. addHook('afterChange', afterChangeCallback);
  505. expect(getDataAtCell(0, 0)).toBe('foo');
  506. expect(getDataAtCell(1, 0)).toBe('bar');
  507. selectCell(0, 0);
  508. keyDown('backspace');
  509. selectCell(1, 0);
  510. keyDown('backspace');
  511. expect(getDataAtCell(0, 0)).toBe(false);
  512. expect(getDataAtCell(1, 0)).toBe(false);
  513. expect(getData()).toEqual([[false], [false]]);
  514. expect(afterChangeCallback.calls.count()).toEqual(2);
  515. expect(afterChangeCallback).toHaveBeenCalledWith([[0, 0, 'foo', false]], 'edit', undefined, undefined, undefined, undefined);
  516. });
  517. it('shouldn\'t change checkbo notate after hitting other keys then DELETE or BACKSPACE (from #bad-value# state)', () => {
  518. handsontable({
  519. data: [['foo'], ['bar']],
  520. columns: [
  521. { type: 'checkbox' }
  522. ]
  523. });
  524. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  525. addHook('afterChange', afterChangeCallback);
  526. expect(getDataAtCell(0, 0)).toBe('foo');
  527. selectCell(0, 0);
  528. keyDown('space');
  529. selectCell(0, 0);
  530. keyDown('c');
  531. expect(getDataAtCell(0, 0)).toBe('foo');
  532. expect(getData()).toEqual([['foo'], ['bar']]);
  533. expect(afterChangeCallback.calls.count()).toEqual(0);
  534. });
  535. it('should not change checkbox state after hitting F2 key', () => {
  536. const onAfterChange = jasmine.createSpy('afterChangeCallback');
  537. handsontable({
  538. data: [[false], [true], [true]],
  539. columns: [
  540. { type: 'checkbox' }
  541. ],
  542. onAfterChange
  543. });
  544. selectCell(0, 0);
  545. keyDown('f2');
  546. expect(getDataAtCell(0, 0)).toBe(false);
  547. expect(onAfterChange.calls.count()).toEqual(0);
  548. });
  549. it('should not change checkbox state after hitting other keys then SPACE, ENTER, DELETE or BACKSPACE', () => {
  550. handsontable({
  551. data: [[false], [true], [true]],
  552. columns: [
  553. { type: 'checkbox' }
  554. ]
  555. });
  556. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  557. addHook('afterChange', afterChangeCallback);
  558. selectCell(0, 0);
  559. keyDown('space');
  560. expect(getDataAtCell(0, 0)).toBe(true);
  561. selectCell(0, 0);
  562. keyDown('c');
  563. expect(getDataAtCell(0, 0)).toBe(true);
  564. expect(afterChangeCallback.calls.count()).toEqual(1);
  565. });
  566. it('should add label on the beginning of a checkbox element', () => {
  567. handsontable({
  568. data: [{ checked: true, label: 'myLabel' }, { checked: false, label: 'myLabel' }],
  569. columns: [
  570. { type: 'checkbox', data: 'checked', label: { position: 'before', property: 'label' } }
  571. ]
  572. });
  573. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  574. addHook('afterChange', afterChangeCallback);
  575. selectCell(0, 0);
  576. keyDown('space');
  577. expect(getDataAtCell(0, 0)).toBe(false);
  578. expect(getDataAtCell(1, 0)).toBe(false);
  579. expect(afterChangeCallback.calls.count()).toEqual(1);
  580. expect(getCell(0, 0).querySelector('label').firstChild.textContent).toEqual('myLabel');
  581. });
  582. it('should add label on the end of a checkbox element', () => {
  583. handsontable({
  584. data: [{ checked: true, label: 'myLabel' }, { checked: false, label: 'myLabel' }],
  585. columns: [
  586. { type: 'checkbox', data: 'checked', label: { position: 'after', property: 'label' } }
  587. ]
  588. });
  589. const afterChangeCallback = jasmine.createSpy('afterChangeCallback');
  590. addHook('afterChange', afterChangeCallback);
  591. selectCell(0, 0);
  592. keyDown('space');
  593. expect(getDataAtCell(0, 0)).toBe(false);
  594. expect(getDataAtCell(1, 0)).toBe(false);
  595. expect(afterChangeCallback.calls.count()).toEqual(1);
  596. expect(getCell(0, 0).querySelector('label').lastChild.textContent).toEqual('myLabel');
  597. });
  598. it('should not add label when value is incorrect (#bad-value)', () => {
  599. handsontable({
  600. data: [{ checked: 1, label: 'myLabel' }, { checked: 0, label: 'myLabel' }],
  601. columns: [
  602. { type: 'checkbox', data: 'checked', label: { position: 'after', property: 'label' } }
  603. ]
  604. });
  605. expect(getCell(0, 0).querySelector('label')).toBe(null);
  606. });
  607. it('by default should add label on the end of a checkbox element', () => {
  608. handsontable({
  609. data: [{ checked: true, label: { test: 'Baz' } }, { checked: false, label: { test: 'Baz' } }],
  610. columns: [
  611. { type: 'checkbox', data: 'checked', label: { property: 'label.test' } }
  612. ]
  613. });
  614. expect(getCell(0, 0).querySelector('label').lastChild.textContent).toEqual('Baz');
  615. });
  616. it('should add label with text filled from `value` label setting (passed as string)', () => {
  617. handsontable({
  618. data: [{ checked: true }, { checked: false }],
  619. columns: [
  620. { type: 'checkbox', data: 'checked', label: { value: 'myLabel' } }
  621. ]
  622. });
  623. expect(getCell(0, 0).querySelector('label').lastChild.textContent).toEqual('myLabel');
  624. });
  625. it('should add label with text filled from `value` label setting (passed as function)', () => {
  626. const labelFunction = jasmine.createSpy();
  627. labelFunction.and.returnValue('myLabel');
  628. handsontable({
  629. autoRowSize: false,
  630. autoColumnSize: false,
  631. data: [{ checked: true }, { checked: false }],
  632. columns: [
  633. { type: 'checkbox', data: 'checked', label: { value: labelFunction } }
  634. ]
  635. });
  636. expect(labelFunction.calls.count()).toBe(2);
  637. expect(labelFunction.calls.argsFor(0)).toEqual([0, 0, 'checked', true]);
  638. expect(labelFunction.calls.argsFor(1)).toEqual([1, 0, 'checked', false]);
  639. expect(getCell(0, 0).querySelector('label').lastChild.textContent).toEqual('myLabel');
  640. });
  641. describe('CheckboxRenderer with ContextMenu', () => {
  642. it('should add class name `htRight` after set align in contextMenu', (done) => {
  643. handsontable({
  644. startRows: 1,
  645. startCols: 1,
  646. contextMenu: ['alignment'],
  647. cells() {
  648. return {
  649. type: 'checkbox'
  650. };
  651. },
  652. height: 100
  653. });
  654. selectCell(0, 0);
  655. contextMenu();
  656. const menu = $('.htContextMenu .ht_master .htCore').find('tbody td').not('.htSeparator');
  657. menu.simulate('mouseover');
  658. setTimeout(() => {
  659. const contextSubMenu = $(`.htContextMenuSub_${menu.text()}`).find('tbody td').eq(2);
  660. contextSubMenu.simulate('mousedown');
  661. contextSubMenu.simulate('mouseup');
  662. expect($('.handsontable.ht_master .htRight').length).toBe(1);
  663. done();
  664. }, 500);
  665. });
  666. });
  667. });