};
render() {
+ const { isFavorite, organization } = this.props;
const { query, optionBarOpen } = this.state;
const isFiltered = Object.keys(query).some(key => query[key] != null);
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>
<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}
<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} />}
*/
// @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}>
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';
</div>}
<h3>{translate('filters')}</h3>
- <SearchFilterContainer {...facetProps} />
</div>
<QualityGateFilter {...facetProps} />
{!isLeakView && [
--- /dev/null
+/*
+ * 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();
+});
--- /dev/null
+// 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>
+`;
<h3>
filters
</h3>
- <withRouter(SearchFilterContainer)
- isFavorite={false}
- query={
- Object {
- "view": "leak",
- }
- }
- />
</div>
<QualityGateFilter
isFavorite={false}
<h3>
filters
</h3>
- <withRouter(SearchFilterContainer)
- isFavorite={true}
- query={
- Object {
- "size": "3",
- }
- }
- />
</div>
<QualityGateFilter
isFavorite={true}
*/
// @flow
import React from 'react';
-import classNames from 'classnames';
import { translate, translateWithParameters } from '../../../helpers/l10n';
type Props = {
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>
);
}
>
<input
autoComplete="off"
- className="input-super-large touched"
+ className="input-super-large"
onChange={[Function]}
placeholder="projects.search"
type="search"
type="search"
value="foo"
/>
- <span
- className="note spacer-left"
- >
- select2.tooShort.2
- </span>
</div>
`;
type="search"
value=""
/>
- <span
- className="note spacer-left"
- >
- select2.tooShort.2
- </span>
</div>
`;
type="search"
value="foo"
/>
- <span
- className="note spacer-left"
- >
- select2.tooShort.2
- </span>
</div>
`;
}
.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 {