fix(applications): flatten mobile config nav and pin domain save
Replace the collapsible mobile configuration menu with an always-visible responsive grid, stack the mobile heading, and swap the domain editor unsaved bar for a sticky Save footer with a scrollable form body.
This commit is contained in:
parent
b39928d631
commit
d71a72a45d
8 changed files with 124 additions and 132 deletions
|
|
@ -238,60 +238,28 @@
|
|||
];
|
||||
@endphp
|
||||
|
||||
@php
|
||||
$activeMenuLabel = collect($groupedMenuItems)->flatMap(fn ($items) => $items)->firstWhere('active', true)['label'] ?? 'Settings';
|
||||
@endphp
|
||||
<aside @class([
|
||||
'application-settings-navigation min-w-0 xl:self-start',
|
||||
'is-flush' => $flush,
|
||||
])
|
||||
x-data="{
|
||||
menuOpen: false,
|
||||
desktop: window.matchMedia('(min-width: 1280px)').matches,
|
||||
init() {
|
||||
const mq = window.matchMedia('(min-width: 1280px)');
|
||||
mq.addEventListener('change', (e) => { this.desktop = e.matches; if (e.matches) { this.menuOpen = false; } });
|
||||
},
|
||||
}"
|
||||
x-on:click.outside="menuOpen = false"
|
||||
x-on:keydown.escape.window="menuOpen = false">
|
||||
{{-- Mobile disclosure: tap to reveal the full grouped nav; hidden at xl (the pinned rail). --}}
|
||||
<button type="button" x-show="!desktop" x-cloak x-on:click="menuOpen = !menuOpen"
|
||||
:aria-expanded="menuOpen" aria-label="Configuration menu"
|
||||
:class="menuOpen && 'ring-1 ring-black/10 dark:ring-white/15'"
|
||||
class="flex h-10 w-full items-center justify-between gap-2 rounded-xl border border-neutral-200 bg-white px-3 text-[13px] font-medium text-black transition-transform duration-100 ease-out hover:bg-neutral-50 active:scale-[0.985] dark:border-white/[0.08] dark:bg-white/[0.05] dark:text-fg dark:hover:bg-white/[0.08]">
|
||||
<span class="flex min-w-0 items-center gap-2">
|
||||
<x-reicon name="settings" class="size-4 shrink-0 text-nav-muted" />
|
||||
<span class="truncate">{{ $activeMenuLabel }}</span>
|
||||
</span>
|
||||
<svg class="size-3.5 shrink-0 text-nav-muted transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]"
|
||||
:class="menuOpen && 'rotate-180'" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="m3.5 4.75 2.5 2.5 2.5-2.5" stroke="currentColor" stroke-width="1.25"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<nav aria-label="Configuration sections" x-show="desktop || menuOpen" x-collapse.duration.200ms x-cloak
|
||||
class="mt-2 flex flex-col gap-0.5 rounded-xl border border-neutral-200 bg-white p-2 shadow-[var(--shadow-dropdown)] dark:border-white/[0.08] dark:bg-white/[0.03] xl:mt-0 xl:rounded-none xl:border-0 xl:bg-transparent xl:p-0 xl:shadow-none xl:dark:bg-transparent">
|
||||
])>
|
||||
<nav aria-label="Configuration sections"
|
||||
class="grid grid-cols-2 gap-0.5 border-y border-neutral-200 py-3 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1 xl:border-y-0 xl:py-0 dark:border-white/[0.06]">
|
||||
@foreach ($groupedMenuItems as $groupLabel => $groupItems)
|
||||
@unless ($loop->first)
|
||||
<div class="my-2 border-t border-neutral-200 dark:border-white/[0.06]" aria-hidden="true"></div>
|
||||
<div class="my-2 hidden border-t border-neutral-200 xl:block dark:border-white/[0.06]" aria-hidden="true"></div>
|
||||
@endunless
|
||||
<div class="nav-section">{{ $groupLabel }}</div>
|
||||
<div class="nav-section hidden xl:block">{{ $groupLabel }}</div>
|
||||
@foreach ($groupItems as $menuItem)
|
||||
@php $sections = $pageSections[$menuItem['route']] ?? []; @endphp
|
||||
<div wire:key="application-settings-group-{{ str($menuItem['label'])->slug() }}"
|
||||
@if (filled($sections)) x-data="{ open: @js($menuItem['active']), activeSection: '' }" @endif
|
||||
class="relative">
|
||||
<div wire:key="application-settings-group-{{ str($menuItem['label'])->slug() }}">
|
||||
<a wire:key="application-settings-link-{{ str($menuItem['label'])->slug() }}"
|
||||
@class([
|
||||
'menu-item',
|
||||
'menu-item-active' => $menuItem['active'],
|
||||
'pr-9' => filled($sections),
|
||||
])
|
||||
@if ($menuItem['navigate'] ?? true) {{ wireNavigate() }} @endif
|
||||
href="{{ route($menuItem['route'], $applicationRouteParameters) }}"
|
||||
x-on:click="menuOpen = false">
|
||||
>
|
||||
<x-reicon :name="$menuIcons[$menuItem['label']] ?? 'settings'" class="menu-item-icon" />
|
||||
<span class="menu-item-label">{{ $menuItem['label'] }}</span>
|
||||
@if ($menuItem['badge'] ?? false)
|
||||
|
|
@ -302,37 +270,23 @@ class="relative">
|
|||
@endif
|
||||
</a>
|
||||
@if (filled($sections))
|
||||
{{-- Expand/collapse the page's sub-sections. Active page opens by
|
||||
default; the label still navigates, the chevron only toggles. --}}
|
||||
<button type="button"
|
||||
class="absolute right-1 top-1 flex size-6 items-center justify-center rounded-md text-nav-muted transition-colors hover:bg-black/[0.04] hover:text-nav-active dark:hover:bg-white/[0.06]"
|
||||
x-on:click.stop.prevent="open = !open" :aria-expanded="open"
|
||||
aria-label="Toggle {{ $menuItem['label'] }} sections">
|
||||
<svg class="size-3.5 transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]"
|
||||
:class="open && 'rotate-90'" viewBox="0 0 12 12" fill="none" aria-hidden="true">
|
||||
<path d="m4.5 3 3 3-3 3" stroke="currentColor" stroke-width="1.25"
|
||||
stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
</button>
|
||||
<div>
|
||||
<div x-show="open" x-collapse.duration.200ms x-cloak
|
||||
class="nav-children flex flex-col gap-0.5 py-1">
|
||||
@foreach ($sections as $section)
|
||||
@if ($menuItem['active'])
|
||||
<button type="button" class="menu-subitem"
|
||||
:class="activeSection === '{{ $section['id'] }}' && 'menu-subitem-active'"
|
||||
x-on:click="menuOpen = false; activeSection = '{{ $section['id'] }}'; history.replaceState(null, '', '#{{ $section['id'] }}'); window.scrollToSettingsSection?.('{{ $section['id'] }}')">
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</button>
|
||||
@else
|
||||
<a class="menu-subitem"
|
||||
href="{{ route($menuItem['route'], $applicationRouteParameters) }}#{{ $section['id'] }}"
|
||||
{{ wireNavigate() }} x-on:click="menuOpen = false">
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
<div class="nav-children hidden flex-col gap-0.5 py-1 xl:flex"
|
||||
x-data="{ activeSection: '' }">
|
||||
@foreach ($sections as $section)
|
||||
@if ($menuItem['active'])
|
||||
<button type="button" class="menu-subitem"
|
||||
:class="activeSection === '{{ $section['id'] }}' && 'menu-subitem-active'"
|
||||
x-on:click="activeSection = '{{ $section['id'] }}'; history.replaceState(null, '', '#{{ $section['id'] }}'); window.scrollToSettingsSection?.('{{ $section['id'] }}')">
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</button>
|
||||
@else
|
||||
<a class="menu-subitem"
|
||||
href="{{ route($menuItem['route'], $applicationRouteParameters) }}#{{ $section['id'] }}"
|
||||
{{ wireNavigate() }}>
|
||||
<span class="menu-item-label text-left">{{ $section['label'] }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<livewire:project.shared.configuration-checker :resource="$application" />
|
||||
<livewire:project.application.heading :application="$application" :wire:key="'application-heading-'.$currentRoute" />
|
||||
|
||||
<section class="application-settings-workspace w-full max-w-none">
|
||||
<section class="application-settings-workspace mt-4 w-full max-w-none lg:mt-0">
|
||||
<div class="grid min-w-0 gap-8 xl:grid-cols-[210px_minmax(0,1fr)] xl:gap-8">
|
||||
<x-application.configuration-sidebar :application="$application" :current-route="$currentRoute" />
|
||||
|
||||
|
|
|
|||
|
|
@ -16,21 +16,13 @@
|
|||
domainSearch: '',
|
||||
modalOpen: @js($showEditDomainModal || $editDomainDnsFailed),
|
||||
editingServiceLabel: @js($editingService ?? ''),
|
||||
editingDomainBaseline: null,
|
||||
get hasAddressChanges() {
|
||||
return this.modalOpen && this.editingDomainBaseline !== null
|
||||
&& JSON.stringify($wire.editingDomainParts) !== this.editingDomainBaseline
|
||||
&& !$wire.showPortWarningModal && !$wire.showDomainConflictModal;
|
||||
},
|
||||
openEditDomain() {
|
||||
this.editingDomainBaseline = JSON.stringify($wire.editingDomainParts);
|
||||
this.editingServiceLabel = $wire.editingService || '';
|
||||
this.modalOpen = true;
|
||||
this.$nextTick(() => document.getElementById('editingDomainParts-host')?.focus?.());
|
||||
},
|
||||
closeEditDomain() {
|
||||
this.modalOpen = false;
|
||||
this.editingDomainBaseline = null;
|
||||
this.editingServiceLabel = '';
|
||||
},
|
||||
matchesDomainSearch(value) {
|
||||
|
|
@ -299,13 +291,11 @@ class="icon-button shrink-0" aria-label="Close">
|
|||
<x-reicon name="x" class="size-4" />
|
||||
</button>
|
||||
</header>
|
||||
<div class="application-settings-section-body relative min-h-0 flex-1 overflow-y-auto"
|
||||
style="-webkit-overflow-scrolling: touch;">
|
||||
<form wire:submit="updateDomain" class="flex flex-col gap-4">
|
||||
<template x-if="modalOpen">
|
||||
<x-unsaved-bar action="updateDomain" dirty="hasAddressChanges"
|
||||
targets="updateDomain,confirmUpdateDomainDespiteDns" />
|
||||
</template>
|
||||
<div class="application-settings-section-body relative flex min-h-0 flex-1 flex-col overflow-hidden">
|
||||
<form wire:submit="updateDomain" class="flex min-h-0 flex-1 flex-col">
|
||||
<div data-testid="domain-settings-scroll"
|
||||
class="flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto overscroll-contain pb-4"
|
||||
style="-webkit-overflow-scrolling: touch;">
|
||||
<div x-show="editingServiceLabel" x-cloak class="w-full">
|
||||
<div class="mb-1.5 flex h-4 w-full items-center gap-1.5">
|
||||
<label class="mb-0! flex items-center gap-1 text-sm font-medium leading-4">Service</label>
|
||||
|
|
@ -326,23 +316,18 @@ class="icon-button shrink-0" aria-label="Close">
|
|||
</x-callout>
|
||||
@endif
|
||||
|
||||
@if ($editDomainDnsFailed)
|
||||
<x-forms.button type="button" isError wire:click="confirmUpdateDomainDespiteDns">Continue</x-forms.button>
|
||||
@endif
|
||||
</form>
|
||||
@php
|
||||
$editingRow = $editingIndex !== null ? ($domainRows[$editingIndex] ?? null) : null;
|
||||
@endphp
|
||||
@if ($editingRow && ! $labelsAreWritable)
|
||||
@can('update', $application)
|
||||
@php
|
||||
$editingKey = hash('sha256', $editingRow['url'].'|'.($editingRow['service'] ?? ''));
|
||||
$editingRedirectKey = $isCompose ? $this->serviceRedirectWireKey($editingRow['service']) : null;
|
||||
$editingRedirectProperty = $isCompose ? 'serviceRedirects.'.$editingRedirectKey : 'redirect';
|
||||
@endphp
|
||||
<div wire:key="editing-application-domain-settings-{{ $editingKey }}"
|
||||
class="mt-4 grid grid-cols-1 gap-4 border-t border-neutral-200 pt-4 sm:grid-cols-2 dark:border-white/10">
|
||||
<p class="sm:col-span-2 text-[12px] text-neutral-500 dark:text-fg-dim">Indexing and redirect changes save automatically.</p>
|
||||
@php
|
||||
$editingRow = $editingIndex !== null ? ($domainRows[$editingIndex] ?? null) : null;
|
||||
@endphp
|
||||
@if ($editingRow && ! $labelsAreWritable)
|
||||
@can('update', $application)
|
||||
@php
|
||||
$editingKey = hash('sha256', $editingRow['url'].'|'.($editingRow['service'] ?? ''));
|
||||
$editingRedirectKey = $isCompose ? $this->serviceRedirectWireKey($editingRow['service']) : null;
|
||||
$editingRedirectProperty = $isCompose ? 'serviceRedirects.'.$editingRedirectKey : 'redirect';
|
||||
@endphp
|
||||
<div wire:key="editing-application-domain-settings-{{ $editingKey }}"
|
||||
class="grid grid-cols-1 gap-4 border-t border-neutral-200 pt-4 sm:grid-cols-2 dark:border-white/10">
|
||||
<x-forms.listbox id="application-domain-indexing-{{ $editingKey }}"
|
||||
label="Search engine indexing" :wire="false" preserveValue
|
||||
:value="$application->isDomainNoindexed($editingRow['url']) ? 'noindex' : 'index'"
|
||||
|
|
@ -363,10 +348,26 @@ class="mt-4 grid grid-cols-1 gap-4 border-t border-neutral-200 pt-4 sm:grid-cols
|
|||
['value' => 'www', 'label' => 'Redirect to www'],
|
||||
['value' => 'non-www', 'label' => 'Redirect to non-www'],
|
||||
]" />
|
||||
</div>
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
@endcan
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div data-testid="domain-settings-footer"
|
||||
class="shrink-0 border-t border-neutral-200 pt-4 dark:border-white/10">
|
||||
<div class="flex flex-wrap items-center justify-end gap-2">
|
||||
@if ($editDomainDnsFailed)
|
||||
<x-forms.button type="button" isError wire:click="confirmUpdateDomainDespiteDns">
|
||||
Continue
|
||||
</x-forms.button>
|
||||
@else
|
||||
<x-forms.button type="submit" wire:target="updateDomain" isHighlighted>
|
||||
Save
|
||||
</x-forms.button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -26,12 +26,11 @@
|
|||
@endphp
|
||||
<div>
|
||||
<div class="mb-3 w-full xl:hidden">
|
||||
{{-- Identity row: name truncates, status + links stay pinned right. --}}
|
||||
<div class="flex w-full min-w-0 items-center gap-3">
|
||||
<h1 class="min-w-0 flex-1 truncate text-[22px]! leading-7! font-semibold! tracking-tight! text-black dark:text-fg">
|
||||
<div class="flex min-w-0 flex-col items-start gap-2">
|
||||
<h1 class="min-w-0 max-w-full truncate text-[24px]! leading-7! font-semibold! tracking-tight! text-black dark:text-fg">
|
||||
{{ $application->name }}
|
||||
</h1>
|
||||
<div class="flex shrink-0 items-center gap-2">
|
||||
<div class="relative flex w-full min-w-0 items-center gap-2">
|
||||
<x-status-summary :status="$application->status" align="right" />
|
||||
<x-applications.links :application="$application" compact />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1909,12 +1909,17 @@
|
|||
->toContain('<span>Search indexing</span>');
|
||||
});
|
||||
|
||||
it('shows save guidance in the application domain settings', function () {
|
||||
it('shows a form save button at the bottom of application domain settings', function () {
|
||||
$view = file_get_contents(resource_path('views/livewire/project/application/domains.blade.php'));
|
||||
|
||||
expect($view)
|
||||
->toContain('Indexing and redirect changes save automatically.')
|
||||
->toContain('<x-unsaved-bar action="updateDomain"');
|
||||
->not->toContain('Indexing and redirect changes save automatically.')
|
||||
->toContain('data-testid="domain-settings-scroll"')
|
||||
->toContain('data-testid="domain-settings-footer"')
|
||||
->toContain('class="shrink-0 border-t')
|
||||
->toContain('<x-forms.button type="submit" wire:target="updateDomain" isHighlighted>')
|
||||
->toContain('Save')
|
||||
->not->toContain('<x-unsaved-bar action="updateDomain"');
|
||||
});
|
||||
|
||||
it('does not render a last checked column in the domains table', function () {
|
||||
|
|
|
|||
|
|
@ -35,12 +35,12 @@
|
|||
|
||||
it('uses interactive status summaries in mobile resource headings', function () {
|
||||
$headings = [
|
||||
resource_path('views/livewire/project/application/heading.blade.php') => '<x-status-summary :status="$application->status" />',
|
||||
resource_path('views/livewire/project/database/heading.blade.php') => '<x-status-summary :status="$database->status" title="Database status" />',
|
||||
resource_path('views/livewire/project/service/heading.blade.php') => '<x-status-summary :status="$service->status" title="Service status" container-name="Containers" />',
|
||||
resource_path('views/livewire/project/application/heading.blade.php'),
|
||||
resource_path('views/livewire/project/database/heading.blade.php'),
|
||||
resource_path('views/livewire/project/service/heading.blade.php'),
|
||||
];
|
||||
|
||||
foreach ($headings as $path => $statusSummary) {
|
||||
foreach ($headings as $path) {
|
||||
$mobileHeading = str(file_get_contents($path))
|
||||
->after('<div class="mb-3 w-full xl:hidden">')
|
||||
->before('<div class="w-full xl:hidden">')
|
||||
|
|
@ -49,7 +49,7 @@
|
|||
expect($mobileHeading)
|
||||
->toContain('flex min-w-0 flex-col items-start gap-2')
|
||||
->toContain('min-w-0 max-w-full truncate')
|
||||
->toContain($statusSummary)
|
||||
->toContain('<x-status-summary')
|
||||
->not->toContain('<x-status-badge');
|
||||
}
|
||||
});
|
||||
|
|
@ -528,15 +528,24 @@
|
|||
->and($css)->toContain('position: fixed;');
|
||||
});
|
||||
|
||||
it('uses the same mobile heading gap on deployment pages as application settings', function () {
|
||||
$configuration = file_get_contents(resource_path('views/livewire/project/application/configuration.blade.php'));
|
||||
$deploymentIndex = file_get_contents(resource_path('views/livewire/project/application/deployment/index.blade.php'));
|
||||
$deploymentShow = file_get_contents(resource_path('views/livewire/project/application/deployment/show.blade.php'));
|
||||
it('uses the same mobile heading gap on application pages', function () {
|
||||
$views = [
|
||||
resource_path('views/livewire/project/application/configuration.blade.php'),
|
||||
resource_path('views/livewire/project/application/backup/index.blade.php'),
|
||||
resource_path('views/livewire/project/application/backup/show.blade.php'),
|
||||
resource_path('views/livewire/project/application/deployment/show.blade.php'),
|
||||
resource_path('views/livewire/project/shared/logs.blade.php'),
|
||||
resource_path('views/livewire/project/shared/execute-container-command.blade.php'),
|
||||
];
|
||||
|
||||
expect($configuration)->toContain('application-settings-workspace mt-4')
|
||||
->and($deploymentIndex)->toContain("'mt-4 max-w-[1180px] lg:mt-0' => ! \$embedded")
|
||||
->and($deploymentShow)->toContain('application-settings-workspace mt-4')
|
||||
->toContain('lg:mt-0');
|
||||
foreach ($views as $view) {
|
||||
expect(file_get_contents($view))
|
||||
->toContain('application-settings-workspace mt-4')
|
||||
->toContain('lg:mt-0');
|
||||
}
|
||||
|
||||
expect(file_get_contents(resource_path('views/livewire/project/application/deployment/index.blade.php')))
|
||||
->toContain("'mt-4 max-w-none lg:mt-0' => ! \$embedded");
|
||||
});
|
||||
|
||||
it('removes desktop top spacing from the deployment log viewer', function () {
|
||||
|
|
|
|||
|
|
@ -76,6 +76,24 @@
|
|||
->toContain("'Operations' => ['Resource Operations', 'Resource Limits', 'Rollback', 'Tags', 'Danger Zone']");
|
||||
});
|
||||
|
||||
it('uses the same responsive settings grid for applications services and databases', function () {
|
||||
$sidebars = [
|
||||
resource_path('views/components/application/configuration-sidebar.blade.php'),
|
||||
resource_path('views/components/service/configuration-sidebar.blade.php'),
|
||||
resource_path('views/components/database/configuration-sidebar.blade.php'),
|
||||
];
|
||||
|
||||
foreach ($sidebars as $sidebar) {
|
||||
expect(file_get_contents($sidebar))
|
||||
->toContain('grid grid-cols-2 gap-0.5')
|
||||
->toContain('sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-1');
|
||||
}
|
||||
|
||||
expect(file_get_contents($sidebars[0]))
|
||||
->not->toContain('aria-label="Configuration menu"')
|
||||
->not->toContain('menuOpen');
|
||||
});
|
||||
|
||||
it('shows the database sidebar on backup pages', function () {
|
||||
$configuration = file_get_contents(resource_path('views/livewire/project/database/configuration.blade.php'));
|
||||
$backups = file_get_contents(resource_path('views/livewire/project/database/backup/index.blade.php'));
|
||||
|
|
|
|||
|
|
@ -213,11 +213,15 @@
|
|||
->screenshot(filename: 'application-danger-zone');
|
||||
});
|
||||
|
||||
it('uses compact application domains with unified settings and a floating save bar', function () {
|
||||
it('uses compact application domains with unified settings and a form save button', function () {
|
||||
config()->set('app.maintenance.store', 'array');
|
||||
InstanceSettings::find(0)->update(['is_dns_validation_enabled' => false]);
|
||||
Cache::forget('instance_settings');
|
||||
$this->application->update(['fqdn' => 'https://first.example.com,https://second.example.com', 'redirect' => 'both']);
|
||||
$this->application->update([
|
||||
'fqdn' => 'https://first.example.com,https://second.example.com',
|
||||
'ports_exposes' => '3000,8069',
|
||||
'redirect' => 'both',
|
||||
]);
|
||||
loginAndSkipBoarding();
|
||||
$url = applicationConfigurationUrl($this->stack['project'], $this->stack['environment'], $this->application).'/domains';
|
||||
$page = visit($url);
|
||||
|
|
@ -232,20 +236,22 @@
|
|||
->click('[aria-label="Settings for https://first.example.com"]')
|
||||
->assertSee('Domain settings')
|
||||
->assertValue('#editingDomainParts-host', 'first.example.com')
|
||||
->assertMissing('.is-dirty [wire\\:click="updateDomain"]')
|
||||
->fill('#editingDomainParts-port', '8069')
|
||||
->fill('#editingDomainParts-path', '/blog')
|
||||
->assertVisible('.is-dirty:not(.is-saving) [wire\\:click="updateDomain"]')
|
||||
->click('[id^="application-domain-indexing-"][id$="-trigger"]')
|
||||
->click('Noindex')
|
||||
->assertSee('Search engine indexing updated.')
|
||||
->assertVisible('.is-dirty:not(.is-saving) [wire\\:click="updateDomain"]')
|
||||
->screenshot(filename: 'application-domain-unified-settings')
|
||||
->click('[wire\\:click="updateDomain"]')
|
||||
->click('Save')
|
||||
->assertDontSee('Domain settings')
|
||||
->assertSee('https://first.example.com/blog')
|
||||
->assertSee('Internal port 8069')
|
||||
->assertNoJavaScriptErrors()
|
||||
->screenshot(filename: 'application-domains-compact');
|
||||
|
||||
expect($this->application->fresh()->domain_port_overrides)
|
||||
->toHaveKey('https://first.example.com/blog', 8069);
|
||||
|
||||
$page->click('[aria-label="Settings for https://second.example.com"]')
|
||||
->fill('#editingDomainParts-path', '/discard')
|
||||
->click('Reset')
|
||||
|
|
|
|||
Loading…
Reference in a new issue