margin-right: 4px; | margin-right: 4px; | ||||
} | } | ||||
.shortcut-button-tiny { | |||||
width: 14px; | |||||
min-width: auto; | |||||
padding: 0; | |||||
height: 14px; | |||||
line-height: inherit; | |||||
font-size: 6px; | |||||
} | |||||
.page-shortcuts-tooltip { | |||||
line-height: 12px; | |||||
} | |||||
.identity-provider { | .identity-provider { | ||||
display: inline-block; | display: inline-block; | ||||
line-height: 14px; | line-height: 14px; |
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; | ||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | import { translate } from 'sonar-ui-common/helpers/l10n'; | ||||
import PageCounter from '../../../components/common/PageCounter'; | import PageCounter from '../../../components/common/PageCounter'; | ||||
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip'; | |||||
interface Props { | |||||
export interface PageActionsProps { | |||||
loading: boolean; | loading: boolean; | ||||
onReload: () => void; | onReload: () => void; | ||||
paging?: T.Paging; | paging?: T.Paging; | ||||
selectedIndex?: number; | selectedIndex?: number; | ||||
} | } | ||||
export default function PageActions(props: Props) { | |||||
export default function PageActions(props: PageActionsProps) { | |||||
return ( | return ( | ||||
<div className="pull-right"> | |||||
<Shortcuts /> | |||||
<div className="display-flex-center display-flex-justify-end"> | |||||
<PageShortcutsTooltip | |||||
className="big-spacer-right" | |||||
leftAndRightLabel={translate('issues.to_navigate')} | |||||
upAndDownLabel={translate('coding_rules.to_select_rules')} | |||||
/> | |||||
<DeferredSpinner loading={props.loading}> | <DeferredSpinner loading={props.loading}> | ||||
<ReloadButton onClick={props.onReload} /> | <ReloadButton onClick={props.onReload} /> | ||||
</div> | </div> | ||||
); | ); | ||||
} | } | ||||
function Shortcuts() { | |||||
return ( | |||||
<span className="note big-spacer-right"> | |||||
<span className="big-spacer-right"> | |||||
<span className="shortcut-button little-spacer-right">↑</span> | |||||
<span className="shortcut-button little-spacer-right">↓</span> | |||||
{translate('coding_rules.to_select_rules')} | |||||
</span> | |||||
<span> | |||||
<span className="shortcut-button little-spacer-right">←</span> | |||||
<span className="shortcut-button little-spacer-right">→</span> | |||||
{translate('issues.to_navigate')} | |||||
</span> | |||||
</span> | |||||
); | |||||
} |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||||
import PageActions, { PageActionsProps } from '../PageActions'; | |||||
export interface Props { | |||||
issue: Pick<T.Issue, 'flows' | 'secondaryLocations'> | undefined; | |||||
} | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); | |||||
expect(shallowRender({ paging: { total: 100 } as T.Paging })).toMatchSnapshot('with paging'); | |||||
}); | |||||
export default function LocationNavigationKeyboardShortcuts({ issue }: Props) { | |||||
if (!issue || (!issue.secondaryLocations.length && !issue.flows.length)) { | |||||
return null; | |||||
} | |||||
const hasSeveralFlows = issue.flows.length > 1; | |||||
return ( | |||||
<div className="navigation-keyboard-shortcuts big-spacer-top text-center"> | |||||
<span> | |||||
alt + ↑ ↓ {hasSeveralFlows && <>←→</>} | |||||
{translate('issues.to_navigate_issue_locations')} | |||||
</span> | |||||
</div> | |||||
); | |||||
function shallowRender(props: Partial<PageActionsProps> = {}) { | |||||
return shallow<PageActionsProps>(<PageActions loading={false} onReload={jest.fn()} {...props} />); | |||||
} | } |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-end" | |||||
> | |||||
<PageShortcutsTooltip | |||||
className="big-spacer-right" | |||||
leftAndRightLabel="issues.to_navigate" | |||||
upAndDownLabel="coding_rules.to_select_rules" | |||||
/> | |||||
<DeferredSpinner | |||||
loading={false} | |||||
> | |||||
<ReloadButton | |||||
onClick={[MockFunction]} | |||||
/> | |||||
</DeferredSpinner> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly: loading 1`] = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-end" | |||||
> | |||||
<PageShortcutsTooltip | |||||
className="big-spacer-right" | |||||
leftAndRightLabel="issues.to_navigate" | |||||
upAndDownLabel="coding_rules.to_select_rules" | |||||
/> | |||||
<DeferredSpinner | |||||
loading={true} | |||||
> | |||||
<ReloadButton | |||||
onClick={[MockFunction]} | |||||
/> | |||||
</DeferredSpinner> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly: with paging 1`] = ` | |||||
<div | |||||
className="display-flex-center display-flex-justify-end" | |||||
> | |||||
<PageShortcutsTooltip | |||||
className="big-spacer-right" | |||||
leftAndRightLabel="issues.to_navigate" | |||||
upAndDownLabel="coding_rules.to_select_rules" | |||||
/> | |||||
<DeferredSpinner | |||||
loading={false} | |||||
> | |||||
<ReloadButton | |||||
onClick={[MockFunction]} | |||||
/> | |||||
</DeferredSpinner> | |||||
<PageCounter | |||||
className="spacer-left" | |||||
label="coding_rules._rules" | |||||
total={100} | |||||
/> | |||||
</div> | |||||
`; |
this.handleCloseBulkChange(); | this.handleCloseBulkChange(); | ||||
}; | }; | ||||
handleReload = () => { | |||||
this.fetchFirstIssues(); | |||||
this.refreshBranchStatus(); | |||||
const { branchLike, onBranchesChange } = this.props; | |||||
if (onBranchesChange && isPullRequest(branchLike)) { | |||||
onBranchesChange(); | |||||
} | |||||
}; | |||||
handleReloadAndOpenFirst = () => { | |||||
this.fetchFirstIssues().then( | |||||
(issues: T.Issue[]) => { | |||||
if (issues.length > 0) { | |||||
this.openIssue(issues[0].key); | |||||
} | |||||
}, | |||||
() => {} | |||||
); | |||||
}; | |||||
selectLocation = (index: number) => { | selectLocation = (index: number) => { | ||||
this.setState(actions.selectLocation(index)); | this.setState(actions.selectLocation(index)); | ||||
}; | }; | ||||
displayBackButton={query.issues.length !== 1} | displayBackButton={query.issues.length !== 1} | ||||
loading={this.state.loading} | loading={this.state.loading} | ||||
onBackClick={this.closeIssue} | onBackClick={this.closeIssue} | ||||
onReload={this.handleReloadAndOpenFirst} | |||||
paging={paging} | |||||
selectedIndex={this.getSelectedIndex()} | |||||
/> | /> | ||||
<ConciseIssuesList | <ConciseIssuesList | ||||
issues={issues} | issues={issues} | ||||
<PageActions | <PageActions | ||||
canSetHome={!this.props.component} | canSetHome={!this.props.component} | ||||
effortTotal={this.state.effortTotal} | effortTotal={this.state.effortTotal} | ||||
onReload={this.handleReload} | |||||
paging={paging} | paging={paging} | ||||
selectedIndex={selectedIndex} | selectedIndex={selectedIndex} | ||||
/> | /> | ||||
} | } | ||||
render() { | render() { | ||||
const { openIssue } = this.state; | |||||
const { openIssue, paging } = this.state; | |||||
const selectedIndex = this.getSelectedIndex(); | |||||
return ( | return ( | ||||
<div className="layout-page issues" id="issues-page"> | <div className="layout-page issues" id="issues-page"> | ||||
<Suggestions suggestions="issues" /> | <Suggestions suggestions="issues" /> |
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton'; | |||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | import { translate } from 'sonar-ui-common/helpers/l10n'; | ||||
import HomePageSelect from '../../../components/controls/HomePageSelect'; | import HomePageSelect from '../../../components/controls/HomePageSelect'; | ||||
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip'; | |||||
import { isSonarCloud } from '../../../helpers/system'; | import { isSonarCloud } from '../../../helpers/system'; | ||||
import IssuesCounter from './IssuesCounter'; | import IssuesCounter from './IssuesCounter'; | ||||
import TotalEffort from './TotalEffort'; | import TotalEffort from './TotalEffort'; | ||||
interface Props { | |||||
export interface PageActionsProps { | |||||
canSetHome: boolean; | canSetHome: boolean; | ||||
effortTotal: number | undefined; | effortTotal: number | undefined; | ||||
onReload: () => void; | |||||
paging: T.Paging | undefined; | |||||
selectedIndex: number | undefined; | |||||
paging?: T.Paging; | |||||
selectedIndex?: number; | |||||
} | } | ||||
export default class PageActions extends React.PureComponent<Props> { | |||||
renderShortcuts() { | |||||
return ( | |||||
<span className="note big-spacer-right"> | |||||
<span className="big-spacer-right"> | |||||
<span className="shortcut-button little-spacer-right">↑</span> | |||||
<span className="shortcut-button little-spacer-right">↓</span> | |||||
{translate('issues.to_select_issues')} | |||||
</span> | |||||
export default function PageActions(props: PageActionsProps) { | |||||
const { canSetHome, effortTotal, paging, selectedIndex } = props; | |||||
<span> | |||||
<span className="shortcut-button little-spacer-right">←</span> | |||||
<span className="shortcut-button little-spacer-right">→</span> | |||||
{translate('issues.to_navigate')} | |||||
</span> | |||||
</span> | |||||
); | |||||
} | |||||
return ( | |||||
<div className="display-flex-center display-flex-justify-end"> | |||||
<PageShortcutsTooltip | |||||
leftAndRightLabel={translate('issues.to_navigate')} | |||||
upAndDownLabel={translate('issues.to_select_issues')} | |||||
/> | |||||
render() { | |||||
const { effortTotal, paging, selectedIndex } = this.props; | |||||
return ( | |||||
<div className="pull-right"> | |||||
{this.renderShortcuts()} | |||||
<div className="issues-page-actions"> | |||||
<ReloadButton onClick={this.props.onReload} /> | |||||
{paging != null && <IssuesCounter current={selectedIndex} total={paging.total} />} | |||||
{effortTotal !== undefined && <TotalEffort effort={effortTotal} />} | |||||
</div> | |||||
{this.props.canSetHome && ( | |||||
<HomePageSelect | |||||
className="huge-spacer-left" | |||||
currentPage={isSonarCloud() ? { type: 'MY_ISSUES' } : { type: 'ISSUES' }} | |||||
/> | |||||
)} | |||||
<div className="spacer-left issues-page-actions"> | |||||
{paging != null && <IssuesCounter current={selectedIndex} total={paging.total} />} | |||||
{effortTotal !== undefined && <TotalEffort effort={effortTotal} />} | |||||
</div> | </div> | ||||
); | |||||
} | |||||
{canSetHome && ( | |||||
<HomePageSelect | |||||
className="huge-spacer-left" | |||||
currentPage={isSonarCloud() ? { type: 'MY_ISSUES' } : { type: 'ISSUES' }} | |||||
/> | |||||
)} | |||||
</div> | |||||
); | |||||
} | } |
fetchBranchStatus.mockClear(); | fetchBranchStatus.mockClear(); | ||||
instance.handleBulkChangeDone(); | instance.handleBulkChangeDone(); | ||||
expect(fetchBranchStatus).toBeCalled(); | expect(fetchBranchStatus).toBeCalled(); | ||||
fetchBranchStatus.mockClear(); | |||||
instance.handleReload(); | |||||
expect(fetchBranchStatus).toBeCalled(); | |||||
}); | }); | ||||
it('should update the open issue when it is changed', async () => { | it('should update the open issue when it is changed', async () => { |
import PageActions from '../PageActions'; | import PageActions from '../PageActions'; | ||||
it('should render', () => { | it('should render', () => { | ||||
expect( | |||||
shallow( | |||||
<PageActions | |||||
canSetHome={true} | |||||
effortTotal={125} | |||||
onReload={jest.fn()} | |||||
paging={{ pageIndex: 1, pageSize: 100, total: 12345 }} | |||||
selectedIndex={5} | |||||
/> | |||||
) | |||||
).toMatchSnapshot(); | |||||
expect(shallow(<PageActions canSetHome={true} effortTotal={125} />)).toMatchSnapshot(); | |||||
}); | }); |
<PageActions | <PageActions | ||||
canSetHome={false} | canSetHome={false} | ||||
effortTotal={1} | effortTotal={1} | ||||
onReload={[Function]} | |||||
paging={ | paging={ | ||||
Object { | Object { | ||||
"pageIndex": 1, | "pageIndex": 1, |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly 1`] = ` | |||||
<div | |||||
className="navigation-keyboard-shortcuts big-spacer-top text-center" | |||||
> | |||||
<span> | |||||
alt + ↑ ↓ | |||||
issues.to_navigate_issue_locations | |||||
</span> | |||||
</div> | |||||
`; | |||||
exports[`should render correctly 2`] = ` | |||||
<div | |||||
className="navigation-keyboard-shortcuts big-spacer-top text-center" | |||||
> | |||||
<span> | |||||
alt + ↑ ↓ | |||||
issues.to_navigate_issue_locations | |||||
</span> | |||||
</div> | |||||
`; |
exports[`should render 1`] = ` | exports[`should render 1`] = ` | ||||
<div | <div | ||||
className="pull-right" | |||||
className="display-flex-center display-flex-justify-end" | |||||
> | > | ||||
<span | |||||
className="note big-spacer-right" | |||||
> | |||||
<span | |||||
className="big-spacer-right" | |||||
> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
↑ | |||||
</span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
↓ | |||||
</span> | |||||
issues.to_select_issues | |||||
</span> | |||||
<span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
→ | |||||
</span> | |||||
issues.to_navigate | |||||
</span> | |||||
</span> | |||||
<PageShortcutsTooltip | |||||
leftAndRightLabel="issues.to_navigate" | |||||
upAndDownLabel="issues.to_select_issues" | |||||
/> | |||||
<div | <div | ||||
className="issues-page-actions" | |||||
className="spacer-left issues-page-actions" | |||||
> | > | ||||
<ReloadButton | |||||
onClick={[MockFunction]} | |||||
/> | |||||
<IssuesCounter | |||||
current={5} | |||||
total={12345} | |||||
/> | |||||
<TotalEffort | <TotalEffort | ||||
effort={125} | effort={125} | ||||
/> | /> |
import * as classNames from 'classnames'; | import * as classNames from 'classnames'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import TypeHelper from '../../../components/shared/TypeHelper'; | import TypeHelper from '../../../components/shared/TypeHelper'; | ||||
import LocationNavigationKeyboardShortcuts from '../components/LocationNavigationKeyboardShortcuts'; | |||||
import ConciseIssueLocations from './ConciseIssueLocations'; | import ConciseIssueLocations from './ConciseIssueLocations'; | ||||
import ConciseIssueLocationsNavigator from './ConciseIssueLocationsNavigator'; | import ConciseIssueLocationsNavigator from './ConciseIssueLocationsNavigator'; | ||||
/> | /> | ||||
</div> | </div> | ||||
{selected && ( | {selected && ( | ||||
<> | |||||
<ConciseIssueLocationsNavigator | |||||
issue={issue} | |||||
onLocationSelect={this.props.onLocationSelect} | |||||
scroll={this.props.scroll} | |||||
selectedFlowIndex={this.props.selectedFlowIndex} | |||||
selectedLocationIndex={this.props.selectedLocationIndex} | |||||
/> | |||||
<LocationNavigationKeyboardShortcuts issue={issue} /> | |||||
</> | |||||
<ConciseIssueLocationsNavigator | |||||
issue={issue} | |||||
onLocationSelect={this.props.onLocationSelect} | |||||
scroll={this.props.scroll} | |||||
selectedFlowIndex={this.props.selectedFlowIndex} | |||||
selectedLocationIndex={this.props.selectedLocationIndex} | |||||
/> | |||||
)} | )} | ||||
</div> | </div> | ||||
); | ); |
*/ | */ | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import BackButton from 'sonar-ui-common/components/controls/BackButton'; | import BackButton from 'sonar-ui-common/components/controls/BackButton'; | ||||
import ReloadButton from 'sonar-ui-common/components/controls/ReloadButton'; | |||||
import IssuesCounter from '../components/IssuesCounter'; | |||||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||||
import PageShortcutsTooltip from '../../../components/ui/PageShortcutsTooltip'; | |||||
interface Props { | |||||
displayBackButton?: boolean; | |||||
export interface ConciseIssuesListHeaderProps { | |||||
displayBackButton: boolean; | |||||
loading: boolean; | loading: boolean; | ||||
onBackClick: () => void; | onBackClick: () => void; | ||||
onReload: () => void; | |||||
paging: T.Paging | undefined; | |||||
selectedIndex: number | undefined; | |||||
} | } | ||||
export default function ConciseIssuesListHeader(props: Props) { | |||||
const { displayBackButton = true, paging, selectedIndex } = props; | |||||
export default function ConciseIssuesListHeader(props: ConciseIssuesListHeaderProps) { | |||||
const { displayBackButton, loading } = props; | |||||
return ( | return ( | ||||
<header className="layout-page-header-panel concise-issues-list-header"> | <header className="layout-page-header-panel concise-issues-list-header"> | ||||
<div className="layout-page-header-panel-inner concise-issues-list-header-inner"> | |||||
{displayBackButton && ( | |||||
<BackButton className="pull-left" disabled={props.loading} onClick={props.onBackClick} /> | |||||
)} | |||||
{props.loading ? ( | |||||
<i className="spinner pull-right" /> | |||||
) : ( | |||||
<ReloadButton className="pull-right" onClick={props.onReload} /> | |||||
)} | |||||
{paging && <IssuesCounter current={selectedIndex} total={paging.total} />} | |||||
<div className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between"> | |||||
{displayBackButton && <BackButton disabled={loading} onClick={props.onBackClick} />} | |||||
<PageShortcutsTooltip | |||||
leftLabel={translate('issues.to_navigate_back')} | |||||
upAndDownLabel={translate('issues.to_select_issues')} | |||||
metaModifierLabel={translate('issues.to_navigate_issue_locations')} | |||||
/> | |||||
{loading && <i className="spinner" />} | |||||
</div> | </div> | ||||
</header> | </header> | ||||
); | ); |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { shallow } from 'enzyme'; | import { shallow } from 'enzyme'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { mockFlowLocation } from '../../../../helpers/testMocks'; | |||||
import LocationNavigationKeyboardShortcuts, { Props } from '../LocationNavigationKeyboardShortcuts'; | |||||
import ConciseIssuesListHeader, { ConciseIssuesListHeaderProps } from '../ConciseIssuesListHeader'; | |||||
it('should render correctly', () => { | it('should render correctly', () => { | ||||
expect(shallowRender().type()).toBeNull(); | |||||
expect(shallowRender({ issue: { flows: [], secondaryLocations: [] } }).type()).toBeNull(); | |||||
expect( | |||||
shallowRender({ issue: { flows: [], secondaryLocations: [mockFlowLocation()] } }) | |||||
).toMatchSnapshot(); | |||||
expect( | |||||
shallowRender({ issue: { flows: [[mockFlowLocation()]], secondaryLocations: [] } }) | |||||
).toMatchSnapshot(); | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); | |||||
expect(shallowRender({ displayBackButton: true })).toMatchSnapshot('with back button'); | |||||
}); | }); | ||||
const shallowRender = (props: Partial<Props> = {}) => { | |||||
return shallow(<LocationNavigationKeyboardShortcuts issue={undefined} {...props} />); | |||||
}; | |||||
function shallowRender(props: Partial<ConciseIssuesListHeaderProps> = {}) { | |||||
return shallow<ConciseIssuesListHeaderProps>( | |||||
<ConciseIssuesListHeader | |||||
displayBackButton={false} | |||||
loading={false} | |||||
onBackClick={jest.fn()} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
selectedFlowIndex={0} | selectedFlowIndex={0} | ||||
selectedLocationIndex={0} | selectedLocationIndex={0} | ||||
/> | /> | ||||
<LocationNavigationKeyboardShortcuts | |||||
issue={ | |||||
Object { | |||||
"actions": Array [], | |||||
"component": "main.js", | |||||
"componentLongName": "main.js", | |||||
"componentQualifier": "FIL", | |||||
"componentUuid": "foo1234", | |||||
"creationDate": "2017-03-01T09:36:01+0100", | |||||
"flows": Array [], | |||||
"fromHotspot": false, | |||||
"key": "AVsae-CQS-9G3txfbFN2", | |||||
"line": 25, | |||||
"message": "Reduce the number of conditional operators (4) used in the expression", | |||||
"project": "myproject", | |||||
"projectKey": "foo", | |||||
"projectName": "Foo", | |||||
"rule": "javascript:S1067", | |||||
"ruleName": "foo", | |||||
"secondaryLocations": Array [], | |||||
"severity": "MAJOR", | |||||
"status": "OPEN", | |||||
"textRange": Object { | |||||
"endLine": 26, | |||||
"endOffset": 15, | |||||
"startLine": 25, | |||||
"startOffset": 0, | |||||
}, | |||||
"transitions": Array [], | |||||
"type": "BUG", | |||||
} | |||||
} | |||||
/> | |||||
</div> | </div> | ||||
`; | `; | ||||
selectedFlowIndex={0} | selectedFlowIndex={0} | ||||
selectedLocationIndex={0} | selectedLocationIndex={0} | ||||
/> | /> | ||||
<LocationNavigationKeyboardShortcuts | |||||
issue={ | |||||
Object { | |||||
"actions": Array [], | |||||
"component": "main.js", | |||||
"componentLongName": "main.js", | |||||
"componentQualifier": "FIL", | |||||
"componentUuid": "foo1234", | |||||
"creationDate": "2017-03-01T09:36:01+0100", | |||||
"flows": Array [ | |||||
Array [ | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
], | |||||
Array [ | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
], | |||||
], | |||||
"fromHotspot": false, | |||||
"key": "AVsae-CQS-9G3txfbFN2", | |||||
"line": 25, | |||||
"message": "Reduce the number of conditional operators (4) used in the expression", | |||||
"project": "myproject", | |||||
"projectKey": "foo", | |||||
"projectName": "Foo", | |||||
"rule": "javascript:S1067", | |||||
"ruleName": "foo", | |||||
"secondaryLocations": Array [ | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
Object { | |||||
"component": "main.js", | |||||
"textRange": Object { | |||||
"endLine": 2, | |||||
"endOffset": 2, | |||||
"startLine": 1, | |||||
"startOffset": 1, | |||||
}, | |||||
}, | |||||
], | |||||
"severity": "MAJOR", | |||||
"status": "OPEN", | |||||
"textRange": Object { | |||||
"endLine": 26, | |||||
"endOffset": 15, | |||||
"startLine": 25, | |||||
"startOffset": 0, | |||||
}, | |||||
"transitions": Array [], | |||||
"type": "BUG", | |||||
} | |||||
} | |||||
/> | |||||
</div> | </div> | ||||
`; | `; |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
<header | |||||
className="layout-page-header-panel concise-issues-list-header" | |||||
> | |||||
<div | |||||
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between" | |||||
> | |||||
<PageShortcutsTooltip | |||||
leftLabel="issues.to_navigate_back" | |||||
metaModifierLabel="issues.to_navigate_issue_locations" | |||||
upAndDownLabel="issues.to_select_issues" | |||||
/> | |||||
</div> | |||||
</header> | |||||
`; | |||||
exports[`should render correctly: loading 1`] = ` | |||||
<header | |||||
className="layout-page-header-panel concise-issues-list-header" | |||||
> | |||||
<div | |||||
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between" | |||||
> | |||||
<PageShortcutsTooltip | |||||
leftLabel="issues.to_navigate_back" | |||||
metaModifierLabel="issues.to_navigate_issue_locations" | |||||
upAndDownLabel="issues.to_select_issues" | |||||
/> | |||||
<i | |||||
className="spinner" | |||||
/> | |||||
</div> | |||||
</header> | |||||
`; | |||||
exports[`should render correctly: with back button 1`] = ` | |||||
<header | |||||
className="layout-page-header-panel concise-issues-list-header" | |||||
> | |||||
<div | |||||
className="layout-page-header-panel-inner concise-issues-list-header-inner display-flex-center display-flex-space-between" | |||||
> | |||||
<BackButton | |||||
disabled={false} | |||||
onClick={[MockFunction]} | |||||
/> | |||||
<PageShortcutsTooltip | |||||
leftLabel="issues.to_navigate_back" | |||||
metaModifierLabel="issues.to_navigate_issue_locations" | |||||
upAndDownLabel="issues.to_select_issues" | |||||
/> | |||||
</div> | |||||
</header> | |||||
`; |
.bulk-change-radio-button:hover { | .bulk-change-radio-button:hover { | ||||
background-color: var(--barBackgroundColor); | background-color: var(--barBackgroundColor); | ||||
} | } | ||||
.navigation-keyboard-shortcuts > span { | |||||
background-color: var(--transparentGray); | |||||
border-radius: 16px; | |||||
display: inline-block; | |||||
font-size: var(--smallFontSize); | |||||
height: 16px; | |||||
padding: calc(var(--gridSize) / 2) var(--gridSize); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2021 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 classNames from 'classnames'; | |||||
import * as React from 'react'; | |||||
import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; | |||||
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; | |||||
export interface PageShortcutsTooltipProps { | |||||
className?: string; | |||||
leftAndRightLabel?: string; | |||||
leftLabel?: string; | |||||
upAndDownLabel?: string; | |||||
metaModifierLabel?: string; | |||||
} | |||||
export default function PageShortcutsTooltip(props: PageShortcutsTooltipProps) { | |||||
const { className, leftAndRightLabel, leftLabel, upAndDownLabel, metaModifierLabel } = props; | |||||
return ( | |||||
<Tooltip | |||||
overlay={ | |||||
<div className="small nowrap"> | |||||
<div> | |||||
{upAndDownLabel && ( | |||||
<span> | |||||
<span className="shortcut-button little-spacer-right">↑</span> | |||||
<span className="shortcut-button spacer-right">↓</span> | |||||
{upAndDownLabel} | |||||
</span> | |||||
)} | |||||
{leftAndRightLabel && ( | |||||
<span className={classNames({ 'big-spacer-left': upAndDownLabel })}> | |||||
<span className="shortcut-button little-spacer-right">←</span> | |||||
<span className="shortcut-button spacer-right">→</span> | |||||
{leftAndRightLabel} | |||||
</span> | |||||
)} | |||||
{leftLabel && ( | |||||
<span className={classNames({ 'big-spacer-left': upAndDownLabel })}> | |||||
<span className="shortcut-button spacer-right">←</span> | |||||
{leftLabel} | |||||
</span> | |||||
)} | |||||
</div> | |||||
{metaModifierLabel && ( | |||||
<div className="big-spacer-top big-padded-top bordered-top"> | |||||
<span className="shortcut-button little-spacer-right">alt</span> | |||||
<span className="little-spacer-right">+</span> | |||||
<span className="shortcut-button little-spacer-right">↑</span> | |||||
<span className="shortcut-button spacer-right">↓</span> | |||||
<span className="shortcut-button little-spacer-right">←</span> | |||||
<span className="shortcut-button spacer-right">→</span> | |||||
{metaModifierLabel} | |||||
</div> | |||||
)} | |||||
</div> | |||||
}> | |||||
<div | |||||
aria-label={` | |||||
${translate('shortcuts.on_page.intro')} | |||||
${ | |||||
upAndDownLabel | |||||
? translateWithParameters('shortcuts.on_page.up_down_x', upAndDownLabel) | |||||
: '' | |||||
} | |||||
${ | |||||
leftAndRightLabel | |||||
? translateWithParameters('shortcuts.on_page.left_right_x', leftAndRightLabel) | |||||
: '' | |||||
} | |||||
${leftLabel ? translateWithParameters('shortcuts.on_page.left_x', leftLabel) : ''} | |||||
${ | |||||
metaModifierLabel | |||||
? translateWithParameters('shortcuts.on_page.meta_x', metaModifierLabel) | |||||
: '' | |||||
} | |||||
`} | |||||
className={classNames( | |||||
className, | |||||
'page-shortcuts-tooltip note text-center display-inline-block' | |||||
)}> | |||||
<div> | |||||
<span className="shortcut-button shortcut-button-tiny">↑</span> | |||||
</div> | |||||
<div> | |||||
<span className="shortcut-button shortcut-button-tiny">←</span> | |||||
<span className="shortcut-button shortcut-button-tiny">↓</span> | |||||
<span className="shortcut-button shortcut-button-tiny">→</span> | |||||
</div> | |||||
</div> | |||||
</Tooltip> | |||||
); | |||||
} |
/* | |||||
* SonarQube | |||||
* Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; | |||||
import * as React from 'react'; | |||||
import PageShortcutsTooltip, { PageShortcutsTooltipProps } from '../PageShortcutsTooltip'; | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('default'); | |||||
expect(shallowRender({ upAndDownLabel: 'foo', leftAndRightLabel: 'bar' })).toMatchSnapshot( | |||||
'with up/down and left/right labels' | |||||
); | |||||
expect(shallowRender({ leftLabel: 'baz' })).toMatchSnapshot('only left label'); | |||||
expect(shallowRender({ metaModifierLabel: 'funky' })).toMatchSnapshot('with meta label'); | |||||
}); | |||||
function shallowRender(props: Partial<PageShortcutsTooltipProps> = {}) { | |||||
return shallow<PageShortcutsTooltipProps>(<PageShortcutsTooltip {...props} />); | |||||
} |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: default 1`] = ` | |||||
<Tooltip | |||||
overlay={ | |||||
<div | |||||
className="small nowrap" | |||||
> | |||||
<div /> | |||||
</div> | |||||
} | |||||
> | |||||
<div | |||||
aria-label=" | |||||
shortcuts.on_page.intro | |||||
" | |||||
className="page-shortcuts-tooltip note text-center display-inline-block" | |||||
> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↑ | |||||
</span> | |||||
</div> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↓ | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
→ | |||||
</span> | |||||
</div> | |||||
</div> | |||||
</Tooltip> | |||||
`; | |||||
exports[`should render correctly: only left label 1`] = ` | |||||
<Tooltip | |||||
overlay={ | |||||
<div | |||||
className="small nowrap" | |||||
> | |||||
<div> | |||||
<span | |||||
className="" | |||||
> | |||||
<span | |||||
className="shortcut-button spacer-right" | |||||
> | |||||
← | |||||
</span> | |||||
baz | |||||
</span> | |||||
</div> | |||||
</div> | |||||
} | |||||
> | |||||
<div | |||||
aria-label=" | |||||
shortcuts.on_page.intro | |||||
shortcuts.on_page.left_x.baz | |||||
" | |||||
className="page-shortcuts-tooltip note text-center display-inline-block" | |||||
> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↑ | |||||
</span> | |||||
</div> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↓ | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
→ | |||||
</span> | |||||
</div> | |||||
</div> | |||||
</Tooltip> | |||||
`; | |||||
exports[`should render correctly: with meta label 1`] = ` | |||||
<Tooltip | |||||
overlay={ | |||||
<div | |||||
className="small nowrap" | |||||
> | |||||
<div /> | |||||
<div | |||||
className="big-spacer-top big-padded-top bordered-top" | |||||
> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
alt | |||||
</span> | |||||
<span | |||||
className="little-spacer-right" | |||||
> | |||||
+ | |||||
</span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
↑ | |||||
</span> | |||||
<span | |||||
className="shortcut-button spacer-right" | |||||
> | |||||
↓ | |||||
</span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button spacer-right" | |||||
> | |||||
→ | |||||
</span> | |||||
funky | |||||
</div> | |||||
</div> | |||||
} | |||||
> | |||||
<div | |||||
aria-label=" | |||||
shortcuts.on_page.intro | |||||
shortcuts.on_page.meta_x.funky | |||||
" | |||||
className="page-shortcuts-tooltip note text-center display-inline-block" | |||||
> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↑ | |||||
</span> | |||||
</div> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↓ | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
→ | |||||
</span> | |||||
</div> | |||||
</div> | |||||
</Tooltip> | |||||
`; | |||||
exports[`should render correctly: with up/down and left/right labels 1`] = ` | |||||
<Tooltip | |||||
overlay={ | |||||
<div | |||||
className="small nowrap" | |||||
> | |||||
<div> | |||||
<span> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
↑ | |||||
</span> | |||||
<span | |||||
className="shortcut-button spacer-right" | |||||
> | |||||
↓ | |||||
</span> | |||||
foo | |||||
</span> | |||||
<span | |||||
className="big-spacer-left" | |||||
> | |||||
<span | |||||
className="shortcut-button little-spacer-right" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button spacer-right" | |||||
> | |||||
→ | |||||
</span> | |||||
bar | |||||
</span> | |||||
</div> | |||||
</div> | |||||
} | |||||
> | |||||
<div | |||||
aria-label=" | |||||
shortcuts.on_page.intro | |||||
shortcuts.on_page.up_down_x.foo | |||||
shortcuts.on_page.left_right_x.bar | |||||
" | |||||
className="page-shortcuts-tooltip note text-center display-inline-block" | |||||
> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↑ | |||||
</span> | |||||
</div> | |||||
<div> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
← | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
↓ | |||||
</span> | |||||
<span | |||||
className="shortcut-button shortcut-button-tiny" | |||||
> | |||||
→ | |||||
</span> | |||||
</div> | |||||
</div> | |||||
</Tooltip> | |||||
`; |
issues.issues=issues | issues.issues=issues | ||||
issues.to_select_issues=to select issues | issues.to_select_issues=to select issues | ||||
issues.to_navigate=to navigate | issues.to_navigate=to navigate | ||||
issues.to_navigate_back=to navigate back | |||||
issues.to_navigate_issue_locations=to navigate issue locations | issues.to_navigate_issue_locations=to navigate issue locations | ||||
issues.to_switch_flows=to switch flows | issues.to_switch_flows=to switch flows | ||||
issues.new_code=New code | issues.new_code=New code | ||||
shortcuts.section.rules.activate=activate selected rule | shortcuts.section.rules.activate=activate selected rule | ||||
shortcuts.section.rules.deactivate=deactivate selected rule | shortcuts.section.rules.deactivate=deactivate selected rule | ||||
shortcuts.on_page.intro=This page allows you to use the following keyboard shortcuts: | |||||
shortcuts.on_page.left_x=Left arrow key: {0} | |||||
shortcuts.on_page.left_right_x=Left and right arrow keys: {0} | |||||
shortcuts.on_page.up_down_x=Up and down arrow keys: {0} | |||||
shortcuts.on_page.meta_x=Alt key + arrow keys: {0} | |||||
tutorials.onboarding=Analyze a new project | tutorials.onboarding=Analyze a new project | ||||
tutorials.skip=Skip this tutorial | tutorials.skip=Skip this tutorial | ||||
tutorials.finish=Finish this tutorial | tutorials.finish=Finish this tutorial |