Skip to content

Commit c3ccd9b

Browse files
[ZEPPELIN-6162] Implement revisions comparator for New UI
### What is this PR for? Port the revision comparison feature from the legacy AngularJS UI to the new Angular 13 frontend. Users can now select two revisions and view paragraph-by-paragraph diffs with color-coded additions and deletions. ### What type of PR is it? Improvement ### What is the Jira issue? * https://issues.apache.org/jira/browse/ZEPPELIN-6162 ### How should this be tested? * Strongly recommended: add automated unit tests for any new or changed behavior * Outline any manual steps to test the PR here. ### Screenshots (if appropriate) ![ZEPPELIN-6162](https://github.com/user-attachments/assets/483c05e1-9fa0-4347-8f50-21d0fbd90db2) ### Questions: * Does the license files need to update? no * Is there breaking changes for older versions? no * Does this needs documentation? no Closes #5155 from prabhjyotsingh/ZEPPELIN-6162. Signed-off-by: ChanHo Lee <[email protected]>
1 parent cf766db commit c3ccd9b

File tree

8 files changed

+479
-12
lines changed

8 files changed

+479
-12
lines changed

zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-data-type-map.interface.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
NoteRename,
3535
NoteRevision,
3636
NoteRevisionForCompare,
37+
NoteRevisionForCompareReceived,
3738
NoteRunningStatus,
3839
NoteUpdate,
3940
NoteUpdated,
@@ -118,6 +119,7 @@ export interface MessageReceiveDataTypeMap {
118119
[OP.ANGULAR_OBJECT_UPDATE]: AngularObjectUpdate;
119120
[OP.ANGULAR_OBJECT_REMOVE]: AngularObjectRemove;
120121
[OP.PARAS_INFO]: ParasInfo;
122+
[OP.NOTE_REVISION_FOR_COMPARE]: NoteRevisionForCompareReceived;
121123
}
122124

123125
export interface MessageSendDataTypeMap {

zeppelin-web-angular/projects/zeppelin-sdk/src/interfaces/message-notebook.interface.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,13 @@ export interface NoteRevisionForCompare {
125125
position: string;
126126
}
127127

128+
export interface NoteRevisionForCompareReceived {
129+
noteId: string;
130+
revisionId: string;
131+
position: string;
132+
note: Note['note'];
133+
}
134+
128135
export interface CollaborativeModeStatus {
129136
status: boolean;
130137
users: string[];

zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.component.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,11 @@
5656
[(activatedExtension)]="activatedExtension"
5757
[permissions]="permissions"
5858
></zeppelin-notebook-permissions>
59-
<zeppelin-notebook-revisions-comparator *ngSwitchCase="'revisions'"></zeppelin-notebook-revisions-comparator>
59+
<zeppelin-notebook-revisions-comparator
60+
*ngSwitchCase="'revisions'"
61+
[noteRevisions]="noteRevisions"
62+
[noteId]="note.id"
63+
></zeppelin-notebook-revisions-comparator>
6064
</div>
6165
<div class="paragraph-area">
6266
<zeppelin-note-form-block

zeppelin-web-angular/src/app/pages/workspace/notebook/notebook.module.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import { NzRadioModule } from 'ng-zorro-antd/radio';
3333
import { NzResizableModule } from 'ng-zorro-antd/resizable';
3434
import { NzSelectModule } from 'ng-zorro-antd/select';
3535
import { NzSwitchModule } from 'ng-zorro-antd/switch';
36+
import { NzTableModule } from 'ng-zorro-antd/table';
37+
import { NzTagModule } from 'ng-zorro-antd/tag';
3638
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
3739

3840
import { ShareModule } from '@zeppelin/share';
@@ -98,7 +100,9 @@ import { NotebookSidebarComponent } from './sidebar/sidebar.component';
98100
DragDropModule,
99101
NzCodeEditorModule,
100102
NzCheckboxModule,
101-
NzResizableModule
103+
NzResizableModule,
104+
NzTableModule,
105+
NzTagModule
102106
]
103107
})
104108
export class NotebookModule {}

zeppelin-web-angular/src/app/pages/workspace/notebook/revisions-comparator/revisions-comparator.component.html

Lines changed: 106 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,111 @@
1010
~ limitations under the License.
1111
-->
1212

