diff --git a/docs/pages/docs.md b/docs/pages/docs.md index d4434b4..8e7a9c2 100644 --- a/docs/pages/docs.md +++ b/docs/pages/docs.md @@ -317,9 +317,9 @@ customElements.define('wcc-counter', Counter); ### (Inferred) Attribute Observability -An optional feature supported by JSX based compilation is a feature called `inferredObservability`. With this enabled, WCC will read any `this` member references in your component's `render` function and map each member instance to -- an entry in the `observedAttributes` array -- automatically handle `attributeChangedCallback` update (by calling `this.render()`) +An optional feature supported by JSX based compilation is `inferredObservability`. With this enabled, WCC will read any `this` member references in your component's `render` function and map each member instance to +1. an entry in the `observedAttributes` array +1. automatically handle `attributeChangedCallback` updates So taking the above counter example, and opting in to this feature, we just need to enable the `inferredObservability` option in the component ```jsx @@ -334,6 +334,7 @@ export default class Counter extends HTMLElement { return (
+ You have clicked {count} times
@@ -348,6 +349,5 @@ And so now when the attribute is set on this component, the component will re-re ``` Some notes / limitations: -- Please be aware of the above linked discussion which is tracking known bugs / feature requests / open items related to all things WCC + JSX. -- We consider the capability of this observability to be "coarse grained" at this time since WCC just re-runs the entire `render` function, replacing of the `innerHTML` for the host component. Thought it is still WIP, we are exploring a more ["fine grained" approach](https://github.com/ProjectEvergreen/wcc/issues/108) that will more efficient than blowing away all the HTML, a la in the style of [**lit-html**](https://lit.dev/docs/templates/overview/) or [**Solid**'s Signals](https://www.solidjs.com/tutorial/introduction_signals). -- This automatically _reflects properties used in the `render` function to attributes_, so YMMV. +- Please be aware of the above linked discussion and issue filter which is tracking any known bugs / feature requests / open items related to all things WCC + JSX. +- This automatically reflects properties used in the `render` function to attributes, so [YMMV](https://dictionary.cambridge.org/us/dictionary/english/ymmv). diff --git a/sandbox/components/counter-dsd.jsx b/sandbox/components/counter-dsd.jsx index 26c7a53..6f1d230 100644 --- a/sandbox/components/counter-dsd.jsx +++ b/sandbox/components/counter-dsd.jsx @@ -4,7 +4,7 @@ export default class CounterDsdJsx extends HTMLElement { connectedCallback() { if (!this.shadowRoot) { console.warn('NO shadowRoot detected for counter-dsd.jsx!'); - this.count = this.getAttribute('count') || 0; + this.count = parseInt(this.getAttribute('count'), 10) || 0; // having an attachShadow call is required for DSD this.attachShadow({ mode: 'open' }); diff --git a/test/cases/jsx-inferred-observability/fixtures/attribute-changed-callback.txt b/test/cases/jsx-inferred-observability/fixtures/attribute-changed-callback.txt index 3801a39..87fb721 100644 --- a/test/cases/jsx-inferred-observability/fixtures/attribute-changed-callback.txt +++ b/test/cases/jsx-inferred-observability/fixtures/attribute-changed-callback.txt @@ -1,13 +1,43 @@ attributeChangedCallback(name, oldValue, newValue) { - function getValue(value) { - return value.charAt(0) === '{' || value.charAt(0) === '[' ? JSON.parse(value) : !isNaN(value) ? parseInt(value, 10) : value === 'true' || value === 'false' ? value === 'true' ? true : false : value; - } - if (newValue !== oldValue) { - switch (name) { - case 'count': - this.count = getValue(newValue); - break; - } - this.render(); - } + function getValue(value) { + return value.charAt(0) === '{' || value.charAt(0) === '[' ? JSON.parse(value) : !isNaN(value) ? parseInt(value, 10) : value === 'true' || value === 'false' ? value === 'true' ? true : false : value; + } + if (newValue !== oldValue) { + switch (name) { + case 'count': + this.count = getValue(newValue); + break; + } + this.update(name, oldValue, newValue); + } +} +update(name, oldValue, newValue) { + console.debug('Update tracking against....', this.constructor.observedAttributes); + console.debug('Updating', name); + console.debug('Swap old', oldValue); + console.debug('For new', newValue); + console.debug('this[name]', this[name]); + const attr = `data-wcc-${ name }`; + const selector = `[${ attr }]`; + console.debug({ attr }); + console.debug({ selector }); + this.querySelectorAll(selector).forEach(el => { + const needle = oldValue || el.getAttribute(attr); + console.debug({ el }); + console.debug({ needle }); + console.debug({ newValue }); + switch (el.getAttribute('data-wcc-ins')) { + case 'text': + el.textContent = el.textContent.replace(needle, newValue); + break; + case 'attr': + if (el.hasAttribute(el.getAttribute(attr))) { + el.setAttribute(el.getAttribute(attr), newValue); + } + break; + } + }); + if (['count'].includes(name)) { + } + console.debug('****************************'); } \ No newline at end of file diff --git a/test/cases/jsx-shadow-dom/src/heading.jsx b/test/cases/jsx-shadow-dom/src/heading.jsx index 68f6d1b..cd97a2b 100644 --- a/test/cases/jsx-shadow-dom/src/heading.jsx +++ b/test/cases/jsx-shadow-dom/src/heading.jsx @@ -17,7 +17,7 @@ export default class HeadingComponent extends HTMLElement { return (
-

Hello, {greeting}!

+

Hello, {greeting}!

); diff --git a/test/cases/jsx/jsx.spec.js b/test/cases/jsx/jsx.spec.js index 981e429..f22686e 100644 --- a/test/cases/jsx/jsx.spec.js +++ b/test/cases/jsx/jsx.spec.js @@ -24,6 +24,7 @@ describe('Run WCC For ', function() { before(async function() { const { html, metadata } = await renderToString(new URL('./src/counter.jsx', import.meta.url)); + console.log({ html }); meta = metadata; dom = new JSDOM(html); }); @@ -69,7 +70,7 @@ describe('Run WCC For ', function() { it('should handle an assignment expression with implicit reactivity using this.render', () => { const element = Array.from(buttons).find(button => button.getAttribute('id') === 'evt-assignment'); - expect(element.getAttribute('onclick')).to.be.equal('this.parentElement.parentElement.count-=1; this.parentElement.parentElement.render();'); + expect(element.getAttribute('onclick')).to.be.equal('this.parentElement.parentElement.count-=1; this.parentElement.parentElement.setAttribute(\'count\', this.parentElement.parentElement.count);'); }); }); diff --git a/test/cases/jsx/src/badge.jsx b/test/cases/jsx/src/badge.jsx index 4251609..8f9bd08 100644 --- a/test/cases/jsx/src/badge.jsx +++ b/test/cases/jsx/src/badge.jsx @@ -33,7 +33,7 @@ export default class BadgeComponent extends HTMLElement { const conditionalText = predicate ? ' 🥳' : ''; return ( - {count}{conditionalText} + {count}{conditionalText} ); } }