Skip to content

Conversation

@tijsverkoyen
Copy link
Member

@tijsverkoyen tijsverkoyen commented Oct 27, 2025

This controller allows to show a confirmation modal before submitting form or clicking a link.

For example when you have a form to delete an entity:

    <div
        {{ stimulus_controller(
            'confirm',
            {
                confirmationMessage: 'Are you sure you want to delete this ' ~ entity.title ~ '?'|trans,
                cancelButtonText: 'Cancel'|trans,
                confirmButtonText: 'Delete'|trans,
            }
        ) }}
    >
        {{ form(delete_form, { attr: { 'data-confirm-target': 'element' }}) }}
    </div>
 ...
{% block header_actions_left %}
    <button class="btn btn-danger" type="submit" form="{{ delete_form.vars.id }}">
        <i class="fas fa-trash mr-2"></i>
        {{ 'Delete'|trans }}
    </button>
{% endblock %}

This will result in:
image

Or an example with a link:

    <div
        {{ stimulus_controller(
            'confirm',
            {
                modalTitle: 'Open sumocoders.be?',
                confirmationMessage: 'Are you sure you want to open the SumoCoders website?',
                cancelButtonText: 'Cancel',
                confirmButtonText: 'Open SumoCoders website',
            }
        ) }}
    >
        <a
            href="https://www.sumocoders.be"
            {{ stimulus_target('confirm', 'element') }}
            class="btn btn-secondary"
        >
            Open sumocoders.be
        </a>
    </div>

Will result in:
image

See https://next-app.activecollab.com/108877/my-work?modal=Task-235217-533

Summary by Sourcery

Add a new Stimulus Confirm controller to prompt users with a Bootstrap modal before executing form submissions or link navigations and update the Stimulus docs with its usage examples.

New Features:

  • Introduce a Stimulus 'confirm' controller that shows a Bootstrap confirmation modal before submitting forms or clicking links

Documentation:

  • Add a Confirm modal section to the Stimulus documentation with usage examples for forms and links

@sourcery-ai
Copy link

sourcery-ai bot commented Oct 27, 2025

Reviewer's Guide

Adds a new Stimulus confirm controller to intercept form submissions and link clicks, rendering a configurable Bootstrap confirmation modal before continuing, and includes updated documentation with usage examples.

Sequence diagram for confirm modal controller intercepting form submit or link click

sequenceDiagram
    actor User
    participant "Stimulus Confirm Controller"
    participant "Bootstrap Modal"
    participant "Form or Link"
    User->>"Form or Link": Click (submit or link)
    "Form or Link"->>"Stimulus Confirm Controller": Event (submit/click)
    "Stimulus Confirm Controller"->>"Bootstrap Modal": Show confirmation modal
    User->>"Bootstrap Modal": Click Confirm
    "Bootstrap Modal"->>"Stimulus Confirm Controller": Confirm event
    "Stimulus Confirm Controller"->>"Form or Link": Proceed (submit or follow link)
Loading

Class diagram for ConfirmController structure

classDiagram
    class ConfirmController {
      +modalTitleValue: String
      +confirmationMessageValue: String
      +cancelButtonTextValue: String
      +confirmButtonTextValue: String
      +closeButtonTextValue: String
      +elementTarget
      +connect()
      +isForm()
      +isLink()
      +createModal()
      +showModal()
      +handleConfirm()
      +disconnect()
    }
    ConfirmController --|> Controller
Loading

File-Level Changes

Change Details Files
Introduce Stimulus confirm controller for form and link confirmation
  • Define static values and targets for modal configuration
  • Intercept form submit and link click events
  • Dynamically build, display, and clean up Bootstrap modal
  • Handle user confirmation to submit or navigate appropriately
assets-public/controllers/confirm_controller.js
Update documentation to cover the new confirm controller
  • Append “Confirm modal” section with code examples for forms and links
  • Reformat existing stimulus.md content for consistent indentation and spacing
docs/frontend/stimulus.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey there - I've reviewed your changes - here's some feedback:

  • In createModal, avoid using global document.querySelector for selecting the confirm button and modal—scope your queries to the modal element you just created to prevent collisions when multiple instances are present.
  • Ensure you remove or namespace the event listeners added in connect (on the elementTarget and confirm button) in disconnect so that handlers aren’t leaked or duplicated on re-initialization.
  • Consider splitting the documentation whitespace/formatting changes into a separate commit to keep the feature-specific diff focused and easier to review.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In createModal, avoid using global document.querySelector for selecting the confirm button and modal—scope your queries to the modal element you just created to prevent collisions when multiple instances are present.
- Ensure you remove or namespace the event listeners added in connect (on the elementTarget and confirm button) in disconnect so that handlers aren’t leaked or duplicated on re-initialization.
- Consider splitting the documentation whitespace/formatting changes into a separate commit to keep the feature-specific diff focused and easier to review.

