diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php
index b59d4c39f1..d0d65d2d37 100644
--- a/resources/views/components/modal-confirmation.blade.php
+++ b/resources/views/components/modal-confirmation.blade.php
@@ -68,6 +68,7 @@
dispatchAction: @js($dispatchAction),
submitting: false,
passwordError: '',
+ checkboxIds: @js(collect($checkboxes)->pluck('id')->values()->all()),
selectedActions: @js(collect($checkboxes)->pluck('id')->filter(fn($id) => $this->$id)->values()->all()),
dispatchEvent: @js($dispatchEvent),
dispatchEventType: @js($dispatchEventType),
@@ -103,6 +104,9 @@
return Promise.resolve(this.passwordError);
}
}
+ // The checkboxes are not bound with wire:model, so send their state with the call.
+ // Methods that read the properties (for example stop() with $docker_cleanup) get what the user saw.
+ this.checkboxIds.forEach(id => $wire.$set(id, this.selectedActions.includes(id), false));
if (this.dispatchAction) {
$wire.dispatch(this.submitAction);
return Promise.resolve(true);
@@ -127,11 +131,9 @@
}
});
},
- toggleAction(id) {
- const index = this.selectedActions.indexOf(id);
- if (index > -1) {
- this.selectedActions.splice(index, 1);
- } else {
+ setAction(id, checked) {
+ this.selectedActions = this.selectedActions.filter(selectedId => selectedId !== id);
+ if (checked) {
this.selectedActions.push(id);
}
}
@@ -245,7 +247,8 @@
@foreach ($checkboxes as $index => $checkbox)
@endforeach
diff --git a/tests/v4/Browser/ModalConfirmationCheckboxTest.php b/tests/v4/Browser/ModalConfirmationCheckboxTest.php
new file mode 100644
index 0000000000..fb988a5c64
--- /dev/null
+++ b/tests/v4/Browser/ModalConfirmationCheckboxTest.php
@@ -0,0 +1,80 @@
+set('app.maintenance.store', 'array');
+
+ $this->stack = seedBrowserResourceStack();
+ $this->application = createBrowserApplication($this->stack, [
+ 'uuid' => 'app-browser-stop-modal',
+ 'name' => 'Stop Modal App',
+ 'status' => 'running:healthy',
+ ]);
+ $this->url = applicationConfigurationUrl($this->stack['project'], $this->stack['environment'], $this->application);
+});
+
+const OPEN_STOP_DIALOG = <<<'JS'
+ () => {
+ document.getElementById('application-mobile-stop-trigger').click();
+
+ return true;
+ }
+ JS;
+
+const CLOSE_DIALOG = <<<'JS'
+ () => {
+ window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
+
+ return true;
+ }
+ JS;
+
+const CLEANUP_CHECKBOX = 'input[id^="docker_cleanup-"]';
+
+it('toggles the checkbox even when its DOM state differs from the selected actions', function () {
+ loginAndSkipBoarding();
+
+ visit($this->url)
+ ->assertSee('Stop Modal App')
+ ->assertScript(OPEN_STOP_DIALOG)
+ ->wait(0.5)
+ ->assertScript('() => { const el = document.querySelector(\''.CLEANUP_CHECKBOX.'\'); el.checked = false; return Alpine.$data(el).selectedActions.includes("docker_cleanup"); }')
+ ->click(CLEANUP_CHECKBOX)
+ ->wait(0.3)
+ ->assertScript('() => document.querySelector(\''.CLEANUP_CHECKBOX.'\').checked', true)
+ ->click(CLEANUP_CHECKBOX)
+ ->wait(0.3)
+ ->assertScript('() => document.querySelector(\''.CLEANUP_CHECKBOX.'\').checked', false)
+ ->screenshot(filename: 'modal-confirmation-checkbox-toggle');
+});
+
+it('stops with the cleanup option that the dialog shows after it was closed and opened again', function () {
+ Queue::fake();
+ loginAndSkipBoarding();
+
+ visit($this->url)
+ ->assertSee('Stop Modal App')
+ ->assertScript(OPEN_STOP_DIALOG)
+ ->wait(0.5)
+ ->click(CLEANUP_CHECKBOX)
+ ->wait(0.3)
+ ->assertScript(CLOSE_DIALOG)
+ ->wait(1)
+ ->assertScript(OPEN_STOP_DIALOG)
+ ->wait(0.5)
+ ->assertScript('() => document.querySelector(\''.CLEANUP_CHECKBOX.'\').checked', true)
+ ->click('Continue')
+ ->wait(0.3)
+ ->click('Confirm')
+ ->wait(1.5)
+ ->screenshot(filename: 'modal-confirmation-checkbox-stop');
+
+ Queue::assertPushed(JobDecorator::class, fn (JobDecorator $job) => $job->getAction() instanceof StopApplication
+ && $job->getParameters()[2] === true);
+});