diff --git a/src/diff/children.js b/src/diff/children.js
index fd19a5555f..383b95cfdb 100644
--- a/src/diff/children.js
+++ b/src/diff/children.js
@@ -205,6 +205,17 @@ export function diffChildren(
// Remove remaining oldChildren if there are any.
for (i = oldChildrenLength; i--; ) {
if (oldChildren[i] != null) {
+ if (
+ typeof newParentVNode.type == 'function' &&
+ oldChildren[i]._dom != null &&
+ oldChildren[i]._dom == newParentVNode._nextDom
+ ) {
+ // If the newParentVNode.__nextDom points to a dom node that is about to
+ // be unmounted, then get the next sibling of that vnode and set
+ // _nextDom to it
+ newParentVNode._nextDom = newParentVNode._nextDom.nextSibling;
+ }
+
unmount(oldChildren[i], oldChildren[i]);
}
}
diff --git a/test/browser/focus.test.js b/test/browser/focus.test.js
index 004a87a1af..96348f6a3d 100644
--- a/test/browser/focus.test.js
+++ b/test/browser/focus.test.js
@@ -13,6 +13,9 @@ describe('focus', () => {
/** @type {() => void} */
let rerender;
+ /** @type {(newState: Partial<{ before: number[]; after: number[] }>) => void} */
+ let setState;
+
/** @type {() => void} */
let prepend, append, shift, pop;
@@ -27,6 +30,8 @@ describe('focus', () => {
after: props.initialAfter || []
};
+ setState = newState => this.setState(newState);
+
prepend = () => {
const before = this.state.before;
const newValue = before[0] ? before[0] - 1 : 1;
@@ -56,13 +61,14 @@ describe('focus', () => {
});
};
- const liHtml = this.props.as == Input ? inputStr : span;
- getDynamicListHtml = () =>
- div([
+ getDynamicListHtml = () => {
+ const liHtml = this.props.as == Input ? inputStr : span;
+ return div([
...this.state.before.map(liHtml),
'',
...this.state.after.map(liHtml)
]);
+ };
}
render(props, state) {
@@ -377,6 +383,22 @@ describe('focus', () => {
validateFocus(input, 'remove sibling before 2');
});
+ it('should maintain focus when removing element directly before input', () => {
+ render(
+ ,
+ scratch
+ );
+
+ let input = focusInput();
+ expect(scratch.innerHTML).to.equal(getDynamicListHtml());
+
+ setState({ before: [0] });
+ rerender();
+
+ expect(scratch.innerHTML).to.equal(getDynamicListHtml());
+ validateFocus(input, 'remove sibling directly before input');
+ });
+
it('should maintain focus when adding input next to the current input', () => {
render(, scratch);
diff --git a/test/browser/fragments.test.js b/test/browser/fragments.test.js
index 937dd1d0d0..2dbbee6aab 100644
--- a/test/browser/fragments.test.js
+++ b/test/browser/fragments.test.js
@@ -237,9 +237,7 @@ describe('Fragment', () => {
expectDomLogToBe([
'
.appendChild(#text)',
'
122.insertBefore(
1,
1)',
- '1.remove()',
- '122.appendChild(
2)',
- '122.appendChild(
2)'
+ '1.remove()'
]);
});
@@ -2658,7 +2656,7 @@ describe('Fragment', () => {
render(, scratch);
expect(scratch.innerHTML).to.equal(div([div(1), div('A')]));
- expectDomLogToBe(['2.remove()', '
1A.appendChild(
A)']);
+ expectDomLogToBe(['
2.remove()']);
});
it('should efficiently unmount nested Fragment children', () => {
@@ -2857,13 +2855,12 @@ describe('Fragment', () => {
'
123AB.insertBefore(
1, 1)',
'
2.remove()',
'
3.remove()',
- '
1.remove()',
- '
1AB.appendChild(
A)',
- '
1BA.appendChild(
B)'
+ '
1.remove()'
]);
});
it('should swap nested fragments correctly', () => {
+ /** @type {() => void} */
let swap;
class App extends Component {
constructor(props) {
@@ -2875,11 +2872,9 @@ describe('Fragment', () => {
if (this.state.first) {
return (
- {
-
- 1. Original item first paragraph
-
- }
+
+ 1. Original item first paragraph
+
2. Original item second paragraph