diff options
author | Jeremy Davis <jeremy.davis@sonarsource.com> | 2019-11-01 16:17:42 +0900 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-11-07 11:45:16 +0100 |
commit | fbdba9e77ae6162ca628895002fb513ee7f093fd (patch) | |
tree | d637b78eab91a6c270f5b4b30efd35df2f978e33 /server/sonar-web/src | |
parent | 969407f6a612223ab19c22c36b5121c2c6659498 (diff) | |
download | sonarqube-fbdba9e77ae6162ca628895002fb513ee7f093fd.tar.gz sonarqube-fbdba9e77ae6162ca628895002fb513ee7f093fd.zip |
SONAR-12652 Fix 'missing' snippet lines when expanding
Diffstat (limited to 'server/sonar-web/src')
4 files changed, 124 insertions, 88 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx index 15a74e45927..dc1736a8af0 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetViewer.tsx @@ -87,11 +87,11 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr } createSnippetsFromProps() { - const snippets = createSnippets( - this.props.snippetGroup.locations, - this.props.last, - this.props.issue.secondaryLocations.length > 0 ? this.props.issue : undefined - ); + const snippets = createSnippets({ + locations: this.props.snippetGroup.locations, + issue: this.props.issue, + addIssueLocation: this.props.issue.secondaryLocations.length > 0 + }); this.setState({ snippets }); } @@ -117,6 +117,22 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr return { wrapper, table }; } + /* + * Clean after animation + */ + cleanDom(index: number) { + const nodes = this.getNodes(index); + + if (!nodes) { + return; + } + + const { wrapper, table } = nodes; + + table.style.marginTop = ''; + wrapper.style.maxHeight = ''; + } + setMaxHeight(index: number, value?: number, up = false) { const nodes = this.getNodes(index); @@ -220,6 +236,7 @@ export default class ComponentSourceSnippetViewer extends React.PureComponent<Pr // Wait for transition to finish before updating dom setTimeout(() => { this.setState({ snippets: newSnippets.filter(s => !s.toDelete) }); + this.cleanDom(snippetIndex); }, 200); } ); diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx index a932293f62b..4aefbdc7670 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import * as React from 'react'; import classNames from 'classnames'; +import * as React from 'react'; import ExpandSnippetIcon from 'sonar-ui-common/components/icons/ExpandSnippetIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { scrollHorizontally } from 'sonar-ui-common/helpers/scrolling'; @@ -30,7 +30,7 @@ import { optimizeLocationMessage, optimizeSelectedIssue } from '../../../components/SourceViewer/helpers/lines'; -import { inSnippet, LINES_BELOW_LAST } from './utils'; +import { inSnippet, LINES_BELOW_ISSUE } from './utils'; interface Props { branchLike: T.BranchLike | undefined; @@ -185,7 +185,7 @@ export default class SnippetViewer extends React.PureComponent<Props> { .filter(l => inSnippet(l, snippet) && (l === issueLine || openIssuesByLine[l])) ); const verticalBuffer = last - ? Math.max(0, LINES_BELOW_LAST - (bottomLine - lowestVisibleIssue)) + ? Math.max(0, LINES_BELOW_ISSUE - (bottomLine - lowestVisibleIssue)) : 0; const displayDuplications = snippet.some(s => !!s.duplicated); diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts index f33a99bccbf..1492af96501 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts @@ -82,97 +82,108 @@ describe('groupLocationsByComponent', () => { describe('createSnippets', () => { it('should merge snippets correctly', () => { - const results = createSnippets( - [ - mockFlowLocation({ - textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } - }), - mockFlowLocation({ - textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 } - }) - ], - false - ); + const locations = [ + mockFlowLocation({ + textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } + }), + mockFlowLocation({ + textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 } + }) + ]; + const results = createSnippets({ + locations, + issue: mockIssue(false, locations[1]), + addIssueLocation: false + }); expect(results).toHaveLength(1); - expect(results[0]).toEqual({ index: 0, start: 14, end: 21 }); + expect(results[0]).toEqual({ index: 0, start: 14, end: 28 }); }); it('should merge snippets correctly, even when not in sequence', () => { - const results = createSnippets( - [ - mockFlowLocation({ - textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } - }), - mockFlowLocation({ - textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } - }), - mockFlowLocation({ - textRange: { startLine: 14, startOffset: 2, endLine: 14, endOffset: 3 } - }) - ], - false - ); + const locations = [ + mockFlowLocation({ + textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } + }), + mockFlowLocation({ + textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } + }), + mockFlowLocation({ + textRange: { startLine: 14, startOffset: 2, endLine: 14, endOffset: 3 } + }) + ]; + const results = createSnippets({ + locations, + issue: mockIssue(false, locations[2]), + addIssueLocation: false + }); expect(results).toHaveLength(2); - expect(results[0]).toEqual({ index: 0, start: 12, end: 18 }); + expect(results[0]).toEqual({ index: 0, start: 12, end: 23 }); expect(results[1]).toEqual({ index: 1, start: 45, end: 49 }); }); it('should merge three snippets together', () => { - const results = createSnippets( - [ - mockFlowLocation({ - textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } - }), - mockFlowLocation({ - textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } - }), - mockFlowLocation({ - textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 } - }), - mockFlowLocation({ - textRange: { startLine: 18, startOffset: 2, endLine: 18, endOffset: 3 } - }) - ], - false - ); + const locations = [ + mockFlowLocation({ + textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } + }), + mockFlowLocation({ + textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } + }), + mockFlowLocation({ + textRange: { startLine: 23, startOffset: 2, endLine: 23, endOffset: 3 } + }), + mockFlowLocation({ + textRange: { startLine: 18, startOffset: 2, endLine: 18, endOffset: 3 } + }) + ]; + const results = createSnippets({ + locations, + issue: mockIssue(false, locations[0]), + addIssueLocation: false + }); expect(results).toHaveLength(2); - expect(results[0]).toEqual({ index: 0, start: 14, end: 24 }); + expect(results[0]).toEqual({ index: 0, start: 14, end: 25 }); expect(results[1]).toEqual({ index: 1, start: 45, end: 49 }); }); it("should prepend the issue's main location if necessary", () => { - const results = createSnippets( - [ - mockFlowLocation({ - textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } - }), - mockFlowLocation({ - textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 } - }) - ], - false, - mockIssue(false, { textRange: { startLine: 5, endLine: 5, startOffset: 0, endOffset: 0 } }) - ); + const locations = [ + mockFlowLocation({ + textRange: { startLine: 47, startOffset: 2, endLine: 47, endOffset: 3 } + }), + mockFlowLocation({ + textRange: { startLine: 22, startOffset: 2, endLine: 22, endOffset: 3 } + }) + ]; + const results = createSnippets({ + locations, + issue: mockIssue(false, { + textRange: { startLine: 5, endLine: 5, startOffset: 0, endOffset: 0 } + }), + addIssueLocation: true + }); expect(results).toHaveLength(3); expect(results[0]).toEqual({ index: 0, start: 3, end: 14 }); }); it('should handle last component', () => { - const results = createSnippets( - [ - mockFlowLocation({ - textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } - }), - mockFlowLocation({ - textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 } - }) - ], - true - ); + const locations = [ + mockFlowLocation({ + textRange: { startLine: 16, startOffset: 10, endLine: 16, endOffset: 14 } + }), + mockFlowLocation({ + textRange: { startLine: 19, startOffset: 2, endLine: 19, endOffset: 3 } + }) + ]; + const results = createSnippets({ + locations, + issue: mockIssue(false, locations[1]), + addIssueLocation: false + }); expect(results).toHaveLength(1); expect(results[0]).toEqual({ index: 0, start: 14, end: 28 }); diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts index b8938650d5f..9a5e7907b91 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts @@ -20,7 +20,7 @@ const LINES_ABOVE = 2; const LINES_BELOW = 2; export const MERGE_DISTANCE = 4; // Merge if snippets are four lines away (separated by 3 lines) or fewer -export const LINES_BELOW_LAST = 9; +export const LINES_BELOW_ISSUE = 9; export const EXPAND_BY_LINES = 10; function unknownComponent(key: string): T.SnippetsByComponent { @@ -54,21 +54,29 @@ function getPrimaryLocation(issue: T.Issue): T.FlowLocation { }; } -export function createSnippets( - locations: T.FlowLocation[], - last: boolean, - issue?: T.Issue -): T.Snippet[] { +function addLinesBellow(params: { issue: T.Issue; locationEnd: number }) { + const { issue, locationEnd } = params; + const issueEndLine = (issue.textRange && issue.textRange.endLine) || 0; + + if (!issueEndLine || issueEndLine === locationEnd) { + return locationEnd + LINES_BELOW_ISSUE; + } + + return locationEnd + LINES_BELOW; +} + +export function createSnippets(params: { + locations: T.FlowLocation[]; + issue: T.Issue; + addIssueLocation: boolean; +}): T.Snippet[] { + const { locations, issue, addIssueLocation } = params; // For each location's range (2 above and 2 below), and then compare with other ranges // to merge snippets that collide. - return (issue ? [getPrimaryLocation(issue), ...locations] : locations).reduce( + return (addIssueLocation ? [getPrimaryLocation(issue), ...locations] : locations).reduce( (snippets: T.Snippet[], loc, index) => { const startIndex = Math.max(1, loc.textRange.startLine - LINES_ABOVE); - const endIndex = - loc.textRange.endLine + - ((issue && index === 0) || (last && index === locations.length - 1) - ? LINES_BELOW_LAST - : LINES_BELOW); + const endIndex = addLinesBellow({ issue, locationEnd: loc.textRange.endLine }); let firstCollision: { start: number; end: number } | undefined; |