Compare commits
2 Commits
research/m
...
fix/pr23-c
| Author | SHA1 | Date | |
|---|---|---|---|
| 8ae582455c | |||
| 75ab0b0ff5 |
12
composer.lock
generated
12
composer.lock
generated
@@ -2802,16 +2802,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/commonmark",
|
"name": "league/commonmark",
|
||||||
"version": "2.8.3",
|
"version": "2.9.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/commonmark.git",
|
"url": "https://github.com/thephpleague/commonmark.git",
|
||||||
"reference": "1902f60f984235023acbe03db6ad614a37b3c3e7"
|
"reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/1902f60f984235023acbe03db6ad614a37b3c3e7",
|
"url": "https://api.github.com/repos/thephpleague/commonmark/zipball/5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
|
||||||
"reference": "1902f60f984235023acbe03db6ad614a37b3c3e7",
|
"reference": "5703d83ba3da3b2e356a5fedc848ed6d8ffb6529",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2848,7 +2848,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "2.9-dev"
|
"dev-main": "2.10-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -2905,7 +2905,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2026-07-12T15:29:16+00:00"
|
"time": "2026-08-03T13:42:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/config",
|
"name": "league/config",
|
||||||
|
|||||||
BIN
docs/screenshots/home-editorial-cadence-desktop.webp
Normal file
BIN
docs/screenshots/home-editorial-cadence-desktop.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 137 KiB |
BIN
docs/screenshots/home-editorial-cadence-mobile.webp
Normal file
BIN
docs/screenshots/home-editorial-cadence-mobile.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
@@ -109,6 +109,43 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.home-chapters {
|
||||||
|
counter-reset: home-chapter -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-chapter {
|
||||||
|
counter-increment: home-chapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-folio {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
color: var(--amare-color-accent);
|
||||||
|
font-size: var(--amare-text-xs);
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-folio--inverse {
|
||||||
|
color: color-mix(in srgb, var(--amare-color-accent-text) 82%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-folio__separator {
|
||||||
|
color: var(--amare-color-sage);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-folio--inverse .home-folio__separator {
|
||||||
|
color: color-mix(in srgb, var(--amare-color-accent-text) 55%, transparent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-folio__number::before {
|
||||||
|
content: counter(home-chapter, decimal-leading-zero);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
@media (prefers-reduced-motion: no-preference) {
|
||||||
.main-nav {
|
.main-nav {
|
||||||
transition: opacity var(--amare-duration-normal) var(--amare-ease-standard);
|
transition: opacity var(--amare-duration-normal) var(--amare-ease-standard);
|
||||||
|
|||||||
@@ -1,10 +1,18 @@
|
|||||||
@props([
|
@props([
|
||||||
'settings',
|
'settings',
|
||||||
|
'editorial' => false,
|
||||||
])
|
])
|
||||||
|
|
||||||
<section aria-labelledby="final-cta-heading" class="border-t border-amare-border bg-amare-bg-deep py-20" data-chapter="final-cta" data-reveal-group>
|
<section aria-labelledby="final-cta-heading" @class([
|
||||||
|
'border-t border-amare-border bg-amare-bg-deep py-20',
|
||||||
|
'home-chapter' => $editorial,
|
||||||
|
]) data-chapter="final-cta" data-reveal-group>
|
||||||
<div class="container-amare space-y-6 text-center" data-reveal data-reveal-from="up">
|
<div class="container-amare space-y-6 text-center" data-reveal data-reveal-from="up">
|
||||||
|
@if ($editorial)
|
||||||
|
<x-home.folio label="Próximo passo" class="justify-center" />
|
||||||
|
@else
|
||||||
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent">Próximo passo</p>
|
<p class="text-xs font-semibold uppercase tracking-[0.14em] text-amare-accent">Próximo passo</p>
|
||||||
|
@endif
|
||||||
<h2 id="final-cta-heading" class="text-headline font-medium text-amare-text">Do casamento ao evento corporativo, tudo começa com uma boa conversa.</h2>
|
<h2 id="final-cta-heading" class="text-headline font-medium text-amare-text">Do casamento ao evento corporativo, tudo começa com uma boa conversa.</h2>
|
||||||
<p class="mx-auto max-w-2xl text-amare-muted">
|
<p class="mx-auto max-w-2xl text-amare-muted">
|
||||||
Compartilhe as primeiras informações do seu evento. A Amare retorna para entender o contexto e orientar os próximos passos.
|
Compartilhe as primeiras informações do seu evento. A Amare retorna para entender o contexto e orientar os próximos passos.
|
||||||
|
|||||||
20
resources/views/components/home/folio.blade.php
Normal file
20
resources/views/components/home/folio.blade.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
@props([
|
||||||
|
'label',
|
||||||
|
'tone' => 'default',
|
||||||
|
])
|
||||||
|
|
||||||
|
@php
|
||||||
|
$tone = $tone === 'inverse' ? 'inverse' : 'default';
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
<p
|
||||||
|
{{ $attributes->class([
|
||||||
|
'home-folio',
|
||||||
|
'home-folio--inverse' => $tone === 'inverse',
|
||||||
|
]) }}
|
||||||
|
data-home-folio
|
||||||
|
>
|
||||||
|
<span data-home-folio-label>{{ $label }}</span>
|
||||||
|
<span class="home-folio__separator" aria-hidden="true">·</span>
|
||||||
|
<span class="home-folio__number" data-home-folio-number aria-hidden="true"></span>
|
||||||
|
</p>
|
||||||
@@ -4,11 +4,15 @@
|
|||||||
|
|
||||||
<section
|
<section
|
||||||
aria-labelledby="hero-heading"
|
aria-labelledby="hero-heading"
|
||||||
class="border-b border-amare-border bg-amare-bg"
|
class="home-chapter border-b border-amare-border bg-amare-bg"
|
||||||
data-chapter="hero"
|
data-chapter="hero"
|
||||||
data-motion="page-open"
|
data-motion="page-open"
|
||||||
>
|
>
|
||||||
<div class="container-amare grid gap-12 py-20 md:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] md:items-center md:py-28" data-reveal-group>
|
<div class="container-amare pt-8 md:pt-10">
|
||||||
|
<x-home.folio label="Capa" data-motion-beat="folio" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="container-amare grid gap-12 pb-20 pt-10 md:grid-cols-[minmax(0,1.1fr)_minmax(0,0.9fr)] md:items-center md:pb-28 md:pt-14" data-reveal-group>
|
||||||
<div class="space-y-8">
|
<div class="space-y-8">
|
||||||
<div data-motion-beat="seal" class="flex items-center gap-4">
|
<div data-motion-beat="seal" class="flex items-center gap-4">
|
||||||
<x-brand.logo mark variant="on-light" class="h-8 w-auto" alt="" />
|
<x-brand.logo mark variant="on-light" class="h-8 w-auto" alt="" />
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
$body = $settings->manifesto_body ?: 'A Amare combina sensibilidade e precisão para criar encontros coerentes com cada cliente, marca e ocasião — sem fórmulas prontas, excessos ou ruído.';
|
$body = $settings->manifesto_body ?: 'A Amare combina sensibilidade e precisão para criar encontros coerentes com cada cliente, marca e ocasião — sem fórmulas prontas, excessos ou ruído.';
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section aria-labelledby="manifesto-heading" class="border-b border-amare-border bg-amare-bg-deep py-20" data-chapter="manifesto">
|
<section aria-labelledby="manifesto-heading" class="home-chapter border-b border-amare-border bg-amare-bg-deep py-20" data-chapter="manifesto">
|
||||||
<div class="container-amare grid gap-8 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal-group>
|
<div class="container-amare grid gap-8 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal-group>
|
||||||
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent" data-reveal data-reveal-from="up">Manifesto</p>
|
<div class="space-y-5 md:pr-8" data-reveal data-reveal-from="up">
|
||||||
|
<x-home.folio label="Manifesto" />
|
||||||
|
<span class="block h-px w-16 bg-amare-border" aria-hidden="true"></span>
|
||||||
|
<p class="max-w-52 text-lg leading-snug text-amare-text">Humana no cuidado. Precisa na entrega.</p>
|
||||||
|
</div>
|
||||||
<div class="space-y-6" data-reveal data-reveal-from="up">
|
<div class="space-y-6" data-reveal data-reveal-from="up">
|
||||||
<h2 id="manifesto-heading" class="max-w-3xl text-headline font-medium text-amare-text">{{ $title }}</h2>
|
<h2 id="manifesto-heading" class="max-w-3xl text-headline font-medium text-amare-text">{{ $title }}</h2>
|
||||||
<p class="max-w-2xl text-xl leading-relaxed text-amare-text">{{ $lead }}</p>
|
<p class="max-w-2xl text-xl leading-relaxed text-amare-text">{{ $lead }}</p>
|
||||||
|
|||||||
@@ -7,12 +7,19 @@
|
|||||||
$intro = $settings->method_intro ?: 'Clareza em cada etapa. Tranquilidade durante todo o processo.';
|
$intro = $settings->method_intro ?: 'Clareza em cada etapa. Tranquilidade durante todo o processo.';
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section aria-labelledby="method-heading" class="border-b border-amare-border bg-amare-bg-archive py-20" data-chapter="method">
|
<section aria-labelledby="method-heading" class="home-chapter border-b border-amare-border bg-amare-bg-archive py-20" data-chapter="method">
|
||||||
<div class="container-amare grid gap-10 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)] md:items-start" data-reveal-group>
|
<div class="container-amare grid gap-10 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)] md:items-start" data-reveal-group>
|
||||||
<div class="space-y-3" data-reveal data-reveal-from="up">
|
<div class="space-y-4" data-reveal data-reveal-from="up">
|
||||||
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent">Método</p>
|
<x-home.folio label="Método" />
|
||||||
<h2 id="method-heading" class="text-headline font-medium text-amare-text">Cuidado orientado por processo.</h2>
|
<h2 id="method-heading" class="text-headline font-medium text-amare-text">Cuidado orientado por processo.</h2>
|
||||||
<p class="text-amare-text-muted">{{ $intro }}</p>
|
<p class="text-amare-text-muted">{{ $intro }}</p>
|
||||||
|
<x-brand.logo
|
||||||
|
mark
|
||||||
|
variant="on-light"
|
||||||
|
class="mt-8 h-8 w-auto opacity-60"
|
||||||
|
alt=""
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="grid gap-5 border-t border-amare-border">
|
<ol class="grid gap-5 border-t border-amare-border">
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@if ($cases->isNotEmpty())
|
@if ($cases->isNotEmpty())
|
||||||
<section aria-labelledby="portfolio-heading" class="border-b border-amare-accent-deep bg-amare-accent-deep py-20 text-amare-accent-text" data-chapter="portfolio">
|
<section aria-labelledby="portfolio-heading" class="home-chapter border-b border-amare-accent-deep bg-amare-accent-deep py-20 text-amare-accent-text" data-chapter="portfolio">
|
||||||
<div class="container-amare space-y-10" data-reveal-group>
|
<div class="container-amare space-y-10" data-reveal-group>
|
||||||
<div class="grid gap-4 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal data-reveal-from="up">
|
<div class="grid gap-4 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal data-reveal-from="up">
|
||||||
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent-text/80">Portfólio</p>
|
<x-home.folio label="Portfólio" tone="inverse" />
|
||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<h2 id="portfolio-heading" class="text-headline font-medium">Celebrações que ganham forma com intenção.</h2>
|
<h2 id="portfolio-heading" class="text-headline font-medium">Celebrações que ganham forma com intenção.</h2>
|
||||||
<p class="max-w-2xl text-amare-accent-text/80">Recortes de eventos conduzidos com escuta, direção e presença em cada etapa.</p>
|
<p class="max-w-2xl text-amare-accent-text/80">Recortes de eventos conduzidos com escuta, direção e presença em cada etapa.</p>
|
||||||
|
|||||||
@@ -7,10 +7,14 @@
|
|||||||
$principles = filled($settings->principles) ? $settings->principles : \App\Models\SiteSetting::defaultPrinciples();
|
$principles = filled($settings->principles) ? $settings->principles : \App\Models\SiteSetting::defaultPrinciples();
|
||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
<section aria-labelledby="positioning-heading" class="border-b border-amare-border py-20" data-chapter="positioning">
|
<section aria-labelledby="positioning-heading" class="home-chapter border-b border-amare-border py-20" data-chapter="positioning">
|
||||||
<div class="container-amare grid gap-10 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal-group>
|
<div class="container-amare grid gap-10 md:grid-cols-[minmax(0,0.8fr)_minmax(0,1.2fr)]" data-reveal-group>
|
||||||
<div class="space-y-3" data-reveal data-reveal-from="up">
|
<div class="space-y-4" data-reveal data-reveal-from="up">
|
||||||
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent">A Amare</p>
|
<x-home.folio label="A Amare" />
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<span class="h-px w-10 bg-amare-border" aria-hidden="true"></span>
|
||||||
|
<p class="text-sm uppercase tracking-[0.12em] text-amare-text-muted">Assessoria boutique · São Paulo</p>
|
||||||
|
</div>
|
||||||
<h2 id="positioning-heading" class="text-headline font-medium text-amare-text">Presença que organiza o essencial.</h2>
|
<h2 id="positioning-heading" class="text-headline font-medium text-amare-text">Presença que organiza o essencial.</h2>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-8" data-reveal data-reveal-from="up">
|
<div class="space-y-8" data-reveal data-reveal-from="up">
|
||||||
|
|||||||
@@ -3,12 +3,17 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
@if ($services->isNotEmpty())
|
@if ($services->isNotEmpty())
|
||||||
<section aria-labelledby="services-heading" class="border-b border-amare-border py-20" data-chapter="services">
|
<section aria-labelledby="services-heading" class="home-chapter border-b border-amare-border py-20" data-chapter="services">
|
||||||
<div class="container-amare space-y-10" data-reveal-group>
|
<div class="container-amare space-y-10" data-reveal-group>
|
||||||
<div class="max-w-2xl space-y-3" data-reveal data-reveal-from="up">
|
<div class="grid gap-6 md:grid-cols-12 md:items-end" data-reveal data-reveal-from="up">
|
||||||
<p class="text-sm font-medium uppercase tracking-[0.18em] text-amare-accent">Atuação</p>
|
<div class="space-y-3 md:col-span-4">
|
||||||
|
<x-home.folio label="Serviços" />
|
||||||
<h2 id="services-heading" class="text-3xl font-medium text-amare-text">Serviços</h2>
|
<h2 id="services-heading" class="text-3xl font-medium text-amare-text">Serviços</h2>
|
||||||
<p class="text-amare-text-muted">Assessoria sob medida para decisões importantes e celebrações bem conduzidas.</p>
|
</div>
|
||||||
|
<div class="space-y-4 md:col-span-6 md:col-start-7">
|
||||||
|
<p class="max-w-xl text-amare-text-muted">Assessoria sob medida para decisões importantes e celebrações bem conduzidas.</p>
|
||||||
|
<p class="border-l border-amare-border pl-4 text-sm uppercase tracking-[0.12em] text-amare-accent">Do íntimo ao corporativo</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ol class="border-t border-amare-border">
|
<ol class="border-t border-amare-border">
|
||||||
|
|||||||
@@ -9,9 +9,10 @@
|
|||||||
@endphp
|
@endphp
|
||||||
|
|
||||||
@if ($testimonials->isNotEmpty())
|
@if ($testimonials->isNotEmpty())
|
||||||
<section aria-labelledby="testimonials-heading" class="border-b border-amare-border bg-amare-bg py-16" data-chapter="testimonials">
|
<section aria-labelledby="testimonials-heading" class="home-chapter border-b border-amare-border bg-amare-bg py-16" data-chapter="testimonials">
|
||||||
<div class="container-amare space-y-8" data-reveal-group>
|
<div class="container-amare space-y-8" data-reveal-group>
|
||||||
<div class="max-w-2xl space-y-3" data-reveal data-reveal-from="up">
|
<div class="max-w-2xl space-y-3" data-reveal data-reveal-from="up">
|
||||||
|
<x-home.folio label="Depoimentos" />
|
||||||
<h2 id="testimonials-heading" class="text-3xl font-medium text-amare-text">Depoimentos</h2>
|
<h2 id="testimonials-heading" class="text-3xl font-medium text-amare-text">Depoimentos</h2>
|
||||||
<p class="text-amare-text-muted">Quem celebrou com a Amare conta como foi a experiência.</p>
|
<p class="text-amare-text-muted">Quem celebrou com a Amare conta como foi a experiência.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -23,12 +24,15 @@
|
|||||||
$paragraphs = array_values(array_filter(array_map('trim', $paragraphs), fn (string $p): bool => $p !== ''));
|
$paragraphs = array_values(array_filter(array_map('trim', $paragraphs), fn (string $p): bool => $p !== ''));
|
||||||
@endphp
|
@endphp
|
||||||
<blockquote class="space-y-4 border-t border-amare-border pt-4" data-reveal data-reveal-from="{{ $loop->odd ? 'left' : 'right' }}">
|
<blockquote class="space-y-4 border-t border-amare-border pt-4" data-reveal data-reveal-from="{{ $loop->odd ? 'left' : 'right' }}">
|
||||||
|
<div class="grid grid-cols-[1.5rem_minmax(0,1fr)] gap-2">
|
||||||
|
<span class="text-3xl leading-none text-amare-sage" aria-hidden="true">“</span>
|
||||||
<div class="space-y-3 text-lg text-amare-text">
|
<div class="space-y-3 text-lg text-amare-text">
|
||||||
@foreach ($paragraphs as $index => $paragraph)
|
@foreach ($paragraphs as $paragraph)
|
||||||
<p>@if ($index === 0)“@endif{{ $paragraph }}@if ($index === count($paragraphs) - 1)”@endif</p>
|
<p>{{ $paragraph }}</p>
|
||||||
@endforeach
|
@endforeach
|
||||||
</div>
|
</div>
|
||||||
<footer class="text-sm text-amare-text-muted">
|
</div>
|
||||||
|
<footer class="pl-8 text-sm text-amare-text-muted">
|
||||||
<cite class="not-italic font-semibold text-amare-text">{{ $testimonial->author_name }}</cite>
|
<cite class="not-italic font-semibold text-amare-text">{{ $testimonial->author_name }}</cite>
|
||||||
@if (filled($testimonial->context))
|
@if (filled($testimonial->context))
|
||||||
<span> — {{ $testimonial->context }}</span>
|
<span> — {{ $testimonial->context }}</span>
|
||||||
|
|||||||
@@ -31,12 +31,14 @@
|
|||||||
|
|
||||||
<x-home.chapter-index :chapters="$chapters" />
|
<x-home.chapter-index :chapters="$chapters" />
|
||||||
|
|
||||||
|
<div class="home-chapters">
|
||||||
<x-home.hero :settings="$content->settings" />
|
<x-home.hero :settings="$content->settings" />
|
||||||
<x-home.manifesto :settings="$content->settings" />
|
<x-home.manifesto :settings="$content->settings" />
|
||||||
<x-home.services :services="$content->featuredServices" />
|
<x-home.services :services="$content->featuredServices" />
|
||||||
<x-home.portfolio :cases="$content->featuredCases" />
|
<x-home.portfolio :cases="$content->featuredCases" />
|
||||||
<x-home.method :settings="$content->settings" />
|
<x-home.method :settings="$content->settings" />
|
||||||
<x-home.testimonials :testimonials="$testimonials" />
|
<x-home.testimonials :testimonials="$content->testimonials" />
|
||||||
<x-home.positioning :settings="$content->settings" />
|
<x-home.positioning :settings="$content->settings" />
|
||||||
<x-home.final-cta :settings="$content->settings" />
|
<x-home.final-cta :settings="$content->settings" editorial />
|
||||||
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
60
tests/Browser/HomeEditorialCadenceTest.php
Normal file
60
tests/Browser/HomeEditorialCadenceTest.php
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Database\Seeders\VisualContentSeeder;
|
||||||
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
use Illuminate\Support\Facades\Artisan;
|
||||||
|
|
||||||
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
|
beforeEach(function (): void {
|
||||||
|
Artisan::call('db:seed', ['--class' => VisualContentSeeder::class, '--force' => true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('keeps the full home cadence accessible and contained at both viewports', function (): void {
|
||||||
|
foreach ([[1440, 1000], [390, 844]] as [$width, $height]) {
|
||||||
|
$page = $this->visit('/', [
|
||||||
|
'reducedMotion' => 'reduce',
|
||||||
|
])->resize($width, $height);
|
||||||
|
|
||||||
|
$page->assertNoAccessibilityIssues(1);
|
||||||
|
|
||||||
|
$layout = $page->script(<<<'JS'
|
||||||
|
() => {
|
||||||
|
const chapters = Array.from(document.querySelectorAll('.home-chapter'));
|
||||||
|
const folios = Array.from(document.querySelectorAll('[data-home-folio]'));
|
||||||
|
const clippedValues = new Set(['clip', 'hidden']);
|
||||||
|
const textNodes = Array.from(document.querySelectorAll(
|
||||||
|
'.home-chapters h1, .home-chapters h2, .home-chapters h3, .home-chapters p, .home-chapters a, .home-chapters span'
|
||||||
|
));
|
||||||
|
|
||||||
|
return {
|
||||||
|
horizontalOverflow: document.documentElement.scrollWidth > window.innerWidth,
|
||||||
|
overlappingChapters: chapters.slice(1).some((chapter, index) => {
|
||||||
|
const previous = chapters[index].getBoundingClientRect();
|
||||||
|
const current = chapter.getBoundingClientRect();
|
||||||
|
return current.top < previous.bottom - 1;
|
||||||
|
}),
|
||||||
|
overflowingFolios: folios.filter((folio) => {
|
||||||
|
const rect = folio.getBoundingClientRect();
|
||||||
|
return rect.left < -1 || rect.right > window.innerWidth + 1;
|
||||||
|
}).length,
|
||||||
|
clippedText: textNodes.filter((node) => {
|
||||||
|
const style = getComputedStyle(node);
|
||||||
|
const clippedX = clippedValues.has(style.overflowX) && node.scrollWidth > node.clientWidth + 1;
|
||||||
|
const clippedY = clippedValues.has(style.overflowY) && node.scrollHeight > node.clientHeight + 1;
|
||||||
|
return clippedX || clippedY;
|
||||||
|
}).length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
JS);
|
||||||
|
|
||||||
|
expect($layout)->toBe([
|
||||||
|
'horizontalOverflow' => false,
|
||||||
|
'overlappingChapters' => false,
|
||||||
|
'overflowingFolios' => 0,
|
||||||
|
'clippedText' => 0,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -302,3 +302,48 @@ it('updates the active chapter while scrolling the home dossier', function (): v
|
|||||||
expect($before)->not->toBeNull();
|
expect($before)->not->toBeNull();
|
||||||
expect($after)->toBe('#method-heading');
|
expect($after)->toBe('#method-heading');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('applies CSS numbering to only rendered home folios in chapter order', function (): void {
|
||||||
|
PortfolioCase::factory()->published()->create([
|
||||||
|
'is_featured' => true,
|
||||||
|
]);
|
||||||
|
Testimonial::factory()->published()->create([
|
||||||
|
'quote' => 'Relato publicado.',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$page = $this->visit('/', [
|
||||||
|
'reducedMotion' => 'reduce',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$numbering = $page->script(<<<'JS'
|
||||||
|
() => {
|
||||||
|
const flow = document.querySelector('.home-chapters');
|
||||||
|
const chapters = Array.from(document.querySelectorAll('.home-chapter'));
|
||||||
|
const folios = Array.from(document.querySelectorAll('[data-home-folio]'));
|
||||||
|
|
||||||
|
return {
|
||||||
|
reset: flow ? getComputedStyle(flow).counterReset : '',
|
||||||
|
increments: chapters.map((chapter) => getComputedStyle(chapter).counterIncrement),
|
||||||
|
labels: folios.map((folio) => folio.querySelector('[data-home-folio-label]')?.textContent?.trim() ?? ''),
|
||||||
|
contents: folios.map((folio) => {
|
||||||
|
const number = folio.querySelector('[data-home-folio-number]');
|
||||||
|
return number ? getComputedStyle(number, '::before').content.replace(/["']/g, '') : '';
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
JS);
|
||||||
|
|
||||||
|
expect($numbering['reset'])->toContain('home-chapter -1');
|
||||||
|
expect($numbering['increments'])->each->toContain('home-chapter 1');
|
||||||
|
expect($numbering['labels'])->toBe([
|
||||||
|
'Capa',
|
||||||
|
'Manifesto',
|
||||||
|
'Serviços',
|
||||||
|
'Portfólio',
|
||||||
|
'Método',
|
||||||
|
'Depoimentos',
|
||||||
|
'A Amare',
|
||||||
|
'Próximo passo',
|
||||||
|
]);
|
||||||
|
expect($numbering['contents'])->each->toBe('counter(home-chapter, decimal-leading-zero)');
|
||||||
|
});
|
||||||
|
|||||||
@@ -51,7 +51,8 @@ foreach ($screens as $screen => $path) {
|
|||||||
])
|
])
|
||||||
->withLocale('pt-BR')
|
->withLocale('pt-BR')
|
||||||
->withTimezone('America/Fortaleza')
|
->withTimezone('America/Fortaleza')
|
||||||
->resize($width, $height)
|
->resize($width, $height),
|
||||||
|
resetScroll: $path === '/',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -197,4 +197,65 @@ class HomePageContentTest extends TestCase
|
|||||||
->assertDontSee('href="#testimonials-heading"', false)
|
->assertDontSee('href="#testimonials-heading"', false)
|
||||||
->assertDontSee('Relato vazio');
|
->assertDontSee('Relato vazio');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_home_folios_follow_rendered_chapter_order_and_hide_decorative_numbers(): void
|
||||||
|
{
|
||||||
|
SiteSetting::instance();
|
||||||
|
|
||||||
|
Service::factory()->published()->featured()->create();
|
||||||
|
PortfolioCase::factory()->published()->create(['is_featured' => true]);
|
||||||
|
Testimonial::factory()->published()->create(['quote' => 'Relato publicado.']);
|
||||||
|
|
||||||
|
$html = $this->get(route('home'))->assertOk()->getContent();
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
'Capa',
|
||||||
|
'Manifesto',
|
||||||
|
'Serviços',
|
||||||
|
'Portfólio',
|
||||||
|
'Método',
|
||||||
|
'Depoimentos',
|
||||||
|
'A Amare',
|
||||||
|
'Próximo passo',
|
||||||
|
], $this->folioLabels((string) $html));
|
||||||
|
$this->assertSame(8, preg_match_all('/data-home-folio-number[^>]*aria-hidden="true"/i', (string) $html));
|
||||||
|
$this->assertSame(1, preg_match_all('/<h1\b/i', (string) $html));
|
||||||
|
|
||||||
|
$css = (string) file_get_contents(resource_path('css/app.css'));
|
||||||
|
|
||||||
|
$this->assertStringContainsString('counter-reset: home-chapter -1', $css);
|
||||||
|
$this->assertStringContainsString('counter-increment: home-chapter', $css);
|
||||||
|
$this->assertStringContainsString('counter(home-chapter, decimal-leading-zero)', $css);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function test_home_folios_are_omitted_with_conditional_chapters_without_leaving_markup_gaps(): void
|
||||||
|
{
|
||||||
|
SiteSetting::instance();
|
||||||
|
|
||||||
|
$html = $this->get(route('home'))->assertOk()->getContent();
|
||||||
|
|
||||||
|
$this->assertSame([
|
||||||
|
'Capa',
|
||||||
|
'Manifesto',
|
||||||
|
'Método',
|
||||||
|
'A Amare',
|
||||||
|
'Próximo passo',
|
||||||
|
], $this->folioLabels((string) $html));
|
||||||
|
$this->assertSame(5, preg_match_all('/data-home-folio-number[^>]*aria-hidden="true"/i', (string) $html));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return list<string> */
|
||||||
|
private function folioLabels(string $html): array
|
||||||
|
{
|
||||||
|
preg_match_all(
|
||||||
|
'/<span\b[^>]*data-home-folio-label[^>]*>\s*([^<]+?)\s*<\/span>/is',
|
||||||
|
$html,
|
||||||
|
$matches,
|
||||||
|
);
|
||||||
|
|
||||||
|
return array_values(array_map(
|
||||||
|
static fn (string $label): string => trim(html_entity_decode($label)),
|
||||||
|
$matches[1],
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ trait StableScreenshot
|
|||||||
* images, so this helper additionally exports standalone diff/expected/actual
|
* images, so this helper additionally exports standalone diff/expected/actual
|
||||||
* PNG files that are directly viewable in CI artifacts.
|
* PNG files that are directly viewable in CI artifacts.
|
||||||
*/
|
*/
|
||||||
public function assertStableScreenshotMatches(AwaitableWebpage $awaitable): void
|
public function assertStableScreenshotMatches(AwaitableWebpage $awaitable, bool $resetScroll = false): void
|
||||||
{
|
{
|
||||||
$page = $awaitable->page();
|
$page = $awaitable->page();
|
||||||
|
|
||||||
@@ -66,6 +66,11 @@ trait StableScreenshot
|
|||||||
}
|
}
|
||||||
JS);
|
JS);
|
||||||
|
|
||||||
|
if ($resetScroll) {
|
||||||
|
$page->evaluate('window.scrollTo(0, 0)');
|
||||||
|
$page->waitForFunction('window.scrollY === 0');
|
||||||
|
}
|
||||||
|
|
||||||
usleep(300_000);
|
usleep(300_000);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user