mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-02-10 23:36:14 -05:00
✨Added {{link_class}} helper
- moved dynamic class logic out of {{link}} helper into shared utils - both {{link}} and {{link_class}} use these utils
This commit is contained in:
parent
833fe49e6f
commit
b48fdaf1be
8 changed files with 404 additions and 82 deletions
|
@ -23,6 +23,7 @@ coreHelpers.is = require('./is');
|
|||
coreHelpers.has = require('./has');
|
||||
coreHelpers.lang = require('./lang');
|
||||
coreHelpers.link = require('./link');
|
||||
coreHelpers.link_class = require('./link_class');
|
||||
coreHelpers.meta_description = require('./meta_description');
|
||||
coreHelpers.meta_title = require('./meta_title');
|
||||
coreHelpers.navigation = require('./navigation');
|
||||
|
@ -57,6 +58,7 @@ registerAllCoreHelpers = function registerAllCoreHelpers() {
|
|||
registerThemeHelper('img_url', coreHelpers.img_url);
|
||||
registerThemeHelper('lang', coreHelpers.lang);
|
||||
registerThemeHelper('link', coreHelpers.link);
|
||||
registerThemeHelper('link_class', coreHelpers.link_class);
|
||||
registerThemeHelper('meta_description', coreHelpers.meta_description);
|
||||
registerThemeHelper('meta_title', coreHelpers.meta_title);
|
||||
registerThemeHelper('navigation', coreHelpers.navigation);
|
||||
|
|
|
@ -1,51 +1,9 @@
|
|||
// # link helper
|
||||
const _ = require('lodash');
|
||||
const {config, SafeString} = require('./proxy');
|
||||
const {config, SafeString, errors, i18n} = require('./proxy');
|
||||
const {buildLinkClasses} = require('./utils');
|
||||
|
||||
const managedAttributes = ['href', 'class', 'activeClass', 'parentActiveClass', 'tagName', 'nohref'];
|
||||
|
||||
function _getHref(hash) {
|
||||
let href = hash.href || '/';
|
||||
return href.string ? href.string : href;
|
||||
}
|
||||
|
||||
function _clean(url) {
|
||||
// Strips anchors and leading and trailing slashes
|
||||
return url.replace(/#.*?$/, '').replace(/^\/|\/$/g, '');
|
||||
}
|
||||
|
||||
// strips trailing slashes and compares urls
|
||||
function _urlMatch(href, location) {
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const strippedHref = _clean(href);
|
||||
const strippedLocation = _clean(location);
|
||||
|
||||
return strippedHref === strippedLocation;
|
||||
}
|
||||
|
||||
// We want to check if the first part of the current url is a match for href
|
||||
function _parentMatch(href, location) {
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent = false;
|
||||
let locParts = _clean(location).split('/');
|
||||
let hrefParts = _clean(href).split('/');
|
||||
|
||||
if (locParts.length <= hrefParts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < hrefParts.length; i += 1) {
|
||||
parent = hrefParts[i] === locParts[i];
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
const managedAttributes = ['href', 'class', 'activeClass', 'parentActiveClass'];
|
||||
|
||||
function _formatAttrs(attributes) {
|
||||
let attributeString = '';
|
||||
|
@ -64,13 +22,23 @@ module.exports = function link(options) {
|
|||
options.hash = options.hash || {};
|
||||
options.data = options.data || {};
|
||||
|
||||
let href = _getHref(options.hash);
|
||||
let location = options.data.root.relativeUrl;
|
||||
let tagName = options.hash.tagName || 'a';
|
||||
let activeClass = _.has(options.hash, 'activeClass') ? options.hash.activeClass : 'nav-current';
|
||||
let parentActiveClass = _.has(options.hash, 'parentActiveClass') ? options.hash.parentActiveClass : `${activeClass || 'nav-current'}-parent`;
|
||||
let classes = options.hash.class ? options.hash.class.toString().split(' ') : [];
|
||||
let noHref = _.has(options.hash, 'nohref') ? options.hash.nohref : false;
|
||||
// If there is no href provided, this is theme dev error, so we throw an error to make this clear.
|
||||
if (!_.has(options.hash, 'href')) {
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.link.hrefIsRequired')
|
||||
});
|
||||
}
|
||||
// If the href attribute is empty, this is probably a dynamic data problem, hard for theme devs to track down
|
||||
// E.g. {{#link for=slug}}{{/link}} in a context where slug returns an empty string
|
||||
// Error's here aren't useful (same as with empty get helper filters) so we fallback gracefully
|
||||
if (!options.hash.href) {
|
||||
options.hash.href = '';
|
||||
}
|
||||
|
||||
let href = options.hash.href.string || options.hash.href;
|
||||
|
||||
// Calculate dynamic properties
|
||||
let classes = buildLinkClasses(config.get('url'), href, options);
|
||||
|
||||
// Remove all the attributes we don't want to do a one-to-one mapping of
|
||||
managedAttributes.forEach((attr) => {
|
||||
|
@ -80,20 +48,13 @@ module.exports = function link(options) {
|
|||
// Setup our one-to-one mapping of attributes;
|
||||
let attributes = options.hash;
|
||||
|
||||
// Calculate dynamic properties
|
||||
let relativeHref = href.replace(config.get('url'), '');
|
||||
if (_urlMatch(relativeHref, location) && activeClass) {
|
||||
classes.push(activeClass);
|
||||
} else if (_parentMatch(relativeHref, location) && parentActiveClass) {
|
||||
classes.push(parentActiveClass);
|
||||
}
|
||||
|
||||
// Prepare output
|
||||
let classString = classes.length > 0 ? `class="${classes.join(' ')}"` : '';
|
||||
let hrefString = !noHref ? `href="${href}"` : '';
|
||||
let hrefString = `href="${href}"`;
|
||||
let attributeString = _.size(attributes) > 0 ? _formatAttrs(attributes) : '';
|
||||
let openingTag = `<${tagName} ${classString} ${hrefString} ${attributeString}>`;
|
||||
let closingTag = `</${tagName}>`;
|
||||
let openingTag = `<a ${classString} ${hrefString} ${attributeString}>`;
|
||||
let closingTag = `</a>`;
|
||||
|
||||
// Clean up any extra spaces
|
||||
openingTag = openingTag.replace(/\s{2,}/g, ' ').replace(/\s>/, '>');
|
||||
|
||||
|
|
29
core/frontend/helpers/link_class.js
Normal file
29
core/frontend/helpers/link_class.js
Normal file
|
@ -0,0 +1,29 @@
|
|||
// # link_class helper
|
||||
const _ = require('lodash');
|
||||
const {config, SafeString, errors, i18n} = require('./proxy');
|
||||
const {buildLinkClasses} = require('./utils');
|
||||
|
||||
module.exports = function link_class(options) { // eslint-disable-line camelcase
|
||||
options = options || {};
|
||||
options.hash = options.hash || {};
|
||||
options.data = options.data || {};
|
||||
|
||||
// If there is no for provided, this is theme dev error, so we throw an error to make this clear.
|
||||
if (!_.has(options.hash, 'for')) {
|
||||
throw new errors.IncorrectUsageError({
|
||||
message: i18n.t('warnings.helpers.link_class.forIsRequired')
|
||||
});
|
||||
}
|
||||
|
||||
// If the for attribute is present but empty, this is probably a dynamic data problem, hard for theme devs to track down
|
||||
// E.g. {{link_class for=slug}} in a context where slug returns an empty string
|
||||
// Error's here aren't useful (same as with empty get helper filters) so we fallback gracefully
|
||||
if (!options.hash.for) {
|
||||
options.hash.for = '';
|
||||
}
|
||||
|
||||
let href = options.hash.for.string || options.hash.for;
|
||||
let classes = buildLinkClasses(config.get('url'), href, options);
|
||||
|
||||
return new SafeString(classes.join(' '));
|
||||
};
|
|
@ -11,3 +11,58 @@ module.exports.findKey = function findKey(key /* ...objects... */) {
|
|||
return result;
|
||||
}, null);
|
||||
};
|
||||
|
||||
function _urlClean(url) {
|
||||
// Strips anchors and leading and trailing slashes
|
||||
return url.replace(/#.*?$/, '').replace(/^\/|\/$/g, '');
|
||||
}
|
||||
|
||||
// strips trailing slashes and compares urls
|
||||
function _urlMatch(href, location) {
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const strippedHref = _urlClean(href);
|
||||
const strippedLocation = _urlClean(location);
|
||||
|
||||
return strippedHref === strippedLocation;
|
||||
}
|
||||
|
||||
// We want to check if the first part of the current url is a match for href
|
||||
function _urlParentMatch(href, location) {
|
||||
if (!location) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent = false;
|
||||
let locParts = _urlClean(location).split('/');
|
||||
let hrefParts = _urlClean(href).split('/');
|
||||
|
||||
if (locParts.length <= hrefParts.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = 0; i < hrefParts.length; i += 1) {
|
||||
parent = hrefParts[i] === locParts[i];
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
module.exports.buildLinkClasses = function buildLinkClasses(siteUrl, href, options) {
|
||||
let relativeHref = href.replace(siteUrl, '');
|
||||
let location = options.data.root.relativeUrl;
|
||||
let classes = options.hash.class ? options.hash.class.toString().split(' ') : [];
|
||||
let activeClass = _.has(options.hash, 'activeClass') ? options.hash.activeClass : 'nav-current';
|
||||
let parentActiveClass = _.has(options.hash, 'parentActiveClass') ? options.hash.parentActiveClass : `${activeClass || 'nav-current'}-parent`;
|
||||
|
||||
// Calculate dynamic properties
|
||||
if (_urlMatch(relativeHref, location) && activeClass) {
|
||||
classes.push(activeClass);
|
||||
} else if (_urlParentMatch(relativeHref, location) && parentActiveClass) {
|
||||
classes.push(parentActiveClass);
|
||||
}
|
||||
|
||||
return classes;
|
||||
};
|
||||
|
|
|
@ -596,6 +596,12 @@
|
|||
"is": {
|
||||
"invalidAttribute": "Invalid or no attribute given to is helper"
|
||||
},
|
||||
"link": {
|
||||
"hrefIsRequired": "The \\{\\{#link\\}\\}\\{\\{/link\\}\\} helper requires an href=\"\" attribute."
|
||||
},
|
||||
"link_class": {
|
||||
"forIsRequired": "The \\{\\{link_class\\}\\} helper requires a for=\"\" attribute."
|
||||
},
|
||||
"navigation": {
|
||||
"invalidData": "navigation data is not an object or is a function",
|
||||
"valuesMustBeDefined": "All values must be defined for label, url and current",
|
||||
|
|
|
@ -9,7 +9,7 @@ describe('Helpers', function () {
|
|||
var hbsHelpers = ['each', 'if', 'unless', 'with', 'helperMissing', 'blockHelperMissing', 'log', 'lookup', 'block', 'contentFor'],
|
||||
ghostHelpers = [
|
||||
'asset', 'author', 'authors', 'body_class', 'concat', 'content', 'date', 'encode', 'excerpt', 'facebook_url', 'foreach', 'get',
|
||||
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'lang', 'link', 'meta_description', 'meta_title', 'navigation',
|
||||
'ghost_foot', 'ghost_head', 'has', 'img_url', 'is', 'lang', 'link', 'link_class', 'meta_description', 'meta_title', 'navigation',
|
||||
'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'reading_time', 't', 'tags', 'title', 'twitter_url',
|
||||
'url'
|
||||
],
|
||||
|
|
276
core/test/unit/helpers/link_class_spec.js
Normal file
276
core/test/unit/helpers/link_class_spec.js
Normal file
|
@ -0,0 +1,276 @@
|
|||
const should = require('should');
|
||||
const helpers = require.main.require('core/frontend/helpers');
|
||||
const handlebars = require.main.require('core/frontend/services/themes/engine').handlebars;
|
||||
|
||||
const configUtils = require('../../utils/configUtils');
|
||||
|
||||
let defaultGlobals;
|
||||
|
||||
function compile(templateString) {
|
||||
const template = handlebars.compile(templateString);
|
||||
template.with = (locals = {}, globals) => {
|
||||
globals = globals || defaultGlobals;
|
||||
|
||||
return template(locals, globals);
|
||||
};
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
describe('{{link_class}} helper', function () {
|
||||
before(function () {
|
||||
handlebars.registerHelper('link_class', helpers.link_class);
|
||||
handlebars.registerHelper('concat', helpers.concat);
|
||||
configUtils.config.set('url', 'https://siteurl.com');
|
||||
defaultGlobals = {
|
||||
data: {
|
||||
site: {
|
||||
url: configUtils.config.get('url')
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
after(function () {
|
||||
configUtils.restore();
|
||||
});
|
||||
|
||||
it('throws an error for missing for=""', function () {
|
||||
(function compileWith() {
|
||||
compile('{{link_class}}')
|
||||
.with({});
|
||||
}).should.throw();
|
||||
});
|
||||
|
||||
it('silently accepts an empty for...', function () {
|
||||
compile('{{link_class for=tag.slug}}')
|
||||
.with({tag: null})
|
||||
.should.eql('');
|
||||
});
|
||||
|
||||
describe('activeClass', function () {
|
||||
it('gets applied correctly', function () {
|
||||
// Test matching relative URL
|
||||
compile('{{link_class for="/about/"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-current');
|
||||
|
||||
// Test non-matching relative URL
|
||||
compile('{{link_class for="/about/"}}')
|
||||
.with({relativeUrl: '/'})
|
||||
.should.eql('');
|
||||
});
|
||||
|
||||
it('ignores anchors', function () {
|
||||
// Anchor in href
|
||||
compile('{{link_class for="/about/#me"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-current');
|
||||
|
||||
// Anchor in relative URL
|
||||
compile('{{link_class for="/about/"}}')
|
||||
.with({relativeUrl: '/about/#me'})
|
||||
.should.eql('nav-current');
|
||||
});
|
||||
|
||||
it('handles missing trailing slashes', function () {
|
||||
compile('{{link_class for="/about"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-current');
|
||||
});
|
||||
|
||||
it('handles absolute URLs', function () {
|
||||
// Correct URL gets class
|
||||
compile('{{link_class for="https://siteurl.com/about/"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-current');
|
||||
|
||||
// Incorrect URL doesn't get class
|
||||
compile('{{link_class for="https://othersite.com/about/"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('');
|
||||
});
|
||||
|
||||
it('handles absolute URL with missing trailing slash', function () {
|
||||
compile('{{link_class for="https://siteurl.com/about"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-current');
|
||||
});
|
||||
|
||||
it('activeClass can be customised', function () {
|
||||
compile('{{link_class for="/about/" activeClass="nav-active"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('nav-active');
|
||||
|
||||
compile('{{link_class for="/about/" activeClass=slug}}')
|
||||
.with({relativeUrl: '/about/', slug: 'fred'})
|
||||
.should.eql('fred');
|
||||
|
||||
compile('{{link_class for="/about/" activeClass=(concat slug "active" separator="-")}}')
|
||||
.with({relativeUrl: '/about/', slug: 'fred'})
|
||||
.should.eql('fred-active');
|
||||
});
|
||||
|
||||
it('activeClass and other classes work together', function () {
|
||||
// Single extra class
|
||||
compile('{{link_class for="/about/" class="about"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('about nav-current');
|
||||
|
||||
// Multiple extra classes
|
||||
compile('{{link_class for="/about/" class="about my-link"}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('about my-link nav-current');
|
||||
});
|
||||
|
||||
it('can disable activeClass with falsey values', function () {
|
||||
// Empty string
|
||||
compile('{{link_class for="/about/" activeClass=""}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('');
|
||||
|
||||
// false
|
||||
compile('{{link_class for="/about/" activeClass=false}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parentActiveClass', function () {
|
||||
it('gets applied correctly', function () {
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="/about/"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="/about/"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
});
|
||||
|
||||
it('ignores anchors', function () {
|
||||
// Anchor in href
|
||||
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="/about/#me"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="/about/#me"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
|
||||
// Anchor in relative URL
|
||||
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="/about/"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/#me'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="/about/"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/#me'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
});
|
||||
|
||||
it('handles missing trailing slashes', function () {
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="/about"}}">parent</li><li class="{{link_class for="/about/team"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="/about"}}">parent</li><li class="{{link_class for="/about/team"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
});
|
||||
|
||||
it('handles absolute URLs', function () {
|
||||
// Correct URL gets class
|
||||
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="https://siteurl.com/about/"}}">parent</li><li class="{{link_class for="https://siteurl.com/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="https://siteurl.com/about/"}}">parent</li><li class="{{link_class for="https://siteurl.com/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
|
||||
// Incorrect URL doesn't get class
|
||||
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="https://othersite.com/about/"}}">parent</li><li class="{{link_class for="https://othersite.com/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="https://othersite.com/about/"}}">parent</li><li class="{{link_class for="https://othersite.com/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="">parent</li><li class="">child</li>');
|
||||
});
|
||||
|
||||
it('handles absolute URLs with missing trailing slashes', function () {
|
||||
// Parent and child links with PARENT as relative URL
|
||||
compile('<li class="{{link_class for="https://siteurl.com/about"}}">parent</li><li class="{{link_class for="https://siteurl.com/about/team"}}">child</li>')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="nav-current">parent</li><li class="">child</li>');
|
||||
|
||||
// Parent and child links with CHILD as relative URL
|
||||
compile('<li class="{{link_class for="https://siteurl.com/about"}}">parent</li><li class="{{link_class for="https://siteurl.com/about/team"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="nav-current">child</li>');
|
||||
});
|
||||
|
||||
it('parentActiveClass can be customised', function () {
|
||||
compile('<li class="{{link_class for="/about/" parentActiveClass="parent"}}">parent</li><li class="{{link_class for="/about/team/"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="parent">parent</li><li class="nav-current">child</li>');
|
||||
|
||||
compile('<li class="{{link_class for="/about/" parentActiveClass="parent"}}">parent</li><li class="{{link_class for="/about/team/" parentActiveClass="parent"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="parent">parent</li><li class="nav-current">child</li>');
|
||||
|
||||
compile('<li class="{{link_class for="/about/" parentActiveClass=slug}}">parent</li><li class="{{link_class for="/about/team/" parentActiveClass="parent"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/', slug: 'fred'})
|
||||
.should.eql('<li class="fred">parent</li><li class="nav-current">child</li>');
|
||||
|
||||
compile('<li class="{{link_class for="/about/" parentActiveClass=(concat slug "-parent")}}">parent</li><li class="{{link_class for="/about/team/" parentActiveClass="parent"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/', slug: 'fred'})
|
||||
.should.eql('<li class="fred-parent">parent</li><li class="nav-current">child</li>');
|
||||
});
|
||||
|
||||
it('customising activeClass also customises parentActiveClass', function () {
|
||||
compile('<li class="{{link_class for="/about/" activeClass="active"}}">parent</li><li class="{{link_class for="/about/team/" activeClass="active"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="active-parent">parent</li><li class="active">child</li>');
|
||||
|
||||
compile('<li class="{{link_class for="/about/" activeClass="active" parentActiveClass="parent"}}">parent</li><li class="{{link_class for="/about/team/" activeClass="active"}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="parent">parent</li><li class="active">child</li>');
|
||||
});
|
||||
|
||||
it('can disable parentActiveClass with falsey values', function () {
|
||||
compile('{{link_class for="/about/" parentActiveClass=""}}')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('');
|
||||
|
||||
compile('{{link_class for="/about/" parentActiveClass=false}}')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('');
|
||||
});
|
||||
|
||||
it('disabling activeClass does not affect parentActiveClass', function () {
|
||||
compile('<li class="{{link_class for="/about/" activeClass=""}}">parent</li><li class="{{link_class for="/about/team/" activeClass=""}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="nav-current-parent">parent</li><li class="">child</li>');
|
||||
|
||||
compile('<li class="{{link_class for="/about/" activeClass=false parentActiveClass="parent"}}">parent</li><li class="{{link_class for="/about/team/" activeClass=false}}">child</li>')
|
||||
.with({relativeUrl: '/about/team/'})
|
||||
.should.eql('<li class="parent">parent</li><li class="">child</li>');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -37,10 +37,17 @@ describe('{{link}} helper', function () {
|
|||
});
|
||||
|
||||
describe('basic behaviour: simple links without context', function () {
|
||||
it('basic <a> tag with default url', function () {
|
||||
compile('{{#link}}text{{/link}}')
|
||||
.with({})
|
||||
.should.eql('<a href="/">text</a>');
|
||||
it('throws an error for missing href=""', function () {
|
||||
(function compileWith() {
|
||||
compile('{{#link}}text{{/link}}')
|
||||
.with({});
|
||||
}).should.throw();
|
||||
});
|
||||
|
||||
it('silently accepts an empty href...', function () {
|
||||
compile('{{#link href=tag.slug}}text{{/link}}')
|
||||
.with({tag: null})
|
||||
.should.eql('<a href="">text</a>');
|
||||
});
|
||||
|
||||
it('<a> tag with a specific URL', function () {
|
||||
|
@ -338,19 +345,5 @@ describe('{{link}} helper', function () {
|
|||
.should.eql('<a class="parent" href="/about/">parent</a><a href="/about/team/">child</a>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('custom tag', function () {
|
||||
it('can change tag', function () {
|
||||
compile('{{#link href="/about/" class="my-class" tagName="li"}}text{{/link}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="my-class nav-current" href="/about/">text</li>');
|
||||
});
|
||||
|
||||
it('can change tag and disable href', function () {
|
||||
compile('{{#link href="/about/" class="my-class" tagName="li" nohref=true}}text{{/link}}')
|
||||
.with({relativeUrl: '/about/'})
|
||||
.should.eql('<li class="my-class nav-current">text</li>');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue