Skip to content
Merged
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "openstack-uicore-foundation",
"version": "5.0.38",
"version": "5.0.41-beta.0",
"description": "ui reactjs components for openstack marketing site",
"main": "lib/openstack-uicore-foundation.js",
"scripts": {
Expand Down
69 changes: 69 additions & 0 deletions src/components/mui/__tests__/mui-formik-file-size-field.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,75 @@ describe("MuiFormikFilesizeField", () => {
});
});

describe("custom units", () => {
it("displays and stores the value as-is when valueUnit and displayUnit are both KB", async () => {
const onSubmit = jest.fn();
renderWithFormik(
{
label: "Max File Size",
onSubmit,
valueUnit: "KB",
displayUnit: "KB"
},
{ max_file_size: 1024 }
);

const field = screen.getByLabelText("Max File Size");
expect(field).toHaveValue(1024);

await act(async () => {
await userEvent.clear(field);
await userEvent.type(field, "2048");
await userEvent.click(screen.getByText("submit"));
});

expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ max_file_size: 2048 }),
expect.anything()
);
});

it("converts bytes to KB for display when displayUnit is KB", () => {
renderWithFormik(
{ label: "Max File Size", onSubmit: jest.fn(), displayUnit: "KB" },
{ max_file_size: 2048 } // 2 * 1024
);

const field = screen.getByLabelText("Max File Size");
expect(field).toHaveValue(2);
});

it("converts KB input to bytes when displayUnit is KB", async () => {
const onSubmit = jest.fn();
renderWithFormik(
{ label: "Max File Size", onSubmit, displayUnit: "KB" },
{ max_file_size: 0 }
);

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

await act(async () => {
await userEvent.clear(field);
await userEvent.type(field, "5");
await userEvent.click(screen.getByText("submit"));
});

expect(onSubmit).toHaveBeenCalledWith(
expect.objectContaining({ max_file_size: 5 * 1024 }),
expect.anything()
);
});

it("shows the displayUnit as the field's unit adornment", () => {
renderWithFormik(
{ label: "Max File Size", onSubmit: jest.fn(), displayUnit: "KB" },
{ max_file_size: 0 }
);

expect(screen.getByText("KB")).toBeInTheDocument();
});
});

describe("blocked keys", () => {
it.each(["e", "E", "+", "-", ".", ","])(
"blocks '%s' key from being entered",
Expand Down
5 changes: 3 additions & 2 deletions src/components/mui/editable-table/mui-table-editable.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ const MuiTableEditable = ({
onArchive,
onDelete,
onCellChange, // New prop for handling cell value changes
deleteDialogBody
deleteDialogBody,
tableSx = {}
}) => {
// State to track which cell is currently being edited
const [editingCell, setEditingCell] = React.useState(null);
Expand Down Expand Up @@ -235,7 +236,7 @@ const MuiTableEditable = ({
component={Paper}
sx={{ borderRadius: 0, boxShadow: "none" }}
>
<Table>
<Table sx={tableSx}>
{/* TABLE HEADER */}
<TableHead sx={{ backgroundColor: "#EAEAEA" }}>
<TableRow>
Expand Down
39 changes: 29 additions & 10 deletions src/components/mui/formik-inputs/mui-formik-file-size-field.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,38 +16,48 @@ 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);
// bytes = value * 1024 ** UNIT_POWERS[unit]
const UNIT_POWERS = { B: 0, KB: 1, MB: 2 };

const MuiFormikFilesizeField = ({ name, label, ...props }) => {
const unitToBytesFactor = (unit) => 1024 ** UNIT_POWERS[unit];

Comment on lines +23 to +26
const MuiFormikFilesizeField = ({
name,
label,
displayUnit,
valueUnit,
...props
}) => {
const [field, meta, helpers] = useField(name);
const [cleared, setCleared] = useState(false);

const emptyValue = meta.initialValue === null ? null : 0;
// value (in valueUnit) -> displayed number (in displayUnit)
const conversionFactor =
unitToBytesFactor(valueUnit) / unitToBytesFactor(displayUnit);

const getDisplayValue = () => {
if (cleared) return "";
if (field.value == null || field.value === 0) {
return field.value === 0 ? 0 : "";
}
return bytesToMb(field.value);
return Math.floor(field.value * conversionFactor);
};

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

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

setCleared(false);
const bytes = Number(mbValue) * BYTES_PER_MB;
helpers.setValue(bytes);
helpers.setValue(Number(displayValue) / conversionFactor);
};
Comment on lines 59 to 61

const handleKeyDown = (e) => {
Expand All @@ -73,7 +83,9 @@ const MuiFormikFilesizeField = ({ name, label, ...props }) => {
onChange={handleChange}
slotProps={{
input: {
endAdornment: <InputAdornment position="end">MB</InputAdornment>
endAdornment: (
<InputAdornment position="end">{displayUnit}</InputAdornment>
)
},
htmlInput: {
min: 0,
Expand All @@ -90,7 +102,14 @@ const MuiFormikFilesizeField = ({ name, label, ...props }) => {

MuiFormikFilesizeField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string.isRequired
label: PropTypes.string.isRequired,
displayUnit: PropTypes.oneOf(Object.keys(UNIT_POWERS)),
valueUnit: PropTypes.oneOf(Object.keys(UNIT_POWERS))
};

MuiFormikFilesizeField.defaultProps = {
displayUnit: "MB",
valueUnit: "B"
};

export default MuiFormikFilesizeField;
5 changes: 3 additions & 2 deletions src/components/mui/sortable-table/mui-table-sortable.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ const MuiTableSortable = ({
deleteDialogBody = null,
onReorder,
idKey = "id",
updateOrderKey = "order"
updateOrderKey = "order",
tableSx = {}
}) => {
const handleChangePage = (_, newPage) => {
onPageChange(newPage + 1);
Expand Down Expand Up @@ -128,7 +129,7 @@ const MuiTableSortable = ({
component={Paper}
sx={{ borderRadius: 0, boxShadow: "none" }}
>
<Table>
<Table sx={tableSx}>
{/* TABLE HEADER */}
<TableHead sx={{ backgroundColor: "#EAEAEA" }}>
<TableRow>
Expand Down
8 changes: 5 additions & 3 deletions src/components/mui/table/mui-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ const MuiTable = ({
deleteDialogTitle = null,
deleteDialogBody = null,
deleteDialogConfirmText = null,
confirmButtonColor = null
confirmButtonColor = null,
tableSx = {}
}) => {
const totalColumnsCount =
columns.length + (onEdit ? 1 : 0) + (onDelete ? 1 : 0) + (onArchive ? 1 : 0) + (onSelect ? 1 : 0);
Expand Down Expand Up @@ -157,7 +158,7 @@ const MuiTable = ({
component={Paper}
sx={{ borderRadius: 0, boxShadow: "none" }}
>
<Table sx={{ tableLayout: "fixed" }}>
<Table sx={{ tableLayout: "fixed", ...tableSx }}>
{/* TABLE HEADER */}
<TableHead sx={{ backgroundColor: "#EAEDF4" }}>
<TableRow>
Expand Down Expand Up @@ -372,7 +373,8 @@ MuiTable.propTypes = {
deleteDialogTitle: PropTypes.string,
deleteDialogBody: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
deleteDialogConfirmText: PropTypes.string,
confirmButtonColor: PropTypes.string
confirmButtonColor: PropTypes.string,
tableSx: PropTypes.object
};

export default MuiTable;
Loading