release: publish v2.0.20 selection and sidebar refinements
Complete zero-width sidebar collapse with synchronized bidirectional motion, add package-row selection and guarded double-click disclosure, render continuous selection edges across nested package boundaries, expand regression coverage, and update the public English changelog and package metadata for v2.0.20.
This commit is contained in:
@@ -115,6 +115,47 @@ describe("desktop shell", () => {
|
||||
expect(html).not.toContain("data-ui-region=\"footer\"");
|
||||
});
|
||||
|
||||
it("collapses a populated sidebar to zero width while preserving the edge toggle", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<AppShell
|
||||
activeView="downloads"
|
||||
onViewChange={() => {}}
|
||||
sidebar={<div>Filter</div>}
|
||||
sidebarStatus={<div>2 Downloads</div>}
|
||||
headerActions={null}
|
||||
toolbar={null}
|
||||
footer={null}
|
||||
contextInfo={null}
|
||||
sidebarCollapsed
|
||||
onSidebarCollapsedChange={() => {}}
|
||||
>
|
||||
<div>Downloads</div>
|
||||
</AppShell>
|
||||
);
|
||||
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8");
|
||||
|
||||
expect(html).toContain("has-collapsed-sidebar");
|
||||
expect(html).toContain("md-shell-sidebar-panel");
|
||||
expect(html).toContain("Seitenleiste ausklappen");
|
||||
expect(shellCss).toMatch(/\.md-shell\.has-collapsed-sidebar \.md-shell-workspace\s*\{[^}]*grid-template-columns:\s*0 minmax\(0,\s*1fr\);/s);
|
||||
});
|
||||
|
||||
it("slides the complete sidebar panel in both directions with the workspace width", () => {
|
||||
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8");
|
||||
|
||||
expect(shellCss).toMatch(/\.md-shell-workspace\s*\{[^}]*transition:\s*grid-template-columns 520ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\);/s);
|
||||
expect(shellCss).toMatch(/\.md-shell-sidebar-panel\s*\{[^}]*transform:\s*translate3d\(0, 0, 0\);[^}]*transition:[^}]*transform 520ms cubic-bezier\(0\.22, 0\.76, 0\.22, 1\)/s);
|
||||
expect(shellCss).toMatch(/\.md-shell-sidebar\.is-collapsed \.md-shell-sidebar-panel\s*\{[^}]*transform:\s*translate3d\(calc\(-100% - 8px\), 0, 0\);[^}]*pointer-events:\s*none;/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", () => {
|
||||
const shellCss = readFileSync(new URL("../src/renderer/shell/shell.css", import.meta.url), "utf8").replaceAll("\r\n", "\n");
|
||||
|
||||
|
||||
@@ -709,7 +709,11 @@ describe("downloads view", () => {
|
||||
const css = readFileSync(new URL("../src/renderer/views/downloads/downloads.css", import.meta.url), "utf8");
|
||||
const source = readFileSync(new URL("../src/renderer/views/downloads/DownloadsTable.tsx", import.meta.url), "utf8");
|
||||
|
||||
expect(css).toMatch(/\.downloads-item-row\.is-selected,\s*\.downloads-package-card\.is-selected\s*>\s*\.downloads-package-row\s*\{[^}]*background:\s*color-mix\(in srgb, var\(--ui-success\) 14%, var\(--ui-canvas\)\);[^}]*box-shadow:\s*inset 3px 0 0 var\(--ui-success\);/s);
|
||||
expect(css).toMatch(/\.downloads-item-row,\s*\.downloads-package-row\s*\{[^}]*border-left:\s*3px solid transparent;/s);
|
||||
expect(css).toMatch(/\.downloads-item-row\.is-selected,\s*\.downloads-package-card\.is-selected\s*>\s*\.downloads-package-row\s*\{[^}]*background:\s*color-mix\(in srgb, var\(--ui-success\) 14%, var\(--ui-canvas\)\);[^}]*border-left-color:\s*var\(--ui-success\);/s);
|
||||
expect(css).toMatch(/\.downloads-item-row\.is-selected::before\s*\{[^}]*content:\s*"";[^}]*position:\s*absolute;[^}]*inset:\s*-1px auto -1px -3px;[^}]*width:\s*3px;[^}]*background:\s*var\(--ui-success\);/s);
|
||||
expect(css).toMatch(/\.downloads-package-card\.is-selected::before\s*\{[^}]*content:\s*"";[^}]*position:\s*absolute;[^}]*inset:\s*0 auto auto 0;[^}]*width:\s*3px;[^}]*height:\s*41px;[^}]*background:\s*var\(--ui-success\);/s);
|
||||
expect(css).toMatch(/\.downloads-package-card:has\(\.downloads-package-items-inner\s*>\s*\.downloads-item-row:last-child\.is-selected\)::after\s*\{[^}]*content:\s*"";[^}]*position:\s*absolute;[^}]*inset:\s*auto auto -1px 0;[^}]*width:\s*3px;[^}]*height:\s*1px;[^}]*background:\s*var\(--ui-success\);/s);
|
||||
expect(css).toMatch(/\.downloads-selection-cell\s+input\[type="checkbox"\]\s*\{[^}]*width:\s*18px;[^}]*height:\s*18px;/s);
|
||||
expect(css).toMatch(/\.downloads-hoster-icon\s*\{[^}]*width:\s*18px;[^}]*height:\s*18px;[^}]*object-fit:\s*contain;/s);
|
||||
expect(css).toMatch(/\.downloads-hoster-icon\[data-hoster="rapidgator"\]\s*\{[^}]*transform:\s*translateY\(-4px\) scale\(2\);/s);
|
||||
@@ -996,12 +1000,39 @@ describe("download table row contracts", () => {
|
||||
expect(calls).toEqual(["select"]);
|
||||
});
|
||||
|
||||
it("only collapses a package through its visible disclosure button", () => {
|
||||
it("selects a package on a plain row click", () => {
|
||||
const calls: Array<[string, boolean, boolean]> = [];
|
||||
const model = withRuntime(createInput());
|
||||
const row = model.packageRows[0];
|
||||
const component = PackageCardContent({
|
||||
actions: createActions({
|
||||
onToggleSelection: (id, ctrl, shift) => calls.push([id, ctrl, shift])
|
||||
}),
|
||||
columnOrder: model.columnOrder,
|
||||
editing: false,
|
||||
editingName: "",
|
||||
gridTemplate: model.gridTemplate,
|
||||
packageSpeedBps: 0,
|
||||
row,
|
||||
selectedIds: new Set<string>(),
|
||||
selectedVersion: 1
|
||||
});
|
||||
const packageRow = findElement(component, (element) => element.props["data-download-row-id"] === row.package.id);
|
||||
|
||||
packageRow.props.onClick({ ctrlKey: false, metaKey: false, shiftKey: false });
|
||||
|
||||
expect(calls).toEqual([[row.package.id, false, false]]);
|
||||
});
|
||||
|
||||
it("collapses a package through its disclosure button or a free-row double click", () => {
|
||||
const calls: string[] = [];
|
||||
const model = withRuntime(createInput());
|
||||
const row = model.packageRows[0];
|
||||
const component = PackageCardContent({
|
||||
actions: createActions({ onTogglePackageCollapse: () => calls.push("collapse") }),
|
||||
actions: createActions({
|
||||
onTogglePackageCollapse: () => calls.push("collapse"),
|
||||
onToggleSelection: () => calls.push("select")
|
||||
}),
|
||||
columnOrder: model.columnOrder,
|
||||
editing: false,
|
||||
editingName: "",
|
||||
@@ -1014,14 +1045,95 @@ describe("download table row contracts", () => {
|
||||
const packageRow = findElement(component, (element) => element.props["data-download-row-id"] === row.package.id);
|
||||
const collapseButton = findElement(component, (element) => element.type === "button" && String(element.props.className || "").includes("downloads-collapse-button"));
|
||||
|
||||
packageRow.props.onClick({ ctrlKey: false, metaKey: false, shiftKey: false, target: {}, currentTarget: {} });
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 1, metaKey: false, shiftKey: false, target: {}, currentTarget: {} });
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 2, metaKey: false, shiftKey: false, target: {}, currentTarget: {} });
|
||||
expect(packageRow.props.onDoubleClick).toBeTypeOf("function");
|
||||
packageRow.props.onDoubleClick({
|
||||
ctrlKey: false,
|
||||
currentTarget: {},
|
||||
metaKey: false,
|
||||
preventDefault: () => calls.push("prevent"),
|
||||
shiftKey: false,
|
||||
target: { closest: () => null }
|
||||
});
|
||||
collapseButton.props.onClick({ stopPropagation: () => {} });
|
||||
|
||||
expect(calls).toEqual(["collapse"]);
|
||||
expect(calls).toEqual(["select", "prevent", "collapse", "collapse"]);
|
||||
expect(collapseButton.props["aria-expanded"]).toBe(true);
|
||||
expect(collapseButton.props["aria-controls"]).toBe(`downloads-package-items-${row.package.id}`);
|
||||
});
|
||||
|
||||
it("does not collapse a package when a row double click starts on interactive content", () => {
|
||||
const calls: string[] = [];
|
||||
const model = withRuntime(createInput());
|
||||
const row = model.packageRows[0];
|
||||
const component = PackageCardContent({
|
||||
actions: createActions({
|
||||
onStartPackageRename: () => calls.push("rename"),
|
||||
onTogglePackageCollapse: () => calls.push("collapse"),
|
||||
onToggleSelection: () => calls.push("select")
|
||||
}),
|
||||
columnOrder: model.columnOrder,
|
||||
editing: false,
|
||||
editingName: "",
|
||||
gridTemplate: model.gridTemplate,
|
||||
packageSpeedBps: 0,
|
||||
row,
|
||||
selectedIds: new Set<string>(),
|
||||
selectedVersion: 1
|
||||
});
|
||||
const packageRow = findElement(component, (element) => element.props["data-download-row-id"] === row.package.id);
|
||||
const title = findElement(component, (element) => element.type === "strong" && String(element.props.className || "").includes("downloads-copyable"));
|
||||
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 1, metaKey: false, shiftKey: false });
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 2, metaKey: false, shiftKey: false });
|
||||
expect(packageRow.props.onDoubleClick).toBeTypeOf("function");
|
||||
packageRow.props.onDoubleClick({
|
||||
ctrlKey: false,
|
||||
currentTarget: {},
|
||||
metaKey: false,
|
||||
preventDefault: () => calls.push("prevent"),
|
||||
shiftKey: false,
|
||||
target: { closest: () => ({}) }
|
||||
});
|
||||
title.props.onDoubleClick({ stopPropagation: () => calls.push("stop") });
|
||||
|
||||
expect(calls).toEqual(["select", "stop", "rename"]);
|
||||
});
|
||||
|
||||
it("keeps an already selected package selected during a row double click", () => {
|
||||
const calls: string[] = [];
|
||||
const model = withRuntime(createInput());
|
||||
const row = model.packageRows[0];
|
||||
const component = PackageCardContent({
|
||||
actions: createActions({
|
||||
onTogglePackageCollapse: () => calls.push("collapse"),
|
||||
onToggleSelection: () => calls.push("select")
|
||||
}),
|
||||
columnOrder: model.columnOrder,
|
||||
editing: false,
|
||||
editingName: "",
|
||||
gridTemplate: model.gridTemplate,
|
||||
packageSpeedBps: 0,
|
||||
row,
|
||||
selectedIds: new Set([row.package.id]),
|
||||
selectedVersion: 1
|
||||
});
|
||||
const packageRow = findElement(component, (element) => element.props["data-download-row-id"] === row.package.id);
|
||||
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 1, metaKey: false, shiftKey: false });
|
||||
packageRow.props.onClick({ ctrlKey: false, detail: 2, metaKey: false, shiftKey: false });
|
||||
packageRow.props.onDoubleClick({
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
preventDefault: () => calls.push("prevent"),
|
||||
shiftKey: false,
|
||||
target: { closest: () => null }
|
||||
});
|
||||
|
||||
expect(calls).toEqual(["prevent", "collapse"]);
|
||||
});
|
||||
|
||||
it("keeps the complete package status and audio details in the tooltip", () => {
|
||||
const audioPackage = {
|
||||
...pkg("audio-package", "Audio package", ["audio-item"]),
|
||||
|
||||
@@ -46,7 +46,7 @@ describe("responsive shell mode", () => {
|
||||
expect(html).toContain("md-shell is-full");
|
||||
expect(css).toContain(".md-shell.is-compact");
|
||||
expect(css).toContain(".md-shell.is-minimum");
|
||||
expect(css).toMatch(/grid-template-columns:\s*56px minmax\(0,\s*1fr\)/);
|
||||
expect(css).toMatch(/\.md-shell\.has-collapsed-sidebar \.md-shell-workspace\s*\{[^}]*grid-template-columns:\s*0 minmax\(0,\s*1fr\);/s);
|
||||
});
|
||||
|
||||
it("keeps responsive rail content hidden behind a visible expand control", () => {
|
||||
|
||||
Reference in New Issue
Block a user