diff options
26 files changed, 387 insertions, 661 deletions
diff --git a/it/it-tests/src/test/java/pageobjects/projects/ProjectsPage.java b/it/it-tests/src/test/java/pageobjects/projects/ProjectsPage.java index 21bc73da4a0..66bc7e5fda4 100644 --- a/it/it-tests/src/test/java/pageobjects/projects/ProjectsPage.java +++ b/it/it-tests/src/test/java/pageobjects/projects/ProjectsPage.java @@ -90,7 +90,7 @@ public class ProjectsPage { } public ProjectsPage searchProject(String search) { - SelenideElement searchInput = $(".projects-facet-search input"); + SelenideElement searchInput = $(".projects-topbar-item-search input"); searchInput.setValue("").sendKeys(search); return this; } @@ -115,11 +115,6 @@ public class ProjectsPage { } private SelenideElement getOpenTopBar() { - SelenideElement topBar = $(".projects-topbar-actions").should(Condition.exist); - if (!topBar.has(Condition.hasClass("open"))){ - $(".js-projects-topbar-open").click(); - } - topBar.should(Condition.hasClass("open")); - return topBar; + return $(".projects-topbar-items").should(Condition.exist); } } diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.js b/server/sonar-web/src/main/js/apps/issues/components/App.js index 144f2f2b682..d827a0d1f1f 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.js +++ b/server/sonar-web/src/main/js/apps/issues/components/App.js @@ -804,8 +804,8 @@ export default class App extends React.PureComponent { {this.renderSide(openIssue)} <div className="layout-page-main"> - <div className="issues-header-panel issues-main-header"> - <div className="issues-header-panel-inner issues-main-header-inner"> + <div className="layout-page-header-panel layout-page-main-header issues-main-header"> + <div className="layout-page-header-panel-inner layout-page-main-header-inner"> <div className="layout-page-main-inner"> {this.renderBulkChange(openIssue)} {openIssue != null diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.js b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.js index e6a5f1c24b3..aed9b836953 100644 --- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.js +++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssuesListHeader.js @@ -36,8 +36,8 @@ export default function ConciseIssuesListHeader(props: Props) { const { paging, selectedIndex } = props; return ( - <header className="issues-header-panel concise-issues-list-header"> - <div className="issues-header-panel-inner concise-issues-list-header-inner"> + <header className="layout-page-header-panel concise-issues-list-header"> + <div className="layout-page-header-panel-inner concise-issues-list-header-inner"> <BackButton className="pull-left" onClick={props.onBackClick} /> {props.loading ? <i className="spinner pull-right" /> diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css index 95f6f533b22..b2041786cf7 100644 --- a/server/sonar-web/src/main/js/apps/issues/styles.css +++ b/server/sonar-web/src/main/js/apps/issues/styles.css @@ -1,55 +1,13 @@ -.issues-header-panel, -.issues-header-panel-inner { - height: 56px; - box-sizing: border-box; -} - -.issues-header-panel { - margin-top: -20px; -} - -.issues-header-panel-inner { - position: fixed; - z-index: 30; - line-height: 24px; - padding-top: 16px; - padding-bottom: 16px; - border-bottom: 1px solid #e6e6e6; - background-color: #f3f3f3; -} - -.issues-main-header { - margin-bottom: 20px; -} - .issues-main-header .component-name { line-height: 24px; } -.issues-main-header-inner { - left: calc(50vw - 360px + 1px); - right: 0; - padding-left: 20px; - padding-right: 20px; -} - -@media (max-width: 1320px) { - .issues-main-header-inner { - left: 301px; - } +.concise-issues-list-header, .concise-issues-list-header-inner { } -.issues-main-header-spinner { - margin-left: 1px; - margin-right: 9px; - margin-top: -1px; +.concise-issues-list-header { } -.concise-issues-list-header, -.concise-issues-list-header-inner {} - -.concise-issues-list-header {} - .concise-issues-list-header-inner { width: 260px; text-align: center; @@ -156,8 +114,7 @@ text-align: right; } -.issues .search-navigator-facet-header, -.issues .search-navigator-facet-list { +.issues .search-navigator-facet-header, .issues .search-navigator-facet-list { padding-left: 0; padding-right: 0; } @@ -167,7 +124,8 @@ padding-bottom: 8px; } -.issues .search-navigator-facet-box:not(.hidden):not(.leak-facet-box) + .search-navigator-facet-box:not(.leak-facet-box) { +.issues .search-navigator-facet-box:not(.hidden):not(.leak-facet-box) + + .search-navigator-facet-box:not(.leak-facet-box) { border-top: none; } diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js index 39bcb30a75a..0707e5ae59d 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationFavoriteProjects.js @@ -26,8 +26,6 @@ export default class OrganizationFavoriteProjects extends React.PureComponent { children?: React.Element<*>, currentUser: { isLoggedIn: boolean }, location: Object, - optionBarOpen: boolean, - optionBarToggle: (open: boolean) => void, organization: { key: string } @@ -53,8 +51,6 @@ export default class OrganizationFavoriteProjects extends React.PureComponent { <FavoriteProjectsContainer currentUser={this.props.currentUser} location={this.props.location} - optionBarOpen={this.props.optionBarOpen} - optionBarToggle={this.props.optionBarToggle} organization={this.props.organization} /> </div> diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js index 39d5c76ce76..cf16b4fe52f 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjects.js @@ -26,8 +26,6 @@ export default class OrganizationProjects extends React.PureComponent { children?: React.Element<*>, currentUser: { isLoggedIn: boolean }, location: Object, - optionBarOpen: boolean, - optionBarToggle: (open: boolean) => void, organization: { key: string } @@ -54,8 +52,6 @@ export default class OrganizationProjects extends React.PureComponent { currentUser={this.props.currentUser} isFavorite={false} location={this.props.location} - optionBarOpen={this.props.optionBarOpen} - optionBarToggle={this.props.optionBarToggle} organization={this.props.organization} /> </div> diff --git a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjectsContainer.js b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjectsContainer.js index faa2bf8df04..a08e864d1b9 100644 --- a/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjectsContainer.js +++ b/server/sonar-web/src/main/js/apps/organizations/components/OrganizationProjectsContainer.js @@ -23,20 +23,10 @@ import { connect } from 'react-redux'; import { getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer'; import { updateOrganization } from '../actions'; -type State = { - optionBarOpen: boolean -}; - class OrganizationProjectsContainer extends React.PureComponent { - state: State = { optionBarOpen: false }; - - handleOptionBarToggle = (open: boolean) => this.setState({ optionBarOpen: open }); - render() { return React.cloneElement(this.props.children, { currentUser: this.props.currentUser, - optionBarOpen: this.state.optionBarOpen, - optionBarToggle: this.handleOptionBarToggle, organization: this.props.organization }); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js index 9720d56560b..93754d09541 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js +++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.js @@ -21,7 +21,6 @@ import React from 'react'; import Helmet from 'react-helmet'; import PageHeaderContainer from './PageHeaderContainer'; -import ProjectsOptionBarContainer from './ProjectsOptionBarContainer'; import ProjectsListContainer from './ProjectsListContainer'; import ProjectsListFooterContainer from './ProjectsListFooterContainer'; import PageSidebar from './PageSidebar'; @@ -31,16 +30,14 @@ import { translate } from '../../../helpers/l10n'; import { SORTING_SWITCH, parseSorting } from '../utils'; import '../styles.css'; -type Props = { +type Props = {| isFavorite: boolean, location: { pathname: string, query: { [string]: string } }, fetchProjects: (query: string, isFavorite: boolean, organization?: {}) => Promise<*>, - optionBarOpen: boolean, - optionBarToggle: (open: boolean) => void, organization?: { key: string }, router: { push: ({ pathname: string, query?: {} }) => void }, currentUser?: { isLoggedIn: boolean } -}; +|}; type State = { query: { [string]: string } @@ -67,11 +64,13 @@ export default class AllProjects extends React.PureComponent { footer && footer.classList.remove('search-navigator-footer'); } - openOptionBar = (evt: Event & { currentTarget: HTMLElement }) => { - evt.currentTarget.blur(); - evt.preventDefault(); - this.props.optionBarToggle(true); - }; + getView = () => this.state.query.view || 'overall'; + + getVisualization = () => this.state.query.visualization || 'risk'; + + getSort = () => this.state.query.sort || 'name'; + + isFiltered = () => Object.keys(this.state.query).some(key => this.state.query[key] != null); handlePerspectiveChange = ({ view, visualization }: { view: string, visualization?: string }) => { const query: { view: ?string, visualization: ?string, sort?: ?string } = { @@ -111,78 +110,78 @@ export default class AllProjects extends React.PureComponent { }); }; - render() { - const { isFavorite, organization, optionBarOpen } = this.props; - const { query } = this.state; - const isFiltered = Object.keys(query).some(key => query[key] != null); - - const view = query.view || 'overall'; - const visualization = query.visualization || 'risk'; - const selectedSort = query.sort || 'name'; - - const sideBarTop = (organization ? 95 : 30) + (optionBarOpen ? 45 : 0); - const contentTop = optionBarOpen ? 65 : 20; + renderSide = () => ( + <div className="layout-page-side-outer"> + <div + className="layout-page-side projects-page-side" + style={{ top: this.props.organization ? 95 : 30 }}> + <div className="layout-page-side-inner"> + <div className="layout-page-filters"> + <PageSidebar + isFavorite={this.props.isFavorite} + organization={this.props.organization} + query={this.state.query} + view={this.getView()} + visualization={this.getVisualization()} + /> + </div> + </div> + </div> + </div> + ); + + renderHeader = () => ( + <div className="layout-page-header-panel layout-page-main-header"> + <div className="layout-page-header-panel-inner layout-page-main-header-inner"> + <div className="layout-page-main-inner"> + <PageHeaderContainer + query={this.state.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + onPerspectiveChange={this.handlePerspectiveChange} + onSortChange={this.handleSortChange} + selectedSort={this.getSort()} + currentUser={this.props.currentUser} + view={this.getView()} + visualization={this.getVisualization()} + /> + </div> + </div> + </div> + ); + + renderMain = () => + (this.getView() === 'visualizations' + ? <div className="layout-page-main-inner"> + <VisualizationsContainer + sort={this.state.query.sort} + visualization={this.getVisualization()} + /> + </div> + : <div className="layout-page-main-inner"> + <ProjectsListContainer + isFavorite={this.props.isFavorite} + isFiltered={this.isFiltered()} + organization={this.props.organization} + cardType={this.getView()} + /> + <ProjectsListFooterContainer + query={this.state.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + /> + </div>); + render() { return ( - <div> + <div className="layout-page projects-page"> <Helmet title={translate('projects.page')} /> - <ProjectsOptionBarContainer - onPerspectiveChange={this.handlePerspectiveChange} - onSortChange={this.handleSortChange} - onToggleOptionBar={this.props.optionBarToggle} - open={optionBarOpen} - selectedSort={selectedSort} - currentUser={this.props.currentUser} - view={view} - visualization={visualization} - /> - - <div className="layout-page projects-page"> - <div className="layout-page-side-outer"> - <div className="layout-page-side projects-page-side" style={{ top: sideBarTop }}> - <div className="layout-page-side-inner"> - <div className="layout-page-filters"> - <PageSidebar - isFavorite={isFavorite} - organization={organization} - query={query} - view={view} - visualization={visualization} - /> - </div> - </div> - </div> - </div> + {this.renderSide()} - <div - className="layout-page-main projects-page-content" - style={{ paddingTop: contentTop }}> - <div className="layout-page-main-inner"> - <PageHeaderContainer - query={query} - isFavorite={isFavorite} - organization={organization} - onOpenOptionBar={this.openOptionBar} - optionBarOpen={optionBarOpen} - /> - {view !== 'visualizations' && - <ProjectsListContainer - isFavorite={isFavorite} - isFiltered={isFiltered} - organization={organization} - cardType={view} - />} - {view !== 'visualizations' && - <ProjectsListFooterContainer - query={query} - isFavorite={isFavorite} - organization={organization} - />} - {view === 'visualizations' && - <VisualizationsContainer sort={query.sort} visualization={visualization} />} - </div> - </div> + <div className="layout-page-main projects-page-content"> + {this.renderHeader()} + {this.renderMain()} </div> </div> ); diff --git a/server/sonar-web/src/main/js/apps/projects/components/App.js b/server/sonar-web/src/main/js/apps/projects/components/App.js index f1b4d2f71c2..83b097ed170 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/App.js +++ b/server/sonar-web/src/main/js/apps/projects/components/App.js @@ -20,32 +20,25 @@ //@flow import React from 'react'; -type State = { - optionBarOpen: boolean -}; - export default class App extends React.PureComponent { - state: State = { optionBarOpen: false }; - componentDidMount() { const elem = document.querySelector('html'); - elem && elem.classList.add('dashboard-page'); + if (elem) { + elem.classList.add('dashboard-page'); + } } componentWillUnmount() { const elem = document.querySelector('html'); - elem && elem.classList.remove('dashboard-page'); + if (elem) { + elem.classList.remove('dashboard-page'); + } } - handleOptionBarToggle = (open: boolean) => this.setState({ optionBarOpen: open }); - render() { return ( <div id="projects-page"> - {React.cloneElement(this.props.children, { - optionBarOpen: this.state.optionBarOpen, - optionBarToggle: this.handleOptionBarToggle - })} + {this.props.children} </div> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js index 988891c41d3..f3e39df8b4d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js +++ b/server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.js @@ -29,8 +29,6 @@ import { searchProjects } from '../../../api/components'; type Props = { currentUser: { isLoggedIn: boolean }, location: { query: {} }, - optionBarOpen: boolean, - optionBarToggle: (open: boolean) => void, router: { replace: (location: { pathname?: string, query?: { [string]: string } }) => void } @@ -107,8 +105,6 @@ class DefaultPageSelector extends React.PureComponent { <AllProjectsContainer isFavorite={false} location={this.props.location} - optionBarOpen={this.props.optionBarOpen} - optionBarToggle={this.props.optionBarToggle} currentUser={this.props.currentUser} /> ); diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js index b4104652feb..dc1aa2de123 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeader.js @@ -19,41 +19,88 @@ */ // @flow import React from 'react'; +import classNames from 'classnames'; import SearchFilterContainer from '../filters/SearchFilterContainer'; +import Tooltip from '../../../components/controls/Tooltip'; +import PerspectiveSelect from './PerspectiveSelect'; +import ProjectsSortingSelect from './ProjectsSortingSelect'; import { translate } from '../../../helpers/l10n'; -type Props = { +type Props = {| + currentUser?: { isLoggedIn: boolean }, isFavorite?: boolean, - loading: boolean, - onOpenOptionBar: () => void, - optionBarOpen?: boolean, + onPerspectiveChange: ({ view: string, visualization?: string }) => void, organization?: { key: string }, + projects: Array<*>, + projectsAppState: { loading: boolean, total?: number }, query: { [string]: string }, - total?: number -}; + onSortChange: (sort: string, desc: boolean) => void, + selectedSort: string, + view: string, + visualization?: string +|}; export default function PageHeader(props: Props) { + const renderSortingSelect = () => { + const { projectsAppState, projects, currentUser, view } = props; + const limitReached = + projects != null && + projectsAppState.total != null && + projects.length < projectsAppState.total; + const defaultOption = currentUser && currentUser.isLoggedIn ? 'name' : 'analysis_date'; + if (view === 'visualizations' && !limitReached) { + return ( + <Tooltip overlay={translate('projects.sort.disabled')}> + <div className="projects-topbar-item disabled"> + <ProjectsSortingSelect + className="js-projects-sorting-select" + defaultOption={defaultOption} + onChange={props.onSortChange} + selectedSort={props.selectedSort} + view={props.view} + /> + </div> + </Tooltip> + ); + } + return ( + <ProjectsSortingSelect + className="projects-topbar-item js-projects-sorting-select" + defaultOption={defaultOption} + onChange={props.onSortChange} + selectedSort={props.selectedSort} + view={props.view} + /> + ); + }; + return ( - <header className="page-header"> + <header className="page-header projects-topbar-items"> + <PerspectiveSelect + className="projects-topbar-item js-projects-perspective-select" + onChange={props.onPerspectiveChange} + view={props.view} + visualization={props.visualization} + /> + + {renderSortingSelect()} + <SearchFilterContainer + className="projects-topbar-item projects-topbar-item-search" isFavorite={props.isFavorite} organization={props.organization} query={props.query} /> - <div className="page-actions projects-page-actions text-right"> - {!props.optionBarOpen && - <a - className="button js-projects-topbar-open spacer-right" - href="#" - onClick={props.onOpenOptionBar}> - {translate('projects.view_settings')} - </a>} - {!!props.loading && <i className="spinner spacer-right" />} + <div + className={classNames('projects-topbar-item', 'is-last', { + 'is-loading': props.projectsAppState.loading + })}> + {!!props.projectsAppState.loading && <i className="spinner spacer-right" />} - {props.total != null && + {props.projectsAppState.total != null && <span> - <strong id="projects-total">{props.total}</strong> + <strong id="projects-total">{props.projectsAppState.total}</strong> {' '} {translate('projects._projects')} </span>} diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js b/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js index 5d817b926fe..7780da0a705 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PageHeaderContainer.js @@ -19,6 +19,11 @@ */ import { connect } from 'react-redux'; import PageHeader from './PageHeader'; -import { getProjectsAppState } from '../../../store/rootReducer'; +import { getProjects, getProjectsAppState } from '../../../store/rootReducer'; -export default connect(state => getProjectsAppState(state))(PageHeader); +const mapStateToProps = state => ({ + projects: getProjects(state), + projectsAppState: getProjectsAppState(state) +}); + +export default connect(mapStateToProps)(PageHeader); diff --git a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js index 2580a34c4b8..8f6f5f41c0d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PerspectiveSelect.js @@ -26,12 +26,12 @@ import { VIEWS, VISUALIZATIONS } from '../utils'; export type Option = { label: string, type: string, value: string }; -type Props = { +type Props = {| className?: string, onChange: ({ view: string, visualization?: string }) => void, view: string, visualization?: string -}; +|}; export default class PerspectiveSelect extends React.PureComponent { options: Array<Option>; diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBar.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBar.js deleted file mode 100644 index 98537d80eba..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBar.js +++ /dev/null @@ -1,106 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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. - */ -//@flow -import React from 'react'; -import classNames from 'classnames'; -import Tooltip from '../../../components/controls/Tooltip'; -import PerspectiveSelect from './PerspectiveSelect'; -import ProjectsSortingSelect from './ProjectsSortingSelect'; -import { translate } from '../../../helpers/l10n'; - -type Props = { - onPerspectiveChange: ({ view: string, visualization?: string }) => void, - onSortChange: (sort: string, desc: boolean) => void, - onToggleOptionBar: boolean => void, - open: boolean, - projects: Array<*>, - projectsAppState: { loading: boolean, total?: number }, - selectedSort: string, - currentUser?: { isLoggedIn: boolean }, - view: string, - visualization?: string -}; - -export default class ProjectsOptionBar extends React.PureComponent { - props: Props; - - closeBar = (evt: Event & { currentTarget: HTMLElement }) => { - evt.currentTarget.blur(); - evt.preventDefault(); - this.props.onToggleOptionBar(false); - }; - - renderSortingSelect() { - const { projectsAppState, projects, currentUser, view } = this.props; - const limitReached = - projects != null && - projectsAppState.total != null && - projects.length < projectsAppState.total; - const defaultOption = currentUser && currentUser.isLoggedIn ? 'name' : 'analysis_date'; - if (view === 'visualizations' && !limitReached) { - return ( - <Tooltip overlay={translate('projects.sort.disabled')}> - <div> - <ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select disabled" - defaultOption={defaultOption} - onChange={this.props.onSortChange} - selectedSort={this.props.selectedSort} - view={this.props.view} - /> - </div> - </Tooltip> - ); - } - return ( - <ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select" - defaultOption={defaultOption} - onChange={this.props.onSortChange} - selectedSort={this.props.selectedSort} - view={this.props.view} - /> - ); - } - - render() { - const { open } = this.props; - return ( - <div className="projects-topbar"> - <div className={classNames('projects-topbar-actions', { open })}> - <div className="projects-topbar-actions-inner"> - <button className="projects-topbar-button" onClick={this.closeBar}> - {translate('close')} - </button> - <div className="projects-topbar-items"> - <PerspectiveSelect - className="projects-topbar-item js-projects-perspective-select" - onChange={this.props.onPerspectiveChange} - view={this.props.view} - visualization={this.props.visualization} - /> - {this.renderSortingSelect()} - </div> - </div> - </div> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBarContainer.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBarContainer.js deleted file mode 100644 index a58605b2355..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsOptionBarContainer.js +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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 { connect } from 'react-redux'; -import ProjectsOptionBar from './ProjectsOptionBar'; -import { getProjects, getProjectsAppState } from '../../../store/rootReducer'; - -const mapStateToProps = state => ({ - projects: getProjects(state), - projectsAppState: getProjectsAppState(state) -}); - -export default connect(mapStateToProps)(ProjectsOptionBar); diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js index 51d6f1fdd5e..2d97d5cad68 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsSortingSelect.js @@ -65,10 +65,7 @@ export default class ProjectsSortingSelect extends React.PureComponent { opt => (opt.value === this.props.defaultOption ? 0 : 1) ).map((opt: { value: string, class?: string }) => ({ value: opt.value, - label: translate('projects.sorting', opt.value) + - (opt.value === this.props.defaultOption - ? ` (${translate('projects.sorting.default')})` - : ''), + label: translate('projects.sorting', opt.value), class: opt.class })); }; @@ -88,7 +85,7 @@ export default class ProjectsSortingSelect extends React.PureComponent { <div className={this.props.className}> <label>{translate('projects.sort_by')}:</label> <Select - className="little-spacer-left input-large" + className="little-spacer-left input-medium" clearable={false} onChange={this.handleSortChange} optionComponent={ProjectsSortingSelectOption} diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js index ef934431b52..0a9b1b2aca0 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js @@ -22,17 +22,65 @@ import { shallow } from 'enzyme'; import PageHeader from '../PageHeader'; it('should render correctly', () => { - expect(shallow(<PageHeader query={{ search: 'test' }} total="12" />)).toMatchSnapshot(); + expect( + shallow(<PageHeader query={{ search: 'test' }} projectsAppState={{ total: 12 }} />) + ).toMatchSnapshot(); }); it('should render correctly while loading', () => { expect( - shallow(<PageHeader query={{ search: '' }} loading={true} isFavorite={true} total="2" />) + shallow( + <PageHeader + query={{ search: '' }} + isFavorite={true} + projectsAppState={{ loading: true, total: 2 }} + /> + ) ).toMatchSnapshot(); }); it('should not render projects total', () => { expect( - shallow(<PageHeader query={{ search: '' }} />).find('#projects-total').exists() + shallow(<PageHeader projectsAppState={{}} query={{ search: '' }} />) + .find('#projects-total') + .exists() ).toBeFalsy(); }); + +it('should render disabled sorting options for visualizations', () => { + expect( + shallow( + <PageHeader + open={true} + projectsAppState={{}} + view="visualizations" + visualization="coverage" + /> + ) + ).toMatchSnapshot(); +}); + +it('should render switch the default sorting option for anonymous users', () => { + expect( + shallow( + <PageHeader + currentUser={{ isLoggedIn: true }} + open={true} + projectsAppState={{}} + view="overall" + visualization="risk" + /> + ).find('ProjectsSortingSelect') + ).toMatchSnapshot(); + expect( + shallow( + <PageHeader + currentUser={{ isLoggedIn: false }} + open={true} + projectsAppState={{}} + view="leak" + visualization="risk" + /> + ).find('ProjectsSortingSelect') + ).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectOptionBar-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectOptionBar-test.js deleted file mode 100644 index c496b37c7d7..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectOptionBar-test.js +++ /dev/null @@ -1,78 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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 React from 'react'; -import { shallow } from 'enzyme'; -import ProjectsOptionBar from '../ProjectsOptionBar'; -import { click } from '../../../../helpers/testUtils'; - -it('should render option bar closed', () => { - expect(shallow(<ProjectsOptionBar open={false} view="overall" />)).toMatchSnapshot(); -}); - -it('should render option bar open', () => { - expect( - shallow( - <ProjectsOptionBar - open={true} - view="leak" - visualization="risk" - projects={[1, 2, 3]} - projectsAppState={{ total: 3 }} - currentUser={{ isLoggedIn: true }} - /> - ) - ).toMatchSnapshot(); -}); - -it('should render disabled sorting options for visualizations', () => { - expect( - shallow(<ProjectsOptionBar open={true} view="visualizations" visualization="coverage" />) - ).toMatchSnapshot(); -}); - -it('should call close method correctly', () => { - const toggle = jest.fn(); - const wrapper = shallow(<ProjectsOptionBar open={true} view="leak" onToggleOptionBar={toggle} />); - click(wrapper.find('.projects-topbar-button')); - expect(toggle.mock.calls).toMatchSnapshot(); -}); - -it('should render switch the default sorting option for anonymous users', () => { - expect( - shallow( - <ProjectsOptionBar - open={true} - view="overall" - visualization="risk" - currentUser={{ isLoggedIn: true }} - /> - ).find('ProjectsSortingSelect') - ).toMatchSnapshot(); - expect( - shallow( - <ProjectsOptionBar - open={true} - view="leak" - visualization="risk" - currentUser={{ isLoggedIn: false }} - /> - ).find('ProjectsSortingSelect') - ).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap index c554011553e..17c8eddd16c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap @@ -2,9 +2,17 @@ exports[`should render correctly 1`] = ` <header - className="page-header" + className="page-header projects-topbar-items" > + <PerspectiveSelect + className="projects-topbar-item js-projects-perspective-select" + /> + <ProjectsSortingSelect + className="projects-topbar-item js-projects-sorting-select" + defaultOption="analysis_date" + /> <withRouter(SearchFilterContainer) + className="projects-topbar-item projects-topbar-item-search" query={ Object { "search": "test", @@ -12,14 +20,8 @@ exports[`should render correctly 1`] = ` } /> <div - className="page-actions projects-page-actions text-right" + className="projects-topbar-item is-last" > - <a - className="button js-projects-topbar-open spacer-right" - href="#" - > - projects.view_settings - </a> <span> <strong id="projects-total" @@ -35,9 +37,17 @@ exports[`should render correctly 1`] = ` exports[`should render correctly while loading 1`] = ` <header - className="page-header" + className="page-header projects-topbar-items" > + <PerspectiveSelect + className="projects-topbar-item js-projects-perspective-select" + /> + <ProjectsSortingSelect + className="projects-topbar-item js-projects-sorting-select" + defaultOption="analysis_date" + /> <withRouter(SearchFilterContainer) + className="projects-topbar-item projects-topbar-item-search" isFavorite={true} query={ Object { @@ -46,14 +56,8 @@ exports[`should render correctly while loading 1`] = ` } /> <div - className="page-actions projects-page-actions text-right" + className="projects-topbar-item is-last is-loading" > - <a - className="button js-projects-topbar-open spacer-right" - href="#" - > - projects.view_settings - </a> <i className="spinner spacer-right" /> @@ -69,3 +73,51 @@ exports[`should render correctly while loading 1`] = ` </div> </header> `; + +exports[`should render disabled sorting options for visualizations 1`] = ` +<header + className="page-header projects-topbar-items" +> + <PerspectiveSelect + className="projects-topbar-item js-projects-perspective-select" + view="visualizations" + visualization="coverage" + /> + <Tooltip + overlay="projects.sort.disabled" + placement="bottom" + > + <div + className="projects-topbar-item disabled" + > + <ProjectsSortingSelect + className="js-projects-sorting-select" + defaultOption="analysis_date" + view="visualizations" + /> + </div> + </Tooltip> + <withRouter(SearchFilterContainer) + className="projects-topbar-item projects-topbar-item-search" + /> + <div + className="projects-topbar-item is-last" + /> +</header> +`; + +exports[`should render switch the default sorting option for anonymous users 1`] = ` +<ProjectsSortingSelect + className="projects-topbar-item js-projects-sorting-select" + defaultOption="name" + view="overall" +/> +`; + +exports[`should render switch the default sorting option for anonymous users 2`] = ` +<ProjectsSortingSelect + className="projects-topbar-item js-projects-sorting-select" + defaultOption="analysis_date" + view="leak" +/> +`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectOptionBar-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectOptionBar-test.js.snap deleted file mode 100644 index 15051129d68..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectOptionBar-test.js.snap +++ /dev/null @@ -1,136 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should call close method correctly 1`] = ` -Array [ - Array [ - false, - ], -] -`; - -exports[`should render disabled sorting options for visualizations 1`] = ` -<div - className="projects-topbar" -> - <div - className="projects-topbar-actions open" - > - <div - className="projects-topbar-actions-inner" - > - <button - className="projects-topbar-button" - onClick={[Function]} - > - close - </button> - <div - className="projects-topbar-items" - > - <PerspectiveSelect - className="projects-topbar-item js-projects-perspective-select" - view="visualizations" - visualization="coverage" - /> - <Tooltip - overlay="projects.sort.disabled" - placement="bottom" - > - <div> - <ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select disabled" - defaultOption="analysis_date" - view="visualizations" - /> - </div> - </Tooltip> - </div> - </div> - </div> -</div> -`; - -exports[`should render option bar closed 1`] = ` -<div - className="projects-topbar" -> - <div - className="projects-topbar-actions" - > - <div - className="projects-topbar-actions-inner" - > - <button - className="projects-topbar-button" - onClick={[Function]} - > - close - </button> - <div - className="projects-topbar-items" - > - <PerspectiveSelect - className="projects-topbar-item js-projects-perspective-select" - view="overall" - /> - <ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select" - defaultOption="analysis_date" - view="overall" - /> - </div> - </div> - </div> -</div> -`; - -exports[`should render option bar open 1`] = ` -<div - className="projects-topbar" -> - <div - className="projects-topbar-actions open" - > - <div - className="projects-topbar-actions-inner" - > - <button - className="projects-topbar-button" - onClick={[Function]} - > - close - </button> - <div - className="projects-topbar-items" - > - <PerspectiveSelect - className="projects-topbar-item js-projects-perspective-select" - view="leak" - visualization="risk" - /> - <ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select" - defaultOption="name" - view="leak" - /> - </div> - </div> - </div> -</div> -`; - -exports[`should render switch the default sorting option for anonymous users 1`] = ` -<ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select" - defaultOption="name" - view="overall" -/> -`; - -exports[`should render switch the default sorting option for anonymous users 2`] = ` -<ProjectsSortingSelect - className="projects-topbar-item js-projects-sorting-select" - defaultOption="analysis_date" - view="leak" -/> -`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap index 7a004599bfb..21c3b3f5d5a 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectsSortingSelect-test.js.snap @@ -12,7 +12,7 @@ exports[`should handle the descending sort direction 1`] = ` autosize={true} backspaceRemoves={true} backspaceToRemoveMessage="Press backspace to remove {label}" - className="little-spacer-left input-large" + className="little-spacer-left input-medium" clearAllText="Clear all" clearValueText="Clear value" clearable={false} @@ -41,7 +41,7 @@ exports[`should handle the descending sort direction 1`] = ` Array [ Object { "class": undefined, - "label": "projects.sorting.name (projects.sorting.default)", + "label": "projects.sorting.name", "value": "name", }, Object { @@ -121,7 +121,7 @@ exports[`should render correctly for leak view 1`] = ` autosize={true} backspaceRemoves={true} backspaceToRemoveMessage="Press backspace to remove {label}" - className="little-spacer-left input-large" + className="little-spacer-left input-medium" clearAllText="Clear all" clearValueText="Clear value" clearable={false} @@ -150,7 +150,7 @@ exports[`should render correctly for leak view 1`] = ` Array [ Object { "class": undefined, - "label": "projects.sorting.analysis_date (projects.sorting.default)", + "label": "projects.sorting.analysis_date", "value": "analysis_date", }, Object { @@ -230,7 +230,7 @@ exports[`should render correctly for overall view 1`] = ` autosize={true} backspaceRemoves={true} backspaceToRemoveMessage="Press backspace to remove {label}" - className="little-spacer-left input-large" + className="little-spacer-left input-medium" clearAllText="Clear all" clearValueText="Clear value" clearable={false} @@ -259,7 +259,7 @@ exports[`should render correctly for overall view 1`] = ` Array [ Object { "class": undefined, - "label": "projects.sorting.name (projects.sorting.default)", + "label": "projects.sorting.name", "value": "name", }, Object { diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js index e6bcf825923..506bec6175f 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilter.js @@ -22,6 +22,7 @@ import React from 'react'; import { translate, translateWithParameters } from '../../../helpers/l10n'; type Props = { + className?: string, handleSearch: (userString?: string) => void, query: { search?: string } }; @@ -63,11 +64,10 @@ export default class SearchFilter extends React.PureComponent { const { userQuery } = this.state; const shortQuery = userQuery != null && userQuery.length === 1; return ( - <div className="projects-facet-search" data-key="search"> + <div className={this.props.className}> <input type="search" value={userQuery || ''} - className="input-super-large" placeholder={translate('projects.search')} onChange={this.handleQueryChange} autoComplete="off" diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js index ec6bad4c55d..74b16a51ae3 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.js @@ -24,12 +24,13 @@ import { debounce } from 'lodash'; import { getFilterUrl } from './utils'; import SearchFilter from './SearchFilter'; -type Props = { +type Props = {| + className?: string, query: { search?: string }, router: { push: ({ pathname: string }) => void }, isFavorite?: boolean, organization?: {} -}; +|}; class SearchFilterContainer extends React.PureComponent { handleSearch: (userQuery?: string) => void; @@ -46,7 +47,13 @@ class SearchFilterContainer extends React.PureComponent { } render() { - return <SearchFilter query={this.props.query} handleSearch={this.handleSearch} />; + return ( + <SearchFilter + className={this.props.className} + query={this.props.query} + handleSearch={this.handleSearch} + /> + ); } } diff --git a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap index a48f32ac99c..86e0c761aa1 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SearchFilter-test.js.snap @@ -1,13 +1,9 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should display a help message when there is less than 2 characters 1`] = ` -<div - className="projects-facet-search" - data-key="search" -> +<div> <input autoComplete="off" - className="input-super-large" onChange={[Function]} placeholder="projects.search" type="search" @@ -22,13 +18,9 @@ exports[`should display a help message when there is less than 2 characters 1`] `; exports[`should display a help message when there is less than 2 characters 2`] = ` -<div - className="projects-facet-search" - data-key="search" -> +<div> <input autoComplete="off" - className="input-super-large" onChange={[Function]} placeholder="projects.search" type="search" @@ -38,13 +30,9 @@ exports[`should display a help message when there is less than 2 characters 2`] `; exports[`should render correctly without any search query 1`] = ` -<div - className="projects-facet-search" - data-key="search" -> +<div> <input autoComplete="off" - className="input-super-large" onChange={[Function]} placeholder="projects.search" type="search" @@ -54,13 +42,9 @@ exports[`should render correctly without any search query 1`] = ` `; exports[`should render with a search query 1`] = ` -<div - className="projects-facet-search" - data-key="search" -> +<div> <input autoComplete="off" - className="input-super-large" onChange={[Function]} placeholder="projects.search" type="search" diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css index f9b1b627502..2af711764d0 100644 --- a/server/sonar-web/src/main/js/apps/projects/styles.css +++ b/server/sonar-web/src/main/js/apps/projects/styles.css @@ -1,7 +1,3 @@ -.projects-page-actions { - margin-bottom: 0; -} - .projects-page-side { transition: top 150ms ease-out; } @@ -10,48 +6,27 @@ transition: padding-top 150ms ease-out; } -.projects-topbar { - position: fixed; - width: 100%; - z-index: 100; -} - -.projects-topbar-actions { - box-sizing: border-box; - position: absolute; - left: 0; - right: 0; - top: -50px; - z-index: 50; - flex-grow: 0.000001; - border-bottom: 1px solid #e6e6e6; - background-color: #fff; - transition: top 150ms ease-out; +.projects-topbar-items { + display: flex; + align-items: center; + flex-grow: 1; } -.projects-topbar-actions.open { - top: 0; +.projects-topbar-item + .projects-topbar-item { + padding-left: 24px; } -.projects-topbar-actions-inner { - position: relative; - margin: auto; - padding: 0 20px; - min-width: 1040px; - max-width: 1280px; +.projects-topbar-item .spinner { + top: -1px; } -.projects-topbar-items { - display: flex; - flex-wrap: nowrap; - justify-content: center; - align-items: center; - flex-grow: 1; - height: 46px; +.projects-topbar-item.is-last { + margin-left: auto; + padding-left: 32px; } -.projects-topbar-item { - padding: 0 24px; +.projects-topbar-item.is-loading { + padding-left: 0; } .projects-topbar-item.disabled { @@ -63,14 +38,22 @@ pointer-events: none !important; } -.projects-topbar-button { - position: absolute; - right: 20px; - top: 10px; +.projects-topbar-item-search { + position: relative; + flex: 1; } -.projects-sidebar { - width: 260px; +.projects-topbar-item-search input { + width: 100%; + max-width: 300px; +} + +.projects-topbar-item-search .note { + position: absolute; + top: 1px; + left: 80px; + line-height: 24px; + pointer-events: none; } .projects-list .page-actions { @@ -237,26 +220,12 @@ border-bottom: 1px solid #e6e6e6; } -.projects-facet-search { - position: absolute; - bottom: 0; - left: 0; - width: 300px; -} - -.projects-facet-search .note { - position: absolute; - top: 1px; - right: 30px; - line-height: 24px; - pointer-events: none; -} - .projects-facets-reset { float: right; } -.projects-facets-reset .button {} +.projects-facets-reset .button { +} .projects-facet-bar { display: inline-block; @@ -272,7 +241,8 @@ } .search-navigator-facet.active .projects-facet-bar-inner, -.search-navigator-facet-highlight-under-container .search-navigator-facet.active ~ .search-navigator-facet .projects-facet-bar-inner { +.search-navigator-facet-highlight-under-container .search-navigator-facet.active + ~ .search-navigator-facet .projects-facet-bar-inner { background-color: #4b9fd5; } diff --git a/server/sonar-web/src/main/less/components/page.less b/server/sonar-web/src/main/less/components/page.less index 6ab19d4d724..fa128f99095 100644 --- a/server/sonar-web/src/main/less/components/page.less +++ b/server/sonar-web/src/main/less/components/page.less @@ -178,15 +178,19 @@ background: #f3f3f3; @media (max-width: 1335px) { - & { width: 310px; } + & { + width: 310px; } + } .search-navigator-facets-list { width: 260px; margin-left: ~"calc(50vw - 640px + 290px - 260px - 37px)"; @media (max-width: 1335px) { - & { margin-left: 20px; } + & { + margin-left: 20px; + } } } } @@ -241,6 +245,40 @@ background-color: #f3f3f3; } +.layout-page-header-panel, .layout-page-header-panel-inner { + height: 56px; + box-sizing: border-box; +} + +.layout-page-header-panel { + margin-top: -20px; +} + +.layout-page-header-panel-inner { + position: fixed; + z-index: 30; + line-height: 24px; + padding-top: 16px; + padding-bottom: 16px; + border-bottom: 1px solid #e6e6e6; + background-color: #f3f3f3; +} + +.layout-page-main-header { + margin-bottom: 20px; +} + +.layout-page-main-header .component-name { + line-height: 24px; +} + +.layout-page-main-header-inner { + left: ~"calc(50vw - 360px + 1px)"; + right: 0; + padding-left: 20px; + padding-right: 20px; +} + @media (max-width: 1320px) { .layout-page-side-outer { width: 300px; @@ -253,4 +291,8 @@ .layout-page-side-inner { margin-left: 0; } -}
\ No newline at end of file + + .layout-page-main-header-inner { + left: 301px; + } +} |