public inbox for [email protected]  
help / color / mirror / Atom feed
[pgAdmin4][RM#3055] Allow user to sort the data in View data mode
25+ messages / 4 participants
[nested] [flat]

* [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
@ 2018-03-25 18:13 Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-03-25 18:13 UTC (permalink / raw)
  To: pgadmin-hackers

Hi,

PFA patch which allow user to sort the data in View data mode.

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [application/octet-stream] RM_3055.diff (92.0K, 3-RM_3055.diff)
  download | inline diff:
diff --git a/docs/en_US/editgrid.rst b/docs/en_US/editgrid.rst
index 1cb23cf..b8b58e5 100644
--- a/docs/en_US/editgrid.rst
+++ b/docs/en_US/editgrid.rst
@@ -46,16 +46,19 @@ Hover over an icon to display a tooltip that describes the icon's functionality.
 |                      |                                                                                                   |             |
 |                      |  Use options on the *Filter* menu to quick-sort or quick-filter the data set:                     |             |
 |                      |                                                                                                   |             |
-|                      |    * Filter: This option opens a dialog that allows you to define a filter.  A filter is a        |             |
+|                      |    * SQL Filter: This option opens a dialog that allows you to define a filter.  A filter is a    |             |
 |                      |      condition that is supplied to an arbitrary WHERE clause that restricts the result set.       |             |
 |                      |                                                                                                   |             |
-|                      |    * Remove Filter: This option removes all selection / exclusion filter conditions.              |             |
+|                      |    * Data Sorting: This option allows user to sort their data as per respective columns order     |             |
+|                      |      using data sorting dialog.                                                                   |             |
 |                      |                                                                                                   |             |
 |                      |    * By Selection: This option refreshes the data set and displays only those rows whose          |             |
 |                      |      column value matches the value in the cell currently selected.                               |             |
 |                      |                                                                                                   |             |
 |                      |    * Exclude Selection: This option refreshes the data set and excludes those rows whose          |             |
 |                      |      column value matches the value in the cell currently selected.                               |             |
+|                      |                                                                                                   |             |
+|                      |    * Remove Filter: This option removes any existing filter conditions.                           |             |
 +----------------------+---------------------------------------------------------------------------------------------------+-------------+
 | *No limit*           | Use the *No limit* drop-down listbox to specify how many rows to display in the output panel.     |             |
 |                      | Select from: *No limit* (the default), *1000 rows*, *500 rows*, or *100 rows*.                    |             |
@@ -95,6 +98,20 @@ To delete a row, press the *Delete* toolbar button.  A popup will open, asking y
 
 To commit the changes to the server, select the *Save* toolbar button.  Modifications to a row are written to the server automatically when you select a different row.
 
+**The Data Sorting dialog**
+
+.. image:: images/editgrid_data_sorting_dialog.png
+    :alt: Edit grid data sorting dialog
+
+Provide information about the data sorting in the edit grid window:
+
+To add new column for data sorting, click on the [+] icon.
 
+* Use the drop-down *Column* to select the column you want to sort.
+* Use the drop-down *Order* to select the sort order for the column.
 
+To discard newly added or an existing row(s) from the grid, click the trash icon.
 
+* Click the *Help* button (?) to access online help.
+* Click the *Ok* button to save work.
+* Click the *Close* button to discard current changes and close the dialog.
\ No newline at end of file
diff --git a/docs/en_US/images/editgrid_data_sorting_dialog.png b/docs/en_US/images/editgrid_data_sorting_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..4db53dbe0fc6be9b3e2168646b2add7f0f4f8701
GIT binary patch
literal 42262
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
TVxu_mAO3<FnCVk*IzRa@1dm;R

literal 0
HcmV?d00001

diff --git a/web/pgadmin/static/js/sqleditor/data_sorting.js b/web/pgadmin/static/js/sqleditor/data_sorting.js
new file mode 100644
index 0000000..2c3849c
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/data_sorting.js
@@ -0,0 +1,339 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string',
+  'pgadmin.alertifyjs', 'sources/pgadmin', 'backbone',
+  'pgadmin.backgrid', 'pgadmin.backform', 'axios',
+  'sources/sqleditor/query_tool_actions',
+  //'pgadmin.browser.node.ui',
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, Backbone,
+  Backgrid, Backform, axios, queryToolActions
+) {
+
+  let order_mapping = {
+    'asc': gettext('ASC'),
+    'desc': gettext('DESC'),
+  };
+
+  let dataSorting = {
+    'dialog': function(handler) {
+      let title = gettext('Data Sorting');
+      axios.get(
+        url_for('sqleditor.get_data_sorting', {
+          'trans_id': handler.transId,
+        }),
+        { headers: {'Cache-Control' : 'no-cache'} }
+      ).then(function (res) {
+        let response = res.data.data.result;
+        let FilterModel = pgAdmin.Browser.DataModel.extend({
+          idAttribute: 'name',
+          defaults: {
+            name: undefined,
+            order: 'asc',
+          },
+          schema: [{
+            id: 'name',
+            name: 'name',
+            label: gettext('Column'),
+            cell: 'select2',
+            editable: true,
+            cellHeaderClasses: 'width_percent_60',
+            headerCell: Backgrid.Extension.CustomHeaderCell,
+            disabled: false,
+            control: 'select2',
+            select2: {
+              allowClear: false,
+            },
+            options: function() {
+              return _.map(response.column_list, (obj) => {
+                return {
+                  value: obj,
+                  label: obj,
+                };
+              });
+            },
+          },
+          {
+            id: 'order',
+            name: 'order',
+            label: gettext('Order'),
+            control: 'select2',
+            cell: 'select2',
+            cellHeaderClasses: 'width_percent_40',
+            headerCell: Backgrid.Extension.CustomHeaderCell,
+            editable: true,
+            deps: ['type'],
+            select2: {
+              allowClear: false,
+            },
+            options: function() {
+              return _.map(order_mapping, (val, key) => {
+                return {
+                  value: key,
+                  label: val,
+                };
+              });
+            },
+          },
+          ],
+          validate: function() {
+            let msg = null;
+            this.errorModel.clear();
+            if (_.isUndefined(this.get('name')) ||
+              _.isNull(this.get('name')) ||
+              String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+              msg = gettext('Please select a column.');
+              this.errorModel.set('name', msg);
+              return msg;
+            } else if (_.isUndefined(this.get('order')) ||
+              _.isNull(this.get('order')) ||
+              String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
+              msg = gettext('Please select the order.');
+              this.errorModel.set('order', msg);
+              return msg;
+            }
+            return null;
+          },
+        });
+
+        let DataSortingCollectionModel = pgAdmin.Browser.DataModel.extend({
+          idAttribute: 'data_sorting',
+          schema: [{
+            id: 'data_sorting',
+            name: 'data_sorting',
+            label: gettext('Please select column(s)'),
+            model: FilterModel,
+            editable: true,
+            type: 'collection',
+            mode: ['create'],
+            control: 'unique-col-collection',
+            uniqueCol: ['name'],
+            canAdd: true,
+            canEdit: false,
+            canDelete: true,
+            visible: true,
+            version_compatible: true,
+          }],
+          validate: function() {
+            return null;
+          },
+        });
+
+        // Check the alertify dialog already loaded then delete it to clear
+        // the cache
+        if (Alertify.dataSorting) {
+          delete Alertify.dataSorting;
+        }
+
+        // Create Dialog
+        Alertify.dialog('dataSorting', function factory() {
+          let $container = $('<div class=\'data_sorting_dialog\'></div>');
+          return {
+            main: function() {
+              this.set('title', gettext('Data Sorting'));
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+              Alertify.pgDialogBuild.apply(this);
+            },
+            setup: function() {
+              return {
+                buttons: [{
+                  text: '',
+                  key: 27,
+                  className: 'btn btn-default pull-left fa fa-lg fa-question',
+                  attrs: {
+                    name: 'dialog_help',
+                    type: 'button',
+                    label: gettext('Help'),
+                    url: url_for('help.static', {
+                      'filename': 'editgrid.html',
+                    }),
+                  },
+                }, {
+                  text: gettext('Ok'),
+                  key: 27,
+                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                  'data-btn-name': 'ok',
+                }, {
+                  text: gettext('Cancel'),
+                  key: 27,
+                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                  'data-btn-name': 'cancel',
+                }],
+                // Set options for dialog
+                options: {
+                  title: title,
+                  //disable both padding and overflow control.
+                  padding: !1,
+                  overflow: !1,
+                  model: 0,
+                  resizable: true,
+                  maximizable: true,
+                  pinnable: false,
+                  closableByDimmer: false,
+                  modal: false,
+                  autoReset: false,
+                },
+              };
+            },
+            hooks: {
+              // triggered when the dialog is closed
+              onclose: function() {
+                if (this.view) {
+                  this.dataSortingCollectionModel.stopSession();
+                  this.view.model.stopSession();
+                  this.view.remove({
+                    data: true,
+                    internal: true,
+                    silent: true,
+                  });
+                }
+              },
+            },
+            prepare: function() {
+              let self = this;
+              $container.html('');
+              // Disable Ok button
+              this.__internal.buttons[1].element.disabled = true;
+
+              // Status bar
+              this.statusBar = $('<div class=\'pg-prop-status-bar pg-el-xs-12 hide\'>' +
+                '  <div class=\'media error-in-footer bg-red-1 border-red-2 font-red-3 text-14\'>' +
+                '    <div class=\'media-body media-middle\'>' +
+                '      <div class=\'alert-icon error-icon\'>' +
+                '        <i class=\'fa fa-exclamation-triangle\' aria-hidden=\'true\'></i>' +
+                '      </div>' +
+                '      <div class=\'alert-text\'>' +
+                '      </div>' +
+                '    </div>' +
+                '  </div>' +
+                '</div>', {
+                  text: '',
+                }).appendTo($container);
+
+              // To show progress on filter Saving/Updating on AJAX
+              this.showFilterProgress = $(
+                '<div id="show_filter_progress" class="wcLoadingIconContainer busy-fetching hidden">' +
+                '<div class="wcLoadingBackground"></div>' +
+                '<span class="wcLoadingIcon fa fa-spinner fa-pulse"></span>' +
+                '<span class="busy-text wcLoadingLabel">' + gettext('Loading data...') + '</span>' +
+                '</div>').appendTo($container);
+
+              $(
+                self.showFilterProgress[0]
+              ).removeClass('hidden');
+
+              self.dataSortingCollectionModel = new DataSortingCollectionModel();
+
+              let fields = Backform.generateViewSchema(
+                  null, self.dataSortingCollectionModel, 'create', null, null, true
+              );
+
+              let view = this.view = new Backform.Dialog({
+                el: '<div></div>',
+                model: self.dataSortingCollectionModel,
+                schema: fields,
+              });
+
+              $(this.elements.body.childNodes[0]).addClass(
+                'alertify_tools_dialog_properties obj_properties'
+              );
+
+              $container.append(view.render().$el);
+
+              // Enable/disable save button and show/hide statusbar based on session
+              view.listenTo(view.model, 'pgadmin-session:start', function() {
+                view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+                  self.statusBar.removeClass('hide');
+                  $(self.statusBar.find('.alert-text')).html(msg);
+                  // Disable Okay button
+                  self.__internal.buttons[1].element.disabled = true;
+                });
+
+                view.listenTo(view.model, 'pgadmin-session:valid', function() {
+                  self.statusBar.addClass('hide');
+                  $(self.statusBar.find('.alert-text')).html('');
+                  // Enable Okay button
+                  self.__internal.buttons[1].element.disabled = false;
+                });
+              });
+
+              view.listenTo(view.model, 'pgadmin-session:stop', function() {
+                view.stopListening(view.model, 'pgadmin-session:invalid');
+                view.stopListening(view.model, 'pgadmin-session:valid');
+              });
+
+              // Starts monitoring changes to model
+              view.model.startNewSession();
+
+              // Set  data in collection
+              let viewModel = view.model.get('data_sorting');
+              viewModel.add(response['data_sorting']);
+
+              // Hide Progress ...
+              $(
+                self.showFilterProgress[0]
+              ).addClass('hidden');
+
+            },
+            // Callback functions when click on the buttons of the Alertify dialogs
+            callback: function(e) {
+              if (e.button.element.name == 'dialog_help') {
+                e.cancel = true;
+                pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                  null, null, e.button.element.getAttribute('label'));
+                return;
+              }
+              let self = this;
+
+              if (e.button['data-btn-name'] === 'ok') {
+                e.cancel = true; // Do not close dialog
+
+                let dataSortingCollectionModel = this.dataSortingCollectionModel.toJSON();
+
+                // Show Progress ...
+                $(
+                  self.showFilterProgress[0]
+                ).removeClass('hidden');
+
+                axios.put(
+                  url_for('sqleditor.set_data_sorting', {
+                    'trans_id': handler.transId,
+                  }),
+                  dataSortingCollectionModel
+                ).then(function () {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+                  setTimeout(
+                    function() {
+                      self.close(); // Close the dialog now
+                      Alertify.success(gettext('Filter updated successfully'));
+                      queryToolActions.executeQuery(handler);
+                    }, 10
+                  );
+
+                }).catch(function (error) {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+
+                  setTimeout(
+                    function() {
+                      Alertify.error(error);
+                    }, 10
+                  );
+                });
+              }
+            },
+          };
+        });
+
+        Alertify.dataSorting(title).resizeTo('65%', '60%');
+      });
+    },
+  };
+  return dataSorting;
+});
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 2f3bb05..1ad1c9a 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -194,10 +194,11 @@
                 </button>
                 <ul class="dropdown-menu dropdown-menu-right">
                     <li>
-                        <a id="btn-filter-menu" href="#" tabindex="0">{{ _('Filter') }}</a>
-                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
+                        <a id="btn-filter-menu" href="#" tabindex="0">{{ _('SQL Filter') }}</a>
+                        <a id="btn-data-sorting" href="#" tabindex="0">{{ _('Data Sorting') }}</a>
                         <a id="btn-include-filter" href="#" tabindex="0">{{ _('By Selection') }}</a>
                         <a id="btn-exclude-filter" href="#" tabindex="0">{{ _('Exclude Selection') }}</a>
+                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
                     </li>
                 </ul>
             </div>
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 6f5d5b7..ed609d2 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -40,6 +40,7 @@ from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
     RegisterQueryToolPreferences
 from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
     read_file_generator
+from pgadmin.tools.sqleditor.utils.data_sorting import DataSorting
 
 MODULE_NAME = 'sqleditor'
 
@@ -106,7 +107,9 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.load_file',
             'sqleditor.save_file',
             'sqleditor.query_tool_download',
-            'sqleditor.connection_status'
+            'sqleditor.connection_status',
+            'sqleditor.get_data_sorting',
+            'sqleditor.set_data_sorting'
         ]
 
     def register_preferences(self):
@@ -1561,3 +1564,37 @@ def query_tool_status(trans_id):
         return internal_server_error(
             errormsg=gettext("Transaction status check failed.")
         )
+
+
[email protected](
+    '/data_sorting/<int:trans_id>',
+    methods=["GET"], endpoint='get_data_sorting'
+)
+@login_required
+def get_data_sorting(trans_id):
+    """
+    This method is used to get all the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return DataSorting.get(*check_transaction_status(trans_id))
+
+
[email protected](
+    '/data_sorting/<int:trans_id>',
+    methods=["PUT"], endpoint='set_data_sorting'
+)
+@login_required
+def set_data_sorting(trans_id):
+    """
+    This method is used to update the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return DataSorting.save(
+        *check_transaction_status(trans_id),
+        request=request,
+        trans_id=trans_id
+    )
diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py
index 8cc96e0..993b0d9 100644
--- a/web/pgadmin/tools/sqleditor/command.py
+++ b/web/pgadmin/tools/sqleditor/command.py
@@ -141,6 +141,10 @@ class SQLFilter(object):
       - This method removes the filter applied.
     * validate_filter(row_filter)
       - This method validates the given filter.
+    * get_data_sorting()
+      - This method returns columns for data sorting
+    * set_data_sorting()
+      - This method saves columns for data sorting
     """
 
     def __init__(self, **kwargs):
@@ -160,8 +164,8 @@ class SQLFilter(object):
         self.sid = kwargs['sid']
         self.did = kwargs['did']
         self.obj_id = kwargs['obj_id']
-        self.__row_filter = kwargs['sql_filter'] if 'sql_filter' in kwargs \
-            else None
+        self.__row_filter = kwargs.get('sql_filter', None)
+        self.__dara_sorting = kwargs.get('data_sorting', None)
 
         manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(self.sid)
         conn = manager.connection(did=self.did)
@@ -210,20 +214,41 @@ class SQLFilter(object):
 
         return status, msg
 
+    def get_data_sorting(self):
+        """
+        This function returns the filter.
+        """
+        if self.__dara_sorting and len(self.__dara_sorting) > 0:
+            return self.__dara_sorting
+        return None
+
+    def set_data_sorting(self, data_filter):
+        """
+        This function validates the filter and set the
+        given filter to member variable.
+        """
+        self.__dara_sorting = data_filter['data_sorting']
+
     def is_filter_applied(self):
         """
         This function returns True if filter is applied else False.
         """
+        is_filter_applied = True
         if self.__row_filter is None or self.__row_filter == '':
-            return False
+            is_filter_applied = False
 
-        return True
+        if not is_filter_applied:
+            if self.__dara_sorting and len(self.__dara_sorting) > 0:
+                is_filter_applied = True
+
+        return is_filter_applied
 
     def remove_filter(self):
         """
         This function remove the filter by setting value to None.
         """
         self.__row_filter = None
+        self.__dara_sorting = None
 
     def append_filter(self, row_filter):
         """
@@ -325,13 +350,58 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         self.cmd_type = kwargs['cmd_type'] if 'cmd_type' in kwargs else None
         self.limit = -1
 
-        if self.cmd_type == VIEW_FIRST_100_ROWS or \
-                self.cmd_type == VIEW_LAST_100_ROWS:
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_LAST_100_ROWS):
             self.limit = 100
 
     def get_primary_keys(self, *args, **kwargs):
         return None, None
 
+    def get_all_columns_with_order(self, default_conn):
+        """
+        Responsible for fetching columns from given object
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Columns which are already sorted which will
+                         be used to populate the Grid in the dialog
+            all_columns: List of all the column for given object which will
+                         be used to fill columns options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def save(self, changed_data, default_conn=None):
         return forbidden(
             errmsg=gettext("Data cannot be saved for the current object.")
@@ -351,6 +421,17 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         """
         self.limit = limit
 
+    def get_pk_order(self):
+        """
+        This function gets the order required for primary keys
+        """
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_ALL_ROWS):
+            return 'asc'
+        elif self.cmd_type == VIEW_LAST_100_ROWS:
+            return 'desc'
+        else:
+            return None
+
 
 class TableCommand(GridCommand):
     """
@@ -385,6 +466,7 @@ class TableCommand(GridCommand):
         has_oids = self.has_oids(default_conn)
 
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
@@ -392,7 +474,8 @@ class TableCommand(GridCommand):
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, limit=self.limit,
-                primary_keys=primary_keys, has_oids=has_oids
+                primary_keys=primary_keys, has_oids=has_oids,
+                data_sorting=data_sorting
             )
         else:
             sql = render_template(
@@ -401,7 +484,7 @@ class TableCommand(GridCommand):
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, sql_filter=sql_filter,
                 limit=self.limit, primary_keys=primary_keys,
-                has_oids=has_oids
+                has_oids=has_oids, data_sorting=data_sorting
             )
 
         return sql
@@ -447,6 +530,73 @@ class TableCommand(GridCommand):
 
         return pk_names, primary_keys
 
+    def get_all_columns_with_order(self, default_conn=None):
+        """
+        It is overridden method specially for Table because we all have to
+        fetch primary keys and rest of the columns both.
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Sorted columns for the Grid
+            all_columns: List of columns for the select2 options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+
+            # Fetch the primary key column names
+            query = render_template(
+                "/".join([self.sql_path, 'primary_keys.sql']),
+                obj_id=self.obj_id
+            )
+
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+                all_sorted_columns.append(
+                    {
+                        'name': row['attname'],
+                        'order': self.get_pk_order()
+                    }
+                )
+
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                # Only append if not already present in the list
+                if row['attname'] not in all_columns:
+                    all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def can_edit(self):
         return True
 
@@ -771,20 +921,22 @@ class ViewCommand(GridCommand):
         to fetch the data for the specified view
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -832,20 +984,22 @@ class ForeignTableCommand(GridCommand):
         to fetch the data for the specified foreign table
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -883,20 +1037,22 @@ class CatalogCommand(GridCommand):
         to fetch the data for the specified catalog object
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -929,6 +1085,9 @@ class QueryToolCommand(BaseCommand, FetchedRowTracker):
     def get_sql(self, default_conn=None):
         return None
 
+    def get_all_columns_with_order(self, default_conn=None):
+        return None
+
     def can_edit(self):
         return False
 
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 46588dc..25599a4 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -602,3 +602,10 @@ input.editor-checkbox:focus {
   font-size: 13px;
   line-height: 3em;
 }
