fix(storage): prevent PR suffix dropdown clipping (#11637)

This commit is contained in:
Andras Bacsai
2026-09-05 15:58:12 +02:00
committed by GitHub
parent 3764771293
commit 08f68016dd
6 changed files with 223 additions and 68 deletions
+4 -1
View File
@@ -2673,7 +2673,10 @@ input[type="search"]::-webkit-search-results-decoration {
}
.volumes-col-backup {
align-items: flex-start;
flex-direction: row;
align-items: center;
justify-content: flex-start;
gap: 0.5rem;
}
.volumes-cell-actions {
@@ -123,7 +123,8 @@
{{ $attributes->whereStartsWith('x-model') }}
{{ $attributes->whereStartsWith('x-effect') }}
@if ($preserveValue) wire:ignore @endif
@click.outside="open = false" @keydown.escape="open = false" @resize.window="open && positionPanel()">
@click.outside="open = false" @keydown.escape="open = false" @resize.window="open && positionPanel()"
@scroll.window.capture="open && positionPanel()">
<button x-ref="trigger" id="{{ $triggerId }}" type="button" class="listbox-trigger" @click="toggle()"
@disabled($disabled) {{ $attributes->whereStartsWith('x-bind:disabled') }} aria-haspopup="listbox"
:aria-expanded="open" @if ($tooltip) :title="current" @endif>
@@ -134,33 +135,35 @@
</svg>
</button>
@if ($portal)
<div id="{{ $panelId }}" class="listbox-panel"
style="position: fixed; z-index: 9999; visibility: hidden" x-show="open"
x-cloak :style="{ visibility: positioned ? 'visible' : 'hidden' }"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-1 scale-[0.98]"
x-transition:enter-end="opacity-100 translate-y-0 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 translate-y-0 scale-100"
x-transition:leave-end="opacity-0 -translate-y-1 scale-[0.98]"
x-effect="if (open) requestAnimationFrame(() => positionPanel($el))" role="listbox">
<div x-show="options.length === 0"
class="px-3 py-2 text-[13px] text-neutral-500 dark:text-fg-dim">
{{ $emptyText }}
<template x-teleport="body">
<div id="{{ $panelId }}" class="listbox-panel"
style="position: fixed; z-index: 9999; visibility: hidden" x-show="open"
x-cloak :style="{ visibility: positioned ? 'visible' : 'hidden' }"
x-transition:enter="transition ease-out duration-100"
x-transition:enter-start="opacity-0 -translate-y-1 scale-[0.98]"
x-transition:enter-end="opacity-100 translate-y-0 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 translate-y-0 scale-100"
x-transition:leave-end="opacity-0 -translate-y-1 scale-[0.98]"
x-effect="if (open) requestAnimationFrame(() => positionPanel($el))" role="listbox">
<div x-show="options.length === 0"
class="px-3 py-2 text-[13px] text-neutral-500 dark:text-fg-dim">
{{ $emptyText }}
</div>
<template x-for="option in options" :key="String(option.value)">
<button type="button" class="listbox-option" role="option"
:class="{ 'listbox-option-disabled': option.disabled }"
:aria-selected="String(option.value) === String(value)" @click="choose(option)">
<span class="truncate" x-text="option.label"></span>
<svg x-show="String(option.value) === String(value)" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"
class="size-3.5 shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
</button>
</template>
</div>
<template x-for="option in options" :key="String(option.value)">
<button type="button" class="listbox-option" role="option"
:class="{ 'listbox-option-disabled': option.disabled }"
:aria-selected="String(option.value) === String(value)" @click="choose(option)">
<span class="truncate" x-text="option.label"></span>
<svg x-show="String(option.value) === String(value)" xmlns="http://www.w3.org/2000/svg"
fill="none" viewBox="0 0 24 24" stroke-width="2.5" stroke="currentColor"
class="size-3.5 shrink-0">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
</button>
</template>
</div>
</template>
@else
<div x-ref="panel" class="listbox-panel" x-show="open" x-cloak
x-transition:enter="transition ease-out duration-100"
@@ -21,10 +21,10 @@
<span class="volumes-col-source">Source Path</span>
<span>Destination Path</span>
@if ($supportsPreviewSuffix)
<span class="volumes-col-pr"
title="Whether preview deployments receive an isolated -pr-N volume suffix.">
PR suffix
</span>
<div class="volumes-col-pr flex items-center gap-1.5">
<span>PR suffix</span>
<x-helper helper="Adds -pr-N to the storage name or path so each preview uses isolated data. Disabling it shares production data with previews." />
</div>
@endif
<span class="volumes-col-backup text-center">Backup</span>
@if ($showActionsColumn)
@@ -74,7 +74,10 @@
@if ($supportsPreviewSuffix)
<div class="volumes-col-pr min-w-0">
<span class="volumes-mobile-label volumes-field-label">PR suffix</span>
<div class="volumes-mobile-label volumes-field-label flex items-center gap-1.5">
<span>PR suffix</span>
<x-helper helper="Adds -pr-N to the storage name or path so each preview uses isolated data. Disabling it shares production data with previews." />
</div>
<span>{{ $form['isPreviewSuffixEnabled'] ? 'Add suffix' : 'Share volume' }}</span>
</div>
@endif
@@ -82,16 +85,22 @@
<div class="volumes-col-backup flex items-center justify-center gap-1.5">
<span class="volumes-mobile-label volumes-field-label">Backup</span>
@if ($hasEnabledBackup)
<a @if ($backupUrl) href="{{ $backupUrl }}" @endif title="Volume backup is enabled">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
</a>
<span @class(['table-badge', 'table-badge-success' => $hasS3Backup])
title="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</span>
@if ($backupUrl)
<a href="{{ $backupUrl }}" @class([
'table-badge underline-offset-2 hover:underline',
'table-badge-success' => $hasS3Backup,
])
title="Volume backup is enabled"
aria-label="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</a>
@else
<span @class(['table-badge', 'table-badge-success' => $hasS3Backup])
title="Volume backup is enabled"
aria-label="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</span>
@endif
@else
<span class="data-table-cell-dash">-</span>
@endif
@@ -103,10 +112,10 @@
@if ($canUpdate)
<x-modal-input title="Configure Volume Backup" :wireIgnore="false">
<x-slot:content>
<button type="button" class="icon-button" title="Configure backup"
aria-label="Configure backup">
<x-reicon name="database" class="size-4" />
</button>
<x-forms.button type="button" class="!px-2.5 !text-xs" canGate="update"
:canResource="$resource">
Backup
</x-forms.button>
</x-slot:content>
@if ($resource instanceof \App\Models\Application)
<livewire:project.application.backup.create :application="$resource"
@@ -165,27 +174,36 @@
@if ($supportsPreviewSuffix)
<div class="volumes-col-pr min-w-0">
<span class="volumes-mobile-label volumes-field-label">PR suffix</span>
<x-forms.listbox id="forms.{{ $id }}.isPreviewSuffixEnabled" :options="[
<div class="volumes-mobile-label volumes-field-label flex items-center gap-1.5">
<span>PR suffix</span>
<x-helper helper="Adds -pr-N to the storage name or path so each preview uses isolated data. Disabling it shares production data with previews." />
</div>
<x-forms.listbox id="forms.{{ $id }}.isPreviewSuffixEnabled" portal :options="[
['value' => true, 'label' => 'Add suffix'],
['value' => false, 'label' => 'Share volume'],
]" />
]" canGate="update" :canResource="$resource" />
</div>
@endif
<div class="volumes-col-backup flex items-center justify-center gap-1.5">
<span class="volumes-mobile-label volumes-field-label">Backup</span>
@if ($hasEnabledBackup)
<a @if ($backupUrl) href="{{ $backupUrl }}" @endif title="Volume backup is enabled">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="2" stroke="currentColor" class="size-4">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 12.75 6 6 9-13.5" />
</svg>
</a>
<span @class(['table-badge', 'table-badge-success' => $hasS3Backup])
title="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</span>
@if ($backupUrl)
<a href="{{ $backupUrl }}" @class([
'table-badge underline-offset-2 hover:underline',
'table-badge-success' => $hasS3Backup,
])
title="Volume backup is enabled"
aria-label="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</a>
@else
<span @class(['table-badge', 'table-badge-success' => $hasS3Backup])
title="Volume backup is enabled"
aria-label="{{ $hasS3Backup ? 'Backups are saved to S3' : 'Backups are stored locally only' }}">
{{ $hasS3Backup ? 'S3' : 'Local' }}
</span>
@endif
@else
<span class="data-table-cell-dash">-</span>
@endif
@@ -200,10 +218,10 @@
@if ($showBackupAction)
<x-modal-input title="Configure Volume Backup" :wireIgnore="false">
<x-slot:content>
<button type="button" class="icon-button" title="Configure backup"
aria-label="Configure backup">
<x-reicon name="database" class="size-4" />
</button>
<x-forms.button type="button" class="!px-2.5 !text-xs" canGate="update"
:canResource="$resource">
Backup
</x-forms.button>
</x-slot:content>
@if ($resource instanceof \App\Models\Application)
<livewire:project.application.backup.create :application="$resource"
@@ -218,10 +236,10 @@
@elseif (method_exists($resource, 'isBackupSolutionAvailable') && $resource->isBackupSolutionAvailable())
<x-modal-input title="New Scheduled Backup" :wireIgnore="false">
<x-slot:content>
<button type="button" class="icon-button" title="Configure backup"
aria-label="Configure backup">
<x-reicon name="database" class="size-4" />
</button>
<x-forms.button type="button" class="!px-2.5 !text-xs" canGate="update"
:canResource="$resource">
Backup
</x-forms.button>
</x-slot:content>
<livewire:project.database.create-scheduled-backup :database="$resource"
wire:key="configure-database-backup-{{ $id }}" />