Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow macroCondition inside modifier #712

Merged
merged 1 commit into from
Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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