Repository: provectus/kafka-ui
Issue: Add a space to all formatted sizes
Labels: good first issue
scope/frontend
status/accepted
type/chore

All sizes in the app are displayed like this: "123KB". Please add a space between the numeric and size parts.

Issue comments:
i wanna work on this, Can you please tell me any easy way of finding all the sizes in the application.
thanks
@haseeb-xd sure, go ahead. 
They might be formatted via the same function through the app. Topic statistics is one of the places with a lot of sizes.
@haseeb-xd any luck?

Pull request review comments:

Patch that resolved the issue:
diff --git a/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java b/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
index e619dc5c95d..d0632817b7d 100644
--- a/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
+++ b/kafka-ui-e2e-checks/src/main/java/com/provectus/kafka/ui/pages/topic/TopicDetails.java
@@ -21,7 +21,7 @@ public class TopicDetails {
     protected SelenideElement clearMessagesBtn = $x(("//div[contains(text(), 'Clear messages')]"));
     protected SelenideElement overviewTab = $x("//a[contains(text(),'Overview')]");
     protected SelenideElement messagesTab = $x("//a[contains(text(),'Messages')]");
-    protected SelenideElement editSettingsTab = $x("//li[@role][contains(text(),'Edit settings')]");
+    protected SelenideElement editSettingsMenu = $x("//li[@role][contains(text(),'Edit settings')]");
     protected SelenideElement removeTopicBtn = $x("//ul[@role='menu']//div[contains(text(),'Remove Topic')]");
     protected SelenideElement confirmBtn = $x("//div[@role='dialog']//button[contains(text(),'Confirm')]");
     protected SelenideElement produceMessageBtn = $x("//div//button[text()='Produce Message']");
@@ -39,7 +39,7 @@ public TopicDetails waitUntilScreenReady() {
     }
 
     @Step
-    public TopicDetails openTopicMenu(TopicMenu menu) {
+    public TopicDetails openDetailsTab(TopicMenu menu) {
         $(By.linkText(menu.getValue())).shouldBe(Condition.visible).click();
         return this;
     }
@@ -52,7 +52,7 @@ public TopicDetails openDotMenu() {
 
     @Step
     public TopicDetails clickEditSettingsMenu() {
-        editSettingsTab.shouldBe(Condition.visible).click();
+        editSettingsMenu.shouldBe(Condition.visible).click();
         return this;
     }
 
diff --git a/kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx b/kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx
index 9723149828c..ae6a7f4b3b6 100644
--- a/kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx
+++ b/kafka-ui-react-app/src/components/Brokers/Broker/__test__/Broker.spec.tsx
@@ -66,7 +66,7 @@ describe('Broker Component', () => {
     expect(
       screen.getByText(brokerDiskUsage?.segmentCount || '')
     ).toBeInTheDocument();
-    expect(screen.getByText('12MB')).toBeInTheDocument();
+    expect(screen.getByText('12 MB')).toBeInTheDocument();
 
     expect(screen.getByText('Segment Count')).toBeInTheDocument();
     expect(
diff --git a/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx b/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
index 4b5f046cd9d..833b0825184 100644
--- a/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
+++ b/kafka-ui-react-app/src/components/Topics/List/__tests__/TopicTable.spec.tsx
@@ -92,13 +92,13 @@ describe('TopicTable Components', () => {
         screen.getByRole('link', { name: '__internal.topic' })
       ).toBeInTheDocument();
       expect(
-        screen.getByRole('row', { name: '__internal.topic 1 0 1 0 0Bytes' })
+        screen.getByRole('row', { name: '__internal.topic 1 0 1 0 0 Bytes' })
       ).toBeInTheDocument();
       expect(
         screen.getByRole('link', { name: 'external.topic' })
       ).toBeInTheDocument();
       expect(
-        screen.getByRole('row', { name: 'external.topic 1 0 1 0 1KB' })
+        screen.getByRole('row', { name: 'external.topic 1 0 1 0 1 KB' })
       ).toBeInTheDocument();
 
       expect(screen.getAllByRole('checkbox').length).toEqual(3);
@@ -211,7 +211,7 @@ describe('TopicTable Components', () => {
         ).toEqual(2);
         // Internal topic action buttons are disabled
         const internalTopicRow = screen.getByRole('row', {
-          name: '__internal.topic 1 0 1 0 0Bytes',
+          name: '__internal.topic 1 0 1 0 0 Bytes',
         });
         expect(internalTopicRow).toBeInTheDocument();
         expect(
@@ -221,7 +221,7 @@ describe('TopicTable Components', () => {
         ).toBeDisabled();
         // External topic action buttons are enabled
         const externalTopicRow = screen.getByRole('row', {
-          name: 'external.topic 1 0 1 0 1KB',
+          name: 'external.topic 1 0 1 0 1 KB',
         });
         expect(externalTopicRow).toBeInTheDocument();
         const extBtn = within(externalTopicRow).getByRole('button', {
diff --git a/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.styled.ts b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.styled.ts
new file mode 100644
index 00000000000..dc24760572b
--- /dev/null
+++ b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.styled.ts
@@ -0,0 +1,5 @@
+import styled from 'styled-components';
+
+export const NoWrap = styled.span`
+  white-space: nowrap;
+`;
diff --git a/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx
index 7cfb35cfd21..5bfc5a1670e 100644
--- a/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx
+++ b/kafka-ui-react-app/src/components/common/BytesFormatted/BytesFormatted.tsx
@@ -1,5 +1,7 @@
 import React from 'react';
 
+import { NoWrap } from './BytesFormatted.styled';
+
 interface Props {
   value: string | number | undefined;
   precision?: number;
@@ -8,22 +10,22 @@ interface Props {
 export const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
 
 const BytesFormatted: React.FC<Props> = ({ value, precision = 0 }) => {
-  const formatedValue = React.useMemo((): string => {
+  const formattedValue = React.useMemo((): string => {
     try {
       const bytes = typeof value === 'string' ? parseInt(value, 10) : value;
       if (Number.isNaN(bytes) || (bytes && bytes < 0)) return `-Bytes`;
-      if (!bytes || bytes < 1024) return `${Math.ceil(bytes || 0)}${sizes[0]}`;
+      if (!bytes || bytes < 1024) return `${Math.ceil(bytes || 0)} ${sizes[0]}`;
       const pow = Math.floor(Math.log2(bytes) / 10);
       const multiplier = 10 ** (precision < 0 ? 0 : precision);
-      return (
-        Math.round((bytes * multiplier) / 1024 ** pow) / multiplier + sizes[pow]
-      );
+      return `${Math.round((bytes * multiplier) / 1024 ** pow) / multiplier} ${
+        sizes[pow]
+      }`;
     } catch (e) {
       return `-Bytes`;
     }
   }, [precision, value]);
 
-  return <span>{formatedValue}</span>;
+  return <NoWrap>{formattedValue}</NoWrap>;
 };
 
 export default BytesFormatted;
diff --git a/kafka-ui-react-app/src/components/common/BytesFormatted/__tests__/BytesFormatted.spec.tsx b/kafka-ui-react-app/src/components/common/BytesFormatted/__tests__/BytesFormatted.spec.tsx
index bfb7b85f502..e97767fa201 100644
--- a/kafka-ui-react-app/src/components/common/BytesFormatted/__tests__/BytesFormatted.spec.tsx
+++ b/kafka-ui-react-app/src/components/common/BytesFormatted/__tests__/BytesFormatted.spec.tsx
@@ -7,29 +7,29 @@ import { render, screen } from '@testing-library/react';
 describe('BytesFormatted', () => {
   it('renders Bytes correctly', () => {
     render(<BytesFormatted value={666} />);
-    expect(screen.getByText('666Bytes')).toBeInTheDocument();
+    expect(screen.getByText('666 Bytes')).toBeInTheDocument();
   });
 
   it('renders correct units', () => {
     let value = 1;
     sizes.forEach((unit) => {
       render(<BytesFormatted value={value} />);
-      expect(screen.getByText(`1${unit}`)).toBeInTheDocument();
+      expect(screen.getByText(`1 ${unit}`)).toBeInTheDocument();
       value *= 1024;
     });
   });
 
   it('renders correct precision', () => {
     render(<BytesFormatted value={2000} precision={100} />);
-    expect(screen.getByText(`1.953125${sizes[1]}`)).toBeInTheDocument();
+    expect(screen.getByText(`1.953125 ${sizes[1]}`)).toBeInTheDocument();
 
     render(<BytesFormatted value={10000} precision={5} />);
-    expect(screen.getByText(`9.76563${sizes[1]}`)).toBeInTheDocument();
+    expect(screen.getByText(`9.76563 ${sizes[1]}`)).toBeInTheDocument();
   });
 
   it('correctly handles invalid props', () => {
     render(<BytesFormatted value={10000} precision={-1} />);
-    expect(screen.getByText(`10${sizes[1]}`)).toBeInTheDocument();
+    expect(screen.getByText(`10 ${sizes[1]}`)).toBeInTheDocument();
 
     render(<BytesFormatted value="some string" />);
     expect(screen.getAllByText(`-${sizes[0]}`).length).toBeTruthy();
@@ -38,6 +38,6 @@ describe('BytesFormatted', () => {
     expect(screen.getAllByText(`-${sizes[0]}`).length).toBeTruthy();
 
     render(<BytesFormatted value={undefined} />);
-    expect(screen.getByText(`0${sizes[0]}`)).toBeInTheDocument();
+    expect(screen.getByText(`0 ${sizes[0]}`)).toBeInTheDocument();
   });
 });
diff --git a/kafka-ui-react-app/src/components/common/NewTable/__test__/Table.spec.tsx b/kafka-ui-react-app/src/components/common/NewTable/__test__/Table.spec.tsx
index dbfdddab662..2c65ea74b3d 100644
--- a/kafka-ui-react-app/src/components/common/NewTable/__test__/Table.spec.tsx
+++ b/kafka-ui-react-app/src/components/common/NewTable/__test__/Table.spec.tsx
@@ -125,10 +125,10 @@ describe('Table', () => {
 
   it('renders SizeCell', () => {
     renderComponent();
-    expect(screen.getByText('1KB')).toBeInTheDocument();
-    expect(screen.getByText('3Bytes')).toBeInTheDocument();
-    expect(screen.getByText('49KB')).toBeInTheDocument();
-    expect(screen.getByText('1MB')).toBeInTheDocument();
+    expect(screen.getByText('1 KB')).toBeInTheDocument();
+    expect(screen.getByText('3 Bytes')).toBeInTheDocument();
+    expect(screen.getByText('49 KB')).toBeInTheDocument();
+    expect(screen.getByText('1 MB')).toBeInTheDocument();
   });
 
   it('renders TimestampCell', () => {


Test patch:
diff --git a/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java b/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
index bf618f0222a..d707b5acafd 100644
--- a/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
+++ b/kafka-ui-e2e-checks/src/test/java/com/provectus/kafka/ui/tests/TopicTests.java
@@ -175,7 +175,7 @@ void produceMessage() {
                 .openTopic(TOPIC_FOR_MESSAGES.getName());
         topicDetails
                 .waitUntilScreenReady()
-                .openTopicMenu(TopicDetails.TopicMenu.MESSAGES)
+                .openDetailsTab(TopicDetails.TopicMenu.MESSAGES)
                 .clickProduceMessageBtn();
         produceMessagePanel
                 .waitUntilScreenReady()
@@ -204,7 +204,7 @@ void clearMessage() {
                 .openTopic(TOPIC_FOR_MESSAGES.getName());
         topicDetails
                 .waitUntilScreenReady()
-                .openTopicMenu(TopicDetails.TopicMenu.OVERVIEW)
+                .openDetailsTab(TopicDetails.TopicMenu.OVERVIEW)
                 .clickProduceMessageBtn();
         produceMessagePanel
                 .waitUntilScreenReady()
@@ -238,7 +238,7 @@ void redirectToConsumerFromTopic() {
                 .openTopic(topicName);
         topicDetails
                 .waitUntilScreenReady()
-                .openTopicMenu(TopicDetails.TopicMenu.CONSUMERS)
+                .openDetailsTab(TopicDetails.TopicMenu.CONSUMERS)
                 .openConsumerGroup(consumerGroupId);
         consumersDetails
                 .waitUntilScreenReady();