diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d415c2..d13e645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,13 @@ ### Removed +## [1.1.0] - 2020-02-15 + +### Changed + +- set history as global state +- accuracy becomes the max pointer size by type + ## [1.0.1] - 2020-02-14 ### Added diff --git a/README.md b/README.md index 63f4026..1ea4763 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/idomusha/use-interaction.svg?branch=master)](https://travis-ci.org/idomusha/use-interaction) [![npm version](https://badge.fury.io/js/use-interaction.svg)](https://badge.fury.io/js/use-interaction) -[![Coverage Status](https://coveralls.io/repos/github/idomusha/use-interaction/badge.svg?branch=master)](https://coveralls.io/github/idomusha/use-interaction?branch=master) +[![Coverage Status](https://coveralls.io/repos/github/idomusha/use-interaction/badge.svg?branch=master&service=github)](https://coveralls.io/github/idomusha/use-interaction?branch=master) React hook `useInteraction()` allows to get the user interaction type: `touch`, `mouse` or `keyboard`. @@ -68,7 +68,9 @@ export const Demo = () => { `object` -- **initialHover**: `boolean` - to not wait at first an action from the user, **canHover** can be defined via this parameter to be effective as soon as the page is loaded (i.e. `true`, default: `null`) +| Property Name | Type | Description | Default Value | +| :--------------- | :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | :-----------: | +| **initialHover** | `boolean` | to not wait an action on the part of the user, **canHover** can be defined via this parameter to be effective as soon as the page is loaded (i.e. `true`) | `false` | const [interaction, history, canHover, accuracy] = useInteraction({initialHover: true}) @@ -83,9 +85,9 @@ export const Demo = () => { - **canHover**: `boolean` - if the user can hover (i.e. `true`, default: `null`) - **accuracy**: `number` - pointer size in pixels (i.e. `23`, default: `null`), --> -| Returned Array | Assigned Name | Type | Description | Default Value | -| :------------- | :-----------: | :--------------: | :-------------------------------------------------------------------------- | :-----------: | -| 1st element | interaction | `string` | interaction type of the user: `touch`, `mouse` or `keyboard` (i.e. `touch`) | `null` | -| 2nd element | history | `Array.` | all interaction types used from the load (i.e. `['touch', 'mouse']`} | `[]` | -| 3rd element | canHover | `boolean` | if the user can hover (i.e. `true`) | `false` | -| 4th element | accuracy | `number` | pointer size in pixels (i.e. `23`) | `null` | +| Returned Array | Type | Description | Default Value | +| :------------------------ | :--------------: | :-------------------------------------------------------------------------- | :-----------: | +| 1st element (interaction) | `string` | interaction type of the user: `touch`, `mouse` or `keyboard` (i.e. `touch`) | `null` | +| 2nd element (history) | `Array.` | all interaction types used from the load (i.e. `['touch', 'mouse']`} | `[]` | +| 3rd element (canHover) | `boolean` | if the user can hover (i.e. `true`) | `false` | +| 4th element (accuracy) | `number` | pointer size in pixels (i.e. `23`) | `null` | diff --git a/package.json b/package.json index cea44a2..07b1e46 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "use-interaction", - "version": "1.0.1", + "version": "1.1.0", "private": false, "description": "React hook for getting and following user interaction type", "keywords": [ diff --git a/src/Demo.js b/src/Demo.js index 3c65e1c..3a4ef49 100644 --- a/src/Demo.js +++ b/src/Demo.js @@ -40,20 +40,20 @@ const Demo = () => { history {' '} - keeps a record of all user interaction types. That way a user that + keeps a record of all user interaction types: that way a user that interacts both with mouse and touch can easily be detected.

canHover {' '} - is a shorcut for any type of interaction except mouse, and allows to + is a shorcut for any type of interaction except mouse: allows to display hidden information to the user in this case.

accuracy {' '} - is the size of contact geometry of the pointer. The higher the number, + is the size of contact geometry of the pointer: the higher the number, the bigger the button size should be defined.

