@cicada/render
Advanced tools
Comparing version 1.1.0 to 1.1.1
@@ -55,2 +55,4 @@ 'use strict'; | ||
var DISPLAY_NONE = '' + _createAppearance.STYLE_SHEET_PREFIX + _createAppearance.DISPLAY_NONE; | ||
function createButton() { | ||
@@ -202,2 +204,21 @@ var rendered = 0; | ||
}); | ||
test('render with visible', function () { | ||
var configWidthVisible = { | ||
type: 'DynamicRender', | ||
bind: 'dynamic', | ||
visible: true | ||
}; | ||
var container = (0, _enzyme.mount)(_react2.default.createElement(_Render2.default, { | ||
stateTree: stateTree, | ||
appearance: appearance, | ||
components: components, | ||
background: (0, _createBackground2.default)(backgroundDef, stateTree, appearance), | ||
config: configWidthVisible | ||
})); | ||
var stateId = stateTree.get('dynamic')._id; | ||
container.instance().appearance.setVisibleById(stateId, false); | ||
expect(container.instance().appearance.isVisibleById(stateId)).toBe(false); | ||
expect(container.root.html()).toEqual('<div class="' + DISPLAY_NONE + '"></div>'); | ||
}); | ||
}); | ||
@@ -204,0 +225,0 @@ |
'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); | ||
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); | ||
var _createClass2 = require('babel-runtime/helpers/createClass'); | ||
var _createClass3 = _interopRequireDefault(_createClass2); | ||
var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); | ||
var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); | ||
var _inherits2 = require('babel-runtime/helpers/inherits'); | ||
var _inherits3 = _interopRequireDefault(_inherits2); | ||
var _react = require('react'); | ||
@@ -49,5 +69,6 @@ | ||
var stateTree = null; /* eslint-disable react/no-multi-comp */ | ||
var DISPLAY_NONE = '' + _createAppearance.STYLE_SHEET_PREFIX + _createAppearance.DISPLAY_NONE; /* eslint-disable react/no-multi-comp */ | ||
/* eslint-disable newline-per-chained-call */ | ||
var stateTree = null; | ||
var appearance = null; | ||
@@ -59,2 +80,43 @@ beforeEach(function () { | ||
/* eslint-disable */ | ||
var Inner = function (_React$Component) { | ||
(0, _inherits3.default)(Inner, _React$Component); | ||
function Inner() { | ||
(0, _classCallCheck3.default)(this, Inner); | ||
return (0, _possibleConstructorReturn3.default)(this, (Inner.__proto__ || (0, _getPrototypeOf2.default)(Inner)).apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(Inner, [{ | ||
key: 'render', | ||
value: function render() { | ||
return _react2.default.createElement( | ||
'div', | ||
null, | ||
'inner' | ||
); | ||
} | ||
}]); | ||
return Inner; | ||
}(_react2.default.Component); | ||
var NestedInner = function (_React$Component2) { | ||
(0, _inherits3.default)(NestedInner, _React$Component2); | ||
function NestedInner() { | ||
(0, _classCallCheck3.default)(this, NestedInner); | ||
return (0, _possibleConstructorReturn3.default)(this, (NestedInner.__proto__ || (0, _getPrototypeOf2.default)(NestedInner)).apply(this, arguments)); | ||
} | ||
(0, _createClass3.default)(NestedInner, [{ | ||
key: 'render', | ||
value: function render() { | ||
return _react2.default.createElement(Inner, null); | ||
} | ||
}]); | ||
return NestedInner; | ||
}(_react2.default.Component); | ||
/* eslint-enable */ | ||
var DemoComponent = { | ||
@@ -70,8 +132,7 @@ getDefaultState: function getDefaultState() { | ||
render: function render(_ref) { | ||
var state = _ref.state, | ||
style = _ref.style; | ||
var state = _ref.state; | ||
return _react2.default.createElement( | ||
'div', | ||
{ style: style }, | ||
null, | ||
state.value | ||
@@ -82,18 +143,58 @@ ); | ||
test('basic set appearance', function () { | ||
var container = (0, _enzyme.mount)(_react2.default.createElement(_Render2.default, { | ||
stateTree: stateTree, | ||
appearance: appearance, | ||
components: { Demo: (0, _connect2.default)(DemoComponent, 'Demo') }, | ||
config: { | ||
bind: 'demo', | ||
type: 'Demo', | ||
visible: true | ||
} | ||
})); | ||
describe('base set appearance', function () { | ||
function checkComponentDisplay(Comp, html) { | ||
var container = (0, _enzyme.mount)(_react2.default.createElement(_Render2.default, { | ||
stateTree: stateTree, | ||
appearance: appearance, | ||
components: { Demo: (0, _connect2.default)(Comp, 'Demo') }, | ||
config: { | ||
bind: 'demo', | ||
type: 'Demo', | ||
visible: true | ||
} | ||
})); | ||
var stateId = stateTree.get('demo')._id; | ||
container.instance().appearance.setVisibleById(stateId, false); | ||
expect(container.instance().appearance.isVisibleById(stateId)).toBe(false); | ||
expect(container.find('div').at(0).prop('style')).toEqual({ display: 'none' }); | ||
var stateId = stateTree.get('demo')._id; | ||
container.instance().appearance.setVisibleById(stateId, false); | ||
expect(container.instance().appearance.isVisibleById(stateId)).toBe(false); | ||
expect(container.root.html()).toEqual(html); | ||
return { container: container, stateId: stateId }; | ||
} | ||
test('basic set and reset', function () { | ||
var _checkComponentDispla = checkComponentDisplay({ render: function render() { | ||
return _react2.default.createElement( | ||
'div', | ||
{ className: 'other' }, | ||
'content' | ||
); | ||
} }, '<div class="other ' + DISPLAY_NONE + '">content</div>'), | ||
container = _checkComponentDispla.container, | ||
stateId = _checkComponentDispla.stateId; | ||
container.instance().appearance.setVisibleById(stateId, true); | ||
expect(container.html()).toEqual('<div class="other">content</div>'); | ||
}); | ||
test('empty set', function () { | ||
checkComponentDisplay({ render: function render() { | ||
return null; | ||
} }, null); | ||
}); | ||
test('inner set', function () { | ||
checkComponentDisplay({ render: function render() { | ||
return _react2.default.createElement(Inner, null); | ||
} }, '<div class="' + DISPLAY_NONE + '">inner</div>'); | ||
}); | ||
// 嵌套多层 | ||
test('nestedInner set', function () { | ||
checkComponentDisplay({ render: function render() { | ||
return _react2.default.createElement(NestedInner, null); | ||
} }, '<div class="' + DISPLAY_NONE + '">inner</div>'); | ||
}); | ||
/* test.skip('stateless set', () => { | ||
const Stateless = () => <div>stateless</div> | ||
// Stateless function components cannot support visible appearance. | ||
checkComponentDisplay({ render: () => <Stateless /> }, '<div>stateless</div>') | ||
}) */ | ||
}); | ||
@@ -134,3 +235,3 @@ | ||
expect(appearance.isVisibleById(stateId)).toBe(false); | ||
expect(container.find('Demo').at(1).find('div').at(0).prop('style')).toEqual({ display: 'none' }); | ||
expect(container.html()).toEqual('<div><div>hide</div><div class="' + DISPLAY_NONE + '"></div></div>'); | ||
}); | ||
@@ -174,3 +275,3 @@ | ||
expect(appearance.isVisibleById(stateId)).toBe(false); | ||
expect(container.find('Demo').at(1).find('div').at(0).prop('style')).toEqual({ display: 'none' }); | ||
expect(container.html()).toEqual('<div><div>hide</div><div class="' + DISPLAY_NONE + '"></div></div>'); | ||
}); |
@@ -409,2 +409,4 @@ 'use strict'; | ||
this.subscribe = function (type, changes) { | ||
// appearance 采用ref去控制不再需要触发render | ||
if (type === _constant.CHANGE_APPEARANCE) return; | ||
// CAUTION 在这里判断是因为未来 React 的 shouldComponentUpdate 会失效 | ||
@@ -411,0 +413,0 @@ if (shouldComponentUpdate === undefined || shouldComponentUpdate(_this6.getRenderArg(), type, changes) !== false) { |
@@ -6,2 +6,3 @@ 'use strict'; | ||
}); | ||
exports.STYLE_SHEET_PREFIX = exports.DISPLAY_NONE = undefined; | ||
@@ -18,2 +19,6 @@ var _extends2 = require('babel-runtime/helpers/extends'); | ||
var _reactDom = require('react-dom'); | ||
var _reactDom2 = _interopRequireDefault(_reactDom); | ||
var _util = require('./util'); | ||
@@ -27,6 +32,9 @@ | ||
var _constant = require('./constant'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var DISPLAY_NONE = exports.DISPLAY_NONE = 'DISPLAY_NONE'; | ||
var STYLE_SHEET_PREFIX = exports.STYLE_SHEET_PREFIX = 'CICADA_APPEAR_'; | ||
var prefixReg = new RegExp('^' + STYLE_SHEET_PREFIX); | ||
/* eslint-disable no-use-before-define */ | ||
function createAppearance(stateTree) { | ||
@@ -37,5 +45,33 @@ var externalProps = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; | ||
var subscribers = {}; | ||
var refCache = {}; | ||
var globalSubscribers = (0, _createSubscribers2.default)(); | ||
var stateIdByComponentPath = (0, _common.createOneToManyContainer)(); | ||
function createStylesheet(clsName, content) { | ||
clsName = '' + STYLE_SHEET_PREFIX + clsName; | ||
if (!document || document.getElementById(clsName)) return clsName; | ||
var styleDom = document.createElement('style'); | ||
styleDom.setAttribute('id', clsName); | ||
styleDom.innerText = '.' + clsName + ' ' + content; | ||
document.head.appendChild(styleDom); | ||
return clsName; | ||
} | ||
var displayNoneStyle = createStylesheet(DISPLAY_NONE, '{ display: none !important; }'); | ||
function updateAppearanceRef(id) { | ||
var ref = refCache[id]; | ||
if (!ref) return; | ||
var _appearance$id = appearance[id], | ||
visible = _appearance$id.visible, | ||
styleList = _appearance$id.styleList, | ||
children = _appearance$id.children; | ||
var classList = visible ? styleList : styleList.concat(displayNoneStyle); | ||
var className = ref.className.split(/\s+/).filter(function (cls) { | ||
return cls && !prefixReg.test(cls); | ||
}).concat(classList).join(' '); | ||
if (className) ref.className = className; | ||
if (children !== undefined) ref.innerHTML = children; | ||
} | ||
function injectExternalListenerArg(id, listener, props) { | ||
@@ -62,20 +98,2 @@ return function () { | ||
var style = appearance[id].style; | ||
if (!appearance[id].visible) { | ||
style.display = 'none'; | ||
} else if (display === _constant.DISPLAY_BLOCK) { | ||
delete style.display; | ||
// style.display = 'block' | ||
} else { | ||
style.display = 'inline-block'; | ||
} | ||
if (appearance[id].children !== undefined) { | ||
componentArg.children = appearance[id].children; | ||
} | ||
// 所有组件属性默认注入style属性 | ||
componentArg.style = style; | ||
var injectedExternalProps = (0, _util.mapValues)(externalProps, function (prop) { | ||
@@ -89,3 +107,24 @@ if (typeof prop !== 'function') return prop; | ||
return render(componentArg); | ||
var comp = render(componentArg); | ||
// maybe null | ||
if (!comp) { | ||
delete refCache[id]; | ||
return comp; | ||
} | ||
if ((0, _util.isStatelessComponent)(comp)) { | ||
console.error('Stateless function components cannot support visible appearance.'); | ||
delete refCache[id]; | ||
return comp; | ||
} | ||
return _react2.default.cloneElement(comp, (0, _extends3.default)({}, comp.props, { | ||
ref: function ref(_ref) { | ||
_ref = _reactDom2.default.findDOMNode(_ref); | ||
if (!_ref || !_ref.classList) { | ||
delete refCache[id]; | ||
return; | ||
} | ||
refCache[id] = _ref; | ||
updateAppearanceRef(id); | ||
} | ||
})); | ||
}; | ||
@@ -116,8 +155,8 @@ } | ||
return { | ||
register: function register(id, _ref, fn) { | ||
var componentPath = _ref.path; | ||
register: function register(id, _ref2, fn) { | ||
var componentPath = _ref2.path; | ||
var unsubscribe = subscribeById(id, fn); | ||
var unsubscribe = (0, _util.concat)([subscribeById(id, fn), subscribeById(id, (0, _util.partial)(updateAppearanceRef, id))]); | ||
var componentPathKey = componentPath !== undefined ? componentPath.join('.') : undefined; | ||
appearance[id] = { visible: true, style: {}, componentPath: componentPath | ||
appearance[id] = { visible: true, styleList: [], componentPath: componentPath | ||
// CAUTION 在与 react-router 这样的库结合使用时,由于会动态创建组件, | ||
@@ -133,2 +172,3 @@ // 所以 Render 无法给组件打上 path, 要兼容这种情况。 | ||
delete appearance[id]; | ||
delete refCache[id]; | ||
if (componentPathKey !== undefined) { | ||
@@ -158,7 +198,22 @@ stateIdByComponentPath.remove(componentPathKey, id); | ||
subscribeById: subscribeById, | ||
mergeStyleById: function mergeStyleById(id, styleToMerge) { | ||
appearance[id].style = (0, _extends3.default)({}, appearance[id].style, styleToMerge); | ||
addStyleById: function addStyleById(id) { | ||
for (var _len2 = arguments.length, styleList = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { | ||
styleList[_key2 - 1] = arguments[_key2]; | ||
} | ||
appearance[id].styleList = (0, _util.union)([], appearance[id].styleList.concat(styleList)); | ||
notify(id); | ||
}, | ||
removeStyleById: function removeStyleById(id) { | ||
for (var _len3 = arguments.length, styleList = Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) { | ||
styleList[_key3 - 1] = arguments[_key3]; | ||
} | ||
appearance[id].styleList = appearance[id].styleList.filter(function (cls) { | ||
return !styleList.includes(cls); | ||
}); | ||
notify(id); | ||
}, | ||
createStylesheet: createStylesheet, | ||
getIdsByComponentPath: function getIdsByComponentPath(pathArr) { | ||
@@ -165,0 +220,0 @@ return stateIdByComponentPath.get(pathArr.join('.')); |
@@ -43,2 +43,4 @@ 'use strict'; | ||
var _constant = require('./constant'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -70,3 +72,5 @@ | ||
_this.subscribe = function () { | ||
_this.subscribe = function (type) { | ||
// appearance 采用ref去控制不再需要触发render | ||
if (type === _constant.CHANGE_APPEARANCE) return; | ||
_this.setState({ version: _this.state.version++ }); | ||
@@ -80,3 +84,3 @@ }; | ||
var _appearance$register = appearance.register(_this.id, config, _this.subscribe), | ||
var _appearance$register = appearance.register(_this.id, config, (0, _util.partial)(_this.subscribe, _constant.CHANGE_APPEARANCE)), | ||
unsubscribeAppearance = _appearance$register.cancel, | ||
@@ -83,0 +87,0 @@ hijack = _appearance$register.hijack; |
@@ -8,2 +8,6 @@ 'use strict'; | ||
var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); | ||
var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); | ||
var _typeof2 = require('babel-runtime/helpers/typeof'); | ||
@@ -72,3 +76,8 @@ | ||
exports.filterMap = filterMap; | ||
exports.isStatelessComponent = isStatelessComponent; | ||
var _react = require('react'); | ||
var _react2 = _interopRequireDefault(_react); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
@@ -453,2 +462,9 @@ | ||
}); | ||
} | ||
function isStatelessComponent(comp) { | ||
if (!comp || !comp.type || !(typeof comp.type === 'function')) return false; | ||
/* eslint-disable no-proto */ | ||
var isReactComponent = (comp.type.__proto__ || (0, _getPrototypeOf2.default)(comp.type)) === _react2.default.Component; | ||
return !isReactComponent; | ||
} |
{ | ||
"name": "@cicada/render", | ||
"version": "1.1.0", | ||
"version": "1.1.1", | ||
"main": "./lib/index.js", | ||
"scripts": { | ||
"test": "jest ./src", | ||
"test:watch": "jest ./src --watch", | ||
"coverage": "jest --coverage ./src", | ||
@@ -8,0 +9,0 @@ "build": "rm -rf lib && babel src --out-dir lib", |
336354
58
8096