+
+/* For Filter status bar */
+.data_sorting_dialog .pg-prop-status-bar {
+  position: absolute;
+  bottom: 37px;
+  z-index: 5;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 923ccea..e2b23be 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
   'sources/sqleditor_utils',
   'sources/sqleditor/execute_query',
   'sources/sqleditor/is_new_transaction_required',
+  'sources/sqleditor/data_sorting',
   'sources/history/index.js',
   'sources/../jsx/history/query_history',
   'react', 'react-dom',
@@ -30,7 +31,7 @@ define('tools.querytool', [
 ], function(
   babelPollyfill, gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror,
   pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, transaction,
+  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, transaction, DataSortingHandler,
   HistoryBundle, queryHistory, React, ReactDOM,
   keyboardShortcuts, queryToolActions, Datagrid) {
   /* Return back, this has been called more than once */
@@ -71,6 +72,7 @@ define('tools.querytool', [
       'click #btn-delete-row': 'on_delete',
       'click #btn-filter': 'on_show_filter',
       'click #btn-filter-menu': 'on_show_filter',
+      'click #btn-data-sorting': 'on_data_sorting',
       'click #btn-include-filter': 'on_include_filter',
       'click #btn-exclude-filter': 'on_exclude_filter',
       'click #btn-remove-filter': 'on_remove_filter',
@@ -1366,6 +1368,21 @@ define('tools.querytool', [
       );
     },
 
+    // Callback function for data sorting button click.
+    on_data_sorting: function(ev) {
+      var self = this;
+
+      this._stopEventPropogation(ev);
+      this._closeDropDown(ev);
+
+      // Trigger the data_sorting signal to the SqlEditorController class
+      self.handler.trigger(
+        'pgadmin-sqleditor:button:data_sorting',
+        self,
+        self.handler
+      );
+    },
+
     // Callback function for include filter button click.
     on_include_filter: function(ev) {
       var self = this;
@@ -2057,6 +2074,7 @@ define('tools.querytool', [
         self.on('pgadmin-sqleditor:button:save', self._save, self);
         self.on('pgadmin-sqleditor:button:deleterow', self._delete, self);
         self.on('pgadmin-sqleditor:button:show_filter', self._show_filter, self);
+        self.on('pgadmin-sqleditor:button:data_sorting', self._data_sorting, self);
         self.on('pgadmin-sqleditor:button:include_filter', self._include_filter, self);
         self.on('pgadmin-sqleditor:button:exclude_filter', self._exclude_filter, self);
         self.on('pgadmin-sqleditor:button:remove_filter', self._remove_filter, self);
@@ -3245,6 +3263,12 @@ define('tools.querytool', [
         });
       },
 
+      // This function will used when user wants custom data sorting
+      _data_sorting: function() {
+        let self = this;
+        DataSortingHandler.dialog(self);
+      },
+
       // This function will include the filter by selection.
       _include_filter: function() {
         var self = this,
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
new file mode 100644
index 0000000..610747d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
@@ -0,0 +1,9 @@
+{# ============= Fetch the columns ============= #}
+{% if obj_id %}
+SELECT at.attname, ty.typname
+    FROM pg_attribute at
+    LEFT JOIN pg_type ty ON (ty.oid = at.atttypid)
+WHERE attrelid={{obj_id}}::oid
+    AND at.attnum > 0
+    AND at.attisdropped = FALSE
+{% endif %}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
index 1cb60d9..add1658 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
@@ -3,7 +3,11 @@ SELECT {% if has_oids %}oid, {% endif %}* FROM {{ conn|qtIdent(nsp_name, object_
 {% if sql_filter %}
 WHERE {{ sql_filter }}
 {% endif %}
-{% if primary_keys %}
+{% if data_sorting and data_sorting|length > 0 %}
+ORDER BY {% for obj in data_sorting %}
+{{ conn|qtIdent(obj.name) }} {{ obj.order|upper }}{% if not loop.last %}, {% else %} {% endif %}
+{% endfor %}
+{% elif primary_keys %}
 ORDER BY {% for p in primary_keys %}{{conn|qtIdent(p)}}{% if cmd_type == 1 or cmd_type == 3 %} ASC{% elif cmd_type == 2 %} DESC{% endif %}
 {% if not loop.last %}, {% else %} {% endif %}{% endfor %}
 {% endif %}
diff --git a/web/pgadmin/tools/sqleditor/utils/data_sorting.py b/web/pgadmin/tools/sqleditor/utils/data_sorting.py
new file mode 100644
index 0000000..7a4c406
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/data_sorting.py
@@ -0,0 +1,91 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Code to handle data sorting in view data mode."""
+import pickle
+import simplejson as json
+from flask_babel import gettext
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
+    update_session_grid_transaction
+
+
+class DataSorting(object):
+    @staticmethod
+    def get(*args):
+        """To fetch the current sorted columns"""
+        status, error_msg, conn, trans_obj, session_obj = args
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+        column_list = []
+        if status and conn is not None and \
+                trans_obj is not None and session_obj is not None:
+            msg = gettext('Success')
+            columns, column_list = trans_obj.get_all_columns_with_order(conn)
+        else:
+            status = False
+            msg = error_msg
+            columns = None
+
+        return make_json_response(
+            data={
+                'status': status,
+                'msg': msg,
+                'result': {
+                    'data_sorting': columns,
+                    'column_list': column_list
+                }
+            }
+        )
+
+    @staticmethod
+    def save(*args, **kwargs):
+        """To save the sorted columns"""
+        # Check the transaction and connection status
+        status, error_msg, conn, trans_obj, session_obj = args
+        trans_id = kwargs['trans_id']
+        request = kwargs['request']
+
+        if request.data:
+            sorting_data = json.loads(request.data, encoding='utf-8')
+        else:
+            sorting_data = request.args or request.form
+
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+
+        if status and conn is not None and \
+           trans_obj is not None and session_obj is not None:
+            trans_obj.set_data_sorting(sorting_data)
+            # As we changed the transaction object we need to
+            # restore it and update the session variable.
+            session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
+            update_session_grid_transaction(trans_id, session_obj)
+            res = gettext('Data sorting object updated successfully')
+        else:
+            return internal_server_error(
+                errormsg=gettext('Failed to update the data on server.')
+            )
+
+        return make_json_response(
+            data={
+                'status': status,
+                'result': res
+            }
+        )
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_data_sorting_callbacks.py b/web/pgadmin/tools/sqleditor/utils/tests/test_data_sorting_callbacks.py
new file mode 100644
index 0000000..6364cfb
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_data_sorting_callbacks.py
@@ -0,0 +1,103 @@
+#######################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Apply Explain plan wrapper to sql object."""
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.data_sorting import DataSorting
+from pgadmin.utils.route import BaseTestGenerator
+
+TX_ID_ERROR_MSG = 'Transaction ID not found in the session.'
+FAILED_TX_MSG = 'Failed to update the data on server.'
+
+
+class MockRequest(object):
+    "To mock request object"
+    def __init__(self):
+        self.data = None
+        self.args = "Test data",
+
+
+class StartRunningDataSortingTest(BaseTestGenerator):
+    """
+    Check that the DataSorting methods works as
+    intended
+    """
+    scenarios = [
+        ('When we do not find Transaction ID in session in get', dict(
+            input_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='get'
+        )),
+        ('When we pass all the values as None in get', dict(
+            input_parameters=(None, None, None, None, None),
+            expected_return_response={
+                'data': {
+                    'status': False,
+                    'msg': None,
+                    'result': {
+                        'data_sorting': None,
+                        'column_list': []
+                    }
+                }
+            },
+            type='get'
+        )),
+
+        ('When we do not find Transaction ID in session in save', dict(
+            input_arg_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='save'
+        )),
+
+        ('When we pass all the values as None in save', dict(
+            input_arg_parameters=(None, None, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'status': 500,
+                'success': 0,
+                'errormsg': FAILED_TX_MSG
+
+            },
+            type='save'
+        ))
+    ]
+
+    def runTest(self):
+        expected_response = make_json_response(
+            **self.expected_return_response
+        )
+        if self.type == 'get':
+            result = DataSorting.get(*self.input_parameters)
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
+        else:
+            result = DataSorting.save(
+                *self.input_arg_parameters, **self.input_kwarg_parameters
+            )
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
diff --git a/web/regression/javascript/sqleditor/data_sorting_specs.js b/web/regression/javascript/sqleditor/data_sorting_specs.js
new file mode 100644
index 0000000..cb19556
--- /dev/null
+++ b/web/regression/javascript/sqleditor/data_sorting_specs.js
@@ -0,0 +1,30 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+import dataSorting from 'sources/sqleditor/data_sorting';
+
+describe('dataSorting', () => {
+  let sqlEditorController;
+  sqlEditorController = jasmine.createSpy('sqlEditorController')
+  describe('dataSorting', () => {
+    describe('when data sorting filter dialog', () => {
+      beforeEach(() => {
+        spyOn(dataSorting, 'dialog');
+      });
+
+      it("it should be defined as function", function() {
+        expect(dataSorting.dialog).toBeDefined();
+      });
+
+      it('it should call without proper handler', () => {
+        expect(dataSorting.dialog).not.toHaveBeenCalledWith({});
+      });
+
+    });
+  });
+});


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-03-26 12:22 ` Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-03-26 12:22 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: pgadmin-hackers

Hi

On Sun, Mar 25, 2018 at 7:13 PM, Murtuza Zabuawala <
[email protected]> wrote:

> Hi,
>
> PFA patch which allow user to sort the data in View data mode.
>

The patch looks good in general, however I'm not sure about the UI, in
particular that the closely-linked dialogue for filtering is a completely
different design. I think it would be better to combine the Sort/Filter
options and use a single dialogue for both, as pgAdmin 3 did (though, maybe
not using separate tabs for each part, but the top and bottom of the same
dialogue.

That would certainly fix the consistency of the dialogues (obviously, as
there would only be one!), and I think would perhaps be a more simple
overall UI, particularly for those that want to sort and filter.

Thoughts?

Thanks.

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-03-26 16:13   ` Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-03-26 16:13 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: pgadmin-hackers

On Mon, Mar 26, 2018 at 5:52 PM, Dave Page <[email protected]> wrote:

> Hi
>
> On Sun, Mar 25, 2018 at 7:13 PM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> Hi,
>>
>> PFA patch which allow user to sort the data in View data mode.
>>
>
> The patch looks good in general, however I'm not sure about the UI, in
> particular that the closely-linked dialogue for filtering is a completely
> different design. I think it would be better to combine the Sort/Filter
> options and use a single dialogue for both, as pgAdmin 3 did (though, maybe
> not using separate tabs for each part, but the top and bottom of the same
> dialogue.
>
> That would certainly fix the consistency of the dialogues (obviously, as
> there would only be one!), and I think would perhaps be a more simple
> overall UI, particularly for those that want to sort and filter.
>
​Sure, I'll send updated it accordingly.​


>
> Thoughts?
>
> Thanks.
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-03-26 18:07     ` Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Joao De Almeida Pereira @ 2018-03-26 18:07 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Dave Page <[email protected]>; pgadmin-hackers

Hi Hackers,

@Murtuza: The patch codewise looks good. Nice to see that we are using
axios instead of jquery ajax calls and that there is some coverage for the
change.
Nevertheless the Javascript testing looks a bit slim and could be improved.
Also the DataSorting class could have some other member functions like the
model validation could be extracted out so that it is easily tested.


@Hackers: This was how we tried to test this feature:
1 - Started pgAdmin
2 - Opened the query tool for a specific server
3 - Executed a SQL statment
4 - Pressed the column header to try to order, nothing happened
5 - Right clicked the column header to see if it was there the option,
nothing

This is the behavior that we were expecting, not to have to open Data View
and then press the icon that is not even near the grid in order to sort the
column. Is this really the way we want people to use the grid in pgAdmin?
Should it be more intuitive?


PS: Also that Orange after the selection is like a push in the eyes and not
in a good way. Maybe we should think about changing the color of the icon
to blue to match the rest of the website or something.

Thanks
Victoria & Joao

On Mon, Mar 26, 2018 at 12:13 PM Murtuza Zabuawala <
[email protected]> wrote:

> On Mon, Mar 26, 2018 at 5:52 PM, Dave Page <[email protected]> wrote:
>
>> Hi
>>
>> On Sun, Mar 25, 2018 at 7:13 PM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> Hi,
>>>
>>> PFA patch which allow user to sort the data in View data mode.
>>>
>>
>> The patch looks good in general, however I'm not sure about the UI, in
>> particular that the closely-linked dialogue for filtering is a completely
>> different design. I think it would be better to combine the Sort/Filter
>> options and use a single dialogue for both, as pgAdmin 3 did (though, maybe
>> not using separate tabs for each part, but the top and bottom of the same
>> dialogue.
>>
>> That would certainly fix the consistency of the dialogues (obviously, as
>> there would only be one!), and I think would perhaps be a more simple
>> overall UI, particularly for those that want to sort and filter.
>>
> ​Sure, I'll send updated it accordingly.​
>
>
>>
>> Thoughts?
>>
>> Thanks.
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
@ 2018-03-26 20:26       ` Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Robert Eckhardt @ 2018-03-26 20:26 UTC (permalink / raw)
  To: Joao De Almeida Pereira <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Dave Page <[email protected]>; pgadmin-hackers

On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
[email protected]> wrote:

> Hi Hackers,
>
> @Murtuza: The patch codewise looks good. Nice to see that we are using
> axios instead of jquery ajax calls and that there is some coverage for the
> change.
> Nevertheless the Javascript testing looks a bit slim and could be
> improved. Also the DataSorting class could have some other member functions
> like the model validation could be extracted out so that it is easily
> tested.
>
>
> @Hackers: This was how we tried to test this feature:
> 1 - Started pgAdmin
> 2 - Opened the query tool for a specific server
> 3 - Executed a SQL statment
> 4 - Pressed the column header to try to order, nothing happened
> 5 - Right clicked the column header to see if it was there the option,
> nothing
>
> This is the behavior that we were expecting, not to have to open Data View
> and then press the icon that is not even near the grid in order to sort the
> column. Is this really the way we want people to use the grid in pgAdmin?
> Should it be more intuitive?
>

Have we considered making the grid behave more like excel or other grids? I
think that having the ascending and descending inside the column header, we
could similarly provide filtering. Something that would give users a more
intuitive place to look.

-- Rob


>
>
> PS: Also that Orange after the selection is like a push in the eyes and
> not in a good way. Maybe we should think about changing the color of the
> icon to blue to match the rest of the website or something.
>
> Thanks
> Victoria & Joao
>
> On Mon, Mar 26, 2018 at 12:13 PM Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> On Mon, Mar 26, 2018 at 5:52 PM, Dave Page <[email protected]> wrote:
>>
>>> Hi
>>>
>>> On Sun, Mar 25, 2018 at 7:13 PM, Murtuza Zabuawala <murtuza.zabuawala@
>>> enterprisedb.com> wrote:
>>>
>>>> Hi,
>>>>
>>>> PFA patch which allow user to sort the data in View data mode.
>>>>
>>>
>>> The patch looks good in general, however I'm not sure about the UI, in
>>> particular that the closely-linked dialogue for filtering is a completely
>>> different design. I think it would be better to combine the Sort/Filter
>>> options and use a single dialogue for both, as pgAdmin 3 did (though, maybe
>>> not using separate tabs for each part, but the top and bottom of the same
>>> dialogue.
>>>
>>> That would certainly fix the consistency of the dialogues (obviously, as
>>> there would only be one!), and I think would perhaps be a more simple
>>> overall UI, particularly for those that want to sort and filter.
>>>
>> ​Sure, I'll send updated it accordingly.​
>>
>>
>>>
>>> Thoughts?
>>>
>>> Thanks.
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-27 09:43         ` Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-03-27 09:43 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Joao De Almeida Pereira <[email protected]>; Murtuza Zabuawala <[email protected]>; pgadmin-hackers

On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
> [email protected]> wrote:
>
>> Hi Hackers,
>>
>> @Murtuza: The patch codewise looks good. Nice to see that we are using
>> axios instead of jquery ajax calls and that there is some coverage for the
>> change.
>> Nevertheless the Javascript testing looks a bit slim and could be
>> improved. Also the DataSorting class could have some other member functions
>> like the model validation could be extracted out so that it is easily
>> tested.
>>
>>
>> @Hackers: This was how we tried to test this feature:
>> 1 - Started pgAdmin
>> 2 - Opened the query tool for a specific server
>> 3 - Executed a SQL statment
>> 4 - Pressed the column header to try to order, nothing happened
>> 5 - Right clicked the column header to see if it was there the option,
>> nothing
>>
>> This is the behavior that we were expecting, not to have to open Data
>> View and then press the icon that is not even near the grid in order to
>> sort the column. Is this really the way we want people to use the grid in
>> pgAdmin? Should it be more intuitive?
>>
>
> Have we considered making the grid behave more like excel or other grids?
> I think that having the ascending and descending inside the column header,
> we could similarly provide filtering. Something that would give users a
> more intuitive place to look.
>

Doing the sorting via header clicks is convenient but very restrictive. How
do you specify multiple columns to sort by for example? The current design
allows you to select columns and the sort order as you see fit.

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-03-27 10:25           ` Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-03-27 10:25 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Robert Eckhardt <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:

>
>
> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]>
> wrote:
>
>>
>>
>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>> [email protected]> wrote:
>>
>>> Hi Hackers,
>>>
>>> @Murtuza: The patch codewise looks good. Nice to see that we are using
>>> axios instead of jquery ajax calls and that there is some coverage for the
>>> change.
>>> Nevertheless the Javascript testing looks a bit slim and could be
>>> improved. Also the DataSorting class could have some other member functions
>>> like the model validation could be extracted out so that it is easily
>>> tested.
>>>
>>>
>>> @Hackers: This was how we tried to test this feature:
>>> 1 - Started pgAdmin
>>> 2 - Opened the query tool for a specific server
>>> 3 - Executed a SQL statment
>>> 4 - Pressed the column header to try to order, nothing happened
>>> 5 - Right clicked the column header to see if it was there the option,
>>> nothing
>>>
>>> This is the behavior that we were expecting, not to have to open Data
>>> View and then press the icon that is not even near the grid in order to
>>> sort the column. Is this really the way we want people to use the grid in
>>> pgAdmin? Should it be more intuitive?
>>>
>>
>> Have we considered making the grid behave more like excel or other grids?
>> I think that having the ascending and descending inside the column header,
>> we could similarly provide filtering. Something that would give users a
>> more intuitive place to look.
>>
>
> Doing the sorting via header clicks is convenient but very restrictive.
> How do you specify multiple columns to sort by for example? The current
> design allows you to select columns and the sort order as you see fit.
>

Another reason we can't use that because w
e have already occupied that behaviour for selecting entire column
​ when user clicks on header.
As Dave suggested, I will be merging it with filter dialog meaning it will
be accessible via direct button on toolbar & keyboard shortcut.​
​

>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-03-27 13:36             ` Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Robert Eckhardt @ 2018-03-27 13:36 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Dave Page <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
[email protected]> wrote:

> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>
>>
>>
>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>> [email protected]> wrote:
>>>
>>>> Hi Hackers,
>>>>
>>>> @Murtuza: The patch codewise looks good. Nice to see that we are using
>>>> axios instead of jquery ajax calls and that there is some coverage for the
>>>> change.
>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>> improved. Also the DataSorting class could have some other member functions
>>>> like the model validation could be extracted out so that it is easily
>>>> tested.
>>>>
>>>>
>>>> @Hackers: This was how we tried to test this feature:
>>>> 1 - Started pgAdmin
>>>> 2 - Opened the query tool for a specific server
>>>> 3 - Executed a SQL statment
>>>> 4 - Pressed the column header to try to order, nothing happened
>>>> 5 - Right clicked the column header to see if it was there the option,
>>>> nothing
>>>>
>>>> This is the behavior that we were expecting, not to have to open Data
>>>> View and then press the icon that is not even near the grid in order to
>>>> sort the column. Is this really the way we want people to use the grid in
>>>> pgAdmin? Should it be more intuitive?
>>>>
>>>
>>> Have we considered making the grid behave more like excel or other
>>> grids? I think that having the ascending and descending inside the column
>>> header, we could similarly provide filtering. Something that would give
>>> users a more intuitive place to look.
>>>
>>
>> Doing the sorting via header clicks is convenient but very restrictive.
>> How do you specify multiple columns to sort by for example? The current
>> design allows you to select columns and the sort order as you see fit.
>>
>
Honestly I'm not sold on my idea, I was just proposing an alternative in an
effort to start a discussion about the user experience. Ideally what I'd
like to see, maybe this happened, is some user research. When we initial
worked on refactoring the results grid we made a bunch of changes. One of
the things we intended to do was to follow up to see how people were using
the grid now so that we could better understand how it was now being used
in order to design and implement features just like this. Clearly we
haven't gotten there yet.


>
> Another reason we can't use that because w
> e have already occupied that behaviour for selecting entire column
> ​ when user clicks on header.
> As Dave suggested, I will be merging it with filter dialog meaning it will
> be accessible via direct button on toolbar & keyboard shortcut.​
> ​
>

How are users currently interacting with that filter dialog?

What I'm suggesting is that we understand how users want to interact with
their results, be those the results of a query or a table view, then we can
design something that meets those needs. I agree that changing the column
selection behavior isn't desirable, however, I also feel like providing the
best user experience is better than holding onto a particular feature
implementation.

-- Rob


>
>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-27 13:54               ` Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-03-27 13:54 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Dave Page <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>
>>>
>>>
>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Hackers,
>>>>>
>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are using
>>>>> axios instead of jquery ajax calls and that there is some coverage for the
>>>>> change.
>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>> improved. Also the DataSorting class could have some other member functions
>>>>> like the model validation could be extracted out so that it is easily
>>>>> tested.
>>>>>
>>>>>
>>>>> @Hackers: This was how we tried to test this feature:
>>>>> 1 - Started pgAdmin
>>>>> 2 - Opened the query tool for a specific server
>>>>> 3 - Executed a SQL statment
>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>> 5 - Right clicked the column header to see if it was there the option,
>>>>> nothing
>>>>>
>>>>> This is the behavior that we were expecting, not to have to open Data
>>>>> View and then press the icon that is not even near the grid in order to
>>>>> sort the column. Is this really the way we want people to use the grid in
>>>>> pgAdmin? Should it be more intuitive?
>>>>>
>>>>
>>>> Have we considered making the grid behave more like excel or other
>>>> grids? I think that having the ascending and descending inside the column
>>>> header, we could similarly provide filtering. Something that would give
>>>> users a more intuitive place to look.
>>>>
>>>
>>> Doing the sorting via header clicks is convenient but very restrictive.
>>> How do you specify multiple columns to sort by for example? The current
>>> design allows you to select columns and the sort order as you see fit.
>>>
>>
> Honestly I'm not sold on my idea, I was just proposing an alternative in
> an effort to start a discussion about the user experience. Ideally what I'd
> like to see, maybe this happened, is some user research. When we initial
> worked on refactoring the results grid we made a bunch of changes. One of
> the things we intended to do was to follow up to see how people were using
> the grid now so that we could better understand how it was now being used
> in order to design and implement features just like this. Clearly we
> haven't gotten there yet.
>
>
>>
>> Another reason we can't use that because w
>> e have already occupied that behaviour for selecting entire column
>> ​ when user clicks on header.
>> As Dave suggested, I will be merging it with filter dialog meaning it
>> will be accessible via direct button on toolbar & keyboard shortcut.​
>> ​
>>
>
> How are users currently interacting with that filter dialog?
>

​By clicking on the toolbar button as well as keyboard shortcut.
​




>
> What I'm suggesting is that we understand how users want to interact with
> their results, be those the results of a query or a table view, then we can
> design something that meets those needs. I agree that changing the column
> selection behavior isn't desirable, however, I also feel like providing the
> best user experience is better than holding onto a particular feature
> implementation.
> ​
>
>

> -- Rob
>
>
>>
>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-03-28 00:37                 ` Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 08:12                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 2 replies; 25+ messages in thread

From: Robert Eckhardt @ 2018-03-28 00:37 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Dave Page <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <murtuza.zabuawala@
enterprisedb.com> wrote:

>
>
> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
> wrote:
>
>>
>>
>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Hackers,
>>>>>>
>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>> for the change.
>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>> like the model validation could be extracted out so that it is easily
>>>>>> tested.
>>>>>>
>>>>>>
>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>> 1 - Started pgAdmin
>>>>>> 2 - Opened the query tool for a specific server
>>>>>> 3 - Executed a SQL statment
>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>> option, nothing
>>>>>>
>>>>>> This is the behavior that we were expecting, not to have to open Data
>>>>>> View and then press the icon that is not even near the grid in order to
>>>>>> sort the column. Is this really the way we want people to use the grid in
>>>>>> pgAdmin? Should it be more intuitive?
>>>>>>
>>>>>
>>>>> Have we considered making the grid behave more like excel or other
>>>>> grids? I think that having the ascending and descending inside the column
>>>>> header, we could similarly provide filtering. Something that would give
>>>>> users a more intuitive place to look.
>>>>>
>>>>
>>>> Doing the sorting via header clicks is convenient but very restrictive.
>>>> How do you specify multiple columns to sort by for example? The current
>>>> design allows you to select columns and the sort order as you see fit.
>>>>
>>>
>> Honestly I'm not sold on my idea, I was just proposing an alternative in
>> an effort to start a discussion about the user experience. Ideally what I'd
>> like to see, maybe this happened, is some user research. When we initial
>> worked on refactoring the results grid we made a bunch of changes. One of
>> the things we intended to do was to follow up to see how people were using
>> the grid now so that we could better understand how it was now being used
>> in order to design and implement features just like this. Clearly we
>> haven't gotten there yet.
>>
>>
>>>
>>> Another reason we can't use that because w
>>> e have already occupied that behaviour for selecting entire column
>>> ​ when user clicks on header.
>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>> ​
>>>
>>
>> How are users currently interacting with that filter dialog?
>>
>
> ​By clicking on the toolbar button as well as keyboard shortcut.
> ​
>
>
>
>

Sorry I wasn't clear. My question was more along the lines of, do we know
if people are using the filter functionality?  What kind of filters are
people using?  What do they like about it? What do they wish they could do
above and beyond sorting, etc.

-- Rob


>
>> What I'm suggesting is that we understand how users want to interact with
>> their results, be those the results of a query or a table view, then we can
>> design something that meets those needs. I agree that changing the column
>> selection behavior isn't desirable, however, I also feel like providing the
>> best user experience is better than holding onto a particular feature
>> implementation.
>> ​
>>
>>
>
>> -- Rob
>>
>>
>>>
>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>>
>>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-28 07:19                   ` Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  1 sibling, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-03-28 07:19 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Dave Page <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

Hi Dave,

Please find updated patch with following changes,
- Combined Filter and Data sorting together same as pgAdmin3.
- Extracted model into separate file
- Change the colour of filter button from orange to blue.
- Updated docs and screenshot.

@Joao,
Could you please provide any reference for learning more about jasmine test
framework?
​

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
> [email protected]> wrote:
>
>>
>>
>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Hackers,
>>>>>>>
>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>> for the change.
>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>> tested.
>>>>>>>
>>>>>>>
>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>> 1 - Started pgAdmin
>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>> 3 - Executed a SQL statment
>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>> option, nothing
>>>>>>>
>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>
>>>>>>
>>>>>> Have we considered making the grid behave more like excel or other
>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>> users a more intuitive place to look.
>>>>>>
>>>>>
>>>>> Doing the sorting via header clicks is convenient but very
>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>> The current design allows you to select columns and the sort order as you
>>>>> see fit.
>>>>>
>>>>
>>> Honestly I'm not sold on my idea, I was just proposing an alternative in
>>> an effort to start a discussion about the user experience. Ideally what I'd
>>> like to see, maybe this happened, is some user research. When we initial
>>> worked on refactoring the results grid we made a bunch of changes. One of
>>> the things we intended to do was to follow up to see how people were using
>>> the grid now so that we could better understand how it was now being used
>>> in order to design and implement features just like this. Clearly we
>>> haven't gotten there yet.
>>>
>>>
>>>>
>>>> Another reason we can't use that because w
>>>> e have already occupied that behaviour for selecting entire column
>>>> ​ when user clicks on header.
>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>> ​
>>>>
>>>
>>> How are users currently interacting with that filter dialog?
>>>
>>
>> ​By clicking on the toolbar button as well as keyboard shortcut.
>> ​
>>
>>
>>
>>
>
> Sorry I wasn't clear. My question was more along the lines of, do we know
> if people are using the filter functionality?  What kind of filters are
> people using?  What do they like about it? What do they wish they could do
> above and beyond sorting, etc.
>
​I have not done any data gathering from users so I can't comment on your
queries.
​ but a​
​s far as I understood from the feature requests that most of the users
expect to have functionality which will allow then to sort columns as it
was in pgAdmin3.​



> -- Rob
>
>
>>
>>> What I'm suggesting is that we understand how users want to interact
>>> with their results, be those the results of a query or a table view, then
>>> we can design something that meets those needs. I agree that changing the
>>> column selection behavior isn't desirable, however, I also feel like
>>> providing the best user experience is better than holding onto a particular
>>> feature implementation.
>>> ​
>>>
>>>
>>
>>> -- Rob
>>>
>>>
>>>>
>>>>
>>>>> --
>>>>> Dave Page
>>>>> Blog: http://pgsnake.blogspot.com
>>>>> Twitter: @pgsnake
>>>>>
>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>
>>>>
>>>
>>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

  [application/octet-stream] RM_3154_v1.diff (132.6K, 4-RM_3154_v1.diff)
  download | inline diff:
diff --git a/docs/en_US/editgrid.rst b/docs/en_US/editgrid.rst
index 1cb23cf..b1a9551 100644
--- a/docs/en_US/editgrid.rst
+++ b/docs/en_US/editgrid.rst
@@ -95,6 +95,24 @@ To delete a row, press the *Delete* toolbar button.  A popup will open, asking y
 
 To commit the changes to the server, select the *Save* toolbar button.  Modifications to a row are written to the server automatically when you select a different row.
 
+**Sort/Filter options dialog**
 
+You can access *Sort/Filter options dialog* by clicking on Filter button, This dialog provides information about the sql filter and data sorting in the edit grid window:
 
+.. image:: images/editgrid_filter_dialog.png
+    :alt: Edit grid filter dialog window
+
+* Use *SQL filter* to provide where clause conditions
+* Use *Data sorting* to sort the data in the output grid
+
+To add new column(s) in data sorting grid, click on the [+] icon.
+
+* Use the drop-down *Column* to select the column you want to sort.
+* Use the drop-down *Order* to select the sort order for the column.
+
+To discard a data sorting, and delete the row from the grid, click the trash icon.
+
+* Click the *Help* button (?) to access online help.
+* Click the *Ok* button to save work.
+* Click the *Close* button to discard current changes and close the dialog.
 
diff --git a/docs/en_US/images/editgrid_filter_dialog.png b/docs/en_US/images/editgrid_filter_dialog.png
new file mode 100644
index 0000000000000000000000000000000000000000..046d9a124363a621e618efc524d745a8a526ebed
GIT binary patch
literal 68460
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
BJDmUk

literal 0
HcmV?d00001

diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog.js b/web/pgadmin/static/js/sqleditor/filter_dialog.js
new file mode 100644
index 0000000..0ba9e35
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog.js
@@ -0,0 +1,243 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string',
+  'pgadmin.alertifyjs', 'sources/pgadmin', 'backbone',
+  'pgadmin.backgrid', 'pgadmin.backform', 'axios',
+  'sources/sqleditor/query_tool_actions',
+  'sources/sqleditor/filter_dialog_model',
+  //'pgadmin.browser.node.ui',
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, Backbone,
+  Backgrid, Backform, axios, queryToolActions, filterDialogModel
+) {
+
+  let FilterDialog = {
+    'dialog': function(handler) {
+      let title = gettext('Sort/Filter options');
+      axios.get(
+        url_for('sqleditor.get_filter_data', {
+          'trans_id': handler.transId,
+        }),
+        { headers: {'Cache-Control' : 'no-cache'} }
+      ).then(function (res) {
+        let response = res.data.data.result;
+
+        // Check the alertify dialog already loaded then delete it to clear
+        // the cache
+        if (Alertify.filterDialog) {
+          delete Alertify.filterDialog;
+        }
+
+        // Create Dialog
+        Alertify.dialog('filterDialog', function factory() {
+          let $container = $('<div class=\'data_sorting_dialog\'></div>');
+          return {
+            main: function() {
+              this.set('title', gettext('Sort/Filter options'));
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+              Alertify.pgDialogBuild.apply(this);
+            },
+            setup: function() {
+              return {
+                buttons: [{
+                  text: '',
+                  key: 112,
+                  className: 'btn btn-default pull-left fa fa-lg fa-question',
+                  attrs: {
+                    name: 'dialog_help',
+                    type: 'button',
+                    label: gettext('Help'),
+                    url: url_for('help.static', {
+                      'filename': 'editgrid.html',
+                    }),
+                  },
+                }, {
+                  text: gettext('Ok'),
+                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                  'data-btn-name': 'ok',
+                }, {
+                  text: gettext('Cancel'),
+                  key: 27,
+                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                  'data-btn-name': 'cancel',
+                }],
+                // Set options for dialog
+                options: {
+                  title: title,
+                  //disable both padding and overflow control.
+                  padding: !1,
+                  overflow: !1,
+                  model: 0,
+                  resizable: true,
+                  maximizable: true,
+                  pinnable: false,
+                  closableByDimmer: false,
+                  modal: false,
+                  autoReset: false,
+                },
+              };
+            },
+            hooks: {
+              // triggered when the dialog is closed
+              onclose: function() {
+                if (this.view) {
+                  this.filterCollectionModel.stopSession();
+                  this.view.model.stopSession();
+                  this.view.remove({
+                    data: true,
+                    internal: true,
+                    silent: true,
+                  });
+                }
+              },
+            },
+            prepare: function() {
+              let self = this;
+              $container.html('');
+              // Disable Ok button
+              this.__internal.buttons[1].element.disabled = true;
+
+              // Status bar
+              this.statusBar = $('<div class=\'pg-prop-status-bar pg-el-xs-12 hide\'>' +
+                '  <div class=\'media error-in-footer bg-red-1 border-red-2 font-red-3 text-14\'>' +
+                '    <div class=\'media-body media-middle\'>' +
+                '      <div class=\'alert-icon error-icon\'>' +
+                '        <i class=\'fa fa-exclamation-triangle\' aria-hidden=\'true\'></i>' +
+                '      </div>' +
+                '      <div class=\'alert-text\'>' +
+                '      </div>' +
+                '    </div>' +
+                '  </div>' +
+                '</div>', {
+                  text: '',
+                }).appendTo($container);
+
+              // To show progress on filter Saving/Updating on AJAX
+              this.showFilterProgress = $(
+                '<div id="show_filter_progress" class="wcLoadingIconContainer busy-fetching hidden">' +
+                '<div class="wcLoadingBackground"></div>' +
+                '<span class="wcLoadingIcon fa fa-spinner fa-pulse"></span>' +
+                '<span class="busy-text wcLoadingLabel">' + gettext('Loading data...') + '</span>' +
+                '</div>').appendTo($container);
+
+              $(
+                self.showFilterProgress[0]
+              ).removeClass('hidden');
+
+              self.filterCollectionModel = filterDialogModel(response);
+
+              let fields = Backform.generateViewSchema(
+                  null, self.filterCollectionModel, 'create', null, null, true
+              );
+
+              let view = this.view = new Backform.Dialog({
+                el: '<div></div>',
+                model: self.filterCollectionModel,
+                schema: fields,
+              });
+
+              $(this.elements.body.childNodes[0]).addClass(
+                'alertify_tools_dialog_properties obj_properties'
+              );
+
+              $container.append(view.render().$el);
+
+              // Enable/disable save button and show/hide statusbar based on session
+              view.listenTo(view.model, 'pgadmin-session:start', function() {
+                view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+                  self.statusBar.removeClass('hide');
+                  $(self.statusBar.find('.alert-text')).html(msg);
+                  // Disable Okay button
+                  self.__internal.buttons[1].element.disabled = true;
+                });
+
+                view.listenTo(view.model, 'pgadmin-session:valid', function() {
+                  self.statusBar.addClass('hide');
+                  $(self.statusBar.find('.alert-text')).html('');
+                  // Enable Okay button
+                  self.__internal.buttons[1].element.disabled = false;
+                });
+              });
+
+              view.listenTo(view.model, 'pgadmin-session:stop', function() {
+                view.stopListening(view.model, 'pgadmin-session:invalid');
+                view.stopListening(view.model, 'pgadmin-session:valid');
+              });
+
+              // Starts monitoring changes to model
+              view.model.startNewSession();
+
+              // Set data in collection
+              let viewDataSortingModel = view.model.get('data_sorting');
+              viewDataSortingModel.add(response['data_sorting']);
+
+              // Hide Progress ...
+              $(
+                self.showFilterProgress[0]
+              ).addClass('hidden');
+
+            },
+            // Callback functions when click on the buttons of the Alertify dialogs
+            callback: function(e) {
+              let self = this;
+
+              if (e.button.element.name == 'dialog_help') {
+                e.cancel = true;
+                pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                  null, null, e.button.element.getAttribute('label'));
+                return;
+              } else if (e.button['data-btn-name'] === 'ok') {
+                e.cancel = true; // Do not close dialog
+
+                let filterCollectionModel = this.filterCollectionModel.toJSON();
+
+                // Show Progress ...
+                $(
+                  self.showFilterProgress[0]
+                ).removeClass('hidden');
+
+                axios.put(
+                  url_for('sqleditor.set_filter_data', {
+                    'trans_id': handler.transId,
+                  }),
+                  filterCollectionModel
+                ).then(function () {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+                  setTimeout(
+                    function() {
+                      self.close(); // Close the dialog now
+                      Alertify.success(gettext('Filter updated successfully'));
+                      queryToolActions.executeQuery(handler);
+                    }, 10
+                  );
+
+                }).catch(function (error) {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+                  handler.onExecuteHTTPError(error);
+
+                  setTimeout(
+                    function() {
+                      Alertify.error(error);
+                    }, 10
+                  );
+                });
+              } else {
+                self.close();
+              }
+            },
+          };
+        });
+
+        Alertify.filterDialog(title).resizeTo('65%', '60%');
+      });
+    },
+  };
+  return FilterDialog;
+});
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
new file mode 100644
index 0000000..c3146a4
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
@@ -0,0 +1,133 @@
+define([
+  'sources/gettext', 'underscore', 'sources/pgadmin',
+  'pgadmin.backform', 'pgadmin.backgrid',
+], function(
+  gettext, _, pgAdmin, Backform, Backgrid
+) {
+
+  let initModel = function(response) {
+
+    let order_mapping = {
+      'asc': gettext('ASC'),
+      'desc': gettext('DESC'),
+    };
+
+    let DataSortingModel = pgAdmin.Browser.DataModel.extend({
+      idAttribute: 'name',
+      defaults: {
+        name: undefined,
+        order: 'asc',
+      },
+      schema: [{
+        id: 'name',
+        name: 'name',
+        label: gettext('Column'),
+        cell: 'select2',
+        editable: true,
+        cellHeaderClasses: 'width_percent_60',
+        headerCell: Backgrid.Extension.CustomHeaderCell,
+        disabled: false,
+        control: 'select2',
+        select2: {
+          allowClear: false,
+        },
+        options: function() {
+          return _.map(response.column_list, (obj) => {
+            return {
+              value: obj,
+              label: obj,
+            };
+          });
+        },
+      },
+      {
+        id: 'order',
+        name: 'order',
+        label: gettext('Order'),
+        control: 'select2',
+        cell: 'select2',
+        cellHeaderClasses: 'width_percent_40',
+        headerCell: Backgrid.Extension.CustomHeaderCell,
+        editable: true,
+        deps: ['type'],
+        select2: {
+          allowClear: false,
+        },
+        options: function() {
+          return _.map(order_mapping, (val, key) => {
+            return {
+              value: key,
+              label: val,
+            };
+          });
+        },
+      },
+      ],
+      validate: function() {
+        let msg = null;
+        this.errorModel.clear();
+        if (_.isUndefined(this.get('name')) ||
+          _.isNull(this.get('name')) ||
+          String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Please select a column.');
+          this.errorModel.set('name', msg);
+          return msg;
+        } else if (_.isUndefined(this.get('order')) ||
+          _.isNull(this.get('order')) ||
+          String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Please select the order.');
+          this.errorModel.set('order', msg);
+          return msg;
+        }
+        return null;
+      },
+    });
+
+    let FilterCollectionModel = pgAdmin.Browser.DataModel.extend({
+      idAttribute: 'sql',
+      defaults: {
+        sql: response.sql || null,
+      },
+      schema: [{
+        id: 'sql',
+        label: gettext('SQL Filter'),
+        cell: 'string',
+        type: 'text', mode: ['create'],
+        control: Backform.SqlFieldControl.extend({
+          render: function() {
+            let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
+            // We need to set focus on editor after the dialog renders
+            setTimeout(() => {
+              obj.sqlCtrl.focus();
+            }, 1000);
+            return obj;
+          },
+        }),
+        extraClasses:['custom_height_css_class'],
+      },{
+        id: 'data_sorting',
+        name: 'data_sorting',
+        label: gettext('Data Sorting'),
+        model: DataSortingModel,
+        editable: true,
+        type: 'collection',
+        mode: ['create'],
+        control: 'unique-col-collection',
+        uniqueCol: ['name'],
+        canAdd: true,
+        canEdit: false,
+        canDelete: true,
+        visible: true,
+        version_compatible: true,
+      }],
+      validate: function() {
+        return null;
+      },
+    });
+
+    let model = new FilterCollectionModel();
+    return model;
+  };
+
+  return initModel;
+});
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 2f3bb05..29e6b81 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -195,9 +195,9 @@
                 <ul class="dropdown-menu dropdown-menu-right">
                     <li>
                         <a id="btn-filter-menu" href="#" tabindex="0">{{ _('Filter') }}</a>
-                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
                         <a id="btn-include-filter" href="#" tabindex="0">{{ _('By Selection') }}</a>
                         <a id="btn-exclude-filter" href="#" tabindex="0">{{ _('Exclude Selection') }}</a>
+                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
                     </li>
                 </ul>
             </div>
@@ -341,23 +341,6 @@
             <div class="editor-title"
                  style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% else %}{{ bgcolor or '#2C76B4' }}{% endif %}; color: {{ fgcolor or 'white' }};"></div>
         </div>
-
-        <div id="filter" class="filter-container hidden">
-            <div class="filter-title">Filter</div>
-            <div class="sql-textarea">
-                <textarea id="sql_filter" rows="5"></textarea>
-            </div>
-            <div class="btn-group">
-                <button id="btn-cancel" type="button" class="btn btn-danger" title="{{ _('Cancel') }}" tabindex="0">
-                    <i class="fa fa-times" aria-hidden="true"></i> {{ _('Cancel') }}
-                </button>
-            </div>
-            <div class="btn-group">
-                <button id="btn-apply" type="button" class="btn btn-primary" title="{{ _('Apply') }}" tabindex="0">
-                    <i class="fa fa-check" aria-hidden="true"></i> {{ _('Apply') }}
-                </button>
-            </div>
-        </div>
         <div id="editor-panel" tabindex="0"></div>
         <iframe id="download-csv" style="display:none"></iframe>
     </div>
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index 6f5d5b7..84f000e 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -40,6 +40,7 @@ from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
     RegisterQueryToolPreferences
 from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
     read_file_generator
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
 
 MODULE_NAME = 'sqleditor'
 
@@ -92,8 +93,6 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.fetch',
             'sqleditor.fetch_all',
             'sqleditor.save',
-            'sqleditor.get_filter',
-            'sqleditor.apply_filter',
             'sqleditor.inclusive_filter',
             'sqleditor.exclusive_filter',
             'sqleditor.remove_filter',
@@ -106,7 +105,9 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.load_file',
             'sqleditor.save_file',
             'sqleditor.query_tool_download',
-            'sqleditor.connection_status'
+            'sqleditor.connection_status',
+            'sqleditor.get_filter_data',
+            'sqleditor.set_filter_data'
         ]
 
     def register_preferences(self):
@@ -782,81 +783,6 @@ def save(trans_id):
         }
     )
 
-
[email protected](
-    '/filter/get/<int:trans_id>',
-    methods=["GET"], endpoint='get_filter'
-)
-@login_required
-def get_filter(trans_id):
-    """
-    This method is used to get the existing filter.
-
-    Args:
-        trans_id: unique transaction id
-    """
-
-    # Check the transaction and connection status
-    status, error_msg, conn, trans_obj, session_obj = \
-        check_transaction_status(trans_id)
-
-    if error_msg == gettext('Transaction ID not found in the session.'):
-        return make_json_response(success=0, errormsg=error_msg,
-                                  info='DATAGRID_TRANSACTION_REQUIRED',
-                                  status=404)
-    if status and conn is not None and \
-       trans_obj is not None and session_obj is not None:
-
-        res = trans_obj.get_filter()
-    else:
-        status = False
-        res = error_msg
-
-    return make_json_response(data={'status': status, 'result': res})
-
-
[email protected](
-    '/filter/apply/<int:trans_id>',
-    methods=["PUT", "POST"], endpoint='apply_filter'
-)
-@login_required
-def apply_filter(trans_id):
-    """
-    This method is used to apply the filter.
-
-    Args:
-        trans_id: unique transaction id
-    """
-    if request.data:
-        filter_sql = json.loads(request.data, encoding='utf-8')
-    else:
-        filter_sql = request.args or request.form
-
-    # Check the transaction and connection status
-    status, error_msg, conn, trans_obj, session_obj = \
-        check_transaction_status(trans_id)
-
-    if error_msg == gettext('Transaction ID not found in the session.'):
-        return make_json_response(success=0, errormsg=error_msg,
-                                  info='DATAGRID_TRANSACTION_REQUIRED',
-                                  status=404)
-
-    if status and conn is not None and \
-       trans_obj is not None and session_obj is not None:
-
-        status, res = trans_obj.set_filter(filter_sql)
-
-        # As we changed the transaction object we need to
-        # restore it and update the session variable.
-        session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
-        update_session_grid_transaction(trans_id, session_obj)
-    else:
-        status = False
-        res = error_msg
-
-    return make_json_response(data={'status': status, 'result': res})
-
-
 @blueprint.route(
     '/filter/inclusive/<int:trans_id>',
     methods=["PUT", "POST"], endpoint='inclusive_filter'
@@ -1561,3 +1487,37 @@ def query_tool_status(trans_id):
         return internal_server_error(
             errormsg=gettext("Transaction status check failed.")
         )
+
+
[email protected](
+    '/filter_dialog/<int:trans_id>',
+    methods=["GET"], endpoint='get_filter_data'
+)
+@login_required
+def get_filter_data(trans_id):
+    """
+    This method is used to get all the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return FilterDialog.get(*check_transaction_status(trans_id))
+
+
[email protected](
+    '/filter_dialog/<int:trans_id>',
+    methods=["PUT"], endpoint='set_filter_data'
+)
+@login_required
+def set_filter_data(trans_id):
+    """
+    This method is used to update the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return FilterDialog.save(
+        *check_transaction_status(trans_id),
+        request=request,
+        trans_id=trans_id
+    )
diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py
index 8cc96e0..993b0d9 100644
--- a/web/pgadmin/tools/sqleditor/command.py
+++ b/web/pgadmin/tools/sqleditor/command.py
@@ -141,6 +141,10 @@ class SQLFilter(object):
       - This method removes the filter applied.
     * validate_filter(row_filter)
       - This method validates the given filter.
+    * get_data_sorting()
+      - This method returns columns for data sorting
+    * set_data_sorting()
+      - This method saves columns for data sorting
     """
 
     def __init__(self, **kwargs):
@@ -160,8 +164,8 @@ class SQLFilter(object):
         self.sid = kwargs['sid']
         self.did = kwargs['did']
         self.obj_id = kwargs['obj_id']
-        self.__row_filter = kwargs['sql_filter'] if 'sql_filter' in kwargs \
-            else None
+        self.__row_filter = kwargs.get('sql_filter', None)
+        self.__dara_sorting = kwargs.get('data_sorting', None)
 
         manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(self.sid)
         conn = manager.connection(did=self.did)
@@ -210,20 +214,41 @@ class SQLFilter(object):
 
         return status, msg
 
+    def get_data_sorting(self):
+        """
+        This function returns the filter.
+        """
+        if self.__dara_sorting and len(self.__dara_sorting) > 0:
+            return self.__dara_sorting
+        return None
+
+    def set_data_sorting(self, data_filter):
+        """
+        This function validates the filter and set the
+        given filter to member variable.
+        """
+        self.__dara_sorting = data_filter['data_sorting']
+
     def is_filter_applied(self):
         """
         This function returns True if filter is applied else False.
         """
+        is_filter_applied = True
         if self.__row_filter is None or self.__row_filter == '':
-            return False
+            is_filter_applied = False
 
-        return True
+        if not is_filter_applied:
+            if self.__dara_sorting and len(self.__dara_sorting) > 0:
+                is_filter_applied = True
+
+        return is_filter_applied
 
     def remove_filter(self):
         """
         This function remove the filter by setting value to None.
         """
         self.__row_filter = None
+        self.__dara_sorting = None
 
     def append_filter(self, row_filter):
         """
@@ -325,13 +350,58 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         self.cmd_type = kwargs['cmd_type'] if 'cmd_type' in kwargs else None
         self.limit = -1
 
-        if self.cmd_type == VIEW_FIRST_100_ROWS or \
-                self.cmd_type == VIEW_LAST_100_ROWS:
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_LAST_100_ROWS):
             self.limit = 100
 
     def get_primary_keys(self, *args, **kwargs):
         return None, None
 
+    def get_all_columns_with_order(self, default_conn):
+        """
+        Responsible for fetching columns from given object
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Columns which are already sorted which will
+                         be used to populate the Grid in the dialog
+            all_columns: List of all the column for given object which will
+                         be used to fill columns options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def save(self, changed_data, default_conn=None):
         return forbidden(
             errmsg=gettext("Data cannot be saved for the current object.")
@@ -351,6 +421,17 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         """
         self.limit = limit
 
+    def get_pk_order(self):
+        """
+        This function gets the order required for primary keys
+        """
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_ALL_ROWS):
+            return 'asc'
+        elif self.cmd_type == VIEW_LAST_100_ROWS:
+            return 'desc'
+        else:
+            return None
+
 
 class TableCommand(GridCommand):
     """
@@ -385,6 +466,7 @@ class TableCommand(GridCommand):
         has_oids = self.has_oids(default_conn)
 
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
@@ -392,7 +474,8 @@ class TableCommand(GridCommand):
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, limit=self.limit,
-                primary_keys=primary_keys, has_oids=has_oids
+                primary_keys=primary_keys, has_oids=has_oids,
+                data_sorting=data_sorting
             )
         else:
             sql = render_template(
@@ -401,7 +484,7 @@ class TableCommand(GridCommand):
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, sql_filter=sql_filter,
                 limit=self.limit, primary_keys=primary_keys,
-                has_oids=has_oids
+                has_oids=has_oids, data_sorting=data_sorting
             )
 
         return sql
@@ -447,6 +530,73 @@ class TableCommand(GridCommand):
 
         return pk_names, primary_keys
 
+    def get_all_columns_with_order(self, default_conn=None):
+        """
+        It is overridden method specially for Table because we all have to
+        fetch primary keys and rest of the columns both.
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Sorted columns for the Grid
+            all_columns: List of columns for the select2 options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+
+            # Fetch the primary key column names
+            query = render_template(
+                "/".join([self.sql_path, 'primary_keys.sql']),
+                obj_id=self.obj_id
+            )
+
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+                all_sorted_columns.append(
+                    {
+                        'name': row['attname'],
+                        'order': self.get_pk_order()
+                    }
+                )
+
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                # Only append if not already present in the list
+                if row['attname'] not in all_columns:
+                    all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def can_edit(self):
         return True
 
@@ -771,20 +921,22 @@ class ViewCommand(GridCommand):
         to fetch the data for the specified view
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -832,20 +984,22 @@ class ForeignTableCommand(GridCommand):
         to fetch the data for the specified foreign table
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -883,20 +1037,22 @@ class CatalogCommand(GridCommand):
         to fetch the data for the specified catalog object
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -929,6 +1085,9 @@ class QueryToolCommand(BaseCommand, FetchedRowTracker):
     def get_sql(self, default_conn=None):
         return None
 
+    def get_all_columns_with_order(self, default_conn=None):
+        return None
+
     def can_edit(self):
         return False
 
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 46588dc..c54590d 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -602,3 +602,26 @@ input.editor-checkbox:focus {
   font-size: 13px;
   line-height: 3em;
 }
+
+/* For Filter status bar */
+.data_sorting_dialog .pg-prop-status-bar {
+  position: absolute;
+  bottom: 37px;
+  z-index: 5;
+}
+
+.data_sorting_dialog .CodeMirror-gutter-wrapper {
+  left: -30px !important;
+}
+
+.data_sorting_dialog .CodeMirror-gutters {
+  left: 0px !important;
+}
+
+.data_sorting_dialog .custom_height_css_class {
+  height: 100px;
+}
+
+.data_sorting_dialog .data_sorting {
+  padding: 10px 0px;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 923ccea..f358abc 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
   'sources/sqleditor_utils',
   'sources/sqleditor/execute_query',
   'sources/sqleditor/is_new_transaction_required',
+  'sources/sqleditor/filter_dialog',
   'sources/history/index.js',
   'sources/../jsx/history/query_history',
   'react', 'react-dom',
@@ -30,7 +31,7 @@ define('tools.querytool', [
 ], function(
   babelPollyfill, gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror,
   pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, transaction,
+  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, transaction, FilterHandler,
   HistoryBundle, queryHistory, React, ReactDOM,
   keyboardShortcuts, queryToolActions, Datagrid) {
   /* Return back, this has been called more than once */
@@ -108,8 +109,7 @@ define('tools.querytool', [
 
     // This function is used to render the template.
     render: function() {
-      var self = this,
-        filter = self.$el.find('#sql_filter');
+      var self = this;
 
       $('.editor-title').text(_.unescape(self.editor_title));
       self.checkConnectionStatus();
@@ -117,31 +117,6 @@ define('tools.querytool', [
       // Fetch and assign the shortcuts to current instance
       self.keyboardShortcutConfig = queryToolActions.getKeyboardShortcuts(self);
 
-      self.filter_obj = CodeMirror.fromTextArea(filter.get(0), {
-        tabindex: '0',
-        lineNumbers: true,
-        mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql',
-        foldOptions: {
-          widget: '\u2026',
-        },
-        foldGutter: {
-          rangeFinder: CodeMirror.fold.combine(
-            CodeMirror.pgadminBeginRangeFinder,
-            CodeMirror.pgadminIfRangeFinder,
-            CodeMirror.pgadminLoopRangeFinder,
-            CodeMirror.pgadminCaseRangeFinder
-          ),
-        },
-        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
-        extraKeys: pgBrowser.editor_shortcut_keys,
-        indentWithTabs: pgAdmin.Browser.editor_options.indent_with_tabs,
-        indentUnit: pgAdmin.Browser.editor_options.tabSize,
-        tabSize: pgAdmin.Browser.editor_options.tabSize,
-        lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
-        autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
-        matchBrackets: pgAdmin.Browser.editor_options.brace_matching,
-      });
-
       // Updates connection status flag
       self.gain_focus = function() {
         setTimeout(function() {
@@ -2160,11 +2135,11 @@ define('tools.querytool', [
               if (self.can_filter && res.data.filter_applied) {
                 $('#btn-filter').removeClass('btn-default');
                 $('#btn-filter-dropdown').removeClass('btn-default');
-                $('#btn-filter').addClass('btn-warning');
-                $('#btn-filter-dropdown').addClass('btn-warning');
+                $('#btn-filter').addClass('btn-primary');
+                $('#btn-filter-dropdown').addClass('btn-primary');
               } else {
-                $('#btn-filter').removeClass('btn-warning');
-                $('#btn-filter-dropdown').removeClass('btn-warning');
+                $('#btn-filter').removeClass('btn-primary');
+                $('#btn-filter-dropdown').removeClass('btn-primary');
                 $('#btn-filter').addClass('btn-default');
                 $('#btn-filter-dropdown').addClass('btn-default');
               }
@@ -3170,79 +3145,10 @@ define('tools.querytool', [
         };
       },
 
-      // This function will show the filter in the text area.
+      // This function will used when user wants custom data sorting
       _show_filter: function() {
-        var self = this;
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Loading the existing filter options...')
-        );
-        $.ajax({
-          url: url_for('sqleditor.get_filter', {
-            'trans_id': self.transId,
-          }),
-          method: 'GET',
-          success: function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            if (res.data.status) {
-              $('#filter').removeClass('hidden');
-              $('#editor-panel').addClass('sql-editor-busy-fetching');
-              self.gridView.filter_obj.refresh();
-
-              if (res.data.result == null)
-                self.gridView.filter_obj.setValue('');
-              else
-                self.gridView.filter_obj.setValue(res.data.result);
-              // Set focus on filter area
-              self.gridView.filter_obj.focus();
-            } else {
-              setTimeout(
-                function() {
-                  alertify.alert(gettext('Get Filter Error'), res.data.result);
-                }, 10
-              );
-            }
-          },
-          error: function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-
-            if (pgAdmin.Browser.UserManagement.is_pga_login_required(e)) {
-              self.save_state('_show_filter', []);
-              return pgAdmin.Browser.UserManagement.pga_login();
-            }
-
-            if(transaction.is_new_transaction_required(e)) {
-              self.save_state('_show_filter', []);
-              return self.init_transaction();
-            }
-
-            var msg;
-            if (e.readyState == 0) {
-              msg =
-                gettext('Not connected to the server or the connection to the server has been closed.');
-            } else {
-              msg = e.responseText;
-              if (e.responseJSON != undefined) {
-                if(e.responseJSON.errormsg != undefined) {
-                  msg = e.responseJSON.errormsg;
-                }
-                if(e.status == 503 && e.responseJSON.info != undefined &&
-                    e.responseJSON.info == 'CONNECTION_LOST') {
-                  setTimeout(function() {
-                    self.save_state('_show_filter', []);
-                    self.handle_connection_lost(false, e);
-                  });
-                }
-              }
-            }
-            setTimeout(
-              function() {
-                alertify.alert(gettext('Get Filter Error'), msg);
-              }, 10
-            );
-          },
-        });
+        let self = this;
+        FilterHandler.dialog(self);
       },
 
       // This function will include the filter by selection.
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
new file mode 100644
index 0000000..610747d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
@@ -0,0 +1,9 @@
+{# ============= Fetch the columns ============= #}
+{% if obj_id %}
+SELECT at.attname, ty.typname
+    FROM pg_attribute at
+    LEFT JOIN pg_type ty ON (ty.oid = at.atttypid)
+WHERE attrelid={{obj_id}}::oid
+    AND at.attnum > 0
+    AND at.attisdropped = FALSE
+{% endif %}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
index 1cb60d9..add1658 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
@@ -3,7 +3,11 @@ SELECT {% if has_oids %}oid, {% endif %}* FROM {{ conn|qtIdent(nsp_name, object_
 {% if sql_filter %}
 WHERE {{ sql_filter }}
 {% endif %}
-{% if primary_keys %}
+{% if data_sorting and data_sorting|length > 0 %}
+ORDER BY {% for obj in data_sorting %}
+{{ conn|qtIdent(obj.name) }} {{ obj.order|upper }}{% if not loop.last %}, {% else %} {% endif %}
+{% endfor %}
+{% elif primary_keys %}
 ORDER BY {% for p in primary_keys %}{{conn|qtIdent(p)}}{% if cmd_type == 1 or cmd_type == 3 %} ASC{% elif cmd_type == 2 %} DESC{% endif %}
 {% if not loop.last %}, {% else %} {% endif %}{% endfor %}
 {% endif %}
diff --git a/web/pgadmin/tools/sqleditor/utils/filter_dialog.py b/web/pgadmin/tools/sqleditor/utils/filter_dialog.py
new file mode 100644
index 0000000..838a35b
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/filter_dialog.py
@@ -0,0 +1,96 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Code to handle data sorting in view data mode."""
+import pickle
+import simplejson as json
+from flask_babel import gettext
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
+    update_session_grid_transaction
+
+
+class FilterDialog(object):
+    @staticmethod
+    def get(*args):
+        """To fetch the current sorted columns"""
+        status, error_msg, conn, trans_obj, session_obj = args
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+        column_list = []
+        if status and conn is not None and \
+                trans_obj is not None and session_obj is not None:
+            msg = gettext('Success')
+            columns, column_list = trans_obj.get_all_columns_with_order(conn)
+            sql = trans_obj.get_filter()
+        else:
+            status = False
+            msg = error_msg
+            columns = None
+            sql = None
+
+
+        return make_json_response(
+            data={
+                'status': status,
+                'msg': msg,
+                'result': {
+                    'data_sorting': columns,
+                    'column_list': column_list,
+                    'sql': sql
+                }
+            }
+        )
+
+    @staticmethod
+    def save(*args, **kwargs):
+        """To save the sorted columns"""
+        # Check the transaction and connection status
+        status, error_msg, conn, trans_obj, session_obj = args
+        trans_id = kwargs['trans_id']
+        request = kwargs['request']
+
+        if request.data:
+            data = json.loads(request.data, encoding='utf-8')
+        else:
+            data = request.args or request.form
+
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+
+        if status and conn is not None and \
+           trans_obj is not None and session_obj is not None:
+            trans_obj.set_data_sorting(data)
+            trans_obj.set_filter(data.get('sql'))
+            # As we changed the transaction object we need to
+            # restore it and update the session variable.
+            session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
+            update_session_grid_transaction(trans_id, session_obj)
+            res = gettext('Data sorting object updated successfully')
+        else:
+            return internal_server_error(
+                errormsg=gettext('Failed to update the data on server.')
+            )
+
+        return make_json_response(
+            data={
+                'status': status,
+                'result': res
+            }
+        )
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py b/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py
new file mode 100644
index 0000000..9747978
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py
@@ -0,0 +1,103 @@
+#######################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Apply Explain plan wrapper to sql object."""
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
+from pgadmin.utils.route import BaseTestGenerator
+
+TX_ID_ERROR_MSG = 'Transaction ID not found in the session.'
+FAILED_TX_MSG = 'Failed to update the data on server.'
+
+
+class MockRequest(object):
+    "To mock request object"
+    def __init__(self):
+        self.data = None
+        self.args = "Test data",
+
+
+class StartRunningDataSortingTest(BaseTestGenerator):
+    """
+    Check that the DataSorting methods works as
+    intended
+    """
+    scenarios = [
+        ('When we do not find Transaction ID in session in get', dict(
+            input_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='get'
+        )),
+        ('When we pass all the values as None in get', dict(
+            input_parameters=(None, None, None, None, None),
+            expected_return_response={
+                'data': {
+                    'status': False,
+                    'msg': None,
+                    'result': {
+                        'data_sorting': None,
+                        'column_list': []
+                    }
+                }
+            },
+            type='get'
+        )),
+
+        ('When we do not find Transaction ID in session in save', dict(
+            input_arg_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='save'
+        )),
+
+        ('When we pass all the values as None in save', dict(
+            input_arg_parameters=(None, None, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'status': 500,
+                'success': 0,
+                'errormsg': FAILED_TX_MSG
+
+            },
+            type='save'
+        ))
+    ]
+
+    def runTest(self):
+        expected_response = make_json_response(
+            **self.expected_return_response
+        )
+        if self.type == 'get':
+            result = FilterDialog.get(*self.input_parameters)
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
+        else:
+            result = FilterDialog.save(
+                *self.input_arg_parameters, **self.input_kwarg_parameters
+            )
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
diff --git a/web/regression/javascript/sqleditor/filter_dialog_specs.js b/web/regression/javascript/sqleditor/filter_dialog_specs.js
new file mode 100644
index 0000000..e13fa09
--- /dev/null
+++ b/web/regression/javascript/sqleditor/filter_dialog_specs.js
@@ -0,0 +1,31 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+import filterDialog from 'sources/sqleditor/filter_dialog';
+import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
+
+describe('filterDialog', () => {
+  let sqlEditorController;
+  sqlEditorController = jasmine.createSpy('sqlEditorController')
+  describe('filterDialog', () => {
+    describe('when using filter dialog', () => {
+      beforeEach(() => {
+        spyOn(filterDialog, 'dialog');
+      });
+
+      it("it should be defined as function", function() {
+        expect(filterDialog.dialog).toBeDefined();
+      });
+
+      it('it should call without proper handler', () => {
+        expect(filterDialog.dialog).not.toHaveBeenCalledWith({});
+      });
+
+    });
+  });
+});


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-04-05 10:45                     ` Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-04-05 10:45 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Robert Eckhardt <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

Can you rebase this please?

On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
[email protected]> wrote:

> Hi Dave,
>
> Please find updated patch with following changes,
> - Combined Filter and Data sorting together same as pgAdmin3.
> - Extracted model into separate file
> - Change the colour of filter button from orange to blue.
> - Updated docs and screenshot.
>
> @Joao,
> Could you please provide any reference for learning more about jasmine
> test framework?
> ​
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
> wrote:
>
>>
>>
>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Hackers,
>>>>>>>>
>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>> for the change.
>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>> tested.
>>>>>>>>
>>>>>>>>
>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>> 1 - Started pgAdmin
>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>> 3 - Executed a SQL statment
>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>> option, nothing
>>>>>>>>
>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>
>>>>>>>
>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>> users a more intuitive place to look.
>>>>>>>
>>>>>>
>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>> The current design allows you to select columns and the sort order as you
>>>>>> see fit.
>>>>>>
>>>>>
>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>> in an effort to start a discussion about the user experience. Ideally what
>>>> I'd like to see, maybe this happened, is some user research. When we
>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>> One of the things we intended to do was to follow up to see how people were
>>>> using the grid now so that we could better understand how it was now being
>>>> used in order to design and implement features just like this. Clearly we
>>>> haven't gotten there yet.
>>>>
>>>>
>>>>>
>>>>> Another reason we can't use that because w
>>>>> e have already occupied that behaviour for selecting entire column
>>>>> ​ when user clicks on header.
>>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>> ​
>>>>>
>>>>
>>>> How are users currently interacting with that filter dialog?
>>>>
>>>
>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>> ​
>>>
>>>
>>>
>>>
>>
>> Sorry I wasn't clear. My question was more along the lines of, do we
>> know if people are using the filter functionality?  What kind of filters
>> are people using?  What do they like about it? What do they wish they could
>> do above and beyond sorting, etc.
>>
> ​I have not done any data gathering from users so I can't comment on your
> queries.
> ​ but a​
> ​s far as I understood from the feature requests that most of the users
> expect to have functionality which will allow then to sort columns as it
> was in pgAdmin3.​
>
>
>
>> -- Rob
>>
>>
>>>
>>>> What I'm suggesting is that we understand how users want to interact
>>>> with their results, be those the results of a query or a table view, then
>>>> we can design something that meets those needs. I agree that changing the
>>>> column selection behavior isn't desirable, however, I also feel like
>>>> providing the best user experience is better than holding onto a particular
>>>> feature implementation.
>>>> ​
>>>>
>>>>
>>>
>>>> -- Rob
>>>>
>>>>
>>>>>
>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>
>>>>>
>>>>
>>>
>>
>


-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-04-05 11:29                       ` Murtuza Zabuawala <[email protected]>
  2018-04-05 13:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 2 replies; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-04-05 11:29 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Robert Eckhardt <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

Hi Dave,

Please find rebased patch.

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:

> Can you rebase this please?
>
> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> Hi Dave,
>>
>> Please find updated patch with following changes,
>> - Combined Filter and Data sorting together same as pgAdmin3.
>> - Extracted model into separate file
>> - Change the colour of filter button from orange to blue.
>> - Updated docs and screenshot.
>>
>> @Joao,
>> Could you please provide any reference for learning more about jasmine
>> test framework?
>> ​
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> EnterpriseDB: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Hackers,
>>>>>>>>>
>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>> for the change.
>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>>> tested.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>> option, nothing
>>>>>>>>>
>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>
>>>>>>>>
>>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>>> users a more intuitive place to look.
>>>>>>>>
>>>>>>>
>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>> see fit.
>>>>>>>
>>>>>>
>>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>>> in an effort to start a discussion about the user experience. Ideally what
>>>>> I'd like to see, maybe this happened, is some user research. When we
>>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>>> One of the things we intended to do was to follow up to see how people were
>>>>> using the grid now so that we could better understand how it was now being
>>>>> used in order to design and implement features just like this. Clearly we
>>>>> haven't gotten there yet.
>>>>>
>>>>>
>>>>>>
>>>>>> Another reason we can't use that because w
>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>> ​ when user clicks on header.
>>>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>> ​
>>>>>>
>>>>>
>>>>> How are users currently interacting with that filter dialog?
>>>>>
>>>>
>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>> ​
>>>>
>>>>
>>>>
>>>>
>>>
>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>> know if people are using the filter functionality?  What kind of
>>> filters are people using?  What do they like about it? What do they wish
>>> they could do above and beyond sorting, etc.
>>>
>> ​I have not done any data gathering from users so I can't comment on your
>> queries.
>> ​ but a​
>> ​s far as I understood from the feature requests that most of the users
>> expect to have functionality which will allow then to sort columns as it
>> was in pgAdmin3.​
>>
>>
>>
>>> -- Rob
>>>
>>>
>>>>
>>>>> What I'm suggesting is that we understand how users want to interact
>>>>> with their results, be those the results of a query or a table view, then
>>>>> we can design something that meets those needs. I agree that changing the
>>>>> column selection behavior isn't desirable, however, I also feel like
>>>>> providing the best user experience is better than holding onto a particular
>>>>> feature implementation.
>>>>> ​
>>>>>
>>>>>
>>>>
>>>>> -- Rob
>>>>>
>>>>>
>>>>>>
>>>>>>
>>>>>>> --
>>>>>>> Dave Page
>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>> Twitter: @pgsnake
>>>>>>>
>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

  [application/octet-stream] RM_3055_v2.diff (50.0K, 4-RM_3055_v2.diff)
  download | inline diff:
diff --git a/docs/en_US/editgrid.rst b/docs/en_US/editgrid.rst
index 1cb23cf..b1a9551 100644
--- a/docs/en_US/editgrid.rst
+++ b/docs/en_US/editgrid.rst
@@ -95,6 +95,24 @@ To delete a row, press the *Delete* toolbar button.  A popup will open, asking y
 
 To commit the changes to the server, select the *Save* toolbar button.  Modifications to a row are written to the server automatically when you select a different row.
 
+**Sort/Filter options dialog**
 
+You can access *Sort/Filter options dialog* by clicking on Filter button, This dialog provides information about the sql filter and data sorting in the edit grid window:
 
+.. image:: images/editgrid_filter_dialog.png
+    :alt: Edit grid filter dialog window
+
+* Use *SQL filter* to provide where clause conditions
+* Use *Data sorting* to sort the data in the output grid
+
+To add new column(s) in data sorting grid, click on the [+] icon.
+
+* Use the drop-down *Column* to select the column you want to sort.
+* Use the drop-down *Order* to select the sort order for the column.
+
+To discard a data sorting, and delete the row from the grid, click the trash icon.
+
+* Click the *Help* button (?) to access online help.
+* Click the *Ok* button to save work.
+* Click the *Close* button to discard current changes and close the dialog.
 
diff --git a/docs/en_US/images/editgrid_filter_dialog.png b/docs/en_US/images/editgrid_filter_dialog.png
new file mode 100644
index 0000000..046d9a1
Binary files /dev/null and b/docs/en_US/images/editgrid_filter_dialog.png differ
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog.js b/web/pgadmin/static/js/sqleditor/filter_dialog.js
new file mode 100644
index 0000000..0ba9e35
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog.js
@@ -0,0 +1,243 @@
+define([
+  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string',
+  'pgadmin.alertifyjs', 'sources/pgadmin', 'backbone',
+  'pgadmin.backgrid', 'pgadmin.backform', 'axios',
+  'sources/sqleditor/query_tool_actions',
+  'sources/sqleditor/filter_dialog_model',
+  //'pgadmin.browser.node.ui',
+], function(
+  gettext, url_for, $, _, S, Alertify, pgAdmin, Backbone,
+  Backgrid, Backform, axios, queryToolActions, filterDialogModel
+) {
+
+  let FilterDialog = {
+    'dialog': function(handler) {
+      let title = gettext('Sort/Filter options');
+      axios.get(
+        url_for('sqleditor.get_filter_data', {
+          'trans_id': handler.transId,
+        }),
+        { headers: {'Cache-Control' : 'no-cache'} }
+      ).then(function (res) {
+        let response = res.data.data.result;
+
+        // Check the alertify dialog already loaded then delete it to clear
+        // the cache
+        if (Alertify.filterDialog) {
+          delete Alertify.filterDialog;
+        }
+
+        // Create Dialog
+        Alertify.dialog('filterDialog', function factory() {
+          let $container = $('<div class=\'data_sorting_dialog\'></div>');
+          return {
+            main: function() {
+              this.set('title', gettext('Sort/Filter options'));
+            },
+            build: function() {
+              this.elements.content.appendChild($container.get(0));
+              Alertify.pgDialogBuild.apply(this);
+            },
+            setup: function() {
+              return {
+                buttons: [{
+                  text: '',
+                  key: 112,
+                  className: 'btn btn-default pull-left fa fa-lg fa-question',
+                  attrs: {
+                    name: 'dialog_help',
+                    type: 'button',
+                    label: gettext('Help'),
+                    url: url_for('help.static', {
+                      'filename': 'editgrid.html',
+                    }),
+                  },
+                }, {
+                  text: gettext('Ok'),
+                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                  'data-btn-name': 'ok',
+                }, {
+                  text: gettext('Cancel'),
+                  key: 27,
+                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                  'data-btn-name': 'cancel',
+                }],
+                // Set options for dialog
+                options: {
+                  title: title,
+                  //disable both padding and overflow control.
+                  padding: !1,
+                  overflow: !1,
+                  model: 0,
+                  resizable: true,
+                  maximizable: true,
+                  pinnable: false,
+                  closableByDimmer: false,
+                  modal: false,
+                  autoReset: false,
+                },
+              };
+            },
+            hooks: {
+              // triggered when the dialog is closed
+              onclose: function() {
+                if (this.view) {
+                  this.filterCollectionModel.stopSession();
+                  this.view.model.stopSession();
+                  this.view.remove({
+                    data: true,
+                    internal: true,
+                    silent: true,
+                  });
+                }
+              },
+            },
+            prepare: function() {
+              let self = this;
+              $container.html('');
+              // Disable Ok button
+              this.__internal.buttons[1].element.disabled = true;
+
+              // Status bar
+              this.statusBar = $('<div class=\'pg-prop-status-bar pg-el-xs-12 hide\'>' +
+                '  <div class=\'media error-in-footer bg-red-1 border-red-2 font-red-3 text-14\'>' +
+                '    <div class=\'media-body media-middle\'>' +
+                '      <div class=\'alert-icon error-icon\'>' +
+                '        <i class=\'fa fa-exclamation-triangle\' aria-hidden=\'true\'></i>' +
+                '      </div>' +
+                '      <div class=\'alert-text\'>' +
+                '      </div>' +
+                '    </div>' +
+                '  </div>' +
+                '</div>', {
+                  text: '',
+                }).appendTo($container);
+
+              // To show progress on filter Saving/Updating on AJAX
+              this.showFilterProgress = $(
+                '<div id="show_filter_progress" class="wcLoadingIconContainer busy-fetching hidden">' +
+                '<div class="wcLoadingBackground"></div>' +
+                '<span class="wcLoadingIcon fa fa-spinner fa-pulse"></span>' +
+                '<span class="busy-text wcLoadingLabel">' + gettext('Loading data...') + '</span>' +
+                '</div>').appendTo($container);
+
+              $(
+                self.showFilterProgress[0]
+              ).removeClass('hidden');
+
+              self.filterCollectionModel = filterDialogModel(response);
+
+              let fields = Backform.generateViewSchema(
+                  null, self.filterCollectionModel, 'create', null, null, true
+              );
+
+              let view = this.view = new Backform.Dialog({
+                el: '<div></div>',
+                model: self.filterCollectionModel,
+                schema: fields,
+              });
+
+              $(this.elements.body.childNodes[0]).addClass(
+                'alertify_tools_dialog_properties obj_properties'
+              );
+
+              $container.append(view.render().$el);
+
+              // Enable/disable save button and show/hide statusbar based on session
+              view.listenTo(view.model, 'pgadmin-session:start', function() {
+                view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+                  self.statusBar.removeClass('hide');
+                  $(self.statusBar.find('.alert-text')).html(msg);
+                  // Disable Okay button
+                  self.__internal.buttons[1].element.disabled = true;
+                });
+
+                view.listenTo(view.model, 'pgadmin-session:valid', function() {
+                  self.statusBar.addClass('hide');
+                  $(self.statusBar.find('.alert-text')).html('');
+                  // Enable Okay button
+                  self.__internal.buttons[1].element.disabled = false;
+                });
+              });
+
+              view.listenTo(view.model, 'pgadmin-session:stop', function() {
+                view.stopListening(view.model, 'pgadmin-session:invalid');
+                view.stopListening(view.model, 'pgadmin-session:valid');
+              });
+
+              // Starts monitoring changes to model
+              view.model.startNewSession();
+
+              // Set data in collection
+              let viewDataSortingModel = view.model.get('data_sorting');
+              viewDataSortingModel.add(response['data_sorting']);
+
+              // Hide Progress ...
+              $(
+                self.showFilterProgress[0]
+              ).addClass('hidden');
+
+            },
+            // Callback functions when click on the buttons of the Alertify dialogs
+            callback: function(e) {
+              let self = this;
+
+              if (e.button.element.name == 'dialog_help') {
+                e.cancel = true;
+                pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                  null, null, e.button.element.getAttribute('label'));
+                return;
+              } else if (e.button['data-btn-name'] === 'ok') {
+                e.cancel = true; // Do not close dialog
+
+                let filterCollectionModel = this.filterCollectionModel.toJSON();
+
+                // Show Progress ...
+                $(
+                  self.showFilterProgress[0]
+                ).removeClass('hidden');
+
+                axios.put(
+                  url_for('sqleditor.set_filter_data', {
+                    'trans_id': handler.transId,
+                  }),
+                  filterCollectionModel
+                ).then(function () {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+                  setTimeout(
+                    function() {
+                      self.close(); // Close the dialog now
+                      Alertify.success(gettext('Filter updated successfully'));
+                      queryToolActions.executeQuery(handler);
+                    }, 10
+                  );
+
+                }).catch(function (error) {
+                  // Hide Progress ...
+                  $(
+                    self.showFilterProgress[0]
+                  ).addClass('hidden');
+                  handler.onExecuteHTTPError(error);
+
+                  setTimeout(
+                    function() {
+                      Alertify.error(error);
+                    }, 10
+                  );
+                });
+              } else {
+                self.close();
+              }
+            },
+          };
+        });
+
+        Alertify.filterDialog(title).resizeTo('65%', '60%');
+      });
+    },
+  };
+  return FilterDialog;
+});
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
new file mode 100644
index 0000000..c3146a4
--- /dev/null
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
@@ -0,0 +1,133 @@
+define([
+  'sources/gettext', 'underscore', 'sources/pgadmin',
+  'pgadmin.backform', 'pgadmin.backgrid',
+], function(
+  gettext, _, pgAdmin, Backform, Backgrid
+) {
+
+  let initModel = function(response) {
+
+    let order_mapping = {
+      'asc': gettext('ASC'),
+      'desc': gettext('DESC'),
+    };
+
+    let DataSortingModel = pgAdmin.Browser.DataModel.extend({
+      idAttribute: 'name',
+      defaults: {
+        name: undefined,
+        order: 'asc',
+      },
+      schema: [{
+        id: 'name',
+        name: 'name',
+        label: gettext('Column'),
+        cell: 'select2',
+        editable: true,
+        cellHeaderClasses: 'width_percent_60',
+        headerCell: Backgrid.Extension.CustomHeaderCell,
+        disabled: false,
+        control: 'select2',
+        select2: {
+          allowClear: false,
+        },
+        options: function() {
+          return _.map(response.column_list, (obj) => {
+            return {
+              value: obj,
+              label: obj,
+            };
+          });
+        },
+      },
+      {
+        id: 'order',
+        name: 'order',
+        label: gettext('Order'),
+        control: 'select2',
+        cell: 'select2',
+        cellHeaderClasses: 'width_percent_40',
+        headerCell: Backgrid.Extension.CustomHeaderCell,
+        editable: true,
+        deps: ['type'],
+        select2: {
+          allowClear: false,
+        },
+        options: function() {
+          return _.map(order_mapping, (val, key) => {
+            return {
+              value: key,
+              label: val,
+            };
+          });
+        },
+      },
+      ],
+      validate: function() {
+        let msg = null;
+        this.errorModel.clear();
+        if (_.isUndefined(this.get('name')) ||
+          _.isNull(this.get('name')) ||
+          String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Please select a column.');
+          this.errorModel.set('name', msg);
+          return msg;
+        } else if (_.isUndefined(this.get('order')) ||
+          _.isNull(this.get('order')) ||
+          String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
+          msg = gettext('Please select the order.');
+          this.errorModel.set('order', msg);
+          return msg;
+        }
+        return null;
+      },
+    });
+
+    let FilterCollectionModel = pgAdmin.Browser.DataModel.extend({
+      idAttribute: 'sql',
+      defaults: {
+        sql: response.sql || null,
+      },
+      schema: [{
+        id: 'sql',
+        label: gettext('SQL Filter'),
+        cell: 'string',
+        type: 'text', mode: ['create'],
+        control: Backform.SqlFieldControl.extend({
+          render: function() {
+            let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
+            // We need to set focus on editor after the dialog renders
+            setTimeout(() => {
+              obj.sqlCtrl.focus();
+            }, 1000);
+            return obj;
+          },
+        }),
+        extraClasses:['custom_height_css_class'],
+      },{
+        id: 'data_sorting',
+        name: 'data_sorting',
+        label: gettext('Data Sorting'),
+        model: DataSortingModel,
+        editable: true,
+        type: 'collection',
+        mode: ['create'],
+        control: 'unique-col-collection',
+        uniqueCol: ['name'],
+        canAdd: true,
+        canEdit: false,
+        canDelete: true,
+        visible: true,
+        version_compatible: true,
+      }],
+      validate: function() {
+        return null;
+      },
+    });
+
+    let model = new FilterCollectionModel();
+    return model;
+  };
+
+  return initModel;
+});
diff --git a/web/pgadmin/tools/datagrid/templates/datagrid/index.html b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
index 2f3bb05..29e6b81 100644
--- a/web/pgadmin/tools/datagrid/templates/datagrid/index.html
+++ b/web/pgadmin/tools/datagrid/templates/datagrid/index.html
@@ -195,9 +195,9 @@
                 <ul class="dropdown-menu dropdown-menu-right">
                     <li>
                         <a id="btn-filter-menu" href="#" tabindex="0">{{ _('Filter') }}</a>
-                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
                         <a id="btn-include-filter" href="#" tabindex="0">{{ _('By Selection') }}</a>
                         <a id="btn-exclude-filter" href="#" tabindex="0">{{ _('Exclude Selection') }}</a>
+                        <a id="btn-remove-filter" href="#" tabindex="0">{{ _('Remove Filter') }}</a>
                     </li>
                 </ul>
             </div>
@@ -341,23 +341,6 @@
             <div class="editor-title"
                  style="background-color: {% if fgcolor %}{{ bgcolor or '#FFFFFF' }}{% else %}{{ bgcolor or '#2C76B4' }}{% endif %}; color: {{ fgcolor or 'white' }};"></div>
         </div>
-
-        <div id="filter" class="filter-container hidden">
-            <div class="filter-title">Filter</div>
-            <div class="sql-textarea">
-                <textarea id="sql_filter" rows="5"></textarea>
-            </div>
-            <div class="btn-group">
-                <button id="btn-cancel" type="button" class="btn btn-danger" title="{{ _('Cancel') }}" tabindex="0">
-                    <i class="fa fa-times" aria-hidden="true"></i> {{ _('Cancel') }}
-                </button>
-            </div>
-            <div class="btn-group">
-                <button id="btn-apply" type="button" class="btn btn-primary" title="{{ _('Apply') }}" tabindex="0">
-                    <i class="fa fa-check" aria-hidden="true"></i> {{ _('Apply') }}
-                </button>
-            </div>
-        </div>
         <div id="editor-panel" tabindex="0"></div>
         <iframe id="download-csv" style="display:none"></iframe>
     </div>
diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py
index f8d7d97..622b7c3 100644
--- a/web/pgadmin/tools/sqleditor/__init__.py
+++ b/web/pgadmin/tools/sqleditor/__init__.py
@@ -40,6 +40,7 @@ from pgadmin.tools.sqleditor.utils.query_tool_preferences import \
     RegisterQueryToolPreferences
 from pgadmin.tools.sqleditor.utils.query_tool_fs_utils import \
     read_file_generator
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
 
 MODULE_NAME = 'sqleditor'
 
@@ -92,8 +93,6 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.fetch',
             'sqleditor.fetch_all',
             'sqleditor.save',
-            'sqleditor.get_filter',
-            'sqleditor.apply_filter',
             'sqleditor.inclusive_filter',
             'sqleditor.exclusive_filter',
             'sqleditor.remove_filter',
@@ -106,7 +105,9 @@ class SqlEditorModule(PgAdminModule):
             'sqleditor.load_file',
             'sqleditor.save_file',
             'sqleditor.query_tool_download',
-            'sqleditor.connection_status'
+            'sqleditor.connection_status',
+            'sqleditor.get_filter_data',
+            'sqleditor.set_filter_data'
         ]
 
     def register_preferences(self):
@@ -782,81 +783,6 @@ def save(trans_id):
         }
     )
 
-
[email protected](
-    '/filter/get/<int:trans_id>',
-    methods=["GET"], endpoint='get_filter'
-)
-@login_required
-def get_filter(trans_id):
-    """
-    This method is used to get the existing filter.
-
-    Args:
-        trans_id: unique transaction id
-    """
-
-    # Check the transaction and connection status
-    status, error_msg, conn, trans_obj, session_obj = \
-        check_transaction_status(trans_id)
-
-    if error_msg == gettext('Transaction ID not found in the session.'):
-        return make_json_response(success=0, errormsg=error_msg,
-                                  info='DATAGRID_TRANSACTION_REQUIRED',
-                                  status=404)
-    if status and conn is not None and \
-       trans_obj is not None and session_obj is not None:
-
-        res = trans_obj.get_filter()
-    else:
-        status = False
-        res = error_msg
-
-    return make_json_response(data={'status': status, 'result': res})
-
-
[email protected](
-    '/filter/apply/<int:trans_id>',
-    methods=["PUT", "POST"], endpoint='apply_filter'
-)
-@login_required
-def apply_filter(trans_id):
-    """
-    This method is used to apply the filter.
-
-    Args:
-        trans_id: unique transaction id
-    """
-    if request.data:
-        filter_sql = json.loads(request.data, encoding='utf-8')
-    else:
-        filter_sql = request.args or request.form
-
-    # Check the transaction and connection status
-    status, error_msg, conn, trans_obj, session_obj = \
-        check_transaction_status(trans_id)
-
-    if error_msg == gettext('Transaction ID not found in the session.'):
-        return make_json_response(success=0, errormsg=error_msg,
-                                  info='DATAGRID_TRANSACTION_REQUIRED',
-                                  status=404)
-
-    if status and conn is not None and \
-       trans_obj is not None and session_obj is not None:
-
-        status, res = trans_obj.set_filter(filter_sql)
-
-        # As we changed the transaction object we need to
-        # restore it and update the session variable.
-        session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
-        update_session_grid_transaction(trans_id, session_obj)
-    else:
-        status = False
-        res = error_msg
-
-    return make_json_response(data={'status': status, 'result': res})
-
-
 @blueprint.route(
     '/filter/inclusive/<int:trans_id>',
     methods=["PUT", "POST"], endpoint='inclusive_filter'
@@ -1561,3 +1487,37 @@ def query_tool_status(trans_id):
         return internal_server_error(
             errormsg=gettext("Transaction status check failed.")
         )
+
+
[email protected](
+    '/filter_dialog/<int:trans_id>',
+    methods=["GET"], endpoint='get_filter_data'
+)
+@login_required
+def get_filter_data(trans_id):
+    """
+    This method is used to get all the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return FilterDialog.get(*check_transaction_status(trans_id))
+
+
[email protected](
+    '/filter_dialog/<int:trans_id>',
+    methods=["PUT"], endpoint='set_filter_data'
+)
+@login_required
+def set_filter_data(trans_id):
+    """
+    This method is used to update the columns for data sorting dialog.
+
+    Args:
+        trans_id: unique transaction id
+    """
+    return FilterDialog.save(
+        *check_transaction_status(trans_id),
+        request=request,
+        trans_id=trans_id
+    )
diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py
index 7ec03c5..fbe37df 100644
--- a/web/pgadmin/tools/sqleditor/command.py
+++ b/web/pgadmin/tools/sqleditor/command.py
@@ -141,6 +141,10 @@ class SQLFilter(object):
       - This method removes the filter applied.
     * validate_filter(row_filter)
       - This method validates the given filter.
+    * get_data_sorting()
+      - This method returns columns for data sorting
+    * set_data_sorting()
+      - This method saves columns for data sorting
     """
 
     def __init__(self, **kwargs):
@@ -160,8 +164,8 @@ class SQLFilter(object):
         self.sid = kwargs['sid']
         self.did = kwargs['did']
         self.obj_id = kwargs['obj_id']
-        self.__row_filter = kwargs['sql_filter'] if 'sql_filter' in kwargs \
-            else None
+        self.__row_filter = kwargs.get('sql_filter', None)
+        self.__dara_sorting = kwargs.get('data_sorting', None)
 
         manager = get_driver(PG_DEFAULT_DRIVER).connection_manager(self.sid)
         conn = manager.connection(did=self.did)
@@ -210,20 +214,41 @@ class SQLFilter(object):
 
         return status, msg
 
+    def get_data_sorting(self):
+        """
+        This function returns the filter.
+        """
+        if self.__dara_sorting and len(self.__dara_sorting) > 0:
+            return self.__dara_sorting
+        return None
+
+    def set_data_sorting(self, data_filter):
+        """
+        This function validates the filter and set the
+        given filter to member variable.
+        """
+        self.__dara_sorting = data_filter['data_sorting']
+
     def is_filter_applied(self):
         """
         This function returns True if filter is applied else False.
         """
+        is_filter_applied = True
         if self.__row_filter is None or self.__row_filter == '':
-            return False
+            is_filter_applied = False
 
-        return True
+        if not is_filter_applied:
+            if self.__dara_sorting and len(self.__dara_sorting) > 0:
+                is_filter_applied = True
+
+        return is_filter_applied
 
     def remove_filter(self):
         """
         This function remove the filter by setting value to None.
         """
         self.__row_filter = None
+        self.__dara_sorting = None
 
     def append_filter(self, row_filter):
         """
@@ -325,13 +350,58 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         self.cmd_type = kwargs['cmd_type'] if 'cmd_type' in kwargs else None
         self.limit = -1
 
-        if self.cmd_type == VIEW_FIRST_100_ROWS or \
-                self.cmd_type == VIEW_LAST_100_ROWS:
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_LAST_100_ROWS):
             self.limit = 100
 
     def get_primary_keys(self, *args, **kwargs):
         return None, None
 
+    def get_all_columns_with_order(self, default_conn):
+        """
+        Responsible for fetching columns from given object
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Columns which are already sorted which will
+                         be used to populate the Grid in the dialog
+            all_columns: List of all the column for given object which will
+                         be used to fill columns options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def save(self, changed_data, default_conn=None):
         return forbidden(
             errmsg=gettext("Data cannot be saved for the current object.")
@@ -351,6 +421,17 @@ class GridCommand(BaseCommand, SQLFilter, FetchedRowTracker):
         """
         self.limit = limit
 
+    def get_pk_order(self):
+        """
+        This function gets the order required for primary keys
+        """
+        if self.cmd_type in (VIEW_FIRST_100_ROWS, VIEW_ALL_ROWS):
+            return 'asc'
+        elif self.cmd_type == VIEW_LAST_100_ROWS:
+            return 'desc'
+        else:
+            return None
+
 
 class TableCommand(GridCommand):
     """
@@ -385,6 +466,7 @@ class TableCommand(GridCommand):
         has_oids = self.has_oids(default_conn)
 
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
@@ -392,7 +474,8 @@ class TableCommand(GridCommand):
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, limit=self.limit,
-                primary_keys=primary_keys, has_oids=has_oids
+                primary_keys=primary_keys, has_oids=has_oids,
+                data_sorting=data_sorting
             )
         else:
             sql = render_template(
@@ -401,7 +484,7 @@ class TableCommand(GridCommand):
                 nsp_name=self.nsp_name, pk_names=pk_names,
                 cmd_type=self.cmd_type, sql_filter=sql_filter,
                 limit=self.limit, primary_keys=primary_keys,
-                has_oids=has_oids
+                has_oids=has_oids, data_sorting=data_sorting
             )
 
         return sql
@@ -447,6 +530,73 @@ class TableCommand(GridCommand):
 
         return pk_names, primary_keys
 
+    def get_all_columns_with_order(self, default_conn=None):
+        """
+        It is overridden method specially for Table because we all have to
+        fetch primary keys and rest of the columns both.
+
+        Args:
+            default_conn: Connection object
+
+        Returns:
+            all_sorted_columns: Sorted columns for the Grid
+            all_columns: List of columns for the select2 options
+        """
+        driver = get_driver(PG_DEFAULT_DRIVER)
+        if default_conn is None:
+            manager = driver.connection_manager(self.sid)
+            conn = manager.connection(did=self.did, conn_id=self.conn_id)
+        else:
+            conn = default_conn
+
+        all_sorted_columns = []
+        data_sorting = self.get_data_sorting()
+        all_columns = []
+        if conn.connected():
+
+            # Fetch the primary key column names
+            query = render_template(
+                "/".join([self.sql_path, 'primary_keys.sql']),
+                obj_id=self.obj_id
+            )
+
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                all_columns.append(row['attname'])
+                all_sorted_columns.append(
+                    {
+                        'name': row['attname'],
+                        'order': self.get_pk_order()
+                    }
+                )
+
+            # Fetch the rest of the column names
+            query = render_template(
+                "/".join([self.sql_path, 'get_columns.sql']),
+                obj_id=self.obj_id
+            )
+            status, result = conn.execute_dict(query)
+            if not status:
+                raise Exception(result)
+
+            for row in result['rows']:
+                # Only append if not already present in the list
+                if row['attname'] not in all_columns:
+                    all_columns.append(row['attname'])
+        else:
+            raise Exception(
+                gettext('Not connected to server or connection with the '
+                        'server has been closed.')
+            )
+        # If user has custom data sorting then pass as it as it is
+        if data_sorting and len(data_sorting) > 0:
+            all_sorted_columns = data_sorting
+
+        return all_sorted_columns, all_columns
+
     def can_edit(self):
         return True
 
@@ -771,20 +921,22 @@ class ViewCommand(GridCommand):
         to fetch the data for the specified view
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -832,20 +984,22 @@ class ForeignTableCommand(GridCommand):
         to fetch the data for the specified foreign table
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -883,20 +1037,22 @@ class CatalogCommand(GridCommand):
         to fetch the data for the specified catalog object
         """
         sql_filter = self.get_filter()
+        data_sorting = self.get_data_sorting()
 
         if sql_filter is None:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                limit=self.limit
+                limit=self.limit, data_sorting=data_sorting
             )
         else:
             sql = render_template(
                 "/".join([self.sql_path, 'objectquery.sql']),
                 object_name=self.object_name,
                 nsp_name=self.nsp_name, cmd_type=self.cmd_type,
-                sql_filter=sql_filter, limit=self.limit
+                sql_filter=sql_filter, limit=self.limit,
+                data_sorting=data_sorting
             )
 
         return sql
@@ -929,6 +1085,9 @@ class QueryToolCommand(BaseCommand, FetchedRowTracker):
     def get_sql(self, default_conn=None):
         return None
 
+    def get_all_columns_with_order(self, default_conn=None):
+        return None
+
     def can_edit(self):
         return False
 
diff --git a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
index 46588dc..c54590d 100644
--- a/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
+++ b/web/pgadmin/tools/sqleditor/static/css/sqleditor.css
@@ -602,3 +602,26 @@ input.editor-checkbox:focus {
   font-size: 13px;
   line-height: 3em;
 }
+
+/* For Filter status bar */
+.data_sorting_dialog .pg-prop-status-bar {
+  position: absolute;
+  bottom: 37px;
+  z-index: 5;
+}
+
+.data_sorting_dialog .CodeMirror-gutter-wrapper {
+  left: -30px !important;
+}
+
+.data_sorting_dialog .CodeMirror-gutters {
+  left: 0px !important;
+}
+
+.data_sorting_dialog .custom_height_css_class {
+  height: 100px;
+}
+
+.data_sorting_dialog .data_sorting {
+  padding: 10px 0px;
+}
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 60dacbb..f8cb05a 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -14,6 +14,7 @@ define('tools.querytool', [
   'sources/sqleditor_utils',
   'sources/sqleditor/execute_query',
   'sources/sqleditor/query_tool_http_error_handler',
+  'sources/sqleditor/filter_dialog',
   'sources/history/index.js',
   'sources/../jsx/history/query_history',
   'react', 'react-dom',
@@ -33,7 +34,7 @@ define('tools.querytool', [
 ], function(
   babelPollyfill, gettext, url_for, $, _, S, alertify, pgAdmin, Backbone, codemirror,
   pgExplain, GridSelector, ActiveCellCapture, clipboard, copyData, RangeSelectionHelper, handleQueryOutputKeyboardEvent,
-  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler,
+  XCellSelectionModel, setStagedRows, SqlEditorUtils, ExecuteQuery, httpErrorHandler, FilterHandler,
   HistoryBundle, queryHistory, React, ReactDOM,
   keyboardShortcuts, queryToolActions, Datagrid, modifyAnimation,
   calculateQueryRunTime, callRenderAfterPoll) {
@@ -112,8 +113,7 @@ define('tools.querytool', [
 
     // This function is used to render the template.
     render: function() {
-      var self = this,
-        filter = self.$el.find('#sql_filter');
+      var self = this;
 
       $('.editor-title').text(_.unescape(self.editor_title));
       self.checkConnectionStatus();
@@ -121,31 +121,6 @@ define('tools.querytool', [
       // Fetch and assign the shortcuts to current instance
       self.keyboardShortcutConfig = queryToolActions.getKeyboardShortcuts(self);
 
-      self.filter_obj = CodeMirror.fromTextArea(filter.get(0), {
-        tabindex: '0',
-        lineNumbers: true,
-        mode: self.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql',
-        foldOptions: {
-          widget: '\u2026',
-        },
-        foldGutter: {
-          rangeFinder: CodeMirror.fold.combine(
-            CodeMirror.pgadminBeginRangeFinder,
-            CodeMirror.pgadminIfRangeFinder,
-            CodeMirror.pgadminLoopRangeFinder,
-            CodeMirror.pgadminCaseRangeFinder
-          ),
-        },
-        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
-        extraKeys: pgBrowser.editor_shortcut_keys,
-        indentWithTabs: pgAdmin.Browser.editor_options.indent_with_tabs,
-        indentUnit: pgAdmin.Browser.editor_options.tabSize,
-        tabSize: pgAdmin.Browser.editor_options.tabSize,
-        lineWrapping: pgAdmin.Browser.editor_options.wrapCode,
-        autoCloseBrackets: pgAdmin.Browser.editor_options.insert_pair_brackets,
-        matchBrackets: pgAdmin.Browser.editor_options.brace_matching,
-      });
-
       // Updates connection status flag
       self.gain_focus = function() {
         setTimeout(function() {
@@ -2141,11 +2116,11 @@ define('tools.querytool', [
               if (self.can_filter && res.data.filter_applied) {
                 $('#btn-filter').removeClass('btn-default');
                 $('#btn-filter-dropdown').removeClass('btn-default');
-                $('#btn-filter').addClass('btn-warning');
-                $('#btn-filter-dropdown').addClass('btn-warning');
+                $('#btn-filter').addClass('btn-primary');
+                $('#btn-filter-dropdown').addClass('btn-primary');
               } else {
-                $('#btn-filter').removeClass('btn-warning');
-                $('#btn-filter-dropdown').removeClass('btn-warning');
+                $('#btn-filter').removeClass('btn-primary');
+                $('#btn-filter-dropdown').removeClass('btn-primary');
                 $('#btn-filter').addClass('btn-default');
                 $('#btn-filter-dropdown').addClass('btn-default');
               }
@@ -3044,50 +3019,8 @@ define('tools.querytool', [
 
       // This function will show the filter in the text area.
       _show_filter: function() {
-        var self = this;
-
-        self.trigger(
-          'pgadmin-sqleditor:loading-icon:show',
-          gettext('Loading the existing filter options...')
-        );
-        $.ajax({
-          url: url_for('sqleditor.get_filter', {
-            'trans_id': self.transId,
-          }),
-          method: 'GET',
-          success: function(res) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            if (res.data.status) {
-              $('#filter').removeClass('hidden');
-              $('#editor-panel').addClass('sql-editor-busy-fetching');
-              self.gridView.filter_obj.refresh();
-
-              if (res.data.result == null)
-                self.gridView.filter_obj.setValue('');
-              else
-                self.gridView.filter_obj.setValue(res.data.result);
-              // Set focus on filter area
-              self.gridView.filter_obj.focus();
-            } else {
-              setTimeout(
-                function() {
-                  alertify.alert(gettext('Get Filter Error'), res.data.result);
-                }, 10
-              );
-            }
-          },
-          error: function(e) {
-            self.trigger('pgadmin-sqleditor:loading-icon:hide');
-            let msg = httpErrorHandler.handleQueryToolAjaxError(
-              pgAdmin, self, e, '_show_filter', [], true
-            );
-            setTimeout(
-              function() {
-                alertify.alert(gettext('Get Filter Error'), msg);
-              }, 10
-            );
-          },
-        });
+        let self = this;
+        FilterHandler.dialog(self);
       },
 
       // This function will include the filter by selection.
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
new file mode 100644
index 0000000..610747d
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/get_columns.sql
@@ -0,0 +1,9 @@
+{# ============= Fetch the columns ============= #}
+{% if obj_id %}
+SELECT at.attname, ty.typname
+    FROM pg_attribute at
+    LEFT JOIN pg_type ty ON (ty.oid = at.atttypid)
+WHERE attrelid={{obj_id}}::oid
+    AND at.attnum > 0
+    AND at.attisdropped = FALSE
+{% endif %}
diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
index 1cb60d9..add1658 100644
--- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
+++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql
@@ -3,7 +3,11 @@ SELECT {% if has_oids %}oid, {% endif %}* FROM {{ conn|qtIdent(nsp_name, object_
 {% if sql_filter %}
 WHERE {{ sql_filter }}
 {% endif %}
-{% if primary_keys %}
+{% if data_sorting and data_sorting|length > 0 %}
+ORDER BY {% for obj in data_sorting %}
+{{ conn|qtIdent(obj.name) }} {{ obj.order|upper }}{% if not loop.last %}, {% else %} {% endif %}
+{% endfor %}
+{% elif primary_keys %}
 ORDER BY {% for p in primary_keys %}{{conn|qtIdent(p)}}{% if cmd_type == 1 or cmd_type == 3 %} ASC{% elif cmd_type == 2 %} DESC{% endif %}
 {% if not loop.last %}, {% else %} {% endif %}{% endfor %}
 {% endif %}
diff --git a/web/pgadmin/tools/sqleditor/utils/filter_dialog.py b/web/pgadmin/tools/sqleditor/utils/filter_dialog.py
new file mode 100644
index 0000000..368dbb4
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/filter_dialog.py
@@ -0,0 +1,96 @@
+##########################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Code to handle data sorting in view data mode."""
+import pickle
+import simplejson as json
+from flask_babelex import gettext
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.update_session_grid_transaction import \
+    update_session_grid_transaction
+
+
+class FilterDialog(object):
+    @staticmethod
+    def get(*args):
+        """To fetch the current sorted columns"""
+        status, error_msg, conn, trans_obj, session_obj = args
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+        column_list = []
+        if status and conn is not None and \
+                trans_obj is not None and session_obj is not None:
+            msg = gettext('Success')
+            columns, column_list = trans_obj.get_all_columns_with_order(conn)
+            sql = trans_obj.get_filter()
+        else:
+            status = False
+            msg = error_msg
+            columns = None
+            sql = None
+
+
+        return make_json_response(
+            data={
+                'status': status,
+                'msg': msg,
+                'result': {
+                    'data_sorting': columns,
+                    'column_list': column_list,
+                    'sql': sql
+                }
+            }
+        )
+
+    @staticmethod
+    def save(*args, **kwargs):
+        """To save the sorted columns"""
+        # Check the transaction and connection status
+        status, error_msg, conn, trans_obj, session_obj = args
+        trans_id = kwargs['trans_id']
+        request = kwargs['request']
+
+        if request.data:
+            data = json.loads(request.data, encoding='utf-8')
+        else:
+            data = request.args or request.form
+
+        if error_msg == gettext('Transaction ID not found in the session.'):
+            return make_json_response(
+                success=0,
+                errormsg=error_msg,
+                info='DATAGRID_TRANSACTION_REQUIRED',
+                status=404
+            )
+
+        if status and conn is not None and \
+           trans_obj is not None and session_obj is not None:
+            trans_obj.set_data_sorting(data)
+            trans_obj.set_filter(data.get('sql'))
+            # As we changed the transaction object we need to
+            # restore it and update the session variable.
+            session_obj['command_obj'] = pickle.dumps(trans_obj, -1)
+            update_session_grid_transaction(trans_id, session_obj)
+            res = gettext('Data sorting object updated successfully')
+        else:
+            return internal_server_error(
+                errormsg=gettext('Failed to update the data on server.')
+            )
+
+        return make_json_response(
+            data={
+                'status': status,
+                'result': res
+            }
+        )
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py b/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py
new file mode 100644
index 0000000..9747978
--- /dev/null
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_filter_dialog_callbacks.py
@@ -0,0 +1,103 @@
+#######################################################################
+#
+# pgAdmin 4 - PostgreSQL Tools
+#
+# Copyright (C) 2013 - 2018, The pgAdmin Development Team
+# This software is released under the PostgreSQL Licence
+#
+##########################################################################
+
+"""Apply Explain plan wrapper to sql object."""
+from pgadmin.utils.ajax import make_json_response, internal_server_error
+from pgadmin.tools.sqleditor.utils.filter_dialog import FilterDialog
+from pgadmin.utils.route import BaseTestGenerator
+
+TX_ID_ERROR_MSG = 'Transaction ID not found in the session.'
+FAILED_TX_MSG = 'Failed to update the data on server.'
+
+
+class MockRequest(object):
+    "To mock request object"
+    def __init__(self):
+        self.data = None
+        self.args = "Test data",
+
+
+class StartRunningDataSortingTest(BaseTestGenerator):
+    """
+    Check that the DataSorting methods works as
+    intended
+    """
+    scenarios = [
+        ('When we do not find Transaction ID in session in get', dict(
+            input_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='get'
+        )),
+        ('When we pass all the values as None in get', dict(
+            input_parameters=(None, None, None, None, None),
+            expected_return_response={
+                'data': {
+                    'status': False,
+                    'msg': None,
+                    'result': {
+                        'data_sorting': None,
+                        'column_list': []
+                    }
+                }
+            },
+            type='get'
+        )),
+
+        ('When we do not find Transaction ID in session in save', dict(
+            input_arg_parameters=(None, TX_ID_ERROR_MSG, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'success': 0,
+                'errormsg': TX_ID_ERROR_MSG,
+                'info': 'DATAGRID_TRANSACTION_REQUIRED',
+                'status': 404
+            },
+            type='save'
+        )),
+
+        ('When we pass all the values as None in save', dict(
+            input_arg_parameters=(None, None, None, None, None),
+            input_kwarg_parameters={
+                'trans_id': None,
+                'request': MockRequest()
+            },
+            expected_return_response={
+                'status': 500,
+                'success': 0,
+                'errormsg': FAILED_TX_MSG
+
+            },
+            type='save'
+        ))
+    ]
+
+    def runTest(self):
+        expected_response = make_json_response(
+            **self.expected_return_response
+        )
+        if self.type == 'get':
+            result = FilterDialog.get(*self.input_parameters)
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
+        else:
+            result = FilterDialog.save(
+                *self.input_arg_parameters, **self.input_kwarg_parameters
+            )
+            self.assertEquals(
+                result.status_code, expected_response.status_code
+            )
diff --git a/web/regression/javascript/sqleditor/filter_dialog_specs.js b/web/regression/javascript/sqleditor/filter_dialog_specs.js
new file mode 100644
index 0000000..e13fa09
--- /dev/null
+++ b/web/regression/javascript/sqleditor/filter_dialog_specs.js
@@ -0,0 +1,31 @@
+//////////////////////////////////////////////////////////////////////////
+//
+// pgAdmin 4 - PostgreSQL Tools
+//
+// Copyright (C) 2013 - 2018, The pgAdmin Development Team
+// This software is released under the PostgreSQL Licence
+//
+//////////////////////////////////////////////////////////////////////////
+import filterDialog from 'sources/sqleditor/filter_dialog';
+import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
+
+describe('filterDialog', () => {
+  let sqlEditorController;
+  sqlEditorController = jasmine.createSpy('sqlEditorController')
+  describe('filterDialog', () => {
+    describe('when using filter dialog', () => {
+      beforeEach(() => {
+        spyOn(filterDialog, 'dialog');
+      });
+
+      it("it should be defined as function", function() {
+        expect(filterDialog.dialog).toBeDefined();
+      });
+
+      it('it should call without proper handler', () => {
+        expect(filterDialog.dialog).not.toHaveBeenCalledWith({});
+      });
+
+    });
+  });
+});


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-04-05 13:25                         ` Joao De Almeida Pereira <[email protected]>
  1 sibling, 0 replies; 25+ messages in thread

From: Joao De Almeida Pereira @ 2018-04-05 13:25 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Dave Page <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Murtuza I tried to apply the patch to master:

git apply ~/Downloads/RM_3055_v2.diff
error: cannot apply binary patch to
'docs/en_US/images/editgrid_filter_dialog.png' without full index line
error: docs/en_US/images/editgrid_filter_dialog.png: patch does not apply


On Thu, Apr 5, 2018 at 7:30 AM Murtuza Zabuawala <
[email protected]> wrote:

> Hi Dave,
>
> Please find rebased patch.
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>
>> Can you rebase this please?
>>
>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> Hi Dave,
>>>
>>> Please find updated patch with following changes,
>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>> - Extracted model into separate file
>>> - Change the colour of filter button from orange to blue.
>>> - Updated docs and screenshot.
>>>
>>> @Joao,
>>> Could you please provide any reference for learning more about jasmine
>>> test framework?
>>> ​
>>>
>>> --
>>> Regards,
>>> Murtuza Zabuawala
>>> EnterpriseDB: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>>
>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Hackers,
>>>>>>>>>>
>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>>> for the change.
>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>>>> tested.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>> option, nothing
>>>>>>>>>>
>>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>>>> users a more intuitive place to look.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>> see fit.
>>>>>>>>
>>>>>>>
>>>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>>>> in an effort to start a discussion about the user experience. Ideally what
>>>>>> I'd like to see, maybe this happened, is some user research. When we
>>>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>>>> One of the things we intended to do was to follow up to see how people were
>>>>>> using the grid now so that we could better understand how it was now being
>>>>>> used in order to design and implement features just like this. Clearly we
>>>>>> haven't gotten there yet.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Another reason we can't use that because w
>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>> ​ when user clicks on header.
>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>> ​
>>>>>>>
>>>>>>
>>>>>> How are users currently interacting with that filter dialog?
>>>>>>
>>>>>
>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>> ​
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>> know if people are using the filter functionality?  What kind of
>>>> filters are people using?  What do they like about it? What do they wish
>>>> they could do above and beyond sorting, etc.
>>>>
>>> ​I have not done any data gathering from users so I can't comment on
>>> your queries.
>>> ​ but a​
>>> ​s far as I understood from the feature requests that most of the users
>>> expect to have functionality which will allow then to sort columns as it
>>> was in pgAdmin3.​
>>>
>>>
>>>
>>>> -- Rob
>>>>
>>>>
>>>>>
>>>>>> What I'm suggesting is that we understand how users want to interact
>>>>>> with their results, be those the results of a query or a table view, then
>>>>>> we can design something that meets those needs. I agree that changing the
>>>>>> column selection behavior isn't desirable, however, I also feel like
>>>>>> providing the best user experience is better than holding onto a particular
>>>>>> feature implementation.
>>>>>> ​
>>>>>>
>>>>>>
>>>>>
>>>>>> -- Rob
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> --
>>>>>>>> Dave Page
>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>> Twitter: @pgsnake
>>>>>>>>
>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-04-05 15:25                         ` Dave Page <[email protected]>
  2018-04-05 17:50                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  1 sibling, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-04-05 15:25 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Robert Eckhardt <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

Thanks, applied.

On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <
[email protected]> wrote:

> Hi Dave,
>
> Please find rebased patch.
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>
>> Can you rebase this please?
>>
>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> Hi Dave,
>>>
>>> Please find updated patch with following changes,
>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>> - Extracted model into separate file
>>> - Change the colour of filter button from orange to blue.
>>> - Updated docs and screenshot.
>>>
>>> @Joao,
>>> Could you please provide any reference for learning more about jasmine
>>> test framework?
>>> ​
>>>
>>> --
>>> Regards,
>>> Murtuza Zabuawala
>>> EnterpriseDB: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>>
>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Hackers,
>>>>>>>>>>
>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>>> for the change.
>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>>>> tested.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>> option, nothing
>>>>>>>>>>
>>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>>>> users a more intuitive place to look.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>> see fit.
>>>>>>>>
>>>>>>>
>>>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>>>> in an effort to start a discussion about the user experience. Ideally what
>>>>>> I'd like to see, maybe this happened, is some user research. When we
>>>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>>>> One of the things we intended to do was to follow up to see how people were
>>>>>> using the grid now so that we could better understand how it was now being
>>>>>> used in order to design and implement features just like this. Clearly we
>>>>>> haven't gotten there yet.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Another reason we can't use that because w
>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>> ​ when user clicks on header.
>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>> ​
>>>>>>>
>>>>>>
>>>>>> How are users currently interacting with that filter dialog?
>>>>>>
>>>>>
>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>> ​
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>> know if people are using the filter functionality?  What kind of
>>>> filters are people using?  What do they like about it? What do they wish
>>>> they could do above and beyond sorting, etc.
>>>>
>>> ​I have not done any data gathering from users so I can't comment on
>>> your queries.
>>> ​ but a​
>>> ​s far as I understood from the feature requests that most of the users
>>> expect to have functionality which will allow then to sort columns as it
>>> was in pgAdmin3.​
>>>
>>>
>>>
>>>> -- Rob
>>>>
>>>>
>>>>>
>>>>>> What I'm suggesting is that we understand how users want to interact
>>>>>> with their results, be those the results of a query or a table view, then
>>>>>> we can design something that meets those needs. I agree that changing the
>>>>>> column selection behavior isn't desirable, however, I also feel like
>>>>>> providing the best user experience is better than holding onto a particular
>>>>>> feature implementation.
>>>>>> ​
>>>>>>
>>>>>>
>>>>>
>>>>>> -- Rob
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> --
>>>>>>>> Dave Page
>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>> Twitter: @pgsnake
>>>>>>>>
>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>
>


-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-04-05 17:50                           ` Joao De Almeida Pereira <[email protected]>
  2018-04-06 03:43                             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Joao De Almeida Pereira @ 2018-04-05 17:50 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Hi Murtuza,
I forgot to mention this in my review........
Do you think we can start using only ES6 instead of keep using requirejs
syntax on new things that we are building?
How much effort do you think it will be?

Like this patch, as an example, do you think it could have been implemented
without using requirejs?

Thanks

On Thu, Apr 5, 2018 at 11:26 AM Dave Page <[email protected]> wrote:

> Thanks, applied.
>
> On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <
> [email protected]> wrote:
>
>> Hi Dave,
>>
>> Please find rebased patch.
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> EnterpriseDB: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>>
>>> Can you rebase this please?
>>>
>>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>> Hi Dave,
>>>>
>>>> Please find updated patch with following changes,
>>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>>> - Extracted model into separate file
>>>> - Change the colour of filter button from orange to blue.
>>>> - Updated docs and screenshot.
>>>>
>>>> @Joao,
>>>> Could you please provide any reference for learning more about jasmine
>>>> test framework?
>>>> ​
>>>>
>>>> --
>>>> Regards,
>>>> Murtuza Zabuawala
>>>> EnterpriseDB: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>>
>>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>
>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>>>> for the change.
>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could
>>>>>>>>>>> be improved. Also the DataSorting class could have some other member
>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>> easily tested.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>>> option, nothing
>>>>>>>>>>>
>>>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>> see fit.
>>>>>>>>>
>>>>>>>>
>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>> was now being used in order to design and implement features just like
>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Another reason we can't use that because w
>>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>>> ​ when user clicks on header.
>>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>>> ​
>>>>>>>>
>>>>>>>
>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>
>>>>>>
>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>> ​
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>>> know if people are using the filter functionality?  What kind of
>>>>> filters are people using?  What do they like about it? What do they wish
>>>>> they could do above and beyond sorting, etc.
>>>>>
>>>> ​I have not done any data gathering from users so I can't comment on
>>>> your queries.
>>>> ​ but a​
>>>> ​s far as I understood from the feature requests that most of the users
>>>> expect to have functionality which will allow then to sort columns as it
>>>> was in pgAdmin3.​
>>>>
>>>>
>>>>
>>>>> -- Rob
>>>>>
>>>>>
>>>>>>
>>>>>>> What I'm suggesting is that we understand how users want to interact
>>>>>>> with their results, be those the results of a query or a table view, then
>>>>>>> we can design something that meets those needs. I agree that changing the
>>>>>>> column selection behavior isn't desirable, however, I also feel like
>>>>>>> providing the best user experience is better than holding onto a particular
>>>>>>> feature implementation.
>>>>>>> ​
>>>>>>>
>>>>>>>
>>>>>>
>>>>>>> -- Rob
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>> --
>>>>>>>>> Dave Page
>>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>>> Twitter: @pgsnake
>>>>>>>>>
>>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 17:50                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
@ 2018-04-06 03:43                             ` Murtuza Zabuawala <[email protected]>
  2018-04-06 05:44                               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-04-06 03:43 UTC (permalink / raw)
  To: Joao De Almeida Pereira <[email protected]>; +Cc: Dave Page <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Hi Joao,


On Thu, Apr 5, 2018 at 11:20 PM, Joao De Almeida Pereira <
[email protected]> wrote:

> Hi Murtuza,
> I forgot to mention this in my review........
> Do you think we can start using only ES6 instead of keep using requirejs
> syntax on new things that we are building?
> How much effort do you think it will be?
>
​Yes, we can.
Let me try to change the code to use ES6 and I'll send patch.​


>
> Like this patch, as an example, do you think it could have been
> implemented without using requirejs?
>

> Thanks
>
> On Thu, Apr 5, 2018 at 11:26 AM Dave Page <[email protected]> wrote:
>
>> Thanks, applied.
>>
>> On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <murtuza.zabuawala@
>> enterprisedb.com> wrote:
>>
>>> Hi Dave,
>>>
>>> Please find rebased patch.
>>>
>>> --
>>> Regards,
>>> Murtuza Zabuawala
>>> EnterpriseDB: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>>
>>> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>>>
>>>> Can you rebase this please?
>>>>
>>>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <murtuza.zabuawala@
>>>> enterprisedb.com> wrote:
>>>>
>>>>> Hi Dave,
>>>>>
>>>>> Please find updated patch with following changes,
>>>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>>>> - Extracted model into separate file
>>>>> - Change the colour of filter button from orange to blue.
>>>>> - Updated docs and screenshot.
>>>>>
>>>>> @Joao,
>>>>> Could you please provide any reference for learning more about jasmine
>>>>> test framework?
>>>>> ​
>>>>>
>>>>> --
>>>>> Regards,
>>>>> Murtuza Zabuawala
>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>>
>>>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>>> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>>
>>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we
>>>>>>>>>>>> are using axios instead of jquery ajax calls and that there is some
>>>>>>>>>>>> coverage for the change.
>>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could
>>>>>>>>>>>> be improved. Also the DataSorting class could have some other member
>>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>>> easily tested.
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>>>> option, nothing
>>>>>>>>>>>>
>>>>>>>>>>>> This is the behavior that we were expecting, not to have to
>>>>>>>>>>>> open Data View and then press the icon that is not even near the grid in
>>>>>>>>>>>> order to sort the column. Is this really the way we want people to use the
>>>>>>>>>>>> grid in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>>> see fit.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>>> was now being used in order to design and implement features just like
>>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>> Another reason we can't use that because w
>>>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>>>> ​ when user clicks on header.
>>>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>>>> ​
>>>>>>>>>
>>>>>>>>
>>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>>
>>>>>>>
>>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>>> ​
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>
>>>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>>>> know if people are using the filter functionality?  What kind of
>>>>>> filters are people using?  What do they like about it? What do they wish
>>>>>> they could do above and beyond sorting, etc.
>>>>>>
>>>>> ​I have not done any data gathering from users so I can't comment on
>>>>> your queries.
>>>>> ​ but a​
>>>>> ​s far as I understood from the feature requests that most of the
>>>>> users expect to have functionality which will allow then to sort columns as
>>>>> it was in pgAdmin3.​
>>>>>
>>>>>
>>>>>
>>>>>> -- Rob
>>>>>>
>>>>>>
>>>>>>>
>>>>>>>> What I'm suggesting is that we understand how users want to
>>>>>>>> interact with their results, be those the results of a query or a table
>>>>>>>> view, then we can design something that meets those needs. I agree that
>>>>>>>> changing the column selection behavior isn't desirable, however, I also
>>>>>>>> feel like providing the best user experience is better than holding onto a
>>>>>>>> particular feature implementation.
>>>>>>>> ​
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>> -- Rob
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> --
>>>>>>>>>> Dave Page
>>>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>>>> Twitter: @pgsnake
>>>>>>>>>>
>>>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>>
>>
>>
>> --
>> Dave Page
>> Blog: http://pgsnake.blogspot.com
>> Twitter: @pgsnake
>>
>> EnterpriseDB UK: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 17:50                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-04-06 03:43                             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-04-06 05:44                               ` Murtuza Zabuawala <[email protected]>
  2018-04-06 09:38                                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Murtuza Zabuawala @ 2018-04-06 05:44 UTC (permalink / raw)
  To: Joao De Almeida Pereira <[email protected]>; +Cc: Dave Page <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Hi,

Please find the patch which will change the syntax from requirejs syntax to
ES6 from previous commit.

--
Regards,
Murtuza Zabuawala
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


On Fri, Apr 6, 2018 at 9:13 AM, Murtuza Zabuawala <
[email protected]> wrote:

> Hi Joao,
>
>
> On Thu, Apr 5, 2018 at 11:20 PM, Joao De Almeida Pereira <
> [email protected]> wrote:
>
>> Hi Murtuza,
>> I forgot to mention this in my review........
>> Do you think we can start using only ES6 instead of keep using requirejs
>> syntax on new things that we are building?
>> How much effort do you think it will be?
>>
> ​Yes, we can.
> Let me try to change the code to use ES6 and I'll send patch.​
>
>
>>
>> Like this patch, as an example, do you think it could have been
>> implemented without using requirejs?
>>
>
>> Thanks
>>
>> On Thu, Apr 5, 2018 at 11:26 AM Dave Page <[email protected]> wrote:
>>
>>> Thanks, applied.
>>>
>>> On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>> Hi Dave,
>>>>
>>>> Please find rebased patch.
>>>>
>>>> --
>>>> Regards,
>>>> Murtuza Zabuawala
>>>> EnterpriseDB: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>>
>>>> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>>>>
>>>>> Can you rebase this please?
>>>>>
>>>>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Dave,
>>>>>>
>>>>>> Please find updated patch with following changes,
>>>>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>>>>> - Extracted model into separate file
>>>>>> - Change the colour of filter button from orange to blue.
>>>>>> - Updated docs and screenshot.
>>>>>>
>>>>>> @Joao,
>>>>>> Could you please provide any reference for learning more about
>>>>>> jasmine test framework?
>>>>>> ​
>>>>>>
>>>>>> --
>>>>>> Regards,
>>>>>> Murtuza Zabuawala
>>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>>
>>>>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>>>> wrote:
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>>>
>>>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we
>>>>>>>>>>>>> are using axios instead of jquery ajax calls and that there is some
>>>>>>>>>>>>> coverage for the change.
>>>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could
>>>>>>>>>>>>> be improved. Also the DataSorting class could have some other member
>>>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>>>> easily tested.
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>>>>> option, nothing
>>>>>>>>>>>>>
>>>>>>>>>>>>> This is the behavior that we were expecting, not to have to
>>>>>>>>>>>>> open Data View and then press the icon that is not even near the grid in
>>>>>>>>>>>>> order to sort the column. Is this really the way we want people to use the
>>>>>>>>>>>>> grid in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>>>> see fit.
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>>>> was now being used in order to design and implement features just like
>>>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Another reason we can't use that because w
>>>>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>>>>> ​ when user clicks on header.
>>>>>>>>>> As Dave suggested, I will be merging it with filter dialog
>>>>>>>>>> meaning it will be accessible via direct button on toolbar & keyboard
>>>>>>>>>> shortcut.​
>>>>>>>>>> ​
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>>>
>>>>>>>>
>>>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>>>> ​
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>> Sorry I wasn't clear. My question was more along the lines of, do
>>>>>>> we know if people are using the filter functionality?  What kind of
>>>>>>> filters are people using?  What do they like about it? What do they wish
>>>>>>> they could do above and beyond sorting, etc.
>>>>>>>
>>>>>> ​I have not done any data gathering from users so I can't comment on
>>>>>> your queries.
>>>>>> ​ but a​
>>>>>> ​s far as I understood from the feature requests that most of the
>>>>>> users expect to have functionality which will allow then to sort columns as
>>>>>> it was in pgAdmin3.​
>>>>>>
>>>>>>
>>>>>>
>>>>>>> -- Rob
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>>> What I'm suggesting is that we understand how users want to
>>>>>>>>> interact with their results, be those the results of a query or a table
>>>>>>>>> view, then we can design something that meets those needs. I agree that
>>>>>>>>> changing the column selection behavior isn't desirable, however, I also
>>>>>>>>> feel like providing the best user experience is better than holding onto a
>>>>>>>>> particular feature implementation.
>>>>>>>>> ​
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>>> -- Rob
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> --
>>>>>>>>>>> Dave Page
>>>>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>>>>> Twitter: @pgsnake
>>>>>>>>>>>
>>>>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Dave Page
>>>>> Blog: http://pgsnake.blogspot.com
>>>>> Twitter: @pgsnake
>>>>>
>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>
>>>>
>>>
>>>
>>> --
>>> Dave Page
>>> Blog: http://pgsnake.blogspot.com
>>> Twitter: @pgsnake
>>>
>>> EnterpriseDB UK: http://www.enterprisedb.com
>>> The Enterprise PostgreSQL Company
>>>
>>
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

  [application/octet-stream] RM_1894_ES6_changes.diff (25.3K, 4-RM_1894_ES6_changes.diff)
  download | inline diff:
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog.js b/web/pgadmin/static/js/sqleditor/filter_dialog.js
index 0ba9e35..8a48c86 100644
--- a/web/pgadmin/static/js/sqleditor/filter_dialog.js
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog.js
@@ -1,243 +1,241 @@
-define([
-  'sources/gettext', 'sources/url_for', 'jquery', 'underscore', 'underscore.string',
-  'pgadmin.alertifyjs', 'sources/pgadmin', 'backbone',
-  'pgadmin.backgrid', 'pgadmin.backform', 'axios',
-  'sources/sqleditor/query_tool_actions',
-  'sources/sqleditor/filter_dialog_model',
-  //'pgadmin.browser.node.ui',
-], function(
-  gettext, url_for, $, _, S, Alertify, pgAdmin, Backbone,
-  Backgrid, Backform, axios, queryToolActions, filterDialogModel
-) {
-
-  let FilterDialog = {
-    'dialog': function(handler) {
-      let title = gettext('Sort/Filter options');
-      axios.get(
-        url_for('sqleditor.get_filter_data', {
-          'trans_id': handler.transId,
-        }),
-        { headers: {'Cache-Control' : 'no-cache'} }
-      ).then(function (res) {
-        let response = res.data.data.result;
-
-        // Check the alertify dialog already loaded then delete it to clear
-        // the cache
-        if (Alertify.filterDialog) {
-          delete Alertify.filterDialog;
-        }
-
-        // Create Dialog
-        Alertify.dialog('filterDialog', function factory() {
-          let $container = $('<div class=\'data_sorting_dialog\'></div>');
-          return {
-            main: function() {
-              this.set('title', gettext('Sort/Filter options'));
-            },
-            build: function() {
-              this.elements.content.appendChild($container.get(0));
-              Alertify.pgDialogBuild.apply(this);
-            },
-            setup: function() {
-              return {
-                buttons: [{
-                  text: '',
-                  key: 112,
-                  className: 'btn btn-default pull-left fa fa-lg fa-question',
-                  attrs: {
-                    name: 'dialog_help',
-                    type: 'button',
-                    label: gettext('Help'),
-                    url: url_for('help.static', {
-                      'filename': 'editgrid.html',
-                    }),
-                  },
-                }, {
-                  text: gettext('Ok'),
-                  className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
-                  'data-btn-name': 'ok',
-                }, {
-                  text: gettext('Cancel'),
-                  key: 27,
-                  className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
-                  'data-btn-name': 'cancel',
-                }],
-                // Set options for dialog
-                options: {
-                  title: title,
-                  //disable both padding and overflow control.
-                  padding: !1,
-                  overflow: !1,
-                  model: 0,
-                  resizable: true,
-                  maximizable: true,
-                  pinnable: false,
-                  closableByDimmer: false,
-                  modal: false,
-                  autoReset: false,
+import gettext from 'sources/gettext';
+import url_for from 'sources/url_for';
+import $ from 'jquery';
+import Alertify from 'pgadmin.alertifyjs';
+import pgAdmin from 'sources/pgadmin';
+import Backform from 'pgadmin.backform';
+import axios from 'axios';
+import queryToolActions from 'sources/sqleditor/query_tool_actions';
+import filterDialogModel from 'sources/sqleditor/filter_dialog_model';
+
+let FilterDialog = {
+  'dialog': function(handler) {
+    let title = gettext('Sort/Filter options');
+    axios.get(
+      url_for('sqleditor.get_filter_data', {
+        'trans_id': handler.transId,
+      }),
+      { headers: {'Cache-Control' : 'no-cache'} }
+    ).then(function (res) {
+      let response = res.data.data.result;
+
+      // Check the alertify dialog already loaded then delete it to clear
+      // the cache
+      if (Alertify.filterDialog) {
+        delete Alertify.filterDialog;
+      }
+
+      // Create Dialog
+      Alertify.dialog('filterDialog', function factory() {
+        let $container = $('<div class=\'data_sorting_dialog\'></div>');
+        return {
+          main: function() {
+            this.set('title', gettext('Sort/Filter options'));
+          },
+          build: function() {
+            this.elements.content.appendChild($container.get(0));
+            Alertify.pgDialogBuild.apply(this);
+          },
+          setup: function() {
+            return {
+              buttons: [{
+                text: '',
+                key: 112,
+                className: 'btn btn-default pull-left fa fa-lg fa-question',
+                attrs: {
+                  name: 'dialog_help',
+                  type: 'button',
+                  label: gettext('Help'),
+                  url: url_for('help.static', {
+                    'filename': 'editgrid.html',
+                  }),
                 },
-              };
-            },
-            hooks: {
-              // triggered when the dialog is closed
-              onclose: function() {
-                if (this.view) {
-                  this.filterCollectionModel.stopSession();
-                  this.view.model.stopSession();
-                  this.view.remove({
-                    data: true,
-                    internal: true,
-                    silent: true,
-                  });
-                }
+              }, {
+                text: gettext('Ok'),
+                className: 'btn btn-primary fa fa-lg fa-save pg-alertify-button',
+                'data-btn-name': 'ok',
+              }, {
+                text: gettext('Cancel'),
+                key: 27,
+                className: 'btn btn-danger fa fa-lg fa-times pg-alertify-button',
+                'data-btn-name': 'cancel',
+              }],
+              // Set options for dialog
+              options: {
+                title: title,
+                //disable both padding and overflow control.
+                padding: !1,
+                overflow: !1,
+                model: 0,
+                resizable: true,
+                maximizable: true,
+                pinnable: false,
+                closableByDimmer: false,
+                modal: false,
+                autoReset: false,
               },
+            };
+          },
+          hooks: {
+            // triggered when the dialog is closed
+            onclose: function() {
+              if (this.view) {
+                this.filterCollectionModel.stopSession();
+                this.view.model.stopSession();
+                this.view.remove({
+                  data: true,
+                  internal: true,
+                  silent: true,
+                });
+              }
             },
-            prepare: function() {
-              let self = this;
-              $container.html('');
-              // Disable Ok button
-              this.__internal.buttons[1].element.disabled = true;
-
-              // Status bar
-              this.statusBar = $('<div class=\'pg-prop-status-bar pg-el-xs-12 hide\'>' +
-                '  <div class=\'media error-in-footer bg-red-1 border-red-2 font-red-3 text-14\'>' +
-                '    <div class=\'media-body media-middle\'>' +
-                '      <div class=\'alert-icon error-icon\'>' +
-                '        <i class=\'fa fa-exclamation-triangle\' aria-hidden=\'true\'></i>' +
-                '      </div>' +
-                '      <div class=\'alert-text\'>' +
-                '      </div>' +
-                '    </div>' +
-                '  </div>' +
-                '</div>', {
-                  text: '',
-                }).appendTo($container);
-
-              // To show progress on filter Saving/Updating on AJAX
-              this.showFilterProgress = $(
-                '<div id="show_filter_progress" class="wcLoadingIconContainer busy-fetching hidden">' +
-                '<div class="wcLoadingBackground"></div>' +
-                '<span class="wcLoadingIcon fa fa-spinner fa-pulse"></span>' +
-                '<span class="busy-text wcLoadingLabel">' + gettext('Loading data...') + '</span>' +
-                '</div>').appendTo($container);
-
-              $(
-                self.showFilterProgress[0]
-              ).removeClass('hidden');
-
-              self.filterCollectionModel = filterDialogModel(response);
-
-              let fields = Backform.generateViewSchema(
-                  null, self.filterCollectionModel, 'create', null, null, true
-              );
+          },
+          prepare: function() {
+            let self = this;
+            $container.html('');
+            // Disable Ok button
+            this.__internal.buttons[1].element.disabled = true;
+
+            // Status bar
+            this.statusBar = $('<div class=\'pg-prop-status-bar pg-el-xs-12 hide\'>' +
+              '  <div class=\'media error-in-footer bg-red-1 border-red-2 font-red-3 text-14\'>' +
+              '    <div class=\'media-body media-middle\'>' +
+              '      <div class=\'alert-icon error-icon\'>' +
+              '        <i class=\'fa fa-exclamation-triangle\' aria-hidden=\'true\'></i>' +
+              '      </div>' +
+              '      <div class=\'alert-text\'>' +
+              '      </div>' +
+              '    </div>' +
+              '  </div>' +
+              '</div>', {
+                text: '',
+              }).appendTo($container);
+
+            // To show progress on filter Saving/Updating on AJAX
+            this.showFilterProgress = $(
+              '<div id="show_filter_progress" class="wcLoadingIconContainer busy-fetching hidden">' +
+              '<div class="wcLoadingBackground"></div>' +
+              '<span class="wcLoadingIcon fa fa-spinner fa-pulse"></span>' +
+              '<span class="busy-text wcLoadingLabel">' + gettext('Loading data...') + '</span>' +
+              '</div>').appendTo($container);
+
+            $(
+              self.showFilterProgress[0]
+            ).removeClass('hidden');
+
+            self.filterCollectionModel = filterDialogModel(response);
+
+            let fields = Backform.generateViewSchema(
+                null, self.filterCollectionModel, 'create', null, null, true
+            );
+
+            let view = this.view = new Backform.Dialog({
+              el: '<div></div>',
+              model: self.filterCollectionModel,
+              schema: fields,
+            });
+
+            $(this.elements.body.childNodes[0]).addClass(
+              'alertify_tools_dialog_properties obj_properties'
+            );
+
+            $container.append(view.render().$el);
+
+            // Enable/disable save button and show/hide statusbar based on session
+            view.listenTo(view.model, 'pgadmin-session:start', function() {
+              view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
+                self.statusBar.removeClass('hide');
+                $(self.statusBar.find('.alert-text')).html(msg);
+                // Disable Okay button
+                self.__internal.buttons[1].element.disabled = true;
+              });
 
-              let view = this.view = new Backform.Dialog({
-                el: '<div></div>',
-                model: self.filterCollectionModel,
-                schema: fields,
+              view.listenTo(view.model, 'pgadmin-session:valid', function() {
+                self.statusBar.addClass('hide');
+                $(self.statusBar.find('.alert-text')).html('');
+                // Enable Okay button
+                self.__internal.buttons[1].element.disabled = false;
               });
+            });
 
-              $(this.elements.body.childNodes[0]).addClass(
-                'alertify_tools_dialog_properties obj_properties'
-              );
+            view.listenTo(view.model, 'pgadmin-session:stop', function() {
+              view.stopListening(view.model, 'pgadmin-session:invalid');
+              view.stopListening(view.model, 'pgadmin-session:valid');
+            });
 
-              $container.append(view.render().$el);
+            // Starts monitoring changes to model
+            view.model.startNewSession();
 
-              // Enable/disable save button and show/hide statusbar based on session
-              view.listenTo(view.model, 'pgadmin-session:start', function() {
-                view.listenTo(view.model, 'pgadmin-session:invalid', function(msg) {
-                  self.statusBar.removeClass('hide');
-                  $(self.statusBar.find('.alert-text')).html(msg);
-                  // Disable Okay button
-                  self.__internal.buttons[1].element.disabled = true;
-                });
+            // Set data in collection
+            let viewDataSortingModel = view.model.get('data_sorting');
+            viewDataSortingModel.add(response['data_sorting']);
 
-                view.listenTo(view.model, 'pgadmin-session:valid', function() {
-                  self.statusBar.addClass('hide');
-                  $(self.statusBar.find('.alert-text')).html('');
-                  // Enable Okay button
-                  self.__internal.buttons[1].element.disabled = false;
-                });
-              });
+            // Hide Progress ...
+            $(
+              self.showFilterProgress[0]
+            ).addClass('hidden');
 
-              view.listenTo(view.model, 'pgadmin-session:stop', function() {
-                view.stopListening(view.model, 'pgadmin-session:invalid');
-                view.stopListening(view.model, 'pgadmin-session:valid');
-              });
+          },
+          // Callback functions when click on the buttons of the Alertify dialogs
+          callback: function(e) {
+            let self = this;
 
-              // Starts monitoring changes to model
-              view.model.startNewSession();
+            if (e.button.element.name == 'dialog_help') {
+              e.cancel = true;
+              pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
+                null, null, e.button.element.getAttribute('label'));
+              return;
+            } else if (e.button['data-btn-name'] === 'ok') {
+              e.cancel = true; // Do not close dialog
 
-              // Set data in collection
-              let viewDataSortingModel = view.model.get('data_sorting');
-              viewDataSortingModel.add(response['data_sorting']);
+              let filterCollectionModel = this.filterCollectionModel.toJSON();
 
-              // Hide Progress ...
+              // Show Progress ...
               $(
                 self.showFilterProgress[0]
-              ).addClass('hidden');
-
-            },
-            // Callback functions when click on the buttons of the Alertify dialogs
-            callback: function(e) {
-              let self = this;
-
-              if (e.button.element.name == 'dialog_help') {
-                e.cancel = true;
-                pgAdmin.Browser.showHelp(e.button.element.name, e.button.element.getAttribute('url'),
-                  null, null, e.button.element.getAttribute('label'));
-                return;
-              } else if (e.button['data-btn-name'] === 'ok') {
-                e.cancel = true; // Do not close dialog
-
-                let filterCollectionModel = this.filterCollectionModel.toJSON();
+              ).removeClass('hidden');
 
-                // Show Progress ...
+              axios.put(
+                url_for('sqleditor.set_filter_data', {
+                  'trans_id': handler.transId,
+                }),
+                filterCollectionModel
+              ).then(function () {
+                // Hide Progress ...
+                $(
+                  self.showFilterProgress[0]
+                ).addClass('hidden');
+                setTimeout(
+                  function() {
+                    self.close(); // Close the dialog now
+                    Alertify.success(gettext('Filter updated successfully'));
+                    queryToolActions.executeQuery(handler);
+                  }, 10
+                );
+
+              }).catch(function (error) {
+                // Hide Progress ...
                 $(
                   self.showFilterProgress[0]
-                ).removeClass('hidden');
+                ).addClass('hidden');
+                handler.onExecuteHTTPError(error);
+
+                setTimeout(
+                  function() {
+                    Alertify.error(error);
+                  }, 10
+                );
+              });
+            } else {
+              self.close();
+            }
+          },
+        };
+      });
 
-                axios.put(
-                  url_for('sqleditor.set_filter_data', {
-                    'trans_id': handler.transId,
-                  }),
-                  filterCollectionModel
-                ).then(function () {
-                  // Hide Progress ...
-                  $(
-                    self.showFilterProgress[0]
-                  ).addClass('hidden');
-                  setTimeout(
-                    function() {
-                      self.close(); // Close the dialog now
-                      Alertify.success(gettext('Filter updated successfully'));
-                      queryToolActions.executeQuery(handler);
-                    }, 10
-                  );
-
-                }).catch(function (error) {
-                  // Hide Progress ...
-                  $(
-                    self.showFilterProgress[0]
-                  ).addClass('hidden');
-                  handler.onExecuteHTTPError(error);
-
-                  setTimeout(
-                    function() {
-                      Alertify.error(error);
-                    }, 10
-                  );
-                });
-              } else {
-                self.close();
-              }
-            },
-          };
-        });
+      Alertify.filterDialog(title).resizeTo('65%', '60%');
+    });
+  },
+};
 
-        Alertify.filterDialog(title).resizeTo('65%', '60%');
-      });
-    },
-  };
-  return FilterDialog;
-});
+module.exports = FilterDialog;
diff --git a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
index c3146a4..e1b977e 100644
--- a/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
+++ b/web/pgadmin/static/js/sqleditor/filter_dialog_model.js
@@ -1,133 +1,132 @@
-define([
-  'sources/gettext', 'underscore', 'sources/pgadmin',
-  'pgadmin.backform', 'pgadmin.backgrid',
-], function(
-  gettext, _, pgAdmin, Backform, Backgrid
-) {
+import gettext from 'sources/gettext';
+import _ from 'underscore';
+import pgAdmin from 'sources/pgadmin';
+import Backgrid from 'pgadmin.backgrid';
+import Backform from 'pgadmin.backform';
 
-  let initModel = function(response) {
+let initModel = function(response) {
 
-    let order_mapping = {
-      'asc': gettext('ASC'),
-      'desc': gettext('DESC'),
-    };
+  let order_mapping = {
+    'asc': gettext('ASC'),
+    'desc': gettext('DESC'),
+  };
 
-    let DataSortingModel = pgAdmin.Browser.DataModel.extend({
-      idAttribute: 'name',
-      defaults: {
-        name: undefined,
-        order: 'asc',
+  let DataSortingModel = pgAdmin.Browser.DataModel.extend({
+    idAttribute: 'name',
+    defaults: {
+      name: undefined,
+      order: 'asc',
+    },
+    schema: [{
+      id: 'name',
+      name: 'name',
+      label: gettext('Column'),
+      cell: 'select2',
+      editable: true,
+      cellHeaderClasses: 'width_percent_60',
+      headerCell: Backgrid.Extension.CustomHeaderCell,
+      disabled: false,
+      control: 'select2',
+      select2: {
+        allowClear: false,
       },
-      schema: [{
-        id: 'name',
-        name: 'name',
-        label: gettext('Column'),
-        cell: 'select2',
-        editable: true,
-        cellHeaderClasses: 'width_percent_60',
-        headerCell: Backgrid.Extension.CustomHeaderCell,
-        disabled: false,
-        control: 'select2',
-        select2: {
-          allowClear: false,
-        },
-        options: function() {
-          return _.map(response.column_list, (obj) => {
-            return {
-              value: obj,
-              label: obj,
-            };
-          });
-        },
+      options: function() {
+        return _.map(response.column_list, (obj) => {
+          return {
+            value: obj,
+            label: obj,
+          };
+        });
       },
-      {
-        id: 'order',
-        name: 'order',
-        label: gettext('Order'),
-        control: 'select2',
-        cell: 'select2',
-        cellHeaderClasses: 'width_percent_40',
-        headerCell: Backgrid.Extension.CustomHeaderCell,
-        editable: true,
-        deps: ['type'],
-        select2: {
-          allowClear: false,
-        },
-        options: function() {
-          return _.map(order_mapping, (val, key) => {
-            return {
-              value: key,
-              label: val,
-            };
-          });
-        },
+    },
+    {
+      id: 'order',
+      name: 'order',
+      label: gettext('Order'),
+      control: 'select2',
+      cell: 'select2',
+      cellHeaderClasses: 'width_percent_40',
+      headerCell: Backgrid.Extension.CustomHeaderCell,
+      editable: true,
+      deps: ['type'],
+      select2: {
+        allowClear: false,
       },
-      ],
-      validate: function() {
-        let msg = null;
-        this.errorModel.clear();
-        if (_.isUndefined(this.get('name')) ||
-          _.isNull(this.get('name')) ||
-          String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
-          msg = gettext('Please select a column.');
-          this.errorModel.set('name', msg);
-          return msg;
-        } else if (_.isUndefined(this.get('order')) ||
-          _.isNull(this.get('order')) ||
-          String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
-          msg = gettext('Please select the order.');
-          this.errorModel.set('order', msg);
-          return msg;
-        }
-        return null;
+      options: function() {
+        return _.map(order_mapping, (val, key) => {
+          return {
+            value: key,
+            label: val,
+          };
+        });
       },
-    });
+    },
+    ],
+    validate: function() {
+      let msg = null;
+      this.errorModel.clear();
+      if (_.isUndefined(this.get('name')) ||
+        _.isNull(this.get('name')) ||
+        String(this.get('name')).replace(/^\s+|\s+$/g, '') == '') {
+        msg = gettext('Please select a column.');
+        this.errorModel.set('name', msg);
+        return msg;
+      } else if (_.isUndefined(this.get('order')) ||
+        _.isNull(this.get('order')) ||
+        String(this.get('order')).replace(/^\s+|\s+$/g, '') == '') {
+        msg = gettext('Please select the order.');
+        this.errorModel.set('order', msg);
+        return msg;
+      }
+      return null;
+    },
+  });
 
-    let FilterCollectionModel = pgAdmin.Browser.DataModel.extend({
-      idAttribute: 'sql',
-      defaults: {
-        sql: response.sql || null,
-      },
-      schema: [{
-        id: 'sql',
-        label: gettext('SQL Filter'),
-        cell: 'string',
-        type: 'text', mode: ['create'],
-        control: Backform.SqlFieldControl.extend({
-          render: function() {
-            let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
-            // We need to set focus on editor after the dialog renders
-            setTimeout(() => {
-              obj.sqlCtrl.focus();
-            }, 1000);
-            return obj;
-          },
-        }),
-        extraClasses:['custom_height_css_class'],
-      },{
-        id: 'data_sorting',
-        name: 'data_sorting',
-        label: gettext('Data Sorting'),
-        model: DataSortingModel,
-        editable: true,
-        type: 'collection',
-        mode: ['create'],
-        control: 'unique-col-collection',
-        uniqueCol: ['name'],
-        canAdd: true,
-        canEdit: false,
-        canDelete: true,
-        visible: true,
-        version_compatible: true,
-      }],
-      validate: function() {
-        return null;
-      },
-    });
+  let FilterCollectionModel = pgAdmin.Browser.DataModel.extend({
+    idAttribute: 'sql',
+    defaults: {
+      sql: response.sql || null,
+    },
+    schema: [{
+      id: 'sql',
+      label: gettext('SQL Filter'),
+      cell: 'string',
+      type: 'text', mode: ['create'],
+      control: Backform.SqlFieldControl.extend({
+        render: function() {
+          let obj = Backform.SqlFieldControl.prototype.render.apply(this, arguments);
+          // We need to set focus on editor after the dialog renders
+          setTimeout(() => {
+            obj.sqlCtrl.focus();
+          }, 1000);
+          return obj;
+        },
+      }),
+      extraClasses:['custom_height_css_class'],
+    },{
+      id: 'data_sorting',
+      name: 'data_sorting',
+      label: gettext('Data Sorting'),
+      model: DataSortingModel,
+      editable: true,
+      type: 'collection',
+      mode: ['create'],
+      control: 'unique-col-collection',
+      uniqueCol: ['name'],
+      canAdd: true,
+      canEdit: false,
+      canDelete: true,
+      visible: true,
+      version_compatible: true,
+    }],
+    validate: function() {
+      return null;
+    },
+  });
+
+  let model = new FilterCollectionModel();
+  return model;
+};
 
-    let model = new FilterCollectionModel();
-    return model;
-  };
 
-  return initModel;
-});
+module.exports = initModel;


^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 17:50                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-04-06 03:43                             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-06 05:44                               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
@ 2018-04-06 09:38                                 ` Dave Page <[email protected]>
  2018-04-06 13:15                                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-04-06 09:38 UTC (permalink / raw)
  To: Murtuza Zabuawala <[email protected]>; +Cc: Joao De Almeida Pereira <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Thanks, applied.

On Fri, Apr 6, 2018 at 6:44 AM, Murtuza Zabuawala <
[email protected]> wrote:

> Hi,
>
> Please find the patch which will change the syntax from requirejs syntax
> to ES6 from previous commit.
>
> --
> Regards,
> Murtuza Zabuawala
> EnterpriseDB: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>
>
> On Fri, Apr 6, 2018 at 9:13 AM, Murtuza Zabuawala <murtuza.zabuawala@
> enterprisedb.com> wrote:
>
>> Hi Joao,
>>
>>
>> On Thu, Apr 5, 2018 at 11:20 PM, Joao De Almeida Pereira <
>> [email protected]> wrote:
>>
>>> Hi Murtuza,
>>> I forgot to mention this in my review........
>>> Do you think we can start using only ES6 instead of keep using requirejs
>>> syntax on new things that we are building?
>>> How much effort do you think it will be?
>>>
>> ​Yes, we can.
>> Let me try to change the code to use ES6 and I'll send patch.​
>>
>>
>>>
>>> Like this patch, as an example, do you think it could have been
>>> implemented without using requirejs?
>>>
>>
>>> Thanks
>>>
>>> On Thu, Apr 5, 2018 at 11:26 AM Dave Page <[email protected]> wrote:
>>>
>>>> Thanks, applied.
>>>>
>>>> On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>> Hi Dave,
>>>>>
>>>>> Please find rebased patch.
>>>>>
>>>>> --
>>>>> Regards,
>>>>> Murtuza Zabuawala
>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>>
>>>>> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>>>>>
>>>>>> Can you rebase this please?
>>>>>>
>>>>>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Dave,
>>>>>>>
>>>>>>> Please find updated patch with following changes,
>>>>>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>>>>>> - Extracted model into separate file
>>>>>>> - Change the colour of filter button from orange to blue.
>>>>>>> - Updated docs and screenshot.
>>>>>>>
>>>>>>> @Joao,
>>>>>>> Could you please provide any reference for learning more about
>>>>>>> jasmine test framework?
>>>>>>> ​
>>>>>>>
>>>>>>> --
>>>>>>> Regards,
>>>>>>> Murtuza Zabuawala
>>>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>
>>>>>>>
>>>>>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>>>>> wrote:
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we
>>>>>>>>>>>>>> are using axios instead of jquery ajax calls and that there is some
>>>>>>>>>>>>>> coverage for the change.
>>>>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and
>>>>>>>>>>>>>> could be improved. Also the DataSorting class could have some other member
>>>>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>>>>> easily tested.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing
>>>>>>>>>>>>>> happened
>>>>>>>>>>>>>> 5 - Right clicked the column header to see if it was there
>>>>>>>>>>>>>> the option, nothing
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> This is the behavior that we were expecting, not to have to
>>>>>>>>>>>>>> open Data View and then press the icon that is not even near the grid in
>>>>>>>>>>>>>> order to sort the column. Is this really the way we want people to use the
>>>>>>>>>>>>>> grid in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>>>>> see fit.
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>>>>> was now being used in order to design and implement features just like
>>>>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> Another reason we can't use that because w
>>>>>>>>>>> e have already occupied that behaviour for selecting entire
>>>>>>>>>>> column
>>>>>>>>>>> ​ when user clicks on header.
>>>>>>>>>>> As Dave suggested, I will be merging it with filter dialog
>>>>>>>>>>> meaning it will be accessible via direct button on toolbar & keyboard
>>>>>>>>>>> shortcut.​
>>>>>>>>>>> ​
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>>>>> ​
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>> Sorry I wasn't clear. My question was more along the lines of, do
>>>>>>>> we know if people are using the filter functionality?  What kind
>>>>>>>> of filters are people using?  What do they like about it? What do they wish
>>>>>>>> they could do above and beyond sorting, etc.
>>>>>>>>
>>>>>>> ​I have not done any data gathering from users so I can't comment on
>>>>>>> your queries.
>>>>>>> ​ but a​
>>>>>>> ​s far as I understood from the feature requests that most of the
>>>>>>> users expect to have functionality which will allow then to sort columns as
>>>>>>> it was in pgAdmin3.​
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>>> -- Rob
>>>>>>>>
>>>>>>>>
>>>>>>>>>
>>>>>>>>>> What I'm suggesting is that we understand how users want to
>>>>>>>>>> interact with their results, be those the results of a query or a table
>>>>>>>>>> view, then we can design something that meets those needs. I agree that
>>>>>>>>>> changing the column selection behavior isn't desirable, however, I also
>>>>>>>>>> feel like providing the best user experience is better than holding onto a
>>>>>>>>>> particular feature implementation.
>>>>>>>>>> ​
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>>> -- Rob
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>> --
>>>>>>>>>>>> Dave Page
>>>>>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>>>>>> Twitter: @pgsnake
>>>>>>>>>>>>
>>>>>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>
>>>>>>
>>>>>> --
>>>>>> Dave Page
>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>> Twitter: @pgsnake
>>>>>>
>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>
>>>>>
>>>>
>>>>
>>>> --
>>>> Dave Page
>>>> Blog: http://pgsnake.blogspot.com
>>>> Twitter: @pgsnake
>>>>
>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>> The Enterprise PostgreSQL Company
>>>>
>>>
>>
>


-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 07:19                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 10:45                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 11:29                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-05 15:25                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-04-05 17:50                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-04-06 03:43                             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-06 05:44                               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-04-06 09:38                                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-04-06 13:15                                   ` Joao De Almeida Pereira <[email protected]>
  0 siblings, 0 replies; 25+ messages in thread

From: Joao De Almeida Pereira @ 2018-04-06 13:15 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Robert Eckhardt <[email protected]>; pgadmin-hackers

Pretty cool, Thanks Murtuza

On Fri, Apr 6, 2018 at 5:38 AM Dave Page <[email protected]> wrote:

> Thanks, applied.
>
> On Fri, Apr 6, 2018 at 6:44 AM, Murtuza Zabuawala <
> [email protected]> wrote:
>
>> Hi,
>>
>> Please find the patch which will change the syntax from requirejs syntax
>> to ES6 from previous commit.
>>
>> --
>> Regards,
>> Murtuza Zabuawala
>> EnterpriseDB: http://www.enterprisedb.com
>> The Enterprise PostgreSQL Company
>>
>>
>> On Fri, Apr 6, 2018 at 9:13 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>> Hi Joao,
>>>
>>>
>>> On Thu, Apr 5, 2018 at 11:20 PM, Joao De Almeida Pereira <
>>> [email protected]> wrote:
>>>
>>>> Hi Murtuza,
>>>> I forgot to mention this in my review........
>>>> Do you think we can start using only ES6 instead of keep using
>>>> requirejs syntax on new things that we are building?
>>>> How much effort do you think it will be?
>>>>
>>> ​Yes, we can.
>>> Let me try to change the code to use ES6 and I'll send patch.​
>>>
>>>
>>>>
>>>> Like this patch, as an example, do you think it could have been
>>>> implemented without using requirejs?
>>>>
>>>
>>>> Thanks
>>>>
>>>> On Thu, Apr 5, 2018 at 11:26 AM Dave Page <[email protected]> wrote:
>>>>
>>>>> Thanks, applied.
>>>>>
>>>>> On Thu, Apr 5, 2018 at 12:29 PM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> Hi Dave,
>>>>>>
>>>>>> Please find rebased patch.
>>>>>>
>>>>>> --
>>>>>> Regards,
>>>>>> Murtuza Zabuawala
>>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>>> The Enterprise PostgreSQL Company
>>>>>>
>>>>>>
>>>>>> On Thu, Apr 5, 2018 at 4:15 PM, Dave Page <[email protected]> wrote:
>>>>>>
>>>>>>> Can you rebase this please?
>>>>>>>
>>>>>>> On Wed, Mar 28, 2018 at 8:19 AM, Murtuza Zabuawala <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Dave,
>>>>>>>>
>>>>>>>> Please find updated patch with following changes,
>>>>>>>> - Combined Filter and Data sorting together same as pgAdmin3.
>>>>>>>> - Extracted model into separate file
>>>>>>>> - Change the colour of filter button from orange to blue.
>>>>>>>> - Updated docs and screenshot.
>>>>>>>>
>>>>>>>> @Joao,
>>>>>>>> Could you please provide any reference for learning more about
>>>>>>>> jasmine test framework?
>>>>>>>> ​
>>>>>>>>
>>>>>>>> --
>>>>>>>> Regards,
>>>>>>>> Murtuza Zabuawala
>>>>>>>> EnterpriseDB: http://www.enterprisedb.com
>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>
>>>>>>>>
>>>>>>>> On Wed, Mar 28, 2018 at 6:07 AM, Robert Eckhardt <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>
>>>>>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>>>>>> wrote:
>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we
>>>>>>>>>>>>>>> are using axios instead of jquery ajax calls and that there is some
>>>>>>>>>>>>>>> coverage for the change.
>>>>>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and
>>>>>>>>>>>>>>> could be improved. Also the DataSorting class could have some other member
>>>>>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>>>>>> easily tested.
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing
>>>>>>>>>>>>>>> happened
>>>>>>>>>>>>>>> 5 - Right clicked the column header to see if it was there
>>>>>>>>>>>>>>> the option, nothing
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>> This is the behavior that we were expecting, not to have to
>>>>>>>>>>>>>>> open Data View and then press the icon that is not even near the grid in
>>>>>>>>>>>>>>> order to sort the column. Is this really the way we want people to use the
>>>>>>>>>>>>>>> grid in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>>>>>
>>>>>>>>>>>>>>
>>>>>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>>>>>
>>>>>>>>>>>>>
>>>>>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>>>>>> see fit.
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>>>>>> was now being used in order to design and implement features just like
>>>>>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>> Another reason we can't use that because w
>>>>>>>>>>>> e have already occupied that behaviour for selecting entire
>>>>>>>>>>>> column
>>>>>>>>>>>> ​ when user clicks on header.
>>>>>>>>>>>> As Dave suggested, I will be merging it with filter dialog
>>>>>>>>>>>> meaning it will be accessible via direct button on toolbar & keyboard
>>>>>>>>>>>> shortcut.​
>>>>>>>>>>>> ​
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>>>>>> ​
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Sorry I wasn't clear. My question was more along the lines of, do
>>>>>>>>> we know if people are using the filter functionality?  What kind
>>>>>>>>> of filters are people using?  What do they like about it? What do they wish
>>>>>>>>> they could do above and beyond sorting, etc.
>>>>>>>>>
>>>>>>>> ​I have not done any data gathering from users so I can't comment
>>>>>>>> on your queries.
>>>>>>>> ​ but a​
>>>>>>>> ​s far as I understood from the feature requests that most of the
>>>>>>>> users expect to have functionality which will allow then to sort columns as
>>>>>>>> it was in pgAdmin3.​
>>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>>> -- Rob
>>>>>>>>>
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> What I'm suggesting is that we understand how users want to
>>>>>>>>>>> interact with their results, be those the results of a query or a table
>>>>>>>>>>> view, then we can design something that meets those needs. I agree that
>>>>>>>>>>> changing the column selection behavior isn't desirable, however, I also
>>>>>>>>>>> feel like providing the best user experience is better than holding onto a
>>>>>>>>>>> particular feature implementation.
>>>>>>>>>>> ​
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>> -- Rob
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>> --
>>>>>>>>>>>>> Dave Page
>>>>>>>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>>>>>>>> Twitter: @pgsnake
>>>>>>>>>>>>>
>>>>>>>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> --
>>>>>>> Dave Page
>>>>>>> Blog: http://pgsnake.blogspot.com
>>>>>>> Twitter: @pgsnake
>>>>>>>
>>>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>>>> The Enterprise PostgreSQL Company
>>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> Dave Page
>>>>> Blog: http://pgsnake.blogspot.com
>>>>> Twitter: @pgsnake
>>>>>
>>>>> EnterpriseDB UK: http://www.enterprisedb.com
>>>>> The Enterprise PostgreSQL Company
>>>>>
>>>>
>>>
>>
>
>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-28 08:12                   ` Dave Page <[email protected]>
  2018-03-28 13:54                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  1 sibling, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-03-28 08:12 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Wed, Mar 28, 2018 at 1:37 AM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
> [email protected]> wrote:
>
>>
>>
>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> Hi Hackers,
>>>>>>>
>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>> for the change.
>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>> tested.
>>>>>>>
>>>>>>>
>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>> 1 - Started pgAdmin
>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>> 3 - Executed a SQL statment
>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>> option, nothing
>>>>>>>
>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>
>>>>>>
>>>>>> Have we considered making the grid behave more like excel or other
>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>> users a more intuitive place to look.
>>>>>>
>>>>>
>>>>> Doing the sorting via header clicks is convenient but very
>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>> The current design allows you to select columns and the sort order as you
>>>>> see fit.
>>>>>
>>>>
>>> Honestly I'm not sold on my idea, I was just proposing an alternative in
>>> an effort to start a discussion about the user experience. Ideally what I'd
>>> like to see, maybe this happened, is some user research. When we initial
>>> worked on refactoring the results grid we made a bunch of changes. One of
>>> the things we intended to do was to follow up to see how people were using
>>> the grid now so that we could better understand how it was now being used
>>> in order to design and implement features just like this. Clearly we
>>> haven't gotten there yet.
>>>
>>>
>>>>
>>>> Another reason we can't use that because w
>>>> e have already occupied that behaviour for selecting entire column
>>>> ​ when user clicks on header.
>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>> ​
>>>>
>>>
>>> How are users currently interacting with that filter dialog?
>>>
>>
>> ​By clicking on the toolbar button as well as keyboard shortcut.
>> ​
>>
>>
>>
>>
>
> Sorry I wasn't clear. My question was more along the lines of, do we know
> if people are using the filter functionality?  What kind of filters are
> people using?  What do they like about it? What do they wish they could do
> above and beyond sorting, etc.
>

Yes, they are, based on the fact we've had issues reported in the past. We
have no idea how they are using it.

Sorting is a separate feature that is often requested. The only reason it's
connected here is that both functionalities in pgAdmin 3 were managed
through the same dialogue which based on lack of complaints from users,
generally worked for them. I do know that the proposed "click on headers"
approach will not work for me, as I have multi-part keys in databases which
I like to sort by in specific ways.

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 08:12                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-03-28 13:54                     ` Robert Eckhardt <[email protected]>
  2018-03-28 15:20                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Robert Eckhardt @ 2018-03-28 13:54 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Wed, Mar 28, 2018 at 4:12 AM, Dave Page <[email protected]> wrote:

>
>
> On Wed, Mar 28, 2018 at 1:37 AM, Robert Eckhardt <[email protected]>
> wrote:
>
>>
>>
>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>> [email protected]> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> Hi Hackers,
>>>>>>>>
>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>> for the change.
>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>> tested.
>>>>>>>>
>>>>>>>>
>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>> 1 - Started pgAdmin
>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>> 3 - Executed a SQL statment
>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>> option, nothing
>>>>>>>>
>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>
>>>>>>>
>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>> users a more intuitive place to look.
>>>>>>>
>>>>>>
>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>> The current design allows you to select columns and the sort order as you
>>>>>> see fit.
>>>>>>
>>>>>
>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>> in an effort to start a discussion about the user experience. Ideally what
>>>> I'd like to see, maybe this happened, is some user research. When we
>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>> One of the things we intended to do was to follow up to see how people were
>>>> using the grid now so that we could better understand how it was now being
>>>> used in order to design and implement features just like this. Clearly we
>>>> haven't gotten there yet.
>>>>
>>>>
>>>>>
>>>>> Another reason we can't use that because w
>>>>> e have already occupied that behaviour for selecting entire column
>>>>> ​ when user clicks on header.
>>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>> ​
>>>>>
>>>>
>>>> How are users currently interacting with that filter dialog?
>>>>
>>>
>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>> ​
>>>
>>>
>>>
>>>
>>
>> Sorry I wasn't clear. My question was more along the lines of, do we
>> know if people are using the filter functionality?  What kind of filters
>> are people using?  What do they like about it? What do they wish they could
>> do above and beyond sorting, etc.
>>
>
> Yes, they are, based on the fact we've had issues reported in the past. We
> have no idea how they are using it.
>
> Sorting is a separate feature that is often requested. The only reason
> it's connected here is that both functionalities in pgAdmin 3 were managed
> through the same dialogue which based on lack of complaints from users,
> generally worked for them. I do know that the proposed "click on headers"
> approach will not work for me, as I have multi-part keys in databases which
> I like to sort by in specific ways.
>
>
As far as the 'click on header' is concerned I think Murtuza's objection is
very valid. I think my overall point is that I believe that sorting and
filtering is a legitimate pain but it is a pain I don't fully understand.
The solution as presented certainly works it just doesn't feel nice. My
concern is that it won't be clear to users what they should do or expect
since the UX is unique to pgAdmin. Less of a concern is that all of these
changes are only happening with the 'view data' section of the code and I
would assume that people who have queried the DB will also have the need to
sort and filter without necessarily rewriting their SQL.

-- Rob


> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 08:12                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-28 13:54                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-28 15:20                       ` Dave Page <[email protected]>
  2018-03-28 15:28                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Dave Page @ 2018-03-28 15:20 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Wed, Mar 28, 2018 at 2:54 PM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Wed, Mar 28, 2018 at 4:12 AM, Dave Page <[email protected]> wrote:
>
>>
>>
>> On Wed, Mar 28, 2018 at 1:37 AM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>> [email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>> Hi Hackers,
>>>>>>>>>
>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>> for the change.
>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>>> tested.
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>> option, nothing
>>>>>>>>>
>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>
>>>>>>>>
>>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>>> users a more intuitive place to look.
>>>>>>>>
>>>>>>>
>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>> see fit.
>>>>>>>
>>>>>>
>>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>>> in an effort to start a discussion about the user experience. Ideally what
>>>>> I'd like to see, maybe this happened, is some user research. When we
>>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>>> One of the things we intended to do was to follow up to see how people were
>>>>> using the grid now so that we could better understand how it was now being
>>>>> used in order to design and implement features just like this. Clearly we
>>>>> haven't gotten there yet.
>>>>>
>>>>>
>>>>>>
>>>>>> Another reason we can't use that because w
>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>> ​ when user clicks on header.
>>>>>> As Dave suggested, I will be merging it with filter dialog meaning it
>>>>>> will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>> ​
>>>>>>
>>>>>
>>>>> How are users currently interacting with that filter dialog?
>>>>>
>>>>
>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>> ​
>>>>
>>>>
>>>>
>>>>
>>>
>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>> know if people are using the filter functionality?  What kind of
>>> filters are people using?  What do they like about it? What do they wish
>>> they could do above and beyond sorting, etc.
>>>
>>
>> Yes, they are, based on the fact we've had issues reported in the past.
>> We have no idea how they are using it.
>>
>> Sorting is a separate feature that is often requested. The only reason
>> it's connected here is that both functionalities in pgAdmin 3 were managed
>> through the same dialogue which based on lack of complaints from users,
>> generally worked for them. I do know that the proposed "click on headers"
>> approach will not work for me, as I have multi-part keys in databases which
>> I like to sort by in specific ways.
>>
>>
> As far as the 'click on header' is concerned I think Murtuza's objection
> is very valid. I think my overall point is that I believe that sorting and
> filtering is a legitimate pain but it is a pain I don't fully understand.
> The solution as presented certainly works it just doesn't feel nice. My
> concern is that it won't be clear to users what they should do or expect
> since the UX is unique to pgAdmin.
>

Right now my concern is getting us back to feature parity in this area with
pgAdmin 3, as users are complaining. Longer term we can look at further
improvements or redesigns as resources allow.



> Less of a concern is that all of these changes are only happening with the
> 'view data' section of the code and I would assume that people who have
> queried the DB will also have the need to sort and filter without
> necessarily rewriting their SQL.
>

That doesn't concern me - there are already very distinct differences in
the two modes for the tool. If/when we have a suitable parser on the front
end we'll be able to dynamically switch between read-write modes and update
sorting criteria in hand-crafted SQL. Until that large amount of work is
done, we have two distinct modes, one with generated SQL and writeable
data, the other with hand-crafted SQL and read-only data.

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 08:12                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-28 13:54                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 15:20                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
@ 2018-03-28 15:28                         ` Robert Eckhardt <[email protected]>
  2018-03-28 15:30                           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  0 siblings, 1 reply; 25+ messages in thread

From: Robert Eckhardt @ 2018-03-28 15:28 UTC (permalink / raw)
  To: Dave Page <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Wed, Mar 28, 2018 at 11:20 AM, Dave Page <[email protected]> wrote:

>
>
> On Wed, Mar 28, 2018 at 2:54 PM, Robert Eckhardt <[email protected]>
> wrote:
>
>>
>>
>> On Wed, Mar 28, 2018 at 4:12 AM, Dave Page <[email protected]> wrote:
>>
>>>
>>>
>>> On Wed, Mar 28, 2018 at 1:37 AM, Robert Eckhardt <[email protected]>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>> [email protected]> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <[email protected]
>>>>> > wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>> wrote:
>>>>>>>
>>>>>>>>
>>>>>>>>
>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>> [email protected]> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>> Hi Hackers,
>>>>>>>>>>
>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>>> for the change.
>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could be
>>>>>>>>>> improved. Also the DataSorting class could have some other member functions
>>>>>>>>>> like the model validation could be extracted out so that it is easily
>>>>>>>>>> tested.
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>> option, nothing
>>>>>>>>>>
>>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Have we considered making the grid behave more like excel or other
>>>>>>>>> grids? I think that having the ascending and descending inside the column
>>>>>>>>> header, we could similarly provide filtering. Something that would give
>>>>>>>>> users a more intuitive place to look.
>>>>>>>>>
>>>>>>>>
>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>> see fit.
>>>>>>>>
>>>>>>>
>>>>>> Honestly I'm not sold on my idea, I was just proposing an alternative
>>>>>> in an effort to start a discussion about the user experience. Ideally what
>>>>>> I'd like to see, maybe this happened, is some user research. When we
>>>>>> initial worked on refactoring the results grid we made a bunch of changes.
>>>>>> One of the things we intended to do was to follow up to see how people were
>>>>>> using the grid now so that we could better understand how it was now being
>>>>>> used in order to design and implement features just like this. Clearly we
>>>>>> haven't gotten there yet.
>>>>>>
>>>>>>
>>>>>>>
>>>>>>> Another reason we can't use that because w
>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>> ​ when user clicks on header.
>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>> ​
>>>>>>>
>>>>>>
>>>>>> How are users currently interacting with that filter dialog?
>>>>>>
>>>>>
>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>> ​
>>>>>
>>>>>
>>>>>
>>>>>
>>>>
>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>> know if people are using the filter functionality?  What kind of
>>>> filters are people using?  What do they like about it? What do they wish
>>>> they could do above and beyond sorting, etc.
>>>>
>>>
>>> Yes, they are, based on the fact we've had issues reported in the past.
>>> We have no idea how they are using it.
>>>
>>> Sorting is a separate feature that is often requested. The only reason
>>> it's connected here is that both functionalities in pgAdmin 3 were managed
>>> through the same dialogue which based on lack of complaints from users,
>>> generally worked for them. I do know that the proposed "click on headers"
>>> approach will not work for me, as I have multi-part keys in databases which
>>> I like to sort by in specific ways.
>>>
>>>
>> As far as the 'click on header' is concerned I think Murtuza's objection
>> is very valid. I think my overall point is that I believe that sorting and
>> filtering is a legitimate pain but it is a pain I don't fully understand.
>> The solution as presented certainly works it just doesn't feel nice. My
>> concern is that it won't be clear to users what they should do or expect
>> since the UX is unique to pgAdmin.
>>
>
> Right now my concern is getting us back to feature parity in this area
> with pgAdmin 3, as users are complaining. Longer term we can look at
> further improvements or redesigns as resources allow.
>

Totally fair.


>
>
>
>> Less of a concern is that all of these changes are only happening with
>> the 'view data' section of the code and I would assume that people who have
>> queried the DB will also have the need to sort and filter without
>> necessarily rewriting their SQL.
>>
>
> That doesn't concern me - there are already very distinct differences in
> the two modes for the tool. If/when we have a suitable parser on the front
> end we'll be able to dynamically switch between read-write modes and update
> sorting criteria in hand-crafted SQL. Until that large amount of work is
> done, we have two distinct modes, one with generated SQL and writeable
> data, the other with hand-crafted SQL and read-only data.
>

Also totally fair. As an aside, we are about to spin up some user
interviews for the large number of objects but during those interviews we
were planning to add some follow up questions on the work we did on the
data grid. If pushing this as is until it can be prioritized is something
that is needed to get the heat off of you I'm ok with that.

I do think we will spend a little time with users so I can better
understand the issues being faced and we will absolutely share that
feedback here.

-- Rob


>
> --
> Dave Page
> Blog: http://pgsnake.blogspot.com
> Twitter: @pgsnake
>
> EnterpriseDB UK: http://www.enterprisedb.com
> The Enterprise PostgreSQL Company
>


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread

* Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode
  2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 12:22 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-26 16:13   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-26 18:07     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Joao De Almeida Pereira <[email protected]>
  2018-03-26 20:26       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 09:43         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-27 10:25           ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-27 13:36             ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-27 13:54               ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
  2018-03-28 00:37                 ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 08:12                   ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-28 13:54                     ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
  2018-03-28 15:20                       ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Dave Page <[email protected]>
  2018-03-28 15:28                         ` Re: [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Robert Eckhardt <[email protected]>
@ 2018-03-28 15:30                           ` Dave Page <[email protected]>
  0 siblings, 0 replies; 25+ messages in thread

From: Dave Page @ 2018-03-28 15:30 UTC (permalink / raw)
  To: Robert Eckhardt <[email protected]>; +Cc: Murtuza Zabuawala <[email protected]>; Joao De Almeida Pereira <[email protected]>; pgadmin-hackers

On Wed, Mar 28, 2018 at 4:28 PM, Robert Eckhardt <[email protected]>
wrote:

>
>
> On Wed, Mar 28, 2018 at 11:20 AM, Dave Page <[email protected]> wrote:
>
>>
>>
>> On Wed, Mar 28, 2018 at 2:54 PM, Robert Eckhardt <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On Wed, Mar 28, 2018 at 4:12 AM, Dave Page <[email protected]> wrote:
>>>
>>>>
>>>>
>>>> On Wed, Mar 28, 2018 at 1:37 AM, Robert Eckhardt <[email protected]>
>>>> wrote:
>>>>
>>>>>
>>>>>
>>>>> On Tue, Mar 27, 2018 at 9:54 AM, Murtuza Zabuawala <
>>>>> [email protected]> wrote:
>>>>>
>>>>>>
>>>>>>
>>>>>> On Tue, Mar 27, 2018 at 7:06 PM, Robert Eckhardt <
>>>>>> [email protected]> wrote:
>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Tue, Mar 27, 2018 at 6:25 AM, Murtuza Zabuawala <
>>>>>>> [email protected]> wrote:
>>>>>>>
>>>>>>>> On Tue, Mar 27, 2018 at 3:13 PM, Dave Page <[email protected]>
>>>>>>>> wrote:
>>>>>>>>
>>>>>>>>>
>>>>>>>>>
>>>>>>>>> On Mon, Mar 26, 2018 at 9:26 PM, Robert Eckhardt <
>>>>>>>>> [email protected]> wrote:
>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> On Mon, Mar 26, 2018 at 2:07 PM, Joao De Almeida Pereira <
>>>>>>>>>> [email protected]> wrote:
>>>>>>>>>>
>>>>>>>>>>> Hi Hackers,
>>>>>>>>>>>
>>>>>>>>>>> @Murtuza: The patch codewise looks good. Nice to see that we are
>>>>>>>>>>> using axios instead of jquery ajax calls and that there is some coverage
>>>>>>>>>>> for the change.
>>>>>>>>>>> Nevertheless the Javascript testing looks a bit slim and could
>>>>>>>>>>> be improved. Also the DataSorting class could have some other member
>>>>>>>>>>> functions like the model validation could be extracted out so that it is
>>>>>>>>>>> easily tested.
>>>>>>>>>>>
>>>>>>>>>>>
>>>>>>>>>>> @Hackers: This was how we tried to test this feature:
>>>>>>>>>>> 1 - Started pgAdmin
>>>>>>>>>>> 2 - Opened the query tool for a specific server
>>>>>>>>>>> 3 - Executed a SQL statment
>>>>>>>>>>> 4 - Pressed the column header to try to order, nothing happened
>>>>>>>>>>> 5 - Right clicked the column header to see if it was there the
>>>>>>>>>>> option, nothing
>>>>>>>>>>>
>>>>>>>>>>> This is the behavior that we were expecting, not to have to open
>>>>>>>>>>> Data View and then press the icon that is not even near the grid in order
>>>>>>>>>>> to sort the column. Is this really the way we want people to use the grid
>>>>>>>>>>> in pgAdmin? Should it be more intuitive?
>>>>>>>>>>>
>>>>>>>>>>
>>>>>>>>>> Have we considered making the grid behave more like excel or
>>>>>>>>>> other grids? I think that having the ascending and descending inside the
>>>>>>>>>> column header, we could similarly provide filtering. Something that would
>>>>>>>>>> give users a more intuitive place to look.
>>>>>>>>>>
>>>>>>>>>
>>>>>>>>> Doing the sorting via header clicks is convenient but very
>>>>>>>>> restrictive. How do you specify multiple columns to sort by for example?
>>>>>>>>> The current design allows you to select columns and the sort order as you
>>>>>>>>> see fit.
>>>>>>>>>
>>>>>>>>
>>>>>>> Honestly I'm not sold on my idea, I was just proposing an
>>>>>>> alternative in an effort to start a discussion about the user experience.
>>>>>>> Ideally what I'd like to see, maybe this happened, is some user research.
>>>>>>> When we initial worked on refactoring the results grid we made a bunch of
>>>>>>> changes. One of the things we intended to do was to follow up to see how
>>>>>>> people were using the grid now so that we could better understand how it
>>>>>>> was now being used in order to design and implement features just like
>>>>>>> this. Clearly we haven't gotten there yet.
>>>>>>>
>>>>>>>
>>>>>>>>
>>>>>>>> Another reason we can't use that because w
>>>>>>>> e have already occupied that behaviour for selecting entire column
>>>>>>>> ​ when user clicks on header.
>>>>>>>> As Dave suggested, I will be merging it with filter dialog meaning
>>>>>>>> it will be accessible via direct button on toolbar & keyboard shortcut.​
>>>>>>>> ​
>>>>>>>>
>>>>>>>
>>>>>>> How are users currently interacting with that filter dialog?
>>>>>>>
>>>>>>
>>>>>> ​By clicking on the toolbar button as well as keyboard shortcut.
>>>>>> ​
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>
>>>>> Sorry I wasn't clear. My question was more along the lines of, do we
>>>>> know if people are using the filter functionality?  What kind of
>>>>> filters are people using?  What do they like about it? What do they wish
>>>>> they could do above and beyond sorting, etc.
>>>>>
>>>>
>>>> Yes, they are, based on the fact we've had issues reported in the past.
>>>> We have no idea how they are using it.
>>>>
>>>> Sorting is a separate feature that is often requested. The only reason
>>>> it's connected here is that both functionalities in pgAdmin 3 were managed
>>>> through the same dialogue which based on lack of complaints from users,
>>>> generally worked for them. I do know that the proposed "click on headers"
>>>> approach will not work for me, as I have multi-part keys in databases which
>>>> I like to sort by in specific ways.
>>>>
>>>>
>>> As far as the 'click on header' is concerned I think Murtuza's objection
>>> is very valid. I think my overall point is that I believe that sorting and
>>> filtering is a legitimate pain but it is a pain I don't fully understand.
>>> The solution as presented certainly works it just doesn't feel nice. My
>>> concern is that it won't be clear to users what they should do or expect
>>> since the UX is unique to pgAdmin.
>>>
>>
>> Right now my concern is getting us back to feature parity in this area
>> with pgAdmin 3, as users are complaining. Longer term we can look at
>> further improvements or redesigns as resources allow.
>>
>
> Totally fair.
>
>
>>
>>
>>
>>> Less of a concern is that all of these changes are only happening with
>>> the 'view data' section of the code and I would assume that people who have
>>> queried the DB will also have the need to sort and filter without
>>> necessarily rewriting their SQL.
>>>
>>
>> That doesn't concern me - there are already very distinct differences in
>> the two modes for the tool. If/when we have a suitable parser on the front
>> end we'll be able to dynamically switch between read-write modes and update
>> sorting criteria in hand-crafted SQL. Until that large amount of work is
>> done, we have two distinct modes, one with generated SQL and writeable
>> data, the other with hand-crafted SQL and read-only data.
>>
>
> Also totally fair. As an aside, we are about to spin up some user
> interviews for the large number of objects but during those interviews we
> were planning to add some follow up questions on the work we did on the
> data grid. If pushing this as is until it can be prioritized is something
> that is needed to get the heat off of you I'm ok with that.
>
> I do think we will spend a little time with users so I can better
> understand the issues being faced and we will absolutely share that
> feedback here.
>

That will certainly be helpful, thanks.

-- 
Dave Page
Blog: http://pgsnake.blogspot.com
Twitter: @pgsnake

EnterpriseDB UK: http://www.enterprisedb.com
The Enterprise PostgreSQL Company


Attachments:

  [image/png] image.png (87.7K, 3-image.png)
  download | view image

^ permalink  raw  reply  [nested|flat] 25+ messages in thread


end of thread, other threads:[~2018-04-06 13:15 UTC | newest]

Thread overview: 25+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2018-03-25 18:13 [pgAdmin4][RM#3055] Allow user to sort the data in View data mode Murtuza Zabuawala <[email protected]>
2018-03-26 12:22 ` Dave Page <[email protected]>
2018-03-26 16:13   ` Murtuza Zabuawala <[email protected]>
2018-03-26 18:07     ` Joao De Almeida Pereira <[email protected]>
2018-03-26 20:26       ` Robert Eckhardt <[email protected]>
2018-03-27 09:43         ` Dave Page <[email protected]>
2018-03-27 10:25           ` Murtuza Zabuawala <[email protected]>
2018-03-27 13:36             ` Robert Eckhardt <[email protected]>
2018-03-27 13:54               ` Murtuza Zabuawala <[email protected]>
2018-03-28 00:37                 ` Robert Eckhardt <[email protected]>
2018-03-28 07:19                   ` Murtuza Zabuawala <[email protected]>
2018-04-05 10:45                     ` Dave Page <[email protected]>
2018-04-05 11:29                       ` Murtuza Zabuawala <[email protected]>
2018-04-05 13:25                         ` Joao De Almeida Pereira <[email protected]>
2018-04-05 15:25                         ` Dave Page <[email protected]>
2018-04-05 17:50                           ` Joao De Almeida Pereira <[email protected]>
2018-04-06 03:43                             ` Murtuza Zabuawala <[email protected]>
2018-04-06 05:44                               ` Murtuza Zabuawala <[email protected]>
2018-04-06 09:38                                 ` Dave Page <[email protected]>
2018-04-06 13:15                                   ` Joao De Almeida Pereira <[email protected]>
2018-03-28 08:12                   ` Dave Page <[email protected]>
2018-03-28 13:54                     ` Robert Eckhardt <[email protected]>
2018-03-28 15:20                       ` Dave Page <[email protected]>
2018-03-28 15:28                         ` Robert Eckhardt <[email protected]>
2018-03-28 15:30                           ` Dave Page <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox