Skip to content

Commit 732b1bc

Browse files
committed
Using decorators for static method and tests
1 parent acd6d17 commit 732b1bc

File tree

9 files changed

+191
-42
lines changed

9 files changed

+191
-42
lines changed

.babelrc

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"presets": ["es2015", "react"]
2+
"presets": ["es2015", "react", "stage-0"],
3+
"plugins": ["transform-decorators-legacy"],
34
}

Makefile

+11-5
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,18 @@ EXAMPLE_SRC = example/src
44
STANDALONE = standalone
55
SRC = src
66
DIST = dist
7+
TEST = test/*.test.js
8+
MOCHA_OPTS = --compilers js:babel-core/register --require test/setup.js -b --timeout 20000 --reporter spec
79

810
lint:
9-
@echo linting...
11+
@echo Linting...
1012
@$(NODE_BIN)/standard --verbose | $(NODE_BIN)/snazzy src/index.js
1113

12-
convertCss:
14+
test: lint
15+
@echo Start testing...
16+
@$(NODE_BIN)/mocha $(MOCHA_OPTS) $(TEST)
17+
18+
convertCSS:
1319
@echo Converting css...
1420
@node bin/transferSass.js
1521

@@ -24,7 +30,7 @@ dev:
2430
@echo starting dev server...
2531
@rm -rf $(EXAMPLE_DIST)
2632
@mkdir -p $(EXAMPLE_DIST)
27-
@make convertCss
33+
@make convertCSS
2834
@$(NODE_BIN)/watchify -t babelify $(EXAMPLE_SRC)/index.js -o $(EXAMPLE_DIST)/index.js -dv
2935
@$(NODE_BIN)/node-sass $(EXAMPLE_SRC)/index.scss $(EXAMPLE_DIST)/index.css
3036
@$(NODE_BIN)/node-sass -w $(EXAMPLE_SRC)/index.scss $(EXAMPLE_DIST)/index.css
@@ -44,10 +50,10 @@ deployCSS:
4450
deploy: lint
4551
@echo Deploy...
4652
@rm -rf dist && mkdir dist
47-
@make convertCss
53+
@make convertCSS
4854
@make deployCSS
4955
@make deployJS
5056
@make genStand
5157
@echo success!
5258

53-
.PHONY: lint convertCss genStand dev deployJS deployCSS deploy
59+
.PHONY: lint convertCSS genStand dev deployJS deployCSS deploy

package.json

+12-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "react tooltip component",
55
"main": "index.js",
66
"scripts": {
7-
"test": "make lint",
7+
"test": "make test",
88
"dev": "make start",
99
"deploy": "make deploy"
1010
},
@@ -46,15 +46,26 @@
4646
},
4747
"devDependencies": {
4848
"babel-cli": "^6.5.1",
49+
"babel-core": "^6.9.1",
4950
"babel-eslint": "^4.1.1",
51+
"babel-plugin-transform-decorators-legacy": "^1.3.4",
5052
"babel-plugin-transform-runtime": "^6.5.0",
5153
"babel-preset-es2015": "^6.5.0",
5254
"babel-preset-react": "^6.5.0",
55+
"babel-preset-stage-0": "^6.5.0",
5356
"babelify": "^7.2.0",
5457
"browserify": "^13.0.0",
5558
"browserify-shim": "^3.8.12",
59+
"chai": "^3.5.0",
60+
"chai-enzyme": "^0.5.0",
61+
"cheerio": "^0.20.0",
62+
"enzyme": "^2.3.0",
5663
"http-server": "^0.8.0",
64+
"jsdom": "^9.2.1",
65+
"mocha": "^2.5.3",
5766
"node-sass": "^3.3.2",
67+
"react-addons-test-utils": "^15.1.0",
68+
"sinon": "^1.17.4",
5869
"snazzy": "^2.0.1",
5970
"standard": "^5.2.2",
6071
"tape": "^4.2.0",

src/constant.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export default {
2+
3+
GLOBAL: {
4+
HIDE: '__react_tooltip_hide_event',
5+
REBUILD: '__react_tooltip_rebuild_event'
6+
}
7+
}

src/decorators/staticMethods.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* Static methods for react-tooltip
3+
*/
4+
import CONSTANT from '../constant'
5+
6+
const dispatchGlobalEvent = (eventName) => {
7+
// Compatibale with IE
8+
// @see http://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work
9+
let event
10+
11+
if (typeof window.Event === 'function') {
12+
event = new window.Event(eventName)
13+
} else {
14+
event = document.createEvent('Event')
15+
event.initEvent(eventName, false, true)
16+
}
17+
18+
window.dispatchEvent(event)
19+
}
20+
21+
export default function (target) {
22+
/**
23+
* Hide all tooltip
24+
* @trigger ReactTooltip.hide()
25+
*/
26+
target.hide = () => {
27+
dispatchGlobalEvent(CONSTANT.GLOBAL.HIDE)
28+
}
29+
30+
/**
31+
* Rebuild all tooltip
32+
* @trigger ReactTooltip.rebuild()
33+
*/
34+
target.rebuild = () => {
35+
dispatchGlobalEvent(CONSTANT.GLOBAL.REBUILD)
36+
}
37+
}

