Skip to content

Commit 797b6e4

Browse files
committed
Merge branch 'main' of github.com:hypertrace/hypertrace-ui into table-use-in-memory-storage
2 parents 78e0ea1 + c03f07f commit 797b6e4

17 files changed

+373
-213
lines changed

projects/common/src/color/color.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ export const enum Color {
5858
Yellow6 = '#facf00',
5959
Yellow7 = '#bd9d00',
6060
Yellow8 = '#6d5b00',
61-
Yellow9 = '#181400'
61+
Yellow9 = '#181400',
62+
Transparent = 'transparent',
63+
OffWhite = '#f6f6f64d'
6264
}
6365

6466
export interface ColorCombination {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { Observable } from 'rxjs';
2+
3+
export interface DownloadFileMetadata {
4+
dataSource: Observable<string>; // This should be a stringified data for any file
5+
fileName: string;
6+
}

projects/components/src/download-json/download-json.component.scss renamed to projects/components/src/download-file/download-file.component.scss

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@import 'color-palette';
22

3-
.download-json {
3+
.download-file {
44
width: 40px;
55
height: 40px;
66
display: flex;

projects/components/src/download-json/download-json.component.test.ts renamed to projects/components/src/download-file/download-file.component.test.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@ import { fakeAsync } from '@angular/core/testing';
33
import { RouterTestingModule } from '@angular/router/testing';
44
import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
55
import { MockComponent } from 'ng-mocks';
6-
import { Observable, of } from 'rxjs';
6+
import { of } from 'rxjs';
77
import { ButtonComponent } from '../button/button.component';
88
import { IconComponent } from '../icon/icon.component';
9-
import { DownloadJsonComponent } from './download-json.component';
10-
import { DownloadJsonModule } from './download-json.module';
9+
import { DownloadFileMetadata } from './download-file-metadata';
10+
import { DownloadFileComponent } from './download-file.component';
11+
import { DownloadFileModule } from './download-file.module';
1112

12-
describe('Download Json Component', () => {
13-
let spectator: Spectator<DownloadJsonComponent>;
13+
describe('Download File Component', () => {
14+
let spectator: Spectator<DownloadFileComponent>;
1415
const mockElement = document.createElement('a');
1516
const createElementSpy = jest.fn().mockReturnValue(mockElement);
1617

1718
const createHost = createHostFactory({
18-
component: DownloadJsonComponent,
19-
imports: [DownloadJsonModule, RouterTestingModule],
19+
component: DownloadFileComponent,
20+
imports: [DownloadFileModule, RouterTestingModule],
2021
declarations: [MockComponent(ButtonComponent), MockComponent(IconComponent)],
2122
providers: [
2223
mockProvider(Document, {
@@ -29,32 +30,32 @@ describe('Download Json Component', () => {
2930
shallow: true
3031
});
3132

32-
const dataSource$: Observable<unknown> = of({
33-
spans: []
34-
});
33+
const metadata: DownloadFileMetadata = {
34+
dataSource: of(''),
35+
fileName: 'download.txt'
36+
};
3537

3638
test('should have only download button, when data is not loading', () => {
37-
spectator = createHost(`<ht-download-json [dataSource]="dataSource"></ht-download-json>`, {
39+
spectator = createHost(`<ht-download-file [metadata]="metadata"></ht-download-file>`, {
3840
hostProps: {
39-
dataSource: dataSource$
41+
metadata: metadata
4042
}
4143
});
4244

4345
expect(spectator.query(ButtonComponent)).toExist();
4446
});
4547

46-
test('should download json file', fakeAsync(() => {
47-
spectator = createHost(`<ht-download-json [dataSource]="dataSource"></ht-download-json>`, {
48+
test('should download file', fakeAsync(() => {
49+
spectator = createHost(`<ht-download-file [metadata]="metadata"></ht-download-file>`, {
4850
hostProps: {
49-
dataSource: dataSource$
51+
metadata: metadata
5052
}
5153
});
5254

5355
spyOn(spectator.component, 'triggerDownload');
5456

5557
expect(spectator.component.dataLoading).toBe(false);
56-
expect(spectator.component.fileName).toBe('download.json');
57-
const element = spectator.query('.download-json');
58+
const element = spectator.query('.download-file');
5859
expect(element).toExist();
5960

6061
spectator.click(element!);

projects/components/src/download-json/download-json.component.ts renamed to projects/components/src/download-file/download-file.component.ts

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
11
import { DOCUMENT } from '@angular/common';
22
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, Input, Renderer2 } from '@angular/core';
33
import { IconType } from '@hypertrace/assets-library';
4-
import { IconSize } from '@hypertrace/components';
5-
import { Observable } from 'rxjs';
64
import { catchError, finalize, take } from 'rxjs/operators';
75
import { ButtonSize, ButtonStyle } from '../button/button';
6+
import { IconSize } from '../icon/icon-size';
87
import { NotificationService } from '../notification/notification.service';
8+
import { DownloadFileMetadata } from './download-file-metadata';
99

1010
@Component({
11-
selector: 'ht-download-json',
11+
selector: 'ht-download-file',
1212
changeDetection: ChangeDetectionStrategy.OnPush,
13-
styleUrls: ['./download-json.component.scss'],
13+
styleUrls: ['./download-file.component.scss'],
1414
template: `
15-
<div class="download-json" (click)="this.triggerDownload()">
15+
<div *ngIf="this.metadata" class="download-file" (click)="this.triggerDownload()">
1616
<ht-button
1717
*ngIf="!this.dataLoading"
1818
class="download-button"
@@ -24,12 +24,9 @@ import { NotificationService } from '../notification/notification.service';
2424
</div>
2525
`
2626
})
27-
export class DownloadJsonComponent {
27+
export class DownloadFileComponent {
2828
@Input()
29-
public dataSource!: Observable<unknown>;
30-
31-
@Input()
32-
public fileName: string = 'download.json';
29+
public metadata?: DownloadFileMetadata;
3330

3431
public dataLoading: boolean = false;
3532
private readonly dlJsonAnchorElement: HTMLAnchorElement;
@@ -45,31 +42,25 @@ export class DownloadJsonComponent {
4542

4643
public triggerDownload(): void {
4744
this.dataLoading = true;
48-
this.dataSource
49-
.pipe(
50-
take(1),
51-
catchError(() => this.notificationService.createFailureToast('Download failed')),
52-
finalize(() => {
53-
this.dataLoading = false;
54-
this.changeDetector.detectChanges();
55-
})
56-
)
57-
.subscribe((data: unknown) => {
58-
if (typeof data === 'string') {
59-
this.downloadData(data);
60-
} else {
61-
this.downloadData(JSON.stringify(data));
62-
}
63-
});
45+
this.metadata!.dataSource.pipe(
46+
take(1),
47+
catchError(() => this.notificationService.createFailureToast('Download failed')),
48+
finalize(() => {
49+
this.dataLoading = false;
50+
this.changeDetector.detectChanges();
51+
})
52+
).subscribe((data: string) => {
53+
this.downloadData(data);
54+
});
6455
}
6556

6657
private downloadData(data: string): void {
6758
this.renderer.setAttribute(
6859
this.dlJsonAnchorElement,
6960
'href',
70-
`data:text/json;charset=utf-8,${encodeURIComponent(data)}`
61+
`data:text/plain;charset=utf-8,${encodeURIComponent(data)}`
7162
);
72-
this.renderer.setAttribute(this.dlJsonAnchorElement, 'download', this.fileName);
63+
this.renderer.setAttribute(this.dlJsonAnchorElement, 'download', this.metadata!.fileName);
7364
this.renderer.setAttribute(this.dlJsonAnchorElement, 'display', 'none');
7465
this.dlJsonAnchorElement.click();
7566
}

projects/components/src/download-json/download-json.module.ts renamed to projects/components/src/download-file/download-file.module.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { NgModule } from '@angular/core';
33
import { ButtonModule } from '../button/button.module';
44
import { IconModule } from '../icon/icon.module';
55
import { NotificationModule } from '../notification/notification.module';
6-
import { DownloadJsonComponent } from './download-json.component';
6+
import { DownloadFileComponent } from './download-file.component';
77

88
@NgModule({
9-
declarations: [DownloadJsonComponent],
9+
declarations: [DownloadFileComponent],
1010
imports: [CommonModule, ButtonModule, NotificationModule, IconModule],
11-
exports: [DownloadJsonComponent]
11+
exports: [DownloadFileComponent]
1212
})
13-
export class DownloadJsonModule {}
13+
export class DownloadFileModule {}

projects/components/src/public-api.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,10 @@ export * from './checkbox/checkbox.module';
2424
export * from './collapsible-sidebar/collapsible-sidebar.component';
2525
export * from './collapsible-sidebar/collapsible-sidebar.module';
2626

27+
// Collapsible sidebar
28+
export * from './viewer/code-viewer/code-viewer.component';
29+
export * from './viewer/code-viewer/code-viewer.module';
30+
2731
// Combo Box
2832
export * from './combo-box/combo-box.module';
2933
export * from './combo-box/combo-box.component';
@@ -75,9 +79,10 @@ export { MenuDropdownComponent } from './menu-dropdown/menu-dropdown.component';
7579
export { MenuItemComponent } from './menu-dropdown/menu-item/menu-item.component';
7680
export { MenuDropdownModule } from './menu-dropdown/menu-dropdown.module';
7781

78-
// Download JSON
79-
export * from './download-json/download-json.component';
80-
export * from './download-json/download-json.module';
82+
// Download File
83+
export * from './download-file/download-file.component';
84+
export * from './download-file/download-file-metadata';
85+
export * from './download-file/download-file.module';
8186

8287
// Dynamic label
8388
export * from './highlighted-label/highlighted-label.component';
@@ -272,10 +277,6 @@ export * from './overlay/overlay';
272277
export * from './overlay/overlay.module';
273278
export * from './overlay/sheet/sheet';
274279

275-
// Snippet
276-
export { SnippetViewerComponent } from './viewer/snippet-viewer/snippet-viewer.component';
277-
export { SnippetViewerModule } from './viewer/snippet-viewer/snippet-viewer.module';
278-
279280
// Spinner
280281
export * from './spinner/spinner.component';
281282
export * from './spinner/spinner.module';
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
@import 'mixins';
2+
3+
@mixin line-base {
4+
display: flex;
5+
align-items: center;
6+
width: 100%;
7+
height: 20px;
8+
9+
&.line-highlight {
10+
background-color: $blue-2;
11+
}
12+
}
13+
14+
.code-viewer {
15+
@include fill-container;
16+
display: grid;
17+
grid-template-rows: 54px auto;
18+
19+
.header {
20+
height: 100%;
21+
display: flex;
22+
align-items: center;
23+
padding: 0 12px;
24+
border-bottom: 1px solid $gray-2;
25+
26+
.title {
27+
@include overline;
28+
}
29+
30+
.header-content {
31+
min-width: 0;
32+
flex: 1 1 auto;
33+
display: flex;
34+
justify-content: flex-end;
35+
36+
.search-box {
37+
width: 140px;
38+
background-color: white;
39+
}
40+
}
41+
}
42+
43+
.content {
44+
@include code;
45+
height: 100%;
46+
overflow-y: auto;
47+
display: grid;
48+
grid-template-columns: 40px auto;
49+
position: relative;
50+
51+
.line-numbers {
52+
width: 100%;
53+
display: flex;
54+
flex-direction: column;
55+
56+
.line-number {
57+
@include line-base;
58+
padding-left: 8px;
59+
}
60+
}
61+
62+
.code-lines {
63+
width: 100%;
64+
display: flex;
65+
flex-direction: column;
66+
overflow-x: auto;
67+
68+
.code-line {
69+
@include line-base;
70+
white-space: break-spaces;
71+
72+
::ng-deep {
73+
mark {
74+
background-color: $yellow-4;
75+
}
76+
}
77+
}
78+
}
79+
80+
.copy-to-clipboard {
81+
position: absolute;
82+
color: $gray-9;
83+
right: 0;
84+
top: 0;
85+
}
86+
}
87+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { FormattingModule } from '@hypertrace/common';
2+
import { createComponentFactory } from '@ngneat/spectator/jest';
3+
import { MockComponent } from 'ng-mocks';
4+
import { CopyToClipboardComponent } from '../../copy-to-clipboard/copy-to-clipboard.component';
5+
import { DownloadFileComponent } from '../../download-file/download-file.component';
6+
import { SearchBoxComponent } from '../../search-box/search-box.component';
7+
import { CodeViewerComponent } from './code-viewer.component';
8+
9+
describe('Code Viewer Component', () => {
10+
const createComponent = createComponentFactory({
11+
component: CodeViewerComponent,
12+
declarations: [
13+
MockComponent(SearchBoxComponent),
14+
MockComponent(DownloadFileComponent),
15+
MockComponent(CopyToClipboardComponent)
16+
],
17+
imports: [FormattingModule],
18+
shallow: true
19+
});
20+
const code = `{\n "key": "value" \n }`;
21+
const downloadFileName = 'code.json';
22+
23+
test('should render everything correctly', () => {
24+
const spectator = createComponent({
25+
props: {
26+
code: ''
27+
}
28+
});
29+
expect(spectator.query('.code-viewer')).not.toExist();
30+
31+
// Set code
32+
spectator.setInput({
33+
code: code
34+
});
35+
36+
expect(spectator.query('.code-viewer')).toExist();
37+
expect(spectator.query(SearchBoxComponent)).toExist();
38+
expect(spectator.query(DownloadFileComponent)).not.toExist();
39+
expect(spectator.query(CopyToClipboardComponent)).not.toExist();
40+
expect(spectator.query('.title')).toHaveText('Code Viewer');
41+
expect(spectator.queryAll('.line-number').length).toBe(3);
42+
expect(spectator.queryAll('.code-line').length).toBe(3);
43+
expect(spectator.queryAll('.line-number.line-highlight').length).toBe(0);
44+
expect(spectator.queryAll('.code-line.line-highlight').length).toBe(0);
45+
46+
// Highlight text
47+
spectator.setInput({
48+
highlightText: 'key'
49+
});
50+
51+
expect(spectator.queryAll('.code-line.line-highlight').length).toBe(1);
52+
expect(spectator.queryAll('.code-line.line-highlight').length).toBe(1);
53+
54+
// Download
55+
spectator.setInput({
56+
downloadFileName: downloadFileName
57+
});
58+
59+
expect(spectator.query(DownloadFileComponent)).toExist();
60+
expect(spectator.query(DownloadFileComponent)?.metadata?.fileName).toBe(downloadFileName);
61+
62+
// Copy to clipboard
63+
spectator.setInput({
64+
enableCopy: true
65+
});
66+
67+
expect(spectator.query(CopyToClipboardComponent)).toExist();
68+
69+
// Search
70+
spectator.triggerEventHandler(SearchBoxComponent, 'valueChange', 'e');
71+
expect(spectator.queryAll('mark').length).toBe(2);
72+
});
73+
});

0 commit comments

Comments
 (0)