Co-authored-by: Grégoire Aubert <gregoire.aubert@sonarsource.com> Co-authored-by: Wouter Admiraal <wouter.admiraal@sonarsource.com>tags/8.2.0.32929
@@ -99,7 +99,7 @@ | |||
.issue-see-rule { | |||
border-bottom: none; | |||
line-height: 16px; | |||
font-size: var(--smallFontSize); | |||
} | |||
.issue-changelog { |
@@ -18,53 +18,45 @@ | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { Button } from 'sonar-ui-common/components/controls/buttons'; | |||
import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; | |||
import Tooltip from 'sonar-ui-common/components/controls/Tooltip'; | |||
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; | |||
import { WorkspaceContextShape } from '../../workspace/context'; | |||
interface Props { | |||
export interface IssueMessageProps { | |||
engine?: string; | |||
manualVulnerability: boolean; | |||
message: string; | |||
openRule: WorkspaceContextShape['openRule']; | |||
onOpenRule: WorkspaceContextShape['openRule']; | |||
organization: string; | |||
rule: string; | |||
ruleKey: string; | |||
} | |||
export default class IssueMessage extends React.PureComponent<Props> { | |||
handleClick = () => { | |||
this.props.openRule({ | |||
key: this.props.rule, | |||
organization: this.props.organization | |||
}); | |||
}; | |||
export default function IssueMessage(props: IssueMessageProps) { | |||
const { engine, manualVulnerability, message, organization, ruleKey } = props; | |||
render() { | |||
return ( | |||
<div className="issue-message"> | |||
<span className="spacer-right">{this.props.message}</span> | |||
<Button | |||
aria-label={translate('issue.rule_details')} | |||
className="button button-link issue-see-rule spacer-right text-top" | |||
onClick={this.handleClick}> | |||
{translate('issue.see_rule')} | |||
</Button> | |||
return ( | |||
<div className="issue-message"> | |||
<span className="spacer-right">{message}</span> | |||
<ButtonLink | |||
aria-label={translate('issue.why_this_issue.long')} | |||
className="issue-see-rule spacer-right" | |||
onClick={() => props.onOpenRule({ key: ruleKey, organization })}> | |||
{translate('issue.why_this_issue')} | |||
</ButtonLink> | |||
{this.props.engine && ( | |||
<Tooltip | |||
overlay={translateWithParameters('issue.from_external_rule_engine', this.props.engine)}> | |||
<div className="badge spacer-right text-top">{this.props.engine}</div> | |||
</Tooltip> | |||
)} | |||
{this.props.manualVulnerability && ( | |||
<Tooltip overlay={translate('issue.manual_vulnerability.description')}> | |||
<div className="badge spacer-right text-top"> | |||
{translate('issue.manual_vulnerability')} | |||
</div> | |||
</Tooltip> | |||
)} | |||
</div> | |||
); | |||
} | |||
{engine && ( | |||
<Tooltip overlay={translateWithParameters('issue.from_external_rule_engine', engine)}> | |||
<div className="badge spacer-right text-top">{engine}</div> | |||
</Tooltip> | |||
)} | |||
{manualVulnerability && ( | |||
<Tooltip overlay={translate('issue.manual_vulnerability.description')}> | |||
<div className="badge spacer-right text-top"> | |||
{translate('issue.manual_vulnerability')} | |||
</div> | |||
</Tooltip> | |||
)} | |||
</div> | |||
); | |||
} |
@@ -77,9 +77,9 @@ export default function IssueTitleBar(props: Props) { | |||
engine={issue.externalRuleEngine} | |||
manualVulnerability={issue.fromHotspot && issue.type === 'VULNERABILITY'} | |||
message={issue.message} | |||
openRule={openRule} | |||
onOpenRule={openRule} | |||
organization={issue.organization} | |||
rule={issue.rule} | |||
ruleKey={issue.rule} | |||
/> | |||
)} | |||
</WorkspaceContext.Consumer> |
@@ -17,19 +17,35 @@ | |||
* 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 IssueMessage from '../IssueMessage'; | |||
import { ButtonLink } from 'sonar-ui-common/components/controls/buttons'; | |||
import { click } from 'sonar-ui-common/helpers/testUtils'; | |||
import IssueMessage, { IssueMessageProps } from '../IssueMessage'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('default'); | |||
expect(shallowRender({ engine: 'js' })).toMatchSnapshot('with engine info'); | |||
expect(shallowRender({ manualVulnerability: true })).toMatchSnapshot('is manual vulnerability'); | |||
}); | |||
it('should render with the message and a link to open the rule', () => { | |||
const element = shallow( | |||
it('should handle click correctly', () => { | |||
const onOpenRule = jest.fn(); | |||
const wrapper = shallowRender({ onOpenRule }); | |||
click(wrapper.find(ButtonLink)); | |||
expect(onOpenRule).toBeCalledWith({ key: 'javascript:S1067', organization: 'myorg' }); | |||
}); | |||
function shallowRender(props: Partial<IssueMessageProps> = {}) { | |||
return shallow<IssueMessageProps>( | |||
<IssueMessage | |||
manualVulnerability={false} | |||
message="Reduce the number of conditional operators (4) used in the expression" | |||
openRule={jest.fn()} | |||
onOpenRule={jest.fn()} | |||
organization="myorg" | |||
rule="javascript:S1067" | |||
ruleKey="javascript:S1067" | |||
{...props} | |||
/> | |||
); | |||
expect(element).toMatchSnapshot(); | |||
}); | |||
} |
@@ -1,6 +1,6 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render with the message and a link to open the rule 1`] = ` | |||
exports[`should render correctly: default 1`] = ` | |||
<div | |||
className="issue-message" | |||
> | |||
@@ -9,12 +9,68 @@ exports[`should render with the message and a link to open the rule 1`] = ` | |||
> | |||
Reduce the number of conditional operators (4) used in the expression | |||
</span> | |||
<Button | |||
aria-label="issue.rule_details" | |||
className="button button-link issue-see-rule spacer-right text-top" | |||
<ButtonLink | |||
aria-label="issue.why_this_issue.long" | |||
className="issue-see-rule spacer-right" | |||
onClick={[Function]} | |||
> | |||
issue.see_rule | |||
</Button> | |||
issue.why_this_issue | |||
</ButtonLink> | |||
</div> | |||
`; | |||
exports[`should render correctly: is manual vulnerability 1`] = ` | |||
<div | |||
className="issue-message" | |||
> | |||
<span | |||
className="spacer-right" | |||
> | |||
Reduce the number of conditional operators (4) used in the expression | |||
</span> | |||
<ButtonLink | |||
aria-label="issue.why_this_issue.long" | |||
className="issue-see-rule spacer-right" | |||
onClick={[Function]} | |||
> | |||
issue.why_this_issue | |||
</ButtonLink> | |||
<Tooltip | |||
overlay="issue.manual_vulnerability.description" | |||
> | |||
<div | |||
className="badge spacer-right text-top" | |||
> | |||
issue.manual_vulnerability | |||
</div> | |||
</Tooltip> | |||
</div> | |||
`; | |||
exports[`should render correctly: with engine info 1`] = ` | |||
<div | |||
className="issue-message" | |||
> | |||
<span | |||
className="spacer-right" | |||
> | |||
Reduce the number of conditional operators (4) used in the expression | |||
</span> | |||
<ButtonLink | |||
aria-label="issue.why_this_issue.long" | |||
className="issue-see-rule spacer-right" | |||
onClick={[Function]} | |||
> | |||
issue.why_this_issue | |||
</ButtonLink> | |||
<Tooltip | |||
overlay="issue.from_external_rule_engine.js" | |||
> | |||
<div | |||
className="badge spacer-right text-top" | |||
> | |||
js | |||
</div> | |||
</Tooltip> | |||
</div> | |||
`; |
@@ -734,7 +734,8 @@ issue.manual_vulnerability=Manual | |||
issue.manual_vulnerability.description=This Vulnerability was created from a Security Hotspot and has its own issue workflow. | |||
issue.rule_details=Rule Details | |||
issue.send_notifications=Send Notifications | |||
issue.see_rule=See Rule | |||
issue.why_this_issue=Why is this an issue? | |||
issue.why_this_issue.long=Why is this an issue? Open the rule's details at the bottom of the page. | |||
issue.transition=Transition | |||
issue.transition.confirm=Confirm | |||
issue.transition.confirm.description=This issue has been reviewed and something should be done eventually to handle it. |