+++ /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.
- */
-// @flow
-import React from 'react';
-import PropTypes from 'prop-types';
-import { withRouter } from 'react-router';
-import { connect } from 'react-redux';
-import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
-
-class Landing extends React.PureComponent {
- static propTypes = {
- currentUser: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).isRequired
- };
-
- componentDidMount() {
- const { currentUser, router, onSonarCloud } = this.props;
- if (currentUser.isLoggedIn) {
- router.replace('/projects');
- } else if (onSonarCloud && onSonarCloud.value === 'true') {
- window.location = 'https://about.sonarcloud.io';
- } else {
- router.replace('/about');
- }
- }
-
- render() {
- return null;
- }
-}
-
-const mapStateToProps = state => ({
- currentUser: getCurrentUser(state),
- onSonarCloud: getGlobalSettingValue(state, 'sonar.sonarcloud.enabled')
-});
-
-export default connect(mapStateToProps)(withRouter(Landing));
--- /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 * as React from 'react';
+import * as PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
+import { CurrentUser, isLoggedIn } from '../types';
+
+interface Props {
+ currentUser: CurrentUser;
+ onSonarCloud: boolean;
+}
+
+class Landing extends React.PureComponent<Props> {
+ static contextTypes = {
+ router: PropTypes.object.isRequired
+ };
+
+ componentDidMount() {
+ const { currentUser, onSonarCloud } = this.props;
+ if (isLoggedIn(currentUser)) {
+ this.context.router.replace('/projects');
+ } else if (onSonarCloud) {
+ window.location.href = 'https://about.sonarcloud.io';
+ } else {
+ this.context.router.replace('/about');
+ }
+ }
+
+ render() {
+ return null;
+ }
+}
+
+const mapStateToProps = (state: any) => {
+ const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
+ return {
+ currentUser: getCurrentUser(state),
+ onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true')
+ };
+};
+
+export default connect<Props>(mapStateToProps)(Landing);
currentUser: { isLoggedIn: boolean },
onClose: () => void,
onTutorialSelect: () => void,
- sonarCloud?: boolean
+ onSonarCloud?: boolean
};
*/
case 'shortcuts':
return <ShortcutsHelp />;
case 'links':
- return this.props.sonarCloud ? (
+ return this.props.onSonarCloud ? (
<LinksHelpSonarCloud onClose={this.props.onClose} />
) : (
<LinksHelp onClose={this.props.onClose} />
currentUser={{ isLoggedIn: false }}
onClose={jest.fn()}
onTutorialSelect={jest.fn()}
- sonarCloud={true}
+ onSonarCloud={true}
/>
);
clickOnSection(wrapper, 'links');
import { connect } from 'react-redux';
import GlobalNavBranding from './GlobalNavBranding';
import GlobalNavMenu from './GlobalNavMenu';
+import GlobalNavExplore from './GlobalNavExplore';
import GlobalNavUserContainer from './GlobalNavUserContainer';
import Search from '../../search/Search';
import GlobalHelp from '../../help/GlobalHelp';
type Props = {
appState: { organizationsEnabled: boolean },
currentUser: { isLoggedIn: boolean, showOnboardingTutorial: boolean },
+ location: { pathname: string },
skipOnboarding: () => void,
- sonarCloud: boolean
+ onSonarCloud: boolean
};
*/
<GlobalNavUserContainer {...this.props} />
</ul>
+ <GlobalNavExplore location={this.props.location} onSonarCloud={this.props.onSonarCloud} />
+
{this.state.helpOpen && (
<GlobalHelp
currentUser={this.props.currentUser}
onClose={this.closeHelp}
onTutorialSelect={this.openOnboardingTutorial}
- sonarCloud={this.props.sonarCloud}
+ onSonarCloud={this.props.onSonarCloud}
/>
)}
return {
currentUser: getCurrentUser(state),
appState: getAppState(state),
- sonarCloud: sonarCloudSetting != null && sonarCloudSetting.value === 'true'
+ onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
};
};
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import { Link } from 'react-router';
+import { translate } from '../../../../helpers/l10n';
+
+interface Props {
+ location: { pathname: string };
+ onSonarCloud: boolean;
+}
+
+export default function GlobalNavExplore({ location, onSonarCloud }: Props) {
+ if (!onSonarCloud) {
+ return null;
+ }
+
+ const active = location.pathname.startsWith('explore');
+
+ return (
+ <ul className="global-navbar-menu spacer-right pull-right">
+ <li>
+ <Link to="/explore/projects" className={active ? 'active' : undefined}>
+ {translate('explore')}
+ </Link>
+ </li>
+ </ul>
+ );
+}
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
+import { isLoggedIn } from '../../../../app/types';
import { translate } from '../../../../helpers/l10n';
import { getQualityGatesUrl } from '../../../../helpers/urls';
import { isMySet } from '../../../../apps/issues/utils';
location: PropTypes.shape({
pathname: PropTypes.string.isRequired
}).isRequired,
- sonarCloud: PropTypes.bool
+ onSonarCloud: PropTypes.bool
};
static defaultProps = {
}
renderProjects() {
+ if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
+ return null;
+ }
+
return (
<li>
<Link to="/projects" activeClassName="active">
- {this.props.sonarCloud ? translate('my_projects') : translate('projects.page')}
+ {this.props.onSonarCloud ? translate('my_projects') : translate('projects.page')}
</Link>
</li>
);
}
renderIssuesLink() {
+ if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
+ return null;
+ }
+
const active = this.props.location.pathname === 'issues';
- if (this.props.sonarCloud) {
+ if (this.props.onSonarCloud) {
return (
<li>
<Link
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import groupsRoutes from '../../apps/groups/routes';
import Issues from '../../apps/issues/components/AppContainer';
+import Explore from '../../apps/explore/Explore';
+import ExploreIssues from '../../apps/explore/ExploreIssues';
+import ExploreProjects from '../../apps/explore/ExploreProjects';
import IssuesPageSelector from '../../apps/issues/IssuesPageSelector';
import marketplaceRoutes from '../../apps/marketplace/routes';
import metricsRoutes from '../../apps/metrics/routes';
<Route path="account" childRoutes={accountRoutes} />
<Route path="coding_rules" childRoutes={codingRulesRoutes} />
<Route path="component" childRoutes={componentRoutes} />
+ <Route path="explore" component={Explore}>
+ <Route path="issues" component={ExploreIssues} />
+ <Route path="projects" component={ExploreProjects} />
+ </Route>
<Route
path="extension/:pluginKey/:extensionKey"
component={GlobalPageExtension}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import { Link } from 'react-router';
+import ContextNavBar from '../../components/nav/ContextNavBar';
+import NavBarTabs from '../../components/nav/NavBarTabs';
+import { translate } from '../../helpers/l10n';
+
+interface Props {
+ children: JSX.Element;
+}
+
+export default function Explore(props: Props) {
+ return (
+ <div id="explore">
+ <ContextNavBar id="explore-navigation" height={65}>
+ <div className="navbar-context-header">
+ <h1 className="display-inline-block">{translate('explore')}</h1>
+ </div>
+
+ <NavBarTabs>
+ <li>
+ <Link to="/explore/projects" activeClassName="active">
+ {translate('projects.page')}
+ </Link>
+ </li>
+ <li>
+ <Link
+ to={{ pathname: '/explore/issues', query: { resolved: 'false' } }}
+ activeClassName="active">
+ {translate('issues.page')}
+ </Link>
+ </li>
+ </NavBarTabs>
+ </ContextNavBar>
+
+ {props.children}
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import AppContainer from '../issues/components/AppContainer';
+import { RawQuery } from '../../helpers/query';
+
+interface Props {
+ location: { pathname: string; query: RawQuery };
+}
+
+export default function ExploreIssues(props: Props) {
+ return <AppContainer myIssues={false} {...props} />;
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import AllProjectsContainer from '../projects/components/AllProjectsContainer';
+import { RawQuery } from '../../helpers/query';
+
+interface Props {
+ location: { pathname: string; query: RawQuery };
+}
+
+export default function ExploreProjects(props: Props) {
+ return <AllProjectsContainer isFavorite={false} {...props} />;
+}
import AppContainer from './components/AppContainer';
import { CurrentUser, isLoggedIn } from '../../app/types';
import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
+import { RawQuery } from '../../helpers/query';
interface StateProps {
currentUser: CurrentUser;
onSonarCloud: boolean;
}
-function IssuesPage({ currentUser, onSonarCloud, ...props }: StateProps) {
+interface Props extends StateProps {
+ location: { pathname: string; query: RawQuery };
+}
+
+function IssuesPage({ currentUser, location, onSonarCloud }: Props) {
const myIssues = (isLoggedIn(currentUser) && onSonarCloud) || undefined;
- return <AppContainer myIssues={myIssues} {...props} />;
+ return <AppContainer location={location} myIssues={myIssues} />;
}
const stateToProps = (state: any) => {
import ComponentBreadcrumbs from './ComponentBreadcrumbs';
import IssuesSourceViewer from './IssuesSourceViewer';
import BulkChangeModal from './BulkChangeModal';
+import NoMyIssues from './NoMyIssues';
import ConciseIssuesList from '../conciseIssuesList/ConciseIssuesList';
import ConciseIssuesListHeader from '../conciseIssuesList/ConciseIssuesListHeader';
import * as actions from '../actions';
<ListFooter total={paging.total} count={issues.length} loadMore={this.fetchMoreIssues} />
)}
- {paging.total === 0 && <EmptySearch />}
+ {paging.total === 0 &&
+ (this.state.myIssues && !this.isFiltered() ? <NoMyIssues /> : <EmptySearch />)}
</div>
);
}
const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps;
interface OwnProps {
+ location: { pathname: string; query: RawQuery };
myIssues?: boolean;
}
--- /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 * as React from 'react';
+import { translate } from '../../../helpers/l10n';
+import '../../../components/common/EmptySearch.css';
+
+export default function NoMyIssues() {
+ return (
+ <div className="empty-search">
+ <h3>{translate('issues.no_my_issues')}</h3>
+ </div>
+ );
+}
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Helmet from 'react-helmet';
+import { omitBy } from 'lodash';
import PageHeader from './PageHeader';
import ProjectsList from './ProjectsList';
import PageSidebar from './PageSidebar';
import Visualizations from '../visualizations/Visualizations';
import { CurrentUser, isLoggedIn } from '../../../app/types';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
+import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import ListFooter from '../../../components/controls/ListFooter';
import { translate } from '../../../helpers/l10n';
import * as storage from '../../../helpers/storage';
export interface Props {
currentUser: CurrentUser;
isFavorite: boolean;
- location: { pathname: string; query: { [x: string]: string } };
+ location: { pathname: string; query: RawQuery };
onSonarCloud: boolean;
organization?: { key: string };
organizationsEnabled: boolean;
}
updateLocationQuery = (newQuery: RawQuery) => {
- this.context.router.push({
- pathname: this.props.location.pathname,
- query: {
- ...this.props.location.query,
- ...newQuery
- }
- });
+ const query = omitBy({ ...this.props.location.query, ...newQuery }, x => !x);
+ this.context.router.push({ pathname: this.props.location.pathname, query });
+ };
+
+ handleClearAll = () => {
+ this.context.router.push({ pathname: this.props.location.pathname });
};
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
- facets={this.state.facets}
- isFavorite={this.props.isFavorite}
- organization={this.props.organization}
- query={this.state.query}
- showFavoriteFilter={!this.props.onSonarCloud}
- view={this.getView()}
- visualization={this.getVisualization()}
- />
+ <ScreenPositionHelper className="layout-page-side-outer">
+ {({ top }) => (
+ <div className="layout-page-side projects-page-side" style={{ top }}>
+ <div className="layout-page-side-inner">
+ <div className="layout-page-filters">
+ <PageSidebar
+ facets={this.state.facets}
+ onClearAll={this.handleClearAll}
+ onQueryChange={this.updateLocationQuery}
+ organization={this.props.organization}
+ query={this.state.query}
+ showFavoriteFilter={!this.props.onSonarCloud}
+ view={this.getView()}
+ visualization={this.getVisualization()}
+ />
+ </div>
</div>
</div>
- </div>
- </div>
+ )}
+ </ScreenPositionHelper>
);
renderHeader = () => (
<div className="layout-page-main-inner">
<PageHeader
currentUser={this.props.currentUser}
- isFavorite={this.props.isFavorite}
loading={this.state.loading}
onPerspectiveChange={this.handlePerspectiveChange}
+ onQueryChange={this.updateLocationQuery}
onSortChange={this.handleSortChange}
organization={this.props.organization}
projects={this.state.projects}
cardType={this.getView()}
isFavorite={this.props.isFavorite}
isFiltered={this.isFiltered()}
+ onSonarCloud={this.props.onSonarCloud}
organization={this.props.organization}
projects={this.state.projects}
query={this.state.query}
{this.renderSide()}
- <div className="layout-page-main projects-page-content">
+ <div className="layout-page-main">
{this.renderHeader()}
{this.renderMain()}
</div>
areThereCustomOrganizations,
getGlobalSettingValue
} from '../../../store/rootReducer';
+import { RawQuery } from '../../../helpers/query';
interface StateProps {
currentUser: CurrentUser;
organizationsEnabled: boolean;
}
+interface OwnProps {
+ isFavorite: boolean;
+ location: { pathname: string; query: RawQuery };
+ organization?: { key: string };
+}
+
const stateToProps = (state: any) => {
const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
return {
};
};
-export default connect<StateProps, any, any>(stateToProps)(lazyLoad(() => import('./AllProjects')));
+export default connect<StateProps, {}, OwnProps>(stateToProps)(
+ lazyLoad(() => import('./AllProjects'))
+);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ onClearAll: () => void;
+}
+
+export default class ClearAll extends React.PureComponent<Props> {
+ handleClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
+ this.props.onClearAll();
+ };
+
+ render() {
+ return (
+ <div className="projects-facets-reset">
+ <button className="button-red" onClick={this.handleClick}>
+ {translate('clear_all_filters')}
+ </button>
+ </div>
+ );
+ }
+}
}
componentDidMount() {
+ if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
+ this.context.router.replace('/explore/projects');
+ }
+
if (!this.props.onSonarCloud) {
this.defineIfShouldBeRedirected();
}
}
render() {
- if (this.props.onSonarCloud) {
+ if (this.props.onSonarCloud && isLoggedIn(this.props.currentUser)) {
return <AllProjectsContainer isFavorite={true} location={this.props.location} />;
}
const { shouldBeRedirected, shouldForceSorting } = this.state;
- if (shouldBeRedirected == null || shouldBeRedirected === true || shouldForceSorting != null) {
- return null;
- } else {
+
+ if (
+ shouldBeRedirected !== undefined &&
+ shouldBeRedirected !== true &&
+ shouldForceSorting === undefined
+ ) {
return <AllProjectsContainer isFavorite={false} location={this.props.location} />;
}
+
+ return null;
}
}
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
-export default function NoFavoriteProjects() {
+interface Props {
+ onSonarCloud: boolean;
+}
+
+export default function NoFavoriteProjects({ onSonarCloud }: Props) {
return (
<div className="projects-empty-list">
<h3>{translate('projects.no_favorite_projects')}</h3>
<p className="big-spacer-top">{translate('projects.no_favorite_projects.engagement')}</p>
<p className="big-spacer-top">
- <Link to="/projects/all" className="button">
+ <Link to={onSonarCloud ? '/explore/projects' : '/projects/all'} className="button">
{translate('projects.explore_projects')}
</Link>
</p>
interface Props {
currentUser: CurrentUser;
- isFavorite?: boolean;
loading: boolean;
onPerspectiveChange: (x: { view: string; visualization?: string }) => void;
+ onQueryChange: (change: RawQuery) => void;
onSortChange: (sort: string, desc: boolean) => void;
organization?: { key: string };
projects?: Project[];
)}
<SearchFilterContainer
- isFavorite={props.isFavorite}
+ onQueryChange={props.onQueryChange}
organization={props.organization}
query={props.query}
/>
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { Link } from 'react-router';
import { flatMap } from 'lodash';
import FavoriteFilterContainer from './FavoriteFilterContainer';
+import ClearAll from './ClearAll';
import LanguagesFilterContainer from '../filters/LanguagesFilterContainer';
import CoverageFilter from '../filters/CoverageFilter';
import DuplicationsFilter from '../filters/DuplicationsFilter';
interface Props {
facets?: Facets;
- isFavorite: boolean;
+ onClearAll: () => void;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: RawQuery;
showFavoriteFilter: boolean;
}
export default function PageSidebar(props: Props) {
- const { facets, query, isFavorite, organization, view, visualization } = props;
+ const { facets, onQueryChange, query, organization, view, visualization } = props;
const isFiltered = Object.keys(query)
.filter(key => !['view', 'visualization', 'sort'].includes(key))
.some(key => query[key] != null);
const isLeakView = view === 'leak';
- const basePathName = organization ? `/organizations/${organization.key}/projects` : '/projects';
- const pathname = basePathName + (isFavorite ? '/favorite' : '');
const maxFacetValue = getMaxFacetValue(facets);
- const facetProps = { isFavorite, maxFacetValue, organization, query };
+ const facetProps = { onQueryChange, maxFacetValue, organization, query };
let linkQuery: RawQuery | undefined = undefined;
if (view !== 'overall') {
)}
<div className="projects-facets-header clearfix">
- {isFiltered && (
- <div className="projects-facets-reset">
- <Link to={{ pathname, query: linkQuery }} className="button button-red">
- {translate('clear_all_filters')}
- </Link>
- </div>
- )}
+ {isFiltered && <ClearAll onClearAll={props.onClearAll} />}
<h3>{translate('filters')}</h3>
</div>
cardType?: string;
isFavorite: boolean;
isFiltered: boolean;
+ onSonarCloud: boolean;
organization?: { key: string };
projects: Project[];
query: Query;
if (isFiltered) {
return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
}
- return isFavorite ? <NoFavoriteProjects /> : <EmptyInstance />;
+ return isFavorite ? (
+ <NoFavoriteProjects onSonarCloud={this.props.onSonarCloud} />
+ ) : (
+ <EmptyInstance />
+ );
}
render() {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:contact 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import ClearAll from '../ClearAll';
+import { click } from '../../../../helpers/testUtils';
+
+it('renders', () => {
+ expect(shallow(<ClearAll onClearAll={jest.fn()} />)).toMatchSnapshot();
+});
+
+it('clears all', () => {
+ const onClearAll = jest.fn();
+ const wrapper = shallow(<ClearAll onClearAll={onClearAll} />);
+ click(wrapper.find('button'));
+ expect(onClearAll).toBeCalled();
+});
import NoFavoriteProjects from '../NoFavoriteProjects';
it('renders', () => {
- expect(shallow(<NoFavoriteProjects />)).toMatchSnapshot();
+ expect(shallow(<NoFavoriteProjects onSonarCloud={false} />)).toMatchSnapshot();
});
currentUser={{ isLoggedIn: false }}
loading={false}
onPerspectiveChange={jest.fn()}
+ onQueryChange={jest.fn()}
onSortChange={jest.fn()}
projects={[]}
query={{ search: 'test' }}
it('should render correctly', () => {
const sidebar = shallow(
<PageSidebar
- isFavorite={true}
+ onClearAll={jest.fn()}
+ onQueryChange={jest.fn()}
query={{ size: '3' }}
showFavoriteFilter={true}
view="overall"
it('should render `leak` view correctly', () => {
const sidebar = shallow(
<PageSidebar
- isFavorite={false}
+ onClearAll={jest.fn()}
+ onQueryChange={jest.fn()}
query={{ view: 'leak' }}
showFavoriteFilter={true}
view="leak"
it('reset function should work correctly with view and visualizations', () => {
const sidebar = shallow(
<PageSidebar
- isFavorite={false}
+ onClearAll={jest.fn()}
+ onQueryChange={jest.fn()}
query={{ view: 'visualizations', visualization: 'bugs' }}
showFavoriteFilter={true}
view="visualizations"
visualization="bugs"
/>
);
- expect(sidebar.find('.projects-facets-reset').exists()).toBeFalsy();
+ expect(sidebar.find('ClearAll').exists()).toBeFalsy();
sidebar.setProps({ query: { size: '3' } });
- expect(sidebar.find('.projects-facets-reset')).toMatchSnapshot();
+ expect(sidebar.find('ClearAll').exists()).toBeTruthy();
});
encodeSpecialCharacters={true}
title="projects.page"
/>
- <div
+ <ScreenPositionHelper
className="layout-page-side-outer"
- >
- <div
- className="layout-page-side projects-page-side"
- style={
- Object {
- "top": 30,
- }
- }
- >
- <div
- className="layout-page-side-inner"
- >
- <div
- className="layout-page-filters"
- >
- <PageSidebar
- isFavorite={false}
- query={
- Object {
- "coverage": undefined,
- "duplications": undefined,
- "gate": undefined,
- "languages": undefined,
- "maintainability": undefined,
- "new_coverage": undefined,
- "new_duplications": undefined,
- "new_lines": undefined,
- "new_maintainability": undefined,
- "new_reliability": undefined,
- "new_security": undefined,
- "reliability": undefined,
- "search": undefined,
- "security": undefined,
- "size": undefined,
- "sort": undefined,
- "tags": undefined,
- "view": undefined,
- "visualization": undefined,
- }
- }
- showFavoriteFilter={true}
- view="overall"
- visualization="risk"
- />
- </div>
- </div>
- </div>
- </div>
+ />
<div
- className="layout-page-main projects-page-content"
+ className="layout-page-main"
>
<div
className="layout-page-header-panel layout-page-main-header"
"isLoggedIn": true,
}
}
- isFavorite={false}
loading={false}
onPerspectiveChange={[Function]}
+ onQueryChange={[Function]}
onSortChange={[Function]}
projects={
Array [
cardType="overall"
isFavorite={false}
isFiltered={false}
+ onSonarCloud={false}
projects={
Array [
Object {
encodeSpecialCharacters={true}
title="projects.page"
/>
- <div
+ <ScreenPositionHelper
className="layout-page-side-outer"
- >
- <div
- className="layout-page-side projects-page-side"
- style={
- Object {
- "top": 30,
- }
- }
- >
- <div
- className="layout-page-side-inner"
- >
- <div
- className="layout-page-filters"
- >
- <PageSidebar
- isFavorite={false}
- query={
- Object {
- "view": "visualizations",
- }
- }
- showFavoriteFilter={true}
- view="visualizations"
- visualization="risk"
- />
- </div>
- </div>
- </div>
- </div>
+ />
<div
- className="layout-page-main projects-page-content"
+ className="layout-page-main"
>
<div
className="layout-page-header-panel layout-page-main-header"
"isLoggedIn": true,
}
}
- isFavorite={false}
loading={false}
onPerspectiveChange={[Function]}
+ onQueryChange={[Function]}
onSortChange={[Function]}
projects={
Array [
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<div
+ className="projects-facets-reset"
+>
+ <button
+ className="button-red"
+ onClick={[Function]}
+ >
+ clear_all_filters
+ </button>
+</div>
+`;
view="overall"
/>
<SearchFilterContainer
+ onQueryChange={[Function]}
query={
Object {
"search": "test",
view="overall"
/>
<SearchFilterContainer
+ onQueryChange={[Function]}
query={
Object {
"search": "test",
</div>
</Tooltip>
<SearchFilterContainer
+ onQueryChange={[Function]}
query={
Object {
"search": "test",
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`reset function should work correctly with view and visualizations 1`] = `
-<div
- className="projects-facets-reset"
->
- <Link
- className="button button-red"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "view": "visualizations",
- "visualization": "bugs",
- },
- }
- }
- >
- clear_all_filters
- </Link>
-</div>
-`;
-
exports[`should render \`leak\` view correctly 1`] = `
<div>
<Connect(FavoriteFilter)
</h3>
</div>
<QualityGateFilter
- isFavorite={false}
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewReliabilityFilter
- isFavorite={false}
key="new_reliability"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewSecurityFilter
- isFavorite={false}
key="new_security"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewMaintainabilityFilter
- isFavorite={false}
key="new_maintainability"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewCoverageFilter
- isFavorite={false}
key="new_coverage"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewDuplicationsFilter
- isFavorite={false}
key="new_duplications"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<NewLinesFilter
- isFavorite={false}
key="new_lines"
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<Connect(LanguagesFilter)
- isFavorite={false}
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
}
/>
<TagsFilter
- isFavorite={false}
+ onQueryChange={[Function]}
query={
Object {
"view": "leak",
<div
className="projects-facets-header clearfix"
>
- <div
- className="projects-facets-reset"
- >
- <Link
- className="button button-red"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": undefined,
- }
- }
- >
- clear_all_filters
- </Link>
- </div>
+ <ClearAll
+ onClearAll={[Function]}
+ />
<h3>
filters
</h3>
</div>
<QualityGateFilter
- isFavorite={true}
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<ReliabilityFilter
- isFavorite={true}
key="reliability"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<SecurityFilter
- isFavorite={true}
key="security"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<MaintainabilityFilter
- isFavorite={true}
key="maintainability"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<CoverageFilter
- isFavorite={true}
key="coverage"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<DuplicationsFilter
- isFavorite={true}
key="duplications"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<SizeFilter
- isFavorite={true}
key="size"
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
value="3"
/>
<Connect(LanguagesFilter)
- isFavorite={true}
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
}
/>
<TagsFilter
- isFavorite={true}
+ onQueryChange={[Function]}
query={
Object {
"size": "3",
import { getCoverageRatingLabel, getCoverageRatingAverageValue } from '../../../helpers/ratings';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5, 6]}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}
} from '../../../helpers/ratings';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5, 6]}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}
*/
import * as React from 'react';
import * as classNames from 'classnames';
-import { Link } from 'react-router';
-import { getFilterUrl } from './utils';
import { formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export type Option = string | number;
interface Props {
property: string;
className?: string;
+ onQueryChange: (change: RawQuery) => void;
options: Option[];
query: { [x: string]: any };
renderOption: (option: Option, isSelected: boolean) => React.ReactNode;
facet?: Facet;
maxFacetValue?: number;
optionClassName?: string;
- isFavorite?: boolean;
organization?: { key: string };
getFacetValueForOption?: (facet: Facet, option: Option) => void;
export default class Filter extends React.PureComponent<Props> {
isSelected(option: Option): boolean {
const { value } = this.props;
- return Array.isArray(value) ? value.includes(option) : option === value;
+ return Array.isArray(value) ? value.includes(option) : String(option) === String(value);
}
highlightUnder(option?: Option): boolean {
);
}
- blurOnClick = (event: React.SyntheticEvent<HTMLElement>) => event.currentTarget.blur();
+ handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+ event.preventDefault();
+ event.currentTarget.blur();
- getPath(option: Option) {
const { property, value } = this.props;
+ const { key: option } = event.currentTarget.dataset;
let urlOption;
- if (Array.isArray(value)) {
- if (this.isSelected(option)) {
- urlOption = value.length > 1 ? value.filter(val => val !== option).join(',') : null;
+ if (option) {
+ if (Array.isArray(value)) {
+ if (this.isSelected(option)) {
+ urlOption = value.length > 1 ? value.filter(val => val !== option).join(',') : null;
+ } else {
+ urlOption = value.concat(option).join(',');
+ }
} else {
- urlOption = value.concat(option).join(',');
+ urlOption = this.isSelected(option) ? null : option;
}
- } else {
- urlOption = this.isSelected(option) ? null : option;
+
+ this.props.onQueryChange({ [property]: urlOption });
}
- return getFilterUrl(this.props, { [property]: urlOption });
- }
+ };
renderOptionBar(facetValue: number | undefined) {
if (facetValue === undefined || !this.props.maxFacetValue) {
this.props.optionClassName
);
- const path = this.getPath(option);
const facetValue =
facet && getFacetValueForOption ? getFacetValueForOption(facet, option) : undefined;
option > value;
return (
- <Link
- key={option}
- className={className}
- to={path}
- data-key={option}
- onClick={this.blurOnClick}>
+ <a className={className} data-key={option} href="#" key={option} onClick={this.handleClick}>
<span className="facet-name">
{this.props.renderOption(option, this.isSelected(option) || isUnderSelectedOption)}
</span>
{this.renderOptionBar(facetValue)}
</span>
)}
- </Link>
+ </a>
);
}
import Rating from '../../../components/ui/Rating';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
- isFavorite?: boolean;
maxFacetValue?: number;
name: string;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property: string;
query: { [x: string]: any };
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property={props.property}
className={props.className}
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}
import { getLanguageByKey, Languages } from '../../../store/languages/reducer';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
facet?: Facet;
- isFavorite?: boolean;
languages: Languages;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
return (
<Filter
+ onQueryChange={this.props.onQueryChange}
property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
value={this.props.value}
facet={this.props.facet}
maxFacetValue={this.props.maxFacetValue}
- isFavorite={this.props.isFavorite}
organization={this.props.organization}
getFacetValueForOption={this.getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.languages')} />}
footer={
<SearchableFilterFooter
- isFavorite={this.props.isFavorite}
+ onQueryChange={this.props.onQueryChange}
organization={this.props.organization}
options={this.getSearchOptions()}
property={property}
import { Facet } from '../types';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import { translate } from '../../../helpers/l10n';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
import { translate } from '../../../helpers/l10n';
import { getSizeRatingLabel } from '../../../helpers/ratings';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className="leak-facet-box"
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
import * as React from 'react';
import Filter from './Filter';
import FilterHeader from './FilterHeader';
+import { Facet } from '../types';
import Level from '../../../components/ui/Level';
import { translate } from '../../../helpers/l10n';
-import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property="gate"
options={['OK', 'WARN', 'ERROR']}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.quality_gate')} />}
import { Facet } from '../types';
import BugIcon from '../../../components/icons-components/BugIcon';
import { translate } from '../../../helpers/l10n';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
-import { getFilterUrl } from './utils';
import SearchBox from '../../../components/controls/SearchBox';
import { translate } from '../../../helpers/l10n';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
query: { search?: string };
- isFavorite?: boolean;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
}
export default class SearchFilterContainer extends React.PureComponent<Props> {
- static contextTypes = {
- router: PropTypes.object.isRequired
- };
-
handleSearch = (userQuery?: string) => {
- const path = getFilterUrl(this.props, { search: userQuery });
- this.context.router.push(path);
+ this.props.onQueryChange({ search: userQuery });
};
render() {
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import * as PropTypes from 'prop-types';
-import { getFilterUrl } from './utils';
import Select from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
+import { RawQuery } from '../../../helpers/query';
interface Props {
+ onQueryChange: (change: RawQuery) => void;
property: string;
query: { [x: string]: any };
options: Array<{ label: string; value: string }>;
}
export default class SearchableFilterFooter extends React.PureComponent<Props> {
- static contextTypes = {
- router: PropTypes.object.isRequired
- };
-
handleOptionChange = ({ value }: { value: string }) => {
const urlOptions = (this.props.query[this.props.property] || []).concat(value).join(',');
- const path = getFilterUrl(this.props, { [this.props.property]: urlOptions });
- this.context.router.push(path);
+ this.props.onQueryChange({ [this.props.property]: urlOptions });
};
render() {
import { Facet } from '../types';
import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
import { translate } from '../../../helpers/l10n';
+import { RawQuery } from '../../../helpers/query';
interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
import { translate } from '../../../helpers/l10n';
import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ratings';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
export interface Props {
className?: string;
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
+ onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
- isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}
import { searchProjectTags } from '../../../api/components';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
+import { RawQuery } from '../../../helpers/query';
interface Props {
facet?: Facet;
- isFavorite?: boolean;
maxFacetValue?: number;
+ onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
return (
<Filter
+ onQueryChange={this.props.onQueryChange}
property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
value={this.props.value}
facet={this.props.facet}
maxFacetValue={this.props.maxFacetValue}
- isFavorite={this.props.isFavorite}
organization={this.props.organization}
getFacetValueForOption={this.getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.tags')} />}
footer={
<SearchableFilterFooter
- isFavorite={this.props.isFavorite}
+ onQueryChange={this.props.onQueryChange}
isLoading={this.state.isLoading}
onInputChange={this.handleSearch}
onOpen={this.handleSearch}
import CoverageFilter from '../CoverageFilter';
it('renders', () => {
- const wrapper = shallow(<CoverageFilter query={{}} />);
+ const wrapper = shallow(<CoverageFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
import DuplicationsFilter from '../DuplicationsFilter';
it('renders', () => {
- const wrapper = shallow(<DuplicationsFilter query={{}} />);
+ const wrapper = shallow(<DuplicationsFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
function shallowRender(props?: any) {
return shallow(
<Filter
+ onQueryChange={jest.fn()}
options={[1, 2, 3]}
property="foo"
query={{}}
import IssuesFilter from '../IssuesFilter';
it('renders', () => {
- const wrapper = shallow(<IssuesFilter name="bugs" property="bugs" query={{}} />);
+ const wrapper = shallow(
+ <IssuesFilter name="bugs" onQueryChange={jest.fn()} property="bugs" query={{}} />
+ );
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
it('should render the languages without the ones in the facet', () => {
const wrapper = shallow(
- <LanguagesFilter facet={languagesFacet} languages={languages} query={{ languages: null }} />
+ <LanguagesFilter
+ facet={languagesFacet}
+ languages={languages}
+ onQueryChange={jest.fn()}
+ query={{ languages: null }}
+ />
);
expect(wrapper).toMatchSnapshot();
});
const wrapper = shallow(
<LanguagesFilter
facet={languagesFacet}
- isFavorite={true}
languages={languages}
+ onQueryChange={jest.fn()}
query={{ languages: ['java', 'cs'] }}
value={['java', 'cs']}
/>
const wrapper = shallow(
<LanguagesFilter
facet={{ ...languagesFacet, g: 1 }}
- isFavorite={true}
languages={manyLanguages}
+ onQueryChange={jest.fn()}
query={{ languages: ['java', 'g'] }}
value={['java', 'g']}
/>
import MaintainabilityFilter from '../MaintainabilityFilter';
it('renders', () => {
- expect(shallow(<MaintainabilityFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<MaintainabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import NewCoverageFilter from '../NewCoverageFilter';
it('renders', () => {
- expect(shallow(<NewCoverageFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<NewCoverageFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import NewDuplicationsFilter from '../NewDuplicationsFilter';
it('renders', () => {
- expect(shallow(<NewDuplicationsFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<NewDuplicationsFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import NewLinesFilter from '../NewLinesFilter';
it('renders', () => {
- const wrapper = shallow(<NewLinesFilter query={{}} />);
+ const wrapper = shallow(<NewLinesFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
import NewMaintainabilityFilter from '../NewMaintainabilityFilter';
it('renders', () => {
- expect(shallow(<NewMaintainabilityFilter query={{}} />)).toMatchSnapshot();
+ expect(
+ shallow(<NewMaintainabilityFilter onQueryChange={jest.fn()} query={{}} />)
+ ).toMatchSnapshot();
});
import NewReliabilityFilter from '../NewReliabilityFilter';
it('renders', () => {
- expect(shallow(<NewReliabilityFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<NewReliabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import NewSecurityFilter from '../NewSecurityFilter';
it('renders', () => {
- expect(shallow(<NewSecurityFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<NewSecurityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import QualityGateFilter from '../QualityGateFilter';
it('renders', () => {
- const wrapper = shallow(<QualityGateFilter query={{}} />);
+ const wrapper = shallow(<QualityGateFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
import ReliabilityFilter from '../ReliabilityFilter';
it('renders', () => {
- expect(shallow(<ReliabilityFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<ReliabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import SearchFilterContainer from '../SearchFilterContainer';
it('searches', () => {
- const push = jest.fn();
- const wrapper = shallow(<SearchFilterContainer query={{}} />, { context: { router: { push } } });
+ const onQueryChange = jest.fn();
+ const wrapper = shallow(<SearchFilterContainer onQueryChange={onQueryChange} query={{}} />, {
+ context: { router: { push: jest.fn() } }
+ });
expect(wrapper).toMatchSnapshot();
wrapper.find('SearchBox').prop<Function>('onChange')('foo');
- expect(push).toBeCalledWith({ pathname: '/projects', query: { search: 'foo' } });
+ expect(onQueryChange).toBeCalledWith({ search: 'foo' });
});
it('should render items without the ones in the facet', () => {
const wrapper = shallow(
<SearchableFilterFooter
+ onQueryChange={jest.fn()}
property="languages"
query={{ languages: ['java'] }}
options={options}
});
it('should render items without the ones in the facet', () => {
- const push = jest.fn();
+ const onQueryChange = jest.fn();
const wrapper = shallow(
<SearchableFilterFooter
+ onQueryChange={onQueryChange}
property="languages"
query={{ languages: ['java'] }}
options={options}
/>,
- { context: { router: { push } } }
+ { context: { router: { push: jest.fn() } } }
);
(wrapper.find('Select').prop('onChange') as Function)({ value: 'js' });
- expect(push).toBeCalledWith({
- pathname: '/projects',
- query: { languages: 'java,js' }
- });
+ expect(onQueryChange).toBeCalledWith({ languages: 'java,js' });
});
import SecurityFilter from '../SecurityFilter';
it('renders', () => {
- expect(shallow(<SecurityFilter query={{}} />)).toMatchSnapshot();
+ expect(shallow(<SecurityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});
import SizeFilter from '../SizeFilter';
it('renders', () => {
- const wrapper = shallow(<SizeFilter query={{}} />);
+ const wrapper = shallow(<SizeFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();
const renderOption = wrapper.prop('renderOption');
const tagsFacet = { lang: 4, sonar: 3, csharp: 1 };
it('should render the tags without the ones in the facet', () => {
- const wrapper = shallow(<TagsFilter query={{ tags: null }} facet={tagsFacet} />);
+ const wrapper = shallow(
+ <TagsFilter onQueryChange={jest.fn()} query={{ tags: null }} facet={tagsFacet} />
+ );
expect(wrapper).toMatchSnapshot();
wrapper.setState({ tags });
expect(wrapper).toMatchSnapshot();
it('should render the tags facet with the selected tags', () => {
const wrapper = shallow(
<TagsFilter
+ onQueryChange={jest.fn()}
query={{ tags: ['lang', 'sonar'] }}
value={['lang', 'sonar']}
facet={tagsFacet}
- isFavorite={true}
/>
);
expect(wrapper).toMatchSnapshot();
it('should render maximum 10 tags in the searchbox results', () => {
const wrapper = shallow(
<TagsFilter
+ onQueryChange={jest.fn()}
query={{ languages: ['java', 'ad'] }}
value={['java', 'ad']}
facet={{ ...tagsFacet, ad: 1 }}
- isFavorite={true}
/>
);
wrapper.setState({ tags: [...tags, 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag', 'ah', 'ai'] });
}
highlightUnder={1}
highlightUnderMax={5}
+ onQueryChange={[Function]}
options={
Array [
1,
}
highlightUnder={1}
highlightUnderMax={5}
+ onQueryChange={[Function]}
options={
Array [
1,
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 1,
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
+ </a>
<div
className="search-navigator-facet-highlight-under-container"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 2,
- },
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 3,
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
</div>
</div>
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 1,
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
+ </a>
<div
className="search-navigator-facet-highlight-under-container"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet active"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {},
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 3,
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
</div>
</div>
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 1,
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 2,
- },
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 3,
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
</div>
`;
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key="a"
+ href="#"
key="a"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "a",
- },
- }
- }
>
<span
className="facet-name"
/>
</div>
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key="b"
+ href="#"
key="b"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "b",
- },
- }
- }
>
<span
className="facet-name"
/>
</div>
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key="c"
+ href="#"
key="c"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "c",
- },
- }
- }
>
<span
className="facet-name"
/>
</div>
</span>
- </Link>
+ </a>
</div>
</div>
`;
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 1,
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 2,
- },
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 3,
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
<footer />
</div>
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet active"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "2",
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet active"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "1",
- },
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": "1,2,3",
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
</div>
`;
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet"
data-key={1}
+ href="#"
key="1"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 1,
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet active"
data-key={2}
+ href="#"
key="2"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {},
- }
- }
>
<span
className="facet-name"
>
2
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key={3}
+ href="#"
key="3"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects",
- "query": Object {
- "foo": 3,
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
+ </a>
</div>
</div>
`;
/>
}
highlightUnder={1}
+ onQueryChange={[Function]}
options={
Array [
1,
}
footer={
<SearchableFilterFooter
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
Object {
name="projects.facets.languages"
/>
}
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
"java",
}
footer={
<SearchableFilterFooter
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
Object {
name="projects.facets.languages"
/>
}
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
"java",
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet active"
data-key="java"
+ href="#"
key="java"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "languages": "cs",
- },
- }
- }
>
<span
className="facet-name"
>
39
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet active"
data-key="cs"
+ href="#"
key="cs"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "languages": "java",
- },
- }
- }
>
<span
className="facet-name"
>
4
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key="js"
+ href="#"
key="js"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "languages": "java,cs,js",
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
+ </a>
</div>
<SearchableFilterFooter
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
Object {
}
footer={
<SearchableFilterFooter
- isFavorite={undefined}
+ onQueryChange={[Function]}
options={
Array [
Object {
name="projects.facets.languages"
/>
}
+ onQueryChange={[Function]}
options={
Array [
"java",
</span>
}
name="Maintainability"
+ onQueryChange={[Function]}
property="maintainability"
query={Object {}}
/>
exports[`renders 1`] = `
<CoverageFilter
className="leak-facet-box"
+ onQueryChange={[Function]}
property="new_coverage"
query={Object {}}
/>
exports[`renders 1`] = `
<DuplicationsFilter
className="leak-facet-box"
+ onQueryChange={[Function]}
property="new_duplications"
query={Object {}}
/>
/>
}
highlightUnder={1}
+ onQueryChange={[Function]}
options={
Array [
1,
</span>
}
name="Maintainability"
+ onQueryChange={[Function]}
property="new_maintainability"
query={Object {}}
/>
</span>
}
name="Reliability"
+ onQueryChange={[Function]}
property="new_reliability"
query={Object {}}
/>
</span>
}
name="Security"
+ onQueryChange={[Function]}
property="new_security"
query={Object {}}
/>
name="projects.facets.quality_gate"
/>
}
+ onQueryChange={[Function]}
options={
Array [
"OK",
</span>
}
name="Reliability"
+ onQueryChange={[Function]}
property="reliability"
query={Object {}}
/>
</span>
}
name="Security"
+ onQueryChange={[Function]}
property="security"
query={Object {}}
/>
/>
}
highlightUnder={1}
+ onQueryChange={[Function]}
options={
Array [
1,
}
footer={
<SearchableFilterFooter
- isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
+ onQueryChange={[Function]}
options={
Array [
Object {
name="projects.facets.tags"
/>
}
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
"lang",
}
footer={
<SearchableFilterFooter
- isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
+ onQueryChange={[Function]}
options={Array []}
organization={undefined}
property="tags"
name="projects.facets.tags"
/>
}
- isFavorite={true}
+ onQueryChange={[Function]}
options={
Array [
"lang",
<div
className="search-navigator-facet-list projects-facet-list"
>
- <Link
+ <a
className="facet search-navigator-facet projects-facet active"
data-key="lang"
+ href="#"
key="lang"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "tags": "sonar",
- },
- }
- }
>
<span
className="facet-name"
>
4
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet active"
data-key="sonar"
+ href="#"
key="sonar"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "tags": "lang",
- },
- }
- }
>
<span
className="facet-name"
>
3
</span>
- </Link>
- <Link
+ </a>
+ <a
className="facet search-navigator-facet projects-facet"
data-key="csharp"
+ href="#"
key="csharp"
onClick={[Function]}
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/projects/favorite",
- "query": Object {
- "tags": "lang,sonar,csharp",
- },
- }
- }
>
<span
className="facet-name"
>
1
</span>
- </Link>
+ </a>
</div>
<SearchableFilterFooter
- isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
+ onQueryChange={[Function]}
options={Array []}
property="tags"
query={
}
footer={
<SearchableFilterFooter
- isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
+ onQueryChange={[Function]}
options={Array []}
organization={undefined}
property="tags"
name="projects.facets.tags"
/>
}
+ onQueryChange={[Function]}
options={
Array [
"lang",
}
footer={
<SearchableFilterFooter
- isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
+ onQueryChange={[Function]}
options={
Array [
Object {
name="projects.facets.tags"
/>
}
+ onQueryChange={[Function]}
options={
Array [
"lang",
-.projects-page-side {
- transition: top 150ms ease-out;
-}
-
-.projects-page-content {
- transition: padding-top 150ms ease-out;
-}
-
.projects-topbar-items {
display: flex;
align-items: center;
duplications=Duplications
edit=Edit
events=Events
+explore=Explore
false=False
favorite=Favorite
file=File
issues.to_switch_flows=to switch flows
issues.leak_period=Leak Period
issues.my_issues=My Issues
+issues.no_my_issues=There are no issues assigned to you.
#------------------------------------------------------------------------------