Skip to content

wp-bind attribute not setting properly on wp-bind--popover #77766

Closed

Description

@kjroelke

Description

Summary

wp-bind return value mishandles the popover attribute.
data-wp-bind--popover="callbacks.popoverState" returns <div data-wp-bind--popover="callbacks.popoverState" popover="true">.

callbacks.popoverState returns "auto"|false but the attribute added to the DOM element is always string.


Expected Behavior

Per the docs I'd expect the attribute to exist/not-exist if a boolean value is returned, or to set the attribute equal to the returned string.

When wp-bind directive references a callback to get its final value:
The wp-bind directive will be executed each time there’s a change on any of the properties of the state or context used inside this callback.
The returned value in the callback function is used to change the value of the associated attribute.
The wp-bind will do different things when the DOM element is applied, depending on its value:

  • If the value is true, the attribute is added: <div attribute>
  • If the value is false, the attribute is removed: <div>
  • If the value is a string, the attribute is added with its value assigned: <div attribute="value">
  • If the attribute name starts with aria- or data- and the value is boolean (either true or false), the attribute is added to the DOM with the boolean value assigned as a string: <div aria-attribute="true">

Concretely:

  • callbacks.popoverState returns true, the div should read <div popover>
  • callbacks.popoverState returns false, the div should read <div>
  • callbacks.popoverState returns "auto", the div should read <div popover="auto">

Step-by-step reproduction instructions

  1. Scaffold an interactive block with npx @wordpress/create-block --template @wordpress/create-block-interactive-template
  2. Add a boolean value to the block's state
  3. Consume the newly created boolean in the block's render.php with data-wp-bind--popover
  4. Update callbacks.logIsOpen to log typeof state['your-boolean-value'] to ensure it is, in fact, a boolean.

Screenshots, screen recording, code snippet

Chrome Element Inspector:
Image

Console:
Image

view.js:
Image

Environment info

  • WordPress via VS Code WordPress Playground Server (latest, 6.9)
  • PHP 7.4
  • Chrome 147.0.7727.117 (Official Build) (arm64)
  • macOS 15.7.5

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Activity

  1. added
    [Type] BugAn existing feature does not function as intended
    on Apr 28, 2026
  2. luisherranz commented on Apr 29, 2026

    @luisherranz
    Member

    Looks legit, thanks for the reporting it, @kjroelke.


    Instructions to solve this are as follows:

    Create a PR using the following instructions:

    1. Update the preact dependency to 10.29.1.
    2. This E2E test will fail: test/e2e/specs/interactivity/directive-bind.spec.ts:203:8 › data-wp-bind › attribute hydration › value is correctly hydrated for different values. This fails becuase Preact, for some reason, has changed how it handles null assignments in the value attribute of input elements and we were testing that because there is a part of the Interactivity API that copies that behavior. Just fix it so the test passes again.

    And create another PR on top of that, using the following instructions:

    1. Inspect this Preact PR that adds support for popover: fix(core): support popover boolean attribute preactjs/preact#4393
    2. Update the part of the Interactivity API code where we are copying that logic to include support for popover:
      useInit( () => {
      const el = ( element.ref as RefObject< HTMLElement > ).current!;
      /*
      * We set the value directly to the corresponding HTMLElement instance
      * property excluding the following special cases. We follow Preact's
      * logic: https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L110-L129
      */
      if ( attribute === 'style' ) {
      if ( typeof result === 'string' ) {
      el.style.cssText = result;
      }
      return;
      } else if (
      attribute !== 'width' &&
      attribute !== 'height' &&
      attribute !== 'href' &&
      attribute !== 'list' &&
      attribute !== 'form' &&
      /*
      * The value for `tabindex` follows the parsing rules for an
      * integer. If that fails, or if the attribute isn't present, then
      * the browsers should "follow platform conventions to determine if
      * the element should be considered as a focusable area",
      * practically meaning that most elements get a default of `-1` (not
      * focusable), but several also get a default of `0` (focusable in
      * order after all elements with a positive `tabindex` value).
      *
      * @see https://html.spec.whatwg.org/#tabindex-value
      */
      attribute !== 'tabIndex' &&
      attribute !== 'download' &&
      attribute !== 'rowSpan' &&
      attribute !== 'colSpan' &&
      attribute !== 'role' &&
      attribute in el
      ) {
      try {
      el[ attribute ] =
      result === null || result === undefined
      ? ''
      : result;
      return;
      } catch {}
      }
      /*
      * aria- and data- attributes have no boolean representation.
      * A `false` value is different from the attribute not being
      * present, so we can't remove it.
      * We follow Preact's logic: https://github.com/preactjs/preact/blob/ea49f7a0f9d1ff2c98c0bdd66aa0cbc583055246/src/diff/props.js#L131C24-L136
      */
      if (
      result !== null &&
      result !== undefined &&
      ( result !== false || attribute[ 4 ] === '-' )
      ) {
      el.setAttribute( attribute, result );
      } else {
      el.removeAttribute( attribute );
      }
      } );
      } );
      } );
  3. USERSATOSHI commented on Apr 29, 2026

    @USERSATOSHI
    Contributor

    Hi @luisherranz, thank you for sharing the approach. It seems there are additional changes related to the popup concerning focus, as keyboard-based focus appears to be failing: https://github.com/WordPress/gutenberg/actions/runs/25099983784/job/73546347497?pr=77785

    I’m currently investigating what may have caused this.

  4. USERSATOSHI commented on Apr 29, 2026

    @USERSATOSHI
    Contributor

    Uh...., this is interesting 😅 .

    Image
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

[Feature] Interactivity APIAPI to add frontend interactivity to blocks.[Status] In ProgressTracking issues with work in progress[Type] BugAn existing feature does not function as intended

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions