Skip to content

Commit

Permalink
implement cx only as a classnames not dedupe
Browse files Browse the repository at this point in the history
  • Loading branch information
aizerin authored and tommmyy committed Sep 11, 2018
1 parent d5e8d25 commit 2ddf1e5
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 54 deletions.
106 changes: 86 additions & 20 deletions packages/ramda-extension/src/__tests__/cx-test.js
Expand Up @@ -5,30 +5,96 @@ import { cx } from '../';
import Benchmark from 'benchmark';
import classNames from 'classnames';

describe('cx', () => {
it('should exists', () => {
expect(cx).toBeDefined();
describe('classNames', function() {
it('keeps object keys with truthy values', function() {
expect(
cx({
a: true,
b: false,
c: 0,
d: null,
e: undefined,
f: 1,
})
).toEqual('a f');
});
it('should handle strings', () => {
expect(cx('one', 'two')).toBe('one two');

it('joins arrays of class names and ignore falsy values', function() {
expect(cx('a', 0, null, undefined, true, 1, 'b')).toEqual('a 1 b');
});

it('supports heterogenous arguments', function() {
expect(cx({ a: true }, 'b', 0)).toEqual('a b');
});
it('should trim strings', () => {
expect(cx(' one ', ' two ')).toBe('one two');

it('should be trimmed', function() {
expect(cx('', 'b', {}, '')).toEqual('b');
});

it('returns an empty string for an empty configuration', function() {
expect(cx({})).toEqual('');
});
it('should handle arrays', () => {
expect(cx(['one', 'two'])).toBe('one two');

it('supports an array of class names', function() {
expect(cx(['a', 'b'])).toEqual('a b');
});
it('should handle nested arrays', () => {
expect(cx(['one', ['two', 'three']])).toBe('one two three');

it('joins array arguments with string arguments', function() {
expect(cx(['a', 'b'], 'c')).toEqual('a b c');
expect(cx('c', ['a', 'b'])).toEqual('c a b');
});
it('should handle objects', () => {
expect(cx({ one: true, two: false })).toBe('one');

it('handles multiple array arguments', function() {
expect(cx(['a', 'b'], ['c', 'd'])).toEqual('a b c d');
});
it('should always overwrite other types', () => {
expect(cx('one', { one: false, two: false, three: true }, 'two')).toBe('three');

it('handles arrays that include falsy and true values', function() {
expect(cx(['a', 0, null, undefined, false, true, 'b'])).toEqual('a b');
});
it('should handle a mix of strings, arrays, and objects', () => {
expect(cx(['one', ['two'], { three: true, four: 0 }], 'five six', { two: false })).toBe('one five six three');

it('handles arrays that include arrays', function() {
expect(cx(['a', ['b', 'c']])).toEqual('a b c');
});

it('handles arrays that include objects', function() {
expect(cx(['a', { b: true, c: false }])).toEqual('a b');
});

it('handles deep array recursion', function() {
expect(cx(['a', ['b', ['c', { d: true }]]])).toEqual('a b c d');
});

it('handles arrays that are empty', function() {
expect(cx('a', [])).toEqual('a');
});

it('handles nested arrays that have empty nested arrays', function() {
expect(cx('a', [[]])).toEqual('a');
});

it('handles all types of truthy and falsy property values as expected', function() {
expect(
cx({
// falsy:
null: null,
emptyString: '',
noNumber: NaN,
zero: 0,
negativeZero: -0,
false: false,
undefined,

// truthy (literally anything else):
nonEmptyString: 'foobar',
whitespace: ' ',
function: Object.prototype.toString,
emptyObject: {},
nonEmptyObject: { a: 1, b: 2 },
emptyList: [],
nonEmptyList: [1, 2, 3],
greaterZero: 1,
})
).toEqual('nonEmptyString whitespace function emptyObject nonEmptyObject emptyList nonEmptyList greaterZero');
});
xit('should be fast as classnames +- 100 ops/sec', () => {
return new Promise((resolve, reject) => {
Expand All @@ -41,9 +107,9 @@ describe('cx', () => {
.add('cx', function() {
cx(...args);
})
// .on('cycle', function(event) {
// console.log(String(event.target));
// })
.on('cycle', function(event) {
console.log(String(event.target));
})
.on('complete', function() {
// console.log(`Fastest is ${this.filter('fastest').map('name')}`);
const classNamesOps = this['0'].hz;
Expand Down
58 changes: 24 additions & 34 deletions packages/ramda-extension/src/cx.js
@@ -1,42 +1,27 @@
import {
always,
append,
compose,
concat,
converge,
filter,
head,
identity,
ifElse,
isEmpty,
last,
map,
not,
o,
toPairs,
uncurryN,
uniq,
unnest,
trim,
} from 'ramda';
import { compose, filter, identity, when, anyPass, flatten, values, mapObjIndexed, map, into } from 'ramda';
import flattenArgs from './flattenArgs';
import joinWithSpace from './joinWithSpace';
import isObject from './isObject';
import isString from './isString';
import rejectEq from './rejectEq';
import applyCompose from './applyCompose';
import { isNumber } from 'util';

const createSaveModifiers = ifElse(isEmpty, always([identity]));
const filterFalsy = filter(identity);
const keepObjectStringNumber = filter(anyPass([isObject, isString, isNumber]));
const keepKeyIfValueIsTruthy = mapObjIndexed((v, k) => v && k);
const destructObject = compose(
filterFalsy,
values,
keepKeyIfValueIsTruthy
);

const getDefinitions = compose(uniq, unnest, map(toPairs));
const createRejects = map(o(rejectEq, head));
const createAppends = map(o(append, head));
const getRejects = createSaveModifiers(o(createRejects, filter(o(not, last))));
const getAppends = createSaveModifiers(o(createAppends, filter(last)));

const createModifiers = o(converge(concat, [getRejects, getAppends]), getDefinitions);
const createAndApplyModifiers = uncurryN(2, o(applyCompose, createModifiers));
const handleArgs = converge(createAndApplyModifiers, [filter(isObject), filter(isString)]);
const transduceArgs = into(
[],
compose(
map(when(isObject, destructObject)),
keepObjectStringNumber,
filterFalsy
)
);

/**
* Conditionally joining classNames together.
Expand All @@ -59,6 +44,11 @@ const handleArgs = converge(createAndApplyModifiers, [filter(isObject), filter(i
*
* @sig String | [String] | Object -> String
*/
const cx = compose(joinWithSpace, uniq, map(trim), handleArgs, flattenArgs);
const cx = compose(
joinWithSpace,
flatten,
transduceArgs,
flattenArgs
);

export default cx;

0 comments on commit 2ddf1e5

Please sign in to comment.