From 69227a611f6ed04e4db6b77df4fa44fedb8fa67d Mon Sep 17 00:00:00 2001 From: uldisrudzitis Date: Thu, 8 Oct 2026 16:19:40 +0300 Subject: [PATCH 1/3] Fix editor preview loading --- .../src/app/preview/preview.component.spec.ts | 126 ++++++++++++++++++ editor/src/app/preview/preview.component.ts | 22 ++- 2 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 editor/src/app/preview/preview.component.spec.ts diff --git a/editor/src/app/preview/preview.component.spec.ts b/editor/src/app/preview/preview.component.spec.ts new file mode 100644 index 00000000..b62d530d --- /dev/null +++ b/editor/src/app/preview/preview.component.spec.ts @@ -0,0 +1,126 @@ +import { fakeAsync, tick } from '@angular/core/testing'; +import { Store } from '@ngxs/store'; +import { of } from 'rxjs'; + +import { PreviewComponent } from './preview.component'; + +/** + * Covers `waitFullLoad`, which decides when a loaded preview document is + * ready for the editor services to attach to. Only the store's `isSetup` + * selection is used, so every other dependency is an empty stub. + */ +describe('PreviewComponent (waitFullLoad)', () => { + const EDITOR_URL = 'https://example.com/engine/editor/?section=works'; + + let component: PreviewComponent; + + beforeEach(() => { + const store = { select: () => of(false) } as unknown as Store; + component = new PreviewComponent( + {} as any, + store, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + {} as any, + ); + }); + + function fakeDocument(href: string, bodyClass: string | null): Document { + return { + location: { href }, + body: bodyClass === null ? null : { className: bodyClass }, + } as unknown as Document; + } + + function fakeIframe(doc: Document | null) { + return { contentDocument: doc } as { contentDocument: Document | null }; + } + + function watch(iframe: { contentDocument: Document | null }) { + const result = { + emitted: [] as unknown[], + completed: false, + error: undefined as unknown, + }; + + (component as any).waitFullLoad(iframe).subscribe({ + next: (value: unknown) => result.emitted.push(value), + error: (error: unknown) => (result.error = error), + complete: () => (result.completed = true), + }); + + return result; + } + + it('emits the iframe once the body has Berta classes', fakeAsync(() => { + const iframe = fakeIframe(fakeDocument(EDITOR_URL, 'xContent-works')); + const result = watch(iframe); + + tick(100); + + expect(result.emitted).toEqual([iframe]); + expect(result.completed).toBeTrue(); + })); + + it('emits right away for the login page', fakeAsync(() => { + const iframe = fakeIframe( + fakeDocument('https://example.com/engine/', null), + ); + const result = watch(iframe); + + tick(100); + + expect(result.emitted).toEqual([iframe]); + })); + + it('keeps waiting while the document has no body yet', fakeAsync(() => { + const doc = fakeDocument(EDITOR_URL, null); + const iframe = fakeIframe(doc); + const result = watch(iframe); + + tick(500); + + expect(result.emitted).toEqual([]); + expect(result.completed).toBeFalse(); + + (doc as any).body = { className: 'xSectionType-default' }; + tick(100); + + expect(result.emitted).toEqual([iframe]); + })); + + it('errors once the body never appears', fakeAsync(() => { + const result = watch(fakeIframe(fakeDocument(EDITOR_URL, null))); + + tick(12000); + + expect(result.emitted).toEqual([]); + expect(result.error).toBeDefined(); + })); + + it('completes without emitting once a newer navigation replaces the document', fakeAsync(() => { + const iframe = fakeIframe(fakeDocument('about:blank', '')); + const result = watch(iframe); + + tick(100); + expect(result.completed).toBeFalse(); + + // A freshly committed document: nothing parsed yet. + iframe.contentDocument = fakeDocument(EDITOR_URL, null); + tick(100); + + expect(result.emitted).toEqual([]); + expect(result.completed).toBeTrue(); + + // Even once the new document is ready, it's left to its own `load`. + (iframe.contentDocument as any).body = { className: 'xContent-works' }; + tick(500); + + expect(result.emitted).toEqual([]); + })); +}); diff --git a/editor/src/app/preview/preview.component.ts b/editor/src/app/preview/preview.component.ts index defc3c61..de223184 100644 --- a/editor/src/app/preview/preview.component.ts +++ b/editor/src/app/preview/preview.component.ts @@ -410,7 +410,19 @@ export class PreviewComponent implements OnInit { observer.complete(); }); + // The document this `load` event is for. A later navigation replaces it + // with a new document that fires its own `load` (and wait), so this one + // must not resolve against it — the new document may not even have an + // element parsed yet. + const loadedDocument = iframe.contentDocument; + checkInterval = setInterval(() => { + if (iframe.contentDocument !== loadedDocument) { + clearInterval(checkInterval); + observer.complete(); + return; + } + const lastUrlPart = iframe.contentDocument?.location.href .replace(/\/$/, '') .split('/') @@ -438,12 +450,12 @@ export class PreviewComponent implements OnInit { return; } + // No body yet means the page is still being parsed. + const body = iframe.contentDocument.body; + if ( - iframe.contentDocument.body && - (iframe.contentDocument.body.classList.length === 0 || - !/(xLoginPageBody|xContent-|xSectionType-)/.test( - iframe.contentDocument.body.className, - )) + !body || + !/(xLoginPageBody|xContent-|xSectionType-)/.test(body.className) ) { lastError = 'Berta classes `xLoginPageBody` or `xContent-[]` or `xSectionType-` are missing from body element'; From cbd15ded9b250f5620ca0eadf6d107fbcdc4c185 Mon Sep 17 00:00:00 2001 From: uldisrudzitis Date: Thu, 8 Oct 2026 16:31:07 +0300 Subject: [PATCH 2/3] Stop removing the "create new entry" link after preview rerenders After replacing preview content, the editor removed the last .xCreateNewEntry in the page before dispatching addEntry. BertaEditor already replaces the link after the entries list on addEntry, so the removal was redundant, and it threw on pages without any such link (no entries list, messy grid view), which stopped addEntry from being dispatched. It also deleted the last entry's own link when the rerender ran before BertaEditor had added the one after the list. Rerenders now only dispatch addEntry. Co-Authored-By: Claude Opus 5.5 --- .../rerender/common/page-layout.service.ts | 7 +- .../default-template-rerender.service.ts | 9 +- .../template-rerender.service.spec.ts | 92 +++++++++++++++++++ .../app/rerender/template-rerender.service.ts | 13 +-- editor/src/app/rerender/utilities/content.ts | 14 +-- 5 files changed, 104 insertions(+), 31 deletions(-) create mode 100644 editor/src/app/rerender/template-rerender.service.spec.ts diff --git a/editor/src/app/rerender/common/page-layout.service.ts b/editor/src/app/rerender/common/page-layout.service.ts index d9068274..1410be0a 100644 --- a/editor/src/app/rerender/common/page-layout.service.ts +++ b/editor/src/app/rerender/common/page-layout.service.ts @@ -1,8 +1,5 @@ import { Injectable } from '@angular/core'; -import { - removeExtraAddBtnAndAddListeners, - replaceContent, -} from '../utilities/content'; +import { reloadBackendJs, replaceContent } from '../utilities/content'; @Injectable({ providedIn: 'root', @@ -36,7 +33,7 @@ export class PageLayoutService { replaceContent(dom, 'pageEntries', viewData.entries); replaceContent(dom, 'portfolioThumbnails', viewData.portfolioThumbnails); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); } protected static handleIsResponsiveSetting( diff --git a/editor/src/app/rerender/default-template-rerender.service.ts b/editor/src/app/rerender/default-template-rerender.service.ts index 89bf2f0b..df0e0c69 100644 --- a/editor/src/app/rerender/default-template-rerender.service.ts +++ b/editor/src/app/rerender/default-template-rerender.service.ts @@ -5,10 +5,7 @@ import { UpdateNavigationSiteSettingsAction } from '../sites/settings/site-setti import { HandleSiteTemplateSettingsAction } from '../sites/template-settings/site-template-settings.actions'; import { TemplateRerenderService } from './template-rerender.service'; import { SiteSettingChildrenHandler } from './types/components'; -import { - replaceContent, - removeExtraAddBtnAndAddListeners, -} from './utilities/content'; +import { reloadBackendJs, replaceContent } from './utilities/content'; @Injectable({ providedIn: 'root', @@ -108,12 +105,12 @@ export class DefaultTemplateRerenderService extends TemplateRerenderService { replaceContent(dom, 'siteBanners', viewData.siteBanners); replaceContent(dom, 'sectionFooter', viewData.sectionFooter); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); } else if (action.settingGroup === 'pageHeading') { replaceContent(dom, 'siteHeader', viewData.siteHeader); } else if (action.settingGroup === 'entryLayout') { replaceContent(dom, 'pageEntries', viewData.entries); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); } else if (action.settingGroup === 'css') { DefaultTemplateRerenderService.handleCssDesignSettingChange( dom, diff --git a/editor/src/app/rerender/template-rerender.service.spec.ts b/editor/src/app/rerender/template-rerender.service.spec.ts new file mode 100644 index 00000000..c63b20cb --- /dev/null +++ b/editor/src/app/rerender/template-rerender.service.spec.ts @@ -0,0 +1,92 @@ +import { Actions, ActionStatus } from '@ngxs/store'; +import { Subject } from 'rxjs'; + +import { TemplateRerenderService } from './template-rerender.service'; +import { TemplateRenderService } from '../render/template-render.service'; +import { AddSectionEntryFromSyncAction } from '../sites/sections/entries/entries-state/section-entries.actions'; +import { HandleSiteSettingsChildrenChangesAction } from '../sites/settings/site-settings.actions'; +import { SiteSettingChildrenHandler } from './types/components'; + +describe('TemplateRerenderService', () => { + const ENTRY = + '
  • entry
  • '; + + let doc: Document; + let iframe: HTMLIFrameElement; + let dispatchEvent: jasmine.Spy; + let actions$: Subject<{ action: unknown; status: ActionStatus }>; + let viewData: Record; + let service: TemplateRerenderService; + + function succeed(action: unknown) { + actions$.next({ action, status: ActionStatus.Successful }); + } + + function dispatchedEvents(): string[] { + return dispatchEvent.calls.allArgs().map(([event]) => event.type); + } + + beforeEach(() => { + doc = document.implementation.createHTMLDocument(''); + dispatchEvent = jasmine.createSpy('dispatchEvent'); + iframe = { + contentDocument: doc, + contentWindow: { dispatchEvent }, + } as unknown as HTMLIFrameElement; + actions$ = new Subject(); + viewData = {}; + service = new TemplateRerenderService( + { getViewData: () => viewData } as unknown as TemplateRenderService, + actions$ as unknown as Actions, + ); + }); + + describe('entry creation', () => { + const created = new AddSectionEntryFromSyncAction('', 'works', { + tag: null, + before_entry: null, + }); + + beforeEach(() => { + viewData.entries = ENTRY + ENTRY; + viewData.portfolioThumbnails = ''; + service.handleEntryCreationRerender(iframe); + }); + + it('rerenders a page that has no "create new entry" link yet', () => { + doc.body.innerHTML = '
      '; + + succeed(created); + + expect(doc.querySelectorAll('#pageEntries .xEntry').length).toBe(2); + expect(dispatchedEvents()).toEqual(['addEntry']); + }); + + it('keeps the "create new entry" link of every entry', () => { + doc.body.innerHTML = '
        '; + + succeed(created); + + expect(doc.querySelectorAll('.xEntry .xCreateNewEntry').length).toBe(2); + }); + + it('rerenders a page without an entries list', () => { + succeed(created); + + expect(dispatchedEvents()).toEqual(['addEntry']); + }); + }); + + it('rerenders site settings on a page without entries', () => { + doc.body.innerHTML = '
        '; + viewData.siteBanners = ''; + service.handleSiteSettingChildrenHandleRerender(iframe, { + banners: { id: 'siteBanners', dataKey: 'siteBanners' }, + } as SiteSettingChildrenHandler); + + succeed(new HandleSiteSettingsChildrenChangesAction('banners')); + + expect(doc.querySelector('#siteBanners .banner')).not.toBeNull(); + expect(dispatchedEvents()).toEqual(['addEntry']); + }); +}); diff --git a/editor/src/app/rerender/template-rerender.service.ts b/editor/src/app/rerender/template-rerender.service.ts index 0dccb7e8..d971d30f 100644 --- a/editor/src/app/rerender/template-rerender.service.ts +++ b/editor/src/app/rerender/template-rerender.service.ts @@ -33,10 +33,7 @@ import { UpdateSiteSettingsAction, } from '../sites/settings/site-settings.actions'; import { Component, SiteSettingChildrenHandler } from './types/components'; -import { - removeExtraAddBtnAndAddListeners, - replaceContent, -} from './utilities/content'; +import { reloadBackendJs, replaceContent } from './utilities/content'; import { HandleSiteTemplateSettingsAction, ResetToDefaultsSiteTemplateSettingsAction, @@ -260,7 +257,7 @@ export class TemplateRerenderService { viewData.portfolioThumbnails, ); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); }); } @@ -272,7 +269,7 @@ export class TemplateRerenderService { replaceContent(iframe.contentDocument, 'pageEntries', viewData.entries); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); }); } @@ -319,7 +316,7 @@ export class TemplateRerenderService { viewData.portfolioThumbnails, ); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); }); } @@ -350,7 +347,7 @@ export class TemplateRerenderService { replaceContent(iframe.contentDocument, comp.id, viewData[comp.dataKey]), ); - removeExtraAddBtnAndAddListeners(iframe); + reloadBackendJs(iframe); } public unsubscribe(win: Window, subList: Subscription[]): void { diff --git a/editor/src/app/rerender/utilities/content.ts b/editor/src/app/rerender/utilities/content.ts index fb0e6a78..d63a168a 100644 --- a/editor/src/app/rerender/utilities/content.ts +++ b/editor/src/app/rerender/utilities/content.ts @@ -11,18 +11,8 @@ export function replaceContent( element.appendChild(dom.createRange().createContextualFragment(sectionHtml)); } -export function removeExtraAddBtnAndAddListeners( - iframe: HTMLIFrameElement, -): void { - // remove extra 'create entry' button due to backend js reload - const createEntriesList = - iframe.contentDocument.getElementsByClassName('xCreateNewEntry'); - createEntriesList[createEntriesList.length - 1].remove(); - - reloadBackendJs(iframe); -} - export function reloadBackendJs(iframe: HTMLIFrameElement) { - // reload backend js + // BertaEditor re-binds the entry listeners and replaces the "create new entry" + // link after the entries list iframe.contentWindow.dispatchEvent(new Event('addEntry')); } From d73034ca6f15fdda1adb8f609c5b72c310a303e6 Mon Sep 17 00:00:00 2001 From: uldisrudzitis Date: Thu, 8 Oct 2026 16:39:51 +0300 Subject: [PATCH 3/3] Rename the preview "addEntry" event to "entriesRerendered" The event fires after every entries rerender (entry changes, settings, page layout), not only when an entry is added, and the function that dispatched it didn't reload anything. Name both after what happens, matching sitesMenuRerendered and sectionsMenuRerendered. Co-Authored-By: Claude Opus 5.5 --- editor/src/app/rerender/common/page-layout.service.ts | 4 ++-- .../app/rerender/default-template-rerender.service.ts | 6 +++--- editor/src/app/rerender/mashup/page-layout.service.ts | 4 ++-- .../app/rerender/template-rerender.service.spec.ts | 6 +++--- editor/src/app/rerender/template-rerender.service.ts | 10 +++++----- editor/src/app/rerender/utilities/content.ts | 9 +++++---- engine/js/BertaEditor.js | 8 ++++---- engine/js/BertaPortfolio.js | 11 +++++++---- 8 files changed, 31 insertions(+), 27 deletions(-) diff --git a/editor/src/app/rerender/common/page-layout.service.ts b/editor/src/app/rerender/common/page-layout.service.ts index 1410be0a..b6aded95 100644 --- a/editor/src/app/rerender/common/page-layout.service.ts +++ b/editor/src/app/rerender/common/page-layout.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { reloadBackendJs, replaceContent } from '../utilities/content'; +import { notifyEntriesRerendered, replaceContent } from '../utilities/content'; @Injectable({ providedIn: 'root', @@ -33,7 +33,7 @@ export class PageLayoutService { replaceContent(dom, 'pageEntries', viewData.entries); replaceContent(dom, 'portfolioThumbnails', viewData.portfolioThumbnails); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); } protected static handleIsResponsiveSetting( diff --git a/editor/src/app/rerender/default-template-rerender.service.ts b/editor/src/app/rerender/default-template-rerender.service.ts index df0e0c69..735a94bf 100644 --- a/editor/src/app/rerender/default-template-rerender.service.ts +++ b/editor/src/app/rerender/default-template-rerender.service.ts @@ -5,7 +5,7 @@ import { UpdateNavigationSiteSettingsAction } from '../sites/settings/site-setti import { HandleSiteTemplateSettingsAction } from '../sites/template-settings/site-template-settings.actions'; import { TemplateRerenderService } from './template-rerender.service'; import { SiteSettingChildrenHandler } from './types/components'; -import { reloadBackendJs, replaceContent } from './utilities/content'; +import { notifyEntriesRerendered, replaceContent } from './utilities/content'; @Injectable({ providedIn: 'root', @@ -105,12 +105,12 @@ export class DefaultTemplateRerenderService extends TemplateRerenderService { replaceContent(dom, 'siteBanners', viewData.siteBanners); replaceContent(dom, 'sectionFooter', viewData.sectionFooter); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); } else if (action.settingGroup === 'pageHeading') { replaceContent(dom, 'siteHeader', viewData.siteHeader); } else if (action.settingGroup === 'entryLayout') { replaceContent(dom, 'pageEntries', viewData.entries); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); } else if (action.settingGroup === 'css') { DefaultTemplateRerenderService.handleCssDesignSettingChange( dom, diff --git a/editor/src/app/rerender/mashup/page-layout.service.ts b/editor/src/app/rerender/mashup/page-layout.service.ts index 1be56ad4..42c74c31 100644 --- a/editor/src/app/rerender/mashup/page-layout.service.ts +++ b/editor/src/app/rerender/mashup/page-layout.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { reloadBackendJs, replaceContent } from '../utilities/content'; +import { notifyEntriesRerendered, replaceContent } from '../utilities/content'; import { createEl } from '../utilities/element'; import { PageLayoutService as MainPageLayoutService } from '../common/page-layout.service'; @@ -50,7 +50,7 @@ export class PageLayoutService extends MainPageLayoutService { PageLayoutService.replaceCommonContent(dom, viewData); replaceContent(dom, 'mashupEntries', viewData.mashupEntries); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); } private static handleMainColumn( diff --git a/editor/src/app/rerender/template-rerender.service.spec.ts b/editor/src/app/rerender/template-rerender.service.spec.ts index c63b20cb..f46f15b4 100644 --- a/editor/src/app/rerender/template-rerender.service.spec.ts +++ b/editor/src/app/rerender/template-rerender.service.spec.ts @@ -59,7 +59,7 @@ describe('TemplateRerenderService', () => { succeed(created); expect(doc.querySelectorAll('#pageEntries .xEntry').length).toBe(2); - expect(dispatchedEvents()).toEqual(['addEntry']); + expect(dispatchedEvents()).toEqual(['entriesRerendered']); }); it('keeps the "create new entry" link of every entry', () => { @@ -73,7 +73,7 @@ describe('TemplateRerenderService', () => { it('rerenders a page without an entries list', () => { succeed(created); - expect(dispatchedEvents()).toEqual(['addEntry']); + expect(dispatchedEvents()).toEqual(['entriesRerendered']); }); }); @@ -87,6 +87,6 @@ describe('TemplateRerenderService', () => { succeed(new HandleSiteSettingsChildrenChangesAction('banners')); expect(doc.querySelector('#siteBanners .banner')).not.toBeNull(); - expect(dispatchedEvents()).toEqual(['addEntry']); + expect(dispatchedEvents()).toEqual(['entriesRerendered']); }); }); diff --git a/editor/src/app/rerender/template-rerender.service.ts b/editor/src/app/rerender/template-rerender.service.ts index d971d30f..fb4e17b2 100644 --- a/editor/src/app/rerender/template-rerender.service.ts +++ b/editor/src/app/rerender/template-rerender.service.ts @@ -33,7 +33,7 @@ import { UpdateSiteSettingsAction, } from '../sites/settings/site-settings.actions'; import { Component, SiteSettingChildrenHandler } from './types/components'; -import { reloadBackendJs, replaceContent } from './utilities/content'; +import { notifyEntriesRerendered, replaceContent } from './utilities/content'; import { HandleSiteTemplateSettingsAction, ResetToDefaultsSiteTemplateSettingsAction, @@ -257,7 +257,7 @@ export class TemplateRerenderService { viewData.portfolioThumbnails, ); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); }); } @@ -269,7 +269,7 @@ export class TemplateRerenderService { replaceContent(iframe.contentDocument, 'pageEntries', viewData.entries); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); }); } @@ -316,7 +316,7 @@ export class TemplateRerenderService { viewData.portfolioThumbnails, ); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); }); } @@ -347,7 +347,7 @@ export class TemplateRerenderService { replaceContent(iframe.contentDocument, comp.id, viewData[comp.dataKey]), ); - reloadBackendJs(iframe); + notifyEntriesRerendered(iframe); } public unsubscribe(win: Window, subList: Subscription[]): void { diff --git a/editor/src/app/rerender/utilities/content.ts b/editor/src/app/rerender/utilities/content.ts index d63a168a..31443ce4 100644 --- a/editor/src/app/rerender/utilities/content.ts +++ b/editor/src/app/rerender/utilities/content.ts @@ -11,8 +11,9 @@ export function replaceContent( element.appendChild(dom.createRange().createContextualFragment(sectionHtml)); } -export function reloadBackendJs(iframe: HTMLIFrameElement) { - // BertaEditor re-binds the entry listeners and replaces the "create new entry" - // link after the entries list - iframe.contentWindow.dispatchEvent(new Event('addEntry')); +export function notifyEntriesRerendered(iframe: HTMLIFrameElement) { + // The preview scripts re-init the replaced entries: BertaEditor re-binds the + // entry listeners and replaces the "create new entry" link after the list, + // BertaPortfolio re-binds the thumbnail links + iframe.contentWindow.dispatchEvent(new Event('entriesRerendered')); } diff --git a/engine/js/BertaEditor.js b/engine/js/BertaEditor.js index 9bd582b5..5d52ca88 100644 --- a/engine/js/BertaEditor.js +++ b/engine/js/BertaEditor.js @@ -13,7 +13,7 @@ var BertaEditor = class { // Bound once: onDOMReadyDo runs again after every re-render and adds these // listeners again, adding the same function twice is a no-op this.onDOMReadyDo = this.onDOMReadyDo.bind(this); - this.onAddEntry = this.onAddEntry.bind(this); + this.onEntriesRerendered = this.onEntriesRerendered.bind(this); this.onBgEditClick = this.onBgEditClick.bind(this); this.onGalleryEditClick = this.onGalleryEditClick.bind(this); this.entryCreate = this.entryCreate.bind(this); @@ -24,13 +24,13 @@ var BertaEditor = class { window.addEventListener(e, this.onDOMReadyDo); }); - window.addEventListener("addEntry", this.onAddEntry); + window.addEventListener("entriesRerendered", this.onEntriesRerendered); window.BertaHelpers.onDomReady(this.onDOMReady.bind(this)); } - onAddEntry() { - // after adding entry sync state + onEntriesRerendered() { + // the entries were replaced after a state change, sync the state window.redux_store.dispatch(Actions.getState(window.getCurrentSite())); this.onDOMReadyDo(); } diff --git a/engine/js/BertaPortfolio.js b/engine/js/BertaPortfolio.js index b1435f76..b24643e7 100644 --- a/engine/js/BertaPortfolio.js +++ b/engine/js/BertaPortfolio.js @@ -5,11 +5,14 @@ var BertaPortfolio = class { } else { this.onDOMReady(); } - window.addEventListener('addEntry', this.onAddPortfolio.bind(this)); + window.addEventListener( + 'entriesRerendered', + this.onEntriesRerendered.bind(this), + ); } - onAddPortfolio() { - // after adding portfolio entry + onEntriesRerendered() { + // the thumbnails may have been replaced too this.portfolioThumbnails(); } @@ -38,7 +41,7 @@ var BertaPortfolio = class { portfolioThumbnails() { document.querySelectorAll('.portfolioThumbnails a').forEach((link) => { - // runs again after every addEntry, links that weren't re-rendered already have a listener + // runs again after every entries rerender, links that weren't re-rendered already have a listener if (link.dataset.portfolioListener) { return; }