diff --git a/.eslintrc.json b/.eslintrc.json index 3d4c2af8..b62561c6 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,6 +4,9 @@ "plugins": [ "react" ], + "env": { + "jest": true + }, "rules": { "consistent-return": 0, "func-names": 0, diff --git a/lib/utils.js b/lib/utils.js index 2fed25e4..6e535133 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,4 +1,3 @@ -/* eslint prefer-spread: 0 */ // Inspired by: https://github.com/JedWatson/classnames const hasOwn = {}.hasOwnProperty; @@ -8,7 +7,7 @@ export const classNames = (styles, ...args) => { if (argType === 'string' || argType === 'number') { classes.push(arg); } else if (Array.isArray(arg)) { - classes.push(classNames.apply(null, arg)); + classes = classes.concat(arg); } else if (argType === 'object') { Object.keys(arg).forEach((key) => { if (hasOwn.call(arg, key) && arg[key]) { @@ -18,7 +17,7 @@ export const classNames = (styles, ...args) => { } return classes; }, []); - return styleKeys.map(key => styles[key]).join(' '); + return [...new Set(styleKeys)].map(key => styles[key]).join(' '); }; export const cleanProps = (props, propTypes) => diff --git a/lib/utils.test.js b/lib/utils.test.js new file mode 100644 index 00000000..09595e18 --- /dev/null +++ b/lib/utils.test.js @@ -0,0 +1,97 @@ +import { classNames } from './utils'; + +describe('utils', () => { + describe('classNames', () => { + it('should calculate string input class ', () => { + const styles = { + myClass: 'my class', + }; + expect(classNames(styles, 'myClass')) + .toBe(styles.myClass); + }); + + it('should calculate int arg classes', () => { + const styles = { + 1: 'uno', + }; + expect(classNames(styles, 1)) + .toBe(`${styles[1]}`); + }); + + it('should calculate multiple arg classes', () => { + const styles = { + myClass: 'my class', + myOtherClass: 'my other class', + }; + + expect(classNames(styles, 'myClass', 'myOtherClass')) + .toBe(`${styles.myClass} ${styles.myOtherClass}`); + }); + + it('should calculate array of classes', () => { + const styles = { + myClass: 'my class', + myOtherClass: 'my other class', + }; + expect(classNames(styles, ['myClass', 'myOtherClass'])) + .toBe(`${styles.myClass} ${styles.myOtherClass}`); + }); + + it('should not duplicate classes', () => { + const styles = { + myClass: 'my class', + }; + expect(classNames(styles, 'myClass', 'myClass')) + .toBe(`${styles.myClass}`); + }); + + it('should handle object of classes', () => { + const styles = { + myClass: 'my class', + myOtherClass: 'my other class', + }; + const switches = { + myClass: true, + myOtherClass: true, + }; + expect(classNames(styles, switches)) + .toBe(`${styles.myClass} ${styles.myOtherClass}`); + }); + + it('should handle object with some classes switched off', () => { + const styles = { + myClass: 'my class', + myOtherClass: 'my other class', + }; + const switches = { + myClass: true, + myOtherClass: false, + }; + expect(classNames(styles, switches)) + .toBe(`${styles.myClass}`); + }); + + it('should handle multiple input types', () => { + const styles = { + myClass: 'my class', + myOtherClass: 'my other class', + 1: 'uno', + stringClass: 'string class', + arrayItemOne: 'array one', + arrayItemTwo: 'array two', + }; + const switches = { + myClass: true, + myOtherClass: true, + }; + expect(classNames( + styles, + 'stringClass', + 1, + ['arrayItemOne', 'arrayItemTwo'], + switches, + )) + .toBe(`${styles.stringClass} ${styles[1]} ${styles.arrayItemOne} ${styles.arrayItemTwo} ${styles.myClass} ${styles.myOtherClass}`); + }); + }); +}); diff --git a/package.json b/package.json index 6c0594d7..21ba1b14 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,10 @@ }, "coverageThreshold": { "global": { - "branches": 93, + "branches": 100, "functions": 100, - "lines": 99, - "statements": 99 + "lines": 100, + "statements": 100 } } },