GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.38k stars 4.06k forks source link

Drag Sorting issues in Web Component with Shadow Dom disabled #2999

Closed jenter closed 4 years ago

jenter commented 4 years ago

Version 16.10

Are you able to reproduce the bug from the demo?

[X] Yes [ ] No

What is the expected behavior?

That GrapesJS can support dragging & sorting interaction with custom Web Components which do not have the Shadow DOM enabled.

Describe the bug

Hello, we use GrapesJS extensively and integrate many of our custom web components, which are generated using the StencilJS project. An issue that we run into is that dragging elements into the Web Components. However, this is only problematic if the Shadow DOM is disabled (shadow: false).

Shadow DOM enabled: [No Bug]

ShadowON

Shadow DOM disabled: [Bug]

ShadowOFF

The issue is that when you are attempting to drop an HTML element in the Web Component, Grapes doesn't seem to understand the DOM / Node references (assuming) and therefore not able to drop within. What's odd is that this bug is not an issue (even with shadow dom disabled) if you wrap any slotted content in a DIV.

# Bug 
<faux-grid-item>
      <p>Example</p>
       ...
# No Bug 
<faux-grid-item>
   <div>
      <p>Example</p>
       ...

This bug/request isn't just for StencilJS, but just in general with Web Components and the Shadow DOM. Are there best practices with Grapes and Web Components with enabling or tracking events to mediate these issues?

Example from screenshot of custom component in stenciljs:

@Component({
  tag: 'faux-grid-item',
  styleUrl: 'faux-grid-item.css',
  shadow: false, // <<=== causes issues
})

Are you able to attach screenshots, screencasts or a live demo?

[X] Yes (attach) [ ] No

artf commented 4 years ago

This bug/request isn't just for StencilJS, but just in general with Web Components and the Shadow DOM. Are there best practices with Grapes and Web Components with enabling or tracking events to mediate these issues?

I'm not working with Web Components that much so if you're able to create a reproducible demo (eg. custom component using Web Components under the hood) that would help us to debug and understand the issue.

jenter commented 4 years ago

@artf sure thing, thanks for the quick response.

I have imported my custom web component library here in this Fiddle to demonstrate the issue: https://jsfiddle.net/jenter77/5btn2ao7/25/

fiddleSort

If you reference my public repo and example "faux-grid-item" web component here you will see it's just a simple component that delays for a couple of seconds and then loads. If shadowDOM is off the sorting/dragging issue bug occurs.

import { Component, State, h } from '@stencil/core';

@Component({
  tag: 'faux-grid-item',
  styleUrl: 'faux-grid-item.css',
  shadow: false, // <<=== causes issues
})

...
artf commented 4 years ago

Hi Jason, I've tried to give a look at what is happening inside the editor and, I'm not sure how the shadow option actually affects this behavior but, the main issue I see is that dynamic <div class="delayed--wrapper"> element. Basically the editor, post parsing, has this component structure

<faux-grid-item>
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
</faux-grid-item>

but the actual DOM is

<faux-grid-item>
   <div class="delayed--wrapper">
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
   </div>
</faux-grid-item>

This misalignment confuses the sorter because div.delayed--wrapper is not connected to any component. Indeed, to "fake" your expected behavior you would need to keep one div inside your template

<faux-grid-item>
   <div>
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
   </div>
</faux-grid-item>

Unfortunately, I don't see how this can be solved (the reason why I'll close the issue). When the sorter tells to drop some Block at position X (eg. after the 2 children) of the FauxGridItem, it will append the new dropped component inside the FauxGridItem's children collection and then the component's view renders the DOM at the same position, but in this case, it won't be possible because FauxGridItem has only 1 child (div.delayed--wrapper).

So to recap, modifying the DOM outside of the editor creates unpredictable side effects. It's the same with frameworks which rely on the virtual DOM (eg. React, Vue, etc.) it's always recommended to avoid dealing directly with the DOM as you might break its virtual state.

jenter commented 4 years ago

@artf thanks for the feedback. Are there any suggestions on how to fire events or attach listeners to/from Grapes that would help in tracking any DOM changes within web components that update later in the cycle?

artf commented 4 years ago

it won't work because you will probably end up having components you wouldn't expect.

For example, you will trigger the parser post-DOM update, then in the export code instead of

<faux-grid-item>
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
</faux-grid-item>

you will have

<faux-grid-item>
   <div class="delayed--wrapper">
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
   </div>
</faux-grid-item>

so then (in the published page), your StencilJS code will generate this

<faux-grid-item>
  <div class="delayed--wrapper">
    <div class="delayed--wrapper">
        <p>Hello</p>
        <p>Hello</p>
        <p>Hello</p>
    </div>
  </div>
</faux-grid-item>

I hope it's clear. The only reasonable thing I can think of use of custom components (eg. grapesjs MJML plugin was made in that way)

jenter commented 4 years ago

@artf thanks for the quick feedback & this is very helpful in our next steps forward. So am I correct in saying: any updates or events should be handled in the webcomponent itself instead of focusing those efforts within any type of GrapesJS binding / firing?

artf commented 4 years ago

Not really, you're not able to guarantee the full coherency if you have an external script that manipulates the DOM of components. My advice would be to create custom components in GrapesJS

jenter commented 4 years ago

@artf makes sense, thank you.