fix(ui): gate copy button on clipboard API support

Only show the copy control when isSecureContext and clipboard.writeText
are available, and style the empty internal hostname state as a readonly
input to match adjacent fields.
This commit is contained in:
Andras Bacsai
2026-08-06 13:58:33 +02:00
parent 9fd2a21f24
commit a6ab360ac2
4 changed files with 15 additions and 9 deletions
@@ -1,17 +1,19 @@
@props(['text', 'label' => null])
<div class="w-full" x-data="{ copied: false, isSecure: window.isSecureContext }">
<div class="w-full"
x-data="{ copied: false, canCopy: window.isSecureContext && typeof navigator.clipboard?.writeText === 'function' }">
@if ($label)
<label class="flex gap-1 items-center mb-1 text-sm font-medium text-black dark:text-white">{{ $label }}</label>
@endif
<div class="relative">
<input type="text" value="{{ $text }}"
class="input input-with-copy-button bg-white dark:bg-coolgray-100 dark:read-only:bg-coolgray-100 dark:read-only:text-white"
class="input bg-white dark:bg-coolgray-100 dark:read-only:bg-coolgray-100 dark:read-only:text-white"
x-bind:class="{ 'input-with-copy-button': canCopy }"
readonly
@keydown.prevent @paste.prevent @cut.prevent @drop.prevent
@focus="$event.target.select()">
<button
x-show="isSecure"
x-show="canCopy"
type="button"
@click.prevent="copied = true; navigator.clipboard.writeText({{ Js::from($text) }}); setTimeout(() => copied = false, 1000)"
class="copy-button flex absolute inset-y-0 right-0 z-10 items-center pr-2 cursor-pointer text-neutral-500 transition-colors hover:text-black focus-visible:ring-2 focus-visible:ring-coollabs focus-visible:ring-offset-2 dark:text-neutral-400 dark:hover:text-white dark:focus-visible:ring-warning dark:focus-visible:ring-offset-base"
@@ -96,11 +96,12 @@
@if ($currentInternalHostname)
<x-forms.copy-button label="Internal hostname" :text="$currentInternalHostname" />
@else
<div>
<p class="mb-1 text-sm font-medium text-black dark:text-white">Internal hostname</p>
<p class="flex min-h-10 items-center rounded border border-neutral-300 px-3 text-sm text-neutral-500 dark:border-coolgray-300 dark:text-fg-dim">
{{ $currentInternalHostnameLoaded ? 'No deployed container found' : 'Loading…' }}
</p>
<div class="w-full">
<label class="mb-1 flex items-center gap-1 text-sm font-medium text-black dark:text-white">Internal hostname</label>
<input type="text"
value="{{ $currentInternalHostnameLoaded ? 'No deployed container found' : 'Loading…' }}"
class="input input-with-copy-button bg-white dark:bg-coolgray-100 dark:read-only:bg-coolgray-100 dark:read-only:text-white"
readonly aria-live="polite">
</div>
@endif
<x-forms.copy-button label="Docker network" :text="$application->destination->network" />
@@ -14,6 +14,7 @@ it('shows internal Docker access details in application general settings', funct
->toContain('Network aliases')
->toContain('wire:init="loadCurrentInternalHostname"')
->toContain('$currentInternalHostname')
->toContain('class="input input-with-copy-button bg-white dark:bg-coolgray-100 dark:read-only:bg-coolgray-100 dark:read-only:text-white"')
->not->toContain('Changes with each deployment')
->toContain("window.scrollToSettingsSection?.('networking-section')")
->and($generalComponent)
@@ -38,7 +38,9 @@ it('renders copy fields as visible readonly controls with an accessible copy act
expect($html)
->toContain('label class="flex gap-1 items-center mb-1 text-sm font-medium text-black dark:text-white"')
->toContain('readonly')
->toContain('input-with-copy-button')
->toContain("canCopy: window.isSecureContext && typeof navigator.clipboard?.writeText === 'function'")
->toContain("x-bind:class=\"{ 'input-with-copy-button': canCopy }\"")
->toContain('x-show="canCopy"')
->toContain('copy-button')
->toContain('aria-label="Copy to clipboard"')
->toContain('title="Copy to clipboard"')