wpengine / wp-graphql-content-blocks

Plugin that extends WPGraphQL to support querying (Gutenberg) Blocks as data
https://faustjs.org/docs/gutenberg/wp-graphql-content-blocks
GNU General Public License v2.0
111 stars 14 forks source link

`core/list-item` content attribute returns html of nested elements #143

Closed madebyfabian closed 1 year ago

madebyfabian commented 1 year ago

I have created the following block structure in the editor for demo: Bildschirm­foto 2023-08-07 um 07 24 43

My GraphQL query:

GraphQL ```graphql query ListPageByUri($uri: ID!) { page(id: $uri, idType: URI) { id title uri editorBlocks(flat: true) { ... on CoreList { name attributes { ordered start } } ... on CoreListItem { name attributes { content } } ... on CoreParagraph { name attributes { content className } } } } } ```

Which returns this JSON:

JSON ```json { "data": { "page": { "id": "cG9zdDoxNDA=", "title": "Team", "uri": "/unternehmen/team", "editorBlocks": [ { "name": "core/list", "attributes": { "ordered": false, "start": null } }, { "name": "core/list-item", "attributes": { "content": "Interesting list item" } }, { "name": "core/list-item", "attributes": { "content": "Another one\n
    \n
  1. Nested ordered
  2. \n\n\n\n
  3. Another nested ordered\n
      \n
    • Deeply nested
    • \n
    \n
  4. \n
\nNested orderedAnother nested ordered\n
    \n
  • Deeply nested
  • \n
\nDeeply nested" } }, { "name": "core/list", "attributes": { "ordered": true, "start": null } }, { "name": "core/list-item", "attributes": { "content": "Nested ordered" } }, { "name": "core/list-item", "attributes": { "content": "Another nested ordered\n
    \n
  • Deeply nested
  • \n
\nDeeply nested" } }, { "name": "core/list", "attributes": { "ordered": false, "start": null } }, { "name": "core/list-item", "attributes": { "content": "Deeply nested" } }, { "name": "core/list-item", "attributes": { "content": "The funding" } }, { "name": "core/list", "attributes": { "ordered": true, "start": null } }, { "name": "core/list-item", "attributes": { "content": "Some numbers\n
    \n
  • nested
  • \n
\nnested" } }, { "name": "core/list", "attributes": { "ordered": false, "start": null } }, { "name": "core/list-item", "attributes": { "content": "nested" } }, { "name": "core/list-item", "attributes": { "content": "Again some numbers" } } ] } }, "extensions": { "debug": [ { "type": "DEBUG_LOGS_INACTIVE", "message": "GraphQL Debug logging is not active. To see debug logs, GRAPHQL_DEBUG must be enabled." } ] } } ```

As you can see, it returns every nested item as a block, correctly. But on the attributes.content field, it includes the HTML for the nested elements.

The current workaround is to just strip the HTML off, but I'd consider this more of a bug.

Thanks for creating this plugin, and thanks in advance!

theodesp commented 1 year ago

Hey @madebyfabian. Thanks for the report.

This is the default behavior. As per the core-list-item block.json the contents of the list item is the innerHTML content of the first <li> selector.

This is how the block editor saves the content of inner lists in the database. It stores a new list under the current list item.

So each list-item html contents will contain the html content of the inner list components hence you are seeing that result.

For example given the following list:

Screenshot 2023-08-08 at 12 09 32

This is how it is saved in the database:

<!-- wp:list -->
<ul>
   <!-- wp:list-item -->
   <li>
      Example List 1<!-- wp:list -->
      <ul>
         <!-- wp:list-item -->
         <li>Nested List 1</li>
         <!-- /wp:list-item -->
         <!-- wp:list-item -->
         <li>
            Nested List 2<!-- wp:list -->
            <ul>
               <!-- wp:list-item -->
               <li>Inner Nested List 3</li>
               <!-- /wp:list-item -->
            </ul>
            <!-- /wp:list -->
         </li>
         <!-- /wp:list-item -->
      </ul>
      <!-- /wp:list -->
   </li>
   <!-- /wp:list-item -->
</ul>
<!-- /wp:list -->

