Skip to content

Canvas

frontend_editing_canvas lets editors edit the components of a Drupal Canvas page from the front end. Hover a component, click Edit, and its form opens in the usual Frontend Editing sidebar.

drush en frontend_editing_canvas

Dependencies: frontend_editing, canvas (1.12 or later). Drupal 11.3 or later.

What it does

  • Outlines the hovered component and shows a bar with its name and an Edit button. The outline and the bar look the same as for paragraphs: same colors (including the configured primary color), same bar, same pencil icon. Components around the hovered one get a lighter outline and no bar.
  • Opens the component's form in the sidebar, in the admin theme. The sidebar can be resized and detached like for any other entity.
  • Keeps every change as an unpublished change (the Canvas draft of the page) and refreshes the page content in place, without a full reload. A badge next to the editing toggle counts the changed components. Its panel lists them and publishes or discards all of them at once, as one new revision. With the save mode "direct", every change is a new revision at once.
  • Lets editors move a component up and down among its siblings, delete it (after a confirmation), duplicate it and add components: with the plus buttons on the top and bottom edge (before and after), and with the plus button in the bar of a component that has slots (into a slot). All of these look and behave like the same actions on paragraphs.
  • Works for components from single directory components, code components and block plugins.
  • Follows the editing mode toggle: hidden components and links when editing is switched off.

The form uses the same field widgets as the Canvas editor, so the same values end up in the page. Nested components (in slots) are supported; hovering picks the innermost component under the pointer.

Limitations

  • Only canvas_page entities. In Canvas 1.12 other entity types, such as nodes, get their components from content templates, which are configuration and not edited from the front end.
  • In the save mode "draft" (the default) editors see and change the draft of the page. It is shared with every other editor and with the Canvas editor: there is no draft per user.
  • In the save mode "direct", every action is its own revision, and a component deleted by mistake is only recoverable from the page's revisions. If the page has a Canvas draft, the form warns that saving creates a revision that conflicts with it.
  • The color picker is a plain color field: no transparency and no Brand Kit colors. Use the Open in Admin UI button in the sidebar header to open the component in Canvas instead.
  • Adding stores the new component with its default values first, then its edit form opens. Closing the sidebar at that point keeps the component with those default values (in the draft, until it is discarded).
  • The badge and panel are floating, next to the editing toggle. The toolbar variants of the paragraphs save queue are not available.
  • Dragging components and adding into the empty page are not available.

Configuration

Installing the module enables the canvas_page entity type in Entity types and bundles. Canvas pages have no bundles, so the Page tab of that form lists the components instead:

Setting Effect
Which components can be changed? All except those selected (the default, with nothing selected: all components) or Only those selected. The components are listed per source: single directory components, code components and blocks. It applies to editing, moving, deleting, duplicating and adding.

The Canvas section of the Frontend Editing settings form (/admin/config/frontend-editing) adds this setting:

Setting Effect
Saving changes Draft (the default): changes wait as unpublished changes of the page, and are published together. Direct: every change is a new revision at once.

Components without any props get no Edit button, but can still be moved, deleted, duplicated and added. Broken components never get any action.

Editors need the access frontend editing permission and the permission to edit the Canvas page (edit canvas_page). The other actions use the same permissions as the items of entity reference fields, so they are managed in one place:

Action Permission
Edit none besides the two above
Move up and down frontend editing move items
Delete frontend editing delete items
Duplicate frontend editing duplicate items
Add frontend editing add items
Publish the unpublished changes publish auto-saves (a Canvas permission)

Editing the page and discarding the unpublished changes need no more than the first two. An editor without publish auto-saves sees the changes and the panel, but somebody else has to publish them.

The choice of components applies to all of these actions: a component that is not allowed cannot be edited, moved, deleted, duplicated or added.

How it works

Canvas does not mark its components on the live page, so the module adds two HTML comments around each component instance (<!-- fe-canvas:start UUID --> and <!-- fe-canvas:end UUID -->) and one pair around the whole page. They are added only for users with access frontend editing, and never in Canvas's own editor preview. A script pairs the comments to find each component's area on the page.

The edit form is built by Canvas itself. Canvas converts the submitted widget values into component inputs in the browser, so this module does the same on the server (Drupal\frontend_editing_canvas\ComponentInputExtractor). That class is the only place that depends on Canvas internals, which Canvas does not yet offer as a stable API.

After a save, the sidebar sends the usual closeSidePanel message. The module answers it by fetching the page again and replacing the content between the page-level comments. The same happens after move, delete and duplicate, which are POST requests with a CSRF token that return JSON, and after a component was added, when the sidebar asks the page to refresh.

Unpublished changes. With the save mode "draft", the front end reads and changes the Canvas auto-save draft of the page (Drupal\frontend_editing_canvas\WorkingCopy returns the draft when there is one, else the page). While a draft exists, editors see it on the page instead of the stored page, and everybody else sees the stored page. Publishing calls Canvas's own publish for the page, after putting the revision log message, the author and the time on the draft, so that Canvas stores one new revision with all the changes. The list in the panel (added, edited, moved, removed) comes from comparing the draft with the stored page. When the stored page changed after the draft was started, publishing would replace those changes: the panel then says so, Publish is disabled and the server refuses the request (409) without touching the draft. Discard all is the way out, the changes of the draft have to be made again on the current page.

The tree is changed by Drupal\frontend_editing_canvas\ComponentTreeEditor. It works on the raw values of the component_tree field and writes every item after its parent, which keeps Canvas's rules for the tree (parents exist, the order of siblings is their order in the field) true. Moving swaps places with the nearest sibling in the same slot, and takes the children along. Duplicating copies the whole subtree with new UUIDs. Deleting removes the subtree.

Tests

  • tests/src/Kernel/: access, markers, input extraction, the tree editor, the add form and the draft mode.
  • tests/src/FunctionalJavascript/: browser tests in Chrome through WebDriver. DirectModeTest covers the hover bar, permissions, editing in the sidebar, move, duplicate, delete (with the confirmation) and add. DraftModeTest covers collecting changes in the draft, publishing them as one revision with a log message, discarding, and the missing publish permission.