Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ npm-debug.log
.Trashes
ehthumbs.db
Thumbs.db
.idea/
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,11 @@ All pointer events are supported:
`onPointerMove`, `onPointerDown`, `onPointerUp`, `onPointerOver`, `onPointerOut`, `onPointerEnter`, `onPointerLeave`, `onPointerCancel`

## Additional Props
`<Pointable />` accepts two special non-pointer event props:
`<Pointable />` accepts special non-pointer event props:

- `tagName [string = 'div']` - If you don't want a `<div />` to be rendered, you can pass any valid element type and it will be rendered instead.
- `touchAction [string = 'auto']` - When used with PEP in a browser that doesn't support pointer events, chances are the CSS property `touch-action` also isn't supported. PEP therefore supports a `touch-action` _attribute_, and this prop allows setting that in a fully declarative manner. You can read more about the PEP attribute [on its repo](https://github.com/jquery/PEP#polyfill-limitations).
- `elementRef [function]` - Provides the generated element to a parent component. (optional)

## Example
[Here's a CodePen using Pointable](http://codepen.io/MillerTime/pen/QKaLky/) that allows toggling between pointer and mouse events, using the same components.
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"scripts": {
"build": "babel src -d lib",
"test": "jest",
"test:watch": "jest --watch",
"prepublish": "npm test && npm run build",
"release": "np"
},
Expand Down Expand Up @@ -57,7 +58,8 @@
"jest": "^20.0.1",
"np": "^2.14.1",
"prop-types": "^15.5.10",
"react": "^15.5.4"
"react": "^15.5.4",
"react-test-renderer": "^15.5.4"
},
"peerDependencies": {
"react": "^15.5.0",
Expand Down
16 changes: 15 additions & 1 deletion src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ const pointerEventProps = Object.keys(pointerEventMap);
// Component with pointer events enabled (specially made for Pointer Events Polyfill)
class Pointable extends React.Component {

constructor() {
super();
this.setRef = this.setRef.bind(this);
}

// When component mounts, check for pointer event listeners in props and register them manually.
componentDidMount() {
initNodeWithPE(this.pointableNode, this.props);
Expand All @@ -35,6 +40,13 @@ class Pointable extends React.Component {
updateNodeWithPE(this.pointableNode, prevProps, this.props);
}

setRef(node) {
this.pointableNode = node;
if(this.props.elementRef) {
this.props.elementRef(node);
}
};

render() {
// Collect unused props to pass along to rendered node.
// This could be done simply with lodash, but avoiding the extra dependency here isn't difficult.
Expand All @@ -47,11 +59,12 @@ class Pointable extends React.Component {
delete otherProps.children;
delete otherProps.tagName;
delete otherProps.touchAction;
delete otherProps.elementRef;

const El = this.props.tagName;

return (
<El ref={node => this.pointableNode = node} {...otherProps}>
<El ref={this.setRef} {...otherProps}>
{this.props.children}
</El>
);
Expand All @@ -62,6 +75,7 @@ class Pointable extends React.Component {
Pointable.propTypes = {
tagName: PropTypes.string.isRequired,
touchAction: PropTypes.oneOf(['auto', 'none', 'pan-x', 'pan-y', 'manipulation']).isRequired,
elementRef: PropTypes.func,
onPointerMove: PropTypes.func,
onPointerDown: PropTypes.func,
onPointerUp: PropTypes.func,
Expand Down
110 changes: 110 additions & 0 deletions test/component.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React from 'react';
import renderer from 'react-test-renderer';
import Pointable from "../src";

let addEventListener;
let setAttribute;

function createNodeMock(element) {
if (element.type === 'div') {
return {
addEventListener,
setAttribute
};
}
return null;
}

const rendererOptions = {createNodeMock};

describe("component", () => {

beforeEach(() => {
addEventListener = jest.fn();
setAttribute = jest.fn();
});

it("renders", () => {
const component = renderer.create(
<Pointable></Pointable>,
rendererOptions
);
const tree = component.toJSON();
expect(tree).toEqual({
"type": "div",
"props": {},
"children": null
});
expect(addEventListener).not.toHaveBeenCalled();
expect(setAttribute).not.toHaveBeenCalled();
});

it("renders with element type", () => {
const component = renderer.create(
<Pointable tagName="section"></Pointable>,
rendererOptions
);
const tree = component.toJSON();
expect(tree).toEqual({
"type": "section",
"props": {},
"children": null
});
expect(addEventListener).not.toHaveBeenCalled();
expect(setAttribute).not.toHaveBeenCalled();
});

it("renders with touch action", () => {
const onPointerMove = () => {};
const component = renderer.create(
<Pointable touchAction="auto" onPointerMove={onPointerMove}></Pointable>,
rendererOptions
);
const tree = component.toJSON();
expect(tree).toEqual({
"type": "div",
"props": {},
"children": null
});
expect(addEventListener).toHaveBeenCalledWith("pointermove", onPointerMove);
expect(setAttribute).toHaveBeenCalledWith('touch-action', 'auto');
});

it("passes ref to parent", () => {
const elementRef = jest.fn();
const ParentComponent = (props) => (
<Pointable elementRef={elementRef} />
);
const component = renderer.create(
<ParentComponent />,
rendererOptions
);
expect(elementRef).toHaveBeenCalled();
expect(addEventListener).not.toHaveBeenCalled();
expect(setAttribute).not.toHaveBeenCalled();
});

[
'onPointerMove',
'onPointerDown',
'onPointerUp',
'onPointerOver',
'onPointerOut',
'onPointerEnter',
'onPointerLeave',
'onPointerCancel'
].forEach((evtName) => {
it(`adds event listener for ${evtName}`, () => {
const domEventName = evtName.substr(2).toLowerCase();
const handler = () => {};
const pointableProps = {
[evtName]: handler
};
const component = renderer.create(
<Pointable {...pointableProps}></Pointable>,
rendererOptions
);
expect(addEventListener).toHaveBeenCalledWith(domEventName, handler);
});
});
});