"keymaster": "1.6.2",
"lodash": "4.17.4",
"prop-types": "15.6.0",
- "rc-tooltip": "3.5.0",
"react": "16.2.0",
"react-day-picker": "7.1.2",
"react-dom": "16.2.0",
<Tooltip
mouseEnterDelay={1}
overlay="master"
- placement="bottom"
>
<span
className="text-limited text-top"
<Tooltip
mouseEnterDelay={1}
overlay="1234 – Feature PR"
- placement="bottom"
>
<span
className="text-limited text-top"
<Tooltip
mouseEnterDelay={1}
overlay="foo"
- placement="bottom"
>
<span
className="text-limited text-top"
branches.orphan_branches
<Tooltip
overlay="branches.orphan_branches.tooltip"
- placement="bottom"
>
<i
className="icon-help spacer-left"
branches.orphan_branches
<Tooltip
overlay="branches.orphan_branches.tooltip"
- placement="bottom"
>
<i
className="icon-help spacer-left"
<Tooltip
mouseEnterDelay={0.5}
overlay="version 0.0.1"
- placement="bottom"
>
<div
className="spacer-left text-limited"
}, 3000);
};
- withTutorialTooltip = (element: React.ReactNode) =>
- this.state.onboardingTutorialTooltip ? (
- <Tooltip defaultVisible={true} overlay={translate('tutorials.follow_later')} trigger="manual">
- {element}
- </Tooltip>
- ) : (
- element
- );
-
render() {
return (
- <NavBar className="navbar-global" id="global-navigation" height={theme.globalNavHeightRaw}>
+ <NavBar className="navbar-global" height={theme.globalNavHeightRaw} id="global-navigation">
<GlobalNavBranding />
<GlobalNavMenu {...this.props} />
<ul className="global-navbar-menu pull-right">
<GlobalNavExplore location={this.props.location} onSonarCloud={this.props.onSonarCloud} />
<li>
- <a className="navbar-help" onClick={this.handleHelpClick} href="#">
- {this.props.onSonarCloud ? <HelpIcon /> : this.withTutorialTooltip(<HelpIcon />)}
- </a>
+ <Tooltip
+ overlay={this.props.onSonarCloud ? undefined : translate('tutorials.follow_later')}
+ visible={this.state.onboardingTutorialTooltip}>
+ <a className="navbar-help" href="#" onClick={this.handleHelpClick}>
+ <HelpIcon />
+ </a>
+ </Tooltip>
</li>
<Search appState={this.props.appState} currentUser={this.props.currentUser} />
{isLoggedIn(this.props.currentUser) &&
- this.props.onSonarCloud &&
- this.withTutorialTooltip(
- <GlobalNavPlus openOnboardingTutorial={this.openOnboardingTutorial} />
+ this.props.onSonarCloud && (
+ <Tooltip
+ overlay={translate('tutorials.follow_later')}
+ visible={this.state.onboardingTutorialTooltip}>
+ <GlobalNavPlus openOnboardingTutorial={this.openOnboardingTutorial} />
+ </Tooltip>
)}
<GlobalNavUserContainer {...this.props} />
</ul>
<GlobalHelp
currentUser={this.props.currentUser}
onClose={this.closeHelp}
- onTutorialSelect={this.openOnboardingTutorial}
onSonarCloud={this.props.onSonarCloud}
+ onTutorialSelect={this.openOnboardingTutorial}
/>
)}
@import './components/alerts.css';
@import './components/issues.css';
@import './components/search-navigator.css';
-@import './components/tooltips.css';
@import './components/dropdowns.css';
@import './components/menu.css';
@import './components/page.css';
<div className="account-project-card clearfix">
<aside className="account-project-side">
{isAnalyzed ? (
- <Tooltip
- overlay={<DateTimeFormatter date={project.lastAnalysisDate} />}
- placement="right">
- <div className="account-project-analysis">
- <DateFromNow date={project.lastAnalysisDate}>
- {(fromNow: string) => (
+ <div className="account-project-analysis">
+ <DateFromNow date={project.lastAnalysisDate}>
+ {fromNow => (
+ <Tooltip overlay={<DateTimeFormatter date={project.lastAnalysisDate} />}>
<span>{translateWithParameters('my_account.projects.analyzed_x', fromNow)}</span>
- )}
- </DateFromNow>
- </div>
- </Tooltip>
+ </Tooltip>
+ )}
+ </DateFromNow>
+ </div>
) : (
<div className="account-project-analysis">
{translate('my_account.projects.never_analyzed')}
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
<div>
<Tooltip
overlay="background_tasks.number_of_workers.warning"
- placement="bottom"
>
<span>
<AlertWarnIcon
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
<div>
<Tooltip
overlay="background_tasks.number_of_workers.warning"
- placement="bottom"
>
<span>
<AlertWarnIcon
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
<div>
<Tooltip
overlay="background_tasks.number_of_workers.warning"
- placement="bottom"
>
<span>
<AlertWarnIcon
</span>
<Tooltip
overlay="background_tasks.change_number_of_workers"
- placement="bottom"
>
<EditButton
className="js-edit button-small spacer-left"
) : (
params.map(param => (
<div className="modal-field" key={param.key}>
- <Tooltip overlay={param.key} placement="left">
- <label>{param.key}</label>
- </Tooltip>
+ <label>
+ <Tooltip overlay={param.key}>
+ <span>{param.key}</span>
+ </Tooltip>
+ </label>
{param.type === 'TEXT' ? (
<textarea
className="width100"
}
return (
- <Tooltip overlay={translate('coding_rules.issues.only_main_branches')} placement="right">
- {totalItem}
- </Tooltip>
+ <Tooltip overlay={translate('coding_rules.issues.only_main_branches')}>{totalItem}</Tooltip>
);
};
)}
</ConfirmButton>
) : (
- <Tooltip overlay={translate('coding_rules.can_not_deactivate')} placement="left">
+ <Tooltip overlay={translate('coding_rules.can_not_deactivate')}>
<Button className="coding-rules-detail-quality-profile-deactivate button-red disabled">
{translate('coding_rules.deactivate')}
</Button>
return (
<span>
- {component.name !== componentName ? (
- <Tooltip overlay={component.name} placement="bottom">
- {breadcrumbItem}
- </Tooltip>
- ) : (
- breadcrumbItem
- )}
+ <Tooltip overlay={component.name !== componentName ? component.name : undefined}>
+ {breadcrumbItem}
+ </Tooltip>
{!isLast && <span className="slash-separator" />}
</span>
);
<DateFormatter date={date} long={true} />
</div>
);
- return (
- <Tooltip placement="left" overlay={tooltip}>
- {label}
- </Tooltip>
- );
+ return <Tooltip overlay={tooltip}>{label}</Tooltip>;
}
</span>
{!isDiff &&
hasHistory && (
- <Tooltip
- overlay={translate('component_measures.show_metric_history')}
- placement="right">
+ <Tooltip overlay={translate('component_measures.show_metric_history')}>
<Link
className="js-show-history spacer-left button button-small"
to={getMeasureHistoryUrl(component.key, metric.key, branchLike)}>
exports[`should correctly show a middle element 1`] = `
<span>
- <a
- href="#"
- onClick={[Function]}
- >
- Foo
- </a>
+ <Tooltip>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ Foo
+ </a>
+ </Tooltip>
<span
className="slash-separator"
/>
exports[`should show the last element without clickable link 1`] = `
<span>
- <span>
- Foo
- </span>
+ <Tooltip>
+ <span>
+ Foo
+ </span>
+ </Tooltip>
</span>
`;
/>
</div>
}
- placement="left"
>
<div
className="domain-measures-leak-header"
</span>
<Tooltip
overlay="component_measures.show_metric_history"
- placement="right"
>
<Link
className="js-show-history spacer-left button button-small"
</span>
<Tooltip
overlay="component_measures.show_metric_history"
- placement="right"
>
<Link
className="js-show-history spacer-left button button-small"
<Tooltip
mouseEnterDelay={0.5}
overlay="issue.this_issue_involves_x_code_locations.7"
- placement="bottom"
>
<LocationIndex
selected={false}
exports[`should display the license field 1`] = `
<Tooltip
overlay="SonarSource license"
- placement="bottom"
>
<li
className="little-spacer-bottom marketplace-plugin-license"
this.mounted = false;
}
- fetchLeaks = (visible: boolean) => {
- if (visible && !this.state.leaks) {
+ fetchLeaks = () => {
+ if (!this.state.leaks) {
getApplicationLeak(this.props.component).then(
leaks => {
if (this.mounted) {
render() {
return (
- <Tooltip onVisibleChange={this.fetchLeaks} overlay={this.renderOverlay()}>
+ <Tooltip onShow={this.fetchLeaks} overlay={this.renderOverlay()}>
<div className="overview-legend overview-legend-spaced-line">
{translate('issues.leak_period')}
</div>
</DateFormatter>
);
return (
- <Tooltip overlay={tooltip} placement="top">
+ <Tooltip overlay={tooltip}>
<div className="overview-legend">
{translateWithParameters('overview.leak_period_x', leakPeriodLabel)}
<br />
const wrapper = shallow(<ApplicationLeakPeriodLegend component="foo" />);
expect(wrapper).toMatchSnapshot();
- wrapper.find('Tooltip').prop<Function>('onVisibleChange')(true);
+ wrapper.find('Tooltip').prop<Function>('onShow')();
await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});
exports[`renders 1`] = `
<Tooltip
- onVisibleChange={[Function]}
+ onShow={[Function]}
overlay={
<i
className="spinner spinner-margin"
/>
}
- placement="bottom"
>
<div
className="overview-legend overview-legend-spaced-line"
exports[`renders 2`] = `
<Tooltip
- onVisibleChange={[Function]}
+ onShow={[Function]}
overlay={
<ul
className="text-left"
</li>
</ul>
}
- placement="bottom"
>
<div
className="overview-legend overview-legend-spaced-line"
<li className="overview-analysis">
<div className="small little-spacer-bottom">
<strong>
- <DateTooltipFormatter date={analysis.date} placement="right" />
+ <DateTooltipFormatter date={analysis.date} />
</strong>
</div>
export default function Event({ event }: Props) {
if (event.category === 'VERSION') {
return (
- <Tooltip overlay={`${translate('version')} ${event.name}`} mouseEnterDelay={0.5}>
+ <Tooltip mouseEnterDelay={0.5} overlay={`${translate('version')} ${event.name}`}>
<span className="overview-analysis-event badge">{event.name}</span>
</Tooltip>
);
<div className="overview-analysis-event">
<span className="note">{translate('event.category', event.category)}:</span>{' '}
{event.description ? (
- <Tooltip overlay={event.description} placement="left" mouseEnterDelay={0.5}>
+ <Tooltip mouseEnterDelay={0.5} overlay={event.description}>
<strong>{event.name}</strong>
</Tooltip>
) : (
<strong>
<DateTooltipFormatter
date="2017-06-10T16:10:59+0200"
- placement="right"
/>
</strong>
</div>
<Tooltip
mouseEnterDelay={0.5}
overlay="version 6.5-SNAPSHOT"
- placement="bottom"
>
<span
className="overview-analysis-event badge"
const value = this.getValue(measure);
const title = value && getRatingTooltip(metricKey, value);
return (
- <Tooltip overlay={title} placement="top">
+ <Tooltip overlay={title}>
<div className="overview-domain-measure-sup">
<DrilldownLink
branchLike={branchLike}
overview.quality_gate.ignored_conditions
<Tooltip
overlay="overview.quality_gate.ignored_conditions.tooltip"
- placement="bottom"
>
<span
className="spacer-left"
return (
<Tooltip
key={serie.name}
- overlay={translate('project_activity.graphs.custom.metric_no_history')}
- placement="bottom">
+ overlay={translate('project_activity.graphs.custom.metric_no_history')}>
<span className="spacer-left spacer-right">{legendItem}</span>
</Tooltip>
);
role="listitem"
tabIndex="0">
<div className="project-activity-time spacer-right">
- <TimeTooltipFormatter className="text-middle" date={date} placement="right" />
+ <TimeTooltipFormatter className="text-middle" date={date} />
</div>
<div className="project-activity-analysis-icon spacer-right" title={analysisTitle} />
<Tooltip
key="foo"
overlay="project_activity.graphs.custom.metric_no_history"
- placement="bottom"
>
<span
className="spacer-left spacer-right"
<Tooltip
mouseEnterDelay={0.5}
overlay="version 6.4"
- placement="bottom"
>
<span
className="badge"
<Tooltip
mouseEnterDelay={0.5}
overlay="version 6.5-SNAPSHOT"
- placement="bottom"
>
<span
className="badge"
<Tooltip
mouseEnterDelay={0.5}
overlay="version 6.5-SNAPSHOT"
- placement="bottom"
>
<span
className="badge"
<Tooltip
mouseEnterDelay={0.5}
overlay="version 6.4"
- placement="bottom"
>
<span
className="badge"
return (
<div className="project-card-languages">
- <Tooltip placement="bottom" overlay={tooltip}>
+ <Tooltip overlay={tooltip}>
<span>{languagesText}</span>
</Tooltip>
</div>
</h2>
{project.analysisDate && <ProjectCardQualityGate status={measures!['alert_status']} />}
<div className="project-card-header-right">
- {isPrivate && (
- <PrivateBadge className="spacer-left" qualifier="TRK" tooltipPlacement="left" />
- )}
+ {isPrivate && <PrivateBadge className="spacer-left" qualifier="TRK" />}
{hasTags && <TagsList className="spacer-left note" tags={project.tags} />}
</div>
</div>
</h2>
{project.analysisDate && <ProjectCardQualityGate status={measures['alert_status']} />}
<div className="project-card-header-right">
- {isPrivate && (
- <PrivateBadge className="spacer-left" qualifier="TRK" tooltipPlacement="left" />
- )}
+ {isPrivate && <PrivateBadge className="spacer-left" qualifier="TRK" />}
{hasTags && <TagsList className="spacer-left note" tags={project.tags} />}
</div>
</div>
/>
<Tooltip
overlay="projects.sort.disabled"
- placement="bottom"
>
<div
className="projects-topbar-item disabled"
</span>
</span>
}
- placement="bottom"
>
<span>
cpp, Java
</span>
</span>
}
- placement="bottom"
>
<span>
unknown, Java
</span>
</span>
}
- placement="bottom"
>
<span>
Java, JavaScript
</span>
</span>
}
- placement="bottom"
>
<span>
JavaScript, Java
>
<Tooltip
overlay="overview.quality_gate_x.ERROR"
- placement="bottom"
>
<div
className="project-card-measure-inner"
/>
<Tooltip
overlay="projects.sort_descending"
- placement="bottom"
>
<ButtonIcon
className="js-projects-sorting-invert spacer-left"
/>
<Tooltip
overlay="projects.sort_ascending"
- placement="bottom"
>
<ButtonIcon
className="js-projects-sorting-invert spacer-left"
/>
<Tooltip
overlay="projects.sort_ascending"
- placement="bottom"
>
<ButtonIcon
className="js-projects-sorting-invert spacer-left"
provisioning.only_provisioned
<Tooltip
overlay="provisioning.only_provisioned.tooltip"
- placement="bottom"
>
<i
className="spacer-left icon-help"
provisioning.only_provisioned
<Tooltip
overlay="provisioning.only_provisioned.tooltip"
- placement="bottom"
>
<i
className="spacer-left icon-help"
</div>
);
- return tooltip ? <Tooltip overlay={overlay}>{badge}</Tooltip> : badge;
+ return <Tooltip overlay={tooltip ? overlay : undefined}>{badge}</Tooltip>;
}
</span>
);
- return tooltip ? (
- <Tooltip overlay={overlay} placement="right">
- {badge}
- </Tooltip>
- ) : (
- badge
- );
+ return <Tooltip overlay={tooltip ? overlay : undefined}>{badge}</Tooltip>;
}
quality_profiles.deprecated_rules
<Tooltip
overlay="quality_profiles.deprecated_rules_description"
- placement="bottom"
>
<i
className="icon-help spacer-left"
quality_profiles.sonarway_missing_rules
<Tooltip
overlay="quality_profiles.sonarway_missing_rules_description"
- placement="bottom"
>
<i
className="icon-help spacer-left"
<HealthCauseItem key={idx} className="spacer-right" health={health} healthCause={cause} />
))}
{name ? (
- <Tooltip
- overlay={translateWithParameters('system.current_health_of_x', name)}
- placement="left">
+ <Tooltip overlay={translateWithParameters('system.current_health_of_x', name)}>
<span>{statusIndicator}</span>
</Tooltip>
) : (
/>
<Tooltip
overlay="system.current_health_of_x.Foo"
- placement="left"
>
<span>
<StatusIndicator
<Checkbox checked={showInternal} onCheck={onToggleInternal}>
<span className="little-spacer-left">{translate('api_documentation.show_internal')}</span>
</Checkbox>
- <Tooltip overlay={translate('api_documentation.internal_tooltip')} placement="right">
+ <Tooltip overlay={translate('api_documentation.internal_tooltip')}>
<span>
<HelpIcon className="spacer-left text-info" />
</span>
{translate('api_documentation.show_deprecated')}
</span>
</Checkbox>
- <Tooltip overlay={translate('api_documentation.deprecation_tooltip')} placement="right">
+ <Tooltip overlay={translate('api_documentation.deprecation_tooltip')}>
<span>
<HelpIcon className="spacer-left text-info" />
</span>
</Checkbox>
<Tooltip
overlay="api_documentation.internal_tooltip"
- placement="right"
>
<span>
<HelpIcon
</Checkbox>
<Tooltip
overlay="api_documentation.deprecation_tooltip"
- placement="right"
>
<span>
<HelpIcon
renderCreate = () => {
if (this.props.webhooksCount >= WEBHOOKS_LIMIT) {
return (
- <Tooltip
- overlay={translateWithParameters('webhooks.maximum_reached', WEBHOOKS_LIMIT)}
- placement="left">
+ <Tooltip overlay={translateWithParameters('webhooks.maximum_reached', WEBHOOKS_LIMIT)}>
<Button className="js-webhook-create disabled">{translate('create')}</Button>
</Tooltip>
);
>
<Tooltip
overlay="webhooks.maximum_reached.10"
- placement="left"
>
<Button
className="js-webhook-create disabled"
circle = <Link to={this.props.link}>{circle}</Link>;
}
- return this.props.tooltip ? (
- <Tooltip overlay={this.props.tooltip}>
+ return (
+ <Tooltip overlay={this.props.tooltip || undefined}>
<g>{circle}</g>
</Tooltip>
- ) : (
- circle
);
}
}
type YScale = ScaleBand<number>;
export default class Histogram extends React.PureComponent<Props> {
- wrapWithTooltip(element: React.ReactNode, index: number) {
- const tooltip = this.props.yTooltips && this.props.yTooltips[index];
- return tooltip ? (
- <Tooltip key={index} overlay={tooltip} placement="top">
- {element}
- </Tooltip>
- ) : (
- element
- );
- }
-
renderBar(d: number, index: number, xScale: XScale, yScale: YScale) {
const { alignTicks, padding = DEFAULT_PADDING } = this.props;
const x = xScale(d) + (alignTicks ? padding[3] : 0);
const y = Math.round(yScale(index)! + yScale.bandwidth() / 2 + BAR_HEIGHT / 2);
- return this.wrapWithTooltip(
- <text className="bar-chart-tick histogram-value" x={x} y={y} dx="1em" dy="0.3em">
- {value}
- </text>,
- index
+ return (
+ <Tooltip overlay={this.props.yTooltips && this.props.yTooltips[index]}>
+ <text className="bar-chart-tick histogram-value" x={x} y={y} dx="1em" dy="0.3em">
+ {value}
+ </text>
+ </Tooltip>
);
}
render() {
const { placement, tooltip } = this.props;
- if (tooltip != null) {
- return (
- <Tooltip overlay={tooltip} placement={placement || 'left'} mouseLeaveDelay={0}>
- {this.renderCell()}
- </Tooltip>
- );
- }
- return this.renderCell();
+ return (
+ <Tooltip overlay={tooltip || undefined} placement={placement || 'left'}>
+ {this.renderCell()}
+ </Tooltip>
+ );
}
}
x={-10}
y={52.3015873015873}
>
- <circle
- className="bubble-chart-bubble"
- r={45}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-10, 52.3015873015873)"
- />
+ <Tooltip>
+ <g>
+ <circle
+ className="bubble-chart-bubble"
+ r={45}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-10, 52.3015873015873)"
+ />
+ </g>
+ </Tooltip>
</Bubble>
`;
x={-75}
y={33.57142857142857}
>
- <circle
- className="bubble-chart-bubble"
- r={33.57142857142857}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-75, 33.57142857142857)"
- />
+ <Tooltip>
+ <g>
+ <circle
+ className="bubble-chart-bubble"
+ r={33.57142857142857}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-75, 33.57142857142857)"
+ />
+ </g>
+ </Tooltip>
</Bubble>
`;
x={-10}
y={52.3015873015873}
>
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to="foo"
- >
- <a
- onClick={[Function]}
- style={Object {}}
- >
- <circle
- className="bubble-chart-bubble"
- r={45}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-10, 52.3015873015873)"
- />
- </a>
- </Link>
+ <Tooltip>
+ <g>
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="foo"
+ >
+ <a
+ onClick={[Function]}
+ style={Object {}}
+ >
+ <circle
+ className="bubble-chart-bubble"
+ r={45}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-10, 52.3015873015873)"
+ />
+ </a>
+ </Link>
+ </g>
+ </Tooltip>
</Bubble>
`;
x={-75}
y={33.57142857142857}
>
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to="bar"
- >
- <a
- onClick={[Function]}
- style={Object {}}
- >
- <circle
- className="bubble-chart-bubble"
- r={33.57142857142857}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-75, 33.57142857142857)"
- />
- </a>
- </Link>
+ <Tooltip>
+ <g>
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to="bar"
+ >
+ <a
+ onClick={[Function]}
+ style={Object {}}
+ >
+ <circle
+ className="bubble-chart-bubble"
+ r={33.57142857142857}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-75, 33.57142857142857)"
+ />
+ </a>
+ </Link>
+ </g>
+ </Tooltip>
</Bubble>
`;
x={-10}
y={52.3015873015873}
>
- <circle
- className="bubble-chart-bubble"
- onClick={[Function]}
- r={45}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-10, 52.3015873015873)"
- />
+ <Tooltip>
+ <g>
+ <circle
+ className="bubble-chart-bubble"
+ onClick={[Function]}
+ r={45}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-10, 52.3015873015873)"
+ />
+ </g>
+ </Tooltip>
</Bubble>
`;
x={-75}
y={33.57142857142857}
>
- <circle
- className="bubble-chart-bubble"
- onClick={[Function]}
- r={33.57142857142857}
- style={
- Object {
- "fill": undefined,
- "stroke": undefined,
- }
- }
- transform="translate(-75, 33.57142857142857)"
- />
+ <Tooltip>
+ <g>
+ <circle
+ className="bubble-chart-bubble"
+ onClick={[Function]}
+ r={33.57142857142857}
+ style={
+ Object {
+ "fill": undefined,
+ "stroke": undefined,
+ }
+ }
+ transform="translate(-75, 33.57142857142857)"
+ />
+ </g>
+ </Tooltip>
</Bubble>
`;
x={0}
y={10}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={53.33333333333333}
- y={15}
- >
- 100.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={53.33333333333333}
+ y={15}
+ >
+ 100.0
+ </text>
+ </Tooltip>
</g>
<g
key="1"
x={0}
y={28}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={40}
- y={33}
- >
- 75.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={40}
+ y={33}
+ >
+ 75.0
+ </text>
+ </Tooltip>
</g>
<g
key="2"
x={0}
y={46}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={80}
- y={51}
- >
- 150.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={80}
+ y={51}
+ >
+ 150.0
+ </text>
+ </Tooltip>
</g>
</g>
</g>
x={0}
y={10}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={53.33333333333333}
- y={15}
- >
- 100.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={53.33333333333333}
+ y={15}
+ >
+ 100.0
+ </text>
+ </Tooltip>
<text
className="bar-chart-tick histogram-tick"
dx="-1em"
x={0}
y={28}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={40}
- y={33}
- >
- 75.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={40}
+ y={33}
+ >
+ 75.0
+ </text>
+ </Tooltip>
<text
className="bar-chart-tick histogram-tick"
dx="-1em"
x={0}
y={46}
/>
- <text
- className="bar-chart-tick histogram-value"
- dx="1em"
- dy="0.3em"
- x={80}
- y={51}
- >
- 150.0
- </text>
+ <Tooltip>
+ <text
+ className="bar-chart-tick histogram-value"
+ dx="1em"
+ dy="0.3em"
+ x={80}
+ y={51}
+ >
+ 150.0
+ </text>
+ </Tooltip>
<text
className="bar-chart-tick histogram-tick"
dx="-1em"
y={10}
/>
<Tooltip
- key="0"
overlay="a - 100"
- placement="top"
>
<text
className="bar-chart-tick histogram-value"
y={28}
/>
<Tooltip
- key="1"
overlay="b - 75"
- placement="top"
>
<text
className="bar-chart-tick histogram-value"
y={46}
/>
<Tooltip
- key="2"
overlay="c - 150"
- placement="top"
>
<text
className="bar-chart-tick histogram-value"
{tick}
</text>
);
- return d.tooltip ? (
- <Tooltip key={index} overlay={d.tooltip}>
+ return (
+ <Tooltip key={index} overlay={d.tooltip || undefined}>
{text}
</Tooltip>
- ) : (
- text
);
});
return <g>{ticks}</g>;
{value}
</text>
);
- return d.tooltip ? (
- <Tooltip key={index} overlay={d.tooltip}>
+ return (
+ <Tooltip key={index} overlay={d.tooltip || undefined}>
{text}
</Tooltip>
- ) : (
- text
);
});
return <g>{ticks}</g>;
y={y}
/>
);
- return d.tooltip ? (
- <Tooltip key={index} overlay={d.tooltip}>
+ return (
+ <Tooltip key={index} overlay={d.tooltip || undefined}>
{rect}
</Tooltip>
- ) : (
- rect
);
});
return <g>{bars}</g>;
overlay={translateWithParameters(
'branches.short_lived.quality_gate.description',
totalIssues
- )}
- placement="right">
+ )}>
<li className="spacer-left">
<HelpIcon className="text-info" />
</li>
return (
<div className={classNames('code-snippet', { 'code-snippet-oneline': isOneLine }, className)}>
<pre>{finalSnippet}</pre>
- {!noCopy && <ClipboardButton copyValue={finalSnippet} tooltipPlacement="top" />}
+ {!noCopy && <ClipboardButton copyValue={finalSnippet} />}
</div>
);
}
interface Props {
className?: string;
qualifier: string;
- tooltipPlacement?: string;
}
-export default function PrivateBadge({ className, qualifier, tooltipPlacement = 'bottom' }: Props) {
+export default function PrivateBadge({ className, qualifier }: Props) {
return (
- <Tooltip
- overlay={translate('visibility.private.description', qualifier)}
- placement={tooltipPlacement}>
+ <Tooltip overlay={translate('visibility.private.description', qualifier)}>
<div className={classNames('outline-badge', className)}>
{translate('visibility.private')}
</div>
}
render() {
- if (this.props.title) {
- return (
- <Tooltip placement="right" overlay={this.props.title}>
- {this.renderLink()}
- </Tooltip>
- );
- } else {
- return this.renderLink();
- }
+ return (
+ <Tooltip overlay={this.props.title || undefined} placement="right">
+ {this.renderLink()}
+ </Tooltip>
+ );
}
}
</li>
<Tooltip
overlay="branches.short_lived.quality_gate.description.1"
- placement="right"
>
<li
className="spacer-left"
<ClipboardButton
copyValue="foo
bar"
- tooltipPlacement="top"
>
- <Button
- className="js-copy-to-clipboard no-select"
- data-clipboard-text="foo
-bar"
- innerRef={[Function]}
+ <Tooltip
+ overlay="copied_action"
+ visible={false}
>
- <button
- className="button js-copy-to-clipboard no-select"
- data-clipboard-text="foo
-bar"
- onClick={[Function]}
- type="button"
+ <TooltipInner
+ mouseEnterDelay={0.1}
+ overlay="copied_action"
+ visible={false}
>
- copy
- </button>
- </Button>
+ <Button
+ className="js-copy-to-clipboard no-select"
+ data-clipboard-text="foo
+bar"
+ innerRef={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ >
+ <button
+ className="button js-copy-to-clipboard no-select"
+ data-clipboard-text="foo
+bar"
+ onClick={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ type="button"
+ >
+ copy
+ </button>
+ </Button>
+ </TooltipInner>
+ </Tooltip>
</ClipboardButton>
</div>
</CodeSnippet>
<ClipboardButton
copyValue="foo \\\\
bar"
- tooltipPlacement="top"
>
- <Button
- className="js-copy-to-clipboard no-select"
- data-clipboard-text="foo \\\\
- bar"
- innerRef={[Function]}
+ <Tooltip
+ overlay="copied_action"
+ visible={false}
>
- <button
- className="button js-copy-to-clipboard no-select"
- data-clipboard-text="foo \\\\
- bar"
- onClick={[Function]}
- type="button"
+ <TooltipInner
+ mouseEnterDelay={0.1}
+ overlay="copied_action"
+ visible={false}
>
- copy
- </button>
- </Button>
+ <Button
+ className="js-copy-to-clipboard no-select"
+ data-clipboard-text="foo \\\\
+ bar"
+ innerRef={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ >
+ <button
+ className="button js-copy-to-clipboard no-select"
+ data-clipboard-text="foo \\\\
+ bar"
+ onClick={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ type="button"
+ >
+ copy
+ </button>
+ </Button>
+ </TooltipInner>
+ </Tooltip>
</ClipboardButton>
</div>
</CodeSnippet>
</pre>
<ClipboardButton
copyValue="foo bar"
- tooltipPlacement="top"
>
- <Button
- className="js-copy-to-clipboard no-select"
- data-clipboard-text="foo bar"
- innerRef={[Function]}
+ <Tooltip
+ overlay="copied_action"
+ visible={false}
>
- <button
- className="button js-copy-to-clipboard no-select"
- data-clipboard-text="foo bar"
- onClick={[Function]}
- type="button"
+ <TooltipInner
+ mouseEnterDelay={0.1}
+ overlay="copied_action"
+ visible={false}
>
- copy
- </button>
- </Button>
+ <Button
+ className="js-copy-to-clipboard no-select"
+ data-clipboard-text="foo bar"
+ innerRef={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ >
+ <button
+ className="button js-copy-to-clipboard no-select"
+ data-clipboard-text="foo bar"
+ onClick={[Function]}
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ type="button"
+ >
+ copy
+ </button>
+ </Button>
+ </TooltipInner>
+ </Tooltip>
</ClipboardButton>
</div>
</CodeSnippet>
exports[`renders 1`] = `
<Tooltip
overlay="visibility.private.description.TRK"
- placement="bottom"
>
<div
className="outline-badge"
`;
exports[`should render correctly with children 1`] = `
-<li>
- <a
- className=""
- href="#"
- onClick={[Function]}
- onFocus={[Function]}
- onMouseOver={[Function]}
- >
- <i
- className="custom-icon"
- />
- <p>
- seconditem
- </p>
- </a>
-</li>
+<Tooltip
+ placement="right"
+>
+ <li>
+ <a
+ className=""
+ href="#"
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseOver={[Function]}
+ >
+ <i
+ className="custom-icon"
+ />
+ <p>
+ seconditem
+ </p>
+ </a>
+ </li>
+</Tooltip>
`;
exports[`should render correctly without children 1`] = `
-<li>
- <a
- className=""
- href="#"
- onClick={[Function]}
- onFocus={[Function]}
- onMouseOver={[Function]}
- >
- myitem
- </a>
-</li>
+<Tooltip
+ placement="right"
+>
+ <li>
+ <a
+ className=""
+ href="#"
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseOver={[Function]}
+ >
+ myitem
+ </a>
+ </li>
+</Tooltip>
`;
exports[`should render with the active class 1`] = `
-<li>
- <a
- className="active"
- href="#"
- onClick={[Function]}
- onFocus={[Function]}
- onMouseOver={[Function]}
- >
- myitem
- </a>
-</li>
+<Tooltip
+ placement="right"
+>
+ <li>
+ <a
+ className="active"
+ href="#"
+ onClick={[Function]}
+ onFocus={[Function]}
+ onMouseOver={[Function]}
+ >
+ myitem
+ </a>
+ </li>
+</Tooltip>
`;
interface Props {
className?: string;
copyValue: string;
- tooltipPlacement?: string;
}
interface State {
};
render() {
- const button = (
- <Button
- className={classNames('js-copy-to-clipboard no-select', this.props.className)}
- data-clipboard-text={this.props.copyValue}
- innerRef={node => (this.copyButton = node)}>
- {translate('copy')}
- </Button>
+ return (
+ <Tooltip overlay={translate('copied_action')} visible={this.state.tooltipShown}>
+ <Button
+ className={classNames('js-copy-to-clipboard no-select', this.props.className)}
+ data-clipboard-text={this.props.copyValue}
+ innerRef={node => (this.copyButton = node)}>
+ {translate('copy')}
+ </Button>
+ </Tooltip>
);
- if (this.state.tooltipShown) {
- return (
- <Tooltip
- defaultVisible={true}
- overlay={translate('copied_action')}
- placement={this.props.tooltipPlacement || 'bottom'}
- trigger="manual">
- {button}
- </Tooltip>
- );
- }
- return button;
}
}
? translate('favorite.current', this.props.qualifier)
: translate('favorite.check', this.props.qualifier);
return (
- <Tooltip overlay={tooltip} placement="left">
+ <Tooltip overlay={tooltip}>
<a
className={classNames('display-inline-block', 'link-no-underline', this.props.className)}
href="#"
const tooltip = checked ? translate('homepage.current') : translate('homepage.check');
return (
- <Tooltip overlay={tooltip} placement="left">
+ <Tooltip overlay={tooltip}>
{checked ? (
<span className={classNames('display-inline-block', this.props.className)}>
<HomeIcon filled={checked} />
checked={checked}
onChange={this.handleChange}
/>
- {option.tooltip ? (
- <Tooltip overlay={option.tooltip}>
- <label htmlFor={htmlId}>{option.label}</label>
- </Tooltip>
- ) : (
+ <Tooltip overlay={option.tooltip || undefined}>
<label htmlFor={htmlId}>{option.label}</label>
- )}
+ </Tooltip>
</li>
);
};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+.tooltip {
+ position: absolute;
+ z-index: var(--tooltipZIndex);
+ display: block;
+ height: auto;
+ box-sizing: border-box;
+ font-size: var(--smallFontSize);
+ font-weight: 300;
+ line-height: 1.5;
+ animation: fadeIn 0.3s forwards;
+}
+
+.tooltip.top {
+ padding: 5px 0;
+ margin-top: -3px;
+}
+
+.tooltip.right {
+ padding: 0 5px;
+ margin-left: 3px;
+}
+
+.tooltip.bottom {
+ padding: 5px 0;
+ margin-top: 3px;
+}
+
+.tooltip.left {
+ padding: 0 5px;
+ margin-left: -3px;
+}
+
+.tooltip-inner {
+ max-width: 300px;
+ padding: 3px 8px;
+ color: #fff;
+ text-align: left;
+ text-decoration: none;
+ background-color: #475760;
+ border-radius: 4px;
+ letter-spacing: 0.04em;
+ overflow: hidden;
+ word-break: break-word;
+}
+
+.tooltip-inner .alert {
+ margin-bottom: 5px;
+ border-radius: 4px;
+}
+
+.tooltip-arrow {
+ position: absolute;
+ width: 0;
+ height: 0;
+ border: solid transparent;
+}
+
+.tooltip.top .tooltip-arrow {
+ bottom: 0;
+ left: 50%;
+ border-width: 5px 5px 0;
+ border-top-color: #475760;
+ transform: translateX(-5px);
+}
+
+.tooltip.right .tooltip-arrow {
+ top: 50%;
+ left: 0;
+ transform: translateY(-5px);
+ border-width: 5px 5px 5px 0;
+ border-right-color: #475760;
+}
+
+.tooltip.left .tooltip-arrow {
+ top: 50%;
+ right: 0;
+ transform: translateY(-5px);
+ border-width: 5px 0 5px 5px;
+ border-left-color: #475760;
+}
+
+.tooltip.bottom .tooltip-arrow {
+ top: 0;
+ left: 50%;
+ transform: translateX(-5px);
+ border-width: 0 5px 5px;
+ border-bottom-color: #475760;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
+}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import TooltipCore from 'rc-tooltip';
+import { createPortal, findDOMNode } from 'react-dom';
+import { throttle } from 'lodash';
+import './Tooltip.css';
+
+export type Placement = 'bottom' | 'right' | 'left' | 'top';
interface Props {
+ children: React.ReactElement<{}>;
mouseEnterDelay?: number;
+ onShow?: () => void;
+ onHide?: () => void;
overlay: React.ReactNode;
- placement?: string;
- [attr: string]: any;
+ placement?: Placement;
+ visible?: boolean;
+}
+
+interface Measurements {
+ height: number;
+ left: number;
+ leftFix: number;
+ top: number;
+ topFix: number;
+ width: number;
}
+interface OwnState {
+ visible: boolean;
+}
+
+type State = OwnState & Partial<Measurements>;
+
+function isMeasured(state: State): state is OwnState & Measurements {
+ return state.height !== undefined;
+}
+
+const EDGE_MARGIN = 4;
+
export default function Tooltip(props: Props) {
- return <TooltipCore destroyTooltipOnHide={true} placement={props.placement} {...props} />;
+ // allows to pass `undefined` to `overlay` to avoid rendering a tooltip
+ // can useful in some cases to render the tooltip conditionally
+ return props.overlay !== undefined ? <TooltipInner {...props} /> : props.children;
}
-(Tooltip as React.StatelessComponent).defaultProps = {
- placement: 'bottom'
-};
+export class TooltipInner extends React.Component<Props, State> {
+ throttledPositionTooltip: (() => void);
+ mouseEnterInterval?: number;
+ tooltipNode?: HTMLElement | null;
+ mounted = false;
+
+ static defaultProps = {
+ mouseEnterDelay: 0.1
+ };
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ visible: props.visible !== undefined ? props.visible : false
+ };
+ this.throttledPositionTooltip = throttle(this.positionTooltip, 10);
+ }
+
+ componentDidMount() {
+ this.mounted = true;
+ if (this.props.visible === true) {
+ this.positionTooltip();
+ this.addEventListeners();
+ }
+ }
+
+ componentDidUpdate(prevProps: Props, prevState: State) {
+ if (
+ // opens
+ (this.props.visible === true && prevProps.visible === false) ||
+ (this.props.visible === undefined &&
+ this.state.visible === true &&
+ prevState.visible === false)
+ ) {
+ this.positionTooltip();
+ this.addEventListeners();
+ } else if (
+ // closes
+ (this.props.visible === false && prevProps.visible === true) ||
+ (this.props.visible === undefined &&
+ this.state.visible === false &&
+ prevState.visible === true)
+ ) {
+ this.clearPosition();
+ this.removeEventListeners();
+ }
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ this.removeEventListeners();
+ }
+
+ addEventListeners = () => {
+ window.addEventListener('resize', this.throttledPositionTooltip);
+ window.addEventListener('scroll', this.throttledPositionTooltip);
+ };
+
+ removeEventListeners = () => {
+ window.removeEventListener('resize', this.throttledPositionTooltip);
+ window.removeEventListener('scroll', this.throttledPositionTooltip);
+ };
+
+ isVisible = () => {
+ return this.props.visible !== undefined ? this.props.visible : this.state.visible;
+ };
+
+ getPlacement = (): Placement => {
+ return this.props.placement || 'bottom';
+ };
+
+ tooltipNodeRef = (node: HTMLElement | null) => {
+ this.tooltipNode = node;
+ };
+
+ positionTooltip = () => {
+ // `findDOMNode(this)` will search for the DOM node for the current component
+ // first it will find a React.Fragment (see `render`),
+ // so it will get the DOM node of the first child, i.e. DOM node of `this.props.children`
+ // docs: https://reactjs.org/docs/refs-and-the-dom.html#exposing-dom-refs-to-parent-components
+
+ // eslint-disable-next-line react/no-find-dom-node
+ const toggleNode = findDOMNode(this);
+
+ if (toggleNode && this.tooltipNode) {
+ const toggleRect = toggleNode.getBoundingClientRect();
+ const tooltipRect = this.tooltipNode.getBoundingClientRect();
+ const { width, height } = tooltipRect;
+
+ let left = 0;
+ let top = 0;
+
+ switch (this.getPlacement()) {
+ case 'bottom':
+ left = toggleRect.left + toggleRect.width / 2 - width / 2;
+ top = toggleRect.top + toggleRect.height;
+ break;
+ case 'top':
+ left = toggleRect.left + toggleRect.width / 2 - width / 2;
+ top = toggleRect.top - height;
+ break;
+ case 'right':
+ left = toggleRect.left + toggleRect.width;
+ top = toggleRect.top + toggleRect.height / 2 - height / 2;
+ break;
+ case 'left':
+ left = toggleRect.left - width;
+ top = toggleRect.top + toggleRect.height / 2 - height / 2;
+ break;
+ }
+
+ // make sure the tooltip fits in the document
+ // it may go out of the current viewport, if it has scrolls
+ const { scrollWidth, scrollHeight } = document.documentElement;
+
+ let leftFix = 0;
+ if (left < EDGE_MARGIN) {
+ leftFix = EDGE_MARGIN - left;
+ } else if (left + width > scrollWidth - EDGE_MARGIN) {
+ leftFix = scrollWidth - EDGE_MARGIN - left - width;
+ }
+
+ let topFix = 0;
+ if (top < EDGE_MARGIN) {
+ topFix = EDGE_MARGIN - top;
+ } else if (top + height > scrollHeight - EDGE_MARGIN) {
+ topFix = scrollHeight - EDGE_MARGIN - top - height;
+ }
+
+ // save width and height (and later set in `render`) to avoid resizing the tooltip element,
+ // when it's placed close to the window edge
+ const measurements: Measurements = {
+ left: window.pageXOffset + left,
+ leftFix,
+ top: window.pageYOffset + top,
+ topFix,
+ width,
+ height
+ };
+ this.setState(measurements);
+ }
+ };
+
+ clearPosition = () => {
+ this.setState({
+ left: undefined,
+ leftFix: undefined,
+ top: undefined,
+ topFix: undefined,
+ width: undefined,
+ height: undefined
+ });
+ };
+
+ handleMouseEnter = () => {
+ this.mouseEnterInterval = window.setTimeout(() => {
+ if (this.mounted) {
+ if (this.props.visible === undefined) {
+ this.setState({ visible: true });
+ }
+ }
+ }, (this.props.mouseEnterDelay || 0) * 1000);
+
+ if (this.props.onShow) {
+ this.props.onShow();
+ }
+ };
+
+ handleMouseLeave = () => {
+ if (this.mouseEnterInterval !== undefined) {
+ window.clearInterval(this.mouseEnterInterval);
+ this.mouseEnterInterval = undefined;
+ }
+ if (this.props.visible === undefined) {
+ this.setState({ visible: false });
+ }
+
+ if (this.props.onHide) {
+ this.props.onHide();
+ }
+ };
+
+ render() {
+ return (
+ <>
+ {React.cloneElement(this.props.children, {
+ onMouseEnter: this.handleMouseEnter,
+ onMouseLeave: this.handleMouseLeave
+ })}
+ {this.isVisible() && (
+ <TooltipPortal>
+ <div
+ className={`tooltip ${this.getPlacement()}`}
+ ref={this.tooltipNodeRef}
+ style={
+ isMeasured(this.state)
+ ? {
+ left: this.state.left + this.state.leftFix,
+ top: this.state.top + this.state.topFix,
+ width: this.state.width,
+ height: this.state.height
+ }
+ : undefined
+ }>
+ <div className="tooltip-inner">{this.props.overlay}</div>
+ <div
+ className="tooltip-arrow"
+ style={
+ isMeasured(this.state)
+ ? { marginLeft: -this.state.leftFix, marginTop: -this.state.topFix }
+ : undefined
+ }
+ />
+ </div>
+ </TooltipPortal>
+ )}
+ </>
+ );
+ }
+}
+
+class TooltipPortal extends React.Component {
+ el: HTMLElement;
+
+ constructor(props: {}) {
+ super(props);
+ this.el = document.createElement('div');
+ }
+
+ componentDidMount() {
+ document.body.appendChild(this.el);
+ }
+
+ componentWillUnmount() {
+ document.body.removeChild(this.el);
+ }
+
+ render() {
+ return createPortal(this.props.children, this.el);
+ }
+}
expect(wrapper).toMatchSnapshot();
jest.runAllTimers();
wrapper.update();
- expect(wrapper.find('Tooltip')).toHaveLength(0);
+ expect(wrapper).toMatchSnapshot();
});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import Tooltip, { TooltipInner } from '../Tooltip';
+
+jest.useFakeTimers();
+
+it('should render', () => {
+ expect(
+ shallow(
+ <TooltipInner overlay={<span id="overlay" />} visible={false}>
+ <div id="tooltip" />
+ </TooltipInner>
+ )
+ ).toMatchSnapshot();
+ expect(
+ shallow(
+ <TooltipInner overlay={<span id="overlay" />} visible={true}>
+ <div id="tooltip" />
+ </TooltipInner>,
+ { disableLifecycleMethods: true }
+ )
+ ).toMatchSnapshot();
+});
+
+it('should open & close', () => {
+ const onShow = jest.fn();
+ const onHide = jest.fn();
+ const wrapper = shallow(
+ <TooltipInner onHide={onHide} onShow={onShow} overlay={<span id="overlay" />}>
+ <div id="tooltip" />
+ </TooltipInner>
+ );
+ wrapper.find('#tooltip').simulate('mouseenter');
+ jest.runOnlyPendingTimers();
+ wrapper.update();
+ expect(wrapper).toMatchSnapshot();
+ expect(onShow).toBeCalled();
+
+ wrapper.find('#tooltip').simulate('mouseleave');
+ expect(wrapper).toMatchSnapshot();
+ expect(onHide).toBeCalled();
+});
+
+it('should not render tooltip without overlay', () => {
+ const wrapper = shallow(
+ <Tooltip overlay={undefined}>
+ <div id="tooltip" />
+ </Tooltip>
+ );
+ expect(wrapper.type()).toBe('div');
+});
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should display correctly 1`] = `
-<Button
- className="js-copy-to-clipboard no-select"
- data-clipboard-text="foo"
- innerRef={[Function]}
+<Tooltip
+ overlay="copied_action"
+ visible={false}
>
- copy
-</Button>
+ <Button
+ className="js-copy-to-clipboard no-select"
+ data-clipboard-text="foo"
+ innerRef={[Function]}
+ >
+ copy
+ </Button>
+</Tooltip>
`;
exports[`should display correctly 2`] = `
<Tooltip
- defaultVisible={true}
overlay="copied_action"
- placement="bottom"
- trigger="manual"
+ visible={true}
+>
+ <Button
+ className="js-copy-to-clipboard no-select"
+ data-clipboard-text="foo"
+ innerRef={[Function]}
+ >
+ copy
+ </Button>
+</Tooltip>
+`;
+
+exports[`should display correctly 3`] = `
+<Tooltip
+ overlay="copied_action"
+ visible={false}
>
<Button
className="js-copy-to-clipboard no-select"
exports[`should render favorite 1`] = `
<Tooltip
overlay="favorite.current.TRK"
- placement="left"
>
<a
className="display-inline-block link-no-underline"
exports[`should render not favorite 1`] = `
<Tooltip
overlay="favorite.check.TRK"
- placement="left"
>
<a
className="display-inline-block link-no-underline"
exports[`should render checked 1`] = `
<Tooltip
overlay="homepage.current"
- placement="left"
>
<span
className="display-inline-block"
exports[`should render unchecked 1`] = `
<Tooltip
overlay="homepage.check"
- placement="left"
>
<a
className="link-no-underline display-inline-block"
/>
<Tooltip
overlay="foo"
- placement="bottom"
>
<label
htmlFor="sample__one"
/>
<Tooltip
overlay="bar"
- placement="bottom"
>
<label
htmlFor="sample__two"
type="radio"
value="one"
/>
- <label
- htmlFor="sample__one"
- >
- first
- </label>
+ <Tooltip>
+ <label
+ htmlFor="sample__one"
+ >
+ first
+ </label>
+ </Tooltip>
</li>
<li
key="two"
type="radio"
value="two"
/>
- <label
- htmlFor="sample__two"
- >
- second
- </label>
+ <Tooltip>
+ <label
+ htmlFor="sample__two"
+ >
+ second
+ </label>
+ </Tooltip>
</li>
</ul>
`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should open & close 1`] = `
+<React.Fragment>
+ <div
+ id="tooltip"
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ />
+ <TooltipPortal>
+ <div
+ className="tooltip bottom"
+ >
+ <div
+ className="tooltip-inner"
+ >
+ <span
+ id="overlay"
+ />
+ </div>
+ <div
+ className="tooltip-arrow"
+ />
+ </div>
+ </TooltipPortal>
+</React.Fragment>
+`;
+
+exports[`should open & close 2`] = `
+<React.Fragment>
+ <div
+ id="tooltip"
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ />
+</React.Fragment>
+`;
+
+exports[`should render 1`] = `
+<React.Fragment>
+ <div
+ id="tooltip"
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ />
+</React.Fragment>
+`;
+
+exports[`should render 2`] = `
+<React.Fragment>
+ <div
+ id="tooltip"
+ onMouseEnter={[Function]}
+ onMouseLeave={[Function]}
+ />
+ <TooltipPortal>
+ <div
+ className="tooltip bottom"
+ >
+ <div
+ className="tooltip-inner"
+ >
+ <span
+ id="overlay"
+ />
+ </div>
+ <div
+ className="tooltip-arrow"
+ />
+ </div>
+ </TooltipPortal>
+</React.Fragment>
+`;
return null;
}
return (
- <Tooltip overlay={this.props.helper} placement="right">
+ <Tooltip overlay={this.props.helper}>
<span>
<HelpIcon className="spacer-left text-info" />
</span>
interface Props {
className?: string;
date: Date | string | number;
- placement?: string;
}
-export default function DateTooltipFormatter({ className, date, placement }: Props) {
+export default function DateTooltipFormatter({ className, date }: Props) {
const parsedDate = parseDate(date);
return (
<DateFormatter date={parsedDate} long={true}>
{formattedDate => (
- <Tooltip
- overlay={<DateTimeFormatter date={parsedDate} />}
- placement={placement}
- mouseEnterDelay={0.5}>
+ <Tooltip mouseEnterDelay={0.5} overlay={<DateTimeFormatter date={parsedDate} />}>
<time className={className} dateTime={parsedDate.toISOString()}>
{formattedDate}
</time>
interface Props {
className?: string;
date: Date | string | number;
- placement?: string;
}
-export default function TimeTooltipFormatter({ className, date, placement }: Props) {
+export default function TimeTooltipFormatter({ className, date }: Props) {
const parsedDate = parseDate(date);
return (
<TimeFormatter date={parsedDate} long={false}>
{formattedTime => (
- <Tooltip
- overlay={<TimeFormatter date={parsedDate} long={true} />}
- placement={placement}
- mouseEnterDelay={0.5}>
+ <Tooltip mouseEnterDelay={0.5} overlay={<TimeFormatter date={parsedDate} long={true} />}>
<time className={className} dateTime={parsedDate.toISOString()}>
{formattedTime}
</time>
togglePopup={this.toggleChangelog}
popup={<ChangelogPopup issue={this.props.issue} onFail={this.props.onFail} />}>
<Tooltip
- overlay={<DateTimeFormatter date={this.props.creationDate} />}
- placement="left"
- mouseEnterDelay={0.5}>
+ mouseEnterDelay={0.5}
+ overlay={<DateTimeFormatter date={this.props.creationDate} />}>
<button
className="button-link issue-action issue-action-with-options js-issue-show-changelog"
onClick={this.handleClick}>
overlay={translateWithParameters(
'issue.this_issue_involves_x_code_locations',
formatMeasure(locationsCount)
- )}
- placement="left">
+ )}>
<LocationIndex>{locationsCount}</LocationIndex>
</Tooltip>
);
date="2017-03-01T09:36:01+0100"
/>
}
- placement="left"
>
<button
className="button-link issue-action issue-action-with-options js-issue-show-changelog"
date="2017-03-01T09:36:01+0100"
/>
}
- placement="left"
>
<button
className="button-link issue-action issue-action-with-options js-issue-show-changelog"
exports[`renders known RATING 1`] = `
<Tooltip
overlay="tooltip"
- placement="bottom"
>
<span>
<Rating
"lib": ["es2017", "dom"],
"module": "esnext",
"moduleResolution": "node",
- "typeRoots": ["./src/main/js/typings", "./node_modules/@types"],
"sourceMap": true
},
"include": ["./src/main/js/**/*"]
version "5.5.3"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.3.tgz#f473dd47e0277a08e28e9bec5aeeb04751f0b8c9"
-add-dom-event-listener@1.x:
- version "1.0.2"
- resolved "https://registry.yarnpkg.com/add-dom-event-listener/-/add-dom-event-listener-1.0.2.tgz#8faed2c41008721cf111da1d30d995b85be42bed"
- dependencies:
- object-assign "4.x"
-
address@1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/address/-/address-1.0.2.tgz#480081e82b587ba319459fef512f516fe03d58af"
core-js "^2.4.0"
regenerator-runtime "^0.10.0"
-babel-runtime@6.x, babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
+babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0, babel-runtime@^6.9.2:
version "6.26.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe"
dependencies:
version "1.0.1"
resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
-component-classes@^1.2.5:
- version "1.2.6"
- resolved "https://registry.yarnpkg.com/component-classes/-/component-classes-1.2.6.tgz#c642394c3618a4d8b0b8919efccbbd930e5cd691"
- dependencies:
- component-indexof "0.0.3"
-
component-emitter@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6"
-component-indexof@0.0.3:
- version "0.0.3"
- resolved "https://registry.yarnpkg.com/component-indexof/-/component-indexof-0.0.3.tgz#11d091312239eb8f32c8f25ae9cb002ffe8d3c24"
-
compressible@~2.0.11:
version "2.0.12"
resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.12.tgz#c59a5c99db76767e9876500e271ef63b3493bd66"
safe-buffer "^5.0.1"
sha.js "^2.4.8"
-create-react-class@15.6.2, create-react-class@15.x, create-react-class@^15.5.1, create-react-class@^15.5.2:
+create-react-class@15.6.2, create-react-class@^15.5.1, create-react-class@^15.5.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.2.tgz#cf1ed15f12aad7f14ef5f2dfe05e6c42f91ef02a"
dependencies:
public-encrypt "^4.0.0"
randombytes "^2.0.0"
-css-animation@^1.3.2:
- version "1.4.1"
- resolved "https://registry.yarnpkg.com/css-animation/-/css-animation-1.4.1.tgz#5b8813125de0fbbbb0bbe1b472ae84221469b7a8"
- dependencies:
- babel-runtime "6.x"
- component-classes "^1.2.5"
-
css-color-names@0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
dependencies:
esutils "^2.0.2"
-dom-align@1.x:
- version "1.6.5"
- resolved "https://registry.yarnpkg.com/dom-align/-/dom-align-1.6.5.tgz#48890ee37563dd43d3b580b75cfb79a6ac8fa004"
-
dom-converter@~0.1:
version "0.1.4"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.1.4.tgz#a45ef5727b890c9bffe6d7c876e7b19cb0e17f3b"
version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.4.tgz#dcc1d7552e150a0640073ba9cb31d70f032950e7"
-lodash._getnative@^3.0.0:
- version "3.9.1"
- resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"
-
lodash.camelcase@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
-lodash.isarguments@^3.0.0:
- version "3.1.0"
- resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a"
-
-lodash.isarray@^3.0.0:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/lodash.isarray/-/lodash.isarray-3.0.4.tgz#79e4eb88c36a8122af86f844aa9bcd851b5fbb55"
-
lodash.isequal@4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
-lodash.keys@^3.1.2:
- version "3.1.2"
- resolved "https://registry.yarnpkg.com/lodash.keys/-/lodash.keys-3.1.2.tgz#4dbc0472b156be50a0b286855d1bd0b0c656098a"
- dependencies:
- lodash._getnative "^3.0.0"
- lodash.isarguments "^3.0.0"
- lodash.isarray "^3.0.0"
-
lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
version "0.8.2"
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
-object-assign@4.x, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
+object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
loose-envify "^1.3.1"
object-assign "^4.1.1"
-prop-types@15.x, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1:
+prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1:
version "15.6.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca"
dependencies:
iconv-lite "0.4.19"
unpipe "1.0.0"
-rc-align@2.x:
- version "2.3.4"
- resolved "https://registry.yarnpkg.com/rc-align/-/rc-align-2.3.4.tgz#d83bdab7560f0142e72a3de1d495dab6ba225249"
- dependencies:
- dom-align "1.x"
- prop-types "^15.5.8"
- rc-util "4.x"
-
-rc-animate@2.x:
- version "2.4.1"
- resolved "https://registry.yarnpkg.com/rc-animate/-/rc-animate-2.4.1.tgz#df3e0f56fe106afe4bf52ff408ced241c5178919"
- dependencies:
- babel-runtime "6.x"
- css-animation "^1.3.2"
- prop-types "15.x"
-
-rc-tooltip@3.5.0:
- version "3.5.0"
- resolved "https://registry.yarnpkg.com/rc-tooltip/-/rc-tooltip-3.5.0.tgz#f73e835ae174ecfa81cddda1b1fa318361f57dc1"
- dependencies:
- babel-runtime "6.x"
- prop-types "^15.5.8"
- rc-trigger "1.x"
-
-rc-trigger@1.x:
- version "1.11.5"
- resolved "https://registry.yarnpkg.com/rc-trigger/-/rc-trigger-1.11.5.tgz#f88f9f84e0e79f8e0ef1c8d1bf8ac2208b715620"
- dependencies:
- babel-runtime "6.x"
- create-react-class "15.x"
- prop-types "15.x"
- rc-align "2.x"
- rc-animate "2.x"
- rc-util "4.x"
-
-rc-util@4.x:
- version "4.1.1"
- resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.1.1.tgz#9350e9c00368b50cd8fbd91775cc1cf9e2ca72b2"
- dependencies:
- add-dom-event-listener "1.x"
- babel-runtime "6.x"
- prop-types "^15.5.10"
- shallowequal "^0.2.2"
-
rc@^1.1.7:
version "1.2.2"
resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.2.tgz#d8ce9cb57e8d64d9c7badd9876c7c34cbe3c7077"
inherits "^2.0.1"
safe-buffer "^5.0.1"
-shallowequal@^0.2.2:
- version "0.2.2"
- resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-0.2.2.tgz#1e32fd5bcab6ad688a4812cb0cc04efc75c7014e"
- dependencies:
- lodash.keys "^3.1.2"
-
shallowequal@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.0.2.tgz#1561dbdefb8c01408100319085764da3fcf83f8f"