fixed regression issue
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user