diff --git a/lib/media.js b/lib/media.js index b96079d..fa7b3ac 100644 --- a/lib/media.js +++ b/lib/media.js @@ -212,27 +212,34 @@ function flatMap(list, fn) { * @returns {array} */ function getStyles(name, site) { - const stylesSource = site && site.styleguide || site && site.slug, + const siteStyleguide = site && site.styleguide, + siteSlug = site && site.slug, assetDir = site && site.assetDir || MEDIA_DIRECTORY, assetPath = site && site.assetPath || '', assetHost = site && site.assetHost || ''; - let availableVariations, cssFilePaths; + let availableVariations, styleguidePath, cssFilePaths; - if (!site.styleguide) { + if (!siteStyleguide) { + // legacy behavior: load .css and ..css cssFilePaths = [ path.join(assetDir, 'css', `${name}.css`), - path.join(assetDir, 'css', `${name}.${stylesSource}.css`) + path.join(assetDir, 'css', `${name}.${siteSlug}.css`) ]; } else { - availableVariations = styleguide.getVariations(site.styleguide); - cssFilePaths = [path.join(assetDir, 'css', `${name}.${stylesSource}.css`)]; + // styleguides: load ..css and _..css + // note: this also loads default styles if the site styleguide doesn't have styles for a certain component / variation + availableVariations = styleguide.getVariations(siteStyleguide); + styleguidePath = path.join(assetDir, 'css', `${name}.${siteStyleguide}.css`); + cssFilePaths = files.fileExists(styleguidePath) ? [styleguidePath] : [path.join(assetDir, 'css', `${name}._default.css`)]; // fall back to default styleguide // if there are variations available for the component, add them to the list // of file paths we want if (availableVariations[name]) { - availableVariations[name].forEach((variation)=>{ - cssFilePaths.push(path.join(assetDir, 'css', `${variation}.${stylesSource}.css`)); + availableVariations[name].forEach((variation) => { + let styleguideVariationPath = path.join(assetDir, 'css', `${variation}.${siteStyleguide}.css`); + + cssFilePaths.push(files.fileExists(styleguideVariationPath) ? styleguideVariationPath : path.join(assetDir, 'css', `${variation}._default.css`)); }); } } diff --git a/lib/media.test.js b/lib/media.test.js index bd1938f..a755420 100644 --- a/lib/media.test.js +++ b/lib/media.test.js @@ -170,10 +170,9 @@ describe(_.startCase(filename), function () { slug: 'foo', styleguide: 'bar' })).to.deep.equal(['/css/name.bar.css']); - }); - it('if a styleguide is set and there are variations availalbe, grab those variation files', function () { + it('if a styleguide is set and there are variations available, grab those variation files', function () { files.fileExists.withArgs(`${process.cwd()}/public/css/name.bar.css`).returns(true); files.fileExists.withArgs(`${process.cwd()}/public/css/name_foo.bar.css`).returns(true); @@ -187,7 +186,37 @@ describe(_.startCase(filename), function () { slug: 'foo', styleguide: 'bar' })).to.deep.equal(['/css/name.bar.css', '/css/name_foo.bar.css']); + }); + + it('if a styleguide is set and the site styleguide does not have styles, use the default styleguide', function () { + files.fileExists.withArgs(`${process.cwd()}/public/css/name.bar.css`).returns(false); + files.fileExists.withArgs(`${process.cwd()}/public/css/name._default.css`).returns(true); + + sandbox.stub(styleguide, 'getVariations', function () { + return {}; + }); + + expect(fn('name', { + slug: 'foo', + styleguide: 'bar' + })).to.deep.equal(['/css/name._default.css']); + }); + it('if a variation + styleguide is set and the site styleguide does not have variation styles, use the variation from the default styleguide', function () { + files.fileExists.withArgs(`${process.cwd()}/public/css/name.bar.css`).returns(true); + files.fileExists.withArgs(`${process.cwd()}/public/css/name_foo.bar.css`).returns(false); + files.fileExists.withArgs(`${process.cwd()}/public/css/name_foo._default.css`).returns(true); + + sandbox.stub(styleguide, 'getVariations', function () { + return { + name: ['name_foo'] + }; + }); + + expect(fn('name', { + slug: 'foo', + styleguide: 'bar' + })).to.deep.equal(['/css/name.bar.css', '/css/name_foo._default.css']); }); }); diff --git a/lib/render.js b/lib/render.js index ccf9c8c..b7d31ee 100644 --- a/lib/render.js +++ b/lib/render.js @@ -44,11 +44,7 @@ function makeHtml(state) { const template = clayUtils.getComponentName(state._layoutRef || state._self), rootPartial = hbs.partials[template]; - // if there are component variations, loop through the data looking for - // components that have variations but do not have a default set - if (!_.isEmpty(state._componentVariations)) { - styleguide.setDefaultVariations(_data, state._componentVariations); - } + styleguide.setDefaultVariations(_data, state._componentVariations); if (!rootPartial) { throw new Error(`Missing template for ${template}`); @@ -78,8 +74,9 @@ function makeState(data, meta) { initialState._self = meta._ref; initialState._components = componentList; - // grab the component variations if there is a styleguide and make available via the state - initialState._componentVariations = _.has(meta.locals.site, 'styleguide') ? styleguide.getVariations(meta.locals.site.styleguide) : {}; + // grab the component variations and make available via the state + // note: if styleguide isn't set, this gets variations from the '_default' styleguide + initialState._componentVariations = styleguide.getVariations(_.get(meta, 'locals.site.styleguide', '_default')); if (meta.locals.edit) { initialState._envVars = ENV_VARS; diff --git a/lib/render.test.js b/lib/render.test.js index f940a17..2c5c053 100644 --- a/lib/render.test.js +++ b/lib/render.test.js @@ -132,7 +132,7 @@ describe(_.startCase(filename), function () { }); }); - it('look for component variations if a styleguide is set', function () { + it('look for component variations', function () { var data = mockData(), meta = mockPageMetaWithStyleguide(), res = mockRes(sandbox); @@ -147,7 +147,7 @@ describe(_.startCase(filename), function () { }); }); - it('set default component variations if there are component variations', function () { + it('set default component variations', function () { var data = mockData(), meta = mockPageMetaWithStyleguide(), res = mockRes(sandbox); @@ -167,27 +167,6 @@ describe(_.startCase(filename), function () { }); }); - it('do not attempt to set default component variations if there are no component variations', function () { - var - data = mockData(), - meta = mockPageMetaWithStyleguide(), - res = mockRes(sandbox); - - sandbox.stub(hbs.partials, 'layout').returns(''); - - lib.setHbs(hbs); - - sandbox.stub(styleguide, 'getVariations', ()=>{ - return {}; - }); - - return lib(data, meta, res) - .then(() => { - sinon.assert.notCalled(styleguide.setDefaultVariations); - }); - }); - - it('renders a component individually', function () { var data = mockData(), res = mockRes(sandbox), diff --git a/lib/styleguide.js b/lib/styleguide.js index ac830a9..19a84df 100644 --- a/lib/styleguide.js +++ b/lib/styleguide.js @@ -28,12 +28,16 @@ function findVariationFiles(styleguide) { * Grabs variations of all components and returns an object organized by * components and their variations * -* @param {String} styleguide - name of the styleguide that is set in the sites' config +* note that variations from the site's styleguide AND the _default styleguide will be added, +* since amphora will fallback gracefully to using the _default css files if they don't +* exist in the site's styleguide +* +* @param {String} [styleguide] - name of the styleguide that is set in the sites' config * @returns {Object} componentVariations - an object that is organized by * component and its variations */ -function getVariations(styleguide) { - const foundVariations = styleguide ? findVariationFiles(styleguide) : [], +function getVariations(styleguide = '_default') { + const foundVariations = styleguide !== '_default' ? findVariationFiles(styleguide).concat(findVariationFiles('_default')) : findVariationFiles('_default'), componentVariations = {}; if (foundVariations.length) { @@ -48,21 +52,30 @@ function getVariations(styleguide) { } /** -* Goes through page data and sets a default for components that -* have variations but don't have any set. Note: this mutates the array! +* Goes through page data and sets a default for components * * @param {object} pageData -* @param {object} variations - all components with variations, usually generated by getVariations +* @param {object} variations - all components with variations, generated by getVariations */ function setDefaultVariations(pageData, variations) { - traverse(pageData).forEach(function (val) { - const componentName = _.isObject(val) && val._ref ? clayUtils.getComponentName(val._ref) : undefined; + let componentName, componentVariations; + + if (!_.isObject(val) || !val._ref) { + return; // we only care about components' root data + } + + componentName = clayUtils.getComponentName(val._ref); + componentVariations = variations[componentName] || []; - if (componentName && variations[componentName]) { - if (!val.componentVariation) { - _.set(val, 'componentVariation', componentName); - } + if (val.componentVariation && !_.includes(componentVariations, val.componentVariation)) { + // component has a variation set, but it doesn't exist in the site's styleguide or the default styleguide! + // render the component with the default variation + _.set(val, 'componentVariation', componentName); + } else if (!val.componentVariation) { + // component has no variation set! + // render the component with the default variation + _.set(val, 'componentVariation', componentName); } }); } diff --git a/lib/styleguide.test.js b/lib/styleguide.test.js index 8f10192..304bac4 100644 --- a/lib/styleguide.test.js +++ b/lib/styleguide.test.js @@ -23,6 +23,11 @@ describe(_.startCase(filename), function () { 'video.css', 'video_fullbleed.css', 'video_mobile.css' + ], + defaultMockData = [ + 'blockquote.css', + 'pull-quote.css', + 'pull-quote_large.css' ]; sandbox = sinon.sandbox.create(); @@ -30,6 +35,10 @@ describe(_.startCase(filename), function () { files.getFiles.withArgs('styleguides/di/components').returns(diMockData); files.getFiles.withArgs('styleguides/vulture/components').returns(vultureMockData); + files.getFiles.withArgs('styleguides/_default/components').returns(defaultMockData); + + // bust memoization cache + lib.getVariations.cache = new Map(); }); afterEach(function () { @@ -39,19 +48,19 @@ describe(_.startCase(filename), function () { it('get all available component variations', function () { var expected = { blockquote: [ 'blockquote_red', 'blockquote_blue' ], - video: [ 'video_fullbleed', 'video_mobile' ] + video: [ 'video_fullbleed', 'video_mobile' ], + 'pull-quote': ['pull-quote_large'] }; expect(lib.getVariations('vulture')).to.deep.equal(expected); }); - it('if there is no styleguide set, there should be no variations set', function () { - expect(lib.getVariations()).to.deep.equal({}); - }); - - it('if there are no component variations, return an empty object', function () { + it('gets default variations', function () { + var expected = { + 'pull-quote': [ 'pull-quote_large' ] + }; - expect(lib.getVariations('di')).to.deep.equal({}); + expect(lib.getVariations('_default')).to.deep.equal(expected); }); it('set default variations for components that have variations but none set', function () { @@ -92,9 +101,11 @@ describe(_.startCase(filename), function () { }, { _ref: 'www.vulture.com/_components/clay-paragraph/instances/cjeq5662h001b3i61rr8fffu1@published', + componentVariation: 'clay-paragraph' }, { _ref: 'www.vulture.com/_components/article-sidebar/instances/foobar@published', + componentVariation: 'article-sidebar', content: [ { _ref: 'www.vulture.com/_components/blockquote/instances/pho@published', @@ -117,6 +128,28 @@ describe(_.startCase(filename), function () { expect(testData).to.deep.equal(expectedData); }); + it('sets default variation if specified variation does not exist', function () { + let testData = { + main: [ + { + _ref: 'domain.com/_components/blockquote/instances/foo', + componentVariation: 'blockquote_red' + } + ] + }, + expectedData = { + main: [ + { + _ref: 'domain.com/_components/blockquote/instances/foo', + componentVariation: 'blockquote' + } + ] + }; + + lib.setDefaultVariations(testData, {}); + expect(testData).to.deep.equal(expectedData); + }); + it('do not change data if there are no components in the page data', function () { var testData = ['Steve Rogers', 'Tony Stark', 'Natasha Romanov', 'Clint Barton', 'Wanda Maximoff'], variations = lib.getVariations('vulture');