Skip to content

Commit 2b06a78

Browse files
committed
frontend: feat: load the next entry points a screen before the end and show a skeleton row meanwhile
1 parent 24dabb5 commit 2b06a78

5 files changed

Lines changed: 21 additions & 10 deletions

File tree

‎frontend/packages/ui/src/ui/in-view/in-view.directive.spec.ts‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,20 @@ type Callback = (entries: Partial<IntersectionObserverEntry>[]) => void;
1414
class FakeObserver {
1515
static live: FakeObserver[] = [];
1616
observed: Element[] = [];
17-
constructor(readonly callback: Callback) { FakeObserver.live.push(this); }
17+
constructor(readonly callback: Callback, readonly options?: IntersectionObserverInit) { FakeObserver.live.push(this); }
1818
observe(el: Element) { this.observed.push(el); }
1919
disconnect() { FakeObserver.live = FakeObserver.live.filter(o => o !== this); }
2020
}
2121

2222
@Component({
2323
standalone: true,
2424
imports: [InViewDirective],
25-
template: `<div class="sentinel" (grInView)="seen.set(seen() + 1)" [grInViewKey]="key()"></div>`,
25+
template: `<div class="sentinel" (grInView)="seen.set(seen() + 1)" [grInViewKey]="key()" [grInViewMargin]="margin()"></div>`,
2626
})
2727
class HostComponent {
2828
seen = signal(0);
2929
key = signal(0);
30+
margin = signal('200px');
3031
}
3132

3233
function render() {
@@ -62,4 +63,11 @@ describe('InViewDirective', () => {
6263
FakeObserver.live[0].callback([{ isIntersecting: true }]);
6364
expect(fixture.componentInstance.seen()).toBe(1);
6465
});
66+
67+
it('fires early by the margin a list asks for', () => {
68+
const fixture = render();
69+
fixture.componentInstance.margin.set('100%');
70+
fixture.detectChanges();
71+
expect(FakeObserver.live[0].options?.rootMargin).toBe('100%');
72+
});
6573
});

‎frontend/packages/ui/src/ui/in-view/in-view.directive.ts‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@ import { Directive, ElementRef, effect, inject, input, output } from '@angular/c
1515
export class InViewDirective {
1616
grInView = output<void>();
1717
grInViewKey = input<unknown>();
18+
grInViewMargin = input('200px');
1819

1920
private host = inject<ElementRef<Element>>(ElementRef);
2021

2122
constructor() {
2223
effect((onCleanup) => {
2324
this.grInViewKey();
25+
const rootMargin = this.grInViewMargin();
2426
if (typeof IntersectionObserver === 'undefined') return;
2527
const observer = new IntersectionObserver(
2628
(entries) => entries.some((e) => e.isIntersecting) && this.grInView.emit(),
27-
{ rootMargin: '200px' },
29+
{ rootMargin },
2830
);
2931
observer.observe(this.host.nativeElement);
3032
onCleanup(() => observer.disconnect());

‎frontend/src/app/features/tasks/task-detail/task-detail.component.html‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,12 @@ <h2 [title]="evalTitle(sel)">{{ evalTitle(sel) }}</h2>
160160
}
161161
@if (entryPoints().length > 0) {
162162
@if (entryPoints().length < entryPointsTotal()) {
163-
<div class="pkg-more" (grInView)="loadMoreEntryPoints()" [grInViewKey]="entryPoints().length">
164-
Loading more ({{ entryPoints().length }} of {{ entryPointsTotal() }})
163+
<div class="pkg pkg-more" aria-busy="true" [attr.aria-label]="'Loading packages, ' + entryPoints().length + ' of ' + entryPointsTotal()"
164+
(grInView)="loadMoreEntryPoints()" [grInViewKey]="entryPoints().length" grInViewMargin="100%">
165+
<gr-skeleton class="si" />
166+
<gr-skeleton class="pkg-name" />
167+
<gr-skeleton class="pkg-bar" height="7px" />
168+
<gr-skeleton class="pkg-deps" width="5rem" />
165169
</div>
166170
}
167171
} @else if (!entryPointsLoading() && isRunning(sel.status)) {

‎frontend/src/app/features/tasks/task-detail/task-detail.component.spec.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -913,7 +913,8 @@ describe('TaskDetailComponent - #636 eval page', () => {
913913
fixture.detectChanges();
914914
const spy = vi.spyOn(tasksService, 'getEntryPoints').mockReturnValue(of({ entry_points: [epSummary('b')], total: 2 }));
915915
const sentinel = fixture.debugElement.query(By.css('.pkg-more'));
916-
expect(fixture.nativeElement.textContent).not.toContain('Show more');
916+
expect(sentinel.nativeElement.querySelectorAll('gr-skeleton').length).toBeGreaterThan(0);
917+
expect(sentinel.nativeElement.textContent.trim()).toBe('');
917918
sentinel.triggerEventHandler('grInView');
918919
expect(spy).toHaveBeenCalledWith('acme', 'demo', 'e1', 25, 1);
919920
fixture.detectChanges();

‎frontend/src/app/features/tasks/task-detail/task-detail.packages.scss‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@
1111

1212
.pkg-more {
1313
margin-top: $space-pair;
14-
padding: $spacing-sm $spacing-md;
15-
color: var(--gr-text-muted);
16-
font-size: $font-size-sm;
17-
text-align: center;
1814
}
1915

2016
.pkg {

0 commit comments

Comments
 (0)