]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9697 Ensure tooltips are always placed correctly (#77)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 4 Apr 2018 06:43:42 +0000 (08:43 +0200)
committerJanos Gyerik <janos.gyerik@sonarsource.com>
Wed, 4 Apr 2018 12:40:51 +0000 (14:40 +0200)
87 files changed:
server/sonar-web/package.json
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranch-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenu-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMeta-test.tsx.snap
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.tsx
server/sonar-web/src/main/js/app/styles/sonar.css
server/sonar-web/src/main/js/apps/account/projects/ProjectCard.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap
server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsIssues.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
server/sonar-web/src/main/js/apps/component-measures/components/Breadcrumb.js
server/sonar-web/src/main/js/apps/component-measures/components/LeakPeriodLegend.js
server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/Breadcrumb-test.js.snap
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/LeakPeriodLegend-test.js.snap
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/MeasureHeader-test.js.snap
server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueLocationBadge-test.tsx.snap
server/sonar-web/src/main/js/apps/marketplace/components/__tests__/__snapshots__/PluginLicense-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.tsx
server/sonar-web/src/main/js/apps/overview/components/LeakPeriodLegend.js
server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/events/Analysis.tsx
server/sonar-web/src/main/js/apps/overview/events/Event.tsx
server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Analysis-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/events/__tests__/__snapshots__/Event-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/main/enhance.tsx
server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/QualityGate-test.js.snap
server/sonar-web/src/main/js/apps/projectActivity/components/GraphsLegendCustom.js
server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysis.js
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/GraphsLegendCustom-test.js.snap
server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.js.snap
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLanguages.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLanguages-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardQualityGate-test.tsx.snap
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.tsx.snap
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/Search-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-gates/components/BuiltInQualityGateBadge.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/BuiltInQualityProfileBadge.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesDeprecatedWarning-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRulesSonarWayComparison-test.tsx.snap
server/sonar-web/src/main/js/apps/system/components/info-items/HealthItem.tsx
server/sonar-web/src/main/js/apps/system/components/info-items/__tests__/__snapshots__/HealthItem-test.tsx.snap
server/sonar-web/src/main/js/apps/web-api/components/Search.tsx
server/sonar-web/src/main/js/apps/web-api/components/__tests__/__snapshots__/Search-test.tsx.snap
server/sonar-web/src/main/js/apps/webhooks/components/PageActions.tsx
server/sonar-web/src/main/js/apps/webhooks/components/__tests__/__snapshots__/PageActions-test.tsx.snap
server/sonar-web/src/main/js/components/charts/BubbleChart.js
server/sonar-web/src/main/js/components/charts/Histogram.tsx
server/sonar-web/src/main/js/components/charts/TreeMapRect.js
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/Histogram-test.tsx.snap
server/sonar-web/src/main/js/components/charts/bar-chart.js
server/sonar-web/src/main/js/components/common/BranchStatus.tsx
server/sonar-web/src/main/js/components/common/CodeSnippet.tsx
server/sonar-web/src/main/js/components/common/PrivateBadge.tsx
server/sonar-web/src/main/js/components/common/SelectListItem.js
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/BranchStatus-test.tsx.snap
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/CodeSnippet-test.tsx.snap
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/PrivateBadge-test.tsx.snap
server/sonar-web/src/main/js/components/common/__tests__/__snapshots__/SelectListItem-test.js.snap
server/sonar-web/src/main/js/components/controls/ClipboardButton.tsx
server/sonar-web/src/main/js/components/controls/FavoriteBase.tsx
server/sonar-web/src/main/js/components/controls/HomePageSelect.tsx
server/sonar-web/src/main/js/components/controls/RadioToggle.tsx
server/sonar-web/src/main/js/components/controls/Tooltip.css [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/Tooltip.tsx
server/sonar-web/src/main/js/components/controls/__tests__/ClipboardButton-test.tsx
server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/ClipboardButton-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/FavoriteBase-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/HomePageSelect-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/RadioToggle-test.tsx.snap
server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/facet/FacetHeader.tsx
server/sonar-web/src/main/js/components/intl/DateTooltipFormatter.tsx
server/sonar-web/src/main/js/components/intl/TimeTooltipFormatter.tsx
server/sonar-web/src/main/js/components/issue/components/IssueChangelog.js
server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.js
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.js.snap
server/sonar-web/src/main/js/components/measure/__tests__/__snapshots__/Measure-test.tsx.snap
server/sonar-web/tsconfig.json
server/sonar-web/yarn.lock

index 6c553106d10093df6ee6aa715430747bc9291571..eadf694d63a7ba62e0578be35c23cbc5f847725c 100644 (file)
@@ -21,7 +21,6 @@
     "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",
index 498d9568f8cb49a1dd0ef16d9c12d278b7c7e3b1..42bc367a2742f5d4db145ad4024b50a9cd5427d7 100644 (file)
@@ -21,7 +21,6 @@ exports[`renders main branch 1`] = `
     <Tooltip
       mouseEnterDelay={1}
       overlay="master"
-      placement="bottom"
     >
       <span
         className="text-limited text-top"
@@ -101,7 +100,6 @@ exports[`renders pull request 1`] = `
     <Tooltip
       mouseEnterDelay={1}
       overlay="1234 â€“ Feature PR"
-      placement="bottom"
     >
       <span
         className="text-limited text-top"
@@ -163,7 +161,6 @@ exports[`renders short-living branch 1`] = `
     <Tooltip
       mouseEnterDelay={1}
       overlay="foo"
-      placement="bottom"
     >
       <span
         className="text-limited text-top"
index b30b921ae6b947d1cc72f537833e9b0ca93261d9..a8c10d0f990335ba9bdf36dee30f5c87dba38157 100644 (file)
@@ -83,7 +83,6 @@ exports[`renders list 1`] = `
         branches.orphan_branches
         <Tooltip
           overlay="branches.orphan_branches.tooltip"
-          placement="bottom"
         >
           <i
             className="icon-help spacer-left"
@@ -181,7 +180,6 @@ exports[`renders list 1`] = `
         branches.orphan_branches
         <Tooltip
           overlay="branches.orphan_branches.tooltip"
-          placement="bottom"
         >
           <i
             className="icon-help spacer-left"
index ca18e4d86d33dd76c0a2f7b3497621c859792204..42db3b6595b50a745613943fc74d1349f6bbc8f9 100644 (file)
@@ -14,7 +14,6 @@ exports[`renders meta for long-living branch 1`] = `
   <Tooltip
     mouseEnterDelay={0.5}
     overlay="version 0.0.1"
-    placement="bottom"
   >
     <div
       className="spacer-left text-limited"
index 75b7773d1e004ebe694270033062c23c5dcb5bea..84d8cde83054f7997a2bc715bed447ad4f194d13 100644 (file)
@@ -110,18 +110,9 @@ class GlobalNav extends React.PureComponent<Props, State> {
     }, 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} />
@@ -129,15 +120,22 @@ class GlobalNav extends React.PureComponent<Props, State> {
         <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>
@@ -146,8 +144,8 @@ class GlobalNav extends React.PureComponent<Props, State> {
           <GlobalHelp
             currentUser={this.props.currentUser}
             onClose={this.closeHelp}
-            onTutorialSelect={this.openOnboardingTutorial}
             onSonarCloud={this.props.onSonarCloud}
+            onTutorialSelect={this.openOnboardingTutorial}
           />
         )}
 
index 5b6284ebeedb171d2bd820b105dc782944123c08..9e5a247cdd1d9c1c7e810733bfef1ffa0123ebb5 100644 (file)
@@ -34,7 +34,6 @@
 @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';
index cfa10ff8e3703926562861eb1e57a8293b3efe0c..15d180785b7c4eb2d4fa12cb235dc7319f745b85 100644 (file)
@@ -39,17 +39,15 @@ export default function ProjectCard({ project }: Props) {
     <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')}
index f7c56962de9e7e6111a25b802efb6f3641d56bb4..48dea3cb94d74b3073fee4aa84c8d3290f27be67 100644 (file)
@@ -14,7 +14,6 @@ exports[`opens form 1`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
@@ -38,7 +37,6 @@ exports[`opens form 2`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
@@ -79,7 +77,6 @@ exports[`renders 2`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
@@ -93,7 +90,6 @@ exports[`renders 3`] = `
 <div>
   <Tooltip
     overlay="background_tasks.number_of_workers.warning"
-    placement="bottom"
   >
     <span>
       <AlertWarnIcon
@@ -114,7 +110,6 @@ exports[`renders 3`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
@@ -128,7 +123,6 @@ exports[`renders 4`] = `
 <div>
   <Tooltip
     overlay="background_tasks.number_of_workers.warning"
-    placement="bottom"
   >
     <span>
       <AlertWarnIcon
@@ -184,7 +178,6 @@ exports[`updates worker count 1`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
@@ -202,7 +195,6 @@ exports[`updates worker count 2`] = `
 <div>
   <Tooltip
     overlay="background_tasks.number_of_workers.warning"
-    placement="bottom"
   >
     <span>
       <AlertWarnIcon
@@ -223,7 +215,6 @@ exports[`updates worker count 2`] = `
   </span>
   <Tooltip
     overlay="background_tasks.change_number_of_workers"
-    placement="bottom"
   >
     <EditButton
       className="js-edit button-small spacer-left"
index 97fa22a04028ffc79ce61300084350e32f2dfba1..9e3f2a258ecd61b3a1b6534dd5940814ae34dc50 100644 (file)
@@ -207,9 +207,11 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
             ) : (
               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"
index 60ccb04f82cb4eb0ed0db7c31b9dd41d8409917c..9eebdbf7771fd6349b68b92f358a58624d2fd500 100644 (file)
@@ -124,9 +124,7 @@ export default class RuleDetailsIssues extends React.PureComponent<Props, State>
     }
 
     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>
     );
   };
 
index 1bb277e6df204bf2b71c62c4904c9df15eb7ebc4..9a406f659f7c06aeb99e3804ef54db732c55efda 100644 (file)
@@ -150,7 +150,7 @@ export default class RuleListItem extends React.PureComponent<Props> {
         )}
       </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>
index 70aaa4266955400fd51bc2ab1bc9cc466ddb291b..78200b351b1e8ed2e4ac3ca9c8e54f1942896857 100644 (file)
@@ -55,13 +55,9 @@ export default class Breadcrumb extends React.PureComponent {
 
     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>
     );
index 03790a9888a2858c74efc281c882d4b766a7e8ef..263b32f79fcc6f0c4380cb17058d1025ef2fa1db 100644 (file)
@@ -57,9 +57,5 @@ export default function LeakPeriodLegend({ className, component, period } /*: Pr
       <DateFormatter date={date} long={true} />
     </div>
   );
-  return (
-    <Tooltip placement="left" overlay={tooltip}>
-      {label}
-    </Tooltip>
-  );
+  return <Tooltip overlay={tooltip}>{label}</Tooltip>;
 }
index 1e247a65c21eb4fac4ee95617d469ecfcd9c3728..a16978f8c5d02c775e5c8bfbe1ec88f5bc7438a1 100644 (file)
@@ -73,9 +73,7 @@ export default function MeasureHeader(props /*: Props*/) {
           </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)}>
index e116a3a254cddf22a27b0eba6c23ed8c2874d30e..e0db087b0a31ba3ad43ef5a58d8a0687f893f5af 100644 (file)
@@ -2,12 +2,14 @@
 
 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"
   />
@@ -16,8 +18,10 @@ exports[`should correctly show a middle element 1`] = `
 
 exports[`should show the last element without clickable link 1`] = `
 <span>
-  <span>
-    Foo
-  </span>
+  <Tooltip>
+    <span>
+      Foo
+    </span>
+  </Tooltip>
 </span>
 `;
index 9ca440a270acb2a3091f2ad8076c615bb06fd62f..c9208efcfee48b1f15f441fa21574053cee7f9fc 100644 (file)
@@ -76,7 +76,6 @@ exports[`should render correctly 1`] = `
       </span>
       <Tooltip
         overlay="component_measures.show_metric_history"
-        placement="right"
       >
         <Link
           className="js-show-history spacer-left button button-small"
@@ -223,7 +222,6 @@ exports[`should work with measure without value 1`] = `
       </span>
       <Tooltip
         overlay="component_measures.show_metric_history"
-        placement="right"
       >
         <Link
           className="js-show-history spacer-left button button-small"
index 1bd1ad335f9e60cf36bc7228d71c4597d8ceadd4..b0ccd9b6dd02672ce9305023863feb4e4c2e4902 100644 (file)
@@ -4,7 +4,6 @@ exports[`should render 1`] = `
 <Tooltip
   mouseEnterDelay={0.5}
   overlay="issue.this_issue_involves_x_code_locations.7"
-  placement="bottom"
 >
   <LocationIndex
     selected={false}
index 64bc114f97a3d0389079420d170b4af44b5eb6c8..85d122c589664f8a8cf865fa3407cd901fbc974a 100644 (file)
@@ -3,7 +3,6 @@
 exports[`should display the license field 1`] = `
 <Tooltip
   overlay="SonarSource license"
-  placement="bottom"
 >
   <li
     className="little-spacer-bottom marketplace-plugin-license"
index 6045dd6871d369e0694e703ddcf9df1fbe03854a..4ff9c39f6706d687d009f90ce4bdf8a0f735d874 100644 (file)
@@ -49,8 +49,8 @@ export default class ApplicationLeakPeriodLegend extends React.Component<Props,
     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) {
@@ -81,7 +81,7 @@ export default class ApplicationLeakPeriodLegend extends React.Component<Props,
 
   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>
index 250054ccf1f8e18cc6c89bf65160b1994dcbd1e6..ac5c8f5ecf8e16b2bc40e2b6757eeeae76ef1e68 100644 (file)
@@ -98,7 +98,7 @@ export default function LeakPeriodLegend({ period } /*: { period: Period } */) {
     </DateFormatter>
   );
   return (
-    <Tooltip overlay={tooltip} placement="top">
+    <Tooltip overlay={tooltip}>
       <div className="overview-legend">
         {translateWithParameters('overview.leak_period_x', leakPeriodLabel)}
         <br />
index 42b2caeb24935f006cb03d343e146b4753b05247..f9c36d4c81c93452a906f0bdd8d35b36306a5131 100644 (file)
@@ -35,7 +35,7 @@ it('renders', async () => {
   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();
 });
index cb842597822d143fbad8714c359a16cef0ea630b..f873abb317d0a7364b6825318da110a090ac9fe3 100644 (file)
@@ -2,13 +2,12 @@
 
 exports[`renders 1`] = `
 <Tooltip
-  onVisibleChange={[Function]}
+  onShow={[Function]}
   overlay={
     <i
       className="spinner spinner-margin"
     />
   }
-  placement="bottom"
 >
   <div
     className="overview-legend  overview-legend-spaced-line"
@@ -20,7 +19,7 @@ exports[`renders 1`] = `
 
 exports[`renders 2`] = `
 <Tooltip
-  onVisibleChange={[Function]}
+  onShow={[Function]}
   overlay={
     <ul
       className="text-left"
@@ -41,7 +40,6 @@ exports[`renders 2`] = `
       </li>
     </ul>
   }
-  placement="bottom"
 >
   <div
     className="overview-legend  overview-legend-spaced-line"
index f80ce5646b88d79599df37d4480384d10cfcb05d..c873d0c598a92ec39840c4d120b610458d635b3c 100644 (file)
@@ -45,7 +45,7 @@ export default function Analysis({ analysis, ...props }: Props) {
     <li className="overview-analysis">
       <div className="small little-spacer-bottom">
         <strong>
-          <DateTooltipFormatter date={analysis.date} placement="right" />
+          <DateTooltipFormatter date={analysis.date} />
         </strong>
       </div>
 
index f90d318845aca8f02ed2e870049a2a4b56e43be1..96f7699b1d98f1b509f76fc48b449421597b2d9e 100644 (file)
@@ -29,7 +29,7 @@ interface Props {
 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>
     );
@@ -39,7 +39,7 @@ export default function Event({ event }: Props) {
     <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>
       ) : (
index a9296187266dfce71b2b502b3f53058f65e34352..7485d11303c49f0bfb24dbf2621b28847f8d25a3 100644 (file)
@@ -10,7 +10,6 @@ exports[`should sort the events with version first 1`] = `
     <strong>
       <DateTooltipFormatter
         date="2017-06-10T16:10:59+0200"
-        placement="right"
       />
     </strong>
   </div>
index f6b5bdfc0e60634d966b01353f5a5d6f00b565d0..85ed92a24ce920bddeb1ea4a875bef3e1c6734ca 100644 (file)
@@ -4,7 +4,6 @@ exports[`should render a version correctly 1`] = `
 <Tooltip
   mouseEnterDelay={0.5}
   overlay="version 6.5-SNAPSHOT"
-  placement="bottom"
 >
   <span
     className="overview-analysis-event badge"
index 2251612e2e3e8aec321c90c6b36ce67d0ccf57ed..9797387082db61ef2240b658400f2f3c88c81b47 100644 (file)
@@ -128,7 +128,7 @@ export default function enhance(ComposedComponent: React.ComponentType<ComposedP
       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}
index 13d1efdd2951f8808ca673527a944c441075374c..2b9f86b60625687f221534fd0a01955b4a153114 100644 (file)
@@ -19,7 +19,6 @@ exports[`renders message about ignored conditions 1`] = `
     overview.quality_gate.ignored_conditions
     <Tooltip
       overlay="overview.quality_gate.ignored_conditions.tooltip"
-      placement="bottom"
     >
       <span
         className="spacer-left"
index cb2a99a4ee802089035dc82932bf91584f29a75f..bead6826b4f47a45e0be844dff7814a4e57db026 100644 (file)
@@ -50,8 +50,7 @@ export default function GraphsLegendCustom({ removeMetric, series } /*: Props */
           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>
           );
index 55f34ffbc183dd99661df037a39689572df4ed5d..5324153a4e0b6830a2df6110b61175b8ee7d34f8 100644 (file)
@@ -74,7 +74,7 @@ export default class ProjectActivityAnalysis extends React.PureComponent {
         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} />
 
index b50a253ae4ce5da9241f53ad7956c48575d3cd60..755cda6620b8f9d7a6b01d18ef03f30e05929e9c 100644 (file)
@@ -31,7 +31,6 @@ exports[`should render correctly the list of series 1`] = `
   <Tooltip
     key="foo"
     overlay="project_activity.graphs.custom.metric_no_history"
-    placement="bottom"
   >
     <span
       className="spacer-left spacer-right"
index 468f7b7ddec884442c2ba7b4bd56936d3b70be6d..80c19c239d35a9c8ef2aca7ee88ba0e2b47bde5d 100644 (file)
@@ -19,7 +19,6 @@ exports[`should correctly filter analyses by category 1`] = `
       <Tooltip
         mouseEnterDelay={0.5}
         overlay="version 6.4"
-        placement="bottom"
       >
         <span
           className="badge"
@@ -99,7 +98,6 @@ exports[`should correctly filter analyses by date range 1`] = `
       <Tooltip
         mouseEnterDelay={0.5}
         overlay="version 6.5-SNAPSHOT"
-        placement="bottom"
       >
         <span
           className="badge"
@@ -179,7 +177,6 @@ exports[`should render correctly 1`] = `
       <Tooltip
         mouseEnterDelay={0.5}
         overlay="version 6.5-SNAPSHOT"
-        placement="bottom"
       >
         <span
           className="badge"
@@ -266,7 +263,6 @@ exports[`should render correctly 1`] = `
       <Tooltip
         mouseEnterDelay={0.5}
         overlay="version 6.4"
-        placement="bottom"
       >
         <span
           className="badge"
index 8ffcf21b4b438bf6ec5eee38affe1d2ffb0637dc..8d0815a9eb4144fc8b95b8db5e78d01b095eab08 100644 (file)
@@ -54,7 +54,7 @@ export default function ProjectCardLanguages({ distribution, languages }: Props)
 
   return (
     <div className="project-card-languages">
-      <Tooltip placement="bottom" overlay={tooltip}>
+      <Tooltip overlay={tooltip}>
         <span>{languagesText}</span>
       </Tooltip>
     </div>
index a6059011e22acc583222c6665df46e1a9c666954..c0328c25e07dfab1b43e1047fb7d57a1ca25a165 100644 (file)
@@ -61,9 +61,7 @@ export default function ProjectCardLeak({ organization, project }: Props) {
           </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>
index 9f7cb39bb3917b6e405f73a1a28ad76c076f6186..72d44b470f7f9c17803176d2e066691e85168914 100644 (file)
@@ -60,9 +60,7 @@ export default function ProjectCardOverall({ organization, project }: Props) {
           </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>
index 5941322b8aefb1a8b0ac1be1aee4b94817a49638..5b8486ad3ec0a5477a9e9a7d06b228507c549903 100644 (file)
@@ -111,7 +111,6 @@ exports[`should render disabled sorting options for visualizations 1`] = `
   />
   <Tooltip
     overlay="projects.sort.disabled"
-    placement="bottom"
   >
     <div
       className="projects-topbar-item disabled"
index a97f467c07def5462f4b4adee0b4047b93072277..a939231762314e0c45ac1aa8771ed4911d04ede7 100644 (file)
@@ -17,7 +17,6 @@ exports[`handles unknown languages 1`] = `
         </span>
       </span>
     }
-    placement="bottom"
   >
     <span>
       cpp, Java
@@ -43,7 +42,6 @@ exports[`handles unknown languages 2`] = `
         </span>
       </span>
     }
-    placement="bottom"
   >
     <span>
       unknown, Java
@@ -69,7 +67,6 @@ exports[`renders 1`] = `
         </span>
       </span>
     }
-    placement="bottom"
   >
     <span>
       Java, JavaScript
@@ -95,7 +92,6 @@ exports[`sorts languages 1`] = `
         </span>
       </span>
     }
