From a3c80c9778d2c4b744afafb8d88dc47f51c448aa Mon Sep 17 00:00:00 2001
From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com>
Date: Tue, 2 Jun 2026 17:09:14 +0200
Subject: [PATCH] fix(forms): focus password fields before visibility toggles
Render password inputs and textareas before their visibility toggle buttons so tab navigation reaches the editable field first.
---
.../components/forms/env-var-input.blade.php | 40 +++++++++----------
.../views/components/forms/input.blade.php | 20 +++++-----
.../views/components/forms/textarea.blade.php | 30 +++++++-------
.../PasswordVisibilityComponentTest.php | 18 +++++++++
4 files changed, 63 insertions(+), 45 deletions(-)
diff --git a/resources/views/components/forms/env-var-input.blade.php b/resources/views/components/forms/env-var-input.blade.php
index f637425c1..976c63b29 100644
--- a/resources/views/components/forms/env-var-input.blade.php
+++ b/resources/views/components/forms/env-var-input.blade.php
@@ -196,26 +196,6 @@
}"
@click.outside="showDropdown = false">
- @if ($type === 'password' && $allowToPeak)
-
- @endif
-
get('placeholder') }}"
@if ($autofocus) autofocus @endif>
+ @if ($type === 'password' && $allowToPeak)
+
+ @endif
+
{{-- Dropdown for suggestions --}}
+ merge(['class' => $defaultClass]) }} @required($required)
+ @if ($modelBinding !== 'null') wire:model={{ $modelBinding }} wire:dirty.class="[box-shadow:inset_4px_0_0_#6b16ed,inset_0_0_0_2px_#e5e5e5] dark:[box-shadow:inset_4px_0_0_#fcd452,inset_0_0_0_2px_#242424]" @endif
+ wire:loading.attr="disabled"
+ @readonly($readonly) @disabled($disabled) id="{{ $htmlId }}"
+ name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
+ aria-placeholder="{{ $attributes->get('placeholder') }}"
+ @if ($autofocus) x-ref="autofocusInput" @endif>
@if ($allowToPeak)
@else
diff --git a/resources/views/components/forms/textarea.blade.php b/resources/views/components/forms/textarea.blade.php
index 22c89fd72..752e67433 100644
--- a/resources/views/components/forms/textarea.blade.php
+++ b/resources/views/components/forms/textarea.blade.php
@@ -31,6 +31,21 @@ function handleKeydown(e) {
@else
@if ($type === 'password')
+ merge(['class' => $defaultClassInput]) }} @required($required)
+ @if ($modelBinding !== 'null') wire:model={{ $modelBinding }} wire:dirty.class="[box-shadow:inset_4px_0_0_#6b16ed,inset_0_0_0_2px_#e5e5e5] dark:[box-shadow:inset_4px_0_0_#fcd452,inset_0_0_0_2px_#242424]" @endif
+ wire:loading.attr="disabled"
+ type="{{ $type }}" @readonly($readonly) @disabled($disabled) id="{{ $htmlId }}"
+ name="{{ $name }}" placeholder="{{ $attributes->get('placeholder') }}"
+ aria-placeholder="{{ $attributes->get('placeholder') }}">
+
@if ($allowToPeak)
@else
diff --git a/tests/Feature/PasswordVisibilityComponentTest.php b/tests/Feature/PasswordVisibilityComponentTest.php
index a5087f5cd..9c3d5d673 100644
--- a/tests/Feature/PasswordVisibilityComponentTest.php
+++ b/tests/Feature/PasswordVisibilityComponentTest.php
@@ -21,6 +21,12 @@
->not->toContain('changePasswordFieldType');
});
+it('renders password input before visibility toggle in tab order', function () {
+ $html = Blade::render('');
+
+ expect(strpos($html, 'toBeLessThan(strpos($html, 'aria-label="Toggle password visibility"'));
+});
+
it('renders password textarea with Alpine-managed visibility state', function () {
$html = Blade::render('');
@@ -31,6 +37,12 @@
->not->toContain('changePasswordFieldType');
});
+it('renders password textarea input before visibility toggle in tab order', function () {
+ $html = Blade::render('');
+
+ expect(strpos($html, 'toBeLessThan(strpos($html, 'aria-label="Toggle password visibility"'));
+});
+
it('renders textarea without monospace classes by default', function () {
$html = Blade::render('');
@@ -53,3 +65,9 @@
->toContain("x-on:click=\"type = type === 'password' ? 'text' : 'password'\"")
->toContain('x-bind:type="type"');
});
+
+it('renders env var password input before visibility toggle in tab order', function () {
+ $html = Blade::render('');
+
+ expect(strpos($html, 'toBeLessThan(strpos($html, 'aria-label="Toggle password visibility"'));
+});