",
20 | "license": "MIT",
21 | "bugs": {
22 | "url": "https://github.com/PaulRosset/rehover/issues"
23 | },
24 | "homepage": "https://github.com/PaulRosset/rehover#readme",
25 | "dependencies": {
26 | "prop-types": "^15.6.1",
27 | "react": "^16.3.2"
28 | },
29 | "devDependencies": {
30 | "babel-cli": "^6.26.0",
31 | "babel-eslint": "^8.2.2",
32 | "babel-plugin-transform-class-properties": "^6.24.1",
33 | "babel-preset-es2015": "^6.24.1",
34 | "babel-preset-react-app": "^3.1.1",
35 | "eslint": "^4.19.0",
36 | "eslint-plugin-prettier": "^2.6.0",
37 | "jest": "^22.4.3",
38 | "prettier": "^1.11.1",
39 | "react-dom": "^16.3.2",
40 | "react-test-renderer": "^16.3.1",
41 | "react-testing-library": "^1.9.0"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | export { default as RehoverProvider, RehoverConsumer } from "./rehover-context";
2 | export { default as Rehover } from "./rehover";
3 |
--------------------------------------------------------------------------------
/src/rehover-context.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | const { Provider, Consumer } = React.createContext({
5 | states: {
6 | isOnSource: false,
7 | isOnTarget: false,
8 | isOpen: false
9 | },
10 | actions: {
11 | onMouseEnterSource: () => {},
12 | onMouseLeaveSource: () => {},
13 | onMouseEnterTarget: () => {},
14 | onMouseLeaveTarget: () => {},
15 | onKeyBoardSource: () => {}
16 | }
17 | });
18 |
19 | export const RehoverConsumer = Consumer;
20 |
21 | export default class RehoverProvider extends Component {
22 | constructor(props) {
23 | super(props);
24 | this.state = {
25 | isOnTarget: false,
26 | isOnSource: false,
27 | isOpen: false
28 | };
29 | this.props.states(this.state);
30 | }
31 |
32 | onMouseEnterSource = () => {
33 | this.setState(
34 | prevState => ({
35 | isOpen: true,
36 | isOnSource: true
37 | }),
38 | () => this.props.states(this.state)
39 | );
40 | };
41 |
42 | onMouseLeaveSource = () => {
43 | setTimeout(() => {
44 | this.setState(
45 | prevState => ({
46 | isOnSource: false,
47 | isOpen: !prevState.isOnTarget ? false : true
48 | }),
49 | () => this.props.states(this.state)
50 | );
51 | }, this.props.delay);
52 | };
53 |
54 | onMouseEnterTarget = () => {
55 | this.setState(
56 | prevState => ({
57 | isOnSource: false,
58 | isOnTarget: true,
59 | isOpen: true
60 | }),
61 | () => this.props.states(this.state)
62 | );
63 | };
64 |
65 | onMouseLeaveTarget = () => {
66 | setTimeout(() => {
67 | this.setState(
68 | prevState => ({
69 | isOnTarget: false,
70 | isOpen: !prevState.isOnSource ? false : true
71 | }),
72 | () => this.props.states(this.state)
73 | );
74 | }, this.props.delay);
75 | };
76 |
77 | onKeyBoardSource = e => {
78 | switch (e.keyCode) {
79 | case 40:
80 | this.setState(
81 | {
82 | isOpen: true,
83 | isOnTarget: true
84 | },
85 | () => this.props.states(this.state)
86 | );
87 | break;
88 | case 38:
89 | this.setState(
90 | {
91 | isOpen: false,
92 | isOnTarget: false
93 | },
94 | () => this.props.states(this.state)
95 | );
96 | break;
97 | default:
98 | break;
99 | }
100 | };
101 |
102 | render() {
103 | return (
104 |
118 | {this.props.children}
119 |
120 | );
121 | }
122 | }
123 |
124 | RehoverProvider.defaultProps = {
125 | delay: 0,
126 | states: () => {}
127 | };
128 |
129 | RehoverProvider.propTypes = {
130 | delay: PropTypes.number,
131 | states: PropTypes.func
132 | };
133 |
--------------------------------------------------------------------------------
/src/rehover.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from "react";
2 | import PropTypes from "prop-types";
3 |
4 | export default class ReHover extends Component {
5 | constructor(props) {
6 | super(props);
7 | this.state = {
8 | isOnTarget: false,
9 | isOnSource: false,
10 | isOpen: false
11 | };
12 | this.props.states(this.state);
13 | }
14 |
15 | onMouseEnterSource = () => {
16 | this.setState(
17 | {
18 | isOpen: true,
19 | isOnSource: true
20 | },
21 | () => this.props.states(this.state)
22 | );
23 | };
24 |
25 | onMouseLeaveSource = () => {
26 | setTimeout(() => {
27 | this.setState(
28 | prevState => ({
29 | isOnSource: false,
30 | isOpen: !prevState.isOnTarget ? false : true
31 | }),
32 | () => this.props.states(this.state)
33 | );
34 | }, this.props.delay);
35 | };
36 |
37 | onMouseEnterTarget = () => {
38 | this.setState(
39 | {
40 | isOnSource: false,
41 | isOnTarget: true,
42 | isOpen: true
43 | },
44 | () => this.props.states(this.state)
45 | );
46 | };
47 |
48 | onMouseLeaveTarget = () => {
49 | setTimeout(() => {
50 | this.setState(
51 | prevState => ({
52 | isOnTarget: false,
53 | isOpen: !prevState.isOnSource ? false : true
54 | }),
55 | () => this.props.states(this.state)
56 | );
57 | }, this.props.delay);
58 | };
59 |
60 | onKeyBoardSource = e => {
61 | switch (e.keyCode) {
62 | case 40:
63 | this.setState(
64 | {
65 | isOpen: true,
66 | isOnTarget: true
67 | },
68 | () => this.props.states(this.state)
69 | );
70 | break;
71 | case 38:
72 | this.setState(
73 | {
74 | isOpen: false,
75 | isOnTarget: false
76 | },
77 | () => this.props.states(this.state)
78 | );
79 | break;
80 | default:
81 | break;
82 | }
83 | };
84 |
85 | render() {
86 | const ChildrenWithMouseEvent = React.Children.map(
87 | this.props.children,
88 | child => {
89 | return child.props.source
90 | ? React.cloneElement(child, {
91 | onMouseEnter: this.onMouseEnterSource,
92 | onMouseLeave: this.onMouseLeaveSource,
93 | onKeyDown: this.onKeyBoardSource,
94 | role: "source",
95 | tabIndex: 0,
96 | "aria-hidden": false
97 | })
98 | : child.props.destination
99 | ? React.cloneElement(child, {
100 | onMouseEnter: this.onMouseEnterTarget,
101 | onMouseLeave: this.onMouseLeaveTarget,
102 | role: "destination",
103 | "aria-hidden": !this.state.isOpen
104 | })
105 | : null;
106 | }
107 | );
108 | return this.state.isOnSource || this.state.isOnTarget
109 | ? ChildrenWithMouseEvent.slice(0, 2)
110 | : ChildrenWithMouseEvent.splice(
111 | ChildrenWithMouseEvent.findIndex(elem => elem.props.source),
112 | 1
113 | );
114 | }
115 | }
116 |
117 | ReHover.defaultProps = {
118 | delay: 0,
119 | states: () => {}
120 | };
121 |
122 | ReHover.propTypes = {
123 | delay: PropTypes.number,
124 | states: PropTypes.func
125 | };
126 |
--------------------------------------------------------------------------------
/test/__snapshots__/index.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`Testing Rehover with Children Compound Testing the normal working Snapshot testing... Shot Only Source, when MouseLeaving 1`] = `
4 |
13 | Source React element
14 |
15 | `;
16 |
17 | exports[`Testing Rehover with Children Compound Testing the normal working Snapshot testing... Shot Props passed 1`] = `
18 | Object {
19 | "children": Array [
20 |
23 | Source React element
24 |
,
25 |
28 | Destination React element
29 |
,
30 | ],
31 | "delay": 150,
32 | "states": [Function],
33 | }
34 | `;
35 |
36 | exports[`Testing Rehover with Children Compound Testing the normal working Snapshot testing... Shot both children nodes, when mouseEntering 1`] = `
37 | Array [
38 |
47 | Source React element
48 |
,
49 |
56 | Destination React element
57 |
,
58 | ]
59 | `;
60 |
--------------------------------------------------------------------------------
/test/index.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { render, Simulate, wait } from "react-testing-library";
3 | import { Rehover } from "../lib/main";
4 | import ReactTestRenderer from "react-test-renderer";
5 |
6 | let testingUtilities;
7 | let Source;
8 | let ShotRenderer;
9 |
10 | describe("Testing Rehover with Children Compound", () => {
11 | describe("Testing the normal working", () => {
12 | beforeAll(() => {
13 | testingUtilities = render(
14 |
15 | Source React element
16 | Destination React element
17 |
18 | );
19 | Source = testingUtilities.getByText("Source React element");
20 | });
21 |
22 | test("Check Source react element presence and check destination is not present", () => {
23 | expect(Source.textContent).toEqual("Source React element");
24 | expect(
25 | testingUtilities.queryByText("Destination React element")
26 | ).toBeNull();
27 | });
28 |
29 | test("Check presence of destination when MouseEntering", () => {
30 | Simulate.mouseEnter(Source);
31 | expect(
32 | testingUtilities.getByText("Destination React element").textContent
33 | ).toEqual("Destination React element");
34 | });
35 |
36 | test("Check when MouseEntering the destination", () => {
37 | //Simulate.mouseLeave(Source);
38 | Simulate.mouseEnter(
39 | testingUtilities.getByText("Destination React element")
40 | );
41 | expect(
42 | testingUtilities.getByText("Destination React element").textContent
43 | ).toEqual("Destination React element");
44 | });
45 |
46 | test("Aria Compatibility, close with Arrow Up", () => {
47 | Simulate.keyDown(Source, { keyCode: 38 });
48 | expect(
49 | testingUtilities.queryByText("Destination React element")
50 | ).toBeNull();
51 | });
52 |
53 | test("Aria Compatibility, Open with Arrow Down", () => {
54 | Simulate.keyDown(Source, { keyCode: 40 });
55 | expect(
56 | testingUtilities.getByText("Destination React element").textContent
57 | ).toEqual("Destination React element");
58 | });
59 |
60 | describe("Snapshot testing...", () => {
61 | beforeAll(() => {
62 | ShotRenderer = ReactTestRenderer.create(
63 |
64 | Source React element
65 | Destination React element
66 |
67 | );
68 | });
69 |
70 | test("Shot both children nodes, when mouseEntering", () => {
71 | ShotRenderer.root.findByType("p").props.onMouseEnter();
72 | expect(ShotRenderer.toJSON()).toMatchSnapshot();
73 | });
74 |
75 | test("Shot Only Source, when MouseLeaving", () => {
76 | ShotRenderer.root.instance.setState({ isOnSource: false });
77 | expect(ShotRenderer.toJSON()).toMatchSnapshot();
78 | });
79 |
80 | test("Shot Props passed", () => {
81 | expect(ShotRenderer.root.instance.props).toMatchSnapshot();
82 | });
83 | });
84 | });
85 | });
86 |
87 | describe("Testing Rehover with Context compound", () => {});
88 |
--------------------------------------------------------------------------------