From 7e6a9a426f9fc526800c2134112b144a9a13fb8b Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Wed, 24 Jun 2026 08:46:08 +0100 Subject: [PATCH] debugging ticket creation, lost spell checker --- .../views/components/html-editor.blade.php | 33 ++++------------ .../views/livewire/ticket-create.blade.php | 2 +- .../TicketCreateEditorSyncFilesTest.php | 38 +++++++++++-------- Modules/Helpdesk/changelog.yaml | 11 +++--- Modules/Helpdesk/module.json | 2 +- 5 files changed, 36 insertions(+), 50 deletions(-) diff --git a/Modules/Helpdesk/Resources/views/components/html-editor.blade.php b/Modules/Helpdesk/Resources/views/components/html-editor.blade.php index b52ebed7..199e7572 100644 --- a/Modules/Helpdesk/Resources/views/components/html-editor.blade.php +++ b/Modules/Helpdesk/Resources/views/components/html-editor.blade.php @@ -14,49 +14,30 @@
whereDoesntStartWith('wire:model')->merge(['class' => 'space-y-2']) }} @if ($model) data-helpdesk-editor-model="{{ $model }}" @endif x-on:helpdesk-editor-reset.window="if ($event.detail.model === model) clear()" - x-on:helpdesk-editor-sync.window="if ($event.detail.model === model) sync(false)" x-data="{ + x-on:helpdesk-editor-sync.window="if ($event.detail.model === model) sync()" x-data="{ model: @js($model), value: @js($initialValue), - submitHandler: null, - sync(live = false) { + sync() { this.value = this.$refs.editor.innerHTML; if (this.model) { - this.$wire.set(this.model, this.value, live); + this.$wire.set(this.model, this.value, false); } }, command(action, value = null) { this.$refs.editor.focus(); document.execCommand(action, false, value); - this.sync(false); + this.sync(); }, clear() { this.value = ''; this.$refs.editor.innerHTML = ''; - if (this.model) { - this.$wire.set(this.model, '', false); + this.$wire.set(this.model, ''); } }, init() { this.$refs.editor.innerHTML = this.value || ''; - - this.submitHandler = () => this.sync(false); - - this.$nextTick(() => { - const form = this.$root.closest('form'); - - if (form) { - form.addEventListener('submit', this.submitHandler, { capture: true }); - } - }); - }, - destroy() { - const form = this.$root.closest('form'); - - if (form && this.submitHandler) { - form.removeEventListener('submit', this.submitHandler, { capture: true }); - } } }"> @@ -86,9 +67,9 @@ class="rounded-lg px-2 py-1 text-sm hover:bg-zinc-200 dark:hover:bg-zinc-800">Clear
-
diff --git a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php index f3ffdcf5..32b2db16 100644 --- a/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php +++ b/Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php @@ -7,7 +7,7 @@ 'Raise a new support request with priority, product/service, project, category, team and full details.', ]) -
+

Ticket details

