From e9af25663ae0ca1c8d3f42d999a7779ad7849c6e Mon Sep 17 00:00:00 2001 From: Tira + Joao Date: Tue, 14 Mar 2017 16:54:22 -0400 Subject: [PATCH 01/11] Add column selector to SQLEditor - Update README with xsel package to enable testing with Pyperclip on linux --- test/javascript/column_selector_spec.js | 137 +++++++++++++++++++++ web/pgadmin/static/js/selection/column_selector.js | 67 ++++++++++ .../sqleditor/templates/sqleditor/js/sqleditor.js | 8 +- web/regression/README | 6 + web/regression/requirements.txt | 1 + 5 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 test/javascript/column_selector_spec.js create mode 100644 web/pgadmin/static/js/selection/column_selector.js diff --git a/test/javascript/column_selector_spec.js b/test/javascript/column_selector_spec.js new file mode 100644 index 00000000..30a98d42 --- /dev/null +++ b/test/javascript/column_selector_spec.js @@ -0,0 +1,137 @@ +define( + ["jquery", + "underscore", + "slickgrid/slick.grid", + "sources/selection/column_selector", + "slickgrid/slick.rowselectionmodel" + ], + function ($, _, SlickGrid, ColumnSelector, RowSelectionModel) { + describe("ColumnSelector", function () { + var container, data, columns, options; + beforeEach(function () { + container = $("
"); + data = [{'some-column-name': 'first value', 'second column': 'second value'}]; + + columns = [ + { + id: '1', + name: 'some-column-name', + selectable: true + }, + { + id: '2', + name: 'second column', + selectable: true + }] + }); + + it("renders a checkbox in the column header", function () { + var columnSelector = new ColumnSelector(columns); + columns = columnSelector.getColumnsWithCheckboxes(); + var grid = new SlickGrid(container, data, columns, options); + + grid.registerPlugin(columnSelector); + grid.invalidate(); + + expect(container.find('.slick-header-columns input').length).toBe(2) + }); + + it("displays the name of the column", function () { + var columnSelector = new ColumnSelector(columns); + columns = columnSelector.getColumnsWithCheckboxes(); + var grid = new SlickGrid(container, data, columns, options); + + grid.registerPlugin(columnSelector); + grid.invalidate(); + + expect($(container.find('.slick-header-columns .slick-column-name')[0]).text()).toBe('some-column-name'); + expect($(container.find('.slick-header-columns .slick-column-name')[1]).text()).toBe('second column'); + }); + + it("preserves the other attributes of column definitions", function () { + var columnSelector = new ColumnSelector(columns); + var selectableColumns = columnSelector.getColumnsWithCheckboxes(); + + expect(selectableColumns[0].id).toBe('1'); + expect(selectableColumns[0].selectable).toBe(true); + }); + + describe("when the user clicks on a column header", function () { + var grid, rowSelectionModel; + beforeEach(function () { + var columnSelector = new ColumnSelector(columns); + columns = columnSelector.getColumnsWithCheckboxes(); + data = []; + for (var i = 0; i < 10; i++) { + data.push({'some-column-name': 'some-value-' + i, 'second column': 'second value ' + i}); + } + grid = new SlickGrid(container, data, columns, options); + + rowSelectionModel = new RowSelectionModel(); + grid.setSelectionModel(rowSelectionModel); + grid.registerPlugin(columnSelector); + grid.invalidate(); + $("body").append(container); + container.find('.slick-header-column')[1].click(); + }); + + afterEach(function () { + $("body").find(container).remove(); + }); + + it("selects the column", function () { + var selectedRanges = rowSelectionModel.getSelectedRanges(); + var column = selectedRanges[0]; + + expect(selectedRanges.length).toEqual(1); + expect(column.fromCell).toBe(1); + expect(column.toCell).toBe(1); + expect(column.fromRow).toBe(0); + expect(column.toRow).toBe(9); + }); + + it("selects another column", function () { + container.find('.slick-header-column')[0].click(); + + var selectedRanges = rowSelectionModel.getSelectedRanges(); + var column1 = selectedRanges[0]; + + expect(selectedRanges.length).toEqual(2); + expect(column1.fromCell).toBe(1); + expect(column1.toCell).toBe(1); + + var column2 = selectedRanges[1]; + + expect(column2.fromCell).toBe(0); + expect(column2.toCell).toBe(0); + }); + + it("allows clicking on the checkbox", function () { + container.find('.slick-header-columns input')[1].click(); + + expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeFalsy(); + + }); + + it("checks the checkbox", function () { + expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeTruthy(); + }); + + describe("click a second time", function () { + beforeEach(function () { + container.find('.slick-header-column')[1].click(); + }); + + it("unchecks checkbox", function () { + expect($(container.find('.slick-header-columns input')[1]).is(':checked')).toBeFalsy(); + }); + + it("unselects the column", function () { + var selectedRanges = rowSelectionModel.getSelectedRanges(); + + expect(selectedRanges.length).toEqual(0); + }) + }); + }); + }); + }); \ No newline at end of file diff --git a/web/pgadmin/static/js/selection/column_selector.js b/web/pgadmin/static/js/selection/column_selector.js new file mode 100644 index 00000000..845b9a90 --- /dev/null +++ b/web/pgadmin/static/js/selection/column_selector.js @@ -0,0 +1,67 @@ +define(['jquery', 'slickgrid'], function ($) { + var ColumnSelector = function (columnDefinitions) { + var Slick = window.Slick; + + var init = function (grid) { + grid.onHeaderClick.subscribe(function (e, eventArgument) { + var column = eventArgument.column; + var _grid = eventArgument.grid; + + updateRanges(_grid, column); + + if(!clickedCheckbox(e)) { + var $checkbox = $("[data-id='checkbox-" + column.id + "']"); + toggleCheckbox($checkbox); + } + }); + }; + + function updateRanges(_grid, column) { + var selectionModel = _grid.getSelectionModel(); + var ranges = selectionModel.getSelectedRanges(); + + var columnIndex = _grid.getColumnIndex(column.id); + + var filteredRanges = ranges.filter(function (range) { + return !range.contains(0, columnIndex); + }); + + if (isNotPreviouslySelectedColumn(filteredRanges, ranges)) { + var range = new Slick.Range(0, columnIndex, _grid.getDataLength() - 1, columnIndex); + filteredRanges.push(range); + } + + selectionModel.setSelectedRanges(filteredRanges); + } + + function clickedCheckbox(e) { + return e.target.type == "checkbox" + } + + function isNotPreviouslySelectedColumn(filteredRanges, ranges) { + return filteredRanges.length == ranges.length + } + + function toggleCheckbox(checkbox) { + if (checkbox.prop("checked")) { + checkbox.prop("checked", false) + } else { + checkbox.prop("checked", true) + } + } + + function getColumnsWithCheckboxes() { + return _.map(columnDefinitions, function (columnDefinition) { + return _.extend(columnDefinition, { + name: "
" + columnDefinition.name + "
" + }); + }); + } + + $.extend(this, { + "init": init, + "getColumnsWithCheckboxes": getColumnsWithCheckboxes + }); + }; + return ColumnSelector; +}); diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js index 1bda0679..40974d9b 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/js/sqleditor.js @@ -2,7 +2,7 @@ define( [ 'jquery', 'underscore', 'underscore.string', 'alertify', 'pgadmin', 'backbone', 'backgrid', 'codemirror', 'pgadmin.misc.explain', - 'sources/selection/clipboard', + 'sources/selection/column_selector', 'sources/selection/clipboard', 'slickgrid', 'bootstrap', 'pgadmin.browser', 'wcdocker', 'codemirror/mode/sql/sql', 'codemirror/addon/selection/mark-selection', @@ -27,7 +27,7 @@ define( 'slickgrid/slick.grid' ], function( - $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, clipboard + $, _, S, alertify, pgAdmin, Backbone, Backgrid, CodeMirror, pgExplain, ColumnSelector, clipboard ) { /* Return back, this has been called more than once */ if (pgAdmin.SqlEditor) @@ -592,6 +592,9 @@ define( grid_columns.push(options) }); + var columnSelector = new ColumnSelector(grid_columns); + grid_columns = columnSelector.getColumnsWithCheckboxes(); + var grid_options = { editable: true, enableAddRow: is_editable, @@ -636,6 +639,7 @@ define( grid.registerPlugin( new Slick.AutoTooltips({ enableForHeaderCells: false }) ); grid.setSelectionModel(new Slick.RowSelectionModel({selectActiveRow: false})); grid.registerPlugin(checkboxSelector); + grid.registerPlugin(columnSelector); var editor_data = { keys: self.handler.primary_keys, diff --git a/web/regression/README b/web/regression/README index 2eb5c65a..72edaaf8 100644 --- a/web/regression/README +++ b/web/regression/README @@ -22,6 +22,12 @@ installed with: (pgadmin4) $ pip install -r $PGADMIN4_SRC/web/regression/requirements.txt +While running in Linux environments install: +sudo apt-get install xsel + +Otherwise the following error happens: +"Pyperclip could not find a copy/paste mechanism for your system" + General Information ------------------- diff --git a/web/regression/requirements.txt b/web/regression/requirements.txt index f644c12a..693ea177 100644 --- a/web/regression/requirements.txt +++ b/web/regression/requirements.txt @@ -1,4 +1,5 @@ chromedriver_installer==0.0.6 +pyperclip~=1.5.27 selenium==3.3.1 testscenarios==0.5.0 testtools==2.0.0 -- 2.12.0