Closed BrunoAFK closed 5 months ago
Describe the bug TypeError: Cannot read properties of null (reading 'href') at /admin/:170:44 at Array.map () at fromBlock (/admin/:168:87) at s. (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:54593) at s.tokenizeBlock (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:485:2582080) at e.exports [as parse] (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:485:2558013) at e.exports.w.parse (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:491:39920) at C (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:44350) at t.markdownToSlate (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:43534) at j (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:473:9761)
To Reproduce I don't know how to reproduce and what is triggering this.
Applicable Versions:
decap-cms@3.1.10
github
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
CMS configuration
backend: name: github repo: repo/site branch: main base_url: https://url.com auth_endpoint: /api/auth media_folder: static/images public_folder: /images display_url: https://url.com search: true editor: preview: false collections: - name: novosti label: Novosti folder: content/novosti create: true slug: "{{slug}}" summary: "{{title}} - {{date | date('DD.MM.YYYY')}} – {{body | truncate(30, '...')}}" filter: field: format value: post media_folder: /static/images/articles public_folder: /images/articles view_groups: - label: Year field: date pattern: \d{4} id: date__\d{4} fields: - label: Format name: format widget: hidden default: post - label: Naslov name: title hint: Naslov članka widget: string - label: Autor name: author widget: hidden - label: Gallery Mode name: fancybox widget: hidden default: "true" - label: Datum objave name: date widget: datetime format: YYYY-MM-DD - label: Opis name: description hint: Opis članka, bit će vidljiv na listi članaka widget: string - label: Slika članka name: thumbnail hint: Slika članka, bit će vidljiva na listi članaka. Dimenzije trebaju biti 500 x 231 (idealno). widget: image default: /images/default/feature.jpg - label: Istaknuta slika članka name: featuredImage hint: Glavna slika članka, idealne dimenzije su 1280x853. Prikazuje se unutar članka. widget: image required: false default: /images/default/feature.jpg - label: Tagovi name: tags widget: hidden default: - novosti allow_add: true - label: Sadržaj name: body widget: markdown publish: true type: folder_based_collection sortable_fields: - commit_date - title - date - commit_author - description view_filters: [] editor: preview: false - name: drustva label: Društva folder: content/drustva create: true slug: "{{slug}}" filter: field: format value: post fields: - label: Format name: format widget: hidden default: post - label: Naslov name: title hint: Naziv društva widget: string - label: Grb drutšva name: thumbnail hint: Grb društva, bit će vidljiva na listi društava. Dimenzije trebaju biti 500 x 231 (idealno). widget: image default: /images/default/grb-vatrogasci.png - label: Istaknuta slika društva name: featuredImage hint: Glavna slika stranice društva, idealne dimenzije su 1280x853. Prikazuje se unutar stranice društva. widget: image required: false - label: Sadržaj name: body widget: markdown publish: true type: folder_based_collection sortable_fields: - commit_date - title - commit_author view_filters: [] view_groups: [] editor: preview: false - name: nabava label: Nabava folder: content/nabava create: true slug: "{{slug}}" summary: "{{title | upper}} - {{date | date('DD.MM.YYYY')}} – {{body | truncate(30, '...')}}" filter: field: format value: post view_groups: - label: Yea ... index.html ... <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Content Manager</title> </head> <body> <script src="https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js"></script> <script> CMS.registerEditorComponent({ id: "youtube", label: "Youtube", fields: [{name: 'id', label: 'Youtube Video ID'}], pattern: /^{{<\s?youtube (\S+)\s?>}}/, fromBlock: function(match) { return { id: match[1] }; }, toBlock: function(obj) { return ` <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; height: auto;"> <iframe src="https://www.youtube.com/embed/${obj.id}?rel=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> </div> `; }, toPreview: function(obj) { return ` <div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden; max-width: 100%; height: auto;"> <iframe src="https://www.youtube.com/embed/${obj.id}?rel=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe> </div> `; } }); CMS.registerEditorComponent({ id: "position-list", label: "Lista pozicija", fields: [ { name: 'items', label: 'Items', widget: 'list', fields: [ { name: 'positionName', label: 'Naziv Pozicije' }, // positionName for the position { name: 'personName', label: 'Ime i prezime' } // personName for the person ] } ], pattern: /^<div class="object-list">([\s\S]*?)<\/div>$/, fromBlock: function(match) { const html = match[1]; // The captured group which is inner HTML of the object-list div const dummyDiv = document.createElement('div'); dummyDiv.innerHTML = html; const items = Array.from(dummyDiv.querySelectorAll('.object-item')).map((div) => { return { positionName: div.querySelector('.object-position').innerText, personName: div.querySelector('.object-name').innerText }; }); return { items: items }; }, toBlock: function(obj) { const itemsHtml = obj.items.map(item => `<div class="object-item"><div class="object-position">${item.positionName}</div><div class="object-name">${item.personName}</div></div>` ).join(""); return `<div class="object-list">${itemsHtml}</div>`; } }); CMS.registerEditorComponent({ id: "contact-list", label: "Kontakti Lista", fields: [ { name: 'contacts', label: 'Kontakti', widget: 'list', fields: [ { label: 'Naziv', name: 'title', widget: 'string' }, { label: 'Titula / Jedinica', name: 'subtitle', widget: 'string', required: false }, { label: 'Adresa', name: 'address', widget: 'string', required: false }, { label: 'OIB', name: 'oib', widget: 'number', hint: 'OIB 11 znamenki', pattern: ['^\\d{11}$', 'Mora biti točno 11 znamenki'], required: false }, { label: 'MB', name: 'mb', widget: 'number', hint: 'MB ima 7 znamenki', pattern: ['^\\d{7}$', 'Mora biti točno 7 znamenki'], required: false }, { label: 'IBAN', name: 'iban', widget: 'string', hint: 'Unesite IBAN', pattern: ['^[A-Z]{2}(\\s*\\d\\s*){19}$', 'Mora započeti s dva slova i sadržavati ukupno 19 znamenki'], required: false }, { label: 'Telefon', name: 'phone', widget: 'string', required: false }, { label: 'Email', name: 'email', widget: 'string', pattern: ['^[\\w.%+-]+@[\\w.-]+\\.[a-zA-Z]{2,}$', 'Unesite važeću email adresu'], required: false }, { label: 'Dodatna Informacija', name: 'extraInfo', widget: 'text', required: false } ] } ], pattern: /^<div class="contact-list">([\s\S]*?)<\/div>$/, fromBlock: function(match) { const html = match[1]; const dummyDiv = document.createElement('div'); dummyDiv.innerHTML = html; const contacts = Array.from(dummyDiv.querySelectorAll('.contact-card')).map((div) => { return { title: div.querySelector('.contact-title').innerText, subtitle: div.querySelector('.contact-subtitle') ? div.querySelector('.contact-subtitle').innerText : '', address: div.querySelector('.contact-address') ? div.querySelector('.contact-address').innerText.replace('Adresa: ', '') : '', oib: div.querySelector('.contact-oib') ? div.querySelector('.contact-oib').innerText.replace('OIB: ', '') : '', mb: div.querySelector('.contact-mb') ? div.querySelector('.contact-mb').innerText.replace('MB: ', '') : '', iban: div.querySelector('.contact-iban') ? div.querySelector('.contact-iban').innerText.replace('IBAN: ', '') : '', phone: div.querySelector('.contact-phone a') ? div.querySelector('.contact-phone a').innerText : '', email: div.querySelector('.contact-email a') ? div.querySelector('.contact-email a').innerText : '', extraInfo: div.querySelector('.contact-extra-info') ? div.querySelector('.contact-extra-info').innerText : '' }; }); return { contacts: contacts }; }, toBlock: function(obj) { const contactsHtml = obj.contacts.map(contact => `<div class="contact-card"> <div class="title-area"> <h2 class="contact-title">${contact.title}</h2> ${contact.subtitle ? `<p class="contact-subtitle">${contact.subtitle}</p>` : ''} </div> ${contact.address ? `<p class="contact-address"><span>Adresa: </span>${contact.address}</p>` : ''} ${contact.oib ? `<p class="contact-oib"><span>OIB: </span>${contact.oib}</p>` : ''} ${contact.mb ? `<p class="contact-mb"><span>MB: </span>${contact.mb}</p>` : ''} ${contact.iban ? `<p class="contact-iban"><span>IBAN: </span>${contact.iban}</p>` : ''} ${contact.phone ? `<p class="contact-phone"><span>Telefon: </span><a href="tel:${contact.phone}">${contact.phone}</a></p>` : ''} ${contact.email ? `<p class="contact-email"><span>Email: </span><a href="mailto:${contact.email}">${contact.email}</a></p>` : ''} ${contact.extraInfo ? `<p class="contact-extra-info">${contact.extraInfo}</p>` : ''} </div>` ).join(""); return `<div class="contact-list">${contactsHtml}</div>`; }, toPreview: function(obj) { const contactsHtml = obj.contacts.map(contact => `<div class="contact-card"> <div class="title-area"> <h2 class="contact-title">${contact.title}</h2> ${contact.subtitle ? `<p class="contact-subtitle">${contact.subtitle}</p>` : ''} </div> ${contact.address ? `<p class="contact-address"><span>Adresa: </span>${contact.address}</p>` : ''} ${contact.oib ? `<p class="contact-oib"><span>OIB: </span>${contact.oib}</p>` : ''} ${contact.mb ? `<p class="contact-mb"><span>MB: </span>${contact.mb}</p>` : ''} ${contact.iban ? `<p class="contact-iban"><span>IBAN: </span>${contact.iban}</p>` : ''} ${contact.phone ? `<p class="contact-phone"><span>Telefon: </span><a href="tel:${contact.phone}">${contact.phone}</a></p>` : ''} ${contact.email ? `<p class="contact-email"><span>Email: </span><a href="mailto:${contact.email}">${contact.email}</a></p>` : ''} ${contact.extraInfo ? `<p class="contact-extra-info">${contact.extraInfo}</p>` : ''} </div>` ).join(""); return `<div class="contact-list">${contactsHtml}</div>`; } }); CMS.registerEditorComponent({ id: "gallery", label: "Galerija", fields: [ { name: 'fancybox', label: 'Ime galerije', hint: "Naziv galerije, nije obavezan. Ako želite imati više galerija na stranici a da su odvojene prilikom pregleda slika, onda je poželjno staviti.", widget: 'string', required: false }, { name: 'gallery', label: 'Galerija', widget: 'list', fields: [ { name: 'image', label: 'Slika', widget: 'image' }, { name: 'alt', label: 'Alt tekst', widget: 'string', required: false } ] } ], pattern: /^<div class="gallery">([\s\S]*?)<\/div>$/, fromBlock: function(match) { const html = match[1]; const dummyDiv = document.createElement('div'); dummyDiv.innerHTML = html; const galleryItems = Array.from(dummyDiv.querySelectorAll('.gallery-item')).map((div) => { return { image: div.querySelector('a').href, alt: div.querySelector('img').alt }; }); return { fancybox: dummyDiv.querySelector('.gallery-item') ? dummyDiv.querySelector('.gallery-item').getAttribute('data-fancybox') : 'gallery', gallery: galleryItems }; }, toBlock: function(obj) { const fancybox = obj.fancybox || 'gallery'; const galleryHtml = obj.gallery.map(item => `<a href="${item.image}" data-fancybox="${fancybox}" data-caption="${item.alt}" class="gallery-item"> <img src="${item.image}" alt="${item.alt}" class="rounded shadow-md mb-4" /> </a>` ).join(""); return `<div class="gallery"> ${galleryHtml} </div>`; }, toPreview: function(obj) { const fancybox = obj.fancybox || 'gallery'; const galleryHtml = obj.gallery.map(item => `<a href="${item.image}" data-fancybox="${fancybox}" data-caption="${item.alt}" class="gallery-item"> <img src="${item.image}" alt="${item.alt}" class="rounded shadow-md mb-4" /> </a>` ).join(""); return `<div class="gallery"> ${galleryHtml} </div>`; } }); </script> </body> </html> ...
Issue is closed; I made a mistake when configuring fromBlock in the gallery.
Describe the bug TypeError: Cannot read properties of null (reading 'href') at /admin/:170:44 at Array.map () at fromBlock (/admin/:168:87) at s. (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:54593) at s.tokenizeBlock (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:485:2582080) at e.exports [as parse] (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:485:2558013) at e.exports.w.parse (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:491:39920) at C (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:44350) at t.markdownToSlate (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:477:43534) at j (https://unpkg.com/decap-cms@3.1.10/dist/decap-cms.js:473:9761)
To Reproduce I don't know how to reproduce and what is triggering this.
Applicable Versions:
decap-cms@3.1.10
github
Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36
CMS configuration