So when you query the following CoreList and CoreListItem component s you get:

"editorBlocks": [
            {
              "apiVersion": 2,
              "name": "core/list",
              "renderedHtml": "\n<ul>\n<li>Example List 1\n<ul>\n<li>Nested List 1</li>\n\n\n\n<li>Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n</li>\n</ul>\n</li>\n</ul>\n",
              "clientId": "64d222aabcdf2",
              "parentClientId": null,
              "attributes": {
                "ordered": false,
                "start": null
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list-item",
              "renderedHtml": "\n<li>Example List 1\n<ul>\n<li>Nested List 1</li>\n\n\n\n<li>Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n</li>\n</ul>\n</li>\n",
              "clientId": "64d222aabcdf6",
              "parentClientId": "64d222aabcdf2",
              "attributes": {
                "content": "Example List 1\n<ul>\n<li>Nested List 1</li>\n\n\n\n<li>Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n</li>\n</ul>\nNested List 1Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\nInner Nested List 3"
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list",
              "renderedHtml": "\n<ul>\n<li>Nested List 1</li>\n\n\n\n<li>Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n</li>\n</ul>\n",
              "clientId": "64d222aabcdf7",
              "parentClientId": "64d222aabcdf6",
              "attributes": {
                "ordered": false,
                "start": null
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list-item",
              "renderedHtml": "\n<li>Nested List 1</li>\n",
              "clientId": "64d222aabcdf8",
              "parentClientId": "64d222aabcdf7",
              "attributes": {
                "content": "Nested List 1"
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list-item",
              "renderedHtml": "\n<li>Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n</li>\n",
              "clientId": "64d222aabcdf9",
              "parentClientId": "64d222aabcdf7",
              "attributes": {
                "content": "Nested List 2\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\nInner Nested List 3"
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list",
              "renderedHtml": "\n<ul>\n<li>Inner Nested List 3</li>\n</ul>\n",
              "clientId": "64d222aabcdfa",
              "parentClientId": "64d222aabcdf9",
              "attributes": {
                "ordered": false,
                "start": null
              }
            },
            {
              "apiVersion": 2,
              "name": "core/list-item",
              "renderedHtml": "\n<li>Inner Nested List 3</li>\n",
              "clientId": "64d222aabcdfb",
              "parentClientId": "64d222aabcdfa",
              "attributes": {
                "content": "Inner Nested List 3"
              }
            }
          ]
        },

Since the contents of the first list item <li> for the Example List 1 is another CoreList block then the content will be the innerHTML of it.

<li>
------------------------------------------innerHTML Start
Example List 1\n
<ul>
   \n
   <li>Nested List 1</li>
   \n\n\n\n
   <li>
      Nested List 2\n
      <ul>
         \n
         <li>Inner Nested List 3</li>
         \n
      </ul>
      \n
   </li>
   \n
</ul>
\nNested List 1Nested List 2\n
<ul>
   \n
   <li>Inner Nested List 3</li>
   \n
</ul>
\nInner Nested List 3
------------------------------------------innerHTML End

Notice that I've used the clientId and parentClientId fields in my query. Since the core/list and core/list-item components are hierarchical you can use those parameters to put the whole tree together using:

const blocks = flatListToHierarchical(editorBlocks, {childrenKey: "innerBlocks"});

So each block item will contain an innerBlocks list of blocks that are considered children blocks and we recursively render them using WordPressBlocksViewer.

This is how we render for example the CoreColumns and CoreColumn Blocks in the @faust/blocks package:

https://github.com/wpengine/faustjs/blob/canary/packages/blocks/src/blocks/CoreColumns.tsx#L30-L39 https://github.com/wpengine/faustjs/blob/canary/packages/blocks/src/blocks/CoreColumn.tsx#L30-L39

madebyfabian commented 1 year ago

Hey @theodesp Thanks for your detailed answer. Sorry for the delay. I was not that deep into wordpress, so I thought it was because of WPGraphQL or this Content Blocks extension. I also already do fetch the client Ids and then create the hierarchy out of it. I just thought maybe it would be more "headless" if the html wasn't included. But if that's how it's stored in the DB, we have to strip it away ourselves I think