Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions src/components/mui/__tests__/mui-formik-quantity-field.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import React from "react";
import { render, screen, act } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Formik, Form } from "formik";
import "@testing-library/jest-dom";
import MuiFormikQuantityField from "../formik-inputs/mui-formik-quantity-field";

const renderWithFormik = (props, initialValues = { testField: [] }) =>
render(
<Formik initialValues={initialValues} onSubmit={props.onSubmit}>
<Form>
<MuiFormikQuantityField name="testField" {...props} />
<button type="submit">submit</button>
</Form>
</Formik>
);

describe("MuiFormikQuantityField", () => {
it("must accept user input", async () => {
const onSubmit = jest.fn();

renderWithFormik({
label: "some field",
onSubmit
});

const field = screen.getByLabelText("some field");
expect(field).toBeInTheDocument();

const submitButton = screen.getByText("submit");
await act(async () => {
await userEvent.type(field, "12345");
await userEvent.click(submitButton);
});

expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
testField: 12345
}),
expect.anything()
);
});

it("must filter invalid characters", async () => {
const onSubmit = jest.fn();

renderWithFormik({
label: "some field",
onSubmit
});

const field = screen.getByLabelText("some field");
expect(field).toBeInTheDocument();

const submitButton = screen.getByText("submit");
await act(async () => {
await userEvent.type(field, "123eEe45");
await userEvent.click(submitButton);
});

expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
testField: 12345
}),
expect.anything()
);
});
});
30 changes: 30 additions & 0 deletions src/components/mui/formik-inputs/mui-formik-quantity-field.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from "react";
import PropTypes from "prop-types";
import MuiFormikTextField from "./mui-formik-textfield";

const BLOCKED_KEYS = ["e", "E", "+", "-"];

const MuiFormikQuantityField = ({ ...props }) => (
<MuiFormikTextField
type="number"
onKeyDown={(e) => {
if (BLOCKED_KEYS.includes(e.key)) {
e.nativeEvent.preventDefault();
e.nativeEvent.stopImmediatePropagation();
}
}}
inputProps={{
min: 0,
inputMode: "numeric"
}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
Comment on lines +16 to +21
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Props spread can silently override inputProps, losing min and inputMode.

If a consumer passes inputProps={{ max: 100 }}, it will completely replace the component's inputProps, losing min: 0 and inputMode: "numeric". Consider destructuring inputProps and merging it.

🔧 Suggested fix
-const MuiFormikQuantityField = ({ ...props }) => (
+const MuiFormikQuantityField = ({ inputProps: customInputProps, ...props }) => (
   <MuiFormikTextField
     type="number"
     onKeyDown={(e) => {
       if (BLOCKED_KEYS.includes(e.key)) {
-        e.nativeEvent.preventDefault();
-        e.nativeEvent.stopImmediatePropagation();
+        e.preventDefault();
       }
     }}
     inputProps={{
       min: 0,
-      inputMode: "numeric"
+      inputMode: "numeric",
+      ...customInputProps
     }}
     // eslint-disable-next-line react/jsx-props-no-spreading
     {...props}
   />
 );
🤖 Prompt for AI Agents
In `@src/components/mui/formik-inputs/mui-formik-quantity-field.js` around lines
16 - 21, The component currently spreads {...props} after passing inputProps={{
min: 0, inputMode: "numeric" }}, which allows a consumer-provided inputProps to
fully overwrite those defaults; update the component to destructure inputProps
from props (e.g., const { inputProps: userInputProps, ...rest } = props) and
merge them with the defaults (preserving min: 0 and inputMode: "numeric") before
spreading the rest of props and the merged inputProps into the input; reference
the existing inputProps usage and the props spread in
mui-formik-quantity-field.js to locate where to perform the merge.

/>
);

MuiFormikQuantityField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string
};

export default MuiFormikQuantityField;
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
formMetafieldsValidation
} from "../../../utils/yup";
import AdditionalInputList from "../../../components/mui/formik-inputs/additional-input/additional-input-list";
import MuiFormikQuantityField from "../../../components/mui/formik-inputs/mui-formik-quantity-field";

const SponsorItemDialog = ({
open,
Expand Down Expand Up @@ -226,7 +227,7 @@ const SponsorItemDialog = ({
<InputLabel htmlFor="default_quantity">
{T.translate("edit_inventory_item.default_quantity")}
</InputLabel>
<MuiFormikTextField
<MuiFormikQuantityField
variant="outlined"
name="default_quantity"
formik={formik}
Expand All @@ -239,7 +240,7 @@ const SponsorItemDialog = ({
"edit_inventory_item.quantity_limit_per_sponsor"
)}
</InputLabel>
<MuiFormikTextField
<MuiFormikQuantityField
variant="outlined"
name="quantity_limit_per_sponsor"
formik={formik}
Expand All @@ -250,7 +251,7 @@ const SponsorItemDialog = ({
<InputLabel htmlFor="quantity_limit_per_show">
{T.translate("edit_inventory_item.quantity_limit_per_show")}
</InputLabel>
<MuiFormikTextField
<MuiFormikQuantityField
variant="outlined"
name="quantity_limit_per_show"
formik={formik}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import useScrollToError from "../../../../hooks/useScrollToError";
import MuiFormikUpload from "../../../../components/mui/formik-inputs/mui-formik-upload";
import MuiFormikPriceField from "../../../../components/mui/formik-inputs/mui-formik-pricefield";
import FormikTextEditor from "../../../../components/inputs/formik-text-editor";
import MuiFormikQuantityField from "../../../../components/mui/formik-inputs/mui-formik-quantity-field";

const buildInitialValues = (data) => ({ ...data });

Expand All @@ -41,7 +42,9 @@ const SponsorFormItemForm = ({ initialValues, onSubmit }) => {
early_bird_rate: decimalValidation(),
standard_rate: decimalValidation(),
onsite_rate: decimalValidation(),
default_quantity: positiveNumberValidation(),
default_quantity: positiveNumberValidation().required(
T.translate("validation.required")
),
quantity_limit_per_sponsor: positiveNumberValidation(),
quantity_limit_per_show: positiveNumberValidation(),
meta_fields: formMetafieldsValidation()
Expand Down Expand Up @@ -119,7 +122,7 @@ const SponsorFormItemForm = ({ initialValues, onSubmit }) => {
/>
</Grid2>
<Grid2 size={4}>
<MuiFormikTextField
<MuiFormikQuantityField
name="quantity_limit_per_show"
label={T.translate(
"sponsor_form_item_list.edit_item.quantity_limit_per_show"
Expand All @@ -130,7 +133,7 @@ const SponsorFormItemForm = ({ initialValues, onSubmit }) => {
/>
</Grid2>
<Grid2 size={4}>
<MuiFormikTextField
<MuiFormikQuantityField
name="quantity_limit_per_sponsor"
label={T.translate(
"sponsor_form_item_list.edit_item.quantity_limit_per_sponsor"
Expand All @@ -141,7 +144,7 @@ const SponsorFormItemForm = ({ initialValues, onSubmit }) => {
/>
</Grid2>
<Grid2 size={4}>
<MuiFormikTextField
<MuiFormikQuantityField
name="default_quantity"
label={T.translate(
"sponsor_form_item_list.edit_item.default_quantity"
Expand Down