Match-id-c6fa3b4d5383d1b9910565ce95be74fa36a714a1
This commit is contained in:
commit
70027b0a1f
|
@ -1,4 +1,4 @@
|
|||
import { allDelegatedHorizonEvents } from '../../event/EventCollection';
|
||||
import { allDelegatedHorizonEvents } from '../../event/EventHub';
|
||||
import { updateCommonProp } from './UpdateCommonProp';
|
||||
import { setStyles } from './StyleHandler';
|
||||
import { lazyDelegateOnRoot, listenNonDelegatedEvent } from '../../event/EventBinding';
|
||||
|
@ -19,7 +19,6 @@ export function setDomProps(dom: Element, props: Object, isNativeTag: boolean, i
|
|||
setStyles(dom, propVal);
|
||||
} else if (isEventProp(propName)) {
|
||||
// 事件监听属性处理
|
||||
// TODO
|
||||
const currentRoot = getCurrentRoot();
|
||||
if (!allDelegatedHorizonEvents.has(propName)) {
|
||||
listenNonDelegatedEvent(propName, dom, propVal);
|
||||
|
|
|
@ -1,27 +1,29 @@
|
|||
/**
|
||||
* 事件绑定实现,分为绑定委托事件和非委托事件
|
||||
*/
|
||||
import { allDelegatedHorizonEvents, allDelegatedNativeEvents } from './EventCollection';
|
||||
import {isDocument} from '../dom/utils/Common';
|
||||
import {
|
||||
getNearestVNode,
|
||||
getNonDelegatedListenerMap,
|
||||
} from '../dom/DOMInternalKeys';
|
||||
import {runDiscreteUpdates} from '../renderer/TreeBuilder';
|
||||
import {isMounted} from '../renderer/vnode/VNodeUtils';
|
||||
import {SuspenseComponent} from '../renderer/vnode/VNodeTags';
|
||||
import {handleEventMain} from './HorizonEventMain';
|
||||
import {decorateNativeEvent} from './customEvents/EventFactory';
|
||||
allDelegatedHorizonEvents,
|
||||
allDelegatedNativeEvents,
|
||||
} from './EventHub';
|
||||
import { isDocument } from '../dom/utils/Common';
|
||||
import { getNearestVNode, getNonDelegatedListenerMap } from '../dom/DOMInternalKeys';
|
||||
import { runDiscreteUpdates } from '../renderer/TreeBuilder';
|
||||
import { handleEventMain } from './HorizonEventMain';
|
||||
import { decorateNativeEvent } from './EventWrapper';
|
||||
import { VNode } from '../renderer/vnode/VNode';
|
||||
|
||||
const listeningMarker = '_horizonListening' + Math.random().toString(36).slice(4);
|
||||
const listeningMarker =
|
||||
'_horizonListening' +
|
||||
Math.random()
|
||||
.toString(36)
|
||||
.slice(4);
|
||||
|
||||
// 触发委托事件
|
||||
function triggerDelegatedEvent(
|
||||
nativeEvtName: string,
|
||||
isCapture: boolean,
|
||||
targetDom: EventTarget,
|
||||
nativeEvent, // 事件对象event
|
||||
nativeEvent // 事件对象event
|
||||
) {
|
||||
// 执行之前的调度事件
|
||||
runDiscreteUpdates();
|
||||
|
@ -33,11 +35,7 @@ function triggerDelegatedEvent(
|
|||
}
|
||||
|
||||
// 监听委托事件
|
||||
function listenToNativeEvent(
|
||||
nativeEvtName: string,
|
||||
delegatedElement: Element,
|
||||
isCapture: boolean,
|
||||
): void {
|
||||
function listenToNativeEvent(nativeEvtName: string, delegatedElement: Element, isCapture: boolean): void {
|
||||
let dom: Element | Document = delegatedElement;
|
||||
// document层次可能触发selectionchange事件,为了捕获这类事件,selectionchange事件绑定在document节点上
|
||||
if (nativeEvtName === 'selectionchange' && !isDocument(delegatedElement)) {
|
||||
|
@ -70,10 +68,16 @@ export function lazyDelegateOnRoot(currentRoot: VNode, eventName: string) {
|
|||
|
||||
const isCapture = isCaptureEvent(eventName);
|
||||
const nativeEvents = allDelegatedHorizonEvents.get(eventName);
|
||||
nativeEvents.forEach(nativeEvents => {
|
||||
listenToNativeEvent(nativeEvents, currentRoot.realNode, isCapture);
|
||||
|
||||
nativeEvents.forEach(nativeEvent => {
|
||||
const nativeFullName = isCapture ? nativeEvent + 'capture' : nativeEvent;
|
||||
if (!currentRoot.delegatedNativeEvents.has(nativeFullName)) {
|
||||
listenToNativeEvent(nativeEvent, currentRoot.realNode, isCapture);
|
||||
currentRoot.delegatedNativeEvents.add(nativeFullName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 通过horizon事件名获取到native事件名
|
||||
function getNativeEvtName(horizonEventName, capture) {
|
||||
let nativeName;
|
||||
|
@ -105,11 +109,7 @@ function getWrapperListener(horizonEventName, nativeEvtName, targetElement, list
|
|||
}
|
||||
|
||||
// 非委托事件单独监听到各自dom节点
|
||||
export function listenNonDelegatedEvent(
|
||||
horizonEventName: string,
|
||||
domElement: Element,
|
||||
listener,
|
||||
): void {
|
||||
export function listenNonDelegatedEvent(horizonEventName: string, domElement: Element, listener): void {
|
||||
const isCapture = isCaptureEvent(horizonEventName);
|
||||
const nativeEvtName = getNativeEvtName(horizonEventName, isCapture);
|
||||
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
import {horizonEventToNativeMap} from './const';
|
||||
|
||||
// 需要委托的horizon事件和原生事件对应关系
|
||||
export const allDelegatedHorizonEvents = new Map();
|
||||
// 所有委托的原生事件集合
|
||||
export const allDelegatedNativeEvents = new Set();
|
||||
|
||||
horizonEventToNativeMap.forEach((dependencies, horizonEvent) => {
|
||||
allDelegatedHorizonEvents.set(horizonEvent, dependencies);
|
||||
allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies);
|
||||
|
||||
dependencies.forEach(d => {
|
||||
allDelegatedNativeEvents.add(d);
|
||||
});
|
||||
});
|
|
@ -1,3 +1,7 @@
|
|||
// 需要委托的horizon事件和原生事件对应关系
|
||||
export const allDelegatedHorizonEvents = new Map();
|
||||
// 所有委托的原生事件集合
|
||||
export const allDelegatedNativeEvents = new Set();
|
||||
|
||||
// Horizon事件和原生事件对应关系
|
||||
export const horizonEventToNativeMap = new Map([
|
||||
|
@ -33,10 +37,9 @@ export const horizonEventToNativeMap = new Map([
|
|||
['onAnimationEnd', ['animationend']],
|
||||
['onAnimationIteration', ['animationiteration']],
|
||||
['onAnimationStart', ['animationstart']],
|
||||
['onTransitionEnd', ['transitionend']]
|
||||
['onTransitionEnd', ['transitionend']],
|
||||
]);
|
||||
|
||||
export const CommonEventToHorizonMap = {
|
||||
export const NativeEventToHorizonMap = {
|
||||
click: 'click',
|
||||
dblclick: 'doubleClick',
|
||||
contextmenu: 'contextMenu',
|
||||
|
@ -69,11 +72,22 @@ export const CommonEventToHorizonMap = {
|
|||
compositionend: 'compositionEnd',
|
||||
compositionupdate: 'compositionUpdate',
|
||||
};
|
||||
|
||||
export const CHAR_CODE_SPACE = 32;
|
||||
|
||||
|
||||
export const EVENT_TYPE_BUBBLE = 'Bubble';
|
||||
export const EVENT_TYPE_CAPTURE = 'Capture';
|
||||
export const EVENT_TYPE_ALL = 'All';
|
||||
|
||||
horizonEventToNativeMap.forEach((dependencies, horizonEvent) => {
|
||||
allDelegatedHorizonEvents.set(horizonEvent, dependencies);
|
||||
allDelegatedHorizonEvents.set(horizonEvent + 'Capture', dependencies);
|
||||
|
||||
dependencies.forEach(d => {
|
||||
allDelegatedNativeEvents.add(d);
|
||||
});
|
||||
});
|
||||
|
||||
export function transformToHorizonEvent(nativeEvtName: string) {
|
||||
const name = NativeEventToHorizonMap[nativeEvtName];
|
||||
// 例:dragEnd -> onDragEnd
|
||||
return !name ? '' : `on${name[0].toUpperCase()}${name.slice(1)}`;
|
||||
}
|
|
@ -1,19 +1,76 @@
|
|||
import type { AnyNativeEvent } from './Types';
|
||||
import { ListenerUnitList } from './Types';
|
||||
import { AnyNativeEvent, ListenerUnitList } from './Types';
|
||||
import type { VNode } from '../renderer/Types';
|
||||
|
||||
import { CommonEventToHorizonMap, EVENT_TYPE_BUBBLE, EVENT_TYPE_CAPTURE, horizonEventToNativeMap } from './const';
|
||||
import { getListeners as getChangeListeners } from './simulatedEvtHandler/ChangeEventHandler';
|
||||
import { setPropertyWritable } from './utils';
|
||||
import { decorateNativeEvent } from './customEvents/EventFactory';
|
||||
import { isInputElement, setPropertyWritable } from './utils';
|
||||
import { decorateNativeEvent } from './EventWrapper';
|
||||
import { getListenersFromTree } from './ListenerGetter';
|
||||
import { asyncUpdates, runDiscreteUpdates } from '../renderer/Renderer';
|
||||
import { findRoot } from '../renderer/vnode/VNodeUtils';
|
||||
import { syncRadiosHandler } from '../dom/valueHandler/InputValueHandler';
|
||||
import {
|
||||
EVENT_TYPE_ALL,
|
||||
EVENT_TYPE_BUBBLE,
|
||||
EVENT_TYPE_CAPTURE,
|
||||
horizonEventToNativeMap,
|
||||
transformToHorizonEvent,
|
||||
} from './EventHub';
|
||||
import { getDomTag } from '../dom/utils/Common';
|
||||
import { updateInputValueIfChanged } from '../dom/valueHandler/ValueChangeHandler';
|
||||
import { getDom } from '../dom/DOMInternalKeys';
|
||||
|
||||
// web规范,鼠标右键key值
|
||||
const RIGHT_MOUSE_BUTTON = 2;
|
||||
|
||||
// 返回是否需要触发change事件标记
|
||||
// | 元素 | 事件 | 需要值变更 |
|
||||
// | --- | --- | --------------- |
|
||||
// | <select/> / <input type="file/> | change | NO |
|
||||
// | <input type="checkbox" /> <input type="radio" /> | click | YES |
|
||||
// | <input type="input /> / <input type="text" /> | input / change | YES |
|
||||
function shouldTriggerChangeEvent(targetDom, evtName) {
|
||||
const { type } = targetDom;
|
||||
const domTag = getDomTag(targetDom);
|
||||
|
||||
if (domTag === 'select' || (domTag === 'input' && type === 'file')) {
|
||||
return evtName === 'change';
|
||||
} else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) {
|
||||
if (evtName === 'click') {
|
||||
return updateInputValueIfChanged(targetDom);
|
||||
}
|
||||
} else if (isInputElement(targetDom)) {
|
||||
if (evtName === 'input' || evtName === 'change') {
|
||||
return updateInputValueIfChanged(targetDom);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 支持input/textarea/select的onChange事件
|
||||
*/
|
||||
function getChangeListeners(
|
||||
nativeEvtName: string,
|
||||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode,
|
||||
): ListenerUnitList {
|
||||
if (!vNode) {
|
||||
return [];
|
||||
}
|
||||
const targetDom = getDom(vNode);
|
||||
|
||||
// 判断是否需要触发change事件
|
||||
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
|
||||
const event = decorateNativeEvent(
|
||||
'onChange',
|
||||
'change',
|
||||
nativeEvt,
|
||||
);
|
||||
return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// 获取事件触发的普通事件监听方法队列
|
||||
function getCommonListeners(
|
||||
nativeEvtName: string,
|
||||
|
@ -22,8 +79,7 @@ function getCommonListeners(
|
|||
target: null | EventTarget,
|
||||
isCapture: boolean,
|
||||
): ListenerUnitList {
|
||||
const name = CommonEventToHorizonMap[nativeEvtName];
|
||||
const horizonEvtName = !name ? '' : `on${name[0].toUpperCase()}${name.slice(1)}`; // 例:dragEnd -> onDragEnd
|
||||
const horizonEvtName = transformToHorizonEvent(nativeEvtName);
|
||||
|
||||
if (!horizonEvtName) {
|
||||
return [];
|
||||
|
@ -66,13 +122,16 @@ function processListeners(listenerList: ListenerUnitList): void {
|
|||
});
|
||||
}
|
||||
|
||||
function getProcessListeners(
|
||||
// 触发可以被执行的horizon事件监听
|
||||
function triggerHorizonEvents(
|
||||
nativeEvtName: string,
|
||||
vNode: VNode | null,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
target,
|
||||
isCapture: boolean,
|
||||
): ListenerUnitList {
|
||||
nativeEvent: AnyNativeEvent,
|
||||
vNode: VNode | null,
|
||||
) {
|
||||
const target = nativeEvent.target || nativeEvent.srcElement;
|
||||
let hasTriggeredChangeEvent = false;
|
||||
|
||||
// 触发普通委托事件
|
||||
let listenerList: ListenerUnitList = getCommonListeners(
|
||||
nativeEvtName,
|
||||
|
@ -83,34 +142,22 @@ function getProcessListeners(
|
|||
);
|
||||
|
||||
// 触发特殊handler委托事件
|
||||
if (!isCapture) {
|
||||
if (horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) {
|
||||
listenerList = listenerList.concat(getChangeListeners(
|
||||
if (!isCapture && horizonEventToNativeMap.get('onChange')!.includes(nativeEvtName)) {
|
||||
const changeListeners = getChangeListeners(
|
||||
nativeEvtName,
|
||||
nativeEvent,
|
||||
vNode,
|
||||
));
|
||||
);
|
||||
if (changeListeners.length) {
|
||||
hasTriggeredChangeEvent = true;
|
||||
listenerList = listenerList.concat(changeListeners);
|
||||
}
|
||||
}
|
||||
return listenerList;
|
||||
}
|
||||
|
||||
// 触发可以被执行的horizon事件监听
|
||||
function triggerHorizonEvents(
|
||||
nativeEvtName: string,
|
||||
isCapture: boolean,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
vNode: VNode | null,
|
||||
) {
|
||||
const nativeEventTarget = nativeEvent.target || nativeEvent.srcElement;
|
||||
|
||||
// 获取委托事件队列
|
||||
const listenerList = getProcessListeners(nativeEvtName, vNode, nativeEvent, nativeEventTarget, isCapture);
|
||||
|
||||
// 处理触发的事件队列
|
||||
processListeners(listenerList);
|
||||
|
||||
return listenerList;
|
||||
return hasTriggeredChangeEvent;
|
||||
}
|
||||
|
||||
|
||||
|
@ -141,15 +188,12 @@ export function handleEventMain(
|
|||
|
||||
// 没有事件在执行,经过调度再执行事件
|
||||
isInEventsExecution = true;
|
||||
let shouldDispatchUpdate = false;
|
||||
let hasTriggeredChangeEvent = false;
|
||||
try {
|
||||
const listeners = asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode));
|
||||
if (listeners.length) {
|
||||
shouldDispatchUpdate = true;
|
||||
}
|
||||
hasTriggeredChangeEvent = asyncUpdates(() => triggerHorizonEvents(nativeEvtName, isCapture, nativeEvent, startVNode));
|
||||
} finally {
|
||||
isInEventsExecution = false;
|
||||
if (shouldDispatchUpdate) {
|
||||
if (hasTriggeredChangeEvent) {
|
||||
runDiscreteUpdates();
|
||||
// 若是Radio,同步同组其他Radio的Handler Value
|
||||
syncRadiosHandler(nativeEvent.target as Element);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { VNode } from '../renderer/Types';
|
||||
import { DomComponent } from '../renderer/vnode/VNodeTags';
|
||||
import { EVENT_TYPE_ALL, EVENT_TYPE_CAPTURE, EVENT_TYPE_BUBBLE } from './const';
|
||||
import { AnyNativeEvent, ListenerUnitList } from './Types';
|
||||
import { EVENT_TYPE_ALL, EVENT_TYPE_BUBBLE, EVENT_TYPE_CAPTURE } from './EventHub';
|
||||
|
||||
// 从vnode属性中获取事件listener
|
||||
function getListenerFromVNode(vNode: VNode, eventName: string): Function | null {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
import {decorateNativeEvent} from '../customEvents/EventFactory';
|
||||
import {getDom} from '../../dom/DOMInternalKeys';
|
||||
import {updateInputValueIfChanged} from '../../dom/valueHandler/ValueChangeHandler';
|
||||
import {isInputElement} from '../utils';
|
||||
import {EVENT_TYPE_ALL} from '../const';
|
||||
import {AnyNativeEvent, ListenerUnitList} from '../Types';
|
||||
import {
|
||||
getListenersFromTree,
|
||||
} from '../ListenerGetter';
|
||||
import {VNode} from '../../renderer/Types';
|
||||
import {getDomTag} from '../../dom/utils/Common';
|
||||
|
||||
// 返回是否需要触发change事件标记
|
||||
// | 元素 | 事件 | 需要值变更 |
|
||||
// | --- | --- | --------------- |
|
||||
// | <select/> / <input type="file/> | change | NO |
|
||||
// | <input type="checkbox" /> <input type="radio" /> | click | YES |
|
||||
// | <input type="input /> / <input type="text" /> | input / change | YES |
|
||||
function shouldTriggerChangeEvent(targetDom, evtName) {
|
||||
const { type } = targetDom;
|
||||
const domTag = getDomTag(targetDom);
|
||||
|
||||
if (domTag === 'select' || (domTag === 'input' && type === 'file')) {
|
||||
return evtName === 'change';
|
||||
} else if (domTag === 'input' && (type === 'checkbox' || type === 'radio')) {
|
||||
if (evtName === 'click') {
|
||||
return updateInputValueIfChanged(targetDom);
|
||||
}
|
||||
} else if (isInputElement(targetDom)) {
|
||||
if (evtName === 'input' || evtName === 'change') {
|
||||
return updateInputValueIfChanged(targetDom);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 支持input/textarea/select的onChange事件
|
||||
*/
|
||||
export function getListeners(
|
||||
nativeEvtName: string,
|
||||
nativeEvt: AnyNativeEvent,
|
||||
vNode: null | VNode
|
||||
): ListenerUnitList {
|
||||
if (!vNode) {
|
||||
return [];
|
||||
}
|
||||
const targetDom = getDom(vNode);
|
||||
|
||||
// 判断是否需要触发change事件
|
||||
if (shouldTriggerChangeEvent(targetDom, nativeEvtName)) {
|
||||
const event = decorateNativeEvent(
|
||||
'onChange',
|
||||
'change',
|
||||
nativeEvt,
|
||||
);
|
||||
return getListenersFromTree(vNode, 'onChange', event, EVENT_TYPE_ALL);
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
|
@ -78,6 +78,7 @@ export class VNode {
|
|||
// 根节点数据
|
||||
toUpdateNodes: Set<VNode> | null; // 保存要更新的节点
|
||||
delegatedEvents: Set<string>
|
||||
delegatedNativeEvents: Set<string>
|
||||
|
||||
belongClassVNode: VNode | null = null; // 记录JSXElement所属class vNode,处理ref的时候使用
|
||||
|
||||
|
@ -98,6 +99,7 @@ export class VNode {
|
|||
this.task = null;
|
||||
this.toUpdateNodes = new Set<VNode>();
|
||||
this.delegatedEvents = new Set<string>();
|
||||
this.delegatedNativeEvents = new Set<string>();
|
||||
this.updates = null;
|
||||
this.stateCallbacks = null;
|
||||
this.state = null;
|
||||
|
|
|
@ -30,6 +30,7 @@ describe('useEffect Hook Test', () => {
|
|||
expect(document.getElementById('p').style.display).toBe('block');
|
||||
// 点击按钮触发num加1
|
||||
container.querySelector('button').click();
|
||||
|
||||
expect(document.getElementById('p').style.display).toBe('none');
|
||||
container.querySelector('button').click();
|
||||
expect(container.querySelector('p').style.display).toBe('inline');
|
||||
|
|
|
@ -22,16 +22,6 @@ describe('Dom Input', () => {
|
|||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('checked属性受控时无法更改', () => {
|
||||
Horizon.render(<input type='checkbox' checked={true} onChange={() => {
|
||||
LogUtils.log('checkbox click');
|
||||
}} />, container);
|
||||
container.querySelector('input').click();
|
||||
// 点击复选框不会改变checked的值
|
||||
expect(LogUtils.getAndClear()).toEqual(['checkbox click']);
|
||||
expect(container.querySelector('input').checked).toBe(true);
|
||||
});
|
||||
|
||||
it('复选框的value属性值可以改变', () => {
|
||||
Horizon.render(
|
||||
<input type='checkbox' value='' onChange={() => {
|
||||
|
@ -96,30 +86,6 @@ describe('Dom Input', () => {
|
|||
).not.toThrow();
|
||||
});
|
||||
|
||||
it('value属性受控时无法更改', () => {
|
||||
const realNode = Horizon.render(<input type='text' value={'text'} onChange={() => {
|
||||
LogUtils.log('text change');
|
||||
}} />, container);
|
||||
|
||||
// 模拟改变text输入框的值
|
||||
// 先修改
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLInputElement.prototype,
|
||||
'value',
|
||||
).set.call(realNode, 'abcd');
|
||||
// 再触发事件
|
||||
realNode.dispatchEvent(
|
||||
new Event('input', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
// 确实发生了input事件
|
||||
expect(LogUtils.getAndClear()).toEqual(['text change']);
|
||||
// value受控,不会改变
|
||||
expect(container.querySelector('input').value).toBe('text');
|
||||
});
|
||||
|
||||
it('value值会转为字符串', () => {
|
||||
const realNode = Horizon.render(<input type='text' value={1} />, container);
|
||||
expect(realNode.value).toBe('1');
|
||||
|
@ -249,30 +215,6 @@ describe('Dom Input', () => {
|
|||
expect(document.getElementById('d').checked).toBe(true);
|
||||
});
|
||||
|
||||
it('受控radio的状态', () => {
|
||||
Horizon.render(
|
||||
<>
|
||||
<input type='radio' name='a' checked={true} />
|
||||
<input id='b' type='radio' name='a' checked={false} />
|
||||
</>, container);
|
||||
expect(container.querySelector('input').checked).toBe(true);
|
||||
expect(document.getElementById('b').checked).toBe(false);
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLInputElement.prototype,
|
||||
'checked',
|
||||
).set.call(document.getElementById('b'), true);
|
||||
// 再触发事件
|
||||
document.getElementById('b').dispatchEvent(
|
||||
new Event('click', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
// 模拟点击单选框B,两个受控radio的状态不会改变
|
||||
expect(container.querySelector('input').checked).toBe(true);
|
||||
expect(document.getElementById('b').checked).toBe(false);
|
||||
});
|
||||
|
||||
it('name改变不影响相同name的radio', () => {
|
||||
const inputRef = Horizon.createRef();
|
||||
const App = () => {
|
||||
|
|
|
@ -53,37 +53,6 @@ describe('Dom Select', () => {
|
|||
expect(realNode.value).toBe('React');
|
||||
});
|
||||
|
||||
it('受控select', () => {
|
||||
const selectNode = (
|
||||
<select value='Vue'>
|
||||
<option value='React'>React.js</option>
|
||||
<option value='Vue'>Vue.js</option>
|
||||
<option value='Angular'>Angular.js</option>
|
||||
</select>
|
||||
);
|
||||
const realNode = Horizon.render(selectNode, container);
|
||||
expect(realNode.value).toBe('Vue');
|
||||
expect(realNode.options[1].selected).toBe(true);
|
||||
// 先修改
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLSelectElement.prototype,
|
||||
'value',
|
||||
).set.call(realNode, 'React');
|
||||
// 再触发事件
|
||||
container.querySelector('select').dispatchEvent(
|
||||
new Event('change', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
// 鼠标改变受控select不生效
|
||||
Horizon.render(selectNode, container);
|
||||
// 'React'项没有被选中
|
||||
expect(realNode.options[0].selected).toBe(false);
|
||||
expect(realNode.options[1].selected).toBe(true);
|
||||
expect(realNode.value).toBe('Vue');
|
||||
});
|
||||
|
||||
it('受控select转为不受控会保存原来select', () => {
|
||||
const selectNode = (
|
||||
<select value='Vue'>
|
||||
|
|
|
@ -38,26 +38,6 @@ describe('Dom Textarea', () => {
|
|||
expect(realNode.value).toBe('React');
|
||||
});
|
||||
|
||||
it('受控组件value不变', () => {
|
||||
let realNode = Horizon.render(<textarea value='text' />, container);
|
||||
expect(realNode.getAttribute('value')).toBe(null);
|
||||
expect(realNode.value).toBe('text');
|
||||
// 先修改
|
||||
Object.getOwnPropertyDescriptor(
|
||||
HTMLTextAreaElement.prototype,
|
||||
'value',
|
||||
).set.call(realNode, 'textabc');
|
||||
// 再触发事件
|
||||
container.querySelector('textarea').dispatchEvent(
|
||||
new Event('change', {
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
);
|
||||
// 组件受控,想要改变value,需要通过onChange改变state
|
||||
expect(realNode.value).toBe('text');
|
||||
});
|
||||
|
||||
it('设置defaultValue', () => {
|
||||
let defaultVal = 'Vue';
|
||||
const textareaNode = <textarea defaultValue={defaultVal} />;
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
import * as Horizon from '@cloudsop/horizon/index.ts';
|
||||
import * as TestUtils from '../jest/testUtils';
|
||||
|
||||
function dispatchChangeEvent(input) {
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
|
||||
nativeInputValueSetter.call(input, 'test');
|
||||
|
||||
input.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
}
|
||||
|
||||
describe('事件', () => {
|
||||
const LogUtils = TestUtils.getLogUtils();
|
||||
it('根节点挂载全量事件', () => {
|
||||
|
@ -162,11 +169,7 @@ describe('事件', () => {
|
|||
LogUtils.log('change');
|
||||
},
|
||||
});
|
||||
|
||||
const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
|
||||
nativeInputValueSetter.call(inputRef.current, 'test');
|
||||
|
||||
inputRef.current.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
dispatchChangeEvent(inputRef.current);
|
||||
|
||||
expect(LogUtils.getAndClear()).toEqual(['change']);
|
||||
});
|
||||
|
@ -209,4 +212,63 @@ describe('事件', () => {
|
|||
// 先选择选项1,radio1应该重新触发onchange
|
||||
clickRadioAndExpect(radio1Ref.current, [2, 1]);
|
||||
});
|
||||
|
||||
it('多根节点下,事件挂载正确', () => {
|
||||
const root1 = document.createElement('div');
|
||||
const root2 = document.createElement('div');
|
||||
root1.key = 'root1';
|
||||
root2.key = 'root2';
|
||||
let input1, input2, update1, update2;
|
||||
|
||||
function App1() {
|
||||
const [props, setProps] = Horizon.useState({});
|
||||
update1 = setProps;
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
ref={n => (input1 = n)}
|
||||
onChange={() => {
|
||||
LogUtils.log('input1 changed');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function App2() {
|
||||
const [props, setProps] = Horizon.useState({});
|
||||
update2 = setProps;
|
||||
|
||||
return (
|
||||
<input
|
||||
{...props}
|
||||
ref={n => (input2 = n)}
|
||||
onChange={() => {
|
||||
LogUtils.log('input2 changed');
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 多根mount阶段挂载onChange事件
|
||||
Horizon.render(<App1 key={1} />, root1);
|
||||
Horizon.render(<App2 key={2} />, root2);
|
||||
|
||||
dispatchChangeEvent(input1);
|
||||
expect(LogUtils.getAndClear()).toEqual(['input1 changed']);
|
||||
dispatchChangeEvent(input2);
|
||||
expect(LogUtils.getAndClear()).toEqual(['input2 changed']);
|
||||
|
||||
// 多根update阶段挂载onClick事件
|
||||
update1({
|
||||
onClick: () => LogUtils.log('input1 clicked'),
|
||||
});
|
||||
update2({
|
||||
onClick: () => LogUtils.log('input2 clicked'),
|
||||
});
|
||||
|
||||
input1.click();
|
||||
expect(LogUtils.getAndClear()).toEqual(['input1 clicked']);
|
||||
input2.click();
|
||||
expect(LogUtils.getAndClear()).toEqual(['input2 clicked']);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { allDelegatedNativeEvents } from '../../../libs/horizon/src/event/EventCollection';
|
||||
import { allDelegatedNativeEvents } from '@cloudsop/horizon/src/event/EventHub';
|
||||
//import * as LogUtils from './logUtils';
|
||||
|
||||
export const stopBubbleOrCapture = (e, value) => {
|
||||
|
|
Loading…
Reference in New Issue