-    placement="bottom"
   >
     <span>
       JavaScript, Java
index 5e2c8b7a7008497e935804b2dbd4dbc01022665b..7b80e2b212c28fadfd859a742239018e5cad95a4 100644 (file)
@@ -6,7 +6,6 @@ exports[`renders 1`] = `
 >
   <Tooltip
     overlay="overview.quality_gate_x.ERROR"
-    placement="bottom"
   >
     <div
       className="project-card-measure-inner"
index cc30f32ad4cb8a9d2fea274b7936ec16315370bc..4ec06a7a8c0777a6ac2272fdb794e0816213bd99 100644 (file)
@@ -60,7 +60,6 @@ exports[`should handle the descending sort direction 1`] = `
   />
   <Tooltip
     overlay="projects.sort_descending"
-    placement="bottom"
   >
     <ButtonIcon
       className="js-projects-sorting-invert spacer-left"
@@ -135,7 +134,6 @@ exports[`should render correctly for leak view 1`] = `
   />
   <Tooltip
     overlay="projects.sort_ascending"
-    placement="bottom"
   >
     <ButtonIcon
       className="js-projects-sorting-invert spacer-left"
@@ -210,7 +208,6 @@ exports[`should render correctly for overall view 1`] = `
   />
   <Tooltip
     overlay="projects.sort_ascending"
