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

DataGrid: Rework selectionFilter simplification if deferred selection is used (T814753, T874992) #12728

Merged
merged 2 commits into from
Apr 20, 2020
Merged
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
89 changes: 63 additions & 26 deletions js/ui/selection/selection.strategy.deferred.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ module.exports = SelectionStrategy.inherit({
return !!dataQuery([itemData]).filter(selectionFilter).toArray().length;
},

_processSelectedItem: function(key) {
_getFilterByKey: function(key) {
const keyField = this.options.key();
let filter = [keyField, '=', key];

Expand All @@ -96,13 +96,13 @@ module.exports = SelectionStrategy.inherit({
},

addSelectedItem: function(key) {
const filter = this._processSelectedItem(key);
const filter = this._getFilterByKey(key);

this._addSelectionFilter(false, filter);
},

removeSelectedItem: function(key) {
const filter = this._processSelectedItem(key);
const filter = this._getFilterByKey(key);

this._addSelectionFilter(true, filter);
},
Expand Down Expand Up @@ -155,21 +155,19 @@ module.exports = SelectionStrategy.inherit({

_addSelectionFilter: function(isDeselect, filter, isSelectAll) {
const that = this;
let needAddFilter = true;
const currentFilter = isDeselect ? ['!', filter] : filter;
const currentOperation = isDeselect ? 'and' : 'or';
let needAddFilter = true;
let selectionFilter = that.options.selectionFilter || [];

selectionFilter = that._denormalizeFilter(selectionFilter);

if(selectionFilter && selectionFilter.length) {
that._removeSameFilter(selectionFilter, filter, isDeselect, isSelectAll);
const lastOperation = that._removeSameFilter(selectionFilter, filter, !isDeselect);
const filterIndex = that._removeSameFilter(selectionFilter, filter, !isDeselect);
const isKeyOperatorsAfterRemoved = this._isKeyFilter(filter) && this._hasKeyFiltersOnlyStartingFromIndex(selectionFilter, filterIndex);

if(lastOperation && (lastOperation !== 'or' && isDeselect || lastOperation !== 'and' && !isDeselect)) {
needAddFilter = false;
selectionFilter = [];
}
needAddFilter = filter.length && !isKeyOperatorsAfterRemoved;

if(needAddFilter) {
selectionFilter = that._addFilterOperator(selectionFilter, currentOperation);
Expand All @@ -193,46 +191,85 @@ module.exports = SelectionStrategy.inherit({
},

_removeFilterByIndex: function(filter, filterIndex, isSelectAll) {
let lastRemoveOperation;
const operation = filter[1];

if(filterIndex > 0) {
lastRemoveOperation = filter.splice(filterIndex - 1, 2)[0];
filter.splice(filterIndex - 1, 2);
} else {
lastRemoveOperation = filter.splice(filterIndex, 2)[1] || 'undefined';
filter.splice(filterIndex, 2);
}

if(isSelectAll && lastRemoveOperation === 'and') {
if(isSelectAll && operation === 'and') {
filter.splice(0, filter.length);
}
},
_isSimpleKeyFilter: function(filter, key) {
return filter.length === 3 && filter[0] === key && filter[1] === '=';
},
_isKeyFilter: function(filter) {
if(filter.length === 2 && filter[0] === '!') {
return this._isKeyFilter(filter[1]);
}
const keyField = this.options.key();

return lastRemoveOperation;
if(Array.isArray(keyField)) {
if(filter.length !== keyField.length * 2 - 1) {
return false;
}
for(let i = 0; i < keyField.length; i++) {
if(i > 0 && filter[i] !== 'and') {
return false;
}
if(!this._isSimpleKeyFilter(filter[i * 2], keyField[i])) {
return false;
}
}
return true;
}

return this._isSimpleKeyFilter(filter, keyField);
},
_hasKeyFiltersOnlyStartingFromIndex: function(selectionFilter, filterIndex) {
if(filterIndex >= 0) {
for(let i = filterIndex; i < selectionFilter.length; i++) {
if(typeof selectionFilter[i] !== 'string' && !this._isKeyFilter(selectionFilter[i])) {
return false;
}
}

return true;
}

return false;
},
_removeSameFilter: function(selectionFilter, filter, inverted, isSelectAll) {
filter = inverted ? ['!', filter] : filter;

const filterIndex = this._findSubFilter(selectionFilter, filter);

if(JSON.stringify(filter) === JSON.stringify(selectionFilter)) {
selectionFilter.splice(0, selectionFilter.length);
return 'undefined';
return 0;
}

const filterIndex = this._findSubFilter(selectionFilter, filter);

if(filterIndex >= 0) {
return this._removeFilterByIndex(selectionFilter, filterIndex, isSelectAll);
this._removeFilterByIndex(selectionFilter, filterIndex, isSelectAll);
return filterIndex;
} else {
for(let i = 0; i < selectionFilter.length; i++) {
const lastRemoveOperation = Array.isArray(selectionFilter[i]) && selectionFilter[i].length > 2 && this._removeSameFilter(selectionFilter[i], filter, false, isSelectAll);

if(lastRemoveOperation) {
if(!selectionFilter[i].length) {
this._removeFilterByIndex(selectionFilter, i, isSelectAll);
} else if(selectionFilter[i].length === 1) {
selectionFilter[i] = selectionFilter[i][0];
if(Array.isArray(selectionFilter[i]) && selectionFilter[i].length > 2) {
const filterIndex = this._removeSameFilter(selectionFilter[i], filter, false, isSelectAll);
if(filterIndex >= 0) {
if(!selectionFilter[i].length) {
this._removeFilterByIndex(selectionFilter, i, isSelectAll);
} else if(selectionFilter[i].length === 1) {
selectionFilter[i] = selectionFilter[i][0];
}
return filterIndex;
}
return lastRemoveOperation;
}
}
return -1;
}
},

Expand Down
103 changes: 98 additions & 5 deletions testing/tests/DevExpress.ui.widgets/selection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1227,9 +1227,7 @@ QUnit.test('changeItemSelection with shift key two times', function(assert) {
selection.changeItemSelection(2, { shift: true });

// assert
assert.deepEqual(selection.selectionFilter(), [
[['id', '=', 2], 'and', ['!', ['id', '=', 5]], 'and', ['!', ['id', '=', 4]]],
'or', ['id', '=', 3]], 'selection filter');
assert.deepEqual(selection.selectionFilter(), [['id', '=', 2], 'or', ['id', '=', 3]], 'selection filter');
});

QUnit.test('selectAll when filter is empty', function(assert) {
Expand Down Expand Up @@ -1529,7 +1527,30 @@ QUnit.test('selectAll -> deselect items -> select item -> deselect item -> selec
selection.selectAll();

// assert
assert.deepEqual(selection.selectionFilter(), [[['id', '=', 3], 'and', ['!', ['id', '=', 4]]], 'or', ['age', '>', 15]], 'selection filter');
assert.deepEqual(selection.selectionFilter(), ['age', '>', 15], 'selection filter');
assert.strictEqual(selection.getSelectAllState(), true, 'select all is true');
selection.getSelectedItemKeys().done(function(keys) {
selectedKeys = keys;
});
assert.deepEqual(selectedKeys, [2, 3, 4, 5, 6, 7], 'selected keys');
});

QUnit.test('select item -> selectAll -> deselect items -> select item -> deselect item -> select All', function(assert) {
let selectedKeys;
const selection = this.createDeferredSelection(this.data);

// act
this.dataSource.filter(['age', '>', 15]);
selection.changeItemSelection(4, { control: true }); // select item
selection.selectAll();
selection.changeItemSelection(1, { control: true }); // deselect item
selection.changeItemSelection(2, { control: true }); // deselect item
selection.changeItemSelection(2, { control: true }); // select item
selection.changeItemSelection(3, { control: true }); // deselect item
selection.selectAll();

// assert
assert.deepEqual(selection.selectionFilter(), [[['id', '=', 5], 'and', ['!', ['id', '=', 2]], 'and', ['!', ['id', '=', 4]]], 'or', ['age', '>', 15]], 'selection filter');
assert.strictEqual(selection.getSelectAllState(), true, 'select all is true');
selection.getSelectedItemKeys().done(function(keys) {
selectedKeys = keys;
Expand Down Expand Up @@ -1560,6 +1581,32 @@ QUnit.test('selectAll -> deselect items -> select/deselect item -> select All',
assert.deepEqual(selectedKeys, [2, 3, 4, 5, 6, 7], 'selected keys');
});

// T814753
QUnit.test('selectAll -> deselect/select items -> deselect item -> select All -> deselect item', function(assert) {
let selectedKeys;
const selection = this.createDeferredSelection(this.data);

// act
this.dataSource.filter(['age', '>', 15]);
selection.selectAll();
selection.changeItemSelection(1, { control: true }); // deselect item
selection.changeItemSelection(2, { control: true }); // deselect item
selection.changeItemSelection(1, { control: true }); // select item
selection.changeItemSelection(2, { control: true }); // select item
selection.changeItemSelection(2, { control: true }); // deselect item
selection.selectAll();
selection.changeItemSelection(1, { control: true }); // deselect item

// assert
assert.deepEqual(selection.selectionFilter(), [['age', '>', 15], 'and', ['!', ['id', '=', 2]]], 'selection filter');
assert.strictEqual(selection.getSelectAllState(), undefined, 'select all is true');
selection.getSelectedItemKeys().done(function(keys) {
selectedKeys = keys;
});
assert.deepEqual(selectedKeys, [3, 4, 5, 6, 7], 'selected keys');
});


QUnit.test('Deselect one item after selectAll', function(assert) {
const selection = this.createDeferredSelection(this.data);

Expand Down Expand Up @@ -1720,6 +1767,7 @@ QUnit.test('select 3 items, deselect 3 items', function(assert) {
assert.deepEqual(selection.selectionFilter(), [], 'selectionFilter is []');
});

// T874992
QUnit.test('select 2 items, deselect item', function(assert) {
let selectedKeys;
const selection = this.createDeferredSelection(this.data);
Expand All @@ -1735,7 +1783,52 @@ QUnit.test('select 2 items, deselect item', function(assert) {

// assert
assert.deepEqual(selectedKeys, [1], 'selected keys');
assert.deepEqual(selection.selectionFilter(), [['id', '=', 1], 'and', ['!', ['id', '=', 2]]], 'selectionFilter');
assert.deepEqual(selection.selectionFilter(), ['id', '=', 1], 'selectionFilter');
});

// T874992
QUnit.test('select 3 items, deselect 2 item, select item', function(assert) {
let selectedKeys;
const selection = this.createDeferredSelection(this.data);

selection.changeItemSelection(0, { control: true });
selection.changeItemSelection(1, { control: true });
selection.changeItemSelection(2, { control: true });
selection.changeItemSelection(0, { control: true });
selection.changeItemSelection(1, { control: true });
selection.changeItemSelection(3, { control: true });

// act
selection.getSelectedItemKeys().done(function(keys) {
selectedKeys = keys;
});

// assert
assert.deepEqual(selectedKeys, [3, 4], 'selected keys');
assert.deepEqual(selection.selectionFilter(), [['id', '=', 3], 'or', ['id', '=', 4]], 'selectionFilter');
});

// T874992
QUnit.test('select all, deselect 3 items, select 2 item, dselect item', function(assert) {
let selectedKeys;
const selection = this.createDeferredSelection(this.data);

selection.selectAll();
selection.changeItemSelection(0, { control: true });
selection.changeItemSelection(1, { control: true });
selection.changeItemSelection(2, { control: true });
selection.changeItemSelection(0, { control: true });
selection.changeItemSelection(1, { control: true });
selection.changeItemSelection(3, { control: true });

// act
selection.getSelectedItemKeys().done(function(keys) {
selectedKeys = keys;
});

// assert
assert.deepEqual(selectedKeys, [1, 2, 5, 6, 7], 'selected keys');
assert.deepEqual(selection.selectionFilter(), [['!', ['id', '=', 3]], 'and', ['!', ['id', '=', 4]]], 'selectionFilter');
});

QUnit.test('select 2 items, deselect and select 1 item', function(assert) {
Expand Down