Figure and Editable Caption

Here’s another, possibly more complete example doing something similar.

const imageNodeDesc = {
  figure: {
    attrs: {src: {}},
    content: "inline<_>*",
    parseDOM: [{
      tag: "figure",
      contentElement: "figcaption", // Helps the parser figure out where the child nodes are
      getAttrs(dom) {
        let img = dom.querySelector("img")
        return {src: img && img.parentNode == dom ? img.src : "default_image.jpg"}
      }
    }],
    toDOM(node) {
      return ["figure", ["img", {src: node.attrs.src}], ["figcaption", 0]]
    },
    draggable: true,
    group: "block",
  }
}

To cause the node to be selected on click, even though it isn’t a leaf node, you could use the handleClickOn prop, passing something like…

function handleFigureClick(view, pos, node, posBefore) {
  if (node.type.name != "figure" && pos != posBefore) return false
  view.props.onAction(new NodeSelection(view.state.doc.resolve(pos).action())
  return true
}