Skip to content

Commit 3d026a4

Browse files
committed
Merge branch 'bugs/fix-resize-10'
2 parents 3a27e2f + 07a51f8 commit 3d026a4

File tree

4 files changed

+75
-48
lines changed

4 files changed

+75
-48
lines changed

examples/content/app.js

+4-3
Original file line numberDiff line numberDiff line change
@@ -38,16 +38,17 @@ class App extends Component {
3838
<h1>Content Example</h1>
3939

4040
<p>
41-
If you provide a <Origin className="target custom" content="This is a custom content.">custom</Origin> content of tooltip via props,
42-
it will be used instead of a <Origin className="target default">default</Origin> content of Tooltip component.
41+
If you provide a <Origin className="target custom" content="This is a custom content.">custom</Origin> content via Origin component's prop,
42+
it will overwrite a <Origin className="target default">default</Origin> content of Tooltip component.
4343
</p>
4444

4545
<p>
4646
What time is it <Origin className="target" place="right" onHover={this.handleHover} onLeave={this.handleLeave}>now</Origin>?
4747
</p>
4848

4949
<Tooltip>
50-
This is a default content.
50+
This is a default content.<br />
51+
It's a second line.
5152
</Tooltip>
5253
</div>
5354
);

src/tooltip.js

+34-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import React, { Component, PropTypes } from 'react';
2+
import ReactDOM from 'react-dom';
23
import { connect } from 'react-redux';
34
import { adjust, resolve } from './utils';
45
import * as styles from './styles';
@@ -38,60 +39,62 @@ class Tooltip extends Component {
3839

3940
constructor(props) {
4041
super(props);
41-
4242
this.state = {};
4343
}
4444

45-
componentWillUpdate(nextProps) {
46-
const { el, place } = nextProps;
47-
if (el && (this.props.el != el || this.props.place !== place)) {
45+
componentWillReceiveProps(nextProps) {
46+
const { el, place, content } = nextProps;
47+
if (el && (this.props.el != el || this.props.place !== place || this.props.content !== content)) {
4848
this.updatePosition(nextProps);
4949
}
5050
}
5151

52-
componentDidUpdate(prevProps) {
53-
const { content } = prevProps;
54-
if (this.props.content !== content) {
55-
this.updatePosition(this.props);
56-
}
52+
updatePosition(props) {
53+
// Setup hidden DOM element to determine size of the content
54+
const content = this.children(props);
55+
ReactDOM.render(<div>{content}</div>, this.refs.shadow, () => {
56+
const state = adjust(this.refs.shadow, props);
57+
this.setState(state);
58+
});
5759
}
5860

59-
updatePosition(props) {
60-
const state = adjust(this.refs.tooltip, props);
61-
this.setState(state);
61+
children(props) {
62+
if (typeof props === 'undefined') {
63+
props = this.props;
64+
}
65+
const { content } = props;
66+
return content ? content : props.children;
6267
}
6368

6469
render () {
65-
const { content, onHover, onLeave } = this.props;
70+
const { onHover, onLeave } = this.props;
6671
const { place, offset } = this.state;
72+
const content = this.children();
6773
const visibility = (this.props.el && this.props.show) ? 'visible' : 'hidden';
6874
const style = {
6975
base: { ...styles.base, ...themes.simple.base, visibility, ...offset },
7076
content: { ...styles.content, ...themes.simple.content },
7177
arrow: { ...styles.arrow },
7278
border: { ...styles.border.base, ...styles.border[place], ...themes.simple.border },
7379
};
74-
75-
let children;
76-
if (content) {
77-
children = content;
78-
} else {
79-
children = this.props.children;
80-
}
80+
style.shadow = { ...style.content, visibility: 'hidden', position: 'absolute' };
8181

8282
return (
83-
<div
84-
ref="tooltip"
85-
style={style.base}
86-
onMouseEnter={onHover}
87-
onMouseLeave={onLeave}
88-
>
89-
<div ref="content" style={style.content}>
90-
{children}
91-
</div>
92-
<div style={style.arrow} key={`a-${place}`}>
93-
<span ref="border" style={style.border} key={`b-${place}`}></span>
83+
<div>
84+
<div
85+
ref="tooltip"
86+
style={style.base}
87+
onMouseEnter={onHover}
88+
onMouseLeave={onLeave}
89+
>
90+
<div ref="content" style={style.content}>
91+
{content}
92+
</div>
93+
<div style={style.arrow} key={`a-${place}`}>
94+
<span ref="border" style={style.border} key={`b-${place}`}></span>
95+
</div>
9496
</div>
97+
<div ref="shadow" style={style.shadow} />
9598
</div>
9699
);
97100
}

src/utils.js

+17-13
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
function dimension(el) {
2+
const rect = el.getBoundingClientRect();
3+
return { width: rect.width, height: rect.height };
4+
}
5+
16
/**
27
* Returns a position of given DOM element.
38
*
@@ -25,29 +30,29 @@ export function position(el) {
2530
* Calculates a position of the tooltip.
2631
*
2732
* @param {string} place - 'top', 'right', 'bottom', or 'left'.
28-
* @param {Object} tooltip - DOM element.
33+
* @param {Object} content - DOM element that contains a content.
2934
* @param {Object} origin - DOM element.
3035
* @return {Object} contains 'top', 'left', and extra keys.
3136
*/
32-
export function placement(place, tooltip, origin) {
37+
export function placement(place, content, origin) {
3338
const gap = 12;
34-
const tip = position(tooltip);
39+
const dim = dimension(content);
3540
const pos = position(origin);
3641

37-
let offset = { width: tip.width, height: tip.height };
42+
let offset = { width: dim.width, height: dim.height };
3843

3944
switch(place) {
4045
case 'top': case 'bottom':
41-
offset.left = `${pos.left + (pos.width * 0.5) - (tip.width * 0.5)}px`;
46+
offset.left = `${pos.left + (pos.width * 0.5) - (dim.width * 0.5)}px`;
4247
break;
4348
case 'left': case 'right':
44-
offset.top = `${pos.top + (pos.height * 0.5) - (tip.height * 0.5)}px`;
49+
offset.top = `${pos.top + (pos.height * 0.5) - (dim.height * 0.5)}px`;
4550
break;
4651
}
4752

4853
switch(place) {
4954
case 'top':
50-
offset.top = `${pos.top - tip.height - gap}px`;
55+
offset.top = `${pos.top - dim.height - gap}px`;
5156
break;
5257
case 'right':
5358
offset.left = `${pos.right + gap}px`;
@@ -56,7 +61,7 @@ export function placement(place, tooltip, origin) {
5661
offset.top = `${pos.top + pos.height + gap}px`;
5762
break;
5863
case 'left':
59-
offset.left = `${pos.left - tip.width - gap}px`;
64+
offset.left = `${pos.left - dim.width - gap}px`;
6065
break;
6166
}
6267

@@ -185,12 +190,11 @@ export function overDirs(tip, el) {
185190
/**
186191
* Places and adjusts a tooltip.
187192
*
188-
* @param {Object} tooltip - DOM element.
189-
* @param {string|Array} place
190-
* @param {Object} origin - DOM element.
193+
* @param {Object} content - DOM element which contans a content.
194+
* @param {Object} props
191195
* @return {Object} 'offset': style data to locate, 'place': final direction of the tooltip
192196
*/
193-
export function adjust(tooltip, props) {
197+
export function adjust(content, props) {
194198
const { el: origin, auto, within } = props;
195199
let { place } = props;
196200
if (typeof place === 'string') {
@@ -204,7 +208,7 @@ export function adjust(tooltip, props) {
204208
const tries = [ ...place ];
205209
while (0 < tries.length) {
206210
current = tries.shift();
207-
pos = placement(current, tooltip, origin);
211+
pos = placement(current, content, origin);
208212
if (typeof first === 'undefined') {
209213
first = { offset: pos, place: current };
210214
}

tests/feature/content.js

+20-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe('Content Example', () => {
3838

3939
const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
4040
assert(getStyleValue(tooltip, 'visibility') === 'visible');
41-
assert(tooltip.innerText === 'This is a default content.\n', 'should be default content');
41+
assert(tooltip.innerText === "This is a default content.\nIt's a second line.\n", 'should be default content');
4242
});
4343
});
4444

@@ -52,6 +52,25 @@ describe('Content Example', () => {
5252
assert(getStyleValue(tooltip, 'visibility') === 'visible');
5353
assert(tooltip.innerText === 'This is a custom content.\n', 'should be custom content');
5454
});
55+
56+
it('should be resized', () => {
57+
// Mouseover on custom
58+
const custom = firstComponent(tree, Origin.WrappedComponent, { className: 'target custom' }).refs.wrapper;
59+
TestUtils.Simulate.mouseEnter(custom);
60+
61+
const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
62+
const customPos = position(tooltip);
63+
TestUtils.Simulate.mouseLeave(custom);
64+
65+
// Mouseover on default
66+
const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target default' }).refs.wrapper;
67+
TestUtils.Simulate.mouseEnter(origin);
68+
69+
const defaultPos = position(tooltip);
70+
TestUtils.Simulate.mouseLeave(origin);
71+
72+
assert(customPos.height < defaultPos.height, "default's height is higher than custom's height");
73+
});
5574
});
5675

5776
describe('continuous updating content', () => {

0 commit comments

Comments
 (0)