src/decorators/windowListener.js

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Events that should be bound to the window
3+
*/
4+
import CONSTANT from '../constant'
5+
6+
export default function (target) {
7+
target.prototype.bindWindowEvents = function () {
8+
// ReactTooltip.hide
9+
window.removeEventListener(CONSTANT.GLOBAL.HIDE, this.globalHide)
10+
window.addEventListener(CONSTANT.GLOBAL.HIDE, this.globalHide, false)
11+
12+
// ReactTooltip.rebuild
13+
window.removeEventListener(CONSTANT.GLOBAL.REBUILD, this.globalRebuild)
14+
window.addEventListener(CONSTANT.GLOBAL.REBUILD, this.globalRebuild, false)
15+
16+
// Resize
17+
window.removeEventListener('resize', this.onWindowResize)
18+
window.addEventListener('resize', this.onWindowResize, false)
19+
}
20+
21+
target.prototype.unbindWindowEvents = function () {
22+
window.removeEventListener(CONSTANT.GLOBAL.HIDE, this.globalHide)
23+
window.removeEventListener(CONSTANT.GLOBAL.REBUILD, this.globalRebuild)
24+
window.removeEventListener('resize', this.onWindowResize)
25+
}
26+
}

src/index.js

+44-35
Original file line numberDiff line numberDiff line change
@@ -3,36 +3,43 @@
33
import React, { Component, PropTypes } from 'react'
44
import ReactDOM from 'react-dom'
55
import classname from 'classnames'
6+
7+
/* Decoraters */
8+
import staticMethods from './decorators/staticMethods'
9+
import windowListener from './decorators/windowListener'
10+
11+
/* CSS */
612
import cssStyle from './style'
713

