]> git.proxmox.com Git - extjs.git/blame - extjs/classic/classic/test/specs/grid/column/Widget.js
add extjs 6.0.1 sources
[extjs.git] / extjs / classic / classic / test / specs / grid / column / Widget.js
CommitLineData
6527f429
DM
1describe("Ext.grid.column.Widget", function() {\r
2 var webkitIt = Ext.isWebKit ? it : xit,\r
3 synchronousLoad = true,\r
4 proxyStoreLoad = Ext.data.ProxyStore.prototype.load,\r
5 loadStore;\r
6\r
7 var Model = Ext.define(null, {\r
8 extend: 'Ext.data.Model',\r
9 fields: ['a', 'b', 'c']\r
10 });\r
11\r
12 var grid, view, store, colRef, navModel;\r
13\r
14 function generateData(end) {\r
15 var data = [],\r
16 i;\r
17\r
18 end = end || 10;\r
19\r
20 for (i = 1; i <= end; i++) {\r
21 data.push({\r
22 id: 'rec' + i,\r
23 a: i + 'a',\r
24 b: i + 'b',\r
25 c: i + 'c'\r
26 });\r
27 }\r
28\r
29 return data;\r
30 }\r
31\r
32 function getColCfg(widget) {\r
33 return {\r
34 text: 'Button',\r
35 xtype: 'widgetcolumn',\r
36 width: 200,\r
37 dataIndex: 'a',\r
38 widget: widget\r
39 };\r
40 }\r
41\r
42 function createGrid(columns, data, cfg) {\r
43 columns = columns || [getColCfg({\r
44 xtype: 'button'\r
45 })];\r
46\r
47 data = data || generateData(4);\r
48\r
49 store = new Ext.data.Store({\r
50 model: Model,\r
51 data: data,\r
52 proxy: {\r
53 type: 'memory',\r
54 data: data\r
55 }\r
56 });\r
57\r
58 grid = new Ext.grid.Panel(Ext.apply({\r
59 renderTo: Ext.getBody(),\r
60 columns: columns,\r
61 width: 1000,\r
62 height: 500,\r
63 border: false,\r
64 store: store,\r
65 viewConfig: {\r
66 mouseOverOutBuffer: 0\r
67 }\r
68 }, cfg));\r
69 view = grid.getView();\r
70 navModel = view.getNavigationModel();\r
71 colRef = grid.getColumnManager().getColumns();\r
72 }\r
73 \r
74 beforeEach(function() {\r
75 // Override so that we can control asynchronous loading\r
76 loadStore = Ext.data.ProxyStore.prototype.load = function() {\r
77 proxyStoreLoad.apply(this, arguments);\r
78 if (synchronousLoad) {\r
79 this.flushLoad.apply(this, arguments);\r
80 }\r
81 return this;\r
82 };\r
83 });\r
84\r
85 afterEach(function() {\r
86 // Undo the overrides.\r
87 Ext.data.ProxyStore.prototype.load = proxyStoreLoad;\r
88\r
89 Ext.destroy(grid);\r
90 grid = store = colRef = null;\r
91 });\r
92\r
93 function getWidget(index, col) {\r
94 col = col || colRef[0];\r
95 return col.getWidget(store.getAt(index));\r
96 }\r
97\r
98 function getPadding() {\r
99 var cell = grid.getView().getEl().down(colRef[0].getCellInnerSelector());\r
100 return parseInt(cell.getStyle('padding-left'), 10) + parseInt(cell.getStyle('padding-right'), 10);\r
101 }\r
102 \r
103 describe('refocusing after using a column widget to trigger a delete', function() {\r
104 it('should refocus the next row upon deletion', function() {\r
105 createGrid([{\r
106 text: 'Button',\r
107 xtype: 'widgetcolumn',\r
108 width: 200,\r
109 dataIndex: 'a',\r
110 widget: {\r
111 xtype: 'button',\r
112 text: 'Delete row',\r
113 handler: function(button) {\r
114 var rec = button.getWidgetRecord();\r
115 store.remove(rec);\r
116 }\r
117 }\r
118 }]);\r
119\r
120 var widget0 = getWidget(0),\r
121 rec0 = store.getAt(0),\r
122 rec1 = store.getAt(1),\r
123 toDelete = widget0.getWidgetRecord(),\r
124 newTop = getWidget(1).getWidgetRecord(),\r
125 storeCount = store.getCount();\r
126\r
127 expect(toDelete).toBe(rec0);\r
128 expect(newTop).toBe(rec1);\r
129\r
130 // Focus the button, and enter actionable mode, then click the button.\r
131 jasmine.fireMouseEvent(widget0.focusEl, 'mousedown');\r
132 widget0.focusEl.focus();\r
133 jasmine.fireKeyEvent(widget0.focusEl, 'keydown', Ext.event.Event.SPACE);\r
134\r
135 // That should have deleted a record\r
136 expect(store.getCount()).toBe(storeCount - 1);\r
137\r
138 // The widget's record must have gone\r
139 expect(store.contains(toDelete)).toBe(false);\r
140\r
141 // Widget 0 must receive focus when any async focus events have run their course\r
142 waitsFor(function() {\r
143 widget0 = getWidget(0);\r
144 return widget0.hasFocus;\r
145 });\r
146\r
147 runs(function() {\r
148 // Widget 0 record must be what we got from widget 1 initially\r
149 expect(widget0.getWidgetRecord()).toBe(newTop);\r
150 });\r
151 });\r
152 });\r
153\r
154 describe("Widget recycling across refresh", function() {\r
155 it("should recycle widgets", function() {\r
156 var cfg = {\r
157 xtype: 'button',\r
158 cls: 'foo'\r
159 };\r
160\r
161 createGrid([getColCfg(cfg)]);\r
162 var w1 = getWidget(0),\r
163 w2 = getWidget(1),\r
164 w3 = getWidget(2),\r
165 w4 = getWidget(3);\r
166\r
167 // The store is cleared on reload.\r
168 // Widget instances MUST be collected for reuse.\r
169 store.reload();\r
170\r
171 // The Widgets should have been reused.\r
172 expect(getWidget(0) === w1).toBe(true);\r
173 expect(getWidget(1) === w2).toBe(true);\r
174 expect(getWidget(2) === w3).toBe(true);\r
175 expect(getWidget(3) === w4).toBe(true);\r
176 });\r
177 });\r
178\r
179 describe("construction", function() {\r
180 it("should not modify the widget config", function() {\r
181 var cfg = {\r
182 xtype: 'button',\r
183 cls: 'foo'\r
184 };\r
185\r
186 createGrid([getColCfg(cfg)]);\r
187 expect(cfg).toEqual({\r
188 xtype: 'button',\r
189 cls: 'foo'\r
190 });\r
191 });\r
192 });\r
193\r
194 describe('widget refocus on row delete', function() {\r
195 // Test that focus reversion upon delete of focus-containing row works.\r
196 webkitIt("should not cause an error when deleting the focused row using an actionable widget", function() {\r
197 createGrid([{\r
198 itemId: 'ct',\r
199 columns: [getColCfg({\r
200 xtype: 'button',\r
201 handler: function(btn) {\r
202 var rec = btn.getWidgetRecord();\r
203 store.remove(rec);\r
204 }\r
205 })]\r
206 }]);\r
207 var btn = colRef[0].getWidget(store.last());\r
208\r
209 // The mousedown part will focus the button, and flip into actionable mode.\r
210 // The click phase will delete the row.\r
211 // Focus should revert to the previous row\r
212 jasmine.fireMouseEvent(btn.el.dom, 'click');\r
213\r
214 // Should remove the last record\r
215 expect(view.all.getCount()).toBe(3);\r
216\r
217 // And focus should have jumped from the mousedowned button (which has gone)\r
218 // to the button above it.\r
219 expect(colRef[0].getWidget(store.last()).hasFocus).toBe(true);\r
220 });\r
221 });\r
222\r
223 describe("stopSelection", function() {\r
224 beforeEach(function() {\r
225 createGrid();\r
226 });\r
227\r
228 it("should not select the row on click of the widget with stopSelection: true", function() {\r
229 jasmine.fireMouseEvent(getWidget(0).getEl().dom, 'click');\r
230 expect(grid.getSelectionModel().isSelected(0)).toBe(false);\r
231 });\r
232\r
233 it("should select the row on click of the widget with stopSelection: false", function() {\r
234 colRef[0].stopSelection = false;\r
235 navModel.setPosition(new Ext.grid.CellContext(grid.view).setPosition(0, 0));\r
236 \r
237 waitsFor(function() {\r
238 return view.containsFocus;\r
239 });\r
240\r
241 // Wait for focus to be in the view.\r
242 // Because IE has async focusing.\r
243 runs(function() {\r
244 jasmine.fireMouseEvent(getWidget(0).getEl().dom, 'click');\r
245 expect(grid.getSelectionModel().isSelected(0)).toBe(true);\r
246 });\r
247 });\r
248 });\r
249\r
250 describe("widget scope resolution", function() {\r
251 it("should resolve to a view controller", function() {\r
252 var Cls = Ext.define(null, {\r
253 extend: 'Ext.app.ViewController',\r
254 onButtonClick: function() {}\r
255 });\r
256\r
257 var ctrl = new Cls();\r
258 createGrid([getColCfg({\r
259 xtype: 'button',\r
260 handler: 'onButtonClick'\r
261 })], undefined, {\r
262 controller: ctrl\r
263 });\r
264\r
265 spyOn(ctrl, 'onButtonClick');\r
266 jasmine.fireMouseEvent(getWidget(0).getEl().dom, 'click');\r
267 expect(ctrl.onButtonClick).toHaveBeenCalled();\r
268 });\r
269\r
270 it("should handle scope: 'this'", function() {\r
271 Ext.define('spec.Button', {\r
272 extend: 'Ext.button.Button',\r
273 alias: 'widget.subbutton',\r
274 onButtonClick: function() {}\r
275 });\r
276\r
277 createGrid([getColCfg({\r
278 xtype: 'subbutton',\r
279 handler: 'onButtonClick',\r
280 scope: 'this'\r
281 })]);\r
282\r
283 var btn = getWidget(0);\r
284 spyOn(btn, 'onButtonClick');\r
285\r
286 jasmine.fireMouseEvent(getWidget(0).getEl().dom, 'click');\r
287 expect(btn.onButtonClick).toHaveBeenCalled();\r
288\r
289 Ext.undefine('spec.Button');\r
290 });\r
291 });\r
292\r
293 function createBufferedSuite(withBuffered) {\r
294 describe(withBuffered ? "with buffered rendering" : "without buffered rendering", function() {\r
295 function makeGrid(columns, data, cfg) {\r
296 cfg = cfg || {};\r
297 cfg.bufferedRenderer = !!withBuffered;\r
298 createGrid(columns, data, cfg);\r
299 }\r
300\r
301 function checkPositions(start) {\r
302 start = start || 0;\r
303\r
304 var col = grid.down('widgetcolumn'),\r
305 view = grid.getView(),\r
306 selector, cells, len, i;\r
307\r
308 if (col && view.viewReady) {\r
309 selector = col.getCellInnerSelector();\r
310 cells = view.getEl().select(selector, true);\r
311 len = cells.getCount();\r
312\r
313 for (i = start; i < len; ++i) {\r
314 expect(getWidget(i, col).getEl().dom.parentNode).toBe(cells.item(i).dom);\r
315 }\r
316 }\r
317 }\r
318\r
319 describe("basic functionality", function() {\r
320 it("should render a widget for each row", function() {\r
321 makeGrid();\r
322 expect(getWidget(0).isComponent).toBe(true);\r
323 expect(getWidget(1).isComponent).toBe(true);\r
324 expect(getWidget(2).isComponent).toBe(true);\r
325 expect(getWidget(3).isComponent).toBe(true);\r
326 checkPositions();\r
327 });\r
328\r
329 it('should not bust the row height when showing a button', function() {\r
330 var columns = [getColCfg({\r
331 xtype: 'button'\r
332 }), {\r
333 text: 'Data',\r
334 dataIndex: 'b'\r
335 }],\r
336 rowHeight;\r
337\r
338 // Widget column initially hidden\r
339 columns[0].hidden = true;\r
340\r
341 makeGrid(columns);\r
342\r
343 // Capture default, text-only row height\r
344 rowHeight = grid.view.getNode(0).offsetHeight;\r
345\r
346 // Show the widget column with buttons\r
347 colRef[0].show();\r
348\r
349 // Showing the button should NOT change the row's height\r
350 // https://sencha.jira.com/browse/EXTJS-13766\r
351 expect(grid.view.getNode(0).offsetHeight).toBe(rowHeight);\r
352 checkPositions();\r
353 });\r
354\r
355 it("should render the matching xtype", function() {\r
356 makeGrid();\r
357 expect(getWidget(0).getXType()).toBe('button');\r
358 checkPositions();\r
359 });\r
360\r
361 it("should pass in other configurations", function() {\r
362 makeGrid([getColCfg({\r
363 xtype: 'button',\r
364 enableToggle: true,\r
365 pressed: true\r
366 })]);\r
367 \r
368 var widget = getWidget(0);\r
369 expect(widget.pressed).toBe(true);\r
370 expect(widget.enableToggle).toBe(true);\r
371 checkPositions();\r
372 });\r
373\r
374 it("should create a new instance for each row", function() {\r
375 makeGrid();\r
376 var w1 = getWidget(0),\r
377 w2 = getWidget(1),\r
378 w3 = getWidget(2),\r
379 w4 = getWidget(3);\r
380\r
381 expect(w2).not.toBe(w1);\r
382 expect(w3).not.toBe(w1);\r
383 expect(w4).not.toBe(w1);\r
384\r
385 expect(w3).not.toBe(w2);\r
386 expect(w4).not.toBe(w2);\r
387\r
388 expect(w4).not.toBe(w3);\r
389\r
390 checkPositions();\r
391 });\r
392\r
393 it("should set the value of the defaultBindProperty on the widget to the dataIndex", function() {\r
394 makeGrid();\r
395 expect(getWidget(0).getText()).toBe('1a');\r
396 expect(getWidget(1).getText()).toBe('2a');\r
397 expect(getWidget(2).getText()).toBe('3a');\r
398 expect(getWidget(3).getText()).toBe('4a');\r
399 checkPositions();\r
400 });\r
401\r
402 it("should not modify the defaultBindProperty if there is no dataIndex", function() {\r
403 makeGrid([{\r
404 xtype: 'widgetcolumn',\r
405 width: 200, \r
406 widget: {\r
407 xtype: 'button',\r
408 text: 'Foo'\r
409 }\r
410 }]);\r
411 expect(getWidget(0).getText()).toBe('Foo');\r
412 expect(getWidget(1).getText()).toBe('Foo');\r
413 expect(getWidget(2).getText()).toBe('Foo');\r
414 expect(getWidget(3).getText()).toBe('Foo');\r
415 checkPositions();\r
416 });\r
417 });\r
418\r
419 describe("tdCls", function() {\r
420 it("should get the tdCls from the widget", function() {\r
421 makeGrid([getColCfg({\r
422 xtype: 'button',\r
423 getTdCls: function() {\r
424 return 'foo';\r
425 }\r
426 })]);\r
427 expect(view.getCellByPosition({row: 0, column: 0})).toHaveCls('foo');\r
428 expect(view.getCellByPosition({row: 1, column: 0})).toHaveCls('foo');\r
429 expect(view.getCellByPosition({row: 2, column: 0})).toHaveCls('foo');\r
430 expect(view.getCellByPosition({row: 3, column: 0})).toHaveCls('foo');\r
431 });\r
432\r
433 it("should combine a tdCls on the column with the tdCls on the widget", function() {\r
434 var cfg = getColCfg({\r
435 xtype: 'button',\r
436 getTdCls: function() {\r
437 return 'foo';\r
438 }\r
439 });\r
440 cfg.tdCls = 'bar';\r
441 makeGrid([cfg]);\r
442 expect(view.getCellByPosition({row: 0, column: 0})).toHaveCls('foo');\r
443 expect(view.getCellByPosition({row: 0, column: 0})).toHaveCls('bar');\r
444 expect(view.getCellByPosition({row: 1, column: 0})).toHaveCls('foo');\r
445 expect(view.getCellByPosition({row: 1, column: 0})).toHaveCls('bar');\r
446 expect(view.getCellByPosition({row: 2, column: 0})).toHaveCls('foo');\r
447 expect(view.getCellByPosition({row: 2, column: 0})).toHaveCls('bar');\r
448 expect(view.getCellByPosition({row: 3, column: 0})).toHaveCls('foo');\r
449 expect(view.getCellByPosition({row: 3, column: 0})).toHaveCls('bar');\r
450 });\r
451 });\r
452\r
453 describe("onWidgetAttach", function() {\r
454 var spy;\r
455 beforeEach(function() {\r
456 spy = jasmine.createSpy();\r
457 });\r
458\r
459 afterEach(function() {\r
460 spy = null;\r
461 });\r
462\r
463 it("should call the method during render", function() {\r
464 var cfg = getColCfg({\r
465 xtype: 'button'\r
466 });\r
467 cfg.onWidgetAttach = spy;\r
468 makeGrid([cfg]);\r
469 expect(spy.callCount).toBe(store.getCount());\r
470 });\r
471\r
472 it("should pass the column, the widget instance and the record", function() {\r
473 var cfg = getColCfg({\r
474 xtype: 'button'\r
475 });\r
476 cfg.onWidgetAttach = spy;\r
477 makeGrid([cfg]);\r
478\r
479 expect(spy.calls[0].args[0]).toBe(colRef[0]);\r
480 expect(spy.calls[0].args[1].isButton).toBe(true);\r
481 expect(spy.calls[0].args[2]).toBe(store.getAt(0));\r
482\r
483 expect(spy.calls[1].args[0]).toBe(colRef[0]);\r
484 expect(spy.calls[1].args[1].isButton).toBe(true);\r
485 expect(spy.calls[1].args[2]).toBe(store.getAt(1));\r
486\r
487 expect(spy.calls[2].args[0]).toBe(colRef[0]);\r
488 expect(spy.calls[2].args[1].isButton).toBe(true);\r
489 expect(spy.calls[2].args[2]).toBe(store.getAt(2));\r
490\r
491 expect(spy.calls[3].args[0]).toBe(colRef[0]);\r
492 expect(spy.calls[3].args[1].isButton).toBe(true);\r
493 expect(spy.calls[3].args[2]).toBe(store.getAt(3));\r
494 });\r
495\r
496 it("should get called when a new record is added", function() {\r
497 var cfg = getColCfg({\r
498 xtype: 'button'\r
499 });\r
500 cfg.onWidgetAttach = spy;\r
501 makeGrid([cfg]);\r
502 spy.reset();\r
503 var rec = store.insert(2, {})[0],\r
504 spyCall = spy.mostRecentCall;\r
505\r
506 expect(spy.callCount).toBe(1);\r
507 expect(spyCall.args[0]).toBe(colRef[0]);\r
508 expect(spyCall.args[1].isButton).toBe(true);\r
509 expect(spyCall.args[2]).toBe(rec);\r
510 });\r
511\r
512 if (withBuffered) {\r
513 it("should only be called for records in the view", function() {\r
514 var cfg = getColCfg({\r
515 xtype: 'button'\r
516 });\r
517 cfg.onWidgetAttach = spy;\r
518 var data = [],\r
519 i = 0,\r
520 recordSize = 10000;\r
521\r
522 for (i = 1; i <= recordSize; ++i) {\r
523 data.push({\r
524 id: 'rec' + i\r
525 });\r
526 }\r
527 makeGrid([cfg], data);\r
528\r
529 var view = grid.getView(),\r
530 nodes = view.getNodes(),\r
531 firstNode = nodes[0],\r
532 len = nodes.length;\r
533\r
534 expect(spy.callCount).toBeLessThan(recordSize);\r
535 for (i = 0; i < len; ++i) {\r
536 expect(spy.calls[i].args[2]).toBe(store.getAt(i));\r
537 }\r
538 checkPositions();\r
539\r
540 spy.reset();\r
541 // Force it to the end, wait for the re-render\r
542 grid.bufferedRenderer.scrollTo(recordSize * 100);\r
543 waitsFor(function() {\r
544 return view.getNodes()[0] !== firstNode;\r
545 });\r
546\r
547 runs(function() {\r
548 nodes = view.getNodes();\r
549 len = nodes.length;\r
550 var offset = recordSize - len;\r
551\r
552 for (i = 0; i < len; ++i) {\r
553 expect(spy.calls[i].args[2]).toBe(store.getAt(i + offset));\r
554 }\r
555 checkPositions(offset);\r
556 });\r
557 });\r
558 }\r
559\r
560 describe("scope", function() {\r
561 it("should default the scope to the column", function() {\r
562 var cfg = getColCfg({\r
563 xtype: 'button'\r
564 });\r
565 cfg.onWidgetAttach = spy;\r
566 makeGrid([cfg]);\r
567 expect(spy.mostRecentCall.object).toBe(colRef[0]);\r
568 });\r
569\r
570 it("should use a passed scope", function() {\r
571 var cfg = getColCfg({\r
572 xtype: 'button'\r
573 }), o = {};\r
574 cfg.onWidgetAttach = spy;\r
575 cfg.scope = o;\r
576 makeGrid([cfg]);\r
577 expect(spy.mostRecentCall.object).toBe(o);\r
578 });\r
579\r
580 it("should be able to resolve to a view controller method", function() {\r
581 var cfg = getColCfg({\r
582 xtype: 'button'\r
583 });\r
584 var ctrl = new Ext.app.ViewController();\r
585 ctrl.doSomething = spy;\r
586 cfg.onWidgetAttach = 'doSomething';\r
587 makeGrid([cfg], null, {\r
588 controller: ctrl\r
589 });\r
590 expect(spy.callCount).toBe(4);\r
591 });\r
592 });\r
593 });\r
594\r
595 describe("add/remove column", function() {\r
596 it("should render widgets when adding the column dynamically", function() {\r
597 makeGrid([]);\r
598 grid.headerCt.add(getColCfg({\r
599 xtype: 'button'\r
600 }));\r
601 colRef = grid.getColumnManager().getColumns();\r
602 expect(getWidget(0).getText()).toBe('1a');\r
603 expect(getWidget(1).getText()).toBe('2a');\r
604 expect(getWidget(2).getText()).toBe('3a');\r
605 expect(getWidget(3).getText()).toBe('4a');\r
606 checkPositions();\r
607 });\r
608\r
609 it("should not cause an error when removing", function() {\r
610 makeGrid();\r
611 expect(function() {\r
612 grid.headerCt.remove(colRef[0]);\r
613 }).not.toThrow();\r
614 });\r
615\r
616 it("should be able to re-use the column", function() {\r
617 makeGrid();\r
618 grid.headerCt.remove(colRef[0], false);\r
619 grid.headerCt.add(colRef[0]);\r
620 expect(getWidget(0).getText()).toBe('1a');\r
621 expect(getWidget(1).getText()).toBe('2a');\r
622 expect(getWidget(2).getText()).toBe('3a');\r
623 expect(getWidget(3).getText()).toBe('4a');\r
624 checkPositions();\r
625 });\r
626\r
627 it("should be able to move a column to the left", function() {\r
628 makeGrid([{}, getColCfg({\r
629 xtype: 'button'\r
630 })]);\r
631 grid.headerCt.moveBefore(colRef[0], colRef[1]);\r
632 checkPositions();\r
633 });\r
634\r
635 it("should be able to move a column to the right", function() {\r
636 makeGrid([getColCfg({\r
637 xtype: 'button'\r
638 }), {}]);\r
639 grid.headerCt.moveAfter(colRef[1], colRef[0]);\r
640 checkPositions();\r
641 });\r
642 });\r
643\r
644 describe("widget sizing", function() {\r
645 it("should not cause an error if the view is not rendered", function() {\r
646 makeGrid(undefined, undefined, {\r
647 renderTo: null\r
648 });\r
649 expect(function() {\r
650 colRef[0].setWidth(400);\r
651 }).not.toThrow();\r
652 });\r
653\r
654 it("should not cause an error if the store is empty", function() {\r
655 expect(function() {\r
656 makeGrid(undefined, []);\r
657 }).not.toThrow();\r
658 });\r
659\r
660 it("should not cause an error if there are no records in the view", function() {\r
661 makeGrid();\r
662 store.removeAll();\r
663 expect(function() {\r
664 colRef[0].setWidth(400);\r
665 }).not.toThrow();\r
666 });\r
667\r
668 it("should not modify the width if the widget is configured with a width", function() {\r
669 makeGrid([getColCfg({\r
670 xtype: 'button',\r
671 width: 50\r
672 })]);\r
673 colRef = grid.getColumnManager().getColumns();\r
674 expect(getWidget(0).getWidth()).toBe(50);\r
675 expect(getWidget(1).getWidth()).toBe(50);\r
676 expect(getWidget(2).getWidth()).toBe(50);\r
677 expect(getWidget(3).getWidth()).toBe(50);\r
678 });\r
679\r
680 it("should set the width to the column size minus the padding by on initial render", function() {\r
681 makeGrid();\r
682\r
683 var padding = getPadding();\r
684\r
685 expect(getWidget(0).getWidth()).toBe(200 - padding);\r
686 expect(getWidget(1).getWidth()).toBe(200 - padding);\r
687 expect(getWidget(2).getWidth()).toBe(200 - padding);\r
688 expect(getWidget(3).getWidth()).toBe(200 - padding);\r
689 });\r
690\r
691 it("should modify the widget size dynamically", function() {\r
692 makeGrid();\r
693\r
694 var padding = getPadding();\r
695\r
696 colRef[0].setWidth(400);\r
697\r
698 expect(getWidget(0).getWidth()).toBe(400 - padding);\r
699 expect(getWidget(1).getWidth()).toBe(400 - padding);\r
700 expect(getWidget(2).getWidth()).toBe(400 - padding);\r
701 expect(getWidget(3).getWidth()).toBe(400 - padding);\r
702\r
703 });\r
704\r
705 it("should modify the size with a flexed column", function() {\r
706 var col = getColCfg({\r
707 xtype: 'button'\r
708 });\r
709 delete col.width;\r
710 col.flex = 1;\r
711 makeGrid([col]);\r
712\r
713 var padding = getPadding();\r
714 expect(getWidget(0).getWidth()).toBe(1000 - padding);\r
715 expect(getWidget(1).getWidth()).toBe(1000 - padding);\r
716 expect(getWidget(2).getWidth()).toBe(1000 - padding);\r
717 expect(getWidget(3).getWidth()).toBe(1000 - padding);\r
718\r
719 grid.setWidth(600);\r
720\r
721 expect(getWidget(0).getWidth()).toBe(600 - padding);\r
722 expect(getWidget(1).getWidth()).toBe(600 - padding);\r
723 expect(getWidget(2).getWidth()).toBe(600 - padding);\r
724 expect(getWidget(3).getWidth()).toBe(600 - padding);\r
725 });\r
726\r
727 it("should run layouts on components initially and when they are sized", function() {\r
728 var col = getColCfg({\r
729 xtype: 'container',\r
730 layout: 'hbox',\r
731 defaultType: 'component',\r
732 items: [{\r
733 flex: 1,\r
734 html: 'A'\r
735 }, {\r
736 flex: 1,\r
737 html: 'B'\r
738 }]\r
739 }), widget;\r
740\r
741 delete col.dataIndex;\r
742\r
743 makeGrid([col], generateData(2));\r
744\r
745 var padding = getPadding(),\r
746 availWidth = 200 - padding;\r
747\r
748 function expectWidthAndLayout(c, width, counter) {\r
749 expect(c.getWidth()).toBe(width);\r
750 expect(c.componentLayoutCounter).toBe(counter);\r
751 }\r
752\r
753 widget = getWidget(0);\r
754 expectWidthAndLayout(widget, availWidth, 1);\r
755 expectWidthAndLayout(widget.items.getAt(0), availWidth / 2, 1);\r
756 expectWidthAndLayout(widget.items.getAt(1), availWidth / 2, 1);\r
757\r
758 widget = getWidget(1);\r
759 expectWidthAndLayout(widget, availWidth, 1);\r
760 expectWidthAndLayout(widget.items.getAt(0), availWidth / 2, 1);\r
761 expectWidthAndLayout(widget.items.getAt(1), availWidth / 2, 1);\r
762\r
763 colRef[0].setWidth(400);\r
764\r
765 availWidth = 400 - padding;\r
766\r
767 widget = getWidget(0);\r
768 expectWidthAndLayout(widget, availWidth, 2);\r
769 expectWidthAndLayout(widget.items.getAt(0), availWidth / 2, 2);\r
770 expectWidthAndLayout(widget.items.getAt(1), availWidth / 2, 2);\r
771\r
772 widget = getWidget(1);\r
773 expectWidthAndLayout(widget, availWidth, 2);\r
774 expectWidthAndLayout(widget.items.getAt(0), availWidth / 2, 2);\r
775 expectWidthAndLayout(widget.items.getAt(1), availWidth / 2, 2);\r
776 });\r
777\r
778 it("should run layouts when the grid has a pending layout", function() {\r
779 var col = getColCfg({\r
780 xtype: 'component'\r
781 }), widget, count;\r
782\r
783 makeGrid([col], generateData(2));\r
784\r
785 widget = getWidget(0);\r
786 count = widget.componentLayoutCounter;\r
787 Ext.suspendLayouts();\r
788 grid.setWidth(grid.getWidth() + 100);\r
789 colRef[0].setWidth(400);\r
790 Ext.resumeLayouts(true);\r
791 expect(widget.componentLayoutCounter).toBe(count + 1);\r
792 });\r
793 });\r
794\r
795 describe("store modifications", function() {\r
796 describe("before render", function() {\r
797 beforeEach(function() {\r
798 makeGrid(undefined, undefined, {\r
799 renderTo: null\r
800 });\r
801 });\r
802\r
803 it("should not cause an error when adding records", function() {\r
804 expect(function() {\r
805 var rec = store.add({});\r
806 }).not.toThrow();\r
807 });\r
808\r
809 it("should not cause an error when removing items", function() {\r
810 expect(function() {\r
811 var rec = store.removeAt(0);\r
812 }).not.toThrow();\r
813 });\r
814\r
815 it("should not cause an error when updating items", function() {\r
816 expect(function() {\r
817 var rec = store.first().set('a', 'X');\r
818 }).not.toThrow();\r
819 });\r
820\r
821 it("should not cause an error when clearing the store", function() {\r
822 expect(function() {\r
823 store.removeAll();\r
824 }).not.toThrow();\r
825 });\r
826 });\r
827\r
828 describe("after render", function() {\r
829 beforeEach(function() {\r
830 makeGrid();\r
831 });\r
832\r
833 it("should add a new widget when adding a record", function() {\r
834 store.add({\r
835 a: 'New'\r
836 });\r
837 expect(getWidget(4).getText()).toBe('New');\r
838 checkPositions();\r
839 });\r
840\r
841 it("should remove the widget when removing a record", function() {\r
842 store.removeAt(3);\r
843 expect(getWidget(3)).toBeNull();\r
844 checkPositions();\r
845 });\r
846\r
847 it("should update the defaultBindProperty when changing a value", function() {\r
848 store.first().set('a', 'NewValue');\r
849\r
850 expect(getWidget(0).getText()).toBe('NewValue');\r
851 checkPositions();\r
852 });\r
853\r
854 it("should add the cell dirty class when changing a value, and remove it when reverting that change", function() {\r
855 var oldValue = store.first().get('a');\r
856\r
857 store.first().set('a', 'NewValue');\r
858\r
859 // Dirty class should be added to the cell\r
860 expect(view.getCellByPosition({\r
861 row: 0,\r
862 column: 0\r
863 }).hasCls(view.dirtyCls)).toBe(true);\r
864\r
865 store.first().set('a', oldValue);\r
866\r
867 // Dirty class should be removed from the cell\r
868 expect(view.getCellByPosition({\r
869 row: 0,\r
870 column: 0\r
871 }).hasCls(view.dirtyCls)).toBe(false);\r
872 });\r
873\r
874 it('should render with a cell dirty class set if the record is already modified', function() {\r
875 // The beforeEach one cannot be used.\r
876 grid.destroy();\r
877\r
878 makeGrid(null, null, {\r
879 renderTo: null\r
880 });\r
881 store.first().set('a', 'NewValue');\r
882 grid.render(document.body);\r
883 \r
884 // Dirty class should be rendered into the cell\r
885 expect(view.getCellByPosition({\r
886 row: 0,\r
887 column: 0\r
888 }).hasCls(view.dirtyCls)).toBe(true);\r
889 });\r
890\r
891 it("should remove all widgets when calling removeAll", function() {\r
892 store.removeAll();\r
893 expect(getWidget(0)).toBeNull();\r
894 checkPositions();\r
895 });\r
896 });\r
897 });\r
898\r
899 describe("widget decoration", function() {\r
900 it("should add a method to get the column from the widget", function() {\r
901 makeGrid();\r
902 expect(getWidget(0).getWidgetColumn()).toBe(colRef[0]);\r
903 expect(getWidget(1).getWidgetColumn()).toBe(colRef[0]);\r
904 expect(getWidget(2).getWidgetColumn()).toBe(colRef[0]);\r
905 expect(getWidget(3).getWidgetColumn()).toBe(colRef[0]);\r
906 });\r
907\r
908 it("should add a method to get the record from the widget", function() {\r
909 makeGrid();\r
910 expect(getWidget(0).getWidgetRecord()).toBe(store.getAt(0));\r
911 expect(getWidget(1).getWidgetRecord()).toBe(store.getAt(1));\r
912 expect(getWidget(2).getWidgetRecord()).toBe(store.getAt(2));\r
913 expect(getWidget(3).getWidgetRecord()).toBe(store.getAt(3));\r
914 });\r
915\r
916 it("should have the record/column available before the bind is called", function() {\r
917 var fooRec, barRec, col;\r
918\r
919 Ext.define('spec.Button', {\r
920 extend: 'Ext.button.Button',\r
921 alias: 'widget.specbutton',\r
922\r
923 updateText: function(text) {\r
924 col = this.getWidgetColumn();\r
925 if (text === 'foo') {\r
926 fooRec = this.getWidgetRecord();\r
927 } else if (text === 'bar') {\r
928 barRec = this.getWidgetRecord();\r
929 }\r
930 this.callParent(arguments);\r
931 }\r
932 });\r
933\r
934 makeGrid([getColCfg({\r
935 xtype: 'specbutton'\r
936 })], []);\r
937\r
938 store.suspendEvents();\r
939 store.add({\r
940 a: 'foo'\r
941 });\r
942 store.resumeEvents();\r
943 grid.getView().refresh();\r
944\r
945 store.add({\r
946 a: 'bar'\r
947 });\r
948\r
949 expect(col).toBe(colRef[0]);\r
950 expect(fooRec).toBe(store.getAt(0));\r
951 expect(barRec).toBe(store.getAt(1));\r
952\r
953 Ext.undefine('spec.Button');\r
954 });\r
955\r
956 describe("getWidgetRecord", function() {\r
957 it("should have the correct reference when an update causes the view to change", function() {\r
958 makeGrid();\r
959 store.getSorters().add({\r
960 property: 'a'\r
961 });\r
962 store.first().set('a', '5a');\r
963 expect(getWidget(0).getWidgetRecord().getId()).toBe('rec2');\r
964 expect(getWidget(1).getWidgetRecord().getId()).toBe('rec3');\r
965 expect(getWidget(2).getWidgetRecord().getId()).toBe('rec4');\r
966 expect(getWidget(3).getWidgetRecord().getId()).toBe('rec1');\r
967 });\r
968\r
969 it("should have the correct references when removing records", function() {\r
970 makeGrid();\r
971 store.removeAt(1);\r
972 expect(getWidget(0).getWidgetRecord().getId()).toBe('rec1');\r
973 expect(getWidget(1).getWidgetRecord().getId()).toBe('rec3');\r
974 expect(getWidget(2).getWidgetRecord().getId()).toBe('rec4');\r
975 });\r
976\r
977 it("should have the correct references when adding records", function() {\r
978 makeGrid();\r
979 store.insert(1, [{}, {}, {}]);\r
980 expect(getWidget(0).getWidgetRecord().getId()).toBe('rec1');\r
981 expect(getWidget(4).getWidgetRecord().getId()).toBe('rec2');\r
982 expect(getWidget(5).getWidgetRecord().getId()).toBe('rec3');\r
983 expect(getWidget(6).getWidgetRecord().getId()).toBe('rec4');\r
984 });\r
985 });\r
986 });\r
987\r
988 describe("reconfigure", function() {\r
989 it("should be able to reconfigure with adding a column", function() {\r
990 makeGrid([]);\r
991 grid.reconfigure(undefined, [getColCfg({\r
992 xtype: 'button'\r
993 })]);\r
994 colRef = grid.getColumnManager().getColumns();\r
995 expect(getWidget(0).getText()).toBe('1a');\r
996 expect(getWidget(1).getText()).toBe('2a');\r
997 expect(getWidget(2).getText()).toBe('3a');\r
998 expect(getWidget(3).getText()).toBe('4a');\r
999 checkPositions();\r
1000 });\r
1001\r
1002 it("should be able to reconfigure with removing a column", function() {\r
1003 makeGrid();\r
1004 expect(function() {\r
1005 grid.reconfigure(undefined, [{\r
1006 dataIndex: 'a'\r
1007 }]);\r
1008 }).not.toThrow();\r
1009 });\r
1010 });\r
1011\r
1012 describe("destroy", function() {\r
1013 it("should destroy components", function() {\r
1014 makeGrid();\r
1015 var count = Ext.ComponentManager.getCount(),\r
1016 toDestroy = 1 + store.getCount();\r
1017\r
1018 grid.headerCt.remove(colRef[0]);\r
1019 // We're destroying the column + each widget\r
1020 expect(Ext.ComponentManager.getCount()).toBe(count - toDestroy);\r
1021 });\r
1022 });\r
1023\r
1024 describe('on refresh', function () {\r
1025 describe('beforerefresh', function () {\r
1026 it('should recycle the widget dom tree hierarchy when refreshed', function () {\r
1027 // See EXTJS-14874.\r
1028 // We need the view to overflow to cause the bug in IE 8.\r
1029 var data = generateData(100),\r
1030 childNodes;\r
1031\r
1032 makeGrid(null, data, {\r
1033 height: 100\r
1034 });\r
1035\r
1036 // Pick a row that is within the buffere rendered range\r
1037 childNodes = getWidget(1).el.dom.childNodes;\r
1038\r
1039 expect(childNodes.length).toBe(1);\r
1040\r
1041 // This will refresh the view and trigger the bug.\r
1042 grid.store.loadData(data);\r
1043 expect(childNodes.length).toBe(1);\r
1044 });\r
1045 });\r
1046 });\r
1047\r
1048 describe("item removal", function() {\r
1049 it("should recycle dom nodes when items are removed", function() {\r
1050 var data = generateData(100),\r
1051 childNodes;\r
1052\r
1053 makeGrid(null, data, {\r
1054 height: 100\r
1055 });\r
1056\r
1057 // Pick a row that is within the buffere rendered range\r
1058 childNodes = getWidget(1).el.dom.childNodes;\r
1059\r
1060 expect(childNodes.length).toBe(1);\r
1061\r
1062 store.removeAt(1);\r
1063 \r
1064 expect(childNodes.length).toBe(1);\r
1065 });\r
1066 });\r
1067\r
1068 describe("hide/show", function() {\r
1069 it("should not create widgets initially when hidden", function() {\r
1070 var count = 0;\r
1071\r
1072 Ext.define('spec.Foo', {\r
1073 extend: 'Ext.Component',\r
1074 alias: 'widget.foo',\r
1075\r
1076 initComponent: function() {\r
1077 ++count;\r
1078 this.callParent();\r
1079 }\r
1080 });\r
1081\r
1082 var col = getColCfg({\r
1083 xtype: 'foo'\r
1084 });\r
1085 col.hidden = true;\r
1086 makeGrid([col]);\r
1087 // Gets called once during construction to set the tdCls\r
1088 expect(count).toBe(1);\r
1089 Ext.undefine('spec.Foo');\r
1090 });\r
1091\r
1092 it("should size the widgets when hidden initially and then shown", function() {\r
1093 var col = getColCfg({\r
1094 xtype: 'button'\r
1095 });\r
1096 col.hidden = true;\r
1097 makeGrid([col]);\r
1098 colRef[0].show();\r
1099 var padding = getPadding();\r
1100 expect(getWidget(0).getWidth()).toBe(200 - padding);\r
1101 expect(getWidget(1).getWidth()).toBe(200 - padding);\r
1102 expect(getWidget(2).getWidth()).toBe(200 - padding);\r
1103 expect(getWidget(3).getWidth()).toBe(200 - padding);\r
1104 });\r
1105\r
1106 it("should not cause an error when hiding the last leaf column in a grouped header", function() {\r
1107 makeGrid([{\r
1108 columns: [getColCfg({\r
1109 xtype: 'button'\r
1110 })]\r
1111 }]);\r
1112 expect(function() {\r
1113 colRef[0].hide();\r
1114 }).not.toThrow();\r
1115 });\r
1116\r
1117 it("should not cause an error when hiding the group header that contains this widget", function() {\r
1118 makeGrid([{\r
1119 itemId: 'ct',\r
1120 columns: [getColCfg({\r
1121 xtype: 'button'\r
1122 })]\r
1123 }]);\r
1124 expect(function() {\r
1125 grid.down('#ct').hide();\r
1126 }).not.toThrow();\r
1127 });\r
1128 });\r
1129 });\r
1130\r
1131 describe('RadioGroup as a widget', function() {\r
1132 var grid;\r
1133 \r
1134 afterEach(function() {\r
1135 grid.destroy();\r
1136 });\r
1137\r
1138 it("should be able to update value from column's dataIndex", function() {\r
1139 var store = Ext.create('Ext.data.Store', {\r
1140 fields: ['name', 'progress',\r
1141 {\r
1142 name: 'radio',\r
1143 isEqual: function(v1, v2) {\r
1144 return String(v1.value) === String(v2.value);\r
1145 }\r
1146 }\r
1147 ],\r
1148 data: [{\r
1149 name: 'Test 1',\r
1150 progress: 0.10,\r
1151 radio: {\r
1152 "value": 2\r
1153 }\r
1154 }, {\r
1155 name: 'Test 2',\r
1156 progress: 0.23,\r
1157 radio: {\r
1158 "value": 1\r
1159 }\r
1160 }, {\r
1161 name: 'Test 3',\r
1162 progress: 0.86,\r
1163 radio: {\r
1164 "value": 2\r
1165 }\r
1166 }, {\r
1167 name: 'Test 4',\r
1168 progress: 0.31,\r
1169 radio: {\r
1170 "value": 1\r
1171 }\r
1172 }]\r
1173 }),\r
1174 widgetColumn,\r
1175 radioGroup,\r
1176 radio,\r
1177 rec = store.getAt(0);\r
1178\r
1179 grid = Ext.create({\r
1180 xtype: 'grid',\r
1181 title: 'Widget Column Demo',\r
1182 store: store,\r
1183\r
1184 columns: [{\r
1185 text: 'Test Number',\r
1186 dataIndex: 'name',\r
1187 width: 150\r
1188 }, {\r
1189 text: 'Progress',\r
1190 dataIndex: 'progress',\r
1191 width: 100\r
1192 }, {\r
1193 xtype: 'widgetcolumn',\r
1194 header: 'Radio Group',\r
1195 dataIndex: 'radio',\r
1196 width: 170,\r
1197 widget: {\r
1198 xtype: 'radiogroup',\r
1199\r
1200 // The local config means child Radio names are scoped to this RadioGroup\r
1201 local: true,\r
1202 columns: 1,\r
1203 vertical: true,\r
1204 items: [{\r
1205 boxLabel: 'Item 1',\r
1206 name: 'value',\r
1207 inputValue: '1'\r
1208 }, {\r
1209 boxLabel: 'Item 2',\r
1210 name: 'value',\r
1211 inputValue: '2'\r
1212 }],\r
1213 listeners: {\r
1214 change: function(radioGroup, newValue, oldValue) {\r
1215 radioGroup.getWidgetRecord().set('radio', newValue);\r
1216 }\r
1217 }\r
1218 }\r
1219 }],\r
1220 height: 400,\r
1221 width: 600,\r
1222 renderTo: Ext.getBody()\r
1223 });\r
1224 widgetColumn = grid.down('widgetcolumn');\r
1225 radioGroup = widgetColumn.getWidget(rec);\r
1226 radio = radioGroup.child('radio[inputValue=1]');\r
1227 jasmine.fireMouseEvent(radio.inputEl, 'click');\r
1228\r
1229 // Record field must have been updated\r
1230 expect(rec.get('radio').value).toBe('1');\r
1231 });\r
1232 });\r
1233 }\r
1234 createBufferedSuite(false);\r
1235 createBufferedSuite(true);\r
1236});\r