## Individual Comments

### Comment 1
<location> `assets-public/controllers/confirm_controller.js:23-26` </location>
<code_context>
+    if (this.isForm()) eventName = 'submit'
+    if (this.isLink()) eventName = 'click'
+
+    this.elementTarget.addEventListener(eventName, (e) => {
+      e.preventDefault()
+      e.stopPropagation()
+      this.showModal()
+    })
+  }
</code_context>

<issue_to_address>
**issue (bug_risk):** Event listener is added on every connect, risking duplicate listeners.

Reconnecting the controller may attach multiple listeners to the same element, leading to unintended effects. To prevent this, ensure only one listener is active by removing it in disconnect or using `{ once: true }` if suitable.
</issue_to_address>

### Comment 2
<location> `assets-public/controllers/confirm_controller.js:104-109` </location>
<code_context>
+    })
+
+    // bind on confirm button click
+    const confirmBtn = document.querySelector('[data-role="confirm-button"]')
+    confirmBtn.addEventListener('click', () => {
+      this.handleConfirm()
+      bootstrapModal.hide()
</code_context>

<issue_to_address>
**suggestion (bug_risk):** Querying for confirm button may select wrong element if multiple modals exist.

Scope the button query to the newly created modal to ensure the correct element is selected, e.g., use `modal.querySelector('[data-role="confirm-button"]')`.

```suggestion
    // bind on confirm button click
    const confirmBtn = modal.querySelector('[data-role="confirm-button"]')
    confirmBtn.addEventListener('click', () => {
      this.handleConfirm()
      bootstrapModal.hide()
    })
```
</issue_to_address>

### Comment 3
<location> `assets-public/controllers/confirm_controller.js:123-130` </location>
<code_context>
+      return
+    }
+
+    if (this.isLink()) {
+      if (this.elementTarget.target) {
+        window.open(this.elementTarget.href, this.elementTarget.target)
+        return
+      }
+
+      window.location = this.elementTarget.href
+    }
+  }
</code_context>

<issue_to_address>
**suggestion:** Directly setting window.location may not preserve link attributes like rel or download.

Please review if important link attributes like rel or download need to be preserved when redirecting, and handle them as needed.

```suggestion
    if (this.isLink()) {
      if (this.elementTarget.target) {
        window.open(this.elementTarget.href, this.elementTarget.target)
        return
      }

      // Handle important link attributes
      if (this.elementTarget.hasAttribute('download')) {
        // Simulate click to trigger download
        this.elementTarget.click()
        return
      }

      if (this.elementTarget.hasAttribute('rel')) {
        // Create a temporary anchor to preserve rel attribute
        const tempLink = document.createElement('a')
        tempLink.href = this.elementTarget.href
        tempLink.rel = this.elementTarget.getAttribute('rel')
        // Copy other relevant attributes if needed
        if (this.elementTarget.hasAttribute('type')) {
          tempLink.type = this.elementTarget.getAttribute('type')
        }
        document.body.appendChild(tempLink)
        tempLink.click()
        document.body.removeChild(tempLink)
        return
      }

      window.location = this.elementTarget.href
    }
```
</issue_to_address>

### Comment 4
<location> `docs/frontend/stimulus.md:30` </location>
<code_context>
 The only differnce is that you add the event `data-action="click->clipboard#copy"` directly on the text.

 ```html
</code_context>

<issue_to_address>
**issue (typo):** Typo: 'differnce' should be 'difference'.

Please update the spelling to 'difference'.
</issue_to_address>

### Comment 5
<location> `docs/frontend/stimulus.md:208` </location>
<code_context>
+
+## Confirm modal
+
+This controller allows to show a confirmation modal before submitting form or clicking a link.
+
+For example when you have a form to delete an entity:
</code_context>

<issue_to_address>
**suggestion (typo):** Grammatical improvement: 'allows to show' should be 'allows you to show'.

Change the sentence to: 'This controller allows you to show a confirmation modal before submitting a form or clicking a link.'

```suggestion
This controller allows you to show a confirmation modal before submitting a form or clicking a link.
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a new Stimulus controller that displays a Bootstrap confirmation modal before form submissions or link navigation, allowing users to confirm or cancel destructive actions.

Key changes:

  • Added a new confirm_controller.js that intercepts form submissions and link clicks to show a customizable confirmation modal
  • Updated Stimulus documentation with comprehensive usage examples for both forms and links

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
assets-public/controllers/confirm_controller.js New Stimulus controller implementing confirmation modal functionality with form and link support
docs/frontend/stimulus.md Added documentation section for the confirm modal controller with usage examples and formatting improvements

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tijsverkoyen tijsverkoyen merged commit 466a96b into master Oct 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants