fix: constrain mobile recipe layout and wrap long source URLs
This commit is contained in:
1 parent
df5efa24e4
commit
bee3b990f9
6 files changed
+56
-11
No files matched your search
@@ -77,6 +77,10 @@ Nach der gestalterischen Überarbeitung wurden TypeScript-Prüfung und Produktio
|
||||
- Visuell angesehen: neue Desktop-Rezeptkarten für Schritte, Smartphone-Rezeptseite mit Schrittbildern, dunkle Desktop-Schrittkarte und mobiler Kochmodus. Die Rezept-/Kochmodus-Screenshots unter `docs/screenshots/` wurden aktualisiert; zusätzliche Bild-/Listenansichten liegen in den Playwright-Testartefakten.
|
||||
- Die JSON-Vorlage in `docs/CHATGPT-PROMPT.md` wurde mit dem tatsächlichen Importparser geprüft; das öffentliche JSON-Schema und die Beispiele wurden neu erzeugt.
|
||||
|
||||
### Nachprüfung: mobile Rezeptbreite
|
||||
|
||||
Der mobile Überlauf wurde am veröffentlichten Pljeskavica-Rezept reproduziert: lange Bildquellen-URLs in den Tipps vergrößerten die minimale Grid-Spaltenbreite und damit auch die Zutatenliste. Die Spalte verwendet nun `minmax(0, 1fr)`, ihre Kinder dürfen schrumpfen und lange Texte umbrechen. Mit dem echten Rezept und dem korrigierten Stylesheet wurden 320, 360, 390, 430 und 768 Pixel Breite ohne Überlauf geprüft. Produktionsbuild und alle 15 öffentlichen Browserabläufe bestanden. Der Regressionstest enthält eine lange Quellen-URL und vergleicht die Inhaltsbreite mit `document.documentElement.clientWidth`; `innerWidth` kann sich auf mobilen Browsern selbst mit dem Überlauf vergrößern und ist dafür ungeeignet.
|
||||
|
||||
## Präzise Grenzen
|
||||
|
||||
- Kein Test auf physischem Smartphone, Safari oder Firefox; keine tatsächliche iOS-/Android-Installation über das Betriebssystem-Installationsmenü.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 941 KiB After Width: | Height: | Size: 938 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 2.8 MiB After Width: | Height: | Size: 2.8 MiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.9 MiB After Width: | Height: | Size: 1.9 MiB |
+11
-1
@@ -869,6 +869,10 @@ small,
|
||||
grid-template-columns: 350px minmax(0, 1fr);
|
||||
gap: 65px;
|
||||
align-items: start;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.recipe-columns > * {
|
||||
min-width: 0;
|
||||
}
|
||||
.ingredients-panel {
|
||||
background: var(--surface);
|
||||
@@ -997,6 +1001,7 @@ small,
|
||||
.ingredient-name {
|
||||
font-size: 12px;
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.ingredient-name small {
|
||||
display: block;
|
||||
@@ -1138,6 +1143,11 @@ small,
|
||||
}
|
||||
.tip-card svg {
|
||||
color: var(--green);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tip-card > div {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
}
|
||||
.tip-card h3 {
|
||||
font-size: 23px;
|
||||
@@ -2304,7 +2314,7 @@ input[type='checkbox'] {
|
||||
flex: 1;
|
||||
}
|
||||
.recipe-columns {
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
gap: 35px;
|
||||
}
|
||||
.ingredients-panel {
|
||||
|
||||
+41
-10
@@ -21,6 +21,7 @@ test('external images and structured steps survive import, editing, scaling and
|
||||
title: `Bilderküche ${info.project.name}`,
|
||||
image: cover,
|
||||
images: ['https://images.example.org/gallery.jpg'],
|
||||
tips: 'Bildquelle: https://commons.wikimedia.org/wiki/File:Pljeskavica_cheese_chili_with_a_very_long_unbroken_filename.jpg\n\nLizenz: https://creativecommons.org/licenses/by-sa/4.0/',
|
||||
steps: pasta.steps.map((s, n) =>
|
||||
n === 0
|
||||
? {
|
||||
@@ -86,9 +87,21 @@ test('external images and structured steps survive import, editing, scaling and
|
||||
await page.getByRole('spinbutton', { name: 'Portionen', exact: true }).fill('4');
|
||||
await expect(first.locator('.instruction-ingredient').first()).toHaveText('80 g Basilikum');
|
||||
await expect(first.locator('.step-supplies')).toContainText('80 g Basilikum');
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
await page
|
||||
.locator('.recipe-columns')
|
||||
.evaluate((el) =>
|
||||
[...el.querySelectorAll('*')].every(
|
||||
(child) =>
|
||||
child.getBoundingClientRect().right <= document.documentElement.clientWidth + 1,
|
||||
),
|
||||
),
|
||||
).toBe(true);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
await page.screenshot({ path: info.outputPath('structured-steps.png'), fullPage: true });
|
||||
await page.getByRole('button', { name: 'Theme wechseln', exact: true }).click();
|
||||
await first.screenshot({ path: info.outputPath('structured-step-dark.png') });
|
||||
@@ -98,9 +111,11 @@ test('external images and structured steps survive import, editing, scaling and
|
||||
);
|
||||
await expect(page.locator('.cooking-step .step-images img')).toHaveCount(2);
|
||||
await expect(page.locator('.cooking-step')).toHaveCSS('opacity', '1');
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
await page.screenshot({ path: info.outputPath('structured-cooking.png'), fullPage: true });
|
||||
expect(errors).toEqual([]);
|
||||
} finally {
|
||||
@@ -147,7 +162,11 @@ test('editor remains usable on the current viewport in dark mode', async ({ page
|
||||
await expect(page.getByRole('textbox', { name: 'Zutat 1', exact: true })).toHaveValue('Karotten');
|
||||
await expect(page.locator('.ingredient-entry').nth(0)).toContainText('optional');
|
||||
await expect(page.locator('.ingredient-entry').nth(1)).toContainText('geschält und gewürfelt');
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
await page.screenshot({
|
||||
path: `docs/screenshots/editor-dark-${info.project.name}.png`,
|
||||
fullPage: true,
|
||||
@@ -166,7 +185,11 @@ test('editor remains usable on the current viewport in dark mode', async ({ page
|
||||
);
|
||||
}
|
||||
await page.getByRole('button', { name: 'Live-Vorschau', exact: true }).click();
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
});
|
||||
test('public library, shareable search, favorites, themes and responsive layouts', async ({
|
||||
page,
|
||||
@@ -186,7 +209,11 @@ test('public library, shareable search, favorites, themes and responsive layouts
|
||||
fullPage: true,
|
||||
animations: 'disabled',
|
||||
});
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
await page.getByRole('textbox', { name: 'Rezepte suchen' }).fill('Basilikum');
|
||||
await expect(page.locator('.recipe-card')).toHaveCount(1);
|
||||
await expect(page).toHaveURL(/q=Basilikum/);
|
||||
@@ -230,7 +257,11 @@ test('portion calculator, ingredient anchor, shopping list and print content', a
|
||||
path: `docs/screenshots/recipe-${info.project.name}.png`,
|
||||
fullPage: true,
|
||||
});
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true);
|
||||
expect(
|
||||
await page.evaluate(
|
||||
() => document.documentElement.scrollWidth <= document.documentElement.clientWidth,
|
||||
),
|
||||
).toBe(true);
|
||||
await page.getByRole('button', { name: 'Zur Einkaufsliste hinzufügen' }).click();
|
||||
await page.goto('/einkaufsliste');
|
||||
await expect(page.locator('.shopping-list')).toContainText('400 g');
|
||||
|
||||
Reference in new issue
Block a user