* feat: aplicar identidade Heritage Editorial ao painel Filament O painel admin ainda usava os defaults de fábrica do Filament (Amber, Zinc, Inter Variable, dark mode ligado) enquanto o site público já seguia o design system Heritage Editorial há várias entregas — a inconsistência ficava evidente para quem navegava entre as duas áreas e não havia nenhum teste travando a configuração do painel. - Cores primary/gray/danger/warning/success viram arrays explícitos de 11 tons (não Color::hex()/string), a única forma de preservar os hexadecimais exatos do DESIGN.md — Color::hex() decompõe a cor e remonta lightness/chroma por uma tabela fixa, perdendo a cor real. Os tons foram escolhidos verificando empiricamente com ButtonComponentColorMap qual shade o botão sólido realmente usa (600/500/50), e o tom 50 do primary é um verde-oliva claro (não branco puro) porque BadgeComponent usa bg-color-50 diretamente e um branco puro tornaria o badge quase invisível sobre o Papel Marfim. - EB Garamond self-hosted via LocalFontProvider (sans/mono/serif), sem reintroduzir uma requisição externa ao fonts.bunny.net — o hook HEAD_END reaproveita o mesmo <x-fonts /> do site público. - Novo tema Vite (resources/css/filament/admin/theme.css) zera radius e shadow só no escopo do Filament, incluindo a variável --radius "bare" (usada em ~227 regras do próprio Filament) e um override manual para o CSS pré-compilado do tooltip Tippy.js, que não é alcançado pelo @theme. - Dark mode desativado: o Heritage Editorial é uma paleta única. - Teste de regressão (AdminPanelBrandParityTest) pinando cores, fontes e configuração do tema, incluindo uma renderização real de /admin/login — foi essa renderização que pegou um bug real: passar a família já entre aspas simples quebrava a declaração CSS (--font-family: ''EB Garamond''), silenciosamente caindo para ui-sans-serif. Bundle público verificado byte a byte: nenhuma declaração CSS existente mudou de valor (apenas classes novas e não usadas pelo site público foram adicionadas ao app.css, um efeito colateral inerte de ter uma segunda entrada Tailwind no mesmo build do Vite). Co-Authored-By: Claude noreply@anthropic.com AI-Assisted: yes AI-Tool: claude-code * fix(build): copiar CSS do Filament no estágio frontend e neutralizar sombras Dois defeitos encontrados ao revisar a paridade visual do painel. O primeiro impedia o build da imagem. O `resources/css/filament/admin/ theme.css` importa o CSS não compilado do próprio Filament, e o estágio `frontend` do Dockerfile copia apenas `package.json`, `vite.config.js`, `resources` e `public` — nunca `vendor`. O `npm run build` falhava na resolução do import, o que derrubaria os jobs `container` e `browser` do CI. Agora a subárvore `vendor/filament` é copiada do estágio do composer, em vez de todo o `vendor`, para manter o contexto pequeno. O segundo é silencioso e mais interessante. O plugin do Tailwind compartilha um único contexto entre todas as entradas do build, então o `@source app/Filament/**` da entrada do painel torna visível o uso de `shadow-*` do Filament e o Tailwind passa a emitir seus valores padrão de `--shadow-sm/md/lg` também no bundle público. Verificado por diff do `app.css` construído com e sem a entrada do tema. Nada no site público usa utilitário de sombra hoje, então a renderização não muda e os baselines visuais seguem válidos. Mas deixar valores reais de sombra definidos no CSS que vai para produção permitiria que um `shadow-sm` futuro em elemento público violasse a Tonal Layer Rule do DESIGN.md em silêncio — e o HeritageEditorialTokensTest só inspeciona arquivos de origem, então não pegaria. Os tokens de sombra passam a ser fixados em transparente no `@theme` do `app.css`, com teste que falha se algum deixar de ser. A regra continua verdadeira no artefato que realmente ship. Co-Authored-By: Claude noreply@anthropic.com AI-Assisted: yes AI-Tool: claude-code --------- Co-authored-by: manoel.neto <manoel.neto@creditas.com>
197 lines
8.2 KiB
PHP
197 lines
8.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Feature\Filament;
|
|
|
|
use App\Providers\Filament\AdminPanelProvider;
|
|
use Filament\Facades\Filament;
|
|
use Filament\FontProviders\LocalFontProvider;
|
|
use Filament\Support\Colors\Color;
|
|
use ReflectionMethod;
|
|
use Tests\TestCase;
|
|
|
|
/**
|
|
* Pins the Heritage Editorial parity applied to the admin panel (MAN-118)
|
|
* the same way HeritageEditorialTokensTest pins the public site's tokens, so
|
|
* a future edit to AdminPanelProvider/the Filament theme entry cannot
|
|
* silently drift back toward Filament's stock Amber/Zinc/dark-mode defaults.
|
|
*/
|
|
class AdminPanelBrandParityTest extends TestCase
|
|
{
|
|
/**
|
|
* @return array<int, string>
|
|
*/
|
|
private function invokeColorRamp(string $method): array
|
|
{
|
|
$reflection = new ReflectionMethod(AdminPanelProvider::class, $method);
|
|
$reflection->setAccessible(true);
|
|
|
|
return $reflection->invoke(new AdminPanelProvider($this->app));
|
|
}
|
|
|
|
public function test_primary_ramp_pins_oliva_heranca_and_oliva_profundo(): void
|
|
{
|
|
$primary = $this->invokeColorRamp('primaryColor');
|
|
|
|
$this->assertSame('#556B2F', $primary[600]);
|
|
$this->assertSame('#3E5219', $primary[700]);
|
|
$this->assertSame('#8B9D77', $primary[300]);
|
|
|
|
// Deliberately NOT pure white: BadgeComponent/badge.css apply
|
|
// `bg-color-50` directly as a badge's background, and a pure-white
|
|
// 50 would render a "primary" badge as an invisible pill against
|
|
// the Papel Marfim (#FBF9F4) panel body.
|
|
$this->assertSame('#F3F5EC', $primary[50]);
|
|
$this->assertNotSame('#FFFFFF', $primary[50]);
|
|
}
|
|
|
|
public function test_gray_ramp_pins_the_paper_and_ink_tokens(): void
|
|
{
|
|
$gray = $this->invokeColorRamp('grayColor');
|
|
|
|
$this->assertSame('#FBF9F4', $gray[50]);
|
|
$this->assertSame('#F0EEE9', $gray[100]);
|
|
$this->assertSame('#E4E2DD', $gray[200]);
|
|
$this->assertSame('#C5C8B8', $gray[300]);
|
|
$this->assertSame('#5D6155', $gray[500]);
|
|
$this->assertSame('#1B1C19', $gray[900]);
|
|
}
|
|
|
|
public function test_semantic_ramps_pin_the_existing_amare_hexes_at_shade_600(): void
|
|
{
|
|
$this->assertSame('#991B1B', $this->invokeColorRamp('dangerColor')[600]);
|
|
$this->assertSame('#92400E', $this->invokeColorRamp('warningColor')[600]);
|
|
$this->assertSame('#166534', $this->invokeColorRamp('successColor')[600]);
|
|
}
|
|
|
|
public function test_oliva_heranca_and_tinta_oliva_pairs_stay_wcag_aa(): void
|
|
{
|
|
$primary = $this->invokeColorRamp('primaryColor');
|
|
$gray = $this->invokeColorRamp('grayColor');
|
|
|
|
// White CTA text on the Oliva Herança solid-button background.
|
|
$this->assertGreaterThanOrEqual(
|
|
Color::WCAG_AA_TEXT,
|
|
Color::calculateContrastRatio($primary[600], '#FFFFFF'),
|
|
);
|
|
|
|
// Tinta Oliva body copy on the Papel Marfim panel background.
|
|
$this->assertGreaterThanOrEqual(
|
|
Color::WCAG_AA_TEXT,
|
|
Color::calculateContrastRatio($gray[900], $gray[50]),
|
|
);
|
|
|
|
// Badge text (BadgeComponent resolves this to shade 500 for this
|
|
// ramp) against the badge's own bg-color-50 surface.
|
|
$this->assertGreaterThanOrEqual(
|
|
Color::WCAG_AA_TEXT,
|
|
Color::calculateContrastRatio($primary[50], $primary[500]),
|
|
);
|
|
}
|
|
|
|
public function test_admin_panel_disables_dark_mode(): void
|
|
{
|
|
$this->assertFalse(Filament::getPanel('admin')->hasDarkMode());
|
|
}
|
|
|
|
public function test_admin_panel_uses_the_amare_brand_name(): void
|
|
{
|
|
$this->assertSame('Amare Assessoria', Filament::getPanel('admin')->getBrandName());
|
|
}
|
|
|
|
public function test_admin_panel_self_hosts_eb_garamond_via_local_font_provider(): void
|
|
{
|
|
$panel = Filament::getPanel('admin');
|
|
|
|
// Passed bare, not pre-quoted: Filament's own base layout wraps
|
|
// getFontFamily() in single quotes itself
|
|
// (`--font-family: '{!! filament()->getFontFamily() !!}';`), so a
|
|
// value of "'EB Garamond'" here would render as the broken
|
|
// `--font-family: ''EB Garamond'';` and silently fail to parse as a
|
|
// font-family, falling back to ui-sans-serif — verified against the
|
|
// actual rendered /admin/login output below, not assumed.
|
|
$this->assertSame('EB Garamond', $panel->getFontFamily());
|
|
$this->assertSame(LocalFontProvider::class, $panel->getFontProvider());
|
|
|
|
// font-mono / font-serif utilities (e.g. KeyValue in
|
|
// ManageSiteSettings) must not silently fall back to a system stack.
|
|
$this->assertSame('EB Garamond', $panel->getMonoFontFamily());
|
|
$this->assertSame(LocalFontProvider::class, $panel->getMonoFontProvider());
|
|
$this->assertSame('EB Garamond', $panel->getSerifFontFamily());
|
|
$this->assertSame(LocalFontProvider::class, $panel->getSerifFontProvider());
|
|
}
|
|
|
|
public function test_admin_login_page_renders_the_self_hosted_face_and_brand(): void
|
|
{
|
|
// The only assertion that actually exercises ->viteTheme(), the
|
|
// HEAD_END render hook, and ->brandLogo() end to end — the panel
|
|
// config getters above can't catch a broken manifest reference, a
|
|
// render hook that throws, or a brandLogo closure that fails outside
|
|
// a real request. Requires `npm run build` to have populated
|
|
// public/build (CI's `test` job does this before running tests).
|
|
$response = $this->get('/admin/login');
|
|
|
|
$response->assertOk();
|
|
|
|
// Filament's base layout emits `--font-family: '<value>';` — a
|
|
// single-quoted value, since it does the wrapping itself.
|
|
$response->assertSee("--font-family: 'EB Garamond';", escape: false);
|
|
$response->assertSee("--mono-font-family: 'EB Garamond';", escape: false);
|
|
$response->assertSee("--serif-font-family: 'EB Garamond';", escape: false);
|
|
$response->assertSee('brand/lockup-on-light.webp', escape: false);
|
|
|
|
// The actual requirement behind pinning LocalFontProvider: no
|
|
// external font request leaks into the panel's <head>.
|
|
$response->assertDontSee('fonts.bunny.net');
|
|
$response->assertDontSee('fonts.googleapis.com');
|
|
}
|
|
|
|
public function test_admin_panel_is_wired_to_its_own_vite_theme_entry(): void
|
|
{
|
|
$this->assertSame(
|
|
'resources/css/filament/admin/theme.css',
|
|
Filament::getPanel('admin')->getViteTheme(),
|
|
);
|
|
}
|
|
|
|
public function test_admin_theme_entry_zeroes_radius_and_shadow_tokens(): void
|
|
{
|
|
$theme = (string) file_get_contents(resource_path('css/filament/admin/theme.css'));
|
|
|
|
$this->assertStringContainsString(
|
|
"@import '../../../../vendor/filament/filament/resources/css/theme.css'",
|
|
$theme,
|
|
);
|
|
$this->assertStringContainsString('--radius: 0', $theme);
|
|
$this->assertStringContainsString('--radius-sm: 0', $theme);
|
|
$this->assertStringContainsString('--radius-md: 0', $theme);
|
|
$this->assertStringContainsString('--radius-lg: 0', $theme);
|
|
$this->assertStringContainsString('--radius-xl: 0', $theme);
|
|
$this->assertStringContainsString('--shadow: 0 0 #0000', $theme);
|
|
$this->assertStringContainsString('--shadow-sm: 0 0 #0000', $theme);
|
|
$this->assertStringContainsString('--shadow-md: 0 0 #0000', $theme);
|
|
$this->assertStringContainsString('--shadow-lg: 0 0 #0000', $theme);
|
|
|
|
// Manual override for the vendored Tippy.js tooltip CSS that ships
|
|
// already-compiled in vendor/filament/support/dist/index.css and
|
|
// therefore never sees the @theme overrides above.
|
|
$this->assertStringContainsString('.tippy-box', $theme);
|
|
}
|
|
|
|
public function test_vite_builds_the_admin_theme_entry(): void
|
|
{
|
|
$vite = (string) file_get_contents(base_path('vite.config.js'));
|
|
|
|
$this->assertStringContainsString("'resources/css/filament/admin/theme.css'", $vite);
|
|
}
|
|
|
|
public function test_admin_panel_brand_logo_reuses_the_public_lockup_asset(): void
|
|
{
|
|
$panel = Filament::getPanel('admin');
|
|
|
|
$this->assertSame(asset('brand/lockup-on-light.webp'), $panel->getBrandLogo());
|
|
$this->assertFileExists(base_path('public/brand/lockup-on-light.webp'));
|
|
}
|
|
}
|