Skip to content
This repository has been archived by the owner on Feb 9, 2022. It is now read-only.

Commit

Permalink
Merge pull request #18 from CAAPIM/variants-and-resolve-fixes
Browse files Browse the repository at this point in the history
Variants and resolve fixes
  • Loading branch information
alebiavati authored Mar 1, 2017
2 parents 0be1b11 + 9163458 commit 91d0527
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 44 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,3 @@ Alternatively, if you are simply using `git commit`, you must follow this format
## License
Copyright (c) 2017 CA. All rights reserved.
This software may be modified and distributed under the terms of the MIT license. To learn more, see the [License](LICENSE.md)

9 changes: 7 additions & 2 deletions src/decorator/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,17 @@
/* eslint-disable import/prefer-default-export */

import { themer } from '../';
import { mapThemeProps } from '../utils';
import { mapThemeProps, applyVariantsProps } from '../utils';

function variantsWrapper(snippet) {
return (props) => snippet(applyVariantsProps(props));
}

export function createDecorator(customThemer) {
const themerInstance = customThemer || themer;
return rawTheme => inputSnippet => {
const { snippet, theme } = themerInstance.resolveAttributes(inputSnippet, [rawTheme]);
const snippetWithVariants = variantsWrapper(inputSnippet);
const { snippet, theme } = themerInstance.resolveAttributes(snippetWithVariants, [rawTheme]);
return (props) => snippet(mapThemeProps(props, theme));
};
}
14 changes: 12 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,21 @@
* of the MIT license. See the LICENSE file for details.
*/

import { createThemer as create, mapThemeProps } from './utils';
import {
createThemer as create,
mapThemeProps,
applyVariantsProps,
} from './utils';
import { createDecorator } from './decorator';

const themer = create();

export { themer, create, createDecorator, mapThemeProps };
export {
themer,
create,
createDecorator,
mapThemeProps,
applyVariantsProps,
};

export default createDecorator(themer);
8 changes: 4 additions & 4 deletions src/middleware/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,14 @@ export default class Middleware {

/**
* Executes all of the methods in the middleware registry. Middleware methods
* expect the snippet and theme.styles to be passed in as properties
* expect the snippet and theme to be passed in as properties
*
* @param {Function} snippet Function that returns valid HTML markup
* @param {Object} styles Executes all middleware function on snippet
* @param {Object} theme Executes all middleware function on snippet
* @return {Function} Resolved results of all middleware methods
*/
resolve(snippet, styles) {
return flowRight(this.registry)(snippet, styles);
resolve(snippet, theme) {
return flowRight(this.registry)(snippet, theme);
}

}
29 changes: 18 additions & 11 deletions src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,24 +98,35 @@ export function combineByAttributes(attr, obj1 = {}, obj2 = {}) {
return obj2[attr];
}

return objectAssign(obj2[attr], obj1[attr]);
if (isPlainObject(obj1[attr]) && isPlainObject(obj2[attr])) {
return objectAssign(obj1[attr], obj2[attr]);
}

if (Array.isArray(obj1[attr])) {
return obj1[attr].concat(obj2[attr]);
}

return [obj1[attr], obj2[attr]];
}

