Skip to content

Commit

Permalink
bem-xjst: short syntax for simple modes (fix #444)
Browse files Browse the repository at this point in the history
  • Loading branch information
miripiruni committed Jan 22, 2018
1 parent 36e253c commit 536d1df
Show file tree
Hide file tree
Showing 2 changed files with 371 additions and 3 deletions.
40 changes: 37 additions & 3 deletions lib/bemxjst/tree.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,13 +326,26 @@ Tree.prototype.flush = function(conditions, item) {

// Body
} else {
var template = new Template(conditions, arg);
template.wrap();
this.templates.push(template);
if (this.isShortcutAllowed(arg, conditions)) {
var keys = Object.keys(arg);
for (var n = 0; n < keys.length; n++) {
var newC = conditions.concat(new PropertyMatch('_mode', keys[n]));
var body = arg[keys[n]];
this.addTemplate(newC, body);
}
} else {
this.addTemplate(conditions, arg);
}
}
}
};

Tree.prototype.addTemplate = function(conditions, arg) {
var template = new Template(conditions, arg);
template.wrap();
this.templates.push(template);
};

Tree.prototype.body = function() {
var children = new Array(arguments.length);
for (var i = 0; i < arguments.length; i++)
Expand All @@ -347,6 +360,27 @@ Tree.prototype.body = function() {
return this.boundBody;
};

Tree.modsCheck = { mods: 1, elemMods: 1 };

Tree.prototype.checkConditions = function(conditions) {
for (var i = 0; i < conditions.length; i++) {
var condition = conditions[i];
if (condition.key === 'block' ||
condition.key === 'elem' ||
(Array.isArray(condition.key) && Tree.modsCheck[condition.key[0]]) ||
condition instanceof CustomMatch) continue;
return false;
}

return true;
};

Tree.prototype.isShortcutAllowed = function(arg, conditions) {
return typeof arg === 'object' &&
!Array.isArray(arg) &&
this.checkConditions(conditions);
};

Tree.prototype.match = function() {
var children = new Array(arguments.length);

Expand Down
334 changes: 334 additions & 0 deletions test/templates-syntax-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,338 @@ describe('Templates syntax', function() {
}, BEMXJSTError);
Error.captureStackTrace = captureStackTrace;
});

