diff --git a/web/package.json b/web/package.json index c9892f60..f567ee25 100644 --- a/web/package.json +++ b/web/package.json @@ -89,7 +89,7 @@ "underscore": "^1.9.1", "underscore.string": "^3.3.5", "watchify": "~3.11.1", - "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#e191a1063c111d5542ff26a7c163940be180333d", + "webcabin-docker": "git+https://github.com/EnterpriseDB/wcDocker/#f3c79e4b9a9e76a8e34736fc33473e65c524050c", "wkx": "^0.4.6" }, "scripts": { diff --git a/web/pgadmin/browser/__init__.py b/web/pgadmin/browser/__init__.py index 5fd08b5c..508a8077 100644 --- a/web/pgadmin/browser/__init__.py +++ b/web/pgadmin/browser/__init__.py @@ -39,6 +39,7 @@ from pgadmin.utils import PgAdminModule from pgadmin.utils.ajax import make_json_response from pgadmin.utils.csrf import pgCSRFProtect from pgadmin.utils.preferences import Preferences +from pgadmin.utils.menu import MenuItem from pgadmin.browser.register_browser_preferences import \ register_browser_preferences from pgadmin.utils.master_password import validate_master_password, \ @@ -215,6 +216,40 @@ class BrowserModule(PgAdminModule): scripts.extend(module.get_own_javascripts()) return scripts + def get_own_menuitems(self): + return { + 'file_items': [ + MenuItem( + name='mnu_locklayout', + module='pgAdmin.Browser', + label=gettext('Lock layout'), + priority=999, + menu_items=[MenuItem( + name='mnu_lock_none', + module='pgAdmin.Browser', + callback='mnu_lock_none', + priority=0, + label=gettext('None'), + checked=True + ), MenuItem( + name='mnu_lock_docking', + module='pgAdmin.Browser', + callback='mnu_lock_docking', + priority=1, + label=gettext('Prevent docking'), + checked=False + ), MenuItem( + name='mnu_lock_full', + module='pgAdmin.Browser', + callback='mnu_lock_full', + priority=2, + label=gettext('Full lock'), + checked=False + )] + ) + ] + } + def register_preferences(self): register_browser_preferences(self) @@ -226,7 +261,8 @@ class BrowserModule(PgAdminModule): return ['browser.index', 'browser.nodes', 'browser.check_master_password', 'browser.set_master_password', - 'browser.reset_master_password'] + 'browser.reset_master_password', + 'browser.lock_layout'] blueprint = BrowserModule(MODULE_NAME, __name__) @@ -815,6 +851,21 @@ def set_master_password(): ) +@blueprint.route("/lock_layout", endpoint="lock_layout", methods=["PUT"]) +def lock_layout(): + data = None + + if hasattr(request.data, 'decode'): + data = request.data.decode('utf-8') + + if data != '': + data = json.loads(data) + + blueprint.lock_layout.set(data['value']) + + return make_json_response() + + # Only register route if SECURITY_CHANGEABLE is set to True # We can't access app context here so cannot # use app.config['SECURITY_CHANGEABLE'] diff --git a/web/pgadmin/browser/register_browser_preferences.py b/web/pgadmin/browser/register_browser_preferences.py index c4d86c66..35ae7340 100644 --- a/web/pgadmin/browser/register_browser_preferences.py +++ b/web/pgadmin/browser/register_browser_preferences.py @@ -8,6 +8,12 @@ ########################################################################## from flask_babelex import gettext +LOCK_LAYOUT_LEVEL = { + 'PREVENT_DOCKING': 'docking', + 'FULL': 'full', + 'NONE': 'none' +} + def register_browser_preferences(self): self.show_system_objects = self.preference.register( @@ -58,6 +64,20 @@ def register_browser_preferences(self): ) ) + self.lock_layout = self.preference.register( + 'display', 'lock_layout', + gettext('Lock layout'), 'radioModern', LOCK_LAYOUT_LEVEL['NONE'], + category_label=gettext('Display'), options=[ + {'label': gettext('None'), 'value': LOCK_LAYOUT_LEVEL['NONE']}, + {'label': gettext('Prevent docking'), + 'value': LOCK_LAYOUT_LEVEL['PREVENT_DOCKING']}, + {'label': gettext('Full'), 'value': LOCK_LAYOUT_LEVEL['FULL']}, + ], + help_str=gettext( + 'Will change this later... ' + ) + ) + self.table_row_count_threshold = self.preference.register( 'properties', 'table_row_count_threshold', gettext("Count rows if estimated less than"), 'integer', 2000, diff --git a/web/pgadmin/browser/static/js/browser.js b/web/pgadmin/browser/static/js/browser.js index 7fbed5a9..77810dc7 100644 --- a/web/pgadmin/browser/static/js/browser.js +++ b/web/pgadmin/browser/static/js/browser.js @@ -15,7 +15,7 @@ define('pgadmin.browser', [ 'sources/csrf', 'pgadmin.browser.utils', 'wcdocker', 'jquery.contextmenu', 'jquery.aciplugin', 'jquery.acitree', 'pgadmin.browser.preferences', 'pgadmin.browser.messages', - 'pgadmin.browser.menu', 'pgadmin.browser.panel', + 'pgadmin.browser.menu', 'pgadmin.browser.panel', 'pgadmin.browser.layout', 'pgadmin.browser.error', 'pgadmin.browser.frame', 'pgadmin.browser.node', 'pgadmin.browser.collection', 'sources/codemirror/addon/fold/pgadmin-sqlfoldcode', @@ -282,22 +282,6 @@ define('pgadmin.browser', [ scripts[n].push({'name': m, 'path': p, loaded: false}); }, masterpass_callback_queue: [], - // Build the default layout - buildDefaultLayout: function(docker) { - var browserPanel = docker.addPanel('browser', wcDocker.DOCK.LEFT); - var dashboardPanel = docker.addPanel( - 'dashboard', wcDocker.DOCK.RIGHT, browserPanel); - docker.addPanel('properties', wcDocker.DOCK.STACKED, dashboardPanel, { - tabOrientation: wcDocker.TAB.TOP, - }); - docker.addPanel('sql', wcDocker.DOCK.STACKED, dashboardPanel); - docker.addPanel( - 'statistics', wcDocker.DOCK.STACKED, dashboardPanel); - docker.addPanel( - 'dependencies', wcDocker.DOCK.STACKED, dashboardPanel); - docker.addPanel( - 'dependents', wcDocker.DOCK.STACKED, dashboardPanel); - }, // Enable/disable menu options enable_disable_menus: function(item) { // Mechanism to enable/disable menus depending on the condition. @@ -354,35 +338,6 @@ define('pgadmin.browser', [ $obj_mnu.append(create_submenu.$el); } }, - save_current_layout: function(layout_id, docker) { - if(docker) { - var layout = docker.save(), - settings = { setting: layout_id, value: layout }; - $.ajax({ - type: 'POST', - url: url_for('settings.store_bulk'), - data: settings, - }); - } - }, - restore_layout: function(docker, layout, defaultLayoutCallback) { - // Try to restore the layout if there is one - if (layout != '') { - try { - docker.restore(layout); - } - catch(err) { - docker.clear(); - if(defaultLayoutCallback) { - defaultLayoutCallback(docker); - } - } - } else { - if(defaultLayoutCallback) { - defaultLayoutCallback(docker); - } - } - }, init: function() { var obj=this; if (obj.initialized) { @@ -797,8 +752,8 @@ define('pgadmin.browser', [ menus = pgMenu[a]; } - if (!_.has(menus, m.name)) { - menus[m.name] = new MenuItem({ + let get_menuitem_obj = function(m) { + return new MenuItem({ name: m.name, label: m.label, module: m.module, category: m.category, callback: m.callback, priority: m.priority, data: m.data, url: m.url || '#', @@ -806,8 +761,21 @@ define('pgadmin.browser', [ enable: (m.enable == '' ? true : (_.isString(m.enable) && m.enable.toLowerCase() == 'false') ? false : m.enable), - node: m.node, + node: m.node, checked: m.checked, }); + }; + + if (!_.has(menus, m.name)) { + menus[m.name] = get_menuitem_obj(m); + + if(m.menu_items) { + let sub_menu_items = []; + + for(let i=0; i { + let browserPref = browser.get_preferences_for_module('browser'); + if(browserPref) { + clearInterval(cacheIntervalId); + + browser.reflectLocklayoutChange(docker); + + browser.onPreferencesChange('browser', function() { + browser.reflectLocklayoutChange(docker); + }); + } + }, 500); + }, + + reflectLocklayoutChange: function(docker) { + let browser = window.opener ? + window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser; + + let browserPref = browser.get_preferences_for_module('browser'); + browser.lock_layout(docker, browserPref.lock_layout); + }, + + lock_layout: function(docker, op) { + let menu_items = this.menus['file']['mnu_locklayout']['menu_items']; + + switch(op) { + case this.lock_layout_levels.PREVENT_DOCKING: + docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.PREVENT_DOCKING); + break; + case this.lock_layout_levels.FULL: + docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.FULL); + break; + case this.lock_layout_levels.NONE: + docker.lockLayout(wcDocker.LOCK_LAYOUT_LEVEL.NONE); + break; + } + + _.each(menu_items, function(menu_item) { + if(menu_item.name != ('mnu_lock_'+op)) { + menu_item.change_checked(false); + } else { + menu_item.change_checked(true); + } + }); + }, + + save_lock_layout: function(op) { + let browser = window.opener ? + window.opener.pgAdmin.Browser : window.top.pgAdmin.Browser; + + $.ajax({ + url: url_for('browser.lock_layout'), + method: 'PUT', + contentType: 'application/json', + data: JSON.stringify({ + 'value': op, + }), + }).done(function() { + browser.cache_preferences('browser'); + }).fail(function(xhr, error) { + Alertify.pgNotifier(error, xhr, gettext('Failed to save lock layout setting.')); + }); + }, + + mnu_lock_docking: function() { + this.lock_layout(this.docker, this.lock_layout_levels.PREVENT_DOCKING); + this.save_lock_layout(this.lock_layout_levels.PREVENT_DOCKING); + }, + + mnu_lock_full: function() { + this.lock_layout(this.docker, this.lock_layout_levels.FULL); + this.save_lock_layout(this.lock_layout_levels.FULL); + }, + + mnu_lock_none: function() { + this.lock_layout(this.docker, this.lock_layout_levels.NONE); + this.save_lock_layout(this.lock_layout_levels.NONE); + }, +}); + +export {pgBrowser}; diff --git a/web/pgadmin/browser/static/js/menu.js b/web/pgadmin/browser/static/js/menu.js index f8c018dc..e76c9ed2 100644 --- a/web/pgadmin/browser/static/js/menu.js +++ b/web/pgadmin/browser/static/js/menu.js @@ -19,6 +19,7 @@ define([ var menu_opts = [ 'name', 'label', 'priority', 'module', 'callback', 'data', 'enable', 'category', 'target', 'url' /* Do not show icon in the menus, 'icon' */ , 'node', + 'checked', 'menu_items', ], defaults = { url: '#', @@ -55,31 +56,48 @@ define([ * Create the jquery element for the menu-item. */ create_el: function(node, item) { - var url = $('', { - 'id': this.name, - 'href': this.url, - 'target': this.target, - 'data-toggle': 'pg-menu', - }).data('pgMenu', { - module: this.module || pgAdmin.Browser, - cb: this.callback, - data: this.data, - }).addClass('dropdown-item'); - this.is_disabled = this.disabled(node, item); - if (this.icon) { - url.append($('', { - 'class': this.icon, - })); - } + if(this.menu_items) { + _.each(this.menu_items, function(submenu_item){ + submenu_item.generate(node, item); + }); + var create_submenu = pgAdmin.Browser.MenuGroup({ + 'label': this.label, + 'id': this.name, + }, this.menu_items); + this.$el = create_submenu.$el; + } else { + var url = $('', { + 'id': this.name, + 'href': this.url, + 'target': this.target, + 'data-toggle': 'pg-menu', + }).data('pgMenu', { + module: this.module || pgAdmin.Browser, + cb: this.callback, + data: this.data, + }).addClass('dropdown-item'); + + this.is_disabled = this.disabled(node, item); + if (this.icon) { + url.append($('', { + 'class': this.icon, + })); + } else if(!_.isUndefined(this.checked)) { + url.append($('', { + 'class': 'fa fa-check '+ (this.checked?'':'visibility-hidden'), + })); + } - url.addClass((this.is_disabled ? ' disabled' : '')); + url.addClass((this.is_disabled ? ' disabled' : '')); - var textSpan = $('').text(' ' + this.label); + var textSpan = $('').text(' ' + this.label); - url.append(textSpan); + url.append(textSpan); + + this.$el = $('
  • ').append(url); + } - this.$el = $('
  • ').append(url); }, /* * Updates the enable/disable state of the menu-item based on the current @@ -154,6 +172,20 @@ define([ return false; }, + + /* + * Change the checked value and update the checked icon on the menu + */ + change_checked(isChecked) { + if(!_.isUndefined(this.checked)) { + this.checked = isChecked; + if(this.checked) { + this.$el.find('.fa-check').removeClass('visibility-hidden'); + } else { + this.$el.find('.fa-check').addClass('visibility-hidden'); + } + } + }, }); /* diff --git a/web/pgadmin/browser/templates/browser/js/utils.js b/web/pgadmin/browser/templates/browser/js/utils.js index cefe9ae7..99d795bc 100644 --- a/web/pgadmin/browser/templates/browser/js/utils.js +++ b/web/pgadmin/browser/templates/browser/js/utils.js @@ -7,6 +7,32 @@ // ////////////////////////////////////////////////////////////// +{% macro A_MENU_ITEM(key, item) -%} +{ + name: "{{ item.name }}", + {% if item.module %}module: {{ item.module }}, + {% endif %}{% if item.url %}url: "{{ item.url }}", + {% endif %}{% if item.target %}target: "{{ item.target }}", + {% endif %}{% if item.callback %}callback: "{{ item.callback }}", + {% endif %}{% if item.category %}category: "{{ item.category }}", + {% endif %}{% if item.icon %}icon: '{{ item.icon }}', + {% endif %}{% if item.data %}data: {{ item.data }}, + {% endif %}label: '{{ item.label }}', applies: ['{{ key.lower() }}'], + priority: {{ item.priority }}, + enable: '{{ item.enable }}', + {% if item.checked is defined %}checked: {% if item.checked %}true{% else %}false{% endif %}, + {% endif %} + {% if item.menu_items %}menu_items: {{MENU_ITEMS(key, item.menu_items)}} + {% endif %} +} +{%- endmacro %} + +{% macro MENU_ITEMS(key, items) -%} +[ + {% for item in items %}{% if loop.index != 1 %}, {% endif %} + {{ A_MENU_ITEM(key, item) }}{% set hasMenus = True %}{% endfor %} +] +{%- endmacro %} define('pgadmin.browser.utils', ['sources/pgadmin'], function(pgAdmin) { @@ -56,19 +82,7 @@ define('pgadmin.browser.utils', var self = this; if (this.counter.total == this.counter.loaded) { {% for key in ('File', 'Edit', 'Object' 'Tools', 'Management', 'Help') %} - obj.add_menus([{% for item in current_app.menu_items['%s_items' % key.lower()] %}{% if loop.index != 1 %}, {% endif %}{ - name: "{{ item.name }}", - {% if item.module %}module: {{ item.module }}, - {% endif %}{% if item.url %}url: "{{ item.url }}", - {% endif %}{% if item.target %}target: "{{ item.target }}", - {% endif %}{% if item.callback %}callback: "{{ item.callback }}", - {% endif %}{% if item.category %}category: "{{ item.category }}", - {% endif %}{% if item.icon %}icon: '{{ item.icon }}', - {% endif %}{% if item.data %}data: {{ item.data }}, - {% endif %}label: '{{ item.label }}', applies: ['{{ key.lower() }}'], - priority: {{ item.priority }}, - enable: '{{ item.enable }}' - }{% set hasMenus = True %}{% endfor %}]); + obj.add_menus({{ MENU_ITEMS(key, current_app.menu_items['%s_items' % key.lower()])}}); {% endfor %} obj.create_menus(); } else { diff --git a/web/pgadmin/preferences/__init__.py b/web/pgadmin/preferences/__init__.py index bbe9693b..44732171 100644 --- a/web/pgadmin/preferences/__init__.py +++ b/web/pgadmin/preferences/__init__.py @@ -48,7 +48,7 @@ class PreferencesModule(PgAdminModule): return { 'file_items': [ MenuItem(name='mnu_preferences', - priority=999, + priority=997, module="pgAdmin.Preferences", callback='show', icon='fa fa-cog', diff --git a/web/pgadmin/preferences/static/js/preferences.js b/web/pgadmin/preferences/static/js/preferences.js index 513e3381..b047b13a 100644 --- a/web/pgadmin/preferences/static/js/preferences.js +++ b/web/pgadmin/preferences/static/js/preferences.js @@ -271,6 +271,8 @@ define('pgadmin.preferences', [ return 'switch'; case 'keyboardshortcut': return 'keyboardShortcut'; + case 'radioModern': + return 'radioModern'; default: if (console && console.warn) { // Warning for developer only. diff --git a/web/pgadmin/settings/__init__.py b/web/pgadmin/settings/__init__.py index fd79634e..b84a5072 100644 --- a/web/pgadmin/settings/__init__.py +++ b/web/pgadmin/settings/__init__.py @@ -39,7 +39,7 @@ class SettingsModule(PgAdminModule): 'file_items': [ MenuItem( name='mnu_resetlayout', - priority=999, + priority=998, module="pgAdmin.Settings", callback='show', icon='fa fa-retweet', diff --git a/web/pgadmin/static/js/backform.pgadmin.js b/web/pgadmin/static/js/backform.pgadmin.js index 2a8792b2..b97fc380 100644 --- a/web/pgadmin/static/js/backform.pgadmin.js +++ b/web/pgadmin/static/js/backform.pgadmin.js @@ -60,6 +60,7 @@ define([ 'select2': 'select2', 'note': 'note', 'color': 'color', + 'radioModern': 'radioModern', }; Backform.getMappedControl = function(type, mode) { @@ -95,6 +96,17 @@ define([ return type; }; + /* Returns raw data as it is */ + var RawFormatter = Backform.RawFormatter = function() {}; + _.extend(RawFormatter.prototype, { + fromRaw: function(rawData) { + return rawData; + }, + toRaw: function(formattedData) { + return formattedData; + }, + }); + var BackformControlInit = Backform.Control.prototype.initialize, BackformControlRemove = Backform.Control.prototype.remove; @@ -422,6 +434,48 @@ define([ }, }); + + Backform.RadioModernControl = Backform.RadioControl.extend({ + defaults: { + controlLabelClassName: Backform.controlLabelClassName, + controlsClassName: Backform.controlsClassName, + extraClasses: [], + helpMessage: '', + name: '', + }, + template: _.template([ + '', + '
    ', + '
    ', + ' <% for (var i=0; i < options.length; i++) { %>', + ' <% var option = options[i]; %>', + ' ', + ' <% } %>', + '
    ', + ' <% if (helpMessage && helpMessage.length) { %>', + ' <%=helpMessage%>', + ' <% } %>', + '
    ', + ].join('\n')), + formatter: RawFormatter, + getValueFromDOM: function() { + return this.formatter.toRaw(this.$el.find('input[type="radio"]:checked').attr('value'), this.model); + }, + render: function() { + Backform.RadioControl.prototype.render.apply(this, arguments); + this.$el.find('.btn').on('keyup', (e)=>{ + switch(e.keyCode) { + case 32: /* Spacebar click */ + $(e.currentTarget).trigger('click'); + break; + } + }); + return this; + }, + }); + // Requires the Bootstrap Switch to work. Backform.SwitchControl = Backform.InputControl.extend({ defaults: { diff --git a/web/pgadmin/static/scss/_bootstrap.overrides.scss b/web/pgadmin/static/scss/_bootstrap.overrides.scss index ed16ec82..55fc5afa 100644 --- a/web/pgadmin/static/scss/_bootstrap.overrides.scss +++ b/web/pgadmin/static/scss/_bootstrap.overrides.scss @@ -310,3 +310,8 @@ td.switch-cell > div.toggle { .btn.disabled, .btn:disabled, .btn[disabled] { opacity: $btn-disabled-opacity; } + +.btn-group label.btn.btn-primary.active { + background-color: $color-primary-light; + color: $color-primary; +} diff --git a/web/pgadmin/tools/maintenance/static/js/maintenance.js b/web/pgadmin/tools/maintenance/static/js/maintenance.js index 2311b506..abe8701e 100644 --- a/web/pgadmin/tools/maintenance/static/js/maintenance.js +++ b/web/pgadmin/tools/maintenance/static/js/maintenance.js @@ -51,8 +51,11 @@ define([ id: 'op', label: gettext('Maintenance operation'), cell: 'string', - type: 'text', + type: 'radioModern', + controlsClassName: 'pgadmin-controls col-12 col-sm-8', + controlLabelClassName: 'control-label col-sm-4 col-12', group: gettext('Options'), + value: 'VACUUM', options: [{ 'label': 'VACUUM', 'value': 'VACUUM', @@ -70,30 +73,6 @@ define([ 'value': 'CLUSTER', }, ], - control: Backform.RadioControl.extend({ - template: _.template([ - '', - '
    ', - ' <% for (var i=0; i < options.length; i++) { %>', - ' <% var option = options[i]; %>', - ' ', - ' <% } %>', - '
    ', - ].join('\n')), - render: function() { - Backform.RadioControl.prototype.render.apply(this, arguments); - this.$el.find('.pg-maintenance-op .btn').on('keyup', (e)=>{ - switch(e.keyCode) { - case 32: /* Spacebar click */ - $(e.currentTarget).trigger('click'); - break; - } - }); - return this; - }, - }), }, { type: 'nested', diff --git a/web/pgadmin/tools/maintenance/static/scss/_maintenance.scss b/web/pgadmin/tools/maintenance/static/scss/_maintenance.scss deleted file mode 100644 index 98f8a04c..00000000 --- a/web/pgadmin/tools/maintenance/static/scss/_maintenance.scss +++ /dev/null @@ -1,4 +0,0 @@ -.btn-group.pg-maintenance-op label.btn.btn-primary.active { - background-color: $color-primary-light; - color: $color-primary; -} diff --git a/web/pgadmin/utils/preferences.py b/web/pgadmin/utils/preferences.py index 656f4463..ec484d2e 100644 --- a/web/pgadmin/utils/preferences.py +++ b/web/pgadmin/utils/preferences.py @@ -428,7 +428,7 @@ class Preferences(object): assert _type is not None, "Type for a preference cannot be none!" assert _type in ( 'boolean', 'integer', 'numeric', 'date', 'datetime', - 'options', 'multiline', 'switch', 'node', 'text', + 'options', 'multiline', 'switch', 'node', 'text', 'radioModern', 'keyboardshortcut' ), "Type cannot be found in the defined list!" diff --git a/web/regression/javascript/browser/layout_spec.js b/web/regression/javascript/browser/layout_spec.js new file mode 100644 index 00000000..c1e6709d --- /dev/null +++ b/web/regression/javascript/browser/layout_spec.js @@ -0,0 +1,109 @@ +///////////////////////////////////////////////////////////// +// +// pgAdmin 4 - PostgreSQL Tools +// +// Copyright (C) 2013 - 2019, The pgAdmin Development Team +// This software is released under the PostgreSQL Licence +// +////////////////////////////////////////////////////////////// + +import {pgBrowser} from 'pgadmin.browser.layout'; +import 'wcdocker'; + +var wcDocker = window.wcDocker; + +describe('layout related functions test', function() { + let menu_items = null; + let dummy_cache = [{ + id: 2, + mid: 1, + module:'browser', + name:'lock_layout', + value: 'none', + }]; + + beforeEach(function(){ + pgBrowser.preferences_cache = dummy_cache; + pgBrowser.docker = { + 'lockLayout': ()=>{}, + }; + + _.extend(pgBrowser,{ + 'menus': { + 'file': { + 'mnu_locklayout': { + 'menu_items': [ + {'name': 'mnu_lock_none', change_checked: ()=> {}}, + {'name': 'mnu_lock_docking', change_checked: ()=> {}}, + {'name': 'mnu_lock_full', change_checked: ()=> {}}, + ], + }, + }, + }, + }); + + menu_items = pgBrowser.menus.file.mnu_locklayout.menu_items; + }); + + describe('for menu actions', function() { + beforeEach(function(){ + spyOn(pgBrowser, 'lock_layout'); + spyOn(pgBrowser, 'save_lock_layout'); + }); + + it('mnu_lock_none', function() { + pgBrowser.mnu_lock_none(); + expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'none'); + expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('none'); + }); + + it('mnu_lock_docking', function() { + pgBrowser.mnu_lock_docking(); + expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'docking'); + expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('docking'); + }); + + it('mnu_lock_full', function() { + pgBrowser.mnu_lock_full(); + expect(pgBrowser.lock_layout).toHaveBeenCalledWith(pgBrowser.docker, 'full'); + expect(pgBrowser.save_lock_layout).toHaveBeenCalledWith('full'); + }); + }); + + describe('lock_layout', function() { + let change_checked_test= function(menu_name) { + for(let i=0; i