feat(richtlinien): add /hilfe/transkription page with RichtlinienRuleCard

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-24 21:28:25 +02:00
parent 7c3a8e7651
commit b234db0472
5 changed files with 276 additions and 0 deletions

View File

@@ -0,0 +1,30 @@
<script lang="ts">
type Props = {
icon: string;
title: string;
body: string;
beispielOutput?: string;
beispielLabel?: string;
};
let { icon, title, body, beispielOutput, beispielLabel = 'Beispiel' }: Props = $props();
</script>
<div class="border-brand-sand break-inside-avoid rounded-sm border bg-white p-5 shadow-sm">
<div class="mb-3 flex items-center gap-2">
<span aria-hidden="true" class="text-xl">{icon}</span>
<h3 class="font-serif text-base font-bold text-ink">{title}</h3>
</div>
<p class="font-serif text-sm leading-relaxed text-ink-2">{body}</p>
{#if beispielOutput !== undefined}
<div class="border-brand-sand mt-4 rounded-sm border bg-[#FAF8F1] px-4 py-3">
<p class="font-sans text-xs font-semibold tracking-wider text-ink-3 uppercase">
{beispielLabel}
</p>
<p class="mt-1 font-sans text-sm text-ink">
<code class="font-mono">{beispielOutput}</code>
</p>
</div>
{/if}
</div>

View File

@@ -0,0 +1,49 @@
import { describe, it, expect, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import RichtlinienRuleCard from './RichtlinienRuleCard.svelte';
afterEach(cleanup);
const defaultProps = {
icon: '✍',
title: 'Unleserliche Wörter',
body: 'Schreiben Sie [unleserlich].',
beispielOutput: '[unleserlich]'
};
describe('RichtlinienRuleCard', () => {
it('renders an h3 with the title', async () => {
render(RichtlinienRuleCard, { props: defaultProps });
await expect
.element(page.getByRole('heading', { level: 3 }))
.toHaveTextContent('Unleserliche Wörter');
});
it('renders the body text', async () => {
render(RichtlinienRuleCard, { props: defaultProps });
await expect.element(page.getByText('Schreiben Sie [unleserlich].')).toBeInTheDocument();
});
it('renders icon in a span with aria-hidden="true"', async () => {
render(RichtlinienRuleCard, { props: defaultProps });
const iconSpan = document.querySelector('span[aria-hidden="true"]');
expect(iconSpan).not.toBeNull();
expect(iconSpan!.textContent).toContain('✍');
});
it('renders beispielOutput in monospace with → arrow', async () => {
render(RichtlinienRuleCard, { props: defaultProps });
const mono = document.querySelector('code, [class*="font-mono"]');
expect(mono).not.toBeNull();
expect(mono!.textContent).toContain('[unleserlich]');
await expect.element(page.getByText(/→/)).toBeInTheDocument();
});
it('does not render beispiel section when beispielOutput is absent', async () => {
render(RichtlinienRuleCard, {
props: { icon: '✍', title: 'Test', body: 'Body' }
});
expect(document.querySelector('code, [class*="font-mono"]')).toBeNull();
});
});