From 2559e38f21589261670e4f4e259b1c92b3eb79f1 Mon Sep 17 00:00:00 2001 From: Patrick Winters Date: Wed, 9 Dec 2015 14:26:14 -0500 Subject: [PATCH 1/3] Configurable strategy support --- src/pacomo.js | 61 +++++++++++++++++++++++++++++++++++---------------- test.js | 25 ++++++++++++++++----- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/src/pacomo.js b/src/pacomo.js index f646526..387218d 100644 --- a/src/pacomo.js +++ b/src/pacomo.js @@ -1,13 +1,38 @@ import classNames from 'classnames' import { isValidElement, cloneElement, Children, PropTypes } from 'react' +export class PacomoStrategy { -export function prefixedClassNames(prefix, ...args) { + constructor(packageName){ + this.packageName = packageName; + } + + static withPackageName(packageName){ + return new PacomoStrategy(packageName) + } + + getComponentName(component){ + return component.displayName || component.name; + } + + getComponentClassName(component){ + const componentName = this.getComponentName(component); + return `${this.packageName}-${componentName}`; + } + + getPropClassName(component, name){ + const prefix = this.getComponentClassName(component); + return `${prefix}-${name}`; + } + +} + +export function prefixedClassNames(strategy, component, ...args) { return ( classNames(...args) .split(/\s+/) - .filter(name => name !== "") - .map(name => `${prefix}-${name}`) + .filter(name => !!name) + .map(name => strategy.getPropClassName(component, name)) .join(' ') ) } @@ -46,7 +71,7 @@ function transformElementProps(props, fn, childrenOnly) { changes.children = transformedChildren } } - + if (!childrenOnly) { for (let key of Object.keys(props)) { if (key == 'children') continue @@ -75,7 +100,7 @@ function skipPropElements(props) { } -export function transformWithPrefix(prefix) { +export function transformWithPrefix(strategy, componentFunction) { const childTransform = element => transform(element) // Prefix all `className` props on the passed in ReactElement object, its @@ -92,7 +117,7 @@ export function transformWithPrefix(prefix) { ) if (element.props.className) { - changes.className = `${rootClass || ''} ${prefixedClassNames(prefix, element.props.className)} ${suffixClasses}` + changes.className = `${rootClass || ''} ${prefixedClassNames(strategy, componentFunction, element.props.className)} ${suffixClasses}` } else if (rootClass) { changes.className = `${rootClass} ${suffixClasses}` @@ -108,19 +133,21 @@ export function transformWithPrefix(prefix) { return transform } +export function withPackageName(packageName){ + return withStrategy(PacomoStrategy.withPackageName(packageName)); +} -export function withPackageName(packageName) { +export function withStrategy(strategy) { return { // Transform a stateless function component transformer(componentFunction) { - const componentName = componentFunction.displayName || componentFunction.name - const prefix = `${packageName}-${componentName}` - const transform = transformWithPrefix(prefix) + const componentName = strategy.getComponentName(componentFunction); + const transform = transformWithPrefix(strategy, componentFunction) const transformedComponent = (props, ...args) => transform( componentFunction(skipPropElements(props), ...args), - prefix, + strategy.getComponentClassName(componentFunction), props.className ) @@ -135,15 +162,14 @@ export function withPackageName(packageName) { // Transform a React.Component class decorator(componentClass) { - const componentName = componentClass.displayName || componentClass.name - const prefix = `${packageName}-${componentName}` - const transform = transformWithPrefix(prefix) + const componentName = strategy.getComponentName(componentClass); + const transform = transformWithPrefix(strategy, componentClass) const DecoratedComponent = class DecoratedComponent extends componentClass { render() { const rawProps = this.props this.props = skipPropElements(this.props) - const transformed = transform(super.render(), prefix, this.props.className) + const transformed = transform(super.render(), strategy.getComponentClassName(componentClass), this.props.className) this.props = rawProps return transformed } @@ -151,10 +177,7 @@ export function withPackageName(packageName) { DecoratedComponent.displayName = `pacomo(${componentName})` - // Add `className` propType, if none exists - DecoratedComponent.propTypes = { className: PropTypes.string, ...componentClass.propTypes } - - return DecoratedComponent + return DecoratedComponent }, } } diff --git a/test.js b/test.js index 2ecbc36..6014307 100644 --- a/test.js +++ b/test.js @@ -1,11 +1,17 @@ import 'babel-polyfill' -import {prefixedClassNames, withPackageName, transformWithPrefix} from './lib/pacomo' +import { + PacomoStrategy, + prefixedClassNames, + withPackageName, + transformWithPrefix +} from './lib/pacomo' import React, {Component, PropTypes} from 'react' import TestUtils from 'react-addons-test-utils' import assert from 'assert' - -const testTransform = transformWithPrefix('test') +const testTransform = transformWithPrefix({ + getPropClassName: (component, name)=> `test-${name}` +}); const { transformer, decorator } = withPackageName('prefix') @@ -16,7 +22,7 @@ const { transformer, decorator } = withPackageName('prefix') function shallowRenderElement(element) { const shallowRenderer = TestUtils.createRenderer() shallowRenderer.render(element) - return shallowRenderer.getRenderOutput() + return shallowRenderer.getRenderOutput() } function shallowRenderComponent(Component, props) { @@ -260,9 +266,16 @@ describe('stateless render', () => { describe('prefixedClassNames', () => { + let strategy; + beforeEach(()=>{ + strategy = { + getPropClassName: (component, name)=> `test-prefix-${name}` + }; + }) + it("accepts an object, producing the correct result", () => { assert.equal( - prefixedClassNames('test-prefix', { + prefixedClassNames(strategy, null, { active: true, inactive: false, }), @@ -273,7 +286,7 @@ describe('prefixedClassNames', () => { it("accepts a string, producing the correct result", () => { assert.equal( - prefixedClassNames('test-prefix', 'test1', 'test2'), + prefixedClassNames(strategy, null, 'test1', 'test2'), 'test-prefix-test1 test-prefix-test2', "`classNames` produces the correct string when a string is passed in" ) From 225b8e98eecba5c6f8ff43859fe0b04129cf6877 Mon Sep 17 00:00:00 2001 From: Patrick Winters Date: Wed, 9 Dec 2015 14:33:07 -0500 Subject: [PATCH 2/3] remove read-only proptype manipulation --- test.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/test.js b/test.js index 6014307..051ee67 100644 --- a/test.js +++ b/test.js @@ -133,16 +133,6 @@ describe('decorator', () => { "other propTypes from original class is passed through" ) }) - - it("should add a className propType if one doesn't already exist", () => { - const decoratedClass = decorator(BareComponent) - - assert.equal( - decoratedClass.propTypes.className, - PropTypes.string, - "className propType exists" - ) - }) }) From 36f8ca50ffe9aff076417ba0bbb03135db216c09 Mon Sep 17 00:00:00 2001 From: Patrick Winters Date: Wed, 23 Dec 2015 19:49:10 -0500 Subject: [PATCH 3/3] strategy as factory function --- src/pacomo.js | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/pacomo.js b/src/pacomo.js index 387218d..c5fc647 100644 --- a/src/pacomo.js +++ b/src/pacomo.js @@ -1,30 +1,21 @@ import classNames from 'classnames' import { isValidElement, cloneElement, Children, PropTypes } from 'react' -export class PacomoStrategy { - - constructor(packageName){ - this.packageName = packageName; - } - - static withPackageName(packageName){ - return new PacomoStrategy(packageName) - } - - getComponentName(component){ - return component.displayName || component.name; - } - - getComponentClassName(component){ - const componentName = this.getComponentName(component); - return `${this.packageName}-${componentName}`; - } - - getPropClassName(component, name){ - const prefix = this.getComponentClassName(component); - return `${prefix}-${name}`; - } - +function pacomoStrategy(packageName){ + const strategy = { + getComponentName: function(component){ + return component.displayName || component.name; + }, + getComponentClassName: function(component){ + const componentName = strategy.getComponentName(component); + return `${packageName}-${componentName}`; + }, + getPropClassName: function(component, name){ + const prefix = strategy.getComponentClassName(component); + return `${prefix}-${name}`; + } + }; + return strategy; } export function prefixedClassNames(strategy, component, ...args) { @@ -134,7 +125,7 @@ export function transformWithPrefix(strategy, componentFunction) { } export function withPackageName(packageName){ - return withStrategy(PacomoStrategy.withPackageName(packageName)); + return withStrategy(pacomoStrategy(packageName)); } export function withStrategy(strategy) {