-    placement="bottom"
   >
     <ButtonIcon
       className="js-projects-sorting-invert spacer-left"
index 07a23973ef753fc8d46504ae8f6be4b66c516928..66cec9365f8c84ad97b72f4d1109c9f6e908b528 100644 (file)
@@ -106,7 +106,6 @@ exports[`render qualifiers filter 1`] = `
               provisioning.only_provisioned
               <Tooltip
                 overlay="provisioning.only_provisioned.tooltip"
-                placement="bottom"
               >
                 <i
                   className="spacer-left icon-help"
@@ -194,7 +193,6 @@ exports[`renders 1`] = `
               provisioning.only_provisioned
               <Tooltip
                 overlay="provisioning.only_provisioned.tooltip"
-                placement="bottom"
               >
                 <i
                   className="spacer-left icon-help"
index 14deb34dab51d07960fd59d4a727bf0c9bf7cc3d..88f976df8fbe6a8392c323edb67c2b4fe9ee9430 100644 (file)
@@ -43,5 +43,5 @@ export default function BuiltInQualityGateBadge({ className, tooltip = true }: P
     </div>
   );
 
-  return tooltip ? <Tooltip overlay={overlay}>{badge}</Tooltip> : badge;
+  return <Tooltip overlay={tooltip ? overlay : undefined}>{badge}</Tooltip>;
 }
