Fassung 1.5.0: die Vorschau wird zur Arbeitsflaeche
CMS quality / php (8.2) (push) Canceled after 0s
CMS quality / php (8.4) (push) Canceled after 0s
CMS quality / browser (push) Canceled after 0s
CMS quality / mysql (push) Canceled after 0s

Abgleich mit dem Flatsome UX Builder (docs/FLATSOME-ABGLEICH.md): die Gestaltung
ist Flatsome voraus, die Luecken liegen beim Elementkatalog und bei der
Bedienung. Umuts Entscheidung: Bedienung zuerst, Shop-Elemente ausgelassen.

Erste Haelfte der Bedienung:

- Werkzeugleiste am Element in der Vorschau. Sie nennt das Element, auf das sie
  wirkt - die Seitenleiste hat dieselben fuenf Aktionen, und zwei gleichnamige
  Bedienelemente auf einem Bildschirm sind fuer eine Sprachausgabe nicht
  unterscheidbar. Beide Wege rufen dieselben Funktionen auf.
- Auswahl- und Ueberfahrrahmen mit Beschriftung.
- Einfuegepunkte an Ober- und Unterkante: das naechste Element landet dort
  statt am Ende.
- Ziehen aus dem Werkzeugkasten in die Vorschau, mit Ablegemarke zwischen den
  Elementen und Rahmen um einen aufnehmenden Behaelter. Der Strukturbaum zieht
  in dieselbe Flaeche.

Die Ebene liegt ueber dem Rahmen, nicht darin: die Vorschau wird bei jeder
Aenderung ersetzt, eingezeichnete Bedienelemente waeren weg und stuenden in der
Auszeichnung, die die Vorschau wahrheitsgetreu zeigen soll. Leiste und
Pluszeichen werden gegenskaliert und bleiben in der Mobilvorschau fingergross.

Im Modell neu: insertBlockAt, moveBlockTo, dropList.

Dabei behoben: die Ebene war aria-hidden und enthielt fokussierbare
Schaltflaechen; canContain zerbrach an einem fehlenden Block beim Ziehen aus
dem Werkzeugkasten; beim Verschieben ans Listenende wurde der Index begrenzt,
bevor die Entnahme verrechnet war.

Geprueft: npm test 22/22, phpunit 434/434, npm run test:editor vollstaendig
mit neuem Abschnitt und ohne axe-Verstoss.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
kantarciandClaude Opus 5 committed 2026-09-14 06:53:23 +02:00
1 parent af5a2cef86
commit 22e4ada830
10 files changed
+841 -27

No files matched your search

