diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-01-02 10:27:22 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2020-01-08 20:46:10 +0100 |
commit | 4691d62918615ce3eb313dad0c405c332a3775b4 (patch) | |
tree | 5e59ec34c0568ce2540b6e0ad3f27d60a673d463 /server/sonar-docs/src | |
parent | 3f6f5496277c76c2498fd245a112931d19830497 (diff) | |
download | sonarqube-4691d62918615ce3eb313dad0c405c332a3775b4.tar.gz sonarqube-4691d62918615ce3eb313dad0c405c332a3775b4.zip |
SONAR-12375 Fix floating TOC in documentation
Diffstat (limited to 'server/sonar-docs/src')
4 files changed, 112 insertions, 37 deletions
diff --git a/server/sonar-docs/src/components/HeadingsLink.tsx b/server/sonar-docs/src/components/HeadingsLink.tsx index fb595c12176..e9d620097de 100644 --- a/server/sonar-docs/src/components/HeadingsLink.tsx +++ b/server/sonar-docs/src/components/HeadingsLink.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { MarkdownHeading } from '../@types/graphql-types'; import HeadingAnchor from './HeadingAnchor'; -const MINIMUM_TOP_MARGIN = 80; const HEADER_SCROLL_MARGIN = 100; interface Props { @@ -31,7 +30,6 @@ interface Props { interface State { activeIndex: number; headers: MarkdownHeading[]; - marginTop: number; } export default class HeadingsLink extends React.PureComponent<Props, State> { @@ -43,18 +41,17 @@ export default class HeadingsLink extends React.PureComponent<Props, State> { activeIndex: -1, headers: props.headers.filter( h => h.depth === 2 && h.value && h.value.toLowerCase() !== 'table of contents' - ), - marginTop: MINIMUM_TOP_MARGIN + ) }; } componentDidMount() { document.addEventListener('scroll', this.scrollHandler, true); - this.scrollHandler(); } componentWillReceiveProps(nextProps: Props) { this.setState({ + activeIndex: -1, headers: nextProps.headers.filter( h => h.depth === 2 && h.value && h.value.toLowerCase() !== 'table of contents' ) @@ -65,6 +62,16 @@ export default class HeadingsLink extends React.PureComponent<Props, State> { document.removeEventListener('scroll', this.scrollHandler, true); } + scrollHandler = () => { + if (this.skipScrollingHandler) { + this.skipScrollingHandler = false; + return; + } + + const scrollTop = window.pageYOffset || document.body.scrollTop; + this.highlightHeading(scrollTop); + }; + highlightHeading = (scrollTop: number) => { let headingIndex = 0; for (let i = 0; i < this.state.headers.length; i++) { @@ -74,11 +81,7 @@ export default class HeadingsLink extends React.PureComponent<Props, State> { } headingIndex = i; } - const scrollLimit = document.body.scrollHeight - document.body.clientHeight; - this.setState({ - activeIndex: headingIndex, - marginTop: Math.max(MINIMUM_TOP_MARGIN, Math.min(scrollTop, scrollLimit)) - }); + this.setState({ activeIndex: headingIndex }); this.markH2(headingIndex + 1, false); }; @@ -99,16 +102,6 @@ export default class HeadingsLink extends React.PureComponent<Props, State> { } }; - scrollHandler = () => { - if (this.skipScrollingHandler) { - this.skipScrollingHandler = false; - return; - } - - const scrollTop = window.pageYOffset || document.body.scrollTop; - this.highlightHeading(scrollTop); - }; - clickHandler = (index: number) => { this.markH2(index, true); }; @@ -120,21 +113,23 @@ export default class HeadingsLink extends React.PureComponent<Props, State> { } return ( - <div className="headings-container" style={{ marginTop: this.state.marginTop + 'px' }}> - <span>On this page</span> - <ul> - {headers.map((header, index) => { - return ( - <HeadingAnchor - active={this.state.activeIndex === index} - clickHandler={this.clickHandler} - index={index + 1} - key={index}> - {header.value} - </HeadingAnchor> - ); - })} - </ul> + <div className="headings-container"> + <div className="headings-container-fixed"> + <span>On this page</span> + <ul> + {headers.map((header, index) => { + return ( + <HeadingAnchor + active={this.state.activeIndex === index} + clickHandler={this.clickHandler} + index={index + 1} + key={index}> + {header.value} + </HeadingAnchor> + ); + })} + </ul> + </div> </div> ); } diff --git a/server/sonar-docs/src/components/__tests__/HeadingsLink-test.tsx b/server/sonar-docs/src/components/__tests__/HeadingsLink-test.tsx new file mode 100644 index 00000000000..5340a531d6d --- /dev/null +++ b/server/sonar-docs/src/components/__tests__/HeadingsLink-test.tsx @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import HeadingsLink from '../HeadingsLink'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<HeadingsLink['props']> = {}) { + return shallow( + <HeadingsLink + headers={[ + { value: 'Table of Contents', depth: 2 }, + { value: 'Foo', depth: 2 }, + { value: 'Br', depth: 2 } + ]} + {...props} + /> + ); +} diff --git a/server/sonar-docs/src/components/__tests__/__snapshots__/HeadingsLink-test.tsx.snap b/server/sonar-docs/src/components/__tests__/__snapshots__/HeadingsLink-test.tsx.snap new file mode 100644 index 00000000000..3449ded4537 --- /dev/null +++ b/server/sonar-docs/src/components/__tests__/__snapshots__/HeadingsLink-test.tsx.snap @@ -0,0 +1,33 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="headings-container" +> + <div + className="headings-container-fixed" + > + <span> + On this page + </span> + <ul> + <HeadingAnchor + active={false} + clickHandler={[Function]} + index={1} + key="0" + > + Foo + </HeadingAnchor> + <HeadingAnchor + active={false} + clickHandler={[Function]} + index={2} + key="1" + > + Br + </HeadingAnchor> + </ul> + </div> +</div> +`; diff --git a/server/sonar-docs/src/layouts/layout.css b/server/sonar-docs/src/layouts/layout.css index adf69979c52..7e5f94ea145 100644 --- a/server/sonar-docs/src/layouts/layout.css +++ b/server/sonar-docs/src/layouts/layout.css @@ -328,10 +328,18 @@ a.search-result .note { } .page-container .headings-container { - float: right; width: 200px; - border-left: 1px solid #cfd3d7; + float: right; + margin-top: 80px; +} + +.page-container .headings-container-fixed { + position: fixed; + width: inherit; padding-left: 26px; + border-left: 1px solid #cfd3d7; + z-index: 100; + background: white; } .page-container .headings-container span { |