diff options
16 files changed, 407 insertions, 338 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx index f93a987b1c1..9c0ac32197a 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx @@ -50,6 +50,16 @@ beforeEach(() => { window.HTMLElement.prototype.scrollIntoView = jest.fn(); }); +it('should navigate to Why is this an issue tab', async () => { + renderProjectIssuesApp('project/issues?issues=issue2&open=issue2&id=myproject&why=1'); + expect( + await screen.findByRole('tab', { + name: `coding_rules.description_section.title.root_cause`, + selected: true, + }) + ).toBeInTheDocument(); +}); + //Improve this to include all the bulk change fonctionality it('should be able to bulk change', async () => { const user = userEvent.setup(); diff --git a/server/sonar-web/src/main/js/apps/issues/styles.css b/server/sonar-web/src/main/js/apps/issues/styles.css index e1d2e28f29e..bc12ffb35a6 100644 --- a/server/sonar-web/src/main/js/apps/issues/styles.css +++ b/server/sonar-web/src/main/js/apps/issues/styles.css @@ -169,18 +169,6 @@ width: 800px; } -.issues .issue { - border: 2px solid transparent; - cursor: pointer; -} - -.issues .issue:focus-within, -.issues .issue:hover { - border: 2px dashed var(--blue); - transition: all 0.3s ease; - outline: 0; -} - .issues .issue a:focus, .issues .issue button:focus { box-shadow: none; diff --git a/server/sonar-web/src/main/js/components/issue/Issue.css b/server/sonar-web/src/main/js/components/issue/Issue.css index 35a34901ecf..6a6731bb901 100644 --- a/server/sonar-web/src/main/js/components/issue/Issue.css +++ b/server/sonar-web/src/main/js/components/issue/Issue.css @@ -22,7 +22,8 @@ padding-top: var(--gridSize); padding-bottom: var(--gridSize); background-color: var(--issueBgColor); - transition: all 0.3s ease, border 0s ease; + transition: all 0.3s ease; + border: 2px solid transparent; cursor: pointer; } @@ -46,10 +47,6 @@ margin-top: 5px; } -.issue.selected + .issue { - border-top-color: transparent; -} - .issue-row { display: flex; margin-bottom: 5px; @@ -59,7 +56,6 @@ .issue-row-meta { padding-right: 5px; white-space: nowrap; - margin-top: 2px; } .issue-message { @@ -85,7 +81,7 @@ } .issue-meta { - line-height: 16px; + line-height: var(--smallFontSize); font-size: var(--smallFontSize); display: flex; } @@ -108,12 +104,6 @@ white-space: nowrap; } -.issue-see-rule { - border-bottom: none; - font-size: var(--smallFontSize); - margin-top: 5px; -} - .issue-changelog { width: 450px; max-height: 320px; @@ -275,7 +265,9 @@ background-color: var(--secondIssueBgColor); } -.issue-message-box.secondary-issue:hover { +.issue-message-box.secondary-issue:hover, +.issue:focus-within, +.issue:hover { border: 2px dashed var(--blue); outline: 0; cursor: pointer; diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx index ec8aa593d90..d60dd94ec08 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx @@ -18,36 +18,34 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { ButtonLink } from '../../../components/controls/buttons'; +import { Link } from 'react-router-dom'; +import { getBranchLikeQuery } from '../../../helpers/branch-like'; import { translate } from '../../../helpers/l10n'; -import { MessageFormatting } from '../../../types/issues'; +import { getComponentIssuesUrl } from '../../../helpers/urls'; +import { BranchLike } from '../../../types/branch-like'; import { RuleStatus } from '../../../types/rules'; -import { WorkspaceContext } from '../../workspace/context'; +import { Issue } from '../../../types/types'; import { IssueMessageHighlighting } from '../IssueMessageHighlighting'; import IssueMessageTags from './IssueMessageTags'; export interface IssueMessageProps { - engine?: string; - quickFixAvailable?: boolean; + issue: Issue; + branchLike?: BranchLike; displayWhyIsThisAnIssue?: boolean; - message: string; - messageFormattings?: MessageFormatting[]; - ruleKey: string; - ruleStatus?: RuleStatus; } export default function IssueMessage(props: IssueMessageProps) { - const { - engine, - quickFixAvailable, - message, - messageFormattings, - ruleKey, - ruleStatus, - displayWhyIsThisAnIssue, - } = props; + const { issue, branchLike, displayWhyIsThisAnIssue } = props; - const { openRule } = React.useContext(WorkspaceContext); + const { externalRuleEngine, quickFixAvailable, message, messageFormattings, ruleStatus } = issue; + + const whyIsThisAnIssueUrl = getComponentIssuesUrl(issue.project, { + ...getBranchLikeQuery(branchLike), + files: issue.componentLongName, + open: issue.key, + resolved: 'false', + why: '1', + }); return ( <> @@ -56,23 +54,20 @@ export default function IssueMessage(props: IssueMessageProps) { <IssueMessageHighlighting message={message} messageFormattings={messageFormattings} /> </span> <IssueMessageTags - engine={engine} + engine={externalRuleEngine} quickFixAvailable={quickFixAvailable} - ruleStatus={ruleStatus} + ruleStatus={ruleStatus as RuleStatus | undefined} /> </div> {displayWhyIsThisAnIssue && ( - <ButtonLink + <Link aria-label={translate('issue.why_this_issue.long')} - className="issue-see-rule spacer-right text-baseline" - onClick={() => - openRule({ - key: ruleKey, - }) - } + className="spacer-right" + target="_blank" + to={whyIsThisAnIssueUrl} > {translate('issue.why_this_issue')} - </ButtonLink> + </Link> )} </> ); diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx index b8bfdecfefa..8e63e16a494 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx @@ -26,7 +26,6 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; import { formatMeasure } from '../../../helpers/measures'; import { getComponentIssuesUrl } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; -import { RuleStatus } from '../../../types/rules'; import { Issue } from '../../../types/types'; import LocationIndex from '../../common/LocationIndex'; import IssueChangelog from './IssueChangelog'; @@ -76,13 +75,9 @@ export default function IssueTitleBar(props: IssueTitleBarProps) { return ( <div className="issue-row"> <IssueMessage - engine={issue.externalRuleEngine} - quickFixAvailable={issue.quickFixAvailable} + issue={issue} + branchLike={props.branchLike} displayWhyIsThisAnIssue={displayWhyIsThisAnIssue} - message={issue.message} - messageFormattings={issue.messageFormattings} - ruleKey={issue.rule} - ruleStatus={issue.ruleStatus as RuleStatus | undefined} /> <div className="issue-row-meta"> <div className="issue-meta-list"> diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx index 10d353040a8..8cea8e54049 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/IssueMessage-test.tsx @@ -19,8 +19,9 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import { mockBranch } from '../../../../helpers/mocks/branch-like'; +import { mockIssue } from '../../../../helpers/testMocks'; import { RuleStatus } from '../../../../types/rules'; -import { ButtonLink } from '../../../controls/buttons'; import IssueMessage, { IssueMessageProps } from '../IssueMessage'; jest.mock('react', () => { @@ -34,35 +35,31 @@ jest.mock('react', () => { it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ engine: 'js' })).toMatchSnapshot('with engine info'); - expect(shallowRender({ quickFixAvailable: true })).toMatchSnapshot('with quick fix'); - expect(shallowRender({ ruleStatus: RuleStatus.Deprecated })).toMatchSnapshot( - 'is deprecated rule' + expect(shallowRender({ issue: mockIssue(false, { externalRuleEngine: 'js' }) })).toMatchSnapshot( + 'with engine info' ); - expect(shallowRender({ ruleStatus: RuleStatus.Removed })).toMatchSnapshot('is removed rule'); + expect(shallowRender({ issue: mockIssue(false, { quickFixAvailable: true }) })).toMatchSnapshot( + 'with quick fix' + ); + expect( + shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Deprecated }) }) + ).toMatchSnapshot('is deprecated rule'); + expect( + shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Removed }) }) + ).toMatchSnapshot('is removed rule'); expect(shallowRender({ displayWhyIsThisAnIssue: false })).toMatchSnapshot( 'hide why is it an issue' ); }); -it('should open why is this an issue workspace', () => { - const openRule = jest.fn(); - (React.useContext as jest.Mock).mockImplementationOnce(() => ({ - externalRulesRepoNames: {}, - openRule, - })); - const wrapper = shallowRender(); - wrapper.find(ButtonLink).simulate('click'); - - expect(openRule).toHaveBeenCalled(); -}); - function shallowRender(props: Partial<IssueMessageProps> = {}) { return shallow<IssueMessageProps>( <IssueMessage - message="Reduce the number of conditional operators (4) used in the expression" + issue={mockIssue(false, { + message: 'Reduce the number of conditional operators (4) used in the expression', + })} displayWhyIsThisAnIssue={true} - ruleKey="javascript:S1067" + branchLike={mockBranch()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap index bc1d76c8876..bff0c5c5ab9 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap @@ -14,13 +14,20 @@ exports[`should render correctly: default 1`] = ` </span> <IssueMessageTags /> </div> - <ButtonLink + <Link aria-label="issue.why_this_issue.long" - className="issue-see-rule spacer-right text-baseline" - onClick={[Function]} + className="spacer-right" + target="_blank" + to={ + { + "hash": "", + "pathname": "/project/issues", + "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject", + } + } > issue.why_this_issue - </ButtonLink> + </Link> </Fragment> `; @@ -57,13 +64,20 @@ exports[`should render correctly: is deprecated rule 1`] = ` ruleStatus="DEPRECATED" /> </div> - <ButtonLink + <Link aria-label="issue.why_this_issue.long" - className="issue-see-rule spacer-right text-baseline" - onClick={[Function]} + className="spacer-right" + target="_blank" + to={ + { + "hash": "", + "pathname": "/project/issues", + "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject", + } + } > issue.why_this_issue - </ButtonLink> + </Link> </Fragment> `; @@ -83,13 +97,20 @@ exports[`should render correctly: is removed rule 1`] = ` ruleStatus="REMOVED" /> </div> - <ButtonLink + <Link aria-label="issue.why_this_issue.long" - className="issue-see-rule spacer-right text-baseline" - onClick={[Function]} + className="spacer-right" + target="_blank" + to={ + { + "hash": "", + "pathname": "/project/issues", + "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject", + } + } > issue.why_this_issue - </ButtonLink> + </Link> </Fragment> `; @@ -109,13 +130,20 @@ exports[`should render correctly: with engine info 1`] = ` engine="js" /> </div> - <ButtonLink + <Link aria-label="issue.why_this_issue.long" - className="issue-see-rule spacer-right text-baseline" - onClick={[Function]} + className="spacer-right" + target="_blank" + to={ + { + "hash": "", + "pathname": "/project/issues", + "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject", + } + } > issue.why_this_issue - </ButtonLink> + </Link> </Fragment> `; @@ -135,12 +163,19 @@ exports[`should render correctly: with quick fix 1`] = ` quickFixAvailable={true} /> </div> - <ButtonLink + <Link aria-label="issue.why_this_issue.long" - className="issue-see-rule spacer-right text-baseline" - onClick={[Function]} + className="spacer-right" + target="_blank" + to={ + { + "hash": "", + "pathname": "/project/issues", + "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject", + } + } > issue.why_this_issue - </ButtonLink> + </Link> </Fragment> `; diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap index ecb2cf2fe3a..d628602e618 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap @@ -5,9 +5,39 @@ exports[`should render correctly: default 1`] = ` className="issue-row" > <IssueMessage - engine="foo" - message="Reduce the number of conditional operators (4) used in the expression" - ruleKey="javascript:S1067" + issue={ + { + "actions": [], + "component": "main.js", + "componentEnabled": true, + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "externalRuleEngine": "foo", + "flows": [], + "flowsWithType": [], + "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": [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": [], + "type": "BUG", + } + } /> <div className="issue-row-meta" @@ -96,9 +126,39 @@ exports[`should render correctly: with filter 1`] = ` className="issue-row" > <IssueMessage - engine="foo" - message="Reduce the number of conditional operators (4) used in the expression" - ruleKey="javascript:S1067" + issue={ + { + "actions": [], + "component": "main.js", + "componentEnabled": true, + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "externalRuleEngine": "foo", + "flows": [], + "flowsWithType": [], + "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": [], + "severity": "MAJOR", + "status": "OPEN", + "textRange": { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": [], + "type": "BUG", + } + } /> <div className="issue-row-meta" @@ -229,8 +289,107 @@ exports[`should render correctly: with multi locations 1`] = ` className="issue-row" > <IssueMessage - message="Reduce the number of conditional operators (4) used in the expression" - ruleKey="javascript:S1067" + issue={ + { + "actions": [], + "component": "main.js", + "componentEnabled": true, + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": [ + [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + ], + "flowsWithType": [], + "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": [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + "severity": "MAJOR", + "status": "OPEN", + "textRange": { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": [], + "type": "BUG", + } + } /> <div className="issue-row-meta" @@ -398,8 +557,115 @@ exports[`should render correctly: with multi locations and link 1`] = ` className="issue-row" > <IssueMessage - message="Reduce the number of conditional operators (4) used in the expression" - ruleKey="javascript:S1067" + branchLike={ + { + "analysisDate": "2018-01-01", + "excludedFromPurge": true, + "isMain": false, + "name": "branch-6.7", + } + } + issue={ + { + "actions": [], + "component": "main.js", + "componentEnabled": true, + "componentLongName": "main.js", + "componentQualifier": "FIL", + "componentUuid": "foo1234", + "creationDate": "2017-03-01T09:36:01+0100", + "flows": [ + [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + ], + "flowsWithType": [], + "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": [ + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + { + "component": "main.js", + "textRange": { + "endLine": 2, + "endOffset": 2, + "startLine": 1, + "startOffset": 1, + }, + }, + ], + "severity": "MAJOR", + "status": "OPEN", + "textRange": { + "endLine": 26, + "endOffset": 15, + "startLine": 25, + "startOffset": 0, + }, + "transitions": [], + "type": "BUG", + } + } /> <div className="issue-row-meta" diff --git a/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx b/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx index 2001941d1ae..bd0fbf2952e 100644 --- a/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx +++ b/server/sonar-web/src/main/js/components/rules/RuleTabViewer.tsx @@ -20,6 +20,7 @@ import classNames from 'classnames'; import { cloneDeep, debounce, groupBy } from 'lodash'; import * as React from 'react'; +import { Location } from 'react-router-dom'; import { dismissNotice } from '../../api/users'; import { CurrentUserContextInterface } from '../../app/components/current-user/CurrentUserContext'; import withCurrentUserContext from '../../app/components/current-user/withCurrentUserContext'; @@ -29,6 +30,7 @@ import { RuleDetails } from '../../types/types'; import { NoticeType } from '../../types/users'; import ScreenPositionHelper from '../common/ScreenPositionHelper'; import BoxedTabs, { getTabId, getTabPanelId } from '../controls/BoxedTabs'; +import withLocation from '../hoc/withLocation'; import MoreInfoRuleDescription from './MoreInfoRuleDescription'; import RuleDescription from './RuleDescription'; import './style.css'; @@ -39,6 +41,7 @@ interface RuleTabViewerProps extends CurrentUserContextInterface { ruleDescriptionContextKey?: string; codeTabContent?: React.ReactNode; scrollInTab?: boolean; + location: Location; } interface State { @@ -83,6 +86,15 @@ export class RuleTabViewer extends React.PureComponent<RuleTabViewerProps, State componentDidMount() { this.setState((prevState) => this.computeState(prevState)); this.attachScrollEvent(); + + const tabs = this.computeTabs(Boolean(this.state.displayEducationalPrinciplesNotification)); + + const query = new URLSearchParams(this.props.location.search); + if (query.has('why')) { + this.setState({ + selectedTab: tabs.find((tab) => tab.key === TabKeys.WhyIsThisAnIssue) || tabs[0], + }); + } } componentDidUpdate(prevProps: RuleTabViewerProps, prevState: State) { @@ -337,4 +349,4 @@ export class RuleTabViewer extends React.PureComponent<RuleTabViewerProps, State } } -export default withCurrentUserContext(RuleTabViewer); +export default withCurrentUserContext(withLocation(RuleTabViewer)); diff --git a/server/sonar-web/src/main/js/components/workspace/Workspace.tsx b/server/sonar-web/src/main/js/components/workspace/Workspace.tsx index 07371757216..5bc8e4b96be 100644 --- a/server/sonar-web/src/main/js/components/workspace/Workspace.tsx +++ b/server/sonar-web/src/main/js/components/workspace/Workspace.tsx @@ -27,7 +27,6 @@ import './styles.css'; import WorkspaceComponentViewer from './WorkspaceComponentViewer'; import WorkspaceNav from './WorkspaceNav'; import WorkspacePortal from './WorkspacePortal'; -import WorkspaceRuleViewer from './WorkspaceRuleViewer'; const WORKSPACE = 'sonarqube-workspace'; interface State { @@ -35,7 +34,7 @@ interface State { externalRulesRepoNames: Dict<string>; height: number; maximized?: boolean; - open: { component?: string; rule?: string }; + open: { component?: string }; rules: RuleDescriptor[]; } @@ -125,17 +124,6 @@ export default class Workspace extends React.PureComponent<{}, State> { this.setState({ open: { component: componentKey } }); }; - handleOpenRule = (rule: RuleDescriptor) => { - this.setState((state: State) => ({ - open: { rule: rule.key }, - rules: uniqBy([...state.rules, rule], (r) => r.key), - })); - }; - - handleRuleReopen = (ruleKey: string) => { - this.setState({ open: { rule: ruleKey } }); - }; - handleComponentClose = (componentKey: string) => { this.setState((state: State) => ({ components: state.components.filter((x) => x.key !== componentKey), @@ -146,16 +134,6 @@ export default class Workspace extends React.PureComponent<{}, State> { })); }; - handleRuleClose = (ruleKey: string) => { - this.setState((state: State) => ({ - rules: state.rules.filter((x) => x.key !== ruleKey), - open: { - ...state.open, - rule: state.open.rule === ruleKey ? undefined : state.open.rule, - }, - })); - }; - handleComponentLoad = (details: { key: string; name: string; qualifier: string }) => { if (this.mounted) { const { key, name, qualifier } = details; @@ -167,15 +145,6 @@ export default class Workspace extends React.PureComponent<{}, State> { } }; - handleRuleLoad = (details: { key: string; name: string }) => { - if (this.mounted) { - const { key, name } = details; - this.setState((state: State) => ({ - rules: state.rules.map((rule) => (rule.key === key ? { ...rule, name } : rule)), - })); - } - }; - handleCollapse = () => { this.setState({ open: {} }); }; @@ -200,7 +169,6 @@ export default class Workspace extends React.PureComponent<{}, State> { const { components, externalRulesRepoNames, height, maximized, open, rules } = this.state; const openComponent = open.component && components.find((x) => x.key === open.component); - const openRule = open.rule && rules.find((x) => x.key === open.rule); const actualHeight = maximized ? window.innerHeight * MAX_HEIGHT : height; @@ -209,7 +177,6 @@ export default class Workspace extends React.PureComponent<{}, State> { value={{ externalRulesRepoNames, openComponent: this.handleOpenComponent, - openRule: this.handleOpenRule, }} > {this.props.children} @@ -219,10 +186,7 @@ export default class Workspace extends React.PureComponent<{}, State> { components={components} onComponentClose={this.handleComponentClose} onComponentOpen={this.handleComponentReopen} - onRuleClose={this.handleRuleClose} - onRuleOpen={this.handleRuleReopen} open={open} - rules={rules} /> )} {openComponent && ( @@ -238,19 +202,6 @@ export default class Workspace extends React.PureComponent<{}, State> { onResize={this.handleResize} /> )} - {openRule && ( - <WorkspaceRuleViewer - height={actualHeight} - maximized={maximized} - onClose={this.handleRuleClose} - onCollapse={this.handleCollapse} - onLoad={this.handleRuleLoad} - onMaximize={this.handleMaximize} - onMinimize={this.handleMinimize} - onResize={this.handleResize} - rule={openRule} - /> - )} </WorkspacePortal> </WorkspaceContext.Provider> ); diff --git a/server/sonar-web/src/main/js/components/workspace/WorkspaceNav.tsx b/server/sonar-web/src/main/js/components/workspace/WorkspaceNav.tsx index 34d4fc5193a..bab413e61a5 100644 --- a/server/sonar-web/src/main/js/components/workspace/WorkspaceNav.tsx +++ b/server/sonar-web/src/main/js/components/workspace/WorkspaceNav.tsx @@ -18,24 +18,19 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { ComponentDescriptor, RuleDescriptor } from './context'; +import { ComponentDescriptor } from './context'; import WorkspaceNavComponent from './WorkspaceNavComponent'; -import WorkspaceNavRule from './WorkspaceNavRule'; export interface Props { components: ComponentDescriptor[]; - rules: RuleDescriptor[]; onComponentClose: (componentKey: string) => void; onComponentOpen: (componentKey: string) => void; - onRuleClose: (ruleKey: string) => void; - onRuleOpen: (ruleKey: string) => void; open: { component?: string; rule?: string }; } export default function WorkspaceNav(props: Props) { // do not show a tab for the currently open component/rule const components = props.components.filter((x) => x.key !== props.open.component); - const rules = props.rules.filter((x) => x.key !== props.open.rule); return ( <nav className="workspace-nav"> @@ -48,15 +43,6 @@ export default function WorkspaceNav(props: Props) { onOpen={props.onComponentOpen} /> ))} - - {rules.map((rule) => ( - <WorkspaceNavRule - key={`rule-${rule.key}`} - onClose={props.onRuleClose} - onOpen={props.onRuleOpen} - rule={rule} - /> - ))} </ul> </nav> ); diff --git a/server/sonar-web/src/main/js/components/workspace/__tests__/Workspace-test.tsx b/server/sonar-web/src/main/js/components/workspace/__tests__/Workspace-test.tsx index 358d8193b43..f1dcd698eb1 100644 --- a/server/sonar-web/src/main/js/components/workspace/__tests__/Workspace-test.tsx +++ b/server/sonar-web/src/main/js/components/workspace/__tests__/Workspace-test.tsx @@ -23,13 +23,7 @@ import { mockBranch } from '../../../helpers/mocks/branch-like'; import { get, save } from '../../../helpers/storage'; import { waitAndUpdate } from '../../../helpers/testUtils'; import { ComponentQualifier } from '../../../types/component'; -import Workspace, { - INITIAL_HEIGHT, - MAX_HEIGHT, - MIN_HEIGHT, - TYPE_KEY, - WorkspaceTypes, -} from '../Workspace'; +import Workspace, { INITIAL_HEIGHT, MIN_HEIGHT, TYPE_KEY, WorkspaceTypes } from '../Workspace'; jest.mock('../../../helpers/storage', () => { return { @@ -76,12 +70,6 @@ it('should render correctly', () => { open: { component: 'foo' }, }) ).toMatchSnapshot('open component'); - expect( - shallowRender({ - rules: [{ key: 'foo' }], - open: { rule: 'foo' }, - }) - ).toMatchSnapshot('open rule'); }); it('should correctly load data from local storage', () => { @@ -134,17 +122,9 @@ it('should allow elements to be loaded and updated', () => { }); const instance = wrapper.instance(); - // Load an non-existent element won't do anything. - instance.handleRuleLoad({ key: 'baz', name: 'Baz' }); - expect(wrapper.state().rules).toEqual([rule]); - instance.handleComponentLoad({ key: 'baz', name: 'Baz', qualifier: ComponentQualifier.TestFile }); expect(wrapper.state().components).toEqual([component]); - // Load an existing element will update some of its properties. - instance.handleRuleLoad({ key: 'bar', name: 'Bar' }); - expect(wrapper.state().rules).toEqual([{ ...rule, name: 'Bar' }]); - instance.handleComponentLoad({ key: 'foo', name: 'Foo', qualifier: ComponentQualifier.File }); expect(wrapper.state().components).toEqual([ { ...component, name: 'Foo', qualifier: ComponentQualifier.File }, @@ -155,18 +135,14 @@ it('should be resizable', () => { (get as jest.Mock).mockReturnValue( JSON.stringify([{ [TYPE_KEY]: WorkspaceTypes.Rule, key: 'foo' }]) ); - const wrapper = shallowRender({ open: { rule: 'foo' } }); + const wrapper = shallowRender(); const instance = wrapper.instance(); instance.handleMaximize(); expect(wrapper.state().maximized).toBe(true); - // We cannot fetch by reference, as the viewer component is lazy loaded. Find - // by string instead. - expect(wrapper.find('WorkspaceRuleViewer').props().height).toBe(WINDOW_HEIGHT * MAX_HEIGHT); instance.handleMinimize(); expect(wrapper.state().maximized).toBe(false); - expect(wrapper.find('WorkspaceRuleViewer').props().height).toBe(INITIAL_HEIGHT); instance.handleResize(-200); expect(wrapper.state().height).toBe(INITIAL_HEIGHT + 200); @@ -176,10 +152,6 @@ it('should be resizable', () => { }); it('should be openable/collapsible', () => { - const rule = { - key: 'baz', - name: 'Baz', - }; const component = { branchLike: mockBranch(), key: 'foo', @@ -190,9 +162,6 @@ it('should be openable/collapsible', () => { instance.handleOpenComponent(component); expect(wrapper.state().open).toEqual({ component: 'foo' }); - instance.handleOpenRule(rule); - expect(wrapper.state().open).toEqual({ rule: 'baz' }); - instance.handleCollapse(); expect(wrapper.state().open).toEqual({}); @@ -203,14 +172,6 @@ it('should be openable/collapsible', () => { expect(wrapper.state().open).toEqual({ component: 'foo' }); instance.handleComponentClose('foo'); expect(wrapper.state().open).toEqual({}); - - instance.handleRuleReopen(rule.key); - expect(wrapper.state().open).toEqual({ rule: 'baz' }); - - instance.handleRuleClose('bar'); - expect(wrapper.state().open).toEqual({ rule: 'baz' }); - instance.handleRuleClose('baz'); - expect(wrapper.state().open).toEqual({}); }); function shallowRender(state?: Partial<Workspace['state']>) { diff --git a/server/sonar-web/src/main/js/components/workspace/__tests__/WorkspaceNav-test.tsx b/server/sonar-web/src/main/js/components/workspace/__tests__/WorkspaceNav-test.tsx index fe57669a76f..954c742a1b3 100644 --- a/server/sonar-web/src/main/js/components/workspace/__tests__/WorkspaceNav-test.tsx +++ b/server/sonar-web/src/main/js/components/workspace/__tests__/WorkspaceNav-test.tsx @@ -29,25 +29,17 @@ it('should not render open component', () => { expect(shallowRender({ open: { component: 'bar' } })).toMatchSnapshot(); }); -it('should not render open rule', () => { - expect(shallowRender({ open: { rule: 'qux' } })).toMatchSnapshot(); -}); - function shallowRender(props?: Partial<Props>) { const components = [ { branchLike: undefined, key: 'foo' }, { branchLike: undefined, key: 'bar' }, ]; - const rules = [{ key: 'qux' }]; return shallow( <WorkspaceNav components={components} onComponentClose={jest.fn()} onComponentOpen={jest.fn()} - onRuleClose={jest.fn()} - onRuleOpen={jest.fn()} open={{}} - rules={rules} {...props} /> ); diff --git a/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/Workspace-test.tsx.snap b/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/Workspace-test.tsx.snap index c6fc77fe21b..e7ef8687609 100644 --- a/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/Workspace-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/Workspace-test.tsx.snap @@ -8,7 +8,6 @@ exports[`should render correctly: default 1`] = ` { "externalRulesRepoNames": {}, "openComponent": [Function], - "openRule": [Function], } } > @@ -25,7 +24,6 @@ exports[`should render correctly: open component 1`] = ` { "externalRulesRepoNames": {}, "openComponent": [Function], - "openRule": [Function], } } > @@ -49,14 +47,11 @@ exports[`should render correctly: open component 1`] = ` } onComponentClose={[Function]} onComponentOpen={[Function]} - onRuleClose={[Function]} - onRuleOpen={[Function]} open={ { "component": "foo", } } - rules={[]} /> <withBranchStatusActions(WorkspaceComponentViewer) component={ @@ -81,54 +76,3 @@ exports[`should render correctly: open component 1`] = ` </WorkspacePortal> </ContextProvider> `; - -exports[`should render correctly: open rule 1`] = ` -<ContextProvider - value={ - { - "externalRulesRepoNames": {}, - "openComponent": [Function], - "openRule": [Function], - } - } -> - <div - className="child" - /> - <WorkspacePortal> - <WorkspaceNav - components={[]} - onComponentClose={[Function]} - onComponentOpen={[Function]} - onRuleClose={[Function]} - onRuleOpen={[Function]} - open={ - { - "rule": "foo", - } - } - rules={ - [ - { - "key": "foo", - }, - ] - } - /> - <WorkspaceRuleViewer - height={300} - onClose={[Function]} - onCollapse={[Function]} - onLoad={[Function]} - onMaximize={[Function]} - onMinimize={[Function]} - onResize={[Function]} - rule={ - { - "key": "foo", - } - } - /> - </WorkspacePortal> -</ContextProvider> -`; diff --git a/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceNav-test.tsx.snap b/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceNav-test.tsx.snap index 2712037667d..8a428d794ac 100644 --- a/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceNav-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/workspace/__tests__/__snapshots__/WorkspaceNav-test.tsx.snap @@ -18,49 +18,6 @@ exports[`should not render open component 1`] = ` onClose={[MockFunction]} onOpen={[MockFunction]} /> - <WorkspaceNavRule - key="rule-qux" - onClose={[MockFunction]} - onOpen={[MockFunction]} - rule={ - { - "key": "qux", - } - } - /> - </ul> -</nav> -`; - -exports[`should not render open rule 1`] = ` -<nav - className="workspace-nav" -> - <ul - className="workspace-nav-list" - > - <WorkspaceNavComponent - component={ - { - "branchLike": undefined, - "key": "foo", - } - } - key="component-foo" - onClose={[MockFunction]} - onOpen={[MockFunction]} - /> - <WorkspaceNavComponent - component={ - { - "branchLike": undefined, - "key": "bar", - } - } - key="component-bar" - onClose={[MockFunction]} - onOpen={[MockFunction]} - /> </ul> </nav> `; @@ -94,16 +51,6 @@ exports[`should render 1`] = ` onClose={[MockFunction]} onOpen={[MockFunction]} /> - <WorkspaceNavRule - key="rule-qux" - onClose={[MockFunction]} - onOpen={[MockFunction]} - rule={ - { - "key": "qux", - } - } - /> </ul> </nav> `; diff --git a/server/sonar-web/src/main/js/components/workspace/context.ts b/server/sonar-web/src/main/js/components/workspace/context.ts index 5b65951ed60..c4050243b4d 100644 --- a/server/sonar-web/src/main/js/components/workspace/context.ts +++ b/server/sonar-web/src/main/js/components/workspace/context.ts @@ -37,11 +37,9 @@ export interface RuleDescriptor { export interface WorkspaceContextShape { externalRulesRepoNames: Dict<string>; openComponent: (component: ComponentDescriptor) => void; - openRule: (rule: RuleDescriptor) => void; } export const WorkspaceContext = createContext<WorkspaceContextShape>({ externalRulesRepoNames: {}, openComponent: () => {}, - openRule: () => {}, }); |