+52 -3
View File
@@ -64,7 +64,7 @@ try {
console.log('Preview status:', await page.locator('#preview-status').innerText());
const frame = page.frameLocator('#preview');
await frame.locator('h1').waitFor();
await page.locator('#preview-status').filter({ hasText: 'Zum Auswählen' }).waitFor();
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
await frame.locator('h1').click();
assert.equal(await page.locator('#properties-title').innerText(), 'Überschrift');
await page.getByLabel('Text', { exact: true }).fill('Meine bearbeitete Website');
@@ -76,6 +76,55 @@ try {
await frame.getByRole('heading', { name: 'Meine bearbeitete Website', exact: true }).waitFor();
checks.push({ name: 'preview selection, property edit, undo and redo', passed: true });
// --- Die Vorschau als Arbeitsflaeche -------------------------------------------------------
// Rahmen, Werkzeugleiste am Element, Einfuegepunkte und Ziehen aus dem Werkzeugkasten.
const inPreview = selector => page.evaluate(
sel => document.querySelector('#preview').contentDocument?.querySelector(sel)?.id ?? null, selector);
await frame.locator('h1').click();
await page.locator('.ov-select').waitFor({ state: 'visible' });
assert.equal(await page.locator('.ov-toolbar-name').innerText(), 'Überschrift');
await page.getByRole('toolbar', { name: 'Aktionen für das gewählte Element' }).waitFor();
// Die Leiste benennt das Element, auf das sie wirkt - sonst haette sie denselben Namen wie
// die gleichnamige Schaltflaeche in der Seitenleiste.
await page.getByRole('button', { name: 'Überschrift duplizieren', exact: true }).waitFor();
await page.getByRole('button', { name: 'Element über Überschrift auswählen', exact: true }).click();
assert.notEqual(await page.locator('#properties-title').innerText(), 'Überschrift');
// Einfuegepunkt: das naechste Element landet genau dort und nicht am Ende.
await frame.locator('h1').click();
const headingId = await inPreview('h1');
await page.getByRole('button', { name: 'Element dahinter einfügen', exact: true }).click();
await page.locator('#insert-hint').waitFor({ state: 'visible' });
await page.locator('#library button[data-add="text"]').click();
assert.equal(await page.locator('#properties-title').innerText(), 'Text');
assert.equal(await page.locator('#insert-hint').isVisible(), false);
await page.waitForFunction(id => {
const next = document.querySelector('#preview').contentDocument?.querySelector(`#${id}`)?.nextElementSibling;
return Boolean(next?.classList.contains('block-text'));
}, headingId);
await page.getByRole('button', { name: 'Rückgängig', exact: true }).click();
// Ziehen aus dem Werkzeugkasten auf die Seite.
await frame.locator('h1').waitFor();
const beforeButtons = await page.locator('#structure [data-block-id]').count();
await page.locator('#library button[data-add="button"]').dragTo(frame.locator('h1'), { targetPosition: { x: 12, y: 4 } });
await page.waitForFunction(count => document.querySelectorAll('#structure [data-block-id]').length === count + 1, beforeButtons);
assert.equal(await page.locator('#properties-title').innerText(), 'Button');
// Oben abgelegt heisst davor, nicht dahinter.
await page.waitForFunction(id => {
const previous = document.querySelector('#preview').contentDocument?.querySelector(`#${id}`)?.previousElementSibling;
return Boolean(previous?.classList.contains('button'));
}, headingId);
await page.getByRole('button', { name: 'Rückgängig', exact: true }).click();
await page.waitForFunction(count => document.querySelectorAll('#structure [data-block-id]').length === count, beforeButtons);
checks.push({ name: 'canvas toolbar, insert points and drag from the toolbox', passed: true });
await page.screenshot({ path: 'reports/editor/canvas-overlay.png' });
// Zurueck in den Zustand, den dieser Abschnitt vorgefunden hat: die folgenden Pruefungen
// setzen eine bestehende Auswahl voraus.
await frame.getByRole('heading', { name: 'Meine bearbeitete Website', exact: true }).click();
await page.locator('#duplicate:not([disabled])').waitFor();
console.log('Canvas overlay, insert points and drag and drop passed.');
// Die Vorschau darf nie sichtbar sein, solange ihre Gestaltung fehlt. Vorher hing das Anwenden
// am Ladeereignis des Rahmens - und das feuert erst, wenn das letzte Bild geladen ist. Auf der
// nachgebauten Startseite von kant-it.de stand die Vorschau dadurch 1440 ms lang mit falschen
@@ -109,7 +158,7 @@ try {
assert.ok(verlauf.includes('hidden'), 'Die Vorschau wird beim Aufbau nicht verdeckt.');
assert.equal(verlauf[verlauf.length - 1], 'sichtbar', 'Die Vorschau bleibt verdeckt.');
await page.click('#undo');
await page.locator('#preview-status').filter({ hasText: 'Zum Auswählen' }).waitFor();
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
checks.push({ name: 'preview never shows unstyled content', passed: true });
console.log('Preview edits and history passed.');
@@ -473,7 +522,7 @@ try {
await page.locator('#structure [data-block-id="headline"]').waitFor();
await page.locator('#structure [data-block-id="headline"]').click();
await page.getByRole('button', { name: 'Design', exact: true }).click();
await page.locator('#preview-status').filter({ hasText: 'Zum Auswählen' }).waitFor();
await page.locator('#preview-status').filter({ hasText: 'Entwurf ·' }).waitFor();
await axe('desktop editor');
await page.screenshot({ path: 'reports/editor/editor-desktop.png', fullPage: true });
await page.getByRole('button', { name: 'Mobil', exact: true }).click();