index 0bc029857f8b41825b52fb83782c39d9089cb1eb..f493d0dc2c806475097aa2c7ecabbe8f087e1796 100644 (file)
@@ -41,11 +41,5 @@ export default function BuiltInQualityProfileBadge({ className, tooltip = true }
     </span>
   );
 
-  return tooltip ? (
-    <Tooltip overlay={overlay} placement="right">
-      {badge}
-    </Tooltip>
-  ) : (
-    badge
-  );
+  return <Tooltip overlay={tooltip ? overlay : undefined}>{badge}</Tooltip>;
 }
index 4072c84db21e7ae7708b03c4d87ad519d14ce7cd..ace1b6fe12a9293ea188571eacc48d6d46a205f1 100644 (file)
@@ -10,7 +10,6 @@ exports[`should render correctly 1`] = `
     quality_profiles.deprecated_rules
     <Tooltip
       overlay="quality_profiles.deprecated_rules_description"
-      placement="bottom"
     >
       <i
         className="icon-help spacer-left"
index 7edb44e1c23854c3d2154dd2972fbf4b57bb0a4b..4719fd50cf5d2b782f54d16832096e21e5da1158 100644 (file)
@@ -10,7 +10,6 @@ exports[`should render correctly 1`] = `
     quality_profiles.sonarway_missing_rules
     <Tooltip
       overlay="quality_profiles.sonarway_missing_rules_description"
