diff --git a/packages/ember-htmlbars/lib/glimmer-component.js b/packages/ember-htmlbars/lib/glimmer-component.js
index e6120f76f2d..5060b8160b1 100644
--- a/packages/ember-htmlbars/lib/glimmer-component.js
+++ b/packages/ember-htmlbars/lib/glimmer-component.js
@@ -8,7 +8,7 @@ import AriaRoleSupport from 'ember-views/mixins/aria_role_support';
import ViewMixin from 'ember-views/mixins/view_support';
import EmberView from 'ember-views/views/view';
-export default CoreView.extend(
+const GlimmerComponent = CoreView.extend(
ViewChildViewsSupport,
ViewStateSupport,
TemplateRenderingSupport,
@@ -24,3 +24,9 @@ export default CoreView.extend(
this._viewRegistry = this._viewRegistry || EmberView.views;
}
});
+
+GlimmerComponent.reopenClass({
+ isGlimmerComponentFactory: true
+});
+
+export default GlimmerComponent;
diff --git a/packages/ember-htmlbars/lib/hooks/component.js b/packages/ember-htmlbars/lib/hooks/component.js
index b5ae33b6d34..ef1e2da1758 100644
--- a/packages/ember-htmlbars/lib/hooks/component.js
+++ b/packages/ember-htmlbars/lib/hooks/component.js
@@ -24,12 +24,14 @@ var IS_ANGLE_CACHE = new Cache(1000, function(key) {
export default function componentHook(renderNode, env, scope, _tagName, params, attrs, templates, visitor) {
var state = renderNode.getState();
+ let isClosureComponent = false;
+
let tagName = _tagName;
if (CONTAINS_DOT_CACHE.get(tagName)) {
- let stream = env.hooks.get(env, scope, tagName);
- let componentCell = stream.value();
+ let componentCell = getComponentCell(env, scope, tagName);
if (isComponentCell(componentCell)) {
+ isClosureComponent = true;
tagName = componentCell[COMPONENT_PATH];
/*
@@ -62,10 +64,12 @@ export default function componentHook(renderNode, env, scope, _tagName, params,
let isTopLevel = false;
let isDasherized = false;
- let angles = IS_ANGLE_CACHE.get(tagName);
+ let angles = IS_ANGLE_CACHE.get(_tagName);
if (angles) {
- tagName = angles[2];
+ if (!isClosureComponent) {
+ tagName = angles[2];
+ }
isAngleBracket = true;
isTopLevel = !!angles[1];
}
@@ -164,3 +168,15 @@ export default function componentHook(renderNode, env, scope, _tagName, params,
manager.render(env, visitor);
}
}
+
+function getComponentCell(env, scope, _tagName) {
+ let tagName = _tagName;
+
+ // Clean tagName if it is a glimmer component
+ if (tagName.charAt(0) === '<') {
+ tagName = tagName.substring(1, tagName.length - 1);
+ }
+
+ let stream = env.hooks.get(env, scope, tagName);
+ return stream.value();
+}
diff --git a/packages/ember-htmlbars/tests/helpers/closure_component_test.js b/packages/ember-htmlbars/tests/helpers/closure_component_test.js
index ba776547acc..f8a697d3df3 100644
--- a/packages/ember-htmlbars/tests/helpers/closure_component_test.js
+++ b/packages/ember-htmlbars/tests/helpers/closure_component_test.js
@@ -1,6 +1,7 @@
import { runAppend, runDestroy } from 'ember-runtime/tests/utils';
import ComponentLookup from 'ember-views/component_lookup';
import Component from 'ember-views/components/component';
+import GlimmerComponent from 'ember-htmlbars/glimmer-component';
import compile from 'ember-template-compiler/system/compile';
import run from 'ember-metal/run_loop';
import isEnabled from 'ember-metal/features';
@@ -26,76 +27,30 @@ if (isEnabled('ember-contextual-components')) {
}
});
- QUnit.test('renders with component helper', function() {
+ QUnit.test('[Component] renders with component helper', function() {
let expectedText = 'Hodi';
- owner.register(
- 'template:components/-looked-up',
- compile(expectedText)
- );
-
- let template = compile('{{component (component "-looked-up")}}');
- component = Component.extend({
- [OWNER]: owner,
- template
- }).create();
- runAppend(component);
- equal(component.$().text(), expectedText, '-looked-up component rendered');
- });
-
- QUnit.test('renders with component helper with invocation params, hash', function() {
- let LookedUp = Component.extend();
- LookedUp.reopenClass({
- positionalParams: ['name']
- });
owner.register(
'component:-looked-up',
- LookedUp
- );
- owner.register(
- 'template:components/-looked-up',
- compile(`{{greeting}} {{name}}`)
- );
-
- let template = compile(
- `{{component (component "-looked-up") "Hodari" greeting="Hodi"}}`
+ Component.extend()
);
- component = Component.extend({
- [OWNER]: owner,
- template
- }).create();
-
- runAppend(component);
- equal(component.$().text(), 'Hodi Hodari', '-looked-up component rendered');
- });
- QUnit.test('renders with component helper with curried params, hash', function() {
- let LookedUp = Component.extend();
- LookedUp.reopenClass({
- positionalParams: ['name']
- });
- owner.register(
- 'component:-looked-up',
- LookedUp
- );
owner.register(
'template:components/-looked-up',
- compile(`{{greeting}} {{name}}`)
+ compile(expectedText)
);
- let template = compile(
- `{{component (component "-looked-up" "Hodari" greeting="Hodi") greeting="Hola"}}`
- );
+ let template = compile('{{component (component "-looked-up")}}');
component = Component.extend({
[OWNER]: owner,
template
}).create();
runAppend(component);
- equal(component.$().text(), 'Hola Hodari', '-looked-up component rendered');
+ equal(component.$().text(), expectedText, '-looked-up component rendered');
});
- QUnit.test('updates when component path is bound', function() {
+ QUnit.test('[Component] updates when component path is bound', function() {
let Mandarin = Component.extend();
owner.register(
'component:-mandarin',
@@ -103,11 +58,11 @@ if (isEnabled('ember-contextual-components')) {
);
owner.register(
'template:components/-mandarin',
- compile(`ni hao`)
+ compile('ni hao')
);
owner.register(
'template:components/-hindi',
- compile(`Namaste`)
+ compile('Namaste')
);
let template = compile('{{component (component lookupComponent)}}');
@@ -121,6 +76,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), `ni hao`,
'mandarin lookupComponent renders greeting');
+
run(() => {
component.set('lookupComponent', '-hindi');
});
@@ -128,14 +84,16 @@ if (isEnabled('ember-contextual-components')) {
'hindi lookupComponent renders greeting');
});
- QUnit.test('updates when curried hash argument is bound', function() {
+ QUnit.test('updates when curried hash arguments is bound in block form', function() {
owner.register(
'template:components/-looked-up',
compile(`{{greeting}}`)
);
let template = compile(
- `{{component (component "-looked-up" greeting=greeting)}}`
+ `{{#with (hash comp=(component "-looked-up" greeting=greeting)) as |my|}}
+ {{#my.comp}}{{/my.comp}}
+ {{/with}}`
);
component = Component.extend({
@@ -144,24 +102,27 @@ if (isEnabled('ember-contextual-components')) {
}).create();
runAppend(component);
- equal(component.$().text(), '', '-looked-up component rendered');
+ equal(component.$().text().trim(), '', '-looked-up component rendered');
run(() => {
component.set('greeting', 'Hodi');
});
- equal(component.$().text(), `Hodi`,
+ equal(component.$().text().trim(), `Hodi`,
'greeting is bound');
});
- QUnit.test('updates when curried hash arguments is bound in block form', function() {
+ QUnit.test('[Component] updates when curried hash argument is bound', function() {
+ owner.register(
+ 'component:-lookup-up',
+ Component.extend()
+ );
+
owner.register(
'template:components/-looked-up',
compile(`{{greeting}}`)
);
let template = compile(
- `{{#with (hash comp=(component "-looked-up" greeting=greeting)) as |my|}}
- {{#my.comp}}{{/my.comp}}
- {{/with}}`
+ `{{component (component "-looked-up" greeting=greeting)}}`
);
component = Component.extend({
@@ -170,69 +131,50 @@ if (isEnabled('ember-contextual-components')) {
}).create();
runAppend(component);
- equal(component.$().text().trim(), '', '-looked-up component rendered');
+ equal(component.$().text(), '', '-looked-up component rendered');
run(() => {
component.set('greeting', 'Hodi');
});
- equal(component.$().text().trim(), `Hodi`,
+ equal(component.$().text(), `Hodi`,
'greeting is bound');
});
- QUnit.test('nested components overwrites named positional parameters', function() {
- let LookedUp = Component.extend();
- LookedUp.reopenClass({
- positionalParams: ['name', 'age']
- });
- owner.register(
- 'component:-looked-up',
- LookedUp
- );
- owner.register(
- 'template:components/-looked-up',
- compile(`{{name}} {{age}}`)
- );
-
- let template = compile(
- `{{component
- (component (component "-looked-up" "Sergio" 28)
- "Marvin" 21)
- "Hodari"}}`
- );
-
+ QUnit.test('[Component] raises an assertion when component path is null', function() {
+ let template = compile(`{{component (component lookupComponent)}}`);
component = Component.extend({
[OWNER]: owner,
template
}).create();
- runAppend(component);
- equal(component.$().text(), 'Hodari 21', '-looked-up component rendered');
+ expectAssertion(() => {
+ runAppend(component);
+ });
});
- QUnit.test('nested components overwrites hash parameters', function() {
+ QUnit.test('[Component] renders with dot path', function() {
+ let expectedText = 'Hodi';
+
owner.register(
- 'template:components/-looked-up',
- compile(`{{greeting}} {{name}} {{age}}`)
+ 'component:-looked-up',
+ Component.extend()
);
- let template = compile(
- `{{component (component (component "-looked-up"
- greeting="Hola" name="Dolores" age=33)
- greeting="Hej" name="Sigmundur")
- greeting=greeting}}`
+ owner.register(
+ 'template:components/-looked-up',
+ compile(`
${expectedText}
`)
);
+ let template = compile('{{#with (hash lookedup=(component "-looked-up")) as |object|}}{{object.lookedup}}{{/with}}');
component = Component.extend({
[OWNER]: owner,
- template,
- greeting: 'Hodi'
+ template
}).create();
runAppend(component);
-
- equal(component.$().text(), 'Hodi Sigmundur 33', '-looked-up component rendered');
+ equal(component.$().text(), expectedText, '-looked-up component rendered');
});
- QUnit.test('bound outer named parameters get updated in the right scope', function() {
+ QUnit.test('[Component] bound outer named parameters get updated in the right scope', function() {
let InnerComponent = Component.extend();
InnerComponent.reopenClass({
positionalParams: ['comp']
@@ -273,7 +215,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), 'Inner 28', '-looked-up component rendered');
});
- QUnit.test('bound outer hash parameters get updated in the right scope', function() {
+ QUnit.test('[Component] bound outer hash parameters get updated in the right scope', function() {
let InnerComponent = Component.extend();
InnerComponent.reopenClass({
positionalParams: ['comp']
@@ -313,7 +255,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), 'Inner 28', '-looked-up component rendered');
});
- QUnit.test('conflicting positional and hash parameters raise and assertion if in the same closure', function() {
+ QUnit.test('[Component] conflicting positional and hash parameters raise and assertion if in the same closure', function() {
let LookedUp = Component.extend();
LookedUp.reopenClass({
positionalParams: ['name']
@@ -340,7 +282,7 @@ if (isEnabled('ember-contextual-components')) {
}, `You cannot specify both a positional param (at position 0) and the hash argument \`name\`.`);
});
- QUnit.test('conflicting positional and hash parameters does not raise and assertion if rerendered', function() {
+ QUnit.test('[Component] conflicting positional and hash parameters does not raise and assertion if rerendered', function() {
let LookedUp = Component.extend();
LookedUp.reopenClass({
positionalParams: ['name']
@@ -372,7 +314,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), 'Hodi Sergio', 'component is rendered');
});
- QUnit.test('conflicting positional and hash parameters does not raise and assertion if in the different closure', function() {
+ QUnit.test('[Component] conflicting positional and hash parameters does not raise and assertion if in the different closure', function() {
let LookedUp = Component.extend();
LookedUp.reopenClass({
positionalParams: ['name']
@@ -398,60 +340,14 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), 'Hodi Sergio', 'component is rendered');
});
- QUnit.test('raises an assertion when component path is null', function() {
- let template = compile(`{{component (component lookupComponent)}}`);
- component = Component.extend({
- [OWNER]: owner,
- template
- }).create();
-
- expectAssertion(() => {
- runAppend(component);
- });
- });
-
- QUnit.test('raises an assertion when component path is not a component name', function() {
- let template = compile(`{{component (component "not-a-component")}}`);
- component = Component.extend({
- [OWNER]: owner,
- template
- }).create();
-
- expectAssertion(() => {
- runAppend(component);
- }, `The component helper cannot be used without a valid component name. You used "not-a-component" via (component "not-a-component")`);
-
- template = compile(`{{component (component compName)}}`);
- component = Component.extend({
- [OWNER]: owner,
- template,
- compName: 'not-a-component'
- }).create();
-
- expectAssertion(() => {
- runAppend(component);
- }, `The component helper cannot be used without a valid component name. You used "not-a-component" via (component compName)`);
- });
-
- QUnit.test('renders with dot path', function() {
+ QUnit.test('[Component] renders with dot path and attr', function() {
let expectedText = 'Hodi';
+
owner.register(
- 'template:components/-looked-up',
- compile(expectedText)
+ 'component:-looked-up',
+ Component.extend()
);
- let template = compile('{{#with (hash lookedup=(component "-looked-up")) as |object|}}{{object.lookedup}}{{/with}}');
- component = Component.extend({
- [OWNER]: owner,
- template
- }).create();
-
- runAppend(component);
- equal(component.$().text(), expectedText, '-looked-up component rendered');
- });
-
- QUnit.test('renders with dot path and attr', function() {
- let expectedText = 'Hodi';
owner.register(
'template:components/-looked-up',
compile('{{expectedText}}')
@@ -469,8 +365,14 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), expectedText, '-looked-up component rendered');
});
- QUnit.test('renders with dot path curried over attr', function() {
+ QUnit.test('[Component] renders with dot path curried over attr', function() {
let expectedText = 'Hodi';
+
+ owner.register(
+ 'component:-looked-up',
+ Component.extend()
+ );
+
owner.register(
'template:components/-looked-up',
compile('{{expectedText}}')
@@ -488,7 +390,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), expectedText, '-looked-up component rendered');
});
- QUnit.test('renders with dot path and with rest positional parameters', function() {
+ QUnit.test('[Component] renders with dot path and with rest positional parameters', function() {
let LookedUp = Component.extend();
LookedUp.reopenClass({
positionalParams: 'params'
@@ -515,7 +417,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$().text(), `${expectedText},Hola`, '-looked-up component rendered with rest params');
});
- QUnit.test('renders with dot path and rest parameter does not leak', function() {
+ QUnit.test('[Component] renders with dot path and rest parameter does not leak', function() {
let value = false;
let MyComponent = Component.extend({
didReceiveAttrs() {
@@ -548,12 +450,13 @@ if (isEnabled('ember-contextual-components')) {
ok(isEmpty(value), 'value is an empty parameter');
});
- QUnit.test('renders with dot path and updates attributes', function() {
+ QUnit.test('[Component] renders with dot path and updates attributes', function() {
owner.register(
'component:my-nested-component',
Component.extend({
didReceiveAttrs() {
- this.set('myProp', this.getAttr('my-parent-attr'));
+ let prop = this.getAttr('my-parent-attr');
+ this.set('myProp', prop);
}
})
);
@@ -593,7 +496,7 @@ if (isEnabled('ember-contextual-components')) {
equal(component.$('#nested-prop').text(), '3', 'value got updated again');
});
- QUnit.test('adding parameters to a closure component\'s instance does not add it to other instances', function(assert) {
+ QUnit.test('[Component] adding parameters to a closure component\'s instance does not add it to other instances', function(assert) {
owner.register(
'template:components/select-box',
compile('{{yield (hash option=(component "select-box-option"))}}')
@@ -616,4 +519,266 @@ if (isEnabled('ember-contextual-components')) {
runAppend(component);
equal(component.$().text(), 'Foo', 'there is only one Foo');
});
+
+ QUnit.test('[Component] raises an assertion when component path is not a component name', function() {
+ let template = compile(`{{component (component "not-a-component")}}`);
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ expectAssertion(() => {
+ runAppend(component);
+ }, `The component helper cannot be used without a valid component name. You used "not-a-component" via (component "not-a-component")`);
+
+ template = compile(`{{component (component compName)}}`);
+ component = Component.extend({
+ [OWNER]: owner,
+ template,
+ compName: 'not-a-component'
+ }).create();
+
+ expectAssertion(() => {
+ runAppend(component);
+ }, `The component helper cannot be used without a valid component name. You used "not-a-component" via (component compName)`);
+ });
+
+ QUnit.test('[Component] renders with component helper with invocation params, hash', function() {
+ let LookedUp = Component.extend();
+ LookedUp.reopenClass({
+ positionalParams: ['name']
+ });
+ owner.register(
+ 'component:-looked-up',
+ LookedUp
+ );
+ owner.register(
+ 'template:components/-looked-up',
+ compile(`{{greeting}} {{name}}
`)
+ );
+
+ let template = compile(
+ `{{component (component "-looked-up") "Hodari" greeting="Hodi"}}`
+ );
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ runAppend(component);
+ equal(component.$().text(), 'Hodi Hodari', '-looked-up component rendered');
+ });
+
+ QUnit.test('[Component] renders with component helper with curried params, hash', function() {
+ let LookedUp = Component.extend();
+ LookedUp.reopenClass({
+ positionalParams: ['name']
+ });
+ owner.register(
+ 'component:-looked-up',
+ LookedUp
+ );
+ owner.register(
+ 'template:components/-looked-up',
+ compile(`{{greeting}} {{name}}`)
+ );
+
+ let template = compile(
+ `{{component (component "-looked-up" "Hodari" greeting="Hodi") greeting="Hola"}}`
+ );
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ runAppend(component);
+ equal(component.$().text(), 'Hola Hodari', '-looked-up component rendered');
+ });
+
+ QUnit.test('[Component] nested components overwrites named positional parameters', function() {
+ let LookedUp = Component.extend();
+ LookedUp.reopenClass({
+ positionalParams: ['name', 'age']
+ });
+ owner.register(
+ 'component:-looked-up',
+ LookedUp
+ );
+ owner.register(
+ 'template:components/-looked-up',
+ compile(`{{name}} {{age}}`)
+ );
+
+ let template = compile(
+ `{{component
+ (component (component "-looked-up" "Sergio" 28)
+ "Marvin" 21)
+ "Hodari"}}`
+ );
+
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ runAppend(component);
+ equal(component.$().text(), 'Hodari 21', '-looked-up component rendered');
+ });
+
+ QUnit.test('[Component] nested components overwrites hash parameters', function() {
+ owner.register(
+ 'component:-looked-up',
+ Component.extend()
+ );
+
+ owner.register(
+ 'template:components/-looked-up',
+ compile(`{{greeting}} {{name}} {{age}}`)
+ );
+
+ let template = compile(
+ `{{component (component (component "-looked-up"
+ greeting="Hola" name="Dolores" age=33)
+ greeting="Hej" name="Sigmundur")
+ greeting=greeting}}`
+ );
+
+ component = Component.extend({
+ [OWNER]: owner,
+ template,
+ greeting: 'Hodi'
+ }).create();
+
+ runAppend(component);
+
+ equal(component.$().text(), 'Hodi Sigmundur 33', '-looked-up component rendered');
+ });
+
+ if (isEnabled('ember-htmlbars-component-generation')) {
+ QUnit.test('[GlimmerComponent] it does not render with component helper', function() {
+ owner.register(
+ 'component:-looked-up',
+ GlimmerComponent.extend()
+ );
+
+ owner.register(
+ 'template:components/-looked-up',
+ compile('Hola
')
+ );
+
+ let template = compile('{{component (component "-looked-up")}}');
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ expectAssertion(function() {
+ runAppend(component);
+ }, /curly braces/);
+ });
+
+ QUnit.test('[GlimmerComponent] it does not render with dot path and curly braces', function() {
+ owner.register(
+ 'component:-looked-up',
+ GlimmerComponent.extend()
+ );
+
+ owner.register(
+ 'template:components/-looked-up',
+ compile('Hola
')
+ );
+
+ let template = compile(`{{#with (hash comp=(component "-looked-up")) as |my|}}
+ {{my.comp}}
+ {{/with}}`);
+
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ expectAssertion(function() {
+ runAppend(component);
+ }, /curly braces/);
+ });
+
+ QUnit.test('[GlimmerComponent] it renders with angle brackets', function() {
+ owner.register(
+ 'component:-looked-up',
+ GlimmerComponent.extend()
+ );
+
+ owner.register(
+ 'template:components/-looked-up',
+ compile('Hola
')
+ );
+
+ let template = compile(`{{#with (hash comp=(component "-looked-up")) as |my|}}
+
+ {{/with}}`);
+
+ component = Component.extend({
+ [OWNER]: owner,
+ template
+ }).create();
+
+ runAppend(component);
+
+ equal(component.$().text().trim(), 'Hola', 'component gets rendered');
+ });
+
+ QUnit.test('[GlimmerComponent] it receives the attributes', function() {
+ owner.register('component:my-glimmer-component', GlimmerComponent.extend({
+ layout: compile('{{value}}'),
+ didReceiveAttrs() {
+ this._super(...arguments);
+ console.log(this.attrs);
+ this.set(`value`, this.attrs.value);
+ }
+ }));
+ let template = compile(`{{#with (hash glimmer=(component "my-glimmer-component")) as |comp|}}
+
+ {{/with}}`);
+
+ component = Component.extend({
+ [OWNER]: owner,
+ val: 12,
+ template
+ }).create();
+
+ runAppend(component);
+
+ equal(component.$().text().trim(), '12', 'component gets the right value');
+ });
+
+ QUnit.test('[GlimmerComponent] it updates the attributes', function() {
+ owner.register('component:my-glimmer-component', GlimmerComponent.extend({
+ layout: compile('{{value}}'),
+ didReceiveAttrs() {
+ this._super(...arguments);
+ console.log(this.attrs);
+ this.set(`value`, this.attrs.value);
+ }
+ }));
+ let template = compile(`{{#with (hash glimmer=(component "my-glimmer-component")) as |comp|}}
+
+ {{/with}}`);
+
+ component = Component.extend({
+ [OWNER]: owner,
+ val: 12,
+ template
+ }).create();
+
+ runAppend(component);
+
+ equal(component.$().text().trim(), '12', 'component gets the right value');
+
+ run(() => {
+ component.set('val', 14);
+ });
+
+ equal(component.$().text().trim(), '14', 'attribute got updated');
+ });
+ }
}