diff --git a/public/embed/shop.js b/public/embed/shop.js index 115bf59..e5e3d43 100644 --- a/public/embed/shop.js +++ b/public/embed/shop.js @@ -113,8 +113,13 @@ '.wks-summen .wks-gesamt{font-weight:700;font-size:1.1rem;border-top:1px solid var(--wks-i-rand);padding-top:.35rem}', '.wks-zusammen{margin:0 0 1rem;display:grid;grid-template-columns:max-content 1fr;gap:.35rem 1rem}', '.wks-zusammen dt{font-weight:600}.wks-zusammen dd{margin:0;overflow-wrap:anywhere;white-space:pre-line}', - '.wks-gewaehr{margin:.75rem 0}', - '.wks-gewaehr img{display:block;max-width:100%;height:auto;border:1px solid var(--wks-i-rand);border-radius:calc(var(--wks-i-radius) * .7)}', + '.wks-gewaehr{margin:1.25rem 0;padding:.9rem 1rem;border:2px solid var(--wks-i-akzent);border-radius:var(--wks-i-radius);background:var(--wks-i-weich)}', + '.wks-gewaehr h4{font-size:1rem;margin:0 0 .6rem}', + '.wks-gewaehr img{display:block;width:100%;max-width:420px;height:auto;margin:0 0 .6rem;border:1px solid var(--wks-i-rand);border-radius:calc(var(--wks-i-radius) * .7);background:#fff}', + '.wks-gewaehr>:last-child{margin-bottom:0}', + '.wks-agb-hinweis{margin:0 0 .75rem}', + 'dialog.wks-dialog.wks-dialog-gross{width:min(64rem,calc(100vw - 1.5rem))}', + '.wks-dialog-bild{display:block;max-width:100%;height:auto;margin:0 auto 1rem;background:#fff}', '.wks-links{display:flex;flex-wrap:wrap;gap:.25rem 1.25rem;margin:0 0 .75rem}', '.wks-box{margin:0 0 1rem;padding:.75rem 1rem;border-radius:var(--wks-i-radius);background:var(--wks-i-weich)}', '.wks-box h4{font-size:1rem;margin:0 0 .25rem}', @@ -645,9 +650,12 @@ dlg = el('dialog', 'wks-dialog') dlg.addEventListener('click', function (e) { if (e.target === dlg) dialogSchliessen() }) dlg.addEventListener('close', function () { if (dlgAusloeser && dlgAusloeser.focus) dlgAusloeser.focus() }) + // Esc schliesst auch dort, wo showModal fehlt (der Browser erledigt es sonst selbst) + dlg.addEventListener('keydown', function (e) { if (e.key === 'Escape' || e.key === 'Esc') { e.preventDefault(); dialogSchliessen() } }) root.appendChild(dlg) } dlgAusloeser = ausloeser || null + dlg.className = 'wks-dialog' + (bloecke.some(function (b) { return !!b.bild }) ? ' wks-dialog-gross' : '') dlg.textContent = '' var innen = el('div', 'wks-dialog-innen') var zu = knopf('wks-schliessen', '×', dialogSchliessen) @@ -661,6 +669,14 @@ text.setAttribute('role', 'region') text.setAttribute('aria-label', ueberschrift) bloecke.forEach(function (b) { + if (b.bild) { + var gross = el('img', 'wks-dialog-bild') + gross.src = b.bild + gross.alt = nichtLeer(b.text) ? b.text : '' + gross.decoding = 'async' + text.appendChild(gross) + return + } if (!nichtLeer(b.text)) return if (b.titel) text.appendChild(el('h4', 'wks-untertitel', b.titel)) text.appendChild(absaetze(b.text, '')) @@ -705,6 +721,31 @@ dialogOeffnen('Versand und Zahlung', bloecke, ausloeser) } + // Hinweis auf das gesetzliche Gewaehrleistungsrecht (Art. 246a § 1 Abs. 1 S. 1 Nr. 11 EGBGB): amtliche Grafik in hervorgehobenem + // Kasten vor der Zusammenfassung der Kasse, sofort geladen, mit dem woertlichen Text als Alternativtext und einer Vergroesserung + function gewaehrKasten () { + var g = gewaehr() + if (!g) return null + var kasten = el('section', 'wks-gewaehr') + kasten.setAttribute('aria-label', 'Ihre gesetzlichen Gewährleistungsrechte') + kasten.appendChild(el('h4', '', 'Ihre gesetzlichen Gewährleistungsrechte')) + var bildUrl = httpsUrl(g.bild) + if (bildUrl) { + var img = el('img') + img.src = bildUrl + img.alt = g.text + img.loading = 'eager' + img.decoding = 'async' + kasten.appendChild(img) + kasten.appendChild(knopf('wks-btn wks-btn-sek', 'Vergrößern', function (e) { + dialogOeffnen('Ihre gesetzlichen Gewährleistungsrechte', [{ bild: bildUrl, text: g.text }], e.currentTarget) + })) + } else { + kasten.appendChild(absaetze(g.text, 'wks-text')) + } + return kasten + } + // ------------------------------------------------------------ Formularhelfer function honigfeld () { @@ -1258,6 +1299,10 @@ landSichtbar() landSel.addEventListener('change', function () { S.land = landSel.value; pruefe() }) + // Gewaehrleistungshinweis: hervorgehoben vor der Zusammenfassung (wird nicht mit der Zusammenfassung neu aufgebaut) + var gkasten = gewaehrKasten() + if (gkasten) form.appendChild(gkasten) + // Pflichtinformationen: stehen unmittelbar ueber dem Knopf und kommen zum grossen Teil vom Server (/pruefen) var pflicht = el('div', 'wks-pflicht') form.appendChild(pflicht) @@ -1265,6 +1310,14 @@ form.appendChild(honigfeld()) var fehlerBox = el('div', 'wks-fehlerbox') form.appendChild(fehlerBox) + // Einbeziehung der AGB (§ 305 Abs. 2 BGB): ausdruecklicher Hinweis unmittelbar ueber dem Knopf, beide Texte oeffnen sich im Dialog + var agbHinweis = el('p', 'wks-agb-hinweis') + agbHinweis.appendChild(document.createTextNode('Es gelten unsere ')) + agbHinweis.appendChild(knopf('wks-link', 'AGB', function (e) { zeigeAgb(e.currentTarget) })) + agbHinweis.appendChild(document.createTextNode('. Informationen zu Ihrem Widerrufsrecht finden Sie in der ')) + agbHinweis.appendChild(knopf('wks-link', 'Widerrufsbelehrung', function (e) { zeigeBelehrung(e.currentTarget) })) + agbHinweis.appendChild(document.createTextNode('.')) + form.appendChild(agbHinweis) var senden = el('button', 'wks-btn', 'Zahlungspflichtig bestellen') senden.type = 'submit' senden.disabled = true @@ -1333,20 +1386,6 @@ links.appendChild(knopf('wks-link', 'Versand & Zahlung lesen', function (e) { zeigeVersand(e.currentTarget) })) pflicht.appendChild(links) - var g = gewaehr() - var bildUrl = g ? httpsUrl(g.bild) : '' - var gbox = el('div', 'wks-gewaehr') - if (bildUrl) { - var img = el('img') - img.src = bildUrl - img.alt = g.text - img.loading = 'lazy' - img.decoding = 'async' - gbox.appendChild(img) - } else if (g) { - gbox.appendChild(absaetze(g.text, 'wks-text')) - } - pflicht.appendChild(gbox) pflicht.appendChild(el('p', '', 'Name, Lieferadresse und Zahlung geben Sie im nächsten Schritt bei unserem Zahlungsdienstleister Stripe ein.')) senden.disabled = S.sendet } diff --git a/test/shopEmbedStatic.test.js b/test/shopEmbedStatic.test.js index 09ed939..bb1e917 100644 --- a/test/shopEmbedStatic.test.js +++ b/test/shopEmbedStatic.test.js @@ -88,7 +88,7 @@ test('Pflichttexte: Knopf der Kasse, Widerrufsfunktion, Gewaehrleistungsbild, St 'Der Shop ist gerade geschlossen.', 'Shop-Technik: WEBklar', 'Lieferung nach: ', 'Abholung möglich', 'Zahlung: ', "'AGB'", "'Widerrufsbelehrung'", "'Versand & Zahlung'", "'Impressum'", "'Datenschutz'", 'zzgl. Versand', 'Vielen Dank für Ihre Bestellung', 'bei uns eingegangen. Eine Eingangsbestätigung haben wir Ihnen per E-Mail geschickt.', - 'rel = \'noopener noreferrer\'', 'img.alt = g.text', "img.loading = 'lazy'", 'gewaehrleistung', + 'rel = \'noopener noreferrer\'', 'img.alt = g.text', "img.loading = 'eager'", 'gewaehrleistung', 'wk_bestellung', 'wk_t', 'wk_abbruch', 'replaceState', 'kontakt_url', 'formToken', 'rueckUrl: location.href', 'nurWiderruf', 'data-webklar-shop', 'data-api', 'data-ansicht', "'ansicht'", 'aria-live', 'aria-pressed', 'tabIndex = -1', 'showModal', ]) { @@ -100,6 +100,38 @@ test('Pflichttexte: Knopf der Kasse, Widerrufsfunktion, Gewaehrleistungsbild, St assert.ok(OHNE_KOMMENTARE.includes("knopf('wks-btn wks-btn-sek', 'Vertrag widerrufen'"), 'Widerruf im Fuss ist ein Knopf') }) +test('I-2: Gewährleistungsgrafik steht hervorgehoben vor der Zusammenfassung, höchstens 420 px, sofort geladen, mit Vergrößern-Dialog', () => { + assert.ok(OHNE_KOMMENTARE.includes("'Ihre gesetzlichen Gewährleistungsrechte'"), 'Überschrift') + assert.match(OHNE_KOMMENTARE, /\.wks-gewaehr img\{[^']*width:100%;max-width:420px/) + assert.match(OHNE_KOMMENTARE, /\.wks-gewaehr\{[^']*border:2px solid/) + const kastenCode = OHNE_KOMMENTARE.slice(OHNE_KOMMENTARE.indexOf('function gewaehrKasten'), OHNE_KOMMENTARE.indexOf('function honigfeld')) + assert.ok(kastenCode.includes("img.alt = g.text") && !kastenCode.includes('lazy'), 'Pflichtbild nicht verzögert laden, Alt-Text = voller Text') + assert.ok(OHNE_KOMMENTARE.includes("img.loading = 'eager'")) + assert.ok(OHNE_KOMMENTARE.includes("'Vergrößern'"), 'Knopf') + assert.match(OHNE_KOMMENTARE, /dialogOeffnen\('Ihre gesetzlichen Gewährleistungsrechte', \[\{ bild: bildUrl, text: g\.text \}\]/) + assert.match(OHNE_KOMMENTARE, /e\.key === 'Escape'/, 'Esc schließt') + assert.ok(OHNE_KOMMENTARE.includes('knopf(\'wks-schliessen\', \'×\', dialogSchliessen)'), 'Schließen-Knopf') + // Reihenfolge in der Kasse: Kasten -> Zusammenfassung -> AGB-Hinweis -> Knopf + const i = (t) => OHNE_KOMMENTARE.indexOf(t) + const kasten = i('form.appendChild(gkasten)') + const pflicht = i('form.appendChild(pflicht)') + const hinweis = i('form.appendChild(agbHinweis)') + const knopf = i("var senden = el('button', 'wks-btn', 'Zahlungspflichtig bestellen')") + assert.ok(kasten > 0 && kasten < pflicht && pflicht < hinweis && hinweis < knopf, 'Reihenfolge') + // nicht mehr in der Zusammenfassung (die wird bei jeder Änderung neu aufgebaut) + const fuelle = OHNE_KOMMENTARE.slice(i('function fuellePflicht'), i('function landName')) + assert.ok(!fuelle.includes('gewaehr'), 'Gewährleistung nicht in fuellePflicht') +}) + +test('I-3: ausdrücklicher AGB-Hinweis direkt über dem Bestellknopf, AGB und Widerrufsbelehrung öffnen die Texte', () => { + const stelle = OHNE_KOMMENTARE.slice(OHNE_KOMMENTARE.indexOf("var agbHinweis = el('p'"), OHNE_KOMMENTARE.indexOf("var senden = el('button'")) + assert.ok(stelle.includes("createTextNode('Es gelten unsere ')")) + assert.ok(stelle.includes("knopf('wks-link', 'AGB', function (e) { zeigeAgb(e.currentTarget) })")) + assert.ok(stelle.includes("createTextNode('. Informationen zu Ihrem Widerrufsrecht finden Sie in der ')")) + assert.ok(stelle.includes("knopf('wks-link', 'Widerrufsbelehrung', function (e) { zeigeBelehrung(e.currentTarget) })")) + assert.ok(stelle.includes("createTextNode('.')")) +}) + test('Weiterleitung zu Stripe nur zu einer https-Adresse (httpsUrl), Links der Rechtstexte nur als https', () => { assert.match(OHNE_KOMMENTARE, /var ziel = d \? httpsUrl\(d\.url\) : ''/) assert.match(OHNE_KOMMENTARE, /location\.href = ziel/)