Make WordPress Core

source: trunk/src/wp-includes/class-wp-customize-section.php

Last change on this file was 59224, checked in by joedolson, 18 months ago

Administration: A11y: Fix accordion accessibility.

Change accordions in the customizer and the navigation menus to make proper usage of accordion markup patterns. This includes adding missing :focus states, using a button element to control tabbing and interaction, instead of the heading elements, and removing instructional text for screen reader users that was used to compensate for the incorrect markup pattern.

Props afercia, rishishah, kushang78, rcreators, krupajnanda, hmbashar, joedolson.
Fixes #42002.

  • Property svn:eol-style set to native
File size: 10.9 KB
Line 
1<?php
2/**
3 * WordPress Customize Section classes
4 *
5 * @package WordPress
6 * @subpackage Customize
7 * @since 3.4.0
8 */
9
10/**
11 * Customize Section class.
12 *
13 * A UI container for controls, managed by the WP_Customize_Manager class.
14 *
15 * @since 3.4.0
16 *
17 * @see WP_Customize_Manager
18 */
19#[AllowDynamicProperties]
20class WP_Customize_Section {
21
22        /**
23         * Incremented with each new class instantiation, then stored in $instance_number.
24         *
25         * Used when sorting two instances whose priorities are equal.
26         *
27         * @since 4.1.0
28         * @var int
29         */
30        protected static $instance_count = 0;
31
32        /**
33         * Order in which this instance was created in relation to other instances.
34         *
35         * @since 4.1.0
36         * @var int
37         */
38        public $instance_number;
39
40        /**
41         * WP_Customize_Manager instance.
42         *
43         * @since 3.4.0
44         * @var WP_Customize_Manager
45         */
46        public $manager;
47
48        /**
49         * Unique identifier.
50         *
51         * @since 3.4.0
52         * @var string
53         */
54        public $id;
55
56        /**
57         * Priority of the section which informs load order of sections.
58         *
59         * @since 3.4.0
60         * @var int
61         */
62        public $priority = 160;
63
64        /**
65         * Panel in which to show the section, making it a sub-section.
66         *
67         * @since 4.0.0
68         * @var string
69         */
70        public $panel = '';
71
72        /**
73         * Capability required for the section.
74         *
75         * @since 3.4.0
76         * @var string
77         */
78        public $capability = 'edit_theme_options';
79
80        /**
81         * Theme features required to support the section.
82         *
83         * @since 3.4.0
84         * @var string|string[]
85         */
86        public $theme_supports = '';
87
88        /**
89         * Title of the section to show in UI.
90         *
91         * @since 3.4.0
92         * @var string
93         */
94        public $title = '';
95
96        /**
97         * Description to show in the UI.
98         *
99         * @since 3.4.0
100         * @var string
101         */
102        public $description = '';
103
104        /**
105         * Customizer controls for this section.
106         *
107         * @since 3.4.0
108         * @var array
109         */
110        public $controls;
111
112        /**
113         * Type of this section.
114         *
115         * @since 4.1.0
116         * @var string
117         */
118        public $type = 'default';
119
120        /**
121         * Active callback.
122         *
123         * @since 4.1.0
124         *
125         * @see WP_Customize_Section::active()
126         *
127         * @var callable Callback is called with one argument, the instance of
128         *               WP_Customize_Section, and returns bool to indicate whether
129         *               the section is active (such as it relates to the URL currently
130         *               being previewed).
131         */
132        public $active_callback = '';
133
134        /**
135         * Show the description or hide it behind the help icon.
136         *
137         * @since 4.7.0
138         *
139         * @var bool Indicates whether the Section's description should be
140         *           hidden behind a help icon ("?") in the Section header,
141         *           similar to how help icons are displayed on Panels.
142         */
143        public $description_hidden = false;
144
145        /**
146         * Constructor.
147         *
148         * Any supplied $args override class property defaults.
149         *
150         * @since 3.4.0
151         *
152         * @param WP_Customize_Manager $manager Customizer bootstrap instance.
153         * @param string               $id      A specific ID of the section.
154         * @param array                $args    {
155         *     Optional. Array of properties for the new Section object. Default empty array.
156         *
157         *     @type int             $priority           Priority of the section, defining the display order
158         *                                               of panels and sections. Default 160.
159         *     @type string          $panel              The panel this section belongs to (if any).
160         *                                               Default empty.
161         *     @type string          $capability         Capability required for the section.
162         *                                               Default 'edit_theme_options'
163         *     @type string|string[] $theme_supports     Theme features required to support the section.
164         *     @type string          $title              Title of the section to show in UI.
165         *     @type string          $description        Description to show in the UI.
166         *     @type string          $type               Type of the section.
167         *     @type callable        $active_callback    Active callback.
168         *     @type bool            $description_hidden Hide the description behind a help icon,
169         *                                               instead of inline above the first control.
170         *                                               Default false.
171         * }
172         */
173        public function __construct( $manager, $id, $args = array() ) {
174                $keys = array_keys( get_object_vars( $this ) );
175                foreach ( $keys as $key ) {
176                        if ( isset( $args[ $key ] ) ) {
177                                $this->$key = $args[ $key ];
178                        }
179                }
180
181                $this->manager = $manager;
182                $this->id      = $id;
183                if ( empty( $this->active_callback ) ) {
184                        $this->active_callback = array( $this, 'active_callback' );
185                }
186                self::$instance_count += 1;
187                $this->instance_number = self::$instance_count;
188
189                $this->controls = array(); // Users cannot customize the $controls array.
190        }
191
192        /**
193         * Check whether section is active to current Customizer preview.
194         *
195         * @since 4.1.0
196         *
197         * @return bool Whether the section is active to the current preview.
198         */
199        final public function active() {
200                $section = $this;
201                $active  = call_user_func( $this->active_callback, $this );
202
203                /**
204                 * Filters response of WP_Customize_Section::active().
205                 *
206                 * @since 4.1.0
207                 *
208                 * @param bool                 $active  Whether the Customizer section is active.
209                 * @param WP_Customize_Section $section WP_Customize_Section instance.
210                 */
211                $active = apply_filters( 'customize_section_active', $active, $section );
212
213                return $active;
214        }
215
216        /**
217         * Default callback used when invoking WP_Customize_Section::active().
218         *
219         * Subclasses can override this with their specific logic, or they may provide
220         * an 'active_callback' argument to the constructor.
221         *
222         * @since 4.1.0
223         *
224         * @return true Always true.
225         */
226        public function active_callback() {
227                return true;
228        }
229
230        /**
231         * Gather the parameters passed to client JavaScript via JSON.
232         *
233         * @since 4.1.0
234         *
235         * @return array The array to be exported to the client as JSON.
236         */
237        public function json() {
238                $array                   = wp_array_slice_assoc( (array) $this, array( 'id', 'description', 'priority', 'panel', 'type', 'description_hidden' ) );
239                $array['title']          = html_entity_decode( $this->title, ENT_QUOTES, get_bloginfo( 'charset' ) );
240                $array['content']        = $this->get_content();
241                $array['active']         = $this->active();
242                $array['instanceNumber'] = $this->instance_number;
243
244                if ( $this->panel ) {
245                        /* translators: &#9656; is the unicode right-pointing triangle. %s: Section title in the Customizer. */
246                        $array['customizeAction'] = sprintf( __( 'Customizing &#9656; %s' ), esc_html( $this->manager->get_panel( $this->panel )->title ) );
247                } else {
248                        $array['customizeAction'] = __( 'Customizing' );
249                }
250
251                return $array;
252        }
253
254        /**
255         * Checks required user capabilities and whether the theme has the
256         * feature support required by the section.
257         *
258         * @since 3.4.0
259         *
260         * @return bool False if theme doesn't support the section or user doesn't have the capability.
261         */
262        final public function check_capabilities() {
263                if ( $this->capability && ! current_user_can( $this->capability ) ) {
264                        return false;
265                }
266
267                if ( $this->theme_supports && ! current_theme_supports( ...(array) $this->theme_supports ) ) {
268                        return false;
269                }
270
271                return true;
272        }
273
274        /**
275         * Get the section's content for insertion into the Customizer pane.
276         *
277         * @since 4.1.0
278         *
279         * @return string Contents of the section.
280         */
281        final public function get_content() {
282                ob_start();
283                $this->maybe_render();
284                return trim( ob_get_clean() );
285        }
286
287        /**
288         * Check capabilities and render the section.
289         *
290         * @since 3.4.0
291         */
292        final public function maybe_render() {
293                if ( ! $this->check_capabilities() ) {
294                        return;
295                }
296
297                /**
298                 * Fires before rendering a Customizer section.
299                 *
300                 * @since 3.4.0
301                 *
302                 * @param WP_Customize_Section $section WP_Customize_Section instance.
303                 */
304                do_action( 'customize_render_section', $this );
305                /**
306                 * Fires before rendering a specific Customizer section.
307                 *
308                 * The dynamic portion of the hook name, `$this->id`, refers to the ID
309                 * of the specific Customizer section to be rendered.
310                 *
311                 * @since 3.4.0
312                 */
313                do_action( "customize_render_section_{$this->id}" );
314
315                $this->render();
316        }
317
318        /**
319         * Render the section UI in a subclass.
320         *
321         * Sections are now rendered in JS by default, see WP_Customize_Section::print_template().
322         *
323         * @since 3.4.0
324         */
325        protected function render() {}
326
327        /**
328         * Render the section's JS template.
329         *
330         * This function is only run for section types that have been registered with
331         * WP_Customize_Manager::register_section_type().
332         *
333         * @since 4.3.0
334         *
335         * @see WP_Customize_Manager::render_template()
336         */
337        public function print_template() {
338                ?>
339                <script type="text/html" id="tmpl-customize-section-<?php echo $this->type; ?>">
340                        <?php $this->render_template(); ?>
341                </script>
342                <?php
343        }
344
345        /**
346         * An Underscore (JS) template for rendering this section.
347         *
348         * Class variables for this section class are available in the `data` JS object;
349         * export custom variables by overriding WP_Customize_Section::json().
350         *
351         * @since 4.3.0
352         *
353         * @see WP_Customize_Section::print_template()
354         */
355        protected function render_template() {
356                ?>
357                <li id="accordion-section-{{ data.id }}" class="accordion-section control-section control-section-{{ data.type }}">
358                        <h3 class="accordion-section-title">
359                                <button type="button" class="accordion-trigger" aria-expanded="false" aria-controls="{{ data.id }}-content">
360                                        {{ data.title }}
361                                </button>
362                        </h3>
363                        <ul class="accordion-section-content" id="{{ data.id }}-content">
364                                <li class="customize-section-description-container section-meta <# if ( data.description_hidden ) { #>customize-info<# } #>">
365                                        <div class="customize-section-title">
366                                                <button class="customize-section-back" tabindex="-1">
367                                                        <span class="screen-reader-text">
368                                                                <?php
369                                                                /* translators: Hidden accessibility text. */
370                                                                _e( 'Back' );
371                                                                ?>
372                                                        </span>
373                                                </button>
374                                                <h3>
375                                                        <span class="customize-action">
376                                                                {{{ data.customizeAction }}}
377                                                        </span>
378                                                        {{ data.title }}
379                                                </h3>
380                                                <# if ( data.description && data.description_hidden ) { #>
381                                                        <button type="button" class="customize-help-toggle dashicons dashicons-editor-help" aria-expanded="false"><span class="screen-reader-text">
382                                                                <?php
383                                                                /* translators: Hidden accessibility text. */
384                                                                _e( 'Help' );
385                                                                ?>
386                                                        </span></button>
387                                                        <div class="description customize-section-description">
388                                                                {{{ data.description }}}
389                                                        </div>
390                                                <# } #>
391
392                                                <div class="customize-control-notifications-container"></div>
393                                        </div>
394
395                                        <# if ( data.description && ! data.description_hidden ) { #>
396                                                <div class="description customize-section-description">
397                                                        {{{ data.description }}}
398                                                </div>
399                                        <# } #>
400                                </li>
401                        </ul>
402                </li>
403                <?php
404        }
405}
406
407/** WP_Customize_Themes_Section class */
408require_once ABSPATH . WPINC . '/customize/class-wp-customize-themes-section.php';
409
410/** WP_Customize_Sidebar_Section class */
411require_once ABSPATH . WPINC . '/customize/class-wp-customize-sidebar-section.php';
412
413/** WP_Customize_Nav_Menu_Section class */
414require_once ABSPATH . WPINC . '/customize/class-wp-customize-nav-menu-section.php';
Note: See TracBrowser for help on using the repository browser.