diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-09-29 15:03:12 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-09-29 17:09:48 +0200 |
commit | 059cf8688eafd9f151a1fad84997ce77e5af511e (patch) | |
tree | b0d0941506386d23955ee9b893c8cc73e0c07b2d /server/sonar-web/src | |
parent | ee41573b8b309a123bc23ac623663107cc410af3 (diff) | |
download | sonarqube-059cf8688eafd9f151a1fad84997ce77e5af511e.tar.gz sonarqube-059cf8688eafd9f151a1fad84997ce77e5af511e.zip |
SONAR-9792 Fix sidebar position with a notification
Diffstat (limited to 'server/sonar-web/src')
6 files changed, 132 insertions, 80 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/App.js b/server/sonar-web/src/main/js/apps/component-measures/components/App.js index a9fc88958ad..a33340530ea 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/App.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/App.js @@ -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 diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap index 85d80ad9d74..24354987bd6 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap +++ b/server/sonar-web/src/main/js/apps/component-measures/components/__tests__/__snapshots__/App-test.js.snap @@ -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]} diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js index 888ce25b468..f5f83df90e7 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.js +++ b/server/sonar-web/src/main/js/apps/issues/components/App.js @@ -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> ); } diff --git a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js index 1499f139787..b756fee0c39 100644 --- a/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js +++ b/server/sonar-web/src/main/js/apps/quality-gates/components/QualityGatesApp.js @@ -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> diff --git a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx index 3a69c4bc4c6..ebc579d5ca5 100644 --- a/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx +++ b/server/sonar-web/src/main/js/apps/web-api/components/WebApiApp.tsx @@ -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 index 00000000000..00f8f375766 --- /dev/null +++ b/server/sonar-web/src/main/js/components/common/ScreenPositionHelper.tsx @@ -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> + ); + } +} |