Skip to content

Commit

Permalink
Use dependentKeyCompat for bindings (#64)
Browse files Browse the repository at this point in the history
* Use `dependentKeyCompat` for bindings

* Bump version
  • Loading branch information
dfreeman authored Mar 22, 2024
1 parent c0510dc commit d41abad
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 3 deletions.
2 changes: 1 addition & 1 deletion ember-exclaim/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "ember-exclaim",
"version": "2.0.0",
"version": "2.1.0",
"description": "An addon allowing apps to expose declarative, JSON-configurable custom UIs backed by Ember components",
"keywords": [
"ember-addon"
Expand Down
19 changes: 17 additions & 2 deletions ember-exclaim/src/-private/env/tracked.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { TrackedObject } from 'tracked-built-ins';
import { dependentKeyCompat } from '@ember/object/compat';
import { HelperSpec, Binding } from '../ui-spec.js';
import { recordCanonicalPath } from '../paths.js';
import { triggerChange } from './index.js';
Expand Down Expand Up @@ -63,7 +64,7 @@ function bindKey(host, key, value, env) {
const bindingPath = value.path.join('.');

recordCanonicalPath(host, key, env, bindingPath);
Object.defineProperty(host, key, {
defineProperty(host, key, {
enumerable: true,
get() {
return value.path.reduce((object, key) => object[key], env);
Expand All @@ -76,7 +77,7 @@ function bindKey(host, key, value, env) {
},
});
} else if (value instanceof HelperSpec) {
Object.defineProperty(host, key, {
defineProperty(host, key, {
enumerable: true,
get() {
return value.invoke(env);
Expand All @@ -86,3 +87,17 @@ function bindKey(host, key, value, env) {
host[key] = bind(value, env);
}
}

function defineProperty(object, key, descriptor) {
// Using `dependentKeyCompat` ensures that any computed properties
// in component implementations will still work correctly. This allows
// shared libraries of Exclaim components to use `@computed` internally
// to be compatible with an environment + `ExclaimUi` using classic
// reactivity OR modern reactivity, providing their consumers an easier
// migration path.
Object.defineProperty(
object,
key,
dependentKeyCompat(object, String(key), descriptor) ?? descriptor,
);
}
33 changes: 33 additions & 0 deletions test-app/tests/integration/exclaim-ui-tracked-test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { setComponentTemplate } from '@ember/component';
import { computed } from '@ember/object';
import Component from '@glimmer/component';
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
Expand Down Expand Up @@ -140,6 +141,38 @@ module('Integration | Component | ExclaimUi | tracked env', function (hooks) {
assert.dom('[data-value]').hasText('goodbye');
});

test('it exposes bindings that can be consumed by legacy computed properties', async function (assert) {
const implementationMap = {
shout: {
component: setComponentTemplate(
hbs`{{this.shoutedValue}}`,
class extends Component {
@computed('args.config.value')
get shoutedValue() {
return this.args.config.value.toUpperCase();
}
},
),
},
};

const ui = {
$component: 'shout',
value: { $bind: 'envValue' },
};

const env = new TrackedObject({ envValue: 'hi' });

await this.renderUI({ implementationMap, ui, env });

assert.dom().hasText('HI');

env.envValue = 'hello';
await settled();

assert.dom().hasText('HELLO');
});

test('it writes bound data back to the env', async function (assert) {
const implementationMap = {
'simple-component': {
Expand Down

0 comments on commit d41abad

Please sign in to comment.