fix(ui): honor the application animation setting globally
This commit is contained in:
@@ -1122,12 +1122,13 @@ export function appendBandwidthSample(
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface BandwidthChartProps {
|
interface BandwidthChartProps {
|
||||||
|
animationsEnabled: boolean;
|
||||||
running: boolean;
|
running: boolean;
|
||||||
paused: boolean;
|
paused: boolean;
|
||||||
speedHistoryRef: React.MutableRefObject<{ time: number; speed: number }[]>;
|
speedHistoryRef: React.MutableRefObject<{ time: number; speed: number }[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const BandwidthChart = memo(function BandwidthChart({ running, paused, speedHistoryRef }: BandwidthChartProps): ReactElement {
|
const BandwidthChart = memo(function BandwidthChart({ animationsEnabled, running, paused, speedHistoryRef }: BandwidthChartProps): ReactElement {
|
||||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
@@ -1260,12 +1261,11 @@ const BandwidthChart = memo(function BandwidthChart({ running, paused, speedHist
|
|||||||
if (!running || paused) {
|
if (!running || paused) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const reducedMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
drawChart();
|
drawChart();
|
||||||
}, reducedMotion ? 1000 : 250);
|
}, animationsEnabled ? 250 : 1000);
|
||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, [drawChart, running, paused]);
|
}, [animationsEnabled, drawChart, running, paused]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleResize = () => {
|
const handleResize = () => {
|
||||||
@@ -5347,7 +5347,7 @@ export function App(): ReactElement {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`md-runtime-root${dragOver ? " drag-over" : ""}${tab === "settings" ? " settings-active" : ""}`}
|
className={`md-runtime-root${snapshot.settings.animatePackageDisclosure ? " is-runtime-motion-enabled" : " is-runtime-motion-disabled"}${dragOver ? " drag-over" : ""}${tab === "settings" ? " settings-active" : ""}`}
|
||||||
onDragEnter={(event) => {
|
onDragEnter={(event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const hasFiles = event.dataTransfer.types.includes("Files");
|
const hasFiles = event.dataTransfer.types.includes("Files");
|
||||||
@@ -5373,6 +5373,7 @@ export function App(): ReactElement {
|
|||||||
>
|
>
|
||||||
<AppShell
|
<AppShell
|
||||||
activeView={tab}
|
activeView={tab}
|
||||||
|
animationsEnabled={snapshot.settings.animatePackageDisclosure}
|
||||||
contextInfo={tab === "downloads" ? (
|
contextInfo={tab === "downloads" ? (
|
||||||
<div>
|
<div>
|
||||||
Paket- und Linkstatus werden laufend aktualisiert. Auswahl, Reihenfolge und aktive Filter bleiben beim Ansichtswechsel erhalten.
|
Paket- und Linkstatus werden laufend aktualisiert. Auswahl, Reihenfolge und aktive Filter bleiben beim Ansichtswechsel erhalten.
|
||||||
@@ -5730,7 +5731,7 @@ export function App(): ReactElement {
|
|||||||
{tab === "statistics" && (
|
{tab === "statistics" && (
|
||||||
<StatisticsContent
|
<StatisticsContent
|
||||||
actions={statisticsActions}
|
actions={statisticsActions}
|
||||||
chart={<BandwidthChart running={snapshot.session.running} paused={snapshot.session.paused} speedHistoryRef={speedHistoryRef} />}
|
chart={<BandwidthChart animationsEnabled={snapshot.settings.animatePackageDisclosure} running={snapshot.session.running} paused={snapshot.session.paused} speedHistoryRef={speedHistoryRef} />}
|
||||||
model={statisticsViewModel}
|
model={statisticsViewModel}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ function getServerShellMode(): ResponsiveShellMode {
|
|||||||
|
|
||||||
export interface AppShellProps {
|
export interface AppShellProps {
|
||||||
activeView: MainView;
|
activeView: MainView;
|
||||||
|
animationsEnabled?: boolean;
|
||||||
onViewChange: (view: MainView) => void;
|
onViewChange: (view: MainView) => void;
|
||||||
sidebar: ReactNode;
|
sidebar: ReactNode;
|
||||||
sidebarStatus: ReactNode;
|
sidebarStatus: ReactNode;
|
||||||
@@ -43,6 +44,7 @@ export interface AppShellProps {
|
|||||||
|
|
||||||
export function AppShell({
|
export function AppShell({
|
||||||
activeView,
|
activeView,
|
||||||
|
animationsEnabled = true,
|
||||||
onViewChange,
|
onViewChange,
|
||||||
sidebar,
|
sidebar,
|
||||||
sidebarStatus,
|
sidebarStatus,
|
||||||
@@ -74,7 +76,7 @@ export function AppShell({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`md-shell is-${responsiveMode}${hasSidebar ? " has-sidebar" : ""}${hasSidebar && effectiveSidebarCollapsed ? " has-collapsed-sidebar" : ""}`}
|
className={`md-shell is-${responsiveMode}${animationsEnabled ? " is-runtime-motion-enabled" : " is-runtime-motion-disabled"}${hasSidebar ? " has-sidebar" : ""}${hasSidebar && effectiveSidebarCollapsed ? " has-collapsed-sidebar" : ""}`}
|
||||||
data-responsive-mode={responsiveMode}
|
data-responsive-mode={responsiveMode}
|
||||||
>
|
>
|
||||||
<AppHeader activeView={activeView} actions={headerActions} onViewChange={onViewChange} />
|
<AppHeader activeView={activeView} actions={headerActions} onViewChange={onViewChange} />
|
||||||
|
|||||||
@@ -55,8 +55,8 @@
|
|||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
color: var(--ui-text);
|
color: var(--ui-text);
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-shell-brand-mark {
|
.md-shell-brand-mark {
|
||||||
@@ -919,50 +919,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.md-dialog-actions,
|
.md-dialog-actions,
|
||||||
.md-update-dialog-actions {
|
.md-update-dialog-actions {
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.md-shell *,
|
|
||||||
.md-shell *::before,
|
|
||||||
.md-shell *::after {
|
|
||||||
animation-duration: 0.01ms !important;
|
|
||||||
animation-iteration-count: 1 !important;
|
|
||||||
scroll-behavior: auto !important;
|
|
||||||
transition-duration: 0.01ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-shell-workspace {
|
|
||||||
transition-duration: 520ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-shell-sidebar-panel {
|
|
||||||
transition-duration: 260ms, 520ms, 0s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-shell-navigation::before {
|
|
||||||
transition-duration: 420ms, 420ms, 420ms, 120ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-application-menu-tree .menu-dropdown {
|
|
||||||
transition-property: opacity, transform, visibility !important;
|
|
||||||
transition-duration: 150ms, 180ms, 0s !important;
|
|
||||||
transition-timing-function: ease, cubic-bezier(0.2, 0.8, 0.2, 1), linear !important;
|
|
||||||
transition-delay: 0s, 0s, 180ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-application-menu-tree .menu-dropdown.is-open {
|
|
||||||
transition-delay: 0s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-application-menu-tree .menu-submenu-dropdown {
|
|
||||||
transition-duration: 0.01ms !important;
|
|
||||||
transition-delay: 0s !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-application-menu-tree .menu-submenu-dropdown.is-open {
|
|
||||||
transition-delay: 0s !important;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,15 +89,7 @@
|
|||||||
transform: translate3d(var(--ui-sliding-selection-x, 0px), 0, 0);
|
transform: translate3d(var(--ui-sliding-selection-x, 0px), 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
html,
|
||||||
.ui-sliding-selection::before {
|
|
||||||
transition-property: transform, width, height !important;
|
|
||||||
transition-duration: var(--ui-sliding-selection-duration, 0ms), var(--ui-sliding-selection-duration, 0ms), var(--ui-sliding-selection-duration, 0ms) !important;
|
|
||||||
transition-timing-function: cubic-bezier(0.22, 0.76, 0.22, 1), cubic-bezier(0.22, 0.76, 0.22, 1), cubic-bezier(0.22, 0.76, 0.22, 1) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
html,
|
|
||||||
body,
|
body,
|
||||||
#root {
|
#root {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|||||||
+9
-10
@@ -406,13 +406,12 @@ textarea,
|
|||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
.is-runtime-motion-disabled,
|
||||||
*,
|
.is-runtime-motion-disabled *,
|
||||||
*::before,
|
.is-runtime-motion-disabled *::before,
|
||||||
*::after {
|
.is-runtime-motion-disabled *::after {
|
||||||
animation-duration: 0.01ms !important;
|
animation-duration: 0.01ms !important;
|
||||||
animation-iteration-count: 1 !important;
|
animation-iteration-count: 1 !important;
|
||||||
scroll-behavior: auto !important;
|
scroll-behavior: auto !important;
|
||||||
transition-duration: 0.01ms !important;
|
transition-duration: 0.01ms !important;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -591,25 +591,6 @@
|
|||||||
transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
transition: transform 220ms cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.downloads-virtual-spacer {
|
|
||||||
transition-duration: 1500ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.downloads-virtual-row {
|
|
||||||
transition-duration: 1500ms, 1500ms, 1500ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.downloads-table.is-column-drag-active [data-download-column],
|
|
||||||
.downloads-table.is-column-drag-settling [data-download-column] {
|
|
||||||
transition-duration: 220ms !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.downloads-table.is-column-drag-active [data-column-dragging="true"] {
|
|
||||||
transition-duration: 0s !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.downloads-column-sort {
|
.downloads-column-sort {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
|
|||||||
@@ -499,9 +499,3 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
.history-detail-disclosure:not(.is-history-motion-disabled) {
|
|
||||||
transition-duration: 520ms, 260ms !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+27
-14
@@ -58,7 +58,6 @@ describe("desktop shell", () => {
|
|||||||
expect(source.match(/menu-dropdown\$\{openMenu ===/g)).toHaveLength(3);
|
expect(source.match(/menu-dropdown\$\{openMenu ===/g)).toHaveLength(3);
|
||||||
expect(css).toMatch(/\.menu-dropdown\s*\{[^}]*opacity:\s*0;[^}]*transform:\s*translateY\(-8px\);[^}]*visibility:\s*hidden;/s);
|
expect(css).toMatch(/\.menu-dropdown\s*\{[^}]*opacity:\s*0;[^}]*transform:\s*translateY\(-8px\);[^}]*visibility:\s*hidden;/s);
|
||||||
expect(css).toMatch(/\.menu-dropdown\.is-open\s*\{[^}]*opacity:\s*1;[^}]*transform:\s*translateY\(0\);[^}]*visibility:\s*visible;/s);
|
expect(css).toMatch(/\.menu-dropdown\.is-open\s*\{[^}]*opacity:\s*1;[^}]*transform:\s*translateY\(0\);[^}]*visibility:\s*visible;/s);
|
||||||
expect(shellCss).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.md-application-menu-tree \.menu-dropdown\s*\{[^}]*transition-duration:\s*150ms, 180ms, 0s !important;/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("anchors every top-level application dropdown to the right header edge", () => {
|
it("anchors every top-level application dropdown to the right header edge", () => {
|
||||||
@@ -78,7 +77,6 @@ describe("desktop shell", () => {
|
|||||||
expect(shellCss).toMatch(/\.md-application-menu-tree :where\(\.menu-dropdown, \.menu-submenu-dropdown\)\s*\{[^}]*overflow:\s*visible;/s);
|
expect(shellCss).toMatch(/\.md-application-menu-tree :where\(\.menu-dropdown, \.menu-submenu-dropdown\)\s*\{[^}]*overflow:\s*visible;/s);
|
||||||
expect(shellCss).toMatch(/\.md-application-menu-tree \.menu-submenu-dropdown\s*\{[^}]*right:\s*100%;[^}]*left:\s*auto;[^}]*opacity:\s*0;[^}]*transform:\s*translateX\(8px\);[^}]*visibility:\s*hidden;[^}]*pointer-events:\s*none;[^}]*transition:[^}]*transform 220ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\)/s);
|
expect(shellCss).toMatch(/\.md-application-menu-tree \.menu-submenu-dropdown\s*\{[^}]*right:\s*100%;[^}]*left:\s*auto;[^}]*opacity:\s*0;[^}]*transform:\s*translateX\(8px\);[^}]*visibility:\s*hidden;[^}]*pointer-events:\s*none;[^}]*transition:[^}]*transform 220ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\)/s);
|
||||||
expect(shellCss).toMatch(/\.md-application-menu-tree \.menu-submenu-dropdown\.is-open\s*\{[^}]*opacity:\s*1;[^}]*transform:\s*translateX\(0\);[^}]*visibility:\s*visible;[^}]*pointer-events:\s*auto;/s);
|
expect(shellCss).toMatch(/\.md-application-menu-tree \.menu-submenu-dropdown\.is-open\s*\{[^}]*opacity:\s*1;[^}]*transform:\s*translateX\(0\);[^}]*visibility:\s*visible;[^}]*pointer-events:\s*auto;/s);
|
||||||
expect(shellCss).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.md-application-menu-tree \.menu-submenu-dropdown\s*\{[^}]*transition-duration:\s*0\.01ms !important;[^}]*transition-delay:\s*0s !important;/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses the product asset in the header brand", () => {
|
it("uses the product asset in the header brand", () => {
|
||||||
@@ -101,11 +99,10 @@ describe("desktop shell", () => {
|
|||||||
expect(html).toContain("data-main-view=\"downloads\"");
|
expect(html).toContain("data-main-view=\"downloads\"");
|
||||||
expect(source).toContain("ResizeObserver");
|
expect(source).toContain("ResizeObserver");
|
||||||
expect(css).toMatch(/\.md-shell-navigation::before\s*\{[^}]*transform:\s*translate3d\(var\(--md-navigation-active-x[^}]*transition:\s*transform 420ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\), width 420ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\)/s);
|
expect(css).toMatch(/\.md-shell-navigation::before\s*\{[^}]*transform:\s*translate3d\(var\(--md-navigation-active-x[^}]*transition:\s*transform 420ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\), width 420ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\)/s);
|
||||||
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.md-shell-navigation::before\s*\{[^}]*transition-duration:\s*420ms, 420ms, 420ms, 120ms !important;/);
|
|
||||||
expect(css).toMatch(/\.md-shell-navigation-item\.is-active\s*\{[^}]*background:\s*transparent;/s);
|
expect(css).toMatch(/\.md-shell-navigation-item\.is-active\s*\{[^}]*background:\s*transparent;/s);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("renders context regions without global placeholders", () => {
|
it("renders context regions without global placeholders", () => {
|
||||||
const html = renderToStaticMarkup(
|
const html = renderToStaticMarkup(
|
||||||
<AppShell
|
<AppShell
|
||||||
activeView="downloads"
|
activeView="downloads"
|
||||||
@@ -126,9 +123,32 @@ describe("desktop shell", () => {
|
|||||||
expect(html).toContain("data-ui-region=\"sidebar\"");
|
expect(html).toContain("data-ui-region=\"sidebar\"");
|
||||||
expect(html).toContain("data-ui-region=\"sidebar-status\"");
|
expect(html).toContain("data-ui-region=\"sidebar-status\"");
|
||||||
expect(html).toContain("data-ui-region=\"main\"");
|
expect(html).toContain("data-ui-region=\"main\"");
|
||||||
expect(html).toContain("1–1 von 1");
|
expect(html).toContain("1–1 von 1");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("uses the explicit application animation setting instead of the Windows motion preference", () => {
|
||||||
|
const renderShell = (animationsEnabled: boolean): string => renderToStaticMarkup(
|
||||||
|
<AppShell
|
||||||
|
activeView="downloads"
|
||||||
|
animationsEnabled={animationsEnabled}
|
||||||
|
onViewChange={() => {}}
|
||||||
|
sidebar={null}
|
||||||
|
sidebarStatus={null}
|
||||||
|
headerActions={null}
|
||||||
|
toolbar={null}
|
||||||
|
footer={null}
|
||||||
|
contextInfo={null}
|
||||||
|
sidebarCollapsed={false}
|
||||||
|
onSidebarCollapsedChange={() => {}}
|
||||||
|
>
|
||||||
|
<div>Downloads</div>
|
||||||
|
</AppShell>
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(renderShell(true)).toContain("is-runtime-motion-enabled");
|
||||||
|
expect(renderShell(false)).toContain("is-runtime-motion-disabled");
|
||||||
|
});
|
||||||
|
|
||||||
it("does not reserve a collapsed sidebar column when sidebar slots are empty", () => {
|
it("does not reserve a collapsed sidebar column when sidebar slots are empty", () => {
|
||||||
const html = renderToStaticMarkup(
|
const html = renderToStaticMarkup(
|
||||||
<AppShell
|
<AppShell
|
||||||
@@ -186,13 +206,6 @@ describe("desktop shell", () => {
|
|||||||
expect(shellCss).toMatch(/\.md-shell-sidebar:hover \.md-shell-sidebar-toggle,\s*\.md-shell-sidebar\.is-collapsed \.md-shell-sidebar-toggle,\s*\.md-shell-sidebar-toggle:focus-visible\s*\{[^}]*opacity:\s*1;/s);
|
expect(shellCss).toMatch(/\.md-shell-sidebar:hover \.md-shell-sidebar-toggle,\s*\.md-shell-sidebar\.is-collapsed \.md-shell-sidebar-toggle,\s*\.md-shell-sidebar-toggle:focus-visible\s*\{[^}]*opacity:\s*1;/s);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps the explicitly requested sidebar motion active when Windows animations are disabled", () => {
|
|
||||||
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8");
|
|
||||||
|
|
||||||
expect(shellCss).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.md-shell-workspace\s*\{[^}]*transition-duration:\s*520ms !important;/s);
|
|
||||||
expect(shellCss).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.md-shell-sidebar-panel\s*\{[^}]*transition-duration:\s*260ms, 520ms, 0s !important;/s);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("keeps every view sidebar on the same content axis", () => {
|
it("keeps every view sidebar on the same content axis", () => {
|
||||||
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8").replaceAll("\r\n", "\n");
|
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8").replaceAll("\r\n", "\n");
|
||||||
|
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ describe("Downloadtabellen-Spalten", () => {
|
|||||||
expect(css).toMatch(/\.downloads-table\.is-column-drag-active \[data-download-column\]\s*\{[^}]*transform:\s*translate3d\(var\(--downloads-column-drag-x, 0px\), 0, 0\);[^}]*transition:\s*transform 220ms/s);
|
expect(css).toMatch(/\.downloads-table\.is-column-drag-active \[data-download-column\]\s*\{[^}]*transform:\s*translate3d\(var\(--downloads-column-drag-x, 0px\), 0, 0\);[^}]*transition:\s*transform 220ms/s);
|
||||||
expect(css).toMatch(/\.downloads-table\.is-column-drag-active \[data-column-dragging="true"\]\s*\{[^}]*transition:\s*none;/s);
|
expect(css).toMatch(/\.downloads-table\.is-column-drag-active \[data-column-dragging="true"\]\s*\{[^}]*transition:\s*none;/s);
|
||||||
expect(css).not.toMatch(/\.downloads-table\.is-column-drag-active \[data-column-dragging="true"\]\s*\{[^}]*background:/s);
|
expect(css).not.toMatch(/\.downloads-table\.is-column-drag-active \[data-column-dragging="true"\]\s*\{[^}]*background:/s);
|
||||||
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.downloads-table\.is-column-drag-active \[data-download-column\][^{]*\{[^}]*transition-duration:\s*220ms !important;/s);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("changes package collapse state only through user actions", () => {
|
it("changes package collapse state only through user actions", () => {
|
||||||
@@ -334,8 +333,8 @@ describe("virtualisierte Paketanimation", () => {
|
|||||||
const css = fs.readFileSync(path.join(process.cwd(), "src/renderer/views/downloads/downloads.css"), "utf8");
|
const css = fs.readFileSync(path.join(process.cwd(), "src/renderer/views/downloads/downloads.css"), "utf8");
|
||||||
|
|
||||||
expect(DOWNLOAD_DISCLOSURE_DURATION_MS).toBe(1500);
|
expect(DOWNLOAD_DISCLOSURE_DURATION_MS).toBe(1500);
|
||||||
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.downloads-virtual-spacer\s*\{[^}]*transition-duration:\s*1500ms !important;/s);
|
expect(css).toMatch(/\.downloads-virtual-spacer\s*\{[^}]*transition:\s*height 1500ms/s);
|
||||||
expect(css).toMatch(/@media \(prefers-reduced-motion: reduce\)[\s\S]*\.downloads-virtual-row\s*\{[^}]*transition-duration:\s*1500ms, 1500ms, 1500ms !important;/s);
|
expect(css).toMatch(/\.downloads-virtual-row\s*\{[^}]*transition:\s*height 1500ms[^}]*transform 1500ms[^}]*opacity 1500ms/s);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("schneidet beim Einfahren den gemeinsamen Paketbereich ab statt Unterzeilen zusammenzuziehen", () => {
|
it("schneidet beim Einfahren den gemeinsamen Paketbereich ab statt Unterzeilen zusammenzuziehen", () => {
|
||||||
|
|||||||
@@ -500,14 +500,14 @@ describe("bandwidth chart palette", () => {
|
|||||||
expect(palette).toEqual({ accent: "rgb(74, 222, 128)" });
|
expect(palette).toEqual({ accent: "rgb(74, 222, 128)" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("labels the live chart and slows redraws when reduced motion is requested", () => {
|
it("labels the live chart and follows the explicit application animation setting", () => {
|
||||||
const source = readFileSync(new URL("../src/renderer/App.tsx", import.meta.url), "utf8");
|
const source = readFileSync(new URL("../src/renderer/App.tsx", import.meta.url), "utf8");
|
||||||
const chartBlock = source.slice(source.indexOf("const BandwidthChart"), source.indexOf("interface DownloadSpeedSparklineProps"));
|
const chartBlock = source.slice(source.indexOf("const BandwidthChart"), source.indexOf("interface DownloadSpeedSparklineProps"));
|
||||||
|
|
||||||
expect(chartBlock).toContain('role="img"');
|
expect(chartBlock).toContain('role="img"');
|
||||||
expect(chartBlock).toContain('aria-label="Bandbreitenverlauf der letzten 60 Sekunden"');
|
expect(chartBlock).toContain('aria-label="Bandbreitenverlauf der letzten 60 Sekunden"');
|
||||||
expect(chartBlock).toContain('window.matchMedia("(prefers-reduced-motion: reduce)")');
|
expect(chartBlock).toContain("animationsEnabled ? 250 : 1000");
|
||||||
expect(chartBlock).toContain("reducedMotion ? 1000 : 250");
|
expect(chartBlock).not.toContain("prefers-reduced-motion");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("asks for confirmation before deleting all saved download statistics", () => {
|
it("asks for confirmation before deleting all saved download statistics", () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user