Skip to content

Commit 8bbc259

Browse files
authored
feat: add new formik input for file size, update page media module (#791)
* feat: add new formik input for file size, add it on page modules for media Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com> * fix: add formik getIn to obtain initialValues, avoid undefined values Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com> * fix: simplify component validations, default value 0, adjust mb size constant and tests Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com> * fix: adjust validation for numbers greater than 0 Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com> * fix: adjust validations with BYTES_PER_MB values Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com> --------- Signed-off-by: Tomás Castillo <tcastilloboireau@gmail.com>
1 parent c39c77d commit 8bbc259

File tree

6 files changed

+144
-4
lines changed

6 files changed

+144
-4
lines changed
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import React from "react";
2+
import { render, screen, act } from "@testing-library/react";
3+
import userEvent from "@testing-library/user-event";
4+
import { Formik, Form } from "formik";
5+
import "@testing-library/jest-dom";
6+
import MuiFormikFilesizeField from "../formik-inputs/mui-formik-file-size-field";
7+
import { BYTES_PER_MB } from "../../../utils/constants";
8+
9+
const renderWithFormik = (props, initialValues = { max_file_size: 0 }) =>
10+
render(
11+
<Formik initialValues={initialValues} onSubmit={props.onSubmit}>
12+
<Form>
13+
<MuiFormikFilesizeField name="max_file_size" {...props} />
14+
<button type="submit">submit</button>
15+
</Form>
16+
</Formik>
17+
);
18+
19+
describe("MuiFormikFilesizeField", () => {
20+
describe("display and store", () => {
21+
it("converts MB input to bytes", async () => {
22+
const onSubmit = jest.fn();
23+
renderWithFormik({
24+
label: "Max File Size",
25+
onSubmit
26+
});
27+
28+
const field = screen.getByLabelText("Max File Size");
29+
const submitButton = screen.getByText("submit");
30+
31+
await act(async () => {
32+
await userEvent.clear(field); // field initializes with 0
33+
await userEvent.type(field, "10");
34+
await userEvent.click(submitButton);
35+
});
36+
37+
expect(onSubmit).toHaveBeenCalledWith(
38+
expect.objectContaining({
39+
max_file_size: 10 * BYTES_PER_MB
40+
}),
41+
expect.anything()
42+
);
43+
});
44+
45+
it("displays bytes as MB", async () => {
46+
const onSubmit = jest.fn();
47+
renderWithFormik(
48+
{
49+
label: "Max File Size",
50+
onSubmit
51+
},
52+
{ max_file_size: 15_728_640 } // 15 * 1_048_576
53+
);
54+
55+
const field = screen.getByLabelText("Max File Size");
56+
expect(field).toHaveValue(15);
57+
});
58+
});
59+
});
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import { InputAdornment } from "@mui/material";
4+
import { useField } from "formik";
5+
import MuiFormikTextField from "./mui-formik-textfield";
6+
import { BYTES_PER_MB } from "../../../utils/constants";
7+
8+
const BLOCKED_KEYS = ["e", "E", "+", "-", ".", ","];
9+
10+
const MuiFormikFilesizeField = ({ name, label, ...props }) => {
11+
const [field, meta, helpers] = useField(name);
12+
13+
const displayValue =
14+
field.value != null ? Math.floor(field.value / BYTES_PER_MB) : 0;
15+
16+
const emptyValue = meta.initialValue === null ? null : 0;
17+
18+
const handleChange = (e) => {
19+
const mbValue = e.target.value;
20+
21+
if (mbValue === "") {
22+
helpers.setValue(emptyValue);
23+
return;
24+
}
25+
26+
const bytes = Number(mbValue) * BYTES_PER_MB;
27+
helpers.setValue(bytes);
28+
};
29+
30+
return (
31+
<MuiFormikTextField
32+
name={name}
33+
label={label}
34+
type="number"
35+
value={displayValue}
36+
onChange={handleChange}
37+
slotProps={{
38+
input: {
39+
endAdornment: <InputAdornment position="end">MB</InputAdornment>
40+
}
41+
}}
42+
onKeyDown={(e) => {
43+
if (BLOCKED_KEYS.includes(e.key)) {
44+
e.nativeEvent.preventDefault();
45+
e.nativeEvent.stopImmediatePropagation();
46+
}
47+
}}
48+
inputProps={{
49+
min: 0,
50+
inputMode: "numeric",
51+
step: 1
52+
}}
53+
// eslint-disable-next-line react/jsx-props-no-spreading
54+
{...props}
55+
/>
56+
);
57+
};
58+
59+
MuiFormikFilesizeField.propTypes = {
60+
name: PropTypes.string.isRequired,
61+
label: PropTypes.string
62+
};
63+
64+
export default MuiFormikFilesizeField;

src/i18n/en.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@
9494
"url": "Wrong URL format",
9595
"date": "Wrong date format.",
9696
"after": "'{field1}' must be after '{field2}'.",
97-
"boolean": "Must be a boolean."
97+
"boolean": "Must be a boolean.",
98+
"mib_aligned": "Must be a MiB aligned value"
9899
},
99100
"landing": {
100101
"os_summit_admin": "Show Admin",

src/pages/sponsors-global/page-templates/page-template-popup/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import * as yup from "yup";
2121
import MuiFormikTextField from "../../../../components/mui/formik-inputs/mui-formik-textfield";
2222
import PageModules from "./page-template-modules-form";
2323
import {
24+
BYTES_PER_MB,
2425
PAGES_MODULE_KINDS,
2526
PAGE_MODULES_MEDIA_TYPES
2627
} from "../../../../utils/constants";
@@ -88,6 +89,19 @@ const PageTemplatePopup = ({ pageTemplate, open, onClose, onSave }) => {
8889
type: yup.string().required(T.translate("validation.required")),
8990
upload_deadline: yup.date().required(T.translate("validation.required")),
9091
description: yup.string().required(T.translate("validation.required")),
92+
max_file_size: yup.number().when("type", {
93+
is: PAGE_MODULES_MEDIA_TYPES.FILE,
94+
then: (schema) =>
95+
schema
96+
.min(BYTES_PER_MB, T.translate("validation.number_positive"))
97+
.required(T.translate("validation.required"))
98+
.test(
99+
"mib-aligned",
100+
T.translate("validation.mib_aligned"),
101+
(value) => value == null || value % BYTES_PER_MB === 0
102+
),
103+
otherwise: (schema) => schema.nullable()
104+
}),
91105
file_type_id: yup.object().when("type", {
92106
is: PAGE_MODULES_MEDIA_TYPES.FILE,
93107
then: (schema) => schema.required(T.translate("validation.required")),

src/pages/sponsors-global/page-templates/page-template-popup/modules/page-template-media-request-module.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { Grid2, Divider, InputLabel } from "@mui/material";
66
import MuiFormikTextField from "../../../../../components/mui/formik-inputs/mui-formik-textfield";
77
import MuiFormikDatepicker from "../../../../../components/mui/formik-inputs/mui-formik-datepicker";
88
import MuiFormikRadioGroup from "../../../../../components/mui/formik-inputs/mui-formik-radio-group";
9+
import MuiFormikFilesizeField from "../../../../../components/mui/formik-inputs/mui-formik-file-size-field";
910
import { PAGE_MODULES_MEDIA_TYPES } from "../../../../../utils/constants";
1011
import MuiFormikAsyncAutocomplete from "../../../../../components/mui/formik-inputs/mui-formik-async-select";
1112
import { queryMediaFileTypes } from "../../../../../actions/media-file-type-actions";
@@ -67,9 +68,8 @@ const MediaRequestModule = ({ baseName, index }) => {
6768
<InputLabel htmlFor={buildFieldName("max_file_size")}>
6869
{T.translate("page_template_list.page_crud.max_file_size")}
6970
</InputLabel>
70-
<MuiFormikTextField
71+
<MuiFormikFilesizeField
7172
name={buildFieldName("max_file_size")}
72-
type="number"
7373
margin="none"
7474
fullWidth
7575
/>

src/utils/constants.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ export const LANGUAGE_CODE_LENGTH = 2;
163163

164164
export const SLICE_TICKET_NUMBER = -15;
165165

166+
export const BYTES_PER_MB = 1_048_576; // 1024 * 1024
167+
166168
export const MARKETING_SETTING_TYPE_TEXT = "TEXT";
167169
export const MARKETING_SETTING_TYPE_TEXTAREA = "TEXTAREA";
168170
export const MARKETING_SETTING_TYPE_FILE = "FILE";
@@ -256,7 +258,7 @@ export const PURCHASE_STATUS = {
256258
PENDING: "Pending",
257259
PAID: "Paid",
258260
CANCELLED: "Cancelled"
259-
}
261+
};
260262

261263
export const SPONSOR_USER_ASSIGNMENT_TYPE = {
262264
EXISTING: "existing",

0 commit comments

Comments
 (0)