Skip to content

Commit

Permalink
Merge pull request #44 from preactjs/sync-props
Browse files Browse the repository at this point in the history
Bring back property binding reflection
  • Loading branch information
marvinhagemeister authored Sep 1, 2020
2 parents f955bc3 + 2cc4cb8 commit 0cc85f1
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 2 deletions.
35 changes: 33 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,41 @@ export default function register(Component, tagName, propNames, options) {
PreactElement.prototype.connectedCallback = connectedCallback;
PreactElement.prototype.attributeChangedCallback = attributeChangedCallback;
PreactElement.prototype.disconnectedCallback = disconnectedCallback;
PreactElement.observedAttributes =

propNames =
propNames ||
Component.observedAttributes ||
Object.keys(Component.propTypes || {});
PreactElement.observedAttributes = propNames;

// Keep DOM properties and Preact props in sync
propNames.forEach((name) => {
Object.defineProperty(PreactElement.prototype, name, {
get() {
return this._vdom.props[name];
},
set(v) {
if (this._vdom) {
this.attributeChangedCallback(name, null, v);
} else {
if (!this._props) this._props = {};
this._props[name] = v;
this.connectedCallback();
}

// Reflect property changes to attributes if the value is a primitive
const type = typeof v;
if (
v == null ||
type === 'string' ||
type === 'boolean' ||
type === 'number'
) {
this.setAttribute(name, v);
}
},
});
});

return customElements.define(
tagName || Component.tagName || Component.displayName || Component.name,
Expand Down Expand Up @@ -47,7 +78,7 @@ function connectedCallback() {

this._vdom = h(
ContextProvider,
{ context },
{ ...this._props, context },
toVdom(this, true, this._vdomComponent)
);
(this.hasAttribute('hydrate') ? hydrate : render)(this._vdom, this._root);
Expand Down
87 changes: 87 additions & 0 deletions src/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,93 @@ it('renders ok, updates on attr change', () => {
document.body.removeChild(root);
});

it('passes property changes to props', () => {
const root = document.createElement('div');
const el = document.createElement('x-clock');

el.time = '10:28:57 PM';
assert.equal(el.time, '10:28:57 PM');

root.appendChild(el);
document.body.appendChild(root);
assert.equal(
root.innerHTML,
'<x-clock time="10:28:57 PM"><span>10:28:57 PM</span></x-clock>'
);

el.time = '11:01:10 AM';
assert.equal(el.time, '11:01:10 AM');

assert.equal(
root.innerHTML,
'<x-clock time="11:01:10 AM"><span>11:01:10 AM</span></x-clock>'
);

document.body.removeChild(root);
});

function DummyButton({ onClick, text = 'click' }) {
return <button onClick={onClick}>{text}</button>;
}

registerElement(DummyButton, 'x-dummy-button', ['onClick', 'text']);

it('passes simple properties changes to props', () => {
const root = document.createElement('div');
const el = document.createElement('x-dummy-button');

el.text = 'foo';
assert.equal(el.text, 'foo');

root.appendChild(el);
document.body.appendChild(root);
assert.equal(
root.innerHTML,
'<x-dummy-button text="foo"><button>foo</button></x-dummy-button>'
);

// Update
el.text = 'bar';
assert.equal(
root.innerHTML,
'<x-dummy-button text="bar"><button>bar</button></x-dummy-button>'
);

document.body.removeChild(root);
});

it('passes complex properties changes to props', () => {
const root = document.createElement('div');
const el = document.createElement('x-dummy-button');

let clicks = 0;
const onClick = () => clicks++;
el.onClick = onClick;
assert.equal(el.onClick, onClick);

root.appendChild(el);
document.body.appendChild(root);
assert.equal(
root.innerHTML,
'<x-dummy-button><button>click</button></x-dummy-button>'
);

act(() => {
el.querySelector('button').click();
});
assert.equal(clicks, 1);

// Update
let other = 0;
el.onClick = () => other++;
act(() => {
el.querySelector('button').click();
});
assert.equal(other, 1);

document.body.removeChild(root);
});

function Foo({ text, children }) {
return (
<span class="wrapper">
Expand Down

0 comments on commit 0cc85f1

Please sign in to comment.