fix(ui): scope resource card keys and env count hide

Limit enter/space deploy handlers to the card root with .self so nested focusable content no longer triggers deploy. Hide the environments table resource count on mobile via a stable class instead of nth-child.
This commit is contained in:
Andras Bacsai
2026-08-12 23:57:32 +02:00
parent 1f550e8fcf
commit 37ed725dc1
5 changed files with 34 additions and 11 deletions
+1 -1
View File
@@ -3681,7 +3681,7 @@ html[data-theme="custom"] .logs-viewer-timestamp {
column-gap: 0.75rem;
}
.environments-table-grid > :nth-child(2) {
.environments-table-grid .environment-resource-count {
display: none;
}
}
@@ -115,8 +115,8 @@
class="application-settings-section-body grid grid-cols-1 justify-start gap-3 text-left md:grid-cols-2 xl:grid-cols-3">
<template x-for="application in filteredGitBasedApplications" :key="application.name">
<article role="button" tabindex="0" :aria-label="'Deploy ' + application.name"
@click="setType(application.id)" @keydown.enter.prevent="setType(application.id)"
@keydown.space.prevent="setType(application.id)"
@click="setType(application.id)" @keydown.enter.self.prevent="setType(application.id)"
@keydown.space.self.prevent="setType(application.id)"
class="group flex min-h-48 cursor-pointer flex-col rounded-xl border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent dark:border-white/[0.08] dark:bg-white/[0.025] dark:hover:border-white/[0.14]">
<div class="flex min-w-0 items-start gap-3">
<div
@@ -162,8 +162,8 @@
<template x-for="application in filteredDockerBasedApplications" :key="application.name">
<article role="button" tabindex="0" :aria-label="'Deploy ' + application.name"
@click="setType(application.id)" @keydown.enter.prevent="setType(application.id)"
@keydown.space.prevent="setType(application.id)"
@click="setType(application.id)" @keydown.enter.self.prevent="setType(application.id)"
@keydown.space.self.prevent="setType(application.id)"
class="group flex min-h-48 cursor-pointer flex-col rounded-xl border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent dark:border-white/[0.08] dark:bg-white/[0.025] dark:hover:border-white/[0.14]">
<div class="flex min-w-0 items-start gap-3">
<div
@@ -212,8 +212,8 @@
class="application-settings-section-body grid grid-cols-1 justify-start gap-3 text-left md:grid-cols-2 xl:grid-cols-3">
<template x-for="database in filteredDatabases" :key="database.id">
<article role="button" tabindex="0" :aria-label="'Deploy ' + database.name"
@click="setType(database.id)" @keydown.enter.prevent="setType(database.id)"
@keydown.space.prevent="setType(database.id)"
@click="setType(database.id)" @keydown.enter.self.prevent="setType(database.id)"
@keydown.space.self.prevent="setType(database.id)"
class="group flex min-h-48 cursor-pointer flex-col rounded-xl border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent dark:border-white/[0.08] dark:bg-white/[0.025] dark:hover:border-white/[0.14]">
<div class="flex min-w-0 items-center gap-3">
<div
@@ -290,8 +290,8 @@
<template x-for="service in filteredServices" :key="service.name">
<article role="button" tabindex="0" :aria-label="'Deploy ' + service.name"
@click="setType('one-click-service-' + service.id)"
@keydown.enter.prevent="setType('one-click-service-' + service.id)"
@keydown.space.prevent="setType('one-click-service-' + service.id)"
@keydown.enter.self.prevent="setType('one-click-service-' + service.id)"
@keydown.space.self.prevent="setType('one-click-service-' + service.id)"
class="group flex min-h-48 cursor-pointer flex-col rounded-xl border border-neutral-200 bg-white p-4 transition-colors hover:border-neutral-300 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-accent dark:border-white/[0.08] dark:bg-white/[0.025] dark:hover:border-white/[0.14]">
<div class="flex min-w-0 items-start gap-3">
<div
@@ -198,7 +198,7 @@
<div
class="environments-table-grid border-b border-neutral-200 bg-neutral-50 px-4 py-2.5 text-[11px] font-medium text-neutral-500 dark:border-white/[0.08] dark:bg-white/[0.025] dark:text-fg-faint">
<div>Environment</div>
<div>Resources</div>
<div class="environment-resource-count">Resources</div>
<div class="environment-description">Description</div>
<div></div>
</div>
@@ -218,7 +218,7 @@
x-text="environment.name"></a>
</div>
<div class="text-[12px] text-neutral-600 dark:text-fg-dim"
<div class="environment-resource-count text-[12px] text-neutral-600 dark:text-fg-dim"
x-text="environment.resourceCount"></div>
<p class="environment-description truncate text-[12px] text-neutral-500 dark:text-fg-dim"
x-text="environment.description || '-'"></p>
@@ -0,0 +1,10 @@
<?php
it('only deploys from keyboard events on the resource card itself', function () {
$view = file_get_contents(resource_path('views/livewire/project/new/select.blade.php'));
expect(substr_count($view, '@keydown.enter.self.prevent='))->toBe(4)
->and(substr_count($view, '@keydown.space.self.prevent='))->toBe(4)
->and($view)->not->toContain('@keydown.enter.prevent=')
->and($view)->not->toContain('@keydown.space.prevent=');
});
@@ -50,3 +50,16 @@ it('does not repeat the resources action outside the resources page', function (
expect(file_get_contents(resource_path('views/livewire/project/resource/index.blade.php')))
->toContain('New resource');
});
it('uses a stable class for the environment resource count on mobile', function () {
$view = file_get_contents(resource_path('views/livewire/project/show.blade.php'));
$css = file_get_contents(resource_path('css/app.css'));
expect($view)
->toContain('class="environment-resource-count">Resources</div>')
->toContain('class="environment-resource-count text-[12px]');
expect($css)
->toContain('.environments-table-grid .environment-resource-count')
->not->toContain('.environments-table-grid > :nth-child(2)');
});