WordPress.org

Make WordPress Core

Ticket #30737: 30737.15.diff

File 30737.15.diff, 49.6 KB (added by westonruter, 6 years ago)

https://github.com/xwp/wordpress-develop/commit/36e0899897d30903c85764f8c1eaab2b96cd42df

  • src/wp-admin/customize.php

    diff --git src/wp-admin/customize.php src/wp-admin/customize.php
    index 9be60c7..e3ea406 100644
    do_action( 'customize_controls_print_scripts' ); 
    175175        <div id="customize-preview" class="wp-full-overlay-main"></div>
    176176        <?php
    177177
    178         // Render control templates.
     178        // Render Panel, Section, and Control templates.
     179        $wp_customize->render_panel_templates();
     180        $wp_customize->render_section_templates();
    179181        $wp_customize->render_control_templates();
    180182
    181183        /**
    do_action( 'customize_controls_print_scripts' ); 
    259261
    260262        // Prepare Customize Setting objects to pass to JavaScript.
    261263        foreach ( $wp_customize->settings() as $id => $setting ) {
    262                 $settings['settings'][ $id ] = array(
    263                         'value'     => $setting->js_value(),
    264                         'transport' => $setting->transport,
    265                         'dirty'     => $setting->dirty,
    266                 );
     264                if ( $setting->check_capabilities() ) {
     265                        $settings['settings'][ $id ] = array(
     266                                'value'     => $setting->js_value(),
     267                                'transport' => $setting->transport,
     268                                'dirty'     => $setting->dirty,
     269                        );
     270                }
    267271        }
    268272
    269273        // Prepare Customize Control objects to pass to JavaScript.
    270274        foreach ( $wp_customize->controls() as $id => $control ) {
    271                 $settings['controls'][ $id ] = $control->json();
     275                if ( $control->check_capabilities() ) {
     276                        $settings['controls'][ $id ] = $control->json();
     277                }
    272278        }
    273279
    274280        // Prepare Customize Section objects to pass to JavaScript.
    275281        foreach ( $wp_customize->sections() as $id => $section ) {
    276                 $settings['sections'][ $id ] = $section->json();
     282                if ( $section->check_capabilities() ) {
     283                        $settings['sections'][ $id ] = $section->json();
     284                }
    277285        }
    278286
    279287        // Prepare Customize Panel objects to pass to JavaScript.
    280         foreach ( $wp_customize->panels() as $id => $panel ) {
    281                 $settings['panels'][ $id ] = $panel->json();
    282                 foreach ( $panel->sections as $section_id => $section ) {
    283                         $settings['sections'][ $section_id ] = $section->json();
     288        foreach ( $wp_customize->panels() as $panel_id => $panel ) {
     289                if ( $panel->check_capabilities() ) {
     290                        $settings['panels'][ $panel_id ] = $panel->json();
     291                        foreach ( $panel->sections as $section_id => $section ) {
     292                                if ( $section->check_capabilities() ) {
     293                                        $settings['sections'][ $section_id ] = $section->json();
     294                                }
     295                        }
    284296                }
    285297        }
    286298
  • src/wp-admin/js/customize-controls.js

    diff --git src/wp-admin/js/customize-controls.js src/wp-admin/js/customize-controls.js
    index 7ed3095..2d7f3ba 100644
     
    156156        Container = api.Class.extend({
    157157                defaultActiveArguments: { duration: 'fast', completeCallback: $.noop },
    158158                defaultExpandedArguments: { duration: 'fast', completeCallback: $.noop },
     159                containerType: 'container',
    159160
    160161                /**
    161162                 * @since 4.1.0
     
    168169                        container.id = id;
    169170                        container.params = {};
    170171                        $.extend( container, options || {} );
     172                        container.templateSelector = 'customize-' + container.containerType + '-' + container.params.type;
    171173                        container.container = $( container.params.content );
     174                        if ( 0 === container.container.length ) {
     175                                container.container = $( container.getContainer() );
     176                        }
    172177
    173178                        container.deferred = {
    174179                                embedded: new $.Deferred()
     
    191196                                container.onChangeExpanded( expanded, args );
    192197                        });
    193198
    194                         container.attachEvents();
     199                        container.deferred.embedded.done( function () {
     200                                container.attachEvents();
     201                        });
    195202
    196203                        api.utils.bubbleChildValueChanges( container, [ 'priority', 'active' ] );
    197204
     
    366373                 * Bring the container into view and then expand this and bring it into view
    367374                 * @param {Object} [params]
    368375                 */
    369                 focus: focus
     376                focus: focus,
     377
     378                /**
     379                 * Return the container html, generated from its JS template, if it exists.
     380                 *
     381                 * @since 4.3.0
     382                 */
     383                getContainer: function () {
     384                        var template,
     385                                container = this;
     386
     387                        if ( 0 !== $( '#tmpl-' + container.templateSelector ).length ) {
     388                                template = wp.template( container.templateSelector );
     389                                if ( template && container.container ) {
     390                                        return $.trim( template( container.params ) );
     391                                }
     392                        }
     393
     394                        return '<li></li>';
     395                }
    370396        });
    371397
    372398        /**
     
    376402         * @augments wp.customize.Class
    377403         */
    378404        api.Section = Container.extend({
     405                containerType: 'section',
    379406
    380407                /**
    381408                 * @since 4.1.0
     
    9771004         * @augments wp.customize.Class
    9781005         */
    9791006        api.Panel = Container.extend({
     1007                containerType: 'panel',
     1008
    9801009                /**
    9811010                 * @since 4.1.0
    9821011                 *
     
    10031032
    10041033                        if ( ! panel.container.parent().is( parentContainer ) ) {
    10051034                                parentContainer.append( panel.container );
     1035                                panel.renderContent();
    10061036                        }
    10071037                        panel.deferred.embedded.resolve();
    10081038                },
     
    10451075                                }
    10461076                                event.preventDefault(); // Keep this AFTER the key filter above
    10471077
     1078                                meta = panel.container.find( '.panel-meta' );
    10481079                                if ( meta.hasClass( 'cannot-expand' ) ) {
    10491080                                        return;
    10501081                                }
     
    11691200                                panelTitle.focus();
    11701201                                container.scrollTop( 0 );
    11711202                        }
     1203                },
     1204
     1205                /**
     1206                 * Render the panel from its JS template, if it exists.
     1207                 *
     1208                 * The panel's container must already exist in the DOM.
     1209                 *
     1210                 * @since 4.3.0
     1211                 */
     1212                renderContent: function () {
     1213                        var template,
     1214                                panel = this;
     1215
     1216                        // Add the content to the container.
     1217                        if ( 0 !== $( '#tmpl-' + panel.templateSelector + '-content' ).length ) {
     1218                                template = wp.template( panel.templateSelector + '-content' );
     1219                                if ( template && panel.container ) {
     1220                                        panel.container.find( '.accordion-sub-container' ).html( template( panel.params ) );
     1221                                }
     1222                        }
    11721223                }
    11731224        });
    11741225
  • src/wp-includes/class-wp-customize-manager.php

    diff --git src/wp-includes/class-wp-customize-manager.php src/wp-includes/class-wp-customize-manager.php
    index 98539b0..47c0407 100644
    final class WP_Customize_Manager { 
    6060        protected $customized;
    6161
    6262        /**
    63          * Controls that may be rendered from JS templates.
     63         * Panel types that may be rendered from JS templates.
     64         *
     65         * @since 4.3.0
     66         * @access protected
     67         * @var array
     68         */
     69        protected $registered_panel_types = array();
     70
     71        /**
     72         * Section types that may be rendered from JS templates.
     73         *
     74         * @since 4.3.0
     75         * @access protected
     76         * @var array
     77         */
     78        protected $registered_section_types = array();
     79
     80        /**
     81         * Control types that may be rendered from JS templates.
    6482         *
    6583         * @since 4.1.0
    6684         * @access protected
    final class WP_Customize_Manager { 
    612630                }
    613631
    614632                foreach ( $this->settings as $id => $setting ) {
    615                         $settings['values'][ $id ] = $setting->js_value();
     633                        if ( $setting->check_capabilities() ) {
     634                                $settings['values'][ $id ] = $setting->js_value();
     635                        }
    616636                }
    617                 foreach ( $this->panels as $id => $panel ) {
    618                         $settings['activePanels'][ $id ] = $panel->active();
    619                         foreach ( $panel->sections as $id => $section ) {
    620                                 $settings['activeSections'][ $id ] = $section->active();
     637                foreach ( $this->panels as $panel_id => $panel ) {
     638                        if ( $panel->check_capabilities() ) {
     639                                $settings['activePanels'][ $panel_id ] = $panel->active();
     640                                foreach ( $panel->sections as $section_id => $section ) {
     641                                        if ( $section->check_capabilities() ) {
     642                                                $settings['activeSections'][ $section_id ] = $section->active();
     643                                        }
     644                                }
    621645                        }
    622646                }
    623647                foreach ( $this->sections as $id => $section ) {
    624                         $settings['activeSections'][ $id ] = $section->active();
     648                        if ( $section->check_capabilities() ) {
     649                                $settings['activeSections'][ $id ] = $section->active();
     650                        }
    625651                }
    626652                foreach ( $this->controls as $id => $control ) {
    627                         $settings['activeControls'][ $id ] = $control->active();
     653                        if ( $control->check_capabilities() ) {
     654                                $settings['activeControls'][ $id ] = $control->active();
     655                        }
    628656                }
    629657
    630658                ?>
    final class WP_Customize_Manager { 
    965993        }
    966994
    967995        /**
     996         * Register a customize panel type.
     997         *
     998         * Registered types are eligible to be rendered via JS and created dynamically.
     999         *
     1000         * @since 4.3.0
     1001         * @access public
     1002         *
     1003         * @param string $panel Name of a custom panel which is a subclass of
     1004         *                        {@see WP_Customize_Panel}.
     1005         */
     1006        public function register_panel_type( $panel ) {
     1007                $this->registered_panel_types[] = $panel;
     1008        }
     1009
     1010        /**
     1011         * Render JS templates for all registered panel types.
     1012         *
     1013         * @since 4.3.0
     1014         * @access public
     1015         */
     1016        public function render_panel_templates() {
     1017                foreach ( $this->registered_panel_types as $panel_type ) {
     1018                        $panel = new $panel_type( $this, 'temp', array() );
     1019                        $panel->print_template();
     1020                }
     1021        }
     1022
     1023        /**
    9681024         * Add a customize section.
    9691025         *
    9701026         * @since 3.4.0
    final class WP_Customize_Manager { 
    10061062        }
    10071063
    10081064        /**
     1065         * Register a customize section type.
     1066         *
     1067         * Registered types are eligible to be rendered via JS and created dynamically.
     1068         *
     1069         * @since 4.3.0
     1070         * @access public
     1071         *
     1072         * @param string $section Name of a custom section which is a subclass of
     1073         *                        {@see WP_Customize_Section}.
     1074         */
     1075        public function register_section_type( $section ) {
     1076                $this->registered_section_types[] = $section;
     1077        }
     1078
     1079        /**
     1080         * Render JS templates for all registered section types.
     1081         *
     1082         * @since 4.3.0
     1083         * @access public
     1084         */
     1085        public function render_section_templates() {
     1086                foreach ( $this->registered_section_types as $section_type ) {
     1087                        $section = new $section_type( $this, 'temp', array() );
     1088                        $section->print_template();
     1089                }
     1090        }
     1091
     1092        /**
    10091093         * Add a customize control.
    10101094         *
    10111095         * @since 3.4.0
    final class WP_Customize_Manager { 
    11761260         */
    11771261        public function register_controls() {
    11781262
    1179                 /* Control Types (custom control classes) */
     1263                /* Panel, Section, and Control Types */
     1264                $this->register_panel_type( 'WP_Customize_Panel' );
     1265                $this->register_section_type( 'WP_Customize_Section' );
     1266                $this->register_section_type( 'WP_Customize_Sidebar_Section' );
    11801267                $this->register_control_type( 'WP_Customize_Color_Control' );
    11811268                $this->register_control_type( 'WP_Customize_Media_Control' );
    11821269                $this->register_control_type( 'WP_Customize_Upload_Control' );
  • src/wp-includes/class-wp-customize-panel.php

    diff --git src/wp-includes/class-wp-customize-panel.php src/wp-includes/class-wp-customize-panel.php
    index f977f06..14a47b4 100644
    class WP_Customize_Panel { 
    214214         * @return array The array to be exported to the client as JSON.
    215215         */
    216216        public function json() {
    217                 $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'type' ) );
     217                $array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'type' ) );
    218218                $array['content'] = $this->get_content();
    219219                $array['active'] = $this->active();
    220220                $array['instanceNumber'] = $this->instance_number;
    class WP_Customize_Panel { 
    289289        }
    290290
    291291        /**
    292          * Render the panel container, and then its contents.
     292         * Render the panel container, and then its contents (via `this->render_content()`) in a subclass.
     293         *
     294         * Panel containers are now rendered in JS by default, see {@see WP_Customize_Panel::print_template()}.
    293295         *
    294296         * @since 4.0.0
    295297         * @access protected
    296298         */
    297         protected function render() {
    298                 $classes = 'accordion-section control-section control-panel control-panel-' . $this->type;
     299        protected function render() {}
     300
     301        /**
     302         * Render the panel UI in a subclass.
     303         *
     304         * Panel contents are now rendered in JS by default, see {@see WP_Customize_Panel::print_template()}.
     305         *
     306         * @since 4.1.0
     307         * @access protected
     308         */
     309        protected function render_content() {}
     310
     311        /**
     312         * Render the panel's JS templates.
     313         *
     314         * This function is only run for panel types that have been registered with
     315         * {@see WP_Customize_Manager::register_panel_type()}.
     316         *
     317         * @since 4.3.0
     318         */
     319        public function print_template() {
    299320                ?>
    300                 <li id="accordion-panel-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
     321                <script type="text/html" id="tmpl-customize-panel-<?php echo esc_attr( $this->type ); ?>-content">
     322                        <?php $this->content_template(); ?>
     323                </script>
     324                <script type="text/html" id="tmpl-customize-panel-<?php echo esc_attr( $this->type ); ?>">
     325                        <?php $this->render_template(); ?>
     326                </script>
     327        <?php
     328        }
     329
     330        /**
     331         * An Underscore (JS) template for rendering this panel's container.
     332         *
     333         * Class variables for this panel class are available in the `data` JS object;
     334         * export custom variables by overriding {@see WP_Customize_Panel::json()}.
     335         *
     336         * @see WP_Customize_Panel::print_template()
     337         *
     338         * @since 4.3.0
     339         */
     340        protected function render_template() {
     341                ?>
     342                <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
    301343                        <h3 class="accordion-section-title" tabindex="0">
    302                                 <?php echo esc_html( $this->title ); ?>
     344                                {{ data.title }}
    303345                                <span class="screen-reader-text"><?php _e( 'Press return or enter to open this panel' ); ?></span>
    304346                        </h3>
    305                         <ul class="accordion-sub-container control-panel-content">
    306                                 <?php $this->render_content(); ?>
    307                         </ul>
     347                        <ul class="accordion-sub-container control-panel-content"></ul>
    308348                </li>
    309349                <?php
    310350        }
    311351
    312352        /**
    313          * Render the sections that have been added to the panel.
     353         * An Underscore (JS) template for this panel's content (but not its container).
    314354         *
    315          * @since 4.1.0
    316          * @access protected
     355         * Class variables for this panel class are available in the `data` JS object;
     356         * export custom variables by overriding {@see WP_Customize_Panel::json()}.
     357         *
     358         * @see WP_Customize_Panel::print_template()
     359         *
     360         * @since 4.3.0
    317361         */
    318         protected function render_content() {
     362        protected function content_template() {
    319363                ?>
    320                 <li class="panel-meta customize-info accordion-section<?php if ( empty( $this->description ) ) { echo ' cannot-expand'; } ?>">
     364                <li class="panel-meta customize-info accordion-section <# if ( ! data.description ) { #> cannot-expand<# } #>">
    321365                        <button class="customize-panel-back" tabindex="-1"><span class="screen-reader-text"><?php _e( 'Back' ); ?></span></button>
    322366                        <div class="accordion-section-title">
    323367                                <span class="preview-notice"><?php
    324368                                        /* translators: %s is the site/panel title in the Customizer */
    325                                         echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">' . esc_html( $this->title ) . '</strong>' );
     369                                        echo sprintf( __( 'You are customizing %s' ), '<strong class="panel-title">{{ data.title }}</strong>' );
    326370                                ?></span>
    327371                                <button class="customize-help-toggle dashicons dashicons-editor-help" tabindex="0" aria-expanded="false"><span class="screen-reader-text"><?php _e( 'Help' ); ?></span></button>
    328372                        </div>
    329                         <?php if ( ! empty( $this->description ) ) : ?>
     373                        <# if ( data.description ) { #>
    330374                                <div class="description customize-panel-description">
    331                                         <?php echo $this->description; ?>
     375                                        {{{ data.description }}}
    332376                                </div>
    333                         <?php endif; ?>
     377                        <# } #>
    334378                </li>
    335379                <?php
    336380        }
  • src/wp-includes/class-wp-customize-section.php

    diff --git src/wp-includes/class-wp-customize-section.php src/wp-includes/class-wp-customize-section.php
    index f41767c..a48f0df 100644
    class WP_Customize_Section { 
    223223         * @return array The array to be exported to the client as JSON.
    224224         */
    225225        public function json() {
    226                 $array = wp_array_slice_assoc( (array) $this, array( 'title', 'description', 'priority', 'panel', 'type' ) );
     226                $array = wp_array_slice_assoc( (array) $this, array( 'id', 'title', 'description', 'priority', 'panel', 'type' ) );
    227227                $array['content'] = $this->get_content();
    228228                $array['active'] = $this->active();
    229229                $array['instanceNumber'] = $this->instance_number;
     230
     231                if ( $this->panel ) {
     232                        /* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
     233                        $array['customizeAction'] = sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
     234                } else {
     235                        $array['customizeAction'] = __( 'Customizing' );
     236                }
     237
    230238                return $array;
    231239        }
    232240
    class WP_Customize_Section { 
    251259        }
    252260
    253261        /**
    254          * Get the section's content template for insertion into the Customizer pane.
     262         * Get the section's content for insertion into the Customizer pane.
    255263         *
    256264         * @since 4.1.0
    257265         *
    class WP_Customize_Section { 
    297305        }
    298306
    299307        /**
    300          * Render the section, and the controls that have been added to it.
     308         * Render the section UI in a subclass.
     309         *
     310         * Sections are now rendered in JS by default, see {@see WP_Customize_Section::print_template()}.
    301311         *
    302312         * @since 3.4.0
    303313         */
    304         protected function render() {
    305                 $classes = 'accordion-section control-section control-section-' . $this->type;
     314        protected function render() {}
     315
     316        /**
     317         * Render the section's JS template.
     318         *
     319         * This function is only run for section types that have been registered with
     320         * {@see WP_Customize_Manager::register_section_type()}.
     321         *
     322         * @since 4.3.0
     323         */
     324        public function print_template() {
     325        ?>
     326                <script type="text/html" id="tmpl-customize-section-<?php echo $this->type; ?>">
     327                        <?php $this->render_template(); ?>
     328                </script>
     329        <?php
     330        }
     331
     332        /**
     333         * An Underscore (JS) template for rendering this section.
     334         *
     335         * Class variables for this section class are available in the `data` JS object;
     336         * export custom variables by overriding {@see WP_Customize_Section::json()}.
     337         *
     338         * @see WP_Customize_Section::print_template()
     339         *
     340         * @since 4.3.0
     341         */
     342        protected function render_template() {
    306343                ?>
    307                 <li id="accordion-section-<?php echo esc_attr( $this->id ); ?>" class="<?php echo esc_attr( $classes ); ?>">
     344                <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
    308345                        <h3 class="accordion-section-title" tabindex="0">
    309                                 <?php echo esc_html( $this->title ); ?>
     346                                {{ data.title }}
    310347                                <span class="screen-reader-text"><?php _e( 'Press return or enter to open' ); ?></span>
    311348                        </h3>
    312349                        <ul class="accordion-section-content">
    class WP_Customize_Section { 
    316353                                                        <span class="screen-reader-text"><?php _e( 'Back' ); ?></span>
    317354                                                </button>
    318355                                                <h3>
    319                                                         <span class="customize-action"><?php
    320                                                                 if ( $this->panel ) {
    321                                                                         /* translators: &#9656; is the unicode right-pointing triangle, and %s is the section title in the Customizer */
    322                                                                         echo sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
    323                                                                 } else {
    324                                                                         _e( 'Customizing' );
    325                                                                 }
    326                                                         ?></span>
    327                                                         <?php echo esc_html( $this->title ); ?>
     356                                                        <span class="customize-action">
     357                                                                {{{ data.customizeAction }}}
     358                                                        </span>
     359                                                        {{ data.title }}
    328360                                                </h3>
    329361                                        </div>
    330                                         <?php if ( ! empty( $this->description ) ) : ?>
    331                                                 <p class="description customize-section-description"><?php echo $this->description; ?></p>
    332                                         <?php endif; ?>
     362                                        <# if ( data.description ) { #>
     363                                                <p class="description customize-section-description">{{{ data.description }}}</p>
     364                                        <# } #>
    333365                                </li>
    334366                        </ul>
    335367                </li>
  • new file tests/phpunit/tests/customize/panel.php

    diff --git tests/phpunit/tests/customize/panel.php tests/phpunit/tests/customize/panel.php
    new file mode 100644
    index 0000000..d1c3bb1
    - +  
     1<?php
     2
     3/**
     4 * Tests for the WP_Customize_Panel class.
     5 *
     6 * @group customize
     7 */
     8class Tests_WP_Customize_Panel extends WP_UnitTestCase {
     9
     10        /**
     11         * @var WP_Customize_Manager
     12         */
     13        protected $manager;
     14
     15        function setUp() {
     16                parent::setUp();
     17                require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
     18                $GLOBALS['wp_customize'] = new WP_Customize_Manager();
     19                $this->manager = $GLOBALS['wp_customize'];
     20                $this->undefined = new stdClass();
     21        }
     22
     23        function tearDown() {
     24                $this->manager = null;
     25                unset( $GLOBALS['wp_customize'] );
     26                parent::tearDown();
     27        }
     28
     29        /**
     30         * @see WP_Customize_Panel::__construct()
     31         */
     32        function test_construct_default_args() {
     33                $panel = new WP_Customize_Panel( $this->manager, 'foo' );
     34                $this->assertInternalType( 'int', $panel->instance_number );
     35                $this->assertEquals( $this->manager, $panel->manager );
     36                $this->assertEquals( 'foo', $panel->id );
     37                $this->assertEquals( 160, $panel->priority );
     38                $this->assertEquals( 'edit_theme_options', $panel->capability );
     39                $this->assertEquals( '', $panel->theme_supports );
     40                $this->assertEquals( '', $panel->title );
     41                $this->assertEquals( '', $panel->description );
     42                $this->assertEmpty( $panel->sections );
     43                $this->assertEquals( 'default', $panel->type );
     44                $this->assertEquals( array( $panel, 'active_callback' ), $panel->active_callback );
     45        }
     46
     47        /**
     48         * @see WP_Customize_Panel::__construct()
     49         */
     50        function test_construct_custom_args() {
     51                $args = array(
     52                        'priority' => 200,
     53                        'capability' => 'edit_posts',
     54                        'theme_supports' => 'html5',
     55                        'title' => 'Hello World',
     56                        'description' => 'Lorem Ipsum',
     57                        'type' => 'horizontal',
     58                        'active_callback' => '__return_true',
     59                );
     60
     61                $panel = new WP_Customize_Panel( $this->manager, 'foo', $args );
     62                foreach ( $args as $key => $value ) {
     63                        $this->assertEquals( $value, $panel->$key );
     64                }
     65        }
     66
     67        /**
     68         * @see WP_Customize_Panel::__construct()
     69         */
     70        function test_construct_custom_type() {
     71                $panel = new Custom_Panel_Test( $this->manager, 'foo' );
     72                $this->assertEquals( 'titleless', $panel->type );
     73        }
     74
     75        /**
     76         * @see WP_Customize_Panel::active()
     77         * @see WP_Customize_Panel::active_callback()
     78         */
     79        function test_active() {
     80                $panel = new WP_Customize_Panel( $this->manager, 'foo' );
     81                $this->assertTrue( $panel->active() );
     82
     83                $panel = new WP_Customize_Panel( $this->manager, 'foo', array(
     84                        'active_callback' => '__return_false',
     85                ) );
     86                $this->assertFalse( $panel->active() );
     87                add_filter( 'customize_panel_active', array( $this, 'filter_active_test' ), 10, 2 );
     88                $this->assertTrue( $panel->active() );
     89        }
     90
     91        /**
     92         * @param bool $active
     93         * @param WP_Customize_Panel $panel
     94         * @return bool
     95         */
     96        function filter_active_test( $active, $panel ) {
     97                $this->assertFalse( $active );
     98                $this->assertInstanceOf( 'WP_Customize_Panel', $panel );
     99                $active = true;
     100                return $active;
     101        }
     102
     103        /**
     104         * @see WP_Customize_Panel::json()
     105         */
     106        function test_json() {
     107                $args = array(
     108                        'priority' => 200,
     109                        'capability' => 'edit_posts',
     110                        'theme_supports' => 'html5',
     111                        'title' => 'Hello World',
     112                        'description' => 'Lorem Ipsum',
     113                        'type' => 'horizontal',
     114                        'active_callback' => '__return_true',
     115                );
     116                $panel = new WP_Customize_Panel( $this->manager, 'foo', $args );
     117                $data = $panel->json();
     118                $this->assertEquals( 'foo', $data['id'] );
     119                foreach ( array( 'title', 'description', 'priority', 'type' ) as $key ) {
     120                        $this->assertEquals( $args[ $key ], $data[ $key ] );
     121                }
     122                $this->assertEmpty( $data['content'] );
     123                $this->assertTrue( $data['active'] );
     124                $this->assertInternalType( 'int', $data['instanceNumber'] );
     125        }
     126
     127        /**
     128         * @see WP_Customize_Panel::check_capabilities()
     129         */
     130        function test_check_capabilities() {
     131                $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
     132                wp_set_current_user( $user_id );
     133
     134                $panel = new WP_Customize_Panel( $this->manager, 'foo' );
     135                $this->assertTrue( $panel->check_capabilities() );
     136                $old_cap = $panel->capability;
     137                $panel->capability = 'do_not_allow';
     138                $this->assertFalse( $panel->check_capabilities() );
     139                $panel->capability = $old_cap;
     140                $this->assertTrue( $panel->check_capabilities() );
     141                $panel->theme_supports = 'impossible_feature';
     142                $this->assertFalse( $panel->check_capabilities() );
     143        }
     144
     145        /**
     146         * @see WP_Customize_Panel::get_content()
     147         */
     148        function test_get_content() {
     149                $panel = new WP_Customize_Panel( $this->manager, 'foo' );
     150                $this->assertEmpty( $panel->get_content() );
     151        }
     152
     153        /**
     154         * @see WP_Customize_Panel::maybe_render()
     155         */
     156        function test_maybe_render() {
     157                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     158                $panel = new WP_Customize_Panel( $this->manager, 'bar' );
     159                $customize_render_panel_count = did_action( 'customize_render_panel' );
     160                add_action( 'customize_render_panel', array( $this, 'action_customize_render_panel_test' ) );
     161                ob_start();
     162                $panel->maybe_render();
     163                $content = ob_get_clean();
     164                $this->assertTrue( $panel->check_capabilities() );
     165                $this->assertEmpty( $content );
     166                $this->assertEquals( $customize_render_panel_count + 1, did_action( 'customize_render_panel' ), 'Unexpected did_action count for customize_render_panel' );
     167                $this->assertEquals( 1, did_action( "customize_render_panel_{$panel->id}" ), "Unexpected did_action count for customize_render_panel_{$panel->id}" );
     168        }
     169
     170        /**
     171         * @see WP_Customize_Panel::maybe_render()
     172         * @param WP_Customize_Panel $panel
     173         */
     174        function action_customize_render_panel_test( $panel ) {
     175                $this->assertInstanceOf( 'WP_Customize_Panel', $panel );
     176        }
     177
     178        /**
     179         * @see WP_Customize_Panel::print_template()
     180         */
     181        function test_print_templates_standard() {
     182                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     183
     184                $panel = new WP_Customize_Panel( $this->manager, 'baz' );
     185                ob_start();
     186                $panel->print_template();
     187                $content = ob_get_clean();
     188                $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-default-content">', $content );
     189                $this->assertContains( 'accordion-section-title', $content );
     190                $this->assertContains( 'control-panel-content', $content );
     191                $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-default">', $content );
     192                $this->assertContains( 'customize-panel-description', $content );
     193                $this->assertContains( 'preview-notice', $content );
     194        }
     195
     196        /**
     197         * @see WP_Customize_Panel::print_template()
     198         */
     199        function test_print_templates_custom() {
     200                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     201
     202                $panel = new Custom_Panel_Test( $this->manager, 'baz' );
     203                ob_start();
     204                $panel->print_template();
     205                $content = ob_get_clean();
     206                $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-titleless-content">', $content );
     207                $this->assertNotContains( 'accordion-section-title', $content );
     208
     209                $this->assertContains( '<script type="text/html" id="tmpl-customize-panel-titleless">', $content );
     210                $this->assertNotContains( 'preview-notice', $content );
     211        }
     212}
     213
     214require_once ABSPATH . WPINC . '/class-wp-customize-panel.php';
     215class Custom_Panel_Test extends WP_Customize_Panel {
     216        public $type = 'titleless';
     217
     218        protected function render_template() {
     219                ?>
     220                <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
     221                        <ul class="accordion-sub-container control-panel-content"></ul>
     222                </li>
     223                <?php
     224        }
     225
     226        protected function content_template() {
     227                ?>
     228                <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
     229                        <# if ( data.description ) { #>
     230                                <div class="accordion-section-content description">
     231                                        {{{ data.description }}}
     232                                </div>
     233                        <# } #>
     234                </li>
     235                <?php
     236        }
     237
     238}
  • new file tests/phpunit/tests/customize/section.php

    diff --git tests/phpunit/tests/customize/section.php tests/phpunit/tests/customize/section.php
    new file mode 100644
    index 0000000..82343e7
    - +  
     1<?php
     2
     3/**
     4 * Tests for the WP_Customize_Section class.
     5 *
     6 * @group customize
     7 */
     8class Tests_WP_Customize_Section extends WP_UnitTestCase {
     9
     10        /**
     11         * @var WP_Customize_Manager
     12         */
     13        protected $manager;
     14
     15        function setUp() {
     16                parent::setUp();
     17                require_once( ABSPATH . WPINC . '/class-wp-customize-manager.php' );
     18                $GLOBALS['wp_customize'] = new WP_Customize_Manager();
     19                $this->manager = $GLOBALS['wp_customize'];
     20                $this->undefined = new stdClass();
     21        }
     22
     23        function tearDown() {
     24                $this->manager = null;
     25                unset( $GLOBALS['wp_customize'] );
     26                parent::tearDown();
     27        }
     28
     29        /**
     30         * @see WP_Customize_Section::__construct()
     31         */
     32        function test_construct_default_args() {
     33                $section = new WP_Customize_Section( $this->manager, 'foo' );
     34                $this->assertInternalType( 'int', $section->instance_number );
     35                $this->assertEquals( $this->manager, $section->manager );
     36                $this->assertEquals( 'foo', $section->id );
     37                $this->assertEquals( 160, $section->priority );
     38                $this->assertEquals( 'edit_theme_options', $section->capability );
     39                $this->assertEquals( '', $section->theme_supports );
     40                $this->assertEquals( '', $section->title );
     41                $this->assertEquals( '', $section->description );
     42                $this->assertEmpty( $section->panel );
     43                $this->assertEquals( 'default', $section->type );
     44                $this->assertEquals( array( $section, 'active_callback' ), $section->active_callback );
     45        }
     46
     47        /**
     48         * @see WP_Customize_Section::__construct()
     49         */
     50        function test_construct_custom_args() {
     51                $args = array(
     52                        'priority' => 200,
     53                        'capability' => 'edit_posts',
     54                        'theme_supports' => 'html5',
     55                        'title' => 'Hello World',
     56                        'description' => 'Lorem Ipsum',
     57                        'type' => 'horizontal',
     58                        'active_callback' => '__return_true',
     59                        'panel' => 'bar',
     60                );
     61
     62                $this->manager->add_panel( 'bar' );
     63
     64                $section = new WP_Customize_Section( $this->manager, 'foo', $args );
     65                foreach ( $args as $key => $value ) {
     66                        $this->assertEquals( $value, $section->$key );
     67                }
     68        }
     69
     70        /**
     71         * @see WP_Customize_Section::__construct()
     72         */
     73        function test_construct_custom_type() {
     74                $section = new Custom_Section_Test( $this->manager, 'foo' );
     75                $this->assertEquals( 'titleless', $section->type );
     76        }
     77
     78        /**
     79         * @see WP_Customize_Section::active()
     80         * @see WP_Customize_Section::active_callback()
     81         */
     82        function test_active() {
     83                $section = new WP_Customize_Section( $this->manager, 'foo' );
     84                $this->assertTrue( $section->active() );
     85
     86                $section = new WP_Customize_Section( $this->manager, 'foo', array(
     87                        'active_callback' => '__return_false',
     88                ) );
     89                $this->assertFalse( $section->active() );
     90                add_filter( 'customize_section_active', array( $this, 'filter_active_test' ), 10, 2 );
     91                $this->assertTrue( $section->active() );
     92        }
     93
     94        /**
     95         * @param bool $active
     96         * @param WP_Customize_Section $section
     97         * @return bool
     98         */
     99        function filter_active_test( $active, $section ) {
     100                $this->assertFalse( $active );
     101                $this->assertInstanceOf( 'WP_Customize_Section', $section );
     102                $active = true;
     103                return $active;
     104        }
     105
     106        /**
     107         * @see WP_Customize_Section::json()
     108         */
     109        function test_json() {
     110                $args = array(
     111                        'priority' => 200,
     112                        'capability' => 'edit_posts',
     113                        'theme_supports' => 'html5',
     114                        'title' => 'Hello World',
     115                        'description' => 'Lorem Ipsum',
     116                        'type' => 'horizontal',
     117                        'panel' => 'bar',
     118                        'active_callback' => '__return_true',
     119                );
     120
     121                $this->manager->add_panel( 'bar' );
     122
     123                $section = new WP_Customize_Section( $this->manager, 'foo', $args );
     124                $data = $section->json();
     125                $this->assertEquals( 'foo', $data['id'] );
     126                foreach ( array( 'title', 'description', 'priority', 'panel', 'type' ) as $key ) {
     127                        $this->assertEquals( $args[ $key ], $data[ $key ] );
     128                }
     129                $this->assertEmpty( $data['content'] );
     130                $this->assertTrue( $data['active'] );
     131                $this->assertInternalType( 'int', $data['instanceNumber'] );
     132        }
     133
     134        /**
     135         * @see WP_Customize_Section::check_capabilities()
     136         */
     137        function test_check_capabilities() {
     138                $user_id = $this->factory->user->create( array( 'role' => 'administrator' ) );
     139                wp_set_current_user( $user_id );
     140
     141                $section = new WP_Customize_Section( $this->manager, 'foo' );
     142                $this->assertTrue( $section->check_capabilities() );
     143                $old_cap = $section->capability;
     144                $section->capability = 'do_not_allow';
     145                $this->assertFalse( $section->check_capabilities() );
     146                $section->capability = $old_cap;
     147                $this->assertTrue( $section->check_capabilities() );
     148                $section->theme_supports = 'impossible_feature';
     149                $this->assertFalse( $section->check_capabilities() );
     150        }
     151
     152        /**
     153         * @see WP_Customize_Section::get_content()
     154         */
     155        function test_get_content() {
     156                $section = new WP_Customize_Section( $this->manager, 'foo' );
     157                $this->assertEmpty( $section->get_content() );
     158        }
     159
     160        /**
     161         * @see WP_Customize_Section::maybe_render()
     162         */
     163        function test_maybe_render() {
     164                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     165                $section = new WP_Customize_Section( $this->manager, 'bar' );
     166                $customize_render_section_count = did_action( 'customize_render_section' );
     167                add_action( 'customize_render_section', array( $this, 'action_customize_render_section_test' ) );
     168                ob_start();
     169                $section->maybe_render();
     170                $content = ob_get_clean();
     171                $this->assertTrue( $section->check_capabilities() );
     172                $this->assertEmpty( $content );
     173                $this->assertEquals( $customize_render_section_count + 1, did_action( 'customize_render_section' ), 'Unexpected did_action count for customize_render_section' );
     174                $this->assertEquals( 1, did_action( "customize_render_section_{$section->id}" ), "Unexpected did_action count for customize_render_section_{$section->id}" );
     175        }
     176
     177        /**
     178         * @see WP_Customize_Section::maybe_render()
     179         * @param WP_Customize_Section $section
     180         */
     181        function action_customize_render_section_test( $section ) {
     182                $this->assertInstanceOf( 'WP_Customize_Section', $section );
     183        }
     184
     185        /**
     186         * @see WP_Customize_Section::print_template()
     187         */
     188        function test_print_templates_standard() {
     189                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     190
     191                $section = new WP_Customize_Section( $this->manager, 'baz' );
     192                ob_start();
     193                $section->print_template();
     194                $content = ob_get_clean();
     195                $this->assertContains( '<script type="text/html" id="tmpl-customize-section-default">', $content );
     196                $this->assertContains( 'accordion-section-title', $content );
     197                $this->assertContains( 'accordion-section-content', $content );
     198        }
     199
     200        /**
     201         * @see WP_Customize_Section::print_template()
     202         */
     203        function test_print_templates_custom() {
     204                wp_set_current_user( $this->factory->user->create( array( 'role' => 'administrator' ) ) );
     205
     206                $section = new Custom_Section_Test( $this->manager, 'baz' );
     207                ob_start();
     208                $section->print_template();
     209                $content = ob_get_clean();
     210                $this->assertContains( '<script type="text/html" id="tmpl-customize-section-titleless">', $content );
     211                $this->assertNotContains( 'accordion-section-title', $content );
     212                $this->assertContains( 'accordion-section-content', $content );
     213        }
     214}
     215
     216require_once ABSPATH . WPINC . '/class-wp-customize-section.php';
     217class Custom_Section_Test extends WP_Customize_Section {
     218        public $type = 'titleless';
     219
     220        protected function render_template() {
     221                ?>
     222                <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
     223                        <ul class="accordion-section-content">
     224                                <# if ( data.description ) { #>
     225                                        <li class="customize-section-description-container">
     226                                                <p class="description customize-section-description">{{{ data.description }}}</p>
     227                                        </li>
     228                                <# } #>
     229                        </ul>
     230                </li>
     231                <?php
     232        }
     233
     234}
  • tests/qunit/fixtures/customize-settings.js

    diff --git tests/qunit/fixtures/customize-settings.js tests/qunit/fixtures/customize-settings.js
    index 993f767..5ef44a1 100644
    window._wpCustomizeSettings = { 
    1515        'controls': {
    1616                'fixture-control': {
    1717                        'active': true,
    18                         'content': '<li id="customize-control-fixture-control" class="customize-control customize-control-text">\n\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t\t\t<span class="customize-control-title">Site Title</span>\n\t\t\t\t\t\t\t\t\t\t<input type="text"  value="sWordPress Developssa!" data-customize-setting-link="blogname" />\n\t\t\t\t</label>\n\t\t\t\t\t\t</li>',
     18                        'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>',
    1919                        'description': '',
    2020                        'instanceNumber': 8,
    2121                        'label': 'Fixture Control',
    window._wpCustomizeSettings = { 
    3535        'panels': {
    3636                'fixture-panel': {
    3737                        'active': true,
    38                         'content': '<li id="accordion-panel-fixture-panel" class="accordion-section control-section control-panel control-panel-default">\n\t\t\t<h3 class="accordion-section-title" tabindex="0">\n\t\t\t\tLipsum\t\t\t\t<span class="screen-reader-text">Press return or enter to open this panel</span>\n\t\t\t</h3>\n\t\t\t<ul class="accordion-sub-container control-panel-content">\n\t\t\t\t\t\t<li class="panel-meta accordion-section control-section">\n\t\t\t<div class="accordion-section-title" tabindex="0">\n\t\t\t\t<span class="preview-notice">You are customizing <strong class="panel-title">Lipsum</strong></span>\n\t\t\t</div>\n\t\t\t\t\t\t\t<div class="accordion-section-content description">\n\t\t\t\t\tLorem Ipsum\t\t\t\t</div>\n\t\t\t\t\t</li>\n\t\t\t\t\t</ul>\n\t\t</li>',
     38                        'content': '<li id="accordion-panel-fixture-panel" class="accordion-section control-section control-panel control-panel-default"> <h3 class="accordion-section-title" tabindex="0"> Fixture Panel <span class="screen-reader-text">Press return or enter to open this panel</span> </h3> <ul class="accordion-sub-container control-panel-content"> <li class="panel-meta customize-info accordion-section cannot-expand"> <button class="customize-panel-back" tabindex="-1"><span class="screen-reader-text">Back</span></button> <div class="accordion-section-title"> <span class="preview-notice">You are customizing <strong class="panel-title">Fixture Panel</strong></span> <button class="customize-help-toggle dashicons dashicons-editor-help" tabindex="0" aria-expanded="false"><span class="screen-reader-text">Help</span></button> </div> </li> </ul> </li>',
    3939                        'description': 'Lorem ipsum',
    4040                        'instanceNumber': 1,
    4141                        'priority': 110,
    42                         'title': 'Lorem Ipsum',
     42                        'title': 'Fixture panel with content',
    4343                        'type': 'default'
     44                },
     45                'fixture-panel-default-templated': {
     46                        'active': true,
     47                        'description': 'Lorem ipsum',
     48                        'instanceNumber': 2,
     49                        'priority': 110,
     50                        'title': 'Fixture default panel using template',
     51                        'type': 'default'
     52                },
     53                'fixture-panel-titleless-templated': {
     54                        'active': true,
     55                        'description': 'Lorem ipsum',
     56                        'instanceNumber': 3,
     57                        'priority': 110,
     58                        'title': 'Fixture titleless panel using template',
     59                        'type': 'titleless'
    4460                }
    4561        },
    4662        'sections': {
    4763                'fixture-section': {
    4864                        'active': true,
    49                         'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default">\n\t\t\t<h3 class="accordion-section-title" tabindex="0">\n\t\t\t\tSite Title &amp; Tagline\t\t\t\t<span class="screen-reader-text">Press return or enter to expand</span>\n\t\t\t</h3>\n\t\t\t<ul class="accordion-section-content">\n\t\t\t\t\t\t\t</ul>\n\t\t</li>',
     65                        'content': '<li id="accordion-section-fixture-section" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>',
    5066                        'description': '',
    5167                        'instanceNumber': 2,
    5268                        'panel': 'fixture-panel',
    5369                        'priority': 20,
    5470                        'title': 'Fixture Section',
    5571                        'type': 'default'
     72                },
     73                'fixture-section-default-templated': {
     74                        'active': true,
     75                        'description': '',
     76                        'instanceNumber': 3,
     77                        'panel': 'fixture-panel',
     78                        'priority': 20,
     79                        'title': 'Fixture default section using template',
     80                        'type': 'default'
     81                },
     82                'fixture-section-titleless-templated': {
     83                        'active': true,
     84                        'description': '',
     85                        'instanceNumber': 4,
     86                        'panel': 'fixture-panel',
     87                        'priority': 20,
     88                        'title': 'Fixture titleless section using template',
     89                        'type': 'titleless'
    5690                }
    5791        },
    5892        'settings': {
  • tests/qunit/index.html

    diff --git tests/qunit/index.html tests/qunit/index.html
    index 55b95fe..2adf20b 100644
     
    99                <script src="../../src/wp-includes/js/underscore.min.js"></script>
    1010                <script src="../../src/wp-includes/js/backbone.min.js"></script>
    1111                <script src="../../src/wp-includes/js/zxcvbn.min.js"></script>
     12                <script src="../../src/wp-includes/js/wp-util.js"></script>
    1213
    1314                <!-- QUnit -->
    1415                <link rel="stylesheet" href="vendor/qunit.css" type="text/css" media="screen" />
     
    3940                <script src="wp-includes/js/shortcode.js"></script>
    4041                <script src="wp-admin/js/customize-controls.js"></script>
    4142                <script src="wp-admin/js/customize-controls-utils.js"></script>
     43
     44                <!-- Customizer templates for sections -->
     45                <script type="text/html" id="tmpl-customize-section-default">
     46                        <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
     47                                <h3 class="accordion-section-title" tabindex="0">
     48                                        {{ data.title }}
     49                                        <span class="screen-reader-text">Press return or enter to expand</span>
     50                                </h3>
     51                                <ul class="accordion-section-content">
     52                                        <# if ( data.description ) { #>
     53                                                <li class="customize-section-description-container">
     54                                                        <p class="description customize-section-description">{{{ data.description }}}</p>
     55                                                </li>
     56                                        <# } #>
     57                                </ul>
     58                        </li>
     59                </script>
     60                <script type="text/html" id="tmpl-customize-section-titleless">
     61                        <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
     62                                <!-- Notice the lack of an h3 with title displayed inside. -->
     63                                <ul class="accordion-section-content">
     64                                        <# if ( data.description ) { #>
     65                                                <li class="customize-section-description-container">
     66                                                        <p class="description customize-section-description">{{{ data.description }}}</p>
     67                                                </li>
     68                                        <# } #>
     69                                </ul>
     70                        </li>
     71                </script>
     72
     73                <!-- Customizer templates for panels -->
     74                <script type="text/html" id="tmpl-customize-panel-default">
     75                        <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
     76                                <h3 class="accordion-section-title" tabindex="0">
     77                                        {{ data.title }}
     78                                        <span class="screen-reader-text">Press return or enter to open this panel</span>
     79                                </h3>
     80                                <ul class="accordion-sub-container control-panel-content"></ul>
     81                        </li>
     82                </script>
     83                <script type="text/html" id="tmpl-customize-panel-default-content">
     84                        <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
     85                                <div class="accordion-section-title" tabindex="0">
     86                                        <span class="preview-notice">You are customizing <strong class="panel-title">{{ data.title }}</strong></span>
     87                                </div>
     88                                <# if ( data.description ) { #>
     89                                        <div class="accordion-section-content description">
     90                                                {{{ data.description }}}
     91                                        </div>
     92                                <# } #>
     93                        </li>
     94                </script>
     95                <script type="text/html" id="tmpl-customize-panel-titleless">
     96                        <li id="accordion-panel-{{ data.id }}" class="accordion-section control-section control-panel control-panel-{{ data.type }}">
     97                                <!-- Notice the lack of an h3 with title displayed inside. -->
     98                                <ul class="accordion-sub-container control-panel-content"></ul>
     99                        </li>
     100                </script>
     101                <script type="text/html" id="tmpl-customize-panel-titleless-content">
     102                        <li class="panel-meta accordion-section control-section<# if ( ! data.description ) { #> cannot-expand<# } #>">
     103                                <!-- Notice lack of title containing preview notice -->
     104                                <# if ( data.description ) { #>
     105                                        <div class="accordion-section-content description">
     106                                                {{{ data.description }}}
     107                                        </div>
     108                                <# } #>
     109                        </li>
     110                </script>
    42111        </body>
    43112</html>
  • tests/qunit/wp-admin/js/customize-controls.js

    diff --git tests/qunit/wp-admin/js/customize-controls.js tests/qunit/wp-admin/js/customize-controls.js
    index cb8767f..215d468 100644
    jQuery( window ).load( function (){ 
    9595                equal( control.section(), 'fixture-section' );
    9696        } );
    9797
     98        // Begin sections.
    9899        module( 'Customizer Section in Fixture' );
    99100        test( 'Fixture section exists', function () {
    100101                ok( wp.customize.section.has( 'fixture-section' ) );
    101102        } );
    102103        test( 'Fixture section has control among controls()', function () {
    103104                var section = wp.customize.section( 'fixture-section' );
    104                 equal( section.controls().length, 1 );
    105                 equal( section.controls()[0].id, 'fixture-control' );
     105                ok( -1 !== _.pluck( section.controls(), 'id' ).indexOf( 'fixture-control' ) );
    106106        } );
    107         test( 'Fixture section has control among controls()', function () {
     107        test( 'Fixture section has has expected panel', function () {
    108108                var section = wp.customize.section( 'fixture-section' );
    109109                equal( section.panel(), 'fixture-panel' );
    110110        } );
    111111
     112        module( 'Customizer Default Section with Template in Fixture' );
     113        test( 'Fixture section exists', function () {
     114                ok( wp.customize.section.has( 'fixture-section-default-templated' ) );
     115        } );
     116        test( 'Fixture section has expected content', function () {
     117                var id = 'fixture-section-default-templated', section;
     118                section = wp.customize.section( id );
     119                ok( ! section.params.content );
     120                ok( !! section.container );
     121                ok( section.container.is( '.control-section.control-section-default' ) );
     122                ok( 1 === section.container.find( '> .accordion-section-title' ).length );
     123                ok( 1 === section.container.find( '> .accordion-section-content' ).length );
     124        } );
     125
     126        module( 'Customizer Custom Type (titleless) Section with Template in Fixture' );
     127        test( 'Fixture section exists', function () {
     128                ok( wp.customize.section.has( 'fixture-section-titleless-templated' ) );
     129        } );
     130        test( 'Fixture section has expected content', function () {
     131                var id = 'fixture-section-titleless-templated', section;
     132                section = wp.customize.section( id );
     133                ok( ! section.params.content );
     134                ok( !! section.container );
     135                ok( section.container.is( '.control-section.control-section-titleless' ) );
     136                ok( 0 === section.container.find( '> .accordion-section-title' ).length );
     137                ok( 1 === section.container.find( '> .accordion-section-content' ).length );
     138        } );
     139
     140        // Begin panels.
    112141        module( 'Customizer Panel in Fixture' );
    113142        test( 'Fixture panel exists', function () {
    114143                ok( wp.customize.panel.has( 'fixture-panel' ) );
    115144        } );
    116         test( 'Fixture section has control among controls()', function () {
     145        test( 'Fixture panel has content', function () {
     146                var panel = wp.customize.panel( 'fixture-panel' );
     147                ok( !! panel.params.content );
     148                ok( !! panel.container );
     149        } );
     150        test( 'Fixture panel has section among its sections()', function () {
    117151                var panel = wp.customize.panel( 'fixture-panel' );
    118                 equal( panel.sections().length, 1 );
    119                 equal( panel.sections()[0].id, 'fixture-section' );
     152                ok( -1 !== _.pluck( panel.sections(), 'id' ).indexOf( 'fixture-section' ) );
    120153        } );
    121154        test( 'Panel is not expanded by default', function () {
    122155                var panel = wp.customize.panel( 'fixture-panel' );
    jQuery( window ).load( function (){ 
    138171                ok( panel.expanded() );
    139172        } );
    140173
     174        module( 'Customizer Default Panel with Template in Fixture' );
     175        test( 'Fixture section exists', function () {
     176                ok( wp.customize.panel.has( 'fixture-panel-default-templated' ) );
     177        } );
     178        test( 'Fixture panel has expected content', function () {
     179                var id = 'fixture-panel-default-templated', panel;
     180                panel = wp.customize.panel( id );
     181                ok( ! panel.params.content );
     182                ok( !! panel.container );
     183                ok( panel.container.is( '.control-panel.control-panel-default' ) );
     184                ok( 1 === panel.container.find( '> .accordion-section-title' ).length );
     185                ok( 1 === panel.container.find( '> .control-panel-content' ).length );
     186        } );
     187
     188        module( 'Customizer Custom Type Panel (titleless) with Template in Fixture' );
     189        test( 'Fixture panel exists', function () {
     190                ok( wp.customize.panel.has( 'fixture-panel-titleless-templated' ) );
     191        } );
     192        test( 'Fixture panel has expected content', function () {
     193                var id = 'fixture-panel-titleless-templated', panel;
     194                panel = wp.customize.panel( id );
     195                ok( ! panel.params.content );
     196                ok( !! panel.container );
     197                ok( panel.container.is( '.control-panel.control-panel-titleless' ) );
     198                ok( 0 === panel.container.find( '> .accordion-section-title' ).length );
     199                ok( 1 === panel.container.find( '> .control-panel-content' ).length );
     200        } );
     201
    141202
    142203        module( 'Dynamically-created Customizer Setting Model' );
    143204        settingId = 'new_blogname';
    jQuery( window ).load( function (){ 
    160221        module( 'Dynamically-created Customizer Section Model' );
    161222
    162223        sectionId = 'mock_title_tagline';
    163         sectionContent = '<li id="accordion-section-mock_title_tagline" class="control-section accordion-section"></li>';
     224        sectionContent = '<li id="accordion-section-mock_title_tagline" class="accordion-section control-section control-section-default"> <h3 class="accordion-section-title" tabindex="0"> Section Fixture <span class="screen-reader-text">Press return or enter to open</span> </h3> <ul class="accordion-section-content"> <li class="customize-section-description-container"> <div class="customize-section-title"> <button class="customize-section-back" tabindex="-1"> <span class="screen-reader-text">Back</span> </button> <h3> <span class="customize-action">Customizing &#9656; Fixture Panel</span> Section Fixture </h3> </div> </li> </ul> </li>';
    164225        sectionData = {
    165226                content: sectionContent,
    166                 active: true
     227                active: true,
     228                type: 'default'
    167229        };
    168230
    169231        mockSection = new wp.customize.Section( sectionId, { params: sectionData } );
    jQuery( window ).load( function (){ 
    277339                content: panelContent,
    278340                title: panelTitle,
    279341                description: panelDescription,
    280                 active: true // @todo This should default to true
     342                active: true, // @todo This should default to true
     343                type: 'default'
    281344        };
    282345
    283346        mockPanel = new wp.customize.Panel( panelId, { params: panelData } );