-
Notifications
You must be signed in to change notification settings - Fork 843
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
tooltip component #174
tooltip component #174
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some code review comments trying to keep the coding style consistent with what we have in EUI
src/components/tooltip/tooltip.js
Outdated
render() { | ||
let {isSticky, visible, size, className, children, ...others} = this.props; | ||
|
||
const newClasses = classnames('tooltip-container', visible ? 'tooltip-container-visible' : 'tooltip-container-hidden', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using this function, you can use an object for clarity when it comes to optional classes:
const newClasses = classnames('tooltip-container', {
'tooltip-container--visible': visible,
'tooltip-container--hidden': !visible,
'tooltip-container--hoverable': isSticky,
[`tooltip-${ size }`]: size !== 'auto'
}, className)
src/components/tooltip/tooltip.js
Outdated
} | ||
|
||
render() { | ||
let {isSticky, visible, size, className, children, ...others} = this.props; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we use const
here?
src/components/tooltip/tooltip.js
Outdated
|
||
constructor(props) { | ||
super(props); | ||
this.state = {visible: props.trigger === 'manual' ? props.display : false}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It'd be best to break this up into multiple lines to keep things simple:
const visible = props.trigger === 'manual' ? props.display : false
this.state = { visible };
Consider adding a comment about what the conditional does
src/components/tooltip/tooltip.js
Outdated
return ( | ||
<div {...newProps}> | ||
{this.props.children} | ||
<Tooltip {...{isSticky, size: this.props.size, visible}}>{tooltip}</Tooltip> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Move this to a variable for readability:
const tooltipProps = {isSticky, size, visible}
return ...<Tooltip {...tooltipProps}>...
src/components/tooltip/tooltip.js
Outdated
onMouseLeave: this.hoverHandler.bind(this) | ||
}; | ||
break; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's move all of this out of the way and into its own function:
const triggerHandler = getTriggerHandler.call(this)
...
function getTriggerHandler() {
if (trigger === 'manual') {
return {}
}
if (triggerHandler === 'click') {
return {
onClick: e => this.clickHandler(e, onClick)
}
}
return {
onClick,
onMouseEnter: this.hoverHandler.bind(this),
onMouseLeave: this.hoverHandler.bind(this)
}
}
src/components/tooltip/tooltip.js
Outdated
} | ||
|
||
render() { | ||
const {isSticky, placement, tooltip, trigger, className, clickHideDelay, onEntered, onExited, theme, size, onClick, display, ...others} = this.props; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe break this up to list one property per line?
src/components/tooltip/tooltip.js
Outdated
onClick(e); | ||
setTimeout(() => { | ||
this.setState({visible: false}); | ||
}, this.props.clickHideDelay); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this component accomplish? It looks like the Focus Trap we're already using elsewhere can do this job without duplicating logic /cc @cjcenizal
src/components/tooltip/tooltip.js
Outdated
export class Tooltip extends React.PureComponent { | ||
static propTypes = { | ||
visible: PropTypes.bool, | ||
size: PropTypes.oneOf(['auto','sm', 'md', 'lg']), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The EUI standard sizing properties are single letters: s
, m
, l
. auto
is fine as-is
src/components/tooltip/tooltip.js
Outdated
static defaultProps = { | ||
visible: true, | ||
size: 'auto', | ||
isSticky: false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd argue that we either have property names like isVisible
isSticky
everywhere or visible
, sticky
, for consistency
src/components/tooltip/tooltip.js
Outdated
isSticky: false | ||
}; | ||
|
||
constructor(props) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant constructor can be safely removed
@nreese Cool. We really need this. I will rewrite the CSS layer (and can do a pass for naming consistency of your props as well) for you tomorrow so don't worry about that side. I can make this pretty with some nice quick animations! From a feature perspective there are a few things that we should probably have.
|
+1 I noticed the Pivotal's example for the right positioned tooltip not only extends visually past the window but also causes the window to scroll horizontally. It would be nice if ours was smarter than that and ensures that the tooltip is always positioned inside the current window. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just have a few minor suggestions but overall this looks like a great start. I think we can merge this to unblock you, and then we can refine the styles and functionality in subsequent PRs.
</p> | ||
</div> | ||
} | ||
demo={ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we extract this demo code into an separate file, so we can also show the source code for it? (Similar to the docs for the other components).
text={ | ||
<div> | ||
<p> | ||
https://styleguide.pivotal.io/tooltips#tooltip-triggers |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove this? I think it's good to keep the link the description of this PR as a reference, but I think we should own this component instead of relying on external documentation for it.
|
||
export default props => ( | ||
<GuidePage title={props.route.name}> | ||
<GuideSection |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a CallOut here with a note that we're going to be changing this around a lot? I just want people to be aware that the Tooltip will change as we define our requirements more clearly.
import {
EuiButton,
EuiCallOut,
EuiCode,
EuiSpacer,
} from '../../../../src/components';
/* ... */
<GuidePage title={props.route.name}>
<EuiCallOut
title="Work in progress"
color="warning"
>
<p>
This component is still undergoing active development, and its interface and implementation
are both subject to change.
</p>
</EuiCallOut>
<EuiSpacer size="l" />
src/components/tooltip/tooltip.js
Outdated
|
||
return ( | ||
<div className={newClasses} {...others}> | ||
<div className="tooltip-content">{children}</div> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Re @snide's comment about body
and title
, I think we should stick with using children
in lieu of body
personally -- this feels more ergonomic to me. We've been trying to use children
wherever possible. And this way we can add the title
prop later without needing to change anything else about the interface.
@nreese Looks like the CI is failing due to linting errors. You can run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just merged some changes into master
which will cause merge conflicts. Here are the steps for resolving them.
@@ -101,6 +101,9 @@ import TextExample | |||
import ToastExample | |||
from '../../views/toast/toast_example'; | |||
|
|||
import TooltipExample |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To fix the merge conflicts with master, this will have to become:
import { TooltipExample }
from '../../views/tooltip/tooltip_example';
@@ -212,6 +215,9 @@ const components = [{ | |||
}, { | |||
name: 'Toast', | |||
component: ToastExample, | |||
}, { | |||
name: 'Tooltip', | |||
component: TooltipExample, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This array of objects has become just a single-dimensional array of references to the example files, so you can replace this object with just TooltipExample
.
} | ||
/> | ||
</GuidePage> | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This entire example will become:
import React from 'react';
import { renderToHtml } from '../../services';
import {
GuidePage,
GuideSection,
GuideSectionTypes,
} from '../../components';
import {
EuiCallOut,
EuiSpacer,
EuiButton,
EuiCode,
} from '../../../../src/components';
import TooltipExamples from './examples';
const examplesSource = require('!!raw-loader!./examples');
const examplesHtml = renderToHtml(TooltipExamples);
export const TooltipExample = {
title: 'Tooltip',
intro: (
<div>
<EuiCallOut
title="Work in progress"
color="warning"
>
<p>
This component is still undergoing active development, and its interface and implementation
are both subject to change.
</p>
</EuiCallOut>
<EuiSpacer size="l" />
</div>
),
sections: [{
title: 'Tooltip',
source: [{
type: GuideSectionTypes.JS,
code: '',
}, {
type: GuideSectionTypes.HTML,
code: '',
}],
text: (
<p>
</p>
),
demo: <TooltipExamples />,
}],
};
<div> | ||
<div> | ||
Check out this | ||
<TooltipTrigger tooltip="I am the body" title="I am the title"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to the way whitespace is interpreted, there is no space between "this" and "tooltip" in these examples:
We can fix this like this:
Check out this {(
<TooltipTrigger tooltip="I am the body" title="I am the title">
<span className="overlay-trigger" tabIndex="0">tooltip with title.</span>
</TooltipTrigger>
)}
@cchaos Are you planning on doing the CSS work against this PR or should I finish getting this PR reviewed and merged and then have the CSS work done against master? |
@nreese Yeah I think that is the plan. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm ok with this for now given the warning at the top. @cchaos and I will work to get the styling done and likely cleanup some of the props / interface so that it's a little simpler, closer to our spec. Functionally though it does everything we need it to do for design to get involved. Thanks @nreese.
Adds tooltip component
Adds new service
noOverflowPlacement
that contains reusable logic for determining placement (top, right, bottom, left) that avoids clipping by window view port