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
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@
"testEnvironment": "jsdom",
"setupFilesAfterEnv": [
"<rootDir>/testSetupFile.js"
]
],
"maxWorkers": 4
}
}
15 changes: 15 additions & 0 deletions src/components/mui/FormItemTable/__tests__/FormItemTable.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,21 @@ jest.mock("../../formik-inputs/mui-formik-select", () => ({
)
}));

jest.mock("../../formik-inputs/mui-formik-select-v2", () => ({
__esModule: true,
default: ({ name, label, options }) => (
<select data-testid={`select-${name}`} name={name}>
<option value="">{label}</option>
{options &&
options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
)
}));

jest.mock("../../formik-inputs/mui-formik-checkbox", () => ({
__esModule: true,
default: ({ name, label }) => (
Expand Down
18 changes: 8 additions & 10 deletions src/components/mui/FormItemTable/components/ItemTableField.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,20 @@
* */

import React from "react";
import { MenuItem } from "@mui/material";
import MuiFormikCheckbox from "../../formik-inputs/mui-formik-checkbox";
import MuiFormikDropdownCheckbox from "../../formik-inputs/mui-formik-dropdown-checkbox";
import MuiFormikDropdownRadio from "../../formik-inputs/mui-formik-dropdown-radio";
import MuiFormikDatepicker from "../../formik-inputs/mui-formik-datepicker";
import MuiFormikTimepicker from "../../formik-inputs/mui-formik-timepicker";
import MuiFormikTextField from "../../formik-inputs/mui-formik-textfield";
import MuiFormikSelect from "../../formik-inputs/mui-formik-select";
import MuiFormikSelectV2 from "../../formik-inputs/mui-formik-select-v2";

const ItemTableField = ({ rowId, field, timeZone, label = "" }) => {
const name = `i-${rowId}-c-${field.class_field}-f-${field.type_id}`;

switch (field.type) {
case "CheckBox":
return <MuiFormikCheckbox name={name} label={label} />;
return <MuiFormikCheckbox name={name} label={label} size="small" />;
case "CheckBoxList":
return (
<MuiFormikDropdownCheckbox
Expand Down Expand Up @@ -71,13 +70,12 @@ const ItemTableField = ({ rowId, field, timeZone, label = "" }) => {
);
case "ComboBox":
return (
<MuiFormikSelect name={name} label={label} size="small">
{field.values.map((v) => (
<MenuItem key={`ddopt-${v.id}`} value={v.id}>
{v.value}
</MenuItem>
))}
</MuiFormikSelect>
<MuiFormikSelectV2
name={name}
label={label}
size="small"
options={field.values.map((v) => ({ value: v.id, label: v.value }))}
/>
);
case "Text":
return (
Expand Down
5 changes: 4 additions & 1 deletion src/components/mui/FormItemTable/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,10 @@ const FormItemTable = ({
{currencyAmountFromCents(row.rates.onsite)}
</TableCell>
{extraColumns.map((exc) => (
<TableCell key={`datacell-${row.form_item_id}-${exc.type_id}`}>
<TableCell
key={`datacell-${row.form_item_id}-${exc.type_id}`}
sx={{ minWidth: 200 }}
>
<ItemTableField
field={exc}
rowId={row.form_item_id}
Expand Down
21 changes: 13 additions & 8 deletions src/components/mui/ItemSettingsModal/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@
import React from "react";
import PropTypes from "prop-types";
import T from "i18n-react/dist/i18n-react";
import Button from "@mui/material/Button";
import Dialog from "@mui/material/Dialog";
import DialogActions from "@mui/material/DialogActions";
import DialogContent from "@mui/material/DialogContent";
import DialogTitle from "@mui/material/DialogTitle";
import { Divider, IconButton, Typography } from "@mui/material";
import {
Button,
Box,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
Divider,
IconButton,
Typography
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import ItemTableField from "../FormItemTable/components/ItemTableField";

Expand Down Expand Up @@ -63,14 +68,14 @@ const ItemSettingsModal = ({ item, timeZone, open, onClose }) => {
}}
/>
{itemFields.map((exc) => (
<React.Fragment key={`item-field-${exc.type_id}`}>
<Box key={`item-field-${exc.type_id}`} sx={{ mb: 2 }}>
<ItemTableField
field={exc}
rowId={item.form_item_id}
timeZone={timeZone}
label={exc.name}
/>
</React.Fragment>
</Box>
))}
</DialogContent>
<DialogActions>
Expand Down
3 changes: 2 additions & 1 deletion src/components/mui/formik-inputs/mui-formik-datepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ const MuiFormikDatepicker = ({ name, label, required, ...props }) => {
onBlur: handleBlur,
error: meta.touched && Boolean(meta.error),
helperText: meta.touched && meta.error,
fullWidth: true
fullWidth: true,
size: "small"
},
day: {
sx: {
Expand Down
20 changes: 18 additions & 2 deletions src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,24 @@ import {
Checkbox,
Divider,
FormControl,
InputLabel,
ListItemText,
MenuItem,
Select
} from "@mui/material";
import { useField } from "formik";
import T from "i18n-react/dist/i18n-react";

const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {
const MuiFormikDropdownCheckbox = ({
name,
label,
options,
placeholder,
...rest
}) => {
const [field, meta, helpers] = useField(name);
const finalPlaceholder =
placeholder || T.translate("general.select_an_option");
const allSelected = options.every(({ value }) =>
field.value?.includes(value)
);
Expand All @@ -46,9 +55,16 @@ const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {

return (
<FormControl fullWidth error={meta.touched && Boolean(meta.error)}>
{label && (
<InputLabel shrink id={`${name}-label`}>
{label}
</InputLabel>
)}
<Select
variant="outlined"
name={name}
label={label}
labelId={`${name}-label`}
multiple
value={field.value || []}
onChange={handleChange}
Expand All @@ -58,7 +74,7 @@ const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {
{...rest}
renderValue={(selected) => {
if (!selected?.length) {
return rest.placeholder || "";
return <em>{finalPlaceholder}</em>;
}
if (allSelected) {
return T.translate("general.all");
Expand Down
16 changes: 15 additions & 1 deletion src/components/mui/formik-inputs/mui-formik-dropdown-radio.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import React from "react";
import {
FormControl,
InputLabel,
ListItemText,
MenuItem,
Radio,
Expand All @@ -22,7 +23,13 @@ import {
import { useField } from "formik";
import T from "i18n-react/dist/i18n-react";

const MuiFormikDropdownRadio = ({ name, options, placeholder, ...rest }) => {
const MuiFormikDropdownRadio = ({
name,
label,
options,
placeholder,
...rest
}) => {
const finalPlaceholder =
placeholder || T.translate("general.select_an_option");
const [field, meta, helpers] = useField(name);
Expand All @@ -33,9 +40,16 @@ const MuiFormikDropdownRadio = ({ name, options, placeholder, ...rest }) => {

return (
<FormControl fullWidth error={meta.touched && Boolean(meta.error)}>
{label && (
<InputLabel shrink id={`${name}-label`}>
{label}
</InputLabel>
)}
<Select
variant="outlined"
name={name}
label={label}
labelId={`${name}-label`}
value={field.value || ""}
onChange={handleChange}
displayEmpty
Expand Down
58 changes: 58 additions & 0 deletions src/components/mui/formik-inputs/mui-formik-select-v2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import React from "react";
import PropTypes from "prop-types";
import T from "i18n-react/dist/i18n-react";
import {
Select,
FormHelperText,
FormControl,
MenuItem,
InputLabel
} from "@mui/material";
import { useField } from "formik";

const MuiFormikSelectV2 = ({ name, label, placeholder, options, ...rest }) => {
const [field, meta] = useField(name);
const finalPlaceholder =
placeholder || T.translate("general.select_an_option");

return (
<FormControl fullWidth error={meta.touched && Boolean(meta.error)}>
{label && <InputLabel id={`${name}-label`}>{label}</InputLabel>}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing shrink prop on InputLabel causes label overlap with placeholder.

When using displayEmpty on Select, the InputLabel should have shrink prop to prevent the label from overlapping the placeholder text. The MuiFormikDropdownCheckbox component correctly uses shrink but this component doesn't.

🐛 Suggested fix
-      {label && <InputLabel id={`${name}-label`}>{label}</InputLabel>}
+      {label && (
+        <InputLabel shrink id={`${name}-label`}>
+          {label}
+        </InputLabel>
+      )}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{label && <InputLabel id={`${name}-label`}>{label}</InputLabel>}
{label && (
<InputLabel shrink id={`${name}-label`}>
{label}
</InputLabel>
)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-select-v2.js` at line 20, The
InputLabel in the MuiFormikSelectV2 component can overlap the placeholder
because it lacks the shrink prop; update the InputLabel rendering (the element
that uses id={`${name}-label`}) to include shrink (e.g., <InputLabel shrink
id={`${name}-label`}> or shrink={true}) so the label is forced to the shrunk
position when using Select with displayEmpty.

<Select
name={name}
label={label}
labelId={`${name}-label`}
displayEmpty
// eslint-disable-next-line react/jsx-props-no-spreading
{...field}
// eslint-disable-next-line react/jsx-props-no-spreading
{...rest}
renderValue={(selected) => {
if (!selected) {
return <em>{finalPlaceholder}</em>;
}
Comment on lines +30 to +33
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Falsy value check may incorrectly show placeholder for valid selections.

The condition !selected will treat 0, "", or other falsy values as "no selection" and display the placeholder. If any option can have 0 or an empty string as its value, the selected option won't display correctly.

🐛 Suggested fix: use explicit null/undefined check
         renderValue={(selected) => {
-          if (!selected) {
+          if (selected === null || selected === undefined) {
             return <em>{finalPlaceholder}</em>;
           }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
renderValue={(selected) => {
if (!selected) {
return <em>{finalPlaceholder}</em>;
}
renderValue={(selected) => {
if (selected === null || selected === undefined) {
return <em>{finalPlaceholder}</em>;
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/mui/formik-inputs/mui-formik-select-v2.js` around lines 30 -
33, In renderValue inside the MUI Formik select component
(mui-formik-select-v2.js), the current falsy check `if (!selected)` wrongly
treats valid values like 0 or "" as empty; change the condition to explicitly
check for null/undefined (e.g., `selected === null || selected === undefined`)
and also handle empty array for multi-selects (`Array.isArray(selected) &&
selected.length === 0`) before returning the placeholder (finalPlaceholder) so
valid falsy selections render correctly.

const selectedOption = options.find(
({ value }) => value === selected
);
return selectedOption ? selectedOption.label : "";
}}
>
{options.map((op) => (
<MenuItem key={`selectop-${op.value}`} value={op.value}>
{op.label}
</MenuItem>
))}
</Select>
{meta.touched && meta.error && (
<FormHelperText>{meta.error}</FormHelperText>
)}
</FormControl>
);
};

MuiFormikSelectV2.propTypes = {
name: PropTypes.string.isRequired,
options: PropTypes.array.isRequired
};

export default MuiFormikSelectV2;
3 changes: 2 additions & 1 deletion src/components/mui/formik-inputs/mui-formik-timepicker.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { TimePicker } from "@mui/x-date-pickers/TimePicker";

import { useField } from "formik";

const MuiFormikTimepicker = ({ name, minTime, maxTime, timeZone }) => {
const MuiFormikTimepicker = ({ name, label, minTime, maxTime, timeZone }) => {
const [field, meta, helpers] = useField(name);

return (
Expand All @@ -35,6 +35,7 @@ const MuiFormikTimepicker = ({ name, minTime, maxTime, timeZone }) => {
slotProps={{
textField: {
name,
label,
error: meta.touched && Boolean(meta.error),
helperText: meta.touched && meta.error,
size: "small",
Expand Down
1 change: 1 addition & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"sort_by": "Sort By",
"sort_asc_label": "A-Z",
"sort_desc_label": "Z-A",
"select_an_option": "Select an option",
"placeholders": {
"search_speakers": "Search Speakers by Name, Email, Speaker Id or Member Id",
"select_acceptance_criteria": "Select acceptance criteria",
Expand Down
24 changes: 13 additions & 11 deletions src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ const parseValue = (item, timeZone) => {
return item.current_value
? parseInt(item.current_value)
: item.minimum_quantity || 0;
case "ComboBox":
case "RadioButtonList":
case "ComboBox": {
const defaultVal = item.values.find((v) => v.is_default)?.id;
return item.current_value || defaultVal || "";
}
case "CheckBoxList": {
const defaultVal = item.values.find((v) => v.is_default)?.id;
return item.current_value || (defaultVal ? [defaultVal] : []);
}
Comment on lines +42 to +45
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

Keep CheckBoxList defaults array-shaped.

CheckBoxList is treated as a multi-value field here and in getYupValidation, but this fallback returns a single id from find(). That drops additional defaults and can start the form in an invalid shape as soon as a default exists.

🐛 Suggested fix
     case "CheckBoxList": {
-      const defaultVal = item.values.find((v) => v.is_default)?.id;
-      return item.current_value || defaultVal || [];
+      if (Array.isArray(item.current_value)) return item.current_value;
+      return (
+        item.values?.filter((v) => v.is_default)?.map((v) => v.id) || []
+      );
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
case "CheckBoxList": {
const defaultVal = item.values.find((v) => v.is_default)?.id;
return item.current_value || defaultVal || [];
}
case "CheckBoxList": {
if (Array.isArray(item.current_value)) return item.current_value;
return (
item.values?.filter((v) => v.is_default)?.map((v) => v.id) || []
);
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/pages/sponsors/sponsor-cart-tab/components/edit-form/index.js` around
lines 42 - 45, The CheckBoxList branch currently picks a single default id via
find(), but CheckBoxList is multi-value; change the defaultVal to an array of
ids by replacing find() with filter(...).map(v => v.id) so defaults remain
array-shaped, then return item.current_value || defaultVal || [] (or explicitly
default to [] if needed); also ensure this aligns with the getYupValidation
handling of CheckBoxList.

case "Text":
case "TextArea":
return item.current_value || "";
case "CheckBox":
return item.current_value ? item.current_value === "True" : false;
case "CheckBoxList":
return item.current_value || [];
case "RadioButtonList":
return item.current_value || "";
case "Time":
return item.current_value
? moment.tz(item.current_value, "HH:mm", timeZone)
Expand Down Expand Up @@ -152,12 +156,10 @@ const buildInitialValues = (form, timeZone) => {

const buildValidationSchema = (items) => {
const schema = items.reduce((acc, item) => {
item.meta_fields
.filter((f) => f.class_field === "Form")
.map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
getYupValidation(f);
});
item.meta_fields.map((f) => {
acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] =
getYupValidation(f);
});
// notes
acc[`i-${item.form_item_id}-c-global-f-notes`] = yup.string(
T.translate("validation.string")
Expand Down
Loading