fixed regression issue

This commit is contained in:
Mark Gregory
2026-06-24 17:51:50 +01:00
parent 5b4b81eb4d
commit 0701713823
5 changed files with 142 additions and 23 deletions
@@ -3,34 +3,122 @@
'label' => 'Details',
'placeholder' => 'Describe the ticket, include steps to reproduce, expected result, actual result and any useful technical details.',
'rows' => 10,
'editorDusk' => null,
])
<div class="space-y-2">
<div
class="space-y-2"
x-data="{
model: @js($model),
value: @entangle($model).defer,
init() {
this.$nextTick(() => {
this.writeEditorValue(this.value || '');
});
},
writeEditorValue(value) {
if (!this.$refs.editor) {
return;
}
this.$refs.editor.textContent = value || '';
},
syncToLivewire(live = false) {
const value = this.$refs.editor?.innerText || '';
this.value = value;
this.$refs.input.value = value;
this.$refs.input.dispatchEvent(new Event('input', { bubbles: true }));
this.$wire.set(this.model, this.value, live);
},
resetEditor() {
this.value = '';
this.writeEditorValue('');
this.syncToLivewire(false);
},
runCommand(command) {
this.$refs.editor?.focus();
document.execCommand(command, false, null);
this.syncToLivewire(false);
},
wrapBlock(tagName) {
this.$refs.editor?.focus();
document.execCommand('formatBlock', false, tagName);
this.syncToLivewire(false);
},
}"
x-on:helpdesk-editor-sync.window="if (($event.detail?.model || model) === model) syncToLivewire(false)"
x-on:helpdesk-editor-reset.window="if (($event.detail?.model || model) === model) resetEditor()"
>
<label for="{{ $model }}-editor" class="block text-sm font-semibold text-zinc-200">
{{ $label }}
</label>
<div class="overflow-hidden rounded-2xl border border-zinc-700 bg-zinc-950 shadow-sm">
<div class="flex flex-wrap items-center gap-2 border-b border-zinc-700 bg-zinc-900/80 px-4 py-2 text-sm text-zinc-300">
<span class="rounded-lg bg-zinc-800 px-2 py-1 font-semibold text-zinc-100">Spellcheck enabled</span>
<span class="text-zinc-500">Use Markdown for formatting, lists, quotes, code and links.</span>
<div class="flex flex-wrap items-center gap-1.5 border-b border-zinc-700 bg-zinc-900/80 px-4 py-2 text-sm text-zinc-300">
<span class="mr-2 rounded-lg bg-zinc-800 px-2 py-1 text-xs font-black uppercase tracking-wide text-zinc-100">Spellcheck enabled</span>
<button type="button" x-on:click="runCommand('bold')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
aria-label="Bold selected text">
B
</button>
<button type="button" x-on:click="runCommand('italic')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black italic text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
aria-label="Italic selected text">
I
</button>
<button type="button" x-on:click="runCommand('underline')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black underline text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70"
aria-label="Underline selected text">
U
</button>
<button type="button" x-on:click="runCommand('insertUnorderedList')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
• List
</button>
<button type="button" x-on:click="runCommand('insertOrderedList')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
1. List
</button>
<button type="button" x-on:click="wrapBlock('blockquote')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
Quote
</button>
<button type="button" x-on:click="wrapBlock('pre')"
class="inline-flex min-h-8 items-center justify-center rounded-lg border border-zinc-700 bg-zinc-950 px-3 py-1.5 text-xs font-black text-zinc-100 shadow-sm transition hover:bg-zinc-800 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
Code
</button>
</div>
<textarea
<div
id="{{ $model }}-editor"
wire:model.defer="{{ $model }}"
rows="{{ $rows }}"
placeholder="{{ $placeholder }}"
x-ref="editor"
contenteditable="true"
spellcheck="true"
lang="en-GB"
autocorrect="on"
autocomplete="off"
autocomplete="on"
autocapitalize="sentences"
inputmode="text"
class="block min-h-[14rem] w-full resize-y border-0 bg-zinc-950 px-5 py-4 text-base leading-7 text-zinc-100 placeholder:text-zinc-500 focus:outline-none focus:ring-2 focus:ring-blue-500"
role="textbox"
aria-multiline="true"
data-placeholder="{{ $placeholder }}"
@if ($editorDusk) data-dusk="{{ $editorDusk }}" dusk="{{ $editorDusk }}" @endif
x-on:blur="syncToLivewire(false)"
class="helpdesk-rich-editor block min-h-[14rem] w-full overflow-y-auto whitespace-pre-wrap break-words border-0 bg-zinc-950 px-5 py-4 text-base leading-7 text-zinc-100 outline-none transition placeholder:text-zinc-500 empty:before:pointer-events-none empty:before:text-zinc-500 empty:before:content-[attr(data-placeholder)] focus:ring-2 focus:ring-blue-500"
></div>
<textarea
x-ref="input"
wire:model.defer="{{ $model }}"
class="sr-only"
tabindex="-1"
aria-hidden="true"
></textarea>
</div>
<p class="text-xs font-semibold text-zinc-400">
Browser spellcheck is handled by the native editor and remains active after punctuation such as commas.
Browser spellcheck stays active in the native editable area. Formatting is synchronised only on blur and submit to avoid Livewire keypress regressions.
</p>
</div>
@@ -245,10 +245,10 @@
<div class="lg:col-span-3 xl:col-span-6">
<x-helpdesk::html-editor
model="body"
label="Details"
rows="12"
placeholder="Describe the issue, expected outcome, steps to reproduce and any useful context..."
wire:model="body"
editor-dusk="ticket-body-editor"
/>
@@ -330,25 +330,28 @@
@enderror
</div>
</div>
<div class="flex items-center justify-end gap-3 border-t border-zinc-700 bg-zinc-900 px-6 py-4">
</div>
<div class="flex flex-col-reverse gap-2 border-t border-zinc-700 bg-zinc-900 px-6 py-4 sm:flex-row sm:items-center sm:justify-end">
<button type="button" wire:click="clearForm" wire:loading.attr="disabled" wire:target="clearForm"
class="inline-flex items-center gap-2 rounded-xl bg-red-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-700 disabled:cursor-not-allowed disabled:opacity-50"
class="inline-flex min-h-10 items-center justify-center rounded-lg bg-red-600 px-4 py-2 text-xs font-black text-white shadow-sm transition hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-400/70 disabled:cursor-not-allowed disabled:opacity-50"
data-dusk="ticket-create-clear" dusk="ticket-create-clear">
<i class="fa-solid fa-eraser" aria-hidden="true"></i>
<i class="fa-solid fa-eraser mr-2" aria-hidden="true"></i>
<span wire:loading.remove wire:target="clearForm">Clear</span>
<span wire:loading wire:target="clearForm">Clearing...</span>
</button>
<a href="{{ route('helpdesk.tickets.index') }}" wire:navigate
class="rounded-xl bg-blue-600 px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-blue-700">
class="inline-flex min-h-10 items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-xs font-black text-white shadow-sm transition hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-400/70">
<i class="fa-solid fa-arrow-left mr-2" aria-hidden="true"></i>
Cancel
</a>
<button type="submit"
class="inline-flex items-center gap-2 rounded-xl bg-green-600 px-5 py-2 text-sm font-semibold text-white shadow-sm hover:bg-green-700 disabled:cursor-not-allowed disabled:opacity-50"
class="inline-flex min-h-10 items-center justify-center rounded-lg bg-emerald-600 px-4 py-2 text-xs font-black text-white shadow-sm transition hover:bg-emerald-700 focus:outline-none focus:ring-2 focus:ring-emerald-400/70 disabled:cursor-not-allowed disabled:opacity-50"
data-dusk="ticket-create-submit" dusk="ticket-create-submit" wire:loading.attr="disabled" wire:target="save,attachments" x-bind:disabled="helpdeskAttachmentsUploading" wire:dirty.class="ring-2 ring-amber-400/40">
<i class="fa-regular fa-floppy-disk" aria-hidden="true"></i>
<span wire:loading.remove wire:target="save,attachments">Create Ticket</span>
<i class="fa-regular fa-floppy-disk mr-2" aria-hidden="true"></i>
<span wire:loading.remove wire:target="save,attachments">Create ticket</span>
<span wire:loading wire:target="attachments">Uploading...</span>
<span wire:loading wire:target="save">Creating...</span>
</button>
@@ -32,15 +32,15 @@ final class DashboardTicketCardsSpellcheckFilesTest extends TestCase
}
#[Test]
public function helpdesk_editor_uses_native_spellcheck_textarea(): void
public function helpdesk_editor_uses_native_spellcheck_contenteditable_area(): void
{
$view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/components/html-editor.blade.php'));
$this->assertIsString($view);
$this->assertStringContainsString('<textarea', $view);
$this->assertStringContainsString('contenteditable="true"', $view);
$this->assertStringContainsString('spellcheck="true"', $view);
$this->assertStringContainsString('lang="en-GB"', $view);
$this->assertStringContainsString('wire:model.defer', $view);
$this->assertStringNotContainsString('contenteditable', $view);
$this->assertStringNotContainsString('x-on:keyup', $view);
$this->assertStringNotContainsString('x-on:input', $view);
}
@@ -22,6 +22,7 @@ final class TicketCreateEditorSyncFilesTest extends TestCase
$this->assertStringNotContainsString('x-on:keyup', $editor);
$this->assertStringContainsString('contenteditable="true"', $editor);
$this->assertStringContainsString('spellcheck="true"', $editor);
$this->assertStringContainsString('lang="en-GB"', $editor);
$this->assertStringContainsString('this.$wire.set(this.model, this.value, live)', $editor);
$this->assertStringContainsString('syncToLivewire(false)', $editor);
}
@@ -35,9 +36,25 @@ final class TicketCreateEditorSyncFilesTest extends TestCase
$view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php'));
$this->assertIsString($view);
$this->assertStringContainsString('wire:model="body"', $view);
$this->assertStringContainsString('model="body"', $view);
$this->assertStringContainsString('x-on:submit.capture', $view);
$this->assertStringContainsString('helpdesk-editor-sync', $view);
$this->assertStringNotContainsString('wire:model.live="body"', $view);
}
/**
* The create-ticket footer buttons should match the compact professional
* pattern already used by Ticket controls.
*/
public function test_ticket_create_footer_buttons_match_ticket_controls_style(): void
{
$view = file_get_contents(base_path('Modules/Helpdesk/Resources/views/livewire/ticket-create.blade.php'));
$this->assertIsString($view);
$this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-red-600 px-4 py-2 text-xs font-black', $view);
$this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-blue-600 px-4 py-2 text-xs font-black', $view);
$this->assertStringContainsString('inline-flex min-h-10 items-center justify-center rounded-lg bg-emerald-600 px-4 py-2 text-xs font-black', $view);
$this->assertStringContainsString('Create ticket', $view);
$this->assertStringNotContainsString('rounded-xl bg-green-600 px-5 py-2 text-sm font-semibold', $view);
}
}
+11
View File
@@ -513,3 +513,14 @@
- Preserved the existing expandable-card behaviour and Livewire navigation initialisation.
- Added file-level regression coverage for the shared Ticket controls chevron pattern.
- Preserved append-only changelog history.
- version: 1.10.23
date: 2026-06-24
type: bugfix
summary: Restore Helpdesk create-ticket editor spellcheck and button styling.
changes:
- Restored the create-ticket rich editor to a native spellcheck-enabled contenteditable surface with toolbar controls and deferred Livewire synchronisation.
- Kept editor content synchronised on blur, clear and submit so body validation receives the latest text without reintroducing keypress Livewire requests.
- Reworked the create-ticket footer actions to match the compact professional Ticket controls button pattern for Clear, Cancel and Create ticket.
- Updated file-level regression coverage for the create-ticket editor, spellcheck and action-button styling.
- Preserved append-only changelog history.