Skip to content

Commit 3fcbb47

Browse files
committed
Update shared Settings API accessibility
1 parent d87980c commit 3fcbb47

8 files changed

Lines changed: 116 additions & 29 deletions

‎settings/class-settings-api.php‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,15 @@ public function admin_init() {
656656
$name = $args['name'];
657657
$type = isset( $args['type'] ) ? $args['type'] : 'text';
658658
$callback = method_exists( $this->settings_form, "callback_{$type}" ) ? array( $this->settings_form, "callback_{$type}" ) : array( $this->settings_form, 'callback_missing' );
659+
$title = $name;
660+
661+
if ( ! in_array( $type, array( 'header', 'repeater' ), true ) ) {
662+
$title = sprintf(
663+
'<label for="%1$s">%2$s</label>',
664+
esc_attr( $this->settings_form->get_field_id( $args ) ),
665+
wp_kses_post( $name )
666+
);
667+
}
659668

660669
// Tag header rows so the settings search can group fields under them.
661670
if ( 'header' === $type ) {
@@ -664,7 +673,7 @@ public function admin_init() {
664673

665674
add_settings_field(
666675
"{$settings_key}[{$id}]", // ID of the settings field. We save it within the settings array.
667-
$name, // Label of the setting.
676+
$title, // Label of the setting.
668677
$callback, // Function to handle the setting.
669678
"{$settings_key}_{$section}", // Page to display the setting. In our case it is the section as defined above.
670679
"{$settings_key}_{$section}", // Name of the section.

‎settings/class-settings-form.php‎

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public function __construct( $args ) {
7272
* @return string Description of the field.
7373
*/
7474
public function get_field_description( $args ) {
75-
$desc = ! empty( $args['desc'] ) ? '<p class="description">' . wp_kses_post( $args['desc'] ) . '</p>' : '';
75+
$desc = ! empty( $args['desc'] ) ? '<p class="description">' . wp_kses( $args['desc'], $this->get_allowed_html() ) . '</p>' : '';
7676

7777
/**
7878
* After Settings Output filter
@@ -349,6 +349,18 @@ protected function get_field_attributes( $args ) {
349349
);
350350
}
351351

352+
/**
353+
* Get the rendered field ID for a field definition.
354+
*
355+
* @param array $args Field arguments.
356+
* @return string Field ID.
357+
*/
358+
public function get_field_id( $args ) {
359+
$field_attributes = $this->get_field_attributes( $args );
360+
361+
return $field_attributes['field_id'];
362+
}
363+
352364
/**
353365
* Returns the allowed HTML tags and attributes for settings form output.
354366
*
@@ -432,11 +444,30 @@ protected function get_allowed_html(): array {
432444
'style' => true,
433445
),
434446
'button' => array(
435-
'type' => true,
436-
'id' => true,
437-
'class' => true,
438-
'style' => true,
439-
'disabled' => true,
447+
'type' => true,
448+
'id' => true,
449+
'class' => true,
450+
'style' => true,
451+
'disabled' => true,
452+
'aria-controls' => true,
453+
'aria-expanded' => true,
454+
'aria-label' => true,
455+
),
456+
'div' => array(
457+
'id' => true,
458+
'class' => true,
459+
'style' => true,
460+
'aria-hidden' => true,
461+
'aria-busy' => true,
462+
'role' => true,
463+
),
464+
'span' => array(
465+
'class' => true,
466+
'style' => true,
467+
'aria-hidden' => true,
468+
'aria-live' => true,
469+
'aria-atomic' => true,
470+
'role' => true,
440471
),
441472
'template' => array(
442473
'class' => true,
@@ -1333,10 +1364,16 @@ public function render_repeater_item( $args, $index, $item = null ) {
13331364
}
13341365

13351366
$parent_disabled = $this->is_field_disabled( $args );
1367+
$content_id = sprintf(
1368+
'%s-%s-%s-content',
1369+
sanitize_key( $this->settings_key ),
1370+
sanitize_key( $args['id'] ),
1371+
'{{ROW_ID}}' === $item_id ? '{{ROW_ID}}' : sanitize_key( $item_id )
1372+
);
13361373
?>
13371374
<div class="wz-repeater-item" data-row-id="<?php echo esc_attr( $item_id ); ?>">
13381375
<input type="hidden" name="<?php echo esc_attr( $this->settings_key ); ?>[<?php echo esc_attr( $args['id'] ); ?>][<?php echo esc_attr( $index ); ?>][row_id]" value="<?php echo esc_attr( $item_id ); ?>" <?php disabled( $parent_disabled ); ?> />
1339-
<div class="repeater-item-header">
1376+
<button type="button" class="repeater-item-header" aria-expanded="false" aria-controls="<?php echo esc_attr( $content_id ); ?>">
13401377
<?php
13411378
$display_field = ! empty( $args['live_update_field'] ) ? $args['live_update_field'] : 'name';
13421379
$live_options = ! empty( $args['live_update_field_options'] ) && is_array( $args['live_update_field_options'] ) ? $args['live_update_field_options'] : array();
@@ -1346,9 +1383,9 @@ public function render_repeater_item( $args, $index, $item = null ) {
13461383
: $fallback_title;
13471384
?>
13481385
<span class="repeater-title"><?php echo esc_html( $display_value ); ?></span>
1349-
<span class="toggle-icon">▼</span>
1350-
</div>
1351-
<div class="repeater-item-content" style="display: none;">
1386+
<span class="toggle-icon" aria-hidden="true">▼</span>
1387+
</button>
1388+
<div id="<?php echo esc_attr( $content_id ); ?>" class="repeater-item-content" aria-hidden="true" style="display: none;">
13521389
<?php
13531390
foreach ( $args['fields'] as $field ) {
13541391
$field_id = sanitize_key( $field['id'] );
@@ -1405,14 +1442,14 @@ public function render_repeater_item( $args, $index, $item = null ) {
14051442

14061443
<div class="repeater-item-footer">
14071444
<div class="repeater-item-actions">
1408-
<button type="button" class="button button-secondary move-up" <?php disabled( $parent_disabled ); ?>>
1409-
<span class="dashicons dashicons-arrow-up-alt2"></span>
1445+
<button type="button" class="button button-secondary move-up" aria-label="<?php esc_attr_e( 'Move item up', 'pfx' ); ?>" <?php disabled( $parent_disabled ); ?>>
1446+
<span class="dashicons dashicons-arrow-up-alt2" aria-hidden="true"></span>
14101447
</button>
1411-
<button type="button" class="button button-secondary move-down" <?php disabled( $parent_disabled ); ?>>
1412-
<span class="dashicons dashicons-arrow-down-alt2"></span>
1448+
<button type="button" class="button button-secondary move-down" aria-label="<?php esc_attr_e( 'Move item down', 'pfx' ); ?>" <?php disabled( $parent_disabled ); ?>>
1449+
<span class="dashicons dashicons-arrow-down-alt2" aria-hidden="true"></span>
14131450
</button>
1414-
<button type="button" class="button button-secondary remove-item" <?php disabled( $parent_disabled ); ?>>
1415-
<span class="dashicons dashicons-trash"></span>
1451+
<button type="button" class="button button-secondary remove-item" aria-label="<?php esc_attr_e( 'Remove item', 'pfx' ); ?>" <?php disabled( $parent_disabled ); ?>>
1452+
<span class="dashicons dashicons-trash" aria-hidden="true"></span>
14161453
</button>
14171454
</div>
14181455
</div>

‎settings/class-settings-wizard-api.php‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -646,7 +646,11 @@ public function render_wizard_page() {
646646
echo '<tr>';
647647
echo '<th scope="row">';
648648
if ( ! empty( $args['name'] ) ) {
649-
echo '<label for="' . esc_attr( $setting_id ) . '">' . esc_html( wp_strip_all_tags( $args['name'] ) ) . '</label>';
649+
if ( in_array( $type, array( 'header', 'repeater' ), true ) ) {
650+
echo wp_kses_post( $args['name'] );
651+
} else {
652+
echo '<label for="' . esc_attr( $this->settings_form->get_field_id( $args ) ) . '">' . wp_kses_post( $args['name'] ) . '</label>';
653+
}
650654
}
651655
echo '</th>';
652656
echo '<td>';

‎settings/css/admin-style-rtl.css‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,26 @@
1919
display: flex;
2020
justify-content: space-between;
2121
align-items: center;
22+
width: 100%;
23+
margin: 0;
24+
background: transparent;
25+
color: inherit;
26+
font: inherit;
27+
text-align: start;
2228
border-bottom: 1px solid #ddd;
29+
border-top: 0;
30+
border-left: 0;
31+
border-right: 0;
2332
padding-bottom: 0.5rem;
2433
cursor: pointer;
2534
font-weight: bold;
2635
}
2736

37+
.wz-repeater-item .repeater-item-header:focus-visible {
38+
outline: 2px solid #2271b1;
39+
outline-offset: 2px;
40+
}
41+
2842
/* Header for repeater field */
2943
.wz-repeater-field-header {
3044
margin-bottom: 0.5rem;

‎settings/css/admin-style.css‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,26 @@
1919
display: flex;
2020
justify-content: space-between;
2121
align-items: center;
22+
width: 100%;
23+
margin: 0;
24+
background: transparent;
25+
color: inherit;
26+
font: inherit;
27+
text-align: start;
2228
border-bottom: 1px solid #ddd;
29+
border-top: 0;
30+
border-right: 0;
31+
border-left: 0;
2332
padding-bottom: 0.5rem;
2433
cursor: pointer;
2534
font-weight: bold;
2635
}
2736

37+
.wz-repeater-item .repeater-item-header:focus-visible {
38+
outline: 2px solid #2271b1;
39+
outline-offset: 2px;
40+
}
41+
2842
/* Header for repeater field */
2943
.wz-repeater-field-header {
3044
margin-bottom: 0.5rem;

‎settings/css/admin-style.min.css‎

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎settings/js/settings-admin-scripts.js‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -343,8 +343,12 @@ jQuery(document).ready(function ($) {
343343
itemsContainer.append(template);
344344
index++;
345345
var newItem = itemsContainer.find('.wz-repeater-item:last');
346-
itemsContainer.find('.repeater-item-header:last .toggle-icon').text('\u25b2');
347-
itemsContainer.find('.repeater-item-content:last').css('display', 'block');
346+
itemsContainer.find('.repeater-item-header:last')
347+
.attr('aria-expanded', 'true')
348+
.find('.toggle-icon').text('\u25b2');
349+
itemsContainer.find('.repeater-item-content:last')
350+
.attr('aria-hidden', 'false')
351+
.css('display', 'block');
348352
if (window.WebberInitTomSelect) {
349353
window.WebberInitTomSelect(newItem.get(0));
350354
}
@@ -378,15 +382,20 @@ jQuery(document).ready(function ($) {
378382
});
379383

380384
// Toggle Accordion.
381-
wrapper.on('click', '.repeater-item-header', function () {
382-
var $this = $(this);
383-
var $toggleIcon = $this.find('.toggle-icon');
384-
var $content = $this.next('.repeater-item-content');
385-
if ($content.is(':visible')) {
386-
$content.slideUp();
385+
wrapper.on('click', '.repeater-item-header', function (e) {
386+
e.preventDefault();
387+
e.stopImmediatePropagation();
388+
var $button = $(this);
389+
var $toggleIcon = $button.find('.toggle-icon');
390+
var $content = $button.next('.repeater-item-content');
391+
var isExpanded = 'true' === $button.attr('aria-expanded');
392+
if (isExpanded) {
393+
$button.attr('aria-expanded', 'false');
394+
$content.attr('aria-hidden', 'true').stop(true, true).hide();
387395
$toggleIcon.text('\u25bc');
388396
} else {
389-
$content.slideDown();
397+
$button.attr('aria-expanded', 'true');
398+
$content.attr('aria-hidden', 'false').stop(true, true).show();
390399
$toggleIcon.text('\u25b2');
391400
}
392401
});

0 commit comments

Comments
 (0)