mirror of
https://github.com/nimbold/Firelink.git
synced 2026-08-10 11:37:21 +00:00
fix(ui): harden download properties window
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
getPropertiesTabIndex,
|
||||
getPropertiesTabs,
|
||||
PROPERTIES_TABS_OVERFLOW_BREAKPOINT,
|
||||
shouldUsePropertiesTabOverflow,
|
||||
} from './propertiesTabs';
|
||||
|
||||
describe('Properties tabs', () => {
|
||||
it('keeps torrent and normal-download sections concise and stable', () => {
|
||||
expect(getPropertiesTabs(false)).toEqual(['overview', 'transfer', 'advanced']);
|
||||
expect(getPropertiesTabs(true)).toEqual(['overview', 'files', 'trackers', 'peers', 'options']);
|
||||
});
|
||||
|
||||
it('uses the responsive overflow control at narrow widths', () => {
|
||||
expect(shouldUsePropertiesTabOverflow(PROPERTIES_TABS_OVERFLOW_BREAKPOINT)).toBe(true);
|
||||
expect(shouldUsePropertiesTabOverflow(PROPERTIES_TABS_OVERFLOW_BREAKPOINT + 1)).toBe(false);
|
||||
expect(shouldUsePropertiesTabOverflow(Number.NaN)).toBe(false);
|
||||
});
|
||||
|
||||
it('moves through tabs according to physical direction', () => {
|
||||
const tabs = getPropertiesTabs(true);
|
||||
expect(getPropertiesTabIndex(tabs, 0, 'ArrowRight', 'ltr')).toBe(1);
|
||||
expect(getPropertiesTabIndex(tabs, 0, 'ArrowLeft', 'ltr')).toBe(tabs.length - 1);
|
||||
expect(getPropertiesTabIndex(tabs, 0, 'ArrowRight', 'rtl')).toBe(tabs.length - 1);
|
||||
expect(getPropertiesTabIndex(tabs, 0, 'ArrowLeft', 'rtl')).toBe(1);
|
||||
expect(getPropertiesTabIndex(tabs, 2, 'Home', 'ltr')).toBe(0);
|
||||
expect(getPropertiesTabIndex(tabs, 2, 'End', 'ltr')).toBe(tabs.length - 1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
export type PropertiesTab = 'overview' | 'files' | 'trackers' | 'peers' | 'options' | 'transfer' | 'advanced';
|
||||
|
||||
const TORRENT_PROPERTIES_TABS: readonly PropertiesTab[] = [
|
||||
'overview',
|
||||
'files',
|
||||
'trackers',
|
||||
'peers',
|
||||
'options',
|
||||
];
|
||||
|
||||
const DOWNLOAD_PROPERTIES_TABS: readonly PropertiesTab[] = [
|
||||
'overview',
|
||||
'transfer',
|
||||
'advanced',
|
||||
];
|
||||
|
||||
export const PROPERTIES_TABS_OVERFLOW_BREAKPOINT = 620;
|
||||
|
||||
export const getPropertiesTabs = (isTorrent: boolean): readonly PropertiesTab[] =>
|
||||
isTorrent ? TORRENT_PROPERTIES_TABS : DOWNLOAD_PROPERTIES_TABS;
|
||||
|
||||
export const shouldUsePropertiesTabOverflow = (width: number): boolean =>
|
||||
Number.isFinite(width) && width <= PROPERTIES_TABS_OVERFLOW_BREAKPOINT;
|
||||
|
||||
export const getPropertiesTabIndex = (
|
||||
tabs: readonly PropertiesTab[],
|
||||
currentIndex: number,
|
||||
key: string,
|
||||
direction: 'ltr' | 'rtl',
|
||||
): number => {
|
||||
if (tabs.length === 0) return -1;
|
||||
if (key === 'Home') return 0;
|
||||
if (key === 'End') return tabs.length - 1;
|
||||
if (key !== 'ArrowLeft' && key !== 'ArrowRight') return -1;
|
||||
|
||||
const step = key === 'ArrowRight'
|
||||
? (direction === 'rtl' ? -1 : 1)
|
||||
: (direction === 'rtl' ? 1 : -1);
|
||||
return (currentIndex + step + tabs.length) % tabs.length;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
PROPERTIES_URL_PREVIEW_MAX_LENGTH,
|
||||
shouldOfferPropertiesUrlExpansion,
|
||||
shouldResetPropertiesUrlExpansion,
|
||||
} from './propertiesUrl';
|
||||
|
||||
describe('Properties URL disclosure', () => {
|
||||
it('offers expansion only for URLs longer than the preview budget', () => {
|
||||
expect(shouldOfferPropertiesUrlExpansion('x'.repeat(PROPERTIES_URL_PREVIEW_MAX_LENGTH))).toBe(false);
|
||||
expect(shouldOfferPropertiesUrlExpansion('x'.repeat(PROPERTIES_URL_PREVIEW_MAX_LENGTH + 1))).toBe(true);
|
||||
});
|
||||
|
||||
it('resets expansion when the displayed download changes', () => {
|
||||
expect(shouldResetPropertiesUrlExpansion('download-1', 'download-1')).toBe(false);
|
||||
expect(shouldResetPropertiesUrlExpansion('download-1', 'download-2')).toBe(true);
|
||||
expect(shouldResetPropertiesUrlExpansion(null, 'download-1')).toBe(true);
|
||||
});
|
||||
|
||||
it('resets expansion when the displayed address changes', () => {
|
||||
expect(shouldResetPropertiesUrlExpansion('download-1', 'download-1', 'magnet:?xt=old', 'magnet:?xt=old')).toBe(false);
|
||||
expect(shouldResetPropertiesUrlExpansion('download-1', 'download-1', 'magnet:?xt=old', 'magnet:?xt=new')).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
export const PROPERTIES_URL_PREVIEW_MAX_LENGTH = 180;
|
||||
|
||||
export const shouldOfferPropertiesUrlExpansion = (url: string): boolean =>
|
||||
url.length > PROPERTIES_URL_PREVIEW_MAX_LENGTH;
|
||||
|
||||
export const shouldResetPropertiesUrlExpansion = (
|
||||
previousDownloadId: string | null,
|
||||
nextDownloadId: string | null,
|
||||
previousUrl?: string | null,
|
||||
nextUrl?: string | null,
|
||||
): boolean => previousDownloadId !== nextDownloadId || previousUrl !== nextUrl;
|
||||
@@ -1,5 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
getWindowControlRailWidth,
|
||||
getWindowControlRevealOffset,
|
||||
resolveWindowControlSide,
|
||||
resolveWindowControlStyle,
|
||||
@@ -37,6 +38,13 @@ describe('resolveWindowControlStyle', () => {
|
||||
expect(getWindowControlRevealOffset('minimal')).toBe(104);
|
||||
});
|
||||
|
||||
it('reports the shared titlebar rail width for each control style', () => {
|
||||
expect(getWindowControlRailWidth('macos')).toBe(60);
|
||||
expect(getWindowControlRailWidth('windows')).toBe(138);
|
||||
expect(getWindowControlRailWidth('gnome')).toBe(104);
|
||||
expect(getWindowControlRailWidth('minimal')).toBe(74);
|
||||
});
|
||||
|
||||
it('resolves automatic control placement from the effective document direction', () => {
|
||||
expect(resolveWindowControlSide('auto', 'ltr')).toBe('left');
|
||||
expect(resolveWindowControlSide('auto', 'rtl')).toBe('right');
|
||||
|
||||
@@ -14,9 +14,19 @@ const WINDOW_CONTROL_REVEAL_OFFSETS: Record<ResolvedWindowControlStyle, number>
|
||||
minimal: 104,
|
||||
};
|
||||
|
||||
const WINDOW_CONTROL_RAIL_WIDTHS: Record<ResolvedWindowControlStyle, number> = {
|
||||
macos: 60,
|
||||
windows: 138,
|
||||
gnome: 104,
|
||||
minimal: 74,
|
||||
};
|
||||
|
||||
export const getWindowControlRevealOffset = (style: ResolvedWindowControlStyle): number =>
|
||||
WINDOW_CONTROL_REVEAL_OFFSETS[style];
|
||||
|
||||
export const getWindowControlRailWidth = (style: ResolvedWindowControlStyle): number =>
|
||||
WINDOW_CONTROL_RAIL_WIDTHS[style];
|
||||
|
||||
export const resolveWindowControlSide = (
|
||||
sidebarPosition: SidebarPosition,
|
||||
direction: 'ltr' | 'rtl',
|
||||
|
||||
Reference in New Issue
Block a user