]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9792 Fix sidebar position with a notification
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Fri, 29 Sep 2017 13:03:12 +0000 (15:03 +0200)
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>
Fri, 29 Sep 2017 15:09:48 +0000 (17:09 +0200)
server/sonar-web/src/main/js/apps/component-measures/components/App.js
server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap
server/sonar-web/src/main/js/apps/issues/components/App.js
server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js
server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx
server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx [new file with mode: 0644]

index a9fc88958ad7ecb5b6b22468cb3cfcd9a3156592..a33340530ea1d96b1df0b45122c8b0a223670dc5 100644 (file)
@@ -24,6 +24,7 @@ import key from 'keymaster';
 import MeasureContentContainer from './MeasureContentContainer';
 import MeasureOverviewContainer from './MeasureOverviewContainer';
 import Sidebar from '../sidebar/Sidebar';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import { hasBubbleChart, parseQuery, serializeQuery } from '../utils';
 import { getBranchName } from '../../../helpers/branches';
 import { translate } from '../../../helpers/l10n';
@@ -148,19 +149,21 @@ export default class App extends React.PureComponent {
       <div className="layout-page" id="component-measures">
         <Helmet title={translate('layout.measures')} />
 
-        <div className="layout-page-side-outer">
-          <div className="layout-page-side" style={{ top: 95 }}>
-            <div className="layout-page-side-inner">
-              <div className="layout-page-filters">
-                <Sidebar
-                  measures={this.state.measures}
-                  selectedMetric={query.metric}
-                  updateQuery={this.updateQuery}
-                />
+        <ScreenPositionHelper className="layout-page-side-outer">
+          {({ top }) => (
+            <div className="layout-page-side" style={{ top }}>
+              <div className="layout-page-side-inner">
+                <div className="layout-page-filters">
+                  <Sidebar
+                    measures={this.state.measures}
+                    selectedMetric={query.metric}
+                    updateQuery={this.updateQuery}
+                  />
+                </div>
               </div>
             </div>
-          </div>
-        </div>
+          )}
+        </ScreenPositionHelper>
 
         {metric != null && (
           <MeasureContentContainer
index 85d80ad9d74eaf3f7f6beff205eef57ac89eea43..24354987bd69a7d6eab58423287d2c516190694d 100644 (file)
@@ -9,32 +9,9 @@ exports[`should render correctly 1`] = `
     encodeSpecialCharacters={true}
     title="layout.measures"
   />
-  <div
+  <ScreenPositionHelper
     className="layout-page-side-outer"
-  >
-    <div
-      className="layout-page-side"
-      style={
-        Object {
-          "top": 95,
-        }
-      }
-    >
-      <div
-        className="layout-page-side-inner"
-      >
-        <div
-          className="layout-page-filters"
-        >
-          <Sidebar
-            measures={Array []}
-            selectedMetric="coverage"
-            updateQuery={[Function]}
-          />
-        </div>
-      </div>
-    </div>
-  </div>
+  />
   <MeasureContentContainer
     className="layout-page-main"
     fetchMeasures={[Function]}
index 888ce25b46811146ea230f3fad5ca2037a6686f5..f5f83df90e752fa113d61ea6263b0c227bec01d9 100644 (file)
@@ -55,6 +55,7 @@ import {
 } from '../utils'; */
 import ListFooter from '../../../components/controls/ListFooter';
 import EmptySearch from '../../../components/common/EmptySearch';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import { getBranchName } from '../../../helpers/branches';
 import { translate, translateWithParameters } from '../../../helpers/l10n';
 import { scrollToElement } from '../../../helpers/scrolling';
@@ -792,16 +793,16 @@ export default class App extends React.PureComponent {
   }
 
   renderSide(openIssue /*: ?Issue */) {
-    const top = this.props.component || this.props.organization ? 95 : 30;
-
     return (
-      <div className="layout-page-side-outer">
-        <div className="layout-page-side" style={{ top }}>
-          <div className="layout-page-side-inner">
-            {openIssue == null ? this.renderFacets() : this.renderConciseIssuesList()}
+      <ScreenPositionHelper className="layout-page-side-outer">
+        {({ top }) => (
+          <div className="layout-page-side" style={{ top }}>
+            <div className="layout-page-side-inner">
+              {openIssue == null ? this.renderFacets() : this.renderConciseIssuesList()}
+            </div>
           </div>
-        </div>
-      </div>
+        )}
+      </ScreenPositionHelper>
     );
   }
 
index 1499f139787f926f65934a2e4efda47042eb610d..b756fee0c39ff27f5a06ad0efa4a6435688d16b0 100644 (file)
@@ -22,6 +22,7 @@ import PropTypes from 'prop-types';
 import Helmet from 'react-helmet';
 import ListHeader from './ListHeader';
 import List from './List';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import {
   fetchQualityGatesAppDetails,
   fetchQualityGates as fetchQualityGatesAPI
@@ -67,22 +68,22 @@ export default class QualityGatesApp extends Component {
   render() {
     const { children, qualityGates, edit, organization } = this.props;
     const defaultTitle = translate('quality_gates.page');
-    const top = organization ? 95 : 30;
     return (
       <div id="quality-gates-page" className="layout-page">
         <Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
 
-        <div className="layout-page-side-outer">
-          <div className="layout-page-side" style={{ top }}>
-            <div className="layout-page-side-inner">
-              <div className="layout-page-filters">
-                <ListHeader canEdit={edit} onAdd={this.handleAdd.bind(this)} />
-                {qualityGates && <List organization={organization} qualityGates={qualityGates} />}
+        <ScreenPositionHelper className="layout-page-side-outer">
+          {({ top }) => (
+            <div className="layout-page-side" style={{ top }}>
+              <div className="layout-page-side-inner">
+                <div className="layout-page-filters">
+                  <ListHeader canEdit={edit} onAdd={this.handleAdd.bind(this)} />
+                  {qualityGates && <List organization={organization} qualityGates={qualityGates} />}
+                </div>
               </div>
             </div>
-          </div>
-        </div>
-
+          )}
+        </ScreenPositionHelper>
         {qualityGates != null &&
           React.Children.map(children, child => React.cloneElement(child, { organization }))}
       </div>
index 3a69c4bc4c60cb83309305c87ed3386b38201f27..ebc579d5ca5a35e778e1d884ad87b5f18d51edba 100644 (file)
@@ -25,6 +25,7 @@ import { Domain as DomainType, fetchWebApi } from '../../../api/web-api';
 import Menu from './Menu';
 import Search from './Search';
 import Domain from './Domain';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
 import { getActionKey, isDomainPathActive } from '../utils';
 import { scrollToElement } from '../../../helpers/scrolling';
 import { translate } from '../../../helpers/l10n';
@@ -141,35 +142,37 @@ export default class WebApiApp extends React.PureComponent<Props, State> {
     return (
       <div className="layout-page">
         <Helmet title={translate('api_documentation.page')} />
-        <div className="layout-page-side-outer">
-          <div className="layout-page-side" style={{ top: 30 }}>
-            <div className="layout-page-side-inner">
-              <div className="layout-page-filters">
-                <div className="web-api-page-header">
-                  <Link to="/web_api/">
-                    <h1>{translate('api_documentation.page')}</h1>
-                  </Link>
+        <ScreenPositionHelper className="layout-page-side-outer">
+          {({ top }) => (
+            <div className="layout-page-side" style={{ top }}>
+              <div className="layout-page-side-inner">
+                <div className="layout-page-filters">
+                  <div className="web-api-page-header">
+                    <Link to="/web_api/">
+                      <h1>{translate('api_documentation.page')}</h1>
+                    </Link>
+                  </div>
+
+                  <Search
+                    showDeprecated={showDeprecated}
+                    showInternal={showInternal}
+                    onSearch={this.handleSearch}
+                    onToggleInternal={this.handleToggleInternal}
+                    onToggleDeprecated={this.handleToggleDeprecated}
+                  />
+
+                  <Menu
+                    domains={this.state.domains}
+                    showDeprecated={showDeprecated}
+                    showInternal={showInternal}
+                    searchQuery={searchQuery}
+                    splat={splat}
+                  />
                 </div>
-
-                <Search
-                  showDeprecated={showDeprecated}
-                  showInternal={showInternal}
-                  onSearch={this.handleSearch}
-                  onToggleInternal={this.handleToggleInternal}
-                  onToggleDeprecated={this.handleToggleDeprecated}
-                />
-
-                <Menu
-                  domains={this.state.domains}
-                  showDeprecated={showDeprecated}
-                  showInternal={showInternal}
-                  searchQuery={searchQuery}
-                  splat={splat}
-                />
               </div>
             </div>
-          </div>
-        </div>
+          )}
+        </ScreenPositionHelper>
 
         <div className="layout-page-main">
           <div className="layout-page-main-inner">
diff --git a/server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx b/server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx
new file mode 100644 (file)
index 0000000..00f8f37
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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 { throttle } from 'lodash';
+
+interface Props {
+  className?: string;
+  children: (position: { top: number; left: number }) => React.ReactElement<any>;
+}
+
+interface State {
+  position: { top: number; left: number };
+}
+
+export default class ScreenPositionHelper extends React.PureComponent<Props, State> {
+  container: HTMLDivElement;
+  throttledUpdatePosition: () => void;
+
+  constructor(props: Props) {
+    super(props);
+    this.state = { position: { top: 0, left: 0 } };
+    this.throttledUpdatePosition = throttle(this.updatePosition, 100);
+  }
+
+  componentDidMount() {
+    this.updatePosition();
+    window.addEventListener('resize', this.throttledUpdatePosition);
+  }
+
+  componentWillUnmount() {
+    window.removeEventListener('resize', this.throttledUpdatePosition);
+  }
+
+  updatePosition = () => {
+    const containerPos = this.container.getBoundingClientRect();
+    this.setState({
+      position: { top: window.scrollY + containerPos.top, left: window.scrollX + containerPos.left }
+    });
+  };
+
+  render() {
+    return (
+      <div
+        className={this.props.className}
+        ref={container => (this.container = container as HTMLDivElement)}>
+        {this.props.children(this.state.position)}
+      </div>
+    );
+  }
+}