Skip to content

Commit f79a615

Browse files
committed
Change auto-placement directions
1 parent a1e6e74 commit f79a615

File tree

7 files changed

+44
-55
lines changed

7 files changed

+44
-55
lines changed

README.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ That's it!
8282
A tooltip component. Please wrap a content which should be shown in a tooltip.
8383

8484
+ `name` *(`string`)*: A name of tooltip. This is used by `<Origin />` component.
85-
+ `place` *(`string`|`string[]`)*: A default direction of tooltip. This value can be overwritten by `<Origin />`'s `place` prop.
85+
+ `place` *(`string`|`string[]`)*: A direction of tooltip. This value can be overwritten by `<Origin />`'s `place` prop. Default is `top`.
86+
+ `auto` *(`boolean`)*: A switch to enable/disable the auto-placement feature. Default is `true`.
8687
+ `within` *(`DOM`)*: A DOM element which is used to restrict the position where this tooltip is placed within.
8788
+ `onHover` *(`Function`)*: A callback function to be called on mouseover at tooltip.
8889
+ `onLeave` *(`Function`)*: A callback function to be called on mouseout at tooltip.
@@ -96,6 +97,7 @@ For advanced usage, you can override the default handlers; `onMouseEnter` and `o
9697
+ `name` *(`string`|`string[]`)*: A name(s) to specify which tooltip(s) should be used.
9798
+ `content` *(`string`|`DOM`|`DOM[]`)*: A content for tooltip. If string, it's sanitized by [DOMPurify](https://github.com/cure53/DOMPurify).
9899
+ `place` *(`string`|`string[]`)*: A name of direction to specify a location of tooltip.
100+
+ `auto` *(`boolean`)*: A switch to enable/disable the auto-placement feature.
99101
+ `tagName` *(`string`)*: A tag name of wrapper element. Default is `span`.
100102
+ `delay` *(`boolean`|`number`|`string`)*: A number of duration for delay feature.
101103
+ `delayOn` *(`string`)*: A name of timing to enable the delay. `show`, `hide`, or `both`. Default is `hide`.

examples/content/app.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,11 @@ class App extends Component {
4545
<p>
4646
If you provide a <Origin className="target custom" content="This is a custom content.">custom</Origin> content via Origin component's prop,
4747
it will overwrite a <Origin className="target default">default</Origin> content of Tooltip component.<br />
48-
<Origin className="target html" place="right" content="This is a <b>html</b> content.<br />Sanitized by <a href='#'>DOMPurify</a>.<script>console.log('Hello XSS!');</script>">HTML</Origin> as string and <Origin className="target dom" content={dom}>DOM element</Origin> are also supported.
48+
<Origin className="target html" content="This is a <b>html</b> content.<br />Sanitized by <a href='#'>DOMPurify</a>.<script>console.log('Hello XSS!');</script>">HTML</Origin> as string and <Origin className="target dom" content={dom}>DOM element</Origin> are also supported.
4949
</p>
5050

5151
<p>
52-
What time is it <Origin className="target right" place="right" onHover={this.handleHover} onLeave={this.handleLeave}>now</Origin>?
52+
What time is it <Origin className="target time" onHover={this.handleHover} onLeave={this.handleLeave}>now</Origin>?
5353
</p>
5454

5555
<Tooltip>

examples/place/app.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,11 @@ class App extends Component {
4646
The 'place' prop <Origin className="target auto-string" place="left,bottom">can</Origin> be also passed as a comma separated string.
4747
</p>
4848

49-
<div ref="restricted" style={{ width: '400px', height: '160px', backgroundColor: 'lightgray', padding: '10px', position: 'relative' }}>
50-
In default, <Origin name="restricted" className="target auto-top" place="top">redux-tooltip</Origin> supposes you want to do auto-placement within the browser window.<br />
49+
<div ref="restricted" style={{ width: '388px', height: '160px', backgroundColor: 'lightgray', padding: '16px', position: 'relative' }}>
50+
In default, <Origin name="restricted" className="target auto-top" place="top">redux-tooltip</Origin> supposes you want to do auto-placement within the browser window.<br /><br />
5151
Using <code>within</code> prop, you can specify <Origin name="restricted" className="target auto-right" place="right">DOM element</Origin> instead of BODY element.
52-
<div style={{ position: 'absolute', bottom: '10px' }}>
53-
This is a <Origin name="restricted" className="target auto-bottom" place="bottom">bottom</Origin> origin.
52+
<div style={{ position: 'absolute', bottom: '16px' }}>
53+
<Origin name="restricted" className="target auto-bottom" place="bottom">This</Origin> is a bottom origin.
5454
</div>
5555
</div>
5656

src/utils.js

+14-25
Original file line numberDiff line numberDiff line change
@@ -71,32 +71,21 @@ export function placement(place, content, origin) {
7171
}
7272

7373
/**
74-
* Returns an opposite direction based on the given.
74+
* Completes missing directions in given direction(s).
7575
*
76-
* @param {string|Array} dir - 'top', 'right', 'bottom', or 'left'.
77-
* @return {string} an opposite direction.
78-
* @throw
76+
* @param {string|Array} dir - a direction or a list of directions.
77+
* @return {Array}
7978
*/
80-
export function opposite(dir) {
81-
let place = dir;
82-
83-
// Alrays use first direction if Array is passed
84-
if (typeof place === 'object' && typeof place.length === 'number' && 0 < place.length) {
85-
place = place[0];
86-
}
87-
88-
switch (place) {
89-
case 'top':
90-
return 'bottom';
91-
case 'bottom':
92-
return 'top';
93-
case 'right':
94-
return 'left';
95-
case 'left':
96-
return 'right';
79+
const DIRS = ['top', 'right', 'bottom', 'left'];
80+
export function complete(dir) {
81+
if (typeof dir === 'string') {
82+
dir = [dir];
83+
} else {
84+
dir = [...dir];
9785
}
9886

99-
throw new Error(`Unknown direction: "${dir}"`);
87+
const missings = DIRS.filter(d => dir.indexOf(d) === -1);
88+
return dir.concat(missings);
10089
}
10190

10291
/**
@@ -193,7 +182,7 @@ export function overDirs(tip, el) {
193182
* Places and adjusts a tooltip.
194183
*
195184
* @param {Object} content - DOM element which contans a content.
196-
* @param {Object} props
185+
* @param {Object} props - props set from Tooltip component.
197186
* @return {Object} 'offset': style data to locate, 'place': final direction of the tooltip
198187
*/
199188
export function adjust(content, props) {
@@ -203,8 +192,8 @@ export function adjust(content, props) {
203192
if (typeof place === 'string') {
204193
place = place.split(',').map(p => p.trim());
205194
}
206-
if (auto && place.length === 1) {
207-
place.push(opposite(place));
195+
if (auto) {
196+
place = complete(place);
208197
}
209198

210199
let pos, dirs, current, first;

tests/feature/content.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ describe('Content Example', () => {
102102
describe('continuous updating content', () => {
103103
it('should be worked', () => {
104104
// Mouseover
105-
const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target right' }).refs.wrapper;
105+
const origin = firstComponent(tree, Origin.WrappedComponent, { className: 'target time' }).refs.wrapper;
106106
TestUtils.Simulate.mouseEnter(origin);
107107

108108
const tooltip = firstComponent(tree, Tooltip.WrappedComponent).refs.tooltip;
@@ -123,7 +123,7 @@ describe('Content Example', () => {
123123

124124
it('should re-calculate a position of the tooltip', () => {
125125
// Mouseover to 'now' origin
126-
const now = firstComponent(tree, Origin.WrappedComponent, { className: 'target right' }).refs.wrapper;
126+
const now = firstComponent(tree, Origin.WrappedComponent, { className: 'target time' }).refs.wrapper;
127127
TestUtils.Simulate.mouseEnter(now);
128128

129129
clock.tick(500);

tests/feature/place.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ describe('Place Example', () => {
180180

181181
const topTipPos = position(tooltip);
182182
const topOriPos = position(top);
183-
assert(topOriPos.bottom < topTipPos.top, 'tooltip should be located on bottom of the origin');
183+
assert(topOriPos.right < topTipPos.left, 'tooltip should be located on right of the origin');
184184

185185
// Mouseover to right
186186
const right = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-right' }).refs.wrapper;
@@ -189,7 +189,7 @@ describe('Place Example', () => {
189189

190190
const rightTipPos = position(tooltip);
191191
const rightOriPos = position(right);
192-
assert(rightTipPos.right < rightOriPos.left, 'tooltip should be located left of the origin');
192+
assert(rightTipPos.bottom < rightOriPos.top, 'tooltip should be located top of the origin');
193193

194194
// Mouseover to bottom
195195
const bottom = firstComponent(tree, Origin.WrappedComponent, { className: 'target auto-bottom' }).refs.wrapper;
@@ -198,7 +198,7 @@ describe('Place Example', () => {
198198

199199
const bottomTipPos = position(tooltip);
200200
const bottomOriPos = position(bottom);
201-
assert(bottomTipPos.bottom < bottomOriPos.top, 'tooltip should be located on top of the origin');
201+
assert(bottomOriPos.right < bottomTipPos.left, 'tooltip should be located on right of the origin');
202202
});
203203

204204
it("should be disabled by auto={false}", () => {

tests/unit/test_utils.js

+16-18
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,27 @@ import assert from 'power-assert';
22
import * as utils from '../../src/utils';
33

44
describe('utils', () => {
5-
describe('.opposite', () => {
5+
describe('.complete', () => {
66
context('with a direction', () => {
7-
it('returns an opposite direction', () => {
8-
assert(utils.opposite('top') === 'bottom');
9-
assert(utils.opposite('right') === 'left');
10-
assert(utils.opposite('bottom') === 'top');
11-
assert(utils.opposite('left') === 'right');
12-
});
13-
});
7+
it('returns a completed list', () => {
8+
assert.deepStrictEqual(utils.complete('top'), ['top', 'right', 'bottom', 'left']);
9+
assert.deepStrictEqual(utils.complete('right'), ['right', 'top', 'bottom', 'left']);
10+
assert.deepStrictEqual(utils.complete('bottom'), ['bottom', 'top', 'right', 'left']);
11+
assert.deepStrictEqual(utils.complete('left'), ['left', 'top', 'right', 'bottom']);
1412

15-
context('with directions as Array', () => {
16-
it('returns an opposite direction', () => {
17-
assert(utils.opposite(['top']) === 'bottom');
18-
assert(utils.opposite(['right', 'left']) === 'left');
13+
assert.deepStrictEqual(utils.complete(['top']), ['top', 'right', 'bottom', 'left']);
14+
assert.deepStrictEqual(utils.complete(['right']), ['right', 'top', 'bottom', 'left']);
15+
assert.deepStrictEqual(utils.complete(['bottom']), ['bottom', 'top', 'right', 'left']);
16+
assert.deepStrictEqual(utils.complete(['left']), ['left', 'top', 'right', 'bottom']);
1917
});
2018
});
2119

22-
context('with invalid direction', () => {
23-
it('throws an exception', () => {
24-
assert.throws(() => utils.opposite(), Error);
25-
assert.throws(() => utils.opposite(''), Error);
26-
assert.throws(() => utils.opposite(123), Error);
27-
assert.throws(() => utils.opposite([]), Error);
20+
context('with a list of directions', () => {
21+
it('returns a completed list', () => {
22+
assert.deepStrictEqual(utils.complete(['top', 'bottom']), ['top', 'bottom', 'right', 'left']);
23+
assert.deepStrictEqual(utils.complete(['left', 'right']), ['left', 'right', 'top', 'bottom']);
24+
assert.deepStrictEqual(utils.complete(['top', 'right', 'bottom', 'left']), ['top', 'right', 'bottom', 'left']);
25+
assert.deepStrictEqual(utils.complete(['top', 'right', 'bottom', 'left']), ['top', 'right', 'bottom', 'left']);
2826
});
2927
});
3028
});

0 commit comments

Comments
 (0)