14+
@staticMethods @windowListener
815
class ReactTooltip extends Component {
916
/**
1017
* Class method
1118
* @see ReactTooltip.hide() && ReactTooltup.rebuild()
1219
*/
13-
static hide () {
14-
/**
15-
* Check for ie
16-
* @see http://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work
17-
*/
18-
if (typeof window.Event === 'function') {
19-
window.dispatchEvent(new window.Event('__react_tooltip_hide_event'))
20-
} else {
21-
let event = document.createEvent('Event')
22-
event.initEvent('__react_tooltip_hide_event', false, true)
23-
window.dispatchEvent(event)
24-
}
25-
}
26-
27-
static rebuild () {
28-
if (typeof window.Event === 'function') {
29-
window.dispatchEvent(new window.Event('__react_tooltip_rebuild_event'))
30-
} else {
31-
let event = document.createEvent('Event')
32-
event.initEvent('__react_tooltip_rebuild_event', false, true)
33-
window.dispatchEvent(event)
34-
}
35-
}
20+
// static hide () {
21+
// /**
22+
// * Check for ie
23+
// * @see http://stackoverflow.com/questions/26596123/internet-explorer-9-10-11-event-constructor-doesnt-work
24+
// */
25+
// if (typeof window.Event === 'function') {
26+
// window.dispatchEvent(new window.Event('__react_tooltip_hide_event'))
27+
// } else {
28+
// let event = document.createEvent('Event')
29+
// event.initEvent('__react_tooltip_hide_event', false, true)
30+
// window.dispatchEvent(event)
31+
// }
32+
// }
33+
34+
// static rebuild () {
35+
// if (typeof window.Event === 'function') {
36+
// window.dispatchEvent(new window.Event('__react_tooltip_rebuild_event'))
37+
// } else {
38+
// let event = document.createEvent('Event')
39+
// event.initEvent('__react_tooltip_rebuild_event', false, true)
40+
// window.dispatchEvent(event)
41+
// }
42+
// }
3643

3744
globalHide () {
3845
if (this.mount) {
@@ -83,15 +90,16 @@ class ReactTooltip extends Component {
8390
componentDidMount () {
8491
this.bindListener()
8592
this.setStyleHeader()
86-
/* Add window event listener for hide and rebuild */
87-
window.removeEventListener('__react_tooltip_hide_event', this.globalHide)
88-
window.addEventListener('__react_tooltip_hide_event', this.globalHide, false)
89-
90-
window.removeEventListener('__react_tooltip_rebuild_event', this.globalRebuild)
91-
window.addEventListener('__react_tooltip_rebuild_event', this.globalRebuild, false)
92-
/* Add listener on window resize */
93-
window.removeEventListener('resize', this.onWindowResize)
94-
window.addEventListener('resize', this.onWindowResize, false)
93+
this.bindWindowEvents()
94+
// /* Add window event listener for hide and rebuild */
95+
// window.removeEventListener('__react_tooltip_hide_event', this.globalHide)
96+
// window.addEventListener('__react_tooltip_hide_event', this.globalHide, false)
97+
98+
// window.removeEventListener('__react_tooltip_rebuild_event', this.globalRebuild)
99+
// window.addEventListener('__react_tooltip_rebuild_event', this.globalRebuild, false)
100+
// /* Add listener on window resize */
101+
// window.removeEventListener('resize', this.onWindowResize)
102+
// window.addEventListener('resize', this.onWindowResize, false)
95103
}
96104

97105
componentWillUpdate () {
@@ -109,9 +117,10 @@ class ReactTooltip extends Component {
109117
this.unbindListener()
110118
this.removeScrollListener()
111119
this.mount = false
112-
window.removeEventListener('__react_tooltip_hide_event', this.globalHide)
113-
window.removeEventListener('__react_tooltip_rebuild_event', this.globalRebuild)
114-
window.removeEventListener('resize', this.onWindowResize)
120+
this.unbindWindowEvents()
121+
// window.removeEventListener('__react_tooltip_hide_event', this.globalHide)
122+
// window.removeEventListener('__react_tooltip_rebuild_event', this.globalRebuild)
123+
// window.removeEventListener('resize', this.onWindowResize)
115124
}
116125

117126
/* TODO: optimize, bind has been trigger too many times */

test/globalMethods.test.js

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/* For Standard.js lint checking */
2+
/* eslint-env mocha */
3+
4+
import React from 'react'
5+
import { mount } from 'enzyme'
6+
import chai, { expect } from 'chai'
7+
import chaiEnzyme from 'chai-enzyme'
8+
import sinon from 'sinon'
9+
import ReactTooltip from '../src'
10+
11+
/* Initial test tools */
12+
chai.use(chaiEnzyme())
13+
14+
describe('Global methods', () => {
15+
it('should be hided by invoking ReactTooltip.hide', () => {
16+
const wrapper = mount(<ReactTooltip />)
17+
wrapper.setState({ show: true })
18+
expect(wrapper).to.have.state('show', true)
19+
ReactTooltip.hide()
20+
setImmediate(() => {
21+
expect(wrapper).to.have.state('show', false)
22+
})
23+
})
24+
25+
it('should be rebuild by invoking ReactTooltip.rebuild', () => {
26+
sinon.spy(ReactTooltip.prototype, 'globalRebuild')
27+
ReactTooltip.rebuild()
28+
setImmediate(() => {
29+
expect(ReactTooltip.prototype.globalRebuild.calledOnce).to.equal(true)
30+
})
31+
})
32+
})

test/setup.js

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Setup jsdom for enzyme mount
3+
* @see https://github.com/airbnb/enzyme/blob/master/docs/guides/jsdom.md#using-enzyme-with-jsdom
4+
*/
5+
import {jsdom} from 'jsdom'
6+
7+
const exposedProperties = ['window', 'navigator', 'document']
8+
9+
global.document = jsdom('')
10+
global.window = document.defaultView
11+
Object.keys(document.defaultView).forEach((property) => {
12+
if (typeof global[property] === 'undefined') {
13+
exposedProperties.push(property)
14+
global[property] = document.defaultView[property]
15+
}
16+
})
17+
18+
global.navigator = {
19+
userAgent: 'node.js'
20+
}

0 commit comments

Comments
 (0)