Skip to content

Commit 50d7c52

Browse files
authored
Merge branch 'WordPress:trunk' into trunk
2 parents 3cf4d37 + 9ace411 commit 50d7c52

4 files changed

Lines changed: 77 additions & 13 deletions

File tree

src/js/_enqueues/wp/wp-tooltip.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*/
99
(() => {
1010

11-
const popovers = /** @type {NodeListOf<HTMLDivElement>} */ ( document.querySelectorAll( '.wp-is-tooltip' ) );
11+
const popovers = /** @type {NodeListOf<HTMLSpanElement>} */ ( document.querySelectorAll( '.wp-is-tooltip' ) );
1212

1313
/** @type {ReturnType<typeof setTimeout>} */
1414
let openTimeout;

src/wp-includes/general-template.php

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -430,13 +430,14 @@ function wp_get_toggletip( $content, $args ) {
430430
$args['type'] = 'toggletip';
431431
return wp_get_tooltip_helper( $content, $args );
432432
}
433+
433434
/**
434435
* Retrieves the markup for an accessible tooltip or toggletip.
435436
*
436437
* Returns a button and either a hover/focus triggered tooltip popover or an action
437438
* triggered toggle tip. Enqueue the `wp-tooltip` style and script where it is used.
438439
* Tooltips are used to show the accessible name of a control.
439-
* Toggletips are be used for longer supporting text explaining context.
440+
* Toggletips are used for longer supporting text explaining context.
440441
*
441442
* @since 7.1.0
442443
*
@@ -474,7 +475,6 @@ function wp_get_tooltip_helper( $content, $args = array() ) {
474475
'icon' => 'dashicons-editor-help',
475476
'class' => '',
476477
'type' => 'tooltip',
477-
'attributes' => array(),
478478
);
479479

480480
$args = wp_parse_args( $args, $defaults );
@@ -488,37 +488,48 @@ function wp_get_tooltip_helper( $content, $args = array() ) {
488488

489489
$icon = '' !== $args['icon'] ? ' ' . $args['icon'] : '';
490490

491+
/*
492+
* The markup only uses phrasing content so it is valid when nested
493+
* in a phrasing context. Sectioning content (e.g. `div`, `dialog`) will
494+
* cause the parser to close an open `p`, creating an empty and breaking
495+
* the layout. See #65660.
496+
*/
491497
if ( 'tooltip' === $args['type'] ) {
492498
// Tooltips are only used to visually display labels.
493499
$label = wp_strip_all_tags( $content, true );
494500
$markup = sprintf(
495-
'<div class="%1$s">' .
501+
'<span class="%1$s">' .
496502
'<button type="button" class="wp-tooltip__toggle" popovertarget="%2$s" aria-label="%3$s">' .
497503
'<span class="dashicons%4$s" aria-hidden="true"></span>' .
498504
'</button>' .
499505
'<span popover="hint" id="%2$s" class="wp-tooltip__bubble" role="tooltip">' .
500506
'<span id="%2$s-text" class="wp-tooltip__text">%5$s</span>' .
501507
'</span>' .
502-
'</div>',
508+
'</span>',
503509
esc_attr( $classes ),
504510
esc_attr( $id ),
505511
esc_attr( $label ),
506512
esc_attr( $icon ),
507513
esc_html( $content ),
508514
);
509515
} else {
516+
/*
517+
* A `span` with `role="dialog"` is used instead of a `dialog` element to keep the
518+
* markup as phrasing content. The `aria-label`, `tabindex`, and `autofocus`
519+
* attributes reproduce the accessible name and focus handling of the native element.
520+
*/
510521
$markup = sprintf(
511-
'<div class="%1$s">' .
522+
'<span class="%1$s">' .
512523
'<button type="button" class="wp-tooltip__toggle" popovertarget="%2$s" aria-label="%3$s" aria-haspopup="dialog">' .
513524
'<span class="dashicons%4$s" aria-hidden="true"></span>' .
514525
'</button>' .
515-
'<dialog popover="auto" id="%2$s" class="wp-tooltip__bubble" autofocus>' .
526+
'<span popover="auto" id="%2$s" class="wp-tooltip__bubble" role="dialog" aria-label="%3$s" tabindex="-1" autofocus>' .
516527
'<span id="%2$s-text" class="wp-tooltip__text">%5$s</span>' .
517528
'<button type="button" class="wp-tooltip__close" popovertarget="%2$s" popovertargetaction="hide" aria-label="%6$s">' .
518529
'<span class="dashicons dashicons-no-alt" aria-hidden="true"></span>' .
519530
'</button>' .
520-
'</dialog>' .
521-
'</div>',
531+
'</span>' .
532+
'</span>',
522533
esc_attr( $classes ),
523534
esc_attr( $id ),
524535
esc_attr( $args['label'] ),

src/wp-includes/post.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7961,10 +7961,11 @@ function clean_post_cache( $post ) {
79617961
*
79627962
* @since 1.5.0
79637963
*
7964-
* @param WP_Post[] $posts Array of post objects (passed by reference).
7965-
* @param string $post_type Optional. Post type. Default 'post'.
7966-
* @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
7967-
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
7964+
* @param WP_Post[] $posts Array of post objects (passed by reference).
7965+
* @param string|string[] $post_type Optional. Single post type, 'any', or an array of post types.
7966+
* Default 'post'.
7967+
* @param bool $update_term_cache Optional. Whether to update the term cache. Default true.
7968+
* @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
79687969
*/
79697970
function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = true, $update_meta_cache = true ) {
79707971
// No point in doing all this work if we didn't match any posts.

tests/phpunit/tests/general/wpGetTooltip.php

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,4 +114,56 @@ public function test_wp_get_tooltip_generates_unique_id() {
114114
$this->assertStringContainsString( 'popovertarget="' . $id . '"', $html );
115115
$this->assertStringContainsString( 'id="' . $id . '-text"', $html );
116116
}
117+
118+
/**
119+
* Tests that the markup consists only of phrasing content so it can be nested
120+
* inside a paragraph (or other phrasing context) without the parser closing
121+
* the enclosing element and leaving a stray empty paragraph behind.
122+
*
123+
* @ticket 65660
124+
*
125+
* @dataProvider data_tooltip_types
126+
*
127+
* @param string $type The tooltip type, 'tooltip' or 'toggletip'.
128+
*/
129+
public function test_wp_get_tooltip_markup_is_phrasing_content( $type ) {
130+
$html = ( 'toggletip' === $type )
131+
? wp_get_toggletip( 'Helpful text.', array( 'id' => 'my-tip' ) )
132+
: wp_get_tooltip( 'Helpful text.', array( 'id' => 'my-tip' ) );
133+
134+
// The wrapper and popover must not use flow-content elements.
135+
$this->assertStringNotContainsString( '<div', $html, 'The markup should not contain a div element.' );
136+
$this->assertStringNotContainsString( '<dialog', $html, 'The markup should not contain a dialog element.' );
137+
138+
// The wrapper is an inline span.
139+
$this->assertStringContainsString( '<span class="wp-tooltip ', $html );
140+
}
141+
142+
/**
143+
* Tests that the toggletip popover preserves dialog semantics and focus
144+
* handling after moving away from the native dialog element.
145+
*
146+
* @ticket 65660
147+
*/
148+
public function test_wp_get_toggletip_bubble_uses_dialog_role_and_autofocus() {
149+
$html = wp_get_toggletip( 'Helpful text.', array( 'id' => 'my-tip' ) );
150+
151+
// The bubble is a span popover exposing a dialog role.
152+
$this->assertStringContainsString( '<span popover="auto" id="my-tip" class="wp-tooltip__bubble" role="dialog"', $html );
153+
154+
// Focus is moved into the popover when opened, matching the native dialog behavior.
155+
$this->assertStringContainsString( 'tabindex="-1" autofocus>', $html );
156+
}
157+
158+
/**
159+
* Data provider.
160+
*
161+
* @return array[]
162+
*/
163+
public function data_tooltip_types() {
164+
return array(
165+
'tooltip' => array( 'tooltip' ),
166+
'toggletip' => array( 'toggletip' ),
167+
);
168+
}
117169
}

0 commit comments

Comments
 (0)