-      placement="bottom"
     >
       <i
         className="icon-help spacer-left"
index fec291e244a8cc17ee9783a51ab007b584bff108..5bea29aae5c14195b7ff1c90a2af281fa5b048f3 100644 (file)
@@ -45,9 +45,7 @@ export default function HealthItem({ biggerHealth, className, name, health, heal
           <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>
       ) : (
index 6b2244457974e54e9c531a8aa2146b0b2adbf029..8f8a741f4b0133f47b33c24e16ac095abe8ae1f2 100644 (file)
@@ -32,7 +32,6 @@ exports[`should render correctly 1`] = `
   />
   <Tooltip
     overlay="system.current_health_of_x.Foo"
-    placement="left"
   >
     <span>
       <StatusIndicator
index 3353d58546e88f51efd8e30470da627eded9a594..1f098d804c9c2b1ec297d27f1dd5ab8d3b7a6768 100644 (file)
@@ -45,7 +45,7 @@ export default function Search(props: Props) {
         <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>
@@ -58,7 +58,7 @@ export default function Search(props: Props) {
             {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>
index 5980b970470be798ac34eae088227845242a073a..339160fb83bcdb4ec84c0336ec5d406064428d6e 100644 (file)
@@ -26,7 +26,6 @@ exports[`should render correctly 1`] = `
     </Checkbox>
     <Tooltip
       overlay="api_documentation.internal_tooltip"
-      placement="right"
     >
       <span>
         <HelpIcon
@@ -51,7 +50,6 @@ exports[`should render correctly 1`] = `
     </Checkbox>
     <Tooltip
       overlay="api_documentation.deprecation_tooltip"
-      placement="right"
     >
       <span>
         <HelpIcon
index 89b9bd37b28769beeaf18770fe93e641147a8188..49518a431df8989e7db58f0f0e937f116a8486bd 100644 (file)
@@ -60,9 +60,7 @@ export default class PageActions extends React.PureComponent<Props, State> {
   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>
       );
index 125694249eec7078506f17f84c3169c57f0d602c..f95b218e3fe5a7544188767e0c692b976317b26a 100644 (file)
@@ -6,7 +6,6 @@ exports[`should not allow to create a new webhook 1`] = `
 >
   <Tooltip
     overlay="webhooks.maximum_reached.10"
-    placement="left"
   >
     <Button
       className="js-webhook-create disabled"
index 9cb5681debf1e701f7e4154bdd99000ada901cbb..73fcce09b78dd1792d3569f034f0de190c284a08 100644 (file)
@@ -69,12 +69,10 @@ export class Bubble extends React.PureComponent {
       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
     );
   }
 }
