Skip to content

Commit

Permalink
fix: portal directive should not move element when not needed (#270)
Browse files Browse the repository at this point in the history
  • Loading branch information
divdavem authored Nov 30, 2023
1 parent ee7a1bd commit a684409
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 4 deletions.
33 changes: 33 additions & 0 deletions core/lib/services/portal.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,37 @@ describe(`Portal`, () => {
portalInstance?.destroy?.();
expect(testArea.innerHTML).toBe(initialMarkup.replace('<div id="element"></div>', ''));
});

test(`should not move when not needed`, () => {
const initialMarkup = '<div><iframe id="element"></iframe><div id="nextElement"></div></div>';
testArea.innerHTML = initialMarkup;
const element = document.getElementById('element') as HTMLIFrameElement;
(element.contentWindow as any).something = 1; // when removing an iframe from the DOM, variables on the window are lost
const nextElement = document.getElementById('nextElement')!;
expect(element.nextSibling).toBe(nextElement);
const portalInstance = portal(element, {insertBefore: nextElement});
expect((element.contentWindow as any).something).toBe(1); // the variable is still here, so the iframe was not removed from the DOM
expect(testArea.innerHTML).toBe(initialMarkup); // the markup should not have changed
portalInstance?.destroy?.();
expect(testArea.innerHTML).toBe(initialMarkup.replace('<iframe id="element"></iframe>', ''));
});

test(`should not move a second time when not needed`, () => {
const initialMarkup = '<div><iframe id="element"></iframe></div><div id="container"></div>';
testArea.innerHTML = initialMarkup;
const element = document.getElementById('element') as HTMLIFrameElement;
const container = document.getElementById('container')!;
const portalInstance = portal(element, {container});
expect(element.parentElement).toBe(container);
(element.contentWindow as any).something = 1; // when removing an iframe from the DOM, variables on the window are lost
const newElement = document.createElement('div');
newElement.setAttribute('id', 'newElement');
container.appendChild(newElement);
const beforeUpdateMarkup = testArea.innerHTML;
portalInstance?.update?.({container, insertBefore: newElement}); // should do nothing, as the element is already at the right place
expect((element.contentWindow as any).something).toBe(1);
expect(testArea.innerHTML).toBe(beforeUpdateMarkup);
portalInstance?.destroy?.();
expect(testArea.innerHTML).toBe('<div></div><div id="container"><div id="newElement"></div></div>');
});
});
12 changes: 8 additions & 4 deletions core/lib/services/portal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,18 @@ export const portal: Directive<PortalDirectiveArg> = (content, newArg) => {
};

const update = (newArg: PortalDirectiveArg) => {
if (newArg !== arg) {
if (newArg !== arg && (newArg?.container !== arg?.container || newArg?.insertBefore !== arg?.insertBefore)) {
arg = newArg;
const container = arg?.container ?? arg?.insertBefore?.parentElement;
if (container) {
if (!replaceComment) {
replaceComment = content.parentNode?.insertBefore(content.ownerDocument.createComment('portal'), content);
const insertBefore = arg?.insertBefore ?? null;
const moveNeeded = content.parentElement !== container || content.nextSibling !== insertBefore;
if (moveNeeded) {
if (!replaceComment) {
replaceComment = content.parentNode?.insertBefore(content.ownerDocument.createComment('portal'), content);
}
container.insertBefore(content, insertBefore);
}
container.insertBefore(content, arg?.insertBefore ?? null);
} else {
removeReplaceComment();
}
Expand Down

0 comments on commit a684409

Please sign in to comment.