diff --git a/app/components/avo/button_component.rb b/app/components/avo/button_component.rb index bf74df0d75..ed48f1628f 100644 --- a/app/components/avo/button_component.rb +++ b/app/components/avo/button_component.rb @@ -27,12 +27,7 @@ def initialize(path = nil, size: :md, style: :outline, color: :gray, icon: nil, def args if @args[:loading] @args[:"data-controller"] = "loading-button" - @args[:"data-loading-button-confirmed-value"] = false @args[:"data-action"] = "click->loading-button#attemptSubmit" - - if @args[:confirm] - @args[:"data-loading-button-confirmation-message-value"] = @args.delete(:confirm) - end end @args[:class] = button_classes diff --git a/app/components/avo/resource_component.rb b/app/components/avo/resource_component.rb index a7c3591ffe..c02a5dc20a 100644 --- a/app/components/avo/resource_component.rb +++ b/app/components/avo/resource_component.rb @@ -187,7 +187,10 @@ def render_save_button(control) style: :primary, loading: true, type: :submit, - icon: "avo/save" do + icon: "avo/save", + data: { + turbo_confirm: @resource.confirm_on_save ? t("avo.are_you_sure") : nil + } do control.label end end diff --git a/app/javascript/js/controllers/loading_button_controller.js b/app/javascript/js/controllers/loading_button_controller.js index 1ad2e6dd30..5c9de98ccc 100644 --- a/app/javascript/js/controllers/loading_button_controller.js +++ b/app/javascript/js/controllers/loading_button_controller.js @@ -2,36 +2,31 @@ import { Controller } from '@hotwired/stimulus' export default class extends Controller { - spinnerMarkup = `
-
-
-
`; + spinnerMarkup = ` +
+
+
+
`; static values = { confirmationMessage: String, confirmed: Boolean, } - attemptSubmit(e) { - // If the user has to confirm the action - if (this.confirmationMessageValue) { - this.confirmAndApply(e) - } else { - this.applyLoader() - } + connect() { + this.button.setAttribute('data-original-content', this.button.innerHTML) + this.dialog = document.getElementById('turbo-confirm') + this.dialogCloseHandler = this.handleDialogClose.bind(this) - return null + this.dialog.addEventListener('close', this.dialogCloseHandler) } - confirmAndApply(e) { - // Intervene only if not confirmed - if (!this.confirmedValue) { - e.preventDefault() + disconnect() { + this.dialog.removeEventListener('close', this.dialogCloseHandler) + } - if (window.confirm(this.confirmationMessageValue)) { - this.applyLoader() - } - } + attemptSubmit() { + this.applyLoader() } get button() { @@ -45,19 +40,18 @@ export default class extends Controller { button.style.height = `${button.getBoundingClientRect().height}px` button.innerHTML = this.spinnerMarkup button.classList.add('justify-center') - - setTimeout(() => { - this.markConfirmed() - button.click() - button.setAttribute('disabled', 'disabled') - }, 1) } - markConfirmed() { - this.confirmedValue = true + handleDialogClose() { + if (this.dialog.returnValue !== 'confirm') { + this.resetButton() + } } - markUnconfirmed() { - this.confirmedValue = false + resetButton() { + const { button } = this + + button.innerHTML = button.getAttribute('data-original-content') + button.removeAttribute('disabled'); } } diff --git a/lib/avo/resources/base.rb b/lib/avo/resources/base.rb index 30962fc3f6..a16c7524bc 100644 --- a/lib/avo/resources/base.rb +++ b/lib/avo/resources/base.rb @@ -60,6 +60,7 @@ def current_user class_attribute :filters_loader class_attribute :view_types class_attribute :grid_view + class_attribute :confirm_on_save, default: false class_attribute :visible_on_sidebar, default: true class_attribute :index_query, default: -> { query diff --git a/spec/dummy/app/avo/resources/store.rb b/spec/dummy/app/avo/resources/store.rb index 370ee655e1..b596c06003 100644 --- a/spec/dummy/app/avo/resources/store.rb +++ b/spec/dummy/app/avo/resources/store.rb @@ -1,5 +1,6 @@ class Avo::Resources::Store < Avo::BaseResource self.includes = [:location] + self.confirm_on_save = true def fields field :id, as: :id diff --git a/spec/features/avo/confirm_on_save_spec.rb b/spec/features/avo/confirm_on_save_spec.rb new file mode 100644 index 0000000000..52cf850e32 --- /dev/null +++ b/spec/features/avo/confirm_on_save_spec.rb @@ -0,0 +1,56 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.feature "ConfirmOnSave", type: :feature do + context "edit" do + let!(:store) { create :store, name: store_name } + let(:store_name) { "original name" } + let(:name_field) { "store[name]" } + let(:changed_name) { "new name" } + + describe "when saving the record" do + it "confirms the operation" do + visit "/admin/resources/stores/#{store.id}/edit" + + save + + expect(page).to have_selector("#turbo-confirm button[value='confirm']") + end + + it "completes the operation on confirmation" do + visit "/admin/resources/stores/#{store.id}/edit" + + expect(page).to have_field name_field + + fill_in "store[name]", with: changed_name + + save + + accept_custom_alert do + click_on "Yes, I'm sure" + end + + expect(store.reload.name).to eq(changed_name) + end + + it "does not complete the operation on denial" do + visit "/admin/resources/stores/#{store.id}/edit" + + expect(page).to have_field name_field + + fill_in "store[name]", with: changed_name + + save + + accept_custom_alert do + click_on "No, cancel" + end + + expect(page).not_to have_selector(".button-spinner") + expect(find_field_value_element("name")).to have_text changed_name + expect(store.name).to eq(store_name) + end + end + end +end