fix(ui): refine overlays, toast actions, and save-state feedback

Improve overlay stacking and positioning, add toast copying, standardize domain labels, and animate unsaved changes while saving.
This commit is contained in:
Andras Bacsai committed 2026-08-11 12:08:44 +02:00
1 parent 41b3ed95aa
commit 1a8a4bb5ef
15 files changed
+133 -36

No files matched your search

+4
View File
@@ -1301,6 +1301,10 @@ it('uses segmented fields when adding and editing application domains', function
->toContain("scheme: 'https'")
->toContain('<x-forms.listbox id="{{ $id }}-protocol"')
->not->toContain('<select id="{{ $id }}-protocol"')
->toContain("['value' => 'https', 'label' => 'https']")
->toContain("['value' => 'http', 'label' => 'http']")
->toContain('class="mb-1.5 flex h-4 w-full items-center gap-1.5"')
->not->toContain('class="mb-1.5 block text-sm font-medium"')
->toContain('min="1"')
->toContain('max="65535"');
});
@@ -75,6 +75,7 @@ it('renders the changed configuration labels without a second backend request',
expect($view)
->toContain(':compact-after="5000"')
->toContain('position="top-right"')
->toContain(':compact-storage-key="$compactStorageKey"')
->toContain('wire:key="configuration-warning-{{ $currentConfigurationHash }}"')
->toContain('x-on:click="configurationDiffModalOpen = true"')
@@ -85,6 +86,7 @@ it('supports timed compact popup notifications', function () {
$view = file_get_contents(resource_path('views/components/popup-small.blade.php'));
expect($view)
->toContain("\$position === 'top-right' ? 'top-16' : 'bottom-4'")
->toContain('compactAfter')
->toContain('compactStorageKey')
->toContain("localStorage.setItem(this.storageKey, 'compact')")
@@ -94,8 +96,10 @@ it('supports timed compact popup notifications', function () {
->toContain('compact = true')
->toContain('@click="restore()"')
->toContain('@click.stop="minimizeToIcon()"')
->toContain('x-show="iconOnly"')
->toContain('x-show="!iconOnly"')
->toContain('<template x-if="iconOnly">')
->toContain('<template x-if="!iconOnly">')
->not->toContain('<button x-show="iconOnly"')
->not->toContain('<div x-show="!iconOnly"')
->not->toContain(':class="iconOnly')
->toContain('x-show="!compact"')
->toContain("'w-[calc(100vw-2rem)] cursor-pointer sm:w-auto sm:max-w-[calc(100vw-2rem)]'");
@@ -12,6 +12,7 @@ it('aggregates preview container and health check status', function () {
->toContain('Health check')
->toContain('Not configured')
->toContain('aria-label="About unconfigured health checks"')
->toContain('class="relative inline-flex align-middle"')
->toContain('Traffic can still be routed to the container')
->toContain('aria-haspopup="menu"')
->toContain('right-auto! left-0!')
+26 -1
View File
@@ -31,11 +31,36 @@ test('unsaved bar delays show and hides while loading to avoid instant-save flas
expect($contents)
->toContain('wire:dirty.class="is-dirty"')
->toContain('wire:loading.class="!opacity-0 !translate-y-6 !pointer-events-none"')
->toContain('wire:loading.class="is-saving"')
->toContain('[&.is-dirty]:delay-300')
->toContain('delay-0');
});
test('unsaved bar uses a stable transition for its entrance', function () {
$contents = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
expect($contents)
->toContain('scale-95')
->toContain('transition-[opacity,transform,scale]')
->toContain('duration-300')
->toContain('ease-[cubic-bezier(0.16,1,0.3,1)]')
->toContain('[&.is-dirty]:scale-100')
->not->toContain('[&.is-dirty]:animate-in');
});
test('unsaved bar transitions out without restarting its entrance animation', function () {
$contents = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
expect($contents)
->toContain('[&.is-saving]:translate-y-6')
->toContain('[&.is-saving]:scale-95')
->toContain('[&.is-saving]:opacity-0')
->toContain('[&.is-saving]:duration-200')
->toContain('[&.is-saving]:ease-in')
->toContain('[&.is-saving]:pointer-events-none')
->not->toContain('[&.is-saving]:animate-out');
});
test('unsaved bar saves with enter and shows the shortcut on the save button', function () {
$contents = file_get_contents(resource_path('views/components/unsaved-bar.blade.php'));
+26
View File
@@ -0,0 +1,26 @@
<?php
test('toast messages default to the bottom right while supporting position overrides', function () {
$toast = file_get_contents(resource_path('views/components/toast.blade.php'));
expect($toast)
->toContain("position: options.position ?? 'bottom-right'")
->toContain("position: 'bottom-right'")
->toContain("this.position = event.detail.position || 'bottom-right'")
->toContain("'right-4 bottom-4 flex-col-reverse': position === 'bottom-right'")
->toContain("'left-1/2 top-4 -translate-x-1/2 flex-col': position === 'top-center'");
});
test('toast copy button shows temporary success feedback', function () {
$toast = file_get_contents(resource_path('views/components/toast.blade.php'));
expect($toast)
->toContain('copied: false')
->toContain('copyToast(toast)')
->toContain('toast.copied = true')
->toContain('toast.copied = false')
->toContain('}, 2000)')
->toContain('x-show="!toast.copied"')
->toContain('x-show="toast.copied"')
->toContain("'bg-emerald-100 text-emerald-700 dark:bg-emerald-500/10 dark:text-emerald-400': toast.copied");
});
+14
View File
@@ -0,0 +1,14 @@
<?php
test('shared tooltips render above toasts and notification banners', function () {
$iconTooltip = file_get_contents(resource_path('views/components/icon-tooltip.blade.php'));
$helper = file_get_contents(resource_path('views/components/helper.blade.php'));
$navbar = file_get_contents(resource_path('views/components/navbar.blade.php'));
$utilities = file_get_contents(resource_path('css/utilities.css'));
expect($iconTooltip)->toContain('z-[10000]')
->and($helper)->toContain('z-[10000]')
->and($navbar)->toContain('z-[10000]')
->and($utilities)->toContain('@utility auth-tooltip')
->and($utilities)->toContain('@apply fixed z-[10000]');
});