fix(portal): address category sort review feedback

This commit is contained in:
4gray committed 2026-05-15 00:06:55 +02:00
1 parent daff96d90b
commit c5815650c2
24 files changed
+131 -32

No files matched your search

@@ -27,10 +27,15 @@
categorySortMode() !== 'server'
"
[matMenuTriggerFor]="categorySortMenu"
[matTooltip]="'Sort: ' + categorySortLabel()"
aria-label="Sort categories"
[matTooltip]="
('WORKSPACE.SORT_LABEL' | translate) +
(categorySortLabelKey() | translate)
"
[attr.aria-label]="
'WORKSPACE.CATEGORY_SORT_ARIA' | translate
"
>
<mat-icon>sort_by_alpha</mat-icon>
<mat-icon>{{ categorySortIcon() }}</mat-icon>
</button>
<mat-menu #categorySortMenu="matMenu">
@for (
@@ -42,7 +47,9 @@
(click)="setCategorySortMode(option.mode)"
>
<mat-icon>{{ option.icon }}</mat-icon>
<span>{{ option.label }}</span>
<span>{{
option.translationKey | translate
}}</span>
@if (categorySortMode() === option.mode) {
<mat-icon class="context-sort-check"
>check</mat-icon
@@ -18,6 +18,11 @@ const translations: Record<string, string> = {
'WORKSPACE.CONTEXT.RADIO_CATEGORIES': 'Radio Categories',
'WORKSPACE.CONTEXT.XTREAM_SYNCING_LIVE': 'Syncing live categories...',
'WORKSPACE.CONTEXT.XTREAM_SYNCING_MOVIES': 'Syncing movie categories...',
'WORKSPACE.CATEGORY_SORT_ARIA': 'Sort categories',
'WORKSPACE.SORT_LABEL': 'Sort: ',
'WORKSPACE.SORT_NAME_ASC': 'Name A-Z',
'WORKSPACE.SORT_NAME_DESC': 'Name Z-A',
'WORKSPACE.SORT_SERVER': 'Server sorting',
'WORKSPACE.SHELL.XTREAM_IMPORT_LOADING':
'Fetching playlist data from source...',
};
@@ -302,6 +307,55 @@ describe('WorkspaceContextPanelComponent', () => {
);
});
it('uses translated category sort labels and distinct mode icons', () => {
fixture.componentRef.setInput('section', 'vod');
xtreamSelectedTypeContentState.set('ready');
fixture.detectChanges();
const sortButton = Array.from(
fixture.nativeElement.querySelectorAll('.context-header__action')
).at(1) as HTMLButtonElement | undefined;
expect(sortButton?.getAttribute('aria-label')).toBe('Sort categories');
expect(fixture.componentInstance.categorySortLabelKey()).toBe(
'WORKSPACE.SORT_SERVER'
);
expect(fixture.componentInstance.categorySortIcon()).toBe('dns');
expect(
fixture.componentInstance.categorySortOptions.map((option) => ({
mode: option.mode,
translationKey: option.translationKey,
icon: option.icon,
}))
).toEqual([
{
mode: 'server',
translationKey: 'WORKSPACE.SORT_SERVER',
icon: 'dns',
},
{
mode: 'name-asc',
translationKey: 'WORKSPACE.SORT_NAME_ASC',
icon: 'sort_by_alpha',
},
{
mode: 'name-desc',
translationKey: 'WORKSPACE.SORT_NAME_DESC',
icon: 'arrow_downward',
},
]);
fixture.componentInstance.setCategorySortMode('name-desc');
fixture.detectChanges();
expect(fixture.componentInstance.categorySortLabelKey()).toBe(
'WORKSPACE.SORT_NAME_DESC'
);
expect(fixture.componentInstance.categorySortIcon()).toBe(
'arrow_downward'
);
});
it('applies the category sort menu modes to stalker categories', () => {
fixture.componentRef.setInput('context', {
provider: 'stalker',
@@ -20,7 +20,6 @@ import { TranslatePipe, TranslateService } from '@ngx-translate/core';
import { StalkerStore } from '@iptvnator/portal/stalker/data-access';
import {
PortalCategorySortMode,
getPortalCategorySortModeLabel,
persistPortalCategorySortMode,
restorePortalCategorySortMode,
sortPortalCategoryItems,
@@ -157,30 +156,36 @@ export class WorkspaceContextPanelComponent {
readonly categorySortMode = signal<PortalCategorySortMode>(
restorePortalCategorySortMode()
);
readonly categorySortLabel = computed(() =>
getPortalCategorySortModeLabel(this.categorySortMode())
readonly categorySortLabelKey = computed(() =>
this.getCategorySortLabelKey(this.categorySortMode())
);
readonly categorySortOptions: ReadonlyArray<{
mode: PortalCategorySortMode;
label: string;
translationKey: string;
icon: string;
}> = [
{
mode: 'server',
label: 'Server sorting',
translationKey: 'WORKSPACE.SORT_SERVER',
icon: 'dns',
},
{
mode: 'name-asc',
label: 'A-Z',
translationKey: 'WORKSPACE.SORT_NAME_ASC',
icon: 'sort_by_alpha',
},
{
mode: 'name-desc',
label: 'Z-A',
icon: 'sort_by_alpha',
translationKey: 'WORKSPACE.SORT_NAME_DESC',
icon: 'arrow_downward',
},
];
readonly categorySortIcon = computed(
() =>
this.categorySortOptions.find(
(option) => option.mode === this.categorySortMode()
)?.icon ?? 'dns'
);
readonly canSearchCategories = computed(
() =>
@@ -450,6 +455,18 @@ export class WorkspaceContextPanelComponent {
}
}
private getCategorySortLabelKey(mode: PortalCategorySortMode): string {
switch (mode) {
case 'name-asc':
return 'WORKSPACE.SORT_NAME_ASC';
case 'name-desc':
return 'WORKSPACE.SORT_NAME_DESC';
case 'server':
default:
return 'WORKSPACE.SORT_SERVER';
}
}
private getCategoryLabel(category: WorkspaceCategoryLike): string {
return category.category_name ?? category.name ?? '';
}