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

site-specific components in component lists #510

Merged
merged 6 commits into from Jul 8, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 15 additions & 0 deletions behaviors/README.md
Expand Up @@ -104,6 +104,21 @@ sideBarArea:

Component lists don't use `_label` or `_display` (and will ignore them if you specify them), but they allow `_placeholder`. It's recommended to add placeholders to component lists, which will display when that list is empty.

##### Site-specific Components

You may optionally specify that components in a list should be included or excluded on a specific site. To do so, add a comma-separated list of sites in parenthesis after the name of the component in the `include` list:

```yaml
include:
- article (site1, site2) # allow on site1 and site2 ONLY
- paragraph # allow on all sites
- image (not:site1) # allow on all sites EXCEPT site1
- link (site1, site2, site3, not:site2, not:site3) # only allow on site1
Copy link

Choose a reason for hiding this comment

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

Hmm, do site-specific options belong in the schema? Seems like schema should be only tied to the component and generic to any site.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

as a less-used +1, I think this is fine, especially since it's only for implementation-specific components (adding specific sites to a list in a component on npm would be dumb)

# if (for some reason) you both include and exclude a site, it'll filter by the
# included sites first, then filter out the excluded. this works the same way as the
# include and exclude lists above
```

**Note:** In the future you will be able to [specify a minimum and maximum number of components](https://github.com/nymag/clay-kiln/issues/298) in your component lists.

#### Defining Display
Expand Down
66 changes: 56 additions & 10 deletions services/components/add-component-handler.js
Expand Up @@ -2,7 +2,8 @@ var references = require('../references'),
dom = require('@nymag/dom'),
_ = require('lodash'),
paneService = require('../pane'),
addComponent = require('./add-component');
addComponent = require('./add-component'),
site = require('../site');

/**
* get parent list element
Expand All @@ -19,22 +20,67 @@ function getParentListElement(el, path) {
}
}

/**
* determine if a component is available on the current site, based on logic
* @param {string} slug e.g. "grubstreet"
* @param {string} logic e.g. (di, press, thecut) or (not:di, not:press, not:thecut) or a mixture of both
* @returns {boolean}
*/
function availableOnCurrentSite(slug, logic) {
var tokens = logic.split(',').map((str) => str.trim()), // trim any extra whitespace
// list of site slugs to include
sitesToInclude = _.reject(tokens, (token) => _.includes(token, 'not:')),
// list of site slugs to exclude (remove the "not:" from the tokens)
sitesToExclude = _.map(_.filter(tokens, (token) => _.includes(token, 'not:')), (token) => token.replace(/not:\s?/ig, ''));

if (!_.isEmpty(sitesToInclude)) {
// if we have any sites explicitly included, then the component is available if we're
// on one of those sites AND we're not on any sites in the excluded list
// note: configuring "(siteName, otherSiteName, not:siteName)" is silly, but possible
return _.includes(sitesToInclude, slug) && !_.includes(sitesToExclude, slug);
} else {
// if we don't explicitly include certain sites, then just make sure the
// current site isn't excluded
return !_.includes(sitesToExclude, slug);
}
}

/**
* see if a component can be added in this list,
* by checking the exclude array and the current site
* @param {string} str component name and optional site logic
* @param {array} exclude
* @returns {boolean}
*/
function filterComponent(str, exclude) {
var matches = str.match(/([\w-]+)(?:\s?\((.*?)\))?/), // e.g. component-name (site logic)
name = matches[1],
siteLogic = matches[2];

if (_.includes(exclude, name)) {
// first, check to make sure a component isn't in the exclude list
return false;
} else if (siteLogic && !availableOnCurrentSite(site.get('slug'), siteLogic)) {
// then, check to make sure we can use this component on the current site
return false;
} else {
// we can add this component to this list on this site!
return true;
}
}

/**
* map through components, filtering out excluded
* then filter out components not allowed on the current site
* then remove any site logic (to only return the component names)
* @param {array} possibleComponents
* @param {array} [exclude] array of components to exclude
* @returns {array} array of elements
*/
function getAddableComponents(possibleComponents, exclude) {
return _.compact(_.map(possibleComponents, function (item) {
if (exclude && exclude.length) {
if (!_.contains(exclude)) {
return item;
}
} else {
return item;
}
}));
return _.map(_.filter(possibleComponents, (item) => filterComponent(item, exclude)), (str) => str.replace(/\s?\(.*?\)/g, ''));
// that regex removes anything in parenthesis (the site logic)
// as well as any spaces between the name of the component and the parenthesis
}

/**
Expand Down
33 changes: 31 additions & 2 deletions services/components/add-component-handler.test.js
@@ -1,7 +1,8 @@
var dirname = __dirname.split('/').pop(),
filename = __filename.split('/').pop().split('.').shift(),
lib = require('./add-component-handler'),
dom = require('@nymag/dom');
dom = require('@nymag/dom'),
site = require('../site');

describe(dirname, function () {
describe(filename, function () {
Expand All @@ -22,7 +23,17 @@ describe(dirname, function () {
});

describe('getAddableComponents', function () {
var fn = lib[this.title];
var fn = lib[this.title],
sandbox;

beforeEach(function () {
sandbox = sinon.sandbox.create();
sandbox.stub(site, 'get').returns('testSite');
});

afterEach(function () {
sandbox.restore();
});

it('works for empty array', function () {
expect(fn([])).to.deep.equal([]);
Expand All @@ -39,6 +50,24 @@ describe(dirname, function () {
it('works when excluding stuff', function () {
expect(fn(['foo'], ['bar'])).to.deep.equal(['foo']);
});

it('allows components included on current site', function () {
expect(fn(['foo (testSite)'])).to.deep.equal(['foo']);
expect(fn(['foo (testSite, otherSite)'])).to.deep.equal(['foo']);
});

it('disallows components not included on current site', function () {
expect(fn(['foo (otherSite)'])).to.deep.equal([]);
});

it('disallows components excluded from current site', function () {
expect(fn(['foo (not:testSite)'])).to.deep.equal([]);
expect(fn(['foo (not: testSite)'])).to.deep.equal([]);
});

it('disallows components included but also excluded from current site', function () {
expect(fn(['foo (testSite, otherSite, not:testSite)'])).to.deep.equal([]);
});
});
});
});
1 change: 1 addition & 0 deletions services/site.js
Expand Up @@ -53,6 +53,7 @@ if (editorEl) {
data.host = editorEl.getAttribute('data-site-host');
data.path = normalizePath(editorEl.getAttribute('data-site-path'));
data.assetPath = editorEl.getAttribute('data-site-assetpath');
data.slug = editorEl.getAttribute('data-site-slug');
data.port = location.port;
data.prefix = path.join(data.host, data.path);
data.protocol = location.protocol;
Expand Down
1 change: 1 addition & 0 deletions template.nunjucks
Expand Up @@ -15,6 +15,7 @@
data-site-host="{{ site.host }}"
data-site-path="{{ site.path}}"
data-site-assetpath="{{ site.assetPath }}"
data-site-slug="{{ site.slug }}"
data-components="{{ _components }}">
<div class="kiln-toolbar-inner">
<button class="user-icon">{% include 'public/media/components/clay-kiln/user-icon.svg' %}</button>
Expand Down