From 522b6ba9bf5ba329f8ffcf67d0bd38a261de414e Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Sun, 14 May 2017 17:42:28 -0400 Subject: [PATCH 1/3] Adding functionality to pass ref to parent element (if required). --- .gitignore | 1 + src/index.jsx | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index efb74d4..6f801c4 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ npm-debug.log .Trashes ehthumbs.db Thumbs.db +.idea/ diff --git a/src/index.jsx b/src/index.jsx index af90b4a..ef6b2e5 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -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); @@ -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. @@ -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 ( - this.pointableNode = node} {...otherProps}> + {this.props.children} ); @@ -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, From b96a2b5ecbd2cb0828cf6396636d104999174a60 Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Sun, 14 May 2017 18:54:20 -0400 Subject: [PATCH 2/3] Adding component render tests. --- package.json | 4 +- test/component.test.js | 110 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 test/component.test.js diff --git a/package.json b/package.json index d56b725..58659cc 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "build": "babel src -d lib", "test": "jest", + "test:watch": "jest --watch", "prepublish": "npm test && npm run build", "release": "np" }, @@ -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", diff --git a/test/component.test.js b/test/component.test.js new file mode 100644 index 0000000..71aae82 --- /dev/null +++ b/test/component.test.js @@ -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( + , + 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( + , + 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( + , + 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) => ( + + ); + const component = renderer.create( + , + 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( + , + rendererOptions + ); + expect(addEventListener).toHaveBeenCalledWith(domEventName, handler); + }); + }); +}); From 91beae7b97eaf165c64a3c4a2253cf3dbbe6513d Mon Sep 17 00:00:00 2001 From: Dave Baskin Date: Sun, 14 May 2017 18:57:29 -0400 Subject: [PATCH 3/3] Updating documentation for elementRef property. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b98097f..d56f6bc 100644 --- a/README.md +++ b/README.md @@ -41,10 +41,11 @@ All pointer events are supported: `onPointerMove`, `onPointerDown`, `onPointerUp`, `onPointerOver`, `onPointerOut`, `onPointerEnter`, `onPointerLeave`, `onPointerCancel` ## Additional Props -`` accepts two special non-pointer event props: +`` accepts special non-pointer event props: - `tagName [string = 'div']` - If you don't want a `
` 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.