Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'master' of git://github.com/SitePen/dgrid

  • Loading branch information...
commit 94bde4129bf31516978fdb6a2121c6804a209f07 2 parents c7d37a0 + 2e073ba
@kriszyp authored
Showing with 2,904 additions and 365 deletions.
  1. +36 −1 CHANGES.md
  2. +35 −6 ColumnSet.js
  3. +4 −3 Grid.js
  4. +1 −1  Keyboard.js
  5. +12 −7 List.js
  6. +23 −13 OnDemandList.js
  7. +45 −1 README.md
  8. +8 −5 Selection.js
  9. +1 −0  _StoreMixin.js
  10. +2 −0  css/columnset.css
  11. +6 −0 css/extensions/ColumnResizer.css
  12. +4 −0 css/extensions/CompoundColumns.css
  13. +22 −18 editor.js
  14. +19 −9 extensions/ColumnHider.js
  15. +22 −12 extensions/ColumnResizer.js
  16. +129 −12 extensions/CompoundColumns.js
  17. +1 −1  package.json
  18. +337 −30 test/extensions/CompoundColumns.html
  19. +8 −4 test/intern/all.js
  20. +34 −28 test/intern/core/OnDemand-removeRow.js
  21. +0 −27 test/intern/core/createDestroy.js
  22. +236 −0 test/intern/core/observable.js
  23. +3 −1 test/intern/functional.js
  24. +0 −2  test/intern/functional/Keyboard.html
  25. +111 −113 test/intern/functional/Keyboard.js
  26. +58 −53 test/intern/functional/KeyboardTab.js
  27. +78 −0 test/intern/functional/editor-OnDemand.html
  28. +65 −0 test/intern/functional/editor.html
  29. +290 −0 test/intern/functional/editor.js
  30. +130 −0 test/intern/functional/selector.html
  31. +190 −0 test/intern/functional/selector.js
  32. +69 −0 test/intern/functional/util.js
  33. +7 −4 test/intern/intern.js
  34. +12 −0 test/intern/intern.local.js
  35. +323 −0 test/intern/plugins/editor.js
  36. +98 −0 test/intern/plugins/selector.js
  37. +465 −0 test/intern/plugins/tree-expand-promise.js
  38. +20 −14 tree.js
