Skip to content

Commit 029c359

Browse files
committed
Refactor FlowTip DOM component so it's easier to test
1 parent 3876c2c commit 029c359

1 file changed

Lines changed: 70 additions & 66 deletions

File tree

src/dom.js

Lines changed: 70 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,12 @@
1-
/* global getComputedStyle window */
1+
/* global window */
22
import {createElement, Component} from 'react';
33
import ReactDOM from 'react-dom';
44

55
import FlowTip from './flowtip';
66
import ResizeObserver from 'react-resize-observer';
77

8-
/**
9-
* Find the closest node that will control the positioning of the FlowTip™
10-
* content.
11-
* @param {Node} _node Initial node to search from.
12-
* @returns {Node} The anchor parent.
13-
*/
14-
const getAnchorParent = (_node) => {
15-
let node = _node.parentNode;
16-
const isParentNode = (node) => {
17-
const style = getComputedStyle(node);
18-
return style && style.position !== 'static';
19-
};
20-
21-
while (node) {
22-
if (isParentNode(node) || node.tagName === 'BODY') {
23-
return node;
24-
}
25-
node = node.parentNode;
26-
}
27-
return node;
28-
};
29-
30-
/**
31-
* Find the closest node that should enclose the FlowTip™'s content. Basically
32-
* the nearest thing with scrollbars.
33-
* @param {[type]} _node [description]
34-
* @param {[type]} parentClass = null [description]
35-
* @returns {[type]} [description]
36-
*/
37-
const getBoundingParent = (_node, parentClass = null) => {
38-
let node = _node.parentNode;
39-
const scrollishStyle = (style) => [
40-
'auto',
41-
'hidden',
42-
'scroll',
43-
].indexOf(style) !== -1;
44-
45-
const isParentNode = (node) => {
46-
const style = getComputedStyle(node);
47-
if (parentClass) {
48-
return node.className.indexOf(parentClass) !== -1;
49-
} else if (style) {
50-
return scrollishStyle(style.overflow) ||
51-
scrollishStyle(style.overflowX) ||
52-
scrollishStyle(style.overflowY);
53-
}
54-
return false;
55-
};
56-
57-
while (node) {
58-
if (isParentNode(node) || node.tagName === 'BODY') {
59-
return node;
60-
}
61-
node = node.parentNode;
62-
}
63-
return node;
64-
};
65-
668
export default (Content, Tail) => {
67-
return class MyFlowTip extends Component {
9+
return class FlowTipDOM extends Component {
6810
static defaultProps = {
6911
clamp: true,
7012
};
@@ -81,13 +23,75 @@ export default (Content, Tail) => {
8123
this.handleScroll = this.handleScroll.bind(this);
8224
}
8325

26+
getWindow() {
27+
return window;
28+
}
29+
30+
/**
31+
* Find the closest node that will control the positioning of the FlowTip™
32+
* content.
33+
* @param {Node} _node Initial node to search from.
34+
* @returns {Node} The anchor parent.
35+
*/
36+
getAnchorParent(_node) {
37+
let node = _node.parentNode;
38+
const isParentNode = (node) => {
39+
const style = this.getWindow().getComputedStyle(node);
40+
return style && style.position !== 'static';
41+
};
42+
43+
while (node) {
44+
if (isParentNode(node) || node.tagName === 'BODY') {
45+
return node;
46+
}
47+
node = node.parentNode;
48+
}
49+
return node;
50+
}
51+
52+
/**
53+
* Find the closest node that should enclose the FlowTip™'s content.
54+
* Basically the nearest thing with scrollbars.
55+
* @param {[type]} _node [description]
56+
* @param {[type]} parentClass = null [description]
57+
* @returns {[type]} [description]
58+
*/
59+
getBoundingParent(_node, parentClass = null) {
60+
let node = _node.parentNode;
61+
const scrollishStyle = (style) => [
62+
'auto',
63+
'hidden',
64+
'scroll',
65+
].indexOf(style) !== -1;
66+
67+
const isParentNode = (node) => {
68+
const style = this.getWindow().getComputedStyle(node);
69+
if (parentClass) {
70+
return node.className.indexOf(parentClass) !== -1;
71+
} else if (style) {
72+
return scrollishStyle(style.overflow) ||
73+
scrollishStyle(style.overflowX) ||
74+
scrollishStyle(style.overflowY);
75+
}
76+
return false;
77+
};
78+
79+
while (node) {
80+
if (isParentNode(node) || node.tagName === 'BODY') {
81+
return node;
82+
}
83+
node = node.parentNode;
84+
}
85+
return node;
86+
}
87+
8488
getAnchorElement() {
85-
return getAnchorParent(ReactDOM.findDOMNode(this.refs.flowtip));
89+
return this.getAnchorParent(ReactDOM.findDOMNode(this.refs.flowtip));
8690
}
8791

8892
getParentElement() {
8993
const {parentClass} = this.props;
90-
return getBoundingParent(this.getAnchorElement(), parentClass);
94+
return this.getBoundingParent(this.getAnchorElement(), parentClass);
9195
}
9296

9397
getAnchorRect() {
@@ -123,11 +127,11 @@ export default (Content, Tail) => {
123127
parent.top = Math.max(parent.top, 0);
124128
parent.height = Math.min(
125129
parent.height,
126-
window.innerHeight - parent.top
130+
this.getWindow().innerHeight - parent.top
127131
);
128132
parent.width = Math.min(
129133
parent.width,
130-
window.innerWidth - parent.left
134+
this.getWindow().innerWidth - parent.left
131135
);
132136
}
133137
parent.width -= scrollerWidth;
@@ -150,13 +154,13 @@ export default (Content, Tail) => {
150154
componentDidMount() {
151155
const parent = this.getParentElement();
152156
this.updateState();
153-
window.addEventListener('scroll', this.handleScroll);
157+
this.getWindow().addEventListener('scroll', this.handleScroll);
154158
parent.addEventListener('scroll', this.handleScroll);
155159
}
156160

157161
componentWillUnmount() {
158162
const parent = this.getParentElement();
159-
window.removeEventListener('scroll', this.handleScroll);
163+
this.getWindow().removeEventListener('scroll', this.handleScroll);
160164
parent.removeEventListener('scroll', this.handleScroll);
161165
}
162166

0 commit comments

Comments
 (0)