diff --git a/.eslintrc.yml b/.eslintrc.yml index 923a5e8..830b359 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -192,6 +192,7 @@ rules: quotes: 'off' radix: error "react/jsx-uses-react": 1 + "react/jsx-uses-vars": 1 require-jsdoc: 'off' require-yield: error rest-spread-spacing: error diff --git a/ComponentInterpolator.js b/ComponentInterpolator.js index d6ec36e..980baca 100644 --- a/ComponentInterpolator.js +++ b/ComponentInterpolator.js @@ -1,6 +1,6 @@ var React = require('react'); var invariant = require('invariant'); -var { string, object } = React.PropTypes; +var { string, object } = require('prop-types'); var WRAPPER_PATTERN = /(\*+)/; var PLACEHOLDER_PATTERN = /(%\{.*?\})/; @@ -47,79 +47,85 @@ var getInjectIndex = function(children, containerName) { return index; }; -var ComponentInterpolator = React.createClass({ - propTypes: { - string: string.isRequired, - wrappers: object - }, - - inferChildren() { - var tokens = (this.props.string || '').split(WRAPPER_PATTERN); - this.keyCounter = 0; - var inferredChildren = this.interpolateAllComponents(tokens); - - var currentChildren = toArray(this.props.children); - - var index = getInjectIndex(currentChildren, ''); - invariant(index >= 0, ' must have a "$1" text child"'); - currentChildren.splice.apply(currentChildren, [index, 1].concat(inferredChildren)); - return currentChildren; - }, - - interpolateAllComponents(tokens, eof) { - var token, child; - var children = []; - var wrappers = this.props.wrappers || {}; - while (tokens.length) { - token = tokens.shift(); - if (token === eof) break; - if (token.match(WRAPPER_PATTERN)) { - invariant( - child = wrappers[token], - ` expected '${token}' wrapper, none found` - ); - - child = injectNewDescendants( - child, - this.interpolateAllComponents(tokens, token), - { key: this.keyCounter++ }, - true - ); - children.push(child); - } - else { - children.push.apply(children, this.interpolatePlaceholders(token)); - } +var Counter = function() { + this.count = 0; + this.next = function() { + return this.count++; + }; +}; + +var inferChildren = function(props) { + var tokens = (props.string || '').split(WRAPPER_PATTERN); + var inferredChildren = interpolateAllComponents(tokens, props); + + var currentChildren = toArray(props.children); + + var index = getInjectIndex(currentChildren, ''); + invariant(index >= 0, ' must have a "$1" text child"'); + currentChildren.splice.apply(currentChildren, [index, 1].concat(inferredChildren)); + return currentChildren; +}; + +var interpolateAllComponents = function(tokens, props, keyCounter, eof) { + var token, child; + var children = []; + var wrappers = props.wrappers || {}; + if (!keyCounter) { + keyCounter = new Counter(); + } + while (tokens.length) { + token = tokens.shift(); + if (token === eof) break; + if (token.match(WRAPPER_PATTERN)) { + invariant( + child = wrappers[token], + ` expected '${token}' wrapper, none found` + ); + + child = injectNewDescendants( + child, + interpolateAllComponents(tokens, props, keyCounter, token), + { key: keyCounter.next() }, + true + ); + children.push(child); } - return children; - }, - - interpolatePlaceholders(string) { - var token, child; - var tokens = string.split(PLACEHOLDER_PATTERN); - var children = []; - while (tokens.length) { - token = tokens.shift(); - if (token.match(PLACEHOLDER_PATTERN)) { - token = token.slice(2, -1); - invariant( - this.props.hasOwnProperty(token), - ` expected '${token}' placeholder value, none found` - ); - child = this.props[token]; - child = child && child.type ? React.cloneElement(child, {key: this.keyCounter++}) : child; - children.push(child); - } else { - children.push(token); - } + else { + children.push.apply(children, interpolatePlaceholders(token, props, keyCounter)); } - return children; - }, + } + return children; +}; - render() { - return React.createElement('span', {}, this.inferChildren()); +var interpolatePlaceholders = function(string, props, keyCounter) { + var token, child; + var tokens = string.split(PLACEHOLDER_PATTERN); + var children = []; + while (tokens.length) { + token = tokens.shift(); + if (token.match(PLACEHOLDER_PATTERN)) { + token = token.slice(2, -1); + invariant( + props.hasOwnProperty(token), + ` expected '${token}' placeholder value, none found` + ); + child = props[token]; + child = child && child.type ? React.cloneElement(child, {key: keyCounter.next()}) : child; + children.push(child); + } else { + children.push(token); + } } -}); + return children; +}; -module.exports = ComponentInterpolator; +var ComponentInterpolator = function(props) { + return React.createElement('span', {}, inferChildren(props)); +}; + +ComponentInterpolator.propTypes = { + string: string.isRequired, + wrappers: object +}; +module.exports = ComponentInterpolator; diff --git a/__tests__/ComponentInterpolator.test.js b/__tests__/ComponentInterpolator.test.js index 89d7fdf..54b4431 100644 --- a/__tests__/ComponentInterpolator.test.js +++ b/__tests__/ComponentInterpolator.test.js @@ -1,55 +1,49 @@ -var subjector = require('../test_utils/subjector'); -var Subject = subjector(__dirname + '/../ComponentInterpolator'); var React = require('react'); -var ReactDOM = require('react-dom'); - -var removeNoise = function(string) { - return string.replace(//g, '') - .replace(/ data-reactid=".*?"/g, ''); -}; +var { shallow } = require('enzyme'); +var CI = require('../ComponentInterpolator'); describe('ComponentInterpolator', function() { it('renders', function() { - var subject = Subject({ - string: 'Hello World', - wrappers: {} - }, ["$1"]); - expect(subject.isMounted()).toEqual(true); - expect(ReactDOM.findDOMNode(subject).textContent).toEqual('Hello World'); + var component = $1; + expect(shallow(component).text()).toEqual('Hello World'); + }); + + it('wraps content in a ', function() { + var component = $1; + expect(shallow(component).html()).toEqual('Howdy!'); }); it('escapes html in the string', function() { - var subject = Subject({ - string: 'My favorite tag is