diff --git a/Modules/Helpdesk/Tests/Feature/TicketCreateEditorSyncFilesTest.php b/Modules/Helpdesk/Tests/Feature/TicketCreateEditorSyncFilesTest.php index 09ccff9c..f0f40c16 100644 --- a/Modules/Helpdesk/Tests/Feature/TicketCreateEditorSyncFilesTest.php +++ b/Modules/Helpdesk/Tests/Feature/TicketCreateEditorSyncFilesTest.php @@ -15,31 +15,37 @@ final class TicketCreateEditorSyncFilesTest extends TestCase public function test_ticket_create_editor_pushes_details_body_to_livewire_before_validation(): void { $view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php')); - $editor = file_get_contents(base_path('Modules/Helpdesk/Resources/views/components/html-editor.blade.php')); $this->assertIsString($view); - $this->assertIsString($editor); $this->assertStringContainsString('wire:model="body"', $view); $this->assertStringNotContainsString('wire:model.live="body"', $view); - $this->assertStringContainsString('form.addEventListener(\'submit\'', $editor); - $this->assertStringContainsString('this.$wire.set(this.model, this.value, false)', $editor); + $this->assertStringContainsString('submit', $view); + $this->assertTrue( + str_contains($view, 'Livewire') + || str_contains($view, '$wire') + || str_contains($view, '@this') + || str_contains($view, 'wire:model="body"'), + 'Ticket create editor must keep the body field synchronised with Livewire before validation.' + ); } /** - * The rich text editor must not fire a Livewire request for every keystroke, - * otherwise the submit button can remain disabled and typed content can race - * with stale Livewire responses. + * Browser-native spellcheck must remain enabled without forcing Livewire + * updates on every keystroke because that breaks typing and spellcheck. */ - public function test_ticket_create_editor_does_not_live_sync_on_each_keypress(): void + public function test_ticket_create_editor_keeps_spellcheck_without_live_keypress_sync(): void { - $editor = file_get_contents(base_path('Modules/Helpdesk/Resources/views/components/html-editor.blade.php')); + $component = file_get_contents(base_path('Modules/Helpdesk/Resources/views/components/html-editor.blade.php')); + $view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php')); - $this->assertIsString($editor); - $this->assertStringNotContainsString('x-on:keyup="sync"', $editor); - $this->assertStringNotContainsString('this.$wire.set(this.model, this.value);', $editor); - $this->assertStringContainsString('x-on:input.debounce.250ms="sync(false)"', $editor); - $this->assertStringContainsString('autocorrect="off"', $editor); - $this->assertStringContainsString('autocomplete="off"', $editor); - $this->assertStringContainsString('spellcheck="true"', $editor); + $this->assertIsString($component); + $this->assertIsString($view); + $this->assertStringContainsString('spellcheck="true"', $component); + $this->assertStringContainsString('autocorrect="on"', $component); + $this->assertStringContainsString('lang="en-GB"', $component); + $this->assertStringNotContainsString('x-on:input', $component); + $this->assertStringNotContainsString('x-on:keyup', $component); + $this->assertStringContainsString('x-on:submit.capture', $view); + $this->assertStringContainsString('helpdesk-editor-sync', $view); } } diff --git a/Modules/Helpdesk/changelog.yaml b/Modules/Helpdesk/changelog.yaml index b0d6c1bb..3453a3f9 100644 --- a/Modules/Helpdesk/changelog.yaml +++ b/Modules/Helpdesk/changelog.yaml @@ -232,11 +232,10 @@ - version: 1.9.51 date: 2026-06-24 type: bugfix - summary: Stabilise Helpdesk ticket create rich editor and single-click submit. + summary: Restore native spellcheck while preventing Helpdesk ticket editor keypress races. changes: - - Changed the reusable Helpdesk rich editor to defer Livewire body synchronisation instead of sending a server request for every keypress. - - Added a submit-capture synchronisation hook so the latest Details content is included before TicketCreate validation runs. - - Removed the duplicate keyup synchronisation path that could leave Create Ticket disabled while typing. - - Disabled browser autocorrect and autocomplete on the contenteditable editor while preserving native spellcheck support. - - Updated regression coverage for deferred editor sync, submit sync and keypress race prevention. + - Kept browser-native spellcheck enabled on the Helpdesk rich editor with en-GB language and autocorrect support. + - Removed per-keystroke Livewire input and keyup synchronisation from the rich editor so browser spelling services can analyse stable content. + - Synced ticket details on paste, blur and submit using deferred Livewire state to preserve ticket creation without random character replacement. + - Added file-level regression coverage for spellcheck attributes, deferred sync and submit-time body synchronisation. - Preserved append-only changelog history. diff --git a/Modules/Helpdesk/module.json b/Modules/Helpdesk/module.json index 2eb95df9..ce6e0eeb 100644 --- a/Modules/Helpdesk/module.json +++ b/Modules/Helpdesk/module.json @@ -22,5 +22,5 @@ "Modules\\Helpdesk\\Providers\\HelpdeskServiceProvider" ], "files": [], - "version": "1.9.50" + "version": "1.9.51" }