aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-docs/src
diff options
context:
space:
mode:
authorWouter Admiraal <wouter.admiraal@sonarsource.com>2020-01-02 10:27:22 +0100
committerSonarTech <sonartech@sonarsource.com>2020-01-08 20:46:10 +0100
commit4691d62918615ce3eb313dad0c405c332a3775b4 (patch)
tree5e59ec34c0568ce2540b6e0ad3f27d60a673d463 /server/sonar-docs/src
parent3f6f5496277c76c2498fd245a112931d19830497 (diff)
downloadsonarqube-4691d62918615ce3eb313dad0c405c332a3775b4.tar.gz
sonarqube-4691d62918615ce3eb313dad0c405c332a3775b4.zip
SONAR-12375 Fix floating TOC in documentation
Diffstat (limited to 'server/sonar-docs/src')
-rw-r--r--server/sonar-docs/src/components/HeadingsLink.tsx65
-rw-r--r--server/sonar-docs/src/components/__tests__/HeadingsLink-test.tsx39
-rw-r--r--server/sonar-docs/src/components/__tests__/__snapshots__/HeadingsLink-test.tsx.snap33
-rw-r--r--server/sonar-docs/src/layouts/layout.css12
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 {