fix: document apply ordering invariant; visible indeterminate checkbox + test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwxdSt4reTm7Dj1oxRvpP3
This commit is contained in:
@@ -25,6 +25,12 @@ type Provider interface {
|
|||||||
Name() string
|
Name() string
|
||||||
ListZones(ctx context.Context, creds Credentials) ([]Zone, error)
|
ListZones(ctx context.Context, creds Credentials) ([]Zone, error)
|
||||||
GetRecords(ctx context.Context, creds Credentials, zoneID string) ([]model.Record, error)
|
GetRecords(ctx context.Context, creds Credentials, zoneID string) ([]model.Record, error)
|
||||||
|
// ApplyChanges MUST apply cs.Diffs in the order they are given and must
|
||||||
|
// not reorder or group them by Kind. The caller (service.Apply)
|
||||||
|
// deliberately places Delete diffs before Add/Update diffs, because some
|
||||||
|
// providers (e.g. Selectel) reject creating a CNAME on a name where a
|
||||||
|
// conflicting A record still exists. Implementations should apply diffs
|
||||||
|
// sequentially in the given order rather than batching by kind.
|
||||||
ApplyChanges(ctx context.Context, creds Credentials, zoneID string, cs diff.Changeset) error
|
ApplyChanges(ctx context.Context, creds Credentials, zoneID string, cs diff.Changeset) error
|
||||||
// Validate checks the credentials are usable (e.g. a trial auth), so a
|
// Validate checks the credentials are usable (e.g. a trial auth), so a
|
||||||
// bad account is rejected at creation time rather than at first import.
|
// bad account is rejected at creation time rather than at first import.
|
||||||
|
|||||||
@@ -131,3 +131,25 @@ test("select-all header checkbox calls onToggleAllUpdates(true) when clicked whi
|
|||||||
await user.click(screen.getByRole("checkbox", { name: /выбрать все.*updates/i }))
|
await user.click(screen.getByRole("checkbox", { name: /выбрать все.*updates/i }))
|
||||||
expect(onToggleAllUpdates).toHaveBeenCalledWith(true)
|
expect(onToggleAllUpdates).toHaveBeenCalledWith(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("select-all header checkbox is indeterminate when only some update rows are selected", () => {
|
||||||
|
const csWithMultipleUpdates: ChangesetResponse = {
|
||||||
|
updates: [
|
||||||
|
{ key: "A www.example.com.", kind: "update", type: "A", name: "www.example.com.", desired: ["1.1.1.1"], actual: ["9.9.9.9"], readOnly: false },
|
||||||
|
{ key: "A api.example.com.", kind: "update", type: "A", name: "api.example.com.", desired: ["1.1.1.2"], actual: ["9.9.9.8"], readOnly: false },
|
||||||
|
{ key: "A cdn.example.com.", kind: "update", type: "A", name: "cdn.example.com.", desired: ["1.1.1.3"], actual: ["9.9.9.7"], readOnly: false },
|
||||||
|
],
|
||||||
|
prunes: [],
|
||||||
|
readOnly: [],
|
||||||
|
inSyncCount: 0,
|
||||||
|
}
|
||||||
|
renderDiff({
|
||||||
|
changeset: csWithMultipleUpdates,
|
||||||
|
// Partial selection: one of three keys — neither all nor none — is what
|
||||||
|
// must drive the header checkbox into the indeterminate ("mixed") state.
|
||||||
|
selectedUpdates: new Set(["A www.example.com."]),
|
||||||
|
})
|
||||||
|
|
||||||
|
const selectAll = screen.getByRole("checkbox", { name: /выбрать все.*updates/i })
|
||||||
|
expect(selectAll).toHaveAttribute("aria-checked", "mixed")
|
||||||
|
})
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ function Checkbox({ className, ...props }: CheckboxPrimitive.Root.Props) {
|
|||||||
<CheckboxPrimitive.Root
|
<CheckboxPrimitive.Root
|
||||||
data-slot="checkbox"
|
data-slot="checkbox"
|
||||||
className={cn(
|
className={cn(
|
||||||
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary",
|
"peer relative flex size-4 shrink-0 items-center justify-center rounded-[4px] border border-input transition-colors outline-none group-has-disabled/field:opacity-50 after:absolute after:-inset-x-3 after:-inset-y-2 focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 aria-invalid:aria-checked:border-primary dark:bg-input/30 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 data-checked:border-primary data-checked:bg-primary data-checked:text-primary-foreground dark:data-checked:bg-primary data-indeterminate:border-primary data-indeterminate:bg-primary/40 data-indeterminate:text-primary-foreground",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
Reference in New Issue
Block a user