Skip to content

Commit 8f38585

Browse files
committed
Merge pull request #13337 from asakusuma/g2-cb
[Glimmer2] Fix class bindings
2 parents bca76a9 + 7b30ab0 commit 8f38585

File tree

3 files changed

+158
-2
lines changed

3 files changed

+158
-2
lines changed

packages/ember-glimmer/lib/environment.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,39 @@ function wrapClassAttribute(args) {
6262
return args;
6363
}
6464

65+
function wrapClassBindingAttribute(args) {
66+
let hasClassBinding = args.named.has('classBinding');
67+
68+
if (hasClassBinding) {
69+
let { value , type } = args.named.at('classBinding');
70+
if (type === 'value') {
71+
let [ prop, truthy, falsy ] = value.split(':');
72+
let spec;
73+
if (prop === '') {
74+
spec = ['helper', ['-class'], [truthy], null];
75+
} else if (falsy) {
76+
let parts = prop.split('.');
77+
spec = ['helper', ['-class'], [['get', parts], truthy, falsy], null];
78+
} else if (truthy) {
79+
let parts = prop.split('.');
80+
spec = ['helper', ['-class'], [['get', parts], truthy], null];
81+
}
82+
83+
if (spec) {
84+
let syntax;
85+
if (args.named.has('class')) {
86+
// If class already exists, merge
87+
let classValue = args.named.at('class').value;
88+
syntax = HelperSyntax.fromSpec(['helper', ['concat'], [classValue, ' ', spec]]);
89+
} else {
90+
syntax = HelperSyntax.fromSpec(spec);
91+
}
92+
args.named.add('class', syntax);
93+
}
94+
}
95+
}
96+
}
97+
6598
export default class Environment extends GlimmerEnvironment {
6699
static create(options) {
67100
return new Environment(options);
@@ -94,6 +127,7 @@ export default class Environment extends GlimmerEnvironment {
94127
let definition = this.getComponentDefinition(path);
95128

96129
if (definition) {
130+
wrapClassBindingAttribute(args);
97131
wrapClassAttribute(args);
98132
return new CurlyComponentSyntax({ args, definition, templates });
99133
}

packages/ember-glimmer/lib/helpers/-class.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@ import { dasherize } from 'ember-runtime/system/string';
33

44
function classHelper({ positional }) {
55
let path = positional.at(0);
6-
let propName = positional.at(1);
6+
let truthyPropName = positional.at(1);
7+
let falsyPropName = positional.at(2);
78
let value = path.value();
89

910
if (value === true) {
10-
return dasherize(propName.value());
11+
if (truthyPropName) {
12+
return dasherize(truthyPropName.value());
13+
}
14+
return null;
1115
}
1216

1317
if (value === false) {
18+
if (falsyPropName) {
19+
return dasherize(falsyPropName.value());
20+
}
1421
return null;
1522
}
1623

packages/ember-glimmer/tests/integration/components/curly-components-test.js

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ import { classes, equalTokens, equalsElement } from '../../utils/test-helpers';
99
import { htmlSafe } from 'ember-htmlbars/utils/string';
1010
import { computed } from 'ember-metal/computed';
1111

12+
const bindingDeprecationMessage = '`Ember.Binding` is deprecated. Consider' +
13+
' using an `alias` computed property instead.';
14+
1215
moduleFor('Components test: curly components', class extends RenderingTest {
1316

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

206+
['@glimmer it should merge classBinding with class']() {
207+
expectDeprecation(bindingDeprecationMessage);
208+
209+
this.registerComponent('foo-bar', { template: 'hello' });
210+
211+
this.render('{{foo-bar classBinding="birdman:respeck" class="myName"}}', { birdman: true });
212+
213+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck myName ember-view') } });
214+
215+
this.runTask(() => this.rerender());
216+
217+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck myName ember-view') } });
218+
}
219+
220+
['@glimmer in glimmer it should apply classBinding without condition always']() {
221+
expectDeprecation(bindingDeprecationMessage);
222+
223+
this.registerComponent('foo-bar', { template: 'hello' });
224+
225+
this.render('{{foo-bar classBinding=":foo"}}');
226+
227+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('foo ember-view') } });
228+
229+
this.runTask(() => this.rerender());
230+
231+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('foo ember-view') } });
232+
}
233+
234+
['@glimmer it should apply classBinding with only truthy condition']() {
235+
expectDeprecation(bindingDeprecationMessage);
236+
237+
this.registerComponent('foo-bar', { template: 'hello' });
238+
239+
this.render('{{foo-bar classBinding="myName:respeck"}}', { myName: true });
240+
241+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck ember-view') } });
242+
243+
this.runTask(() => this.rerender());
244+
245+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('respeck ember-view') } });
246+
}
247+
248+
['@glimmer it should apply classBinding with only falsy condition']() {
249+
expectDeprecation(bindingDeprecationMessage);
250+
251+
this.registerComponent('foo-bar', { template: 'hello' });
252+
253+
this.render('{{foo-bar classBinding="myName::shade"}}', { myName: false });
254+
255+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('shade ember-view') } });
256+
257+
this.runTask(() => this.rerender());
258+
259+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('shade ember-view') } });
260+
}
261+
262+
['@glimmer it should apply nothing when classBinding is falsy but only supplies truthy class']() {
263+
expectDeprecation(bindingDeprecationMessage);
264+
265+
this.registerComponent('foo-bar', { template: 'hello' });
266+
267+
this.render('{{foo-bar classBinding="myName:respeck"}}', { myName: false });
268+
269+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
270+
271+
this.runTask(() => this.rerender());
272+
273+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
274+
}
275+
276+
['@glimmer it should apply nothing when classBinding is truthy but only supplies falsy class']() {
277+
expectDeprecation(bindingDeprecationMessage);
278+
279+
this.registerComponent('foo-bar', { template: 'hello' });
280+
281+
this.render('{{foo-bar classBinding="myName::shade"}}', { myName: true });
282+
283+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
284+
285+
this.runTask(() => this.rerender());
286+
287+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('ember-view') } });
288+
}
289+
290+
['@glimmer it should apply classBinding with falsy condition']() {
291+
expectDeprecation(bindingDeprecationMessage);
292+
293+
this.registerComponent('foo-bar', { template: 'hello' });
294+
295+
this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { swag: false });
296+
297+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('scrub ember-view') } });
298+
299+
this.runTask(() => this.rerender());
300+
301+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('scrub ember-view') } });
302+
}
303+
304+
['@glimmer it should apply classBinding with truthy condition']() {
305+
expectDeprecation(bindingDeprecationMessage);
306+
307+
this.registerComponent('foo-bar', { template: 'hello' });
308+
309+
this.render('{{foo-bar classBinding="swag:fresh:scrub"}}', { swag: true });
310+
311+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('fresh ember-view') } });
312+
313+
this.runTask(() => this.rerender());
314+
315+
this.assertComponentElement(this.firstChild, { tagName: 'div', content: 'hello', attrs: { 'class': classes('fresh ember-view') } });
316+
}
317+
203318
['@test should not apply falsy class name']() {
204319
this.registerComponent('foo-bar', { template: 'hello' });
205320

0 commit comments

Comments
 (0)