This is an automated email from the ASF dual-hosted git repository.
villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 0e9c0f621a feat(formatting): Add memory units adaptive formatter to
format bytes (#30559)
0e9c0f621a is described below
commit 0e9c0f621ac9ddbcf889045f3d4772b1ee213e8d
Author: Mateusz Kopeć <[email protected]>
AuthorDate: Sat Oct 12 01:02:03 2024 +0200
feat(formatting): Add memory units adaptive formatter to format bytes
(#30559)
---
.../src/utils/D3Formatting.ts | 2 +
.../factories/createMemoryFormatter.ts | 55 +++++++++++++
.../superset-ui-core/src/number-format/index.ts | 1 +
.../factories/createMemoryFormatter.test.ts | 94 ++++++++++++++++++++++
.../test/number-format/index.test.ts | 2 +
superset-frontend/src/setup/setupFormatters.ts | 5 +-
6 files changed, 158 insertions(+), 1 deletion(-)
diff --git
a/superset-frontend/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts
b/superset-frontend/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts
index a8fd6312cb..5541c4a4b4 100644
---
a/superset-frontend/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts
+++
b/superset-frontend/packages/superset-ui-chart-controls/src/utils/D3Formatting.ts
@@ -57,6 +57,8 @@ export const D3_FORMAT_OPTIONS: [string, string][] = [
...d3Formatted,
['DURATION', t('Duration in ms (66000 => 1m 6s)')],
['DURATION_SUB', t('Duration in ms (1.40008 => 1ms 400µs 80ns)')],
+ ['MEMORY_DECIMAL', t('Memory in bytes - decimal (1024B => 1.024kB)')],
+ ['MEMORY_BINARY', t('Memory in bytes - binary (1024B => 1KiB)')],
];
export const D3_TIME_FORMAT_DOCS = t(
diff --git
a/superset-frontend/packages/superset-ui-core/src/number-format/factories/createMemoryFormatter.ts
b/superset-frontend/packages/superset-ui-core/src/number-format/factories/createMemoryFormatter.ts
new file mode 100644
index 0000000000..8d62948939
--- /dev/null
+++
b/superset-frontend/packages/superset-ui-core/src/number-format/factories/createMemoryFormatter.ts
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 NumberFormatter from '../NumberFormatter';
+
+export default function createMemoryFormatter(
+ config: {
+ description?: string;
+ id?: string;
+ label?: string;
+ binary?: boolean;
+ decimals?: number;
+ } = {},
+) {
+ const { description, id, label, binary, decimals = 2 } = config;
+
+ return new NumberFormatter({
+ description,
+ formatFunc: value => {
+ if (value === 0) return '0B';
+
+ const sign = value > 0 ? '' : '-';
+ const absValue = Math.abs(value);
+
+ const suffixes = binary
+ ? ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
+ : ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB'];
+ const base = binary ? 1024 : 1000;
+
+ const i = Math.min(
+ suffixes.length - 1,
+ Math.floor(Math.log(absValue) / Math.log(base)),
+ );
+ return `${sign}${parseFloat((absValue / Math.pow(base,
i)).toFixed(decimals))}${suffixes[i]}`;
+ },
+ id: id ?? 'memory_format',
+ label: label ?? `Memory formatter`,
+ });
+}
diff --git
a/superset-frontend/packages/superset-ui-core/src/number-format/index.ts
b/superset-frontend/packages/superset-ui-core/src/number-format/index.ts
index c65537552e..b9835d332d 100644
--- a/superset-frontend/packages/superset-ui-core/src/number-format/index.ts
+++ b/superset-frontend/packages/superset-ui-core/src/number-format/index.ts
@@ -31,5 +31,6 @@ export {
export { default as NumberFormatterRegistry } from './NumberFormatterRegistry';
export { default as createD3NumberFormatter } from
'./factories/createD3NumberFormatter';
export { default as createDurationFormatter } from
'./factories/createDurationFormatter';
+export { default as createMemoryFormatter } from
'./factories/createMemoryFormatter';
export { default as createSiAtMostNDigitFormatter } from
'./factories/createSiAtMostNDigitFormatter';
export { default as createSmartNumberFormatter } from
'./factories/createSmartNumberFormatter';
diff --git
a/superset-frontend/packages/superset-ui-core/test/number-format/factories/createMemoryFormatter.test.ts
b/superset-frontend/packages/superset-ui-core/test/number-format/factories/createMemoryFormatter.test.ts
new file mode 100644
index 0000000000..e4dc37d77a
--- /dev/null
+++
b/superset-frontend/packages/superset-ui-core/test/number-format/factories/createMemoryFormatter.test.ts
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 { NumberFormatter, createMemoryFormatter } from '@superset-ui/core';
+
+test('creates an instance of MemoryFormatter', () => {
+ const formatter = createMemoryFormatter();
+ expect(formatter).toBeInstanceOf(NumberFormatter);
+});
+
+test('formats bytes in human readable format with default options', () => {
+ const formatter = createMemoryFormatter();
+ expect(formatter(0)).toBe('0B');
+ expect(formatter(50)).toBe('50B');
+ expect(formatter(555)).toBe('555B');
+ expect(formatter(1000)).toBe('1kB');
+ expect(formatter(1111)).toBe('1.11kB');
+ expect(formatter(1024)).toBe('1.02kB');
+ expect(formatter(1337)).toBe('1.34kB');
+ expect(formatter(1999)).toBe('2kB');
+ expect(formatter(10 * 1000)).toBe('10kB');
+ expect(formatter(100 * 1000)).toBe('100kB');
+ expect(formatter(Math.pow(1000, 2))).toBe('1MB');
+ expect(formatter(Math.pow(1000, 3))).toBe('1GB');
+ expect(formatter(Math.pow(1000, 4))).toBe('1TB');
+ expect(formatter(Math.pow(1000, 5))).toBe('1PB');
+ expect(formatter(Math.pow(1000, 6))).toBe('1EB');
+ expect(formatter(Math.pow(1000, 7))).toBe('1ZB');
+ expect(formatter(Math.pow(1000, 8))).toBe('1YB');
+ expect(formatter(Math.pow(1000, 9))).toBe('1RB');
+ expect(formatter(Math.pow(1000, 10))).toBe('1QB');
+ expect(formatter(Math.pow(1000, 11))).toBe('1000QB');
+ expect(formatter(Math.pow(1000, 12))).toBe('1000000QB');
+});
+
+test('formats negative bytes in human readable format with default options',
() => {
+ const formatter = createMemoryFormatter();
+ expect(formatter(-50)).toBe('-50B');
+});
+
+test('formats float bytes in human readable format with default options', ()
=> {
+ const formatter = createMemoryFormatter();
+ expect(formatter(10.666)).toBe('10.67B');
+ expect(formatter(1200.666)).toBe('1.2kB');
+});
+
+test('formats bytes in human readable format with additional binary option',
() => {
+ const formatter = createMemoryFormatter({ binary: true });
+ expect(formatter(0)).toBe('0B');
+ expect(formatter(50)).toBe('50B');
+ expect(formatter(555)).toBe('555B');
+ expect(formatter(1000)).toBe('1000B');
+ expect(formatter(1111)).toBe('1.08KiB');
+ expect(formatter(1024)).toBe('1KiB');
+ expect(formatter(1337)).toBe('1.31KiB');
+ expect(formatter(2047)).toBe('2KiB');
+ expect(formatter(10 * 1024)).toBe('10KiB');
+ expect(formatter(100 * 1024)).toBe('100KiB');
+ expect(formatter(Math.pow(1024, 2))).toBe('1MiB');
+ expect(formatter(Math.pow(1024, 3))).toBe('1GiB');
+ expect(formatter(Math.pow(1024, 4))).toBe('1TiB');
+ expect(formatter(Math.pow(1024, 5))).toBe('1PiB');
+ expect(formatter(Math.pow(1024, 6))).toBe('1EiB');
+ expect(formatter(Math.pow(1024, 7))).toBe('1ZiB');
+ expect(formatter(Math.pow(1024, 8))).toBe('1YiB');
+ expect(formatter(Math.pow(1024, 9))).toBe('1024YiB');
+ expect(formatter(Math.pow(1024, 10))).toBe('1048576YiB');
+});
+
+test('formats bytes in human readable format with additional decimals option',
() => {
+ const formatter0decimals = createMemoryFormatter({ decimals: 0 });
+ expect(formatter0decimals(0)).toBe('0B');
+ expect(formatter0decimals(1111)).toBe('1kB');
+
+ const formatter3decimals = createMemoryFormatter({ decimals: 3 });
+ expect(formatter3decimals(0)).toBe('0B');
+ expect(formatter3decimals(1111)).toBe('1.111kB');
+});
diff --git
a/superset-frontend/packages/superset-ui-core/test/number-format/index.test.ts
b/superset-frontend/packages/superset-ui-core/test/number-format/index.test.ts
index 09395e722e..103f5e44a9 100644
---
a/superset-frontend/packages/superset-ui-core/test/number-format/index.test.ts
+++
b/superset-frontend/packages/superset-ui-core/test/number-format/index.test.ts
@@ -21,6 +21,7 @@ import {
createD3NumberFormatter,
createDurationFormatter,
createSiAtMostNDigitFormatter,
+ createMemoryFormatter,
formatNumber,
getNumberFormatter,
getNumberFormatterRegistry,
@@ -35,6 +36,7 @@ describe('index', () => {
createD3NumberFormatter,
createDurationFormatter,
createSiAtMostNDigitFormatter,
+ createMemoryFormatter,
formatNumber,
getNumberFormatter,
getNumberFormatterRegistry,
diff --git a/superset-frontend/src/setup/setupFormatters.ts
b/superset-frontend/src/setup/setupFormatters.ts
index e18aeba9dc..384b1be9e3 100644
--- a/superset-frontend/src/setup/setupFormatters.ts
+++ b/superset-frontend/src/setup/setupFormatters.ts
@@ -28,6 +28,7 @@ import {
createSmartDateFormatter,
createSmartDateVerboseFormatter,
createSmartDateDetailedFormatter,
+ createMemoryFormatter,
} from '@superset-ui/core';
import { FormatLocaleDefinition } from 'd3-format';
import { TimeLocaleDefinition } from 'd3-time-format';
@@ -76,7 +77,9 @@ export default function setupFormatters(
.registerValue(
'DURATION_SUB',
createDurationFormatter({ formatSubMilliseconds: true }),
- );
+ )
+ .registerValue('MEMORY_DECIMAL', createMemoryFormatter({ binary: false }))
+ .registerValue('MEMORY_BINARY', createMemoryFormatter({ binary: true }));
const timeFormatterRegistry = getTimeFormatterRegistry();