]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-17228 Fixing scroll behaviour in setting tabs
authorRevanshu Paliwal <revanshu.paliwal@sonarsource.com>
Mon, 29 Aug 2022 09:03:29 +0000 (11:03 +0200)
committersonartech <sonartech@sonarsource.com>
Tue, 30 Aug 2022 20:03:13 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/settings/components/SettingsAppRenderer.tsx
server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/SubCategoryDefinitionsList-test.tsx
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/SettingsAppRenderer-test.tsx.snap
server/sonar-web/src/main/js/apps/settings/components/authentication/Authentication.tsx

index e292205d6266e6b1146cae4a054074daf9fad081..0e2c35c6d3644262bc0274d9d30648f37cddeb3c 100644 (file)
@@ -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,
index 64c255172c58b9e948a610ae8100119aa884b825..a133d64cc907a95f8f9361ee31bb6291b6dd6e7e 100644 (file)
@@ -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' });
       }
     }
   };
index 2bfbbf61873137c2e2443c82ca41c742e006a4d9..78925da7276ded70d551dcc8f385bb74b1b391da 100644 (file)
@@ -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> = {}) {
index a40cc74b2c11b418432f3f562b2448f632abe751..7de14af9ca2c980ef5d4122e5251b26a0a471734 100644 (file)
@@ -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"
index 1dc541cf5721a06117a7a4cae803eac8e39df947..88dd2eb57fe9b954152c2ccd75357fade2af10d4 100644 (file)
@@ -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