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

[Glimmer2] Fix class bindings #13337

Merged
merged 1 commit into from
May 6, 2016
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
34 changes: 34 additions & 0 deletions packages/ember-glimmer/lib/environment.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,39 @@ function wrapClassAttribute(args) {
return args;
}

function wrapClassBindingAttribute(args) {
let hasClassBinding = args.named.has('classBinding');

if (hasClassBinding) {
let { value , type } = args.named.at('classBinding');
if (type === 'value') {
let [ prop, truthy, falsy ] = value.split(':');
let spec;
if (prop === '') {
spec = ['helper', ['-class'], [truthy], null];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't need the helper

} else if (falsy) {
let parts = prop.split('.');
spec = ['helper', ['-class'], [['get', parts], truthy, falsy], null];
} else if (truthy) {
let parts = prop.split('.');
spec = ['helper', ['-class'], [['get', parts], truthy], null];
}

if (spec) {
let syntax;
if (args.named.has('class')) {
// If class already exists, merge
let classValue = args.named.at('class').value;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This currently won't work with helpers. If we need to support this, we'll need a way to generate a spec from a helper.

syntax = HelperSyntax.fromSpec(['helper', ['concat'], [classValue, ' ', spec]]);
} else {
syntax = HelperSyntax.fromSpec(spec);
}
args.named.add('class', syntax);
}
}
}
}

export default class Environment extends GlimmerEnvironment {
static create(options) {
return new Environment(options);
Expand Down Expand Up @@ -94,6 +127,7 @@ export default class Environment extends GlimmerEnvironment {
let definition = this.getComponentDefinition(path);

if (definition) {
wrapClassBindingAttribute(args);
wrapClassAttribute(args);
return new CurlyComponentSyntax({ args, definition, templates });
}
Expand Down
11 changes: 9 additions & 2 deletions packages/ember-glimmer/lib/helpers/-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,21 @@ import { dasherize } from 'ember-runtime/system/string';

function classHelper({ positional }) {
let path = positional.at(0);
let propName = positional.at(1);
let truthyPropName = positional.at(1);
let falsyPropName = positional.at(2);
let value = path.value();

if (value === true) {
return dasherize(propName.value());
if (truthyPropName) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this not the case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return dasherize(truthyPropName.value());
}
return null;
}

if (value === false) {
if (falsyPropName) {
return dasherize(falsyPropName.value());
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { classes, equalTokens, equalsElement } from '../../utils/test-helpers';
import { htmlSafe } from 'ember-htmlbars/utils/string';
import { computed } from 'ember-metal/computed';

const bindingDeprecationMessage = '`Ember.Binding` is deprecated. Consider' +
' using an `alias` computed property instead.';

moduleFor('Components test: curly components', class extends RenderingTest {

['@test it can render a basic component']() {
Expand Down Expand Up @@ -200,6 +203,118 @@ moduleFor('Components test: curly components', class extends RenderingTest {
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('foo ember-view') } });
}

['@glimmer it should merge classBinding with class']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="birdman:respeck" class="myName"}}', { birdman: true });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck myName ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck myName ember-view') } });
}

['@glimmer in glimmer it should apply classBinding without condition always']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding=":foo"}}');

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('foo ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('foo ember-view') } });
}

['@glimmer it should apply classBinding with only truthy condition']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="myName:respeck"}}', { myName: true });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck ember-view') } });
}

['@glimmer it should apply classBinding with only falsy condition']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="myName::shade"}}', { myName: false });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('shade ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('shade ember-view') } });
}

['@glimmer it should apply nothing when classBinding is falsy but only supplies truthy class']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="myName:respeck"}}', { myName: false });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
}

['@glimmer it should apply nothing when classBinding is truthy but only supplies falsy class']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="myName::shade"}}', { myName: true });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
}

['@glimmer it should apply classBinding with falsy condition']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { swag: false });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('scrub ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('scrub ember-view') } });
}

['@glimmer it should apply classBinding with truthy condition']() {
expectDeprecation(bindingDeprecationMessage);

this.registerComponent('foo-bar', { template: 'hello' });

this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { swag: true });

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('fresh ember-view') } });

this.runTask(() => this.rerender());

this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('fresh ember-view') } });
}

['@test should not apply falsy class name']() {
this.registerComponent('foo-bar', { template: 'hello' });

Expand Down