index 96b48bb97e054b595f723e8644a9b3caa7b39af6..3695459f1583fbfbc87a00ba0e72b1fc51d4aa26 100644 (file)
@@ -40,17 +40,6 @@ type XScale = ScaleLinear<number, number>;
 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;
 
@@ -73,11 +62,12 @@ export default class Histogram extends React.PureComponent<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>
     );
   }
 
index 5c5cf79e6eb095843e742b517dfd457df61ac5b7..9cc3c2e0738a232c496f3847d70b3f230d029046 100644 (file)
@@ -112,13 +112,10 @@ export default class TreeMapRect extends React.PureComponent {
 
   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>
+    );
   }
 }
index b4cc499c2bef4b13aabab6238bd0a0b263b272c2..fbbd76666bd80f263f9f02dfa2ef066e8aa0bd91 100644 (file)
@@ -7,17 +7,21 @@ exports[`should display bubbles 1`] = `
   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>
 `;
 
@@ -28,17 +32,21 @@ exports[`should display bubbles 2`] = `
   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>
 `;
 
@@ -50,28 +58,32 @@ exports[`should render bubble links 1`] = `
   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>
 `;
 
@@ -83,28 +95,32 @@ exports[`should render bubble links 2`] = `
   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>
 `;
 
@@ -117,18 +133,22 @@ exports[`should render bubbles with click handlers 1`] = `
   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>
 `;
 
@@ -141,17 +161,21 @@ exports[`should render bubbles with click handlers 2`] = `
   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>
 `;
index 04aacd7b139aad13931d8b03ae5a053028d40a03..f52a4ea6cf340f618a0c40d104148e48ac686c4c 100644 (file)
@@ -68,15 +68,17 @@ exports[`renders with yValues 1`] = `
           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"
@@ -88,15 +90,17 @@ exports[`renders with yValues 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"
@@ -108,15 +112,17 @@ exports[`renders with yValues 1`] = `
           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>
@@ -143,15 +149,17 @@ exports[`renders with yValues and yTicks 1`] = `
           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"
@@ -172,15 +180,17 @@ exports[`renders with yValues and yTicks 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>
         <text
           className="bar-chart-tick histogram-tick"
           dx="-1em"
@@ -201,15 +211,17 @@ exports[`renders with yValues and yTicks 1`] = `
           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"
@@ -246,9 +258,7 @@ exports[`renders with yValues, yTicks and yTooltips 1`] = `
           y={10}
         />
         <Tooltip
-          key="0"
           overlay="a - 100"
-          placement="top"
         >
           <text
             className="bar-chart-tick histogram-value"
@@ -281,9 +291,7 @@ exports[`renders with yValues, yTicks and yTooltips 1`] = `
           y={28}
         />
         <Tooltip
-          key="1"
           overlay="b - 75"
-          placement="top"
         >
           <text
             className="bar-chart-tick histogram-value"
@@ -316,9 +324,7 @@ exports[`renders with yValues, yTicks and yTooltips 1`] = `
           y={46}
         />
         <Tooltip
-          key="2"
           overlay="c - 150"
-          placement="top"
         >
           <text
             className="bar-chart-tick histogram-value"
index a312cefc95a90b83801a55cc8769a2b148c2d17f..c137b2f74e900751b80d52958e70116d619543ff 100644 (file)
@@ -86,12 +86,10 @@ export const BarChart = createReactClass({
           {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>;
@@ -118,12 +116,10 @@ export const BarChart = createReactClass({
           {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>;
@@ -147,12 +143,10 @@ export const BarChart = createReactClass({
           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>;
index b2e401121124d296b46c826ce0dbb38be5845a31..4042b4ed3d13c50745fe2508979da966b385e5a6 100644 (file)
@@ -75,8 +75,7 @@ export default function BranchStatus({ branchLike, concise = false }: Props) {
             overlay={translateWithParameters(
               'branches.short_lived.quality_gate.description',
               totalIssues
-            )}
-            placement="right">
+            )}>
             <li className="spacer-left">
               <HelpIcon className="text-info" />
             </li>
index 6e2469597bc828c605e198db94a54d3d044741e2..01520de13584a55e40aa93f83ce1a351f89f72a1 100644 (file)
@@ -41,7 +41,7 @@ export default function CodeSnippet({ className, isOneLine, noCopy, snippet }: P
   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>
   );
 }
index 0a557e8379f932f9f69f6882abb091274580b74a..3fdad3d849e3d29763dc56ac99bf3cef42972590 100644 (file)
@@ -25,14 +25,11 @@ import { translate } from '../../helpers/l10n';
 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>
index 83926ea37df158b095833b3bccf07f3a6faa88bd..309564a9b5d259769ea696f07fca28a149361e4a 100644 (file)
@@ -69,14 +69,10 @@ export default class SelectListItem extends React.PureComponent {
   }
 
   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>
+    );
   }
 }
index e10d3e2826b76cc5de7dc0e70ef7c97068c399d3..369c4f0b9d247bfe87746b75272be5df0deb4e19 100644 (file)
@@ -169,7 +169,6 @@ exports[`renders status of short-living branches 4`] = `
   </li>
   <Tooltip
     overlay="branches.short_lived.quality_gate.description.1"
-    placement="right"
   >
     <li
       className="spacer-left"
index 7cae9eb805a0e17e5a8c45e8950ee12023183652..734f034202e46e35702ac9e47f8c3bd940f03540 100644 (file)
@@ -15,24 +15,38 @@ bar
     <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>
@@ -74,24 +88,38 @@ exports[`renders correctly with array snippet 1`] = `
     <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>
@@ -115,22 +143,36 @@ exports[`renders correctly with array snippet 2`] = `
     </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>
index 2ec0bd7a8918d5aa80314c45366f7392b093632f..b52275d7c3b28ffdef9b6e960bd6acce6ade5cd4 100644 (file)
@@ -3,7 +3,6 @@
 exports[`renders 1`] = `
 <Tooltip
   overlay="visibility.private.description.TRK"
-  placement="bottom"
 >
   <div
     className="outline-badge"
index 9f5258904d7a898879d79f060a58e33e6a006904..09632d1a13d7e08eb5f6aea55a1c64e749b823f5 100644 (file)
@@ -20,48 +20,60 @@ exports[`should render correctly with a tooltip 1`] = `
 `;
 
 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>
 `;
index 6969fc0271fbdc84a39bfe2b60fa099d0db16d51..ee3e74a8830db66d2868588bee0fdd1714376ef2 100644 (file)
@@ -27,7 +27,6 @@ import { translate } from '../../helpers/l10n';
 interface Props {
   className?: string;
   copyValue: string;
-  tooltipPlacement?: string;
 }
 
 interface State {
@@ -77,25 +76,15 @@ export default class ClipboardButton extends React.PureComponent<Props, 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;
   }
 }
