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
59 changes: 59 additions & 0 deletions src/components/mui/__tests__/mui-formik-file-size-field.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
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 MuiFormikFilesizeField from "../formik-inputs/mui-formik-file-size-field";
import { BYTES_PER_MB } from "../../../utils/constants";

const renderWithFormik = (props, initialValues = { max_file_size: 0 }) =>
render(
<Formik initialValues={initialValues} onSubmit={props.onSubmit}>
<Form>
<MuiFormikFilesizeField name="max_file_size" {...props} />
<button type="submit">submit</button>
</Form>
</Formik>
);

describe("MuiFormikFilesizeField", () => {
describe("display and store", () => {
it("converts MB input to bytes", async () => {
const onSubmit = jest.fn();
renderWithFormik({
label: "Max File Size",
onSubmit
});

const field = screen.getByLabelText("Max File Size");
const submitButton = screen.getByText("submit");

await act(async () => {
await userEvent.clear(field); // field initializes with 0
await userEvent.type(field, "10");
await userEvent.click(submitButton);
});

expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({
max_file_size: 10 * BYTES_PER_MB
}),
expect.anything()
);
});

it("displays bytes as MB", async () => {
const onSubmit = jest.fn();
renderWithFormik(
{
label: "Max File Size",
onSubmit
},
{ max_file_size: 15_728_640 } // 15 * 1_048_576
);

const field = screen.getByLabelText("Max File Size");
expect(field).toHaveValue(15);
});
});
});
64 changes: 64 additions & 0 deletions src/components/mui/formik-inputs/mui-formik-file-size-field.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from "react";
import PropTypes from "prop-types";
import { InputAdornment } from "@mui/material";
import { useField } from "formik";
import MuiFormikTextField from "./mui-formik-textfield";
import { BYTES_PER_MB } from "../../../utils/constants";

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

const MuiFormikFilesizeField = ({ name, label, ...props }) => {
const [field, meta, helpers] = useField(name);

const displayValue =
field.value != null ? Math.floor(field.value / BYTES_PER_MB) : 0;

const emptyValue = meta.initialValue === null ? null : 0;

const handleChange = (e) => {
const mbValue = e.target.value;

if (mbValue === "") {
helpers.setValue(emptyValue);
return;
}

const bytes = Number(mbValue) * BYTES_PER_MB;
helpers.setValue(bytes);
};

return (
<MuiFormikTextField
name={name}
label={label}
type="number"
value={displayValue}

Choose a reason for hiding this comment

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

do we need this? formik will pick up the value with the name

Copy link
Author

Choose a reason for hiding this comment

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

this is required to display a simpler value to the user (MB) instead of the value sent and received from the API (bytes)

onChange={handleChange}
slotProps={{
input: {
endAdornment: <InputAdornment position="end">MB</InputAdornment>
}
}}
onKeyDown={(e) => {
if (BLOCKED_KEYS.includes(e.key)) {
e.nativeEvent.preventDefault();
e.nativeEvent.stopImmediatePropagation();
}
}}
inputProps={{
min: 0,
inputMode: "numeric",
step: 1
}}
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
);
};

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

export default MuiFormikFilesizeField;
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Grid2, Divider, InputLabel } from "@mui/material";
import MuiFormikTextField from "../../../../../components/mui/formik-inputs/mui-formik-textfield";
import MuiFormikDatepicker from "../../../../../components/mui/formik-inputs/mui-formik-datepicker";
import MuiFormikRadioGroup from "../../../../../components/mui/formik-inputs/mui-formik-radio-group";
import MuiFormikFilesizeField from "../../../../../components/mui/formik-inputs/mui-formik-file-size-field";
import { PAGE_MODULES_MEDIA_TYPES } from "../../../../../utils/constants";
import MuiFormikAsyncAutocomplete from "../../../../../components/mui/formik-inputs/mui-formik-async-select";
import { queryMediaFileTypes } from "../../../../../actions/media-file-type-actions";
Expand Down Expand Up @@ -67,9 +68,8 @@ const MediaRequestModule = ({ baseName, index }) => {
<InputLabel htmlFor={buildFieldName("max_file_size")}>
{T.translate("page_template_list.page_crud.max_file_size")}
</InputLabel>
<MuiFormikTextField
<MuiFormikFilesizeField
name={buildFieldName("max_file_size")}
type="number"
margin="none"
fullWidth
/>
Expand Down
4 changes: 3 additions & 1 deletion src/utils/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ export const LANGUAGE_CODE_LENGTH = 2;

export const SLICE_TICKET_NUMBER = -15;

export const BYTES_PER_MB = 1_048_576; // 1024 * 1024

export const MARKETING_SETTING_TYPE_TEXT = "TEXT";
export const MARKETING_SETTING_TYPE_TEXTAREA = "TEXTAREA";
export const MARKETING_SETTING_TYPE_FILE = "FILE";
Expand Down Expand Up @@ -256,7 +258,7 @@ export const PURCHASE_STATUS = {
PENDING: "Pending",
PAID: "Paid",
CANCELLED: "Cancelled"
}
};

export const SPONSOR_USER_ASSIGNMENT_TYPE = {
EXISTING: "existing",
Expand Down