From 17efb870afbb1b5f9e58a37af5580b63cd5afb43 Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Tue, 24 Mar 2015 10:24:55 +0100 Subject: [PATCH 01/24] add maDatagridItemSelector --- .../Crud/list/maDatagridItemSelector.js | 33 +++++++++ .../Crud/list/maDatagridItemSelectorSpec.js | 67 +++++++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js create mode 100644 src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js new file mode 100644 index 00000000..cd1a44f6 --- /dev/null +++ b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js @@ -0,0 +1,33 @@ +/*global define*/ + +define(function () { + 'use strict'; + + function DatagridItemSelectorDirective() { + return { + restrict: 'E', + scope: { + entry: '=', + selection: '=' + }, + template: '', + link: function (scope) { + scope.selected = scope.selection.indexOf(scope.entry) !== -1; + + scope.select= function (selected) { + scope.selected = !selected; + if (!selected) { + scope.selection.push(scope.entry); + return; + } + var index = scope.selection.indexOf(scope.entry); + scope.selection.splice(index, 1); + }; + } + }; + } + + DatagridItemSelectorDirective.$inject = []; + + return DatagridItemSelectorDirective; +}); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js new file mode 100644 index 00000000..190e02f0 --- /dev/null +++ b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js @@ -0,0 +1,67 @@ +/*global define,angular,inject,describe,it,expect,beforeEach,module*/ + +define(function (require) { + 'use strict'; + + describe('directive: ma-datagrid-item-selector', function () { + var directive = require('ng-admin/Crud/list/maDatagridItemSelector'), + Entry = require('ng-admin/es6/lib/Entry'), + $compile, + scope, + directiveUsage = '' + ; + + angular.module('testapp_DatagridItemSelector', []) + .directive('maDatagridItemSelector', directive); + require('angular-mocks'); + + beforeEach(module('testapp_DatagridItemSelector')); + + beforeEach(inject(function (_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; + scope.entry = new Entry('entity', {some: 'values'}, 'entity_1'); + scope.selection = [new Entry('entity', {some: 'values'}, 'entity_2'), new Entry('entity', {some: 'values'}, 'entity_3')]; + })); + + it('should be unchecked if entry is not in selection', function () { + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].checked).toBe(false); + }); + + it('should be checked if entry is in selection', function () { + scope.selection.push(scope.entry); + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].checked).toBe(true); + }); + + it('should add unselected entry to selection on click', function () { + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); + + expect(element.children()[0].checked).toBe(true); + expect(scope.selection.length).toBe(3); + expect(scope.selection.indexOf(scope.entry)).toBe(2); + }); + + it('should remove selected entry from selection on click', function () { + scope.selection.push(scope.entry); + + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); + + expect(element.children()[0].checked).toBe(false); + expect(scope.selection.length).toBe(2); + expect(scope.selection.indexOf(scope.entry)).toBe(-1); + }); + + }); +}); From 82e104bb47aca0e726741c63f6ac848f948b76d0 Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Tue, 24 Mar 2015 15:41:00 +0100 Subject: [PATCH 02/24] add maDatagridMultiSelector directive --- .../Crud/list/maDatagridMultiSelector.js | 45 +++++++++ .../Crud/list/maDatagridMultiSelectorSpec.js | 92 +++++++++++++++++++ 2 files changed, 137 insertions(+) create mode 100644 src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js create mode 100644 src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js new file mode 100644 index 00000000..7d4b4eab --- /dev/null +++ b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js @@ -0,0 +1,45 @@ +/*global define*/ + +define(function () { + 'use strict'; + + function DatagridMultiSelectorDirective() { + return { + restrict: 'E', + scope: { + entries: '=', + selection: '=' + }, + template: '', + link: function (scope, element) { + var selection = JSON.stringify(scope.selection); + var entries = JSON.stringify(scope.entries); + + if (selection === entries){ + scope.selected = true; + } + if (selection === '[]'){ + scope.selected = false; + } + if (selection !== '[]' &&selection !== entries){ + scope.selected = null; + element.find('input').prop('indeterminate', true); + } + + scope.select = function (selected) { + scope.selected = !selected; + element.find('input').prop('indeterminate', false); + if (!selected) { + scope.selection = scope.entries; + return; + } + scope.selection = []; + }; + } + }; + } + + DatagridMultiSelectorDirective.$inject = []; + + return DatagridMultiSelectorDirective; +}); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js new file mode 100644 index 00000000..a0928b02 --- /dev/null +++ b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js @@ -0,0 +1,92 @@ +/*global define,angular,inject,describe,it,expect,beforeEach,module*/ + +define(function (require) { + 'use strict'; + + describe('directive: ma-datagrid-multi-selector', function () { + var directive = require('ng-admin/Crud/list/maDatagridMultiSelector'), + Entry = require('ng-admin/es6/lib/Entry'), + $compile, + scope, + directiveUsage = '' + ; + + angular.module('testapp_DatagridMultiSelector', []) + .directive('maDatagridMultiSelector', directive); + require('angular-mocks'); + + beforeEach(module('testapp_DatagridMultiSelector')); + + beforeEach(inject(function (_$compile_, _$rootScope_) { + $compile = _$compile_; + scope = _$rootScope_; + scope.entries = [new Entry('entity', {some: 'values'}, 'entity_1'), new Entry('entity', {some: 'values'}, 'entity_2'), new Entry('entity', {some: 'values'}, 'entity_3')]; + scope.selection = [new Entry('entity', {some: 'values'}, 'entity_2'), new Entry('entity', {some: 'values'}, 'entity_3')]; + })); + + it('checkbox should be indeterminate if entries does not correspond to selection', function () { + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(true); + expect(element.children()[0].checked).toBe(false); + }); + + it('checkbox should be true if entries correspond to selection', function () { + scope.selection = scope.entries; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + }); + + it('checkbox should be false if selection is empty', function () { + scope.selection = []; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(false); + }); + + it('checkbox should become true when clicking if selection is empty', function () { + scope.selection = []; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + + expect(scope.selection).toEqual(scope.entries); + }); + + it('checkbox should become true when clicking if selection differ from entries', function () { + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + + expect(scope.selection).toEqual(scope.entries); + }); + + it('checkbox should become false when clicking if selection is full', function () { + scope.selection = scope.entries; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(false); + + expect(scope.selection).toEqual([]); + }); + + }); +}); From c21ea2fd29519fd1d9b0c4964350eb836e73b78e Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Tue, 24 Mar 2015 18:12:43 +0100 Subject: [PATCH 03/24] use selectors in list datagrid --- src/javascripts/ng-admin/Crud/CrudModule.js | 2 + .../ng-admin/Crud/list/Datagrid.html | 6 ++ .../ng-admin/Crud/list/DatagridController.js | 1 + .../Crud/list/maDatagridItemSelector.js | 18 ++++-- .../Crud/list/maDatagridMultiSelector.js | 39 ++++++------ .../Crud/list/maDatagridItemSelectorSpec.js | 23 ++++++- .../Crud/list/maDatagridMultiSelectorSpec.js | 61 ++++++++++++++++--- .../test/unit/Crud/list/maDatagridSpec.js | 6 +- 8 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/CrudModule.js b/src/javascripts/ng-admin/Crud/CrudModule.js index ee970f3f..12ba808d 100644 --- a/src/javascripts/ng-admin/Crud/CrudModule.js +++ b/src/javascripts/ng-admin/Crud/CrudModule.js @@ -52,6 +52,8 @@ define(function (require) { CrudModule.directive('maDatagrid', require('ng-admin/Crud/list/maDatagrid')); CrudModule.directive('maDatagridPagination', require('ng-admin/Crud/list/maDatagridPagination')); CrudModule.directive('maDatagridInfinitePagination', require('ng-admin/Crud/list/maDatagridInfinitePagination')); + CrudModule.directive('maDatagridItemSelector', require('ng-admin/Crud/list/maDatagridItemSelector')); + CrudModule.directive('maDatagridMultiSelector', require('ng-admin/Crud/list/maDatagridMultiSelector')); CrudModule.directive('maFilter', require('ng-admin/Crud/filter/maFilter')); CrudModule.directive('maColumn', require('ng-admin/Crud/column/maColumn')); diff --git a/src/javascripts/ng-admin/Crud/list/Datagrid.html b/src/javascripts/ng-admin/Crud/list/Datagrid.html index ada1b018..173aa68f 100644 --- a/src/javascripts/ng-admin/Crud/list/Datagrid.html +++ b/src/javascripts/ng-admin/Crud/list/Datagrid.html @@ -1,6 +1,9 @@ + + diff --git a/src/javascripts/ng-admin/Crud/list/DatagridController.js b/src/javascripts/ng-admin/Crud/list/DatagridController.js index 94e4c28a..1008a3d2 100644 --- a/src/javascripts/ng-admin/Crud/list/DatagridController.js +++ b/src/javascripts/ng-admin/Crud/list/DatagridController.js @@ -17,6 +17,7 @@ define(function () { this.$location = $location; this.$anchorScroll = $anchorScroll; this.filters = {}; + $scope.selection = []; this.$scope.gotoDetail = this.gotoDetail.bind(this); diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js index cd1a44f6..bde27fb0 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js @@ -10,18 +10,24 @@ define(function () { entry: '=', selection: '=' }, - template: '', + template: '', link: function (scope) { - scope.selected = scope.selection.indexOf(scope.entry) !== -1; + var selected; - scope.select= function (selected) { - scope.selected = !selected; - if (!selected) { - scope.selection.push(scope.entry); + scope.$watch('selection', function (selection) { + selected = selection.indexOf(scope.entry) !== -1; + scope.selected = selected; + }); + + scope.select = function () { + selected = !selected; + if (selected) { + scope.selection = scope.selection.concat(scope.entry); return; } var index = scope.selection.indexOf(scope.entry); scope.selection.splice(index, 1); + scope.selection = scope.selection.slice(); }; } }; diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js index 7d4b4eab..9bd387c2 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js @@ -10,27 +10,32 @@ define(function () { entries: '=', selection: '=' }, - template: '', + template: '', link: function (scope, element) { - var selection = JSON.stringify(scope.selection); var entries = JSON.stringify(scope.entries); + var selected; - if (selection === entries){ - scope.selected = true; - } - if (selection === '[]'){ - scope.selected = false; - } - if (selection !== '[]' &&selection !== entries){ - scope.selected = null; - element.find('input').prop('indeterminate', true); - } + scope.$watch('selection', function (selection) { + selection = JSON.stringify(selection); + if (selection === entries){ + selected = true; + element.find('input').prop('indeterminate', false); + } + if (selection === '[]'){ + selected = false; + element.find('input').prop('indeterminate', false); + } + if (selection !== '[]' && selection !== entries){ + selected = false; + element.find('input').prop('indeterminate', true); + } + scope.selected = selected; + }); - scope.select = function (selected) { - scope.selected = !selected; - element.find('input').prop('indeterminate', false); - if (!selected) { - scope.selection = scope.entries; + scope.select = function () { + selected = !selected; + if (selected) { + scope.selection = scope.entries.slice(); return; } scope.selection = []; diff --git a/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js index 190e02f0..88b16cb1 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js @@ -39,13 +39,33 @@ define(function (require) { expect(element.children()[0].checked).toBe(true); }); + it('should become checked if entry is removed from selection', function () { + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].checked).toBe(false); + scope.selection = scope.selection.concat(scope.entry); + scope.$digest(); + expect(element.children()[0].checked).toBe(true); + }); + + it('should become unchecked if entry is added to selection', function () { + scope.selection = scope.selection.concat(scope.entry); + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].checked).toBe(true); + scope.selection = []; + scope.$digest(); + expect(element.children()[0].checked).toBe(false); + }); + it('should add unselected entry to selection on click', function () { var element = $compile(directiveUsage)(scope); scope.$digest(); element.find('input').triggerHandler('click'); - expect(element.children()[0].checked).toBe(true); expect(scope.selection.length).toBe(3); expect(scope.selection.indexOf(scope.entry)).toBe(2); }); @@ -58,7 +78,6 @@ define(function (require) { element.find('input').triggerHandler('click'); - expect(element.children()[0].checked).toBe(false); expect(scope.selection.length).toBe(2); expect(scope.selection.indexOf(scope.entry)).toBe(-1); }); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js index a0928b02..b7bd84f8 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js @@ -50,15 +50,66 @@ define(function (require) { expect(element.children()[0].checked).toBe(false); }); - it('checkbox should become true when clicking if selection is empty', function () { + it('checkbox should become indeterminate once selection stop corresponding to selection', function () { + scope.selection = scope.entries; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + + scope.selection = scope.selection.pop(); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(true); + expect(element.children()[0].checked).toBe(false); + }); + + + it('checkbox should become false once selection become empty', function () { + scope.selection = scope.entries; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + scope.selection = []; + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(false); + }); + + it('checkbox should become true once entries correspond to selection', function () { var element = $compile(directiveUsage)(scope); scope.$digest(); - element.find('input').triggerHandler('click'); + expect(element.children()[0].indeterminate).toBe(true); + expect(element.children()[0].checked).toBe(false); + + scope.selection = scope.entries; + scope.$digest(); expect(element.children()[0].indeterminate).toBe(false); expect(element.children()[0].checked).toBe(true); + }); + + it('checkbox should be false if selection is empty', function () { + scope.selection = []; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(false); + }); + + it('checkbox should become true when clicking if selection is empty', function () { + scope.selection = []; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + element.find('input').triggerHandler('click'); expect(scope.selection).toEqual(scope.entries); }); @@ -69,9 +120,6 @@ define(function (require) { element.find('input').triggerHandler('click'); - expect(element.children()[0].indeterminate).toBe(false); - expect(element.children()[0].checked).toBe(true); - expect(scope.selection).toEqual(scope.entries); }); @@ -82,9 +130,6 @@ define(function (require) { element.find('input').triggerHandler('click'); - expect(element.children()[0].indeterminate).toBe(false); - expect(element.children()[0].checked).toBe(false); - expect(scope.selection).toEqual([]); }); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridSpec.js index bb315954..a22efef8 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridSpec.js @@ -53,8 +53,8 @@ define(function (require) { scope.$digest(); - expect(element[0].querySelector('thead th:nth-child(2)').innerHTML).toContain('Actions'); - expect(element[0].querySelector('tbody tr td:nth-child(2) list-actions').nodeName).toContain('LIST-ACTIONS'); + expect(element[0].querySelector('thead th:nth-child(3)').innerHTML).toContain('Actions'); + expect(element[0].querySelector('tbody tr td:nth-child(3) list-actions').nodeName).toContain('LIST-ACTIONS'); }); @@ -69,7 +69,7 @@ define(function (require) { element = $compile(directiveUsage)(scope); scope.$digest(); - expect(element[0].querySelector('tbody tr td:nth-child(1) ma-column').nodeName).toContain('MA-COLUMN'); + expect(element[0].querySelector('tbody tr td:nth-child(2) ma-column').nodeName).toContain('MA-COLUMN'); }); }); }); From fc29d91a3550f32e95990dce6ce7349da670c92c Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Wed, 25 Mar 2015 13:28:26 +0100 Subject: [PATCH 04/24] move selection logic in datagridController --- .../ng-admin/Crud/list/Datagrid.html | 4 +- .../ng-admin/Crud/list/DatagridController.js | 28 +++++++- .../Crud/list/maDatagridItemSelector.js | 23 +++---- .../Crud/list/maDatagridMultiSelector.js | 27 +++----- .../Crud/list/maDatagridControllerSpec.js | 66 +++++++++++++++++++ .../Crud/list/maDatagridItemSelectorSpec.js | 22 ------- .../Crud/list/maDatagridMultiSelectorSpec.js | 29 -------- 7 files changed, 113 insertions(+), 86 deletions(-) create mode 100644 src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js diff --git a/src/javascripts/ng-admin/Crud/list/Datagrid.html b/src/javascripts/ng-admin/Crud/list/Datagrid.html index 173aa68f..a9933272 100644 --- a/src/javascripts/ng-admin/Crud/list/Datagrid.html +++ b/src/javascripts/ng-admin/Crud/list/Datagrid.html @@ -2,7 +2,7 @@
+ + @@ -16,6 +19,9 @@
+ +
- + @@ -20,7 +20,7 @@
- + diff --git a/src/javascripts/ng-admin/Crud/list/DatagridController.js b/src/javascripts/ng-admin/Crud/list/DatagridController.js index 1008a3d2..802cfee6 100644 --- a/src/javascripts/ng-admin/Crud/list/DatagridController.js +++ b/src/javascripts/ng-admin/Crud/list/DatagridController.js @@ -17,7 +17,10 @@ define(function () { this.$location = $location; this.$anchorScroll = $anchorScroll; this.filters = {}; - $scope.selection = []; + this.$scope.selection = []; + + $scope.toggleSelect = this.toggleSelect.bind(this); + $scope.toggleSelectAll = this.toggleSelectAll.bind(this); this.$scope.gotoDetail = this.gotoDetail.bind(this); @@ -95,6 +98,29 @@ define(function () { return this.$scope.name + '.' + field.name(); }; + DatagridController.prototype.toggleSelect = function (entry) { + var selection = this.$scope.selection.slice(); + + var index = selection.indexOf(entry); + + if (index === -1) { + this.$scope.selection = selection.concat(entry); + return; + } + selection.splice(index, 1); + this.$scope.selection = selection; + }; + + DatagridController.prototype.toggleSelectAll = function () { + + if (this.$scope.selection.length < this.$scope.entries.length) { + this.$scope.selection = this.$scope.entries; + return; + } + + this.$scope.selection = []; + }; + DatagridController.$inject = ['$scope', '$location', '$anchorScroll']; return DatagridController; diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js index bde27fb0..7562ea01 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagridItemSelector.js @@ -8,27 +8,20 @@ define(function () { restrict: 'E', scope: { entry: '=', - selection: '=' + selection: '=', + toggleSelect: '&' }, - template: '', + template: '', link: function (scope) { - var selected; + + scope.toggle = function (entry) { + scope.toggleSelect({entry: entry}); + }; scope.$watch('selection', function (selection) { - selected = selection.indexOf(scope.entry) !== -1; - scope.selected = selected; + scope.selected = selection.indexOf(scope.entry) !== -1; }); - scope.select = function () { - selected = !selected; - if (selected) { - scope.selection = scope.selection.concat(scope.entry); - return; - } - var index = scope.selection.indexOf(scope.entry); - scope.selection.splice(index, 1); - scope.selection = scope.selection.slice(); - }; } }; } diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js index 9bd387c2..c607f553 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js @@ -8,38 +8,31 @@ define(function () { restrict: 'E', scope: { entries: '=', - selection: '=' + selection: '=', + toggleSelectAll: '&' }, - template: '', + template: '', link: function (scope, element) { - var entries = JSON.stringify(scope.entries); - var selected; scope.$watch('selection', function (selection) { + var entries = JSON.stringify(scope.entries); selection = JSON.stringify(selection); if (selection === entries){ - selected = true; + scope.selected = true; element.find('input').prop('indeterminate', false); + return; } if (selection === '[]'){ - selected = false; + scope.selected = false; element.find('input').prop('indeterminate', false); + return; } if (selection !== '[]' && selection !== entries){ - selected = false; + scope.selected = false; element.find('input').prop('indeterminate', true); - } - scope.selected = selected; - }); - - scope.select = function () { - selected = !selected; - if (selected) { - scope.selection = scope.entries.slice(); return; } - scope.selection = []; - }; + }); } }; } diff --git a/src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js new file mode 100644 index 00000000..c7665fd9 --- /dev/null +++ b/src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js @@ -0,0 +1,66 @@ +/*global define,angular,inject,describe,it,expect,beforeEach,module*/ + +define(function (require) { + 'use strict'; + + describe('controller: ma-datagrid', function () { + var DataGridController = require('ng-admin/Crud/list/DatagridController'), + Entity = require('ng-admin/es6/lib/Entity/Entity'), + Entry = require('ng-admin/es6/lib/Entry'); + var dataGridController, entries; + + beforeEach(function () { + entries = [ + new Entry('my_entity', {value: 1}, 1), + new Entry('my_entity', {value: 2}, 2), + new Entry('my_entity', {value: 3}, 3), + ]; + + dataGridController = new DataGridController({ + entity: function () { + return new Entity('my_entity'); + }, + entries: entries + }, { + search: function () { + return {}; + } + }); + }); + + describe('toggleSelect', function () { + + it('should add entry in selection if it was not in it', function () { + dataGridController.toggleSelect(entries[0]); + expect(dataGridController.$scope.selection).toEqual([entries[0]]); + }); + + it('should remove entry from selection if it was in it', function () { + dataGridController.$scope.selection = entries; + dataGridController.toggleSelect(entries[0]); + expect(dataGridController.$scope.selection).toEqual([entries[1], entries[2]]); + }); + + }); + + describe('toggleSelectAll', function () { + it('should empty selection if it was full', function () { + dataGridController.$scope.selection = entries; + dataGridController.toggleSelectAll(); + expect(dataGridController.$scope.selection).toEqual([]); + }); + + it('should add all entries if selection was empty', function () { + dataGridController.$scope.selection = [entries]; + dataGridController.toggleSelectAll(); + expect(dataGridController.$scope.selection).toEqual(entries); + }); + + it('should select all entries if selection was incomplete', function () { + dataGridController.$scope.selection = [entries[0]]; + dataGridController.toggleSelectAll(); + expect(dataGridController.$scope.selection).toEqual(entries); + }); + }); + }); +}); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js index 88b16cb1..1ab2edf8 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridItemSelectorSpec.js @@ -60,27 +60,5 @@ define(function (require) { expect(element.children()[0].checked).toBe(false); }); - it('should add unselected entry to selection on click', function () { - var element = $compile(directiveUsage)(scope); - scope.$digest(); - - element.find('input').triggerHandler('click'); - - expect(scope.selection.length).toBe(3); - expect(scope.selection.indexOf(scope.entry)).toBe(2); - }); - - it('should remove selected entry from selection on click', function () { - scope.selection.push(scope.entry); - - var element = $compile(directiveUsage)(scope); - scope.$digest(); - - element.find('input').triggerHandler('click'); - - expect(scope.selection.length).toBe(2); - expect(scope.selection.indexOf(scope.entry)).toBe(-1); - }); - }); }); diff --git a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js index b7bd84f8..594656ac 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js @@ -104,34 +104,5 @@ define(function (require) { expect(element.children()[0].checked).toBe(false); }); - it('checkbox should become true when clicking if selection is empty', function () { - scope.selection = []; - var element = $compile(directiveUsage)(scope); - scope.$digest(); - - element.find('input').triggerHandler('click'); - - expect(scope.selection).toEqual(scope.entries); - }); - - it('checkbox should become true when clicking if selection differ from entries', function () { - var element = $compile(directiveUsage)(scope); - scope.$digest(); - - element.find('input').triggerHandler('click'); - - expect(scope.selection).toEqual(scope.entries); - }); - - it('checkbox should become false when clicking if selection is full', function () { - scope.selection = scope.entries; - var element = $compile(directiveUsage)(scope); - scope.$digest(); - - element.find('input').triggerHandler('click'); - - expect(scope.selection).toEqual([]); - }); - }); }); From 06d8b477aebcffc294638651f996a451d25d1bba Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Wed, 25 Mar 2015 14:20:48 +0100 Subject: [PATCH 05/24] consider selection to be complete even if order do not match --- .../Crud/list/maDatagridMultiSelector.js | 30 +++++++++++++++---- .../Crud/list/maDatagridMultiSelectorSpec.js | 29 ++++++++++++++++-- 2 files changed, 51 insertions(+), 8 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js index c607f553..144d4fcf 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagridMultiSelector.js @@ -3,6 +3,17 @@ define(function () { 'use strict'; + var compareArray = function (a1, a2) { + if (a2.length !== a1.length) { + return false; + } + + return a1.reduce(function (prev, cur) { + if(prev === false) { return prev; } + return a2.indexOf(cur) !== -1; + }, true); + }; + function DatagridMultiSelectorDirective() { return { restrict: 'E', @@ -14,24 +25,31 @@ define(function () { template: '', link: function (scope, element) { - scope.$watch('selection', function (selection) { - var entries = JSON.stringify(scope.entries); - selection = JSON.stringify(selection); - if (selection === entries){ + function updateState(selection, entries) { + var equal = compareArray(entries, selection); + + if (equal){ scope.selected = true; element.find('input').prop('indeterminate', false); return; } - if (selection === '[]'){ + if (selection.length === 0){ scope.selected = false; element.find('input').prop('indeterminate', false); return; } - if (selection !== '[]' && selection !== entries){ + if (selection !== '[]' && !equal){ scope.selected = false; element.find('input').prop('indeterminate', true); return; } + } + + scope.$watch('selection', function (selection) { + updateState(selection, scope.entries); + }); + scope.$watch('entries', function (entries) { + updateState(scope.selection, entries); }); } }; diff --git a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js index 594656ac..3103b3da 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridMultiSelectorSpec.js @@ -41,6 +41,15 @@ define(function (require) { expect(element.children()[0].checked).toBe(true); }); + it('checkbox should be true if entries correspond to selection even if order differ', function () { + scope.selection = [scope.entries[2], scope.entries[1], scope.entries[0]]; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + }); + it('checkbox should be false if selection is empty', function () { scope.selection = []; var element = $compile(directiveUsage)(scope); @@ -50,7 +59,7 @@ define(function (require) { expect(element.children()[0].checked).toBe(false); }); - it('checkbox should become indeterminate once selection stop corresponding to selection', function () { + it('checkbox should become indeterminate once selection stop corresponding to entries', function () { scope.selection = scope.entries; var element = $compile(directiveUsage)(scope); scope.$digest(); @@ -58,7 +67,23 @@ define(function (require) { expect(element.children()[0].indeterminate).toBe(false); expect(element.children()[0].checked).toBe(true); - scope.selection = scope.selection.pop(); + scope.selection = [scope.selection.pop()]; + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(true); + expect(element.children()[0].checked).toBe(false); + }); + + it('checkbox should become indeterminate once entries stop corresponding to selection', function () { + scope.selection = scope.entries; + var element = $compile(directiveUsage)(scope); + scope.$digest(); + + expect(element.children()[0].indeterminate).toBe(false); + expect(element.children()[0].checked).toBe(true); + + scope.entries = scope.entries.concat(new Entry('entity', {some: 'value'}, 'entity_4')); + scope.$digest(); expect(element.children()[0].indeterminate).toBe(true); From 24d736e2d0c849996390eec81e77dacbf077db44 Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Wed, 25 Mar 2015 15:11:20 +0100 Subject: [PATCH 06/24] give listActions access to the selection --- src/javascripts/ng-admin/Crud/list/DatagridController.js | 1 - src/javascripts/ng-admin/Crud/list/ListController.js | 1 + src/javascripts/ng-admin/Crud/list/list.html | 3 ++- src/javascripts/ng-admin/Crud/list/maDatagrid.js | 1 + src/javascripts/ng-admin/Crud/misc/ViewActions.js | 3 ++- ...maDatagridControllerSpec.js => DatagridControllerSpec.js} | 5 +++-- 6 files changed, 9 insertions(+), 5 deletions(-) rename src/javascripts/test/unit/Crud/list/{maDatagridControllerSpec.js => DatagridControllerSpec.js} (95%) diff --git a/src/javascripts/ng-admin/Crud/list/DatagridController.js b/src/javascripts/ng-admin/Crud/list/DatagridController.js index 802cfee6..94cdb943 100644 --- a/src/javascripts/ng-admin/Crud/list/DatagridController.js +++ b/src/javascripts/ng-admin/Crud/list/DatagridController.js @@ -17,7 +17,6 @@ define(function () { this.$location = $location; this.$anchorScroll = $anchorScroll; this.filters = {}; - this.$scope.selection = []; $scope.toggleSelect = this.toggleSelect.bind(this); $scope.toggleSelectAll = this.toggleSelectAll.bind(this); diff --git a/src/javascripts/ng-admin/Crud/list/ListController.js b/src/javascripts/ng-admin/Crud/list/ListController.js index 5fdad2b7..9612eef9 100644 --- a/src/javascripts/ng-admin/Crud/list/ListController.js +++ b/src/javascripts/ng-admin/Crud/list/ListController.js @@ -27,6 +27,7 @@ define(function () { this.infinitePagination = this.view.infinitePagination(); this.nextPageCallback = this.nextPage.bind(this); this.setPageCallback = this.setPage.bind(this); + this.selection = []; $scope.$on('$destroy', this.destroy.bind(this)); }; diff --git a/src/javascripts/ng-admin/Crud/list/list.html b/src/javascripts/ng-admin/Crud/list/list.html index cc3ba465..9dedffb8 100644 --- a/src/javascripts/ng-admin/Crud/list/list.html +++ b/src/javascripts/ng-admin/Crud/list/list.html @@ -1,6 +1,6 @@
- + @@ -19,6 +19,7 @@

diff --git a/src/javascripts/ng-admin/Crud/list/maDatagrid.js b/src/javascripts/ng-admin/Crud/list/maDatagrid.js index ee86fe0f..9dd2cdf7 100644 --- a/src/javascripts/ng-admin/Crud/list/maDatagrid.js +++ b/src/javascripts/ng-admin/Crud/list/maDatagrid.js @@ -13,6 +13,7 @@ define(function (require) { scope: { name: '@', entries: '=', + selection: '=', fields: '&', listActions: '&', entity: '&' diff --git a/src/javascripts/ng-admin/Crud/misc/ViewActions.js b/src/javascripts/ng-admin/Crud/misc/ViewActions.js index eda0b772..e2e81983 100644 --- a/src/javascripts/ng-admin/Crud/misc/ViewActions.js +++ b/src/javascripts/ng-admin/Crud/misc/ViewActions.js @@ -14,7 +14,8 @@ define(function (require) { scope: { 'override': '&', 'entry': '=', - 'entity': '=' + 'entity': '=', + 'selection': '=' }, template: viewActionsTemplate, link: function($scope, element, attrs, controller, transcludeFn) { diff --git a/src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js b/src/javascripts/test/unit/Crud/list/DatagridControllerSpec.js similarity index 95% rename from src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js rename to src/javascripts/test/unit/Crud/list/DatagridControllerSpec.js index c7665fd9..93859154 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridControllerSpec.js +++ b/src/javascripts/test/unit/Crud/list/DatagridControllerSpec.js @@ -1,4 +1,4 @@ -/*global define,angular,inject,describe,it,expect,beforeEach,module*/ +/*global define,describe,it,expect,beforeEach*/ define(function (require) { 'use strict'; @@ -20,7 +20,8 @@ define(function (require) { entity: function () { return new Entity('my_entity'); }, - entries: entries + entries: entries, + selection: [] }, { search: function () { return {}; From 223c69403eeabddf0c897bfb4471a33e3be4a80b Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Wed, 25 Mar 2015 15:28:18 +0100 Subject: [PATCH 07/24] allow to activate/deactivate selection via listView.selectable() --- src/javascripts/ng-admin/Crud/list/Datagrid.html | 4 ++-- src/javascripts/ng-admin/Crud/list/ListController.js | 2 +- src/javascripts/ng-admin/es6/lib/View/ListView.js | 11 +++++++++++ src/javascripts/test/unit/Crud/list/maDatagridSpec.js | 6 +++--- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/javascripts/ng-admin/Crud/list/Datagrid.html b/src/javascripts/ng-admin/Crud/list/Datagrid.html index a9933272..38334b6c 100644 --- a/src/javascripts/ng-admin/Crud/list/Datagrid.html +++ b/src/javascripts/ng-admin/Crud/list/Datagrid.html @@ -1,7 +1,7 @@ - -
+ @@ -19,7 +19,7 @@
+ diff --git a/src/javascripts/ng-admin/Crud/list/ListController.js b/src/javascripts/ng-admin/Crud/list/ListController.js index 9612eef9..bd3c5186 100644 --- a/src/javascripts/ng-admin/Crud/list/ListController.js +++ b/src/javascripts/ng-admin/Crud/list/ListController.js @@ -27,7 +27,7 @@ define(function () { this.infinitePagination = this.view.infinitePagination(); this.nextPageCallback = this.nextPage.bind(this); this.setPageCallback = this.setPage.bind(this); - this.selection = []; + this.selection = view.selectable() ? [] : null; $scope.$on('$destroy', this.destroy.bind(this)); }; diff --git a/src/javascripts/ng-admin/es6/lib/View/ListView.js b/src/javascripts/ng-admin/es6/lib/View/ListView.js index 1046c5d6..417fc4fc 100644 --- a/src/javascripts/ng-admin/es6/lib/View/ListView.js +++ b/src/javascripts/ng-admin/es6/lib/View/ListView.js @@ -9,6 +9,7 @@ class ListView extends View { this._infinitePagination = false; this._listActions = []; this._filters = []; + this._selectable = false; this._sortField = 'id'; this._sortDir = 'DESC'; @@ -86,6 +87,16 @@ class ListView extends View { return this; } + + selectable(enabled) { + if (!arguments.length) { + return this._selectable; + } + + this._selectable = enabled; + + return this; + } } export default ListView; diff --git a/src/javascripts/test/unit/Crud/list/maDatagridSpec.js b/src/javascripts/test/unit/Crud/list/maDatagridSpec.js index a22efef8..bb315954 100644 --- a/src/javascripts/test/unit/Crud/list/maDatagridSpec.js +++ b/src/javascripts/test/unit/Crud/list/maDatagridSpec.js @@ -53,8 +53,8 @@ define(function (require) { scope.$digest(); - expect(element[0].querySelector('thead th:nth-child(3)').innerHTML).toContain('Actions'); - expect(element[0].querySelector('tbody tr td:nth-child(3) list-actions').nodeName).toContain('LIST-ACTIONS'); + expect(element[0].querySelector('thead th:nth-child(2)').innerHTML).toContain('Actions'); + expect(element[0].querySelector('tbody tr td:nth-child(2) list-actions').nodeName).toContain('LIST-ACTIONS'); }); @@ -69,7 +69,7 @@ define(function (require) { element = $compile(directiveUsage)(scope); scope.$digest(); - expect(element[0].querySelector('tbody tr td:nth-child(2) ma-column').nodeName).toContain('MA-COLUMN'); + expect(element[0].querySelector('tbody tr td:nth-child(1) ma-column').nodeName).toContain('MA-COLUMN'); }); }); }); From b09564cb8c040abb29c78c3c2cb65b75a5bc3e8a Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Wed, 25 Mar 2015 15:35:24 +0100 Subject: [PATCH 08/24] update readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index e6bc92cf..ae58bf4a 100644 --- a/README.md +++ b/README.md @@ -314,6 +314,12 @@ Alternately, if you pass a string, it is compiled just like an Angular template, ''; listView.listActions(template); +* `selectable(boolean)` +Enable selection of entry in list. +The selection can be passed to custom listActions: +``` +listView.listActions(); +``` ## Fields A field is the representation of a property of an entity. From 5f171b28962919913118e1acf2172e1275c84884 Mon Sep 17 00:00:00 2001 From: ThieryMichel Date: Thu, 26 Mar 2015 16:21:53 +0100 Subject: [PATCH 09/24] add batchActions --- src/javascripts/ng-admin/Crud/CrudModule.js | 1 + .../ng-admin/Crud/list/ListController.js | 3 +- src/javascripts/ng-admin/Crud/list/list.html | 1 + .../ng-admin/Crud/misc/ViewBatchActions.js | 34 +++++++++++++++++++ .../Crud/misc/view-batch-actions.html | 15 ++++++++ .../ng-admin/es6/lib/View/ListView.js | 26 +++++++------- src/sass/ng-admin.scss | 2 +- 7 files changed, 67 insertions(+), 15 deletions(-) create mode 100644 src/javascripts/ng-admin/Crud/misc/ViewBatchActions.js create mode 100644 src/javascripts/ng-admin/Crud/misc/view-batch-actions.html diff --git a/src/javascripts/ng-admin/Crud/CrudModule.js b/src/javascripts/ng-admin/Crud/CrudModule.js index 12ba808d..61e24518 100644 --- a/src/javascripts/ng-admin/Crud/CrudModule.js +++ b/src/javascripts/ng-admin/Crud/CrudModule.js @@ -77,6 +77,7 @@ define(function (require) { CrudModule.directive('maDeleteButton', require('ng-admin/Crud/button/maDeleteButton')); CrudModule.directive('maViewActions', require('ng-admin/Crud/misc/ViewActions')); + CrudModule.directive('maViewBatchActions', require('ng-admin/Crud/misc/ViewBatchActions')); CrudModule.directive('compile', require('ng-admin/Crud/misc/Compile')); CrudModule.config(require('ng-admin/Crud/routing')); diff --git a/src/javascripts/ng-admin/Crud/list/ListController.js b/src/javascripts/ng-admin/Crud/list/ListController.js index bd3c5186..29f1db76 100644 --- a/src/javascripts/ng-admin/Crud/list/ListController.js +++ b/src/javascripts/ng-admin/Crud/list/ListController.js @@ -16,6 +16,7 @@ define(function () { this.title = view.title(); this.description = view.description(); this.actions = view.actions(); + this.batchActions = view.batchActions(); this.loadingPage = false; this.filters = this.$filter('orderElement')(view.filters()); this.hasFilters = Object.keys(this.filters).length > 0; @@ -27,7 +28,7 @@ define(function () { this.infinitePagination = this.view.infinitePagination(); this.nextPageCallback = this.nextPage.bind(this); this.setPageCallback = this.setPage.bind(this); - this.selection = view.selectable() ? [] : null; + this.selection = this.batchActions.length ? [] : null; $scope.$on('$destroy', this.destroy.bind(this)); }; diff --git a/src/javascripts/ng-admin/Crud/list/list.html b/src/javascripts/ng-admin/Crud/list/list.html index 9dedffb8..a83e12c7 100644 --- a/src/javascripts/ng-admin/Crud/list/list.html +++ b/src/javascripts/ng-admin/Crud/list/list.html @@ -4,6 +4,7 @@ +