Problem/Motivation

Per #3500093: Layout Builder time outs with new version of editoria11y, the Editoria11y library attachment is removed from Layout Builder routes for the time being due to:

"I learned during the beta that Layout Builder has scroll listeners that make it incompatible with another module modifying the page markup -- it results in what you describe. Something is very wrong if Editoria11y is running while the Layout Builder editor interface is open."

However, for sites where the majority of content editing happens in blocks that are placed on Layout Builder pages, this choice severely limits the utility of Editoria11y: people can view the warnings when viewing a Layout Builder node, but when they are editing Layout Builder blocks, specifically those with CKEditor-enabled rich text content, they do not receive the Editoria11y library assistance.

Some sites' design may allow for using Editoria11y with Layout Builder safely: for example, in our organization, we use the module Layout Builder iFrame Module to open layout_builder.update_block and layout_builder.add_block routes. These operations are thus effectively sandboxed within an iframe that opens in the modal, and will not pollute the DOM where the Layout Builder editing interface is instantiated. See screenshot, showing how the Editoria11y object is scoped to the iframe within the modal:

Screenshot of Layout Builder iframe modal and content editing with CKEditor

Proposed resolution

Continue to suppress Editoria11y on most layout_builder. routes, but add a configuration option to allow sites to use it on layout_builder.update_block and layout_builder.add_block. For example:

