Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Default attributes (e.g., "anchor") not available in server-side context #69

Closed
smithjw1 opened this issue Jul 15, 2024 · 3 comments
Closed
Assignees
Labels
bug Something isn't working

Comments

@smithjw1
Copy link
Contributor

Describe the bug

Currently, there is a significant issue with default attributes, such as the anchor attribute. These attributes are not available in the server-side context, causing them to be missing in the API response. This discrepancy can lead to functionality problems and inconsistencies when handling blocks, particularly those relying on default attributes for proper rendering and behavior.

To Reproduce

  • Create a post and add any block that supports anchor
  • In the block settings go to "Advanced" and set an anchor
  • Attempt to retrieve the block attributes through the server-side API
  • Observe that the anchor attribute and other client-side registered attributes (if applicable) are missing in the API response

Expected behavior

Default attributes, such as anchor should be consistently included in the API response.

Actual behavior

Specific default attributes, such as ancho are currently missing from the API response.

Client-side registered attributes may be included in the API response if their values differ from the default settings.

@alecgeatches
Copy link
Contributor

alecgeatches commented Jul 30, 2024

Did some research on this today:

Where are attributes defined?

From @Zamfi99's report here on the anchor attribute:

By running wp.blocks.getBlockTypes() in the Gutenberg editor, below is the anchor attribute definition for the core/image block.

[
    {
        "anchor": {
            "attribute": "id",
            "selector": "*",
            "source": "attribute",
            "type": "string"
        }
    }
]

This definition comes from block-editor/src/hooks/anchor.js:

const ANCHOR_SCHEMA = {
    type: 'string',
    source: 'attribute',
    attribute: 'id',
    selector: '*',
};

As discussed above and in linked issues, this definition is only available client-side. Although we can pull the anchor attribute definition from within the editor, we don't have a way to automatically pull this into the server-side PHP parsing code. It's only defined in JavaScript and run client-side.

What other block support attributes exist?

I tried making a block that had the full set of block supports enabled:

Toggle supports list:
"supports": {
    "anchor": true,
    "align": true,
    "alignWide": true,
    "ariaLabel": true,
    "background": {
        "backgroundImage": true,
        "backgroundSize": true
    },
    "className": true,
    "color": {
        "background": true,
        "button": true,
        "enableContrastChecker": true,
        "gradients": true,
        "heading": true,
        "link": true,
        "text": true
    },
    "customClassName": true,
    "dimensions": {
        "aspectRatio": true,
        "minHeight": true
    },
    "filter": {
        "duotone": true
    },
    "html": true,
    "inserter": true,
    "interactivity": {
        "clientNavigation": true,
        "interactive": true
    },
    "layout": {
        "allowSwitching": true,
        "allowEditing": true,
        "allowInheriting": true,
        "allowSizingOnChildren": true,
        "allowVerticalAlignment": true,
        "allowJustification": true,
        "allowOrientation": true,
        "allowCustomContentAndWideSize": true
    },
    "lock": true,
    "multiple": true,
    "position": {
        "sticky": true
    },
    "renaming": true,
    "reusable": true,
    "shadow": true,
    "spacing": {
        "margin": true,
        "padding": true,
        "blockGap": true
    },
    "typography": {
        "fontSize": true,
        "lineHeight": true,
        "textAlign": true
    },
    "splitting": true
}

and a block with no supports. Then, using wp.blocks.getBlockTypes() on the client-side, I compared the full set of properties available on the no-support block, and the full-support block:

Block with no supports Block with every support
{
    "lock": {
        "type": "object"
    },
    "metadata": {
        "type": "object"
    },
    "className": {
        "type": "string"
    }
}
{
    "lock": {
        "type": "object"
    },
    "metadata": {
        "type": "object"
    },
    "align": {
        "type": "string",
        "enum": [
            "left",
            "center",
            "right",
            "wide",
            "full",
            ""
        ]
    },
    "className": {
        "type": "string"
    },
    "style": {
        "type": "object"
    },
    "backgroundColor": {
        "type": "string"
    },
    "textColor": {
        "type": "string"
    },
    "gradient": {
        "type": "string"
    },
    "fontSize": {
        "type": "string"
    },
    "layout": {
        "type": "object"
    },
    "anchor": {
        "type": "string",
        "source": "attribute",
        "attribute": "id",
        "selector": "*"
    },
    "ariaLabel": {
        "type": "string",
        "source": "attribute",
        "attribute": "aria-label",
        "selector": "*"
    }
}

There's a big difference between the two lists. However, we can ignore any properties without a source. These properties are passed directly in the block header, which we get from parse_blocks() and forward to block output. Unsourced attributes will be represented in the Block Data API correctly.

Ignoring those, there are two remaining sourced attributes:

