diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-05-29 16:51:25 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-06-09 08:26:48 +0200 |
commit | 0c575091622e0992de4c856d02e34b3e037f8f9f (patch) | |
tree | 609c1a8aa43d0e6fa3adbd1c62594ac7a6b085de /server | |
parent | 8a173aa3175c846291b8b56f2cfd84ab0e782627 (diff) | |
download | sonarqube-0c575091622e0992de4c856d02e34b3e037f8f9f.tar.gz sonarqube-0c575091622e0992de4c856d02e34b3e037f8f9f.zip |
SONAR-9254 Move the projects search box on top of the projects cards
Diffstat (limited to 'server')
9 files changed, 155 insertions, 62 deletions
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 8b172222b75..46d752be2db 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 @@ -120,6 +120,7 @@ export default class AllProjects extends React.PureComponent { }; render() { + const { isFavorite, organization } = this.props; const { query, optionBarOpen } = this.state; const isFiltered = Object.keys(query).some(key => query[key] != null); @@ -127,7 +128,7 @@ export default class AllProjects extends React.PureComponent { const visualization = query.visualization || 'risk'; const selectedSort = query.sort || 'name'; - const top = (this.props.organization ? 95 : 30) + (optionBarOpen ? 45 : 0); + const top = (organization ? 95 : 30) + (optionBarOpen ? 45 : 0); return ( <div> @@ -149,8 +150,8 @@ export default class AllProjects extends React.PureComponent { <div className="layout-page-side-inner"> <div className="layout-page-filters"> <PageSidebar - isFavorite={this.props.isFavorite} - organization={this.props.organization} + isFavorite={isFavorite} + organization={organization} query={query} view={view} visualization={visualization} @@ -162,19 +163,24 @@ export default class AllProjects extends React.PureComponent { <div className="layout-page-main"> <div className="layout-page-main-inner"> - <PageHeaderContainer onOpenOptionBar={this.openOptionBar} /> + <PageHeaderContainer + query={query} + isFavorite={isFavorite} + organization={organization} + onOpenOptionBar={this.openOptionBar} + /> {view !== 'visualizations' && <ProjectsListContainer - isFavorite={this.props.isFavorite} + isFavorite={isFavorite} isFiltered={isFiltered} - organization={this.props.organization} + organization={organization} cardType={view} />} {view !== 'visualizations' && <ProjectsListFooterContainer query={query} - isFavorite={this.props.isFavorite} - organization={this.props.organization} + isFavorite={isFavorite} + organization={organization} />} {view === 'visualizations' && <VisualizationsContainer sort={query.sort} visualization={visualization} />} 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 f3866ee3935..00022b19545 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,17 +19,26 @@ */ // @flow import React from 'react'; +import SearchFilterContainer from '../filters/SearchFilterContainer'; import { translate } from '../../../helpers/l10n'; type Props = { + isFavorite?: boolean, loading: boolean, onOpenOptionBar: () => void, + organization?: { key: string }, + query: { [string]: string }, total?: number }; export default function PageHeader(props: Props) { return ( <header className="page-header"> + <SearchFilterContainer + isFavorite={props.isFavorite} + organization={props.organization} + query={props.query} + /> <div className="page-actions projects-page-actions text-right"> <div className="spacer-bottom"> <a className="button js-projects-topbar-open" href="#" onClick={props.onOpenOptionBar}> diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js index 3e7ca9e4591..302ee1941ff 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js @@ -34,7 +34,6 @@ import NewLinesFilter from '../filters/NewLinesFilter'; import QualityGateFilter from '../filters/QualityGateFilter'; import ReliabilityFilter from '../filters/ReliabilityFilter'; import SecurityFilter from '../filters/SecurityFilter'; -import SearchFilterContainer from '../filters/SearchFilterContainer'; import SizeFilter from '../filters/SizeFilter'; import TagsFilterContainer from '../filters/TagsFilterContainer'; import { translate } from '../../../helpers/l10n'; @@ -84,7 +83,6 @@ export default function PageSidebar({ </div>} <h3>{translate('filters')}</h3> - <SearchFilterContainer {...facetProps} /> </div> <QualityGateFilter {...facetProps} /> {!isLeakView && [ 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 new file mode 100644 index 00000000000..ef934431b52 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.js @@ -0,0 +1,38 @@ +/* + * 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 PageHeader from '../PageHeader'; + +it('should render correctly', () => { + expect(shallow(<PageHeader query={{ search: 'test' }} total="12" />)).toMatchSnapshot(); +}); + +it('should render correctly while loading', () => { + expect( + shallow(<PageHeader query={{ search: '' }} loading={true} isFavorite={true} total="2" />) + ).toMatchSnapshot(); +}); + +it('should not render projects total', () => { + expect( + shallow(<PageHeader query={{ search: '' }} />).find('#projects-total').exists() + ).toBeFalsy(); +}); 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 new file mode 100644 index 00000000000..9783b6a6c35 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.js.snap @@ -0,0 +1,79 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<header + className="page-header" +> + <withRouter(SearchFilterContainer) + query={ + Object { + "search": "test", + } + } + /> + <div + className="page-actions projects-page-actions text-right" + > + <div + className="spacer-bottom" + > + <a + className="button js-projects-topbar-open" + href="#" + > + projects.view_settings + </a> + </div> + <span> + <strong + id="projects-total" + > + 12 + </strong> + + projects._projects + </span> + </div> +</header> +`; + +exports[`should render correctly while loading 1`] = ` +<header + className="page-header" +> + <withRouter(SearchFilterContainer) + isFavorite={true} + query={ + Object { + "search": "", + } + } + /> + <div + className="page-actions projects-page-actions text-right" + > + <div + className="spacer-bottom" + > + <a + className="button js-projects-topbar-open" + href="#" + > + projects.view_settings + </a> + </div> + <i + className="spinner spacer-right" + /> + <span> + <strong + id="projects-total" + > + 2 + </strong> + + projects._projects + </span> + </div> +</header> +`; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap index 5460d9a0e3d..d48dbb01e6d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap @@ -40,14 +40,6 @@ exports[`should render \`leak\` view correctly 1`] = ` <h3> filters </h3> - <withRouter(SearchFilterContainer) - isFavorite={false} - query={ - Object { - "view": "leak", - } - } - /> </div> <QualityGateFilter isFavorite={false} @@ -151,14 +143,6 @@ exports[`should render correctly 1`] = ` <h3> filters </h3> - <withRouter(SearchFilterContainer) - isFavorite={true} - query={ - Object { - "size": "3", - } - } - /> </div> <QualityGateFilter isFavorite={true} 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 1ce1141fa11..e6bcf825923 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 @@ -19,7 +19,6 @@ */ // @flow import React from 'react'; -import classNames from 'classnames'; import { translate, translateWithParameters } from '../../../helpers/l10n'; type Props = { @@ -62,23 +61,21 @@ export default class SearchFilter extends React.PureComponent { render() { const { userQuery } = this.state; - const inputClassName = classNames('input-super-large', { - touched: userQuery != null && userQuery.length === 1 - }); - + const shortQuery = userQuery != null && userQuery.length === 1; return ( <div className="projects-facet-search" data-key="search"> <input type="search" value={userQuery || ''} - className={inputClassName} + className="input-super-large" placeholder={translate('projects.search')} onChange={this.handleQueryChange} autoComplete="off" /> - <span className="note spacer-left"> - {translateWithParameters('select2.tooShort', 2)} - </span> + {shortQuery && + <span className="note spacer-left"> + {translateWithParameters('select2.tooShort', 2)} + </span>} </div> ); } 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 0fd5b0f6cb4..a48f32ac99c 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 @@ -7,7 +7,7 @@ exports[`should display a help message when there is less than 2 characters 1`] > <input autoComplete="off" - className="input-super-large touched" + className="input-super-large" onChange={[Function]} placeholder="projects.search" type="search" @@ -34,11 +34,6 @@ exports[`should display a help message when there is less than 2 characters 2`] type="search" value="foo" /> - <span - className="note spacer-left" - > - select2.tooShort.2 - </span> </div> `; @@ -55,11 +50,6 @@ exports[`should render correctly without any search query 1`] = ` type="search" value="" /> - <span - className="note spacer-left" - > - select2.tooShort.2 - </span> </div> `; @@ -76,10 +66,5 @@ exports[`should render with a search query 1`] = ` type="search" value="foo" /> - <span - className="note spacer-left" - > - select2.tooShort.2 - </span> </div> `; 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 a6c471bef8c..afe6b79b614 100644 --- a/server/sonar-web/src/main/js/apps/projects/styles.css +++ b/server/sonar-web/src/main/js/apps/projects/styles.css @@ -223,21 +223,18 @@ } .projects-facet-search { - position: relative; - padding-top: 10px; - padding-bottom: 10px; -} - -.projects-facet-search .note { position: absolute; - opacity: 0; + bottom: 0; left: 0; - bottom: -7px; - transition: opacity 0.3s ease; + width: 300px; } -.projects-facet-search input.touched ~ .note { - opacity: 1; +.projects-facet-search .note { + position: absolute; + top: 1px; + right: 30px; + line-height: 24px; + pointer-events: none; } .projects-facets-reset { |