diff --git a/docs/en_US/getting_started.rst b/docs/en_US/getting_started.rst index 69b28361b..bc11c02b8 100644 --- a/docs/en_US/getting_started.rst +++ b/docs/en_US/getting_started.rst @@ -74,6 +74,7 @@ Contents: browser pgadmin_menu_bar + pgadmin_toolbar pgadmin_tabbed_browser pgadmin_tree_control preferences diff --git a/docs/en_US/images/main_left_pane.png b/docs/en_US/images/main_left_pane.png index e88596b86..a6f87abed 100644 Binary files a/docs/en_US/images/main_left_pane.png and b/docs/en_US/images/main_left_pane.png differ diff --git a/docs/en_US/images/pgadmin_toolbar.png b/docs/en_US/images/pgadmin_toolbar.png new file mode 100644 index 000000000..11913dd04 Binary files /dev/null and b/docs/en_US/images/pgadmin_toolbar.png differ diff --git a/docs/en_US/pgadmin_toolbar.rst b/docs/en_US/pgadmin_toolbar.rst new file mode 100644 index 000000000..295408c82 --- /dev/null +++ b/docs/en_US/pgadmin_toolbar.rst @@ -0,0 +1,16 @@ +.. _pgadmin_toolbar: + +************************* +`pgAdmin Toolbar`:index: +************************* + +The pgAdmin toolbar provides shortcut buttons for frequently used features like View Data and the Query Tool +which are most frequently used in pgAdmin. +This toolbar is visible on the Browser panel. Buttons get enabled/disabled based on the selected browser node. + +.. image:: /images/pgadmin_toolbar.png + :alt: pgAdmin Toolbar + +* Use the :ref:`Query Tool ` button to open the Query Tool in the current database context. +* Use the :ref:`View Data ` button to view/edit the data stored in a selected table. +* Use the :ref:`Filtered Rows ` button to access the Data Filter popup to apply a filter to a set of data for viewing/editing. \ No newline at end of file diff --git a/docs/en_US/release_notes_3_4.rst b/docs/en_US/release_notes_3_4.rst index 88f411a8e..9f7ae68b0 100644 --- a/docs/en_US/release_notes_3_4.rst +++ b/docs/en_US/release_notes_3_4.rst @@ -12,6 +12,7 @@ Features | `Feature #2927 `_ - Move all CSS into SCSS files for consistency and ease of colour maintenance etc. | `Feature #3514 `_ - Add optional data point markers and mouse-over tooltips to display values on graphs. +| `Feature #3564 `_ - Add shortcuts for View Data and the Query tool to the Browser header bar. Bug fixes ********* diff --git a/web/package.json b/web/package.json index 334db0ccb..1f83c697e 100644 --- a/web/package.json +++ b/web/package.json @@ -94,7 +94,7 @@ "underscore": "^1.8.3", "underscore.string": "^3.3.4", "watchify": "~3.9.0", - "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker", + "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#5fbdf05a6757464d2fa2c2b017322c97688b2105", "wkx": "^0.4.5" }, "scripts": { diff --git a/web/pgadmin/browser/static/css/browser.css b/web/pgadmin/browser/static/css/browser.css index 011c6334a..b9694c4c3 100644 --- a/web/pgadmin/browser/static/css/browser.css +++ b/web/pgadmin/browser/static/css/browser.css @@ -60,3 +60,18 @@ .pgadmin_header_logo { cursor: default; } + +.pg-toolbar-btn { + width: 25px !important; + height: 23px !important; + border: 1px solid !important; + background-color: #fff !important; + font-size: 14px !important; + color: #333 !important; + border-color: #adadad !important; +} + +.pg-toolbar-btn:hover { + border: 1px solid $color-gray-light !important; + background-color: #e6e6e6 !important; +} diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js index b955c592f..ee02504df 100644 --- a/web/pgadmin/browser/static/js/browser.js +++ b/web/pgadmin/browser/static/js/browser.js @@ -2,7 +2,7 @@ define('pgadmin.browser', [ 'sources/tree/tree', 'sources/gettext', 'sources/url_for', 'require', 'jquery', 'underscore', 'underscore.string', 'bootstrap', 'sources/pgadmin', 'pgadmin.alertifyjs', 'bundled_codemirror', - 'sources/check_node_visibility', 'pgadmin.browser.utils', 'wcdocker', + 'sources/check_node_visibility', './toolbar', 'pgadmin.browser.utils', 'wcdocker', 'jquery.contextmenu', 'jquery.aciplugin', 'jquery.acitree', 'pgadmin.browser.preferences', 'pgadmin.browser.messages', 'pgadmin.browser.menu', 'pgadmin.browser.panel', @@ -13,7 +13,7 @@ define('pgadmin.browser', [ ], function( tree, gettext, url_for, require, $, _, S, Bootstrap, pgAdmin, Alertify, - codemirror, checkNodeVisibility + codemirror, checkNodeVisibility, toolBar ) { window.jQuery = window.$ = $; // Some scripts do export their object in the window only. @@ -119,6 +119,9 @@ define('pgadmin.browser', [ isPrivate: true, icon: 'fa fa-binoculars', content: '
', + onCreate: function(panel) { + toolBar.initializeToolbar(panel, wcDocker); + }, }), // Properties of the object node 'properties': new pgAdmin.Browser.Panel({ @@ -289,6 +292,9 @@ define('pgadmin.browser', [ } }; + toolBar.enable(gettext('View Data'), false); + toolBar.enable(gettext('Filtered Rows'), false); + // All menus from the object menus (except the create drop-down // menu) needs to be removed. $obj_mnu.empty(); diff --git a/web/pgadmin/browser/static/js/toolbar.js b/web/pgadmin/browser/static/js/toolbar.js new file mode 100644 index 000000000..a42e9ade0 --- /dev/null +++ b/web/pgadmin/browser/static/js/toolbar.js @@ -0,0 +1,90 @@ +import gettext from 'sources/gettext'; +import _ from 'underscore'; +import pgAdmin from 'sources/pgadmin'; + +let _toolbarButtons = {}; +let _browserPanel = null; + +// Default Tool Bar Buttons. +let _defaultToolBarButtons = [ + { + label: gettext('Filtered Rows'), + btnClass: 'fa fa-filter', + text: '', + toggled: false, + toggleClass: '', + parentClass: 'pg-toolbar-btn', + enabled: false, + }, + { + label: gettext('View Data'), + btnClass: 'fa fa-table', + text: '', + toggled: false, + toggleClass: '', + parentClass: 'pg-toolbar-btn', + enabled: false, + }, + { + label: gettext('Query Tool'), + btnClass: 'fa fa-bolt', + text: '', + toggled: false, + toggleClass: '', + parentClass: 'pg-toolbar-btn', + enabled: false, + }, +]; + +// Place holder for non default tool bar buttons. +let _otherToolbarButtons = []; + +// This function is used to add button into the browser panel. +function registerToolBarButton(btn) { + if (!(btn.label in _toolbarButtons)) { + _browserPanel.addButton( + btn.label, btn.btnClass, btn.text, btn.label, btn.toggled, + btn.toggleClass, btn.parentClass, btn.enabled + ); + + _toolbarButtons[btn.label] = btn; + } +} + +// This function is used to add tool bar button and +// listen on the button event. +export function initializeToolbar(panel, wcDocker) { + _browserPanel = panel; + + // Iterate through default tool bar buttons and add them into the + // browser panel. + _.each(_defaultToolBarButtons, (btn) => { + registerToolBarButton(btn); + }); + + // Iterate through other tool bar buttons and add them into the + // browser panel. + _.each(_otherToolbarButtons, (btn) => { + registerToolBarButton(btn); + }); + + // Listen on button click event. + panel.on(wcDocker.EVENT.BUTTON, function(data) { + if ('name' in data && data.name === 'Query Tool') + pgAdmin.DataGrid.show_query_tool('', pgAdmin.Browser.tree.selected()); + else if ('name' in data && data.name === 'View Data') + pgAdmin.DataGrid.show_data_grid({mnuid: 1}, pgAdmin.Browser.tree.selected()); + else if ('name' in data && data.name === 'Filtered Rows') + pgAdmin.DataGrid.show_filtered_row({mnuid: 4}, pgAdmin.Browser.tree.selected()); + }); +} + +// This function is used to enable/disable the specific button +// based on their label. +export function enable(label, enable) { + if (label in _toolbarButtons) { + _browserPanel.buttonEnable(label, enable); + } else { + console.warn('Developer warning: No tool button found with label: ' + label); + } +} diff --git a/web/pgadmin/feature_tests/browser_tool_bar_test.py b/web/pgadmin/feature_tests/browser_tool_bar_test.py new file mode 100644 index 000000000..ca89f9d54 --- /dev/null +++ b/web/pgadmin/feature_tests/browser_tool_bar_test.py @@ -0,0 +1,115 @@ +########################################################################## +# +# pgAdmin 4 - PostgreSQL Tools +# +# Copyright (C) 2013 - 2018, The pgAdmin Development Team +# This software is released under the PostgreSQL Licence +# +########################################################################## + +from __future__ import print_function +import sys +import random +import time + +from regression.python_test_utils import test_utils +from regression.feature_utils.base_feature_test import BaseFeatureTest +from selenium.common.exceptions import TimeoutException, \ + StaleElementReferenceException + + +class BrowserToolBarFeatureTest(BaseFeatureTest): + """ + This feature test will test the tool bar on Browser panel. + """ + + scenarios = [ + ("Browser tool bar feature test", dict()) + ] + + test_table_name = "" + + def before(self): + self.page.wait_for_spinner_to_disappear() + self.page.add_server(self.server) + self.test_table_name = "test_table" + str(random.randint(1000, 3000)) + test_utils.create_table(self.server, self.test_db, + self.test_table_name) + + def runTest(self): + # Check for query tool button + print("\nQuery Tool ToolBar Button ", + file=sys.stderr, end="") + self.test_query_tool_button() + print("OK.", file=sys.stderr) + + # Check for view data button + print("\nView Data ToolBar Button ", + file=sys.stderr, end="") + self.test_view_data_tool_button() + print("OK.", file=sys.stderr) + + # Check for filtered rows button + print("\nFiltered Rows ToolBar Button ", + file=sys.stderr, end="") + self.test_filtered_rows_tool_button() + print("OK.", file=sys.stderr) + + def after(self): + self.page.remove_server(self.server) + + def _locate_database_tree_node(self): + self.page.toggle_open_tree_item(self.server['name']) + self.page.toggle_open_tree_item('Databases') + self.page.toggle_open_tree_item(self.test_db) + + def test_query_tool_button(self): + self._locate_database_tree_node() + + retry_count = 0 + while retry_count < 5: + try: + self.page.find_by_xpath( + "//*[contains(@class,'pg-toolbar-btn') and " + "contains(@title, 'Query Tool')]").click() + break + except StaleElementReferenceException: + retry_count += 1 + + time.sleep(0.5) + self.page.find_by_xpath("//*[contains(@class,'wcTabIcon fa fa-bolt')]") + + def test_view_data_tool_button(self): + self.page.toggle_open_tree_item('Schemas') + self.page.toggle_open_tree_item('public') + self.page.toggle_open_tree_item('Tables') + self.page.select_tree_item(self.test_table_name) + + retry_count = 0 + while retry_count < 5: + try: + self.page.find_by_xpath( + "//*[contains(@class,'pg-toolbar-btn') and " + "contains(@title, 'View Data')]").click() + break + except StaleElementReferenceException: + retry_count += 1 + + time.sleep(0.5) + self.page.find_by_xpath("//*[contains(@class,'wcTabIcon fa fa-bolt')]") + + def test_filtered_rows_tool_button(self): + retry_count = 0 + while retry_count < 5: + try: + self.page.find_by_xpath( + "//*[contains(@class,'pg-toolbar-btn') and " + "contains(@title, 'Filtered Rows')]").click() + break + except StaleElementReferenceException: + retry_count += 1 + + time.sleep(0.5) + self.page.find_by_xpath("//*[contains(@class, 'ajs-header') and " + "contains(., 'Data Filter')]") + self.page.click_modal('Cancel') diff --git a/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py b/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py index a5f033ed8..563ac0b71 100644 --- a/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py +++ b/web/pgadmin/feature_tests/query_tool_auto_complete_tests.py @@ -13,8 +13,6 @@ import random from selenium.webdriver import ActionChains from selenium.webdriver.common.keys import Keys -from selenium.webdriver.support.ui import WebDriverWait -from selenium.webdriver.support import expected_conditions as EC from regression.python_test_utils import test_utils from regression.feature_utils.base_feature_test import BaseFeatureTest diff --git a/web/pgadmin/static/scss/_webcabin.overrides.scss b/web/pgadmin/static/scss/_webcabin.overrides.scss index 3d2c8b164..d263b6b88 100644 --- a/web/pgadmin/static/scss/_webcabin.overrides.scss +++ b/web/pgadmin/static/scss/_webcabin.overrides.scss @@ -43,6 +43,11 @@ margin-right: 2px; } +.wcFrameButton.disabled { + pointer-events: none; + opacity: 0.4; +} + .wcFrameMiniButton { width: 8px; } @@ -289,7 +294,10 @@ ul.dropdown-menu > li.menu-item > a > i.wcTabIcon { width: 14px; } -.wcFrameButton .fa.fa-close { +.wcFrameButton .fa.fa-close, +.wcFrameButton .fa.fa-filter, +.wcFrameButton .fa.fa-table, +.wcFrameButton .fa.fa-bolt { margin-top: 3.5px; padding-left: 1px; } diff --git a/web/pgadmin/tools/datagrid/static/js/datagrid.js b/web/pgadmin/tools/datagrid/static/js/datagrid.js index 28d42b1c7..6b386fde7 100644 --- a/web/pgadmin/tools/datagrid/static/js/datagrid.js +++ b/web/pgadmin/tools/datagrid/static/js/datagrid.js @@ -4,11 +4,11 @@ define('pgadmin.datagrid', [ 'sources/sqleditor_utils', 'backbone', 'tools/datagrid/static/js/show_data', 'tools/datagrid/static/js/get_panel_title', - 'tools/datagrid/static/js/show_query_tool', + 'tools/datagrid/static/js/show_query_tool', 'pgadmin.browser.toolbar', 'wcdocker', ], function( gettext, url_for, $, _, alertify, pgAdmin, codemirror, sqlEditorUtils, - Backbone, showData, panelTitle, showQueryTool + Backbone, showData, panelTitle, showQueryTool, toolBar ) { // Some scripts do export their object in the window only. // Generally the one, which do no have AMD support. @@ -64,10 +64,16 @@ define('pgadmin.datagrid', [ * otherwise disabled. */ view_menu_enabled = function(obj) { - if (!_.isUndefined(obj) && !_.isNull(obj)) - return (_.indexOf(supported_nodes, obj._type) !== -1 ? true : false); - else - return false; + var isEnabled = (() => { + if (!_.isUndefined(obj) && !_.isNull(obj)) + return (_.indexOf(supported_nodes, obj._type) !== -1 ? true : false); + else + return false; + })(); + + toolBar.enable(gettext('View Data'), isEnabled); + toolBar.enable(gettext('Filtered Rows'), isEnabled); + return isEnabled; }, /* Enable/disable Query tool menu in tools based @@ -76,20 +82,26 @@ define('pgadmin.datagrid', [ * otherwise enabled. */ query_tool_menu_enabled = function(obj) { - if (!_.isUndefined(obj) && !_.isNull(obj)) { - if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) { - if (obj._type == 'database' && obj.allowConn) - return true; - else if (obj._type != 'database') - return true; - else + var isEnabled = (() => { + if (!_.isUndefined(obj) && !_.isNull(obj)) { + if (_.indexOf(pgAdmin.unsupported_nodes, obj._type) == -1) { + if (obj._type == 'database' && obj.allowConn) { + return true; + } else if (obj._type != 'database') { + return true; + } else { + return false; + } + } else { return false; + } } else { return false; } - } else { - return false; - } + })(); + + toolBar.enable(gettext('Query Tool'), isEnabled); + return isEnabled; }; // Define the nodes on which the menus to be appear diff --git a/web/webpack.shim.js b/web/webpack.shim.js index 41bf1033e..c2711036c 100644 --- a/web/webpack.shim.js +++ b/web/webpack.shim.js @@ -189,6 +189,7 @@ var webpackShimConfig = { 'pgadmin.browser.object_sql': path.join(__dirname, './pgadmin/misc/sql/static/js/sql'), 'pgadmin.browser.object_statistics': path.join(__dirname, './pgadmin/misc/statistics/static/js/statistics'), 'pgadmin.browser.panel': path.join(__dirname, './pgadmin/browser/static/js/panel'), + 'pgadmin.browser.toolbar': path.join(__dirname, './pgadmin/browser/static/js/toolbar'), 'pgadmin.browser.server.privilege': path.join(__dirname, './pgadmin/browser/server_groups/servers/static/js/privilege'), 'pgadmin.browser.server.variable': path.join(__dirname, './pgadmin/browser/server_groups/servers/static/js/variable'), 'pgadmin.browser.table.partition.utils': path.join(__dirname, './pgadmin/browser/server_groups/servers/databases/schemas/tables/static/js/partition.utils'), diff --git a/web/yarn.lock b/web/yarn.lock index af11c59bc..f35ad96d2 100644 --- a/web/yarn.lock +++ b/web/yarn.lock @@ -9517,9 +9517,9 @@ watchpack@^1.4.0: graceful-fs "^4.1.2" neo-async "^2.5.0" -"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker": +"webcabin-docker@git+https://github.com/EnterpriseDB/wcDocker/#5fbdf05a6757464d2fa2c2b017322c97688b2105": version "2.2.4-dev" - resolved "git+https://github.com/EnterpriseDB/wcDocker#447cb46953537b6a0e007890910787ac0ab18f38" + resolved "git+https://github.com/EnterpriseDB/wcDocker/#5fbdf05a6757464d2fa2c2b017322c97688b2105" dependencies: FileSaver "^0.10.0" font-awesome "^4.7.0"