@@ -433,9 +433,6 @@ export default class App extends React.PureComponent { | |||
): Promise<Array<Issue>> => { | |||
const { issues, openIssue, paging } = this.state; | |||
/* eslint-disable no-console */ | |||
console.log(`loadin issues from line ${from} to line ${to}`); | |||
if (!openIssue || !paging) { | |||
return Promise.reject(); | |||
} |
@@ -160,16 +160,20 @@ export default class BulkChangeModal extends React.PureComponent { | |||
handleSubmit = (e: Event) => { | |||
e.preventDefault(); | |||
const query = pickBy({ | |||
assign: this.state.assignee, | |||
set_type: this.state.type, | |||
set_severity: this.state.severity, | |||
add_tags: this.state.addTags && this.state.addTags.join(), | |||
remove_tags: this.state.removeTags && this.state.removeTags.join(), | |||
do_transition: this.state.transition, | |||
comment: this.state.comment, | |||
sendNotifications: this.state.notifications | |||
}); | |||
const query = pickBy( | |||
{ | |||
assign: this.state.assignee, | |||
set_type: this.state.type, | |||
set_severity: this.state.severity, | |||
add_tags: this.state.addTags && this.state.addTags.join(), | |||
remove_tags: this.state.removeTags && this.state.removeTags.join(), | |||
do_transition: this.state.transition, | |||
comment: this.state.comment, | |||
sendNotifications: this.state.notifications | |||
}, | |||
// remove null, but keep empty string | |||
x => x != null | |||
); | |||
const issueKeys = this.state.issues.map(issue => issue.key); | |||
this.setState({ submitting: true }); |
@@ -47,7 +47,7 @@ export default class ComponentBreadcrumbs extends React.PureComponent { | |||
<Organization linkClassName="link-no-underline" organizationKey={issue.organization} />} | |||
{displayProject && | |||
<span> | |||
<span title={issue.projectName}> | |||
<Link to={getProjectUrl(issue.project)} className="link-no-underline"> | |||
{limitComponentName(issue.projectName)} | |||
</Link> | |||
@@ -56,7 +56,7 @@ export default class ComponentBreadcrumbs extends React.PureComponent { | |||
{displaySubProject && | |||
issue.subProject != null && | |||
<span> | |||
<span title={issue.subProjectName}> | |||
<Link to={getProjectUrl(issue.subProject)} className="link-no-underline"> | |||
{limitComponentName(issue.subProjectName)} | |||
</Link> | |||
@@ -64,7 +64,9 @@ export default class ComponentBreadcrumbs extends React.PureComponent { | |||
</span>} | |||
<Link to={getProjectUrl(issue.component)} className="link-no-underline"> | |||
{collapsePath(issue.componentLongName)} | |||
<span title={issue.componentLongName}> | |||
{collapsePath(issue.componentLongName)} | |||
</span> | |||
</Link> | |||
</div> | |||
); |
@@ -30,7 +30,7 @@ type Props = { | |||
const IssuesCounter = (props: Props) => ( | |||
<span> | |||
<strong> | |||
{props.current != null && <span>{props.current + 1} / </span>} | |||
{props.current != null && <span>{formatMeasure(props.current + 1, 'INT')} / </span>} | |||
{formatMeasure(props.total, 'INT')} | |||
</strong> | |||
{' '} |
@@ -0,0 +1,31 @@ | |||
/* | |||
* 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 { shallow } from 'enzyme'; | |||
import IssuesCounter from '../IssuesCounter'; | |||
it('formats numbers', () => { | |||
expect(shallow(<IssuesCounter current={1234} total={987654321} />)).toMatchSnapshot(); | |||
}); | |||
it('does not show current', () => { | |||
expect(shallow(<IssuesCounter current={null} total={987654321} />)).toMatchSnapshot(); | |||
}); |
@@ -0,0 +1,25 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`does not show current 1`] = ` | |||
<span> | |||
<strong> | |||
987,654,321 | |||
</strong> | |||
issues.issues | |||
</span> | |||
`; | |||
exports[`formats numbers 1`] = ` | |||
<span> | |||
<strong> | |||
<span> | |||
1,235 | |||
/ | |||
</span> | |||
987,654,321 | |||
</strong> | |||
issues.issues | |||
</span> | |||
`; |
@@ -19,11 +19,11 @@ | |||
*/ | |||
// @flow | |||
import React from 'react'; | |||
import classNames from 'classnames'; | |||
import moment from 'moment'; | |||
import { max } from 'lodash'; | |||
import FacetBox from './components/FacetBox'; | |||
import FacetHeader from './components/FacetHeader'; | |||
import FacetItem from './components/FacetItem'; | |||
import { BarChart } from '../../../components/charts/bar-chart'; | |||
import DateInput from '../../../components/controls/DateInput'; | |||
import { translate } from '../../../helpers/l10n'; | |||
@@ -55,6 +55,13 @@ export default class CreationDateFacet extends React.PureComponent { | |||
property = 'createdAt'; | |||
hasValue = (): boolean => | |||
this.props.createdAfter.length > 0 || | |||
this.props.createdAt.length > 0 || | |||
this.props.createdBefore.length > 0 || | |||
this.props.createdInLast.length > 0 || | |||
this.props.sinceLeakPeriod; | |||
handleHeaderClick = () => { | |||
this.props.onToggle(this.property); | |||
}; | |||
@@ -93,15 +100,11 @@ export default class CreationDateFacet extends React.PureComponent { | |||
}); | |||
}; | |||
handlePeriodClick = (period?: string) => (e: Event & { target: HTMLElement }) => { | |||
e.preventDefault(); | |||
e.target.blur; | |||
handlePeriodClick = (period: string) => { | |||
this.resetTo({ createdInLast: period }); | |||
}; | |||
handleLeakPeriodClick = () => (e: Event & { target: HTMLElement }) => { | |||
e.preventDefault(); | |||
e.target.blur; | |||
handleLeakPeriodClick = () => { | |||
this.resetTo({ sinceLeakPeriod: true }); | |||
}; | |||
@@ -200,39 +203,51 @@ export default class CreationDateFacet extends React.PureComponent { | |||
renderPrefefinedPeriods() { | |||
const { component, createdInLast, sinceLeakPeriod } = this.props; | |||
return ( | |||
<div className="spacer-top"> | |||
<span className="spacer-right">{translate('issues.facet.createdAt.or')}</span> | |||
<a className="spacer-right" href="#" onClick={this.handlePeriodClick()}> | |||
{translate('issues.facet.createdAt.all')} | |||
</a> | |||
<div className="spacer-top issues-predefined-periods"> | |||
<FacetItem | |||
active={!this.hasValue()} | |||
facetMode="" | |||
name={translate('issues.facet.createdAt.all')} | |||
onClick={this.handlePeriodClick} | |||
stat={null} | |||
value="" | |||
/> | |||
{component == null && | |||
<a | |||
className={classNames('spacer-right', { 'active-link': createdInLast === '1w' })} | |||
href="#" | |||
onClick={this.handlePeriodClick('1w')}> | |||
{translate('issues.facet.createdAt.last_week')} | |||
</a>} | |||
<FacetItem | |||
active={createdInLast === '1w'} | |||
facetMode="" | |||
name={translate('issues.facet.createdAt.last_week')} | |||
onClick={this.handlePeriodClick} | |||
stat={null} | |||
value="1w" | |||
/>} | |||
{component == null && | |||
<a | |||
className={classNames('spacer-right', { 'active-link': createdInLast === '1m' })} | |||
href="#" | |||
onClick={this.handlePeriodClick('1m')}> | |||
{translate('issues.facet.createdAt.last_month')} | |||
</a>} | |||
<FacetItem | |||
active={createdInLast === '1m'} | |||
facetMode="" | |||
name={translate('issues.facet.createdAt.last_month')} | |||
onClick={this.handlePeriodClick} | |||
stat={null} | |||
value="1m" | |||
/>} | |||
{component == null && | |||
<a | |||
className={classNames('spacer-right', { 'active-link': createdInLast === '1y' })} | |||
href="#" | |||
onClick={this.handlePeriodClick('1y')}> | |||
{translate('issues.facet.createdAt.last_year')} | |||
</a>} | |||
<FacetItem | |||
active={createdInLast === '1y'} | |||
facetMode="" | |||
name={translate('issues.facet.createdAt.last_year')} | |||
onClick={this.handlePeriodClick} | |||
stat={null} | |||
value="1y" | |||
/>} | |||
{component != null && | |||
<a | |||
className={classNames('spacer-right', { 'active-link': sinceLeakPeriod })} | |||
href="#" | |||
onClick={this.handleLeakPeriodClick()}> | |||
{translate('issues.leak_period')} | |||
</a>} | |||
<FacetItem | |||
active={sinceLeakPeriod} | |||
facetMode="" | |||
name={translate('issues.leak_period')} | |||
onClick={this.handleLeakPeriodClick} | |||
stat={null} | |||
value="" | |||
/>} | |||
</div> | |||
); | |||
} | |||
@@ -249,13 +264,6 @@ export default class CreationDateFacet extends React.PureComponent { | |||
} | |||
render() { | |||
const hasValue = | |||
this.props.createdAfter.length > 0 || | |||
this.props.createdAt.length > 0 || | |||
this.props.createdBefore.length > 0 || | |||
this.props.createdInLast.length > 0 || | |||
this.props.sinceLeakPeriod; | |||
return ( | |||
<FacetBox property={this.property}> | |||
<FacetHeader | |||
@@ -263,7 +271,7 @@ export default class CreationDateFacet extends React.PureComponent { | |||
onClear={this.handleClear} | |||
onClick={this.handleHeaderClick} | |||
open={this.props.open} | |||
values={hasValue ? 1 : 0} | |||
values={this.hasValue() ? 1 : 0} | |||
/> | |||
{this.props.open && this.renderInner()} |
@@ -216,3 +216,12 @@ | |||
.issues-workspace-list-component + .issues-workspace-list-item { | |||
margin-top: 0; | |||
} | |||
.issues-predefined-periods { | |||
display: flex; | |||
} | |||
.issues-predefined-periods .search-navigator-facet { | |||
width: auto; | |||
margin-right: 4px; | |||
} |
@@ -158,6 +158,7 @@ export default class SourceViewerCode extends React.PureComponent { | |||
issueLocations={this.getIssueLocationsForLine(line)} | |||
issues={issuesForLine} | |||
key={line.line} | |||
last={index === this.props.sources.length - 1 && !this.props.hasSourcesAfter} | |||
line={line} | |||
loadDuplications={this.props.loadDuplications} | |||
onClick={this.props.onLineClick} |
@@ -46,6 +46,7 @@ type Props = {| | |||
highlightedSymbols?: Array<string>, | |||
issueLocations: Array<LinearIssueLocation>, | |||
issues: Array<Issue>, | |||
last: boolean, | |||
line: SourceLine, | |||
loadDuplications: (SourceLine, HTMLElement) => void, | |||
onClick: (SourceLine, HTMLElement) => void, | |||
@@ -94,7 +95,8 @@ export default class Line extends React.PureComponent { | |||
const className = classNames('source-line', { | |||
'source-line-highlighted': this.props.highlighted, | |||
'source-line-shadowed': filtered === false, | |||
'source-line-filtered': filtered === true | |||
'source-line-filtered': filtered === true, | |||
'source-line-last': this.props.last | |||
}); | |||
return ( |
@@ -366,4 +366,9 @@ input.issue-action-options-search { | |||
.issue:not(.selected) .location-index { | |||
background-color: #ccc; | |||
} | |||
.issue .menu { | |||
max-height: 120px; | |||
overflow: auto; | |||
} |
@@ -89,6 +89,10 @@ | |||
} | |||
} | |||
.source-line-last .source-line-code-inner { | |||
padding-bottom: 80px; | |||
} | |||
.source-viewer pre { | |||
height: @source-line-height; | |||
padding: 0; |
@@ -797,7 +797,6 @@ issues.facet.rules=Rule | |||
issues.facet.resolutions=Resolution | |||
issues.facet.languages=Language | |||
issues.facet.createdAt=Creation Date | |||
issues.facet.createdAt.or=Or: | |||
issues.facet.createdAt.all=All | |||
issues.facet.createdAt.last_week=Last week | |||
issues.facet.createdAt.last_month=Last month |