diff --git a/bind.js b/bind.js index eaa1eaa..e3a189a 100644 --- a/bind.js +++ b/bind.js @@ -15,7 +15,8 @@ export default function classNames () { function parseValue (arg) { if (typeof arg === 'string') { - return this && this[arg] || arg; + const mappedClass = this && this[arg]; + return typeof mappedClass === 'string' && mappedClass || arg; } if (typeof arg !== 'object') { @@ -34,7 +35,8 @@ function parseValue (arg) { for (const key in arg) { if (hasOwn.call(arg, key) && arg[key]) { - classes = appendClass(classes, this && this[key] || key); + const mappedClass = this && this[key]; + classes = appendClass(classes, typeof mappedClass === 'string' && mappedClass || key); } } diff --git a/tests/bind.js b/tests/bind.js index ba375e6..b26156e 100644 --- a/tests/bind.js +++ b/tests/bind.js @@ -79,6 +79,31 @@ describe('bind', () => { }); describe('classNamesBound', () => { + it('keeps unmapped string classes that match Object.prototype properties', () => { + for (const name of ['toString', 'constructor', 'hasOwnProperty', '__proto__']) { + assert.equal(classNamesBound(name), name); + assert.equal(classNamesBound([name]), name); + assert.equal(classNamesBound('a', name), '#a ' + name); + } + }); + + it('keeps unmapped object keys that match Object.prototype properties', () => { + for (const name of ['constructor', 'hasOwnProperty', '__proto__']) { + assert.equal(classNamesBound({[name]: true}), name); + } + }); + + it('uses string mappings for prototype-named and inherited classes', () => { + const mappings = Object.setPrototypeOf({ + toString: '#toString', constructor: '#constructor', ['__proto__']: '#proto' + }, {inherited: '#inherited'}); + const bound = classNames.bind(mappings); + assert.equal(bound('toString', 'constructor', '__proto__', 'inherited'), + '#toString #constructor #proto #inherited'); + assert.equal(bound({constructor: true, ['__proto__']: true, inherited: true}), + '#constructor #proto #inherited'); + }); + it('keeps object keys with truthy values', () => { assert.equal(classNamesBound({ a: true,