From 5f9db5cf4d6f6552f94aba3bffa43b4b71e2f48c Mon Sep 17 00:00:00 2001 From: Konstantin Schaper Date: Thu, 2 Jun 2022 13:25:53 +0200 Subject: [PATCH] New diff view props for controlling hunk/line interaction (#2047) Move hunk styling from review plugin to core and make reusable --- gradle/changelog/hunk_hover_icon.yaml | 2 + .../src/__snapshots__/storyshots.test.ts.snap | 2192 +++++++++++++++-- .../ui-components/src/repos/Diff.stories.tsx | 8 + scm-ui/ui-components/src/repos/DiffTypes.ts | 9 + .../ui-components/src/repos/LazyDiffFile.tsx | 36 +- 5 files changed, 1993 insertions(+), 254 deletions(-) create mode 100644 gradle/changelog/hunk_hover_icon.yaml diff --git a/gradle/changelog/hunk_hover_icon.yaml b/gradle/changelog/hunk_hover_icon.yaml new file mode 100644 index 0000000000..d1af8bed36 --- /dev/null +++ b/gradle/changelog/hunk_hover_icon.yaml @@ -0,0 +1,2 @@ +- type: added + description: New diff view props for controlling hunk/line interaction ([#2047](https://github.com/scm-manager/scm-manager/pull/2047)) diff --git a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap index 20a3e7673d..4a1068d8fd 100644 --- a/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap +++ b/scm-ui/ui-components/src/__snapshots__/storyshots.test.ts.snap @@ -21141,7 +21141,7 @@ exports[`Storyshots Repositories/Diff Binaries 1`] = ` >
@@ -21230,7 +21230,7 @@ exports[`Storyshots Repositories/Diff Binaries 1`] = `
@@ -21437,7 +21437,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -21527,7 +21527,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -22168,7 +22168,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -23133,7 +23133,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -23658,7 +23658,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -24172,7 +24172,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -25363,7 +25363,7 @@ exports[`Storyshots Repositories/Diff Changing Content 1`] = `
@@ -25960,7 +25960,7 @@ exports[`Storyshots Repositories/Diff Collapsed 1`] = `
@@ -26072,7 +26072,7 @@ exports[`Storyshots Repositories/Diff Collapsed 1`] = `
@@ -26184,7 +26184,7 @@ exports[`Storyshots Repositories/Diff Collapsed 1`] = `
@@ -26296,7 +26296,7 @@ exports[`Storyshots Repositories/Diff Collapsed 1`] = `
@@ -26408,7 +26408,7 @@ exports[`Storyshots Repositories/Diff Collapsed 1`] = `
@@ -26529,7 +26529,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = ` >
@@ -26603,7 +26603,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = `
@@ -26692,7 +26692,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = `
@@ -27549,7 +27549,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = `
@@ -28002,7 +28002,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = `
@@ -28440,7 +28440,7 @@ exports[`Storyshots Repositories/Diff CollapsingWithFunction 1`] = `
@@ -28604,7 +28604,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = ` >
@@ -28694,7 +28694,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -29335,7 +29335,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -30300,7 +30300,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -30825,7 +30825,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -31339,7 +31339,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -32530,7 +32530,7 @@ exports[`Storyshots Repositories/Diff Expandable 1`] = `
@@ -33104,7 +33104,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -33745,7 +33745,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -34710,7 +34710,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -35235,7 +35235,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -35749,7 +35749,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -36940,7 +36940,7 @@ exports[`Storyshots Repositories/Diff External state management 1`] = `
@@ -37517,7 +37517,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -38101,7 +38101,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -38962,7 +38962,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -39419,7 +39419,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -39876,7 +39876,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -40940,7 +40940,7 @@ exports[`Storyshots Repositories/Diff File Annotation 1`] = `
@@ -41493,7 +41493,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = `
@@ -42089,7 +42089,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = `
@@ -42962,7 +42962,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = `
@@ -43431,7 +43431,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = `
@@ -43900,7 +43900,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = `
@@ -44976,7 +44976,7 @@ exports[`Storyshots Repositories/Diff File Controls 1`] = ` `; -exports[`Storyshots Repositories/Diff Hunks 1`] = ` +exports[`Storyshots Repositories/Diff Highlight line on hover 1`] = `
@@ -45513,7 +45513,7 @@ exports[`Storyshots Repositories/Diff Hunks 1`] = ` + + + 211 + + + 214 + + + approver.stream() + + + + + 212 + + + 215 + + + .filter(recipient -> user.getId().equals(recipient)) + + + + + 213 + + + 216 + + + .findFirst() + + + + + 214 + + + + .ifPresent(pullRequest::removeApprover); + + + + + 215 + + + + getStore(repository).update(pullRequest); + + + + + + 217 + + + .ifPresent( + + + + + + 218 + + + approval -> { + + + + + + 219 + + + pullRequest.removeApprover(approval); + + + + + + 220 + + + getStore(repository).update(pullRequest); + + + + + + 221 + + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVAL_REMOVED)); + + + + + + 222 + + + }); + + + + + 216 + + + 223 + + + } + + + + + 217 + + + 224 + + + + + + + + 218 + + + 225 + + + @Override + + + + +
+
+
+
+`; + +exports[`Storyshots Repositories/Diff Hunk gutter hover icon 1`] = ` +
+
+
+
+
+
+ +

+ src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java +

+ + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ 25 + + 25 + + import java.util.Set; +
+ 26 + + 26 + + import java.util.stream.Collectors; +
+ 27 + + 27 + + +
+ + 28 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVAL_REMOVED; +
+ + 29 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVED; +
+ 28 + + 30 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.MERGED; +
+ 29 + + 31 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.OPEN; +
+ 30 + + 32 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.REJECTED; +
+
+
+ 200 + + 202 + + PullRequest pullRequest = getPullRequestFromStore(repository, pullRequestId); +
+ 201 + + 203 + + pullRequest.addApprover(user.getId()); +
+ 202 + + 204 + + getStore(repository).update(pullRequest); +
+ + 205 + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVED)); +
+ 203 + + 206 + + } +
+ 204 + + 207 + + +
+ 205 + + 208 + + @Override +
+
+
+ 211 + + 214 + + approver.stream() +
+ 212 + + 215 + + .filter(recipient -> user.getId().equals(recipient)) +
+ 213 + + 216 + + .findFirst() +
+ 214 + + + .ifPresent(pullRequest::removeApprover); +
+ 215 + + + getStore(repository).update(pullRequest); +
+ + 217 + + .ifPresent( +
+ + 218 + + approval -> { +
+ + 219 + + pullRequest.removeApprover(approval); +
+ + 220 + + getStore(repository).update(pullRequest); +
+ + 221 + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVAL_REMOVED)); +
+ + 222 + + }); +
+ 216 + + 223 + + } +
+ 217 + + 224 + + +
+ 218 + + 225 + + @Override +
+
+
+
+
+`; + +exports[`Storyshots Repositories/Diff Hunks 1`] = ` +
+
+
+
+
+
+ +

+ src/main/java/com/cloudogu/scm/review/pullrequest/service/DefaultPullRequestService.java +

+ + modify + +
+
+
+
+ +
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -46357,7 +48045,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -46949,7 +48637,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -47818,7 +49506,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -48271,7 +49959,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -48724,7 +50412,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -49784,7 +51472,7 @@ exports[`Storyshots Repositories/Diff Line Annotation 1`] = `
@@ -50333,7 +52021,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -50953,7 +52641,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -51872,7 +53560,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -52355,7 +54043,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -52838,7 +54526,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -53974,7 +55662,7 @@ exports[`Storyshots Repositories/Diff OnClick 1`] = `
@@ -54548,7 +56236,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -55221,7 +56909,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -56168,7 +57856,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -56673,7 +58361,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -57178,7 +58866,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -58407,7 +60095,7 @@ exports[`Storyshots Repositories/Diff Side-By-Side 1`] = `
@@ -59041,7 +60729,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting (Markdown) 1`] = `
@@ -59378,7 +61066,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -59958,7 +61646,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -60815,7 +62503,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -61268,7 +62956,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -61721,7 +63409,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -62781,7 +64469,7 @@ exports[`Storyshots Repositories/Diff SyntaxHighlighting 1`] = `
@@ -63319,7 +65007,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
@@ -63960,7 +65648,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
@@ -64925,7 +66613,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
@@ -65450,7 +67138,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
@@ -65964,7 +67652,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
@@ -67155,7 +68843,7 @@ exports[`Storyshots Repositories/Diff WithLinkToFile 1`] = `
; }) + .add("Hunk gutter hover icon", () => { + const hunkDiffFiles = parser.parse(hunksDiff); + return ; + }) + .add("Highlight line on hover", () => { + const hunkDiffFiles = parser.parse(hunksDiff); + return ; + }) .add("Binaries", () => { const binaryDiffFiles = parser.parse(binaryDiff); return ; diff --git a/scm-ui/ui-components/src/repos/DiffTypes.ts b/scm-ui/ui-components/src/repos/DiffTypes.ts index 0147d1833d..2562513b0f 100644 --- a/scm-ui/ui-components/src/repos/DiffTypes.ts +++ b/scm-ui/ui-components/src/repos/DiffTypes.ts @@ -64,4 +64,13 @@ export type DiffObjectProps = { isCollapsed?: (file: File) => boolean; onCollapseStateChange?: (file: File, newState?: boolean) => void; hunkClass?: (hunk: Hunk) => string; + /** + * Fontawesome Icon Unicode + * + * @see https://fontawesome.com/icons + * @example + * "\f075" + */ + hunkGutterHoverIcon?: string; + highlightLineOnHover?: boolean; }; diff --git a/scm-ui/ui-components/src/repos/LazyDiffFile.tsx b/scm-ui/ui-components/src/repos/LazyDiffFile.tsx index 15087c88d6..f8d64f4892 100644 --- a/scm-ui/ui-components/src/repos/LazyDiffFile.tsx +++ b/scm-ui/ui-components/src/repos/LazyDiffFile.tsx @@ -61,6 +61,33 @@ type State = Collapsible & { expansionError?: any; }; +const StyledHunk = styled(Hunk)`${props => { + let style = props.icon ? ` + .diff-gutter:hover::after { + font-size: inherit; + margin-left: 0.5em; + font-family: "Font Awesome 5 Free"; + content: "${props.icon}"; + color: var(--scm-column-selection); + } + ` : ""; + if (!props.actionable) { + style += ` + .diff-gutter { + cursor: default; + } + `; + } + if (props.highlightLineOnHover) { + style += ` + tr.diff-line:hover > td { + background-color: var(--sh-selected-color); + } + `; + } + return style; +}}`; + const DiffFilePanel = styled.div` /* remove bottom border for collapsed panels */ ${(props: Collapsible) => (props.collapsed ? "border-bottom: none;" : "")}; @@ -315,13 +342,18 @@ class DiffFile extends React.Component { ); } + const gutterEvents = this.createGutterEvents(hunk); + items.push( - ); if (file._links?.lines) {
+ 25 + + 25 + + import java.util.Set; +
+ 26 + + 26 + + import java.util.stream.Collectors; +
+ 27 + + 27 + + +
+ + 28 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVAL_REMOVED; +
+ + 29 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestApprovalEvent.ApprovalCause.APPROVED; +
+ 28 + + 30 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.MERGED; +
+ 29 + + 31 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.OPEN; +
+ 30 + + 32 + + import static com.cloudogu.scm.review.pullrequest.service.PullRequestStatus.REJECTED; +
+
+
+ 200 + + 202 + + PullRequest pullRequest = getPullRequestFromStore(repository, pullRequestId); +
+ 201 + + 203 + + pullRequest.addApprover(user.getId()); +
+ 202 + + 204 + + getStore(repository).update(pullRequest); +
+ + 205 + + eventBus.post(new PullRequestApprovalEvent(repository, pullRequest, APPROVED)); +
+ 203 + + 206 + + } +
+ 204 + + 207 + + +
+ 205 + + 208 + + @Override +
+
+