Skip to content

Commit

Permalink
Fix dojo#496: Maintain focus through a row update.
Browse files Browse the repository at this point in the history
Also added code to clean up focus settings on blur. This was not being done before. I needed it for my update and it seems like a good thing to have in general.
  • Loading branch information
edhager committed Jun 28, 2013
1 parent d0e680f commit 4b37719
Show file tree
Hide file tree
Showing 3 changed files with 180 additions and 18 deletions.
94 changes: 76 additions & 18 deletions Keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,44 @@ var Keyboard = declare(null, {
});
}
enableNavigation(this.contentNode);

// When a row is updated, one row is removed and another is inserted.
// Aspect removeRow to record info about the old row so focus can be restored to the new node.
aspect.before(this, "removeRow", function(row){
// Looking for focused rows not headers
var focusedNode = grid._focusedNode;
if(focusedNode){
var focusedRow = grid.row(focusedNode);
// Is the focused node in the row being removed?
if(focusedRow && focusedRow.element === row){
// Save the row id.
grid._restoreFocusNode = {
rowId: row.getAttribute("id")
};
// If focus is on a cell, record the column id as well.
if(this.cellNavigation){
var column = grid.cell(focusedNode).column;
if(column){
grid._restoreFocusNode.columnId = column.id;
}
}
}else{
grid._restoreFocusNode = undefined;
}
}
}, true);

aspect.after(this, "insertRow", function(row){
var restoreFocus = grid._restoreFocusNode;
if(restoreFocus && restoreFocus.rowId === row.getAttribute("id")){
if(restoreFocus.columnId == null){
grid.focus(row);
}else{
grid.focus(grid.cell(row, restoreFocus.columnId).element)
}
}
return row;
});
},

addKeyHandler: function(key, callback, isHeader){
Expand Down Expand Up @@ -178,11 +216,15 @@ var Keyboard = declare(null, {
input,
numInputs,
inputFocused,
i;

i,
grid = this;

// Remove any saved focus restore info.
this._restoreFocusNode = undefined;

element = cell && cell.element;
if(!element){ return; }

if(this.cellNavigation){
inputs = element.getElementsByTagName("input");
for(i = 0, numInputs = inputs.length; i < numInputs; i++){
Expand All @@ -207,20 +249,6 @@ var Keyboard = declare(null, {
// Opera throws if you try to set it to true if it is already true.
event.bubbles = true;
}
if(focusedNode){
// Clean up previously-focused element
// Remove the class name and the tabIndex attribute
put(focusedNode, "!dgrid-focus[!tabIndex]");
if(has("ie") < 8){
// Clean up after workaround below (for non-input cases)
focusedNode.style.position = "";
}

// Expose object representing focused cell or row losing focus, via
// event.cell or event.row; which is set depends on cellNavigation.
event[cellOrRowType] = this[cellOrRowType](focusedNode);
on.emit(element, "dgrid-cellfocusout", event);
}
focusedNode = this[focusedNodeProperty] = element;

// Expose object representing focused cell or row gaining focus, via
Expand All @@ -239,10 +267,40 @@ var Keyboard = declare(null, {
element.tabIndex = this.tabIndex;
element.focus();
}

var signal = on(element, "blur", function(event){
signal.remove();
grid._handleBlur(element, isHeader);
});

put(element, ".dgrid-focus");
on.emit(focusedNode, "dgrid-cellfocusin", event);
},


_handleBlur: function(element, isHeader){
var focusedNodeProperty = "_focused" + (isHeader ? "Header" : "") + "Node",
focusedNode = this[focusedNodeProperty],
cellOrRowType = this.cellNavigation ? "cell" : "row";

// Clean up previously-focused element
// Remove the class name and the tabIndex attribute
put(element, "!dgrid-focus[!tabIndex]");
if(has("ie") < 8){
// Clean up after workaround below (for non-input cases)
element.style.position = "";
}

// Expose object representing focused cell or row losing focus, via
// event.cell or event.row; which is set depends on cellNavigation.
event[cellOrRowType] = this[cellOrRowType](element);
on.emit(element, "dgrid-cellfocusout", event);

// If focus has not moved to another cell/row, then remove the property.
if(focusedNode === element){
this[focusedNodeProperty] = undefined;
}
},

focusHeader: function(element){
this._focusOnNode(element || this._focusedHeaderNode, true);
},
Expand Down
98 changes: 98 additions & 0 deletions test/Focus_row_updates.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
<!DOCTYPE html>
<html>
<head>
<title>GH #226: Row loses selection after .notify</title>
<meta charset="utf-8">

<style>
@import "../../dojo/resources/dojo.css";
@import "../../dijit/themes/claro/claro.css";
@import "../css/skins/claro.css";

body {
margin: 20px;
}

#grid1, #grid2 {
width: 400px;
height: 250px;
}

.testCaseTitle {
margin: 10px;
font-weight: bold;
}
</style>

<script src="/dojo/dojo.js" data-dojo-config="async: true"></script>
<script>
require([
"dojo/_base/declare",
"dojo/_base/lang",
"dojo/on",
"dojo/store/Memory",
"dgrid/OnDemandGrid",
"dgrid/Keyboard",
"dojo/store/Observable",
"dojo/domReady!"
], function(declare, lang, on, Memory, OnDemandGrid, Keyboard, Observable){

var grid1, grid2,
i, storeData, store,
CustomGrid, columns;

storeData = [];
for(i = 0; i < 10; i++){
storeData.push({
id: i,
first: "John" + i,
last: "Doe" + i
});
}

store = Observable(new Memory({
data: storeData
}));

columns = {
first: "First Name",
last: "Last Name"
};

grid1 = new declare([OnDemandGrid, Keyboard])({
columns: columns,
store: store,
cellNavigation: false
}, 'grid1');

grid2 = new declare([OnDemandGrid, Keyboard])({
columns: columns,
store: store,
cellNavigation: true
}, 'grid2');


var updateCnt = 0;
setInterval(function () {
updateCnt += 1;
store.put({id:5, first: 'Update ' + updateCnt, last: 'Update ' + updateCnt})
}, 3000);

});

</script>
</head>
<body class="claro">

<hr/>
<div class="testCaseTitle">Grid row updates with Observable store and row focus</div>
<div id="grid1"></div>
<div style="margin-top: 10px">ID 5 updates every 3 seconds. Set focus there and make sure it remains.</div>

<hr/>
<div class="testCaseTitle">Grid row updates with Observable store and cell focus</div>
<div id="grid2"></div>
<div style="margin-top: 10px">ID 5 updates every 3 seconds. Set focus there and make sure it remains.</div>

</body>
</html>
6 changes: 6 additions & 0 deletions test/data/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -285,6 +285,12 @@
"title": "Test Selection",
"parent": ""
},
{
"name": "Focus_row_updates.html",
"url": "Focus_row_updates.html",
"title": "Test Focus with store notifications",
"parent": ""
},
{
"name": "selector.html",
"url": "selector.html",
Expand Down

0 comments on commit 4b37719

Please sign in to comment.