describe('shortcut', function() {
describe('should work in chain with "non output modes"', function() {
it('block() mode only', function() {
test(function() {
block('button')({
tag: 'button',
mix: 'mixed'
});
},
{ block: 'button' },
'<button class="button mixed"></button>');
});

it('match() mode', function() {
test(function() {
block('b').match(function() {
return this.ctx.flag;
})({ tag: 'span', mix: 'mixed' });
},
{ block: 'b', flag: 1 },
'<span class="b mixed"></span>');
});

it('mod() mode', function() {
test(function() {
block('b').mod('modName', 'modVal')({ tag: 'span', mix: 'mixed' });
},
{ block: 'b', mods: { modName: 'modVal' } },
'<span class="b b_modName_modVal mixed"></span>');
});

it('elem() mode', function() {
test(function() {
block('b').elem('e')({ tag: 'span', mix: 'mixed' });
},
{ block: 'b', elem: 'e' },
'<span class="b__e mixed"></span>');
});

it('elemMod() mode', function() {
test(function() {
block('b')
.elem('e')
.elemMod('elemModName', 'elemModVal')
({ tag: 'span', mix: 'mixed' });
},
{ block: 'b', elem: 'e', elemMods: { elemModName: 'elemModVal' } },
'<span class="b__e b__e_elemModName_elemModVal mixed"></span>');
});

it('custom user defined mode', function() {
test(function() {
block('b')({
myMode: function() { return 1 + 1; }
});

block('b').content()(function() {
return apply('myMode');
});
},
{ block: 'b' },
'<div class="b">2</div>');
});
});

describe('should not work in chain with "output modes"', function() {
it('addAttrs() mode', function() {
test(function() {
block('b').addAttrs()({
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b', attrs: { 'data-test': 'one-two' } },
'<div class="b" data-test="one-two" ' +
'mix="doesnt-work-as-mix-for-block-b"></div>');
});

it('addElemMods() mode', function() {
test(function() {
block('b').elem('e').addElemMods()({
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b', elem: 'e' },
'<div class="b__e b__e_mix_doesnt-work-as-mix-for-block-b"></div>');
});

it('addJs() mode', function() {
test(function() {
block('b').addJs()({
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b' },
'<div class="b i-bem" data-bem=\'' +
'{"b":{"mix":"doesnt-work-as-mix-for-block-b"}}\'></div>');
});

it('addMix() mode', function() {
test(function() {
block('b').addMix()({
content: 'doesnt-work-as-content-for-block-b'
});
},
{ block: 'b' },
'<div class="b"></div>');
});

it('addMods() mode', function() {
test(function() {
block('b').addMods()({
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b' },
'<div class="b b_mix_doesnt-work-as-mix-for-block-b"></div>');
});

it('appendContent() mode', function() {
test(function() {
block('b').appendContent()({
block: 'regular-content',
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b', content: 'test' },
'<div class="b">test' +
'<div class="regular-content doesnt-work-as-mix-for-block-b">' +
'</div>' +
'</div>');
});

it('attrs() mode', function() {
test(function() {
block('b').attrs()({
id: 'test',
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b' },
'<div class="b" id="test" mix="doesnt-work-as-mix-for-block-b">' +
'</div>');
});

it('bem() mode', function() {
test(function() {
block('b').bem()({
content: 'doesnt-work',
mix: 'doesnt-work'
});
},
{ block: 'b' },
'<div class="b"></div>');
});

it('cls() mode', function() {
test(function() {
block('b').cls()({
mix: 'doesnt-work',
toString: function() { return 'regular-html-class'; }
});
},
{ block: 'b' },
'<div class="b regular-html-class"></div>');
});

it('content() mode', function() {
test(function() {
block('b').content()({
block: 'more',
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b' },
'<div class="b">' +
'<div class="more doesnt-work-as-mix-for-block-b"></div>' +
'</div>');
});

it('custom user defined mode', function() {
test(function() {
block('b').content()({
any: function() { return 1 + 1; }
});

block('b').content()(function() {
return apply('any');
});
},
{ block: 'b' },
'<div class="b"></div>');
});

it('def() mode', function() {
test(function() {
block('b').def()({
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b' },
{ mix: 'doesnt-work-as-mix-for-block-b' });
});

it('elemMods() mode', function() {
test(function() {
block('b').elem('e').elemMods()({
shortcut: 'doesnt-work',
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b', elem: 'e' },
'<div class="b__e b__e_shortcut_doesnt-work ' +
'b__e_mix_doesnt-work-as-mix-for-block-b"></div>');
});

it('prependContent() mode', function() {
test(function() {
block('b').prependContent()({
block: 'regular-content',
mix: 'doesnt-work-as-mix-for-block-b'
});
},
{ block: 'b', content: 'test' },
'<div class="b">' +
'<div class="regular-content doesnt-work-as-mix-for-block-b">' +
'</div>' +
'test</div>');
});

it('elemMods() mode', function() {
test(function() {
block('b').tag()({
toString: function() { return 'html'; }
});
},
{ block: 'b' },
'<html class="b"></html>');
});

// Checked:
// 'extend',
// 'js',
// 'mix',
// 'mods',
// 'replace',
// 'wrap'
});

describe('composition tests', function() {
it('should work in composition', function() {
test(function() {
block('button')(
{
tag: 'button',
mix: 'mixed'
},
content()('bem-xjst')
);
},
{ block: 'button' },
'<button class="button mixed">bem-xjst</button>');
});

it('more complex test', function() {
test(function() {
block('button')(
{ attrs: { id: 'from-shortcut' } },
match(function() { return true; })(
{
tag: 'button',
mix: 'mixed'
},
content()('bem-xjst')
)
);
},
{ block: 'button' },
'<button class="button mixed" id="from-shortcut">bem-xjst</button>');
});
});

it('should support body as function', function() {
test(function() {
block('b')({
attrs: function() {
return { id: 'from-shortcut' };
},
content: function() {
return 42;
}
});
},
{ block: 'b' },
'<div class="b" id="from-shortcut">42</div>');
});

it('should support arguments in body function', function() {
test(function() {
block('b')({
content: function(node, ctx) { return ctx.answer; },
mix: function(node) { return node.ctx.customMix; }
});
},
{ block: 'b', answer: 42, customMix: 'from-shortcut' },
'<div class="b from-shortcut">42</div>');
});

it('should support applyNext() in body function', function() {
test(function() {
block('b')(
content()(function() { return applyNext() + ' 2'; }),
{ content: function() { return applyNext() + ' 3'; } },
match(function() { return true; })(
content()(function() { return applyNext() + ' 4'; }),
{ content: function() { return applyNext() + ' 5'; } }
)
);
},
{ block: 'b', content: '1' },
'<div class="b">1 2 3 4 5</div>');
});

it('should support applyCtx() in body function', function() {
test(function() {
block('test').def()('OK');
block('b')(
{ content: function() { return applyCtx({ block: 'test' }); } }
);
},
{ block: 'b', content: '1' },
'<div class="b">OK</div>');
});
});
});

0 comments on commit 536d1df

Please sign in to comment.