feat(quick-260907-fgv): Anmeldeseite zeigt Logo links, Text rechts daneben
Auf Wunsch des Users stehen Bildmarke und Text auf dem gelben Anmelde-Panel jetzt nebeneinander statt untereinander — dieselbe Anordnung wie in der gelieferten Datei tessera-logo-schriftzug.svg: links die Marke, rechts der Name und darunter der Untertitel. Dafuer nimmt die horizontale Variante der Marken-Komponente einen optionalen Untertitel entgegen. Die gestapelte Variante entfaellt — sie war nach dieser Umstellung nirgends mehr im Einsatz, alle uebrigen fuenf Stellen standen schon vorher nebeneinander. Der Test auf die entfallene Variante wurde nicht geloescht, sondern durch zwei Tests auf das neue Verhalten ersetzt: einer prueft, dass ohne Angabe kein Untertitel erscheint, der andere die tatsaechliche Reihenfolge im Dokument (Marke vor Name, Name vor Untertitel) statt nur deren Vorhandensein. Die Erwartungstexte sind von Hand geschrieben und nicht aus derselben Quelle gebildet, aus der die Komponente sie zieht. 222/222 Web-Tests gruen, Typpruefung sauber, im Browser gegengeprueft. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01K5jtbGzC5Sf9npJ3JCjKhq
This commit is contained in:
@@ -43,11 +43,16 @@ export default function LoginPage() {
|
||||
className="hidden md:flex md:flex-1 items-center justify-center"
|
||||
style={{ backgroundColor: BRAND_YELLOW }}
|
||||
>
|
||||
<div className="flex flex-col items-center gap-6 px-8 text-center text-gray-900">
|
||||
<TesseraLogo variant="stacked" size={112} plateOutline={false} />
|
||||
<p className="text-xl text-gray-800 max-w-md leading-relaxed">
|
||||
{t('branding.tagline')}
|
||||
</p>
|
||||
<div className="px-8 text-gray-900">
|
||||
<TesseraLogo
|
||||
variant="horizontal"
|
||||
size={104}
|
||||
plateOutline={false}
|
||||
tagline={t('branding.tagline')}
|
||||
className="gap-6"
|
||||
wordmarkClassName="text-5xl"
|
||||
taglineClassName="text-lg text-gray-800 max-w-xs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -46,11 +46,36 @@ describe('TesseraLogo', () => {
|
||||
expect(screen.getByText('Tessera')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('variant "stacked" renders the img-role mark and the visible wordmark text', () => {
|
||||
render(<TesseraLogo variant="stacked" />);
|
||||
it('renders no tagline unless one is passed', () => {
|
||||
render(<TesseraLogo variant="horizontal" />);
|
||||
|
||||
expect(screen.getByRole('img', { name: 'Tessera Logo' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Tessera')).toBeInTheDocument();
|
||||
// Hand-geschriebener Erwartungstext, bewusst NICHT aus derselben Quelle
|
||||
// gebildet, aus der die Komponente ihn zoege.
|
||||
expect(
|
||||
screen.queryByText('Modulare Workflow-Plattform fuer Ihr Unternehmen'),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('places mark, wordmark and tagline in that document order when a tagline is passed', () => {
|
||||
render(
|
||||
<TesseraLogo
|
||||
variant="horizontal"
|
||||
tagline="Modulare Workflow-Plattform fuer Ihr Unternehmen"
|
||||
/>,
|
||||
);
|
||||
|
||||
const mark = screen.getByRole('img', { name: 'Tessera Logo' });
|
||||
const wordmark = screen.getByText('Tessera');
|
||||
const tagline = screen.getByText(
|
||||
'Modulare Workflow-Plattform fuer Ihr Unternehmen',
|
||||
);
|
||||
|
||||
expect(mark).toBeInTheDocument();
|
||||
// Node.compareDocumentPosition: DOCUMENT_POSITION_FOLLOWING === 4.
|
||||
// Prueft die tatsaechliche Reihenfolge im Baum — Bildmarke vor Schriftzug,
|
||||
// Schriftzug vor Untertitel — statt nur deren Vorhandensein.
|
||||
expect(mark.compareDocumentPosition(wordmark) & 4).toBe(4);
|
||||
expect(wordmark.compareDocumentPosition(tagline) & 4).toBe(4);
|
||||
});
|
||||
|
||||
it('enables the dark-mode plate outline class by default', () => {
|
||||
|
||||
@@ -3,13 +3,19 @@
|
||||
import { useTranslations } from 'next-intl';
|
||||
import { BRAND_OLIVE, BRAND_PLATE, BRAND_YELLOW } from './brand';
|
||||
|
||||
export type TesseraLogoVariant = 'mark' | 'horizontal' | 'stacked';
|
||||
export type TesseraLogoVariant = 'mark' | 'horizontal';
|
||||
|
||||
export interface TesseraLogoProps {
|
||||
/** Welche Bausteine gerendert werden. Vorgabe: `'horizontal'`. */
|
||||
variant?: TesseraLogoVariant;
|
||||
/** Kantenlaenge der Bildmarke in Pixeln. Vorgabe: 32. */
|
||||
size?: number;
|
||||
/**
|
||||
* Optionaler Untertitel unter dem Schriftzug, rechts neben der Bildmarke —
|
||||
* die Anordnung der gelieferten Datei `tessera-logo-schriftzug.svg`. Ohne
|
||||
* Angabe steht rechts nur der Name. Nur fuer `variant="horizontal"`.
|
||||
*/
|
||||
tagline?: string;
|
||||
/**
|
||||
* Schaltet die halbtransparente helle Kontur der Grundplatte im dunklen
|
||||
* Erscheinungsbild ein. Auf Flaechen, die unabhaengig vom
|
||||
@@ -19,8 +25,10 @@ export interface TesseraLogoProps {
|
||||
plateOutline?: boolean;
|
||||
/** Zusaetzliche Klassen fuer das aeusserste Element der gewaehlten Variante. */
|
||||
className?: string;
|
||||
/** Zusaetzliche Klassen fuer den Schriftzug (nur `horizontal`/`stacked`). */
|
||||
/** Zusaetzliche Klassen fuer den Schriftzug (nur `horizontal`). */
|
||||
wordmarkClassName?: string;
|
||||
/** Zusaetzliche Klassen fuer den Untertitel (nur zusammen mit `tagline`). */
|
||||
taglineClassName?: string;
|
||||
}
|
||||
|
||||
function LogoMark({
|
||||
@@ -86,9 +94,11 @@ function LogoMark({
|
||||
export function TesseraLogo({
|
||||
variant = 'horizontal',
|
||||
size = 32,
|
||||
tagline,
|
||||
plateOutline = true,
|
||||
className,
|
||||
wordmarkClassName,
|
||||
taglineClassName,
|
||||
}: TesseraLogoProps) {
|
||||
const t = useTranslations('common');
|
||||
const ariaLabel = t('brand.logoAlt');
|
||||
@@ -104,26 +114,21 @@ export function TesseraLogo({
|
||||
);
|
||||
}
|
||||
|
||||
if (variant === 'stacked') {
|
||||
// Bildmarke links, Schriftzug rechts daneben. Traegt die Komponente einen
|
||||
// Untertitel, steht er unter dem Namen — dieselbe Anordnung wie in der
|
||||
// gelieferten Datei `tessera-logo-schriftzug.svg`.
|
||||
return (
|
||||
<span className={`inline-flex flex-col items-center gap-2 ${className ?? ''}`}>
|
||||
<span className={`inline-flex items-center gap-3 ${className ?? ''}`}>
|
||||
<LogoMark size={size} plateOutline={plateOutline} ariaLabel={ariaLabel} />
|
||||
<span
|
||||
className={`text-2xl font-bold tracking-tight ${wordmarkClassName ?? ''}`}
|
||||
>
|
||||
<span className="inline-flex flex-col text-left">
|
||||
<span className={`font-bold tracking-tight leading-none ${wordmarkClassName ?? ''}`}>
|
||||
{t('appName')}
|
||||
</span>
|
||||
{tagline ? (
|
||||
<span className={`mt-1 leading-snug ${taglineClassName ?? ''}`}>
|
||||
{tagline}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={`inline-flex items-center gap-2 ${className ?? ''}`}>
|
||||
<LogoMark size={size} plateOutline={plateOutline} ariaLabel={ariaLabel} />
|
||||
<span
|
||||
className={`font-bold tracking-tight ${wordmarkClassName ?? ''}`}
|
||||
>
|
||||
{t('appName')}
|
||||
) : null}
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user