Skip to content

Commit

Permalink
Merge pull request #712 from simonihmig/macro-modifier
Browse files Browse the repository at this point in the history
Allow macroCondition inside modifier
  • Loading branch information
rwjblue authored Mar 8, 2021
2 parents 99486bd + e1d0040 commit b7168ec
Show file tree
Hide file tree
Showing 8 changed files with 17,605 additions and 9,163 deletions.
8 changes: 8 additions & 0 deletions packages/macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,14 @@ Macros can also be used inside of templates:
{{/if}}
```

Starting with Ember 3.25 you can also use it to conditionally apply modifiers:

```hbs
<button {{(if (macroCondition true) on) "click" this.something}}>Submit</button>
```

However, in all cases the argument to `macroCondition` must be statically analyzable:

```js
import { macroCondition } from '@embroider/macros';

Expand Down
10 changes: 10 additions & 0 deletions packages/macros/src/glimmer/ast-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,16 @@ export function makeSecondTransform() {
},
ElementNode(node: any) {
node.modifiers = node.modifiers.filter((modifier: any) => {
if (
modifier.path.type === 'SubExpression' &&
modifier.path.path.type === 'PathExpression' &&
modifier.path.path.original === 'if'
) {
modifier.path = macroIfExpression(modifier.path, env.syntax.builders);
if (modifier.path.type === 'UndefinedLiteral') {
return false;
}
}
if (modifier.path.type !== 'PathExpression') {
return true;
}
Expand Down
15 changes: 15 additions & 0 deletions packages/macros/tests/glimmer/macro-condition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,20 @@ describe(`macroCondition`, function () {
);
expect(code).toMatch(/\{\{my-assertion ["']red["']\}\}/);
});

test('macroCondition in modifier position when true', function () {
let code = transform(`<button {{(if (macroCondition true) on) "click" this.something}}>Submit</button>`);
expect(code).toMatch('<button {{on "click" this.something}}>Submit</button>');
});

test('macroCondition in modifier position when false', function () {
let code = transform(`<button {{(if (macroCondition false) off on) "click" this.something}}>Submit</button>`);
expect(code).toMatch('<button {{on "click" this.something}}>Submit</button>');
});

test('macroCondition in modifier position when false with no alternate', function () {
let code = transform(`<button {{(if (macroCondition false) on) "click" this.something}}>Submit</button>`);
expect(code).toMatch('<button>Submit</button>');
});
});
});
2 changes: 1 addition & 1 deletion test-packages/macro-tests/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"ember-maybe-import-regenerator": "^0.1.6",
"ember-qunit": "^4.4.1",
"ember-resolver": "^5.0.1",
"ember-source": "~3.10.0",
"ember-source": "~3.25.0",
"ember-welcome-page": "^4.0.0",
"eslint-plugin-ember": "^7.0.0",
"eslint-plugin-node": "^9.0.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,87 +4,87 @@ import { render, click } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { helper } from '@ember/component/helper';

module('Integration | Macro | macroCondition', function(hooks) {
module('Integration | Macro | macroCondition', function (hooks) {
setupRenderingTest(hooks);

test('macroCondition in content position when true', async function(assert) {
test('macroCondition in content position when true', async function (assert) {
await render(hbs`{{#if (macroCondition true)}}red{{else}}blue{{/if}}`);
assert.equal(this.element.textContent.trim(), 'red');
});

test('macroCondition in content position when false', async function(assert) {
test('macroCondition in content position when false', async function (assert) {
await render(hbs`{{#if (macroCondition false)}}red{{else}}blue{{/if}}`);
assert.equal(this.element.textContent.trim(), 'blue');
});

test('macroCondition in content position when false with no alternate', async function(assert) {
test('macroCondition in content position when false with no alternate', async function (assert) {
await render(hbs`{{#if (macroCondition false)}}red{{/if}}`);
assert.equal(this.element.textContent.trim(), '');
});

test('macroCondition in subexpression position when true', async function(assert) {
test('macroCondition in subexpression position when true', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(hbs`{{my-assertion (if (macroCondition true) 'red' 'blue') }}`);
});

test('macroCondition inside string', async function(assert) {
test('macroCondition inside string', async function (assert) {
assert.expect(1);
await render(hbs`<div class="target {{if (macroCondition true) 'red' 'blue' }}"></div>`);
assert.ok(this.element.querySelector('.target').matches('.red'));
});

test('macroCondition in subexpression position when false', async function(assert) {
test('macroCondition in subexpression position when false', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, 'blue');
})
);
await render(hbs`{{my-assertion (if (macroCondition false) 'red' 'blue') }}`);
});

test('macroCondition in subexpression position when false with no alternate', async function(assert) {
test('macroCondition in subexpression position when false with no alternate', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, undefined);
})
);
await render(hbs`{{my-assertion (if (macroCondition false) 'red') }}`);
});

test('macroMaybeAttrs when true', async function(assert) {
test('macroMaybeAttrs when true', async function (assert) {
await render(hbs`<div data-test-target {{macroMaybeAttrs true data-optional data-flavor="vanilla" }} ></div>`);
let target = this.element.querySelector('[data-test-target]');
assert.ok(target.matches('[data-optional]'), 'found data-optional');
assert.ok(target.matches('[data-flavor="vanilla"]'), 'found data-flavor');
});

test('macroMaybeAttrs propagates bound paths', async function(assert) {
test('macroMaybeAttrs propagates bound paths', async function (assert) {
this.set('flavor', 'vanilla');
await render(hbs`<div data-test-target {{macroMaybeAttrs true data-flavor=this.flavor }} ></div>`);
let target = this.element.querySelector('[data-test-target]');
assert.ok(target.matches('[data-flavor="vanilla"]'), 'found data-flavor');
});

test('macroMaybeAttrs when false', async function(assert) {
test('macroMaybeAttrs when false', async function (assert) {
await render(hbs`<div data-test-target {{macroMaybeAttrs false data-optional data-flavor="vanilla" }} ></div>`);
let target = this.element.querySelector('[data-test-target]');
assert.ok(!target.matches('[data-optional]'));
assert.ok(!target.matches('[data-flavor="vanilla"]'));
});

test('macroMaybeAttrs leaves other modifiers alone', async function(assert) {
test('macroMaybeAttrs leaves other modifiers alone', async function (assert) {
assert.expect(1);
this.doThing = function() {
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(
Expand All @@ -94,36 +94,67 @@ module('Integration | Macro | macroCondition', function(hooks) {
await click(target);
});

test('macroCondition composes with other macros, true case', async function(assert) {
test('macroCondition composes with other macros, true case', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(hbs`{{my-assertion (if (macroCondition (macroDependencySatisfies 'ember-source' '3.x')) 'red' 'blue') }}`);
await render(
hbs`{{my-assertion (if (macroCondition (macroDependencySatisfies 'ember-source' '3.x')) 'red' 'blue') }}`
);
});

test('macroCondition composes with other macros, false case', async function(assert) {
test('macroCondition composes with other macros, false case', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, 'blue');
})
);
await render(hbs`{{my-assertion (if (macroCondition (macroDependencySatisfies 'ember-source' '10.x')) 'red' 'blue') }}`);
await render(
hbs`{{my-assertion (if (macroCondition (macroDependencySatisfies 'ember-source' '10.x')) 'red' 'blue') }}`
);
});

test('macroCondition composes with self', async function(assert) {
test('macroCondition composes with self', async function (assert) {
assert.expect(1);
this.owner.register(
'helper:my-assertion',
helper(function([value]) {
helper(function ([value]) {
assert.strictEqual(value, 'red');
})
);
await render(hbs`{{my-assertion (if (macroCondition true) (if (macroCondition false) 'green' 'red') 'blue') }}`);
});

test('macroCondition in modifier position when true', async function (assert) {
assert.expect(1);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(hbs`<button {{(if (macroCondition true) on) "click" this.doThing}}>Submit</button>`);
await click('button');
});

test('macroCondition in modifier position when false', async function (assert) {
assert.expect(1);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(hbs`<button {{(if (macroCondition false) off on) "click" this.doThing}}>Submit</button>`);
await click('button');
});

test('macroCondition in modifier position when false with no alternate', async function (assert) {
assert.expect(0);
this.doThing = function () {
assert.ok(true, 'it ran');
};
await render(hbs`<button {{(if (macroCondition false) on) "click" this.doThing}}>Submit</button>`);
await click('button');
});
});
4 changes: 1 addition & 3 deletions test-packages/support/vendor/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
This is vendored from ember 3.8.0.
This is vendored from ember 3.25.0.

I did it this way because if I try to depend directly on ember-source, I end up with a version of fs-tree-diff that has bad types in it that messes up my build.

Also, I backported https://github.com/glimmerjs/glimmer-vm/pull/932 into it. That bug doesn't directly effect the use of macros in real apps, because they only happen in stage3 and never get re-printed. But in our test suite we do re-print their output.
Loading

0 comments on commit b7168ec

Please sign in to comment.