click(2)} />, scratch, scratch.firstChild);
- expect(root.style.cssText).to.equal('color: rgb(0, 255, 255);');
+ expect(proto.removeEventListener)
+ .to.have.been.calledOnce
+ .and.calledWith('mousedown');
- root = render((
-
test
- ), scratch, root);
+ fireEvent(scratch.childNodes[0], 'mousedown');
+ expect(mousedown).not.to.have.been.called;
- expect(root.style.cssText).to.equal('display: inline;');
+ proto.removeEventListener.resetHistory();
+ click.resetHistory();
+ mousedown.resetHistory();
- root = render((
-
test
- ), scratch, root);
+ render(
, scratch, scratch.firstChild);
- expect(root.style.cssText).to.equal('background-color: rgb(0, 255, 255);');
- });
+ expect(proto.removeEventListener)
+ .to.have.been.calledOnce
+ .and.calledWith('click');
+
+ fireEvent(scratch.childNodes[0], 'click');
+ expect(click).not.to.have.been.called;
+ });
- it('should support dangerouslySetInnerHTML', () => {
- let html = '
foo & bar';
- let root = render(
, scratch);
+ it('should use capturing for event props ending with *Capture', () => {
+ let click = sinon.spy(),
+ focus = sinon.spy();
- expect(scratch.firstChild, 'set').to.have.property('innerHTML', html);
- expect(scratch.innerHTML).to.equal('
'+html+'
');
+ let root = render((
+
+
+
+ ), scratch);
- root = render(
ab
, scratch, root);
+ root.firstElementChild.click();
+ root.firstElementChild.focus();
- expect(scratch, 'unset').to.have.property('innerHTML', `
ab
`);
+ expect(click, 'click').to.have.been.calledOnce;
- render(
, scratch, root);
+ if (DISABLE_FLAKEY!==true) {
+ // Focus delegation requires a 50b hack I'm not sure we want to incur
+ expect(focus, 'focus').to.have.been.calledOnce;
- expect(scratch.innerHTML, 're-set').to.equal('
'+html+'
');
+ // IE doesn't set it
+ expect(click).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
+ expect(focus).to.have.been.calledWithMatch({ eventPhase: 0 }); // capturing
+ }
+ });
});
- it('should apply proper mutation for VNodes with dangerouslySetInnerHTML attr', () => {
- class Thing extends Component {
- constructor(props, context) {
- super(props, context);
- this.state.html = this.props.html;
- }
- render(props, { html }) {
- return html ?
:
;
+ describe('dangerouslySetInnerHTML', () => {
+ it('should support dangerouslySetInnerHTML', () => {
+ let html = '
foo & bar';
+ // eslint-disable-next-line react/no-danger
+ let root = render(
, scratch);
+
+ expect(scratch.firstChild, 'set').to.have.property('innerHTML', html);
+ expect(scratch.innerHTML).to.equal('
'+html+'
');
+
+ root = render(
ab
, scratch, root);
+
+ expect(scratch, 'unset').to.have.property('innerHTML', `
ab
`);
+
+ // eslint-disable-next-line react/no-danger
+ render(
, scratch, root);
+
+ expect(scratch.innerHTML, 're-set').to.equal('
'+html+'
');
+ });
+
+ it('should apply proper mutation for VNodes with dangerouslySetInnerHTML attr', () => {
+ class Thing extends Component {
+ constructor(props, context) {
+ super(props, context);
+ this.state.html = this.props.html;
+ }
+ render(props, { html }) {
+ // eslint-disable-next-line react/no-danger
+ return html ?
:
;
+ }
}
- }
- let thing;
+ let thing;
- render(
thing=c } html="test" />, scratch);
+ render( thing=c} html="test" />, scratch);
- expect(scratch.innerHTML).to.equal('test
');
+ expect(scratch.innerHTML).to.equal('test
');
- thing.setState({ html: false });
- thing.forceUpdate();
+ thing.setState({ html: false });
+ thing.forceUpdate();
- expect(scratch.innerHTML).to.equal('');
+ expect(scratch.innerHTML).to.equal('');
- thing.setState({ html: 'test' });
- thing.forceUpdate();
+ thing.setState({ html: 'test' });
+ thing.forceUpdate();
- expect(scratch.innerHTML).to.equal('test
');
- });
+ expect(scratch.innerHTML).to.equal('test
');
+ });
- it('should hydrate with dangerouslySetInnerHTML', () => {
- let html = 'foo & bar';
- scratch.innerHTML = `${html}
`;
- render(, scratch, scratch.lastChild);
+ it('should hydrate with dangerouslySetInnerHTML', () => {
+ let html = 'foo & bar';
+ scratch.innerHTML = `${html}
`;
+ // eslint-disable-next-line react/no-danger
+ render(, scratch, scratch.lastChild);
- expect(scratch.firstChild).to.have.property('innerHTML', html);
- expect(scratch.innerHTML).to.equal(`${html}
`);
+ expect(scratch.firstChild).to.have.property('innerHTML', html);
+ expect(scratch.innerHTML).to.equal(`${html}
`);
+ });
});
it('should reconcile mutated DOM attributes', () => {
@@ -509,7 +620,7 @@ describe('render()', () => {
};
const DOMElement = html``;
- const preactElement = ;
+ const preactElement = ;
render(preactElement, scratch, DOMElement);
expect(scratch).to.have.property('innerHTML', '');
@@ -529,7 +640,7 @@ describe('render()', () => {
}
let comp;
- let root = render( comp = c } />, scratch, root);
+ let root = render( comp = c} />, scratch, root);
let c = document.createElement('c');
c.textContent = 'baz';
@@ -554,18 +665,18 @@ describe('render()', () => {
// Re-rendering from the root is non-destructive if the root was a previous render:
comp.alt = false;
- root = render( comp = c } />, scratch, root);
+ root = render( comp = c} />, scratch, root);
expect(scratch.firstChild.children, 'root re-render').to.have.length(4);
expect(scratch.innerHTML, 'root re-render').to.equal(``);
comp.alt = true;
- root = render( comp = c } />, scratch, root);
+ root = render( comp = c} />, scratch, root);
expect(scratch.firstChild.children, 'root re-render 2').to.have.length(4);
expect(scratch.innerHTML, 'root re-render 2').to.equal(``);
- root = render( comp = c } />
, scratch, root);
+ root = render( comp = c} />
, scratch, root);
expect(scratch.firstChild.children, 'root re-render changed').to.have.length(3);
expect(scratch.innerHTML, 'root re-render changed').to.equal(``);
@@ -588,44 +699,69 @@ describe('render()', () => {
expect(sortAttributes(html)).to.equal(sortAttributes(''));
});
- it('should not execute append operation when child is at last', (done) => {
+ it('should not execute append operation when child is at last', () => {
+ // See developit/preact#717 for discussion about the issue this addresses
+
+ let todoText = 'new todo that I should complete';
let input;
+ let setText;
+ let addTodo;
+
+ const ENTER = 13;
+
class TodoList extends Component {
constructor(props) {
super(props);
this.state = { todos: [], text: '' };
- this.setText = this.setText.bind(this);
- this.addTodo = this.addTodo.bind(this);
+ setText = this.setText = this.setText.bind(this);
+ addTodo = this.addTodo = this.addTodo.bind(this);
}
setText(e) {
this.setState({ text: e.target.value });
}
- addTodo() {
- let { todos, text } = this.state;
- todos = todos.concat({ text });
- this.setState({ todos, text: '' });
+ addTodo(e) {
+ if (e.keyCode === ENTER) {
+ let { todos, text } = this.state;
+ todos = todos.concat({ text });
+ this.setState({ todos, text: '' });
+ }
}
render() {
- const {todos, text} = this.state;
+ const { todos, text } = this.state;
return (
-
- { todos.map( todo => (
{todo.text}
)) }
+
+ { todos.map( todo => ([
+
{todo.text},
+
[ Delete ],
+
+ ])) }
input = i} />
);
}
}
- const root = render(
, scratch);
+
+ render(
, scratch);
+
+ // Simulate user typing
input.focus();
- input.value = 1;
- root._component.setText({
+ input.value = todoText;
+ setText({
target: input
});
- root._component.addTodo();
+
+ // Simulate user pressing enter
+ addTodo({
+ keyCode: ENTER
+ });
+
+ // Before Preact rerenders, focus should be on the input
+ expect(document.activeElement).to.equal(input);
+
+ rerender();
+
+ // After Preact rerenders, focus should remain on the input
expect(document.activeElement).to.equal(input);
- setTimeout(() =>{
- expect(/1/.test(scratch.innerHTML)).to.equal(true);
- done();
- }, 10);
+ expect(scratch.innerHTML).to.contain(`
${todoText}`);
});
});