13-
<div class="revisions-comarator">
14-
<div>
15-
<h2>Revisions comparator</h2>
13+
<div class="revisions-comparator" nz-row [nzGutter]="16">
14+
<div nz-col [nzSpan]="8">
15+
<div class="commit-tree">
16+
<nz-table
17+
#revisionTable
18+
[nzData]="sortedRevisions"
19+
[nzShowPagination]="false"
20+
[nzScroll]="{ y: '25vh' }"
21+
nzSize="small"
22+
>
23+
<thead>
24+
<tr>
25+
<th>Revision name</th>
26+
<th>Date</th>
27+
</tr>
28+
</thead>
29+
<tbody>
30+
<tr
31+
*ngFor="let revision of revisionTable.data; let i = index; let last = last"
32+
[class.cursor-hand]="!last"
33+
[class.selected-revision]="revision.message === currentSecondRevisionLabel"
34+
(click)="!last && onRevisionRowClick(i)"
35+
>
36+
<td>{{ revision.message }}</td>
37+
<td>{{ formatRevisionDate(revision.time) }}</td>
38+
</tr>
39+
</tbody>
40+
</nz-table>
41+
</div>
42+
43+
<div class="revisions-comparator-bar">
44+
<nz-select
45+
*ngIf="noteRevisions.length > 0"
46+
class="revision-select"
47+
[ngModel]="selectedFirstRevisionId"
48+
nzPlaceHolder="Choose..."
49+
(ngModelChange)="onFirstRevisionSelect($event)"
50+
>
51+
<nz-option
52+
*ngFor="let revision of sortedRevisions"
53+
[nzValue]="revision.id"
54+
[nzLabel]="revision.message + (revision.time ? ' - ' + formatRevisionDate(revision.time) : '')"
55+
></nz-option>
56+
</nz-select>
57+
<span class="compare-label">compare with</span>
58+
<nz-select
59+
*ngIf="noteRevisions.length > 0"
60+
class="revision-select"
61+
[ngModel]="selectedSecondRevisionId"
62+
[nzDisabled]="firstNoteRevisionForCompare === null"
63+
nzPlaceHolder="Choose..."
64+
(ngModelChange)="onSecondRevisionSelect($event)"
65+
>
66+
<nz-option
67+
*ngFor="let revision of sortedRevisions"
68+
[nzValue]="revision.id"
69+
[nzLabel]="revision.message + (revision.time ? ' - ' + formatRevisionDate(revision.time) : '')"
70+
></nz-option>
71+
</nz-select>
72+
</div>
73+
74+
<div class="diff-panel">
75+
<div class="paragraphs-div">
76+
<div
77+
*ngFor="let p of mergeNoteRevisionsDiff"
78+
class="paragraph-item"
79+
[class.paragraph-item-selected]="currentParagraphDiffDisplay?.paragraph?.id === p.paragraph.id"
80+
(click)="changeCurrentParagraphDiffDisplay(p.paragraph.id)"
81+
>
82+
<div class="paragraph-item-heading">
83+
<span class="paragraph-id">{{ p.paragraph.id }}</span>
84+
<strong *ngIf="p.paragraph.title" class="paragraph-title">({{ p.paragraph.title }})</strong>
85+
<nz-tag *ngIf="p.type === 'added'" nzColor="green">added</nz-tag>
86+
<nz-tag *ngIf="p.type === 'deleted'" nzColor="red">deleted</nz-tag>
87+
<nz-tag *ngIf="p.type === 'compared' && !p.identical" nzColor="orange">differences</nz-tag>
88+
<nz-tag *ngIf="p.type === 'compared' && p.identical" nzColor="default">identical</nz-tag>
89+
<span class="paragraph-first-string">{{ p.firstString }}</span>
90+
</div>
91+
</div>
92+
<div *ngIf="currentSecondRevisionLabel === 'Choose...'" class="empty-paragraph-message">
93+
Please select a revision
94+
</div>
95+
</div>
96+
</div>
97+
</div>
98+
99+
<div nz-col [nzSpan]="16" class="code-panel-col">
100+
<span class="code-panel-title">
101+
Revision:
102+
<strong>{{ currentFirstRevisionLabel }} --> {{ currentSecondRevisionLabel }}</strong>
103+
</span>
104+
<pre *ngIf="currentParagraphDiffDisplay?.type === 'added'" class="code-panel color-green-row">{{
105+
currentParagraphDiffDisplay?.paragraph?.text
106+
}}</pre>
107+
<pre *ngIf="currentParagraphDiffDisplay?.type === 'deleted'" class="code-panel color-red-row">{{
108+
currentParagraphDiffDisplay?.paragraph?.text
109+
}}</pre>
110+
<pre *ngIf="currentParagraphDiffDisplay?.type === 'compared'" class="code-panel"><span
111+
*ngFor="let seg of currentParagraphDiffDisplay?.segments"
112+
[class.color-green-row]="seg.type === 'insert'"
113+
[class.color-red-row]="seg.type === 'delete'"
114+
[class.color-black]="seg.type === 'equal'"
115+
>{{ seg.text }}</span></pre>
116+
<pre *ngIf="currentParagraphDiffDisplay === null" class="code-panel empty-code-panel">
117+
<div>Nothing to display</div>
118+
</pre>
16119
</div>
17-
<nz-divider></nz-divider>
18-
<div></div>
19120
</div>

zeppelin-web-angular/src/app/pages/workspace/notebook/revisions-comparator/revisions-comparator.component.less

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,150 @@
99
* See the License for the specific language governing permissions and
1010
* limitations under the License.
1111
*/
12+
@import "theme-mixin";
1213

14+
.themeMixin({
15+
.revisions-comparator {
16+
padding: 10px 15px 15px;
17+
}
18+
19+
.commit-tree {
20+
margin-bottom: 10px;
21+
22+
::ng-deep .ant-table-body {
23+
overflow-y: auto !important;
24+
}
25+
}
26+
27+
.cursor-hand {
28+
cursor: pointer;
29+
}
30+
31+
.selected-revision {
32+
background-color: fade(@primary-6, 15%) !important;
33+
}
34+
35+
.revisions-comparator-bar {
36+
display: flex;
37+
align-items: center;
38+
gap: 8px;
39+
padding-bottom: 12px;
40+
flex-wrap: wrap;
41+
42+
.revision-select {
43+
flex: 1;
44+
min-width: 100px;
45+
display: block;
46+
}
47+
48+
.compare-label {
49+
white-space: nowrap;
50+
}
51+
}
52+
53+
.diff-panel {
54+
border: 1px solid @border-color-base;
55+
border-radius: 4px;
56+
}
57+
58+
.paragraphs-div {
59+
overflow: auto;
60+
max-height: 35vh;
61+
}
62+
63+
.paragraph-item {
64+
transition: background-color 200ms ease-out;
65+
border-bottom: 1px solid @border-color-split;
66+
cursor: pointer;
67+
68+
&:hover {
69+
background-color: fade(@primary-6, 8%);
70+
}
71+
72+
&.paragraph-item-selected {
73+
background-color: fade(@primary-6, 15%);
74+
}
75+
}
76+
77+
.paragraph-item-heading {
78+
padding: 8px 12px;
79+
}
80+
81+
.paragraph-id {
82+
font-family: monospace;
83+
font-size: 12px;
84+
color: @text-color-secondary;
85+
}
86+
87+
.paragraph-title {
88+
padding: 0 5px;
89+
}
90+
91+
.paragraph-first-string {
92+
display: block;
93+
height: 1.8em;
94+
overflow: hidden;
95+
padding-top: 4px;
96+
white-space: nowrap;
97+
text-overflow: ellipsis;
98+
font-size: 12px;
99+
color: @text-color-secondary;
100+
}
101+
102+
.empty-paragraph-message {
103+
font-size: 1.5em;
104+
color: @text-color-secondary;
105+
text-align: center;
106+
padding: 40px 0;
107+
}
108+
109+
.code-panel-col {
110+
display: flex;
111+
flex-direction: column;
112+
}
113+
114+
.code-panel-title {
115+
font-size: 14px;
116+
padding: 5px 0 8px;
117+
}
118+
119+
.code-panel {
120+
flex: 1;
121+
width: 100%;
122+
min-height: 50vh;
123+
max-height: 70vh;
124+
overflow-y: auto;
125+
border: 1px solid @border-color-base;
126+
border-radius: 4px;
127+
padding: 8px;
128+
margin: 0;
129+
font-size: 13px;
130+
}
131+
132+
.empty-code-panel {
133+
text-align: center;
134+
display: flex;
135+
align-items: center;
136+
justify-content: center;
137+
font-size: 24px;
138+
color: @text-color-secondary;
139+
}
140+
141+
::ng-deep {
142+
.color-green-row {
143+
background-color: fade(@green-6, 15%);
144+
display: block;
145+
color: @green-6;
146+
}
147+
148+
.color-red-row {
149+
background-color: fade(@red-6, 15%);
150+
display: block;
151+
color: @red-6;
152+
}
153+
154+
.color-black {
155+
color: inherit;
156+
}
157+
}
158+
});

0 commit comments

Comments
 (0)