Address sources header review feedback

This commit is contained in:
4gray committed 2026-06-20 12:39:00 +02:00
1 parent 4e732d3e51
commit e3388de989
2 files changed
+144 -23

No files matched your search

@@ -6,7 +6,7 @@
<header class="sources-header">
<div class="sources-header__meta">
<h2 class="sources-title">{{ title() }}</h2>
<p class="sources-subtitle">{{ subtitle() }}</p>
<span class="sources-subtitle">{{ subtitle() }}</span>
</div>
<button
type="button"
@@ -1,26 +1,147 @@
import { readFileSync } from 'fs';
import { join } from 'path';
import { Component, input, output } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatMenuModule } from '@angular/material/menu';
import { ActivatedRoute, convertToParamMap } from '@angular/router';
import { provideMockStore } from '@ngrx/store/testing';
import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs';
import {
selectActiveTypeFilters,
selectAllPlaylistsMeta,
} from '@iptvnator/m3u-state';
import { SortBy, SortOrder, SortService } from '@iptvnator/services';
import { WORKSPACE_SHELL_ACTIONS } from '@iptvnator/workspace/shell/util';
import { WorkspaceSourcesComponent } from './workspace-sources.component';
describe('WorkspaceSourcesComponent styles', () => {
const html = readFileSync(
join(__dirname, 'workspace-sources.component.html'),
'utf8'
);
const scss = readFileSync(
join(__dirname, 'workspace-sources.component.scss'),
'utf8'
);
@Component({
selector: 'app-recent-playlists',
template: '',
standalone: true,
})
class MockRecentPlaylistsComponent {
readonly searchQueryInput = input('');
readonly addPlaylistClicked = output<void>();
}
it('uses the shared panel header pattern for the sources toolbar', () => {
expect(html).toContain('class="sources-header__meta"');
expect(scss).toContain(
"@use '../../../../../../ui/styles/panel-header' as panel;"
);
expect(scss).toContain(
'@include panel.standard-panel-header($sticky: true);'
);
expect(scss).toContain('@include panel.standard-panel-meta();');
expect(scss).toContain('@include panel.standard-panel-title();');
expect(scss).toContain('@include panel.standard-panel-subtitle();');
describe('WorkspaceSourcesComponent', () => {
let fixture: ComponentFixture<WorkspaceSourcesComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [WorkspaceSourcesComponent, NoopAnimationsModule],
providers: [
provideMockStore({
selectors: [
{
selector: selectActiveTypeFilters,
value: ['m3u', 'xtream', 'stalker'],
},
{
selector: selectAllPlaylistsMeta,
value: [
{
_id: 'playlist-1',
title: 'Playlist 1',
},
],
},
],
}),
{
provide: ActivatedRoute,
useValue: {
queryParamMap: of(convertToParamMap({})),
},
},
{
provide: SortService,
useValue: {
getSortOptions: () =>
of({
by: SortBy.DATE_ADDED,
order: SortOrder.DESC,
}),
setSortOptions: jest.fn(),
},
},
{
provide: WORKSPACE_SHELL_ACTIONS,
useValue: {
openAddPlaylistDialog: jest.fn(),
},
},
{
provide: TranslateService,
useValue: {
instant: (
key: string,
params?: Record<string, string | number>
) => {
if (key === 'WORKSPACE.SOURCES.ALL_PLAYLISTS') {
return 'All Playlists';
}
if (
key === 'WORKSPACE.SOURCES.PLAYLIST_COUNT_ONE'
) {
return '1 playlist';
}
if (
key === 'WORKSPACE.SOURCES.PLAYLIST_COUNT_OTHER'
) {
return `${params?.['count']} playlists`;
}
if (key === 'HOME.SORT_OPTIONS.NEWEST') {
return 'Date added (Newest first)';
}
return key;
},
get: (key: string) => of(key),
stream: (key: string) => of(key),
onLangChange: of(null),
onTranslationChange: of(null),
onDefaultLangChange: of(null),
currentLang: 'en',
defaultLang: 'en',
},
},
],
})
.overrideComponent(WorkspaceSourcesComponent, {
set: {
imports: [
MatButtonModule,
MatIconModule,
MatMenuModule,
MockRecentPlaylistsComponent,
TranslatePipe,
],
},
})
.compileComponents();
fixture = TestBed.createComponent(WorkspaceSourcesComponent);
});
it('renders the shared panel header structure without paragraph subtitle margins', async () => {
fixture.detectChanges();
await fixture.whenStable();
const header: HTMLElement =
fixture.nativeElement.querySelector('.sources-header');
const meta: HTMLElement =
fixture.nativeElement.querySelector('.sources-header__meta');
const title: HTMLElement =
fixture.nativeElement.querySelector('.sources-title');
const subtitle: HTMLElement =
fixture.nativeElement.querySelector('.sources-subtitle');
expect(header).not.toBeNull();
expect(meta).not.toBeNull();
expect(title.textContent?.trim()).toBe('All Playlists');
expect(subtitle.textContent?.trim()).toBe('1 playlist');
expect(subtitle.tagName).toBe('SPAN');
});
});