Use Stimulus and Turbo

If you are using both Stimulus and Turbo from Hotwired, here’s a Stimulus controller I recommend setting up on your popover layouts to improve your developer experience and easily controller the popovers.

This uses the Stimulus dispatch API and Turbo events.

<%# app/popovers/layouts/popover.html.erb %>

<div class="popover-layout" data-controller="popover">
  <%= yield %>
</div>
// app/javascript/controllers/popover_controller.js

import { Controller } from "@hotwired/stimulus"

// Connects to data-controller="popover"
export default class extends Controller {
  connect() {
    this.dispatch('open', {
      target: this.coupdoeilElement, detail: { popover: this.popoverElement, controller: this }
    })
  }
  
  disconnect() {
    this.dispatch('close', { target: this.coupdoeilElement })
  }

  closeOnSubmitSuccess(event) {
    if (event.detail.success) {
      this.close()
    } else {
      // handle error if needed
    }
  }
  
  close() {
    this.popoverElement.close()
  }
  
  get coupdoeilElement() {
    return this.popoverElement.coupdoeilElement
  }

  get popoverElement() {
    return this.element.parentElement
  }
}

The full file can also be found in the repository: app/javascript/controllers/popover_controller.js.

This way, you get events when the popover opens and closes.

document.addEventListener("popover:open", (event) => {
  console.log(event.target) // the <coup-doeil> element
  console.log(event.detail.popover) // the popover element (the wrapper, abose the layout)
  console.log(event.detail.controller) // the stimulus controller
})

document.addEventListener("popover:close", (event) => {
  console.log(event.target) // the <coup-doeil> element
})

You can also trigger closing when a form is submitted but only when it’s successful, using Turbo submit-end event:

<%# app/popovers/contact_popover/subscribe.html.erb %>

<div>
  <%= form_for [:subscribe, @contact], 
               html: { data: { action: "turbo:submit-end->popover#closeOnSubmitSuccess" } } do |f| %>
    <%# … %>
    <%= f.submit class: "btn btn-primary" %>
  <% end %>
</div>