unexpected-htmllike-jsx-adapter
Version:
Adapter for unexpected-htmllike for ReactElements, e.g. JSX and the shallow renderer
175 lines (136 loc) • 5.04 kB
JavaScript
import React from 'react';
import ObjectAssign from 'object-assign';
import getIteratorFn from './getIteratorFn';
const REACT_ELEMENT_TYPE =
(typeof Symbol === 'function' && Symbol.for && Symbol.for('react.element')) ||
0xeac7;
function isRawType(value) {
var type = typeof value;
return type === 'string' ||
type === 'number' ||
type === 'boolean' ||
type === 'undefined' ||
value === null;
}
const DefaultOptions = {
concatTextContent: false
};
function convertValueTypeToString(value) {
if (typeof value === 'string') { // Common case can be fasttracked
return value;
}
if (value === null || value === undefined) {
return '';
}
return '' + value;
}
function concatenateStringChildren(accum, value) {
if (isRawType(value) && accum.length &&
isRawType(accum[accum.length - 1]))
{
accum[accum.length - 1] = convertValueTypeToString(accum[accum.length - 1]) + convertValueTypeToString(value);
return accum;
}
accum.push(value);
return accum;
}
function isValidChild(child) {
const typeofChild = typeof child;
return (typeofChild === 'string' ||
typeofChild === 'number' ||
(typeofChild === 'function' && child._expectIt) ||
(typeof child === 'object' && child !== null &&
(child._isReactElement /* React 0.13 */
|| child.$$typeof === REACT_ELEMENT_TYPE /* React 0.14 */))
);
}
function flatten(value) {
let iteratorFn = getIteratorFn(value);
if (!Array.isArray(value) && typeof value !== 'string' && iteratorFn) {
const iterator = iteratorFn.call(value);
const childrenArray = [];
let step;
while (!(step = iterator.next()).done) {
childrenArray.push(step.value);
}
return childrenArray;
}
if (!Array.isArray(value)) {
return [value];
}
return value.reduce((result, item) => result.concat(flatten(item)), [])
}
class ReactElementAdapter {
constructor(options) {
this._options = ObjectAssign({}, DefaultOptions, options);
}
getName(element) {
if (typeof element.type === 'string') {
return element.type;
}
return element.type.displayName || element.type.name || 'no-display-name';
}
getAttributes(element) {
var realProps = {};
if (element.props) {
for (var key in element.props) {
if (key !== 'children') {
realProps[key] = element.props[key];
}
}
}
if (this._options.includeKeyProp && element.key) {
realProps.key = element.key;
}
if (this._options.includeRefProp && element.ref) {
realProps.ref = element.ref;
}
return realProps;
}
getChildren(element) {
var children = element.props.children;
var childrenArray = [];
var iteratorFn;
// This is not using React.Children.forEach / map / toArray because it drops invalid children,
// which would be fine, but we want to explicitly include the `expect.it()` function as a valid child
// to enable inline assertions
// This mirrors the React.Children.forEach logic, as seen at
// https://github.com/facebook/react/blob/35962a00084382b49d1f9e3bd36612925f360e5b/src/shared/utils/traverseAllChildren.js
// with the exception that we remove the nulls
// Basically strings & numbers && elements are allowed (elements classed as objects & functions for simplicity)
if (Array.isArray(children)) {
childrenArray = flatten(children).filter(child => isValidChild(child));
} else if (isValidChild(children)) {
childrenArray = [ children ];
} else if (iteratorFn = getIteratorFn(children)) {
const iterator = iteratorFn.call(children);
let step;
while (!(step = iterator.next()).done) {
childrenArray.push(step.value);
}
}
if (this._options.convertToString ||
(this._options.convertMultipleRawToStrings &&
childrenArray.length > 1)) {
childrenArray = childrenArray.reduce((agg, child) => {
if (child !== null && child !== undefined && isRawType(child)) {
child = convertValueTypeToString(child);
}
agg.push(child);
return agg;
}, []);
}
if (this._options.concatTextContent) {
childrenArray = childrenArray.reduce(concatenateStringChildren, []);
}
return childrenArray;
}
setOptions(newOptions) {
this._options = ObjectAssign({}, this._options, newOptions);
}
getOptions() {
return this._options;
}
}
ReactElementAdapter.prototype.classAttributeName = 'className';
export default ReactElementAdapter;