View
37 CHANGES.md
@@ -1,12 +1,23 @@
This document outlines changes since 0.3.0. For older changelogs, see the
[dgrid wiki](https://github.com/SitePen/dgrid/wiki).
-# master (0.3.13-dev)
+# master (0.3.14-dev)
+
+### Extensions
+
+* The `CompoundColumns` extension is now capable of interoperating with the
+ `ColumnHider` and `ColumnResizer` extensions; note that it must be mixed in
+ after these extensions in order to work properly. (#834)
+* Improved performance of first resize with `ColumnResizer`. (#832)
+
+# 0.3.13
## Significant changes
### General/Core
+* `List#destroy` now resets `_started` to `false` to safeguard against debounced
+ rendering-sensitive code running after the instance's DOM is no longer relevant. (#792)
* Added logic to account for `dojo/store/Observable`'s propensity to drop items
at page boundaries, primarily in the `List` module. (#701, #714)
* Items added to stores which should appear at the end of a list or grid will now
@@ -14,12 +25,36 @@ This document outlines changes since 0.3.0. For older changelogs, see the
* Fixed a long-standing regression in the `util/has-css3` module's
`css-transforms3d` test due to a modified classname. (#776, thanks amuraco)
+### Mixins
+
+* Updated `Selection` to prefer `pointer` over `MSPointer` where available.
+ This fixes ctrl+clicking behavior in IE11. Note that this change replaces the
+ `has("mspointer")` feature with `has("pointer")`, which returns `"pointer"`,
+ `"MSPointer"`, or `false`. (#794)
+
+### Column Plugins
+
+* The `expand` method added by the `tree` plugin will now return a promise,
+ resolving after child data has loaded. (#739)
+* The `canEdit` function supported by `editor` columns is now passed the proper
+ up-to-date `value`. (#751)
+
+### Extensions
+
+* The `CompoundColumns` extension is now capable of interoperating with the
+ `ColumnSet` mixin; see `test/extensions/CompoundColumns.html` for examples.
+ (#383)
+
## Other changes and fixes
### General/Core
* Fixed an issue where `sort` would be ignored if it was a function with 0 arity,
such as a hitched function being passed to a Memory store. (#771)
+* Fixed an accessibility bug in non-Firefox browsers by only overriding
+ `bodyNode.tabIndex` specifically for Firefox. (#823)
+* Fixed a `loadingMessage` regression in `OnDemandList` which manifested
+ particularly when `total` is not properly set in `QueryResults`. (#769)
### Mixins
View
41 ColumnSet.js
@@ -9,10 +9,10 @@ function(kernel, declare, lang, Deferred, listen, aspect, query, has, miscUtil,
try{
WheelEvent("wheel");
supported = true;
- }catch(e){ // empty catch block; prevent debuggers from snagging
- }finally{
- return supported;
+ }catch(e){
+ // empty catch block; prevent debuggers from snagging
}
+ return supported;
});
var colsetidAttr = "data-dgrid-column-set-id";
@@ -40,7 +40,31 @@ function(kernel, declare, lang, Deferred, listen, aspect, query, has, miscUtil,
scroller.scrollLeft = scrollLeft < 0 ? 0 : scrollLeft;
}
-
+
+ function getColumnSetSubRows(subRows, columnSetId){
+ // Builds a subRow collection that only contains columns that correspond to
+ // a given column set id.
+ if(!subRows || !subRows.length){
+ return;
+ }
+ var subset = [];
+ var idPrefix = columnSetId + "-";
+ for(var i = 0, numRows = subRows.length; i < numRows; i++){
+ var row = subRows[i];
+ var subsetRow = [];
+ subsetRow.className = row.className;
+ for(var k = 0, numCols = row.length; k < numCols; k++){
+ var column = row[k];
+ // The column id begins with the column set id.
+ if(column.id != null && column.id.indexOf(idPrefix) === 0){
+ subsetRow.push(column);
+ }
+ }
+ subset.push(subsetRow);
+ }
+ return subset;
+ }
+
var horizMouseWheel;
if(!has("touch")){
horizMouseWheel = has("event-mousewheel") || has("event-wheel") ? function(grid){
@@ -108,7 +132,8 @@ function(kernel, declare, lang, Deferred, listen, aspect, query, has, miscUtil,
// iterate through the columnSets
var cell = put(tr, tag + ".dgrid-column-set-cell.dgrid-column-set-" + i +
" div.dgrid-column-set[" + colsetidAttr + "=" + i + "]");
- cell.appendChild(this.inherited(arguments, [tag, each, this.columnSets[i], object]));
+ var subset = getColumnSetSubRows(subRows || this.subRows , i) || this.columnSets[i];
+ cell.appendChild(this.inherited(arguments, [tag, each, subset, object]));
}
return row;
},
@@ -187,15 +212,19 @@ function(kernel, declare, lang, Deferred, listen, aspect, query, has, miscUtil,
}
this.inherited(arguments);
},
+
configStructure: function(){
+ // Squash the column sets together so the grid and other dgrid extensions and mixins can
+ // configure the columns and create any needed subrows.
this.columns = {};
+ this.subRows = [];
for(var i = 0, l = this.columnSets.length; i < l; i++){
- // iterate through the columnSets
var columnSet = this.columnSets[i];
for(var j = 0; j < columnSet.length; j++){
columnSet[j] = this._configColumns(i + "-" + j + "-", columnSet[j]);
}
}
+ this.inherited(arguments);
},
_positionScrollers: function (){
View
7 Grid.js
@@ -391,13 +391,14 @@ function(kernel, declare, listen, has, put, List, miscUtil){
column.field = columnId;
}
columnId = column.id = column.id || (isNaN(columnId) ? columnId : (prefix + columnId));
- if(isArray){ this.columns[columnId] = column; }
-
// allow further base configuration in subclasses
if(this._configColumn){
this._configColumn(column, columnId, rowColumns, prefix);
+ // Allow the subclasses to modify the column id.
+ columnId = column.id;
}
-
+ if(isArray){ this.columns[columnId] = column; }
+
// add grid reference to each column object for potential use by plugins
column.grid = this;
if(typeof column.init === "function"){ column.init(); }
View
2  Keyboard.js
@@ -240,7 +240,7 @@ var Keyboard = declare(null, {
if(focusInfo.active){
// Row/cell was previously focused, so focus the new one immediately
this.focus(newTarget);
- }else{
+ }else if(newTarget.element){
// Row/cell was not focused, but we still need to update tabIndex
// and the element's class to be consistent with the old one
put(newTarget.element, ".dgrid-focus");
View
19 List.js
@@ -1,5 +1,5 @@
-define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/on", "dojo/has", "./util/misc", "dojo/has!touch?./TouchScroll", "xstyle/has-class", "put-selector/put", "dojo/_base/sniff", "xstyle/css!./css/dgrid.css"],
-function(kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){
+define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/dom", "dojo/on", "dojo/has", "./util/misc", "dojo/has!touch?./TouchScroll", "xstyle/has-class", "put-selector/put", "dojo/_base/sniff", "xstyle/css!./css/dgrid.css"],
+function(kernel, declare, dom, listen, has, miscUtil, TouchScroll, hasClass, put){
// Add user agent/feature CSS classes
hasClass("mozilla", "opera", "webkit", "ie", "ie-6", "ie-6-7", "quirks", "no-quirks", "touch");
@@ -226,9 +226,12 @@ function(kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){
}
bodyNode = this.bodyNode = put(domNode, "div.dgrid-scroller");
- // firefox 4 until at least 10 adds overflow: auto elements to the tab index by default for some
- // reason; force them to be not tabbable
- bodyNode.tabIndex = -1;
+ // Firefox 4+ adds overflow: auto elements to the tab index by default;
+ // force them to not be tabbable, but restrict this to Firefox,
+ // since it breaks accessibility support in other browsers
+ if(has("ff")){
+ bodyNode.tabIndex = -1;
+ }
this.headerScrollNode = put(domNode, "div.dgrid-header-scroll.dgrid-scrollbar-width.ui-widget-header");
@@ -399,6 +402,7 @@ function(kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){
delete this._listeners;
}
+ this._started = false;
this.cleanup();
// destroy DOM
put(this.domNode, "!");
@@ -596,8 +600,9 @@ function(kernel, declare, listen, has, miscUtil, TouchScroll, hasClass, put){
}
}
function correctElement(row){
- // If a node has been orphaned, try to retrieve the correct, in-document, element
- if(!row.offsetParent && byId(row.id)){
+ // If a node has been orphaned, try to retrieve the correct in-document element
+ // (use isDescendant since offsetParent is faulty in IE<9)
+ if(!dom.isDescendant(row, self.domNode) && byId(row.id)){
return self.row(row.id.slice(self.id.length + 5)).element;
}
// Fall back to the originally-specified element
View
36 OnDemandList.js
@@ -1,5 +1,5 @@
-define(["./List", "./_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/Deferred", "dojo/on", "./util/misc", "put-selector/put"],
-function(List, _StoreMixin, declare, lang, Deferred, listen, miscUtil, put){
+define(["./List", "./_StoreMixin", "dojo/_base/declare", "dojo/_base/lang", "dojo/_base/Deferred", "dojo/dom", "dojo/on", "./util/misc", "put-selector/put"],
+function(List, _StoreMixin, declare, lang, Deferred, dom, listen, miscUtil, put){
return declare([List, _StoreMixin], {
// summary:
@@ -195,6 +195,9 @@ return declare([List, _StoreMixin], {
}else{
// if total is 0, IE quirks mode can't handle 0px height for some reason, I don't know why, but we are setting display: none for now
preloadNode.style.display = "none";
+ // This is a hack to get Observable to recognize that this is the
+ // last page, like is done in the processScroll function
+ options.count++;
}
if (self._previousScrollPosition) {
@@ -496,11 +499,7 @@ return declare([List, _StoreMixin], {
}
adjustHeight(preload);
- // create a loading node as a placeholder while the data is loaded
- var loadingNode = put(beforeNode, "-div.dgrid-loading[style=height:" + count * grid.rowHeight + "px]"),
- innerNode = put(loadingNode, "div.dgrid-" + (below ? "below" : "above"));
- innerNode.innerHTML = grid.loadingMessage;
- loadingNode.count = count;
+
// use the query associated with the preload node to get the next "page"
if("level" in preload.query){
options.queryLevel = preload.query.level;
@@ -511,6 +510,12 @@ return declare([List, _StoreMixin], {
continue;
}
+ // create a loading node as a placeholder while the data is loaded
+ var loadingNode = put(beforeNode, "-div.dgrid-loading[style=height:" + count * grid.rowHeight + "px]"),
+ innerNode = put(loadingNode, "div.dgrid-" + (below ? "below" : "above"));
+ innerNode.innerHTML = grid.loadingMessage;
+ loadingNode.count = count;
+
// Query now to fill in these rows.
// Keep _trackError-wrapped results separate, since if results is a
// promise, it will lose QueryResults functions when chained by `when`
@@ -619,13 +624,18 @@ return declare([List, _StoreMixin], {
var observers = this.observers;
var observer = observers[thisIndex];
if(observer){
- // First we need to verify that all the rows really have been removed. If there
+ // justCleanup is set to true when the list is being cleaned out. The rows are left in the DOM
+ // and later they are removed altogether. Skip the check for overlapping rows because
+ // in the end, all of the rows will be removed and all of the observers need to be canceled.
+ if(!justCleanup){
+ // We need to verify that all the rows really have been removed. If there
// are overlapping rows, it is possible another element exists
- var rows = observer.rows;
- for(var i = 0; i < rows.length; i++){
- if(rows[i] != rowElement && rows[i].offsetParent){
- // still rows in this list, abandon
- return this.inherited(arguments);
+ var rows = observer.rows;
+ for(var i = 0; i < rows.length; i++){
+ if(rows[i] != rowElement && dom.isDescendant(rows[i], this.domNode)){
+ // still rows in this list, abandon
+ return this.inherited(arguments);
+ }
}
}
observer.cancel();
View
46 README.md
@@ -49,7 +49,7 @@ directory structure like the following:
* `util` (optional, e.g. if pursuing a custom build)
dgrid works best with the latest revision of Dojo 1.7 or higher. As of this
-writing, [Dojo 1.9.1](http://download.dojotoolkit.org/release-1.9.1/) is
+writing, [Dojo 1.9.2](http://download.dojotoolkit.org/release-1.9.2/) is
recommended.
Note that while dgrid supports Dojo 1.8 and 1.9 and may take advantage of features
@@ -105,6 +105,50 @@ Then kick off the runner with the following command:
node node_modules/intern-geezer/runner config=dgrid/test/intern/intern
```
+## Running via local Selenium server
+
+### Windows
+
+Obtain the latest version of the Selenium server and the IE driver server from
+[Selenium's Download page](http://docs.seleniumhq.org/download/). (The IE driver server needs to be
+placed in a folder on your PATH.)
+
+The Selenium server can be started by executing:
+
+```
+java -jar path\to\selenium-server-standalone-<version>.jar
+```
+
+### Mac OS X
+
+The easiest way to obtain the Selenium standalone server for Mac OS X is by
+using [Homebrew](http://brew.sh/). Once Homebrew is installed, run the following
+commands:
+
+```sh
+brew update # ensure you have the lastest formulae
+brew install selenium-server-standalone
+brew install chromedriver # for automating tests in Chrome
+```
+
+Recent versions of `selenium-server-standalone` install a `selenium-server`
+script which can be used to start up the server. For additional information
+(e.g. how to start the server at login), see the output of
+`brew info selenium-server-standalone`.
+
+### Running the tests
+
+Once the Selenium server is running, kick off the Intern test runner with the
+following command (run from the directory containing dgrid):
+
+```
+node node_modules/intern-geezer/runner config=dgrid/test/intern/intern.local
+```
+
+The configuration in `intern.local.js` overrides `intern.js` to not use
+Sauce Connect, and to attempt to run Firefox and Chrome by default (this can
+be customized as desired according to the browsers you have installed).
+
# Community
## Reporting Issues
View
13 Selection.js
@@ -1,8 +1,9 @@
define(["dojo/_base/kernel", "dojo/_base/declare", "dojo/_base/Deferred", "dojo/on", "dojo/has", "dojo/aspect", "./List", "dojo/has!touch?./util/touch", "put-selector/put", "dojo/query", "dojo/_base/sniff"],
function(kernel, declare, Deferred, on, has, aspect, List, touchUtil, put){
-has.add("mspointer", function(global, doc, element){
- return "onmspointerdown" in element;
+has.add("pointer", function(global, doc, element){
+ return "onpointerdown" in element ? "pointer" :
+ "onmspointerdown" in element ? "MSPointer" : false;
});
// Add feature test for user-select CSS property for optionally disabling
@@ -32,8 +33,10 @@ has.add("dom-selectstart", typeof document.onselectstart !== "undefined");
var ctrlEquiv = has("mac") ? "metaKey" : "ctrlKey",
hasUserSelect = has("css-user-select"),
- downType = has("mspointer") ? "MSPointerDown" : "mousedown",
- upType = has("mspointer") ? "MSPointerUp" : "mouseup";
+ hasPointer = has("pointer"),
+ hasMSPointer = hasPointer && hasPointer.slice(0, 2) === "MS",
+ downType = hasPointer ? hasPointer + (hasMSPointer ? "Down" : "down") : "mousedown",
+ upType = hasPointer ? hasPointer + (hasMSPointer ? "Up" : "up") : "mouseup";
function makeUnselectable(node, unselectable){
// Utility function used in fallback path for recursively setting unselectable
@@ -296,7 +299,7 @@ return declare(null, {
select: []
};
- if(has("touch") && !has("mspointer")){
+ if(has("touch") && !has("pointer")){
// listen for touch taps if available
on(this.contentNode, touchUtil.selector(selector, touchUtil.tap), function(evt){
grid._handleSelect(evt, this);
View
1  _StoreMixin.js
@@ -95,6 +95,7 @@ function(kernel, declare, lang, Deferred, listen, aspect, put){
if (column.set){
this._columnsWithSet[column.field] = column;
}
+ this.inherited(arguments);
},
_updateNotifyHandle: function(store){
View
2  css/columnset.css
@@ -2,10 +2,12 @@
overflow: hidden;
width: 100%;
position: relative; /* This is needed because we setting position: relative on cells in the grid for focus in IE7*/
+ height: 100%;
}
.dgrid-column-set-cell {
vertical-align: top;
+ height: 100%;
}
.dgrid-column-set-scroller {
position: absolute;
View
6 css/extensions/ColumnResizer.css
@@ -20,6 +20,12 @@ html.has-ie-6 .dgrid-resize-handle {
border-color: pink;
filter: chroma(color=pink);
}
+html.has-mozilla .dgrid .dgrid-resize-handle:focus,
+html.has-opera .dgrid .dgrid-resize-handle:focus {
+ /* Override focus outline style set in dgrid.css */
+ outline: none;
+}
+
.dgrid-resize-header-container {
height:100%;
}
View
4 css/extensions/CompoundColumns.css
@@ -1,3 +1,7 @@
+.dgrid-spacer-row {
+ height: 0;
+}
+
.dgrid-spacer-row th { /* Need to make these cells zero height/invisible, but still force the table layout */
padding-top: 0;
padding-bottom: 0;
View
40 editor.js
@@ -368,29 +368,33 @@ function edit(cell) {
cellElement = cell.element.contents || cell.element;
if((cmp = column.editorInstance)){ // shared editor (editOn used)
- if(activeCell != cellElement &&
- (!column.canEdit || column.canEdit(cell.row.data, value))){
- activeCell = cellElement;
+ if(activeCell != cellElement){
+ // get the cell value
row = cell.row;
dirty = this.dirty && this.dirty[row.id];
value = (dirty && field in dirty) ? dirty[field] :
column.get ? column.get(row.data) : row.data[field];
-
- showEditor(column.editorInstance, column, cellElement, value);
-
- // focus / blur-handler-resume logic is surrounded in a setTimeout
- // to play nice with Keyboard's dgrid-cellfocusin as an editOn event
- dfd = new Deferred();
- setTimeout(function(){
- // focus the newly-placed control (supported by form widgets and HTML inputs)
- if(cmp.focus){ cmp.focus(); }
- // resume blur handler once editor is focused
- if(column._editorBlurHandle){ column._editorBlurHandle.resume(); }
- dfd.resolve(cmp);
- }, 0);
-
- return dfd.promise;
+ // check to see if the cell can be edited
+ if(!column.canEdit || column.canEdit(cell.row.data, value)){
+ activeCell = cellElement;
+
+ showEditor(column.editorInstance, column, cellElement, value);
+
+ // focus / blur-handler-resume logic is surrounded in a setTimeout
+ // to play nice with Keyboard's dgrid-cellfocusin as an editOn event
+ dfd = new Deferred();
+ setTimeout(function(){
+ // focus the newly-placed control (supported by form widgets and HTML inputs)
+ if(cmp.focus){ cmp.focus(); }
+ // resume blur handler once editor is focused
+ if(column._editorBlurHandle){ column._editorBlurHandle.resume(); }
+ dfd.resolve(cmp);
+ }, 0);
+
+ return dfd.promise;
+ }
}
+
}else if(column.editor){ // editor but not shared; always-on
cmp = cellElement.widget || cellElement.input;
if(cmp){
View
28 extensions/ColumnHider.js
@@ -65,7 +65,7 @@ function(declare, has, listen, miscUtil, put, i18n){
var subRows = this.subRows,
first = true,
- srLength, cLength, sr, c, checkbox;
+ srLength, cLength, sr, c;
delete this._columnHiderFirstCheckbox;
@@ -86,8 +86,10 @@ function(declare, has, listen, miscUtil, put, i18n){
div, checkId, checkbox;
if(col.hidden){
- // Hidden state is true; hide the column.
+ // Hide the column (reset first to avoid short-circuiting logic)
+ col.hidden = false;
this._hideColumn(id);
+ col.hidden = true;
}
// Allow cols to opt out of the hider (e.g. for selector column).
@@ -247,7 +249,8 @@ function(declare, has, listen, miscUtil, put, i18n){
// Hides the column indicated by the given id.
// Use miscUtil function directly, since we clean these up ourselves anyway
- var selectorPrefix = "#" + miscUtil.escapeCssIdentifier(this.domNode.id) + " .dgrid-column-",
+ var grid = this,
+ selectorPrefix = "#" + miscUtil.escapeCssIdentifier(this.domNode.id) + " .dgrid-column-",
tableRule; // used in IE8 code path
if (this._columnHiderRules[id]) {
@@ -262,21 +265,28 @@ function(declare, has, listen, miscUtil, put, i18n){
window.setTimeout(function(){
tableRule.remove();
+ grid.resize();
}, 0);
}
},
+ _showColumn: function(id){
+ // summary:
+ // Shows the column indicated by the given id
+ // (by removing the rule responsible for hiding it).
+
+ if(this._columnHiderRules[id]){
+ this._columnHiderRules[id].remove();
+ delete this._columnHiderRules[id];
+ }
+ },
+
_updateColumnHiddenState: function(id, hidden){
// summary:
// Performs internal work for toggleColumnHiddenState; see the public
// method for more information.
- if(!hidden){
- this._columnHiderRules[id] && this._columnHiderRules[id].remove();
- delete this._columnHiderRules[id];
- }else{
- this._hideColumn(id);
- }
+ this[hidden ? '_hideColumn' : '_showColumn'](id);
// Update hidden state in actual column definition,
// in case columns are re-rendered.
View
34 extensions/ColumnResizer.js
@@ -65,20 +65,26 @@ function subRowAssoc(subRows){
return associations;
}
-function resizeColumnWidth(grid, colId, width, parentType){
+function resizeColumnWidth(grid, colId, width, parentType, doResize){
// don't react to widths <= 0, e.g. for hidden columns
if(width <= 0){ return; }
var column = grid.columns[colId],
- event = {
- grid: grid,
- columnId: colId,
- width: width,
- bubbles: true,
- cancelable: true
- },
+ event,
rule;
+ if(!column){
+ return;
+ }
+
+ event = {
+ grid: grid,
+ columnId: colId,
+ width: width,
+ bubbles: true,
+ cancelable: true
+ };
+
if(parentType){
event.parentType = parentType;
}
@@ -105,7 +111,11 @@ function resizeColumnWidth(grid, colId, width, parentType){
// keep a reference for future removal
grid._columnSizes[colId] = rule;
- grid.resize();
+
+ if(doResize !== false){
+ grid.resize();
+ }
+
return true;
}
}
@@ -256,8 +266,8 @@ return declare(null, {
put(headerTextNode, childNodes[0]);
}
- put(colNode, headerTextNode, "div.dgrid-resize-handle.resizeNode-"+id).columnId =
- assoc ? assoc[id] : id;
+ put(colNode, headerTextNode, "div.dgrid-resize-handle.resizeNode-"+id).columnId =
+ assoc && assoc[id] || id;
}
if(!grid.mouseMoveListen){
@@ -346,7 +356,7 @@ return declare(null, {
// Set a baseline size for each column based on
// its original measure
colNodes.forEach(function(colNode, i){
- this.resizeColumnWidth(colNode.columnId, colWidths[i]);
+ resizeColumnWidth(this, colNode.columnId, colWidths[i], null, false);
}, this);
this._resizedColumns = true;
View
141 extensions/CompoundColumns.js
@@ -1,9 +1,10 @@
define([
"dojo/_base/lang",
"dojo/_base/declare",
- "dgrid/util/misc",
+ "dojo/sniff",
+ "../util/misc",
"xstyle/css!../css/extensions/CompoundColumns.css"
-], function(lang, declare, miscUtil){
+], function(lang, declare, has, miscUtil){
return declare(null, {
// summary:
// Extension allowing for specification of columns with additional
@@ -25,28 +26,36 @@ define([
// the first row is a special spacer row
var columns = (this.subRows && this.subRows[0]) || this.columns,
headerRows = [[]],
+ topHeaderRow = headerRows[0],
contentColumns = [];
// This first row is spacer row that will be made invisible (zero height)
// with CSS, but it must be rendered as the first row since that is what
// the table layout is driven by.
headerRows[0].className = "dgrid-spacer-row";
- function processColumns(columns, level, hasLabel){
+ function processColumns(columns, level, hasLabel, parent){
var numColumns = 0,
noop = function(){},
- column, children, hasChildLabels;
+ children,
+ hasChildLabels;
- function processColumn(column, i){
+ function processColumn(column){
children = column.children;
- hasChildLabels = column.children && (column.showChildHeaders !== false);
+ hasChildLabels = children && (column.showChildHeaders !== false);
+ // Set a reference to the parent column so later the children's ids can
+ // be updated to indicate the parent-child relationship.
+ column.parentColumn = parent;
if(children){
- // it has children, recursively process the children
- numColumns += (column.colSpan = processColumns(children, level + 1, hasChildLabels));
+ // it has children
+ // make sure the column has an id
+ if(column.id == null){
+ column.id = ((parent && parent.id) || level-1) + "-" + topHeaderRow.length;
+ }
}else{
// it has no children, it is a normal header, add it to the content columns
contentColumns.push(column);
// add each one to the first spacer header row for proper layout of the header cells
- headerRows[0].push(lang.delegate(column, {renderHeaderCell: noop}));
+ topHeaderRow.push(lang.delegate(column, {renderHeaderCell: noop}));
numColumns++;
}
if(!hasChildLabels){
@@ -54,12 +63,20 @@ define([
// we define the rowSpan as a negative, the number of levels less than the total number of rows, which we don't know yet
column = lang.delegate(column, {rowSpan: -level});
}
+
+ if(children){
+ // Recursively process the children; this is specifically
+ // performed *after* any potential lang.delegate calls
+ // so the parent reference will receive additional info
+ numColumns += (column.colSpan =
+ processColumns(children, level + 1, hasChildLabels, column));
+ }
+
// add the column to the header rows at the appropriate level
if(hasLabel){
(headerRows[level] || (headerRows[level] = [])).push(column);
}
}
-
miscUtil.each(columns, processColumn, this);
return numColumns;
}
@@ -80,11 +97,111 @@ define([
}
// we need to set this to be used for subRows, so we make it a single row
contentColumns = [contentColumns];
- // set our header rows so that the grid will use the alternate header row
+ // set our header rows so that the grid will use the alternate header row
// configuration for rendering the headers
- contentColumns.headerRows = headerRows;
+ contentColumns.headerRows = headerRows;
this.subRows = contentColumns;
this.inherited(arguments);
+ },
+
+ renderHeader: function(){
+ var i,
+ columns = this.subRows[0],
+ headerColumns = this.subRows.headerRows[0];
+
+ this.inherited(arguments);
+
+ // The object delegation performed in configStructure unfortunately
+ // "protects" the original column definition objects (referenced by
+ // columns and subRows) from obtaining headerNode information, so
+ // copy them back in.
+ for(i = columns.length; i--;){
+ columns[i].headerNode = headerColumns[i].headerNode;
+ }
+ },
+
+ _configColumn: function(column, columnId, rowColumns, prefix){
+ // Updates the id on a column definition that is a child to include
+ // the parent's id.
+ var parent = column.parentColumn;
+ if(parent){
+ // Adjust the id to incorporate the parent's id.
+ // Remove the prefix if it was used to create the id
+ var id = columnId.indexOf(prefix) === 0 ? columnId.substring(prefix.length) : columnId;
+ prefix = parent.id + "-";
+ columnId = column.id = prefix + id;
+ }
+ this.inherited(arguments, [column, columnId, rowColumns, prefix]);
+ },
+
+ _updateCompoundHiddenStates: function(id, hidden){
+ // summary:
+ // Called from _hideColumn and _showColumn (for ColumnHider)
+ // to adjust parent header cells
+
+ var column = this.columns[id],
+ colSpan;
+
+ if(column && column.hidden == hidden){
+ // Avoid redundant processing (since it would cause colSpan skew)
+ return;
+ }
+
+ // column will be undefined when this is called for parents
+ while(column && column.parentColumn){
+ // Update colSpans / hidden state of parents
+ column = column.parentColumn;
+ colSpan = column.colSpan = column.colSpan + (hidden ? -1 : 1);
+
+ if(colSpan){
+ column.headerNode.colSpan = colSpan;
+ }
+ if(colSpan === 1 && !hidden){
+ this._showColumn(column.id);
+ }else if(!colSpan && hidden){
+ this._hideColumn(column.id);
+ }
+ }
+ },
+
+ _hideColumn: function(id){
+ var self = this;
+
+ this._updateCompoundHiddenStates(id, true);
+ this.inherited(arguments);
+
+ if(has("ff")){
+ // Firefox causes display quirks in certain situations;
+ // avoid them by forcing reflow of the header
+ this.headerNode.style.display = "none";
+ setTimeout(function(){
+ self.headerNode.style.display = "";
+ self.resize();
+ }, 0);
+ }
+ },
+
+ _showColumn: function(id){
+ this._updateCompoundHiddenStates(id, false);
+ this.inherited(arguments);
+ },
+
+ _getResizedColumnWidths: function(){
+ // Overrides ColumnResizer method to report the total width and
+ // last column correctly for CompoundColumns structures
+
+ var total = 0,
+ columns = this.columns,
+ id;
+
+ for(id in columns){
+ total += columns[id].headerNode.offsetWidth;
+ }
+
+ return {
+ totalWidth: total,
+ lastColId: this.subRows[0][this.subRows[0].length - 1].id
+ };
}
});
});
View
2  package.json
@@ -1,7 +1,7 @@
{
"name": "dgrid",
"author": "Kris Zyp",
- "version": "0.3.13-dev",
+ "version": "0.3.14-dev",
"description": "A lightweight, mobile-ready, data-driven, modular widget designed for lists and grids",
"licenses": [
{
View
367 test/extensions/CompoundColumns.html
@@ -9,58 +9,365 @@
@import "../../css/skins/claro.css";
.dgrid {
margin: 10px;
- }
- #grid {
width: 80%;
}
- #grid .field-col1 {
+
+ .field-col1 {
width: 20%;
}
- #grid .field-col2 {
+ .field-col2 {
width: 20%;
}
- #grid .field-col3 {
+ .field-col3 {
width: auto;
}
- #grid .field-col4 {
+ .field-col4 {
width: 16%;
}
- #grid .field-col5 {
+ .field-col5 {
width: 50px;
}
+
+ #gridcscc {
+ width: 800px;
+ }
+
+ #gridcscc .field-col1,
+ #gridcscc .field-col2,
+ #gridcscc .field-col3,
+ #gridcscc .field-col4,
+ #gridcscc .field-col5 {
+ width: 300px;
+ }
+
+ .dgrid-column-set-0 {
+ width: 50%;
+ }
+
+ #gridcscc .dgrid-header .dgrid-row-table {
+ height: 45px; /* For IE */
+ }
</style>
+ <script src="../../../dojo/dojo.js" data-dojo-config="async: true"></script>
<script>
- var start= new Date().getTime();
- </script>
- <script src="../../../dojo/dojo.js"
- data-dojo-config="async: true"></script>
- <script>
- require(["dgrid/OnDemandGrid", "dgrid/extensions/CompoundColumns", "dgrid/Selection", "dgrid/Keyboard", "dojo/_base/declare", "dojo/_base/lang", "dgrid/test/data/base", "dojo/domReady!"],
- function(Grid, CompoundColumns, Selection, Keyboard, declare, lang, testStore){
- //columns1 = lang.clone(columns);
+ require(["dgrid/OnDemandGrid", "dgrid/extensions/CompoundColumns",
+ "dgrid/ColumnSet", "dgrid/Selection", "dgrid/Keyboard",
+ "dgrid/extensions/ColumnResizer", "dgrid/extensions/ColumnHider",
+ "dojo/_base/declare", "dojo/_base/lang", "dojo/on",
+ "dgrid/test/data/base", "dojo/domReady!"],
+ function(Grid, CompoundColumns, ColumnSet, Selection, Keyboard, ColumnResizer, ColumnHider,
+ declare, lang, on, testStore){
+
+ function byId(id){
+ return document.getElementById(id);
+ }
+
+ function getColumns(){
+ return [
+ { field: 'col1', label: 'First Column' },
+ { label: "Full Name", children: [
+ { label: 'Given', children: [
+ { field: 'col2', label: 'First' },
+ { field: 'col5', label: 'Middle', sortable: false }
+ ] },
+ { field: 'col3', label: 'Last' }
+ ] },
+ { label: "No Sub-Headers", className: "noSub", showChildHeaders: false, children: [
+ { field: 'col4' },
+ { field: 'col5', formatter: function(data){ return Math.round(data); } }
+ ] },
+ { field:'col1', label: 'Last Column' }
+ ];
+ }
+
+ function getHiddenColumns() {
+ var columns = getColumns();
+ // Hide some columns to test that the parent span update
+ // logic still executes correctly
+ columns[1].children[0].children[1].hidden = true;
+ columns[2].children[1].hidden = true;
+ return columns;
+ }
+
+ function getColumnSets(set){
+ if(set === 2){
+ return [
+ [
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 3', field: 'col3' }
+ ]
+ ],
+ [
+ [
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4' },
+ { label: 'Column 5', field: 'col5' }
+ ]
+ }
+ ]
+ ]
+ ];
+ }else if(set === 3){
+ return [
+ [
+ [
+ {
+ label: 'Columns 1 and 2',
+ children:
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 2', field: 'col2', sortable: false }
+ ]
+ }
+ ]
+ ],
+ [
+ [
+ {
+ label: "Columns 3 and 4",
+ children: [
+ { label: 'Column 3', field: 'col3' },
+ { label: 'Column 4', field: 'col4' }
+ ]
+ }
+ ]
+ ],
+ [
+ [
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4' },
+ { label: 'Column 5', field: 'col5' }
+ ]
+ }
+ ]
+ ]
+ ];
+ }else if(set === 4){
+ return [
+ [
+ [
+ {
+ label: 'Columns 1 and 2',
+ children:
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 2', field: 'col2', sortable: false }
+ ]
+ }
+ ]
+ ],
+ [
+ [
+ {
+ label: "Columns 4 & 5, 3, 4, 4 & 5",
+ children: [
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4' },
+ { label: 'Column 5', field: 'col5' }
+ ]
+ },
+ { label: 'Column 3', field: 'col3' },
+ { label: 'Column 4', field: 'col4' },
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4' },
+ { label: 'Column 5', field: 'col5' }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ ];
+ }else{
+ // Set 1 / default
+ return [
+ [
+ [
+ {
+ label: 'Columns 1 and 2',
+ children:
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 2', field: 'col2', sortable: false }
+ ]
+ },
+ { label: 'Column 4', field: 'col4' }
+ ]
+ ],
+ [
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 4', field: 'col4' }
+ ]
+ ]
+ ];
+ }
+ }
+
+ function getHiddenColumnSets(){
+ // Variant of getColumnSets(4)
+ return [
+ [
+ [
+ {
+ label: 'Columns 1 and 2',
+ children:
+ [
+ { label: 'Column 1', field: 'col1' },
+ { label: 'Column 2', field: 'col2', sortable: false, hidden: true }
+ ]
+ }
+ ]
+ ],
+ [
+ [
+ {
+ label: "Columns 4 & 5, 3, 4, 4 & 5",
+ children: [
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4', hidden: true },
+ { label: 'Column 5', field: 'col5', hidden: true }
+ ]
+ },
+ { label: 'Column 3', field: 'col3', hidden: true },
+ { label: 'Column 4', field: 'col4' },
+ {
+ label: "Columns 4 and 5",
+ children: [
+ { label: 'Column 4', field: 'col4' },
+ { label: 'Column 5', field: 'col5', hidden: true }
+ ]
+ }
+ ]
+ }
+ ]
+ ]
+ ];
+ }
- var CompoundedGrid = declare([Grid, CompoundColumns, Selection, Keyboard]);
- window.grid = new CompoundedGrid({
+ window.grid = new (declare([Grid, Selection, Keyboard, CompoundColumns]))({
store: testStore,
- columns: [
- {field: 'col1', label: 'First Column'},
- {label: "Full Name", children: [
- {label: 'Given', children:[
- {field: 'col2', label: 'First'},
- {field: 'col5', label: 'Middle', sortable: false}]},
- {field: 'col3', label: 'Last'}]},
- {label: "No Sub-Headers", className: "noSub", showChildHeaders: false, children: [
- {field: 'col4'},
- {field: 'col5', formatter: function(data){ return Math.round(data); }}
- ]},
- {field:'col1', label: 'Last Column'}
- ]
+ columns: getColumns()
}, "grid");
+
+ window.gridcscc = new (declare([Grid, CompoundColumns, ColumnSet, Selection, Keyboard]))({
+ store: testStore,
+ columnSets: getColumnSets(1)
+ }, "gridcscc");
+
+ on(byId("btnCsccColumnSets1"), "click", function(){
+ gridcscc.set("columnSets", getColumnSets(1));
+ });
+ on(byId("btnCsccColumnSets2"), "click", function(){
+ gridcscc.set("columnSets", getColumnSets(2));
+ });
+ on(byId("btnCsccColumnSets3"), "click", function(){
+ gridcscc.set("columnSets", getColumnSets(3));
+ });
+ on(byId("btnCsccColumnSets4"), "click", function(){
+ gridcscc.set("columnSets", getColumnSets(4));
+ });
+
+
+ var form = byId("formCombined"),
+ gridCombined,
+ hasCombinedColumnSets;
+ function createCombinedGrid(event){
+ var mixins = [Grid],
+ args = { store: testStore };
+
+ hasCombinedColumnSets = form.elements.set.checked;
+
+ if(event){
+ event.preventDefault();
+ }
+
+ // Note that order of mixins is important with CompoundColumns,
+ // since it extends ColumnHider and ColumnResizer methods,
+ // but needs to be extended by ColumnSet for structure.
+
+ if(form.elements.hider.checked){
+ mixins.push(ColumnHider);
+ }
+ if(form.elements.resizer.checked){
+ mixins.push(ColumnResizer);
+ }
+ mixins.push(CompoundColumns);
+
+ if(hasCombinedColumnSets){
+ mixins.push(ColumnSet);
+ args.columnSets = getColumnSets(4);
+ }else{
+ args.columns = getColumns();
+ }
+
+ if(gridCombined){
+ gridCombined.destroy();
+ }
+ gridCombined = window.gridCombined = new (declare(mixins))(args);
+ document.body.insertBefore(gridCombined.domNode, form);
+ gridCombined.startup();
+
+ if(event){
+ // If called via form submission, keep the browser
+ // scrolled to the form at the bottom
+ // (otherwise it will jump up due to the grid
+ // being destroyed and replaced)
+ form.scrollIntoView();
+ }
+ }
+ on(form, "submit", createCombinedGrid);
+ createCombinedGrid();
+
+ on(byId("btnChcrResetColumns"), "click", function(){
+ if(hasCombinedColumnSets){
+ gridCombined.set("columnSets", getColumnSets(4));
+ }else{
+ gridCombined.set("columns", getColumns());
+ }
+ });
+ on(byId("btnChcrHideColumns"), "click", function(){
+ if(hasCombinedColumnSets){
+ gridCombined.set("columnSets", getHiddenColumnSets());
+ }else{
+ gridCombined.set("columns", getHiddenColumns());
+ }
+ });
});
</script>
</head>
<body class="claro">
<h2>A basic grid with compound columns</h2>
<div id="grid"></div>
+
+ <h2>A grid with column sets and compound columns</h2>
+ <div id="gridcscc"></div>
+ <p>Buttons to test resetting columnSets:
+ <button id="btnCsccColumnSets1">set 1</button>
+ <button id="btnCsccColumnSets2">set 2</button>
+ <button id="btnCsccColumnSets3">set 3</button>
+ <button id="btnCsccColumnSets4">set 4</button>
+ </p>
+
+ <h2>A grid with compound columns + column hiding, column resizing, and/or column sets</h2>
+ <form id="formCombined">
+ <label><input type="checkbox" name="set" checked> ColumnSet</label>
+ <label><input type="checkbox" name="hider" checked> ColumnHider</label>
+ <label><input type="checkbox" name="resizer" checked> ColumnResizer</label>
+ <button type="submit">Re-create grid</button>
+ <button type="button" id="btnChcrResetColumns">Reset columns</button>
+ <button type="button" id="btnChcrHideColumns">Set hidden columns</button>
+ </form>
</body>
</html>
View
12 test/intern/all.js
@@ -3,10 +3,14 @@ define([
'intern/node_modules/dojo/has!host-browser?./core/addCssRule',
'intern/node_modules/dojo/has!host-browser?./core/setClass',
'intern/node_modules/dojo/has!host-browser?./core/columns',
- 'intern/node_modules/dojo/has!host-browser?./mixins/Keyboard',
- 'intern/node_modules/dojo/has!host-browser?./mixins/Selection',
'intern/node_modules/dojo/has!host-browser?./core/stores',
'intern/node_modules/dojo/has!host-browser?./core/_StoreMixin',
+ 'intern/node_modules/dojo/has!host-browser?./core/observable',
'intern/node_modules/dojo/has!host-browser?./core/OnDemand-removeRow',
- 'intern/node_modules/dojo/has!host-browser?./extensions/Pagination'
-], function(){});
+ 'intern/node_modules/dojo/has!host-browser?./plugins/editor',
+ 'intern/node_modules/dojo/has!host-browser?./plugins/selector',
+ 'intern/node_modules/dojo/has!host-browser?./plugins/tree-expand-promise',
+ 'intern/node_modules/dojo/has!host-browser?./extensions/Pagination',
+ 'intern/node_modules/dojo/has!host-browser?./mixins/Keyboard',
+ 'intern/node_modules/dojo/has!host-browser?./mixins/Selection'
+], function(){});
View
62 test/intern/core/OnDemand-removeRow.js
@@ -17,7 +17,7 @@ define([
function testInitialObservers(list, comment){
var observers = list.observers;
arrayUtil.forEach([true, true, true], function(test, i){
- assert.isTrue(!!observers[i] === test, [comment, "index is " + i + ", Expected is " + test]);
+ assert.strictEqual(!!observers[i], test, [comment, "index is " + i + ", Expected is " + test]);
});
}
@@ -33,7 +33,7 @@ define([
test.beforeEach(function(){
var data = [];
- for(var i = 0; i < 1000; i++){
+ for(var i = 0; i < 100; i++){
data.push({id: i, value: i});
}
@@ -41,12 +41,12 @@ define([
list = new OnDemandList({
id: "list1",
store: store,
- queryRowsOverlap: 0,
+ queryRowsOverlap: 2,
renderRow: function(object){
return put("div", object.value);
},
- minRowsPerPage: 10,
- maxRowsPerPage: 10
+ minRowsPerPage: 12,
+ maxRowsPerPage: 12
});
put(document.body, list.domNode);
put(list.domNode, "[style=height:300px]");
@@ -57,56 +57,62 @@ define([
list.destroy();
});
+ test.test("OnDemandList w/observers - remove 1 observer worth", function(){
+ testInitialObservers(list, "Initial");
+ for(var i = 0; i < 9; i++){
+ var rowNode = document.getElementById("list1-row-" + i);
+ assert.strictEqual(0, rowNode.observerIndex, "Row's observerIndex");
+ list.removeRow(rowNode);
+ testInitialObservers(list, "Iteration " + i);
+ }
+
+ list.removeRow(document.getElementById("list1-row-9"));
+ assert.isFalse(!!list.observers[0], "First observer should not exist.");
+ assert.isTrue(!!list.observers[1], "Second observer should exist.");
+ assert.isTrue(!!list.observers[2], "Third observer should exist.");
+ });
+
+ test.test("OnDemandList w/observers and overlap - remove 2 observer worth", function(){
+ testInitialObservers(list, "Initial");
+ for(var i = 0; i < 20; i++){
+ var rowNode = document.getElementById("list1-row-" + i);
+ list.removeRow(rowNode);
+ }
+ assert.isFalse(!!list.observers[0], "First observer should not exist.");
+ assert.isFalse(!!list.observers[1], "Second observer should not exist.");
+ assert.isTrue(!!list.observers[2], "Third observer should exist.");
+ });
+
test.test("OnDemandList w/observers - remove all, clean up only", function(){
- var countRefs = lang.partial(countObserverReferences, "list1");
testInitialObservers(list, "Initial");
- assert.strictEqual(10, countRefs(0));
- assert.strictEqual(10, countRefs(1));
for(var i = 0; i < 9; i++){
var rowNode = document.getElementById("list1-row-" + i);
assert.strictEqual(0, rowNode.observerIndex, "Row's observerIndex");
- assert.strictEqual(10 - i, countRefs(0), "Iteration " + i + ": observer reference count");
list.removeRow(rowNode, true);
testInitialObservers(list, "Iteration " + i);
- assert.strictEqual(9 - i, countRefs(0), "Iteration " + i + ": observer reference count");
- assert.strictEqual(10, countRefs(1));
}
list.removeRow(document.getElementById("list1-row-9"), true);
- assert.strictEqual(0, countRefs(0));
- assert.strictEqual(10, countRefs(1));
- arrayUtil.forEach([false, true, true], function(test, i){
- var observers = list.observers;
- assert.isTrue(!!observers[i] === test, ["i: " + i + ", test = " + test, observers]);
- });
+ assert.isFalse(!!list.observers[0], "First observer should not exist.");
+ assert.isTrue(!!list.observers[1], "Second observer should exist.");
+ assert.isTrue(!!list.observers[2], "Third observer should exist.");
});
test.test("OnDemandList w/observers - remove last 2, clean up only", function(){
- var countRefs = lang.partial(countObserverReferences, "list1");
// Removing the last two rows from observer #0 should not cancel the observer.
testInitialObservers(list, "Initial");
- assert.strictEqual(10, countRefs(0));
- assert.strictEqual(10, countRefs(1));
list.removeRow(document.getElementById("list1-row-7"), true);
testInitialObservers(list, "Removed row 8");
- assert.strictEqual(9, countRefs(0));
- assert.strictEqual(10, countRefs(1));
list.removeRow(document.getElementById("list1-row-8"), true);
testInitialObservers(list, "Removed row 9");
- assert.strictEqual(8, countRefs(0));
- assert.strictEqual(10, countRefs(1));
list.removeRow(document.getElementById("list1-row-1"), true);
testInitialObservers(list, "Removed row 1");
- assert.strictEqual(7, countRefs(0));
- assert.strictEqual(10, countRefs(1));
list.removeRow(document.getElementById("list1-row-0"), true);
testInitialObservers(list, "Removed row 0");
- assert.strictEqual(6, countRefs(0));
- assert.strictEqual(10, countRefs(1));
});
});
});
View
27 test/intern/core/createDestroy.js
@@ -27,32 +27,5 @@ define([
assert.notStrictEqual(document.body, list.parentNode,
"List is removed from body after destroy");
});
-
- test.test("editor grid", function(){
- // make sure the registry is initially empty
- assert.strictEqual(0, registry.length,
- "dijit registry should have no entries before creating grid");
-
- // build a grid with editors, place it, and render
- var grid = new Grid({
- columns: {
- order: "step",
- name: editor({}, TextBox, "dblclick"),
- description: editor({ label: "what to do", sortable: false }, TextBox)
- }
- });
- document.body.appendChild(grid.domNode);
- grid.startup();
- grid.renderArray(testOrderedData);
-
- // check the registry
- assert.strictEqual(testOrderedData.length + 1, registry.length,
- "dijit registry has 1 entry per row plus 1 shared editor widget");
-
- // kill and check the registry again
- grid.destroy();
- assert.strictEqual(0, registry.length,
- "dijit registry has 0 entries after destroy");
- });
});
});
View
236 test/intern/core/observable.js
@@ -0,0 +1,236 @@
+define([
+ "intern!tdd",
+ "intern/chai!assert",
+ "dojo/_base/declare",
+ "dojo/query",
+ "dojo/store/Memory",
+ "dojo/store/Observable",
+ "dgrid/OnDemandList",
+ "put-selector/put"
+], function(test, assert, declare, query, Memory, Observable, OnDemandList, put){
+
+ var widget,
+ storeCounter = 0;
+
+ function destroyWidget(){
+ if(widget){
+ widget.destroy();
+ widget = null;
+ }
+ }
+
+ function indexToId(index){
+ return (index + 1) * 10;
+ }
+
+ function createData(numStoreItems){
+ var data = [];
+ for(var i = 0; i < numStoreItems; i++){
+ var id = indexToId(i);
+ data.push({id: id, value: "Value " + id + " / Store " + storeCounter});
+ }
+ return data;
+ }
+
+ function createStore(numStoreItems){
+ storeCounter++;
+ return Observable(new Memory({
+ data: createData(numStoreItems)
+ }));
+ }
+
+ function createList(numStoreItems, itemsPerQuery, overlap){
+ widget = new OnDemandList({
+ store: createStore(numStoreItems),
+ minRowsPerPage: itemsPerQuery,
+ maxRowsPerPage: itemsPerQuery,
+ queryRowsOverlap: overlap,
+ renderRow: function(object){
+ return put("div", object.value);
+ },
+ sort: "id"
+ });
+ document.body.appendChild(widget.domNode);
+ widget.startup();
+ }
+
+ function itemTest(itemAction, index, numToModify, backwards){
+ // Creates a single test case for performing an action on numToModify rows/items.
+ var description = itemAction.actionName + " " + numToModify + " item" + (numToModify > 1 ? "s" : "") +
+ " starting at index " + index + ", in " + (backwards ? "decreasing" : "increasing") + " order";
+
+ numToModify = numToModify || 1;
+
+ test.test(description, function(){
+ var i,
+ cnt,
+ step = function(){
+ cnt++;
+ backwards ? i-- : i++;
+ },
+ tmp,
+ expectedValues = [],
+ msgPrefix;
+
+ function testRow(element, i){
+ var expectedValue = expectedValues[i];
+ if(expectedValue == null || expectedValue.deleted){
+ assert.isTrue(element == null, msgPrefix + "row at index " + i + " should not be found");
+ }else{
+ expectedValue = expectedValue.value;
+ assert.isTrue(element != null, msgPrefix + "row at index " + i + " with an expected value of \"" + expectedValue + "\" is missing");
+ assert.strictEqual(expectedValue, element.innerHTML, msgPrefix + element.innerHTML + " should be " + expectedValue);
+ }
+ }
+
+ // Perform the actions and update the array of expected values.
+ expectedValues = createData(widget.store.data.length);
+ for(i = index, cnt = 0; cnt < numToModify; step()){
+ itemAction(indexToId(i), expectedValues);
+ }
+
+ // Use the dgrid widget API to test if the action was performed properly.
+ msgPrefix = "dgrid API: ";
+ tmp = [];
+ for(i = 0; i < expectedValues.length; i++){
+ var expectedValue = expectedValues[i],
+ expectedId = expectedValue.id;
+ testRow(widget.row(expectedId).element, i);
+ if(!expectedValue.deleted){
+ tmp.push(expectedValue);
+ }
+ }
+ expectedValues = tmp;
+
+ // Query the DOM to verify the structure matches the expected results.
+ msgPrefix = "DOM query: ";
+ query(widget.columns ? ".dgrid-content .field-value" : ".dgrid-row", widget.domNode).forEach(testRow);
+ });
+ }
+
+ function itemTestSuite(widgetClassName, storeSize, itemsPerQuery, overlap, config){
+ // Create a test suite that performs one action type (itemAction) on 1 to config.itemsModifiedMax with
+ // a given amount of overlap.
+ var index, numToModify;
+
+ test.suite(widgetClassName + " with " + overlap + " overlap", function(){
+
+ test.beforeEach(function(){
+ createList(storeSize, itemsPerQuery, overlap);
+ });
+
+ test.afterEach(destroyWidget);
+
+ // Modify items counting up.
+ for(numToModify = 1; numToModify <= config.itemsModifiedMax; numToModify++){
+ for(index = 0; index <= (storeSize - numToModify); index++){
+ itemTest(config.itemAction, index, numToModify);
+ }
+ }
+ // Modify items counting down. Starting at a count of 2 because
+ // single item modification were tested above.
+ for(numToModify = 2; numToModify <= config.itemsModifiedMax; numToModify++){
+ for(index = numToModify - 1; index < storeSize; index++){
+ itemTest(config.itemAction, index, numToModify, true);
+ }
+ }
+ });
+ }
+
+ function itemActionTestSuite(description, itemAction, config){
+ // Creates multiple item test suites for a given action (itemAction):
+ // - a list that executes a single query
+ // - lists with overlap from 0 to config.itemOverlapMax
+
+ // Note: for debugging, comment out the contents of destroyWidget so the dgrid widgets are not destroyed.
+ // Each widget uses a different store id and those ids are used in the row contents allowing you to
+ // easily match up an error message like
+ // "Error: dgrid API: row at index 2 with an expected value of "Value 30 / Store 10 / Changed!" is missing"
+ // with the correct widget on the page.
+ config.itemAction = itemAction;
+
+ test.suite(description, function(){
+ // Test widgets with only one query: total item count equals item count per query.
+ itemTestSuite("OnDemandList one query", config.itemsPerQuery, config.itemsPerQuery, 0, config);
+
+ // Test widgets that make multiple query requests: twice as many items as items per query so multiple
+ // queries will create multiple observers.
+ var storeSize = config.itemsPerQuery * 2;
+ // Test with OnDemandList with varying overlap values
+ for(var overlap = 0; overlap <= config.itemOverlapMax; overlap++){
+ itemTestSuite("OnDemandList multiple queries", storeSize, config.itemsPerQuery, overlap, config);
+ }
+ });
+ }
+
+ test.suite("observable lists", function(){
+ // Creates test suites that execute the following actions on OnDemandLists with varying amount of
+ // overlap and modifying varying number of items:
+ // - modify existing items
+ // - remove existing items
+ // - add new items before existing items
+ // - add new items after existing items
+
+ function findIndex(id, objs){
+ for(var i = 0; i < objs.length; i++){
+ var obj = objs[i];
+ if(obj && obj.id === id){
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ var modifyAction = function(id, expectedValues){
+ var index = findIndex(id, expectedValues);
+ var value = expectedValues[index].value + " / Changed!";
+ var dataObj = {id: id, value: value};
+ widget.store.put(dataObj);
+ expectedValues[index] = dataObj;
+ };
+ modifyAction.actionName = "Modify";
+
+ var removeAction = function(id, expectedValues){
+ widget.store.remove(id);
+ var index = findIndex(id, expectedValues);
+ expectedValues[index].deleted = true;
+ };
+ removeAction.actionName = "Remove";
+
+ var addBeforeAction = function(id, expectedValues){
+ var index = findIndex(id, expectedValues);
+ var obj = {id: id - 5, value: expectedValues[index].value + " / Added before!"};
+ widget.store.add(obj);
+ expectedValues.splice(index, 0, obj);
+ };
+ addBeforeAction.actionName = "Add before";
+
+ var addAfterAction = function(id, expectedValues){
+ var index = findIndex(id, expectedValues);
+ var obj = {id: id + 5, value: expectedValues[index].value + " / Added after!"};
+ widget.store.add(obj);
+ expectedValues.splice(index + 1, 0, obj);
+ };
+ addAfterAction.actionName = "Add after";
+
+ // Run a test case with each action (modify, remove, add before, add after) and vary the amount of
+ // queryRowsOverlap and vary the number of items modified during each test case. A configuration
+ // object controls the amount of variation. The properties are:
+ // itemsPerQuery - The OnDemandList is configured to request this number of items per query.
+ // This property also determines the size of the store. Test cases run with a store size
+ // equal to this number and test cases run with a store size twice this number.
+ // itemOverlapMax - Each test case is executed with a queryRowsOverap value of 0 up to this number.
+ // itemsModifiedMax - Each test is executed where the number of items modified, deleted or added is
+ // 1 up to this number; all of the test cases are run where 1 item is modified and then again
+ // with 2 items being modified and so on.
+ var config = {
+ itemsPerQuery: 3,
+ itemOverlapMax: 2,
+ itemsModifiedMax: 2
+ };
+ itemActionTestSuite("Modify store items", modifyAction, config);
+ itemActionTestSuite("Remove store items", removeAction, config);
+ itemActionTestSuite("Insert store items before", addBeforeAction, config);
+ itemActionTestSuite("Insert store items after", addAfterAction, config);
+ });
+});
View
4 test/intern/functional.js
@@ -1,4 +1,6 @@
define([
'./functional/Keyboard',
- './functional/KeyboardTab'
+ './functional/KeyboardTab',
+ './functional/selector',
+ './functional/editor'
], function(){});
View
2  test/intern/functional/Keyboard.html
@@ -38,12 +38,10 @@
}, "list");
list.renderArray(data.items);
grid = new KeyboardGrid({
- store: testStore,
columns: columns
}, "grid");
grid.renderArray(data.items);
rowGrid = new KeyboardGrid({
- store: testStore,
columns: columns,
cellNavigation: false
}, "rowGrid");
View
224 test/intern/functional/Keyboard.js
@@ -1,110 +1,97 @@
define([
- "intern!object",
+ "intern!tdd",
"intern/chai!assert",
+ "dojo/node!wd/lib/special-keys",
"require"
-], function(test, assert, require){
- var keys = {
- up: "\uE013",
- down: "\uE015",
- left: "\uE012",
- right: "\uE014",
- home: "\uE011",
- end: "\uE010"
- },
-
- testUpDownKeys = function(gridId, cellNavigation){
- var rootQuery = "#" + gridId + " #" + gridId + "-row-";
- return function(){
- return this.get("remote")
- .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
- .clickElement()
- .type([keys.down])
- .end()
- .elementByCssSelector(rootQuery + "1" + (cellNavigation ? " .dgrid-column-col1" : ""))
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the down arrow key should move focus one element down");
- })
- .type([keys.up])
- .end()
- .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the up arrow key should move focus one element up");
- })
- .end();
- };
- },
+], function(test, assert, specialKeys, require){
+ function testUpDownKeys(gridId, cellNavigation){
+ var rootQuery = "#" + gridId + " #" + gridId + "-row-";
+ return function(){
+ return this.get("remote")
+ .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .clickElement()
+ .type([specialKeys["Down arrow"]])
+ .end()
+ .elementByCssSelector(rootQuery + "1" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = (arr.indexOf("dgrid-focus") !== -1);
+ assert.ok(containsClass, "the down arrow key should move focus one element down");
+ })
+ .type([specialKeys["Up arrow"]])
+ .end()
+ .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = (arr.indexOf("dgrid-focus") !== -1);
+ assert.ok(containsClass, "the up arrow key should move focus one element up");
+ })
+ .end();
+ };
+ }
- testLeftRightKeys = function(gridId, header){
- var rootQuery = header ? ("#" + gridId + " .dgrid-header") : ("#" + gridId + " #" + gridId + "-row-0");
- return function(){
- return this.get("remote")
- .elementByCssSelector(rootQuery + " .dgrid-column-col1")
- .clickElement()
- .type([keys.right])
- .end()
- .elementByCssSelector(rootQuery + " .dgrid-column-col2")
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the right arrow key should move focus one element right");
- })
- .type([keys.left])
- .end()
- .elementByCssSelector(rootQuery + " .dgrid-column-col1")
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the left arrow key should move focus one element left");
- })
- .end();
- };
- },
+ function testLeftRightKeys(gridId, header){
+ var rootQuery = header ? ("#" + gridId + " .dgrid-header") : ("#" + gridId + " #" + gridId + "-row-0");
+ return function(){
+ return this.get("remote")
+ .elementByCssSelector(rootQuery + " .dgrid-column-col1")
+ .clickElement()
+ .type([specialKeys["Right arrow"]])
+ .end()
+ .elementByCssSelector(rootQuery + " .dgrid-column-col2")
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = (arr.indexOf("dgrid-focus") !== -1);
+ assert.ok(containsClass, "the right arrow key should move focus one element right");
+ })
+ .type([specialKeys["Left arrow"]])
+ .end()
+ .elementByCssSelector(rootQuery + " .dgrid-column-col1")
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = (arr.indexOf("dgrid-focus") !== -1);
+ assert.ok(containsClass, "the left arrow key should move focus one element left");
+ })
+ .end();
+ };
+ }
- testHomeEndKeys = function(gridId, cellNavigation, onDemand){
- var rootQuery = "#" + gridId + " #" + gridId + "-row-";
- return function(){
- return this.get("remote")
- .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
- .clickElement()
- .type([keys.end])
- .end()
- .elementByCssSelector("#" + gridId + " .dgrid-content>" + (onDemand ? ":nth-last-child(2)" : ":last-child") + (cellNavigation ? " .dgrid-column-col1" : ""))
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass;
- if(onDemand){
- //TODO we should check for the class with the right store item id...
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the end key should move focus to the last element in the list");
- }else{
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the end key should move focus to the last element in the list");
- }
- })
- .type([keys.home])
- .end()
- .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
- .getAttribute("class")
- .then(function(classNames){
- var arr = classNames.split(" "),
- containsClass = (arr.indexOf("dgrid-focus") !== -1);
- assert.ok(containsClass, "the home key should move focus to the first element in the list");
- })
- .end();
- };
+ function testHomeEndKeys(gridId, cellNavigation, onDemand){
+ var rootQuery = "#" + gridId + " #" + gridId + "-row-";
+ return function(){
+ return this.get("remote")
+ .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .clickElement()
+ .type([specialKeys.End])
+ .end()
+ .setImplicitWaitTimeout(1000)
+ // Note that this assumes the list is always 100 items, 0-99
+ .elementByCssSelector("#" + gridId + "-row-99" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = arr.indexOf("dgrid-focus") !== -1;
+ assert.ok(containsClass, "the end key should move focus to the last element in the list");
+ })
+ .type([specialKeys.Home])
+ .end()
+ .elementByCssSelector(rootQuery + "0" + (cellNavigation ? " .dgrid-column-col1" : ""))
+ .getAttribute("class")
+ .then(function(classNames){
+ var arr = classNames.split(" "),
+ containsClass = (arr.indexOf("dgrid-focus") !== -1);
+ assert.ok(containsClass, "the home key should move focus to the first element in the list");
+ })
+ .end();
};
+ }
- return test({
- before: function(){
+ test.suite("Keyboard functional tests", function(){
+ test.before(function(){
// Get our html page. This page should load all necessary scripts
// since this functional test module runs on the server and can't load
// such scripts. Further, in the html page, set a global "ready" var
@@ -112,28 +99,39 @@ define([
return this.get("remote")
.get(require.toUrl("./Keyboard.html"))
.waitForCondition("ready", 5000);
- },
-
- "grid (cellNavigation: true) -> up + down arrow keys" : testUpDownKeys("grid", true),
+ });
+
+ test.test("grid (cellNavigation: true) -> up + down arrow keys",
+ testUpDownKeys("grid", true));
- "grid (cellNavigation: false) -> up + down arrow keys" : testUpDownKeys("rowGrid"),
+ test.test("grid (cellNavigation: false) -> up + down arrow keys",
+ testUpDownKeys("rowGrid"));
- "list -> up + down arrow keys" : testUpDownKeys("list"),
+ test.test("list -> up + down arrow keys",
+ testUpDownKeys("list"));
- "grid row -> left + right arrow keys" : testLeftRightKeys("grid"),
+ test.test("grid row -> left + right arrow keys",
+ testLeftRightKeys("grid"));
- "grid header -> left + right arrow keys" : testUpDownKeys("grid", true),
+ test.test("grid header -> left + right arrow keys",
+ testUpDownKeys("grid", true));
- "simple grid (cellNavigation: true) -> home + end keys" : testHomeEndKeys("grid", true),
+ test.test("simple grid (cellNavigation: true) -> home + end keys",
+ testHomeEndKeys("grid", true));
- "simple grid (cellNavigation: false) -> home + end keys" : testHomeEndKeys("rowGrid"),
+ test.test("simple grid (cellNavigation: false) -> home + end keys",
+ testHomeEndKeys("rowGrid"));
- "simple list -> home + end keys" : testHomeEndKeys("list"),
+ test.test("simple list -> home + end keys",
+ testHomeEndKeys("list"));
- "on-demand grid (cellNavigation: true) -> home + end keys" : testHomeEndKeys("grid-ondemand", true, true),
+ test.test("on-demand grid (cellNavigation: true) -> home + end keys",
+ testHomeEndKeys("grid-ondemand", true, true));
- "on-demand simple grid (cellNavigation: false) -> home + end keys" : testHomeEndKeys("rowGrid-ondemand", false, true),
+ test.test("on-demand simple grid (cellNavigation: false) -> home + end keys",
+ testHomeEndKeys("rowGrid-ondemand", false, true));
- "on-demand simple list -> home + end keys" : testHomeEndKeys("list-ondemand", false, true)
+ test.test("on-demand simple list -> home + end keys",
+ testHomeEndKeys("list-ondemand", false, true));
});
});
View
111 test/intern/functional/KeyboardTab.js
@@ -1,78 +1,83 @@
define([
- "intern!object",
+ "intern!tdd",
"intern/chai!assert",
+ "dojo/node!wd/lib/special-keys",
"require"
-], function(test, assert, require){
- var tabKey = "\uE004";
- return test({
- before: function(){
+], function(test, assert, specialKeys, require){
+ var tabKey = specialKeys.Tab;
+ test.suite("Keyboard tab key functional tests", function(){
+ test.before(function(){
// Get our html page. This page should load all necessary scripts
// since this functional test module runs on the server and can't load
// such scripts. Further, in the html page, set a global "ready" var
// to true to tell the runner to continue.
return this.get("remote")
.get(require.toUrl("./KeyboardTab.html"))
- .waitForCondition("ready", 125000);
- },
+ .waitForCondition("ready", 5000);
+ });
- "grids with and without headers -> tab key": function(){
+ test.test("grids with and without headers -> tab key", function(){
return this.get("remote")
.active()
- .getAttribute("id")
- .then(function(id){
- assert.strictEqual(id, "showHeaderButton", "Focus is on the button: " + id);
- })
+ .getAttribute("id")
+ .then(function(id){
+ assert.strictEqual(id, "showHeaderButton", "Focus is on the button: " + id);
+ })
.type(tabKey)
.active()
- .getAttribute("role").then(function(role){
- assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
- })
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
+ })
.type(tabKey)
- .active().getAttribute("role").then(function(role){
- assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
- })
- .text().then(function(text){
- assert.strictEqual(text, "0", "The cell with focus contains 0: " + text);
- })
+ .active()
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
+ })
+ .text().then(function(text){
+ assert.strictEqual(text, "0", "The cell with focus contains 0: " + text);
+ })
.type(tabKey)
- .active().getAttribute("role").then(function(role){
- assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
- })
- .text().then(function(text){
- assert.strictEqual(text, "10", "The cell with focus contains 10: " + text);
- })
- .reset()
+ .active()
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
+ })
+ .text().then(function(text){
+ assert.strictEqual(text, "10", "The cell with focus contains 10: " + text);
+ })
+ .reset()
.elementById("showHeaderButton")
- .click()
+ .click()
.active()
- .getAttribute("id")
- .then(function(id){
- assert.strictEqual(id, "showHeaderButton", "Focus is on the button: " + id);
- })
+ .getAttribute("id")
+ .then(function(id){
+ assert.strictEqual(id, "showHeaderButton", "Focus is on the button: " + id);
+ })
.type(tabKey)
.active()
- .getAttribute("role").then(function(role){
- assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
- })
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
+ })
.type(tabKey)
- .active().getAttribute("role").then(function(role){
- assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
- })
- .text().then(function(text){
- assert.strictEqual(text, "0", "The cell with focus contains 0: " + text);
- })
+ .active()
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
+ })
+ .text().then(function(text){
+ assert.strictEqual(text, "0", "The cell with focus contains 0: " + text);
+ })
.type(tabKey)
.active()
- .getAttribute("role").then(function(role){
- assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
- })
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "columnheader", "Focus is on a column header: " + role);
+ })
.type(tabKey)
- .active().getAttribute("role").then(function(role){
- assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
- })
- .text().then(function(text){
- assert.strictEqual(text, "10", "The cell with focus contains 10: " + text);
- });
- }
+ .active()
+ .getAttribute("role").then(function(role){
+ assert.strictEqual(role, "gridcell", "Focus is on a grid cell: " + role);
+ })
+ .text().then(function(text){
+ assert.strictEqual(text, "10", "The cell with focus contains 10: " + text);
+ });
+ });
});
-});
+});
View
78 test/intern/functional/editor-OnDemand.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test Cell Editors</title>
+ <style>
+ @import "../../../../../dojo/resources/dojo.css";
+ @import "../../../../../dijit/themes/claro/claro.css";
+ @import "../../../../css/skins/claro.css";
+ </style>
+ </head>
+
+ <body class="claro">
+ <h2>OnDemandGrid with editors</h2>
+ <div id="grid"></div>
+
+ <script src="../../../../../dojo/dojo.js" data-dojo-config="async: 1"></script>
+
+ <script>
+ var grid,
+ gridSaveStack = [],
+ ready,
+ // Toggled with each grid.save call to signal test module that a save is complete
+ saveComplete;
+
+ require([
+ "dojo/aspect",
+ "dojo/store/Memory",
+ "dojo/store/Observable",
+ "dgrid/OnDemandGrid",
+ "dgrid/editor"
+ ], function (aspect, Memory, Observable, OnDemandGrid, editor) {
+ var store = new Observable(new Memory({ data: [
+ { id: 0, name: "1", description: "one" },
+ { id: 1, name: "2", description: "two" },
+ { id: 2, name: "3", description: "three" }
+ ]}));
+
+ grid = new OnDemandGrid({
+ store: store,
+ columns: {
+ id: "ID",
+ name: editor({
+ label: "Name",
+ editor: "text",
+ autoSave: true
+ }),
+ description: editor({
+ label: "Description",
+ editor: "text",
+ editOn: "click",
+ autoSave: true
+ })
+ }
+ }, "grid");
+
+ aspect.before(grid, "save", function () {
+ var dirtyRowId;
+
+ saveComplete = false;
+ for (dirtyRowId in grid.dirty) {
+ // col3 and col4 are the only columns with edit enabled; push the edited value
+ // into 'gridSaveStack'
+ gridSaveStack.push(grid.dirty[dirtyRowId].name || grid.dirty[dirtyRowId].description);
+ }
+ });
+
+ aspect.after(grid, "save", function (savePromise) {
+ savePromise.then(function () {
+ saveComplete = true;
+ });
+ });
+
+ ready = true;
+ });
+ </script>
+ </body>
+</html>
View
65 test/intern/functional/editor.html
@@ -0,0 +1,65 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test Cell Editors</title>
+ <style>
+ @import "../../../../dojo/resources/dojo.css";
+ @import "../../../../dijit/themes/claro/claro.css";
+ @import "../../../css/skins/claro.css";
+ </style>
+ </head>
+
+ <body class="claro">
+ <h2>Grid with editors</h2>
+ <div id="grid"></div>
+
+ <script src="../../../../dojo/dojo.js" data-dojo-config="async: 1"></script>
+
+ <script>
+ var grid,
+ datachangeStack = [],
+ ready = false;
+
+ require([
+ "dojo/_base/declare",
+ "dgrid/Grid",
+ "dgrid/OnDemandList",
+ "dgrid/editor"
+ ], function (declare, Grid, OnDemandList, editor) {
+ // global from dgrid/test/data/base
+ // testOrderedData
+
+ grid = new Grid({
+ columns: {
+ id: "ID",
+ name: editor({
+ label: "Name",
+ editor: "text"
+ }),
+ description: editor({
+ label: "Description",
+ editor: "text",
+ editOn: "click"
+ })
+ }
+ }, "grid");
+
+ grid.renderArray([
+ { name: "1", description: "one" },
+ { name: "2", description: "two" },
+ { name: "3", description: "three" }
+ ]);
+
+ // Push values received by the "dgrid-datachange" event into a global stack.
+ // The functional test can read from this stack to verify the values it is
+ // inputting are being emitted by the "dgrid-datachange" event.
+ grid.on("dgrid-datachange", function (event) {
+ datachangeStack.push(event.value);
+ });
+
+ ready = true;
+ });
+ </script>
+ </body>
+</html>