diff --git a/packages/core/src/dom_components/model/Components.ts b/packages/core/src/dom_components/model/Components.ts index ce45676f5..ff04ff28b 100644 --- a/packages/core/src/dom_components/model/Components.ts +++ b/packages/core/src/dom_components/model/Components.ts @@ -70,6 +70,8 @@ const getComponentsFromDefs = ( result = all[id] as any; const { onAttributes, onStyle } = updateOptions; const component = result as unknown as Component; + // Detach the reused model from its old container so re-inserting it updates `collection`/`parent` + component.collection?.remove(component, { silent: true } as any); const htmlImportOpts = { ...opts, parsedImportSource: 'html' as const }; tagName && component.set({ tagName }, { ...htmlImportOpts, silent: true }); diff --git a/packages/core/test/specs/dom_components/model/Component.ts b/packages/core/test/specs/dom_components/model/Component.ts index 5f984efac..a7bd37978 100644 --- a/packages/core/test/specs/dom_components/model/Component.ts +++ b/packages/core/test/specs/dom_components/model/Component.ts @@ -582,6 +582,31 @@ describe('Component', () => { ); }); + test('resetFromString re-parents a reused component when its wrapper is removed', () => { + const wrapper = dcomp.getWrapper()!; + wrapper.components().resetFromString(`

Insert your text here

`); + + const div = wrapper.components().at(0); + const paragraph = div.components().at(0); + expect(div.getId()).toBe('i5y6'); + expect(paragraph.getId()).toBe('i8sd'); + expect(paragraph.parent()).toBe(div); + + // Remove the wrapping div, promoting the reused

to a direct child of the wrapper. + wrapper.components().resetFromString(`

Insert your text here

`); + + const allById = dcomp.allById(); + // The same

model instance is reused, not rebuilt. + expect(wrapper.components().at(0)).toBe(paragraph); + expect(allById['i8sd']).toBe(paragraph); + // The removed div is gone from the global registry. + expect(allById['i5y6']).toBeUndefined(); + // The reused

reports the wrapper as its parent, not the removed div. + expect(paragraph.parent()).toBe(wrapper); + expect(paragraph.collection).toBe(wrapper.components()); + expect(em.getHtml()).toBe('

Insert your text here

'); + }); + test('Ability to stop/change propagation chain', () => { obj.append({ removable: false,