Ticket #35210: 35210_4.diff
File 35210_4.diff, 13.1 KB (added by , 8 years ago) |
---|
-
src/wp-admin/css/customize-controls.css
diff --git a/src/wp-admin/css/customize-controls.css b/src/wp-admin/css/customize-controls.css index f2fbc86..b04907e 100644
a b body { 264 264 box-sizing: border-box; 265 265 } 266 266 267 #customize-notifications-area { 268 position: absolute; 269 top: 46px; 270 width: 100%; 271 max-height: 210px; 272 overflow-x: hidden; 273 overflow-y: auto; 274 border-bottom: 1px solid #ddd; 275 } 276 277 #customize-notifications-area > ul, 278 #customize-notifications-area .notice { 279 margin: 0; 280 } 281 282 #customize-notifications-area .notice + .notice { 283 margin-top: 1px; 284 } 285 267 286 #customize-info, 268 287 #customize-theme-controls .customize-pane-parent, 269 288 #customize-theme-controls .customize-pane-child { -
src/wp-admin/js/customize-controls.js
diff --git a/src/wp-admin/js/customize-controls.js b/src/wp-admin/js/customize-controls.js index 56c061f..0032baf 100644
a b 3888 3888 api.section = new api.Values({ defaultConstructor: api.Section }); 3889 3889 api.panel = new api.Values({ defaultConstructor: api.Panel }); 3890 3890 3891 // Create the collection for global Notifications. 3892 api.notifications = new api.Notifications(); 3893 3891 3894 /** 3892 3895 * An object that fetches a preview in the background of the document, which 3893 3896 * allows for seamless replacement of an existing preview. … … 5391 5394 } 5392 5395 5393 5396 var scrollTop = parentContainer.scrollTop(), 5394 isScrollingUp = ( lastScrollTop ) ? scrollTop <= lastScrollTop : true;5397 scrollDirection; 5395 5398 5399 if ( ! lastScrollTop ) { 5400 scrollDirection = 1; 5401 } else { 5402 if ( scrollTop === lastScrollTop ) { 5403 scrollDirection = 0; 5404 } else if ( scrollTop > lastScrollTop ) { 5405 scrollDirection = 1; 5406 } else { 5407 scrollDirection = -1; 5408 } 5409 } 5396 5410 lastScrollTop = scrollTop; 5397 positionStickyHeader( activeHeader, scrollTop, isScrollingUp);5411 positionStickyHeader( activeHeader, scrollTop, scrollDirection ); 5398 5412 }, 8 ) ); 5399 5413 5414 // Update header position on sidebar layout change. 5415 parentContainer.on( 'customize:sidebar:updateLayout', function() { 5416 if ( activeHeader && activeHeader.element.hasClass( 'is-sticky' ) ) { 5417 activeHeader.element.css( 'top', parentContainer.css( 'top' ) ); 5418 } 5419 } ); 5420 5400 5421 // Release header element if it is sticky. 5401 5422 releaseStickyHeader = function( headerElement ) { 5402 5423 if ( ! headerElement.hasClass( 'is-sticky' ) ) { … … 5410 5431 5411 5432 // Reset position of the sticky header. 5412 5433 resetStickyHeader = function( headerElement, headerParent ) { 5413 headerElement 5414 .removeClass( 'maybe-sticky is-in-view' ) 5415 .css( { 5416 width: '', 5417 top: '' 5418 } ); 5419 headerParent.css( 'padding-top', '' ); 5434 if ( headerElement.hasClass( 'is-in-view' ) ) { 5435 headerElement 5436 .removeClass( 'maybe-sticky is-in-view' ) 5437 .css( { 5438 width: '', 5439 top: '' 5440 } ); 5441 headerParent.css( 'padding-top', '' ); 5442 } 5420 5443 }; 5421 5444 5422 5445 // Get header height. … … 5430 5453 }; 5431 5454 5432 5455 // Reposition header on throttled `scroll` event. 5433 positionStickyHeader = function( header, scrollTop, isScrollingUp ) { 5456 positionStickyHeader = function( header, scrollTop, scrollDirection ) { 5457 if ( 0 === scrollDirection ) { 5458 return; 5459 } 5460 5434 5461 var headerElement = header.element, 5435 5462 headerParent = header.parent, 5436 5463 headerHeight = header.height, 5437 5464 headerTop = parseInt( headerElement.css( 'top' ), 10 ), 5438 5465 maybeSticky = headerElement.hasClass( 'maybe-sticky' ), 5439 5466 isSticky = headerElement.hasClass( 'is-sticky' ), 5440 isInView = headerElement.hasClass( 'is-in-view' ); 5467 isInView = headerElement.hasClass( 'is-in-view' ), 5468 isScrollingUp = ( -1 === scrollDirection ); 5469 5441 5470 5442 5471 // When scrolling down, gradually hide sticky header. 5443 5472 if ( ! isScrollingUp ) { … … 5480 5509 headerElement 5481 5510 .addClass( 'is-sticky' ) 5482 5511 .css( { 5483 top: '',5512 top: parentContainer.css( 'top' ), 5484 5513 width: headerParent.outerWidth() + 'px' 5485 5514 } ); 5486 5515 } -
src/wp-includes/class-wp-customize-manager.php
diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php index f96a220..1a6663e 100644
a b public function __construct( $args = array() ) { 358 358 add_action( 'customize_controls_init', array( $this, 'prepare_controls' ) ); 359 359 add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_control_scripts' ) ); 360 360 361 // Render Panel, Section, and Control templates. 361 // Render Common, Panel, Section, and Control templates. 362 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_common_templates' ), 1 ); 362 363 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_panel_templates' ), 1 ); 363 364 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_section_templates' ), 1 ); 364 365 add_action( 'customize_controls_print_footer_scripts', array( $this, 'render_control_templates' ), 1 ); … … public function refresh_nonces() { 2271 2272 } 2272 2273 2273 2274 /** 2275 * Render JS templates for all common elements. 2276 * 2277 * @since 4.7.0 2278 * @access public 2279 */ 2280 public function render_common_templates() { 2281 ?> 2282 <script type="text/html" id="tmpl-customize-notifications"> 2283 <ul> 2284 <# _.each( data.notifications, function( notification ) { #> 2285 <li class="notice notice-{{ notification.type || 'info' }} {{ data.altNotice ? 'notice-alt' : '' }} {{ notification.isDismissible ? 'is-dismissible' : '' }}" data-code="{{ notification.code }}" data-type="{{ notification.type }}"> 2286 <p>{{ notification.message || notification.code }}</p> 2287 <# if ( notification.isDismissible ) { #> 2288 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss</span></button> 2289 <# } #> 2290 </li> 2291 <# } ); #> 2292 </ul> 2293 </script> 2294 <?php 2295 } 2296 2297 /** 2274 2298 * Add a customize setting. 2275 2299 * 2276 2300 * @since 3.4.0 -
src/wp-includes/js/customize-base.js
diff --git a/src/wp-includes/js/customize-base.js b/src/wp-includes/js/customize-base.js index a1528de..8659ef5 100644
a b window.wp = window.wp || {}; 792 792 } 793 793 }); 794 794 795 /** 796 * A collection of observable notifications. 797 * 798 * @since 4.8.0 799 * @class 800 * @augments wp.customize.Values 801 */ 802 api.Notifications = api.Values.extend({ 803 804 /** 805 * The default constructor for items of the collection. 806 * 807 * @since 4.8.0 808 * @type {object} 809 */ 810 defaultConstructor: api.Notification, 811 812 /** 813 * The default template name for the notifications area. 814 * 815 * @since 4.8.0 816 * @type {string} 817 */ 818 defaultTemplateName: 'customize-notifications', 819 820 /** 821 * The notifications area parent container selector. 822 * 823 * @since 4.8.0 824 * @type {string} 825 */ 826 parentSelector: '#widgets-right', 827 828 /** 829 * Initialize notifications area. 830 * 831 * @since 4.8.0 832 * @constructor 833 * @param options 834 * @this {wp.customize.notifications} 835 */ 836 initialize: function( options ) { 837 var self = this, 838 debouncedRenderNotifications; 839 840 $.extend( this, options || {} ); 841 api.Values.prototype.initialize.call( self, null, options ); 842 843 _.bindAll( self, 'dismissNotification' ); 844 845 /* 846 * Note that this debounced/deferred rendering is needed because the 'remove' event 847 * is triggered just _before_ the notification is actually removed. 848 * Refer to: https://core.trac.wordpress.org/ticket/37269 849 */ 850 debouncedRenderNotifications = _.debounce( function() { 851 self.render(); 852 } ); 853 self.bind( 'add', function( notification ) { 854 wp.a11y.speak( notification.message, 'assertive' ); 855 debouncedRenderNotifications(); 856 } ); 857 self.bind( 'remove', debouncedRenderNotifications ); 858 }, 859 860 /** 861 * Find and return notifications area container element. 862 * 863 * @since 4.8.0 864 * @returns {jQuery} Container jQuery element or an empty DIV. 865 * @this {wp.customize.notifications} 866 */ 867 getContainer: function() { 868 var self = this, 869 parent; 870 871 if ( ! self.container ) { 872 parent = $( self.parentSelector ); 873 874 if ( ! parent || ! parent.length ) { 875 parent = $( '<div></div>' ); 876 } 877 self.container = $( '<div></div>', { 878 id: 'customize-notifications-area' 879 } ); 880 parent.prepend( self.container ); 881 882 self.container.on( 'click', '.notice-dismiss', self.dismissNotification ); 883 } 884 885 return self.container; 886 }, 887 888 /** 889 * Remove notifications area from the DOM. 890 * 891 * @since 4.8.0 892 * @this {wp.customize.notifications} 893 */ 894 destroy: function() { 895 this.container.next() 896 .css( 'top', '' ) 897 .trigger( 'customize:sidebar:updateLayout' ); 898 this.container.remove(); 899 this.container = null; 900 }, 901 902 /** 903 * Render notifications area. 904 * 905 * @since 4.8.0 906 * @this {wp.customize.notifications} 907 */ 908 render: function() { 909 var self = this, 910 container = self.getContainer(), 911 sidebar = container.next(), 912 template = wp.template( self.defaultTemplateName ), 913 notifications, containerHeight, containerInitialTop; 914 915 notifications = []; 916 self.each( function( notification ) { 917 notifications.unshift( notification ); 918 } ); 919 920 if ( _.isEmpty( notifications ) ) { 921 self.destroy(); 922 } else { 923 container.empty().append( $.trim( 924 template( { notifications: notifications } ) 925 ) ); 926 sidebar.css( 'top', '' ); 927 containerHeight = container.outerHeight() + 1; 928 containerInitialTop = parseInt( sidebar.css( 'top' ), 10 ); 929 sidebar 930 .css( 'top', containerInitialTop + containerHeight + 'px' ) 931 .trigger( 'customize:sidebar:updateLayout' ); 932 } 933 }, 934 935 /** 936 * Remove notification from the collection on user request. 937 * 938 * @since 4.8.0 939 * @param {Event} e 940 * @this {wp.customize.notifications} 941 */ 942 dismissNotification: function( e ) { 943 var self = this, 944 code = $( e.currentTarget ).closest( '[data-code]' ).data( 'code' ); 945 946 if ( code && self.has( code ) ) { 947 self.remove( code ); 948 } 949 } 950 }); 951 795 952 // The main API object is also a collection of all customizer settings. 796 953 api = $.extend( new api.Values(), api ); 797 954 -
tests/qunit/index.html
diff --git a/tests/qunit/index.html b/tests/qunit/index.html index 9a17ec2..4d4393b 100644
a b <h3 class="accordion-section-title" tabindex="0"> 134 134 <# } #> 135 135 </li> 136 136 </script> 137 <script type="text/html" id="tmpl-customize-notifications"> 138 <ul class="{{ ( data.listClass ) ? data.listClass : '' }}"> 139 <# _.each( data.notifications, function( notification ) { #> 140 <li data-code="{{ notification.code }}" data-type="{{ notification.type }}"> 141 <p>{{ notification.message || notification.code }}</p> 142 <# if ( notification.isDismissible ) { #> 143 <button type="button" class="notice-dismiss"><span class="screen-reader-text">Dismiss</span></button> 144 <# } #> 145 </li> 146 <# } ); #> 147 </ul> 148 </script> 137 149 <script type="text/html" id="tmpl-customize-control-notifications"> 138 150 <ul> 139 151 <# _.each( data.notifications, function( notification ) { #> -
tests/qunit/wp-admin/js/customize-base.js
diff --git a/tests/qunit/wp-admin/js/customize-base.js b/tests/qunit/wp-admin/js/customize-base.js index 4f726bf..fb0b5d3 100644
a b jQuery( function( $ ) { 203 203 queryParams = wp.customize.utils.parseQueryString( 'a=1&b=' ); 204 204 assert.ok( _.isEqual( queryParams, { 'a': '1', b: '' } ) ); 205 205 } ); 206 207 module( 'Customize Base: notifications collection' ); 208 test( 'Notifications collection exists', function() { 209 ok( wp.customize.notifications ); 210 equal( wp.customize.notifications.defaultConstructor, wp.customize.Notification ); 211 } ); 212 213 test( 'Notification objects are rendered as part of notifications collection', function () { 214 var container = wp.customize.notifications.getContainer(), 215 items; 216 217 wp.customize.notifications.add( 'mycode-1', new wp.customize.Notification( 'mycode-1' ) ); 218 wp.customize.notifications.render(); 219 items = container.find( 'li' ); 220 equal( items.length, 1 ); 221 equal( items.first().data( 'code' ), 'mycode-1' ); 222 223 wp.customize.notifications.add( 'mycode-2', new wp.customize.Notification( 'mycode-2', { 224 isDismissible: true 225 } ) ); 226 wp.customize.notifications.render(); 227 items = container.find( 'li' ); 228 equal( items.length, 2 ); 229 equal( items.first().data( 'code' ), 'mycode-2' ); 230 equal( items.last().data( 'code' ), 'mycode-1' ); 231 232 equal( items.first().find( '.notice-dismiss' ).length, 1 ); 233 equal( items.last().find( '.notice-dismiss' ).length, 0 ); 234 235 wp.customize.notifications.remove( 'mycode-2' ); 236 wp.customize.notifications.render(); 237 items = container.find( 'li' ); 238 equal( items.length, 1 ); 239 equal( items.first().data( 'code' ), 'mycode-1' ); 240 241 wp.customize.notifications.remove( 'mycode-1' ); 242 wp.customize.notifications.render(); 243 equal( container.parent().length, 0, 'Notifications area is removed from DOM if the collection is empty.' ); 244 } ); 206 245 });