From 829897be3e33d4faab8cce9ca6bd9c330002c6a1 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Tue, 17 Dec 2013 08:00:23 +0800 Subject: [PATCH] MDL-43410 JavaScript: Stop event propagation when opening the action menu In certain situations, the click event is propagated through to the hideIfOutside and immediately closes the menu. This happens when blocks are dock for example. --- .../activities_edit_with_block_dock.feature | 29 +++++++++++++++++++ course/tests/behat/behat_course.php | 24 +++++++++++++++ .../moodle-core-actionmenu-debug.js | 3 +- .../moodle-core-actionmenu-min.js | 4 +-- .../moodle-core-actionmenu.js | 3 +- lib/yui/src/actionmenu/js/actionmenu.js | 3 +- 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 course/tests/behat/activities_edit_with_block_dock.feature diff --git a/course/tests/behat/activities_edit_with_block_dock.feature b/course/tests/behat/activities_edit_with_block_dock.feature new file mode 100644 index 00000000000..36803cbd380 --- /dev/null +++ b/course/tests/behat/activities_edit_with_block_dock.feature @@ -0,0 +1,29 @@ +@core @core_course @_cross_browser +Feature: Open the edit menu when a block is docked + In order to edit an activity with a block docked + As a teacher + I need to open the action menu + + @javascript + Scenario: Open the action menu with a block docked + Given the following "users" exists: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@asd.com | + And the following "courses" exists: + | fullname | shortname | format | + | Course 1 | C1 | weeks | + And the following "course enrolments" exists: + | user | course | role | + | teacher1 | C1 | editingteacher | + And I log in as "teacher1" + And I follow "Course 1" + And I turn editing mode on + And I add a "Glossary" to section "1" and I fill the form with: + | Name | Test glossary name | + | Description | Test glossary description | + And I dock "Navigation" block + When I open "Test glossary name" actions menu + Then "Test glossary name" actions menu should be open + And I reload the page + When I open "Test glossary name" actions menu + Then "Test glossary name" actions menu should be open diff --git a/course/tests/behat/behat_course.php b/course/tests/behat/behat_course.php index d31c1b966a5..e4a9133e951 100644 --- a/course/tests/behat/behat_course.php +++ b/course/tests/behat/behat_course.php @@ -643,6 +643,30 @@ class behat_course extends behat_base { return new Given('I click on "a[role=\'menuitem\']" "css_element" in the "' . $this->escape($activityname) . '" activity'); } + /** + * Checks that the specified activity's action menu is open. + * + * @Then /^"(?P(?:[^"]|\\")*)" actions menu should be open$/ + * @throws DriverException The step is not available when Javascript is disabled + * @param string $activityname + * @return Given + */ + public function actions_menu_should_be_open($activityname) { + + if (!$this->running_javascript()) { + throw new DriverException('Activities actions menu not available when Javascript is disabled'); + } + + // If it is already closed we do nothing. + $activitynode = $this->get_activity_node($activityname); + $classes = array_flip(explode(' ', $activitynode->getAttribute('class'))); + if (empty($classes['action-menu-shown'])) { + throw new ExpectationException(sprintf("The action menu for '%s' is not open", $activityname), $this->getSession()); + } + + return; + } + /** * Indents to the right the activity or resource specified by it's name. Editing mode should be on. * diff --git a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js index 6f6c786c060..9b0f72079dc 100644 --- a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js +++ b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-debug.js @@ -286,7 +286,8 @@ ACTIONMENU.prototype = { var menu = e.target.ancestor(SELECTOR.MENU), menuvisible = (menu.hasClass('show')); - e.preventDefault(); + // Prevent event propagation as it will trigger the hideIfOutside event handler in certain situations. + e.halt(true); this.hideMenu(); if (menuvisible) { // The menu was visible and the user has clicked to toggle it again. diff --git a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js index 2806b0f8828..38f3d5e2592 100644 --- a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js +++ b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu-min.js @@ -1,2 +1,2 @@ -YUI.add("moodle-core-actionmenu",function(e,t){var n=e.one(e.config.doc.body),r={MENUSHOWN:"action-menu-shown"},i={CAN_RECEIVE_FOCUS_SELECTOR:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",MENUBAR:'[role="menubar"]',MENUITEM:'[role="menuitem"]',MENUCONTENT:".menu[data-rel=menu-content]",MENUCONTENTCHILD:"li a",MENUCHILD:".menu li a",TOGGLE:".toggle-display",KEEPOPEN:'[data-keepopen="1"]',MENUBARITEMS:['[role="menubar"] > [role="menuitem"]','[role="menubar"] > [role="presentation"] > [role="menuitem"]'],MENUITEMS:['> [role="menuitem"]','> [role="presentation"] > [role="menuitem"]']},s,o={TL:"tl",TR:"tr",BL:"bl",BR:"br"};s=function(){s.superclass.constructor.apply(this,arguments)},s.prototype={dialogue:null,events:[],owner:null,menulink:null,menuChildren:null,firstMenuChild:null,lastMenuChild:null,initializer:function(){e.all(i.MENU).each(this.enhance,this),n.delegate("key",this.moveMenuItem,"down:37,39",i.MENUBARITEMS.join(","),this),n.delegate("click",this.toggleMenu,i.MENU+" "+i.TOGGLE,this),n.delegate("key",this.showIfHidden,"down:enter,38,40",i.MENU+" "+i.TOGGLE,this),n.delegate("key",function(e){e.currentTarget.simulate("click"),e.preventDefault()},"down:32",i.MENUBARITEMS.join(","))},enhance:function(e){var t=e.one(i.MENUCONTENT),n;if(!t)return!1;n=t.getData("align")||this.get("align").join("-"),e.one(i.TOGGLE).set("aria-haspopup",!0),t.set("aria-hidden",!0),t.hasClass("align-"+n)||t.addClass("align-"+n),t.hasChildNodes()&&e.setAttribute("data-enhanced","1")},moveMenuItem:function(e){var t,n=e.target.ancestor(i.MENUITEM,!0);return e.keyCode===37?t=this.getMenuItem(n,!0):e.keyCode===39&&(t=this.getMenuItem(n)),t&&t.focus(),this},getMenuItem:function(e,t){var n=e.ancestor(i.MENUBAR),r;if(!n)return null;r=n.all(i.MENUITEMS.join(","));if(!r)return null;var s=r.size();if(s===1)return null;var o=0,u=1,a=0;for(o=0;o=this.menuChildren.size()&&(r=0),t=this.menuChildren.item(r),o++;while(oa?(c=i=a,p=n=o):n=o+a&&(p=o+a-i),s>f&&l.toLowerCase()==="hidden"&&(h=s=f,e.setStyle("overflow","auto"));if(r>=u&&r+s>u+f){d=u+f-s;try{v=e.getStyle("boxShadow").replace(/.*? (\d+)px \d+px$/,"$1"),(new RegExp(/^\d+$/)).test(v)&&d-u>v&&(d-=v)}catch(m){}}p!==null&&e.setX(p),d!==null&&e.setY(d),c!==null&&e.setStyle("width",c.toString()+"px"),h!==null&&e.setStyle("height",h.toString()+"px")}},e.extend(s,e.Base,s.prototype,{NAME:"moodle-core-actionmenu",ATTRS:{align:{value:[o.TR,o.BR]}}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.instance=null,M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new s(e)},M.core.actionmenu.newDOMNode=function(e){if(M.core.actionmenu.instance=== -null)return!0;e.all(i.MENU).each(M.core.actionmenu.instance.enhance,M.core.actionmenu.instance)}},"@VERSION@",{requires:["base","event","node-event-simulate"]}); +YUI.add("moodle-core-actionmenu",function(e,t){var n=e.one(e.config.doc.body),r={MENUSHOWN:"action-menu-shown"},i={CAN_RECEIVE_FOCUS_SELECTOR:'input:not([type="hidden"]), a[href], button, textarea, select, [tabindex]',MENU:".moodle-actionmenu[data-enhance=moodle-core-actionmenu]",MENUBAR:'[role="menubar"]',MENUITEM:'[role="menuitem"]',MENUCONTENT:".menu[data-rel=menu-content]",MENUCONTENTCHILD:"li a",MENUCHILD:".menu li a",TOGGLE:".toggle-display",KEEPOPEN:'[data-keepopen="1"]',MENUBARITEMS:['[role="menubar"] > [role="menuitem"]','[role="menubar"] > [role="presentation"] > [role="menuitem"]'],MENUITEMS:['> [role="menuitem"]','> [role="presentation"] > [role="menuitem"]']},s,o={TL:"tl",TR:"tr",BL:"bl",BR:"br"};s=function(){s.superclass.constructor.apply(this,arguments)},s.prototype={dialogue:null,events:[],owner:null,menulink:null,menuChildren:null,firstMenuChild:null,lastMenuChild:null,initializer:function(){e.all(i.MENU).each(this.enhance,this),n.delegate("key",this.moveMenuItem,"down:37,39",i.MENUBARITEMS.join(","),this),n.delegate("click",this.toggleMenu,i.MENU+" "+i.TOGGLE,this),n.delegate("key",this.showIfHidden,"down:enter,38,40",i.MENU+" "+i.TOGGLE,this),n.delegate("key",function(e){e.currentTarget.simulate("click"),e.preventDefault()},"down:32",i.MENUBARITEMS.join(","))},enhance:function(e){var t=e.one(i.MENUCONTENT),n;if(!t)return!1;n=t.getData("align")||this.get("align").join("-"),e.one(i.TOGGLE).set("aria-haspopup",!0),t.set("aria-hidden",!0),t.hasClass("align-"+n)||t.addClass("align-"+n),t.hasChildNodes()&&e.setAttribute("data-enhanced","1")},moveMenuItem:function(e){var t,n=e.target.ancestor(i.MENUITEM,!0);return e.keyCode===37?t=this.getMenuItem(n,!0):e.keyCode===39&&(t=this.getMenuItem(n)),t&&t.focus(),this},getMenuItem:function(e,t){var n=e.ancestor(i.MENUBAR),r;if(!n)return null;r=n.all(i.MENUITEMS.join(","));if(!r)return null;var s=r.size();if(s===1)return null;var o=0,u=1,a=0;for(o=0;o=this.menuChildren.size()&&(r=0),t=this.menuChildren.item(r),o++;while(oa?(c=i=a,p=n=o):n=o+a&&(p=o+a-i),s>f&&l.toLowerCase()==="hidden"&&(h=s=f,e.setStyle("overflow","auto"));if(r>=u&&r+s>u+f){d=u+f-s;try{v=e.getStyle("boxShadow").replace(/.*? (\d+)px \d+px$/,"$1"),(new RegExp(/^\d+$/)).test(v)&&d-u>v&&(d-=v)}catch(m){}}p!==null&&e.setX(p),d!==null&&e.setY(d),c!==null&&e.setStyle("width",c.toString()+"px"),h!==null&&e.setStyle("height",h.toString()+"px")}},e.extend(s,e.Base,s.prototype,{NAME:"moodle-core-actionmenu",ATTRS:{align:{value:[o.TR,o.BR]}}}),M.core=M.core||{},M.core.actionmenu=M.core.actionmenu||{},M.core.actionmenu.instance=null,M.core.actionmenu.init=M.core.actionmenu.init||function(e){M.core.actionmenu.instance=M.core.actionmenu.instance||new s(e)},M.core.actionmenu.newDOMNode=function(e){if(M.core.actionmenu.instance===null +)return!0;e.all(i.MENU).each(M.core.actionmenu.instance.enhance,M.core.actionmenu.instance)}},"@VERSION@",{requires:["base","event","node-event-simulate"]}); diff --git a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js index 02a6e8a0011..f08c612b3e0 100644 --- a/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js +++ b/lib/yui/build/moodle-core-actionmenu/moodle-core-actionmenu.js @@ -284,7 +284,8 @@ ACTIONMENU.prototype = { var menu = e.target.ancestor(SELECTOR.MENU), menuvisible = (menu.hasClass('show')); - e.preventDefault(); + // Prevent event propagation as it will trigger the hideIfOutside event handler in certain situations. + e.halt(true); this.hideMenu(); if (menuvisible) { // The menu was visible and the user has clicked to toggle it again. diff --git a/lib/yui/src/actionmenu/js/actionmenu.js b/lib/yui/src/actionmenu/js/actionmenu.js index 7eb93de0d01..9e84085d7f3 100644 --- a/lib/yui/src/actionmenu/js/actionmenu.js +++ b/lib/yui/src/actionmenu/js/actionmenu.js @@ -284,7 +284,8 @@ ACTIONMENU.prototype = { var menu = e.target.ancestor(SELECTOR.MENU), menuvisible = (menu.hasClass('show')); - e.preventDefault(); + // Prevent event propagation as it will trigger the hideIfOutside event handler in certain situations. + e.halt(true); this.hideMenu(); if (menuvisible) { // The menu was visible and the user has clicked to toggle it again.