Acceptance criteria

  1. When an entity is configured to use Layout Builder AND it is set to render add/edit block interfaces in a modal (e.g., by using Layout Builder iFrame Module)...
  2. When a user visits the Layout Builder "Layout" route (e.g., /node/1/layout, the Editoria11y interface continues to be suppressed (since it could interfere negatively with Layout Builder scroll actions)
  3. When a user visits the Layout Builder add/edit route for an inline block (layout_builder.update_block and layout_builder.add_block) that uses the CKEditor editing interface, the Editoria11y library is attached and checks for violations in real-time against changes within that CKEditor editing interface.
  4. In that context, the "parent" entity's information is passed into the Editoria11y library via drupalSettings, allowing it to be contextually aware of -- and affect -- any dismissals associated with the pid for the given parent entity.
  5. Dismissals initiated from the entity "View" route are reflected when the user is viewing the add/edit block of the Layout Builder interface
  6. Dismissals initiated from the Layout Builder add/edit interface are reflected when the user is viewing the entity "View" route.
  7. On the "Content Accessibility" route (/admin/reports/editoria11y), all violations are correctly aggregated for the given parent entity route.

Sample setup

1. Install a site using the "Standard" installation profile
2. Enable "Editoria11y", "Layout Builder," and "Layout Builder iFrame Modal".
3. Configure the "Basic Page" manage display to use Layout Builder and to allow individual overrides.
4. Create a "Basic Page" node.
5. Use that node's "Layout" tab to create a "Basic Block" as an inline element.
6. The editing interface for the "Basic Block" will open in a modal, per Layout Builder iFrame Modal, and the Editoria11y interface will display in the bottom right corner of the modal.
7. Creating an accessibility violation in the Basic Block's content area, such as a "Click here" link, will trigger a warning in the CKEditor area.
8. Dismissing this warning in either the Layout Builder Add/Edit interface for the Block or the "View" route for the node will register it as dismissed in both context, and restoring dismissals will similarly be reflected in both contexts.

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mark_fullmer created an issue. See original summary.

mark_fullmer’s picture

Title: Allow on block update Layout Builder routes, excluding other Layout Builder routes » Allow on block add/update Layout Builder routes while excluding other Layout Builder routes
mark_fullmer’s picture

Issue summary: View changes
mark_fullmer’s picture

Issue summary: View changes
itmaybejj’s picture

Yes, that would effectively sandbox the Layout Builder incompatibilities.

Dismissal synchronization won't work without some extra code -- depending on the context available in the iframe, we would most likely need to have the wrapper page gather the routing information and the dismissals-on-page object, and pass it into the iframe. I've done this in a few places in WordPress to support TinyMCE and Gutenberg.

There's also the option to leave the Ed11y instance outside the iframe, and use its fixedRoots capability to dive the iframe scan inside the frame. That looks in part like this in WordPress (based on a mutation observer or Drupal behavior):

  ed11yInit.ed11yBlockOuterInit = function () {
    const iframe = ed11yInit.findBlockIframe();

    ed11yInit.canvasReady(iframe, async () => {
      // ...
      if (iframe) {
        // Iframed canvas (default in WP 6.3+): library scans the iframe body via fixedRoots.
        const body = iframe.contentWindow.document.body;
        ed11yInit.options.fixedRoots = [{ fixedRoot: body, framePositioner: iframe }];
        ed11yInit.options.editableContent = [body];
        ed11yInit.options.ignoreAllIfAbsent = false;
      } else {
        //...
      }
     // ...

      ed11yInit.shutMenusOnPop();
      ed11yInit.firstCheck();
      ed11yInit.syncDismissals();
      ed11yInit.activeIframe = iframe || null;

      ed11yInit.canvasObserver = new MutationObserver(() => {
        const next = ed11yInit.findBlockIframe();
        if (next !== ed11yInit.activeIframe) {
          ed11yInit.handleCanvasSwap(next);
        }
      });
      ed11yInit.canvasObserver.observe(document.body, { childList: true, subtree: true });
    });
  };

  ed11yInit.handleCanvasSwap = function (newIframe) {
    if (newIframe) {
      ed11yInit.canvasReady(newIframe, () => {
        const body = newIframe.contentWindow.document.body;
        setFixedRoots([{ fixedRoot: body, framePositioner: newIframe }], [body]);
        ed11yInit.activeIframe = newIframe;
      });
    } else {
      // iframe removed: caller is now using the inline canvas. Drop fixedRoots so
      // the library re-scans the outer document via checkRoots.
      setFixedRoots([], '.interface-interface-skeleton__content');
      ed11yInit.activeIframe = null;
    }
  };

Drupal can do simpler things based on behaviors -- see the modal handling.

So...it's a good idea, just not trivial.

I'm happy to talk you through the moving parts if you want to try or put together an estimate if UT wanted to sponsor something.

It might also be possible to fully support Layout Builder at this point with some changes to the upstream library. Off the top of my head the jump-to-tip and next/previous buttons would need to be hidden. It was primarily competing scroll actions that were incompatible.

mark_fullmer’s picture

Thanks for the perspective and guidance! Fully supporting Layout Builder with changes to the upstream library seems like the ideal route to go, but I also can accept that the effort might not be justified, seeing how the focus in the Drupal community is now on Canvas, rather than Layout Builder.

I follow in principle the idea of making the dismissals object available to the iframe holding the edit form so it can properly sync the element_id/dismissalKey with the corresponding rendered view. But I would greatly benefit from a primer on the moving parts, and a bit more explanation about how the unique element_id is generated.

To respect your time, I think it might be most efficient to do a video call, if you're open to that. I'll use the Drupal contact form to reach out about that.

itmaybejj’s picture

Good plan. Regardless of where the wind is blowing I know we'll be running LB sites for many more years so it would be great to support it if it's possible now.

mark_fullmer’s picture

Title: Allow on block add/update Layout Builder routes while excluding other Layout Builder routes » Allow on add/update Layout Builder routes when executed in isolation (modal)
Issue summary: View changes

mark_fullmer’s picture

Status: Active » Needs review

Thanks for the orientation today, @itmaybejj. I've added Acceptance Criteria and Sample Setup in the issue description above.

Since automated testing would involve declaring a new test dependency with the layout_builder_iframe_modal module, I've not provided new tests here; existing tests should demonstrate that the affordance added has no adverse effects. If you do think we should add test coverage, let me know.

itmaybejj’s picture

Assigned: Unassigned » itmaybejj
Status: Needs review » Fixed

Shipped in 3.0.8. Check in if you want a CSA license for helping with the project.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.