{
    "anchor": {
        "type": "string",
        "source": "attribute",
        "attribute": "id",
        "selector": "*"
    },
    "ariaLabel": {
        "type": "string",
        "source": "attribute",
        "attribute": "aria-label",
        "selector": "*"
    }
}

The anchor attribute can be tested in the Gutenberg editor under block settings sidebar -> "Advanced" dropdown -> "HTML ANCHOR" section. I wasn't able to find an ariaLabel UI, but manually adding a aria-label="some text" attribute to the root node of the block persisted correctly without errors.

Conclusion

As far as I can tell, there are only two block supports that use client-side only defined attributes. Here are some possible options for us:

  1. Add manual support for anchor and ariaLabel on the server-side parser. We could check to see if a block has those supports enabled, and then add these attribute definitions to a block's properties. The upside is that this would solve the problem and there are only two known missing properties. The downside is that this would require manual updates to keep in sync with Gutenberg, and those definitions will likely change. Due to Gutenberg's development velocity this will almost certainly break support for some attributes in the future or overlook support for new attributes.

  2. Add a way to enable support for these attributes via a filter. We could provide a code example that provides support for these attributes, but it would be on the plugin's users to fix if the example code goes out of sync. The upside is this would give customers an easy way to address this issue, but the downside would be the same as those above, just with the maintenance burden shifted to our customers instead of us.

  3. Add Gutenberg-level support for block supports in PHP. Ideally, when we call WP_Block_Type_Registry::get_instance(), it should also know which supports are provided for that block and fill in the corresponding attribute information server-side. This would completely solve the issue for us and our customers, but would require some buy-in and changes to Gutenberg / WordPress core before the issue would be fixed.

@smithjw1, @ingeniumed Any thoughts here or other ideas? Thank you!

@alecgeatches
Copy link
Contributor

@Zamfi99 to help with your original issue in #68, here is some filter code that can be used to "officially" register anchor and ariaLabel in the Block Data API:

add_action( 'init', function () {
    $block_registry    = \WP_Block_Type_Registry::get_instance();
    $registered_blocks = $block_registry->get_all_registered();

    foreach ( $registered_blocks as $block_name => $block_type ) {
        $additional_block_attributes = [];

        if ( isset( $block_type->supports['anchor'] ) && $block_type->supports['anchor'] ) {
            $additional_block_attributes['anchor'] = [
                'type'      => 'string',
                'source'    => 'attribute',
                'attribute' => 'id',
                'selector'  => '*',
            ];
        }

        if ( isset( $block_type->supports['ariaLabel'] ) && $block_type->supports['ariaLabel'] ) {
            $additional_block_attributes['ariaLabel'] = [
                'type'      => 'string',
                'source'    => 'attribute',
                'attribute' => 'aria-label',
                'selector'  => '*',
            ];
        }

        if ( ! empty( $additional_block_attributes ) ) {
            // Re-register block with support attributes
            $block_registry->unregister( $block_name );

            $block_type->attributes = array_merge( $block_type->attributes, $additional_block_attributes );
            $block_registry->register( $block_type );
        }
    }
} );

This will loop through registered blocks, find anchor and ariaLabel supports, and add those as attributes in the server-side registration. This will run on every request. Alternatively, the code above can run only during Block Data API execution by using the vip_block_data_api__before_parse_post_content filter:

add_filter( 'vip_block_data_api__before_parse_post_content', function ( $post_content ) {
    $block_registry    = \WP_Block_Type_Registry::get_instance();
    $registered_blocks = $block_registry->get_all_registered();

    foreach ( $registered_blocks as $block_name => $block_type ) {
        /* ... */
    }

    return $post_content;
} );

I've tested this code with an image:

<!-- wp:image {"id":39,"sizeSlug":"large","linkDestination":"none"} -->
<figure class="wp-block-image size-large" id="custom-anchor"><img src="http://my.site/wp-content/uploads/image.jpg" alt="" class="wp-image-39"/></figure>
<!-- /wp:image -->

and the result correctly holds the anchor attribute:

{
    "name": "core/image",
    "attributes": {
        "id": 39,
        "sizeSlug": "large",
        "linkDestination": "none",
        "url": "http://my.site/wp-content/uploads/image.jpg",
        "alt": "",
        "anchor": "custom-anchor",
        "width": 1024,
        "height": 683
    }
}

I also tested that ariaLabel is correctly sourced on a core/group block.

As discussed above, this may or may not be added to this plugin or WordPress core in the feature. Hopefully this code can be helpful for the present while we work that out.

@alecgeatches
Copy link
Contributor

I'm closing this issue for the present. Please see the above comment for instructions on how to add anchor and other client-side attributes to server-side responses: #69 (comment). Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants