Skip to content

Commit

Permalink
Merge 44ec32f into d3f5844
Browse files Browse the repository at this point in the history
  • Loading branch information
JoviDeCroock committed May 12, 2019
2 parents d3f5844 + 44ec32f commit e367a88
Show file tree
Hide file tree
Showing 3 changed files with 216 additions and 16 deletions.
5 changes: 0 additions & 5 deletions src/diff/children.js
Expand Up @@ -32,7 +32,6 @@ export function diffChildren(parentDom, newParentVNode, oldParentVNode, context,
let oldChildren = (oldParentVNode && oldParentVNode._children) || EMPTY_ARR;

let oldChildrenLength = oldChildren.length;
let oldChild;

// Only in very specific places should this logic be invoked (top level `render` and `diffElementNodes`).
// I'm using `EMPTY_OBJ` to signal when `diffChildren` is invoked in these situations. I can't use `null`
Expand All @@ -48,7 +47,6 @@ export function diffChildren(parentDom, newParentVNode, oldParentVNode, context,
else {
for (i = 0; !oldDom && i < oldChildrenLength; i++) {
oldDom = oldChildren[i] && oldChildren[i]._dom;
oldChild = oldChildren[i];
}
}
}
Expand All @@ -73,9 +71,6 @@ export function diffChildren(parentDom, newParentVNode, oldParentVNode, context,
oldVNode = oldChildren[j];
if (oldVNode && (oldVNode.key!=null ? (childVNode.key === oldVNode.key) : (childVNode.key==null && childVNode.type === oldVNode.type))) {
oldChildren[j] = undefined;
if (oldChildrenLength !== newChildren.length && oldVNode.type !== (oldChild && oldChild.type)) {
oldDom = oldVNode._dom;
}
break;
}
oldVNode = null;
Expand Down
72 changes: 72 additions & 0 deletions test/browser/components.test.js
Expand Up @@ -421,6 +421,78 @@ describe('Components', () => {
expect(scratch.innerHTML, 'switching to textnode 2').to.equal('asdf');
});

// Test for Issue developit/preact#1616
it('should maintain order when setting state (that inserts dom-elements)', () => {
let add, addTwice, reset;
const Entry = props => (
<div>{props.children}</div>
);

class App extends Component {
constructor(props) {
super(props);

this.state = { values: ['abc'] };

add = this.add = this.add.bind(this);
addTwice = this.addTwice = this.addTwice.bind(this);
reset = this.reset = this.reset.bind(this);
}

add() {
this.setState({ values: [...this.state.values, 'def'] });
}

addTwice() {
this.setState({ values: [...this.state.values, 'def', 'ghi'] });
}

reset() {
this.setState({ values: ['abc'] });
}

render() {
return (
<div>
{this.state.values.map(v => (
<Entry>
{v}
</Entry>
))}
<button>First Button</button>
<button>Second Button</button>
<button>Third Button</button>
</div>
);
}
}

render(<App />, scratch);
expect(scratch.firstChild.innerHTML).to.equal('<div>abc</div>' +
'<button>First Button</button><button>Second Button</button><button>Third Button</button>');

add();
rerender();
expect(scratch.firstChild.innerHTML).to.equal('<div>abc</div><div>def' +
'</div><button>First Button</button><button>Second Button</button><button>Third Button</button>');

add();
rerender();
expect(scratch.firstChild.innerHTML).to.equal('<div>abc</div><div>def</div><div>def' +
'</div><button>First Button</button><button>Second Button</button><button>Third Button</button>');

reset();
rerender();
expect(scratch.firstChild.innerHTML).to.equal('<div>abc</div>' +
'<button>First Button</button><button>Second Button</button><button>Third Button</button>');

addTwice();
rerender();
expect(scratch.firstChild.innerHTML).to.equal('<div>abc</div><div>def</div><div>ghi' +
'</div><button>First Button</button><button>Second Button</button><button>Third Button</button>');
});


// Test for Issue developit/preact#254
it('should not recycle common class children with different keys', () => {
let idx = 0;
Expand Down
155 changes: 144 additions & 11 deletions test/browser/focus.test.js
Expand Up @@ -15,6 +15,7 @@ describe('focus', () => {

const List = ({ children }) => <div>{children}</div>;
const ListItem = ({ children }) => <span>{children}</span>;
const InputWithId = ({ i }) => <input id={`input-${i}`} type="text" />;
const Input = () => <input type="text" />;

function focusInput() {
Expand All @@ -30,6 +31,19 @@ describe('focus', () => {
return input;
}

function focusInputById() {
if (!scratch) return;

const input = scratch.querySelector('#input-0');
input.value = 'a word';
input.focus();
input.setSelectionRange(2, 5);

expect(document.activeElement).to.equal(input);

return input;
}

/**
* Validate an input tag has maintained focus
* @param {HTMLInputElement} input The input to validate
Expand Down Expand Up @@ -114,7 +128,7 @@ describe('focus', () => {

render((
<List>
<ListItem>1</ListItem>
<ListItem key="1">1</ListItem>
<Input />
</List>
), scratch);
Expand All @@ -124,9 +138,9 @@ describe('focus', () => {

render((
<List>
<ListItem>1</ListItem>
<ListItem key="1">1</ListItem>
<Input />
<ListItem>2</ListItem>
<ListItem key="2">2</ListItem>
</List>
), scratch);
validateFocus(input, 'insert sibling after');
Expand All @@ -135,10 +149,10 @@ describe('focus', () => {

render((
<List>
<ListItem>1</ListItem>
<ListItem key="1">1</ListItem>
<Input />
<ListItem>2</ListItem>
<ListItem>3</ListItem>
<ListItem key="2">2</ListItem>
<ListItem key="3">3</ListItem>
</List>
), scratch);
validateFocus(input, 'insert sibling after again');
Expand All @@ -147,17 +161,19 @@ describe('focus', () => {

render((
<List>
<ListItem>0</ListItem>
<ListItem>1</ListItem>
<ListItem key="0">0</ListItem>
<ListItem key="1">1</ListItem>
<Input />
<ListItem>2</ListItem>
<ListItem>3</ListItem>
<ListItem key="2">2</ListItem>
<ListItem key="3">3</ListItem>
</List>
), scratch);
validateFocus(input, 'insert sibling before again');

expect(scratch.innerHTML).to.equal('<div><span>0</span><span>1</span><input type="text"><span>2</span><span>3</span></div>');
});

it('should maintain focus when removing elements around input', () => {
it('should maintain focus when conditional elements around input', () => {
render((
<List>
<ListItem>0</ListItem>
Expand All @@ -172,6 +188,7 @@ describe('focus', () => {

render((
<List>
{false && <ListItem>0</ListItem>}
<ListItem>1</ListItem>
<Input />
<ListItem>2</ListItem>
Expand All @@ -184,9 +201,11 @@ describe('focus', () => {

render((
<List>
{false && <ListItem>0</ListItem>}
<ListItem>1</ListItem>
<Input />
<ListItem>2</ListItem>
{false && <ListItem>3</ListItem>}
</List>
), scratch);
validateFocus(input, 'remove sibling after');
Expand All @@ -195,8 +214,68 @@ describe('focus', () => {

render((
<List>
{false && <ListItem>0</ListItem>}
<ListItem>1</ListItem>
<Input />
{false && <ListItem>2</ListItem>}
{false && <ListItem>3</ListItem>}
</List>
), scratch);
validateFocus(input, 'remove sibling after 2');

input = focusInput();
render((
<List>
{false && <ListItem>0</ListItem>}
{false && <ListItem>1</ListItem>}
<Input />
{false && <ListItem>2</ListItem>}
{false && <ListItem>3</ListItem>}
</List>
), scratch);
validateFocus(input, 'remove sibling before 2');
});

it('should maintain focus when removing elements around input', () => {
render((
<List>
<ListItem key="0">0</ListItem>
<ListItem key="1">1</ListItem>
<Input />
<ListItem key="2">2</ListItem>
<ListItem key="3">3</ListItem>
</List>
), scratch);

let input = focusInput();

render((
<List>
<ListItem key="1">1</ListItem>
<Input />
<ListItem key="2">2</ListItem>
<ListItem key="3">3</ListItem>
</List>
), scratch);
validateFocus(input, 'remove sibling before');

input = focusInput();

render((
<List>
<ListItem key="1">1</ListItem>
<Input />
<ListItem key="2">2</ListItem>
</List>
), scratch);
validateFocus(input, 'remove sibling after');

input = focusInput();

render((
<List>
<ListItem key="1">1</ListItem>
<Input />
</List>
), scratch);
validateFocus(input, 'remove sibling after 2');
Expand All @@ -211,6 +290,60 @@ describe('focus', () => {
validateFocus(input, 'remove sibling before 2');
});

it('should maintain focus when adding input next to the current input', () => {
render((
<List>
<InputWithId i={0} />
</List>
), scratch);

let input = focusInputById();

render((
<List>
<Input key="1" />
<InputWithId i={0} />
</List>
), scratch);
validateFocus(input, 'add input before');

input = focusInputById();

render((
<List>
<Input key="1" />
<InputWithId i={0} />
<Input key="2" />
</List>
), scratch);
validateFocus(input, 'add input after');

input = focusInputById();

render((
<List>
<Input key="0" />
<Input key="1" />
<InputWithId i={0} />
<Input key="2" />
</List>
), scratch);
validateFocus(input, 'add input first place');

input = focusInputById();

render((
<List>
<Input key="-1" />
<Input key="0" />
<Input key="1" />
<InputWithId i={0} />
<Input key="2" />
</List>
), scratch);
validateFocus(input, 'add input before');
});

it('should maintain focus when hydrating', () => {
const html = div([
span('1'),
Expand Down

0 comments on commit e367a88

Please sign in to comment.