diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-12-22 13:56:38 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2021-01-04 20:14:29 +0000 |
commit | 2b93ed0c8f1f64655274cb9063d4e9231f23204a (patch) | |
tree | ba5eddf44a5dc6a33c3ee4f03a2c13d1016f4973 | |
parent | 8af47608143b0282b4cf7fa4cc77d4e6ce7d3dde (diff) | |
download | sonarqube-2b93ed0c8f1f64655274cb9063d4e9231f23204a.tar.gz sonarqube-2b93ed0c8f1f64655274cb9063d4e9231f23204a.zip |
SONAR-13140 Improve side-menu tab scrolling on settings page
11 files changed, 472 insertions, 257 deletions
diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx index 375b4e1dd73..0116ba05a8d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx @@ -27,11 +27,6 @@ import { getCategoryName } from '../utils'; import { ADDITIONAL_CATEGORIES } from './AdditionalCategories'; import CATEGORY_OVERRIDES from './CategoryOverrides'; -interface Category { - key: string; - name: string; -} - export interface CategoriesListProps { branchesEnabled?: boolean; categories: string[]; @@ -40,55 +35,51 @@ export interface CategoriesListProps { selectedCategory: string; } -export class CategoriesList extends React.PureComponent<CategoriesListProps> { - renderLink(category: Category) { - const { component, defaultCategory, selectedCategory } = this.props; - const pathname = this.props.component ? '/project/settings' : '/settings'; - const query = { - category: category.key !== defaultCategory ? category.key.toLowerCase() : undefined, - id: component && component.key - }; - return ( - <IndexLink - className={classNames({ - active: category.key.toLowerCase() === selectedCategory.toLowerCase() - })} - title={category.name} - to={{ pathname, query }}> - {category.name} - </IndexLink> - ); - } +export function CategoriesList(props: CategoriesListProps) { + const { branchesEnabled, categories, component, defaultCategory, selectedCategory } = props; + const pathname = component ? '/project/settings' : '/settings'; - render() { - const { branchesEnabled } = this.props; - - const categoriesWithName = this.props.categories - .filter(key => !CATEGORY_OVERRIDES[key.toLowerCase()]) - .map(key => ({ - key, - name: getCategoryName(key) - })) - .concat( - ADDITIONAL_CATEGORIES.filter(c => c.displayTab) - .filter(c => - this.props.component - ? // Project settings - c.availableForProject - : // Global settings - c.availableGlobally - ) - .filter(c => branchesEnabled || !c.requiresBranchesEnabled) - ); - const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase()); - return ( - <ul className="side-tabs-menu"> - {sortedCategories.map(category => ( - <li key={category.key}>{this.renderLink(category)}</li> - ))} - </ul> + const categoriesWithName = categories + .filter(key => !CATEGORY_OVERRIDES[key.toLowerCase()]) + .map(key => ({ + key, + name: getCategoryName(key) + })) + .concat( + ADDITIONAL_CATEGORIES.filter(c => c.displayTab) + .filter(c => + component + ? // Project settings + c.availableForProject + : // Global settings + c.availableGlobally + ) + .filter(c => branchesEnabled || !c.requiresBranchesEnabled) ); - } + const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase()); + + return ( + <ul className="side-tabs-menu"> + {sortedCategories.map(category => ( + <li key={category.key}> + <IndexLink + className={classNames({ + active: category.key.toLowerCase() === selectedCategory.toLowerCase() + })} + title={category.name} + to={{ + pathname, + query: { + category: category.key !== defaultCategory ? category.key.toLowerCase() : undefined, + id: component && component.key + } + }}> + {category.name} + </IndexLink> + </li> + ))} + </ul> + ); } const mapStateToProps = (state: Store) => ({ diff --git a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx index 4659329d9d5..7377d4f2164 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx @@ -23,9 +23,15 @@ import { Helmet } from 'react-helmet-async'; import { connect } from 'react-redux'; import { WithRouterProps } from 'react-router'; import { translate } from 'sonar-ui-common/helpers/l10n'; +import { + addSideBarClass, + addWhitePageClass, + removeSideBarClass, + removeWhitePageClass +} from 'sonar-ui-common/helpers/pages'; import Suggestions from '../../../app/components/embed-docs-modal/Suggestions'; +import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper'; import { getSettingsAppDefaultCategory, Store } from '../../../store/rootReducer'; -import '../side-tabs.css'; import { fetchSettings } from '../store/actions'; import '../styles.css'; import { ADDITIONAL_CATEGORIES } from './AdditionalCategories'; @@ -50,6 +56,8 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> { componentDidMount() { this.mounted = true; + addSideBarClass(); + addWhitePageClass(); this.fetchSettings(); } @@ -61,6 +69,8 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> { componentWillUnmount() { this.mounted = false; + removeSideBarClass(); + removeWhitePageClass(); } fetchSettings = () => { @@ -91,32 +101,42 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> { (!isProjectSettings && foundAdditionalCategory.availableGlobally)); return ( - <div className="page page-limited" id="settings-page"> + <div id="settings-page"> <Suggestions suggestions="settings" /> <Helmet defer={false} title={translate('settings.page')} /> - <PageHeader component={this.props.component} /> - <div className="side-tabs-layout settings-layout"> - <div className="side-tabs-side"> - <AllCategoriesList - component={this.props.component} - defaultCategory={this.props.defaultCategory} - selectedCategory={selectedCategory} - /> - </div> - <div className="side-tabs-main"> - {foundAdditionalCategory && shouldRenderAdditionalCategory ? ( - foundAdditionalCategory.renderComponent({ - component: this.props.component, - selectedCategory: originalCategory - }) - ) : ( - <CategoryDefinitionsList - category={selectedCategory} - component={this.props.component} - /> + <div className="layout-page"> + <ScreenPositionHelper className="layout-page-side-outer"> + {({ top }) => ( + <div className="layout-page-side" style={{ top }}> + <div className="layout-page-side-inner"> + <AllCategoriesList + component={this.props.component} + defaultCategory={this.props.defaultCategory} + selectedCategory={selectedCategory} + /> + </div> + </div> )} + </ScreenPositionHelper> + + <div className="layout-page-main"> + <div className="layout-page-main-inner"> + <div className="big-padded"> + {foundAdditionalCategory && shouldRenderAdditionalCategory ? ( + foundAdditionalCategory.renderComponent({ + component: this.props.component, + selectedCategory: originalCategory + }) + ) : ( + <CategoryDefinitionsList + category={selectedCategory} + component={this.props.component} + /> + )} + </div> + </div> </div> </div> </div> diff --git a/server/sonar-web/src/main/js/apps/settings/components/PageHeader.tsx b/server/sonar-web/src/main/js/apps/settings/components/PageHeader.tsx index 61a7b24e83a..8ea6c4906b9 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/PageHeader.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/PageHeader.tsx @@ -21,11 +21,11 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import InstanceMessage from '../../../components/common/InstanceMessage'; -interface Props { +export interface PageHeaderProps { component?: T.Component; } -export default function PageHeader({ component }: Props) { +export default function PageHeader({ component }: PageHeaderProps) { const title = component ? translate('project_settings.page') : translate('settings.page'); const description = component ? ( @@ -35,9 +35,13 @@ export default function PageHeader({ component }: Props) { ); return ( - <header className="page-header"> - <h1 className="page-title">{title}</h1> - <div className="page-description">{description}</div> + <header className="top-bar-outer"> + <div className="top-bar"> + <div className="top-bar-inner bordered-bottom big-padded-top padded-bottom"> + <h1 className="page-title">{title}</h1> + <div className="page-description spacer-top">{description}</div> + </div> + </div> </header> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx index e4b62b7165f..4d7abe86501 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx @@ -61,25 +61,18 @@ jest.mock('../AdditionalCategories', () => ({ ] as AdditionalCategory[] })); -it('should render correctly in global mode', () => { - expect(shallowRender()).toMatchSnapshot(); -}); - -it('should render correctly in project mode', () => { - expect(shallowRender({ component: mockComponent() })).toMatchSnapshot(); -}); - -it('should render correctly when branches are disabled', () => { - expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot(); +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('global mode'); + expect(shallowRender({ selectedCategory: 'CAT_2' })).toMatchSnapshot('selected category'); + expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('project mode'); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('branches disabled'); }); function shallowRender(props?: Partial<CategoriesListProps>) { - const categories = ['general']; - - return shallow( + return shallow<CategoriesListProps>( <CategoriesList branchesEnabled={true} - categories={categories} + categories={['general']} defaultCategory="general" selectedCategory="" {...props} diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx index eb9f80b6741..cb4dc239eaa 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx @@ -19,7 +19,14 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import { + addSideBarClass, + addWhitePageClass, + removeSideBarClass, + removeWhitePageClass +} from 'sonar-ui-common/helpers/pages'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; +import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper'; import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; import { ALM_INTEGRATION, @@ -30,11 +37,27 @@ import { } from '../AdditionalCategoryKeys'; import { App } from '../AppContainer'; +jest.mock('sonar-ui-common/helpers/pages', () => ({ + addSideBarClass: jest.fn(), + addWhitePageClass: jest.fn(), + removeSideBarClass: jest.fn(), + removeWhitePageClass: jest.fn() +})); + it('should render default view correctly', async () => { const wrapper = shallowRender(); + expect(addSideBarClass).toBeCalled(); + expect(addWhitePageClass).toBeCalled(); + await waitAndUpdate(wrapper); expect(wrapper).toMatchSnapshot(); + expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot(); + + wrapper.unmount(); + + expect(removeSideBarClass).toBeCalled(); + expect(removeWhitePageClass).toBeCalled(); }); it('should render newCodePeriod correctly', async () => { diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx new file mode 100644 index 00000000000..25b8cb4d09e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/PageHeader-test.tsx @@ -0,0 +1,33 @@ +/* + * 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 { mockComponent } from '../../../../helpers/testMocks'; +import PageHeader, { PageHeaderProps } from '../PageHeader'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('for project'); +}); + +function shallowRender(props: Partial<PageHeaderProps> = {}) { + return shallow<PageHeaderProps>(<PageHeader {...props} />); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap index 5a1a854e1cf..161cb051782 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AllCategoriesList-test.tsx.snap @@ -1,6 +1,51 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly in global mode 1`] = ` +exports[`should render correctly: branches disabled 1`] = ` +<ul + className="side-tabs-menu" +> + <li + key="CAT_2" + > + <IndexLink + className="" + title="CAT_2_NAME" + to={ + Object { + "pathname": "/settings", + "query": Object { + "category": "cat_2", + "id": undefined, + }, + } + } + > + CAT_2_NAME + </IndexLink> + </li> + <li + key="general" + > + <IndexLink + className="" + title="general" + to={ + Object { + "pathname": "/settings", + "query": Object { + "category": undefined, + "id": undefined, + }, + } + } + > + general + </IndexLink> + </li> +</ul> +`; + +exports[`should render correctly: global mode 1`] = ` <ul className="side-tabs-menu" > @@ -64,7 +109,7 @@ exports[`should render correctly in global mode 1`] = ` </ul> `; -exports[`should render correctly in project mode 1`] = ` +exports[`should render correctly: project mode 1`] = ` <ul className="side-tabs-menu" > @@ -128,15 +173,34 @@ exports[`should render correctly in project mode 1`] = ` </ul> `; -exports[`should render correctly when branches are disabled 1`] = ` +exports[`should render correctly: selected category 1`] = ` <ul className="side-tabs-menu" > <li - key="CAT_2" + key="CAT_1" > <IndexLink className="" + title="CAT_1_NAME" + to={ + Object { + "pathname": "/settings", + "query": Object { + "category": "cat_1", + "id": undefined, + }, + } + } + > + CAT_1_NAME + </IndexLink> + </li> + <li + key="CAT_2" + > + <IndexLink + className="active" title="CAT_2_NAME" to={ Object { diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap index 96e4786b9d9..fcc9f2ce7d7 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap @@ -2,7 +2,6 @@ exports[`should render ALM integration correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -15,22 +14,27 @@ exports[`should render ALM integration correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > - <div - className="side-tabs-side" + <ScreenPositionHelper + className="layout-page-side-outer" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="almintegration" - /> - </div> + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-main" + className="layout-page-main" > - <withRouter(Connect(withAppState(AlmIntegration))) - selectedCategory="almintegration" - /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <withRouter(Connect(withAppState(AlmIntegration))) + selectedCategory="almintegration" + /> + </div> + </div> </div> </div> </div> @@ -38,7 +42,6 @@ exports[`should render ALM integration correctly 1`] = ` exports[`should render analysis scope correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -51,22 +54,27 @@ exports[`should render analysis scope correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > - <div - className="side-tabs-side" + <ScreenPositionHelper + className="layout-page-side-outer" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="exclusions" - /> - </div> + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-main" + className="layout-page-main" > - <AnalysisScope - selectedCategory="exclusions" - /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <AnalysisScope + selectedCategory="exclusions" + /> + </div> + </div> </div> </div> </div> @@ -74,7 +82,6 @@ exports[`should render analysis scope correctly 1`] = ` exports[`should render default view correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -87,21 +94,50 @@ exports[`should render default view correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > + <ScreenPositionHelper + className="layout-page-side-outer" + > + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-side" + className="layout-page-main" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="general" - /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="general" + /> + </div> + </div> </div> + </div> +</div> +`; + +exports[`should render default view correctly 2`] = ` +<div + className="layout-page-side-outer" +> + <div + className="layout-page-side" + style={ + Object { + "top": 0, + } + } + > <div - className="side-tabs-main" + className="layout-page-side-inner" > - <Connect(SubCategoryDefinitionsList) - category="general" + <Connect(CategoriesList) + defaultCategory="general" + selectedCategory="general" /> </div> </div> @@ -110,7 +146,6 @@ exports[`should render default view correctly 1`] = ` exports[`should render languages correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -123,22 +158,27 @@ exports[`should render languages correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > - <div - className="side-tabs-side" + <ScreenPositionHelper + className="layout-page-side-outer" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="languages" - /> - </div> + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-main" + className="layout-page-main" > - <withRouter(Connect(Languages)) - selectedCategory="languages" - /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <withRouter(Connect(Languages)) + selectedCategory="languages" + /> + </div> + </div> </div> </div> </div> @@ -146,7 +186,6 @@ exports[`should render languages correctly 1`] = ` exports[`should render newCodePeriod correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -159,20 +198,25 @@ exports[`should render newCodePeriod correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > - <div - className="side-tabs-side" + <ScreenPositionHelper + className="layout-page-side-outer" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="new_code_period" - /> - </div> + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-main" + className="layout-page-main" > - <NewCodePeriod /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <NewCodePeriod /> + </div> + </div> </div> </div> </div> @@ -180,7 +224,6 @@ exports[`should render newCodePeriod correctly 1`] = ` exports[`should render pull request decoration binding correctly 1`] = ` <div - className="page page-limited" id="settings-page" > <Suggestions @@ -193,22 +236,27 @@ exports[`should render pull request decoration binding correctly 1`] = ` /> <PageHeader /> <div - className="side-tabs-layout settings-layout" + className="layout-page" > - <div - className="side-tabs-side" + <ScreenPositionHelper + className="layout-page-side-outer" > - <Connect(CategoriesList) - defaultCategory="general" - selectedCategory="pull_request_decoration_binding" - /> - </div> + <Component /> + </ScreenPositionHelper> <div - className="side-tabs-main" + className="layout-page-main" > - <Connect(SubCategoryDefinitionsList) - category="pull_request_decoration_binding" - /> + <div + className="layout-page-main-inner" + > + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="pull_request_decoration_binding" + /> + </div> + </div> </div> </div> </div> diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap new file mode 100644 index 00000000000..93f8b63b548 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/PageHeader-test.tsx.snap @@ -0,0 +1,53 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +<header + className="top-bar-outer" +> + <div + className="top-bar" + > + <div + className="top-bar-inner bordered-bottom big-padded-top padded-bottom" + > + <h1 + className="page-title" + > + settings.page + </h1> + <div + className="page-description spacer-top" + > + <InstanceMessage + message="settings.page.description" + /> + </div> + </div> + </div> +</header> +`; + +exports[`should render correctly: for project 1`] = ` +<header + className="top-bar-outer" +> + <div + className="top-bar" + > + <div + className="top-bar-inner bordered-bottom big-padded-top padded-bottom" + > + <h1 + className="page-title" + > + project_settings.page + </h1> + <div + className="page-description spacer-top" + > + project_settings.page.description + </div> + </div> + </div> +</header> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/side-tabs.css b/server/sonar-web/src/main/js/apps/settings/side-tabs.css deleted file mode 100644 index 5fb044b8967..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/side-tabs.css +++ /dev/null @@ -1,85 +0,0 @@ -/* - * 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. - */ -.side-tabs-layout { - display: flex; - justify-content: space-between; - align-items: stretch; -} - -.modal .side-tabs-layout { - padding-left: 10px; - background-color: var(--barBackgroundColor); -} - -.side-tabs-main { - position: relative; - z-index: var(--normalZIndex); - flex-grow: 1; - padding: 15px 20px; - border: 1px solid var(--barBorderColor); - box-sizing: border-box; - background-color: #fff; - overflow: auto; - height: 100vh; -} - -.modal .side-tabs-main { - border-top: none; - border-bottom: none; - border-right: none; -} - -.side-tabs-side { - position: relative; - z-index: var(--aboveNormalZIndex); - width: 160px; - flex-shrink: 0; - padding: 10px 0; - box-sizing: border-box; - transform: translateX(1px); -} - -.side-tabs-menu > li { - margin-bottom: 4px; -} - -.side-tabs-menu > li > a { - display: block; - padding: 10px 10px; - line-height: 1.5; - border-top-left-radius: 3px; - border-bottom-left-radius: 3px; - border: 1px solid var(--barBorderColor); - border-right: none; - overflow: hidden; - text-overflow: ellipsis; - transition: color 0.3s ease, background-color 0.3s ease; -} - -.side-tabs-menu > li > a:hover, -.side-tabs-menu > li > a:focus, -.side-tabs-menu > li > a.active { - background-color: #fff; -} - -.side-tabs-menu > li > a.active { - color: var(--baseFontColor); - cursor: default; -} diff --git a/server/sonar-web/src/main/js/apps/settings/styles.css b/server/sonar-web/src/main/js/apps/settings/styles.css index fbcb0bb05d5..87f7b5b89dc 100644 --- a/server/sonar-web/src/main/js/apps/settings/styles.css +++ b/server/sonar-web/src/main/js/apps/settings/styles.css @@ -17,8 +17,47 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -.settings-layout { - margin-bottom: 60px; +#settings-page .layout-page-side, +#settings-page .layout-page-side-outer { + width: calc(50vw - 480px); + border-right: none; +} + +#settings-page .layout-page-side-inner { + width: 160px; + margin-left: calc(50vw - 639px); /* 640px -1px for overlapping the border */ +} + +#settings-page .layout-page-main { + padding: 0; +} + +#settings-page .layout-page-main-inner { + max-width: 1110px; +} + +#settings-page .top-bar-outer { + height: 80px; +} + +#settings-page .top-bar { + background-color: #f3f3f3; + position: fixed; + z-index: 55; /* todo */ + left: 0; + right: 0; +} + +#settings-page .top-bar-inner { + max-width: 1280px; + margin: 0 auto; + height: 80px; + box-sizing: border-box; +} + +#settings-page .page-title, +#settings-page .page-description { + float: none; } .settings-definitions-list > li + li { @@ -105,3 +144,35 @@ max-width: 400px; min-width: 200px; } + +.side-tabs-menu { + margin-top: calc(2 * var(--gridSize)); +} + +.side-tabs-menu > li { + margin-bottom: 4px; +} + +.side-tabs-menu > li > a { + display: block; + padding: 10px 10px; + line-height: 1.5; + border-top-left-radius: 3px; + border-bottom-left-radius: 3px; + border: 1px solid var(--barBorderColor); + border-right: none; + overflow: hidden; + text-overflow: ellipsis; + transition: color 0.3s ease, background-color 0.3s ease; +} + +.side-tabs-menu > li > a:hover, +.side-tabs-menu > li > a:focus, +.side-tabs-menu > li > a.active { + background-color: #fff; +} + +.side-tabs-menu > li > a.active { + color: var(--baseFontColor); + cursor: default; +} |