Skip to content

Commit

Permalink
[Tooltip] Fix some new issues
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviertassinari committed Jul 17, 2018
1 parent 7562658 commit e10ab9b
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 17 deletions.
2 changes: 1 addition & 1 deletion packages/material-ui/src/Popper/Popper.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ class Popper extends React.Component {
: {
// It's using scrollParent by default, we can use the viewport when using a portal.
preventOverflow: {
boundariesElement: 'viewport',
boundariesElement: 'window',
},
}),
...modifiers,
Expand Down
50 changes: 39 additions & 11 deletions packages/material-ui/src/Tooltip/Tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ class Tooltip extends React.Component {

defaultId = null;

internalState = {
hover: false,
focus: false,
};

constructor(props) {
super(props);

Expand All @@ -91,9 +96,7 @@ class Tooltip extends React.Component {

componentDidMount() {
warning(
!this.childrenRef ||
!this.childrenRef.disabled ||
!this.childrenRef.tagName.toLowerCase() === 'button',
!this.childrenRef.disabled || !this.childrenRef.tagName.toLowerCase() === 'button',
[
'Material-UI: you are providing a disabled `button` child to the Tooltip component.',
'A disabled element does not fire events.',
Expand Down Expand Up @@ -125,18 +128,31 @@ class Tooltip extends React.Component {
const { children, enterDelay } = this.props;
const childrenProps = children.props;

if (event.type === 'focus' && childrenProps.onFocus) {
childrenProps.onFocus(event);
if (event.type === 'focus') {
this.internalState.focus = true;

if (childrenProps.onFocus) {
childrenProps.onFocus(event);
}
}

if (event.type === 'mouseenter' && childrenProps.onMouseEnter) {
childrenProps.onMouseEnter(event);
if (event.type === 'mouseenter') {
this.internalState.hover = true;

if (childrenProps.onMouseEnter) {
childrenProps.onMouseEnter(event);
}
}

if (this.ignoreNonTouchEvents && event.type !== 'touchstart') {
return;
}

// Remove the title ahead of time.
// We don't want to wait for the next render commit.
// We would risk displaying two tooltips at the same time (native + this one).
this.childrenRef.setAttribute('title', '');

clearTimeout(this.enterTimer);
clearTimeout(this.leaveTimer);
if (enterDelay) {
Expand All @@ -163,12 +179,20 @@ class Tooltip extends React.Component {
const { children, leaveDelay } = this.props;
const childrenProps = children.props;

if (event.type === 'blur' && childrenProps.onBlur) {
childrenProps.onBlur(event);
if (event.type === 'blur') {
this.internalState.focus = false;

if (childrenProps.onBlur) {
childrenProps.onBlur(event);
}
}

if (event.type === 'mouseleave' && childrenProps.onMouseLeave) {
childrenProps.onMouseLeave(event);
if (event.type === 'mouseleave') {
this.internalState.hover = false;

if (childrenProps.onMouseLeave) {
childrenProps.onMouseLeave(event);
}
}

clearTimeout(this.enterTimer);
Expand All @@ -184,6 +208,10 @@ class Tooltip extends React.Component {
};

handleClose = event => {
if (this.internalState.focus || this.internalState.hover) {
return;
}

if (!this.isControlled) {
this.setState({ open: false });
}
Expand Down
32 changes: 27 additions & 5 deletions packages/material-ui/src/Tooltip/Tooltip.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ describe('<Tooltip />', () => {
};

before(() => {
shallow = createShallow({ dive: true });
shallow = createShallow({ dive: true, disableLifecycleMethods: true });
mount = createMount();
classes = getClasses(<Tooltip {...defaultProps} />);
});
Expand Down Expand Up @@ -58,11 +58,12 @@ describe('<Tooltip />', () => {

it('should respond to external events', () => {
const wrapper = shallow(<Tooltip {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
assert.strictEqual(wrapper.state().open, false);
children.simulate('mouseEnter', {});
children.simulate('mouseEnter', { type: 'mouseenter' });
assert.strictEqual(wrapper.state().open, true);
children.simulate('blur', {});
children.simulate('mouseLeave', { type: 'mouseleave' });
assert.strictEqual(wrapper.state().open, false);
});

Expand All @@ -73,17 +74,32 @@ describe('<Tooltip />', () => {
const wrapper = shallow(
<Tooltip {...defaultProps} open onOpen={handleRequestOpen} onClose={handleClose} />,
);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
assert.strictEqual(handleRequestOpen.callCount, 0);
assert.strictEqual(handleClose.callCount, 0);
children.simulate('mouseEnter', { type: 'mouseover' });
children.simulate('mouseEnter', { type: 'mouseenter' });
assert.strictEqual(handleRequestOpen.callCount, 1);
assert.strictEqual(handleClose.callCount, 0);
children.simulate('blur', { type: 'blur' });
children.simulate('mouseLeave', { type: 'mouseleave' });
assert.strictEqual(handleRequestOpen.callCount, 1);
assert.strictEqual(handleClose.callCount, 1);
});

it('should close when the interaction is over', () => {
const wrapper = shallow(<Tooltip {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
assert.strictEqual(wrapper.state().open, false);
children.simulate('mouseEnter', { type: 'mouseenter' });
children.simulate('focus', { type: 'focus' });
assert.strictEqual(wrapper.state().open, true);
children.simulate('mouseLeave', { type: 'mouseleave' });
assert.strictEqual(wrapper.state().open, true);
children.simulate('blur', { type: 'blur' });
assert.strictEqual(wrapper.state().open, false);
});

describe('touch screen', () => {
let clock;

Expand All @@ -97,6 +113,7 @@ describe('<Tooltip />', () => {

it('should not respond to quick events', () => {
const wrapper = shallow(<Tooltip {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
children.simulate('touchStart', { type: 'touchstart', persist });
children.simulate('touchEnd', { type: 'touchend', persist });
Expand All @@ -107,13 +124,15 @@ describe('<Tooltip />', () => {

it('should open on long press', () => {
const wrapper = shallow(<Tooltip {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
children.simulate('touchStart', { type: 'touchstart', persist });
children.simulate('focus', { type: 'focus' });
children.simulate('mouseover', { type: 'mouseover' });
clock.tick(1e3);
assert.strictEqual(wrapper.state().open, true);
children.simulate('touchEnd', { type: 'touchend', persist });
children.simulate('blur', { type: 'blur' });
clock.tick(1500);
assert.strictEqual(wrapper.state().open, false);
});
Expand All @@ -138,6 +157,7 @@ describe('<Tooltip />', () => {

it('should take the enterDelay into account', () => {
const wrapper = shallow(<Tooltip enterDelay={111} {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
children.simulate('focus', { type: 'focus', persist });
assert.strictEqual(wrapper.state().open, false);
Expand All @@ -147,6 +167,7 @@ describe('<Tooltip />', () => {

it('should take the leaveDelay into account', () => {
const wrapper = shallow(<Tooltip leaveDelay={111} {...defaultProps} />);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
children.simulate('focus', { type: 'focus' });
assert.strictEqual(wrapper.state().open, true);
Expand All @@ -169,6 +190,7 @@ describe('<Tooltip />', () => {
</button>
</Tooltip>,
);
wrapper.instance().childrenRef = document.createElement('div');
const children = wrapper.childAt(0).childAt(0);
const type = name.slice(2).toLowerCase();
children.simulate(type, { type, persist });
Expand Down

0 comments on commit e10ab9b

Please sign in to comment.