Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue #1738: [datatable-sort] Sort order inconsistent when comparing on equal value #1868

Open
wants to merge 1 commit into
base: dev-master
Choose a base branch
from
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']});