Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/datatable/docs/index.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -1253,7 +1253,7 @@ YUI({ filter: 'raw' }).use('datatable-column-widths', function (Y) {
</p>

<p>
By default, when `datatable-sort` is included, DataTables will inspects
By default, when `datatable-sort` is included, DataTables will inspect
the `columns` objects, looking for `sortable: true` to enable table sorting
by those columns, triggered by clicking on their respective headers.
</p>
Expand Down
18 changes: 16 additions & 2 deletions src/datatable/js/sort.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ As new records are inserted into the table's `data` ModelList, they will be inse
The current sort order is stored in the `sortBy` attribute. Assigning this value at instantiation will automatically sort your data.

Sorting is done by a simple value comparison using &lt; and &gt; on the field
value. If you need custom sorting, add a sort function in the column's
value. In the case where values are the same, the order of insertion is used
as a tiebreaker. If you need custom sorting, add a sort function in the column's
`sortFn` property. Columns whose content is generated by formatters, but don't
relate to a single `key`, require a `sortFn` to be sortable.

Expand Down Expand Up @@ -493,7 +494,7 @@ Y.mix(Sortable.prototype, {
// extra function hop during sorting. Lesser of three evils?
this.data._compare = function (a, b) {
var cmp = 0,
i, len, col, dir, cs, aa, bb;
i, len, col, dir, cs, aa, bb, aId, bId;

for (i = 0, len = self._sortBy.length; !cmp && i < len; ++i) {
col = self._sortBy[i];
Expand All @@ -511,6 +512,19 @@ Y.mix(Sortable.prototype, {
bb = bb.toLowerCase();
}
cmp = (aa > bb) ? dir : ((aa < bb) ? -dir : 0);

if (cmp === 0) {
/**
* In the case where values are the same, the model's clientId is used
* as a tiebreaker, effectively sorting by order of insertion. Can't
* do a straight parseInt on the clientId because the `_` is invalid
* and it will not return the number. So, `model_` is stripped
* and the `+` operator is used as a short way to cast to a Number.
*/
aId = +(a.get('clientId').replace(a.constructor.NAME + '_', ''));
bId = +(b.get('clientId').replace(b.constructor.NAME + '_', ''));
cmp = (aId > bId) ? dir : ((aId < bId) ? -dir : 0);
}
}
}

Expand Down
68 changes: 67 additions & 1 deletion src/datatable/tests/unit/assets/datatable-sort-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,72 @@ suite.add(new Y.Test.Case({
table.destroy();
},

"sort should order based on insertion order when sorting against same values": function () {
var table = new Y.DataTable({
columns: ['a', 'b'],
data: [
{ a: 'a1', b: 1 },
{ a: 'a1', b: 2 },
{ a: 'a1', b: 3 },
{ a: 'a1', b: 4 },
{ a: 'a1', b: 5 },
{ a: 'a1', b: 6 },
{ a: 'a2', b: 7 },
{ a: 'a3', b: 8 },
{ a: 'a4', b: 9 },
{ a: 'a5', b: 10 },
{ a: 'a6', b: 11 },
],
sortable: true
}).render(),
size,
idx;

// Test initial sort order
table.data.each(function (item, index) {
Y.Assert.areEqual(++index, item.get('b'), 'item ' + item.get('a') + ' should be ' + index + ' in default order');
});

// Test sort directions multiple times because they could change order each time

table.sort({a:-1});
size = table.data.size();
table.data.each(function (item, index) {
idx = size - index;
Y.Assert.areEqual(idx, item.get('b'), 'item ' + item.get('a') + ' should be ' + idx + ' when desc sort');
});

table.sort('a');
table.data.each(function (item, index) {
Y.Assert.areEqual(++index, item.get('b'), 'item ' + item.get('a') + ' should be ' + index + ' in asc order');
});

table.sort({a:-1});
size = table.data.size();
table.data.each(function (item, index) {
idx = size - index;
Y.Assert.areEqual(idx, item.get('b'), 'item ' + item.get('a') + ' should be ' + idx + ' when desc sort');
});

table.sort('a');
table.data.each(function (item, index) {
Y.Assert.areEqual(++index, item.get('b'), 'item ' + item.get('a') + ' should be ' + index + ' in asc order');
});

table.sort({a:-1});
size = table.data.size();
table.data.each(function (item, index) {
idx = size - index;
Y.Assert.areEqual(idx, item.get('b'), 'item ' + item.get('a') + ' should be ' + idx + ' when desc sort');
});

table.sort('a');
table.data.each(function (item, index) {
Y.Assert.areEqual(++index, item.get('b'), 'item ' + item.get('a') + ' should be ' + index + ' in asc order');
});

table.destroy();
},

// reverse sorting a column
"reverse sorting a column": function () {
Expand Down Expand Up @@ -432,4 +498,4 @@ suite.add(new Y.Test.Case({
Y.Test.Runner.add(suite);


}, '@VERSION@' ,{requires:['datatable-sort', 'test', 'node-event-simulate']});
}, '@VERSION@' ,{requires:['datatable', 'test', 'node-event-simulate']});