From 665c30f255240d17f07a661742bf1be4c6fe28e4 Mon Sep 17 00:00:00 2001 From: Rishabh Date: Wed, 2 Mar 2022 22:03:28 +0530 Subject: [PATCH] Added new {{tiers}} theme helper refs https://github.com/TryGhost/Team/issues/1004 - adds new `{{tiers}}` helper behind `multipleProducts` flag - `{{tiers}}` outputs a string with list of tiers that have access to specific post when used in a post context in theme - outputs empty string when used out of a post context and without access to `visibility` property - uses tiers attached to post column for data --- core/frontend/helpers/tiers.js | 59 +++++++++ test/unit/frontend/helpers/tiers.test.js | 120 ++++++++++++++++++ .../theme-engine/handlebars/helpers.test.js | 2 +- 3 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 core/frontend/helpers/tiers.js create mode 100644 test/unit/frontend/helpers/tiers.test.js diff --git a/core/frontend/helpers/tiers.js b/core/frontend/helpers/tiers.js new file mode 100644 index 0000000000..34aaf22cd2 --- /dev/null +++ b/core/frontend/helpers/tiers.js @@ -0,0 +1,59 @@ +// # Tiers Helper +// Usage: `{{tiers}}`, `{{tiers separator=' - ' prefix=' : ' suffix=''}}` +// +// Returns a string of the tiers with access to the post. +// By default, tiers are separated by commas. +const {labs} = require('../services/proxy'); +const {SafeString, escapeExpression} = require('../services/rendering'); + +const isString = require('lodash/isString'); + +function tiers(options = {}) { + options = options || {}; + options.hash = options.hash || {}; + + const separator = isString(options.hash.separator) ? options.hash.separator : ', '; + const lastSeparator = isString(options.hash.lastSeparator) ? options.hash.lastSeparator : ' and '; + const prefix = isString(options.hash.prefix) ? options.hash.prefix : ''; + let suffix = isString(options.hash.suffix) ? options.hash.suffix : ''; + + let output = ''; + + let accessProductsList = this.tiers; + + if (accessProductsList && accessProductsList.length > 0) { + const tierNames = accessProductsList.map((tier) => { + return escapeExpression(tier.name); + }); + + if (tierNames.length === 1) { + output = tierNames[0]; + suffix = isString(options.hash.suffix) ? options.hash.suffix : ' tier'; + } else { + suffix = isString(options.hash.suffix) ? options.hash.suffix : ' tiers'; + const firsts = tierNames.slice(0, tierNames.length - 1); + const last = tierNames[tierNames.length - 1]; + output = firsts.join(separator) + lastSeparator + last; + } + } + + if (output) { + output = prefix + output + suffix; + } + + return new SafeString(output); +} + +module.exports = function productsLabsWrapper() { + let self = this; + let args = arguments; + + return labs.enabledHelper({ + flagKey: 'multipleProducts', + flagName: 'Tiers', + helperName: 'tiers', + helpUrl: 'https://ghost.org/docs/themes/' + }, () => { + return tiers.apply(self, args); + }); +}; diff --git a/test/unit/frontend/helpers/tiers.test.js b/test/unit/frontend/helpers/tiers.test.js new file mode 100644 index 0000000000..6fac4c6087 --- /dev/null +++ b/test/unit/frontend/helpers/tiers.test.js @@ -0,0 +1,120 @@ +const should = require('should'); +const tiersHelper = require('../../../../core/frontend/helpers/tiers'); +const {mockManager} = require('../../../utils/e2e-framework'); + +describe('{{tiers}} helper', function () { + beforeEach(function () { + mockManager.mockLabsEnabled('multipleProducts'); + }); + + it('can return string with tiers', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {}}); + should.exist(rendered); + + String(rendered).should.equal('tier 1, tier 2 and tier 3 tiers'); + }); + + it('can use a different separator', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {separator: '|'}}); + should.exist(rendered); + + String(rendered).should.equal('tier 1|tier 2 and tier 3 tiers'); + }); + + it('can use a different final separator', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {lastSeparator: ' as well as '}}); + should.exist(rendered); + + String(rendered).should.equal('tier 1, tier 2 as well as tier 3 tiers'); + }); + + it('can add a single prefix to multiple tiers', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {prefix: 'on '}}); + should.exist(rendered); + + String(rendered).should.equal('on tier 1, tier 2 and tier 3 tiers'); + }); + + it('can add a single suffix to multiple tiers', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {suffix: ' products'}}); + should.exist(rendered); + + String(rendered).should.equal('tier 1, tier 2 and tier 3 products'); + }); + + it('can override empty suffix to multiple tiers', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {suffix: ''}}); + should.exist(rendered); + + String(rendered).should.equal('tier 1, tier 2 and tier 3'); + }); + + it('can add a prefix and suffix to multiple tiers', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {prefix: 'on ', suffix: ' products'}}); + should.exist(rendered); + + String(rendered).should.equal('on tier 1, tier 2 and tier 3 products'); + }); + + it('can add a prefix and suffix with HTML', function () { + const tiers = [ + {name: 'tier 1'}, + {name: 'tier 2'}, + {name: 'tier 3'} + ]; + + const rendered = tiersHelper.call({tiers: tiers}, {hash: {suffix: ' •', prefix: '… '}}); + should.exist(rendered); + + String(rendered).should.equal('… tier 1, tier 2 and tier 3 •'); + }); + + it('does not add prefix or suffix if no tiers exist', function () { + const rendered = tiersHelper.call({}, {hash: {prefix: 'on ', suffix: ' products'}}); + should.exist(rendered); + + String(rendered).should.equal(''); + }); +}); diff --git a/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js b/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js index 1ebe102729..ba0e8c13fe 100644 --- a/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js +++ b/test/unit/frontend/services/theme-engine/handlebars/helpers.test.js @@ -13,7 +13,7 @@ describe('Helpers', function () { 'next_post', 'page_url', 'pagination', 'plural', 'post_class', 'prev_post', 'price', 'raw', 'reading_time', 't', 'tags', 'title', 'twitter_url', 'url' ]; - const experimentalHelpers = ['match', 'products']; + const experimentalHelpers = ['match', 'products', 'tiers']; const expectedHelpers = _.concat(hbsHelpers, ghostHelpers, experimentalHelpers);