mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-03-21 18:04:39 -05:00
New SendouForm fix 2 user reported bugs
This commit is contained in:
parent
400f5ce301
commit
562cfab0d0
|
|
@ -86,11 +86,12 @@ export async function findVods({
|
|||
query = query.where("VideoMatch.stageId", "=", stageId);
|
||||
}
|
||||
}
|
||||
if (typeof weapon === "number") {
|
||||
if (weapon) {
|
||||
query = query.where(
|
||||
"VideoMatchPlayer.weaponSplId",
|
||||
"in",
|
||||
weaponIdToArrayWithAlts(weapon),
|
||||
// TODO: temporary fix until we have a proper search params parsing in place
|
||||
weaponIdToArrayWithAlts(Number(weapon) as MainWeaponId),
|
||||
);
|
||||
}
|
||||
const result = await query
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { userEvent } from "vitest/browser";
|
|||
import { render } from "vitest-browser-react";
|
||||
import { FormField } from "~/form/FormField";
|
||||
import type { WeaponPoolItem } from "~/form/fields/WeaponPoolFormField";
|
||||
import { SendouForm } from "~/form/SendouForm";
|
||||
import { SendouForm, useFormFieldContext } from "~/form/SendouForm";
|
||||
import type {
|
||||
MainWeaponId,
|
||||
ModeShort,
|
||||
|
|
@ -216,4 +216,82 @@ describe("VodForm", () => {
|
|||
await expect.element(addButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("setItemField batching", () => {
|
||||
test("updating multiple fields on same array item preserves all updates", async () => {
|
||||
let setItemFieldRef: ((field: string, value: unknown) => void) | null =
|
||||
null;
|
||||
|
||||
function CaptureSetItemField() {
|
||||
const { values, setValueFromPrev } = useFormFieldContext();
|
||||
const matches = values.matches as Array<Record<string, unknown>>;
|
||||
|
||||
setItemFieldRef = (field: string, value: unknown) => {
|
||||
setValueFromPrev("matches", (prev) => {
|
||||
const currentArray = (prev ?? []) as Record<string, unknown>[];
|
||||
const newArray = [...currentArray];
|
||||
newArray[0] = { ...currentArray[0], [field]: value };
|
||||
return newArray;
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div data-testid="values">
|
||||
{JSON.stringify({
|
||||
weaponsTeamOne: matches[0]?.weaponsTeamOne,
|
||||
weaponsTeamTwo: matches[0]?.weaponsTeamTwo,
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const router = createMemoryRouter(
|
||||
[
|
||||
{
|
||||
path: "/",
|
||||
element: (
|
||||
<SendouForm
|
||||
title="Test VOD Form"
|
||||
schema={vodFormBaseSchema}
|
||||
defaultValues={createDefaultValues({
|
||||
matches: [
|
||||
{
|
||||
weaponsTeamOne: [],
|
||||
weaponsTeamTwo: [],
|
||||
},
|
||||
],
|
||||
})}
|
||||
>
|
||||
{() => <CaptureSetItemField />}
|
||||
</SendouForm>
|
||||
),
|
||||
},
|
||||
],
|
||||
{ initialEntries: ["/"] },
|
||||
);
|
||||
|
||||
const screen = await render(<RouterProvider router={router} />);
|
||||
|
||||
const teamOneWeapons = [
|
||||
{ id: 0 as MainWeaponId, isFavorite: false },
|
||||
{ id: 10 as MainWeaponId, isFavorite: false },
|
||||
];
|
||||
const teamTwoWeapons = [
|
||||
{ id: 20 as MainWeaponId, isFavorite: false },
|
||||
{ id: 30 as MainWeaponId, isFavorite: false },
|
||||
];
|
||||
|
||||
setItemFieldRef!("weaponsTeamOne", teamOneWeapons);
|
||||
setItemFieldRef!("weaponsTeamTwo", teamTwoWeapons);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
|
||||
const valuesEl = screen.getByTestId("values");
|
||||
const valuesText = valuesEl.element().textContent ?? "";
|
||||
const parsedValues = JSON.parse(valuesText);
|
||||
|
||||
expect(parsedValues.weaponsTeamOne).toEqual(teamOneWeapons);
|
||||
expect(parsedValues.weaponsTeamTwo).toEqual(teamTwoWeapons);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -310,9 +310,15 @@ export function FormField({
|
|||
const itemValues = arrayValue[idx] ?? {};
|
||||
|
||||
const setItemField = (fieldName: string, fieldValue: unknown) => {
|
||||
const newArray = [...arrayValue];
|
||||
newArray[idx] = { ...newArray[idx], [fieldName]: fieldValue };
|
||||
handleChange(newArray);
|
||||
context?.setValueFromPrev(name, (prev) => {
|
||||
const currentArray = (prev ?? []) as Record<string, unknown>[];
|
||||
const newArray = [...currentArray];
|
||||
newArray[idx] = {
|
||||
...currentArray[idx],
|
||||
[fieldName]: fieldValue,
|
||||
};
|
||||
return newArray;
|
||||
});
|
||||
};
|
||||
|
||||
const remove = () => {
|
||||
|
|
|
|||
|
|
@ -1117,5 +1117,34 @@ describe("SendouForm", () => {
|
|||
.element(screen.getByText("This field is required"))
|
||||
.toBeVisible();
|
||||
});
|
||||
|
||||
test("setItemField batches multiple field updates correctly", async () => {
|
||||
const schema = z.object({
|
||||
members: array({
|
||||
label: "labels.members",
|
||||
min: 1,
|
||||
max: 10,
|
||||
field: fieldset({
|
||||
fields: z.object({
|
||||
name: textFieldOptional({ label: "labels.name", maxLength: 100 }),
|
||||
bio: textFieldOptional({ label: "labels.bio", maxLength: 100 }),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
const screen = await renderForm(schema, {
|
||||
defaultValues: { members: [{ name: "", bio: "" }] },
|
||||
});
|
||||
|
||||
const inputA = screen.getByLabelText("Name");
|
||||
const inputB = screen.getByLabelText("Bio");
|
||||
|
||||
await userEvent.type(inputA.element(), "Value A");
|
||||
await userEvent.type(inputB.element(), "Value B");
|
||||
|
||||
await expect.element(inputA).toHaveValue("Value A");
|
||||
await expect.element(inputB).toHaveValue("Value B");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ export interface FormContextValue<T extends z.ZodRawShape = z.ZodRawShape> {
|
|||
onFieldChange?: (name: string, newValue: unknown) => void;
|
||||
values: Record<string, unknown>;
|
||||
setValue: (name: string, value: unknown) => void;
|
||||
setValueFromPrev: (name: string, updater: (prev: unknown) => unknown) => void;
|
||||
revalidateAll: (updatedValues: Record<string, unknown>) => void;
|
||||
submitToServer: (values: Record<string, unknown>) => void;
|
||||
fetcherState: "idle" | "loading" | "submitting";
|
||||
|
|
@ -160,6 +161,17 @@ export function SendouForm<T extends z.ZodRawShape>({
|
|||
}
|
||||
};
|
||||
|
||||
const setValueFromPrev = (
|
||||
name: string,
|
||||
updater: (prev: unknown) => unknown,
|
||||
) => {
|
||||
setValues((prevValues) => {
|
||||
const prevValue = prevValues[name];
|
||||
const newValue = updater(prevValue);
|
||||
return { ...prevValues, [name]: newValue };
|
||||
});
|
||||
};
|
||||
|
||||
const validateAndPrepare = (): boolean => {
|
||||
setHasSubmitted(true);
|
||||
setVisibleServerErrors({});
|
||||
|
|
@ -286,6 +298,7 @@ export function SendouForm<T extends z.ZodRawShape>({
|
|||
revalidateAll,
|
||||
values,
|
||||
setValue,
|
||||
setValueFromPrev,
|
||||
submitToServer,
|
||||
fetcherState: fetcher.state,
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user