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

Can't nest custom editor components #7180

Open
tobiasBora opened this issue Apr 14, 2024 · 5 comments
Open

Can't nest custom editor components #7180

tobiasBora opened this issue Apr 14, 2024 · 5 comments
Labels
area: extensions/editor-components pinned type: feature code contributing to the implementation of a feature and/or user facing functionality

Comments

@tobiasBora
Copy link

tobiasBora commented Apr 14, 2024

Describe the bug

If I define a custom editor component, I can only specify a regexp as a pattern, like pattern: /^<details>$\s*?<summary>(.*?)<\/summary>\n\n(.*?)\n^<\/details>$/ms,. But this will parse nested components poorly, for instance:

<details>
  <summary>This is a collapsible note</summary>
  This is the content of a collapsible note, that's soooo awesome!!!
<details>
  <summary>Nested collapsible note.</summary>
  Is this even working??
</details>
</details>

Is not parsed correctly since the regexp will match the first <detail> with the first </detail>, like in:

<details>
  <summary>This is a collapsible note</summary>
  This is the content of a collapsible note, that's soooo awesome!!!
<details>
  <summary>Nested collapsible note.</summary>
  Is this even working??
</details>

Unfortunately, I don't think any regexp expression can work here, as an automata "cannot count"… so we should allow the user to use a true XML/MDX parser. So we could maybe provide two options, one with a very generic function that is just given the rest of the file to parse, and one easier function that is made for xml files that are the most common kind of data anyway.

To Reproduce

Add a custom editor component, I just used the one from the documentation:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="robots" content="noindex" />
    <title>Content Manager</title>
    <script src="https://identity.netlify.com/v1/netlify-identity-widget.js"></script>
  </head>
  <body>
    <!-- Include the script that builds the page and powers Decap CMS -->
    <script src="https://unpkg.com/decap-cms@^3.0.0/dist/decap-cms.js"></script>
    <script>
      CMS.registerEditorComponent({
        // Internal id of the component
        id: "collapsible-note",
        // Visible label
        label: "Collapsible Note",
        // Fields the user need to fill out when adding an instance of the component
        fields: [
          {
            name: 'summary',
            label: 'Summary',
            widget: 'string'
          },
          {
            name: 'contents',
            label: 'Contents',
            widget: 'markdown'
          }
        ],
        // Regex pattern used to search for instances of this block in the markdown document.
        // Patterns are run in a multiline environment (against the entire markdown document),
        // and so generally should make use of the multiline flag (`m`). If you need to capture
        // newlines in your capturing groups, you can either use something like
        // `([\S\s]*)`, or you can additionally enable the "dot all" flag (`s`),
        // which will cause `(.*)` to match newlines as well.
        //
        // Additionally, it's recommended that you use non-greedy capturing groups (e.g.
        // `(.*?)` vs `(.*)`), especially if matching against newline characters.
        pattern: /^<details>$\s*?<summary>(.*?)<\/summary>\n\n(.*?)\n^<\/details>$/ms,
        // Given a RegExp Match object
        // (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#return_value),
        // return an object with one property for each field defined in `fields`.
        //
        // This is used to populate the custom widget in the markdown editor in the CMS.
        fromBlock: function(match) {
          return {
            summary: match[1],
            contents: match[2]
          };
        },
        // Given an object with one property for each field defined in `fields`,
        // return the string you wish to be inserted into your markdown.
        //
        // This is used to serialize the data from the custom widget to the
        // markdown document
        toBlock: function(data) {
          return `
<details>
  <summary>${data.summary}</summary>

  ${data.contents}

</details>
          `;
        },
        // Preview output for this component. Can either be a string or a React component
        // (component gives better render performance)
        toPreview: function(data) {
          return `
<details>
  <summary>${data.summary}</summary>

  ${data.contents}

</details>
          `;
        }
      });
    </script>
  </body>
</html>

Expected behavior
This should just parse correctly without any error

Screenshots

Applicable Versions:

  • Decap CMS version: 3.0.0
  • Git provider: local
  • OS: NixOs

CMS configuration

and the following configuration

# when using the default proxy server port
local_backend: true

media_folder: "static/images/uploads" # Media files will be stored in the repo under images/uploads
public_folder: "/images/uploads" # The src attribute for uploaded media will begin with /images/uploads

backend:
  name: git-gateway
  branch: main # Branch to update (optional; defaults to master)
  commit_messages:
    create: Create {{collection}} “{{slug}}”
    update: Update {{collection}} “{{slug}}”
    delete: Delete {{collection}} “{{slug}}”
    uploadMedia: Upload “{{path}}”
    deleteMedia: Delete “{{path}}”
    openAuthoring: '{{message}}'
  publish_mode: editorial_workflow
  site_url: http://localhost:5173/
    