diff --git a/src/useInteraction.js b/src/useInteraction.js index c42974a..418f760 100644 --- a/src/useInteraction.js +++ b/src/useInteraction.js @@ -1,8 +1,13 @@ import { useState, useEffect, useCallback } from 'react' import PropTypes from 'prop-types' import { round } from 'lodash' +import { setGlobal, useGlobal } from 'reactn' -let history = [] +setGlobal({ + history: [], + prevInteraction: null, + accuracy: null, +}) const getKey = event => (event.keyCode ? event.keyCode : event.which) @@ -11,13 +16,15 @@ const getTarget = event => event.target || event.srcElement const useInteraction = ({ initialHover = false } = {}) => { const [interaction, setInteraction] = useState(null) const [canHover, setCanHover] = useState(initialHover) - const [accuracy, setAccuracy] = useState(null) const [firedEvent, setFiredEvent] = useState({ touchStart: null, mouseMove: null, mouseOver: null, keyDown: null, }) + const [history, setHistory] = useGlobal('history') + const [prevInteraction, setPrevInteraction] = useGlobal('prevInteraction') + const [accuracy, setAccuracy] = useGlobal('accuracy') const inputs = ['input', 'select', 'textarea'] const keys = { 9: 'tab', @@ -42,10 +49,10 @@ const useInteraction = ({ initialHover = false } = {}) => { mouseMove: false, })) - history = [...new Set([...history, 'touch'])] + setHistory([...new Set([...history, 'touch'])]) setInteraction('touch') setCanHover(false) - }, [setFiredEvent]) + }, [history, setHistory]) const handleInteractionMouse = useCallback(() => { // prevent false positive on mousemove with touch devices @@ -77,11 +84,17 @@ const useInteraction = ({ initialHover = false } = {}) => { firedEvent.mouseMove === true || firedEvent.touchStart === false ) { - history = [...new Set([...history, 'mouse'])] + setHistory([...new Set([...history, 'mouse'])]) setInteraction('mouse') setCanHover(true) } - }, [firedEvent, setFiredEvent, setCanHover]) + }, [ + firedEvent.touchStart, + firedEvent.keyDown, + firedEvent.mouseMove, + setHistory, + history, + ]) const handleInteractionKeyboard = useCallback( event => { @@ -108,19 +121,23 @@ const useInteraction = ({ initialHover = false } = {}) => { if (interaction === 'keyboard') return - history = [...new Set([...history, 'keyboard'])] + setHistory([...new Set([...history, 'keyboard'])]) setInteraction('keyboard') setCanHover(false) } }, - [inputs, keys, interaction, setFiredEvent] + [keys, inputs, interaction, setHistory, history] ) const handleInteractionPointer = useCallback( event => { - setAccuracy(round(event.height, 1)) + const nextAccuracy = round(event.height, 1) + if (nextAccuracy > accuracy || prevInteraction !== event.pointerType) { + setAccuracy(nextAccuracy) + } + setPrevInteraction(event.pointerType) }, - [setAccuracy] + [accuracy, prevInteraction, setAccuracy, setPrevInteraction] ) useEffect(() => { diff --git a/src/useInteraction.test.js b/src/useInteraction.test.js index ab52111..cf2d7f6 100644 --- a/src/useInteraction.test.js +++ b/src/useInteraction.test.js @@ -1,112 +1,114 @@ -import React from 'react' -import { renderHook, cleanup, act } from '@testing-library/react-hooks' -import { fireEvent, createEvent, render, wait } from '@testing-library/react' - -import useInteraction from './useInteraction' -import Demo from './Demo' - -afterEach(cleanup) - -test('should init interaction type of the user', () => { - const { result } = renderHook(() => useInteraction()) - - expect(result.current[0]).toBe(null) - expect(result.current[1]).toMatchObject([]) - expect(result.current[2]).toBeFalsy() - expect(result.current[3]).toBe(null) -}) - -test('should init interaction type of the user with initialHover', () => { - const { result } = renderHook(() => useInteraction({ initialHover: true })) - - expect(result.current[0]).toBe(null) - expect(result.current[1]).toMatchObject([]) - expect(result.current[2]).toBeTruthy() - expect(result.current[3]).toBe(null) -}) - -test('should set interaction type of the user to touch', () => { - const { result } = renderHook(() => useInteraction()) - - act(() => { - fireEvent.touchStart(document.body, {}) - }) - - expect(result.current[0]).toBe('touch') - expect(result.current[2]).toBeFalsy() -}) - -test('should set interaction type of the user to mouse', () => { - const { result } = renderHook(() => useInteraction()) - - act(() => { - fireEvent.mouseMove(document.body, {}) - }) - - expect(result.current[0]).toBe('mouse') - expect(result.current[2]).toBeTruthy() -}) - -test('should set interaction type of the user to keyboard', () => { - const { result, unmount } = renderHook(() => useInteraction()) - - act(() => { - fireEvent.keyDown(document.body, { - key: 'Tab', - code: 'Tab', - keyCode: 9, - which: 9, - }) - }) - - expect(result.current[0]).toBe('keyboard') - expect(result.current[2]).toBeFalsy() - unmount() -}) - -test('should not set interaction type of the user to keyboard', () => { - const { result } = renderHook(() => useInteraction()) - const { getByTestId } = render() - - act(() => { - fireEvent.mouseMove(document.body, {}) - fireEvent.keyDown(getByTestId('test-input'), { - key: ' ', - code: 'Space', - keyCode: 32, - which: 32, - }) - }) - - expect(result.current[0]).toBe('mouse') - expect(result.current[2]).toBeTruthy() -}) - -test('should set interaction type of the user to touch and then to mouse', () => { - const { result } = renderHook(() => useInteraction()) - - act(() => { - fireEvent.touchStart(document.body, {}) - fireEvent.mouseMove(document.body, {}) - }) - - wait(() => { - expect(result.current[0]).toBe('mouse') - expect(result.current[1]).toMatchObject(['touch', 'mouse']) - expect(result.current[2]).toBeTruthy() - }) -}) - -test('should set accuracy of the pointer', () => { - const { result } = renderHook(() => useInteraction()) - - const pointerDown = createEvent.click(document.body, { height: 23.666666666 }) - - act(() => { - fireEvent(document.body, pointerDown) - }) - - wait(() => { - expect(result.current[3]).toBe(23.7) - }) -}) +import React from 'react' +import { renderHook, cleanup, act } from '@testing-library/react-hooks' +import { fireEvent, createEvent, render, wait } from '@testing-library/react' + +import useInteraction from './useInteraction' +import Demo from './Demo' + +afterEach(cleanup) + +test('should init interaction type of the user', () => { + const { result } = renderHook(() => useInteraction()) + + expect(result.current[0]).toBe(null) + expect(result.current[1]).toMatchObject([]) + expect(result.current[2]).toBeFalsy() + expect(result.current[3]).toBe(null) +}) + +test('should init interaction type of the user with initialHover', () => { + const { result } = renderHook(() => useInteraction({ initialHover: true })) + + expect(result.current[0]).toBe(null) + expect(result.current[1]).toMatchObject([]) + expect(result.current[2]).toBeTruthy() + expect(result.current[3]).toBe(null) +}) + +test('should set interaction type of the user to touch', () => { + const { result } = renderHook(() => useInteraction()) + + act(() => { + fireEvent.touchStart(document.body, {}) + }) + + expect(result.current[0]).toBe('touch') + expect(result.current[2]).toBeFalsy() +}) + +test('should set interaction type of the user to mouse', () => { + const { result } = renderHook(() => useInteraction()) + + act(() => { + fireEvent.mouseMove(document.body, {}) + }) + + expect(result.current[0]).toBe('mouse') + expect(result.current[2]).toBeTruthy() +}) + +test('should set interaction type of the user to keyboard', () => { + const { result, unmount } = renderHook(() => useInteraction()) + + act(() => { + fireEvent.keyDown(document.body, { + key: 'Tab', + code: 'Tab', + keyCode: 9, + which: 9, + }) + }) + + expect(result.current[0]).toBe('keyboard') + expect(result.current[2]).toBeFalsy() + unmount() +}) + +test('should not set interaction type of the user to keyboard', () => { + const { result } = renderHook(() => useInteraction()) + const { getByTestId } = render() + + act(() => { + fireEvent.mouseMove(document.body, {}) + fireEvent.keyDown(getByTestId('test-input'), { + key: ' ', + code: 'Space', + keyCode: 32, + which: 32, + }) + }) + + expect(result.current[0]).toBe('mouse') + expect(result.current[2]).toBeTruthy() +}) + +test('should set interaction type of the user to touch and then to mouse', () => { + const { result } = renderHook(() => useInteraction()) + + act(() => { + fireEvent.touchStart(document.body, {}) + fireEvent.mouseMove(document.body, {}) + }) + + wait(() => { + expect(result.current[0]).toBe('mouse') + expect(result.current[1]).toMatchObject(['touch', 'mouse']) + expect(result.current[2]).toBeTruthy() + }) +}) + +test('should set accuracy of the pointer', () => { + const { result } = renderHook(() => useInteraction()) + + const pointerDown = createEvent.pointerDown(document.body, { + height: 23.666666666, + }) + + act(() => { + fireEvent(document.body, pointerDown) + }) + + wait(() => { + expect(result.current[3]).toBe(23.7) + }) +})