From 059d04b971e9e2344e10cff32944c23b001dcb4b Mon Sep 17 00:00:00 2001 From: Roman Vyakhirev Date: Wed, 28 May 2025 12:31:50 +0200 Subject: [PATCH] fix(file-uploader-web): fix file size units --- packages/modules/file-uploader/package.json | 2 +- .../file-uploader-web/CHANGELOG.md | 4 ++ .../file-uploader-web/package.json | 3 +- .../src/components/FileEntry.tsx | 2 +- .../file-uploader-web/src/package.xml | 2 +- .../src/stores/FileUploaderStore.ts | 8 +-- .../src/utils/__tests__/fileSize.spec.ts | 56 +++++++++++++++++++ .../file-uploader-web/src/utils/fileSize.ts | 28 ++++++++++ pnpm-lock.yaml | 31 +--------- 9 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/fileSize.spec.ts create mode 100644 packages/pluggableWidgets/file-uploader-web/src/utils/fileSize.ts diff --git a/packages/modules/file-uploader/package.json b/packages/modules/file-uploader/package.json index 90457ac3a6..f1e7ad8302 100644 --- a/packages/modules/file-uploader/package.json +++ b/packages/modules/file-uploader/package.json @@ -1,7 +1,7 @@ { "name": "@mendix/file-uploader", "moduleName": "File Uploader module", - "version": "2.2.0", + "version": "2.2.1", "copyright": "© Mendix Technology BV 2025. All rights reserved.", "license": "Apache-2.0", "private": true, diff --git a/packages/pluggableWidgets/file-uploader-web/CHANGELOG.md b/packages/pluggableWidgets/file-uploader-web/CHANGELOG.md index a222ffe4f4..6a1b7344a4 100644 --- a/packages/pluggableWidgets/file-uploader-web/CHANGELOG.md +++ b/packages/pluggableWidgets/file-uploader-web/CHANGELOG.md @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Fixed + +- We updated file size display in the file uploader to show commonly used units (KB, MB, GB) instead of previously incorrect units (Kb, Mb, Gb). Instead of using technically correct binary units (KiB, MiB, GiB), we chose a format more familiar to users. + ## [2.2.0] - 2025-05-07 ### Added diff --git a/packages/pluggableWidgets/file-uploader-web/package.json b/packages/pluggableWidgets/file-uploader-web/package.json index ffb0b436fd..65cce980a2 100644 --- a/packages/pluggableWidgets/file-uploader-web/package.json +++ b/packages/pluggableWidgets/file-uploader-web/package.json @@ -1,7 +1,7 @@ { "name": "@mendix/file-uploader-web", "widgetName": "FileUploader", - "version": "2.2.0", + "version": "2.2.1", "description": "", "copyright": "© Mendix Technology BV 2025. All rights reserved.", "license": "Apache-2.0", @@ -43,7 +43,6 @@ }, "dependencies": { "classnames": "^2.2.6", - "filesize.js": "^2.0.0", "mime-types": "^2.1.35", "mobx": "6.12.3", "mobx-react-lite": "4.0.7", diff --git a/packages/pluggableWidgets/file-uploader-web/src/components/FileEntry.tsx b/packages/pluggableWidgets/file-uploader-web/src/components/FileEntry.tsx index e7327cc04e..a01788d3a6 100644 --- a/packages/pluggableWidgets/file-uploader-web/src/components/FileEntry.tsx +++ b/packages/pluggableWidgets/file-uploader-web/src/components/FileEntry.tsx @@ -4,8 +4,8 @@ import { UploadInfo } from "./UploadInfo"; import { createElement, ReactElement, useCallback, MouseEvent, KeyboardEvent, ReactNode } from "react"; import { FileStatus, FileStore } from "../stores/FileStore"; import { observer } from "mobx-react-lite"; -import fileSize from "filesize.js"; import { FileIcon } from "./FileIcon"; +import { fileSize } from "../utils/fileSize"; import { FileUploaderContainerProps } from "../../typings/FileUploaderProps"; import { ActionsBar } from "./ActionsBar"; diff --git a/packages/pluggableWidgets/file-uploader-web/src/package.xml b/packages/pluggableWidgets/file-uploader-web/src/package.xml index cbe41b1af4..0cd4209854 100644 --- a/packages/pluggableWidgets/file-uploader-web/src/package.xml +++ b/packages/pluggableWidgets/file-uploader-web/src/package.xml @@ -1,6 +1,6 @@ - + diff --git a/packages/pluggableWidgets/file-uploader-web/src/stores/FileUploaderStore.ts b/packages/pluggableWidgets/file-uploader-web/src/stores/FileUploaderStore.ts index e54117f0ff..8efe71b29e 100644 --- a/packages/pluggableWidgets/file-uploader-web/src/stores/FileUploaderStore.ts +++ b/packages/pluggableWidgets/file-uploader-web/src/stores/FileUploaderStore.ts @@ -23,7 +23,7 @@ export class FileUploaderStore { _widgetName: string; _uploadMode: UploadModeEnum; - _maxFileSizeMb = 0; + _maxFileSizeMiB = 0; _maxFileSize = 0; _ds?: ListValue; _maxFilesPerUpload: number; @@ -34,8 +34,8 @@ export class FileUploaderStore { constructor(props: FileUploaderContainerProps, translations: TranslationsStore) { this._widgetName = props.name; - this._maxFileSizeMb = props.maxFileSize; - this._maxFileSize = this._maxFileSizeMb * 1024 * 1024; + this._maxFileSizeMiB = props.maxFileSize; + this._maxFileSize = this._maxFileSizeMiB * 1024 * 1024; this._maxFilesPerUpload = props.maxFilesPerUpload; this._uploadMode = props.uploadMode; @@ -149,7 +149,7 @@ export class FileUploaderStore { if (e.code === "file-too-large") { return this.translations.get( "uploadFailureFileIsTooBigMessage", - this._maxFileSizeMb.toString() + this._maxFileSizeMiB.toString() ); } return e.message; diff --git a/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/fileSize.spec.ts b/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/fileSize.spec.ts new file mode 100644 index 0000000000..ed2787a436 --- /dev/null +++ b/packages/pluggableWidgets/file-uploader-web/src/utils/__tests__/fileSize.spec.ts @@ -0,0 +1,56 @@ +import { fileSize } from "../fileSize"; + +describe("fileSize", () => { + it("should return '0B' for size 0", () => { + expect(fileSize(0)).toBe("0 B"); + }); + + it("should return '1B' for size 1", () => { + expect(fileSize(1)).toBe("1 B"); + }); + + it("should return '1KB' for size 1024", () => { + expect(fileSize(1024)).toBe("1 KB"); + }); + + it("should return '1MB' for size 1048576 (1024 * 1024)", () => { + expect(fileSize(1024 * 1024)).toBe("1 MB"); + }); + + it("should return '1GB' for size 1073741824 (1024 * 1024 * 1024)", () => { + expect(fileSize(1024 * 1024 * 1024)).toBe("1 GB"); + }); + + it("should handle large sizes correctly", () => { + expect(fileSize(1024 ** 5.0001)).toBe("1 PB"); + }); + + it("empty for negative sizes", () => { + expect(fileSize(-1)).toBe(""); + }); + + it("should return one decimal digit for sizes less than 100", () => { + expect(fileSize(64.06 * 1024)).toBe("64.1 KB"); + expect(fileSize(85.4 * 1024)).toBe("85.4 KB"); + expect(fileSize(99.9 * 1024)).toBe("99.9 KB"); + }); + + it("should return two decimal digits for sizes less than 10", () => { + expect(fileSize(5.009 * 1024)).toBe("5.01 KB"); + expect(fileSize(9.11 * 1024)).toBe("9.11 KB"); + expect(fileSize(1.91 * 1024)).toBe("1.91 KB"); + + expect(fileSize(100.91 * 1024)).toBe("100 KB"); + }); + + it("should return the round sizes for sizes below the thresholds", () => { + expect(fileSize(85.03 * 1024)).toBe("85 KB"); + expect(fileSize(7.001 * 1024)).toBe("7 KB"); + expect(fileSize(70268742)).toBe("67 MB"); + }); + + it("should return the correct unit for sizes just below the next threshold", () => { + expect(fileSize(1023)).toBe("1023 B"); + expect(fileSize(1024 * 1024 - 1)).toBe("1023 KB"); + }); +}); diff --git a/packages/pluggableWidgets/file-uploader-web/src/utils/fileSize.ts b/packages/pluggableWidgets/file-uploader-web/src/utils/fileSize.ts new file mode 100644 index 0000000000..53e756f790 --- /dev/null +++ b/packages/pluggableWidgets/file-uploader-web/src/utils/fileSize.ts @@ -0,0 +1,28 @@ +/** + * Utility function to format file size into human-readable string. + * It uses IEC 60027 standard, meaning the number is divided by 1024 for each unit. + * And prefixes are B, KB, MB, GB, TB, PB, EB, ZB, YB. + * While the prefixes are not technically correct, they are widely used in the industry. + */ +export function fileSize(size: number): string { + if (size < 0) { + return ""; + } + + const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; + let unitIndex = 0; + + while (size >= 1024 && unitIndex < units.length - 1) { + size = size / 1024; + unitIndex++; + } + + const formattedSize = + size < 10 && size % 1 > 0.005 + ? size.toFixed(2) + : size < 100 && size % 1 > 0.05 + ? size.toFixed(1) + : Math.floor(size); + + return `${formattedSize} ${units[unitIndex]}`; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 09d44831db..38e1a1ddeb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1413,9 +1413,6 @@ importers: classnames: specifier: ^2.2.6 version: 2.3.2 - filesize.js: - specifier: ^2.0.0 - version: 2.0.0 mime-types: specifier: ^2.1.35 version: 2.1.35(patch_hash=f54449b9273bc9e74fb67a14fcd001639d788d038b7eb0b5f43c10dff2b1adfb) @@ -7042,9 +7039,6 @@ packages: filelist@1.0.4: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - filesize.js@2.0.0: - resolution: {integrity: sha512-dOngzfsRfjwTQkG3UPY3GI+OqbBj4mO1UW1FoDYN2SF/p7hACKXHKGjgA62ug4KtORXcXdlEKOXrgfzt+qB2Nw==} - fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -13332,7 +13326,7 @@ snapshots: '@types/react-dom': 18.2.14 '@types/react-native': 0.72.8(react-native@0.75.3(@babel/core@7.27.1)(@babel/preset-env@7.26.9(@babel/core@7.27.1))(@types/react@18.2.36)(react@18.2.0)(typescript@5.1.6)) '@types/testing-library__jest-dom': 5.14.9 - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.1.6))(eslint@7.32.0)(typescript@5.1.6) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.2))(eslint@7.32.0)(typescript@5.8.2) '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.2) ansi-colors: 4.1.1 babel-eslint: 10.1.0(eslint@7.32.0) @@ -14795,25 +14789,6 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.1.6))(eslint@7.32.0)(typescript@5.1.6)': - dependencies: - '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 5.62.0(eslint@7.32.0)(typescript@5.8.2) - '@typescript-eslint/scope-manager': 5.62.0 - '@typescript-eslint/type-utils': 5.62.0(eslint@7.32.0)(typescript@5.8.2) - '@typescript-eslint/utils': 5.62.0(eslint@7.32.0)(typescript@5.8.2) - debug: 4.4.1 - eslint: 7.32.0 - graphemer: 1.4.0 - ignore: 5.3.2 - natural-compare-lite: 1.4.0 - semver: 7.7.2 - tsutils: 3.21.0(typescript@5.8.2) - optionalDependencies: - typescript: 5.1.6 - transitivePeerDependencies: - - supports-color - '@typescript-eslint/eslint-plugin@5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.2))(eslint@7.32.0)(typescript@5.8.2)': dependencies: '@eslint-community/regexpp': 4.12.1 @@ -17266,7 +17241,7 @@ snapshots: '@typescript-eslint/experimental-utils': 4.33.0(eslint@7.32.0)(typescript@5.8.2) eslint: 7.32.0 optionalDependencies: - '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.1.6))(eslint@7.32.0)(typescript@5.1.6) + '@typescript-eslint/eslint-plugin': 5.62.0(@typescript-eslint/parser@5.62.0(eslint@7.32.0)(typescript@5.8.2))(eslint@7.32.0)(typescript@5.8.2) transitivePeerDependencies: - supports-color - typescript @@ -17701,8 +17676,6 @@ snapshots: dependencies: minimatch: 5.0.1 - filesize.js@2.0.0: {} - fill-range@7.0.1: dependencies: to-regex-range: 5.0.1