mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-02 02:34:42 +08:00
fix(sync): sync cloud-sync parent checkbox with selection count
This commit is contained in:
@@ -341,7 +341,7 @@ function confirmSelection() {
|
||||
<div v-if="catalog" class="min-h-0 flex-1 space-y-2 overflow-y-auto px-5 py-4">
|
||||
<details open class="border-b pb-2">
|
||||
<summary class="flex cursor-pointer list-none items-center gap-2 py-2 text-sm font-medium">
|
||||
<input :checked="allSelected('connections', catalog.connections)" type="checkbox" class="size-4 accent-primary" @click.stop.prevent="toggleAll('connections', catalog.connections)" />
|
||||
<input :checked="allSelected('connections', catalog.connections)" :disabled="!catalog.connections.length" type="checkbox" class="size-4 accent-primary" @click.stop @change="toggleAll('connections', catalog.connections)" />
|
||||
<span>{{ t("settings.syncSelectionConnections") }}</span>
|
||||
<span class="ml-auto text-xs text-muted-foreground">{{ selection.connections?.length ?? 0 }}/{{ catalog.connections.length }}</span>
|
||||
</summary>
|
||||
@@ -362,7 +362,7 @@ function confirmSelection() {
|
||||
|
||||
<details open class="border-b pb-2">
|
||||
<summary class="flex cursor-pointer list-none items-center gap-2 py-2 text-sm font-medium">
|
||||
<input :checked="allSelected('tunnelProfiles', catalog.tunnelProfiles)" type="checkbox" class="size-4 accent-primary" @click.stop.prevent="toggleAll('tunnelProfiles', catalog.tunnelProfiles)" />
|
||||
<input :checked="allSelected('tunnelProfiles', catalog.tunnelProfiles)" :disabled="!catalog.tunnelProfiles.length" type="checkbox" class="size-4 accent-primary" @click.stop @change="toggleAll('tunnelProfiles', catalog.tunnelProfiles)" />
|
||||
<span>{{ t("settings.syncSelectionTunnels") }}</span>
|
||||
<span class="ml-auto text-xs text-muted-foreground">{{ selection.tunnelProfiles?.length ?? 0 }}/{{ catalog.tunnelProfiles.length }}</span>
|
||||
</summary>
|
||||
|
||||
+72
@@ -109,3 +109,75 @@ describe.each([
|
||||
expect(root.textContent).not.toContain(actionLabel);
|
||||
});
|
||||
});
|
||||
|
||||
describe.each(["restore", "upload"] as const)("CloudSyncSelectionDialog %s category checkboxes", (mode) => {
|
||||
it.each(["connections", "tunnelProfiles"] as const)("disables the %s parent checkbox when the category is empty", async (category) => {
|
||||
root = document.createElement("div");
|
||||
document.body.append(root);
|
||||
app = createApp(CloudSyncSelectionDialog, {
|
||||
open: true,
|
||||
mode,
|
||||
catalog: { ...catalog, [category]: [] },
|
||||
});
|
||||
app.mount(root);
|
||||
await nextTick();
|
||||
|
||||
const section = root.querySelectorAll("details")[category === "connections" ? 0 : 1];
|
||||
const parent = section.querySelector<HTMLInputElement>("summary input")!;
|
||||
expect(parent.disabled).toBe(true);
|
||||
parent.click();
|
||||
await nextTick();
|
||||
expect(parent.checked).toBe(false);
|
||||
expect(section.textContent).toContain("0/0");
|
||||
expect(section.open).toBe(true);
|
||||
});
|
||||
|
||||
it.each(["connections", "tunnelProfiles"] as const)("keeps the %s parent checkbox in sync after select-all clicks", async (category) => {
|
||||
const items = Array.from({ length: 5 }, (_, index) => ({ id: `item-${index}`, label: `Item ${index}` }));
|
||||
root = document.createElement("div");
|
||||
document.body.append(root);
|
||||
app = createApp(CloudSyncSelectionDialog, {
|
||||
open: true,
|
||||
mode,
|
||||
catalog: { ...catalog, [category]: items },
|
||||
});
|
||||
app.mount(root);
|
||||
await nextTick();
|
||||
|
||||
const section = root.querySelectorAll("details")[category === "connections" ? 0 : 1];
|
||||
const parent = section.querySelector<HTMLInputElement>("summary input")!;
|
||||
const children = Array.from(section.querySelectorAll<HTMLInputElement>("label input"));
|
||||
expect(parent.checked).toBe(true);
|
||||
|
||||
let clickEvent: MouseEvent | undefined;
|
||||
parent.addEventListener("click", (event) => {
|
||||
clickEvent = event;
|
||||
});
|
||||
parent.click();
|
||||
await nextTick();
|
||||
// Canceling native checkbox activation rolls back checked in browsers.
|
||||
// happy-dom does not emulate that rollback, so also inspect the event.
|
||||
expect(clickEvent?.defaultPrevented).toBe(false);
|
||||
expect(section.textContent).toContain("0/5");
|
||||
expect(parent.checked).toBe(false);
|
||||
expect(children.every((child) => !child.checked)).toBe(true);
|
||||
expect(section.open).toBe(true);
|
||||
|
||||
parent.click();
|
||||
await nextTick();
|
||||
expect(section.textContent).toContain("5/5");
|
||||
expect(parent.checked).toBe(true);
|
||||
expect(children.every((child) => child.checked)).toBe(true);
|
||||
|
||||
children[0].click();
|
||||
await nextTick();
|
||||
expect(section.textContent).toContain("4/5");
|
||||
expect(parent.checked).toBe(false);
|
||||
|
||||
parent.click();
|
||||
await nextTick();
|
||||
expect(section.textContent).toContain("5/5");
|
||||
expect(parent.checked).toBe(true);
|
||||
expect(children.every((child) => child.checked)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user