/**
* Append variants passed as "true" props to the root style element
*
* @param {Object} props The props passed to the render method
* @param {Object} theme The theme as calculated
* @return {Object} The styles as per post-variant processing
* @param {Object} props The props passed to the render method
* @return {Object} The styles as per post-variant processing
* @public
*/
export function applyVariantsProps(props, resolvedTheme) {
const variants = resolvedTheme.variants;
export function applyVariantsProps(props) {
const resolvedTheme = props.theme;
if (!resolvedTheme || !resolvedTheme.variants || !resolvedTheme.styles) {
return props;
}

const mappedStyles = extend({ root: '' }, resolvedTheme.styles);
const mappedProps = extend({}, props);
const renderedVariants = {};

forEach(variants, (variantValue, variantKey) => {
forEach(resolvedTheme.variants, (variantValue, variantKey) => {
// get variant props as object
let variantProps;
if (isPlainObject(variantValue)) {
Expand Down Expand Up @@ -149,9 +160,5 @@ export function applyVariantsProps(props, resolvedTheme) {
}

export function mapThemeProps(props, resolvedTheme) {
if (resolvedTheme.variants && resolvedTheme.styles) {
const variantsProps = applyVariantsProps(props, resolvedTheme);
return variantsProps;
}
return { ...props, theme: resolvedTheme };
}
17 changes: 17 additions & 0 deletions tests/decorator/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* of the MIT license. See the LICENSE file for details.
*/

import { create, mapThemeProps } from '../../src';
import { createDecorator } from '../../src/decorator';
import {
testThemeSimple,
Expand Down Expand Up @@ -35,4 +36,20 @@ describe('decorator', () => {
const testProps = { content: 'Hello', stop: true, go: true };
expect(decoratedSnippet(testProps)).toBe('<h1 class="big-text-class red-black-class green-grass-class">Hello</h1>');
});

it('should should apply variants after middlewares are resolved', () => {
const testTheme = { styles: { root: 'root-123', test: 'test-123' } };
const customThemer = create();

const testMiddleware = (middlewareSnippet, middlewareTheme) => {
const mappedTheme = { ...middlewareTheme, variants: { test: true } };
return (props) => middlewareSnippet(mapThemeProps(props, mappedTheme));
};

customThemer.setMiddleware(testMiddleware);
const customThemerDecorator = createDecorator(customThemer);
const decoratedSnippet = customThemerDecorator(testTheme)(snippet);
const testProps = { content: 'Hello', test: true };
expect(decoratedSnippet(testProps)).toBe('<h1 class="root-123 test-123">Hello</h1>');
});
});
13 changes: 12 additions & 1 deletion tests/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,12 @@
* of the MIT license. See the LICENSE file for details.
*/

import themerDecorator, { create, themer as themerInstance, mapThemeProps } from '../src';
import themerDecorator, {
create,
themer as themerInstance,
mapThemeProps,
applyVariantsProps,
} from '../src';
import Themer from '../src/Themer';
import { testThemeSimple, snippet } from './fixtures';

Expand Down Expand Up @@ -45,4 +50,10 @@ describe('exports', () => {
expect(typeof mapThemeProps).toBe('function');
});
});

describe('applyVariantsProps', () => {
it('should export applyVariantsProps function that correctly maps variants to props', () => {
expect(typeof applyVariantsProps).toBe('function');
});
});
});
101 changes: 78 additions & 23 deletions tests/utils/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,28 @@ describe('utils', () => {
it('should resolve to item if not a function', () => {
expect(resolveArray('test-item-to-resolve')).toBe('test-item-to-resolve');
});
it('should pass content of first item into second item, if second item is a function', () => {
const testArray = [
'test',
(val) => `${val}-item`,
];
expect(resolveArray(testArray)).toBe('test-item');
});
it('should pass retune value of function to next funciton', () => {
const testArray = [
'test',
(val) => `${val}-item`,
(val) => `${val}-test`,
];
expect(resolveArray(testArray)).toBe('test-item-test');
});
it('should skip item in array if falsy', () => {
expect(resolveArray(['test', null, (val) => `${val}-item`])).toBe('test-item');
const testArray = [
'test',
null,
(val) => `${val}-item`,
];
expect(resolveArray(testArray)).toBe('test-item');
});
});

Expand All @@ -50,57 +70,102 @@ describe('utils', () => {
const combinedObj = combineByAttributes();
expect(combinedObj).toEqual({});
});
it('should return merged object if both elements are plain objects', () => {
const testObj1 = { testAttr: { test: 1 } };
const testObj2 = { testAttr: { prop: 2 } };
expect(combineByAttributes('testAttr', testObj1, testObj2)).toEqual({
test: 1,
prop: 2,
});
});
it('should merge objects with priority to second object', () => {
const testObj1 = { testAttr: { test: 1 } };
const testObj2 = { testAttr: { test: 2 } };
expect(combineByAttributes('testAttr', testObj1, testObj2)).toEqual({
test: 2,
});
});
it('should return array if any of the attributes are not plain objects', () => {
const testObj1 = { testAttr: 'test' };
const testObj2 = { testAttr: { test: 2 } };
expect(combineByAttributes('testAttr', testObj1, testObj2)).toEqual([
'test',
{ test: 2 },
]);
});
it('should add item to array if first object attr is array', () => {
const testObj1 = { testAttr: ['test'] };
const testObj2 = { testAttr: { test: 2 } };
expect(combineByAttributes('testAttr', testObj1, testObj2)).toEqual([
'test',
{ test: 2 },
]);
});
});