index fd628fc376254298466ed4ec7564e2615a086d57..0d4dc401c97b2ad614bfcd3e6050cabf93cee532 100644 (file)
@@ -87,7 +87,7 @@ export default class FavoriteBase extends React.PureComponent<Props, State> {
       ? 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="#"
index cf84c84e776995552f920e6dadbebddb5e1ffc0f..3c1ec66c76aa190c6ef0afe54f2e4bb0a12cad5d 100644 (file)
@@ -59,7 +59,7 @@ class HomePageSelect extends React.PureComponent<Props> {
     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} />
index 21ff253d8981f0317b946b59dfaca3589e640245..3bc54c832858a0c8fbe0b39dc99e85e84ea27c85 100644 (file)
@@ -61,13 +61,9 @@ export default class RadioToggle extends React.PureComponent<Props> {
           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>
     );
   };
diff --git a/server/sonar-web/src/main/js/components/controls/Tooltip.css b/server/sonar-web/src/main/js/components/controls/Tooltip.css
new file mode 100644 (file)
index 0000000..c2529a0
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * 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;
+  }
+}
index c7ddb7316bcb287daa7c8a198896f4ab2ff0a9b2..a16c9c691dbe59297c02d4b12dc715e06cd90272 100644 (file)
  * 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);
+  }
+}
index a33520ea19edc49eef6c54d5f52df6c602eae468..c4094eff24aa7f49608f0876a2121252752906a3 100644 (file)
@@ -31,5 +31,5 @@ it('should display correctly', () => {
   expect(wrapper).toMatchSnapshot();
   jest.runAllTimers();
   wrapper.update();
-  expect(wrapper.find('Tooltip')).toHaveLength(0);
+  expect(wrapper).toMatchSnapshot();
 });
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx b/server/sonar-web/src/main/js/components/controls/__tests__/Tooltip-test.tsx
new file mode 100644 (file)
index 0000000..0f4bbb0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * 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');
+});
index 4ad6724747bfe4fa1b15102804c21f821b5db3f7..d25ba1092378dd13f6bf1258d91cbbdb48c17aef 100644 (file)
@@ -1,21 +1,39 @@
 // 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"
index 8186fad3a49d656c2211d171fbebf1b5bf5cb701..f24dedb75319babc9a8e9a2b4152de703ef63e8a 100644 (file)
@@ -3,7 +3,6 @@
 exports[`should render favorite 1`] = `
 <Tooltip
   overlay="favorite.current.TRK"
-  placement="left"
 >
   <a
     className="display-inline-block link-no-underline"
@@ -20,7 +19,6 @@ exports[`should render favorite 1`] = `
 exports[`should render not favorite 1`] = `
 <Tooltip
   overlay="favorite.check.TRK"
-  placement="left"
 >
   <a
     className="display-inline-block link-no-underline"
index 4ccff84cbdcfbdf34b3b7aaf77d43eaaf5abbde9..6a95b160deb6989e7815f71129ffc82bf25fb354 100644 (file)
@@ -3,7 +3,6 @@
 exports[`should render checked 1`] = `
 <Tooltip
   overlay="homepage.current"
-  placement="left"
 >
   <span
     className="display-inline-block"
@@ -18,7 +17,6 @@ exports[`should render checked 1`] = `
 exports[`should render unchecked 1`] = `
 <Tooltip
   overlay="homepage.check"
-  placement="left"
 >
   <a
     className="link-no-underline display-inline-block"
index d9c8fa8d1f9c7e0d3b189548bc16666e7cb06ae2..bfc31e1a9e68bde7c32993cc0bb423e3e5284bc0 100644 (file)
@@ -17,7 +17,6 @@ exports[`accepts advanced options fields 1`] = `
     />
     <Tooltip
       overlay="foo"
-      placement="bottom"
     >
       <label
         htmlFor="sample__one"
@@ -40,7 +39,6 @@ exports[`accepts advanced options fields 1`] = `
     />
     <Tooltip
       overlay="bar"
