Skip to content
This repository has been archived by the owner on Nov 17, 2022. It is now read-only.

Commit

Permalink
Feat/support event transform (#496)
Browse files Browse the repository at this point in the history
* feat: add events to transform event

* feat: add events

* feat: support transform event

* chore: delete events file

* feat: modify transformPrototypes

* test: add test for onclick

* test: add test for events

* chore: update version

* chore: add CHANGELOG

* chore: update CHANGELOG
  • Loading branch information
answershuto authored Sep 5, 2022
1 parent aa26a91 commit 1cf3800
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 3 deletions.
5 changes: 5 additions & 0 deletions packages/rax-compat/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Changelog

## v0.1.2

- Fix: support event transform.
4 changes: 2 additions & 2 deletions packages/rax-compat/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rax-compat",
"version": "0.1.1",
"version": "0.1.2",
"description": "Rax compatible mode, running rax project on the react runtime.",
"files": [
"esm",
Expand Down Expand Up @@ -71,4 +71,4 @@
"author": "ice-admin@alibaba-inc.com",
"license": "MIT",
"homepage": "https://github.com/ice-lab/ice-next#readme"
}
}
5 changes: 4 additions & 1 deletion packages/rax-compat/src/create-element.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { createElement as _createElement, useEffect, useCallback, useRef, useSta
import { cached, convertUnit } from 'style-unit';
import { observerElement } from './visibility';
import { isFunction, isObject, isNumber } from './type';
import transformPrototypes from './prototypes';


// https://github.com/alibaba/rax/blob/master/packages/driver-dom/src/index.js
Expand Down Expand Up @@ -78,7 +79,7 @@ export function createElement<P extends {
props?: Attributes & P | null,
...children: ReactNode[]): ReactElement {
// Get a shallow copy of props, to avoid mutating the original object.
const rest = Object.assign({}, props);
let rest: Attributes & P = Object.assign({}, props);
const { onAppear, onDisappear } = rest;

// Delete props that are not allowed in react.
Expand All @@ -100,6 +101,8 @@ export function createElement<P extends {
type = createInputCompat(type);
}

rest = transformPrototypes(rest);

// Compat for visibility events.
if (isFunction(onAppear) || isFunction(onDisappear)) {
return _createElement(
Expand Down
118 changes: 118 additions & 0 deletions packages/rax-compat/src/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

// Fork from https://github.com/facebook/react/blob/main/packages/react-dom/src/events/DOMEventProperties.js#L36
const events = [
'abort',
'auxClick',
'animationEnd',
'animationIteration',
'animationStart',
'cancel',
'canPlay',
'canPlayThrough',
'click',
'close',
'contextMenu',
'copy',
'cut',
'dblclick',
'drag',
'dragEnd',
'dragEnter',
'dragExit',
'dragLeave',
'dragOver',
'dragStart',
'drop',
'durationChange',
'emptied',
'encrypted',
'ended',
'error',
'gotPointerCapture',
'input',
'invalid',
'keyDown',
'keyPress',
'keyUp',
'load',
'loadedData',
'loadedMetadata',
'loadStart',
'lostPointerCapture',
'mouseDown',
'mouseMove',
'mouseOut',
'mouseOver',
'mouseUp',
'paste',
'pause',
'play',
'playing',
'pointerCancel',
'pointerDown',
'pointerMove',
'pointerOut',
'pointerOver',
'pointerUp',
'progress',
'rateChange',
'reset',
'resize',
'seeked',
'seeking',
'stalled',
'submit',
'suspend',
'transitionEnd',
'timeUpdate',
'touchCancel',
'touchEnd',
'touchStart',
'volumeChange',
'scroll',
'toggle',
'touchMove',
'waiting',
'wheel',
// EnterLeaveEventPlugin.registerEvents need't to convert to other bubbling events in rax compat,
// which are directly brokered, leaving the rest to React.
// https://github.com/facebook/react/blob/main/packages/react-dom/src/events/plugins/EnterLeaveEventPlugin.js#L29
'mouseEnter',
'mouseLeave',
'pointerEnter',
'pointerLeave',
// https://github.com/facebook/react/blob/main/packages/react-dom/src/events/plugins/ChangeEventPlugin.js#L37
'change',
// https://github.com/facebook/react/blob/main/packages/react-dom/src/events/plugins/SelectEventPlugin.js
'select',
// https://github.com/facebook/react/blob/main/packages/react-dom/src/events/plugins/BeforeInputEventPlugin.js
'beforeInput',
'compositionEnd',
'compositionStart',
'compositionUpdate',
];

// A map for transform event to react event.
// Such as ontouchstart transform to onTouchStart.
export const registrationNameToReactEvent: Map<string, string> = new Map();

registerEvents();

function registerDirectEvent(
registrationName: string,
reactEventName: string,
) {
registrationNameToReactEvent.set(`on${registrationName}`, reactEventName);
}

function registerEvents() {
for (let i = 0; i < events.length; i++) {
const eventName: string = events[i];
const domEventName: string = eventName.toLowerCase();
const capitalizedEvent = eventName[0].toUpperCase() + eventName.slice(1);
registerDirectEvent(domEventName, `on${capitalizedEvent}`);
}
// Special cases where event names don't match.
registerDirectEvent('dblclick', 'onDoubleClick');
}

27 changes: 27 additions & 0 deletions packages/rax-compat/src/prototypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { registrationNameToReactEvent } from './events';

export default function transformPrototypes(props: Object): Object {
const resProps: Object = {};
Object.keys(props).forEach((propKey: string) => {
let resKey: string = propKey;
let resValue: string = props[propKey];
// Transform the event so that it works properly in React.
// ontouchstart can work in rax, but react will check event in event plugin.
// Rax compat should transform event which can work in rax runtime.
// React support onDoubleClick but dblclick event is web Standards events.
// etc...
if (propKey.startsWith('on')) {
const lowerCasedPropkey: string = propKey.toLowerCase();
if (registrationNameToReactEvent.has(lowerCasedPropkey)) {
const reactEvent: string = registrationNameToReactEvent.get(lowerCasedPropkey);
if (reactEvent !== propKey) {
resKey = reactEvent;
}
}
}

resProps[resKey] = resValue;
});

return resProps;
}
115 changes: 115 additions & 0 deletions packages/rax-compat/tests/events.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { expect, it, describe, vi } from 'vitest';
import React, { Children } from 'react';
import { useRef, useEffect } from '../src/index';
import { createElement } from '../src/create-element';
import findDOMNode from '../src/find-dom-node';
import { render } from '@testing-library/react';
import transformPrototypes from '../src/prototypes'

describe('events', () => {
it('should work with onclick', () => {
function App() {
const ref = useRef(null);

const obj = {
handleClick: () => console.log('click'),
}

const click = vi.spyOn(obj, 'handleClick')

useEffect(() => {
const dom = findDOMNode(ref.current);
dom.click();
expect(click).toHaveBeenCalled()
});

return createElement('div', {
onclick: obj.handleClick,
ref: ref
}, 'click me')
}

render(<App />);
});

it('should work with ontouchstart', () => {
expect(transformPrototypes({
ontouchstart: () => { }
}).onTouchStart).toBeInstanceOf(Function);
});

it('should work with onclick', () => {
expect(transformPrototypes({
onclick: () => { }
}).onClick).toBeInstanceOf(Function);
expect(transformPrototypes({
onclick: () => { }
}).onclick).toBe(undefined);
});

it('should work with onClick', () => {
expect(transformPrototypes({
onClick: () => { }
}).onClick).toBeInstanceOf(Function);
});

it('should work with ondblclick', () => {
expect(transformPrototypes({
ondblclick: () => { }
}).onDoubleClick).toBeInstanceOf(Function);
expect(transformPrototypes({
ondblclick: () => { }
}).ondblclick).toBe(undefined);
});

it('should work with onDblclick', () => {
expect(transformPrototypes({
onDblclick: () => { }
}).onDoubleClick).toBeInstanceOf(Function);
expect(transformPrototypes({
onDblclick: () => { }
}).onDblclick).toBe(undefined);
});

it('should work with onDoubleClick', () => {
expect(transformPrototypes({
onDoubleClick: () => { }
}).onDoubleClick).toBeInstanceOf(Function);
});

it('should work with onmouseenter', () => {
expect(transformPrototypes({
onmouseenter: () => { }
}).onMouseEnter).toBeInstanceOf(Function);
expect(transformPrototypes({
onmouseenter: () => { }
}).onmouseenter).toBe(undefined);
});

it('should work with onpointerenter', () => {
expect(transformPrototypes({
onpointerenter: () => { }
}).onPointerEnter).toBeInstanceOf(Function);
expect(transformPrototypes({
onpointerenter: () => { }
}).onpointerenter).toBe(undefined);
});

it('should work with onchange', () => {
expect(transformPrototypes({
onchange: () => { }
}).onChange).toBeInstanceOf(Function);
expect(transformPrototypes({
onchange: () => { }
}).onchange).toBe(undefined);
});

it('should work with onbeforeinput', () => {
expect(transformPrototypes({
onbeforeinput: () => { }
}).onBeforeInput).toBeInstanceOf(Function);
expect(transformPrototypes({
onbeforeinput: () => { }
}).onbeforeinput).toBe(undefined);
});
});

1 comment on commit 1cf3800

@vercel
Copy link

@vercel vercel bot commented on 1cf3800 Sep 5, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

ice-v3 – ./

ice-v3-git-release-next-ice-v3.vercel.app
ice-v3-ice-v3.vercel.app
ice-v3.vercel.app

Please sign in to comment.