diff --git a/src/components/forms/admin-access-form.js b/src/components/forms/admin-access-form.js
index e243d9146..e8e9674d0 100644
--- a/src/components/forms/admin-access-form.js
+++ b/src/components/forms/admin-access-form.js
@@ -21,7 +21,7 @@ import Grid2 from "@mui/material/Grid2";
import Typography from "@mui/material/Typography";
import MemberInput from "openstack-uicore-foundation/lib/components/inputs/member-input";
import SummitInput from "openstack-uicore-foundation/lib/components/inputs/summit-input";
-import MuiFormikTextField from "../mui/formik-inputs/mui-formik-textfield";
+import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield";
import { requiredStringValidation } from "../../utils/yup";
const validationSchema = yup.object().shape({
diff --git a/src/components/forms/sponsor-settings-form/index.js b/src/components/forms/sponsor-settings-form/index.js
index 358160da7..8634406ae 100644
--- a/src/components/forms/sponsor-settings-form/index.js
+++ b/src/components/forms/sponsor-settings-form/index.js
@@ -18,8 +18,8 @@ import * as yup from "yup";
import { Box, Button, Grid2 } from "@mui/material";
import { epochToMomentTimeZone } from "openstack-uicore-foundation/lib/utils/methods";
import MuiFormikDatepicker from "openstack-uicore-foundation/lib/components/mui/formik-inputs/datepicker";
-import MuiFormikTextField from "../../mui/formik-inputs/mui-formik-textfield";
-import MuiFormikCheckbox from "../../mui/formik-inputs/mui-formik-checkbox";
+import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield";
+import MuiFormikCheckbox from "openstack-uicore-foundation/lib/components/mui/formik-inputs/checkbox";
import styles from "./styles.module.less";
import {
addEmailListValidator,
diff --git a/src/components/mui/__tests__/mui-formik-checkbox-group.test.js b/src/components/mui/__tests__/mui-formik-checkbox-group.test.js
deleted file mode 100644
index 914f212ed..000000000
--- a/src/components/mui/__tests__/mui-formik-checkbox-group.test.js
+++ /dev/null
@@ -1,163 +0,0 @@
-// mui-formik-checkbox-group.test.js
-import React from "react";
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import { Formik, Form } from "formik";
-import "@testing-library/jest-dom";
-import MuiFormikCheckboxGroup from "../formik-inputs/mui-formik-checkbox-group";
-
-// Helper function to render the component with Formik
-const renderWithFormik = (props, initialValues = { testField: [] }) =>
- render(
-
-
-
- );
-
-describe("MuiFormikCheckboxGroup", () => {
- const options = [
- { value: 1, label: "Option 1" },
- { value: 2, label: "Option 2" },
- { value: 3, label: "Option 3" }
- ];
-
- test("renders the component with label", () => {
- renderWithFormik({ label: "Test Label", options });
-
- expect(screen.getByText("Test Label")).toBeInTheDocument();
- expect(screen.getByText("Option 1")).toBeInTheDocument();
- expect(screen.getByText("Option 2")).toBeInTheDocument();
- expect(screen.getByText("Option 3")).toBeInTheDocument();
- });
-
- test("renders all checkboxes unchecked when no values are selected", () => {
- renderWithFormik({ options });
-
- const checkboxes = screen.getAllByRole("checkbox");
- expect(checkboxes).toHaveLength(3);
- checkboxes.forEach((checkbox) => {
- expect(checkbox).not.toBeChecked();
- });
- });
-
- test("renders with pre-selected values", () => {
- renderWithFormik({ options }, { testField: [1, 3] });
-
- const checkboxes = screen.getAllByRole("checkbox");
- expect(checkboxes[0]).toBeChecked(); // Option 1
- expect(checkboxes[1]).not.toBeChecked(); // Option 2
- expect(checkboxes[2]).toBeChecked(); // Option 3
- });
-
- test("handles checking a checkbox", async () => {
- renderWithFormik({ options });
-
- const checkboxes = screen.getAllByRole("checkbox");
- await userEvent.click(checkboxes[0]);
-
- // After clicking, the first checkbox should be checked
- expect(checkboxes[0]).toBeChecked();
- expect(checkboxes[1]).not.toBeChecked();
- expect(checkboxes[2]).not.toBeChecked();
- });
-
- test("handles unchecking a checkbox", async () => {
- renderWithFormik({ options }, { testField: [1, 2] });
-
- const checkboxes = screen.getAllByRole("checkbox");
- // Initial state
- expect(checkboxes[0]).toBeChecked();
- expect(checkboxes[1]).toBeChecked();
-
- // Uncheck the first checkbox
- await userEvent.click(checkboxes[0]);
-
- // After clicking, only the second checkbox should remain checked
- expect(checkboxes[0]).not.toBeChecked();
- expect(checkboxes[1]).toBeChecked();
- expect(checkboxes[2]).not.toBeChecked();
- });
-
- test("handles non-array initial values gracefully", () => {
- renderWithFormik({ options }, { testField: null });
-
- const checkboxes = screen.getAllByRole("checkbox");
- expect(checkboxes).toHaveLength(3);
- checkboxes.forEach((checkbox) => {
- expect(checkbox).not.toBeChecked();
- });
- });
-
- test("displays error message when touched and has error", () => {
- render(
-
-
-
- );
-
- expect(screen.getByText("This field is required")).toBeInTheDocument();
- });
-
- test("does not display error message when not touched", () => {
- render(
-
-
-
- );
-
- expect(
- screen.queryByText("This field is required")
- ).not.toBeInTheDocument();
- });
-
- test("parses checkbox values as integers", async () => {
- // Using a mock to inspect the actual value set in Formik
- const mockSetValue = jest.fn();
-
- render(
-
- {() => {
- // Override useField to spy on setValue
- const originalUseField = require("formik").useField;
- jest
- .spyOn(require("formik"), "useField")
- .mockImplementation((props) => {
- const [field, meta, helpers] = originalUseField(props);
- return [field, meta, { ...helpers, setValue: mockSetValue }];
- });
-
- return (
-
- );
- }}
-
- );
-
- const checkboxes = screen.getAllByRole("checkbox");
- await userEvent.click(checkboxes[0]);
-
- // Check that setValue was called with the correct value (integer 1, not string '1')
- expect(mockSetValue).toHaveBeenCalledWith([1]);
-
- // Restore the original implementation
- require("formik").useField.mockRestore();
- });
-});
diff --git a/src/components/mui/__tests__/mui-formik-file-size-field.test.js b/src/components/mui/__tests__/mui-formik-file-size-field.test.js
deleted file mode 100644
index 0ea854d49..000000000
--- a/src/components/mui/__tests__/mui-formik-file-size-field.test.js
+++ /dev/null
@@ -1,223 +0,0 @@
-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(
-
-
-
- );
-
-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);
- 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);
- });
-
- it("shows 0 when initial value is 0", () => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: 0 }
- );
-
- const field = screen.getByLabelText("Max File Size");
- expect(field).toHaveValue(0);
- });
- });
-
- describe("clearing behavior", () => {
- it("shows empty field after clearing", async () => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: 5 * BYTES_PER_MB }
- );
-
- const field = screen.getByLabelText("Max File Size");
- expect(field).toHaveValue(5);
-
- await act(async () => {
- await userEvent.clear(field);
- });
-
- expect(field).toHaveValue(null); // empty number input
- });
-
- it("submits 0 after clearing when initial value was numeric", async () => {
- const onSubmit = jest.fn();
- renderWithFormik(
- { label: "Max File Size", onSubmit },
- { max_file_size: 5 * BYTES_PER_MB }
- );
-
- const field = screen.getByLabelText("Max File Size");
- const submitButton = screen.getByText("submit");
-
- await act(async () => {
- await userEvent.clear(field);
- await userEvent.click(submitButton);
- });
-
- expect(onSubmit).toHaveBeenCalledWith(
- expect.objectContaining({ max_file_size: 0 }),
- expect.anything()
- );
- });
-
- it("submits null after clearing when initial value was null", async () => {
- const onSubmit = jest.fn();
- renderWithFormik(
- { label: "Max File Size", onSubmit },
- { max_file_size: null }
- );
-
- const field = screen.getByLabelText("Max File Size");
- const submitButton = screen.getByText("submit");
-
- await act(async () => {
- await userEvent.type(field, "10");
- await userEvent.clear(field);
- await userEvent.click(submitButton);
- });
-
- expect(onSubmit).toHaveBeenCalledWith(
- expect.objectContaining({ max_file_size: null }),
- expect.anything()
- );
- });
-
- it("accepts new value after clearing", async () => {
- const onSubmit = jest.fn();
- renderWithFormik(
- { label: "Max File Size", onSubmit },
- { max_file_size: 5 * BYTES_PER_MB }
- );
-
- const field = screen.getByLabelText("Max File Size");
- const submitButton = screen.getByText("submit");
-
- await act(async () => {
- await userEvent.clear(field);
- await userEvent.type(field, "20");
- await userEvent.click(submitButton);
- });
-
- expect(onSubmit).toHaveBeenCalledWith(
- expect.objectContaining({ max_file_size: 20 * BYTES_PER_MB }),
- expect.anything()
- );
- });
- });
-
- describe("zero as first character", () => {
- it("blocks zero as first character", async () => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: null }
- );
-
- const field = screen.getByLabelText("Max File Size");
-
- await act(async () => {
- await userEvent.type(field, "0");
- });
-
- // zero is not a valid first character, field stays empty
- expect(field).toHaveValue(null);
- });
-
- it("blocks leading zero followed by digits", async () => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: null }
- );
-
- const field = screen.getByLabelText("Max File Size");
-
- await act(async () => {
- await userEvent.type(field, "099");
- });
-
- // leading zero blocked, only "99" accepted
- expect(field).toHaveValue(99);
- });
-
- it("allows zero in non-leading position", async () => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: null }
- );
-
- const field = screen.getByLabelText("Max File Size");
-
- await act(async () => {
- await userEvent.type(field, "10");
- });
-
- expect(field).toHaveValue(10);
- });
- });
-
- describe("blocked keys", () => {
- it.each(["e", "E", "+", "-", ".", ","])(
- "blocks '%s' key from being entered",
- async (key) => {
- renderWithFormik(
- { label: "Max File Size", onSubmit: jest.fn() },
- { max_file_size: 0 }
- );
-
- const field = screen.getByLabelText("Max File Size");
-
- await act(async () => {
- await userEvent.clear(field);
- await userEvent.type(field, key);
- });
-
- // field should remain empty since the key was blocked
- expect(field).toHaveValue(null);
- }
- );
- });
-});
diff --git a/src/components/mui/__tests__/mui-formik-quantity-field.test.js b/src/components/mui/__tests__/mui-formik-quantity-field.test.js
deleted file mode 100644
index a73942773..000000000
--- a/src/components/mui/__tests__/mui-formik-quantity-field.test.js
+++ /dev/null
@@ -1,68 +0,0 @@
-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(
-
-
-
- );
-
-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()
- );
- });
-});
diff --git a/src/components/mui/__tests__/mui-formik-radio-group.test.js b/src/components/mui/__tests__/mui-formik-radio-group.test.js
deleted file mode 100644
index 4bd6883c9..000000000
--- a/src/components/mui/__tests__/mui-formik-radio-group.test.js
+++ /dev/null
@@ -1,148 +0,0 @@
-// mui-formik-radio-group.test.js
-import React from "react";
-import { render, screen } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import { Formik, Form } from "formik";
-import "@testing-library/jest-dom";
-import MuiFormikRadioGroup from "../formik-inputs/mui-formik-radio-group";
-
-// Helper function to render the component with Formik
-const renderWithFormik = (props, initialValues = { testField: "" }) => render(
-
-
-
- );
-
-describe("MuiFormikRadioGroup", () => {
- const options = [
- { value: "option1", label: "Option 1" },
- { value: "option2", label: "Option 2" },
- { value: "option3", label: "Option 3" }
- ];
-
- test("renders the component with label", () => {
- renderWithFormik({ label: "Test Radio Group", options });
-
- expect(screen.getByText("Test Radio Group")).toBeInTheDocument();
- expect(screen.getByText("Option 1")).toBeInTheDocument();
- expect(screen.getByText("Option 2")).toBeInTheDocument();
- expect(screen.getByText("Option 3")).toBeInTheDocument();
- });
-
- test("renders all radio buttons with none selected when no default value", () => {
- renderWithFormik({ options });
-
- const radioButtons = screen.getAllByRole("radio");
- expect(radioButtons).toHaveLength(3);
- radioButtons.forEach((radio) => {
- expect(radio).not.toBeChecked();
- });
- });
-
- test("renders with pre-selected value", () => {
- renderWithFormik({ options }, { testField: "option2" });
-
- const radioButtons = screen.getAllByRole("radio");
- expect(radioButtons[0]).not.toBeChecked(); // Option 1
- expect(radioButtons[1]).toBeChecked(); // Option 2
- expect(radioButtons[2]).not.toBeChecked(); // Option 3
- });
-
- test("handles selecting a radio button", async () => {
- renderWithFormik({ options });
-
- const radioButtons = screen.getAllByRole("radio");
- await userEvent.click(radioButtons[0]);
-
- // After clicking, the first radio should be checked and others should be unchecked
- expect(radioButtons[0]).toBeChecked();
- expect(radioButtons[1]).not.toBeChecked();
- expect(radioButtons[2]).not.toBeChecked();
- });
-
- test("handles changing selection", async () => {
- renderWithFormik({ options }, { testField: "option1" });
-
- const radioButtons = screen.getAllByRole("radio");
- // Initial state
- expect(radioButtons[0]).toBeChecked();
- expect(radioButtons[1]).not.toBeChecked();
- expect(radioButtons[2]).not.toBeChecked();
-
- // Change selection to option2
- await userEvent.click(radioButtons[1]);
-
- // After clicking, only the second radio should be checked
- expect(radioButtons[0]).not.toBeChecked();
- expect(radioButtons[1]).toBeChecked();
- expect(radioButtons[2]).not.toBeChecked();
- });
-
- test("displays error message when touched and has error", () => {
- render(
-
-
-
- );
-
- expect(screen.getByText("This field is required")).toBeInTheDocument();
- });
-
- test("does not display error message when not touched", () => {
- render(
-
-
-
- );
-
- expect(
- screen.queryByText("This field is required")
- ).not.toBeInTheDocument();
- });
-
- test("passes additional props to RadioGroup", () => {
- renderWithFormik({ options, row: true });
-
- // The RadioGroup should have the row prop
- const radioGroup = screen.getByRole("radiogroup");
- expect(radioGroup).toHaveClass("MuiFormGroup-row");
- });
-
- test("generates correct key for each radio option", () => {
- renderWithFormik({ options });
-
- // Check that the keys are generated correctly by inspecting the rendered DOM
- const radioLabels = screen.getAllByRole("radio");
- expect(radioLabels).toHaveLength(3);
-
- const radioButtons = screen.getAllByRole("radio");
- // Check that the values match what we expect
- expect(radioButtons[0]).toHaveAttribute("value", "option1");
- expect(radioButtons[1]).toHaveAttribute("value", "option2");
- expect(radioButtons[2]).toHaveAttribute("value", "option3");
- });
-
- test("defaults to empty when no label is provided", () => {
- renderWithFormik({ options });
-
- // There should be no FormLabel element
- const formLabels = document.querySelectorAll(".MuiFormLabel-root");
- expect(formLabels.length).toBe(0);
- });
-});
diff --git a/src/components/mui/__tests__/mui-sponsor-input.test.js b/src/components/mui/__tests__/mui-sponsor-input.test.js
deleted file mode 100644
index c1ee32974..000000000
--- a/src/components/mui/__tests__/mui-sponsor-input.test.js
+++ /dev/null
@@ -1,239 +0,0 @@
-// mui-sponsor-input.test.js
-import React from "react";
-import { render, screen, waitFor, act } from "@testing-library/react";
-import userEvent from "@testing-library/user-event";
-import { Formik, Form } from "formik";
-import "@testing-library/jest-dom";
-import MuiSponsorInput from "../formik-inputs/mui-sponsor-input";
-
-// Mock the sponsor actions
-jest.mock("../../../actions/sponsor-actions", () => ({
- querySponsors: jest.fn((query, summitId, callback) => {
- const mockResults = [
- { id: 1, name: "Sponsor One" },
- { id: 2, name: "Sponsor Two" },
- { id: 3, name: "Another Sponsor" }
- ].filter((s) => s.name.toLowerCase().includes(query.toLowerCase()));
-
- callback(mockResults);
- return Promise.resolve();
- })
-}));
-
-// Helper function to render the component with Formik
-const renderWithFormik = (props, initialValues = { sponsor: "" }) =>
- render(
-
-
-
- );
-
-describe("MuiSponsorInput", () => {
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- test("renders the component with placeholder", () => {
- renderWithFormik();
-
- // Placeholder should be visible
- expect(
- screen.getByPlaceholderText("Search sponsors...")
- ).toBeInTheDocument();
- });
-
- test("opens dropdown and fetches options when typing", async () => {
- jest.useFakeTimers();
- const user = userEvent.setup({
- advanceTimers: jest.advanceTimersByTime
- });
- const { querySponsors } = require("../../../actions/sponsor-actions");
-
- renderWithFormik();
-
- const input = screen.getByPlaceholderText("Search sponsors...");
- await user.click(input);
- await user.type(input, "Sponsor");
-
- await act(async () => {
- jest.advanceTimersByTime(300);
- });
-
- await waitFor(() => {
- expect(querySponsors).toHaveBeenCalledWith(
- "Sponsor",
- 123,
- expect.any(Function)
- );
- });
-
- expect(await screen.findByText("Sponsor One")).toBeInTheDocument();
- expect(await screen.findByText("Sponsor Two")).toBeInTheDocument();
-
- jest.useRealTimers();
- });
-
- test("selects a sponsor in single selection mode", async () => {
- jest.useFakeTimers();
- const user = userEvent.setup({
- advanceTimers: jest.advanceTimersByTime
- });
- renderWithFormik();
-
- const input = screen.getByPlaceholderText("Search sponsors...");
- await user.click(input);
- await user.type(input, "Sponsor");
- await act(async () => {
- jest.advanceTimersByTime(300);
- });
-
- const option = screen.getAllByText(/Sponsor One/)[0];
- await user.click(option);
-
- await waitFor(() => {
- expect(input.value).toBe("Sponsor One");
- });
-
- jest.useRealTimers();
- });
-
- test("supports multiple selection when isMulti is true", async () => {
- jest.useFakeTimers();
- const user = userEvent.setup({
- advanceTimers: jest.advanceTimersByTime
- });
- renderWithFormik({ isMulti: true }, { sponsor: [] });
-
- const input = screen.getByPlaceholderText("Search sponsors...");
- await user.click(input);
- await user.type(input, "Sponsor");
- await act(async () => {
- jest.advanceTimersByTime(300);
- });
-
- const option1 = screen.getAllByText(/Sponsor One/)[0];
- await user.click(option1);
-
- await user.clear(input);
- await user.type(input, "Two");
- await act(async () => {
- jest.advanceTimersByTime(300);
- });
-
- const option2 = screen.getAllByText(/Sponsor Two/)[0];
- await user.click(option2);
-
- expect(screen.getByText(/Sponsor One/)).toBeInTheDocument();
- expect(screen.getByText(/Sponsor Two/)).toBeInTheDocument();
-
- jest.useRealTimers();
- });
-
- test("handles plain value format correctly", async () => {
- renderWithFormik({ plainValue: true });
-
- // Type and select an option
- const input = screen.getByPlaceholderText("Search sponsors...");
- await userEvent.click(input);
- await userEvent.type(input, "Sponsor");
-
- // Wait for options to load
- await waitFor(() => {
- expect(screen.getByText("Sponsor One")).toBeInTheDocument();
- });
-
- // Select the first option
- await userEvent.click(screen.getByText("Sponsor One"));
-
- // Check that the value is set correctly (this would need to inspect Formik's state)
- // We can't easily test this directly, but we can confirm the displayed value
- expect(input.value).toBe("Sponsor One");
- });
-
- test("displays error message when field has error", () => {
- render(
-
-
-
- );
-
- // Error message should be displayed
- expect(screen.getByText("Sponsor is required")).toBeInTheDocument();
- });
-
- test("initializes with preselected value in single selection mode", () => {
- renderWithFormik(
- { plainValue: false },
- { sponsor: { id: 1, name: "Sponsor One" } }
- );
-
- // The selected value should be displayed
- expect(screen.getByDisplayValue("Sponsor One")).toBeInTheDocument();
- });
-
- test("initializes with preselected values in multi selection mode", () => {
- renderWithFormik(
- { isMulti: true, plainValue: false },
- {
- sponsor: [
- { id: 1, name: "Sponsor One" },
- { id: 2, name: "Sponsor Two" }
- ]
- }
- );
-
- // Both selected values should be displayed as chips
- expect(screen.getByText("Sponsor One")).toBeInTheDocument();
- expect(screen.getByText("Sponsor Two")).toBeInTheDocument();
- });
-
- test("debounces API calls", async () => {
- jest.useFakeTimers();
- const user = userEvent.setup({
- advanceTimers: jest.advanceTimersByTime
- });
- const { querySponsors } = require("../../../actions/sponsor-actions");
- renderWithFormik();
-
- const input = screen.getByPlaceholderText("Search sponsors...");
- await user.click(input);
-
- // Type characters rapidly
- await user.type(input, "Spo");
-
- // Debounce hasn't fired yet — timer is still pending
- expect(querySponsors).toHaveBeenCalledTimes(0);
-
- // Advance past the 250ms debounce
- await act(async () => {
- jest.advanceTimersByTime(300);
- });
-
- expect(querySponsors).toHaveBeenCalledTimes(1);
- expect(querySponsors).toHaveBeenCalledWith(
- "Spo",
- 123,
- expect.any(Function)
- );
-
- jest.useRealTimers();
- });
-});
diff --git a/src/components/mui/formik-inputs/company-input-mui.js b/src/components/mui/formik-inputs/company-input-mui.js
deleted file mode 100644
index 301a451a0..000000000
--- a/src/components/mui/formik-inputs/company-input-mui.js
+++ /dev/null
@@ -1,206 +0,0 @@
-/**
- * Copyright 2020 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState, useEffect, useMemo } from "react";
-import PropTypes from "prop-types";
-import TextField from "@mui/material/TextField";
-import Autocomplete, { createFilterOptions } from "@mui/material/Autocomplete";
-import CircularProgress from "@mui/material/CircularProgress";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import { useField } from "formik";
-import { queryCompanies } from "../../../actions/company-actions";
-import { DEBOUNCE_WAIT_250 } from "../../../utils/constants";
-
-const filter = createFilterOptions();
-
-const CompanyInputMUI = ({
- id,
- name,
- placeholder,
- plainValue,
- isMulti = false,
- allowCreate = false,
- ...rest
-}) => {
- const [field, meta, helpers] = useField(name);
- const [options, setOptions] = useState([]);
- const [open, setOpen] = useState(false);
- const [inputValue, setInputValue] = useState("");
- const [loading, setLoading] = useState(false);
- const [isDebouncing, setIsDebouncing] = useState(false);
-
- const { value } = field;
- const error = meta.touched && meta.error;
-
- const fetchOptions = async (input) => {
- if (!input) {
- setOptions([]);
- return;
- }
-
- setIsDebouncing(false);
- setLoading(true);
-
- const normalize = (results) =>
- results.map((r) => ({
- value: r.id.toString(),
- label: r.name
- }));
-
- await queryCompanies(input, (results) => {
- setOptions(normalize(results));
- setLoading(false);
- });
- };
-
- useEffect(() => {
- if (inputValue) {
- setIsDebouncing(true);
- const delayDebounce = setTimeout(() => {
- fetchOptions(inputValue);
- }, DEBOUNCE_WAIT_250);
- return () => clearTimeout(delayDebounce);
- }
- setIsDebouncing(false);
- }, [inputValue]);
-
- const selectedValue = useMemo(() => {
- if (!value) return isMulti ? [] : null;
-
- if (isMulti) {
- return value.map((v) =>
- plainValue
- ? { value: v, label: v }
- : { value: v.id?.toString(), label: v.name }
- );
- }
- return plainValue
- ? { value, label: value }
- : { value: value.id?.toString(), label: value.name };
- }, [value, plainValue, isMulti]);
-
- const handleChange = (_, newValue) => {
- let theValue;
-
- if (!newValue || (Array.isArray(newValue) && newValue.length === 0)) {
- theValue = isMulti ? [] : plainValue ? "" : { id: "", name: "" };
- } else if (isMulti) {
- theValue = plainValue
- ? newValue.map((v) => v.label)
- : newValue.map((v) => ({
- id: parseInt(v.value),
- name: v.label
- }));
- } else {
- theValue = plainValue
- ? newValue.inputValue || newValue.label
- : {
- id: newValue.inputValue ? 0 : parseInt(newValue.value),
- name: newValue.inputValue || newValue.label
- };
- }
-
- helpers.setValue(theValue);
- };
-
- const handleFilterOptions = (options, params) => {
- const filtered = filter(options, params);
-
- if (!allowCreate || loading || isDebouncing) return filtered;
-
- const { inputValue } = params;
- const isExisting = options.some(
- (option) => inputValue.toLowerCase() === option.label.toLowerCase()
- );
-
- if (inputValue !== "" && !isExisting) {
- filtered.push({
- inputValue,
- value: null,
- label: `Create "${inputValue}"`
- });
- }
- return filtered;
- };
-
- const getOptionLabel = (option) => {
- if (option.inputValue) {
- return option.inputValue;
- }
- return option.label || "";
- };
-
- return (
- setOpen(true)}
- onClose={() => setOpen(false)}
- options={options}
- value={selectedValue}
- getOptionLabel={getOptionLabel}
- isOptionEqualToValue={(option, value) => option.value === value.value}
- onInputChange={(_, newInputValue) => {
- setInputValue(newInputValue);
- }}
- filterOptions={handleFilterOptions}
- multiple={isMulti}
- onChange={handleChange}
- loading={loading}
- fullWidth
- popupIcon={}
- renderOption={(props, option) => (
-
- {option.label}
-
- )}
- renderInput={(params) => (
-
- {loading && }
- {params.InputProps?.endAdornment}
- >
- )
- }}
- />
- )}
- {...rest}
- />
- );
-};
-
-CompanyInputMUI.propTypes = {
- id: PropTypes.string,
- name: PropTypes.string.isRequired,
- placeholder: PropTypes.string,
- plainValue: PropTypes.bool,
- isMulti: PropTypes.bool,
- allowCreate: PropTypes.bool
-};
-
-export default CompanyInputMUI;
diff --git a/src/components/mui/formik-inputs/item-price-tiers.js b/src/components/mui/formik-inputs/item-price-tiers.js
index 43b6f5825..2a98aec16 100644
--- a/src/components/mui/formik-inputs/item-price-tiers.js
+++ b/src/components/mui/formik-inputs/item-price-tiers.js
@@ -9,7 +9,7 @@ import {
InputLabel,
TextField
} from "@mui/material";
-import MuiFormikPriceField from "./mui-formik-pricefield";
+import MuiFormikPriceField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/price-field";
import { RATE_FIELDS, isRateEnabled } from "../../../utils/rate-helpers";
const TIERS = [
diff --git a/src/components/mui/formik-inputs/mui-formik-async-select.js b/src/components/mui/formik-inputs/mui-formik-async-select.js
index a9bf2054e..b574b198f 100644
--- a/src/components/mui/formik-inputs/mui-formik-async-select.js
+++ b/src/components/mui/formik-inputs/mui-formik-async-select.js
@@ -8,6 +8,10 @@ import {
import { useField } from "formik";
import { DEBOUNCE_WAIT_250 } from "../../../utils/constants";
+// Stays local: uicore's async-select.js lacks defaultOptions, filterOptions,
+// and the plain-value-sync effect this component has. company-dialog.js
+// depends on defaultOptions specifically. Deferred as a follow-up rather
+// than migrated blindly in this pass.
const MuiFormikAsyncAutocomplete = ({
name,
queryFunction,
diff --git a/src/components/mui/formik-inputs/mui-formik-checkbox-group.js b/src/components/mui/formik-inputs/mui-formik-checkbox-group.js
deleted file mode 100644
index 24e9c4d11..000000000
--- a/src/components/mui/formik-inputs/mui-formik-checkbox-group.js
+++ /dev/null
@@ -1,75 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import {
- Checkbox,
- FormControl,
- FormControlLabel,
- FormGroup,
- FormHelperText,
- FormLabel
-} from "@mui/material";
-import { useField } from "formik";
-import { INT_BASE } from "../../../utils/constants";
-
-const MuiFormikCheckboxGroup = ({ name, label, options, ...props }) => {
- const [field, meta, helpers] = useField({ name });
-
- // Ensure field.value is an array
- const values = Array.isArray(field.value) ? field.value : [];
-
- const handleChange = (ev) => {
- const { value, checked } = ev.target;
-
- if (checked) {
- // Add the value to the array if it's checked
- helpers.setValue([...values, parseInt(value, INT_BASE)]);
- } else {
- // Remove the value from the array if it's unchecked
- helpers.setValue(
- values.filter((val) => val !== parseInt(value, INT_BASE))
- );
- }
- };
-
- return (
-
- {label && {label}}
-
- {options.map((op) => (
-
- }
- label={op.label}
- />
- ))}
-
- {meta.touched && meta.error && (
- {meta.error}
- )}
-
- );
-};
-
-MuiFormikCheckboxGroup.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string,
- options: PropTypes.array.isRequired
-};
-
-export default MuiFormikCheckboxGroup;
diff --git a/src/components/mui/formik-inputs/mui-formik-checkbox.js b/src/components/mui/formik-inputs/mui-formik-checkbox.js
deleted file mode 100644
index 8ca9e13ee..000000000
--- a/src/components/mui/formik-inputs/mui-formik-checkbox.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import {
- Checkbox,
- FormControl,
- FormControlLabel,
- FormHelperText
-} from "@mui/material";
-import { useField } from "formik";
-
-const MuiFormikCheckbox = ({ name, label, ...props }) => {
- const [field, meta] = useField({ name, type: "checkbox" });
-
- return (
-
-
- }
- label={label}
- />
- {meta.touched && meta.error && (
- {meta.error}
- )}
-
- );
-};
-
-MuiFormikCheckbox.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired
-};
-
-export default MuiFormikCheckbox;
diff --git a/src/components/mui/formik-inputs/mui-formik-color-field.js b/src/components/mui/formik-inputs/mui-formik-color-field.js
index 4dc74e45e..8f81a446e 100644
--- a/src/components/mui/formik-inputs/mui-formik-color-field.js
+++ b/src/components/mui/formik-inputs/mui-formik-color-field.js
@@ -3,6 +3,9 @@ import PropTypes from "prop-types";
import { useField } from "formik";
import { MuiColorInput } from "mui-color-input";
+// Stays local: wraps the third-party mui-color-input package. uicore has no
+// color-field concept at all, so there's nothing to migrate to.
+//
// MuiColorInput fires onChange on every pointermove while dragging the color
// panel, so the picked value is kept in local state for instant visual
// feedback and only committed to Formik (value + touched + validation) on
diff --git a/src/components/mui/formik-inputs/mui-formik-datetimepicker.js b/src/components/mui/formik-inputs/mui-formik-datetimepicker.js
index 925c8783c..5d8904fdc 100644
--- a/src/components/mui/formik-inputs/mui-formik-datetimepicker.js
+++ b/src/components/mui/formik-inputs/mui-formik-datetimepicker.js
@@ -18,6 +18,8 @@ import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker";
import { useField } from "formik";
+// Stays local: uicore's datepicker.js wraps MUI's date-only DatePicker, not
+// DateTimePicker. Not a substitute for this date+time field.
const MuiFormikDatetimepicker = ({
name,
label,
diff --git a/src/components/mui/formik-inputs/mui-formik-discountfield.js b/src/components/mui/formik-inputs/mui-formik-discountfield.js
deleted file mode 100644
index 81bde06c5..000000000
--- a/src/components/mui/formik-inputs/mui-formik-discountfield.js
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState } from "react";
-import PropTypes from "prop-types";
-import { InputAdornment } from "@mui/material";
-import { useField } from "formik";
-import MuiFormikTextField from "./mui-formik-textfield";
-import { DISCOUNT_TYPES, ONE_HUNDRED } from "../../../utils/constants";
-
-const BLOCKED_KEYS = ["e", "E", "+", "-"];
-
-const MuiFormikDiscountField = ({
- name,
- label,
- discountType,
- inCents = false,
- disabled = false,
- ...props
-}) => {
- // eslint-disable-next-line no-unused-vars
- const [field, meta, helpers] = useField(name);
- const [cleared, setCleared] = useState(false);
- const emptyValue = meta.initialValue === null ? null : 0;
-
- const getDisplayValue = () => {
- if (cleared) return "";
- if (field.value == null || field.value === 0) {
- return field.value === 0 ? 0 : "";
- }
- return inCents ? field.value / ONE_HUNDRED : field.value;
- };
-
- const adornment =
- discountType === DISCOUNT_TYPES.RATE
- ? {
- endAdornment: %
- }
- : {
- startAdornment: $
- };
-
- const inputProps =
- discountType === DISCOUNT_TYPES.RATE
- ? { max: 100, inputMode: "numeric", step: 1 }
- : { inputMode: "decimal", step: 1 };
-
- const handleChange = (e) => {
- const newVal = e.target.value;
-
- if (newVal === "") {
- setCleared(true);
- helpers.setValue(emptyValue);
- return;
- }
-
- setCleared(false);
- const numericValue = Number(newVal);
- const newDiscount = inCents ? numericValue * ONE_HUNDRED : numericValue;
-
- helpers.setValue(newDiscount);
- };
-
- const handleKeyDown = (e) => {
- if (BLOCKED_KEYS.includes(e.key)) {
- e.preventDefault();
- e.stopPropagation();
- }
- };
-
- return (
-
- );
-};
-
-MuiFormikDiscountField.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string
-};
-
-export default MuiFormikDiscountField;
diff --git a/src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js b/src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js
deleted file mode 100644
index 52c5d1699..000000000
--- a/src/components/mui/formik-inputs/mui-formik-dropdown-checkbox.js
+++ /dev/null
@@ -1,88 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React from "react";
-import {
- Checkbox,
- Divider,
- FormControl,
- ListItemText,
- MenuItem,
- Select
-} from "@mui/material";
-import { useField } from "formik";
-import T from "i18n-react/dist/i18n-react";
-
-const MuiFormikDropdownCheckbox = ({ name, options, ...rest }) => {
- const [field, meta, helpers] = useField(name);
- const allSelected = options.every(({ value }) =>
- field.value?.includes(value)
- );
-
- const handleChange = (event) => {
- const { value } = event.target;
-
- // If "all" was clicked
- if (value.includes("all")) {
- if (allSelected) {
- helpers.setValue([]);
- } else {
- helpers.setValue(options.map((opt) => opt.value));
- }
- } else {
- helpers.setValue(value);
- }
- };
-
- return (
-
-
-
- );
-};
-
-export default MuiFormikDropdownCheckbox;
diff --git a/src/components/mui/formik-inputs/mui-formik-dropdown-radio.js b/src/components/mui/formik-inputs/mui-formik-dropdown-radio.js
deleted file mode 100644
index 222d1eef1..000000000
--- a/src/components/mui/formik-inputs/mui-formik-dropdown-radio.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React from "react";
-import {
- FormControl,
- ListItemText,
- MenuItem,
- Radio,
- Select
-} from "@mui/material";
-import { useField } from "formik";
-import T from "i18n-react/dist/i18n-react";
-
-const MuiFormikDropdownRadio = ({ name, options, placeholder, ...rest }) => {
- const finalPlaceholder =
- placeholder || T.translate("general.select_an_option");
- const [field, meta, helpers] = useField(name);
-
- const handleChange = (event) => {
- helpers.setValue(event.target.value);
- };
-
- return (
-
-
-
- );
-};
-
-export default MuiFormikDropdownRadio;
diff --git a/src/components/mui/formik-inputs/mui-formik-file-size-field.js b/src/components/mui/formik-inputs/mui-formik-file-size-field.js
deleted file mode 100644
index 8e8ea47d8..000000000
--- a/src/components/mui/formik-inputs/mui-formik-file-size-field.js
+++ /dev/null
@@ -1,84 +0,0 @@
-import React, { useState } 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 bytesToMb = (bytes) => Math.floor(bytes / BYTES_PER_MB);
-
-const MuiFormikFilesizeField = ({ name, label, ...props }) => {
- const [field, meta, helpers] = useField(name);
- const [cleared, setCleared] = useState(false);
-
- const emptyValue = meta.initialValue === null ? null : 0;
-
- const getDisplayValue = () => {
- if (cleared) return "";
- if (field.value == null || field.value === 0) {
- return field.value === 0 ? 0 : "";
- }
- return bytesToMb(field.value);
- };
-
- const handleChange = (e) => {
- const mbValue = e.target.value;
-
- if (mbValue === "") {
- setCleared(true);
- helpers.setValue(emptyValue);
- return;
- }
-
- setCleared(false);
- const bytes = Number(mbValue) * BYTES_PER_MB;
- helpers.setValue(bytes);
- };
-
- const handleKeyDown = (e) => {
- if (BLOCKED_KEYS.includes(e.key)) {
- e.preventDefault();
- e.stopPropagation();
- return;
- }
- // Block "0" as first character — only 1-9 are valid leading digits.
- // When value is empty or already "0", prevent any "0" keypress.
- if (e.key === "0" && (e.target.value === "" || e.target.value === "0")) {
- e.preventDefault();
- e.stopPropagation();
- }
- };
-
- return (
- MB
- },
- htmlInput: {
- min: 0,
- inputMode: "numeric",
- step: 1
- }
- }}
- onKeyDown={handleKeyDown}
- // eslint-disable-next-line react/jsx-props-no-spreading
- {...props}
- />
- );
-};
-
-MuiFormikFilesizeField.propTypes = {
- name: PropTypes.string.isRequired,
- // label is optional: callers may supply an external InputLabel instead
- label: PropTypes.string
-};
-
-export default MuiFormikFilesizeField;
diff --git a/src/components/mui/formik-inputs/mui-formik-pricefield.js b/src/components/mui/formik-inputs/mui-formik-pricefield.js
deleted file mode 100644
index 0d91b8402..000000000
--- a/src/components/mui/formik-inputs/mui-formik-pricefield.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import React, { useState } from "react";
-import PropTypes from "prop-types";
-import { InputAdornment } from "@mui/material";
-import { useField } from "formik";
-import MuiFormikTextField from "./mui-formik-textfield";
-import { DECIMAL_DIGITS, ONE_HUNDRED } from "../../../utils/constants";
-
-const BLOCKED_KEYS = ["e", "E", "+", "-"];
-
-const MuiFormikPriceField = ({
- name,
- label,
- inCents = false,
- inputProps = { step: 0.01 },
- ...props
-}) => {
- // eslint-disable-next-line no-unused-vars
- const [field, meta, helpers] = useField(name);
- const [cleared, setCleared] = useState(false);
- const [isFocused, setIsFocused] = useState(false);
- const [focusedValue, setFocusedValue] = useState("");
-
- // emptyValue is always 0 when editing this field, null is handled by N/A checkbox
- const emptyValue = 0;
-
- const getRawString = () => {
- if (cleared || field.value == null) return "";
- if (field.value === 0) return "0";
- const raw = inCents ? field.value / ONE_HUNDRED : field.value;
- return String(Number(raw.toFixed(DECIMAL_DIGITS)));
- };
-
- const getDisplayValue = () => {
- if (isFocused) return focusedValue;
- if (cleared) return "";
- if (field.value == null || field.value === 0) {
- return field.value === 0 ? 0 : "";
- }
- const str = getRawString();
- const dotIdx = str.indexOf(".");
- if (dotIdx === -1) return `${str}.00`;
- if (str.length - dotIdx - 1 === 1) return `${str}0`;
- return str;
- };
-
- const handleFocus = () => {
- setIsFocused(true);
- setFocusedValue(getRawString());
- };
-
- const handleBlur = (e) => {
- setIsFocused(false);
- field.onBlur(e);
- if (props.onBlur) props.onBlur(e);
- };
-
- const handleChange = (e) => {
- const newVal = e.target.value;
- setFocusedValue(newVal);
-
- if (newVal === "") {
- setCleared(true);
- helpers.setValue(emptyValue);
- return;
- }
-
- setCleared(false);
- const numericValue = Number(newVal);
- const newPrice = inCents
- ? Math.round(numericValue * ONE_HUNDRED)
- : numericValue;
-
- helpers.setValue(newPrice);
- };
-
- const handleKeyDown = (e) => {
- if (BLOCKED_KEYS.includes(e.key)) {
- e.preventDefault();
- e.stopPropagation();
- }
- };
-
- return (
- $
- }
- }}
- inputProps={{
- min: 0,
- inputMode: "decimal",
- ...inputProps
- }}
- onKeyDown={handleKeyDown}
- // eslint-disable-next-line react/jsx-props-no-spreading
- {...props}
- />
- );
-};
-
-MuiFormikPriceField.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string
-};
-
-export default MuiFormikPriceField;
diff --git a/src/components/mui/formik-inputs/mui-formik-quantity-field.js b/src/components/mui/formik-inputs/mui-formik-quantity-field.js
deleted file mode 100644
index 133716542..000000000
--- a/src/components/mui/formik-inputs/mui-formik-quantity-field.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import MuiFormikTextField from "./mui-formik-textfield";
-
-const BLOCKED_KEYS = ["e", "E", "+", "-"];
-
-const MuiFormikQuantityField = ({ ...props }) => (
- {
- 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}
- />
-);
-
-MuiFormikQuantityField.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string
-};
-
-export default MuiFormikQuantityField;
diff --git a/src/components/mui/formik-inputs/mui-formik-radio-group.js b/src/components/mui/formik-inputs/mui-formik-radio-group.js
deleted file mode 100644
index 4c49bb4c3..000000000
--- a/src/components/mui/formik-inputs/mui-formik-radio-group.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import {
- FormControl,
- FormControlLabel,
- FormHelperText,
- FormLabel,
- Radio,
- RadioGroup
-} from "@mui/material";
-import { useField } from "formik";
-
-const MuiFormikRadioGroup = ({
- name,
- label,
- marginWrapper = "normal",
- options,
- ...props
-}) => {
- const [field, meta] = useField({ name });
-
- return (
-
- {label && {label}}
-
- {options.map((op) => (
-
- }
- label={op.label}
- />
- ))}
-
- {meta.touched && meta.error && (
- {meta.error}
- )}
-
- );
-};
-
-MuiFormikRadioGroup.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string,
- marginWrapper: PropTypes.string,
- options: PropTypes.array.isRequired
-};
-
-export default MuiFormikRadioGroup;
diff --git a/src/components/mui/formik-inputs/mui-formik-select-group.js b/src/components/mui/formik-inputs/mui-formik-select-group.js
deleted file mode 100644
index 60be7c1df..000000000
--- a/src/components/mui/formik-inputs/mui-formik-select-group.js
+++ /dev/null
@@ -1,352 +0,0 @@
-/**
- * Copyright 2020 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState, useEffect, useMemo } from "react";
-import {
- MenuItem,
- Checkbox,
- ListItemText,
- CircularProgress,
- Select,
- OutlinedInput,
- Box,
- ListSubheader,
- Divider
-} from "@mui/material";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import PropTypes from "prop-types";
-import { useField } from "formik";
-
-const getCustomIcon = (loading) => () =>
- (
-
- {loading && }
-
-
- );
-
-const MuiFormikSelectGroup = ({
- name,
- queryFunction,
- queryParams = [],
- placeholder = "Select options",
- showSelectAll = false,
- selectAllLabel = "Select All",
- getOptionLabel = (item) => item.name,
- getOptionValue = (item) => item.id,
- noOptionsLabel = "No items",
- getGroupId = null,
- getGroupLabel = null,
- disabled = false
-}) => {
- const [field, meta, helpers] = useField(name);
- const [options, setOptions] = useState([]);
- const [groupedOptions, setGroupedOptions] = useState([]);
- const [loading, setLoading] = useState(false);
-
- const isAllSelected =
- Array.isArray(field.value) && field.value.includes("all");
- const value = isAllSelected ? options : field.value || [];
- const error = meta.touched && meta.error;
-
- const fetchOptions = async () => {
- setLoading(true);
- try {
- await queryFunction(...queryParams, (results) => {
- setOptions(results);
-
- if (getGroupId && getGroupLabel) {
- // using map no avoid duplicate groups
- const groupsMap = new Map();
-
- results.forEach((item) => {
- const groupId = getGroupId(item);
- const groupLabel = getGroupLabel(item);
-
- if (!groupsMap.has(groupId)) {
- groupsMap.set(groupId, {
- id: groupId,
- label: groupLabel,
- options: []
- });
- }
-
- groupsMap.get(groupId).options.push(item);
- });
-
- setGroupedOptions(Array.from(groupsMap.values()));
- } else {
- setGroupedOptions([
- {
- id: "default",
- label: null,
- options: results
- }
- ]);
- }
- setLoading(false);
- });
- } catch (error) {
- setLoading(false);
- }
- };
-
- useEffect(() => {
- fetchOptions();
- }, []);
-
- const handleChange = (event) => {
- const selectedValues = event.target.value;
-
- if (selectedValues.includes("selectAll")) {
- const currentValues = Array.isArray(value)
- ? value.map(getOptionValue)
- : [];
-
- if (isAllSelected || currentValues.length === options.length) {
- helpers.setValue([]);
- } else {
- helpers.setValue(["all"]);
- }
- return;
- }
-
- const filteredValues = selectedValues.filter((val) => val !== "selectAll");
-
- const selectedItems = filteredValues
- .map((val) => {
- const found = options.find((item) => getOptionValue(item) === val);
- return found;
- })
- .filter(Boolean);
-
- helpers.setValue(selectedItems);
- };
-
- const selectedValues = isAllSelected
- ? options.map(getOptionValue)
- : Array.isArray(value)
- ? value.map((item) => getOptionValue(item))
- : [];
-
- const renderGroupedOptions = () =>
- groupedOptions
- .map((group, groupIndex) => [
- group.label && (
-
- {group.label}
-
- ),
- ...group.options.map((option) => {
- const optionValue = getOptionValue(option);
- const isChecked = selectedValues.includes(optionValue);
-
- return (
-
- );
- }),
- group.label && groupIndex < groupedOptions.length - 1 && (
-
- )
- ])
- .flat()
- .filter(Boolean);
-
- const renderMenuContent = () => {
- if (loading) {
- return (
-
- );
- }
-
- if (options.length === 0) {
- return (
-
- );
- }
-
- return (
- <>
- {showSelectAll && (
- <>
-
-
- >
- )}
- {renderGroupedOptions()}
- >
- );
- };
-
- const IconWithLoading = useMemo(() => getCustomIcon(loading), [loading]);
-
- return (
- <>
- }
- displayEmpty
- disabled={disabled || loading}
- renderValue={(selected) => {
- if (!selected || selected.length === 0) {
- return (
-
- {loading ? "Loading..." : placeholder}
-
- );
- }
- return selected
- .map((val) => {
- const item = options.find((opt) => getOptionValue(opt) === val);
- return item ? getOptionLabel(item) : val;
- })
- .join(", ");
- }}
- error={Boolean(error)}
- IconComponent={IconWithLoading}
- >
- {renderMenuContent()}
-
- {error && (
-
- {error}
-
- )}
- >
- );
-};
-
-MuiFormikSelectGroup.propTypes = {
- name: PropTypes.string.isRequired,
- queryFunction: PropTypes.func.isRequired,
- queryParams: PropTypes.array,
- placeholder: PropTypes.string,
- showSelectAll: PropTypes.bool,
- selectAllLabel: PropTypes.string,
- getOptionLabel: PropTypes.func,
- getOptionValue: PropTypes.func,
- getGroupId: PropTypes.func,
- getGroupLabel: PropTypes.func,
- noOptionsLabel: PropTypes.string,
- disabled: PropTypes.bool
-};
-
-export default MuiFormikSelectGroup;
diff --git a/src/components/mui/formik-inputs/mui-formik-select.js b/src/components/mui/formik-inputs/mui-formik-select.js
deleted file mode 100644
index 0271a8d70..000000000
--- a/src/components/mui/formik-inputs/mui-formik-select.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import React, { Children } from "react";
-import PropTypes from "prop-types";
-import {
- Select,
- FormHelperText,
- FormControl,
- InputAdornment,
- IconButton,
- InputLabel
-} from "@mui/material";
-import ClearIcon from "@mui/icons-material/Clear";
-import { useField } from "formik";
-
-const MuiFormikSelect = ({
- name,
- label,
- placeholder,
- children,
- isClearable,
- ...rest
-}) => {
- const [field, meta, helpers] = useField(name);
-
- const handleClear = (ev) => {
- ev.stopPropagation();
- helpers.setValue("");
- };
-
- const hasValue = field?.value && field.value !== "";
- const shouldShrink = hasValue || Boolean(placeholder);
-
- return (
-
- {label && (
-
- {label}
-
- )}
-
- {meta.touched && meta.error && (
- {meta.error}
- )}
-
- );
-};
-
-MuiFormikSelect.propTypes = {
- name: PropTypes.string.isRequired,
- children: PropTypes.node.isRequired,
- placeholder: PropTypes.string,
- isClearable: PropTypes.bool
-};
-
-export default MuiFormikSelect;
diff --git a/src/components/mui/formik-inputs/mui-formik-summit-addon-select.js b/src/components/mui/formik-inputs/mui-formik-summit-addon-select.js
deleted file mode 100644
index 2216d9f83..000000000
--- a/src/components/mui/formik-inputs/mui-formik-summit-addon-select.js
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React from "react";
-import PropTypes from "prop-types";
-import { useField } from "formik";
-import SummitAddonSelect from "../summit-addon-select";
-
-const MuiFormikSummitAddonSelect = ({
- name,
- summitId,
- placeholder = "Select...",
- inputProps = {}
-}) => {
- const [field, meta, helpers] = useField(name);
-
- return (
-
- );
-};
-
-MuiFormikSummitAddonSelect.propTypes = {
- name: PropTypes.string.isRequired,
- summitId: PropTypes.number.isRequired,
- placeholder: PropTypes.string,
- inputProps: PropTypes.object
-};
-
-export default MuiFormikSummitAddonSelect;
diff --git a/src/components/mui/formik-inputs/mui-formik-switch.js b/src/components/mui/formik-inputs/mui-formik-switch.js
deleted file mode 100644
index 9b7f00e6c..000000000
--- a/src/components/mui/formik-inputs/mui-formik-switch.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import {
- FormControl,
- FormControlLabel,
- FormHelperText,
- Switch
-} from "@mui/material";
-import { useField } from "formik";
-
-const MuiFormikSwitch = ({ name, label, ...props }) => {
- const [field, meta] = useField({ name });
-
- return (
-
-
- }
- label={label}
- />
- {meta.touched && meta.error && (
- {meta.error}
- )}
-
- );
-};
-
-MuiFormikSwitch.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string.isRequired
-};
-
-export default MuiFormikSwitch;
diff --git a/src/components/mui/formik-inputs/mui-formik-textfield.js b/src/components/mui/formik-inputs/mui-formik-textfield.js
deleted file mode 100644
index d94a0bee1..000000000
--- a/src/components/mui/formik-inputs/mui-formik-textfield.js
+++ /dev/null
@@ -1,56 +0,0 @@
-import React from "react";
-import PropTypes from "prop-types";
-import { Box, TextField, Typography } from "@mui/material";
-import { useField } from "formik";
-
-const MuiFormikTextField = ({
- name,
- label,
- maxLength,
- required = false,
- ...props
-}) => {
- const [field, meta] = useField(name);
- const currentLength = field.value?.length || 0;
-
- let finalLabel = "";
-
- if (label) {
- finalLabel = required ? `${label} *` : label;
- }
-
- return (
-
-
- {maxLength && (
-
- {`${maxLength - currentLength} characters left`}
-
- )}
-
- );
-};
-
-MuiFormikTextField.propTypes = {
- name: PropTypes.string.isRequired,
- label: PropTypes.string,
- maxLength: PropTypes.number,
- required: PropTypes.bool
-};
-
-export default MuiFormikTextField;
diff --git a/src/components/mui/formik-inputs/mui-formik-timepicker.js b/src/components/mui/formik-inputs/mui-formik-timepicker.js
deleted file mode 100644
index 5b98355b5..000000000
--- a/src/components/mui/formik-inputs/mui-formik-timepicker.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React from "react";
-import PropTypes from "prop-types";
-import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider";
-import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
-import { TimePicker } from "@mui/x-date-pickers/TimePicker";
-
-import { useField } from "formik";
-
-const MuiFormikTimepicker = ({
- name,
- minTime,
- maxTime,
- timeZone,
- disabled = false
-}) => {
- const [field, meta, helpers] = useField(name);
-
- return (
-
-
-
- );
-};
-
-MuiFormikTimepicker.propTypes = {
- name: PropTypes.string.isRequired
-};
-
-export default MuiFormikTimepicker;
diff --git a/src/components/mui/formik-inputs/mui-sponsor-input.js b/src/components/mui/formik-inputs/mui-sponsor-input.js
deleted file mode 100644
index a3f818e28..000000000
--- a/src/components/mui/formik-inputs/mui-sponsor-input.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * Copyright 2020 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState, useEffect, useMemo } from "react";
-import PropTypes from "prop-types";
-import TextField from "@mui/material/TextField";
-import Autocomplete from "@mui/material/Autocomplete";
-import CircularProgress from "@mui/material/CircularProgress";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import { useField } from "formik";
-import { querySponsors } from "../../../actions/sponsor-actions";
-import { DEBOUNCE_WAIT_250 } from "../../../utils/constants";
-
-const MuiSponsorInput = ({
- id,
- name,
- placeholder,
- plainValue,
- isMulti = false,
- summitId,
- ...rest
-}) => {
- const [field, meta, helpers] = useField(name);
- const [options, setOptions] = useState([]);
- const [open, setOpen] = useState(false);
- const [inputValue, setInputValue] = useState("");
- const [loading, setLoading] = useState(false);
-
- const { value } = field;
- const error = meta.touched && meta.error;
-
- const fetchOptions = async (input) => {
- if (!input) {
- setOptions([]);
- return;
- }
-
- setLoading(true);
-
- const normalize = (results) =>
- results.map((r) => ({
- value: r.id.toString(),
- label: r.name
- }));
-
- await querySponsors(input, summitId, (results) => {
- setOptions(normalize(results));
- setLoading(false);
- });
- };
-
- useEffect(() => {
- if (inputValue) {
- const delayDebounce = setTimeout(() => {
- fetchOptions(inputValue);
- }, DEBOUNCE_WAIT_250);
- return () => clearTimeout(delayDebounce);
- }
- }, [inputValue]);
-
- const selectedValue = useMemo(() => {
- if (!value) return isMulti ? [] : null;
-
- if (isMulti) {
- return value.map((v) =>
- plainValue
- ? { value: v, label: v }
- : { value: v.id?.toString(), label: v.name }
- );
- }
- return plainValue
- ? { value, label: value }
- : { value: value.id?.toString(), label: value.name };
- }, [value, plainValue, isMulti]);
-
- const handleChange = (_, newValue) => {
- let theValue;
-
- if (!newValue || (Array.isArray(newValue) && newValue.length === 0)) {
- theValue = isMulti ? [] : plainValue ? "" : { id: "", name: "" };
- } else if (isMulti) {
- theValue = plainValue
- ? newValue.map((v) => v.label)
- : newValue.map((v) => ({
- id: parseInt(v.value),
- name: v.label
- }));
- } else {
- theValue = plainValue
- ? newValue.label
- : { id: parseInt(newValue.value), name: newValue.label };
- }
-
- helpers.setValue(theValue);
- };
-
- const errorMessage =
- error && (typeof error === "string" ? error : error[Object.keys(error)[0]]);
-
- return (
- setOpen(true)}
- onClose={() => setOpen(false)}
- options={options}
- value={selectedValue}
- getOptionLabel={(option) => option.label}
- isOptionEqualToValue={(option, value) => option.value === value.value}
- onInputChange={(_, newInputValue) => {
- setInputValue(newInputValue);
- }}
- multiple={isMulti}
- onChange={handleChange}
- loading={loading}
- fullWidth
- popupIcon={}
- renderInput={(params) => (
-
- {loading && }
- {params.InputProps?.endAdornment}
- >
- )
- }}
- />
- )}
- {...rest}
- />
- );
-};
-
-MuiSponsorInput.propTypes = {
- id: PropTypes.string,
- name: PropTypes.string.isRequired,
- placeholder: PropTypes.string,
- plainValue: PropTypes.bool,
- isMulti: PropTypes.bool
-};
-
-export default MuiSponsorInput;
diff --git a/src/components/mui/formik-inputs/sponsorship-input-mui.js b/src/components/mui/formik-inputs/sponsorship-input-mui.js
deleted file mode 100644
index 230d685e8..000000000
--- a/src/components/mui/formik-inputs/sponsorship-input-mui.js
+++ /dev/null
@@ -1,162 +0,0 @@
-/**
- * Copyright 2020 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState, useEffect, useMemo } from "react";
-import PropTypes from "prop-types";
-import TextField from "@mui/material/TextField";
-import Autocomplete from "@mui/material/Autocomplete";
-import CircularProgress from "@mui/material/CircularProgress";
-import { useField } from "formik";
-import { querySponsorships } from "../../../actions/sponsorship-actions";
-import { DEBOUNCE_WAIT_250 } from "../../../utils/constants";
-
-const SponsorshipTypeInputMUI = ({
- id,
- name,
- placeholder,
- plainValue,
- isMulti = false,
- ...rest
-}) => {
- const [field, meta, helpers] = useField(name);
- const [options, setOptions] = useState([]);
- const [open, setOpen] = useState(false);
- const [inputValue, setInputValue] = useState("");
- const [loading, setLoading] = useState(false);
-
- const { value } = field;
- const error = meta.touched && meta.error;
-
- const errorMessage =
- typeof error === "object" ? error?.id || error?.name || "" : error;
-
- const fetchOptions = async (input) => {
- if (!input) {
- setOptions([]);
- return;
- }
-
- setLoading(true);
-
- const normalize = (results) =>
- results.map((r) => ({
- value: r.id.toString(),
- label: r.name
- }));
-
- await querySponsorships(input, (results) => {
- setOptions(normalize(results));
- setLoading(false);
- });
- };
-
- useEffect(() => {
- if (inputValue) {
- const delayDebounce = setTimeout(() => {
- fetchOptions(inputValue);
- }, DEBOUNCE_WAIT_250);
- return () => clearTimeout(delayDebounce);
- }
- }, [inputValue]);
-
- const selectedValue = useMemo(() => {
- if (!value) return isMulti ? [] : null;
-
- if (isMulti) {
- return value.map((v) =>
- plainValue
- ? { value: v, label: v }
- : { value: v.id?.toString(), label: v.name }
- );
- }
- return plainValue
- ? { value, label: value }
- : { value: value.id?.toString(), label: value.name };
- }, [value, plainValue, isMulti]);
-
- const handleChange = (_, newValue) => {
- let theValue;
-
- if (!newValue || (Array.isArray(newValue) && newValue.length === 0)) {
- theValue = isMulti ? [] : plainValue ? "" : { id: "", name: "" };
- } else if (isMulti) {
- theValue = plainValue
- ? newValue.map((v) => v.label)
- : newValue.map((v) => ({
- id: parseInt(v.value),
- name: v.label
- }));
- } else {
- theValue = plainValue
- ? newValue.label
- : { id: parseInt(newValue.value), name: newValue.label };
- }
-
- helpers.setValue(theValue);
- };
-
- return (
- setOpen(true)}
- onClose={() => setOpen(false)}
- options={options}
- value={selectedValue}
- getOptionLabel={(option) => option.label}
- isOptionEqualToValue={(option, value) => option.value === value.value}
- onInputChange={(_, newInputValue) => {
- setInputValue(newInputValue);
- }}
- multiple={isMulti}
- onChange={handleChange}
- loading={loading}
- fullWidth
- renderInput={(params) => (
-
- {loading && }
- {params.InputProps?.endAdornment}
- >
- )
- }}
- />
- )}
- {...rest}
- />
- );
-};
-
-SponsorshipTypeInputMUI.propTypes = {
- id: PropTypes.string,
- name: PropTypes.string.isRequired,
- placeholder: PropTypes.string,
- plainValue: PropTypes.bool,
- isMulti: PropTypes.bool
-};
-
-export default SponsorshipTypeInputMUI;
diff --git a/src/components/mui/formik-inputs/sponsorship-summit-select-mui.js b/src/components/mui/formik-inputs/sponsorship-summit-select-mui.js
deleted file mode 100644
index c880de311..000000000
--- a/src/components/mui/formik-inputs/sponsorship-summit-select-mui.js
+++ /dev/null
@@ -1,166 +0,0 @@
-/**
- * Copyright 2020 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useState, useEffect, useMemo } from "react";
-import {
- MenuItem,
- Checkbox,
- ListItemText,
- CircularProgress,
- Select,
- OutlinedInput,
- Box
-} from "@mui/material";
-import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
-import PropTypes from "prop-types";
-import { useField } from "formik";
-import { querySponsorshipsBySummit } from "../../../actions/sponsorship-actions";
-
-const getCustomIcon = (loading) => {
- const Icon = () => (
-
- {loading && }
-
-
- );
- return Icon;
-};
-
-const SponsorshipsBySummitSelectMUI = ({
- name,
- summitId,
- placeholder,
- plainValue,
- hiddenOptions = []
-}) => {
- const [field, meta, helpers] = useField(name);
- const [options, setOptions] = useState([]);
- const [loading, setLoading] = useState(false);
-
- const value = field.value || [];
- const error = meta.touched && meta.error;
-
- const fetchOptions = async () => {
- setLoading(true);
- await querySponsorshipsBySummit("", summitId, (results) => {
- const normalized = results
- .filter((r) => !hiddenOptions.includes(r.id))
- .map((r) => ({
- value: r.id.toString(),
- label: r.type.name
- }));
- setOptions(normalized);
- setLoading(false);
- });
- };
-
- useEffect(() => {
- fetchOptions();
- }, []);
-
- const handleChange = (event) => {
- const selected = event.target.value;
- const selectedItems = plainValue
- ? selected
- : selected.map((id) => {
- const match = options.find((o) => o.value === id);
- return { id: parseInt(match.value), name: match.label };
- });
-
- helpers.setValue(selectedItems);
- };
-
- const selectedValues = plainValue
- ? value
- : value.map((v) => v.id?.toString());
-
- const IconWithLoading = useMemo(() => getCustomIcon(loading), [loading]);
-
- return (
- <>
- }
- displayEmpty
- renderValue={(selected) => {
- if (selected.length === 0) {
- return {placeholder};
- }
- return options
- .filter((opt) => selected.includes(opt.value))
- .map((opt) => opt.label)
- .join(", ");
- }}
- error={Boolean(error)}
- IconComponent={IconWithLoading}
- >
- {loading ? (
-
- ) : (
- options.map((option) => (
-
- ))
- )}
-
- {error && (
-
- {error}
-
- )}
- >
- );
-};
-
-SponsorshipsBySummitSelectMUI.propTypes = {
- name: PropTypes.string.isRequired,
- summitId: PropTypes.number.isRequired,
- placeholder: PropTypes.string,
- plainValue: PropTypes.bool
-};
-
-export default SponsorshipsBySummitSelectMUI;
diff --git a/src/components/mui/mui-qr-badge-popup.js b/src/components/mui/mui-qr-badge-popup.js
index 1a227423a..694932bfe 100644
--- a/src/components/mui/mui-qr-badge-popup.js
+++ b/src/components/mui/mui-qr-badge-popup.js
@@ -34,7 +34,7 @@ import {
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import { useSnackbarMessage } from "openstack-uicore-foundation/lib/components/mui/snackbar-notification";
-import MuiFormikTextField from "./formik-inputs/mui-formik-textfield";
+import MuiFormikTextField from "openstack-uicore-foundation/lib/components/mui/formik-inputs/textfield";
import QrReader from "../qr-reader";
import { getTypeValue, toSlug } from "../../utils/extra-questions";
import MuiFormikAsyncAutocomplete from "./formik-inputs/mui-formik-async-select";
diff --git a/src/components/mui/summit-addon-select.js b/src/components/mui/summit-addon-select.js
deleted file mode 100644
index 470114517..000000000
--- a/src/components/mui/summit-addon-select.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Copyright 2026 OpenStack Foundation
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- * http://www.apache.org/licenses/LICENSE-2.0
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * */
-
-import React, { useEffect, useState } from "react";
-import { MenuItem, Select } from "@mui/material";
-import PropTypes from "prop-types";
-import { querySummitAddons } from "../../actions/sponsor-actions";
-
-const SummitAddonSelect = ({
- value,
- summitId,
- placeholder = "Select...",
- onChange,
- inputProps = {}
-}) => {
- const [options, setOptions] = useState([]);
-
- useEffect(() => {
- querySummitAddons(summitId, (results) => {
- const normalized = results.map((r) => ({
- value: r,
- label: r
- }));
- setOptions(normalized);
- });
- }, []);
-
- const handleChange = (event) => {
- onChange(event.target.value);
- };
-
- return (
-
- );
-};
-
-SummitAddonSelect.propTypes = {
- value: PropTypes.string,
- summitId: PropTypes.number.isRequired,
- placeholder: PropTypes.string,
- onChange: PropTypes.func.isRequired
-};
-
-export default SummitAddonSelect;
diff --git a/src/pages/events/components/__tests__/event-type-dialog.test.js b/src/pages/events/components/__tests__/event-type-dialog.test.js
index 117a18bd6..9edb6eb01 100644
--- a/src/pages/events/components/__tests__/event-type-dialog.test.js
+++ b/src/pages/events/components/__tests__/event-type-dialog.test.js
@@ -35,7 +35,7 @@ jest.mock(
);
jest.mock(
- "../../../../components/mui/formik-inputs/mui-formik-select",
+ "openstack-uicore-foundation/lib/components/mui/formik-inputs/select",
() =>
function MockSelect({ name, children, disabled }) {
return (
diff --git a/src/pages/events/components/event-type-dialog.js b/src/pages/events/components/event-type-dialog.js
index 9faa3427e..8850809db 100644
--- a/src/pages/events/components/event-type-dialog.js
+++ b/src/pages/events/components/event-type-dialog.js
@@ -36,9 +36,9 @@ import Tab from "@mui/material/Tab";
import Tabs from "@mui/material/Tabs";
import TextField from "@mui/material/TextField";
import CloseIcon from "@mui/icons-material/Close";
+import MuiFormikSelect from "openstack-uicore-foundation/lib/components/mui/formik-inputs/select";
import useScrollToError from "../../../hooks/useScrollToError";
import MuiFormikAsyncAutocomplete from "../../../components/mui/formik-inputs/mui-formik-async-select";
-import MuiFormikSelect from "../../../components/mui/formik-inputs/mui-formik-select";
import MuiFormikColorField from "../../../components/mui/formik-inputs/mui-formik-color-field";
import {
positiveNumberValidation,
@@ -260,6 +260,21 @@ const EventTypeDialog = ({
"edit_event_type.placeholders.select_class"
)}
disabled={values.id !== 0}
+ renderValue={(selected) => {
+ if (!selected) {
+ return (
+
+ {T.translate(
+ "edit_event_type.placeholders.select_class"
+ )}
+
+ );
+ }
+ const selectedOption = classNameDdl.find(
+ (opt) => opt.value === selected
+ );
+ return selectedOption?.label || selected;
+ }}
>
{classNameDdl.map((opt) => (