UNPKG

d2-ui

Version:
169 lines (135 loc) 4.33 kB
import React from 'react'; import isEmpty from 'lodash/isEmpty'; import isSubset from 'is-subset'; import { coercePropValue, propsOfNode, splitSelector, isCompoundSelector, selectorType, AND, SELECTOR, nodeHasType, } from './Utils'; export function childrenOfNode(node) { if (!node) return []; const maybeArray = propsOfNode(node).children; const result = []; React.Children.forEach(maybeArray, child => { if (child !== null && child !== false && typeof child !== 'undefined') { result.push(child); } }); return result; } export function hasClassName(node, className) { let classes = propsOfNode(node).className || ''; classes = classes.replace(/\s/g, ' '); return ` ${classes} `.indexOf(` ${className} `) > -1; } export function treeForEach(tree, fn) { if (tree !== null && tree !== false && typeof tree !== 'undefined') { fn(tree); } childrenOfNode(tree).forEach(node => treeForEach(node, fn)); } export function treeFilter(tree, fn) { const results = []; treeForEach(tree, node => { if (fn(node)) { results.push(node); } }); return results; } function pathFilter(path, fn) { return path.filter(tree => treeFilter(tree, fn).length !== 0); } export function pathToNode(node, root) { const queue = [root]; const path = []; const hasNode = (testNode) => node === testNode; while (queue.length) { const current = queue.pop(); const children = childrenOfNode(current); if (current === node) return pathFilter(path, hasNode); path.push(current); if (children.length === 0) { // leaf node. if it isn't the node we are looking for, we pop. path.pop(); } queue.push.apply(queue, children); } return null; } export function parentsOfNode(node, root) { return pathToNode(node, root).reverse(); } export function nodeHasId(node, id) { return propsOfNode(node).id === id; } export function nodeHasProperty(node, propKey, stringifiedPropValue) { const nodeProps = propsOfNode(node); const propValue = coercePropValue(propKey, stringifiedPropValue); const descriptor = Object.getOwnPropertyDescriptor(nodeProps, propKey); if (descriptor && descriptor.get) { return false; } const nodePropValue = nodeProps[propKey]; if (nodePropValue === undefined) { return false; } if (propValue) { return nodePropValue === propValue; } return nodeProps.hasOwnProperty(propKey); } export function nodeMatchesObjectProps(node, props) { return isSubset(propsOfNode(node), props); } export function buildPredicate(selector) { switch (typeof selector) { case 'function': // selector is a component constructor return node => node && node.type === selector; case 'string': if (isCompoundSelector.test(selector)) { return AND(splitSelector(selector).map(buildPredicate)); } switch (selectorType(selector)) { case SELECTOR.CLASS_TYPE: return node => hasClassName(node, selector.substr(1)); case SELECTOR.ID_TYPE: return node => nodeHasId(node, selector.substr(1)); case SELECTOR.PROP_TYPE: { const propKey = selector.split(/\[([a-zA-Z\-]*?)(=|\])/)[1]; const propValue = selector.split(/=(.*?)\]/)[1]; return node => nodeHasProperty(node, propKey, propValue); } default: // selector is a string. match to DOM tag or constructor displayName return node => nodeHasType(node, selector); } case 'object': if (!Array.isArray(selector) && selector !== null && !isEmpty(selector)) { return node => nodeMatchesObjectProps(node, selector); } throw new TypeError( 'Enzyme::Selector does not support an array, null, or empty object as a selector' ); default: throw new TypeError('Enzyme::Selector expects a string, object, or Component Constructor'); } } export function getTextFromNode(node) { if (node === null || node === undefined) { return ''; } if (typeof node === 'string' || typeof node === 'number') { return String(node); } if (node.type && typeof node.type === 'function') { return `<${node.type.displayName || node.type.name} />`; } return childrenOfNode(node).map(getTextFromNode).join('').replace(/\s+/, ' '); }