Skip to content

Commit 53a83ce

Browse files
committed
refactor(files-tree-row): added files tree row for clean structure
1 parent 0f64c52 commit 53a83ce

5 files changed

Lines changed: 272 additions & 2 deletions

File tree

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
@if (file().previousFolder) {
2+
<div class="files-table-row previous-folder">
3+
<p-button
4+
class="link-btn-no-padding w-full h-full"
5+
styleClass="w-full h-full justify-content-start"
6+
link
7+
(onClick)="openParentFolder.emit()"
8+
osfStopPropagation
9+
>
10+
<i class="fas fa-sm fa-angle-left"></i>
11+
<i class="fas fa-folder"></i>
12+
{{ file().name }}
13+
</p-button>
14+
</div>
15+
} @else {
16+
<div class="files-table-row" tabindex="0">
17+
<div class="table-cell flex align-items-center" [class.pl-6]="hasFoldersStack()">
18+
<p-button
19+
class="link-btn-no-padding"
20+
link
21+
[label]="file().name"
22+
[icon]="isFolder() ? 'fas fa-folder' : 'fas fa-file blue-icon'"
23+
(onClick)="onOpenEntry()"
24+
osfStopPropagation
25+
/>
26+
</div>
27+
28+
<div class="files-table-cell">
29+
@if (downloadsCount()) {
30+
{{ downloadsCount() }} {{ 'common.labels.downloads' | translate }}
31+
}
32+
</div>
33+
34+
<div class="files-table-cell">
35+
{{ file().size | fileSize }}
36+
</div>
37+
38+
<div class="files-table-cell">
39+
{{ file().dateModified | date: 'MMM d, y hh:mm a' }}
40+
</div>
41+
42+
@if (showMenu()) {
43+
<div class="ml-auto">
44+
<osf-file-menu
45+
(action)="menuAction.emit($event)"
46+
[isFolder]="isFolder()"
47+
[allowedActions]="allowedMenuActions()"
48+
osfStopPropagation
49+
>
50+
</osf-file-menu>
51+
</div>
52+
}
53+
</div>
54+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
@use "styles/mixins" as mix;
2+
3+
.files-table-row {
4+
display: grid;
5+
align-items: center;
6+
grid-template-columns:
7+
minmax(mix.rem(200px), 32rem) minmax(mix.rem(150px), 0.7fr) minmax(mix.rem(100px), 100px)
8+
minmax(mix.rem(150px), 1fr) minmax(mix.rem(50px), 50px);
9+
grid-template-rows: mix.rem(44px);
10+
border-bottom: 1px solid var(--grey-2);
11+
padding: 0 0.75rem;
12+
cursor: pointer;
13+
14+
&.previous-folder {
15+
grid-template-columns: auto;
16+
}
17+
18+
&:hover {
19+
background: var(--bg-blue-3);
20+
}
21+
22+
&:active {
23+
background: var(--bg-blue-2);
24+
}
25+
26+
.table-cell {
27+
width: 100%;
28+
height: 100%;
29+
}
30+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { MockComponent } from 'ng-mocks';
2+
3+
import { ComponentFixture, TestBed } from '@angular/core/testing';
4+
5+
import { FileKind } from '@osf/shared/enums/file-kind.enum';
6+
import { FileMenuType } from '@osf/shared/enums/file-menu-type.enum';
7+
import { FileModel } from '@shared/models/files/file.model';
8+
import { FileMenuFlags } from '@shared/models/files/file-menu-action.model';
9+
10+
import { provideOSFCore } from '@testing/osf.testing.provider';
11+
12+
import { FileMenuComponent } from '../file-menu/file-menu.component';
13+
14+
import { FilesTreeRowComponent } from './files-tree-row.component';
15+
16+
describe('FilesTreeRowComponent', () => {
17+
let component: FilesTreeRowComponent;
18+
let fixture: ComponentFixture<FilesTreeRowComponent>;
19+
20+
const ALL_MENU_ACTIONS: FileMenuFlags = {
21+
[FileMenuType.Download]: true,
22+
[FileMenuType.Copy]: true,
23+
[FileMenuType.Move]: true,
24+
[FileMenuType.Delete]: true,
25+
[FileMenuType.Rename]: true,
26+
[FileMenuType.Share]: true,
27+
[FileMenuType.Embed]: true,
28+
};
29+
30+
function createTestFile(overrides: Partial<FileModel> = {}): FileModel {
31+
return {
32+
id: 'f1',
33+
guid: 'g1',
34+
name: 'test.pdf',
35+
kind: FileKind.File,
36+
path: '/test.pdf',
37+
size: 2048,
38+
materializedPath: '/test.pdf',
39+
dateModified: '2024-06-01T12:00:00.000Z',
40+
extra: {
41+
hashes: { md5: 'm', sha256: 's' },
42+
downloads: 5,
43+
},
44+
links: {
45+
info: 'i',
46+
move: 'm',
47+
upload: 'u',
48+
delete: 'd',
49+
download: 'dl',
50+
render: 'r',
51+
html: 'h',
52+
self: 's',
53+
},
54+
filesLink: null,
55+
previousFolder: false,
56+
provider: 'osfstorage',
57+
...overrides,
58+
};
59+
}
60+
61+
beforeEach(() => {
62+
TestBed.configureTestingModule({
63+
imports: [FilesTreeRowComponent, MockComponent(FileMenuComponent)],
64+
providers: [provideOSFCore()],
65+
});
66+
67+
fixture = TestBed.createComponent(FilesTreeRowComponent);
68+
component = fixture.componentInstance;
69+
fixture.componentRef.setInput('file', createTestFile());
70+
fixture.componentRef.setInput('allowedMenuActions', ALL_MENU_ACTIONS);
71+
fixture.detectChanges();
72+
});
73+
74+
it('should create', () => {
75+
expect(component).toBeTruthy();
76+
});
77+
78+
it('should set isFolder when kind is folder', () => {
79+
fixture.componentRef.setInput('file', createTestFile({ kind: FileKind.Folder }));
80+
fixture.detectChanges();
81+
82+
expect(component.isFolder()).toBe(true);
83+
});
84+
85+
it('should set isFolder false when kind is file', () => {
86+
fixture.componentRef.setInput('file', createTestFile({ kind: FileKind.File }));
87+
fixture.detectChanges();
88+
89+
expect(component.isFolder()).toBe(false);
90+
});
91+
92+
it('should clear downloadsCount for folder', () => {
93+
fixture.componentRef.setInput(
94+
'file',
95+
createTestFile({
96+
kind: FileKind.Folder,
97+
extra: { hashes: { md5: 'm', sha256: 's' }, downloads: 99 },
98+
})
99+
);
100+
fixture.detectChanges();
101+
102+
expect(component.downloadsCount()).toBe('');
103+
});
104+
105+
it('should expose downloadsCount for file with downloads', () => {
106+
fixture.componentRef.setInput(
107+
'file',
108+
createTestFile({
109+
kind: FileKind.File,
110+
extra: { hashes: { md5: 'm', sha256: 's' }, downloads: 12 },
111+
})
112+
);
113+
fixture.detectChanges();
114+
115+
expect(component.downloadsCount()).toBe(12);
116+
});
117+
118+
it('should clear downloadsCount when downloads is zero', () => {
119+
fixture.componentRef.setInput(
120+
'file',
121+
createTestFile({
122+
kind: FileKind.File,
123+
extra: { hashes: { md5: 'm', sha256: 's' }, downloads: 0 },
124+
})
125+
);
126+
fixture.detectChanges();
127+
128+
expect(component.downloadsCount()).toBe('');
129+
});
130+
});
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import { TranslatePipe } from '@ngx-translate/core';
2+
3+
import { Button } from 'primeng/button';
4+
5+
import { DatePipe } from '@angular/common';
6+
import { ChangeDetectionStrategy, Component, computed, input, output } from '@angular/core';
7+
8+
import { StopPropagationDirective } from '@osf/shared/directives/stop-propagation.directive';
9+
import { FileKind } from '@osf/shared/enums/file-kind.enum';
10+
import { FileModel } from '@shared/models/files/file.model';
11+
import { FileMenuAction, FileMenuFlags } from '@shared/models/files/file-menu-action.model';
12+
13+
import { FileSizePipe } from '../../pipes/file-size.pipe';
14+
import { FileMenuComponent } from '../file-menu/file-menu.component';
15+
16+
@Component({
17+
selector: 'osf-files-tree-row',
18+
imports: [Button, DatePipe, FileSizePipe, TranslatePipe, FileMenuComponent, StopPropagationDirective],
19+
templateUrl: './files-tree-row.component.html',
20+
styleUrl: './files-tree-row.component.scss',
21+
changeDetection: ChangeDetectionStrategy.OnPush,
22+
})
23+
export class FilesTreeRowComponent {
24+
file = input.required<FileModel>();
25+
hasFoldersStack = input<boolean>(false);
26+
showMenu = input<boolean>(false);
27+
allowedMenuActions = input.required<FileMenuFlags>();
28+
29+
readonly isFolder = computed(() => this.file().kind === FileKind.Folder);
30+
31+
readonly downloadsCount = computed(() => {
32+
if (!this.file().extra.downloads || this.isFolder()) {
33+
return '';
34+
}
35+
return this.file().extra.downloads;
36+
});
37+
38+
openParentFolder = output<void>();
39+
openEntry = output<FileModel>();
40+
menuAction = output<FileMenuAction>();
41+
42+
onOpenEntry(): void {
43+
this.openEntry.emit(this.file());
44+
}
45+
}

src/styles/overrides/tree.scss

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
.p-tree {
33
padding: 0;
44

5+
.files-table-row {
6+
.p-button {
7+
--p-button-label-font-weight: 400;
8+
--p-button-link-color: var(--dark-blue-1);
9+
}
10+
}
11+
512
.p-tree-node-toggle-button {
613
display: none;
714
}
@@ -18,7 +25,7 @@
1825

1926
.p-tree-node-dragover {
2027
.files-table-row {
21-
background: var(--bg-blue-3);
28+
background-color: var(--bg-blue-3);
2229
}
2330
}
2431

@@ -53,7 +60,11 @@
5360
.p-tree-node-selected {
5461
.files-table-row {
5562
color: var(--white);
56-
background: var(--pr-blue-1);
63+
background-color: var(--pr-blue-1);
64+
65+
.p-button {
66+
--p-button-link-color: var(--white);
67+
}
5768

5869
.blue-icon {
5970
color: var(--white);

0 commit comments

Comments
 (0)