aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>2022-08-29 11:03:29 +0200
committersonartech <sonartech@sonarsource.com>2022-08-30 20:03:13 +0000
commita74e990dbbddbfac7c608a537304180d6869e406 (patch)
tree7e2161f511d589caa6d5739194ded629a5ed27b4
parent7e53bc4c96453b744ebe478d912378c989ea7a78 (diff)
downloadsonarqube-a74e990dbbddbfac7c608a537304180d6869e406.tar.gz
sonarqube-a74e990dbbddbfac7c608a537304180d6869e406.zip
SONAR-17228 Fixing scroll behaviour in setting tabs
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx14
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx5
5 files changed, 18 insertions, 24 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
index e292205d626..0e2c35c6d36 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
@@ -90,7 +90,8 @@ export function SettingsAppRenderer(props: SettingsAppRendererProps) {
<div className="layout-page-main">
<div className="layout-page-main-inner">
- <div className="big-padded">
+ {/* Adding a key to force re-rendering of the category content, so that it resets the scroll position */}
+ <div className="big-padded" key={selectedCategory}>
{foundAdditionalCategory && shouldRenderAdditionalCategory ? (
foundAdditionalCategory.renderComponent({
categories,
diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
index 64c255172c5..a133d64cc90 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
@@ -21,7 +21,6 @@ import { groupBy, sortBy } from 'lodash';
import * as React from 'react';
import { Location, withRouter } from '../../../components/hoc/withRouter';
import { sanitizeStringRestricted } from '../../../helpers/sanitize';
-import { scrollToElement } from '../../../helpers/scrolling';
import { SettingDefinitionAndValue } from '../../../types/settings';
import { Component } from '../../../types/types';
import { getSubCategoryDescription, getSubCategoryName } from '../utils';
@@ -36,16 +35,13 @@ export interface SubCategoryDefinitionsListProps {
subCategory?: string;
}
-const SCROLL_OFFSET_TOP = 200;
-const SCROLL_OFFSET_BOTTOM = 500;
-
export class SubCategoryDefinitionsList extends React.PureComponent<
SubCategoryDefinitionsListProps
> {
componentDidUpdate(prevProps: SubCategoryDefinitionsListProps) {
const { hash } = this.props.location;
if (hash && prevProps.location.hash !== hash) {
- const query = `[data-key=${hash.substr(1).replace(/[.#/]/g, '\\$&')}]`;
+ const query = `[data-key=${hash.substring(1).replace(/[.#/]/g, '\\$&')}]`;
const element = document.querySelector<HTMLHeadingElement | HTMLLIElement>(query);
this.scrollToSubCategoryOrDefinition(element);
}
@@ -54,12 +50,8 @@ export class SubCategoryDefinitionsList extends React.PureComponent<
scrollToSubCategoryOrDefinition = (element: HTMLHeadingElement | HTMLLIElement | null) => {
if (element) {
const { hash } = this.props.location;
- if (hash && hash.substr(1) === element.getAttribute('data-key')) {
- scrollToElement(element, {
- topOffset: SCROLL_OFFSET_TOP,
- bottomOffset: SCROLL_OFFSET_BOTTOM,
- smooth: true
- });
+ if (hash && hash.substring(1) === element.getAttribute('data-key')) {
+ element.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
}
}
};
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
index 2bfbbf61873..78925da7276 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
@@ -20,7 +20,6 @@
import { mount, shallow } from 'enzyme';
import * as React from 'react';
import { mockSettingWithCategory } from '../../../../helpers/mocks/settings';
-import { scrollToElement } from '../../../../helpers/scrolling';
import { mockLocation } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import {
@@ -37,24 +36,19 @@ it('should render correctly', () => {
expect(shallowRender({ subCategory: 'qg' })).toMatchSnapshot('subcategory');
});
-it('should scroll if hash is defined', async () => {
+it('should scroll if hash is defined and updated', async () => {
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
const wrapper = shallowRender({ location: mockLocation({ hash: '#qg' }) });
await waitAndUpdate(wrapper);
wrapper.find('h2').forEach(node => mount(node.getElement()));
- expect(scrollToElement).toBeCalled();
-});
-
-it('should scroll when hash is updated', async () => {
- const wrapper = shallowRender({ location: mockLocation({ hash: '#qg' }) });
+ expect(window.HTMLElement.prototype.scrollIntoView).toBeCalled();
wrapper.setProps({ location: mockLocation({ hash: '#email' }) });
- await waitAndUpdate(wrapper);
-
- expect(scrollToElement).toBeCalled();
+ expect(window.HTMLElement.prototype.scrollIntoView).toBeCalled();
});
function shallowRender(props: Partial<SubCategoryDefinitionsListProps> = {}) {
diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
index a40cc74b2c1..7de14af9ca2 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
@@ -49,6 +49,7 @@ exports[`should render almintegration correctly 1`] = `
>
<div
className="big-padded"
+ key="almintegration"
>
<withRouter(withAppStateContext(AlmIntegration))
categories={
@@ -133,6 +134,7 @@ exports[`should render default view correctly 1`] = `
>
<div
className="big-padded"
+ key="general"
>
<CategoryDefinitionsList
category="general"
@@ -241,6 +243,7 @@ exports[`should render exclusions correctly 1`] = `
>
<div
className="big-padded"
+ key="exclusions"
>
<AnalysisScope
categories={
@@ -325,6 +328,7 @@ exports[`should render languages correctly 1`] = `
>
<div
className="big-padded"
+ key="languages"
>
<withRouter(Languages)
categories={
@@ -409,6 +413,7 @@ exports[`should render new_code_period correctly 1`] = `
>
<div
className="big-padded"
+ key="new_code_period"
>
<NewCodePeriod />
</div>
@@ -467,6 +472,7 @@ exports[`should render pull_request_decoration_binding correctly 1`] = `
>
<div
className="big-padded"
+ key="pull_request_decoration_binding"
>
<CategoryDefinitionsList
category="pull_request_decoration_binding"
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
index 1dc541cf572..88dd2eb57fe 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx
@@ -122,14 +122,15 @@ export default function Authentication(props: Props) {
selected={currentTab}
tabs={tabs}
/>
-
+ {/* Adding a key to force re-rendering of the tab container, so that it resets the scroll position */}
<ScreenPositionHelper>
{({ top }) => (
<div
style={{
maxHeight: `calc(100vh - ${top + HEIGHT_ADJUSTMENT}px)`
}}
- className="bordered overflow-y-auto tabbed-definitions">
+ className="bordered overflow-y-auto tabbed-definitions"
+ key={currentTab}>
<div className="big-padded">
<Alert variant="info">
<FormattedMessage