diff --git a/app/View/Components/Forms/Input.php b/app/View/Components/Forms/Input.php index e94b181375..3824bfe46d 100644 --- a/app/View/Components/Forms/Input.php +++ b/app/View/Components/Forms/Input.php @@ -25,7 +25,7 @@ class Input extends Component public bool $readonly = false, public ?string $helper = null, public bool $allowToPeak = true, - public bool $copyable = false, + public ?bool $copyable = null, public bool $isMultiline = false, public string $defaultClass = 'input', public string $autocomplete = 'off', @@ -71,6 +71,9 @@ class Input extends Component if (is_null($this->name)) { $this->name = $this->modelBinding !== 'null' ? $this->modelBinding : (string) $this->id; } + // Password fields get a copy button unless the caller opts out (account password forms). + $this->copyable ??= $this->type === 'password'; + // Durable class (not type-attr based): Alpine may toggle type to "text" when revealing, // and settings-workspace CSS otherwise overrides utility padding-right. $hasPeek = $this->type === 'password' && $this->allowToPeak; diff --git a/resources/views/auth/confirm-password.blade.php b/resources/views/auth/confirm-password.blade.php index ee5cbb0858..757f5a01f9 100644 --- a/resources/views/auth/confirm-password.blade.php +++ b/resources/views/auth/confirm-password.blade.php @@ -22,7 +22,7 @@
@csrf - {{ __('auth.confirm_password') }} diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index 0788e43f13..c25e620dc9 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -25,13 +25,13 @@ - @else - @endenv diff --git a/resources/views/auth/register.blade.php b/resources/views/auth/register.blade.php index 4a468e010f..160495bd27 100644 --- a/resources/views/auth/register.blade.php +++ b/resources/views/auth/register.blade.php @@ -37,9 +37,9 @@ $email = getOldOrLocal('email', 'test3@example.com'); autocomplete="name" label="{{ __('input.name') }}" /> - -
diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php index 8ef66418d4..52c5567ab5 100644 --- a/resources/views/auth/reset-password.blade.php +++ b/resources/views/auth/reset-password.blade.php @@ -20,9 +20,9 @@ @csrf - -
diff --git a/resources/views/components/forms/input.blade.php b/resources/views/components/forms/input.blade.php index 69cb937879..7b5b610295 100644 --- a/resources/views/components/forms/input.blade.php +++ b/resources/views/components/forms/input.blade.php @@ -5,7 +5,10 @@ // single JS expression here — a directive inside the tag would // break Blade's component-tag compiler. $copyResolve = null; - if ($copyable) { + if ($copyable && $type === 'password') { + // Copy the field as shown: it includes unsaved edits and works without Livewire. + $copyResolve = "\$el.parentElement.querySelector('input').value"; + } elseif ($copyable) { $copyModel = $modelBinding !== 'null' ? $modelBinding : $attributes->get('wire:model'); $copyResolve = $copyModel ? "\$wire.get('".$copyModel."')" diff --git a/resources/views/livewire/force-password-reset.blade.php b/resources/views/livewire/force-password-reset.blade.php index 8fb441bd67..e34b16a325 100644 --- a/resources/views/livewire/force-password-reset.blade.php +++ b/resources/views/livewire/force-password-reset.blade.php @@ -2,9 +2,9 @@ description="Your password must be updated before you can continue to the dashboard."> - -
diff --git a/resources/views/livewire/profile/index.blade.php b/resources/views/livewire/profile/index.blade.php index 33a072a6af..eddc9ae5ea 100644 --- a/resources/views/livewire/profile/index.blade.php +++ b/resources/views/livewire/profile/index.blade.php @@ -208,10 +208,10 @@
- + required type="password" :copyable="false" /> + + type="password" :copyable="false" />
diff --git a/tests/Feature/PasswordVisibilityComponentTest.php b/tests/Feature/PasswordVisibilityComponentTest.php index dbed7e6c54..431fca1fba 100644 --- a/tests/Feature/PasswordVisibilityComponentTest.php +++ b/tests/Feature/PasswordVisibilityComponentTest.php @@ -74,3 +74,24 @@ it('renders env var password input before visibility toggle in tab order', funct expect(strpos($html, 'toBeLessThan(strpos($html, 'aria-label="Toggle password visibility"')); }); + +it('adds a copy button that reads the live field value to password inputs by default', function () { + $html = Blade::render(''); + + expect($html) + ->toContain('Copy to clipboard') + ->toContain('input-with-copy-and-peek') + ->toContain('$el.parentElement.querySelector('input').value'); +}); + +it('omits the copy button when a password input opts out', function () { + $html = Blade::render(''); + + expect($html) + ->toContain('Toggle password visibility') + ->not->toContain('Copy to clipboard'); +}); + +it('keeps text inputs without a copy button unless they opt in', function () { + expect(Blade::render(''))->not->toContain('Copy to clipboard'); +});