describe('applyVariantsProps', () => {
it('should be a funciton', () => {
expect(typeof applyVariantsProps).toBe('function');
});
it('should return props unmodified when no theme is found', () => {
const testProps = { content: 'Hello' };
expect(applyVariantsProps(testProps)).toEqual(testProps);
});
it('should return props unmodified when no variants are found', () => {
const theme = { styles: { root: 'root-class-123', test: 'test-123' } };
const testProps = { content: 'Hello', theme };
expect(applyVariantsProps(testProps)).toEqual(testProps);
});
it('should return props unmodified when no styles are found', () => {
const theme = { variants: { test: true } };
const testProps = { content: 'Hello', theme };
expect(applyVariantsProps(testProps)).toEqual(testProps);
});
it('should map simple variants to root class', () => {
const testResolvedTheme = { styles: { root: 'root-class-123', test: 'test-123' }, variants: { test: true } };
const testProps = { content: 'Hello', test: true };
const theme = { styles: { root: 'root-class-123', test: 'test-123' }, variants: { test: true } };
const testProps = { content: 'Hello', test: true, theme };
const mappedProps = {
content: 'Hello',
theme: { styles: { root: 'root-class-123 test-123', test: 'test-123' }, variants: { test: true } },
};
expect(applyVariantsProps(testProps, testResolvedTheme)).toEqual(mappedProps);
expect(applyVariantsProps(testProps)).toEqual(mappedProps);
});
it('should not map variants if class does not exist', () => {
const testResolvedTheme = { styles: { root: 'root-class-123' }, variants: { test: true } };
const testProps = { content: 'Hello', test: true };
const theme = { styles: { root: 'root-class-123' }, variants: { test: true } };
const testProps = { content: 'Hello', test: true, theme };
const mappedProps = {
content: 'Hello',
theme: { styles: { root: 'root-class-123' }, variants: {} },
};
expect(applyVariantsProps(testProps, testResolvedTheme)).toEqual(mappedProps);
expect(applyVariantsProps(testProps)).toEqual(mappedProps);
});
it('should map complex variants to root class', () => {
const testResolvedTheme = {
const theme = {
styles: { root: 'root-class-123', test: 'test-123' },
variants: {
test: { prop1: 'prop1Value', prop2: true },
},
};
const testProps = { content: 'Hello', prop1: 'prop1Value', prop2: true };
const testProps = { content: 'Hello', prop1: 'prop1Value', prop2: true, theme };
const mappedProps = {
content: 'Hello',
theme: { styles: { root: 'root-class-123 test-123', test: 'test-123' }, variants: { test: true } },
};
expect(applyVariantsProps(testProps, testResolvedTheme)).toEqual(mappedProps);
expect(applyVariantsProps(testProps)).toEqual(mappedProps);
});
it('should not map complex variants if not all prop-checks pass', () => {
const testResolvedTheme = {
const theme = {
styles: { root: 'root-class-123', test: 'test-123' },
variants: {
test: { prop1: 'prop1Value', prop2: true },
},
};
const testProps = { content: 'Hello', prop1: 'prop1Value' };
const testProps = { content: 'Hello', prop1: 'prop1Value', theme };
const mappedProps = {
content: 'Hello',
theme: { styles: { root: 'root-class-123', test: 'test-123' }, variants: {} },
};
expect(applyVariantsProps(testProps, testResolvedTheme)).toEqual(mappedProps);
expect(applyVariantsProps(testProps)).toEqual(mappedProps);
});
});

Expand All @@ -115,15 +180,5 @@ describe('utils', () => {
const mappedProps = mapThemeProps(testProps, testResolvedTheme);
expect(mappedProps).toEqual({ ...testProps, theme: testResolvedTheme });
});

it('should map variants props, if defined', () => {
const testResolvedTheme = { styles: { root: 'root-class-123', test: 'test-123' }, variants: { test: true } };
const testProps = { content: 'Hello', test: true };
const mappedProps = mapThemeProps(testProps, testResolvedTheme);
expect(mappedProps).toEqual({
content: 'Hello',
theme: { styles: { root: 'root-class-123 test-123', test: 'test-123' }, variants: { test: true } },
});
});
});
});

0 comments on commit 91d0527

Please sign in to comment.