diff --git a/package.json b/package.json index bd8752fe5..126412f62 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "moment-duration-format": "^2.3.2", "moment-timezone": "^0.5.33", "mui-color-input": "^9.0.0", - "openstack-uicore-foundation": "5.0.36", + "openstack-uicore-foundation": "5.0.38-beta.3", "p-limit": "^6.1.0", "path-browserify": "^1.0.1", "postcss-loader": "^6.2.1", diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js index ee4cf65e9..c220ede42 100644 --- a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/__tests__/edit-cart-form.test.js @@ -33,7 +33,7 @@ jest.mock("openstack-uicore-foundation/lib/components", () => { const React = require("react"); return { __esModule: true, - MuiFormItemTable: ({ data, onNotesClick, onSettingsClick }) => + MuiFormItemTable: ({ data }) => React.createElement( "div", null, @@ -41,25 +41,7 @@ jest.mock("openstack-uicore-foundation/lib/components", () => { React.createElement( "div", { key: item.form_item_id }, - React.createElement("span", null, item.name), - React.createElement( - "button", - { - "aria-label": "edit", - onClick: () => onNotesClick && onNotesClick(item), - type: "button" - }, - "edit" - ), - React.createElement( - "button", - { - "aria-label": "settings", - onClick: () => onSettingsClick && onSettingsClick(item), - type: "button" - }, - "settings" - ) + React.createElement("span", null, item.name) ) ) ), @@ -432,70 +414,37 @@ describe("EditCartForm", () => { }); describe("Interactive Features", () => { - test("opens notes modal when notes button clicked", async () => { + test("renders all form items in MuiFormItemTable", async () => { renderWithStore(); await waitFor(() => { expect(screen.getByText("Item 1")).toBeInTheDocument(); - }); - - // Find the Edit icon button (notes) by aria-label - const notesButton = screen.getAllByLabelText("edit")[0]; - - await userEvent.click(notesButton); - - await waitFor(() => { - expect(screen.getByRole("dialog")).toBeInTheDocument(); + expect(screen.getByText("Item 2")).toBeInTheDocument(); }); }); - test("closes notes modal", async () => { + test("renders Save and Cancel buttons", async () => { renderWithStore(); await waitFor(() => { - expect(screen.getByText("Item 1")).toBeInTheDocument(); - }); - - const notesButton = screen.getAllByLabelText("edit")[0]; - - await userEvent.click(notesButton); - - await waitFor(() => { - expect(screen.getByRole("dialog")).toBeInTheDocument(); - }); - - // Find close button within the dialog - const closeButton = screen.getByLabelText("close"); - - await userEvent.click(closeButton); - - await waitFor(() => { - expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); + expect(screen.getByText(/general.save/)).toBeInTheDocument(); + expect(screen.getByText(/general.cancel/)).toBeInTheDocument(); }); }); - test("opens settings modal when settings button clicked", async () => { + test("does not render any dialog by default", async () => { renderWithStore(); await waitFor(() => { expect(screen.getByText("Item 1")).toBeInTheDocument(); }); - const settingsButtons = screen.getAllByRole("button"); - const settingsButton = settingsButtons.find( - (btn) => btn.getAttribute("aria-label") === "settings" - ); - - await userEvent.click(settingsButton); - - await waitFor(() => { - expect(screen.getByRole("dialog")).toBeInTheDocument(); - }); + expect(screen.queryByRole("dialog")).not.toBeInTheDocument(); }); }); describe("Item Field Error Message", () => { - test("shows error message below Save when a required Item field is empty after submit", async () => { + test("blocks form submission when a required Item field is empty", async () => { const formWithRequiredItemField = { ...mockCartForm, items: [ @@ -522,10 +471,9 @@ describe("EditCartForm", () => { await userEvent.click(screen.getByText(/general.save/)); + // Validation errors prevent form submission; updateCartForm should not be called await waitFor(() => { - expect( - screen.getByText("validation.additional_items") - ).toBeInTheDocument(); + expect(mockUpdateCartForm).not.toHaveBeenCalled(); }); }); diff --git a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js index 02a06f67b..334571027 100644 --- a/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js +++ b/src/pages/sponsors/sponsor-page/tabs/sponsor-cart-tab/components/edit-form/index.js @@ -11,7 +11,7 @@ * limitations under the License. * */ -import React, { useMemo, useState } from "react"; +import React, { useMemo } from "react"; import T from "i18n-react/dist/i18n-react"; import { connect } from "react-redux"; import { FormikProvider, useFormik } from "formik"; @@ -24,8 +24,6 @@ import { MuiFormItemTable, getCurrentApplicableRate } from "openstack-uicore-foundation/lib/components"; -import NotesModal from "openstack-uicore-foundation/lib/components/mui/notes-modal"; -import ItemSettingsModal from "openstack-uicore-foundation/lib/components/mui/item-settings-modal"; import { DISCOUNT_TYPES } from "../../../../../../../utils/constants"; import showConfirmDialog from "../../../../../../../components/mui/showConfirmDialog"; @@ -38,11 +36,12 @@ const parseValue = (item, timeZone) => { case "RadioButtonList": case "ComboBox": { const defaultVal = item.values.find((v) => v.is_default)?.id; - return item.current_value || defaultVal || ""; + const val = item.current_value || defaultVal; + return val ? String(val) : ""; } case "CheckBoxList": { const defaultVal = item.values.find((v) => v.is_default)?.id; - return item.current_value || (defaultVal ? [defaultVal] : []); + return item.current_value || (defaultVal ? [String(defaultVal)] : []); } case "Text": case "TextArea": @@ -67,39 +66,33 @@ const getYupValidation = (field) => { switch (field.type) { case "Quantity": { - schema = yup.number(T.translate("validation.number")); + schema = yup.number(); if (field.minimum_quantity > 0) { - schema = schema.min( - field.minimum_quantity, - T.translate("validation.minimum", { minimum: field.minimum_quantity }) - ); + schema = schema.min(field.minimum_quantity, " "); } if (field.maximum_quantity > 0) { - schema = schema.max( - field.maximum_quantity, - T.translate("validation.maximum", { maximum: field.maximum_quantity }) - ); + schema = schema.max(field.maximum_quantity, " "); } if (field.is_required) { - schema = schema.required(T.translate("validation.required")); + schema = schema.required(" "); } break; } case "Text": case "TextArea": { - schema = yup.string(T.translate("validation.string")); + schema = yup.string(); if (field.is_required) { - schema = schema.required(T.translate("validation.required")); + schema = schema.required(" "); } break; } case "Time": case "DateTime": { - schema = yup.date(T.translate("validation.date")); + schema = yup.date(); if (field.is_required) { - schema = schema.required(T.translate("validation.required")); + schema = schema.required(" "); } else { schema = schema.nullable(); } @@ -107,10 +100,10 @@ const getYupValidation = (field) => { } case "RadioButtonList": case "ComboBox": { - schema = yup.string(T.translate("validation.string")); + schema = yup.string(); if (field.is_required) { - schema = schema.required(T.translate("validation.required")); + schema = schema.required(" "); } break; } @@ -118,26 +111,23 @@ const getYupValidation = (field) => { schema = yup.boolean(); if (field.is_required) { - schema = schema.oneOf([true], T.translate("validation.required")); + schema = schema.oneOf([true], " "); } break; } case "CheckBoxList": { - schema = yup - .array() - .of(yup.string()) - .typeError(T.translate("validation.wrong_format")); + schema = yup.array().of(yup.string()).typeError(" "); if (field.is_required) { - schema = schema.min(1, T.translate("validation.required")); + schema = schema.min(1, " "); } break; } default: { - schema = yup.string(T.translate("validation.wrong_format")); + schema = yup.string(); if (field.is_required) { - schema = schema.required(T.translate("validation.required")); + schema = schema.required(" "); } break; } @@ -177,30 +167,20 @@ const buildValidationSchema = (items) => { const quantityKey = `i-${item.form_item_id}-c-global-f-quantity`; // notes - acc[`i-${item.form_item_id}-c-global-f-notes`] = yup.string( - T.translate("validation.string") - ); + acc[`i-${item.form_item_id}-c-global-f-notes`] = yup.string(); // validation for the global quantity input - let globalQtySchema = yup - .number(T.translate("validation.number")) - .min(0, `${T.translate("validation.minimum")} 0`); + let globalQtySchema = yup.number().min(0, " "); if (item.quantity_limit_per_sponsor > 0) { globalQtySchema = globalQtySchema.max( item.quantity_limit_per_sponsor, - T.translate("validation.maximum", { - maximum: item.quantity_limit_per_sponsor - }) + " " ); } - globalQtySchema = globalQtySchema.required( - T.translate("validation.required") - ); + globalQtySchema = globalQtySchema.required(" "); acc[quantityKey] = globalQtySchema; // custom rate - acc[`i-${item.form_item_id}-c-global-f-custom_rate`] = yup.number( - T.translate("validation.number") - ); + acc[`i-${item.form_item_id}-c-global-f-custom_rate`] = yup.number(); item.meta_fields.map((f) => { acc[`i-${item.form_item_id}-c-${f.class_field}-f-${f.type_id}`] = yup @@ -215,10 +195,8 @@ const buildValidationSchema = (items) => { return acc; }, {}); - schema.discount_amount = yup.number(T.translate("validation.number")); - schema.discount_type = yup - .string(T.translate("validation.string")) - .nullable(); + schema.discount_amount = yup.number(); + schema.discount_type = yup.string().nullable(); return schema; }; @@ -230,9 +208,6 @@ const EditForm = ({ onSaveForm, onCancel }) => { - const [notesItem, setNotesItem] = useState(null); - const [settingsItem, setSettingsItem] = useState(null); - const currentApplicableRate = useMemo( () => getCurrentApplicableRate(showTimeZone, showMetadata), [showMetadata, showTimeZone] @@ -318,10 +293,6 @@ const EditForm = ({ // wait for formik to re-initialize with form items if (!form || Object.keys(formik.values).length === 0) return null; - const hasItemFieldErrors = Object.keys(formik.errors).some( - (key) => key.includes("-c-Item-") && formik.touched[key] - ); - return ( <> @@ -332,14 +303,19 @@ const EditForm = ({ {form?.items.length} {T.translate("general.items")} - + - {hasItemFieldErrors && ( - - - {T.translate("validation.additional_items")} - - - )} - setNotesItem(null)} - /> - setSettingsItem(null)} - /> ); diff --git a/yarn.lock b/yarn.lock index 91c0331f4..0dbc170dc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9068,10 +9068,10 @@ open@^10.0.3: is-inside-container "^1.0.0" wsl-utils "^0.1.0" -openstack-uicore-foundation@5.0.36: - version "5.0.36" - resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.36.tgz#a6da3a0793227b74706cb651af22471f9374859d" - integrity sha512-8Ia0/dh1xZNK9SrykP9+trzyMalyZI2TsirYrbWp0TREdV8vtS+dsNoRxeRMOaYejQtni6JkgHoTBZIC5c7hMw== +openstack-uicore-foundation@5.0.38-beta.3: + version "5.0.38-beta.3" + resolved "https://registry.yarnpkg.com/openstack-uicore-foundation/-/openstack-uicore-foundation-5.0.38-beta.3.tgz#9e7aa3ce165c43654f192006d4efcafa5c615e3e" + integrity sha512-5ZkT9eNKgEWMFTF0TM6wRVt0h6vysFtz27ND82XOimTChmAh0xvxuefVIJHe8iNMMK8Nl+EacRyLe/rUga3wYA== dependencies: use-sync-external-store "^1.6.0"