Skip to content

Commit 02431a1

Browse files
Dataset details table headers improvements (GSI-1967) (#232)
* Moved heading calculation to table component Changed the text of heading Added tooltip for downloading the metadata * Fixed e2e tests * Added rudimentary mechanism to hide the tooltip after hovering on the expansion panel header * Implemented improved hover mechanism * Bump version
1 parent 9919230 commit 02431a1

File tree

6 files changed

+90
-55
lines changed

6 files changed

+90
-55
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "data-portal",
3-
"version": "2.2.7",
3+
"version": "2.2.8",
44
"scripts": {
55
"ng": "ng",
66
"start": "ng serve",
Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
<mat-expansion-panel>
2-
<mat-expansion-panel-header>{{ header() }}</mat-expansion-panel-header
2+
<mat-expansion-panel-header
3+
matTooltip="Download the full metadata to view all properties"
4+
matTooltipPosition="above"
5+
matTooltipShowDelay="500"
6+
matTooltipPositionAtOrigin="true"
7+
[matTooltipDisabled]="tooltipDisabled()"
8+
(mouseenter)="startHoverHeader()"
9+
(mouseleave)="endHoverHeader()"
10+
>{{ header() }}</mat-expansion-panel-header
311
><ng-template matExpansionPanelContent
412
><div class="overflow-auto">
513
<table
@@ -9,43 +17,45 @@
917
matSort
1018
>
1119
<caption class="cdk-visually-hidden">
12-
{{ caption() }}
20+
{{
21+
caption()
22+
}}
1323
</caption>
14-
@for (col of columns(); track col){
15-
<ng-container [matColumnDef]="col.columnDef">
16-
<th mat-header-cell *matHeaderCellDef mat-sort-header [class]="col.class">
17-
{{col.header}}
18-
</th>
19-
@if (col.columnDef === 'hash') {
20-
<td mat-cell *matCellDef="let item">
21-
<app-with-copy-button
22-
[value]="item.file_information?.sha256_hash"
23-
notifyMessage="The full hash has been copied to the clipboard"
24-
></app-with-copy-button>
25-
</td>
26-
} @else {
27-
<td mat-cell *matCellDef="let item">
28-
{{col.columnDef | detailsDataRenderer: item : col.accessor :
29-
storageLabels()}}
30-
</td>
31-
}
32-
</ng-container>
24+
@for (col of columns(); track col) {
25+
<ng-container [matColumnDef]="col.columnDef">
26+
<th mat-header-cell *matHeaderCellDef mat-sort-header [class]="col.class">
27+
{{ col.header }}
28+
</th>
29+
@if (col.columnDef === 'hash') {
30+
<td mat-cell *matCellDef="let item">
31+
<app-with-copy-button
32+
[value]="item.file_information?.sha256_hash"
33+
notifyMessage="The full hash has been copied to the clipboard"
34+
></app-with-copy-button>
35+
</td>
36+
} @else {
37+
<td mat-cell *matCellDef="let item">
38+
{{
39+
col.columnDef
40+
| detailsDataRenderer: item : col.accessor : storageLabels()
41+
}}
42+
</td>
43+
}
44+
</ng-container>
3345
}
3446

3547
<tr mat-header-row *matHeaderRowDef="displayCols()"></tr>
3648
<tr mat-row *matRowDef="let row; columns: displayCols()"></tr>
3749
</table>
3850

3951
@if (numItems() > 10) {
40-
<mat-paginator
41-
[pageSize]="defaultTablePageSize"
42-
[pageSizeOptions]="tablePageSizeOptions"
43-
showFirstLastButtons
44-
aria-label="Select page"
45-
class="sticky left-0"
46-
>
47-
</mat-paginator>
48-
}
49-
</div></ng-template
50-
></mat-expansion-panel
51-
>
52+
<mat-paginator
53+
[pageSize]="defaultTablePageSize"
54+
[pageSizeOptions]="tablePageSizeOptions"
55+
showFirstLastButtons
56+
aria-label="Select page"
57+
class="sticky left-0"
58+
>
59+
</mat-paginator>
60+
}</div></ng-template
61+
></mat-expansion-panel>

src/app/metadata/features/dataset-details-table/dataset-details-table.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,23 @@ import {
1212
inject,
1313
input,
1414
QueryList,
15+
signal,
1516
ViewChild,
1617
ViewChildren,
1718
} from '@angular/core';
1819
import { MatExpansionModule } from '@angular/material/expansion';
1920
import { MatPaginator, MatPaginatorModule } from '@angular/material/paginator';
2021
import { MatSort, MatSortModule } from '@angular/material/sort';
2122
import { MatTableDataSource, MatTableModule } from '@angular/material/table';
23+
import { MatTooltip } from '@angular/material/tooltip';
2224
import {
2325
datasetDetailsTableColumns,
2426
dataSortingDataAccessor,
2527
} from '@app/metadata/models/dataset-details-table';
2628
import { DetailsDataRendererPipe } from '@app/metadata/pipes/details-data-renderer-pipe';
2729
import { WellKnownValueService } from '@app/metadata/services/well-known-value';
2830
import { WithCopyButton } from '@app/shared/features/with-copy-button/with-copy-button';
31+
import { ParseBytes } from '@app/shared/pipes/parse-bytes-pipe';
2932

3033
/**
3134
* Component for the dataset details table
@@ -39,14 +42,50 @@ import { WithCopyButton } from '@app/shared/features/with-copy-button/with-copy-
3942
MatPaginatorModule,
4043
MatSortModule,
4144
WithCopyButton,
45+
MatTooltip,
4246
],
4347
templateUrl: './dataset-details-table.html',
4448
styleUrl: './dataset-details-table.scss',
4549
})
4650
export class DatasetDetailsTableComponent implements AfterViewInit {
47-
tableName = input.required<string>();
51+
tableName = input.required<'experiments' | 'samples' | 'files'>();
4852
data = input.required<any[]>();
49-
header = input.required<string>();
53+
54+
protected header = computed(() => {
55+
let header = `List of ${this.tableName()} (${this.numItems()} total`;
56+
if (this.tableName() === 'files') {
57+
const totalBytes = this.data().reduce(
58+
(acc, file) => acc + (file.file_information?.size ?? 0),
59+
0,
60+
);
61+
header = `${header}, ${ParseBytes.prototype.transform(totalBytes)}`;
62+
}
63+
return `${header})`;
64+
});
65+
66+
protected tooltipDisabled = signal(false);
67+
68+
#hoverHeaderTime: number = 0;
69+
70+
/**
71+
* When entering the tooltip, memorize start time
72+
*/
73+
startHoverHeader(): void {
74+
this.#hoverHeaderTime = Date.now();
75+
}
76+
77+
/**
78+
* After leaving the tooltip, disable if it was shown long enough
79+
*/
80+
endHoverHeader(): void {
81+
if (Date.now() - this.#hoverHeaderTime < 1000) return;
82+
const key = `hint.${this.tableName()}.details.shown`;
83+
if (!sessionStorage.getItem(key)) {
84+
sessionStorage.setItem(key, 'true');
85+
this.tooltipDisabled.set(true);
86+
}
87+
this.#hoverHeaderTime = 0;
88+
}
5089

5190
protected numItems = computed(() => this.data().length);
5291

@@ -95,5 +134,8 @@ export class DatasetDetailsTableComponent implements AfterViewInit {
95134
this.matPaginators.changes.subscribe(() => {
96135
if (this.paginator) this.dataSource.paginator = this.paginator;
97136
});
137+
138+
const key = `hint.${this.tableName()}.details.shown`;
139+
if (sessionStorage.getItem(key)) this.tooltipDisabled.set(true);
98140
}
99141
}

src/app/metadata/features/dataset-details/dataset-details.html

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,16 @@ <h2 class="inline-flex items-center text-2xl">
275275
<app-dataset-details-table
276276
tableName="experiments"
277277
[data]="experiments()"
278-
[header]="experimentsHeader()"
279278
></app-dataset-details-table>
280279

281280
<app-dataset-details-table
282281
tableName="samples"
283282
[data]="samples()"
284-
[header]="samplesHeader()"
285283
></app-dataset-details-table>
286284

287285
<app-dataset-details-table
288286
tableName="files"
289287
[data]="files()"
290-
[header]="filesHeader()"
291288
></app-dataset-details-table>
292289
</div>
293290
}

src/app/metadata/features/dataset-details/dataset-details.ts

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import { ValidateDOI } from '@app/metadata/pipes/validate-doi-pipe';
3232
import { DatasetInformationService } from '@app/metadata/services/dataset-information';
3333
import { MetadataService } from '@app/metadata/services/metadata';
3434
import { WellKnownValueService } from '@app/metadata/services/well-known-value';
35-
import { ParseBytes } from '@app/shared/pipes/parse-bytes-pipe';
3635
import { UnderscoreToSpace } from '@app/shared/pipes/underscore-to-space-pipe';
3736
import { ConfigService } from '@app/shared/services/config';
3837
import { NavigationTrackingService } from '@app/shared/services/navigation';
@@ -163,19 +162,6 @@ export class DatasetDetailsComponent implements OnInit {
163162
this.files().reduce((acc, file) => acc + (file.file_information?.size ?? 0), 0),
164163
);
165164

166-
experimentsHeader = computed(
167-
() =>
168-
`Experiments Summary (${this.numExperiments()} experiment${this.numExperiments() === 1 ? '' : 's'})`,
169-
);
170-
samplesHeader = computed(
171-
() =>
172-
`Samples Summary (${this.numSamples()} sample${this.numSamples() === 1 ? '' : 's'})`,
173-
);
174-
filesHeader = computed(
175-
() =>
176-
`Files Summary (${this.numFiles()} file${this.numFiles() === 1 ? '' : 's'}, ${ParseBytes.prototype.transform(this.numBytes())} in total)`,
177-
);
178-
179165
#datasetDetailsErrorEffect = effect(() => {
180166
if (this.#datasetDetails.error()) {
181167
this.#notify.showError('Error fetching dataset details.');

tests/browse.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,15 +63,15 @@ test('can navigate to dataset details', async ({ page }) => {
6363
await expect(main).toContainText('Test dataset with some details for testing.');
6464

6565
await expect(main).toContainText('Test study description.');
66-
await expect(main).toContainText('Files Summary (12 files, 6.16 GB in total)');
66+
await expect(main).toContainText('List of files (12 total, 6.16 GB)');
6767

6868
// files table should not yet be visible
6969
await expect(main).not.toContainText('File ID');
7070
await expect(main).not.toContainText('GHGAF12345678901243');
7171
await expect(main).not.toContainText('Research data file 3');
7272
await expect(main).not.toContainText('Tübingen 3');
7373

74-
const openFile = main.getByRole('button', { name: 'Files Summary' });
74+
const openFile = main.getByRole('button', { name: 'List of files' });
7575
await expect(openFile).toBeVisible();
7676
await openFile.click();
7777

0 commit comments

Comments
 (0)