collections:
  - name: "post" # Used in routes, e.g., /admin/collections/blog
    label: "Posts" # Used in the UI
    preview_path: blog/{{slug}}
    folder: "src/lib/posts/" # The path to the folder where the documents are stored
    create: true # Allow users to create new documents in this collection
    slug: "{{year}}-{{month}}-{{day}}-{{slug}}" # Filename template, e.g., YYYY-MM-DD-title.md
    fields: # The fields for each document, usually in front matter
      - { label: "Layout", name: "layout", widget: "hidden", default: "blog" }
      - { label: "Title", name: "title", widget: "string" }
      - { label: "Publish Date", name: "date", widget: "datetime" }
      - { label: "Featured Image", name: "thumbnail", widget: "image" }
      - { label: "Rating (scale of 1-5)", name: "rating", widget: "number" }
      - { label: "Body", name: "body", widget: "markdown" }
  - name: pages
    label: Pages
    label_singular: 'Page'
    folder: content/pages
    create: true
    # adding a nested object will show the collection folder structure
    nested:
      depth: 100 # max depth to show in the collection tree
      summary: '{{title}}' # optional summary for a tree node, defaults to the inferred title field
    fields:
      - label: Title
        name: title
        widget: string
      - label: Body
        name: body
        widget: markdown
    # adding a meta object with a path property allows editing the path of entries
    # moving an existing entry will move the entire sub tree of the entry to the new location
    meta: { path: { widget: string, label: 'Path', index_file: 'index' } }

Additional context

@tobiasBora tobiasBora added the type: bug code to address defects in shipped code label Apr 14, 2024
@tobiasBora
Copy link
Author

tobiasBora commented Aug 22, 2024

@martinjagodic Is the stale label indicating that you plan to close this issue due to no activity? I'm still very much interested in this as I really need to have full support of MDX files to switch to decap-cms.

@martinjagodic martinjagodic added area: extensions/editor-components type: feature code contributing to the implementation of a feature and/or user facing functionality and removed status: stale type: bug code to address defects in shipped code labels Aug 22, 2024
@martinjagodic
Copy link
Member

@tobiasBora Yes, there seemed to be no interest from anyone else in this topic. This could be because the issue description has some inconsistencies. You talk about nesting custom widgets, but in the code, you specify a custom editor component, which is different. It appears that you want nested ECs, so please update the issue to make that clear.

I edited the labels to keep this issue around.

Would you be willing to contribute a pull request to improve this?

@tobiasBora tobiasBora changed the title Can't nest custom widgets Can't nest custom editor components Aug 22, 2024
@tobiasBora
Copy link
Author

tobiasBora commented Aug 22, 2024

I see, I was not really familiar with the naming I think, hence the confusion. I tried to fix the naming issue above, hopefully it is clearer.

Yes, there seemed to be no interest from anyone else in this topic

I'm surprised nobody needs nested components, that seems to be a fairly basic feature, for instance when one wants to define a carousel of pictures, one need to embed inside a "carousel" component multiple pictures components, possibly with title attributes etc… How are people solving these issues?

Would you be willing to contribute a pull request to improve this?

I might try in the future yes (but right now I don't have much time), but since manually parsing HTML/MDX is not so easy, I think the ultimate solution would be to directly provide an additionally integration for HTML/MDX, for instance via https://github.com/syntax-tree/mdast-util-mdx (while letting the possibility to the user to provide support for additional languages not so easy to parse), but this might require a bit more thoughts on how to go from MDX syntax tree to EC and EC to MDX.

@martinjagodic
Copy link
Member

We use nested ECs regularly, but it depends a bit on your SSG of choice. If you combine it with Hugo's shortcodes, it works very well. This is not well documented because it depends on the use case and the SSG, so you have to find regexes that work for you.

Support for MDX is tracked in #7158.

@tobiasBora
Copy link
Author

tobiasBora commented Aug 22, 2024

We use nested ECs regularly,

Ok, I meant nested EC with the same EC used inside the parent EC (so my example with photos does not apply, but the one with hidden content does). This simply cannot be parsed with regexp, similar to how regexp are unable to parse matched parenthesis, so I don't think it depends much on the used SSG.

Support for MDX is tracked in #7158.

Thanks, good to know! They mention custom formatters that should indeed be usable in this context (at the cost of rewritting a full formatter), which is a first step indeed, thanks. I'll see how far I can go starting from there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: extensions/editor-components pinned type: feature code contributing to the implementation of a feature and/or user facing functionality
Projects
None yet
Development

No branches or pull requests

2 participants