-      placement="bottom"
     >
       <label
         htmlFor="sample__two"
@@ -67,11 +65,13 @@ exports[`renders 1`] = `
       type="radio"
       value="one"
     />
-    <label
-      htmlFor="sample__one"
-    >
-      first
-    </label>
+    <Tooltip>
+      <label
+        htmlFor="sample__one"
+      >
+        first
+      </label>
+    </Tooltip>
   </li>
   <li
     key="two"
@@ -84,11 +84,13 @@ exports[`renders 1`] = `
       type="radio"
       value="two"
     />
-    <label
-      htmlFor="sample__two"
-    >
-      second
-    </label>
+    <Tooltip>
+      <label
+        htmlFor="sample__two"
+      >
+        second
+      </label>
+    </Tooltip>
   </li>
 </ul>
 `;
diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap b/server/sonar-web/src/main/js/components/controls/__tests__/__snapshots__/Tooltip-test.tsx.snap
new file mode 100644 (file)
index 0000000..f7e3657
--- /dev/null
@@ -0,0 +1,73 @@
+// 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>
+`;
index f1394dba8988a92952d6aea7aa9467766d9c4ace..00b6803c1247324890c57f09483df8c6eaec3d98 100644 (file)
@@ -47,7 +47,7 @@ export default class FacetHeader extends React.PureComponent<Props> {
       return null;
     }
     return (
-      <Tooltip overlay={this.props.helper} placement="right">
+      <Tooltip overlay={this.props.helper}>
         <span>
           <HelpIcon className="spacer-left text-info" />
         </span>
index 6cc226349dd94acb90128032d8c770c42d0f45d6..ff16ad68432b60bc054f5aa8de7b6a6d2f9599da 100644 (file)
@@ -26,18 +26,14 @@ import { parseDate } from '../../helpers/dates';
 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>
index 0c16f785b8a82e28ee0c24aeb47a552fc0c1f836..e73ba24ee7c52a14716a1f1b065634acf2edcd83 100644 (file)
@@ -25,18 +25,14 @@ import { parseDate } from '../../helpers/dates';
 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>
index e009ef1035859a3343e44ee6e0ff793267a0d250..43f5adc2d919a178bff8c5028c0a1df50db7cc26 100644 (file)
@@ -56,9 +56,8 @@ export default class IssueChangelog extends React.PureComponent {
         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}>
index 13de79da2039f263b917e7cedb22875743a485b0..3dbe2919e6e1c2e86f499b3b75621b11089533ef 100644 (file)
@@ -60,8 +60,7 @@ export default function IssueTitleBar(props /*: Props */) {
       overlay={translateWithParameters(
         'issue.this_issue_involves_x_code_locations',
         formatMeasure(locationsCount)
-      )}
-      placement="left">
+      )}>
       <LocationIndex>{locationsCount}</LocationIndex>
     </Tooltip>
   );
index 121ff82e65ccca4b618e24852b979a5632d8ab4f..97f8c58d32acdfc6f76e28fd9e186e39dc70e975 100644 (file)
@@ -34,7 +34,6 @@ exports[`should open the popup when the button is clicked 2`] = `
         date="2017-03-01T09:36:01+0100"
       />
     }
-    placement="left"
   >
     <button
       className="button-link issue-action issue-action-with-options js-issue-show-changelog"
@@ -80,7 +79,6 @@ exports[`should render correctly 1`] = `
         date="2017-03-01T09:36:01+0100"
       />
     }
-    placement="left"
   >
     <button
       className="button-link issue-action issue-action-with-options js-issue-show-changelog"
index c9ca3a03a7ac6f7e78312aea79509b5929e13cfd..437ca28a87b549b53b3dda6f269ed4f59f23083d 100644 (file)
@@ -9,7 +9,6 @@ exports[`renders LEVEL 1`] = `
 exports[`renders known RATING 1`] = `
 <Tooltip
   overlay="tooltip"
-  placement="bottom"
 >
   <span>
     <Rating
index b26b72ace21ee5fd653b4715f6225cdc3006bd34..4e877399ac58dce749d921e1f2b4c3987046a005 100644 (file)
@@ -12,7 +12,6 @@
     "lib": ["es2017", "dom"],
     "module": "esnext",
     "moduleResolution": "node",
-    "typeRoots": ["./src/main/js/typings", "./node_modules/@types"],
     "sourceMap": true
   },
   "include": ["./src/main/js/**/*"]
index 1bb9573b7e531e317c48fee79db37a28912fdb81..dd0142459a64387d7e7d792a81aecfbd0503b072 100644 (file)
@@ -155,12 +155,6 @@ acorn@^5.0.0, acorn@^5.1.2, acorn@^5.3.0, acorn@^5.4.0:
   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"
@@ -1070,7 +1064,7 @@ babel-runtime@6.23.0:
     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:
@@ -1743,20 +1737,10 @@ commondir@^1.0.1:
   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"
@@ -1942,7 +1926,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
     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:
@@ -1985,13 +1969,6 @@ crypto-browserify@^3.11.0:
     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"
@@ -2395,10 +2372,6 @@ doctrine@^2.0.2, doctrine@^2.1.0:
   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"
@@ -4837,10 +4810,6 @@ lodash-es@^4.2.0, lodash-es@^4.2.1:
   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"
@@ -4857,26 +4826,10 @@ lodash.flattendeep@^4.4.0:
   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"
@@ -5422,7 +5375,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
   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"
 
@@ -6181,7 +6134,7 @@ prop-types@15.6.0:
     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:
@@ -6319,50 +6272,6 @@ raw-body@2.3.2:
     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"
@@ -7114,12 +7023,6 @@ sha.js@^2.4.0, sha.js@^2.4.8:
     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"